/*

This file is part of Sencha Touch 2

Copyright (c) 2012 Sencha Inc

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

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

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

*/
/**
 * @class Ext
 * @singleton
 */
(function() {
    var global = this,
        objectPrototype = Object.prototype,
        toString = objectPrototype.toString,
        enumerables = true,
        enumerablesTest = { toString: 1 },
        emptyFn = function(){},
        i;

    if (typeof Ext === 'undefined') {
        global.Ext = {};
    }

    Ext.global = global;

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

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

    /**
     * An array containing extra enumerables for old browsers
     * @property {String[]}
     */
    Ext.enumerables = enumerables;

    /**
     * Copies all the properties of config to the specified object.
     * Note that if recursive merging and cloning without referencing the original objects / arrays is needed, use
     * {@link Ext.Object#merge} instead.
     * @param {Object} object The receiver of the properties
     * @param {Object} config The source of the properties
     * @param {Object} defaults A different object that will also be applied for default values
     * @return {Object} returns obj
     */
    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-',
        scopeResetCSS: false
    }, Ext.buildSettings || {});

    Ext.apply(Ext, {
        /**
         * A reusable empty function
         */
        emptyFn: emptyFn,

        baseCSSPrefix: Ext.buildSettings.baseCSSPrefix,

        /**
         * Copies all the properties of config to object if they don't already exist.
         * @param {Object} object The receiver of the properties
         * @param {Object} config The source of the properties
         * @return {Object} returns obj
         */
        applyIf: function(object, config) {
            var property;

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

            return object;
        },

        /**
         * Iterates either an array or an object. This method delegates to
         * {@link Ext.Array#each Ext.Array.each} if the given value is iterable, and {@link Ext.Object#each Ext.Object.each} otherwise.
         *
         * @param {Object/Array} object The object or array to be iterated.
         * @param {Function} fn The function to be called for each iteration. See and {@link Ext.Array#each Ext.Array.each} and
         * {@link Ext.Object#each Ext.Object.each} for detailed lists of arguments passed to this function depending on the given object
         * type that is being iterated.
         * @param {Object} scope (Optional) The scope (`this` reference) in which the specified function is executed.
         * Defaults to the object being iterated itself.
         * @markdown
         */
        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, {

        /**
         * This method deprecated. Use {@link Ext#define Ext.define} instead.
         * @method
         * @param {Function} superclass
         * @param {Object} overrides
         * @return {Function} The subclass constructor from the <tt>overrides</tt> parameter, or a generated one if not provided.
         * @deprecated 4.0.0 Please use {@link Ext#define Ext.define} instead
         */
        extend: function() {
            // inline overrides
            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) {
                // First we check if the user passed in just the superClass with overrides
                if (Ext.isObject(superclass)) {
                    overrides = superclass;
                    superclass = subclass;
                    subclass = overrides.constructor !== objectConstructor ? overrides.constructor : function() {
                        superclass.apply(this, arguments);
                    };
                }

                if (!superclass) {
                    Ext.Error.raise({
                        sourceClass: 'Ext',
                        sourceMethod: 'extend',
                        msg: 'Attempting to extend from a class which has not been loaded on the page.'
                    });
                }

                // We create a new temporary class
                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;
            };
        }(),

        /**
         * Proxy to {@link Ext.Base#override}. Please refer {@link Ext.Base#override} for further details.
         *
         * @param {Object} cls The class to override
         * @param {Object} overrides The properties to add to origClass. This should be specified as an object literal
         * containing one or more properties.
         * @method override
         * @markdown
         * @deprecated 4.1.0 Please use {@link Ext#define Ext.define} instead
         */
        override: function(cls, overrides) {
            if (cls.$isClass) {
                return cls.override(overrides);
            }
            else {
                Ext.apply(cls.prototype, overrides);
            }
        }
    });

    // A full set of static methods to do type checking
    Ext.apply(Ext, {

        /**
         * Returns the given value itself if it's not empty, as described in {@link Ext#isEmpty}; returns the default
         * value (second argument) otherwise.
         *
         * @param {Object} value The value to test
         * @param {Object} defaultValue The value to return if the original value is empty
         * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
         * @return {Object} value, if non-empty, else defaultValue
         */
        valueFrom: function(value, defaultValue, allowBlank){
            return Ext.isEmpty(value, allowBlank) ? defaultValue : value;
        },

        /**
         * Returns the type of the given variable in string format. List of possible values are:
         *
         * - `undefined`: If the given value is `undefined`
         * - `null`: If the given value is `null`
         * - `string`: If the given value is a string
         * - `number`: If the given value is a number
         * - `boolean`: If the given value is a boolean value
         * - `date`: If the given value is a `Date` object
         * - `function`: If the given value is a function reference
         * - `object`: If the given value is an object
         * - `array`: If the given value is an array
         * - `regexp`: If the given value is a regular expression
         * - `element`: If the given value is a DOM Element
         * - `textnode`: If the given value is a DOM text node and contains something other than whitespace
         * - `whitespace`: If the given value is a DOM text node and contains only whitespace
         *
         * @param {Object} value
         * @return {String}
         * @markdown
         */
        typeOf: function(value) {
            if (value === null) {
                return 'null';
            }

            var type = typeof value;

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

            var 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 (/\S/).test(value.nodeValue) ? 'textnode' : 'whitespace';
                    }
                    else {
                        return 'element';
                    }
                }

                return 'object';
            }

            Ext.Error.raise({
                sourceClass: 'Ext',
                sourceMethod: 'typeOf',
                msg: 'Failed to determine the type of the specified value "' + value + '". This is most likely a bug.'
            });
        },

        /**
         * Returns true if the passed value is empty, false otherwise. The value is deemed to be empty if it is either:
         *
         * - `null`
         * - `undefined`
         * - a zero-length array
         * - a zero-length string (Unless the `allowEmptyString` parameter is set to `true`)
         *
         * @param {Object} value The value to test
         * @param {Boolean} allowEmptyString (optional) true to allow empty strings (defaults to false)
         * @return {Boolean}
         * @markdown
         */
        isEmpty: function(value, allowEmptyString) {
            return (value === null) || (value === undefined) || (!allowEmptyString ? value === '' : false) || (Ext.isArray(value) && value.length === 0);
        },

        /**
         * Returns true if the passed value is a JavaScript Array, false otherwise.
         *
         * @param {Object} target The target to test
         * @return {Boolean}
         * @method
         */
        isArray: ('isArray' in Array) ? Array.isArray : function(value) {
            return toString.call(value) === '[object Array]';
        },

        /**
         * Returns true if the passed value is a JavaScript Date object, false otherwise.
         * @param {Object} object The object to test
         * @return {Boolean}
         */
        isDate: function(value) {
            return toString.call(value) === '[object Date]';
        },

        /**
         * Returns true if the passed value is a JavaScript Object, false otherwise.
         * @param {Object} value The value to test
         * @return {Boolean}
         * @method
         */
        isObject: (toString.call(null) === '[object Object]') ?
        function(value) {
            // check ownerDocument here as well to exclude DOM nodes
            return value !== null && value !== undefined && toString.call(value) === '[object Object]' && value.ownerDocument === undefined;
        } :
        function(value) {
            return toString.call(value) === '[object Object]';
        },

        /**
         * @private
         */
        isSimpleObject: function(value) {
            return value instanceof Object && value.constructor === Object;
        },
        /**
         * Returns true if the passed value is a JavaScript 'primitive', a string, number or boolean.
         * @param {Object} value The value to test
         * @return {Boolean}
         */
        isPrimitive: function(value) {
            var type = typeof value;

            return type === 'string' || type === 'number' || type === 'boolean';
        },

        /**
         * Returns true if the passed value is a JavaScript Function, false otherwise.
         * @param {Object} value The value to test
         * @return {Boolean}
         * @method
         */
        isFunction:
        // Safari 3.x and 4.x returns 'function' for typeof <NodeList>, hence we need to fall back to using
        // Object.prorotype.toString (slower)
        (typeof document !== 'undefined' && typeof document.getElementsByTagName('body') === 'function') ? function(value) {
            return toString.call(value) === '[object Function]';
        } : function(value) {
            return typeof value === 'function';
        },

        /**
         * Returns true if the passed value is a number. Returns false for non-finite numbers.
         * @param {Object} value The value to test
         * @return {Boolean}
         */
        isNumber: function(value) {
            return typeof value === 'number' && isFinite(value);
        },

        /**
         * Validates that a value is numeric.
         * @param {Object} value Examples: 1, '1', '2.34'
         * @return {Boolean} True if numeric, false otherwise
         */
        isNumeric: function(value) {
            return !isNaN(parseFloat(value)) && isFinite(value);
        },

        /**
         * Returns true if the passed value is a string.
         * @param {Object} value The value to test
         * @return {Boolean}
         */
        isString: function(value) {
            return typeof value === 'string';
        },

        /**
         * Returns true if the passed value is a boolean.
         *
         * @param {Object} value The value to test
         * @return {Boolean}
         */
        isBoolean: function(value) {
            return typeof value === 'boolean';
        },

        /**
         * Returns true if the passed value is an HTMLElement
         * @param {Object} value The value to test
         * @return {Boolean}
         */
        isElement: function(value) {
            return value ? value.nodeType === 1 : false;
        },

        /**
         * Returns true if the passed value is a TextNode
         * @param {Object} value The value to test
         * @return {Boolean}
         */
        isTextNode: function(value) {
            return value ? value.nodeName === "#text" : false;
        },

        /**
         * Returns true if the passed value is defined.
         * @param {Object} value The value to test
         * @return {Boolean}
         */
        isDefined: function(value) {
            return typeof value !== 'undefined';
        },

        /**
         * Returns true if the passed value is iterable, false otherwise
         * @param {Object} value The value to test
         * @return {Boolean}
         */
        isIterable: function(value) {
            return (value && typeof value !== 'string') ? value.length !== undefined : false;
        }
    });

    Ext.apply(Ext, {

        /**
         * Clone almost any type of variable including array, object, DOM nodes and Date without keeping the old reference
         * @param {Object} item The variable to clone
         * @return {Object} clone
         */
        clone: function(item) {
            if (item === null || item === undefined) {
                return item;
            }

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

            // Strings
            var type = toString.call(item);

            // Dates
            if (type === '[object Date]') {
                return new Date(item.getTime());
            }

            var i, j, k, clone, key;

            // Arrays
            if (type === '[object Array]') {
                i = item.length;

                clone = [];

                while (i--) {
                    clone[i] = Ext.clone(item[i]);
                }
            }
            // Objects
            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];
                        clone[k] = item[k];
                    }
                }
            }

            return clone || item;
        },

        /**
         * @private
         * Generate a unique reference of Ext in the global scope, useful for sandboxing
         */
        getUniqueGlobalNamespace: function() {
            var uniqueGlobalNamespace = this.uniqueGlobalNamespace;

            if (uniqueGlobalNamespace === undefined) {
                var i = 0;

                do {
                    uniqueGlobalNamespace = 'ExtBox' + (++i);
                } while (Ext.global[uniqueGlobalNamespace] !== undefined);

                Ext.global[uniqueGlobalNamespace] = Ext;
                this.uniqueGlobalNamespace = uniqueGlobalNamespace;
            }

            return uniqueGlobalNamespace;
        },

        /**
         * @private
         */
        functionFactory: function() {
            var args = Array.prototype.slice.call(arguments),
                ln = args.length;

            if (ln > 0) {
                args[ln - 1] = 'var Ext=window.' + this.getUniqueGlobalNamespace() + ';' + args[ln - 1];
            }

            return Function.prototype.constructor.apply(Function.prototype, args);
        },

        /**
         * @private
         */
        globalEval: ('execScript' in global) ? function(code) {
            global.execScript(code)
        } : function(code) {
            (function(){
                eval(code);
            })();
        },

        /**
         * @private
         * @property
         */
        Logger: {
            log: function(message, priority) {
                if ('console' in global) {
                    if (!priority || !(priority in global.console)) {
                        priority = 'log';
                    }
                    message = '[' + priority.toUpperCase() + '] ' + message;
                    global.console[priority](message);
                }
            },
            verbose: function(message) {
                this.log(message, 'verbose');
            },
            info: function(message) {
                this.log(message, 'info');
            },
            warn: function(message) {
                this.log(message, 'warn');
            },
            error: function(message) {
                throw new Error(message);
            },
            deprecate: function(message) {
                this.log(message, 'warn');
            }
        }
    });

    /**
     * Old alias to {@link Ext#typeOf}
     * @deprecated 4.0.0 Please use {@link Ext#typeOf} instead
     * @method
     * @alias Ext#typeOf
     */
    Ext.type = Ext.typeOf;

})();

/**
 * @author Jacky Nguyen <jacky@sencha.com>
 * @docauthor Jacky Nguyen <jacky@sencha.com>
 * @class Ext.Version
 *
 * A utility class that wrap around a string version number and provide convenient
 * method to perform comparison. See also: {@link Ext.Version#compare compare}. Example:

    var version = new Ext.Version('1.0.2beta');
    console.log("Version is " + version); // Version is 1.0.2beta

    console.log(version.getMajor()); // 1
    console.log(version.getMinor()); // 0
    console.log(version.getPatch()); // 2
    console.log(version.getBuild()); // 0
    console.log(version.getRelease()); // beta

    console.log(version.isGreaterThan('1.0.1')); // True
    console.log(version.isGreaterThan('1.0.2alpha')); // True
    console.log(version.isGreaterThan('1.0.2RC')); // False
    console.log(version.isGreaterThan('1.0.2')); // False
    console.log(version.isLessThan('1.0.2')); // True

    console.log(version.match(1.0)); // True
    console.log(version.match('1.0.2')); // True

 * @markdown
 */
(function() {

// Current core version
var version = '4.1.0', Version;
    Ext.Version = Version = Ext.extend(Object, {

        /**
         * @param {String/Number} version The version number in the follow standard format: major[.minor[.patch[.build[release]]]]
         * Examples: 1.0 or 1.2.3beta or 1.2.3.4RC
         * @return {Ext.Version} this
         */
        constructor: function(version) {
            var toNumber = this.toNumber,
                parts, releaseStartIndex;

            if (version instanceof Version) {
                return version;
            }

            this.version = this.shortVersion = String(version).toLowerCase().replace(/_/g, '.').replace(/[\-+]/g, '');

            releaseStartIndex = this.version.search(/([^\d\.])/);

            if (releaseStartIndex !== -1) {
                this.release = this.version.substr(releaseStartIndex, version.length);
                this.shortVersion = this.version.substr(0, releaseStartIndex);
            }

            this.shortVersion = this.shortVersion.replace(/[^\d]/g, '');

            parts = this.version.split('.');

            this.major = toNumber(parts.shift());
            this.minor = toNumber(parts.shift());
            this.patch = toNumber(parts.shift());
            this.build = toNumber(parts.shift());

            return this;
        },

        toNumber: function(value) {
            value = parseInt(value || 0, 10);

            if (isNaN(value)) {
                value = 0;
            }

            return value;
        },

        /**
         * Override the native toString method
         * @private
         * @return {String} version
         */
        toString: function() {
            return this.version;
        },

        /**
         * Override the native valueOf method
         * @private
         * @return {String} version
         */
        valueOf: function() {
            return this.version;
        },

        /**
         * Returns the major component value
         * @return {Number} major
         */
        getMajor: function() {
            return this.major || 0;
        },

        /**
         * Returns the minor component value
         * @return {Number} minor
         */
        getMinor: function() {
            return this.minor || 0;
        },

        /**
         * Returns the patch component value
         * @return {Number} patch
         */
        getPatch: function() {
            return this.patch || 0;
        },

        /**
         * Returns the build component value
         * @return {Number} build
         */
        getBuild: function() {
            return this.build || 0;
        },

        /**
         * Returns the release component value
         * @return {Number} release
         */
        getRelease: function() {
            return this.release || '';
        },

        /**
         * Returns whether this version if greater than the supplied argument
         * @param {String/Number} target The version to compare with
         * @return {Boolean} True if this version if greater than the target, false otherwise
         */
        isGreaterThan: function(target) {
            return Version.compare(this.version, target) === 1;
        },

        /**
         * Returns whether this version if greater than or equal to the supplied argument
         * @param {String/Number} target The version to compare with
         * @return {Boolean} True if this version if greater than or equal to the target, false otherwise
         */
        isGreaterThanOrEqual: function(target) {
            return Version.compare(this.version, target) >= 0;
        },

        /**
         * Returns whether this version if smaller than the supplied argument
         * @param {String/Number} target The version to compare with
         * @return {Boolean} True if this version if smaller than the target, false otherwise
         */
        isLessThan: function(target) {
            return Version.compare(this.version, target) === -1;
        },

        /**
         * Returns whether this version if less than or equal to the supplied argument
         * @param {String/Number} target The version to compare with
         * @return {Boolean} True if this version if less than or equal to the target, false otherwise
         */
        isLessThanOrEqual: function(target) {
            return Version.compare(this.version, target) <= 0;
        },

        /**
         * Returns whether this version equals to the supplied argument
         * @param {String/Number} target The version to compare with
         * @return {Boolean} True if this version equals to the target, false otherwise
         */
        equals: function(target) {
            return Version.compare(this.version, target) === 0;
        },

        /**
         * Returns whether this version matches the supplied argument. Example:
         * <pre><code>
         * var version = new Ext.Version('1.0.2beta');
         * console.log(version.match(1)); // True
         * console.log(version.match(1.0)); // True
         * console.log(version.match('1.0.2')); // True
         * console.log(version.match('1.0.2RC')); // False
         * </code></pre>
         * @param {String/Number} target The version to compare with
         * @return {Boolean} True if this version matches the target, false otherwise
         */
        match: function(target) {
            target = String(target);
            return this.version.substr(0, target.length) === target;
        },

        /**
         * Returns this format: [major, minor, patch, build, release]. Useful for comparison
         * @return {Number[]}
         */
        toArray: function() {
            return [this.getMajor(), this.getMinor(), this.getPatch(), this.getBuild(), this.getRelease()];
        },

        /**
         * Returns shortVersion version without dots and release
         * @return {String}
         */
        getShortVersion: function() {
            return this.shortVersion;
        },

        /**
         * Convenient alias to {@link Ext.Version#isGreaterThan isGreaterThan}
         * @param {String/Number} target
         * @return {Boolean}
         */
        gt: function() {
            return this.isGreaterThan.apply(this, arguments);
        },

        /**
         * Convenient alias to {@link Ext.Version#isLessThan isLessThan}
         * @param {String/Number} target
         * @return {Boolean}
         */
        lt: function() {
            return this.isLessThan.apply(this, arguments);
        },

        /**
         * Convenient alias to {@link Ext.Version#isGreaterThanOrEqual isGreaterThanOrEqual}
         * @param {String/Number} target
         * @return {Boolean}
         */
        gtEq: function() {
            return this.isGreaterThanOrEqual.apply(this, arguments);
        },

        /**
         * Convenient alias to {@link Ext.Version#isLessThanOrEqual isLessThanOrEqual}
         * @param {String/Number} target
         * @return {Boolean}
         */
        ltEq: function() {
            return this.isLessThanOrEqual.apply(this, arguments);
        }
    });

    Ext.apply(Version, {
        // @private
        releaseValueMap: {
            'dev': -6,
            'alpha': -5,
            'a': -5,
            'beta': -4,
            'b': -4,
            'rc': -3,
            '#': -2,
            'p': -1,
            'pl': -1
        },

        /**
         * Converts a version component to a comparable value
         *
         * @static
         * @param {Object} value The value to convert
         * @return {Object}
         */
        getComponentValue: function(value) {
            return !value ? 0 : (isNaN(value) ? this.releaseValueMap[value] || value : parseInt(value, 10));
        },

        /**
         * Compare 2 specified versions, starting from left to right. If a part contains special version strings,
         * they are handled in the following order:
         * 'dev' < 'alpha' = 'a' < 'beta' = 'b' < 'RC' = 'rc' < '#' < 'pl' = 'p' < 'anything else'
         *
         * @static
         * @param {String} current The current version to compare to
         * @param {String} target The target version to compare to
         * @return {Number} Returns -1 if the current version is smaller than the target version, 1 if greater, and 0 if they're equivalent
         */
        compare: function(current, target) {
            var currentValue, targetValue, i;

            current = new Version(current).toArray();
            target = new Version(target).toArray();

            for (i = 0; i < Math.max(current.length, target.length); i++) {
                currentValue = this.getComponentValue(current[i]);
                targetValue = this.getComponentValue(target[i]);

                if (currentValue < targetValue) {
                    return -1;
                } else if (currentValue > targetValue) {
                    return 1;
                }
            }

            return 0;
        }
    });

    Ext.apply(Ext, {
        /**
         * @private
         */
        versions: {},

        /**
         * @private
         */
        lastRegisteredVersion: null,

        /**
         * Set version number for the given package name.
         *
         * @param {String} packageName The package name, for example: 'core', 'touch', 'extjs'
         * @param {String/Ext.Version} version The version, for example: '1.2.3alpha', '2.4.0-dev'
         * @return {Ext}
         */
        setVersion: function(packageName, version) {
            Ext.versions[packageName] = new Version(version);
            Ext.lastRegisteredVersion = Ext.versions[packageName];

            return this;
        },

        /**
         * Get the version number of the supplied package name; will return the last registered version
         * (last Ext.setVersion call) if there's no package name given.
         *
         * @param {String} packageName (Optional) The package name, for example: 'core', 'touch', 'extjs'
         * @return {Ext.Version} The version
         */
        getVersion: function(packageName) {
            if (packageName === undefined) {
                return Ext.lastRegisteredVersion;
            }

            return Ext.versions[packageName];
        },

        /**
         * Create a closure for deprecated code.
         *
    // This means Ext.oldMethod is only supported in 4.0.0beta and older.
    // If Ext.getVersion('extjs') returns a version that is later than '4.0.0beta', for example '4.0.0RC',
    // the closure will not be invoked
    Ext.deprecate('extjs', '4.0.0beta', function() {
        Ext.oldMethod = Ext.newMethod;

        ...
    });

         * @param {String} packageName The package name
         * @param {String} since The last version before it's deprecated
         * @param {Function} closure The callback function to be executed with the specified version is less than the current version
         * @param {Object} scope The execution scope (<tt>this</tt>) if the closure
         * @markdown
         */
        deprecate: function(packageName, since, closure, scope) {
            if (Version.compare(Ext.getVersion(packageName), since) < 1) {
                closure.call(scope);
            }
        }
    }); // End Versioning

    Ext.setVersion('core', version);

})();

/**
 * @class Ext.String
 *
 * A collection of useful static methods to deal with strings
 * @singleton
 */

Ext.String = {
    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,

    /**
     * Convert certain characters (&, <, >, and ") to their HTML character equivalents for literal display in web pages.
     * @param {String} value The string to encode
     * @return {String} The encoded text
     * @method
     */
    htmlEncode: (function() {
        var entities = {
            '&': '&amp;',
            '>': '&gt;',
            '<': '&lt;',
            '"': '&quot;'
        }, keys = [], p, regex;

        for (p in entities) {
            keys.push(p);
        }

        regex = new RegExp('(' + keys.join('|') + ')', 'g');

        return function(value) {
            return (!value) ? value : String(value).replace(regex, function(match, capture) {
                return entities[capture];
            });
        };
    })(),

    /**
     * Convert certain characters (&, <, >, and ") from their HTML character equivalents.
     * @param {String} value The string to decode
     * @return {String} The decoded text
     * @method
     */
    htmlDecode: (function() {
        var entities = {
            '&amp;': '&',
            '&gt;': '>',
            '&lt;': '<',
            '&quot;': '"'
        }, keys = [], p, regex;

        for (p in entities) {
            keys.push(p);
        }

        regex = new RegExp('(' + keys.join('|') + '|&#[0-9]{1,5};' + ')', 'g');

        return function(value) {
            return (!value) ? value : String(value).replace(regex, function(match, capture) {
                if (capture in entities) {
                    return entities[capture];
                } else {
                    return String.fromCharCode(parseInt(capture.substr(2), 10));
                }
            });
        };
    })(),

    /**
     * Appends content to the query string of a URL, handling logic for whether to place
     * a question mark or ampersand.
     * @param {String} url The URL to append to.
     * @param {String} string The content to append to the URL.
     * @return (String) The resulting URL
     */
    urlAppend : function(url, string) {
        if (!Ext.isEmpty(string)) {
            return url + (url.indexOf('?') === -1 ? '?' : '&') + string;
        }

        return url;
    },

    /**
     * Trims whitespace from either end of a string, leaving spaces within the string intact.  Example:
     * @example
var s = '  foo bar  ';
alert('-' + s + '-');         //alerts "- foo bar -"
alert('-' + Ext.String.trim(s) + '-');  //alerts "-foo bar-"

     * @param {String} string The string to escape
     * @return {String} The trimmed string
     */
    trim: function(string) {
        return string.replace(Ext.String.trimRegex, "");
    },

    /**
     * Capitalize the given string
     * @param {String} string
     * @return {String}
     */
    capitalize: function(string) {
        return string.charAt(0).toUpperCase() + string.substr(1);
    },

    /**
     * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
     * @param {String} value The string to truncate
     * @param {Number} length The maximum length to allow before truncating
     * @param {Boolean} word True to try to find a common word break
     * @return {String} The converted text
     */
    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;
    },

    /**
     * Escapes the passed string for use in a regular expression
     * @param {String} string
     * @return {String}
     */
    escapeRegex: function(string) {
        return string.replace(Ext.String.escapeRegexRe, "\\$1");
    },

    /**
     * Escapes the passed string for ' and \
     * @param {String} string The string to escape
     * @return {String} The escaped string
     */
    escape: function(string) {
        return string.replace(Ext.String.escapeRe, "\\$1");
    },

    /**
     * Utility function that allows you to easily switch a string between two alternating values.  The passed value
     * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
     * they are already different, the first value passed in is returned.  Note that this method returns the new value
     * but does not change the current string.
     * <pre><code>
    // alternate sort directions
    sort = Ext.String.toggle(sort, 'ASC', 'DESC');

    // instead of conditional logic:
    sort = (sort == 'ASC' ? 'DESC' : 'ASC');
       </code></pre>
     * @param {String} string The current string
     * @param {String} value The value to compare to the current string
     * @param {String} other The new value to use if the string already equals the first value passed in
     * @return {String} The new value
     */
    toggle: function(string, value, other) {
        return string === value ? other : value;
    },

    /**
     * Pads the left side of a string with a specified character.  This is especially useful
     * for normalizing number and date strings.  Example usage:
     *
     * <pre><code>
var s = Ext.String.leftPad('123', 5, '0');
// s now contains the string: '00123'
       </code></pre>
     * @param {String} string The original string
     * @param {Number} size The total length of the output string
     * @param {String} character (optional) The character with which to pad the original string (defaults to empty string " ")
     * @return {String} The padded string
     */
    leftPad: function(string, size, character) {
        var result = String(string);
        character = character || " ";
        while (result.length < size) {
            result = character + result;
        }
        return result;
    },

    /**
     * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
     * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
     * <pre><code>
var cls = 'my-class', text = 'Some text';
var s = Ext.String.format('&lt;div class="{0}">{1}&lt;/div>', cls, text);
// s now contains the string: '&lt;div class="my-class">Some text&lt;/div>'
       </code></pre>
     * @param {String} string The tokenized string to be formatted
     * @param {String} value1 The value to replace token {0}
     * @param {String} value2 Etc...
     * @return {String} The formatted string
     */
    format: function(format) {
        var args = Ext.Array.toArray(arguments, 1);
        return format.replace(Ext.String.formatRe, function(m, i) {
            return args[i];
        });
    },

    /**
     * Returns a string with a specified number of repititions a given string pattern.
     * The pattern be separated by a different string.
     *
     *      var s = Ext.String.repeat('---', 4); // = '------------'
     *      var t = Ext.String.repeat('--', 3, '/'); // = '--/--/--'
     *
     * @param {String} pattern The pattern to repeat.
     * @param {Number} count The number of times to repeat the pattern (may be 0).
     * @param {String} sep An option string to separate each pattern.
     */
    repeat: function(pattern, count, sep) {
        for (var buf = [], i = count; i--; ) {
            buf.push(pattern);
        }
        return buf.join(sep || '');
    }
};

/**
 * Old alias to {@link Ext.String#htmlEncode}
 * @deprecated Use {@link Ext.String#htmlEncode} instead
 * @method
 * @member Ext
 * @alias Ext.String#htmlEncode
 */
Ext.htmlEncode = Ext.String.htmlEncode;


/**
 * Old alias to {@link Ext.String#htmlDecode}
 * @deprecated Use {@link Ext.String#htmlDecode} instead
 * @method
 * @member Ext
 * @alias Ext.String#htmlDecode
 */
Ext.htmlDecode = Ext.String.htmlDecode;

/**
 * Old alias to {@link Ext.String#urlAppend}
 * @deprecated Use {@link Ext.String#urlAppend} instead
 * @method
 * @member Ext
 * @alias Ext.String#urlAppend
 */
Ext.urlAppend = Ext.String.urlAppend;

/**
 * @class Ext.Array
 * @singleton
 * @author Jacky Nguyen <jacky@sencha.com>
 * @docauthor Jacky Nguyen <jacky@sencha.com>
 *
 * A set of useful static methods to deal with arrays; provide missing methods for older browsers.
 */
(function() {

    var arrayPrototype = Array.prototype,
        slice = arrayPrototype.slice,
        supportsSplice = function () {
            var array = [],
                lengthBefore,
                j = 20;

            if (!array.splice) {
                return false;
            }

            // This detects a bug in IE8 splice method:
            // see http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/6e946d03-e09f-4b22-a4dd-cd5e276bf05a/

            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; //41
            array.splice(13, 0, "XXX"); // add one element

            if (lengthBefore+1 != array.length) {
                return false;
            }
            // end IE8 bug

            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;

    try {
        // IE 6 - 8 will throw an error when using Array.prototype.slice on NodeList
        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);
    }

    /*
    Does the same work as splice, but with a slightly more convenient signature. The splice
    method has bugs in IE8, so this is the implementation we use on that platform.

    The rippling of items in the array can be tricky. Consider two use cases:

                  index=2
                  removeCount=2
                 /=====\
        +---+---+---+---+---+---+---+---+
        | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
        +---+---+---+---+---+---+---+---+
                         /  \/  \/  \/  \
                        /   /\  /\  /\   \
                       /   /  \/  \/  \   +--------------------------+
                      /   /   /\  /\   +--------------------------+   \
                     /   /   /  \/  +--------------------------+   \   \
                    /   /   /   /+--------------------------+   \   \   \
                   /   /   /   /                             \   \   \   \
                  v   v   v   v                               v   v   v   v
        +---+---+---+---+---+---+       +---+---+---+---+---+---+---+---+---+
        | 0 | 1 | 4 | 5 | 6 | 7 |       | 0 | 1 | a | b | c | 4 | 5 | 6 | 7 |
        +---+---+---+---+---+---+       +---+---+---+---+---+---+---+---+---+
        A                               B        \=========/
                                                 insert=[a,b,c]

    In case A, it is obvious that copying of [4,5,6,7] must be left-to-right so
    that we don't end up with [0,1,6,7,6,7]. In case B, we have the opposite; we
    must go right-to-left or else we would end up with [0,1,a,b,c,4,4,4,4].
    */
    function replaceSim (array, index, removeCount, insert) {
        var add = insert ? insert.length : 0,
            length = array.length,
            pos = fixArrayIndex(array, index);

        // we try to use Array.push when we can for efficiency...
        if (pos === length) {
            if (add) {
                array.push.apply(array, insert);
            }
        } else {
            var remove = Math.min(removeCount, length - pos),
                tailOldPos = pos + remove,
                tailNewPos = tailOldPos + add - remove,
                tailCount = length - tailOldPos,
                lengthAfterRemove = length - remove,
                i;

            if (tailNewPos < tailOldPos) { // case A
                for (i = 0; i < tailCount; ++i) {
                    array[tailNewPos+i] = array[tailOldPos+i];
                }
            } else if (tailNewPos > tailOldPos) { // case B
                for (i = tailCount; i--; ) {
                    array[tailNewPos+i] = array[tailOldPos+i];
                }
            } // else, add == remove (nothing to do)

            if (add && pos === lengthAfterRemove) {
                array.length = lengthAfterRemove; // truncate array
                array.push.apply(array, insert);
            } else {
                array.length = lengthAfterRemove + add; // reserves space
                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 < 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));
    }

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

    // NOTE: from here on, use erase, replace or splice (not native methods)...
    ExtArray = Ext.Array = {
        /**
         * Iterates an array or an iterable value and invoke the given callback function for each item.
         *
         *     var countries = ['Vietnam', 'Singapore', 'United States', 'Russia'];
         *
         *     Ext.Array.each(countries, function(name, index, countriesItSelf) {
         *         console.log(name);
         *     });
         *
         *     var sum = function() {
         *         var sum = 0;
         *
         *         Ext.Array.each(arguments, function(value) {
         *             sum += value;
         *         });
         *
         *         return sum;
         *     };
         *
         *     sum(1, 2, 3); // returns 6
         *
         * The iteration can be stopped by returning false in the function callback.
         *
         *     Ext.Array.each(countries, function(name, index, countriesItSelf) {
         *         if (name === 'Singapore') {
         *             return false; // break here
         *         }
         *     });
         *
         * {@link Ext#each Ext.each} is alias for {@link Ext.Array#each Ext.Array.each}
         *
         * @param {Array/NodeList/Object} iterable The value to be iterated. If this
         * argument is not iterable, the callback function is called once.
         * @param {Function} fn The callback function. If it returns false, the iteration stops and this method returns
         * the current `index`.
         * @param {Object} fn.item The item at the current `index` in the passed `array`
         * @param {Number} fn.index The current `index` within the `array`
         * @param {Array} fn.allItems The `array` itself which was passed as the first argument
         * @param {Boolean} fn.return Return false to stop iteration.
         * @param {Object} scope (Optional) The scope (`this` reference) in which the specified function is executed.
         * @param {Boolean} reverse (Optional) Reverse the iteration order (loop from the end to the beginning)
         * Defaults false
         * @return {Boolean} See description for the `fn` parameter.
         */
        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;
        },

        /**
         * Iterates an array and invoke the given callback function for each item. Note that this will simply
         * delegate to the native Array.prototype.forEach method if supported. It doesn't support stopping the
         * iteration by returning false in the callback function like {@link Ext.Array#each}. However, performance
         * could be much better in modern browsers comparing with {@link Ext.Array#each}
         *
         * @param {Array} array The array to iterate
         * @param {Function} fn The callback function.
         * @param {Object} fn.item The item at the current `index` in the passed `array`
         * @param {Number} fn.index The current `index` within the `array`
         * @param {Array}  fn.allItems The `array` itself which was passed as the first argument
         * @param {Object} scope (Optional) The execution scope (`this`) in which the specified function is executed.
         */
        forEach: supportsForEach ? function(array, fn, scope) {
                return 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);
            }
        },

        /**
         * Get the index of the provided `item` in the given `array`, a supplement for the
         * missing arrayPrototype.indexOf in Internet Explorer.
         *
         * @param {Array} array The array to check
         * @param {Object} item The item to look for
         * @param {Number} from (Optional) The index at which to begin the search
         * @return {Number} The index of item in the array (or -1 if it is not found)
         */
        indexOf: (supportsIndexOf) ? function(array, item, from) {
            return array.indexOf(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;
        },

        /**
         * Checks whether or not the given `array` contains the specified `item`
         *
         * @param {Array} array The array to check
         * @param {Object} item The item to look for
         * @return {Boolean} True if the array contains the item, false otherwise
         */
        contains: supportsIndexOf ? function(array, item) {
            return array.indexOf(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;
        },

        /**
         * Converts any iterable (numeric indices and a length property) into a true array.
         *
         *     function test() {
         *         var args = Ext.Array.toArray(arguments),
         *             fromSecondToLastArgs = Ext.Array.toArray(arguments, 1);
         *
         *         alert(args.join(' '));
         *         alert(fromSecondToLastArgs.join(' '));
         *     }
         *
         *     test('just', 'testing', 'here'); // alerts 'just testing here';
         *                                      // alerts 'testing here';
         *
         *     Ext.Array.toArray(document.getElementsByTagName('div')); // will convert the NodeList into an array
         *     Ext.Array.toArray('splitted'); // returns ['s', 'p', 'l', 'i', 't', 't', 'e', 'd']
         *     Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l', 'i']
         *
         * {@link Ext#toArray Ext.toArray} is alias for {@link Ext.Array#toArray Ext.Array.toArray}
         *
         * @param {Object} iterable the iterable object to be turned into a true Array.
         * @param {Number} start (Optional) a zero-based index that specifies the start of extraction. Defaults to 0
         * @param {Number} end (Optional) a zero-based index that specifies the end of extraction. Defaults to the last
         * index of the iterable value
         * @return {Array} array
         */
        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;
        },

        /**
         * Plucks the value of a property from each item in the Array. Example:
         *
         *     Ext.Array.pluck(Ext.query("p"), "className"); // [el1.className, el2.className, ..., elN.className]
         *
         * @param {Array/NodeList} array The Array of items to pluck the value from.
         * @param {String} propertyName The property name to pluck from each element.
         * @return {Array} The value from each item in the 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;
        },

        /**
         * Creates a new array with the results of calling a provided function on every element in this array.
         *
         * @param {Array} array
         * @param {Function} fn Callback function for each item
         * @param {Object} scope Callback function scope
         * @return {Array} results
         */
        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;
        },

        /**
         * Executes the specified function for each array element until the function returns a falsy value.
         * If such an item is found, the function will return false immediately.
         * Otherwise, it will return true.
         *
         * @param {Array} array
         * @param {Function} fn Callback function for each item
         * @param {Object} scope Callback function scope
         * @return {Boolean} True if no false value is returned by the callback function.
         */
        every: function(array, fn, scope) {
            if (!fn) {
                Ext.Error.raise('Ext.Array.every must have a callback function passed as second argument.');
            }
            if (supportsEvery) {
                return array.every(fn, scope);
            }

            var i = 0,
                ln = array.length;

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

            return true;
        },

        /**
         * Executes the specified function for each array element until the function returns a truthy value.
         * If such an item is found, the function will return true immediately. Otherwise, it will return false.
         *
         * @param {Array} array
         * @param {Function} fn Callback function for each item
         * @param {Object} scope Callback function scope
         * @return {Boolean} True if the callback function returns a truthy value.
         */
        some: function(array, fn, scope) {
            if (!fn) {
                Ext.Error.raise('Ext.Array.some must have a callback function passed as second argument.');
            }
            if (supportsSome) {
                return array.some(fn, scope);
            }

            var i = 0,
                ln = array.length;

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

            return false;
        },

        /**
         * Filter through an array and remove empty item as defined in {@link Ext#isEmpty Ext.isEmpty}
         *
         * See {@link Ext.Array#filter}
         *
         * @param {Array} array
         * @return {Array} results
         */
        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;
        },

        /**
         * Returns a new array with unique items
         *
         * @param {Array} array
         * @return {Array} 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;
        },

        /**
         * Creates a new array with all of the elements of this array for which
         * the provided filtering function returns true.
         *
         * @param {Array} array
         * @param {Function} fn Callback function for each item
         * @param {Object} scope Callback function scope
         * @return {Array} results
         */
        filter: function(array, fn, scope) {
            if (supportsFilter) {
                return array.filter(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;
        },

        /**
         * Converts a value to an array if it's not already an array; returns:
         *
         * - An empty array if given value is `undefined` or `null`
         * - Itself if given value is already an array
         * - An array copy if given value is {@link Ext#isIterable iterable} (arguments, NodeList and alike)
         * - An array with one item which is the given value, otherwise
         *
         * @param {Object} value The value to convert to an array if it's not already is an array
         * @param {Boolean} newReference (Optional) True to clone the given array and return a new reference if necessary,
         * defaults to false
         * @return {Array} array
         */
        from: function(value, newReference) {
            if (value === undefined || value === null) {
                return [];
            }

            if (Ext.isArray(value)) {
                return (newReference) ? slice.call(value) : value;
            }

            if (value && value.length !== undefined && typeof value !== 'string') {
                return ExtArray.toArray(value);
            }

            return [value];
        },

        /**
         * Removes the specified item from the array if it exists
         *
         * @param {Array} array The array
         * @param {Object} item The item to remove
         * @return {Array} The passed array itself
         */
        remove: function(array, item) {
            var index = ExtArray.indexOf(array, item);

            if (index !== -1) {
                erase(array, index, 1);
            }

            return array;
        },

        /**
         * Push an item into the array only if the array doesn't contain it yet
         *
         * @param {Array} array The array
         * @param {Object} item The item to include
         */
        include: function(array, item) {
            if (!ExtArray.contains(array, item)) {
                array.push(item);
            }
        },

        /**
         * Clone a flat array without referencing the previous one. Note that this is different
         * from Ext.clone since it doesn't handle recursive cloning. It's simply a convenient, easy-to-remember method
         * for Array.prototype.slice.call(array)
         *
         * @param {Array} array The array
         * @return {Array} The clone array
         */
        clone: function(array) {
            return slice.call(array);
        },

        /**
         * Merge multiple arrays into one with unique items.
         *
         * {@link Ext.Array#union} is alias for {@link Ext.Array#merge}
         *
         * @param {Array} array1
         * @param {Array} array2
         * @param {Array} etc
         * @return {Array} merged
         */
        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);
        },

        /**
         * Merge multiple arrays into one with unique items that exist in all of the arrays.
         *
         * @param {Array} array1
         * @param {Array} array2
         * @param {Array} etc
         * @return {Array} intersect
         */
        intersect: function() {
            var intersect = [],
                arrays = slice.call(arguments),
                i, j, k, minArray, array, x, y, ln, arraysLn, arrayLn;

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

            // Find the smallest array
            for (i = x = 0,ln = arrays.length; i < ln,array = arrays[i]; i++) {
                if (!minArray || array.length < minArray.length) {
                    minArray = array;
                    x = i;
                }
            }

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

            // Use the smallest unique'd array as the anchor loop. If the other array(s) do contain
            // an item in the small array, we're likely to find it before reaching the end
            // of the inner loop and can terminate the search early.
            for (i = 0,ln = minArray.length; i < ln,x = minArray[i]; i++) {
                var count = 0;

                for (j = 0,arraysLn = arrays.length; j < arraysLn,array = arrays[j]; j++) {
                    for (k = 0,arrayLn = array.length; k < arrayLn,y = array[k]; k++) {
                        if (x === y) {
                            count++;
                            break;
                        }
                    }
                }

                if (count === arraysLn) {
                    intersect.push(x);
                }
            }

            return intersect;
        },

        /**
         * Perform a set difference A-B by subtracting all items in array B from array A.
         *
         * @param {Array} arrayA
         * @param {Array} arrayB
         * @return {Array} difference
         */
        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;
        },

        /**
         * Returns a shallow copy of a part of an array. This is equivalent to the native
         * call "Array.prototype.slice.call(array, begin, end)". This is often used when "array"
         * is "arguments" since the arguments object does not supply a slice method but can
         * be the context object to Array.prototype.slice.
         *
         * @param {Array} array The array (or arguments object).
         * @param {Number} begin The index at which to begin. Negative values are offsets from
         * the end of the array.
         * @param {Number} end The index at which to end. The copied items do not include
         * end. Negative values are offsets from the end of the array. If end is omitted,
         * all items up to the end of the array are copied.
         * @return {Array} The copied piece of the array.
         */
        slice: function(array, begin, end) {
            return slice.call(array, begin, end);
        },

        /**
         * Sorts the elements of an Array.
         * By default, this method sorts the elements alphabetically and ascending.
         *
         * @param {Array} array The array to sort.
         * @param {Function} sortFn (optional) The comparison function.
         * @return {Array} The sorted array.
         */
        sort: function(array, sortFn) {
            if (supportsSort) {
                if (sortFn) {
                    return array.sort(sortFn);
                } else {
                    return array.sort();
                }
            }

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

        /**
         * Recursively flattens into 1-d Array. Injects Arrays inline.
         *
         * @param {Array} array The array to flatten
         * @return {Array} The 1-d 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);
        },

        /**
         * Returns the minimum value in the Array.
         *
         * @param {Array/NodeList} array The Array from which to select the minimum value.
         * @param {Function} comparisonFn (optional) a function to perform the comparision which determines minimization.
         * If omitted the "<" operator will be used. Note: gt = 1; eq = 0; lt = -1
         * @return {Object} minValue The minimum value
         */
        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;
        },

        /**
         * Returns the maximum value in the Array.
         *
         * @param {Array/NodeList} array The Array from which to select the maximum value.
         * @param {Function} comparisonFn (optional) a function to perform the comparision which determines maximization.
         * If omitted the ">" operator will be used. Note: gt = 1; eq = 0; lt = -1
         * @return {Object} maxValue The maximum value
         */
        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;
        },

        /**
         * Calculates the mean of all items in the array.
         *
         * @param {Array} array The Array to calculate the mean value of.
         * @return {Number} The mean.
         */
        mean: function(array) {
            return array.length > 0 ? ExtArray.sum(array) / array.length : undefined;
        },

        /**
         * Calculates the sum of all items in the given array.
         *
         * @param {Array} array The Array to calculate the sum value of.
         * @return {Number} The sum.
         */
        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;
        },

        _replaceSim: replaceSim, // for unit testing
        _spliceSim: spliceSim,

        /**
         * Removes items from an array. This is functionally equivalent to the splice method
         * of Array, but works around bugs in IE8's splice method and does not copy the
         * removed elements in order to return them (because very often they are ignored).
         *
         * @param {Array} array The Array on which to replace.
         * @param {Number} index The index in the array at which to operate.
         * @param {Number} removeCount The number of items to remove at index.
         * @return {Array} The array passed.
         * @method
         */
        erase: erase,

        /**
         * Inserts items in to an array.
         *
         * @param {Array} array The Array on which to replace.
         * @param {Number} index The index in the array at which to operate.
         * @param {Array} items The array of items to insert at index.
         * @return {Array} The array passed.
         */
        insert: function (array, index, items) {
            return replace(array, index, 0, items);
        },

        /**
         * Replaces items in an array. This is functionally equivalent to the splice method
         * of Array, but works around bugs in IE8's splice method and is often more convenient
         * to call because it accepts an array of items to insert rather than use a variadic
         * argument list.
         *
         * @param {Array} array The Array on which to replace.
         * @param {Number} index The index in the array at which to operate.
         * @param {Number} removeCount The number of items to remove at index (can be 0).
         * @param {Array} insert (optional) An array of items to insert at index.
         * @return {Array} The array passed.
         * @method
         */
        replace: replace,

        /**
         * Replaces items in an array. This is equivalent to the splice method of Array, but
         * works around bugs in IE8's splice method. The signature is exactly the same as the
         * splice method except that the array is the first argument. All arguments following
         * removeCount are inserted in the array at index.
         *
         * @param {Array} array The Array on which to replace.
         * @param {Number} index The index in the array at which to operate.
         * @param {Number} removeCount The number of items to remove at index (can be 0).
         * @return {Array} An array containing the removed items.
         * @method
         */
        splice: splice
    };

    /**
     * @method
     * @member Ext
     * @alias Ext.Array#each
     */
    Ext.each = ExtArray.each;

    /**
     * @method
     * @member Ext.Array
     * @alias Ext.Array#merge
     */
    ExtArray.union = ExtArray.merge;

    /**
     * Old alias to {@link Ext.Array#min}
     * @deprecated 4.0.0 Please use {@link Ext.Array#min} instead
     * @method
     * @member Ext
     * @alias Ext.Array#min
     */
    Ext.min = ExtArray.min;

    /**
     * Old alias to {@link Ext.Array#max}
     * @deprecated 4.0.0 Please use {@link Ext.Array#max} instead
     * @method
     * @member Ext
     * @alias Ext.Array#max
     */
    Ext.max = ExtArray.max;

    /**
     * Old alias to {@link Ext.Array#sum}
     * @deprecated 4.0.0 Please use {@link Ext.Array#sum} instead
     * @method
     * @member Ext
     * @alias Ext.Array#sum
     */
    Ext.sum = ExtArray.sum;

    /**
     * Old alias to {@link Ext.Array#mean}
     * @deprecated 4.0.0 Please use {@link Ext.Array#mean} instead
     * @method
     * @member Ext
     * @alias Ext.Array#mean
     */
    Ext.mean = ExtArray.mean;

    /**
     * Old alias to {@link Ext.Array#flatten}
     * @deprecated 4.0.0 Please use {@link Ext.Array#flatten} instead
     * @method
     * @member Ext
     * @alias Ext.Array#flatten
     */
    Ext.flatten = ExtArray.flatten;

    /**
     * Old alias to {@link Ext.Array#clean}
     * @deprecated 4.0.0 Please use {@link Ext.Array#clean} instead
     * @method
     * @member Ext
     * @alias Ext.Array#clean
     */
    Ext.clean = ExtArray.clean;

    /**
     * Old alias to {@link Ext.Array#unique}
     * @deprecated 4.0.0 Please use {@link Ext.Array#unique} instead
     * @method
     * @member Ext
     * @alias Ext.Array#unique
     */
    Ext.unique = ExtArray.unique;

    /**
     * Old alias to {@link Ext.Array#pluck Ext.Array.pluck}
     * @deprecated 4.0.0 Please use {@link Ext.Array#pluck Ext.Array.pluck} instead
     * @method
     * @member Ext
     * @alias Ext.Array#pluck
     */
    Ext.pluck = ExtArray.pluck;

    /**
     * @method
     * @member Ext
     * @alias Ext.Array#toArray
     */
    Ext.toArray = function() {
        return ExtArray.toArray.apply(ExtArray, arguments);
    };
})();

/**
 * @class Ext.Number
 *
 * A collection of useful static methods to deal with numbers
 * @singleton
 */

(function() {

var isToFixedBroken = (0.9).toFixed() !== '1';

Ext.Number = {
    /**
     * Checks whether or not the passed number is within a desired range.  If the number is already within the
     * range it is returned, otherwise the min or max value is returned depending on which side of the range is
     * exceeded. Note that this method returns the constrained value but does not change the current number.
     * @param {Number} number The number to check
     * @param {Number} min The minimum number in the range
     * @param {Number} max The maximum number in the range
     * @return {Number} The constrained value if outside the range, otherwise the current value
     */
    constrain: function(number, min, max) {
        number = parseFloat(number);

        if (!isNaN(min)) {
            number = Math.max(number, min);
        }
        if (!isNaN(max)) {
            number = Math.min(number, max);
        }
        return number;
    },

    /**
     * Snaps the passed number between stopping points based upon a passed increment value.
     * @param {Number} value The unsnapped value.
     * @param {Number} increment The increment by which the value must move.
     * @param {Number} minValue The minimum value to which the returned value must be constrained. Overrides the increment..
     * @param {Number} maxValue The maximum value to which the returned value must be constrained. Overrides the increment..
     * @return {Number} The value of the nearest snap target.
     */
    snap : function(value, increment, minValue, maxValue) {
        var newValue = value,
            m;

        if (!(increment && value)) {
            return value;
        }
        m = value % increment;
        if (m !== 0) {
            newValue -= m;
            if (m * 2 >= increment) {
                newValue += increment;
            } else if (m * 2 < -increment) {
                newValue -= increment;
            }
        }
        return Ext.Number.constrain(newValue, minValue,  maxValue);
    },

    /**
     * Formats a number using fixed-point notation
     * @param {Number} value The number to format
     * @param {Number} precision The number of digits to show after the decimal point
     */
    toFixed: function(value, precision) {
        if (isToFixedBroken) {
            precision = precision || 0;
            var pow = Math.pow(10, precision);
            return (Math.round(value * pow) / pow).toFixed(precision);
        }

        return value.toFixed(precision);
    },

    /**
     * Validate that a value is numeric and convert it to a number if necessary. Returns the specified default value if
     * it is not.

Ext.Number.from('1.23', 1); // returns 1.23
Ext.Number.from('abc', 1); // returns 1

     * @param {Object} value
     * @param {Number} defaultValue The value to return if the original value is non-numeric
     * @return {Number} value, if numeric, defaultValue otherwise
     */
    from: function(value, defaultValue) {
        if (isFinite(value)) {
            value = parseFloat(value);
        }

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

})();

/**
 * This method is deprecated, please use {@link Ext.Number#from Ext.Number.from} instead
 *
 * @deprecated 4.0.0 Replaced by Ext.Number.from
 * @member Ext
 * @method num
 */
Ext.num = function() {
    return Ext.Number.from.apply(this, arguments);
};
/**
 * @author Jacky Nguyen <jacky@sencha.com>
 * @docauthor Jacky Nguyen <jacky@sencha.com>
 * @class Ext.Object
 *
 * A collection of useful static methods to deal with objects
 *
 * @singleton
 */

(function() {

// The "constructor" for chain:
var TemplateClass = function(){};

var ExtObject = Ext.Object = {

    /**
     * Returns a new object with the given object as the prototype chain.
     * @param {Object} object The prototype chain for the new object.
     */
    chain: function (object) {
        TemplateClass.prototype = object;
        var result = new TemplateClass();
        TemplateClass.prototype = null;
        return result;
    },

    /**
     * Convert a `name` - `value` pair to an array of objects with support for nested structures; useful to construct
     * query strings. For example:

    var objects = Ext.Object.toQueryObjects('hobbies', ['reading', 'cooking', 'swimming']);

    // objects then equals:
    [
        { name: 'hobbies', value: 'reading' },
        { name: 'hobbies', value: 'cooking' },
        { name: 'hobbies', value: 'swimming' },
    ];

    var objects = Ext.Object.toQueryObjects('dateOfBirth', {
        day: 3,
        month: 8,
        year: 1987,
        extra: {
            hour: 4
            minute: 30
        }
    }, true); // Recursive

    // objects then equals:
    [
        { name: 'dateOfBirth[day]', value: 3 },
        { name: 'dateOfBirth[month]', value: 8 },
        { name: 'dateOfBirth[year]', value: 1987 },
        { name: 'dateOfBirth[extra][hour]', value: 4 },
        { name: 'dateOfBirth[extra][minute]', value: 30 },
    ];

     * @param {String} name
     * @param {Object} value
     * @param {Boolean} recursive
     * @markdown
     */
    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;
    },

    /**
     * Takes an object and converts it to an encoded query string

- Non-recursive:

    Ext.Object.toQueryString({foo: 1, bar: 2}); // returns "foo=1&bar=2"
    Ext.Object.toQueryString({foo: null, bar: 2}); // returns "foo=&bar=2"
    Ext.Object.toQueryString({'some price': '$300'}); // returns "some%20price=%24300"
    Ext.Object.toQueryString({date: new Date(2011, 0, 1)}); // returns "date=%222011-01-01T00%3A00%3A00%22"
    Ext.Object.toQueryString({colors: ['red', 'green', 'blue']}); // returns "colors=red&colors=green&colors=blue"

- Recursive:

    Ext.Object.toQueryString({
        username: 'Jacky',
        dateOfBirth: {
            day: 1,
            month: 2,
            year: 1911
        },
        hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
    }, true); // returns the following string (broken down and url-decoded for ease of reading purpose):
              // username=Jacky
              //    &dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911
              //    &hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&hobbies[3][0]=nested&hobbies[3][1]=stuff

     *
     * @param {Object} object The object to encode
     * @param {Boolean} recursive (optional) Whether or not to interpret the object in recursive format.
     * (PHP / Ruby on Rails servers and similar). Defaults to false
     * @return {String} queryString
     * @markdown
     */
    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('&');
    },

    /**
     * Converts a query string back into an object.
     *
- Non-recursive:

    Ext.Object.fromQueryString(foo=1&bar=2); // returns {foo: 1, bar: 2}
    Ext.Object.fromQueryString(foo=&bar=2); // returns {foo: null, bar: 2}
    Ext.Object.fromQueryString(some%20price=%24300); // returns {'some price': '$300'}
    Ext.Object.fromQueryString(colors=red&colors=green&colors=blue); // returns {colors: ['red', 'green', 'blue']}

- Recursive:

    Ext.Object.fromQueryString("username=Jacky&dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911&hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&hobbies[3][0]=nested&hobbies[3][1]=stuff", true);

    // returns
    {
        username: 'Jacky',
        dateOfBirth: {
            day: '1',
            month: '2',
            year: '1911'
        },
        hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
    }

     * @param {String} queryString The query string to decode
     * @param {Boolean} recursive (Optional) Whether or not to recursively decode the string. This format is supported by
     * PHP / Ruby on Rails servers and similar. Defaults to false
     * @return {Object}
     */
    fromQueryString: function(queryString, recursive) {
        var parts = queryString.replace(/^\?/, '').split('&'),
            object = {},
            temp, components, name, value, i, ln,
            part, j, subLn, matchedKeys, matchedName,
            keys, key, nextKey;

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

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

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

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

                    if (!matchedName) {
                        throw new Error('[Ext.Object.fromQueryString] Malformed query string given, failed parsing name from "' + part + '"');
                    }

                    name = matchedName[0];
                    keys = [];

                    if (matchedKeys === null) {
                        object[name] = value;
                        continue;
                    }

                    for (j = 0, subLn = matchedKeys.length; j < subLn; j++) {
                        key = matchedKeys[j];
                        key = (key.length === 2) ? '' : key.substring(1, key.length - 1);
                        keys.push(key);
                    }

                    keys.unshift(name);

                    temp = object;

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

                        if (j === subLn - 1) {
                            if (Ext.isArray(temp) && key === '') {
                                temp.push(value);
                            }
                            else {
                                temp[key] = value;
                            }
                        }
                        else {
                            if (temp[key] === undefined || typeof temp[key] === 'string') {
                                nextKey = keys[j+1];

                                temp[key] = (Ext.isNumeric(nextKey) || nextKey === '') ? [] : {};
                            }

                            temp = temp[key];
                        }
                    }
                }
            }
        }

        return object;
    },

    /**
     * Iterate through an object and invoke the given callback function for each iteration. The iteration can be stop
     * by returning `false` in the callback function. For example:

    var person = {
        name: 'Jacky'
        hairColor: 'black'
        loves: ['food', 'sleeping', 'wife']
    };

    Ext.Object.each(person, function(key, value, myself) {
        console.log(key + ":" + value);

        if (key === 'hairColor') {
            return false; // stop the iteration
        }
    });

     * @param {Object} object The object to iterate
     * @param {Function} fn The callback function. Passed arguments for each iteration are:

- {String} `key`
- {Mixed} `value`
- {Object} `object` The object itself

     * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
     * @markdown
     */
    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;
                }
            }
        }
    },

    /**
     * Merges any number of objects recursively without referencing them or their children.

    var extjs = {
        companyName: 'Ext JS',
        products: ['Ext JS', 'Ext GWT', 'Ext Designer'],
        isSuperCool: true
        office: {
            size: 2000,
            location: 'Palo Alto',
            isFun: true
        }
    };

    var newStuff = {
        companyName: 'Sencha Inc.',
        products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
        office: {
            size: 40000,
            location: 'Redwood City'
        }
    };

    var sencha = Ext.Object.merge({}, extjs, newStuff);

    // sencha then equals to
    {
        companyName: 'Sencha Inc.',
        products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
        isSuperCool: true
        office: {
            size: 30000,
            location: 'Redwood City'
            isFun: true
        }
    }

     * @param {Object} source ...
     * @return {Object} merged The object that is created as a result of merging all the objects passed in.
     * @markdown
     */
    merge: function(source) {
        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 = source[key];
                    if (sourceKey && sourceKey.constructor === Object) {
                        mergeFn(sourceKey, value);
                    }
                    else {
                        source[key] = cloneFn(value);
                    }
                }
                else {
                    source[key] = value;
                }
            }
        }

        return source;
    },

    /**
     * @private
     * @param source
     */
    mergeIf: function(source) {
        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 source)) {
                    value = object[key];

                    if (value && value.constructor === Object) {
                        source[key] = cloneFn(value);
                    }
                    else {
                        source[key] = value;
                    }
                }
            }
        }

        return source;
    },

    /**
     * Returns the first matching key corresponding to the given value.
     * If no matching value is found, null is returned.

    var person = {
        name: 'Jacky',
        loves: 'food'
    };

    alert(Ext.Object.getKey(sencha, 'food')); // alerts 'loves'

     * @param {Object} object
     * @param {Object} value The value to find
     * @markdown
     */
    getKey: function(object, value) {
        for (var property in object) {
            if (object.hasOwnProperty(property) && object[property] === value) {
                return property;
            }
        }

        return null;
    },

    /**
     * Gets all values of the given object as an array.

    var values = Ext.Object.getValues({
        name: 'Jacky',
        loves: 'food'
    }); // ['Jacky', 'food']

     * @param {Object} object
     * @return {Array} An array of values from the object
     * @markdown
     */
    getValues: function(object) {
        var values = [],
            property;

        for (property in object) {
            if (object.hasOwnProperty(property)) {
                values.push(object[property]);
            }
        }

        return values;
    },

    /**
     * Gets all keys of the given object as an array.

    var values = Ext.Object.getKeys({
        name: 'Jacky',
        loves: 'food'
    }); // ['name', 'loves']

     * @param {Object} object
     * @return {String[]} An array of keys from the object
     * @method
     */
    getKeys: ('keys' in Object) ? Object.keys : function(object) {
        var keys = [],
            property;

        for (property in object) {
            if (object.hasOwnProperty(property)) {
                keys.push(property);
            }
        }

        return keys;
    },

    /**
     * Gets the total number of this object's own properties

    var size = Ext.Object.getSize({
        name: 'Jacky',
        loves: 'food'
    }); // size equals 2

     * @param {Object} object
     * @return {Number} size
     * @markdown
     */
    getSize: function(object) {
        var size = 0,
            property;

        for (property in object) {
            if (object.hasOwnProperty(property)) {
                size++;
            }
        }

        return size;
    },

    /**
     * @private
     */
    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;
    },

    defineProperty: ('defineProperty' in Object) ? Object.defineProperty : function(object, name, descriptor) {
        if (descriptor.get) {
            object.__defineGetter__(name, descriptor.get);
        }

        if (descriptor.set) {
            object.__defineSetter__(name, descriptor.set);
        }
    }
};

/**
 * A convenient alias method for {@link Ext.Object#merge}
 *
 * @member Ext
 * @method merge
 */
Ext.merge = Ext.Object.merge;

/**
 * @private
 */
Ext.mergeIf = Ext.Object.mergeIf;

/**
 * A convenient alias method for {@link Ext.Object#toQueryString}
 *
 * @member Ext
 * @method urlEncode
 * @deprecated 4.0.0 Please use {@link Ext.Object#toQueryString Ext.Object.toQueryString} instead
 */
Ext.urlEncode = function() {
    var args = Ext.Array.from(arguments),
        prefix = '';

    // Support for the old `pre` argument
    if ((typeof args[1] === 'string')) {
        prefix = args[1] + '&';
        args[1] = false;
    }

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

/**
 * A convenient alias method for {@link Ext.Object#fromQueryString}
 *
 * @member Ext
 * @method urlDecode
 * @deprecated 4.0.0 Please use {@link Ext.Object#fromQueryString Ext.Object.fromQueryString} instead
 */
Ext.urlDecode = function() {
    return ExtObject.fromQueryString.apply(ExtObject, arguments);
};

})();

/**
 * @class Ext.Function
 *
 * A collection of useful static methods to deal with function callbacks
 * @singleton
 * @alternateClassName Ext.util.Functions
 */
Ext.Function = {

    /**
     * A very commonly used method throughout the framework. It acts as a wrapper around another method
     * which originally accepts 2 arguments for `name` and `value`.
     * The wrapped function then allows "flexible" value setting of either:
     *
     * - `name` and `value` as 2 arguments
     * - one single object argument with multiple key - value pairs
     *
     * For example:
     *
     *     var setValue = Ext.Function.flexSetter(function(name, value) {
     *         this[name] = value;
     *     });
     *
     *     // Afterwards
     *     // Setting a single name - value
     *     setValue('name1', 'value1');
     *
     *     // Settings multiple name - value pairs
     *     setValue({
     *         name1: 'value1',
     *         name2: 'value2',
     *         name3: 'value3'
     *     });
     *
     * @param {Function} setter
     * @returns {Function} flexSetter
     */
    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;
        };
    },

    /**
     * Create a new function from the provided `fn`, change `this` to the provided scope, optionally
     * overrides arguments for the call. (Defaults to the arguments passed by the caller)
     *
     * {@link Ext#bind Ext.bind} is alias for {@link Ext.Function#bind Ext.Function.bind}
     *
     * @param {Function} fn The function to delegate.
     * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
     * **If omitted, defaults to the browser window.**
     * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
     * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
     * if a number the args are inserted at the specified position
     * @return {Function} The new function
     */
    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); // copy arguments first
                Ext.Array.insert(callArgs, appendArgs, args);
            }

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

    /**
     * Create a new function from the provided `fn`, the arguments of which are pre-set to `args`.
     * New arguments passed to the newly created callback when it's invoked are appended after the pre-set ones.
     * This is especially useful when creating callbacks.
     *
     * For example:
     *
     *     var originalFunction = function(){
     *         alert(Ext.Array.from(arguments).join(' '));
     *     };
     *
     *     var callback = Ext.Function.pass(originalFunction, ['Hello', 'World']);
     *
     *     callback(); // alerts 'Hello World'
     *     callback('by Me'); // alerts 'Hello World by Me'
     *
     * {@link Ext#pass Ext.pass} is alias for {@link Ext.Function#pass Ext.Function.pass}
     *
     * @param {Function} fn The original function
     * @param {Array} args The arguments to pass to new callback
     * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
     * @return {Function} The new callback function
     */
    pass: function(fn, args, scope) {
        if (!Ext.isArray(args)) {
            args = Ext.Array.clone(args);
        }

        return function() {
            args.push.apply(args, arguments);
            return fn.apply(scope || this, args);
        };
    },

    /**
     * Create an alias to the provided method property with name `methodName` of `object`.
     * Note that the execution scope will still be bound to the provided `object` itself.
     *
     * @param {Object/Function} object
     * @param {String} methodName
     * @return {Function} aliasFn
     */
    alias: function(object, methodName) {
        return function() {
            return object[methodName].apply(object, arguments);
        };
    },

    /**
     * Create a "clone" of the provided method. The returned method will call the given
     * method passing along all arguments and the "this" pointer and return its result.
     *
     * @param {Function} method
     * @return {Function} cloneFn
     */
    clone: function(method) {
        return function() {
            return method.apply(this, arguments);
        };
    },

    /**
     * Creates an interceptor function. The passed function is called before the original one. If it returns false,
     * the original one is not called. The resulting function returns the results of the original function.
     * The passed function is called with the parameters of the original function. Example usage:
     *
     *     var sayHi = function(name){
     *         alert('Hi, ' + name);
     *     }
     *
     *     sayHi('Fred'); // alerts "Hi, Fred"
     *
     *     // create a new function that validates input without
     *     // directly modifying the original function:
     *     var sayHiToFriend = Ext.Function.createInterceptor(sayHi, function(name){
     *         return name == 'Brian';
     *     });
     *
     *     sayHiToFriend('Fred');  // no alert
     *     sayHiToFriend('Brian'); // alerts "Hi, Brian"
     *
     * @param {Function} origFn The original function.
     * @param {Function} newFn The function to call before the original
     * @param {Object} scope (optional) The scope (`this` reference) in which the passed function is executed.
     * **If omitted, defaults to the scope in which the original function is called or the browser window.**
     * @param {Object} returnValue (optional) The value to return if the passed function return false (defaults to null).
     * @return {Function} The new function
     */
    createInterceptor: function(origFn, newFn, scope, returnValue) {
        var method = origFn;
        if (!Ext.isFunction(newFn)) {
            return origFn;
        }
        else {
            return function() {
                var me = this,
                    args = arguments;
                newFn.target = me;
                newFn.method = origFn;
                return (newFn.apply(scope || me || window, args) !== false) ? origFn.apply(me || window, args) : returnValue || null;
            };
        }
    },

    /**
     * Creates a delegate (callback) which, when called, executes after a specific delay.
     *
     * @param {Function} fn The function which will be called on a delay when the returned function is called.
     * Optionally, a replacement (or additional) argument list may be specified.
     * @param {Number} delay The number of milliseconds to defer execution by whenever called.
     * @param {Object} scope (optional) The scope (`this` reference) used by the function at execution time.
     * @param {Array} args (optional) Override arguments for the call. (Defaults to the arguments passed by the caller)
     * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
     * if a number the args are inserted at the specified position.
     * @return {Function} A function which, when called, executes the original function after the specified delay.
     */
    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);
        }
    },

    /**
     * Calls this function after the number of millseconds specified, optionally in a specific scope. Example usage:
     *
     *     var sayHi = function(name){
     *         alert('Hi, ' + name);
     *     }
     *
     *     // executes immediately:
     *     sayHi('Fred');
     *
     *     // executes after 2 seconds:
     *     Ext.Function.defer(sayHi, 2000, this, ['Fred']);
     *
     *     // this syntax is sometimes useful for deferring
     *     // execution of an anonymous function:
     *     Ext.Function.defer(function(){
     *         alert('Anonymous');
     *     }, 100);
     *
     * {@link Ext#defer Ext.defer} is alias for {@link Ext.Function#defer Ext.Function.defer}
     *
     * @param {Function} fn The function to defer.
     * @param {Number} millis The number of milliseconds for the setTimeout call
     * (if less than or equal to 0 the function is executed immediately)
     * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
     * **If omitted, defaults to the browser window.**
     * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
     * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
     * if a number the args are inserted at the specified position
     * @return {Number} The timeout id that can be used with clearTimeout
     */
    defer: function(fn, millis, scope, args, appendArgs) {
        fn = Ext.Function.bind(fn, scope, args, appendArgs);
        if (millis > 0) {
            return setTimeout(fn, millis);
        }
        fn();
        return 0;
    },

    /**
     * Create a combined function call sequence of the original function + the passed function.
     * The resulting function returns the results of the original function.
     * The passed function is called with the parameters of the original function. Example usage:
     *
     *     var sayHi = function(name){
     *         alert('Hi, ' + name);
     *     }
     *
     *     sayHi('Fred'); // alerts "Hi, Fred"
     *
     *     var sayGoodbye = Ext.Function.createSequence(sayHi, function(name){
     *         alert('Bye, ' + name);
     *     });
     *
     *     sayGoodbye('Fred'); // both alerts show
     *
     * @param {Function} originalFn The original function.
     * @param {Function} newFn The function to sequence
     * @param {Object} scope (optional) The scope (`this` reference) in which the passed function is executed.
     * If omitted, defaults to the scope in which the original function is called or the browser window.
     * @return {Function} The new function
     */
    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;
            };
        }
    },

    /**
     * Creates a delegate function, optionally with a bound scope which, when called, buffers
     * the execution of the passed function for the configured number of milliseconds.
     * If called again within that period, the impending invocation will be canceled, and the
     * timeout period will begin again.
     *
     * @param {Function} fn The function to invoke on a buffered timer.
     * @param {Number} buffer The number of milliseconds by which to buffer the invocation of the
     * function.
     * @param {Object} scope (optional) The scope (`this` reference) in which
     * the passed function is executed. If omitted, defaults to the scope specified by the caller.
     * @param {Array} args (optional) Override arguments for the call. Defaults to the arguments
     * passed by the caller.
     * @return {Function} A function which invokes the passed function after buffering for the specified time.
     */
    createBuffered: function(fn, buffer, scope, args) {
        var timerId;

        return function() {
            if (!scope) {
                scope = this;
            }

            if (!args) {
                args = Array.prototype.slice.call(arguments);
            }

            if (timerId) {
                clearTimeout(timerId);
                timerId = null;
            }

            timerId = setTimeout(function(){
                fn.apply(scope, args);
            }, buffer);
        };
    },

    /**
     * Creates a throttled version of the passed function which, when called repeatedly and
     * rapidly, invokes the passed function only after a certain interval has elapsed since the
     * previous invocation.
     *
     * This is useful for wrapping functions which may be called repeatedly, such as
     * a handler of a mouse move event when the processing is expensive.
     *
     * @param {Function} fn The function to execute at a regular time interval.
     * @param {Number} interval The interval **in milliseconds** on which the passed function is executed.
     * @param {Object} scope (optional) The scope (`this` reference) in which
     * the passed function is executed. If omitted, defaults to the scope specified by the caller.
     * @returns {Function} A function which invokes the passed function at the specified interval.
     */
    createThrottled: function(fn, interval, scope) {
        var lastCallTime, elapsed, lastArgs, timer, execute = function() {
            fn.apply(scope || this, lastArgs);
            lastCallTime = new Date().getTime();
        };

        return function() {
            elapsed = new Date().getTime() - lastCallTime;
            lastArgs = arguments;

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

    interceptBefore: function(object, methodName, fn) {
        var method = object[methodName] || Ext.emptyFn;

        return object[methodName] = function() {
            var ret = fn.apply(this, arguments);
            method.apply(this, arguments);

            return ret;
        };
    },

    interceptAfter: function(object, methodName, fn) {
        var method = object[methodName] || Ext.emptyFn;

        return object[methodName] = function() {
            method.apply(this, arguments);
            return fn.apply(this, arguments);
        };
    }
};

/**
 * @method
 * @member Ext
 * @alias Ext.Function#defer
 */
Ext.defer = Ext.Function.alias(Ext.Function, 'defer');

/**
 * @method
 * @member Ext
 * @alias Ext.Function#pass
 */
Ext.pass = Ext.Function.alias(Ext.Function, 'pass');

/**
 * @method
 * @member Ext
 * @alias Ext.Function#bind
 */
Ext.bind = Ext.Function.alias(Ext.Function, 'bind');

/**
 * @class Ext.JSON
 * Modified version of Douglas Crockford"s json.js that doesn"t
 * mess with the Object prototype
 * http://www.json.org/js.html
 * @singleton
 */
Ext.JSON = new(function() {
    var useHasOwn = !! {}.hasOwnProperty,
    isNative = function() {
        var useNative = null;

        return 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) {
        if (!Ext.isDefined(o) || o === null) {
            return "null";
        } else if (Ext.isArray(o)) {
            return encodeArray(o);
        } else if (Ext.isDate(o)) {
            return Ext.JSON.encodeDate(o);
        } else if (Ext.isString(o)) {
            return encodeString(o);
        } else if (typeof o == "number") {
            //don't use isNumber here, since finite checks happen inside isNumber
            return isFinite(o) ? String(o) : "null";
        } else if (Ext.isBoolean(o)) {
            return String(o);
        } else if (Ext.isObject(o)) {
            return encodeObject(o);
        } else if (typeof o === "function") {
            return "null";
        }
        return 'undefined';
    },
    m = {
        "\b": '\\b',
        "\t": '\\t',
        "\n": '\\n',
        "\f": '\\f',
        "\r": '\\r',
        '"': '\\"',
        "\\": '\\\\',
        '\x0b': '\\u000b' //ie doesn't handle \v
    },
    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) {
        var a = ["[", ""],
        // Note empty string in case there are no serializable members.
        len = o.length,
        i;
        for (i = 0; i < len; i += 1) {
            a.push(doEncode(o[i]), ',');
        }
        // Overwrite trailing comma (or empty string)
        a[a.length - 1] = ']';
        return a.join("");
    },
    encodeObject = function(o) {
        var a = ["{", ""],
        // Note empty string in case there are no serializable members.
        i;
        for (i in o) {
            if (!useHasOwn || o.hasOwnProperty(i)) {
                a.push(doEncode(i), ":", doEncode(o[i]), ',');
            }
        }
        // Overwrite trailing comma (or empty string)
        a[a.length - 1] = '}';
        return a.join("");
    };

    /**
     * <p>Encodes a Date. This returns the actual string which is inserted into the JSON string as the literal expression.
     * <b>The returned value includes enclosing double quotation marks.</b></p>
     * <p>The default return format is "yyyy-mm-ddThh:mm:ss".</p>
     * <p>To override this:</p><pre><code>
Ext.JSON.encodeDate = function(d) {
    return Ext.Date.format(d, '"Y-m-d"');
};
     </code></pre>
     * @param {Date} d The Date to encode
     * @return {String} The string literal to use in a JSON string.
     */
    this.encodeDate = function(o) {
        return '"' + o.getFullYear() + "-" 
        + pad(o.getMonth() + 1) + "-"
        + pad(o.getDate()) + "T"
        + pad(o.getHours()) + ":"
        + pad(o.getMinutes()) + ":"
        + pad(o.getSeconds()) + '"';
    };

    /**
     * Encodes an Object, Array or other value
     * @param {Object} o The variable to encode
     * @return {String} The JSON string
     */
    this.encode = function() {
        var ec;
        return function(o) {
            if (!ec) {
                // setup encoding function on first access
                ec = isNative() ? JSON.stringify : doEncode;
            }
            return ec(o);
        };
    }();


    /**
     * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
     * @param {String} json The JSON string
     * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
     * @return {Object} The resulting object
     */
    this.decode = function() {
        var dc;
        return function(json, safe) {
            if (!dc) {
                // setup decoding function on first access
                dc = isNative() ? JSON.parse : doDecode;
            }
            try {
                return dc(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
                });
            }
        };
    }();

})();
/**
 * Shorthand for {@link Ext.JSON#encode}
 * @member Ext
 * @method encode
 * @alias Ext.JSON#encode
 */
Ext.encode = Ext.JSON.encode;
/**
 * Shorthand for {@link Ext.JSON#decode}
 * @member Ext
 * @method decode
 * @alias Ext.JSON#decode
 */
Ext.decode = Ext.JSON.decode;


Ext.Error = {
    raise: function(object) {
        throw new Error(object.msg);
    }
};

/**
 *
 */
Ext.Date = {
    now: Date.now,

    /**
     * @private
     * Private for now
     */
    toString: function(date) {
        if (!date) {
            date = new 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');
    }
};


/**
 * @author Jacky Nguyen <jacky@sencha.com>
 * @class Ext.Base
 *
 * The root of all classes created with {@link Ext#define}.
 *
 * Ext.Base is the building block of all Ext classes. All classes in Ext inherit from Ext.Base.
 * All prototype and static members of this class are inherited by all other classes.
 */
(function(flexSetter) {

var noArgs = [],
    Base = function(){};

    // These static properties will be copied to every newly created class with {@link Ext#define}
    Ext.apply(Base, {
        $className: 'Ext.Base',

        $isClass: true,

        /**
         * Create a new instance of this Class.
         *
         *     Ext.define('My.cool.Class', {
         *         ...
         *     });
         *
         *     My.cool.Class.create({
         *         someConfig: true
         *     });
         *
         * All parameters are passed to the constructor of the class.
         *
         * @return {Object} the created instance.
         * @static
         * @inheritable
         */
        create: function() {
            return Ext.create.apply(Ext, [this].concat(Array.prototype.slice.call(arguments, 0)));
        },

        /**
         * @private
         * @static
         * @inheritable
         */
        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 inheritance
            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 = prototype.defaultConfig = new prototype.configClass;
            prototype.initConfigList = prototype.initConfigList.slice();
            prototype.initConfigMap = Ext.Object.chain(prototype.initConfigMap);
        },

        /**
         * @private
         * @static
         * @inheritable
         */
        '$onExtended': [],

        /**
         * @private
         * @static
         * @inheritable
         */
        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);
                }
            }
        },

        /**
         * @private
         * @static
         * @inheritable
         */
        onExtended: function(fn, scope) {
            this.$onExtended.push({
                fn: fn,
                scope: scope
            });

            return this;
        },

        /**
         * @private
         * @static
         * @inheritable
         */
        addConfig: function(config, fullMerge) {
            var prototype = this.prototype,
                initConfigList = prototype.initConfigList,
                initConfigMap = prototype.initConfigMap,
                defaultConfig = prototype.defaultConfig,
                hasInitConfigItem, name, value;

            fullMerge = Boolean(fullMerge);

            for (name in config) {
                if (config.hasOwnProperty(name) && (fullMerge || !(name in defaultConfig))) {
                    value = config[name];
                    hasInitConfigItem = initConfigMap[name];

                    if (value !== null) {
                        if (!hasInitConfigItem) {
                            initConfigMap[name] = true;
                            initConfigList.push(name);
                        }
                    }
                    else if (hasInitConfigItem) {
                        initConfigMap[name] = false;
                        Ext.Array.remove(initConfigList, name);
                    }
                }
            }

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

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

        /**
         * Add / override static properties of this class.
         *
         *     Ext.define('My.cool.Class', {
         *         ...
         *     });
         *
         *     My.cool.Class.addStatics({
         *         someProperty: 'someValue',      // My.cool.Class.someProperty = 'someValue'
         *         method1: function() { ... },    // My.cool.Class.method1 = function() { ... };
         *         method2: function() { ... }     // My.cool.Class.method2 = function() { ... };
         *     });
         *
         * @param {Object} members
         * @return {Ext.Base} this
         * @static
         * @inheritable
         */
        addStatics: function(members) {
            var member, name;
            var className = Ext.getClassName(this);

            for (name in members) {
                if (members.hasOwnProperty(name)) {
                    member = members[name];
                    if (typeof member == 'function') {
                        member.displayName = className + '.' + name;
                    }
                    this[name] = member;
                }
            }

            return this;
        },

        /**
         * @private
         * @static
         * @inheritable
         */
        addInheritableStatics: function(members) {
            var inheritableStatics,
                hasInheritableStatics,
                prototype = this.prototype,
                name, member;

            inheritableStatics = prototype.$inheritableStatics;
            hasInheritableStatics = prototype.$hasInheritableStatics;

            if (!inheritableStatics) {
                inheritableStatics = prototype.$inheritableStatics = [];
                hasInheritableStatics = prototype.$hasInheritableStatics = {};
            }

            var className = Ext.getClassName(this);

            for (name in members) {
                if (members.hasOwnProperty(name)) {
                    member = members[name];
                    if (typeof member == 'function') {
                        member.displayName = className + '.' + name;
                    }
                    this[name] = member;

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

            return this;
        },

        /**
         * Add methods / properties to the prototype of this class.
         *
         *     Ext.define('My.awesome.Cat', {
         *         constructor: function() {
         *             ...
         *         }
         *     });
         *
         *      My.awesome.Cat.implement({
         *          meow: function() {
         *             alert('Meowww...');
         *          }
         *      });
         *
         *      var kitty = new My.awesome.Cat;
         *      kitty.meow();
         *
         * @param {Object} members
         * @static
         * @inheritable
         */
        addMembers: function(members) {
            var prototype = this.prototype,
                enumerables = Ext.enumerables,
                names = [],
                i, ln, name, member;

            var className = this.$className || '';

            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.$owner = this;
                        member.$name = name;
                        member.displayName = className + '#' + name;
                    }

                    prototype[name] = member;
                }
            }

            return this;
        },

        /**
         * @private
         * @static
         * @inheritable
         */
        addMember: function(name, member) {
            if (typeof member == 'function' && !member.$isClass && member !== Ext.emptyFn) {
                member.$owner = this;
                member.$name = name;
                member.displayName = (this.$className || '') + '#' + name;
            }

            this.prototype[name] = member;

            return this;
        },

        /**
         * @private
         * @static
         * @inheritable
         */
        implement: function() {
            this.addMembers.apply(this, arguments);
        },

        /**
         * Borrow another class' members to the prototype of this class.
         *
         *     Ext.define('Bank', {
         *         money: '$$$',
         *         printMoney: function() {
         *             alert('$$$$$$$');
         *         }
         *     });
         *
         *     Ext.define('Thief', {
         *         ...
         *     });
         *
         *     Thief.borrow(Bank, ['money', 'printMoney']);
         *
         *     var steve = new Thief();
         *
         *     alert(steve.money); // alerts '$$$'
         *     steve.printMoney(); // alerts '$$$$$$$'
         *
         * @param {Ext.Base} fromClass The class to borrow members from
         * @param {Array/String} members The names of the members to borrow
         * @return {Ext.Base} this
         * @static
         * @inheritable
         * @private
         */
        borrow: function(fromClass, members) {
            var prototype = this.prototype,
                fromPrototype = fromClass.prototype,
                className = Ext.getClassName(this),
                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 = function() {
                        return toBorrow.apply(this, arguments);
                    };

                    if (className) {
                        fn.displayName = className + '#' + name;
                    }

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

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

            return this;
        },

        /**
         * Override members of this class. Overridden methods can be invoked via
         * {@link Ext.Base#callParent}.
         *
         *     Ext.define('My.Cat', {
         *         constructor: function() {
         *             alert("I'm a cat!");
         *         }
         *     });
         *
         *     My.Cat.override({
         *         constructor: function() {
         *             alert("I'm going to be a cat!");
         *
         *             var instance = this.callParent(arguments);
         *
         *             alert("Meeeeoooowwww");
         *
         *             return instance;
         *         }
         *     });
         *
         *     var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
         *                               // alerts "I'm a cat!"
         *                               // alerts "Meeeeoooowwww"
         *
         * As of 4.1, direct use of this method is deprecated. Use {@link Ext#define Ext.define}
         * instead:
         *
         *     Ext.define('My.CatOverride', {
         *         override: 'My.Cat',
         *         constructor: function() {
         *             alert("I'm going to be a cat!");
         *
         *             var instance = this.callParent(arguments);
         *
         *             alert("Meeeeoooowwww");
         *
         *             return instance;
         *         }
         *     });
         *
         * The above accomplishes the same result but can be managed by the {@link Ext.Loader}
         * which can properly order the override and its target class and the build process
         * can determine whether the override is needed based on the required state of the
         * target class (My.Cat).
         *
         * @param {Object} members The properties to add to this class. This should be
         * specified as an object literal containing one or more properties.
         * @return {Ext.Base} this class
         * @static
         * @inheritable
         * @deprecated 4.1.0 Please use {@link Ext#define Ext.define} instead
         */
        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 = []; // clean slate for prototype (1st pass) and static (2nd pass)
                statics = null; // not needed 1st pass, but needs to be cleared for 2nd pass

                for (name in members) { // hasOwnProperty is checked in the next loop...
                    if (name == 'statics') {
                        statics = members[name];
                    } 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) {
                            if (typeof member.$owner != 'undefined') {
                                member = cloneFunction(member);
                            }

                            var className = me.$className;
                            if (className) {
                                member.displayName = className + '#' + name;
                            }

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

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

                        target[name] = member;
                    }
                }

                target = me; // 2nd pass is for statics
                members = statics; // statics will be null on 2nd pass
            } while (members);

            return this;
        },

        /**
         * @private
         * @static
         * @inheritable
         */
        callParent: function(args) {
            var method;

            // This code is intentionally inlined for the least amount of debugger stepping
            return (method = this.callParent.caller) && (method.$previous ||
                  ((method = method.$owner ? method : method.caller) &&
                        method.$owner.superclass.$class[method.$name])).apply(this, args || noArgs);
        },

        /**
         * Used internally by the mixins pre-processor
         * @private
         * @static
         * @inheritable
         */
        mixin: function(name, mixinClass) {
            var mixin = mixinClass.prototype,
                prototype = this.prototype,
                key;

            if (typeof mixin.onClassMixedIn != 'undefined') {
                mixin.onClassMixedIn.call(mixinClass, this);
            }

            if (!prototype.hasOwnProperty('mixins')) {
                if ('mixins' in prototype) {
                    prototype.mixins = Ext.Object.chain(prototype.mixins);
                }
                else {
                    prototype.mixins = {};
                }
            }

            for (key in mixin) {
                if (key === 'mixins') {
                    Ext.merge(prototype.mixins, mixin[key]);
                }
                else if (typeof prototype[key] == 'undefined' && key != 'mixinId' && key != 'config') {
                    prototype[key] = mixin[key];
                }
            }

            if ('config' in mixin) {
                this.addConfig(mixin.config, false);
            }

            prototype.mixins[name] = mixin;
        },

        /**
         * Get the current class' name in string format.
         *
         *     Ext.define('My.cool.Class', {
         *         constructor: function() {
         *             alert(this.self.getName()); // alerts 'My.cool.Class'
         *         }
         *     });
         *
         *     My.cool.Class.getName(); // 'My.cool.Class'
         *
         * @return {String} className
         * @static
         * @inheritable
         */
        getName: function() {
            return Ext.getClassName(this);
        },

        /**
         * Create aliases for existing prototype methods. Example:
         *
         *     Ext.define('My.cool.Class', {
         *         method1: function() { ... },
         *         method2: function() { ... }
         *     });
         *
         *     var test = new My.cool.Class();
         *
         *     My.cool.Class.createAlias({
         *         method3: 'method1',
         *         method4: 'method2'
         *     });
         *
         *     test.method3(); // test.method1()
         *
         *     My.cool.Class.createAlias('method5', 'method3');
         *
         *     test.method5(); // test.method3() -> test.method1()
         *
         * @param {String/Object} alias The new method name, or an object to set multiple aliases. See
         * {@link Ext.Function#flexSetter flexSetter}
         * @param {String/Object} origin The original method name
         * @static
         * @inheritable
         * @method
         */
        createAlias: flexSetter(function(alias, origin) {
            this.override(alias, function() {
                return this[origin].apply(this, arguments);
            });
        }),

        /**
         * @private
         * @static
         * @inheritable
         */
        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: [],

        initConfigMap: {},

        /**
         * Get the reference to the class from which this object was instantiated. Note that unlike {@link Ext.Base#self},
         * `this.statics()` is scope-independent and it always returns the class from which it was called, regardless of what
         * `this` points to during run-time
         *
         *     Ext.define('My.Cat', {
         *         statics: {
         *             totalCreated: 0,
         *             speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
         *         },
         *
         *         constructor: function() {
         *             var statics = this.statics();
         *
         *             alert(statics.speciesName);     // always equals to 'Cat' no matter what 'this' refers to
         *                                             // equivalent to: My.Cat.speciesName
         *
         *             alert(this.self.speciesName);   // dependent on 'this'
         *
         *             statics.totalCreated++;
         *         },
         *
         *         clone: function() {
         *             var cloned = new this.self;                      // dependent on 'this'
         *
         *             cloned.groupName = this.statics().speciesName;   // equivalent to: My.Cat.speciesName
         *
         *             return cloned;
         *         }
         *     });
         *
         *
         *     Ext.define('My.SnowLeopard', {
         *         extend: 'My.Cat',
         *
         *         statics: {
         *             speciesName: 'Snow Leopard'     // My.SnowLeopard.speciesName = 'Snow Leopard'
         *         },
         *
         *         constructor: function() {
         *             this.callParent();
         *         }
         *     });
         *
         *     var cat = new My.Cat();                 // alerts 'Cat', then alerts 'Cat'
         *
         *     var snowLeopard = new My.SnowLeopard(); // alerts 'Cat', then alerts 'Snow Leopard'
         *
         *     var clone = snowLeopard.clone();
         *     alert(Ext.getClassName(clone));         // alerts 'My.SnowLeopard'
         *     alert(clone.groupName);                 // alerts 'Cat'
         *
         *     alert(My.Cat.totalCreated);             // alerts 3
         *
         * @protected
         * @return {Ext.Class}
         */
        statics: function() {
            var method = this.statics.caller,
                self = this.self;

            if (!method) {
                return self;
            }

            return method.$owner;
        },

        /**
         * Call the "parent" method of the current method. That is the method previously
         * overridden by derivation or by an override (see {@link Ext#define}).
         *
         *      Ext.define('My.Base', {
         *          constructor: function (x) {
         *              this.x = x;
         *          },
         *
         *          statics: {
         *              method: function (x) {
         *                  return x;
         *              }
         *          }
         *      });
         *
         *      Ext.define('My.Derived', {
         *          extend: 'My.Base',
         *
         *          constructor: function () {
         *              this.callParent([21]);
         *          }
         *      });
         *
         *      var obj = new My.Derived();
         *
         *      alert(obj.x);  // alerts 21
         *
         * This can be used with an override as follows:
         *
         *      Ext.define('My.DerivedOverride', {
         *          override: 'My.Derived',
         *
         *          constructor: function (x) {
         *              this.callParent([x*2]); // calls original My.Derived constructor
         *          }
         *      });
         *
         *      var obj = new My.Derived();
         *
         *      alert(obj.x);  // now alerts 42
         *
         * This also works with static methods.
         *
         *      Ext.define('My.Derived2', {
         *          extend: 'My.Base',
         *
         *          statics: {
         *              method: function (x) {
         *                  return this.callParent([x*2]); // calls My.Base.method
         *              }
         *          }
         *      });
         *
         *      alert(My.Base.method(10);     // alerts 10
         *      alert(My.Derived2.method(10); // alerts 20
         *
         * Lastly, it also works with overridden static methods.
         *
         *      Ext.define('My.Derived2Override', {
         *          override: 'My.Derived2',
         *
         *          statics: {
         *              method: function (x) {
         *                  return this.callParent([x*2]); // calls My.Derived2.method
         *              }
         *          }
         *      });
         *
         *      alert(My.Derived2.method(10); // now alerts 40
         *
         * @protected
         * @param {Array/Arguments} args The arguments, either an array or the `arguments` object
         * from the current method, for example: `this.callParent(arguments)`
         * @return {Object} Returns the result of calling the parent method
         */
        callParent: function(args) {
            // NOTE: this code is deliberately as few expressions (and no function calls)
            // as possible so that a debugger can skip over this noise with the minimum number
            // of steps. Basically, just hit Step Into until you are where you really wanted
            // to be.
            var method,
                superMethod = (method = this.callParent.caller) && (method.$previous ||
                        ((method = method.$owner ? method : method.caller) &&
                                method.$owner.superclass[method.$name]));

            if (!superMethod) {
                method = this.callParent.caller;
                var parentClass, methodName;

                if (!method.$owner) {
                    if (!method.caller) {
                        throw new Error("Attempting to call a protected method from the public scope, which is not allowed");
                    }

                    method = method.caller;
                }

                parentClass = method.$owner.superclass;
                methodName = method.$name;

                if (!(methodName in parentClass)) {
                    throw new Error("this.callParent() was called but there's no such method (" + methodName +
                                ") found in the parent class (" + (Ext.getClassName(parentClass) || 'Object') + ")");
                }
            }

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

        /**
         * @property {Ext.Class} self
         *
         * Get the reference to the current class from which this object was instantiated. Unlike {@link Ext.Base#statics},
         * `this.self` is scope-dependent and it's meant to be used for dynamic inheritance. See {@link Ext.Base#statics}
         * for a detailed comparison
         *
         *     Ext.define('My.Cat', {
         *         statics: {
         *             speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
         *         },
         *
         *         constructor: function() {
         *             alert(this.self.speciesName); / dependentOL on 'this'
         *         },
         *
         *         clone: function() {
         *             return new this.self();
         *         }
         *     });
         *
         *
         *     Ext.define('My.SnowLeopard', {
         *         extend: 'My.Cat',
         *         statics: {
         *             speciesName: 'Snow Leopard'         // My.SnowLeopard.speciesName = 'Snow Leopard'
         *         }
         *     });
         *
         *     var cat = new My.Cat();                     // alerts 'Cat'
         *     var snowLeopard = new My.SnowLeopard();     // alerts 'Snow Leopard'
         *
         *     var clone = snowLeopard.clone();
         *     alert(Ext.getClassName(clone));             // alerts 'My.SnowLeopard'
         *
         * @protected
         */
        self: Base,

        // Default constructor, simply returns `this`
        constructor: function() {
            return this;
        },


        wasInstantiated: false,

        /**
         * Initialize configuration for this class. a typical example:
         *
         *     Ext.define('My.awesome.Class', {
         *         // The default config
         *         config: {
         *             name: 'Awesome',
         *             isAwesome: true
         *         },
         *
         *         constructor: function(config) {
         *             this.initConfig(config);
         *         }
         *     });
         *
         *     var awesome = new My.awesome.Class({
         *         name: 'Super Awesome'
         *     });
         *
         *     alert(awesome.getName()); // 'Super Awesome'
         *
         * @protected
         * @param {Object} instanceConfig
         * @return {Object} mixins The mixin prototypes as key - value pairs
         */
        initConfig: function(instanceConfig) {
            var configNameCache = Ext.Class.configNameCache,
                prototype = this.self.prototype,
                initConfigList = this.initConfigList,
                initConfigMap = this.initConfigMap,
                config = new this.configClass,
                defaultConfig = this.defaultConfig,
                i, ln, name, value, nameMap, getName;

            this.initConfig = Ext.emptyFn;

            this.initialConfig = instanceConfig || {};

            if (instanceConfig) {
                Ext.merge(config, instanceConfig);
            }

            this.config = config;

            // Optimize initConfigList *once* per class based on the existence of apply* and update* methods
            // Happens only once during the first instantiation
            if (!prototype.hasOwnProperty('wasInstantiated')) {
                prototype.wasInstantiated = true;

                for (i = 0,ln = initConfigList.length; i < ln; i++) {
                    name = initConfigList[i];
                    nameMap = configNameCache[name];
                    value = defaultConfig[name];

                    if (!(nameMap.apply in prototype)
                        && !(nameMap.update in prototype)
                        && prototype[nameMap.set].$isDefault
                        && typeof value != 'object') {
                        prototype[nameMap.internal] = defaultConfig[name];
                        initConfigMap[name] = false;
                        Ext.Array.remove(initConfigList, name);
                        i--;
                        ln--;
                    }
                }
            }

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

                for (name in instanceConfig) {
                    if (name in defaultConfig && !initConfigMap[name]) {
                        initConfigList.push(name);
                    }
                }
            }

            // Point all getters to the initGetters
            for (i = 0,ln = initConfigList.length; i < ln; i++) {
                name = initConfigList[i];
                nameMap = configNameCache[name];
                this[nameMap.get] = this[nameMap.initGet];
            }

            this.beforeInitConfig(config);

            for (i = 0,ln = initConfigList.length; i < ln; i++) {
                name = initConfigList[i];
                nameMap = configNameCache[name];
                getName = nameMap.get;

                if (this.hasOwnProperty(getName)) {
                    this[nameMap.set].call(this, config[name]);
                    delete this[getName];
                }
            }

            return this;
        },

        beforeInitConfig: Ext.emptyFn,

        /**
         * @private
         */
        getCurrentConfig: function() {
            var defaultConfig = this.defaultConfig,
                configNameCache = Ext.Class.configNameCache,
                config = {},
                name, nameMap;

            for (name in defaultConfig) {
                nameMap = configNameCache[name];
                config[name] = this[nameMap.get].call(this);
            }

            return config;
        },

        /**
         * @private
         */
        setConfig: function(config, applyIfNotSet) {
            if (!config) {
                return this;
            }

            var configNameCache = Ext.Class.configNameCache,
                currentConfig = this.config,
                defaultConfig = this.defaultConfig,
                initialConfig = this.initialConfig,
                configList = [],
                name, i, ln, nameMap;

            applyIfNotSet = Boolean(applyIfNotSet);

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

                currentConfig[name] = config[name];

                if (name in defaultConfig) {
                    configList.push(name);
                    nameMap = configNameCache[name];
                    this[nameMap.get] = this[nameMap.initGet];
                }
            }

            for (i = 0,ln = configList.length; i < ln; i++) {
                name = configList[i];
                nameMap = configNameCache[name];
                this[nameMap.set].call(this, config[name]);
                delete this[nameMap.get];
            }

            return this;
        },

        /**
         * @private
         */
        getConfig: function(name) {
            return this[Ext.Class.configNameCache[name].get].call(this);
        },

        /**
         * @private
         */
        hasConfig: function(name) {
            return (name in this.defaultConfig);
        },

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

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

        /**
         * @private
         */
        onConfigUpdate: function(names, callback, scope) {
            var self = this.self,
                className = self.$className,
                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;
                newUpdater.displayName = className + '#' + updaterName;

                this[updaterName] = newUpdater;
            }
        },

        /**
         * @private
         */
        destroy: function() {
            this.destroy = Ext.emptyFn;
            this.isDestroyed = true;
        }
    });

    /**
     * Call the original method that was previously overridden with {@link Ext.Base#override}
     *
     *     Ext.define('My.Cat', {
     *         constructor: function() {
     *             alert("I'm a cat!");
     *         }
     *     });
     *
     *     My.Cat.override({
     *         constructor: function() {
     *             alert("I'm going to be a cat!");
     *
     *             var instance = this.callOverridden();
     *
     *             alert("Meeeeoooowwww");
     *
     *             return instance;
     *         }
     *     });
     *
     *     var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
     *                               // alerts "I'm a cat!"
     *                               // alerts "Meeeeoooowwww"
     *
     * @param {Array/Arguments} args The arguments, either an array or the `arguments` object
     * from the current method, for example: `this.callOverridden(arguments)`
     * @return {Object} Returns the result of calling the overridden method
     * @protected
     */
    Base.prototype.callOverridden = Base.prototype.callParent;

    Ext.Base = Base;

})(Ext.Function.flexSetter);

/**
 * @author Jacky Nguyen <jacky@sencha.com>
 * @class Ext.Class
 *
 * Handles class creation throughout the framework. This is a low level factory that is used by Ext.ClassManager and generally
 * should not be used directly. If you choose to use Ext.Class you will lose out on the namespace, aliasing and depency loading
 * features made available by Ext.ClassManager. The only time you would use Ext.Class directly is to create an anonymous class.
 *
 * If you wish to create a class you should use {@link Ext#define Ext.define} which aliases
 * {@link Ext.ClassManager#create Ext.ClassManager.create} to enable namespacing and dynamic dependency resolution.
 *
 * Ext.Class is the factory and **not** the superclass of everything. For the base class that **all** Ext classes inherit
 * from, see {@link Ext.Base}.
 */
(function() {
    var ExtClass,
        Base = Ext.Base,
        baseStaticMembers = [],
        baseStaticMember, baseStaticMemberLength;

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

    baseStaticMemberLength = baseStaticMembers.length;

    /**
     * @method constructor
     * Creates a new anonymous class.
     *
     * @param {Object} data An object represent the properties of this class
     * @param {Function} onCreated Optional, the callback function to be executed when this class is fully created.
     * Note that the creation process can be asynchronous depending on the pre-processors used.
     *
     * @return {Ext.Base} The newly created class
     */
    Ext.Class = ExtClass = function(Class, data, onCreated) {
        if (typeof Class != 'function') {
            onCreated = data;
            data = Class;
            Class = null;
        }

        if (!data) {
            data = {};
        }

        Class = ExtClass.create(Class);

        ExtClass.process(Class, data, onCreated);

        return Class;
    };

    Ext.apply(ExtClass, {
        /**
         * @private
         * @static
         */
        onBeforeCreated: function(Class, data, hooks) {
            Class.addMembers(data);

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

        /**
         * @private
         * @static
         */
        create: function(Class) {
            var name, i;

            if (!Class) {
                Class = function() {
                    return this.constructor.apply(this, arguments);
                };
            }

            for (i = 0; i < baseStaticMemberLength; i++) {
                name = baseStaticMembers[i];
                Class[name] = Base[name];
            }

            return Class;
        },

        /**
         * @private
         * @static
         */
        process: function(Class, data, onCreated) {
            var preprocessorStack = data.preprocessors || ExtClass.defaultPreprocessors,
                preprocessors = this.preprocessors,
                hooks = {
                    onBeforeCreated: this.onBeforeCreated,
                    onCreated: onCreated || Ext.emptyFn
                },
                index = 0,
                name, preprocessor, properties,
                i, ln, fn, property, process;

            delete data.preprocessors;

            process = function(Class, data, hooks) {
                fn = null;

                while (fn === null) {
                    name = preprocessorStack[index++];

                    if (name) {
                        preprocessor = preprocessors[name];
                        properties = preprocessor.properties;

                        if (properties === true) {
                            fn = preprocessor.fn;
                        }
                        else {
                            for (i = 0,ln = properties.length; i < ln; i++) {
                                property = properties[i];

                                if (data.hasOwnProperty(property)) {
                                    fn = preprocessor.fn;
                                    break;
                                }
                            }
                        }
                    }
                    else {
                        hooks.onBeforeCreated.apply(this, arguments);
                        return;
                    }
                }

                if (fn.call(this, Class, data, hooks, process) !== false) {
                    process.apply(this, arguments);
                }
            };

            process.call(this, Class, data, hooks);
        },

        /**
         * @private
         * @static
         */
        preprocessors: {},

        /**
         * Register a new pre-processor to be used during the class creation process
         *
         * @private
         * @static
         * @param {String} name The pre-processor's name
         * @param {Function} fn The callback function to be executed. Typical format:
         *
         *     function(cls, data, fn) {
         *         // Your code here
         *
         *         // Execute this when the processing is finished.
         *         // Asynchronous processing is perfectly ok
         *         if (fn) {
         *             fn.call(this, cls, data);
         *         }
         *     });
         *
         * Passed arguments for this function are:
         *
         * - `{Function} cls`: The created class
         * - `{Object} data`: The set of properties passed in {@link Ext.Class} constructor
         * - `{Function} fn`: The callback function that <b>must</b> to be executed when this pre-processor finishes,
         * regardless of whether the processing is synchronous or aynchronous
         *
         * @return {Ext.Class} this
         */
        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;
        },

        /**
         * Retrieve a pre-processor callback function by its name, which has been registered before
         *
         * @private
         * @static
         * @param {String} name
         * @return {Function} preprocessor
         */
        getPreprocessor: function(name) {
            return this.preprocessors[name];
        },

        /**
         * @private
         * @static
         */
        getPreprocessors: function() {
            return this.preprocessors;
        },

        /**
         * @private
         * @static
         */
        defaultPreprocessors: [],

        /**
         * Retrieve the array stack of default pre-processors
         * @private
         * @static
         * @return {Function} defaultPreprocessors
         */
        getDefaultPreprocessors: function() {
            return this.defaultPreprocessors;
        },

        /**
         * Set the default array stack of default pre-processors
         *
         * @private
         * @static
         * @param {Array} preprocessors
         * @return {Ext.Class} this
         */
        setDefaultPreprocessors: function(preprocessors) {
            this.defaultPreprocessors = Ext.Array.from(preprocessors);

            return this;
        },

        /**
         * Insert this pre-processor at a specific position in the stack, optionally relative to
         * any existing pre-processor. For example:
         *
         *     Ext.Class.registerPreprocessor('debug', function(cls, data, fn) {
         *         // Your code here
         *
         *         if (fn) {
         *             fn.call(this, cls, data);
         *         }
         *     }).insertDefaultPreprocessor('debug', 'last');
         *
         * @private
         * @static
         * @param {String} name The pre-processor name. Note that it needs to be registered with
         * {@link Ext.Class#registerPreprocessor registerPreprocessor} before this
         * @param {String} offset The insertion position. Four possible values are:
         * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
         * @param {String} relativeName
         * @return {Ext.Class} 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;
        },

        /**
         * @private
         * @static
         */
        configNameCache: {},

        /**
         * @private
         * @static
         */
        getConfigNameMap: function(name) {
            var cache = this.configNameCache,
                map = cache[name],
                capitalizedName;

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

                map = cache[name] = {
                    name: name,
                    internal: '_' + name,
                    initializing: 'is' + capitalizedName + 'Initializing',
                    apply: 'apply' + capitalizedName,
                    update: 'update' + capitalizedName,
                    set: 'set' + capitalizedName,
                    get: 'get' + capitalizedName,
                    initGet: 'initGet' + capitalizedName,
                    doSet : 'doSet' + capitalizedName,
                    changeEvent: name.toLowerCase() + 'change'
                }
            }

            return map;
        },

        /**
         * @private
         * @static
         */
        generateSetter: function(nameMap) {
            var internalName = nameMap.internal,
                getName = nameMap.get,
                applyName = nameMap.apply,
                updateName = nameMap.update,
                setter;

            setter = function(value) {
                var oldValue = this[internalName],
                    applier = this[applyName],
                    updater = this[updateName];

                delete this[getName];

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

            setter.$isDefault = true;

            return setter;
        },

        /**
         * @private
         * @static
         */
        generateInitGetter: function(nameMap) {
            var name = nameMap.name,
                setName = nameMap.set,
                getName = nameMap.get,
                initializingName = nameMap.initializing;

            return function() {
                this[initializingName] = true;
                delete this[getName];

                this[setName].call(this, this.config[name]);
                delete this[initializingName];

                return this[getName].apply(this, arguments);
            }
        },

        /**
         * @private
         * @static
         */
        generateGetter: function(nameMap) {
            var internalName = nameMap.internal;

            return function() {
                return this[internalName];
            }
        }
    });

    /**
     * @cfg {String} extend
     * The parent class that this class extends. For example:
     *
     *     Ext.define('Person', {
     *         say: function(text) { alert(text); }
     *     });
     *
     *     Ext.define('Developer', {
     *         extend: 'Person',
     *         say: function(text) { this.callParent(["print "+text]); }
     *     });
     */
    ExtClass.registerPreprocessor('extend', function(Class, data) {
        var Base = Ext.Base,
            basePrototype = Base.prototype,
            extend = data.extend,
            Parent, parentPrototype, name;

        delete data.extend;

        if (extend && extend !== Object) {
            Parent = extend;
        }
        else {
            Parent = Base;
        }

        parentPrototype = Parent.prototype;

        if (!Parent.$isClass) {
            for (name in basePrototype) {
                if (!parentPrototype[name]) {
                    parentPrototype[name] = basePrototype[name];
                }
            }
        }

        Class.extend(Parent);

        Class.triggerExtended.apply(Class, arguments);

        if (data.onClassExtended) {
            Class.onExtended(data.onClassExtended, Class);
            delete data.onClassExtended;
        }

    }, true);

    /**
     * @cfg {Object} statics
     * List of static methods for this class. For example:
     *
     *     Ext.define('Computer', {
     *          statics: {
     *              factory: function(brand) {
     *                  // 'this' in static methods refer to the class itself
     *                  return new this(brand);
     *              }
     *          },
     *
     *          constructor: function() { ... }
     *     });
     *
     *     var dellComputer = Computer.factory('Dell');
     */
    ExtClass.registerPreprocessor('statics', function(Class, data) {
        Class.addStatics(data.statics);

        delete data.statics;
    });

    /**
     * @cfg {Object} inheritableStatics
     * List of inheritable static methods for this class.
     * Otherwise just like {@link #statics} but subclasses inherit these methods.
     */
    ExtClass.registerPreprocessor('inheritableStatics', function(Class, data) {
        Class.addInheritableStatics(data.inheritableStatics);

        delete data.inheritableStatics;
    });

    /**
     * @cfg {Object} config
     *
     * List of configuration options with their default values.
     *
     * **Note** You need to make sure {@link Ext.Base#initConfig} is called from your constructor if you are defining
     * your own class or singleton, unless you are extending a Component. Otherwise the generated getter and setter
     * methods will not be initialized.
     *
     * Each config item will have its own setter and getter method automatically generated inside the class prototype
     * during class creation time, if the class does not have those methods explicitly defined.
     *
     * As an example, let’s convert the name property of a Person class to be a config item, then add extra age and
     * gender items.
     *
     *     Ext.define('My.sample.Person', {
     *         config: {
     *             name: 'Mr. Unknown',
     *             age: 0,
     *             gender: 'Male'
     *         },
     *
     *         constructor: function(config) {
     *             this.initConfig(config);
     *
     *             return this;
     *         }
     *
     *         // ...
     *     });
     *
     * Within the class, this.name still has the default value of “Mr. Unknown”. However, it’s now publicly accessible
     * without sacrificing encapsulation, via setter and getter methods.
     *
     *     var jacky = new Person({
     *         name: "Jacky",
     *         age: 35
     *     });
     *
     *     alert(jacky.getAge());      // alerts 35
     *     alert(jacky.getGender());   // alerts "Male"
     *
     *     jacky.walk(10);             // alerts "Jacky is walking 10 steps"
     *
     *     jacky.setName("Mr. Nguyen");
     *     alert(jacky.getName());     // alerts "Mr. Nguyen"
     *
     *     jacky.walk(10);             // alerts "Mr. Nguyen is walking 10 steps"
     *
     * Notice that we changed the class constructor to invoke this.initConfig() and pass in the provided config object.
     * Two key things happened:
     *
     *  - The provided config object when the class is instantiated is recursively merged with the default config object.
     *  - All corresponding setter methods are called with the merged values.
     *
     * Beside storing the given values, thoughout the frameworks, setters generally have two key responsibilities:
     *
     *  - Filtering / validation / transformation of the given value before it’s actually stored within the instance.
     *  - Notification (such as firing events) / post-processing after the value has been set, or changed from a
     *    previous value.
     *
     * By standardize this common pattern, the default generated setters provide two extra template methods that you
     * can put your own custom logics into, i.e: a “applyFoo” and “updateFoo” method for a “foo” config item, which are
     * executed before and after the value is actually set, respectively. Back to the example class, let’s validate that
     * age must be a valid positive number, and fire an 'agechange' if the value is modified.
     *
     *     Ext.define('My.sample.Person', {
     *         config: { .... },
     *
     *         constructor: { .... }
     *
     *         applyAge: function(age) {
     *             if (typeof age != 'number' || age &lt; 0) {
     *                 console.warn("Invalid age, must be a positive number");
     *                 return;
     *             }
     *
     *             return age;
     *         },
     *
     *         updateAge: function(newAge, oldAge) {
     *             // age has changed from "oldAge" to "newAge"
     *             this.fireEvent('agechange', this, newAge, oldAge);
     *         }
     *
     *         // ....
     *     });
     *
     *     var jacky = new Person({
     *         name: "Jacky",
     *         age: 'invalid'
     *     });
     *
     *     alert(jacky.getAge());      // alerts 0
     *
     *     alert(jacky.setAge(-100));  // alerts 0
     *     alert(jacky.getAge());      // alerts 0
     *
     *     alert(jacky.setAge(35));    // alerts 0
     *     alert(jacky.getAge());      // alerts 35
     *
     * In other words, when leveraging the config feature, you mostly never need to define setter and getter methods
     * explicitly. Instead, "apply*" and "update*" methods should be implemented where neccessary. Your code will be
     * consistent throughout and only contain the minimal logic that you actually care about.
     *
     * When it comes to inheritance, the default config of the parent class is automatically, recursively merged with
     * the child’s default config. The same applies for mixins.
     */
    ExtClass.registerPreprocessor('config', function(Class, data) {
        var config = data.config,
            prototype = Class.prototype,
            defaultConfig = prototype.config,
            nameMap, name, setName, getName, initGetName, internalName, value;

        delete data.config;

        for (name in config) {
            // Once per config item, per class hierachy
            if (config.hasOwnProperty(name) && !(name in defaultConfig)) {
                value = config[name];
                nameMap = this.getConfigNameMap(name);
                setName = nameMap.set;
                getName = nameMap.get;
                initGetName = nameMap.initGet;
                internalName = nameMap.internal;

                data[initGetName] = this.generateInitGetter(nameMap);

                if (value === null && !data.hasOwnProperty(internalName)) {
                    data[internalName] = null;
                }

                if (!data.hasOwnProperty(getName)) {
                    data[getName] = this.generateGetter(nameMap);
                }

                if (!data.hasOwnProperty(setName)) {
                    data[setName] = this.generateSetter(nameMap);
                }
            }
        }

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

    /**
     * @cfg {Object} mixins
     * List of classes to mix into this class. For example:
     *
     *     Ext.define('CanSing', {
     *          sing: function() {
     *              alert("I'm on the highway to hell...")
     *          }
     *     });
     *
     *     Ext.define('Musician', {
     *          extend: 'Person',
     *
     *          mixins: {
     *              canSing: 'CanSing'
     *          }
     *     })
     */
    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 (name in mixins) {
                    if (mixins.hasOwnProperty(name)) {
                        Class.mixin(name, mixins[name]);
                    }
                }
            }
        });
    });

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

/**
 * @author Jacky Nguyen <jacky@sencha.com>
 * @class  Ext.ClassManager
 *
 * Ext.ClassManager manages all classes and handles mapping from string class name to
 * actual class objects throughout the whole framework. It is not generally accessed directly, rather through
 * these convenient shorthands:
 *
 * - {@link Ext#define Ext.define}
 * - {@link Ext#create Ext.create}
 * - {@link Ext#widget Ext.widget}
 * - {@link Ext#getClass Ext.getClass}
 * - {@link Ext#getClassName Ext.getClassName}
 *
 * ## Basic syntax:
 *
 *     Ext.define(className, properties);
 *
 * in which `properties` is an object represent a collection of properties that apply to the class. See
 * {@link Ext.ClassManager#create} for more detailed instructions.
 *
 *     Ext.define('Person', {
 *          name: 'Unknown',
 *
 *          constructor: function(name) {
 *              if (name) {
 *                  this.name = name;
 *              }
 *
 *              return this;
 *          },
 *
 *          eat: function(foodType) {
 *              alert("I'm eating: " + foodType);
 *
 *              return this;
 *          }
 *     });
 *
 *     var aaron = new Person("Aaron");
 *     aaron.eat("Sandwich"); // alert("I'm eating: Sandwich");
 *
 * Ext.Class has a powerful set of extensible {@link Ext.Class#registerPreprocessor pre-processors} which takes care of
 * everything related to class creation, including but not limited to inheritance, mixins, configuration, statics, etc.
 *
 * ## Inheritance:
 *
 *     Ext.define('Developer', {
 *          extend: 'Person',
 *
 *          constructor: function(name, isGeek) {
 *              this.isGeek = isGeek;
 *
 *              // Apply a method from the parent class' prototype
 *              this.callParent([name]);
 *
 *              return this;
 *
 *          },
 *
 *          code: function(language) {
 *              alert("I'm coding in: " + language);
 *
 *              this.eat("Bugs");
 *
 *              return this;
 *          }
 *     });
 *
 *     var jacky = new Developer("Jacky", true);
 *     jacky.code("JavaScript"); // alert("I'm coding in: JavaScript");
 *                               // alert("I'm eating: Bugs");
 *
 * See {@link Ext.Base#callParent} for more details on calling superclass' methods
 *
 * ## Mixins:
 *
 *     Ext.define('CanPlayGuitar', {
 *          playGuitar: function() {
 *             alert("F#...G...D...A");
 *          }
 *     });
 *
 *     Ext.define('CanComposeSongs', {
 *          composeSongs: function() { }
 *     });
 *
 *     Ext.define('CanSing', {
 *          sing: function() {
 *              alert("I'm on the highway to hell...")
 *          }
 *     });
 *
 *     Ext.define('Musician', {
 *          extend: 'Person',
 *
 *          mixins: {
 *              canPlayGuitar: 'CanPlayGuitar',
 *              canComposeSongs: 'CanComposeSongs',
 *              canSing: 'CanSing'
 *          }
 *     })
 *
 *     Ext.define('CoolPerson', {
 *          extend: 'Person',
 *
 *          mixins: {
 *              canPlayGuitar: 'CanPlayGuitar',
 *              canSing: 'CanSing'
 *          },
 *
 *          sing: function() {
 *              alert("Ahem....");
 *
 *              this.mixins.canSing.sing.call(this);
 *
 *              alert("[Playing guitar at the same time...]");
 *
 *              this.playGuitar();
 *          }
 *     });
 *
 *     var me = new CoolPerson("Jacky");
 *
 *     me.sing(); // alert("Ahem...");
 *                // alert("I'm on the highway to hell...");
 *                // alert("[Playing guitar at the same time...]");
 *                // alert("F#...G...D...A");
 *
 * ## Config:
 *
 *     Ext.define('SmartPhone', {
 *          config: {
 *              hasTouchScreen: false,
 *              operatingSystem: 'Other',
 *              price: 500
 *          },
 *
 *          isExpensive: false,
 *
 *          constructor: function(config) {
 *              this.initConfig(config);
 *
 *              return this;
 *          },
 *
 *          applyPrice: function(price) {
 *              this.isExpensive = (price > 500);
 *
 *              return price;
 *          },
 *
 *          applyOperatingSystem: function(operatingSystem) {
 *              if (!(/^(iOS|Android|BlackBerry)$/i).test(operatingSystem)) {
 *                  return 'Other';
 *              }
 *
 *              return operatingSystem;
 *          }
 *     });
 *
 *     var iPhone = new SmartPhone({
 *          hasTouchScreen: true,
 *          operatingSystem: 'iOS'
 *     });
 *
 *     iPhone.getPrice(); // 500;
 *     iPhone.getOperatingSystem(); // 'iOS'
 *     iPhone.getHasTouchScreen(); // true;
 *
 *     iPhone.isExpensive; // false;
 *     iPhone.setPrice(600);
 *     iPhone.getPrice(); // 600
 *     iPhone.isExpensive; // true;
 *
 *     iPhone.setOperatingSystem('AlienOS');
 *     iPhone.getOperatingSystem(); // 'Other'
 *
 * ## Statics:
 *
 *     Ext.define('Computer', {
 *          statics: {
 *              factory: function(brand) {
 *                 // 'this' in static methods refer to the class itself
 *                  return new this(brand);
 *              }
 *          },
 *
 *          constructor: function() { }
 *     });
 *
 *     var dellComputer = Computer.factory('Dell');
 *
 * Also see {@link Ext.Base#statics} and {@link Ext.Base#self} for more details on accessing
 * static properties within class methods
 *
 * @singleton
 */
(function(Class, alias, arraySlice, arrayFrom, global) {
    var Manager = Ext.ClassManager = {

        /**
         * @property classes
         * @type Object
         * All classes which were defined through the ClassManager. Keys are the
         * name of the classes and the values are references to the classes.
         * @private
         */
        classes: {},

        /**
         * @private
         */
        existCache: {},

        /**
         * @private
         */
        namespaceRewrites: [{
            from: 'Ext.',
            to: Ext
        }],

        /**
         * @private
         */
        maps: {
            alternateToName: {},
            aliasToName: {},
            nameToAliases: {},
            nameToAlternates: {},
            overridesByName: {}
        },

        /** @private */
        enableNamespaceParseCache: true,

        /** @private */
        namespaceParseCache: {},

        /** @private */
        instantiators: [],

        /**
         * Checks if a class has already been created.
         *
         * @param {String} className
         * @return {Boolean} exist
         */
        isCreated: function(className) {
            var existCache = this.existCache,
                i, ln, part, root, parts;

            if (typeof className != 'string' || className.length < 1) {
                throw new Error("[Ext.ClassManager] Invalid classname, must be a string and must not be empty");
            }

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

        /**
         * @private
         */
        createdListeners: [],

        /**
         * @private
         */
        nameCreatedListeners: {},

        /**
         * @private
         */
        triggerCreated: function(className) {
            var listeners = this.createdListeners,
                nameListeners = this.nameCreatedListeners,
                i, ln, listener;

            for (i = 0,ln = listeners.length; i < ln; i++) {
                listener = listeners[i];
                listener.fn.call(listener.scope, className);
            }

            listeners = nameListeners[className];

            if (listeners) {
                for (i = 0,ln = listeners.length; i < ln; i++) {
                    listener = listeners[i];
                    listener.fn.call(listener.scope, className);
                }

                delete nameListeners[className];
            }
        },

        /**
         * @private
         */
        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);
            }
        },

        /**
         * Supports namespace rewriting
         * @private
         */
        parseNamespace: function(namespace) {
            if (typeof namespace != 'string') {
                throw new Error("[Ext.ClassManager] Invalid namespace, must be a string");
            }

            var cache = this.namespaceParseCache;

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

            var parts = [],
                rewrites = this.namespaceRewrites,
                root = global,
                name = namespace,
                rewrite, from, to, i, ln;

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

        /**
         * Creates a namespace and assign the `value` to the created object
         *
         *     Ext.ClassManager.setNamespace('MyCompany.pkg.Example', someObject);
         *     alert(MyCompany.pkg.Example === someObject); // alerts true
         *
         * @param {String} name
         * @param {Mixed} value
         */
        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];
        },

        /**
         * The new Ext.ns, supports namespace rewriting
         * @private
         */
        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;
        },

        /**
         * Sets a name reference to a class.
         *
         * @param {String} name
         * @param {Object} value
         * @return {Ext.ClassManager} this
         */
        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;
        },

        /**
         * Retrieve a class by its name.
         *
         * @param {String} name
         * @return {Ext.Class} class
         */
        get: function(name) {
            var classes = this.classes;

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

            var root = global,
                parts = this.parseNamespace(name),
                part, i, ln;

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

        /**
         * Register the alias for a class.
         *
         * @param {Ext.Class/String} cls a reference to a class or a className
         * @param {String} alias Alias to use when referring to this class
         */
        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) {
                if (aliasToNameMap[alias]) {
                    Ext.Logger.info("[Ext.ClassManager] Overriding existing alias: '" + alias + "' " +
                        "of: '" + aliasToNameMap[alias] + "' with: '" + className + "'. Be sure it's intentional.");
                }

                aliasToNameMap[alias] = className;
            }

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

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

            return this;
        },

        /**
         * Get a reference to the class by its alias.
         *
         * @param {String} alias
         * @return {Ext.Class} class
         */
        getByAlias: function(alias) {
            return this.get(this.getNameByAlias(alias));
        },

        /**
         * Get the name of a class by its alias.
         *
         * @param {String} alias
         * @return {String} className
         */
        getNameByAlias: function(alias) {
            return this.maps.aliasToName[alias] || '';
        },

        /**
         * Get the name of a class by its alternate name.
         *
         * @param {String} alternate
         * @return {String} className
         */
        getNameByAlternate: function(alternate) {
            return this.maps.alternateToName[alternate] || '';
        },

        /**
         * Get the aliases of a class by the class name
         *
         * @param {String} name
         * @return {Array} aliases
         */
        getAliasesByName: function(name) {
            return this.maps.nameToAliases[name] || [];
        },

        /**
         * Get the name of the class by its reference or its instance;
         * usually invoked by the shorthand {@link Ext#getClassName Ext.getClassName}

         *     Ext.ClassManager.getName(Ext.Action); // returns "Ext.Action"

         * @param {Ext.Class/Object} object
         * @return {String} className
         * @markdown
         */
        getName: function(object) {
            return object && object.$className || '';
        },

        /**
         * Get the class of the provided object; returns null if it's not an instance
         * of any class created with Ext.define. This is usually invoked by the shorthand {@link Ext#getClass Ext.getClass}
         *
         *     var component = new Ext.Component();
         *
         *     Ext.ClassManager.getClass(component); // returns Ext.Component
         *
         * @param {Object} object
         * @return {Ext.Class} class
         */
        getClass: function(object) {
            return object && object.self || null;
        },

        /**
         * @private
         */
        applyOverrides: function (name) {
            var me = this,
                overridesByName = me.maps.overridesByName,
                overrides = overridesByName[name],
                length = overrides && overrides.length || 0,
                createOverride = me.createOverride,
                i;

            delete overridesByName[name];

            for (i = 0; i < length; ++i) {
                createOverride.apply(me, overrides[i]);
            }
        },

        /**
         * @private
         */
        create: function(className, data, createdFn) {
            if (typeof className != 'string') {
                throw new Error("[Ext.define] Invalid class name '" + className + "' specified, must be a non-empty string");
            }

            data.$className = className;

            return new Class(data, function() {
                var postprocessorStack = data.postprocessors || Manager.defaultPostprocessors,
                    registeredPostprocessors = Manager.postprocessors,
                    index = 0,
                    postprocessors = [],
                    postprocessor, process, i, ln, j, subLn, postprocessorProperties, postprocessorProperty,
                    alternateNames;

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

                process = function(clsName, cls, clsData) {
                    postprocessor = postprocessors[index++];

                    if (!postprocessor) {
                        Manager.set(className, cls);

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

                        Manager.triggerCreated(className);
                        return;
                    }

                    if (postprocessor.call(this, clsName, cls, clsData, process) !== false) {
                        process.apply(this, arguments);
                    }
                };

                process.call(Manager, className, this, data);

                //TODO: Take this out, hook into classCreated instead
                Manager.applyOverrides(className);
                alternateNames = Manager.maps.nameToAlternates[className];

                for (i = 0, ln = alternateNames && alternateNames.length || 0; i < ln; ++i) {
                    Manager.applyOverrides(alternateNames[i]);
                }
            });
        },

        createOverride: function (overrideName, data, createdFn) {
            var me = this,
                className = data.override,
                cls = me.get(className),
                overrideBody, overridesByName, overrides;

            if (cls) {
                // We use a "faux class" here because it has all the mechanics we need to
                // work with the loader via uses/requires and loader history (for build).
                // This way we don't have to refactor any of the class-loader relationship.

                // hoist any 'requires' or 'uses' from the body onto the faux class:
                overrideBody = Ext.apply({}, data);
                delete overrideBody.requires;
                delete overrideBody.uses;
                delete overrideBody.override;

                me.create(overrideName, {
                        constructor: function () {
                            throw new Error("Cannot create instance of override '" + overrideName + "'");
                        },
                        requires: data.requires,
                        uses: data.uses,
                        override: className
                    }, function () {
                        this.active = true;
                        if (cls.override) { // if (normal class)
                            cls.override(overrideBody);
                        } else { // else (singleton)
                            cls.self.override(overrideBody);
                        }

                        if (createdFn) {
                            // called once the override is applied and with the context of the
                            // overridden class (the override itself is a meaningless, name-only
                            // thing).
                            createdFn.call(cls);
                        }
                    });
            } else {
                // The class is not loaded and may never load, but in case it does we add
                // the override arguments to an internal map keyed by the className. When
                // (or if) the class loads, we will call this method again with those same
                // arguments to complete the override.
                overridesByName = me.maps.overridesByName;
                overrides = overridesByName[className] || (overridesByName[className] = []);
                overrides.push(Array.prototype.slice.call(arguments, 0));

                // place an inactive stub in the namespace (appeases the Loader and could
                // be useful diagnostically)
                me.setNamespace(overrideName, {
                    override: className
                });
            }
        },

        /**
         * Instantiate a class by its alias; usually invoked by the convenient shorthand {@link Ext#createByAlias Ext.createByAlias}
         * If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has not been defined yet, it will
         * attempt to load the class via synchronous loading.
         *
         *     var window = Ext.ClassManager.instantiateByAlias('widget.window', { width: 600, height: 800, ... });
         *
         * @param {String} alias
         * @param {Mixed...} args Additional arguments after the alias will be passed to the class constructor.
         * @return {Object} instance
         */
        instantiateByAlias: function() {
            var alias = arguments[0],
                args = arraySlice.call(arguments),
                className = this.getNameByAlias(alias);

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

                if (!className) {
                    throw new Error("[Ext.createByAlias] Cannot create an instance of unrecognized alias: " + alias);
                }

                Ext.Logger.warn("[Ext.Loader] Synchronously loading '" + className + "'; consider adding " +
                     "Ext.require('" + alias + "') above Ext.onReady");

                Ext.syncRequire(className);
            }

            args[0] = className;

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

        /**
         * Instantiate a class by either full name, alias or alternate name; usually invoked by the convenient
         * shorthand {@link Ext#create Ext.create}
         *
         * If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has not been defined yet, it will
         * attempt to load the class via synchronous loading.
         *
         * For example, all these three lines return the same result:
         *
         *     // alias
         *     var window = Ext.ClassManager.instantiate('widget.window', { width: 600, height: 800, ... });
         *
         *     // alternate name
         *     var window = Ext.ClassManager.instantiate('Ext.Window', { width: 600, height: 800, ... });
         *
         *     // full class name
         *     var window = Ext.ClassManager.instantiate('Ext.window.Window', { width: 600, height: 800, ... });
         *
         * @param {String} name
         * @param {Mixed} args,... Additional arguments after the name will be passed to the class' constructor.
         * @return {Object} instance
         */
        instantiate: function() {
            var name = arguments[0],
                args = arraySlice.call(arguments, 1),
                alias = name,
                possibleName, cls;

            if (typeof name != 'function') {
                if ((typeof name != 'string' || name.length < 1)) {
                    throw new Error("[Ext.create] Invalid class name or alias '" + name + "' specified, must be a non-empty string");
                }

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

            // No record of this class name, it's possibly an alias, so look it up
            if (!cls) {
                possibleName = this.getNameByAlias(name);

                if (possibleName) {
                    name = possibleName;

                    cls = this.get(name);
                }
            }

            // Still no record of this class name, it's possibly an alternate name, so look it up
            if (!cls) {
                possibleName = this.getNameByAlternate(name);

                if (possibleName) {
                    name = possibleName;

                    cls = this.get(name);
                }
            }

            // Still not existing at this point, try to load it via synchronous mode as the last resort
            if (!cls) {
                Ext.Logger.warn("[Ext.Loader] Synchronously loading '" + name + "'; consider adding '" +
                    ((possibleName) ? alias : name) + "' explicitly as a require of the corresponding class");

                Ext.syncRequire(name);

                cls = this.get(name);
            }

            if (!cls) {
                throw new Error("[Ext.create] Cannot create an instance of unrecognized class name / alias: " + alias);
            }

            if (typeof cls != 'function') {
                throw new Error("[Ext.create] '" + name + "' is a singleton and cannot be instantiated");
            }

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

        /**
         * @private
         * @param name
         * @param args
         */
        dynInstantiate: function(name, args) {
            args = arrayFrom(args, true);
            args.unshift(name);

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

        /**
         * @private
         * @param length
         */
        getInstantiator: function(length) {
            var instantiators = this.instantiators,
                instantiator;

            instantiator = instantiators[length];

            if (!instantiator) {
                var i = length,
                    args = [];

                for (i = 0; i < length; i++) {
                    args.push('a[' + i + ']');
                }

                instantiator = instantiators[length] = new Function('c', 'a', 'return new c(' + args.join(',') + ')');
                instantiator.displayName = "Ext.ClassManager.instantiate" + length;
            }

            return instantiator;
        },

        /**
         * @private
         */
        postprocessors: {},

        /**
         * @private
         */
        defaultPostprocessors: [],

        /**
         * Register a post-processor function.
         *
         * @private
         * @param {String} name
         * @param {Function} postprocessor
         */
        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;
        },

        /**
         * Set the default post processors array stack which are applied to every class.
         *
         * @private
         * @param {String/Array} The name of a registered post processor or an array of registered names.
         * @return {Ext.ClassManager} this
         */
        setDefaultPostprocessors: function(postprocessors) {
            this.defaultPostprocessors = arrayFrom(postprocessors);

            return this;
        },

        /**
         * Insert this post-processor at a specific position in the stack, optionally relative to
         * any existing post-processor
         *
         * @private
         * @param {String} name The post-processor name. Note that it needs to be registered with
         * {@link Ext.ClassManager#registerPostprocessor} before this
         * @param {String} offset The insertion position. Four possible values are:
         * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
         * @param {String} relativeName
         * @return {Ext.ClassManager} 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;
        },

        /**
         * Converts a string expression to an array of matching class names. An expression can either refers to class aliases
         * or class names. Expressions support wildcards:
         *
         *      // returns ['Ext.window.Window']
         *     var window = Ext.ClassManager.getNamesByExpression('widget.window');
         *
         *     // returns ['widget.panel', 'widget.window', ...]
         *     var allWidgets = Ext.ClassManager.getNamesByExpression('widget.*');
         *
         *     // returns ['Ext.data.Store', 'Ext.data.ArrayProxy', ...]
         *     var allData = Ext.ClassManager.getNamesByExpression('Ext.data.*');
         *
         * @param {String} expression
         * @return {Array} classNames
         * @markdown
         */
        getNamesByExpression: function(expression) {
            var nameToAliasesMap = this.maps.nameToAliases,
                names = [],
                name, alias, aliases, possibleName, regex, i, ln;

            if (typeof expression != 'string' || expression.length < 1) {
                throw new Error("[Ext.ClassManager.getNamesByExpression] Expression " + expression + " is invalid, must be a non-empty string");
            }

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

    /**
     * @cfg {String[]} alias
     * @member Ext.Class
     * List of short aliases for class names.  Most useful for defining xtypes for widgets:
     *
     *     Ext.define('MyApp.CoolPanel', {
     *         extend: 'Ext.panel.Panel',
     *         alias: ['widget.coolpanel'],
     *         title: 'Yeah!'
     *     });
     *
     *     // Using Ext.create
     *     Ext.widget('widget.coolpanel');
     *     // Using the shorthand for widgets and in xtypes
     *     Ext.widget('panel', {
     *         items: [
     *             {xtype: 'coolpanel', html: 'Foo'},
     *             {xtype: 'coolpanel', html: 'Bar'}
     *         ]
     *     });
     */
    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']);

    /**
     * @cfg {Boolean} singleton
     * @member Ext.Class
     * When set to true, the class will be instantiated as singleton.  For example:
     *
     *     Ext.define('Logger', {
     *         singleton: true,
     *         log: function(msg) {
     *             console.log(msg);
     *         }
     *     });
     *
     *     Logger.log('Hello');
     */
    Manager.registerPostprocessor('singleton', function(name, cls, data, fn) {
        fn.call(this, name, new cls(), data);
        return false;
    });

    /**
     * @cfg {String/String[]} alternateClassName
     * @member Ext.Class
     * Defines alternate names for this class.  For example:
     *
     *     Ext.define('Developer', {
     *         alternateClassName: ['Coder', 'Hacker'],
     *         code: function(msg) {
     *             alert('Typing... ' + msg);
     *         }
     *     });
     *
     *     var joe = Ext.create('Developer');
     *     joe.code('stackoverflow');
     *
     *     var rms = Ext.create('Hacker');
     *     rms.code('hack hack');
     */
    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];

            if (typeof alternate != 'string') {
                throw new Error("[Ext.define] Invalid alternate of: '" + alternate + "' for class: '" + name + "'; must be a valid string");
            }

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

    Ext.apply(Ext, {
        /**
         * Convenient shorthand, see {@link Ext.ClassManager#instantiate}
         * @member Ext
         * @method create
         */
        create: alias(Manager, 'instantiate'),

        /**
         * Convenient shorthand to create a widget by its xtype, also see {@link Ext.ClassManager#instantiateByAlias}
         *
         *     var button = Ext.widget('button'); // Equivalent to Ext.create('widget.button')
         *     var panel = Ext.widget('panel'); // Equivalent to Ext.create('widget.panel')
         *
         * @member Ext
         * @method widget
         */
        widget: function(name) {
            var args = arraySlice.call(arguments);
            args[0] = 'widget.' + name;

            return Manager.instantiateByAlias.apply(Manager, args);
        },

        /**
         * Convenient shorthand, see {@link Ext.ClassManager#instantiateByAlias}
         * @member Ext
         * @method createByAlias
         */
        createByAlias: alias(Manager, 'instantiateByAlias'),

        /**
         * Defines a class or override. A basic class is defined like this:
         *
         *      Ext.define('My.awesome.Class', {
         *          someProperty: 'something',
         *
         *          someMethod: function(s) {
         *              console.log(s + this.someProperty);
         *          }
         *      });
         *
         *      var obj = new My.awesome.Class();
         *
         *      obj.someMethod('Say '); // logs 'Say something' to the console
         *
         * To defines an override, include the `override` property. The content of an
         * override is aggregated with the specified class in order to extend or modify
         * that class. This can be as simple as setting default property values or it can
         * extend and/or replace methods. This can also extend the statics of the class.
         *
         * One use for an override is to break a large class into manageable pieces.
         *
         *      // File: /src/app/Panel.js
         *
         *      Ext.define('My.app.Panel', {
         *          extend: 'Ext.panel.Panel',
         *          requires: [
         *              'My.app.PanelPart2',
         *              'My.app.PanelPart3'
         *          ]
         *
         *          constructor: function (config) {
         *              this.callParent(arguments); // calls Ext.panel.Panel's constructor
         *              //...
         *          },
         *
         *          statics: {
         *              method: function () {
         *                  return 'abc';
         *              }
         *          }
         *      });
         *
         *      // File: /src/app/PanelPart2.js
         *      Ext.define('My.app.PanelPart2', {
         *          override: 'My.app.Panel',
         *
         *          constructor: function (config) {
         *              this.callParent(arguments); // calls My.app.Panel's constructor
         *              //...
         *          }
         *      });
         *
         * Another use for an override is to provide optional parts of classes that can be
         * independently required. In this case, the class may even be unaware of the
         * override altogether.
         *
         *      Ext.define('My.ux.CoolTip', {
         *          override: 'Ext.tip.ToolTip',
         *
         *          constructor: function (config) {
         *              this.callParent(arguments); // calls Ext.tip.ToolTip's constructor
         *              //...
         *          }
         *      });
         *
         * The above override can now be required as normal.
         *
         *      Ext.define('My.app.App', {
         *          requires: [
         *              'My.ux.CoolTip'
         *          ]
         *      });
         *
         * Overrides can also contain statics:
         *
         *      Ext.define('My.app.BarMod', {
         *          override: 'Ext.foo.Bar',
         *
         *          statics: {
         *              method: function (x) {
         *                  return this.callParent([x * 2]); // call Ext.foo.Bar.method
         *              }
         *          }
         *      });
         *
         * IMPORTANT: An override is only included in a build if the class it overrides is
         * required. Otherwise, the override, like the target class, is not included.
         *
         * @param {String} className The class name to create in string dot-namespaced format, for example:
         * 'My.very.awesome.Class', 'FeedViewer.plugin.CoolPager'
         *
         * It is highly recommended to follow this simple convention:
         *  - The root and the class name are 'CamelCased'
         *  - Everything else is lower-cased
         *
         * @param {Object} data The key - value pairs of properties to apply to this class. Property names can be of
         * any valid strings, except those in the reserved listed below:
         *
         *  - `mixins`
         *  - `statics`
         *  - `config`
         *  - `alias`
         *  - `self`
         *  - `singleton`
         *  - `alternateClassName`
         *  - `override`
         *
         * @param {Function} createdFn Optional callback to execute after the class (or override)
         * is created. The execution scope (`this`) will be the newly created class itself.
         * @return {Ext.Base}
         *
         * @member Ext
         * @method define
         */
        define: function (className, data, createdFn) {
            if (data.override) {
                return Manager.createOverride.apply(Manager, arguments);
            }

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

        /**
         * Convenient shorthand, see {@link Ext.ClassManager#getName}
         * @member Ext
         * @method getClassName
         */
        getClassName: alias(Manager, 'getName'),

        /**
         *
         * @param {Mixed} object
         */
        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';
        },

        /**
         * Convenient shorthand, see {@link Ext.ClassManager#getClass}
         * @member Ext
         * @method getClass
         */
        getClass: alias(Manager, 'getClass'),

        /**
         * Creates namespaces to be used for scoping variables and classes so that they are not global.
         * Specifying the last node of a namespace implicitly creates all other nodes. Usage:
         *
         *     Ext.namespace('Company', 'Company.data');
         *
         *      // equivalent and preferable to the above syntax
         *     Ext.namespace('Company.data');
         *
         *     Company.Widget = function() { ... };
         *
         *     Company.data.CustomStore = function(config) { ... };
         *
         * @param {String} namespace1
         * @param {String} namespace2
         * @param {String} etc
         * @return {Object} The namespace object. (If multiple arguments are passed, this will be the last namespace created)
         * @function
         * @member Ext
         * @method namespace
         */
        namespace: alias(Manager, 'createNamespaces')
    });

    /**
     * Old name for {@link Ext#widget}.
     * @deprecated 4.0.0 Please use {@link Ext#widget} instead.
     * @method createWidget
     * @member Ext
     */
    Ext.createWidget = Ext.widget;

    /**
     * Convenient alias for {@link Ext#namespace Ext.namespace}
     * @member Ext
     * @method ns
     */
    Ext.ns = Ext.namespace;

    Class.registerPreprocessor('className', function(cls, data) {
        if (data.$className) {
            cls.$className = data.$className;
            cls.displayName = cls.$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 (typeof alias != 'string' || alias.length < 1) {
                throw new Error("[Ext.define] Invalid alias of: '" + alias + "' for class: '" + name + "'; must be a valid string");
            }

            if (alias.substring(0, widgetPrefixLength) === widgetPrefix) {
                xtype = alias.substring(widgetPrefixLength);
                Ext.Array.include(xtypes, xtype);
            }
        }

        cls.xtype = data.xtype = xtypes[0];
        data.xtypes = xtypes;

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

            if (!xtypesMap[xtype]) {
                xtypesMap[xtype] = true;
                xtypesChain.push(xtype);
            }
        }

        data.xtypesChain = xtypesChain;
        data.xtypesMap = xtypesMap;

        Ext.Function.interceptAfter(data, 'onClassCreated', function() {
            var mixins = prototype.mixins,
                key, mixin;

            for (key in mixins) {
                if (mixins.hasOwnProperty(key)) {
                    mixin = mixins[key];

                    xtypes = mixin.xtypes;

                    if (xtypes) {
                        for (i = 0,ln = xtypes.length; i < ln; i++) {
                            xtype = xtypes[i];

                            if (!xtypesMap[xtype]) {
                                xtypesMap[xtype] = true;
                                xtypesChain.push(xtype);
                            }
                        }
                    }
                }
            }
        });

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

            if (typeof xtype != 'string' || xtype.length < 1) {
                throw new Error("[Ext.define] Invalid xtype of: '" + xtype + "' for class: '" + name + "'; must be a valid non-empty string");
            }

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

        data.alias = aliases;

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

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

/**
 * @author Jacky Nguyen <jacky@sencha.com>
 * @docauthor Jacky Nguyen <jacky@sencha.com>
 * @class Ext.Loader
 *

Ext.Loader is the heart of the new dynamic dependency loading capability in Ext JS 4+. It is most commonly used
via the {@link Ext#require} shorthand. Ext.Loader supports both asynchronous and synchronous loading
approaches, and leverage their advantages for the best development flow. We'll discuss about the pros and cons of each approach:

# Asynchronous Loading #

- Advantages:
	+ Cross-domain
	+ No web server needed: you can run the application via the file system protocol (i.e: `file://path/to/your/index
 .html`)
	+ Best possible debugging experience: error messages come with the exact file name and line number

- Disadvantages:
	+ Dependencies need to be specified before-hand

### Method 1: Explicitly include what you need: ###

    // Syntax
    Ext.require({String/Array} expressions);

    // Example: Single alias
    Ext.require('widget.window');

    // Example: Single class name
    Ext.require('Ext.window.Window');

    // Example: Multiple aliases / class names mix
    Ext.require(['widget.window', 'layout.border', 'Ext.data.Connection']);

    // Wildcards
    Ext.require(['widget.*', 'layout.*', 'Ext.data.*']);

### Method 2: Explicitly exclude what you don't need: ###

    // Syntax: Note that it must be in this chaining format.
    Ext.exclude({String/Array} expressions)
       .require({String/Array} expressions);

    // Include everything except Ext.data.*
    Ext.exclude('Ext.data.*').require('*');

    // Include all widgets except widget.checkbox*,
    // which will match widget.checkbox, widget.checkboxfield, widget.checkboxgroup, etc.
    Ext.exclude('widget.checkbox*').require('widget.*');

# Synchronous Loading on Demand #

- *Advantages:*
	+ There's no need to specify dependencies before-hand, which is always the convenience of including ext-all.js
 before

- *Disadvantages:*
	+ Not as good debugging experience since file name won't be shown (except in Firebug at the moment)
	+ Must be from the same domain due to XHR restriction
	+ Need a web server, same reason as above

There's one simple rule to follow: Instantiate everything with Ext.create instead of the `new` keyword

    Ext.create('widget.window', { ... }); // Instead of new Ext.window.Window({...});

    Ext.create('Ext.window.Window', {}); // Same as above, using full class name instead of alias

    Ext.widget('window', {}); // Same as above, all you need is the traditional `xtype`

Behind the scene, {@link Ext.ClassManager} will automatically check whether the given class name / alias has already
 existed on the page. If it's not, Ext.Loader will immediately switch itself to synchronous mode and automatic load the given
 class and all its dependencies.

# Hybrid Loading - The Best of Both Worlds #

It has all the advantages combined from asynchronous and synchronous loading. The development flow is simple:

### Step 1: Start writing your application using synchronous approach. Ext.Loader will automatically fetch all
 dependencies on demand as they're needed during run-time. For example: ###

    Ext.onReady(function(){
        var window = Ext.createWidget('window', {
            width: 500,
            height: 300,
            layout: {
                type: 'border',
                padding: 5
            },
            title: 'Hello Dialog',
            items: [{
                title: 'Navigation',
                collapsible: true,
                region: 'west',
                width: 200,
                html: 'Hello',
                split: true
            }, {
                title: 'TabPanel',
                region: 'center'
            }]
        });

        window.show();
    })

### Step 2: Along the way, when you need better debugging ability, watch the console for warnings like these: ###

    [Ext.Loader] Synchronously loading 'Ext.window.Window'; consider adding Ext.require('Ext.window.Window') before your application's code
    ClassManager.js:432
    [Ext.Loader] Synchronously loading 'Ext.layout.container.Border'; consider adding Ext.require('Ext.layout.container.Border') before your application's code

Simply copy and paste the suggested code above `Ext.onReady`, i.e:

    Ext.require('Ext.window.Window');
    Ext.require('Ext.layout.container.Border');

    Ext.onReady(...);

Everything should now load via asynchronous mode.

# Deployment #

It's important to note that dynamic loading should only be used during development on your local machines.
During production, all dependencies should be combined into one single JavaScript file. Ext.Loader makes
the whole process of transitioning from / to between development / maintenance and production as easy as
possible. Internally {@link Ext.Loader#history Ext.Loader.history} maintains the list of all dependencies your application
needs in the exact loading sequence. It's as simple as concatenating all files in this array into one,
then include it on top of your application.

This process will be automated with Sencha Command, to be released and documented towards Ext JS 4 Final.

 * @singleton
 * @markdown
 */

(function(Manager, Class, flexSetter, alias, pass, arrayFrom, arrayErase, arrayInclude) {

    var
        dependencyProperties = ['extend', 'mixins', 'requires'],
        Loader;

    Loader = Ext.Loader = {

        /**
         * @private
         */
        isInHistory: {},

        /**
         * An array of class names to keep track of the dependency loading order.
         * This is not guaranteed to be the same everytime due to the asynchronous
         * nature of the Loader.
         *
         * @property history
         * @type Array
         */
        history: [],

        /**
         * Configuration
         * @private
         */
        config: {
            /**
             * Whether or not to enable the dynamic dependency loading feature
             * Defaults to false
             * @cfg {Boolean} enabled
             */
            enabled: false,

            /**
             * @cfg {Boolean} disableCaching
             * Appends current timestamp to script files to prevent caching
             * Defaults to true
             */
            disableCaching: true,

            /**
             * @cfg {String} disableCachingParam
             * The get parameter name for the cache buster's timestamp.
             * Defaults to '_dc'
             */
            disableCachingParam: '_dc',

            /**
             * @cfg {Object} paths
             * The mapping from namespaces to file paths
    {
        'Ext': '.', // This is set by default, Ext.layout.container.Container will be
                    // loaded from ./layout/Container.js

        'My': './src/my_own_folder' // My.layout.Container will be loaded from
                                    // ./src/my_own_folder/layout/Container.js
    }
             * Note that all relative paths are relative to the current HTML document.
             * If not being specified, for example, <code>Other.awesome.Class</code>
             * will simply be loaded from <code>./Other/awesome/Class.js</code>
             */
            paths: {
                'Ext': '.'
            }
        },

        /**
         * Set the configuration for the loader. This should be called right after ext-(debug).js
         * is included in the page, and before Ext.onReady. i.e:

    <script type="text/javascript" src="ext-core-debug.js"></script>
    <script type="text/javascript">
        Ext.Loader.setConfig({
          enabled: true,
          paths: {
              'My': 'my_own_path'
          }
        });
    <script>
    <script type="text/javascript">
        Ext.require(...);

        Ext.onReady(function() {
          // application code here
        });
    </script>

         * Refer to config options of {@link Ext.Loader} for the list of possible properties
         *
         * @param {Object} config The config object to override the default values
         * @return {Ext.Loader} this
         * @markdown
         */
        setConfig: function(name, value) {
            if (Ext.isObject(name) && arguments.length === 1) {
                Ext.merge(this.config, name);
            }
            else {
                this.config[name] = (Ext.isObject(value)) ? Ext.merge(this.config[name], value) : value;
            }

            return this;
        },

        /**
         * Get the config value corresponding to the specified name. If no name is given, will return the config object
         * @param {String} name The config property name
         * @return {Object/Mixed}
         */
        getConfig: function(name) {
            if (name) {
                return this.config[name];
            }

            return this.config;
        },

        /**
         * Sets the path of a namespace.
         * For Example:

    Ext.Loader.setPath('Ext', '.');

         * @param {String/Object} name See {@link Ext.Function#flexSetter flexSetter}
         * @param {String} path See {@link Ext.Function#flexSetter flexSetter}
         * @return {Ext.Loader} this
         * @method
         * @markdown
         */
        setPath: flexSetter(function(name, path) {
            this.config.paths[name] = path;

            return this;
        }),

        /**
         * Translates a className to a file path by adding the
         * the proper prefix and converting the .'s to /'s. For example:

    Ext.Loader.setPath('My', '/path/to/My');

    alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/path/to/My/awesome/Class.js'

         * Note that the deeper namespace levels, if explicitly set, are always resolved first. For example:

    Ext.Loader.setPath({
        'My': '/path/to/lib',
        'My.awesome': '/other/path/for/awesome/stuff',
        'My.awesome.more': '/more/awesome/path'
    });

    alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/other/path/for/awesome/stuff/Class.js'

    alert(Ext.Loader.getPath('My.awesome.more.Class')); // alerts '/more/awesome/path/Class.js'

    alert(Ext.Loader.getPath('My.cool.Class')); // alerts '/path/to/lib/cool/Class.js'

    alert(Ext.Loader.getPath('Unknown.strange.Stuff')); // alerts 'Unknown/strange/Stuff.js'

         * @param {String} className
         * @return {String} path
         * @markdown
         */
        getPath: function(className) {
            var path = '',
                paths = this.config.paths,
                prefix = this.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(/\/\.\//g, '/') + className.replace(/\./g, "/") + '.js';
        },

        /**
         * @private
         * @param {String} className
         */
        getPrefix: function(className) {
            var paths = this.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;
        },

        /**
         * Loads all classes by the given names and all their direct dependencies; optionally executes the given callback function when
         * finishes, within the optional scope. This method is aliased by {@link Ext#require Ext.require} for convenience
         * @param {String/Array} expressions Can either be a string or an array of string
         * @param {Function} fn (Optional) The callback function
         * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
         * @param {String/Array} excludes (Optional) Classes to be excluded, useful when being used with expressions
         * @markdown
         */
        require: function(expressions, fn, scope, excludes) {
            if (fn) {
                fn.call(scope);
            }
        },

        /**
         * Synchronously loads all classes by the given names and all their direct dependencies; optionally executes the given callback function when finishes, within the optional scope. This method is aliased by {@link Ext#syncRequire} for convenience
         * @param {String/Array} expressions Can either be a string or an array of string
         * @param {Function} fn (Optional) The callback function
         * @param {Object} scope (Optional) The execution scope (`this`) of the callback function
         * @param {String/Array} excludes (Optional) Classes to be excluded, useful when being used with expressions
         * @markdown
         */
        syncRequire: function() {},

        /**
         * Explicitly exclude files from being loaded. Useful when used in conjunction with a broad include expression.
         * Can be chained with more `require` and `exclude` methods, eg:

    Ext.exclude('Ext.data.*').require('*');

    Ext.exclude('widget.button*').require('widget.*');

         * @param {Array} excludes
         * @return {Object} object contains `require` method for chaining
         * @markdown
         */
        exclude: function(excludes) {
            var me = this;

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

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

        /**
         * Add a new listener to be executed when all required scripts are fully loaded
         *
         * @param {Function} fn The function callback to be executed
         * @param {Object} scope The execution scope (<code>this</code>) of the callback function
         * @param {Boolean} withDomReady Whether or not to wait for document dom ready as well
         */
        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);
        }
    };

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

        /**
         * Flag indicating whether there are still files being loaded
         * @private
         */
        isLoading: false,

        /**
         * Maintain the queue for all dependencies. Each item in the array is an object of the format:
         * {
         *      requires: [...], // The required classes for this queue item
         *      callback: function() { ... } // The function to execute when all classes specified in requires exist
         * }
         * @private
         */
        queue: [],

        /**
         * Maintain the list of files that have already been handled so that they never get double-loaded
         * @private
         */
        isClassFileLoaded: {},

        /**
         * @private
         */
        isFileLoaded: {},

        /**
         * Maintain the list of listeners to execute when all required scripts are fully loaded
         * @private
         */
        readyListeners: [],

        /**
         * Contains optional dependencies to be loaded last
         * @private
         */
        optionalRequires: [],

        /**
         * Map of fully qualified class names to an array of dependent classes.
         * @private
         */
        requiresMap: {},

        /**
         * @private
         */
        numPendingFiles: 0,

        /**
         * @private
         */
        numLoadedFiles: 0,

        /** @private */
        hasFileLoadError: false,

        /**
         * @private
         */
        classNameToFilePathMap: {},

        /**
         * @private
         */
        syncModeEnabled: false,

        scriptElements: {},

        /**
         * Refresh all items in the queue. If all dependencies for an item exist during looping,
         * it will execute the callback and call refreshQueue again. Triggers onReady when the queue is
         * empty
         * @private
         */
        refreshQueue: function() {
            var queue = this.queue,
                ln = queue.length,
                i, item, j, requires, references;

            if (ln === 0) {
                this.triggerReady();
                return;
            }

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

                if (item) {
                    requires = item.requires;
                    references = item.references;

                    // Don't bother checking when the number of files loaded
                    // is still less than the array length
                    if (requires.length > this.numLoadedFiles) {
                        continue;
                    }

                    j = 0;

                    do {
                        if (Manager.isCreated(requires[j])) {
                            // Take out from the queue
                            arrayErase(requires, j, 1);
                        }
                        else {
                            j++;
                        }
                    } while (j < requires.length);

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

            return this;
        },

        /**
         * Inject a script element to document's head, call onLoad and onError accordingly
         * @private
         */
        injectScriptElement: function(url, onLoad, onError, scope) {
            var script = document.createElement('script'),
                me = this,
                onLoadFn = function() {
                    me.cleanupScriptElement(script);
                    onLoad.call(scope);
                },
                onErrorFn = function() {
                    me.cleanupScriptElement(script);
                    onError.call(scope);
                };

            script.type = 'text/javascript';
            script.src = url;
            script.onload = onLoadFn;
            script.onerror = onErrorFn;
            script.onreadystatechange = function() {
                if (this.readyState === 'loaded' || this.readyState === 'complete') {
                    onLoadFn();
                }
            };

            this.documentHead.appendChild(script);

            return script;
        },

        removeScriptElement: function(url) {
            var scriptElements = this.scriptElements;

            if (scriptElements[url]) {
                this.cleanupScriptElement(scriptElements[url], true);
                delete scriptElements[url];
            }

            return this;
        },

        /**
         * @private
         */
        cleanupScriptElement: function(script, remove) {
            script.onload = null;
            script.onreadystatechange = null;
            script.onerror = null;

            if (remove) {
                this.documentHead.removeChild(script);
            }

            return this;
        },

        /**
         * Load a script file, supports both asynchronous and synchronous approaches
         *
         * @param {String} url
         * @param {Function} onLoad
         * @param {Object} scope
         * @param {Boolean} synchronous
         * @private
         */
        loadScriptFile: function(url, onLoad, onError, scope, synchronous) {
            var me = this,
                isFileLoaded = this.isFileLoaded,
                scriptElements = this.scriptElements,
                noCacheUrl = url + (this.getConfig('disableCaching') ? ('?' + this.getConfig('disableCachingParam') + '=' + Ext.Date.now()) : ''),
                isCrossOriginRestricted = false,
                xhr, status, onScriptError;

            if (isFileLoaded[url]) {
                return this;
            }

            scope = scope || this;

            this.isLoading = true;

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

                if (!Ext.isReady && Ext.onDocumentReady) {
                    Ext.onDocumentReady(function() {
                        if (!isFileLoaded[url]) {
                            scriptElements[url] = me.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
                        }
                    });
                }
                else {
                    scriptElements[url] = this.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;

                if (!isCrossOriginRestricted) {
                    isCrossOriginRestricted = (status === 0);
                }

                if (isCrossOriginRestricted
                ) {
                    onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; It's likely that the file is either " +
                                       "being loaded from a different domain or from the local file system whereby cross origin " +
                                       "requests are not allowed due to security reasons. Use asynchronous loading with " +
                                       "Ext.require instead.", synchronous);
                }
                else if (status >= 200 && status < 300
                ) {
                    // Debugger friendly, file names are still shown even though they're eval'ed code
                    // Breakpoints work on both Firebug and Chrome's Web Inspector
                    Ext.globalEval(xhr.responseText + "\n//@ sourceURL=" + url);

                    onLoad.call(scope);
                }
                else {
                    onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; please " +
                                       "verify that the file exists. " +
                                       "XHR status code: " + status, synchronous);
                }

                // Prevent potential IE memory leak
                xhr = null;
            }
        },

        // documented above
        syncRequire: function() {
            var syncModeEnabled = this.syncModeEnabled;

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

            this.require.apply(this, arguments);

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

            this.refreshQueue();
        },

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

            if (excludes) {
                excludes = arrayFrom(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 = arrayFrom(expressions);

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

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

                        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 the dynamic dependency feature is not being used, throw an error
            // if the dependencies are not defined
            if (classNames.length > 0) {
                if (!this.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 this;
            }

            syncModeEnabled = this.syncModeEnabled;

            if (!syncModeEnabled) {
                queue.push({
                    requires: classNames.slice(), // this array will be modified as the queue is processed,
                                                  // so we need a copy of it
                    callback: callback,
                    scope: scope
                });
            }

            ln = classNames.length;

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

                filePath = this.getPath(className);

                // If we are synchronously loading a file that has already been asychronously loaded before
                // we need to destroy the script tag and revert the count
                // This file will then be forced loaded in synchronous
                if (syncModeEnabled && isClassFileLoaded.hasOwnProperty(className)) {
                    this.numPendingFiles--;
                    this.removeScriptElement(filePath);
                    delete isClassFileLoaded[className];
                }

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

                    classNameToFilePathMap[className] = filePath;

                    this.numPendingFiles++;

                    this.loadScriptFile(
                        filePath,
                        pass(this.onFileLoaded, [className, filePath], this),
                        pass(this.onFileLoadError, [className, filePath]),
                        this,
                        syncModeEnabled
                    );
                }
            }

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

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

            return this;
        },

        /**
         * @private
         * @param {String} className
         * @param {String} filePath
         */
        onFileLoaded: function(className, filePath) {
            this.numLoadedFiles++;

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

            this.numPendingFiles--;

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

            if (!this.syncModeEnabled && this.numPendingFiles === 0 && this.isLoading && !this.hasFileLoadError) {
                var queue = this.queue,
                    missingClasses = [],
                    missingPaths = [],
                    requires,
                    i, ln, j, subLn;

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

                    for (j = 0,subLn = requires.length; j < subLn; j++) {
                        if (this.isClassFileLoaded[requires[j]]) {
                            missingClasses.push(requires[j]);
                        }
                    }
                }

                if (missingClasses.length < 1) {
                    return;
                }

                missingClasses = Ext.Array.filter(Ext.Array.unique(missingClasses), function(item) {
                    return !this.requiresMap.hasOwnProperty(item);
                }, this);

                for (i = 0,ln = missingClasses.length; i < ln; i++) {
                    missingPaths.push(this.classNameToFilePathMap[missingClasses[i]]);
                }

                throw new Error("The following classes are not declared even if their files have been " +
                            "loaded: '" + missingClasses.join("', '") + "'. Please check the source code of their " +
                            "corresponding files for possible typos: '" + missingPaths.join("', '"));
            }
        },

        /**
         * @private
         */
        onFileLoadError: function(className, filePath, errorMessage, isSynchronous) {
            this.numPendingFiles--;
            this.hasFileLoadError = true;

            throw new Error("[Ext.Loader] " + errorMessage);
        },

        /**
         * @private
         */
        addOptionalRequires: function(requires) {
            var optionalRequires = this.optionalRequires,
                i, ln, require;

            requires = arrayFrom(requires);

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

                arrayInclude(optionalRequires, require);
            }

            return this;
        },

        /**
         * @private
         */
        triggerReady: function(force) {
            var readyListeners = this.readyListeners,
                optionalRequires = this.optionalRequires,
                listener;

            if (this.isLoading || force) {
                this.isLoading = false;

                if (optionalRequires.length !== 0) {
                    // Clone then empty the array to eliminate potential recursive loop issue
                    optionalRequires = optionalRequires.slice();

                    // Empty the original array
                    this.optionalRequires.length = 0;

                    this.require(optionalRequires, pass(this.triggerReady, [true], this), this);
                    return this;
                }

                while (readyListeners.length) {
                    listener = readyListeners.shift();
                    listener.fn.call(listener.scope);

                    if (this.isLoading) {
                        return this;
                    }
                }
            }

            return this;
        },

        // duplicate definition (documented above)
        onReady: function(fn, scope, withDomReady, options) {
            var oldFn;

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

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

            if (!this.isLoading) {
                fn.call(scope);
            }
            else {
                this.readyListeners.push({
                    fn: fn,
                    scope: scope
                });
            }
        },

        /**
         * @private
         * @param {String} className
         */
        historyPush: function(className) {
            var isInHistory = this.isInHistory;

            if (className && this.isClassFileLoaded.hasOwnProperty(className) && !isInHistory[className]) {
                isInHistory[className] = true;
                this.history.push(className);
            }

            return this;
        }
    });


    /**
     * Convenient alias of {@link Ext.Loader#require}. Please see the introduction documentation of
     * {@link Ext.Loader} for examples.
     * @member Ext
     * @method require
     */
    Ext.require = alias(Loader, 'require');

    /**
     * Synchronous version of {@link Ext#require}, convenient alias of {@link Ext.Loader#syncRequire}.
     *
     * @member Ext
     * @method syncRequire
     */
    Ext.syncRequire = alias(Loader, 'syncRequire');

    /**
     * Convenient shortcut to {@link Ext.Loader#exclude}
     * @member Ext
     * @method exclude
     */
    Ext.exclude = alias(Loader, 'exclude');

    /**
     * @member Ext
     * @method onReady
     */
    Ext.onReady = function(fn, scope, options) {
        Loader.onReady(fn, scope, true, options);
    };

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

        /*
        Loop through the dependencyProperties, look for string class names and push
        them into a stack, regardless of whether the property's value is a string, array or object. For example:
        {
              extend: 'Ext.MyClass',
              requires: ['Ext.some.OtherClass'],
              mixins: {
                  observable: 'Ext.util.Observable';
              }
        }
        which will later be transformed into:
        {
              extend: Ext.MyClass,
              requires: [Ext.some.OtherClass],
              mixins: {
                  observable: Ext.util.Observable;
              }
        }
        */

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

        var deadlockPath = [],
            requiresMap = Loader.requiresMap,
            detectDeadlock;

        /*
        Automatically detect deadlocks before-hand,
        will throw an error with detailed path for ease of debugging. Examples of deadlock cases:

        - A extends B, then B extends A
        - A requires B, B requires C, then C requires A

        The detectDeadlock function will recursively transverse till the leaf, hence it can detect deadlocks
        no matter how deep the path is.
        */

        if (className) {
            requiresMap[className] = dependencies;
            if (!Loader.requiredByMap) Loader.requiredByMap = {};
            Ext.Array.each(dependencies, function(dependency){
                if (!Loader.requiredByMap[dependency]) Loader.requiredByMap[dependency] = [];
                Loader.requiredByMap[dependency].push(className);
            });
            detectDeadlock = function(cls) {
                deadlockPath.push(cls);

                if (requiresMap[cls]) {
                    if (Ext.Array.contains(requiresMap[cls], className)) {
                        throw new Error("Deadlock detected while loading dependencies! '" + className + "' and '" +
                                deadlockPath[1] + "' " + "mutually require each other. Path: " +
                                deadlockPath.join(' -> ') + " -> " + deadlockPath[0]);
                    }

                    for (i = 0,ln = requiresMap[cls].length; i < ln; i++) {
                        detectDeadlock(requiresMap[cls][i]);
                    }
                }
            };

            detectDeadlock(className);
        }


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

    /**
     * @cfg {String[]} uses
     * @member Ext.Class
     * List of optional classes to load together with this class. These aren't neccessarily loaded before
     * this class is created, but are guaranteed to be available before Ext.onReady listeners are
     * invoked
     */
    Manager.registerPostprocessor('uses', function(name, cls, data) {
        var uses = arrayFrom(data.uses),
            items = [],
            i, ln, item;

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

            if (typeof item == 'string') {
                items.push(item);
            }
        }

        Loader.addOptionalRequires(items);
    });

    Manager.onCreated(function(className) {
        this.historyPush(className);
    }, Loader);

})(Ext.ClassManager, Ext.Class, Ext.Function.flexSetter, Ext.Function.alias,
   Ext.Function.pass, Ext.Array.from, Ext.Array.erase, Ext.Array.include);



/*

This file is part of Sencha Touch 2

Copyright (c) 2012 Sencha Inc

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

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

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

*/
/**
 * @class Ext.EventManager
 *
 * This object has been deprecated in Sencha Touch 2.0.0. Please refer to the method documentation for specific alternatives.
 *
 * @deprecated 2.0.0
 * @singleton
 * @private
 */


/**
 * @class Ext
 *
 * Ext is the global namespace for the whole Sencha Touch framework. Every class, function and configuration for the
 * whole framework exists under this single global variable. The Ext singleton itself contains a set of useful helper
 * functions (like {@link #apply}, {@link #min} and others), but most of the framework that you use day to day exists
 * in specialized classes (for example {@link Ext.Panel}, {@link Ext.Carousel} and others).
 *
 * If you are new to Sencha Touch we recommend starting with the [Getting Started Guide][getting_started] to
 * get a feel for how the framework operates. After that, use the more focused guides on subjects like panels, forms and data
 * to broaden your understanding. The MVC guides take you through the process of building full applications using the
 * framework, and detail how to deploy them to production.
 *
 * The functions listed below are mostly utility functions used internally by many of the classes shipped in the
 * framework, but also often useful in your own apps.
 *
 * A method that is crucial to beginning your application is {@link #setup Ext.setup}. Please refer to it's documentation, or the
 * [Getting Started Guide][getting_started] as a reference on beginning your application.
 *
 *     Ext.setup({
 *         onReady: function() {
 *             Ext.Viewport.add({
 *                 xtype: 'component',
 *                 html: 'Hello world!'
 *             });
 *         }
 *     });
 *
 * [getting_started]: #!/guide/getting_started
 */
Ext.setVersion('touch', '2.0.0.rc');

Ext.apply(Ext, {
    /**
     * The version of the framework
     * @type String
     */
    version: Ext.getVersion('touch'),

    /**
     * @private
     */
    idSeed: 0,

    /**
     * Repaints the whole page. This fixes frequently encountered painting issues in mobile Safari.
     */
    repaint: function() {
        var mask = Ext.getBody().createChild({
            cls: Ext.baseCSSPrefix + 'mask ' + Ext.baseCSSPrefix + 'mask-transparent'
        });
        setTimeout(function() {
            mask.destroy();
        }, 0);
    },

    /**
     * Generates unique ids. If the element already has an id, it is unchanged
     * @param {Mixed} el (optional) The element to generate an id for
     * @param {String} prefix (optional) Id prefix (defaults "ext-gen")
     * @return {String} The generated Id.
     */
    id: function(el, prefix) {
        if (el && el.id) {
            return el.id;
        }

        el = Ext.getDom(el) || {};

        if (el === document || el === document.documentElement) {
            el.id = 'ext-application';
        }
        else if (el === document.body) {
            el.id = 'ext-viewport';
        }
        else if (el === window) {
            el.id = 'ext-window';
        }

        el.id = el.id || ((prefix || 'ext-element-') + (++Ext.idSeed));

        return el.id;
    },

    /**
     * Returns the current document body as an {@link Ext.Element}.
     * @return Ext.Element The document body
     */
    getBody: function() {
        if (!Ext.documentBodyElement) {
            if (!document.body) {
                throw new Error("[Ext.getBody] document.body does not exist at this point");
            }

            Ext.documentBodyElement = Ext.get(document.body);
        }

        return Ext.documentBodyElement;
    },

    /**
     * Returns the current document head as an {@link Ext.Element}.
     * @return Ext.Element The document head
     */
    getHead: function() {
        if (!Ext.documentHeadElement) {
            Ext.documentHeadElement = Ext.get(document.head || document.getElementsByTagName('head')[0]);
        }

        return Ext.documentHeadElement;
    },

    /**
     * Returns the current HTML document object as an {@link Ext.Element}.
     * @return Ext.Element The document
     */
    getDoc: function() {
        if (!Ext.documentElement) {
            Ext.documentElement = Ext.get(document);
        }

        return Ext.documentElement;
    },

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

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

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

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

            if (item) {
                if (Ext.isArray(item)) {
                    this.destroy.apply(this, item);
                }
                else if (Ext.isFunction(item.destroy)) {
                    item.destroy();
                }
            }
        }
    },

     /**
      * Return the dom node for the passed String (id), dom node, or Ext.Element.
      * Here are some examples:
      * <pre><code>
// gets dom node based on id
var elDom = Ext.getDom('elId');
// gets dom node based on the dom node
var elDom1 = Ext.getDom(elDom);

// If we don&#39;t know if we are working with an
// Ext.Element or a dom node use Ext.getDom
function(el){
 var dom = Ext.getDom(el);
 // do something with the dom node
}
       </code></pre>
     * <b>Note</b>: the dom node to be found actually needs to exist (be rendered, etc)
     * when this method is called to be successful.
     * @param {Mixed} el
     * @return HTMLElement
     */
    getDom: function(el) {
        if (!el || !document) {
            return null;
        }

        return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
    },

    /**
     * <p>Removes this element from the document, removes all DOM event listeners, and deletes the cache reference.
     * All DOM event listeners are removed from this element.
     * @param {HTMLElement} node The node to remove
     */
    removeNode: function(node) {
        if (node && node.parentNode && node.tagName != 'BODY') {
            Ext.get(node).clearListeners();
            node.parentNode.removeChild(node);
            delete Ext.cache[node.id];
        }
    },

    /**
     * @private
     */
    defaultSetupConfig: {
        eventPublishers: {
            dom: {
                xclass: 'Ext.event.publisher.Dom'
            },
            touchGesture: {
                xclass: 'Ext.event.publisher.TouchGesture',
                recognizers: {
                    drag: {
                        xclass: 'Ext.event.recognizer.Drag'
                    },
                    tap: {
                        xclass: 'Ext.event.recognizer.Tap'
                    },
                    doubleTap: {
                        xclass: 'Ext.event.recognizer.DoubleTap'
                    },
                    longPress: {
                        xclass: 'Ext.event.recognizer.LongPress'
                    },
                    swipe: {
                        xclass: 'Ext.event.recognizer.HorizontalSwipe'
                    },
                    pinch: {
                        xclass: 'Ext.event.recognizer.Pinch'
                    },
                    rotate: {
                        xclass: 'Ext.event.recognizer.Rotate'
                    }
                }
            },
            componentDelegation: {
                xclass: 'Ext.event.publisher.ComponentDelegation'
            },
            componentPaint: {
                xclass: 'Ext.event.publisher.ComponentPaint'
            },
            componentSize: {
                xclass: 'Ext.event.publisher.ComponentSize'
            }
        },

        logger: {
            enabled: true,
            xclass: 'Ext.log.Logger',
            minPriority: 'deprecate',
            writers: {
                console: {
                    xclass: 'Ext.log.writer.Console',
                    throwOnErrors: true,
                    formatter: {
                        xclass: 'Ext.log.formatter.Default'
                    }
                }
            }
        },

        animator: {
            xclass: 'Ext.fx.Runner'
        },

        viewport: {
            xclass: 'Ext.viewport.Viewport'
        }
    },

    /**
     * @private
     */
    isSetup: false,

    /**
     * @private
     */
    setupListeners: [],

    /**
     * @private
     */
    onSetup: function(fn, scope) {
        if (Ext.isSetup) {
            fn.call(scope);
        }
        else {
            Ext.setupListeners.push({
                fn: fn,
                scope: scope
            });
        }
    },

    /**
     * Ext.setup is used to launch a basic application. It handles creating an {@link Ext.Viewport} instance for you.
     *
     *     Ext.setup({
     *         onReady: function() {
     *             Ext.Viewport.add({
     *                 xtype: 'component',
     *                 html: 'Hello world!'
     *             });
     *         }
     *     });
     *
     * @param {Object} config An object with the following config options:
     *
     * @param {Function} config.onReady
     * A function to be called when the application is ready. Your application logic should be here. Please see the example above.
     *
     * @param {Object} config.viewport
     * An object to be used when creating the global {@link Ext.Viewport} instance. Please refer to the {@link Ext.Viewport}
     * documentation for more information.
     *
     *     Ext.setup({
     *         viewport: {
     *             layout: 'vbox'
     *         },
     *         onReady: function() {
     *             Ext.Viewport.add({
     *                 flex: 1,
     *                 html: 'top (flex: 1)'
     *             });
     *
     *             Ext.Viewport.add({
     *                 flex: 4,
     *                 html: 'bottom (flex: 4)'
     *             });
     *         }
     *     });
     *
     * @param {String/Object} config.icon
     * A icon configuration for this application. This will only apply to iOS applications which are saved to the homescreen.
     *
     * You can either pass a string which will be applied to all different sizes:
     *
     *     Ext.setup({
     *         icon: 'icon.png',
     *         onReady: function() {
     *             console.log('Launch...');
     *         }
     *     });
     *
     * Or an object which has a location for different sizes:
     *
     *     Ext.setup({
     *         icon: {
     *             '57': 'icon57.png',
     *             '77': 'icon77.png',
     *             '114': 'icon114.png'
     *         },
     *         onReady: function() {
     *             console.log('Launch...');
     *         }
     *     });
     *
     * @param {String} config.icon.57 The icon to be used on non-retna display devices (iPhone 3GS and below).
     * @param {String} config.icon.77 The icon to be used on the iPad.
     * @param {String} config.icon.114 The icon to be used on retna display devices (iPhone 4 and above).
     *
     * @param {Boolean} glossOnIcon
     * True to add a gloss effect to the icon.
     *
     * @param {String} phoneStartupScreen
     * Sets the apple-touch-icon `<meta>` tag so your home screen application can have a startup screen on phones.
     * Please look here for more information: http://developer.apple.com/library/IOs/#documentation/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html
     *
     * @param {String} tabletStartupScreen
     * Sets the apple-touch-icon `<meta>` tag so your home screen application can have a startup screen on tablets.
     * Please look here for more information: http://developer.apple.com/library/IOs/#documentation/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html
     *
     * @param {String} statusBarStyle
     * The style of status bar to be shown on applications added to the iOS homescreen. Valid options are:
     *
     * * `default`
     * * `black`
     * * `black-translucent`
     *
     * @param {String[]} config.requires
     * An array of required classes for your application which will be automatically loaded if {@link Ext.Loader#enabled} is set
     * to `true`. Please refer to {@link Ext.Loader} and {@link Ext.Loader#require} for more information.
     *
     *     Ext.setup({
     *         requires: ['Ext.Button', 'Ext.tab.Panel'],
     *         onReady: function() {
     *             //...
     *         }
     *     });
     *
     * @param {Object} config.eventPublishers
     * Sencha Touch, by default, includes various {@link Ext.event.recognizer.Recognizer} subclasses to recognise events fired
     * in your application. The list of default recognisers can be found in the documentation for {@link Ext.event.recognizer.Recognizer}.
     *
     * To change the default recognisers, you can use the following syntax:
     *
     *     Ext.setup({
     *         eventPublishers: {
     *             touchGesture: {
     *                 recognizers: {
     *                     swipe: {
     *                         //this will include both vertical and horizontal swipe recognisers
     *                         xclass: 'Ext.event.recognizer.Swipe'
     *                     }
     *                 }
     *             }
     *         },
     *         onReady: function() {
     *             //...
     *         }
     *     });
     *
     * You can also disable recognizers using this syntax:
     *
     *     Ext.setup({
     *         eventPublishers: {
     *             touchGesture: {
     *                 recognizers: {
     *                     swipe: null,
     *                     pinch: null,
     *                     rotate: null
     *                 }
     *             }
     *         },
     *         onReady: function() {
     *             //...
     *         }
     *     });
     */
    setup: function(config) {
        var defaultSetupConfig = Ext.defaultSetupConfig,
            onReady = config.onReady || Ext.emptyFn,
            scope = config.scope,
            requires = Ext.Array.from(config.requires),
            extOnReady = Ext.onReady,
            icon = config.icon,
            callback, viewport, precomposed;

        Ext.setup = function() {
            throw new Error("Ext.setup has already been called before");
        };

        delete config.requires;
        delete config.onReady;
        delete config.scope;

        Ext.require(['Ext.event.Dispatcher', 'Ext.MessageBox']);

        callback = function() {
            var listeners = Ext.setupListeners,
                ln = listeners.length,
                i, listener;

            delete Ext.setupListeners;
            Ext.isSetup = true;

            for (i = 0; i < ln; i++) {
                listener = listeners[i];
                listener.fn.call(listener.scope);
            }

            Ext.onReady = extOnReady;
            Ext.onReady(onReady, scope);
        };

        Ext.onReady = function(fn, scope) {
            var origin = onReady;

            onReady = function() {
                origin();
                Ext.onReady(fn, scope);
            };
        };

        config = Ext.merge({}, defaultSetupConfig, config);

        Ext.onDocumentReady(function() {
            Ext.factoryConfig(config, function(data) {
                Ext.event.Dispatcher.getInstance().setPublishers(data.eventPublishers);

                if (data.logger) {
                    Ext.Logger = data.logger;
                }

                if (data.animator) {
                    Ext.Animator = data.animator;
                }

                if (data.viewport) {
                    Ext.Viewport = viewport = data.viewport;

                    if (!scope) {
                        scope = viewport;
                    }

                    Ext.require(requires, function() {
                        Ext.Viewport.on('ready', callback, null, {single: true});
                    });
                }
                else {
                    Ext.require(requires, callback);
                }
            });
        });

        /*
         * Note: previously we only added these icon meta tags to iOS devices but as Android 2.1+ reads the same tags
         * we now add them if they're defined
         */
        if (!document.body) {
            var phoneIcon = config.phoneIcon,
                tabletIcon = config.tabletIcon,
                tabletStartupScreen = config.tabletStartupScreen,
                statusBarStyle = config.statusBarStyle,
                phoneStartupScreen = config.phoneStartupScreen,
                isIpad = Ext.os.is.iPad;

            // Inject meta viewport tag
            document.write(
                '<meta id="extViewportMeta" ' +
                       'name="viewport" ' +
                       'content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">');
            document.write('<meta name="apple-mobile-web-app-capable" content="yes">');
            document.write('<meta name="apple-touch-fullscreen" content="yes">');

            //status bar style
            if (Ext.isString(statusBarStyle)) {
                document.write('<meta name="apple-mobile-web-app-status-bar-style" content="' + statusBarStyle + '">');
            }

            //startup screens
            if (tabletStartupScreen && isIpad) {
                document.write('<link rel="apple-touch-startup-image" href="' + tabletStartupScreen + '">');
            }

            if (phoneStartupScreen && !isIpad) {
                document.write('<link rel="apple-touch-startup-image" href="' + phoneStartupScreen + '">');
            }

            // icon
            if (Ext.isString(icon) || Ext.isString(phoneIcon) || Ext.isString(tabletIcon)) {
                icon = {
                    '57': phoneIcon || tabletIcon || icon,
                    '72': tabletIcon || phoneIcon || icon,
                    '114': phoneIcon || tabletIcon || icon
                };
            }

            precomposed = (config.glossOnIcon === false) ? '-precomposed' : '';

            if (icon) {
                var icon72 = icon['72'],
                    icon57 = icon['57'],
                    icon114 = icon['114'],
                    iconString = '<link rel="apple-touch-icon';
                if (isIpad && icon72) {
                    document.write(iconString + precomposed + '" sizes="72x72" href="' + icon72 + '">');
                }
                else if (!isIpad) {
                    if (icon57) {
                        document.write(iconString + precomposed + '" href="' + icon57 + '">');
                    }
                    if (icon114) {
                        document.write(iconString + precomposed + '" sizes="114x114" href="' + icon114 + '">');
                    }
                }
            }
        }
    },

    /**
     * @member Ext
     * @method application
     *
     * Loads Ext.app.Application class and starts it up with given configuration after the page is ready.
     *
     *     Ext.application({
     *         launch: function() {
     *             alert('Application launched!');
     *         }
     *     });
     *
     * See {@link Ext.app.Application} for details.
     *
     * @param {Object} config An object with the following config options:
     *
     * @param {Function} config.launch
     * A function to be called when the application is ready. Your application logic should be here. Please see {@link Ext.app.Application}
     * for details.
     *
     * @param {Object} config.viewport
     * An object to be used when creating the global {@link Ext.Viewport} instance. Please refer to the {@link Ext.Viewport}
     * documentation for more information.
     *
     *     Ext.application({
     *         viewport: {
     *             layout: 'vbox'
     *         },
     *         launch: function() {
     *             Ext.Viewport.add({
     *                 flex: 1,
     *                 html: 'top (flex: 1)'
     *             });
     *
     *             Ext.Viewport.add({
     *                 flex: 4,
     *                 html: 'bottom (flex: 4)'
     *             });
     *         }
     *     });
     *
     * @param {String/Object} config.icon
     * A icon configuration for this application. This will only apply to iOS applications which are saved to the homescreen.
     *
     * You can either pass a string which will be applied to all different sizes:
     *
     *     Ext.setup({
     *         icon: 'icon.png',
     *         onReady: function() {
     *             console.log('Launch...');
     *         }
     *     });
     *
     * Or an object which has a location for different sizes:
     *
     *     Ext.setup({
     *         icon: {
     *             '57': 'icon57.png',
     *             '77': 'icon77.png',
     *             '114': 'icon114.png'
     *         },
     *         onReady: function() {
     *             console.log('Launch...');
     *         }
     *     });
     *
     * @param {String} config.icon.57 The icon to be used on non-retna display devices (iPhone 3GS and below).
     * @param {String} config.icon.77 The icon to be used on the iPad.
     * @param {String} config.icon.114 The icon to be used on retna display devices (iPhone 4 and above).
     *
     * @param {Boolean} glossOnIcon
     * True to add a gloss effect to the icon.
     *
     * @param {String} phoneStartupScreen
     * Sets the apple-touch-icon `<meta>` tag so your home screen application can have a startup screen on phones.
     * Please look here for more information: http://developer.apple.com/library/IOs/#documentation/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html
     *
     * @param {String} tabletStartupScreen
     * Sets the apple-touch-icon `<meta>` tag so your home screen application can have a startup screen on tablets.
     * Please look here for more information: http://developer.apple.com/library/IOs/#documentation/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html
     *
     * @param {String} statusBarStyle
     * The style of status bar to be shown on applications added to the iOS homescreen. Valid options are:
     *
     * * `default`
     * * `black`
     * * `black-translucent`
     *
     * @param {String[]} config.requires
     * An array of required classes for your application which will be automatically loaded if {@link Ext.Loader#enabled} is set
     * to `true`. Please refer to {@link Ext.Loader} and {@link Ext.Loader#require} for more information.
     *
     *     Ext.application({
     *         requires: ['Ext.Button', 'Ext.tab.Panel'],
     *         launch: function() {
     *             //...
     *         }
     *     });
     *
     * @param {Object} config.eventPublishers
     * Sencha Touch, by default, includes various {@link Ext.event.recognizer.Recognizer} subclasses to recognise events fired
     * in your application. The list of default recognisers can be found in the documentation for {@link Ext.event.recognizer.Recognizer}.
     *
     * To change the default recognisers, you can use the following syntax:
     *
     *     Ext.application({
     *         eventPublishers: {
     *             touchGesture: {
     *                 recognizers: {
     *                     swipe: {
     *                         //this will include both vertical and horizontal swipe recognisers
     *                         xclass: 'Ext.event.recognizer.Swipe'
     *                     }
     *                 }
     *             }
     *         },
     *         launch: function() {
     *             //...
     *         }
     *     });
     *
     * You can also disable recognizers using this syntax:
     *
     *     Ext.application({
     *         eventPublishers: {
     *             touchGesture: {
     *                 recognizers: {
     *                     swipe: null,
     *                     pinch: null,
     *                     rotate: null
     *                 }
     *             }
     *         },
     *         launch: function() {
     *             //...
     *         }
     *     });
     */
    application: function(config) {
        var onReady,
            scope;

        if (!config) {
            config = {};
        }

        Ext.Loader.setPath(config.name, config.appFolder || 'app');

        config.requires = Ext.Array.from(config.requires);
        config.requires.push('Ext.app.Application');

        onReady = config.onReady;
        scope = config.scope;

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

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

        Ext.setup(config);
    },

    /**
     * @private
     * @param config
     * @param callback
     * @member Ext
     */
    factoryConfig: function(config, callback) {
        var isSimpleObject = Ext.isSimpleObject(config);

        if (isSimpleObject && config.xclass) {
            var className = config.xclass;

            delete config.xclass;

            Ext.require(className, function() {
                Ext.factoryConfig(config, function(cfg) {
                    callback(Ext.create(className, cfg));
                });
            });

            return;
        }

        var isArray = Ext.isArray(config),
            keys = [],
            key, value, i, ln;

        if (isSimpleObject || isArray) {
            if (isSimpleObject) {
                for (key in config) {
                    if (config.hasOwnProperty(key)) {
                        value = config[key];
                        if (Ext.isSimpleObject(value) || Ext.isArray(value)) {
                            keys.push(key);
                        }
                    }
                }
            }
            else {
                for (i = 0,ln = config.length; i < ln; i++) {
                    value = config[i];

                    if (Ext.isSimpleObject(value) || Ext.isArray(value)) {
                        keys.push(i);
                    }
                }
            }

            i = 0;
            ln = keys.length;

            if (ln === 0) {
                callback(config);
                return;
            }

            function fn(value) {
                config[key] = value;
                i++;
                factory();
            }

            function factory() {
                if (i >= ln) {
                    callback(config);
                    return;
                }

                key = keys[i];
                value = config[key];

                Ext.factoryConfig(value, fn);
            }

            factory();
            return;
        }

        callback(config);
    },

    /**
     * @private
     * @param config
     * @param classReference
     * @member Ext
     */
    factory: function(config, classReference, instance, aliasNamespace) {
        var manager = Ext.ClassManager,
            newInstance;

        // If config is falsy or a valid instance, destroy the current instance
        // (if it exists) and replace with the new one
        if (!config || config.isInstance) {
            if (instance && instance !== config) {
                instance.destroy();
            }

            return config;
        }

        if (aliasNamespace) {
             // If config is a string value, treat is as an alias
            if (typeof config == 'string') {
                return manager.instantiateByAlias(aliasNamespace + '.' + config);
            }
            // Same if 'type' is given in config
            else if (Ext.isObject(config) && 'type' in config) {
                return manager.instantiateByAlias(aliasNamespace + '.' + config.type, config);
            }
        }
        else if (typeof config == 'string') {
            return Ext.getCmp(config);
        }

        if (config === true) {
            if (instance) {
                return instance;
            }
            else {
                return manager.instantiate(classReference);
            }
        }

        if (!Ext.isObject(config)) {
            Ext.Logger.error("Invalid config, must be a valid config object");
        }

        if ('xtype' in config) {
            newInstance = manager.instantiateByAlias('widget.' + config.xtype, config);
        }

        if ('xclass' in config) {
            newInstance = manager.instantiate(config.xclass, config);
        }

        if (newInstance) {
            if (instance) {
                instance.destroy();
            }

            return newInstance;
        }

        if (instance) {
            return instance.setConfig(config);
        }

        return manager.instantiate(classReference, config);
    },

    /**
     * @private
     * @member Ext
     */
    deprecateClassMember: function(cls, oldName, newName, message) {
        return this.deprecateProperty(cls.prototype, oldName, newName, message);
    },

    /**
     * @private
     * @member Ext
     */
    deprecateClassMembers: function(cls, members) {
       var prototype = cls.prototype,
           oldName, newName;

       for (oldName in members) {
           if (members.hasOwnProperty(oldName)) {
               newName = members[oldName];

               this.deprecateProperty(prototype, oldName, newName);
           }
       }
    },

    /**
     * @private
     * @member Ext
     */
    deprecateProperty: function(object, oldName, newName, message) {
        if (!message) {
            message = "'" + oldName + "' is deprecated";
        }
        if (newName) {
            message += ", please use '" + newName + "' instead";
        }

        if (newName) {
            Ext.Object.defineProperty(object, oldName, {
                get: function() {
                    Ext.Logger.deprecate(message, 1);
                    return this[newName];
                },
                set: function(value) {
                    Ext.Logger.deprecate(message, 1);

                    this[newName] = value;
                },
                configurable: true
            });
        }
    },

    /**
     * @private
     * @member Ext
     */
    deprecatePropertyValue: function(object, name, value, message) {
        Ext.Object.defineProperty(object, name, {
            get: function() {
                Ext.Logger.deprecate(message, 1);
                return value;
            },
            configurable: true
        });
    },

    /**
     * @private
     * @member Ext
     */
    deprecateMethod: function(object, name, method, message) {
        object[name] = function() {
            Ext.Logger.deprecate(message, 2);
            if (method) {
                return method.apply(this, arguments);
            }
        };
    },

    /**
     * @private
     * @member Ext
     */
    deprecateClassMethod: function(cls, name, method, message) {
        if (typeof name != 'string') {
            var from, to;

            for (from in name) {
                if (name.hasOwnProperty(from)) {
                    to = name[from];
                    Ext.deprecateClassMethod(cls, from, to);
                }
            }
            return;
        }

        var isLateBinding = typeof method == 'string',
            member;

        if (!message) {
            message = "'" + name + "()' is deprecated, please use '" + (isLateBinding ? method : method.name) +
                "()' instead";
        }

        if (isLateBinding) {
            member = function() {
                Ext.Logger.deprecate(message, this);

                return this[method].apply(this, arguments);
            };
        }
        else {
            member = function() {
                Ext.Logger.deprecate(message, this);

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

        if (name in cls.prototype) {
            Ext.Object.defineProperty(cls.prototype, name, {
                value: null,
                writable: true,
                configurable: true
            });
        }

        cls.addMember(name, member);
    },

    /**
     * Useful snippet to show an exact, narrowed-down list of top-level Components that are not yet destroyed.
     * @private
     */
    showLeaks: function() {
        var map = Ext.ComponentManager.all.map,
            leaks = [],
            parent;

        Ext.Object.each(map, function(id, component) {
            while ((parent = component.getParent()) && map.hasOwnProperty(parent.getId())) {
                component = parent;
            }

            if (leaks.indexOf(component) === -1) {
                leaks.push(component);
            }
        });

        console.log(leaks);
    },

    /**
     * True when the document is fully initialized and ready for action
     * @type Boolean
     * @member Ext
     */
    isReady : false,

    /**
     * @private
     * @member Ext
     */
    readyListeners: [],

    /**
     * @private
     * @member Ext
     */
    triggerReady: function() {
        var listeners = Ext.readyListeners,
            i, ln, listener;

        if (!Ext.isReady) {
            Ext.isReady = true;

            for (i = 0,ln = listeners.length; i < ln; i++) {
                listener = listeners[i];
                listener.fn.call(listener.scope);
            }
            delete Ext.readyListeners;
        }
    },

    /**
     * @private
     * @member Ext
     */
    onDocumentReady: function(fn, scope) {
        if (Ext.isReady) {
            fn.call(scope);
        }
        else {
            var triggerFn = Ext.triggerReady;

            Ext.readyListeners.push({
                fn: fn,
                scope: scope
            });

            if (Ext.browser.is.PhoneGap && !Ext.os.is.Desktop) {
                if (!Ext.readyListenerAttached) {
                    Ext.readyListenerAttached = true;
                    document.addEventListener('deviceready', triggerFn, false);
                }
            }
            else {
                if (document.readyState.match(/interactive|complete|loaded/) !== null) {
                    triggerFn();
                }
                else if (!Ext.readyListenerAttached) {
                    Ext.readyListenerAttached = true;
                    window.addEventListener('DOMContentLoaded', triggerFn, false);
                }
            }
        }
    },

    /**
     * Calls function after specified delay, or right away when delay == 0.
     * @param {Function} callback The callback to execute
     * @param {Object} scope (optional) The scope to execute in
     * @param {Array} args (optional) The arguments to pass to the function
     * @param {Number} delay (optional) Pass a number to delay the call by a number of milliseconds.
     * @member Ext
     */
    callback: function(callback, scope, args, delay) {
        if (Ext.isFunction(callback)) {
            args = args || [];
            scope = scope || window;
            if (delay) {
                Ext.defer(callback, delay, scope, args);
            } else {
                callback.apply(scope, args);
            }
        }
    }
});


/**
 * Provides useful information about the current browser. Should not be manually instantiated unless for unit-testing;
 * access the global instance stored in Ext.browser instead. Example:
 *
 * <pre><code>
 * if (Ext.browser.is.IE) {
 *      // IE specific code here
 * }
 *
 * if (Ext.browser.is.WebKit) {
 *      // WebKit specific code here
 * }
 *
 * console.log("Version " + Ext.browser.version);
 * </code></pre>
 *
 * For a full list of supported values, refer to: {@link Ext.env.Browser#is}
 */
Ext.define('Ext.env.Browser', {
    requires: ['Ext.Version'],

    statics: {
        browserNames: {
            ie: 'IE',
            firefox: 'Firefox',
            safari: 'Safari',
            chrome: 'Chrome',
            opera: 'Opera',
            dolfin: 'Dolfin',
            webosbrowser: 'webOSBrowser',
            chromeMobile: 'ChromeMobile',
            silk: 'Silk',
            other: 'Other'
        },
        engineNames: {
            webkit: 'WebKit',
            gecko: 'Gecko',
            presto: 'Presto',
            trident: 'Trident',
            other: 'Other'
        },
        enginePrefixes: {
            webkit: 'AppleWebKit/',
            gecko: 'Gecko/',
            presto: 'Presto/',
            trident: 'Trident/'
        },
        browserPrefixes: {
            ie: 'MSIE ',
            firefox: 'Firefox/',
            chrome: 'Chrome/',
            safari: 'Version/',
            opera: 'Opera/',
            dolfin: 'Dolfin/',
            webosbrowser: 'wOSBrowser/',
            chromeMobile: 'CrMo/',
            silk: 'Silk/'
        }
    },

    styleDashPrefixes: {
        WebKit: '-webkit-',
        Gecko: '-moz-',
        Trident: '-ms-',
        Presto: '-o-',
        Other: ''
    },

    stylePrefixes: {
        WebKit: 'Webkit',
        Gecko: 'Moz',
        Trident: 'ms',
        Presto: 'O',
        Other: ''
    },

    propertyPrefixes: {
        WebKit: 'webkit',
        Gecko: 'moz',
        Trident: 'ms',
        Presto: 'o',
        Other: ''
    },

    // scope: Ext.env.Browser.prototype

    /**
     * A "hybrid" property, can be either accessed as a method call, i.e:
     * <pre><code>
     * if (Ext.browser.is('IE')) { ... }
     * </code></pre>
     *
     * or as an object with boolean properties, i.e:
     * <pre><code>
     * if (Ext.browser.is.IE) { ... }
     * </code></pre>
     *
     * Versions can be conveniently checked as well. For example:
     * <pre><code>
     * if (Ext.browser.is.IE6) { ... } // Equivalent to (Ext.browser.is.IE && Ext.browser.version.equals(6))
     * </code></pre>
     *
     * Note that only {@link Ext.Version#getMajor major component}  and {@link Ext.Version#getShortVersion simplified}
     * value of the version are available via direct property checking.
     *
     * Supported values are: IE, Firefox, Safari, Chrome, Opera, WebKit, Gecko, Presto, Trident and Other
     *
     * @param {String} value The OS name to check
     * @return {Boolean}
     */
    is: Ext.emptyFn,

    /**
     * Read-only - the full name of the current browser
     * Possible values are: IE, Firefox, Safari, Chrome, Opera and Other
     * @type String
     */
    name: null,

    /**
     * Read-only, refer to {@link Ext.Version}
     * @type Ext.Version
     */
    version: null,

    /**
     * Read-only - the full name of the current browser's engine
     * Possible values are: WebKit, Gecko, Presto, Trident and Other
     * @type String
     */
    engineName: null,

    /**
     * Read-only, refer to {@link Ext.Version}
     * @type Ext.Version
     */
    engineVersion: null,

    setFlag: function(name, value) {
        if (typeof value == 'undefined') {
            value = true;
        }

        this.is[name] = value;
        this.is[name.toLowerCase()] = value;

        return this;
    },

    constructor: function(userAgent) {
        /**
         * @property {String}
         * Browser User Agent string.
         */
        this.userAgent = userAgent;

        is = this.is = function(name) {
            return is[name] === true;
        };

        var statics = this.statics(),
            browserMatch = userAgent.match(new RegExp('((?:' + Ext.Object.getValues(statics.browserPrefixes).join(')|(?:') + '))([\\w\\._]+)')),
            engineMatch = userAgent.match(new RegExp('((?:' + Ext.Object.getValues(statics.enginePrefixes).join(')|(?:') + '))([\\w\\._]+)')),
            browserNames = statics.browserNames,
            browserName = browserNames.other,
            engineNames = statics.engineNames,
            engineName = engineNames.other,
            browserVersion = '',
            engineVersion = '',
            isWebView = false,
            is, i, name;

        if (browserMatch) {
            browserName = browserNames[Ext.Object.getKey(statics.browserPrefixes, browserMatch[1])];

            browserVersion = new Ext.Version(browserMatch[2]);
        }

        if (engineMatch) {
            engineName = engineNames[Ext.Object.getKey(statics.enginePrefixes, engineMatch[1])];
            engineVersion = new Ext.Version(engineMatch[2]);
        }

        Ext.apply(this, {
            engineName: engineName,
            engineVersion: engineVersion,
            name: browserName,
            version: browserVersion
        });

        this.setFlag(browserName);

        if (browserVersion) {
            this.setFlag(browserName + (browserVersion.getMajor() || ''));
            this.setFlag(browserName + browserVersion.getShortVersion());
        }

        for (i in browserNames) {
            if (browserNames.hasOwnProperty(i)) {
                name = browserNames[i];

                this.setFlag(name, browserName === name);
            }
        }

        this.setFlag(name);

        if (engineVersion) {
            this.setFlag(engineName + (engineVersion.getMajor() || ''));
            this.setFlag(engineName + engineVersion.getShortVersion());
        }

        for (i in engineNames) {
            if (engineNames.hasOwnProperty(i)) {
                name = engineNames[i];

                this.setFlag(name, engineName === name);
            }
        }

        this.setFlag('Standalone', !!navigator.standalone);

        if (typeof window.PhoneGap != 'undefined') {
            isWebView = true;
            this.setFlag('PhoneGap');
        }
        else if (!!window.isNK) {
            isWebView = true;
            this.setFlag('Sencha');
        }

        // Flag to check if it we are in the WebView
        this.setFlag('WebView', isWebView);

        /**
         * @property {Boolean}
         * True if browser is using strict mode.
         */
        this.isStrict = document.compatMode == "CSS1Compat";

        /**
         * @property {Boolean}
         * True if page is running over SSL.
         */
        this.isSecure = /^https/i.test(window.location.protocol);

        return this;
    },

    getStyleDashPrefix: function() {
        return this.styleDashPrefixes[this.engineName];
    },

    getStylePrefix: function() {
        return this.stylePrefixes[this.engineName];
    },

    getVendorProperyName: function(name) {
        var prefix = this.propertyPrefixes[this.engineName];

        if (prefix.length > 0) {
            return prefix + Ext.String.capitalize(name);
        }

        return name;
    }

}, function() {
    var browserEnv = Ext.browser = new this(Ext.global.navigator.userAgent);

});

/**
 * Provide useful information about the current operating system environment. Access the global instance stored in
 * Ext.os. Example:
 *
 *     if (Ext.os.is.Windows) {
 *          // Windows specific code here
 *     }
 *
 *     if (Ext.os.is.iOS) {
 *          // iPad, iPod, iPhone, etc.
 *     }
 *
 *     console.log("Version " + Ext.os.version);
 *
 * For a full list of supported values, refer to: {@link Ext.env.OS#is}
 */
Ext.define('Ext.env.OS', {

    requires: ['Ext.Version'],

    statics: {
        names: {
            ios: 'iOS',
            android: 'Android',
            webos: 'webOS',
            blackberry: 'BlackBerry',
            rimTablet: 'RIMTablet',
            mac: 'MacOS',
            win: 'Windows',
            linux: 'Linux',
            bada: 'Bada',
            other: 'Other'
        },
        prefixes: {
            ios: 'i(?:Pad|Phone|Pod)(?:.*)CPU(?: iPhone)? OS ',
            android: '(Android |HTC_|Silk/)', // Some HTC devices ship with an OSX userAgent by default,
                                        // so we need to add a direct check for HTC_
            blackberry: 'BlackBerry(?:.*)Version\/',
            rimTablet: 'RIM Tablet OS ',
            webos: '(?:webOS|hpwOS)\/',
            bada: 'Bada\/'
        }
    },

    /**
     * A "hybrid" property, can be either accessed as a method call, i.e:
     *
     *     if (Ext.os.is('Android')) { ... }
     *
     * or as an object with boolean properties, i.e:
     *
     *     if (Ext.os.is.Android) { ... }
     *
     * Versions can be conveniently checked as well. For example:
     *
     *     if (Ext.os.is.Android2) { ... } // Equivalent to (Ext.os.is.Android && Ext.os.version.equals(2))
     *
     *     if (Ext.os.is.iOS32) { ... } // Equivalent to (Ext.os.is.iOS && Ext.os.version.equals(3.2))
     *
     * Note that only {@link Ext.Version#getMajor major component} and {@link Ext.Version#getShortVersion simplified}
     * value of the version are available via direct property checking. Supported values are: iOS, iPad, iPhone, iPod,
     * Android, WebOS, BlackBerry, Bada, MacOSX, Windows, Linux and Other
     * @param {String} value The OS name to check
     * @return {Boolean}
     */
    is: Ext.emptyFn,

    /**
     * @property {String} [name=null]
     * Read-only - the full name of the current operating system Possible values are: iOS, Android, WebOS, BlackBerry,
     * MacOSX, Windows, Linux and Other
     */
    name: null,

    /**
     * @property {Ext.Version} [version=null]
     * Read-only, refer to {@link Ext.Version}
     */
    version: null,

    setFlag: function(name, value) {
        if (typeof value == 'undefined') {
            value = true;
        }

        this.is[name] = value;
        this.is[name.toLowerCase()] = value;

        return this;
    },

    constructor: function(userAgent, platform) {
        var statics = this.statics(),
            names = statics.names,
            prefixes = statics.prefixes,
            name,
            version = '',
            i, prefix, match, item, is;

        is = this.is = function(name) {
            return this.is[name] === true;
        };

        for (i in prefixes) {
            if (prefixes.hasOwnProperty(i)) {
                prefix = prefixes[i];

                match = userAgent.match(new RegExp('(?:'+prefix+')([^\\s;]+)'));

                if (match) {
                    name = names[i];

                    // This is here because some HTC android devices show an OSX Snow Leopard userAgent by default.
                    // And the Kindle Fire doesn't have any indicator of Android as the OS in its User Agent
                    if (match[1] && (match[1] == "HTC_" || match[1] == "Silk/")) {
                        version = new Ext.Version("2.3");
                    } else {
                        version = new Ext.Version(match[match.length - 1]);
                    }

                    break;
                }
            }
        }

        if (!name) {
            name = names[(userAgent.toLowerCase().match(/mac|win|linux/) || ['other'])[0]];
            version = new Ext.Version('');
        }

        this.name = name;
        this.version = version;

        if (platform) {
            this.setFlag(platform);
        }

        this.setFlag(name);

        if (version) {
            this.setFlag(name + (version.getMajor() || ''));
            this.setFlag(name + version.getShortVersion());
        }

        for (i in names) {
            if (names.hasOwnProperty(i)) {
                item = names[i];

                if (!is.hasOwnProperty(name)) {
                    this.setFlag(item, (name === item));
                }
            }
        }

        return this;
    }

}, function() {

    var navigation = Ext.global.navigator,
        userAgent = navigation.userAgent,
        osEnv, osName, deviceType;


    Ext.os = osEnv = new this(userAgent, navigation.platform);

    osName = osEnv.name;

    var search = window.location.search.match(/deviceType=(Tablet|Phone)/),
        nativeDeviceType = window.deviceType;

    // Override deviceType by adding a get variable of deviceType. NEEDED FOR DOCS APP.
    // E.g: example/kitchen-sink.html?deviceType=Phone
    if (search && search[1]) {
        deviceType = search[1];
    }
    else if (nativeDeviceType === 'iPhone') {
        deviceType = 'Phone';
    }
    else if (nativeDeviceType === 'iPad') {
        deviceType = 'Tablet';
    }
    else {
        if (!osEnv.is.Android && !osEnv.is.iOS && /Windows|Linux|MacOS/.test(osName)) {
            deviceType = 'Desktop';
        }
        else if (osEnv.is.iPad || osEnv.is.Android3 || (osEnv.is.Android4 && userAgent.search(/mobile/i) == -1)) {
            deviceType = 'Tablet';
        }
        else {
            deviceType = 'Phone';
        }
    }

    osEnv.setFlag(deviceType, true);
    osEnv.deviceType = deviceType;


    /**
     * @class Ext.is
     * Used to detect if the current browser supports a certain feature, and the type of the current browser.
     * @deprecated 2.0.0
     * Please refer to the {@link Ext.env.Browser}, {@link Ext.env.OS} and {@link Ext.feature.has} classes instead.
     */
});

/**
 * A class to detect if the current browser supports various features.
 *
 * Please refer to the documentation of {@link Ext.feature.has} on how to use it.
 *
 *     if (Ext.feature.has.Canvas) {
 *         // do some cool things with canvas here
 *     }
 */
Ext.define('Ext.env.Feature', {

    requires: ['Ext.env.Browser', 'Ext.env.OS'],

    constructor: function() {
        this.testElements = {};

        this.has = function(name) {
            return !!this.has[name];
        };

        return this;
    },

    getTestElement: function(tag, createNew) {
        if (tag === undefined) {
            tag = 'div';
        }
        else if (typeof tag !== 'string') {
            return tag;
        }

        if (createNew) {
            return document.createElement(tag);
        }

        if (!this.testElements[tag]) {
            this.testElements[tag] = document.createElement(tag);
        }

        return this.testElements[tag];
    },

    isStyleSupported: function(name, tag) {
        var elementStyle = this.getTestElement(tag).style,
            cName = Ext.String.capitalize(name);

        if (typeof elementStyle[name] !== 'undefined'
            || typeof elementStyle[Ext.browser.getStylePrefix(name) + cName] !== 'undefined') {
            return true;
        }

        return false;
    },

    isEventSupported: function(name, tag) {
        if (tag === undefined) {
            tag = window;
        }

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

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

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

                element.removeAttribute(eventName);
            }
        }

        return isSupported;
    },

    getSupportedPropertyName: function(object, name) {
        var vendorName = Ext.browser.getVendorProperyName(name);

        if (vendorName in object) {
            return vendorName;
        }
        else if (name in object) {
            return name;
        }

        return null;
    },

    registerTest: Ext.Function.flexSetter(function(name, fn) {
        this.has[name] = fn.call(this);

        return this;
    })

}, function() {

    Ext.feature = new this;

    var has = Ext.feature.has;

    /**
     * @class Ext.feature.has
     * A simple class to verify if a browser feature exists or not on the current device.
     *
     *     if (Ext.feature.has.Canvas) {
     *         // do some cool things with canvas here
     *     }
     *
     * See the list of properties below too see which features are available for detection.
     */

    Ext.feature.registerTest({
        /**
         * @member Ext.feature.has
         * @property {Boolean} Canvas
         * True if the current device supports Canvas.
         */
        Canvas: function() {
            var element = this.getTestElement('canvas');
            return !!(element && element.getContext && element.getContext('2d'));
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} Svg
         * True if the current device supports SVG.
         */
        Svg: function() {
            var doc = document;

            return !!(doc.createElementNS && !!doc.createElementNS("http:/" + "/www.w3.org/2000/svg", "svg").createSVGRect);
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} Vml
         * True if the current device supports VML.
         */
        Vml: function() {
            var element = this.getTestElement(),
                ret = false;

            element.innerHTML = "<!--[if vml]><br><![endif]-->";
            ret = (element.childNodes.length === 1);
            element.innerHTML = "";

            return ret;
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} Touch
         * True if the current device supports touch events (`touchstart`).
         */
        Touch: function() {
            return this.isEventSupported('touchstart') && !(Ext.os && Ext.os.name.match(/Windows|MacOSX|Linux/));
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} Orientation
         * True if the current device supports different orientations.
         */
        Orientation: function() {
            return ('orientation' in window) && this.isEventSupported('orientationchange');
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} OrientationChange
         * True if the current device supports the `orientationchange` event.
         */
        OrientationChange: function() {
            return this.isEventSupported('orientationchange');
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} DeviceMotion
         * True if the current device supports the `devicemotion` event.
         */
        DeviceMotion: function() {
            return this.isEventSupported('devicemotion');
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} Geolocation
         * True if the current device supports Geolocation.
         */
        Geolocation: function() {
            return 'geolocation' in window.navigator;
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} SqlDatabase
         * True if the current device supports SQL Databases.
         */
        SqlDatabase: function() {
            return 'openDatabase' in window;
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} WebSockets
         */
        WebSockets: function() {
            return 'WebSocket' in window;
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} Range
         */
        Range: function() {
            return !!document.createRange;
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} CreateContextualFragment
         */
        CreateContextualFragment: function() {
            var range = !!document.createRange ? document.createRange() : false;
            return range && !!range.createContextualFragment;
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} History
         */
        History: function() {
            return ('history' in window && 'pushState' in window.history);
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} CssTransforms
         * True if the current device supports CSS Transform animations.
         */
        CssTransforms: function() {
            return this.isStyleSupported('transform');
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} Css3dTransforms
         * True if the current device supports CSS 3D Transform animations.
         */
        Css3dTransforms: function() {
            // See https://sencha.jira.com/browse/TOUCH-1544
            return this.has('CssTransforms') && this.isStyleSupported('perspective') && !Ext.os.is.Android2;
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} CssAnimations
         * True if the current device supports CSS Animations.
         */
        CssAnimations: function() {
            return this.isStyleSupported('animationName');
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} CssTransitions
         * True if the current device supports CSS Transitions.
         */
        CssTransitions: function() {
            return this.isStyleSupported('transitionProperty');
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} Audio
         * True if the current device supports the `<audio>` tag.
         */
        Audio: function() {
            return !!this.getTestElement('audio').canPlayType;
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} Video
         * True if the current device supports the `<video>` tag.
         */
        Video: function() {
            return !!this.getTestElement('video').canPlayType;
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} ClassList
         * True if document environment supports the HTML5 classList API.
         */
        ClassList: function() {
            return "classList" in this.getTestElement();
        }
    });

});

/**
 * @class Ext.DomQuery
 * @alternateClassName Ext.dom.Query
 *
 * Provides functionality to select elements on the page based on a CSS selector. 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.
 *
 * ## 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] has an attribute "foo" that 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 nth 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 nth 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//\\
 *
 * ## 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"
 */
Ext.define('Ext.dom.Query', {
    /**
     * Selects a group of elements.
     * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
     * @param {HTMLElement/String} [root] The start of the query (defaults to document).
     * @return {HTMLElement[]} An Array of DOM elements which match the selector. If there are
     * no matches, and empty Array is returned.
     */
    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') {

                //support for node attribute selection
                if (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;
    },

    /**
     * Selects a single element.
     * @param {String} selector The selector/xpath query
     * @param {HTMLElement/String} [root] The start of the query (defaults to document).
     * @return {HTMLElement} The DOM element which matched the selector.
     */
    selectNode: function(q, root) {
        return this.select(q, root)[0];
    },

    /**
     * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
     * @param {String/HTMLElement/Array} el An element id, element or array of elements
     * @param {String} selector The simple selector to test
     * @return {Boolean}
     */
    is: function(el, q) {
        if (typeof el == "string") {
            el = document.getElementById(el);
        }
        return this.select(q).indexOf(el) !== -1;
    },

    isXml: function(el) {
        var docEl = (el ? el.ownerDocument || el : 0).documentElement;
        return docEl ? docEl.nodeName !== "HTML" : false;
    }

}, function() {
    Ext.ns('Ext.core');
    Ext.core.DomQuery = Ext.DomQuery = new this();
    Ext.query = Ext.Function.alias(Ext.DomQuery, 'select');
});

/**
 * @class Ext.DomHelper
 * @alternateClassName Ext.dom.Helper
 *
 * The DomHelper class provides a layer of abstraction from DOM and transparently supports creating elements via DOM or
 * using HTML fragments. It also has the ability to create HTML fragment templates from your DOM building code.
 *
 * ## DomHelper element specification object
 *
 * A specification object is used when creating elements. Attributes of this object are assumed to be element
 * attributes, except for 4 special attributes:
 *
 * * **tag**: The tag name of the element
 * * **children (or cn)**: An array of the same kind of element definition objects to be created and appended. These
 * can be nested as deep as you want.
 * * **cls**: The class attribute of the element. This will end up being either the "class" attribute on a HTML
 * fragment or className for a DOM node, depending on whether DomHelper is using fragments or DOM.
 * * **html**: The innerHTML for the element
 *
 * ## Insertion methods
 *
 * Commonly used insertion methods:
 *
 * * {@link #append}
 * * {@link #insertBefore}
 * * {@link #insertAfter}
 * * {@link #overwrite}
 * * {@link #insertHtml}
 *
 * ## Example
 *
 * This is an example, where an unordered list with 3 children items is appended to an existing element with id
 * 'my-div':
 *
 *     var dh = Ext.DomHelper; // create shorthand alias
 *     // specification object
 *     var spec = {
 *         id: 'my-ul',
 *         tag: 'ul',
 *         cls: 'my-list',
 *         // append children after creating
 *         children: [     // may also specify 'cn' instead of 'children'
 *             {tag: 'li', id: 'item0', html: 'List Item 0'},
 *             {tag: 'li', id: 'item1', html: 'List Item 1'},
 *             {tag: 'li', id: 'item2', html: 'List Item 2'}
 *         ]
 *     };
 *     var list = dh.append(
 *         'my-div', // the context element 'my-div' can either be the id or the actual node
 *         spec      // the specification object
 *     );
 *
 * Element creation specification parameters in this class may also be passed as an Array of specification objects.
 * This can be used to insert multiple sibling nodes into an existing container very efficiently. For example, to add
 * more list items to the example above:
 *
 *     dh.append('my-ul', [
 *         {tag: 'li', id: 'item3', html: 'List Item 3'},
 *         {tag: 'li', id: 'item4', html: 'List Item 4'}
 *     ]);
 *
 * ## Templating
 *
 * The real power is in the built-in templating. Instead of creating or appending any elements, createTemplate returns
 * a Template object which can be used over and over to insert new elements. Revisiting the example above, we could
 * utilize templating this time:
 *
 *     // create the node
 *     var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});
 *     // get template
 *     var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});
 *
 *     for(var i = 0; i < 5; i++){
 *         tpl.append(list, i); // use template to append to the actual node
 *     }
 *
 * An example using a template:
 *
 *     var html = '"{0}" href="{1}" class="nav">{2}';
 *
 *     var tpl = new Ext.DomHelper.createTemplate(html);
 *     tpl.append('blog-roll', ['link1', 'http://www.tommymaintz.com/', "Tommy's Site"]);
 *     tpl.append('blog-roll', ['link2', 'http://www.avins.org/', "Jamie's Site"]);
 *
 * The same example using named parameters:
 *
 *     var html = '"{id}" href="{url}" class="nav">{text}';
 *
 *     var tpl = new Ext.DomHelper.createTemplate(html);
 *     tpl.append('blog-roll', {
 *         id: 'link1',
 *         url: 'http://www.tommymaintz.com/',
 *         text: "Tommy's Site"
 *     });
 *     tpl.append('blog-roll', {
 *         id: 'link2',
 *         url: 'http://www.avins.org/',
 *         text: "Jamie's Site"
 *     });
 *
 * ## Compiling Templates
 *
 * Templates are applied using regular expressions. The performance is great, but if you are adding a bunch of DOM
 * elements using the same template, you can increase performance even further by "compiling" the template. The way
 * "compile()" works is the template is parsed and broken up at the different variable points and a dynamic function is
 * created and eval'ed. The generated function performs string concatenation of these parts and the passed variables
 * instead of using regular expressions.
 *
 *     var html = '"{id}" href="{url}" class="nav">{text}';
 *
 *     var tpl = new Ext.DomHelper.createTemplate(html);
 *     tpl.compile();
 *
 *     //... use template like normal
 *
 * ## Performance Boost
 *
 * DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead of DOM can
 * significantly boost performance.
 *
 * Element creation specification parameters may also be strings. If useDom is false, then the string is used as
 * innerHTML. If useDom is true, a string specification results in the creation of a text node. Usage:
 *
 *     Ext.DomHelper.useDom = true; // force it to use DOM; reduces performance
 *
 */
Ext.define('Ext.dom.Helper', {
    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,

    attribXlat: { 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,
            attr, val, tag, i, closeTags;

        if (typeof spec == "string") {
            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).push('"');
                        } else {
                            buffer.push(' ', me.attribXlat[attr] || attr, '="', val, '"');
                        }
                    }
                }
            }

            // Now either just close the tag or try to add children and close the tag.
            if (me.emptyTags.test(tag)) {
                buffer.push('/>');
            } else {
                buffer.push('>');

                // Apply the tpl html, and cn specifications
                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);
                }

                // we generate a lot of close tags, so cache them rather than push 3 parts
                closeTags = me.closeTags;
                buffer.push(closeTags[tag] || (closeTags[tag] = '</' + tag + '>'));
            }
        }

        return buffer;
    },

    /**
     * Converts the styles from the given object to text. The styles are CSS style names
     * with their associated value.
     *
     * The basic form of this method returns a string:
     *
     *      var s = Ext.DomHelper.generateStyles({
     *          backgroundColor: 'red'
     *      });
     *
     *      // s = 'background-color:red;'
     *
     * Alternatively, this method can append to an output array.
     *
     *      var buf = [];
     *
     *      ...
     *
     *      Ext.DomHelper.generateStyles({
     *          backgroundColor: 'red'
     *      }, buf);
     *
     * In this case, the style text is pushed on to the array and the array is returned.
     *
     * @param {Object} styles The object describing the styles.
     * @param {String[]} [buffer] The output buffer.
     * @return {String/String[]} If buffer is passed, it is returned. Otherwise the style
     * string is returned.
     */
    generateStyles: function (styles, buffer) {
        var a = buffer || [],
            name;

        for (name in styles) {
            if (styles.hasOwnProperty(name)) {
                a.push(this.decamelizeName(name), ':', styles[name], ';');
            }
        }

        return buffer || a.join('');
    },

    /**
     * Returns the markup for the passed Element(s) config.
     * @param {Object} spec The DOM object spec (and children)
     * @return {String}
     */
    markup: function(spec) {
        if (typeof spec == "string") {
            return spec;
        }

        var buf = this.generateMarkup(spec, []);
        return buf.join('');
    },

    /**
     * Applies a style specification to an element.
     * @param {String/HTMLElement} el The element to apply styles to
     * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or
     * a function which returns such a specification.
     */
    applyStyles: function(el, styles) {
        Ext.fly(el).applyStyles(styles);
    },

    /**
     * @private
     * Fix for browsers which no longer support createContextualFragment
     */
    createContextualFragment: function(html){
        var div = document.createElement("div"),
            fragment = document.createDocumentFragment(),
            i = 0,
            length, childNodes;

        div.innerHTML = html;
        childNodes = div.childNodes;
        length = childNodes.length;

        for (; i < length; i++) {
            fragment.appendChild(childNodes[i].cloneNode(true));
        }

        return fragment;
    },

    /**
     * Inserts an HTML fragment into the DOM.
     * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
     *
     * For example take the following HTML: `<div>Contents</div>`
     *
     * Using different `where` values inserts element to the following places:
     *
     * - beforeBegin: `<HERE><div>Contents</div>`
     * - afterBegin: `<div><HERE>Contents</div>`
     * - beforeEnd: `<div>Contents<HERE></div>`
     * - afterEnd: `<div>Contents</div><HERE>`
     *
     * @param {HTMLElement/TextNode} el The context element
     * @param {String} html The HTML fragment
     * @return {HTMLElement} The new node
     */
    insertHtml: function(where, el, html) {
        var setStart, range, frag, rangeEl, isBeforeBegin, isAfterBegin;

        where = where.toLowerCase();

        if (Ext.isTextNode(el)) {
            if (where == 'afterbegin' ) {
                where = 'beforebegin';
            }
            else if (where == 'beforeend') {
                where = 'afterend';
            }
        }

        isBeforeBegin = where == 'beforebegin';
        isAfterBegin = where == 'afterbegin';

        range = Ext.feature.has.CreateContextualFragment ? el.ownerDocument.createRange() : undefined;
        setStart = 'setStart' + (this.endRe.test(where) ? 'After' : 'Before');

        if (isBeforeBegin || where == 'afterend') {
            if (range) {
                range[setStart](el);
                frag = range.createContextualFragment(html);
            }
            else {
                frag = this.createContextualFragment(html);
            }
            el.parentNode.insertBefore(frag, isBeforeBegin ? el : el.nextSibling);
            return el[(isBeforeBegin ? 'previous' : 'next') + 'Sibling'];
        }
        else {
            rangeEl = (isAfterBegin ? 'first' : 'last') + 'Child';
            if (el.firstChild) {
                if (range) {
                    range[setStart](el[rangeEl]);
                    frag = range.createContextualFragment(html);
                } else {
                    frag = this.createContextualFragment(html);
                }

                if (isAfterBegin) {
                    el.insertBefore(frag, el.firstChild);
                } else {
                    el.appendChild(frag);
                }
            } else {
                el.innerHTML = html;
            }
            return el[rangeEl];
        }
    },

    /**
     * Creates new DOM element(s) and inserts them before el.
     * @param {String/HTMLElement/Ext.Element} el The context element
     * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
     * @param {Boolean} [returnElement] true to return a Ext.Element
     * @return {HTMLElement/Ext.Element} The new node
     */
    insertBefore: function(el, o, returnElement) {
        return this.doInsert(el, o, returnElement, 'beforebegin');
    },

    /**
     * Creates new DOM element(s) and inserts them after el.
     * @param {String/HTMLElement/Ext.Element} el The context element
     * @param {Object} o The DOM object spec (and children)
     * @param {Boolean} [returnElement] true to return a Ext.Element
     * @return {HTMLElement/Ext.Element} The new node
     */
    insertAfter: function(el, o, returnElement) {
        return this.doInsert(el, o, returnElement, 'afterend');
    },

    /**
     * Creates new DOM element(s) and inserts them as the first child of el.
     * @param {String/HTMLElement/Ext.Element} el The context element
     * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
     * @param {Boolean} [returnElement] true to return a Ext.Element
     * @return {HTMLElement/Ext.Element} The new node
     */
    insertFirst: function(el, o, returnElement) {
        return this.doInsert(el, o, returnElement, 'afterbegin');
    },

    /**
     * Creates new DOM element(s) and appends them to el.
     * @param {String/HTMLElement/Ext.Element} el The context element
     * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
     * @param {Boolean} [returnElement] true to return a Ext.Element
     * @return {HTMLElement/Ext.Element} The new node
     */
    append: function(el, o, returnElement) {
        return this.doInsert(el, o, returnElement, 'beforeend');
    },

    /**
     * Creates new DOM element(s) and overwrites the contents of el with them.
     * @param {String/HTMLElement/Ext.Element} el The context element
     * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
     * @param {Boolean} [returnElement] true to return a Ext.Element
     * @return {HTMLElement/Ext.Element} The new node
     */
    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) {
        var newNode = this.insertHtml(pos, Ext.getDom(el), this.markup(o));
        return returnElement ? Ext.get(newNode, true) : newNode;
    },

    /**
     * Creates a new Ext.Template from the DOM object spec.
     * @param {Object} o The DOM object spec (and children)
     * @return {Ext.Template} The new template
     */
    createTemplate: function(o) {
        var html = this.markup(o);
        return new Ext.Template(html);
    }
}, function() {
    Ext.ns('Ext.core');
    Ext.core.DomHelper = Ext.DomHelper = new this;
});

/**
 * An Identifiable mixin.
 * @private
 */
Ext.define('Ext.mixin.Identifiable', {
    statics: {
        uniqueIds: {}
    },

    isIdentifiable: true,

    mixinId: 'identifiable',

    idCleanRegex: /\.|[^\w\-]/g,

    defaultIdPrefix: 'ext-',

    defaultIdSeparator: '-',

    getOptimizedId: function() {
        return this.id;
    },

    getUniqueId: function() {
        var id = this.id,
            prototype, separator, xtype, uniqueIds, prefix;

        if (!id) {
            prototype = this.self.prototype;
            separator = this.defaultIdSeparator;

            uniqueIds = Ext.mixin.Identifiable.uniqueIds;

            if (!prototype.hasOwnProperty('identifiablePrefix')) {
                xtype = this.xtype;

                if (xtype) {
                    prefix = this.defaultIdPrefix + xtype + separator;
                }
                else {
                    prefix = prototype.$className.replace(this.idCleanRegex, separator).toLowerCase() + separator;
                }

                prototype.identifiablePrefix = prefix;
            }

            prefix = this.identifiablePrefix;

            if (!uniqueIds.hasOwnProperty(prefix)) {
                uniqueIds[prefix] = 0;
            }

            id = this.id = prefix + (++uniqueIds[prefix]);
        }

        this.getUniqueId = this.getOptimizedId;

        return id;
    },

    setId: function(id) {
        this.id = id;
    },

    /**
     * Retrieves the id of this component. Will autogenerate an id if one has not already been set.
     * @return {String} id
     */
    getId: function() {
        var id = this.id;

        if (!id) {
            id = this.getUniqueId();
        }

        this.getId = this.getOptimizedId;

        return id;
    }
});

/**
 * Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.
 *
 * All instances of this class inherit the methods of Ext.Fx making visual effects easily available to all DOM elements.
 *
 * Note that the events documented in this class are not Ext events, they encapsulate browser events. To access the
 * underlying browser event, see Ext.EventObject.browserEvent. Some older browsers may not support the full range of
 * events. Which events are supported is beyond the control of Sencha Touch.
 *
 * ## Usage
 *
 *     // by id
 *     var el = Ext.get("my-div");
 *
 *     // by DOM element reference
 *     var el = Ext.get(myDivElement);
 *
 * ## Composite (Collections of) Elements
 *
 * For working with collections of Elements, see Ext.CompositeElement
 *
 * @mixins Ext.mixin.Observable
 */
Ext.define('Ext.dom.Element', {
    alternateClassName: 'Ext.Element',

    mixins: [
        'Ext.mixin.Identifiable'
    ],

    requires: [
        'Ext.dom.Query',
        'Ext.dom.Helper'
    ],

    observableType: 'element',

    xtype: 'element',

    statics: {
        CREATE_ATTRIBUTES: {
            style: 'style',
            className: 'className',
            cls: 'cls',
            classList: 'classList',
            text: 'text',
            hidden: 'hidden',
            html: 'html',
            children: 'children'
        },

        create: function(attributes, domNode) {
            var ATTRIBUTES = this.CREATE_ATTRIBUTES,
                element, elementStyle, tag, value, name, i, ln;

            if (!attributes) {
                attributes = {};
            }

            if (attributes.isElement) {
                return attributes.dom;
            }
            else if ('nodeType' in attributes) {
                return attributes;
            }

            if (typeof attributes == 'string') {
                return document.createTextNode(attributes);
            }

            tag = attributes.tag;

            if (!tag) {
                tag = 'div';
            }

            element = document.createElement(tag);
            elementStyle = element.style;

            for (name in attributes) {
                if (name != 'tag' && attributes.hasOwnProperty(name)) {
                    value = attributes[name];

                    switch (name) {
                        case ATTRIBUTES.style:
                                if (typeof value == 'string') {
                                    element.setAttribute(name, value);
                                }
                                else {
                                    for (i in value) {
                                        if (value.hasOwnProperty(i)) {
                                            elementStyle[i] = value[i];
                                        }
                                    }
                                }
                            break;

                        case ATTRIBUTES.className:
                        case ATTRIBUTES.cls:
                            element.className = value;
                            break;

                        case ATTRIBUTES.classList:
                            element.className = value.join(' ');
                            break;

                        case ATTRIBUTES.text:
                            element.textContent = value;
                            break;

                        case ATTRIBUTES.hidden:
                            if (value) {
                                element.style.display = 'none';
                            }
                            break;

                        case ATTRIBUTES.html:
                            element.innerHTML = value;
                            break;

                        case ATTRIBUTES.children:
                            for (i = 0,ln = value.length; i < ln; i++) {
                                element.appendChild(this.create(value[i], true));
                            }
                            break;

                        default:
                            element.setAttribute(name, value);
                    }
                }
            }

            if (domNode) {
                return element;
            }
            else {
                return this.get(element);
            }
        },

        documentElement: null,

        cache: {},

        /**
         * Retrieves Ext.dom.Element objects. {@link Ext#get} is alias for {@link Ext.dom.Element#get}.
         *
         * **This method does not retrieve {@link Ext.Component Component}s.** This method retrieves Ext.dom.Element
         * objects which encapsulate DOM elements. To retrieve a Component by its ID, use {@link Ext.ComponentManager#get}.
         *
         * Uses simple caching to consistently return the same object. Automatically fixes if an object was recreated with
         * the same id via AJAX or DOM.
         *
         * @param {String/HTMLElement/Ext.Element} el The id of the node, a DOM Node or an existing Element.
         * @return {Ext.dom.Element} The Element object (or null if no matching element was found)
         * @static
         * @inheritable
         */
        get: function(element) {
            var cache = this.cache,
                instance, dom, id;

            if (!element) {
                return null;
            }

            if (typeof element == 'string') {
                if (cache.hasOwnProperty(element)) {
                    return cache[element];
                }

                if (!(dom = document.getElementById(element))) {
                    return null;
                }

                cache[element] = instance = new this(dom);

                return instance;
            }

            if ('tagName' in element) { // dom element
                id = element.id;

                if (cache.hasOwnProperty(id)) {
                    return cache[id];
                }

                instance = new this(element);
                cache[instance.getId()] = instance;

                return instance;
            }

            if (element.isElement) {
                return element;
            }

            if (element.isComposite) {
                return element;
            }

            if (Ext.isArray(element)) {
                return this.select(element);
            }

            if (element === document) {
                // create a bogus element object representing the document object
                if (!this.documentElement) {
                    this.documentElement = new this(document.documentElement);
                    this.documentElement.setId('ext-application');
                }

                return this.documentElement;
            }

            return null;
        },

        data: function(element, key, value) {
            var cache = Ext.cache,
                id, data;

            element = this.get(element);

            if (!element) {
                return null;
            }

            id = element.id;

            data = cache[id].data;

            if (!data) {
                cache[id].data = data = {};
            }

            if (arguments.length == 2) {
                return data[key];
            }
            else {
                return (data[key] = value);
            }
        }
    },

    isElement: true,

    constructor: function(dom) {
        if (typeof dom == 'string') {
            dom = document.getElementById(dom);
        }

        if (!dom) {
            throw new Error("Invalid domNode reference or an id of an existing domNode: " + dom);
        }

        /**
         * The DOM element
         * @property dom
         * @type HTMLElement
         */
        this.dom = dom;

        this.getUniqueId();
    },

    attach: function (dom) {
        this.dom = dom;
        this.id = dom.id;
        return this;
    },

    getUniqueId: function() {
        var id = this.id,
            dom;

        if (!id) {
            dom = this.dom;

            if (dom.id.length > 0) {
                this.id = id = dom.id;
            }
            else {
                dom.id = id = this.mixins.identifiable.getUniqueId.call(this);
            }

            this.self.cache[id] = this;
        }

        return id;
    },

    setId: function(id) {
        var currentId = this.id,
            cache = this.self.cache;

        if (currentId) {
            delete cache[currentId];
        }

        this.dom.id = id;

        /**
         * The DOM element ID
         * @property id
         * @type String
         */
        this.id = id;

        cache[id] = this;

        return this;
    },

    /**
     * Sets the innerHTML of this element.
     * @param {String} html The new HTML
     */
    setHtml: function(html) {
        this.dom.innerHTML = html;
    },

    /**
     * Returns the innerHTML of an Element.
     * @return {String}
     */
    getHtml: function() {
        return this.dom.innerHTML;
    },

    setText: function(text) {
        this.dom.textContent = text;
    },

    redraw: function() {
        var dom = this.dom,
            domStyle = dom.style;

        domStyle.display = 'none';
        dom.offsetHeight;
        domStyle.display = '';
    },

    isPainted: function() {
        return Boolean(this.dom.offsetParent);
    },

    /**
     * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
     * @param {Object} attributes The object with the attributes
     * @param {Boolean} [useSet=true] false to override the default setAttribute to use expandos.
     * @return {Ext.dom.Element} this
     */
    set: function(attributes, useSet) {
        var dom = this.dom,
            attribute, value;

        for (attribute in attributes) {
            if (attributes.hasOwnProperty(attribute)) {
                value = attributes[attribute];

                if (attribute == 'style') {
                    this.applyStyles(value);
                }
                else if (attribute == 'cls') {
                    dom.className = value;
                }
                else if (useSet !== false) {
                    if (value === undefined) {
                        dom.removeAttribute(attribute);
                    } else {
                        dom.setAttribute(attribute, value);
                    }
                }
                else {
                    dom[attribute] = value;
                }
            }
        }

        return this;
    },

    /**
     * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
     * @param {String} selector The simple selector to test
     * @return {Boolean} True if this element matches the selector, else false
     */
    is: function(selector) {
        return Ext.DomQuery.is(this.dom, selector);
    },

    /**
     * Returns the value of the "value" attribute
     * @param {Boolean} asNumber true to parse the value as a number
     * @return {String/Number}
     */
    getValue: function(asNumber) {
        var value = this.dom.value;

        return asNumber ? parseInt(value, 10) : value;
    },

    /**
     * Returns the value of an attribute from the element's underlying DOM node.
     * @param {String} name The attribute name
     * @param {String} [namespace] The namespace in which to look for the attribute
     * @return {String} The attribute value
     */
    getAttribute: function(name, namespace) {
        var dom = this.dom;

        return dom.getAttributeNS(namespace, name) || dom.getAttribute(namespace + ":" + name)
               || dom.getAttribute(name) || dom[name];
    },

    /**
     * Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode}
     */
    destroy: function() {
        this.isDestroyed = true;

        var cache = Ext.Element.cache,
            dom = this.dom;

        if (dom && dom.parentNode && dom.tagName != 'BODY') {
            dom.parentNode.removeChild(dom);
        }

        delete cache[this.id];
        delete this.dom;
    }

}, function(Element) {
    Ext.elements = Ext.cache = Element.cache;

    this.addStatics({
        Fly: new Ext.Class({
            extend: Element,

            constructor: function(dom) {
                this.dom = dom;
            }
        }),

        _flyweights: {},

        /**
         * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference
         * to this element - the dom node can be overwritten by other code. {@link Ext#fly} is alias for
         * {@link Ext.dom.Element#fly}.
         *
         * Use this to make one-time references to DOM elements which are not going to be accessed again either by
         * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link
         * Ext#get Ext.get} will be more appropriate to take advantage of the caching provided by the Ext.dom.Element
         * class.
         *
         * @param {String/HTMLElement} element The dom node or id
         * @param {String} [named] Allows for creation of named reusable flyweights to prevent conflicts (e.g.
         * internally Ext uses "_global")
         * @return {Ext.dom.Element} The shared Element object (or null if no matching element was found)
         * @static
         */
        fly: function(element, named) {
            var fly = null,
                flyweights = Element._flyweights;

            named = named || '_global';

            element = Ext.getDom(element);

            if (element) {
                fly = flyweights[named] || (flyweights[named] = new Element.Fly());
                fly.dom = element;
                fly.isSynchronized = false;
            }

            return fly;
        }
    });

    /**
     * @member Ext
     * @method get
     * @alias Ext.dom.Element#get
     */
    Ext.get = function(element) {
        return Element.get.call(Element, element);
    };

    /**
     * @member Ext
     * @method fly
     * @alias Ext.dom.Element#fly
     */
    Ext.fly = function() {
        return Element.fly.apply(Element, arguments);
    };

    Ext.ClassManager.onCreated(function() {
        Element.mixin('observable', Ext.mixin.Observable);
    }, null, 'Ext.mixin.Observable');


});

/**
 * @class Ext.dom.Element
 */
Ext.dom.Element.addStatics({
    unitRe: /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
    camelRe: /(-[a-z])/gi,
    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'},

    /**
     * Test if size has a unit, otherwise appends the passed unit string, or the default for this Element.
     * @param size {Object} The size to set
     * @param units {String} The units to append to a numeric size value
     * @private
     * @static
     */
    addUnits: function(size, units) {
        // Most common case first: Size is set to a number
        if (Ext.isNumber(size)) {
            return size + (units || this.defaultUnit || 'px');
        }

        // Size set to a value which means "auto"
        if (size === "" || size == "auto" || size === undefined || size === null) {
            return size || '';
        }

        // Otherwise, warn if it's not a valid CSS measurement
        if (!this.unitRe.test(size)) {
            Ext.Logger.warn("Warning, size detected as NaN on Element.addUnits.");
            return size || '';
        }
        return size;
    },

    /**
     * @static
     * @private
     */
    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;
    },

    /**
     * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
     * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
     * @static
     * @param {Number/String} box The encoded margins
     * @return {Object} An object with margin sizes for top, right, bottom and left
     */
    parseBox: function(box) {
        if (typeof box != 'string') {
            box = box.toString();
        }

        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: parseFloat(parts[0]) || 0,
            right: parseFloat(parts[1]) || 0,
            bottom: parseFloat(parts[2]) || 0,
            left: parseFloat(parts[3]) || 0
        };
    },

    /**
     * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
     * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
     * @static
     * @param {Number/String} box The encoded margins
     * @param {String} units The type of units to add
     * @return {String} An string with unitized (px if units is not specified) metrics for top, right, bottom and left
     */
    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);

    },

    // private
    camelReplaceFn: function(m, a) {
        return a.charAt(1).toUpperCase();
    },

    /**
     * Normalizes CSS property keys from dash delimited to camel case JavaScript Syntax.
     * For example:
     *
     * - border-width -> borderWidth
     * - padding-top -> paddingTop
     *
     * @static
     * @param {String} prop The property to normalize
     * @return {String} The normalized string
     */
    normalize: function(prop) {
        // TODO: Mobile optimization?
//        if (prop == 'float') {
//            prop = Ext.supports.Float ? 'cssFloat' : 'styleFloat';
//        }
        return this.propertyCache[prop] || (this.propertyCache[prop] = prop.replace(this.camelRe, this.camelReplaceFn));
    },

    /**
     * Returns the top Element that is located at the passed coordinates
     * @static
     * @param {Number} x The x coordinate
     * @param {Number} y The y coordinate
     * @return {String} The found Element
     */
    fromPoint: function(x, y) {
        return Ext.get(document.elementFromPoint(x, y));
    },

    /**
     * Converts a CSS string into an object with a property for each style.
     *
     * The sample code below would return an object with 2 properties, one
     * for background-color and one for color.
     *
     *     var css = 'background-color: red;color: blue; ';
     *     console.log(Ext.dom.Element.parseStyles(css));
     *
     * @static
     * @param {String} styles A CSS string
     * @return {Object} styles
     */
    parseStyles: function(styles) {
        var out = {},
            cssRe = this.cssRe,
            matches;

        if (styles) {
            // Since we're using the g flag on the regex, we need to set the lastIndex.
            // This automatically happens on some implementations, but not others, see:
            // http://stackoverflow.com/questions/2645273/javascript-regular-expression-literal-persists-between-function-calls
            // http://blog.stevenlevithan.com/archives/fixing-javascript-regexp
            cssRe.lastIndex = 0;
            while ((matches = cssRe.exec(styles))) {
                out[matches[1]] = matches[2];
            }
        }
        return out;
    }
});


/**
 * @class Ext.dom.Element
 */

/**
 * @class Ext.dom.Element
 */
Ext.dom.Element.addMembers({

    /**
     * Appends the passed element(s) to this element
     * @param {HTMLElement/Ext.dom.Element} element a DOM Node or an existing Element.
     * @return {Ext.dom.Element} This element
     */
    appendChild: function(element) {
        this.dom.appendChild(Ext.getDom(element));

        return this;
    },

    removeChild: function(element) {
        this.dom.removeChild(Ext.getDom(element));

        return this;
    },

    append: function() {
        this.appendChild.apply(this, arguments);
    },

    /**
     * Appends this element to the passed element
     * @param {String/HTMLElement/Ext.dom.Element} el The new parent element.
     * The id of the node, a DOM Node or an existing Element.
     * @return {Ext.dom.Element} This element
     */
    appendTo: function(el) {
        Ext.getDom(el).appendChild(this.dom);
        return this;
    },

    /**
     * Inserts this element before the passed element in the DOM
     * @param {String/HTMLElement/Ext.dom.Element} el The element before which this element will be inserted.
     * The id of the node, a DOM Node or an existing Element.
     * @return {Ext.dom.Element} This element
     */
    insertBefore: function(el) {
        el = Ext.getDom(el);
        el.parentNode.insertBefore(this.dom, el);
        return this;
    },

    /**
     * Inserts this element after the passed element in the DOM
     * @param {String/HTMLElement/Ext.dom.Element} el The element to insert after.
     * The id of the node, a DOM Node or an existing Element.
     * @return {Ext.dom.Element} This element
     */
    insertAfter: function(el) {
        el = Ext.getDom(el);
        el.parentNode.insertBefore(this.dom, el.nextSibling);
        return this;
    },


    /**
     * Inserts an element as the first child of this element
     * @param {String/HTMLElement/Ext.dom.Element} element The id or element to insert
     * @return {Ext.dom.Element} this
     */
    insertFirst: function(element) {
        var elementDom = Ext.getDom(element),
            dom = this.dom,
            firstChild = dom.firstChild;

        if (!firstChild) {
            dom.appendChild(elementDom);
        }
        else {
            dom.insertBefore(elementDom, firstChild);
        }

        return this;
    },

    /**
     * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
     * @param {String/HTMLElement/Ext.dom.Element/Object/Array} el The id, element to insert or a DomHelper config
     * to create and insert *or* an array of any of those.
     * @param {String} where (optional) 'before' or 'after' defaults to before
     * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.dom.Element
     * @return {Ext.dom.Element} The inserted Element. If an array is passed, the last inserted element is returned.
     */
    insertSibling: function(el, where, returnDom) {
        var me = this, rt,
            isAfter = (where || 'before').toLowerCase() == 'after',
            insertEl;

        if (Ext.isArray(el)) {
            insertEl = me;
            Ext.each(el, function(e) {
                rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
                if (isAfter) {
                    insertEl = rt;
                }
            });
            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 = Ext.core.DomHelper.append(me.dom.parentNode, el, !returnDom);
            } else {
                rt = Ext.core.DomHelper[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
            }
        }
        return rt;
    },

    /**
     * Replaces the passed element with this element
     * @param {String/HTMLElement/Ext.dom.Element} el The element to replace.
     * The id of the node, a DOM Node or an existing Element.
     * @return {Ext.dom.Element} This element
     */
    replace: function(el) {
        el = Ext.get(el);
        this.insertBefore(el);
        el.remove();
        return this;
    },

    /**
     * Replaces this element with the passed element
     * @param {String/HTMLElement/Ext.dom.Element/Object} el The new element (id of the node, a DOM Node
     * or an existing Element) or a DomHelper config of an element to create
     * @return {Ext.dom.Element} This element
     */
    replaceWith: function(el) {
        var me = this;

        if (el.nodeType || el.dom || typeof el == 'string') {
            el = Ext.get(el);
            me.dom.parentNode.insertBefore(el, 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.Element.addToCache(me.isFlyweight ? new Ext.dom.Element(me.dom) : me);
        return me;
    },

    /**
     * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
     * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
     * automatically generated with the specified attributes.
     * @param {HTMLElement} insertBefore (optional) a child element of this element
     * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
     * @return {Ext.dom.Element} The new child element
     */
    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[!this.dom.firstChild ? 'insertFirst' : 'append'](this.dom, config, returnDom !== true);
        }
    },

    /**
     * Creates and wraps this element with another element
     * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
     * @param {Boolean} domNode (optional) True to return the raw DOM element instead of Ext.dom.Element
     * @return {HTMLElement/Ext.dom.Element} The newly created wrapper element
     */
    wrap: function(config, domNode) {
        var dom = this.dom,
            wrapper = this.self.create(config, domNode),
            wrapperDom = (domNode) ? wrapper : wrapper.dom,
            parentNode = dom.parentNode;

        if (parentNode) {
            parentNode.insertBefore(wrapperDom, dom);
        }

        wrapperDom.appendChild(dom);

        return wrapper;
    },

    wrapAllChildren: function(config) {
        var dom = this.dom,
            children = dom.childNodes,
            wrapper = this.self.create(config),
            wrapperDom = wrapper.dom;

        while (children.length > 0) {
            wrapperDom.appendChild(dom.firstChild);
        }

        dom.appendChild(wrapperDom);

        return wrapper;
    },

    unwrapAllChildren: function() {
        var dom = this.dom,
            children = dom.childNodes,
            parentNode = dom.parentNode;

        if (parentNode) {
            while (children.length > 0) {
                parentNode.insertBefore(dom, dom.firstChild);
            }

            this.destroy();
        }
    },

    unwrap: function() {
        var dom = this.dom,
            parentNode = dom.parentNode,
            grandparentNode;

        if (parentNode) {
            grandparentNode = parentNode.parentNode;
            grandparentNode.insertBefore(dom, parentNode);
            grandparentNode.removeChild(parentNode);
        }
        else {
            grandparentNode = document.createDocumentFragment();
            grandparentNode.appendChild(dom);
        }

        return this;
    },

    /**
     * Inserts an html fragment into this element
     * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
     * See {@link Ext.DomHelper#insertHtml} for details.
     * @param {String} html The HTML fragment
     * @param {Boolean} returnEl (optional) True to return an Ext.dom.Element (defaults to false)
     * @return {HTMLElement/Ext.dom.Element} The inserted node (or nearest related if more than 1 inserted)
     */
    insertHtml: function(where, html, returnEl) {
        var el = Ext.core.DomHelper.insertHtml(where, this.dom, html);
        return returnEl ? Ext.get(el) : el;
    }
});

/**
 * @class Ext.dom.Element
 */
Ext.dom.Element.override({

    /**
     * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
     * @return {Number} The X position of the element
     */
    getX: function(el) {
        return this.getXY(el)[0];
    },

    /**
     * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
     * @return {Number} The Y position of the element
     */
    getY: function(el) {
        return this.getXY(el)[1];
    },

    /**
     * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
     * @return {Array} The XY position of the element
     */

    getXY: function() {
        var webkitConvert = window.webkitConvertPointFromNodeToPage;

        if (webkitConvert) {
            return function() {
                var point = webkitConvert(this.dom, new WebKitPoint(0, 0));
                return [point.x, point.y];
            }
        }
        else return function() {
            var rect = this.dom.getBoundingClientRect(),
                round = Math.round;

            return [round(rect.left + window.pageXOffset), round(rect.top + window.pageYOffset)];
        }
    }(),

    /**
     * Returns the offsets of this element from the passed element. Both element must be part of the DOM tree
     * and not have display:none to have page coordinates.
     * @param {Mixed} element The element to get the offsets from.
     * @return {Array} The XY page offsets (e.g. [100, -200])
     */
    getOffsetsTo: function(el) {
        var o = this.getXY(),
            e = Ext.fly(el, '_internal').getXY();
        return [o[0] - e[0], o[1] - e[1]];
    },

    /**
     * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
     * @param {Number} The X position of the element
     * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
     * @return {Ext.dom.Element} this
     */
    setX: function(x) {
        return this.setXY([x, this.getY()]);
    },

    /**
     * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
     * @param {Number} The Y position of the element
     * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
     * @return {Ext.dom.Element} this
     */
    setY: function(y) {
        return this.setXY([this.getX(), y]);
    },

    /**
     * Sets the position of the element in page coordinates, regardless of how the element is positioned.
     * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
     * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
     * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
     * @return {Ext.dom.Element} this
     */
    setXY: function(pos) {
        var me = this;

        if (arguments.length > 1) {
            pos = [pos, arguments[1]];
        }

        // me.position();
        var pts = me.translatePoints(pos),
            style = me.dom.style;

        for (pos in pts) {
            if (!pts.hasOwnProperty(pos)) {
                continue;
            }
            if (!isNaN(pts[pos])) style[pos] = pts[pos] + "px";
        }
        return me;
    },

    /**
     * Gets the left X coordinate
     * @return {Number}
     */
    getLeft: function() {
        return parseInt(this.getStyle('left'), 10) || 0;
    },

    /**
     * Gets the right X coordinate of the element (element X position + element width)
     * @return {Number}
     */
    getRight: function() {
        return parseInt(this.getStyle('right'), 10) || 0;
    },

    /**
     * Gets the top Y coordinate
     * @return {Number}
     */
    getTop: function() {
        return parseInt(this.getStyle('top'), 10) || 0;
    },

    /**
     * Gets the bottom Y coordinate of the element (element Y position + element height)
     * @return {Number}
     */
    getBottom: function() {
        return parseInt(this.getStyle('bottom'), 10) || 0;
    },

    /**
     * Translates the passed page coordinates into left/top css values for this element
     * @param {Number/Array} x The page x or an array containing [x, y]
     * @param {Number} y (optional) The page y, required if x is not an array
     * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
     */
    translatePoints: function(x, y) {
        y = isNaN(x[1]) ? y : x[1];
        x = isNaN(x[0]) ? x : x[0];

        var me = this,
            relative = me.isStyle('position', 'relative'),
            o = me.getXY(),
            l = parseInt(me.getStyle('left'), 10),
            t = parseInt(me.getStyle('top'), 10);

        l = !isNaN(l) ? l : (relative ? 0 : me.dom.offsetLeft);
        t = !isNaN(t) ? t : (relative ? 0 : me.dom.offsetTop);

        return {left: (x - o[0] + l), top: (y - o[1] + t)};
    },

    /**
     * Sets the element's box. Use getBox() on another element to get a box object.
     * @param {Object} box The box to fill, for example:
     *
     *      {
     *          left: ...,
     *          top: ...,
     *          width: ...,
     *          height: ...
     *      }
     *
     * @return {Ext.dom.Element} this
     */
    setBox: function(box) {
        var me = this,
            width = box.width,
            height = box.height,
            top = box.top,
            left = box.left;

        if (left !== undefined) {
            me.setLeft(left);
        }
        if (top !== undefined) {
            me.setTop(top);
        }
        if (width !== undefined) {
            me.setWidth(width);
        }
        if (height !== undefined) {
            me.setHeight(height);
        }

        return this;
    },

    /**
     * Return an object defining the area of this Element which can be passed to {@link #setBox} to
     * set another Element's size/location to match this element.
     * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
     * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
     * @return {Object} box An object in the format<pre><code>
     {
     x: &lt;Element's X position>,
     y: &lt;Element's Y position>,
     width: &lt;Element's width>,
     height: &lt;Element's height>,
     bottom: &lt;Element's lower bound>,
     right: &lt;Element's rightmost bound>
     }
     </code></pre>
     * The returned object may also be addressed as an Array where index 0 contains the X position
     * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
     */
    getBox: function(contentBox, local) {
        var me = this,
            dom = me.dom,
            width = dom.offsetWidth,
            height = dom.offsetHeight,
            xy, box, l, r, t, b;

        if (!local) {
            xy = me.getXY();
        }
        else if (contentBox) {
            xy = [0, 0];
        }
        else {
            xy = [parseInt(me.getStyle("left"), 10) || 0, parseInt(me.getStyle("top"), 10) || 0];
        }

        if (!contentBox) {
            box = {
                x: xy[0],
                y: xy[1],
                0: xy[0],
                1: xy[1],
                width: width,
                height: height
            };
        }
        else {
            l = me.getBorderWidth.call(me, "l") + me.getPadding.call(me, "l");
            r = me.getBorderWidth.call(me, "r") + me.getPadding.call(me, "r");
            t = me.getBorderWidth.call(me, "t") + me.getPadding.call(me, "t");
            b = me.getBorderWidth.call(me, "b") + me.getPadding.call(me, "b");
            box = {
                x: xy[0] + l,
                y: xy[1] + t,
                0: xy[0] + l,
                1: xy[1] + t,
                width: width - (l + r),
                height: height - (t + b)
            };
        }

        box.left = box.x;
        box.top = box.y;
        box.right = box.x + box.width;
        box.bottom = box.y + box.height;

        return box;
    },

    /**
     * Return an object defining the area of this Element which can be passed to {@link #setBox} to
     * set another Element's size/location to match this element.
     * @param {Boolean} asRegion(optional) If true an Ext.util.Region will be returned
     * @return {Object} box An object in the format<pre><code>
     {
     x: &lt;Element's X position>,
     y: &lt;Element's Y position>,
     width: &lt;Element's width>,
     height: &lt;Element's height>,
     bottom: &lt;Element's lower bound>,
     right: &lt;Element's rightmost bound>
     }
     </code></pre>
     * The returned object may also be addressed as an Array where index 0 contains the X position
     * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
     */
    getPageBox: function(getRegion) {
        var me = this,
            el = me.dom,
            w = el.offsetWidth,
            h = el.offsetHeight,
            xy = me.getXY(),
            t = xy[1],
            r = xy[0] + w,
            b = xy[1] + h,
            l = xy[0];

        if (!el) {
            return new Ext.util.Region();
        }

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

/**
 * @class Ext.dom.Element
 */

Ext.dom.Element.addMembers({
    WIDTH: 'width',
    HEIGHT: 'height',
    MIN_WIDTH: 'min-width',
    MIN_HEIGHT: 'min-height',
    MAX_WIDTH: 'max-width',
    MAX_HEIGHT: 'max-height',
    TOP: 'top',
    RIGHT: 'right',
    BOTTOM: 'bottom',
    LEFT: 'left',
    /**
     * @property VISIBILITY
     * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element
     */
    VISIBILITY: 1,

    /**
     * @property DISPLAY
     * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element
     */
    DISPLAY: 2,

    /**
     * @property OFFSETS
     * Visibility mode constant for use with {@link #setVisibilityMode}. Use offsets to hide element
     */
    OFFSETS: 3,

    SEPARATOR: '-',

    trimRe: /^\s+|\s+$/g,
    wordsRe: /\w/g,
    spacesRe: /\s+/,
    styleSplitRe: /\s*(?::|;)\s*/,
    transparentRe: /^(?:transparent|(?:rgba[(](?:\s*\d+\s*[,]){3}\s*0\s*[)]))$/i,
    classNameSplitRegex: /[\s]+/,

    borders: {
        t: 'border-top-width',
        r: 'border-right-width',
        b: 'border-bottom-width',
        l: 'border-left-width'
    },

    paddings: {
        t: 'padding-top',
        r: 'padding-right',
        b: 'padding-bottom',
        l: 'padding-left'
    },

    margins: {
        t: 'margin-top',
        r: 'margin-right',
        b: 'margin-bottom',
        l: 'margin-left'
    },

    /**
     * @property {String} defaultUnit
     * The default unit to append to CSS values where a unit isn't provided.
     */
    defaultUnit: "px",

    isSynchronized: false,

    /**
     * @private
     */
    synchronize: function() {
        var dom = this.dom,
            hasClassMap = {},
            className = dom.className,
            classList, i, ln, name;

        if (className.length > 0) {
            classList = dom.className.split(this.classNameSplitRegex);

            for (i = 0, ln = classList.length; i < ln; i++) {
                name = classList[i];
                hasClassMap[name] = true;
            }
        }
        else {
            classList = [];
        }

        this.classList = classList;

        this.hasClassMap = hasClassMap;

        this.isSynchronized = true;

        return this;
    },

    /**
     * Adds the given CSS class(es) to this Element
     * @param {String} names The CSS class(es) to add to this element
     * @param {String} prefix Optional prefix to prepend to each class
     * @param {String} suffix Optional suffix to append to each class
     */
    addCls: function(names, prefix, suffix) {
        if (!names) {
            return this;
        }

        if (!this.isSynchronized) {
            this.synchronize();
        }

        var dom = this.dom,
            map = this.hasClassMap,
            classList = this.classList,
            SEPARATOR = this.SEPARATOR,
            i, ln, name;

        prefix = prefix ? prefix + SEPARATOR : '';
        suffix = suffix ? SEPARATOR + suffix : '';

        if (typeof names == 'string') {
            names = names.split(this.spacesRe);
        }

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

            if (!map[name]) {
                map[name] = true;
                classList.push(name);
            }
        }

        dom.className = classList.join(' ');

        return this;
    },

    /**
     * Removes the given CSS class(es) from this Element
     * @param {String} names The CSS class(es) to remove from this element
     * @param {String} prefix Optional prefix to prepend to each class to be removed
     * @param {String} suffix Optional suffix to append to each class to be removed
     */
    removeCls: function(names, prefix, suffix) {
        if (!names) {
            return this;
        }

        if (!this.isSynchronized) {
            this.synchronize();
        }

        if (!suffix) {
            suffix = '';
        }

        var dom = this.dom,
            map = this.hasClassMap,
            classList = this.classList,
            SEPARATOR = this.SEPARATOR,
            i, ln, name;

        prefix = prefix ? prefix + SEPARATOR : '';
        suffix = suffix ? SEPARATOR + suffix : '';

        if (typeof names == 'string') {
            names = names.split(this.spacesRe);
        }

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

            if (map[name]) {
                delete map[name];
                Ext.Array.remove(classList, name);
            }
        }

        dom.className = classList.join(' ');

        return this;
    },

    /**
     * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
     * @param {String} oldClassName The CSS class to replace
     * @param {String} newClassName The replacement CSS class
     * @return {Ext.dom.Element} this
     */
    replaceCls: function(oldName, newName, prefix, suffix) {
        return this.removeCls(oldName, prefix, suffix).addCls(newName, prefix, suffix);
    },

    /**
     * Checks if the specified CSS class exists on this element's DOM node.
     * @param {String} className The CSS class to check for
     * @return {Boolean} True if the class exists, else false
     */
    hasCls: function(name) {
        if (!this.isSynchronized) {
            this.synchronize();
        }

        return this.hasClassMap.hasOwnProperty(name);
    },

    /**
     * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
     * @param {String} className The CSS class to toggle
     * @return {Ext.dom.Element} this
     */
    toggleCls: function(className) {
        return this.hasCls(className) ? this.removeCls(className) : this.addCls(className);
    },

    /**
     * Set the width of this Element.
     * @param {Number/String} width The new width.
     * @return {Ext.dom.Element} this
     */
    setWidth: function(width) {
        return this.setLengthValue(this.WIDTH, width);
    },

    /**
     * Set the height of this Element.
     * @param {Number/String} height The new height.
     * @return {Ext.dom.Element} this
     */
    setHeight: function(height) {
        return this.setLengthValue(this.HEIGHT, height);
    },

    /**
     * Set the size of this Element.
     *
     * @param {Number/String} width The new width. This may be one of:
     *
     * - A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).
     * - A String used to set the CSS width style. Animation may **not** be used.
     * - A size object in the format `{width: widthValue, height: heightValue}`.
     *
     * @param {Number/String} height The new height. This may be one of:
     *
     * - A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).
     * - A String used to set the CSS height style. Animation may **not** be used.
     * @return {Ext.dom.Element} this
     */
    setSize: function(width, height) {
        if (Ext.isObject(width)) {
            // in case of object from getSize()
            height = width.height;
            width = width.width;
        }

        this.setWidth(width);
        this.setHeight(height);

        return this;
    },

    /**
     * Set the minimum width of this Element.
     * @param {Number/String} width The new minimum width.
     * @return {Ext.dom.Element} this
     */
    setMinWidth: function(width) {
        return this.setLengthValue(this.MIN_WIDTH, width);
    },

    /**
     * Set the minimum height of this Element.
     * @param {Number/String} height The new minimum height.
     * @return {Ext.dom.Element} this
     */
    setMinHeight: function(height) {
        return this.setLengthValue(this.MIN_HEIGHT, height);
    },

    /**
     * Set the maximum width of this Element.
     * @param {Number/String} width The new maximum width.
     * @return {Ext.dom.Element} this
     */
    setMaxWidth: function(width) {
        return this.setLengthValue(this.MAX_WIDTH, width);
    },

    /**
     * Set the maximum height of this Element.
     * @param {Number/String} height The new maximum height.
     * @return {Ext.dom.Element} this
     */
    setMaxHeight: function(height) {
        return this.setLengthValue(this.MAX_HEIGHT, height);
    },

    /**
     * Sets the element's top position directly using CSS style (instead of {@link #setY}).
     * @param {String} top The top CSS property value
     * @return {Ext.dom.Element} this
     */
    setTop: function(top) {
        return this.setLengthValue(this.TOP, top);
    },

    /**
     * Sets the element's CSS right style.
     * @param {String} right The right CSS property value
     * @return {Ext.dom.Element} this
     */
    setRight: function(right) {
        return this.setLengthValue(this.RIGHT, right);
    },

    /**
     * Sets the element's CSS bottom style.
     * @param {String} bottom The bottom CSS property value
     * @return {Ext.dom.Element} this
     */
    setBottom: function(bottom) {
        return this.setLengthValue(this.BOTTOM, bottom);
    },

    /**
     * Sets the element's left position directly using CSS style (instead of {@link #setX}).
     * @param {String} left The left CSS property value
     * @return {Ext.dom.Element} this
     */
    setLeft: function(left) {
        return this.setLengthValue(this.LEFT, left);
    },

    setMargin: function(margin) {
        var domStyle = this.dom.style;

        if (margin || margin === 0) {
            margin = this.self.unitizeBox((margin === true) ? 5 : margin);
            domStyle.setProperty('margin', margin, 'important');
        }
        else {
            domStyle.removeProperty('margin-top');
            domStyle.removeProperty('margin-right');
            domStyle.removeProperty('margin-bottom');
            domStyle.removeProperty('margin-left');
        }
    },

    setPadding: function(padding) {
        var domStyle = this.dom.style;

        if (padding || padding === 0) {
            padding = this.self.unitizeBox((padding === true) ? 5 : padding);
            domStyle.setProperty('padding', padding, 'important');
        }
        else {
            domStyle.removeProperty('padding-top');
            domStyle.removeProperty('padding-right');
            domStyle.removeProperty('padding-bottom');
            domStyle.removeProperty('padding-left');
        }
    },

    setBorder: function(border) {
        var domStyle = this.dom.style;

        if (border || border === 0) {
            border = this.self.unitizeBox((border === true) ? 1 : border);
            domStyle.setProperty('border-width', border, 'important');
        }
        else {
            domStyle.removeProperty('border-top-width');
            domStyle.removeProperty('border-right-width');
            domStyle.removeProperty('border-bottom-width');
            domStyle.removeProperty('border-left-width');
        }
    },

    setLengthValue: function(name, value) {
        var domStyle = this.dom.style;

        if (value === null) {
            domStyle.removeProperty(name);
            return this;
        }

        if (typeof value == 'number') {
            value = value + 'px';
        }

        domStyle.setProperty(name, value, 'important');
        return this;
    },

    /**
     * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
     * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
     * @param {Boolean} visible Whether the element is visible
     * @return {Ext.Element} this
     */
    setVisible: function(visible) {
        var mode = this.getVisibilityMode(),
            method = visible ? 'removeCls' : 'addCls';

        switch (mode) {
            case this.VISIBILITY:
                this.removeCls(['x-hidden-display', 'x-hidden-offsets']);
                this[method]('x-hidden-visibility');
                break;

            case this.DISPLAY:
                this.removeCls(['x-hidden-visibility', 'x-hidden-offsets']);
                this[method]('x-hidden-display');
                break;

            case this.OFFSETS:
                this.removeCls(['x-hidden-visibility', 'x-hidden-display']);
                this[method]('x-hidden-offsets');
                break;
        }

        return this;
    },

    getVisibilityMode: function() {
        var dom = this.dom,
            mode = Ext.dom.Element.data(dom, 'visibilityMode');

        if (mode === undefined) {
            Ext.dom.Element.data(dom, 'visibilityMode', mode = this.DISPLAY);
        }

        return mode;
    },

    /**
     * Use this to change the visisbiliy mode between {@link #VISIBILITY}, {@link #DISPLAY} or {@link #OFFSETS}.
     */
    setVisibilityMode: function(mode) {
        this.self.data(this.dom, 'visibilityMode', mode);

        return this;
    },

    /**
     * Shows this element.
     * Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
     */
    show: function() {
        var dom = this.dom;
        if (dom) {
            dom.style.removeProperty('display');
        }
    },

    /**
     * Hides this element.
     * Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
     */
    hide: function() {
        var dom = this.dom,
            domStyle = dom.style,
            needsRedraw = Ext.os.is.iOS5;

        if (domStyle.getPropertyValue('display') !== 'none') {
            // iOS5 sometimes has a long delay before redrawing elements with their CSS 'display' set to 'none'
            // This force a redraw to make sure the element is hidden instantly
            if (needsRedraw) {
                domStyle.setProperty('display', 'none', 'important');
                dom.offsetHeight;
                domStyle.removeProperty('display');
                dom.offsetHeight;
            }

            domStyle.setProperty('display', 'none', 'important');
        }

    },

    setVisibility: function(isVisible) {
        var domStyle = this.dom.style;

        if (isVisible) {
            domStyle.removeProperty('visibility');
        }
        else {
            domStyle.setProperty('visibility', 'hidden', 'important');
        }
    },

    /**
     * This shared object is keyed by style name (e.g., 'margin-left' or 'marginLeft'). The
     * values are objects with the following properties:
     *
     *  * `name` (String) : The actual name to be presented to the DOM. This is typically the value
     *      returned by {@link #normalize}.
     *  * `get` (Function) : A hook function that will perform the get on this style. These
     *      functions receive "(dom, el)" arguments. The `dom` parameter is the DOM Element
     *      from which to get ths tyle. The `el` argument (may be null) is the Ext.Element.
     *  * `set` (Function) : A hook function that will perform the set on this style. These
     *      functions receive "(dom, value, el)" arguments. The `dom` parameter is the DOM Element
     *      from which to get ths tyle. The `value` parameter is the new value for the style. The
     *      `el` argument (may be null) is the Ext.Element.
     *
     * The `this` pointer is the object that contains `get` or `set`, which means that
     * `this.name` can be accessed if needed. The hook functions are both optional.
     * @private
     * @markdown
     */
    styleHooks: {},

    // private
    addStyles: function(sides, styles) {
        var totalSize = 0,
            sidesArr = sides.match(this.wordsRe),
            i = 0,
            len = sidesArr.length,
            side, size;
        for (; i < len; i++) {
            side = sidesArr[i];
            size = side && parseInt(this.getStyle(styles[side]), 10);
            if (size) {
                totalSize += Math.abs(size);
            }
        }
        return totalSize;
    },

    /**
     * Checks if the current value of a style is equal to a given value.
     * @param {String} style property whose value is returned.
     * @param {String} value to check against.
     * @return {Boolean} true for when the current value equals the given value.
     */
    isStyle: function(style, val) {
        return this.getStyle(style) == val;
    },

    /**
     * Normalizes currentStyle and computedStyle.
     * @param {String} prop The style property whose value is returned.
     * @return {String} The current value of the style property for this element.
     */
    getStyle: function(prop) {
        var me = this,
            dom = me.dom,
            hook = me.styleHooks[prop],
            cs, result;

        if (dom == document) {
            return null;
        }
        if (!hook) {
            me.styleHooks[prop] = hook = { name: this.self.normalize(prop) };
        }
        if (hook.get) {
            return hook.get(dom, me);
        }

        cs = window.getComputedStyle(dom, '');

        // why the dom.style lookup? It is not true that "style == computedStyle" as
        // well as the fact that 0/false are valid answers...
        result = (cs && cs[hook.name]); // || dom.style[hook.name];

        // Webkit returns rgb values for transparent, how does this work n IE9+
        //        if (!supportsTransparentColor && result == 'rgba(0, 0, 0, 0)') {
        //            result = 'transparent';
        //        }

        return result;
    },

    /**
     * Wrapper for setting style properties, also takes single object parameter of multiple styles.
     * @param {String/Object} property The style property to be set, or an object of multiple styles.
     * @param {String} [value] The value to apply to the given property, or null if an object was passed.
     * @return {Ext.dom.Element} this
     */
    setStyle: function(prop, value) {
        var me = this,
            dom = me.dom,
            hooks = me.styleHooks,
            style = dom.style,
            valueFrom = Ext.valueFrom,
            name, hook;

        // we don't promote the 2-arg form to object-form to avoid the overhead...
        if (typeof prop == 'string') {
            hook = hooks[prop];

            if (!hook) {
                hooks[prop] = hook = { name: Ext.dom.Element.normalize(prop) };
            }
            value = valueFrom(value, '');

            if (hook.set) {
                hook.set(dom, value, me);
            } else {
                style[hook.name] = value;
            }
        }
        else {
            for (name in prop) {
                if (prop.hasOwnProperty(name)) {
                    hook = hooks[name];

                    if (!hook) {
                        hooks[name] = hook = { name: Ext.dom.Element.normalize(name) };
                    }

                    value = valueFrom(prop[name], '');

                    if (hook.set) {
                        hook.set(dom, value, me);
                    }
                    else {
                        style[hook.name] = value;
                    }
                }
            }
        }

        return me;
    },

    /**
     * Returns the offset height of the element
     * @param {Boolean} [contentHeight] true to get the height minus borders and padding
     * @return {Number} The element's height
     */
    getHeight: function(contentHeight) {
        var dom = this.dom,
            height = contentHeight ? (dom.clientHeight - this.getPadding("tb")) : dom.offsetHeight;
        return height > 0 ? height : 0;
    },

    /**
     * Returns the offset width of the element
     * @param {Boolean} [contentWidth] true to get the width minus borders and padding
     * @return {Number} The element's width
     */
    getWidth: function(contentWidth) {
        var dom = this.dom,
            width = contentWidth ? (dom.clientWidth - this.getPadding("lr")) : dom.offsetWidth;
        return width > 0 ? width : 0;
    },

    /**
     * Gets the width of the border(s) for the specified side(s)
     * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
     * passing `'lr'` would get the border **l**eft width + the border **r**ight width.
     * @return {Number} The width of the sides passed added together
     */
    getBorderWidth: function(side) {
        return this.addStyles(side, this.borders);
    },

    /**
     * Gets the width of the padding(s) for the specified side(s)
     * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
     * passing `'lr'` would get the padding **l**eft + the padding **r**ight.
     * @return {Number} The padding of the sides passed added together
     */
    getPadding: function(side) {
        return this.addStyles(side, this.paddings);
    },

    /**
     * More flexible version of {@link #setStyle} for setting style properties.
     * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
     * a function which returns such a specification.
     * @return {Ext.dom.Element} this
     */
    applyStyles: function(styles) {
        if (styles) {
            var dom = this.dom,
                styleType, i, len;

            if (typeof styles == 'function') {
                styles = styles.call();
            }
            styleType = typeof styles;
            if (styleType == 'string') {
                styles = Ext.util.Format.trim(styles).split(this.styleSplitRe);
                for (i = 0, len = styles.length; i < len;) {
                    dom.style[Ext.dom.Element.normalize(styles[i++])] = styles[i++];
                }
            }
            else if (styleType == 'object') {
                this.setStyle(styles);
            }
        }
    },

    /**
     * Returns the size of the element.
     * @param {Boolean} [contentSize] true to get the width/size minus borders and padding
     * @return {Object} An object containing the element's size:
     * @return {Number} return.width
     * @return {Number} return.height
     */
    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)
        };
    },

    /**
     * Forces the browser to repaint this element
     * @return {Ext.dom.Element} this
     */
    repaint: function() {
        var dom = this.dom;
        this.addCls(Ext.baseCSSPrefix + 'repaint');
        setTimeout(function() {
            Ext.fly(dom).removeCls(Ext.baseCSSPrefix + 'repaint');
        }, 1);
        return this;
    },

    /**
     * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
     * then it returns the calculated width of the sides (see getPadding)
     * @param {String} [sides] Any combination of l, r, t, b to get the sum of those sides
     * @return {Object/Number}
     */
    getMargin: function(side) {
        var me = this,
            hash = {t: "top", l: "left", r: "right", b: "bottom"},
            o = {},
            key;

        if (!side) {
            for (key in me.margins) {
                o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
            }
            return o;
        } else {
            return me.addStyles.call(me, side, me.margins);
        }
    }
});


/**
 * @class Ext.dom.Element
 */
Ext.dom.Element.addMembers({
    getParent: function() {
        return Ext.get(this.dom.parentNode);
    },

    getFirstChild: function() {
        return Ext.get(this.dom.firstElementChild);
    },

    /**
     * Returns true if this element is an ancestor of the passed element
     * @param {HTMLElement/String} element The element to check
     * @return {Boolean} True if this element is an ancestor of el, else false
     */
    contains: function(element) {
        if (!element) {
            return false;
        }

        var dom = Ext.getDom(element);

        // we need el-contains-itself logic here because isAncestor does not do that:
        return (dom === this.dom) || this.self.isAncestor(this.dom, dom);
    },

    /**
     * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
     * @param {String} selector The simple selector to test
     * @param {Number/String/HTMLElement/Ext.Element} maxDepth (optional)
     * The max depth to search as a number or element (defaults to 50 || document.body)
     * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
     * @return {HTMLElement} The matching DOM node (or null if no match was found)
     */
    findParent: function(simpleSelector, maxDepth, returnEl) {
        var p = this.dom,
            b = document.body,
            depth = 0,
            stopEl;

        maxDepth = maxDepth || 50;
        if (isNaN(maxDepth)) {
            stopEl = Ext.getDom(maxDepth);
            maxDepth = Number.MAX_VALUE;
        }
        while (p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl) {
            if (Ext.DomQuery.is(p, simpleSelector)) {
                return returnEl ? Ext.get(p) : p;
            }
            depth++;
            p = p.parentNode;
        }
        return null;
    },

    /**
     * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
     * @param {String} selector The simple selector to test
     * @param {Number/String/HTMLElement/Ext.Element} maxDepth (optional)
     * The max depth to search as a number or element (defaults to 10 || document.body)
     * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
     * @return {HTMLElement} The matching DOM node (or null if no match was found)
     */
    findParentNode: function(simpleSelector, maxDepth, returnEl) {
        var p = Ext.fly(this.dom.parentNode, '_internal');
        return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
    },

    /**
     * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
     * This is a shortcut for findParentNode() that always returns an Ext.dom.Element.
     * @param {String} selector The simple selector to test
     * @param {Number/String/HTMLElement/Ext.Element} maxDepth (optional)
     * The max depth to search as a number or element (defaults to 10 || document.body)
     * @return {Ext.dom.Element} The matching DOM node (or null if no match was found)
     */
    up: function(simpleSelector, maxDepth) {
        return this.findParentNode(simpleSelector, maxDepth, true);
    },

    select: function(selector, composite) {
        return Ext.dom.Element.select(selector, this.dom, composite);
    },

    /**
     * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
     * @param {String} selector The CSS selector
     * @return {HTMLElement[]} An array of the matched nodes
     */
    query: function(selector) {
        return Ext.DomQuery.select(selector, this.dom);
    },

    /**
     * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
     * @param {String} selector The CSS selector
     * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.dom.Element (defaults to false)
     * @return {HTMLElement/Ext.dom.Element} The child Ext.dom.Element (or DOM node if returnDom = true)
     */
    down: function(selector, returnDom) {
        var n = Ext.DomQuery.selectNode(selector, this.dom);
        return returnDom ? n : Ext.get(n);
    },

    /**
     * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
     * @param {String} selector The CSS selector
     * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.dom.Element (defaults to false)
     * @return {HTMLElement/Ext.dom.Element} The child Ext.dom.Element (or DOM node if returnDom = true)
     */
    child: function(selector, returnDom) {
        var node,
            me = this,
            id;
        id = Ext.get(me).id;
        // Escape . or :
        id = id.replace(/[\.:]/g, "\\$0");
        node = Ext.DomQuery.selectNode('#' + id + " > " + selector, me.dom);
        return returnDom ? node : Ext.get(node);
    },

     /**
     * Gets the parent node for this element, optionally chaining up trying to match a selector
     * @param {String} selector (optional) Find a parent node that matches the passed simple selector
     * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.dom.Element
     * @return {Ext.dom.Element/HTMLElement} The parent node or null
     */
    parent: function(selector, returnDom) {
        return this.matchNode('parentNode', 'parentNode', selector, returnDom);
    },

     /**
     * Gets the next sibling, skipping text nodes
     * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
     * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.dom.Element
     * @return {Ext.dom.Element/HTMLElement} The next sibling or null
     */
    next: function(selector, returnDom) {
        return this.matchNode('nextSibling', 'nextSibling', selector, returnDom);
    },

    /**
     * Gets the previous sibling, skipping text nodes
     * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
     * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.dom.Element
     * @return {Ext.dom.Element/HTMLElement} The previous sibling or null
     */
    prev: function(selector, returnDom) {
        return this.matchNode('previousSibling', 'previousSibling', selector, returnDom);
    },


    /**
     * Gets the first child, skipping text nodes
     * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
     * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.dom.Element
     * @return {Ext.dom.Element/HTMLElement} The first child or null
     */
    first: function(selector, returnDom) {
        return this.matchNode('nextSibling', 'firstChild', selector, returnDom);
    },

    /**
     * Gets the last child, skipping text nodes
     * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
     * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.dom.Element
     * @return {Ext.dom.Element/HTMLElement} The last child or null
     */
    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);
    }
});

/**
 * This class encapsulates a *collection* of DOM elements, providing methods to filter members, or to perform collective
 * actions upon the whole set.
 *
 * Although they are not listed, this class supports all of the methods of {@link Ext.dom.Element} and
 * {@link Ext.Anim}. The methods from these classes will be performed on all the elements in this collection.
 *
 * Example:
 *
 *     var els = Ext.select("#some-el div.some-class");
 *     // or select directly from an existing element
 *     var el = Ext.get('some-el');
 *     el.select('div.some-class');
 *
 *     els.setWidth(100); // all elements become 100 width
 *     els.hide(true); // all elements fade out and hide
 *     // or
 *     els.setWidth(100).hide(true);
 */
Ext.define('Ext.dom.CompositeElementLite', {
    alternateClassName: ['Ext.CompositeElementLite', 'Ext.CompositeElement'],

    requires: ['Ext.dom.Element'],

    statics: {
        /**
         * @private
         * @static
         * Copies all of the functions from Ext.dom.Element's prototype onto CompositeElementLite's prototype.
         */
        importElementMethods: function() {

        }
    },

    constructor: function(elements, root) {
        /**
         * @property {HTMLElement[]} elements
         * The Array of DOM elements which this CompositeElement encapsulates. Read-only.
         *
         * This will not *usually* be accessed in developers' code, but developers wishing to augment the capabilities
         * of the CompositeElementLite class may use it when adding methods to the class.
         *
         * For example to add the `nextAll` method to the class to **add** all following siblings of selected elements,
         * the code would be
         *
         *     Ext.override(Ext.dom.CompositeElementLite, {
         *         nextAll: function() {
         *             var elements = this.elements, i, l = elements.length, n, r = [], ri = -1;
         *
         *             // Loop through all elements in this Composite, accumulating
         *             // an Array of all siblings.
         *             for (i = 0; i < l; i++) {
         *                 for (n = elements[i].nextSibling; n; n = n.nextSibling) {
         *                     r[++ri] = n;
         *                 }
         *             }
         *
         *             // Add all found siblings to this Composite
         *             return this.add(r);
         *         }
         *     });
         *
         */
        this.elements = [];
        this.add(elements, root);
        this.el = new Ext.dom.Element.Fly();
    },

    isComposite: true,

    // private
    getElement: function(el) {
        // Set the shared flyweight dom property to the current element
        return this.el.attach(el);
    },

    // private
    transformElement: function(el) {
        return Ext.getDom(el);
    },

    /**
     * Returns the number of elements in this Composite.
     * @return {Number}
     */
    getCount: function() {
        return this.elements.length;
    },

    /**
     * Adds elements to this Composite object.
     * @param {HTMLElement[]/Ext.dom.CompositeElementLite} els Either an Array of DOM elements to add, or another Composite
     * object who's elements should be added.
     * @return {Ext.dom.CompositeElementLite} This Composite object.
     */
    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;

        for (i = 0; i < ln; i++) {
            element = elements[i];

            if (element) {
                Ext.dom.Element.prototype[fn].apply(this.getElement(element), args);
            }
        }
        return this;
    },

    /**
     * Returns a flyweight Element of the dom element object at the specified index
     * @param {Number} index
     * @return {Ext.dom.Element}
     */
    item: function(index) {
        var el = this.elements[index],
            out = null;

        if (el) {
            out = this.getElement(el);
        }

        return out;
    },

    // fixes scope with flyweight
    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;
    },
    /**
     * Calls the passed function for each element in this composite.
     * @param {Function} fn The function to call.
     * @param {Ext.dom.Element} fn.el The current Element in the iteration. **This is the flyweight
     * (shared) Ext.dom.Element instance, so if you require a a reference to the dom node, use el.dom.**
     * @param {Ext.dom.CompositeElementLite} fn.c This Composite object.
     * @param {Number} fn.index The zero-based index in the iteration.
     * @param {Object} [scope] The scope (this reference) in which the function is executed.
     * Defaults to the Element.
     * @return {Ext.dom.CompositeElementLite} 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;
    },

    /**
     * Clears this Composite and adds the elements passed.
     * @param {HTMLElement[]/Ext.dom.CompositeElementLite} els Either an array of DOM elements, or another Composite from which
     * to fill this Composite.
     * @return {Ext.dom.CompositeElementLite} this
     */
    fill: function(els) {
        var me = this;
        me.elements = [];
        me.add(els);
        return me;
    },

    /**
     * Filters this composite to only elements that match the passed selector.
     * @param {String/Function} selector A string CSS selector or a comparison function. The comparison function will be
     * called with the following arguments:
     * @param {Ext.dom.Element} selector.el The current DOM element.
     * @param {Number} selector.index The current index within the collection.
     * @return {Ext.dom.CompositeElementLite} this
     */
    filter: function(selector) {
        var els = [],
                me = this,
                fn = Ext.isFunction(selector) ? selector
                        : function(el) {
                    return el.is(selector);
                };

        me.each(function(el, self, i) {
            if (fn(el, i) !== false) {
                els[els.length] = me.transformElement(el);
            }
        });

        me.elements = els;
        return me;
    },

    /**
     * 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) {
        return Ext.Array.indexOf(this.elements, this.transformElement(el));
    },

    /**
     * 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.
     * @return {Ext.dom.CompositeElementLite} this
     */
    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;
    },

    /**
     * Removes all elements.
     */
    clear: function() {
        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;

        Ext.each(els, function(e) {
            yels.push(Ext.get(e));
        });

        return this;
    },

    /**
     * Returns the first Element
     * @return {Ext.dom.Element}
     */
    first: function() {
        return this.item(0);
    },

    /**
     * Returns the last Element
     * @return {Ext.dom.Element}
     */
    last: function() {
        return this.item(this.getCount() - 1);
    },

    /**
     * Returns true if this composite contains the passed element
     * @param {String/HTMLElement/Ext.Element/Number} el The id of an element, or an Ext.Element, or an HtmlElement to
     * find within the composite collection.
     * @return {Boolean}
     */
    contains: function(el) {
        return this.indexOf(el) != -1;
    },

    /**
     * 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
     * @return {Ext.dom.CompositeElementLite} this
     */
    removeElement: function(keys, removeDom) {
        var me = this,
                elements = this.elements,
                el;

        Ext.each(keys, function(val) {
            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 this;
    }

}, function() {
    var Element = Ext.dom.Element,
        elementPrototype = Element.prototype,
        prototype = this.prototype,
        name;

    for (name in elementPrototype) {
        if (typeof elementPrototype[name] == 'function'){
            (function(key) {
                prototype[key] = prototype[key] || function() {
                    return this.invoke(key, arguments);
                };
            }).call(prototype, name);
        }
    }

    prototype.on = prototype.addListener;

    if (Ext.DomQuery){
        Element.selectorFunction = Ext.DomQuery.select;
    }

    /**
     * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
     * to be applied to many related elements in one statement through the returned
     * {@link Ext.dom.CompositeElementLite CompositeElementLite} object.
     * @param {String/HTMLElement[]} selector The CSS selector or an array of elements
     * @param {HTMLElement/String} [root] The root element of the query or id of the root
     * @return {Ext.dom.CompositeElementLite}
     * @member Ext.dom.Element
     * @method select
     */
   Element.select = function(selector, root) {
        var elements;

        if (typeof selector == "string") {
            elements = Element.selectorFunction(selector, root);
        }
        else if (selector.length !== undefined) {
            elements = selector;
        }
        else {
            throw new Error("[Ext.select] Invalid selector specified: " + selector);
        }

        return new Ext.CompositeElementLite(elements);
    };

    /**
     * @member Ext
     * @method select
     * @alias Ext.dom.Element#select
     */
    Ext.select = function() {
        return Element.select.apply(Element, arguments);
    };
});



/*

This file is part of Sencha Touch 2

Copyright (c) 2012 Sencha Inc

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

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

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

*/
/**
 * @private
 *
 * <p>Provides a registry of all Components (instances of {@link Ext.Component} or any subclass
 * thereof) on a page so that they can be easily accessed by {@link Ext.Component component}
 * {@link Ext.Component#getId id} (see {@link #get}, or the convenience method {@link Ext#getCmp Ext.getCmp}).</p>
 * <p>This object also provides a registry of available Component <i>classes</i>
 * indexed by a mnemonic code known as the Component's `xtype`.
 * The <code>xtype</code> provides a way to avoid instantiating child Components
 * when creating a full, nested config object for a complete Ext page.</p>
 * <p>A child Component may be specified simply as a <i>config object</i>
 * as long as the correct `xtype` is specified so that if and when the Component
 * needs rendering, the correct type can be looked up for lazy instantiation.</p>
 * <p>For a list of all available `xtype`, see {@link Ext.Component}.</p>
 */
Ext.define('Ext.ComponentManager', {
    alternateClassName: 'Ext.ComponentMgr',
    singleton: true,

    constructor: function() {
        var map = {};

        // The sole reason for this is just to support the old code of ComponentQuery
        this.all = {
            map: map,

            getArray: function() {
                var list = [],
                    id;

                for (id in map) {
                    list.push(map[id]);
                }

                return list;
            }
        };

        this.map = map;
    },

    /**
     * Registers an item to be managed
     * @param {Object} component The item to register
     */
    register: function(component) {
        this.map[component.getId()] = component;
    },

    /**
     * Unregisters an item by removing it from this manager
     * @param {Object} component The item to unregister
     */
    unregister: function(component) {
        delete this.map[component.getId()];
    },

    /**
     * Checks if an item type is registered.
     * @param {String} component The mnemonic string by which the class may be looked up
     * @return {Boolean} Whether the type is registered.
     */
    isRegistered : function(component){
        return this.map[component] !== undefined;
    },

    /**
     * Returns an item by id.
     * For additional details see {@link Ext.util.HashMap#get}.
     * @param {String} id The id of the item
     * @return {Object} The item, undefined if not found.
     */
    get: function(id) {
        return this.map[id];
    },

    /**
     * Creates a new Component from the specified config object using the
     * config object's xtype to determine the class to instantiate.
     * @param {Object} config A configuration object for the Component you wish to create.
     * @param {Function} defaultType (optional) The constructor to provide the default Component type if
     * the config object does not contain a <code>xtype</code>. (Optional if the config contains a <code>xtype</code>).
     * @return {Ext.Component} The newly instantiated Component.
     */
    create: function(component, defaultType) {
        if (component.isComponent) {
            return component;
        }
        else if (Ext.isString(component)) {
            return Ext.createByAlias('widget.' + component);
        }
        else {
            var type = component.xtype || defaultType;

            return Ext.createByAlias('widget.' + type, component);
        }
    },

    registerType: Ext.emptyFn
});

/**
 * @class Ext.ComponentQuery
 * @extends Object
 * @singleton
 *
 * Provides searching of Components within Ext.ComponentManager (globally) or a specific
 * Ext.container.Container on the document with a similar syntax to a CSS selector.
 *
 * Components can be retrieved by using their {@link Ext.Component xtype} with an optional . prefix
 *
 * - `component` or `.component`
 * - `gridpanel` or `.gridpanel`
 *
 * An itemId or id must be prefixed with a #
 *
 * - `#myContainer`
 *
 * Attributes must be wrapped in brackets
 *
 * - `component[autoScroll]`
 * - `panel[title="Test"]`
 *
 * Member expressions from candidate Components may be tested. If the expression returns a *truthy* value,
 * the candidate Component will be included in the query:
 *
 *     var disabledFields = myFormPanel.query("{isDisabled()}");
 *
 * Pseudo classes may be used to filter results in the same way as in {@link Ext.DomQuery DomQuery}:
 *
 *     // Function receives array and returns a filtered array.
 *     Ext.ComponentQuery.pseudos.invalid = function(items) {
 *         var i = 0, l = items.length, c, result = [];
 *         for (; i < l; i++) {
 *             if (!(c = items[i]).isValid()) {
 *                 result.push(c);
 *             }
 *         }
 *         return result;
 *     };
 *
 *     var invalidFields = myFormPanel.query('field:invalid');
 *     if (invalidFields.length) {
 *         invalidFields[0].getEl().scrollIntoView(myFormPanel.body);
 *         for (var i = 0, l = invalidFields.length; i < l; i++) {
 *             invalidFields[i].getEl().frame("red");
 *         }
 *     }
 *
 * Default pseudos include:
 *
 * - not
 *
 * Queries return an array of components.
 * Here are some example queries.
 *
 *     // retrieve all Ext.Panels in the document by xtype
 *     var panelsArray = Ext.ComponentQuery.query('panel');
 *
 *     // retrieve all Ext.Panels within the container with an id myCt
 *     var panelsWithinmyCt = Ext.ComponentQuery.query('#myCt panel');
 *
 *     // retrieve all direct children which are Ext.Panels within myCt
 *     var directChildPanel = Ext.ComponentQuery.query('#myCt > panel');
 *
 *     // retrieve all grids and trees
 *     var gridsAndTrees = Ext.ComponentQuery.query('gridpanel, treepanel');
 *
 * For easy access to queries based from a particular Container see the {@link Ext.Container#query},
 * {@link Ext.Container#down} and {@link Ext.Container#child} methods. Also see
 * {@link Ext.Component#up}.
 */
Ext.define('Ext.ComponentQuery', {
    singleton: true,
    uses: ['Ext.ComponentManager']
}, function() {

    var cq = this,

        // A function source code pattern with a placeholder which accepts an expression which yields a truth value when applied
        // as a member on each item in the passed array.
        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) {
            // Argument list for the operation is [ itemsArray, operationArg1, operationArg2...]
            // The operation's method loops over each item in the candidate array and
            // returns an array of items which match its criteria
            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.ownerCt || candidate.floatParent))) {
                    result.push(candidate);
                }
            }
            return result;
        },

        // Filters the passed candidate array and returns only items which match the passed xtype
        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;
            }
        },

        // Filters the passed candidate array and returns only items which have the passed className
        filterByClassName = function(items, className) {
            var EA = Ext.Array,
                result = [],
                i = 0,
                length = items.length,
                candidate;
            for (; i < length; i++) {
                candidate = items[i];
                if (candidate.el ? candidate.el.hasCls(className) : EA.contains(candidate.initCls(), className)) {
                    result.push(candidate);
                }
            }
            return result;
        },

        // Filters the passed candidate array and returns only items which have the specified property match
        filterByAttribute = function(items, property, operator, value) {
            var result = [],
                i = 0,
                length = items.length,
                candidate, getter, getValue;
            for (; i < length; i++) {
                candidate = items[i];
                getter = Ext.Class.getConfigNameMap(property).get;
                if (candidate[getter]) {
                    getValue = candidate[getter]();
                    if (!value ? !!getValue : (String(getValue) === value)) {
                        result.push(candidate);
                    }
                }
                else if (candidate.config && candidate.config[property]) {
                    if (!value ? !!candidate.config[property] : (String(candidate.config[property]) === value)) {
                        result.push(candidate);
                    }
                }
                else if (!value ? !!candidate[property] : (String(candidate[property]) === value)) {
                    result.push(candidate);
                }
            }
            return result;
        },

        // Filters the passed candidate array and returns only items which have the specified itemId or id
        filterById = function(items, id) {
            var result = [],
                i = 0,
                length = items.length,
                candidate;
            for (; i < length; i++) {
                candidate = items[i];
                if (candidate.getId() === id || candidate.getItemId() === id) {
                    result.push(candidate);
                }
            }
            return result;
        },

        // Filters the passed candidate array and returns only items which the named pseudo class matcher filters in
        filterByPseudo = function(items, name, value) {
            return cq.pseudos[name](items, value);
        },

        // Determines leading mode
        // > for direct child, and ^ to switch to ownerCt axis
        modeRe = /^(\s?([>\^])\s?|\s|$)/,

        // Matches a token with possibly (true|false) appended for the "shallow" parameter
        tokenRe = /^(#)?([\w\-]+|\*)(?:\((true|false)\))?/,

        matchers = [{
            // Checks for .xtype with possibly (true|false) appended for the "shallow" parameter
            re: /^\.([\w\-]+)(?:\((true|false)\))?/,
            method: filterByXType
        },{
            // checks for [attribute=value]
            re: /^(?:[\[](?:@)?([\w\-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]])/,
            method: filterByAttribute
        }, {
            // checks for #cmpItemId
            re: /^#([\w\-]+)/,
            method: filterById
        }, {
            // checks for :<pseudo_class>(<selector>)
            re: /^\:([\w\-]+)(?:\(((?:\{[^\}]+\})|(?:(?!\{)[^\s>\/]*?(?!\})))\))?/,
            method: filterByPseudo
        }, {
            // checks for {<member_expression>}
            re: /^(?:\{([^\}]+)\})/,
            method: filterFnPattern
        }];

    cq.Query = Ext.extend(Object, {
        constructor: function(cfg) {
            cfg = cfg || {};
            Ext.apply(this, cfg);
        },

        /**
         * @private
         * Executes this Query upon the selected root.
         * The root provides the initial source of candidate Component matches which are progressively
         * filtered by iterating through this Query's operations cache.
         * If no root is provided, all registered Components are searched via the ComponentManager.
         * root may be a Container who's descendant Components are filtered
         * root may be a Component with an implementation of getRefItems which provides some nested Components such as the
         * docked items within a Panel.
         * root may be an array of candidate Components to filter using this Query.
         */
        execute : function(root) {
            var operations = this.operations,
                i = 0,
                length = operations.length,
                operation,
                workingItems;

            // no root, use all Components in the document
            if (!root) {
                workingItems = Ext.ComponentManager.all.getArray();
            }
            // Root is a candidate Array
            else if (Ext.isArray(root)) {
                workingItems = root;
            }

            // We are going to loop over our operations and take care of them
            // one by one.
            for (; i < length; i++) {
                operation = operations[i];

                // The mode operation requires some custom handling.
                // All other operations essentially filter down our current
                // working items, while mode replaces our current working
                // items by getting children from each one of our current
                // working items. The type of mode determines the type of
                // children we get. (e.g. > only gets direct children)
                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 this is the last operation, it means our current working
                // items are the final matched items. Thus return them!
                if (i === length -1) {
                    return workingItems;
                }
            }
            return [];
        },

        is: function(component) {
            var operations = this.operations,
                components = Ext.isArray(component) ? component : [component],
                originalLength = components.length,
                lastOperation = operations[operations.length-1],
                ln, i;

            components = filterItems(components, lastOperation);
            if (components.length === originalLength) {
                if (operations.length > 1) {
                    for (i = 0, ln = components.length; i < ln; i++) {
                        if (Ext.Array.indexOf(this.execute(), components[i]) === -1) {
                            return false;
                        }
                    }
                }
                return true;
            }
            return false;
        }
    });

    Ext.apply(this, {

        // private cache of selectors and matching ComponentQuery.Query objects
        cache: {},

        // private cache of pseudo class filter functions
        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;
            }
        },

        /**
         * Returns an array of matched Components from within the passed root object.
         *
         * This method filters returned Components in a similar way to how CSS selector based DOM
         * queries work using a textual selector string.
         *
         * See class summary for details.
         *
         * @param {String} selector The selector string to filter returned Components
         * @param {Ext.Container} root The Container within which to perform the query.
         * If omitted, all Components within the document are included in the search.
         *
         * This parameter may also be an array of Components to filter according to the selector.</p>
         * @returns {Ext.Component[]} The matched Components.
         *
         * @member Ext.ComponentQuery
         */
        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.parse(selector);
//                query = this.cache[selector];
//                if (!query) {
//                    this.cache[selector] = query = this.parse(selector);
//                }
                results = results.concat(query.execute(root));
            }

            // multiple selectors, potential to find duplicates
            // lets filter them out.
            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;
        },

        /**
         * Tests whether the passed Component matches the selector string.
         * @param {Ext.Component} component The Component to test
         * @param {String} selector The selector string to test against.
         * @return {Boolean} True if the Component matches the selector.
         * @member Ext.ComponentQuery
         */
        is: function(component, selector) {
            if (!selector) {
                return true;
            }
            var query = this.cache[selector];
            if (!query) {
                this.cache[selector] = query = this.parse(selector);
            }
            return query.is(component);
        },

        parse: function(selector) {
            var operations = [],
                length = matchers.length,
                lastSelector,
                tokenMatch,
                matchedChar,
                modeMatch,
                selectorMatch,
                i, matcher, method;

            // We are going to parse the beginning of the selector over and
            // over again, slicing off the selector any portions we converted into an
            // operation, until it is an empty string.
            while (selector && lastSelector !== selector) {
                lastSelector = selector;

                // First we check if we are dealing with a token like #, * or an xtype
                tokenMatch = selector.match(tokenRe);

                if (tokenMatch) {
                    matchedChar = tokenMatch[1];

                    // If the token is prefixed with a # we push a filterById operation to our stack
                    if (matchedChar === '#') {
                        operations.push({
                            method: filterById,
                            args: [Ext.String.trim(tokenMatch[2])]
                        });
                    }
                    // If the token is prefixed with a . we push a filterByClassName operation to our stack
                    // FIXME: Not enabled yet. just needs \. adding to the tokenRe prefix
                    else if (matchedChar === '.') {
                        operations.push({
                            method: filterByClassName,
                            args: [Ext.String.trim(tokenMatch[2])]
                        });
                    }
                    // If the token is a * or an xtype string, we push a filterByXType
                    // operation to the stack.
                    else {
                        operations.push({
                            method: filterByXType,
                            args: [Ext.String.trim(tokenMatch[2]), Boolean(tokenMatch[3])]
                        });
                    }

                    // Now we slice of the part we just converted into an operation
                    selector = selector.replace(tokenMatch[0], '');
                }

                // If the next part of the query is not a space or > or ^, it means we
                // are going to check for more things that our current selection
                // has to comply to.
                while (!(modeMatch = selector.match(modeRe))) {
                    // Lets loop over each type of matcher and execute it
                    // on our current selector.
                    for (i = 0; selector && i < length; i++) {
                        matcher = matchers[i];
                        selectorMatch = selector.match(matcher.re);
                        method = matcher.method;

                        // If we have a match, add an operation with the method
                        // associated with this matcher, and pass the regular
                        // expression matches are arguments to the operation.
                        if (selectorMatch) {
                            operations.push({
                                method: Ext.isString(matcher.method)
                                    // Turn a string method into a function by formatting the string with our selector matche expression
                                    // A new method is created for different match expressions, eg {id=='textfield-1024'}
                                    // Every expression may be different in different selectors.
                                    ? 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; // Break on match
                        }
                        // Exhausted all matches: It's an error
                        if (i === (length - 1)) {
                            Ext.Error.raise('Invalid ComponentQuery selector: "' + arguments[0] + '"');
                        }
                    }
                }

                // Now we are going to check for a mode change. This means a space
                // or a > to determine if we are going to select all the children
                // of the currently matched items, or a ^ if we are going to use the
                // ownerCt axis as the candidate source.
                if (modeMatch[1]) { // Assignment, and test for truthiness!
                    operations.push({
                        mode: modeMatch[2]||modeMatch[1]
                    });
                    selector = selector.replace(modeMatch[0], '');
                }
            }

            //  Now that we have all our operations in an array, we are going
            // to create a new Query using these operations.
            return new cq.Query({
                operations: operations
            });
        }
    });
});
/**
 * 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' loop context level. This is adjusted up by one
     * prior to calling {@link #doFor} and down by one after calling the corresponding
     * {@link #doEnd} that closes the loop. This will be 1 on the first {@link #doFor}
     * 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 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;

        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> 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;
                    me.doFor(actions['for'], actions);
                    stack.push({ type: 'for', actions: actions });
                }
                else if (actions.exec) {
                    me.doExec(actions.exec, actions);
                    stack.push({ type: 'exec', actions: actions });
                }
                /*
                else {
                    // todo - error
                }
                /**/
            } else {
                frame = stack.pop();
                me.doEnd(frame.type, frame.actions);
                if (frame.type == 'for') {
                    --me.level;
                }
            }
        }
    },

    // Internal regexes

    topRe:     /(?:(\{\%)|(\{\[)|\{([^{}]*)\})|(?:<tpl([^>]*)\>)|(?:<\/tpl>)/g,
    actionsRe: /\s*(elif|elseif|if|for|exec|switch|case|eval)\s*\=\s*(?:(?:["]([^"]*)["])|(?:[']([^']*)[']))\s*/g,
    defaultRe: /^\s*default\s*$/,
    elseRe:    /^\s*else\s*$/
});
/**
 * @author Ed Spencer
 * @private
 *
 * Represents a single action as {@link Ext.app.Application#dispatch dispatched} by an Application. This is typically
 * generated as a result of a url change being matched by a Route, triggering Application's dispatch function.
 *
 * This is a private class and its functionality and existence may change in the future. Use at your own risk.
 *
 */
Ext.define('Ext.app.Action', {
    config: {
        /**
         * @cfg {Object} scope The scope in which the {@link #action} should be called
         */
        scope: null,

        /**
         * @cfg {Ext.app.Application} application The Application that this Action is bound to
         */
        application: null,

        /**
         * @cfg {Ext.app.Controller} controller The {@link Ext.app.Controller controller} whose {@link #action} should
         * be called
         */
        controller: null,

        /**
         * @cfg {String} action The name of the action on the {@link #controller} that should be called
         */
        action: null,

        /**
         * @cfg {Array} args The set of arguments that will be passed to the controller's {@link #action}
         */
        args: [],

        /**
         * @cfg {String} url The url that was decoded into the controller/action/args in this Action
         */
        url: undefined,
        data: {},
        title: null,

        /**
         * @cfg {Array} beforeFilters The (optional) set of functions to call before the {@link #action} is called.
         * This is usually handled directly by the Controller or Application when an Ext.app.Action instance is
         * created, but is alterable before {@link #resume} is called.
         * @accessor
         */
        beforeFilters: [],

        /**
         * @private
         * Keeps track of which before filter is currently being executed by {@link #resume}
         */
        currentFilterIndex: -1
    },

    constructor: function(config) {
        this.initConfig(config);

        this.getUrl();
    },

    /**
     * Starts execution of this Action by calling each of the {@link #beforeFilters} in turn (if any are specified),
     * before calling the Controller {@link #action}. Same as calling {@link #resume}.
     */
    execute: function() {
        this.resume();
    },

    /**
     * Resumes the execution of this Action (or starts it if it had not been started already). This iterates over all
     * of the configured {@link #beforeFilters} and calls them. Each before filter is called with this Action as the
     * sole argument, and is expected to call action.resume() in order to allow the next filter to be called, or if
     * this is the final filter, the original {@link Ext.app.Controller Controller} function.
     */
    resume: function() {
        var index   = this.getCurrentFilterIndex() + 1,
            filters = this.getBeforeFilters(),
            controller = this.getController(),
            nextFilter = filters[index];

        if (nextFilter) {
            this.setCurrentFilterIndex(index);
            nextFilter.call(controller, this);
        } else {
            controller[this.getAction()].apply(controller, this.getArgs());
        }
    },

    /**
     * @private
     */
    applyUrl: function(url) {
        if (url === null || url === undefined) {
            url = this.urlEncode();
        }

        return url;
    },

    /**
     * @private
     * If the controller config is a string, swap it for a reference to the actuall controller instance
     * @param {String} controller The controller name
     */
    applyController: function(controller) {
        var app = this.getApplication(),
            profile = app.getCurrentProfile();

        if (Ext.isString(controller)) {
            controller = app.getController(controller, profile ? profile.getNamespace() : null);
        }

        return controller;
    },

    /**
     * @private
     */
    urlEncode: function() {
        var controller = this.getController(),
            splits;

        if (controller instanceof Ext.app.Controller) {
            splits = controller.$className.split('.');
            controller = splits[splits.length - 1];
        }

        return controller + "/" + this.getAction();
    }
});
/**
 * @author Ed Spencer
 * @private
 *
 * Represents a mapping between a url and a controller/action pair. May also contain additional params. This is a
 * private internal class that should not need to be used by end-developer code. Its API and existence are subject to
 * change so use at your own risk.
 *
 * For information on how to use routes we suggest reading the following guides:
 *
 * * <a href="#!/guide/history_support">Using History Support</a>
 * * <a href="#!/guide/apps_intro">Intro to Applications</a>
 * * <a href="#!/guide/controllers">Using Controllers</a>
 *
 */
Ext.define('Ext.app.Route', {

    config: {
        /**
         * @cfg {Object} conditions Optional set of conditions for each token in the url string. Each key should be one
         * of the tokens, each value should be a regex that the token should accept. For example, if you have a Route
         * with a url like "files/:fileName" and you want it to match urls like "files/someImage.jpg" then you can set
         * these conditions to allow the :fileName token to accept strings containing a period ("."):
         *
         *     conditions: {
         *         ':fileName': "[0-9a-zA-Z\.]+"
         *     }
         *
         */
        conditions: {},

        /**
         * @cfg {String} url The url regex to match against. Required
         */
        url: null,

        /**
         * @cfg {String} controller The name of the Controller whose {@link #action} will be called if this route is
         * matched
         */
        controller: null,

        /**
         * @cfg {String} action The name of the action that will be called on the {@link #controller} if this route is
         * matched
         */
        action: null,

        /**
         * @private
         * @cfg {Boolean} initialized Indicates whether or not this Route has been initialized. We don't initialize
         * straight away so as to save unnecessary processing.
         */
        initialized: false
    },

    constructor: function(config) {
        this.initConfig(config);
    },

    /**
     * Attempts to recognize a given url string and return controller/action pair for it
     * @param {String} url The url to recognize
     * @return {Object} The matched data, or false if no match
     */
    recognize: function(url) {
        if (!this.getInitialized()) {
            this.initialize();
        }

        if (this.recognizes(url)) {
            var matches = this.matchesFor(url),
                args    = url.match(this.matcherRegex);

            args.shift();

            return Ext.applyIf(matches, {
                controller: this.getController(),
                action    : this.getAction(),
                historyUrl: url,
                args      : args
            });
        }
    },

    /**
     * @private
     * Sets up the relevant regular expressions used to match against this route
     */
    initialize: function() {
        /*
         * The regular expression we use to match a segment of a route mapping
         * this will recognise segments starting with a colon,
         * e.g. on 'namespace/:controller/:action', :controller and :action will be recognised
         */
        this.paramMatchingRegex = new RegExp(/:([0-9A-Za-z\_]*)/g);

        /*
         * Converts a route string into an array of symbols starting with a colon. e.g.
         * ":controller/:action/:id" => [':controller', ':action', ':id']
         */
        this.paramsInMatchString = this.getUrl().match(this.paramMatchingRegex) || [];

        this.matcherRegex = this.createMatcherRegex(this.getUrl());

        this.setInitialized(true);
    },

    /**
     * @private
     * Returns true if this Route matches the given url string
     * @param {String} url The url to test
     * @return {Boolean} True if this Route recognizes the url
     */
    recognizes: function(url) {
        return this.matcherRegex.test(url);
    },

    /**
     * @private
     * Returns a hash of matching url segments for the given url.
     * @param {String} url The url to extract matches for
     * @return {Object} matching url segments
     */
    matchesFor: function(url) {
        var params = {},
            keys   = this.paramsInMatchString,
            values = url.match(this.matcherRegex),
            length = keys.length,
            i;

        //first value is the entire match so reject
        values.shift();

        for (i = 0; i < length; i++) {
            params[keys[i].replace(":", "")] = values[i];
        }

        return params;
    },

    /**
     * @private
     * Returns an array of matching url segments for the given url.
     * @param {String} url The url to extract matches for
     * @return {Array} matching url segments
     */
    argsFor: function(url) {
        var args   = [],
            keys   = this.paramsInMatchString,
            values = url.match(this.matcherRegex),
            length = keys.length,
            i;

        //first value is the entire match so reject
        values.shift();

        for (i = 0; i < length; i++) {
            args.push(keys[i].replace(':', ""));
            params[keys[i].replace(":", "")] = values[i];
        }

        return params;
    },

    /**
     * @private
     * Constructs a url for the given config object by replacing wildcard placeholders in the Route's url
     * @param {Object} config The config object
     * @return {String} The constructed url
     */
    urlFor: function(config) {
        var url = this.getUrl();

        for (var key in config) {
            url = url.replace(":" + key, config[key]);
        }

        return url;
    },

    /**
     * @private
     * Takes the configured url string including wildcards and returns a regex that can be used to match
     * against a url
     * @param {String} url The url string
     * @return {RegExp} The matcher regex
     */
    createMatcherRegex: function(url) {
        /**
         * Converts a route string into an array of symbols starting with a colon. e.g.
         * ":controller/:action/:id" => [':controller', ':action', ':id']
         */
        var paramsInMatchString = this.paramsInMatchString,
            length = paramsInMatchString.length,
            i, cond, matcher;

        for (i = 0; i < length; i++) {
            cond    = this.getConditions()[paramsInMatchString[i]];
            matcher = Ext.util.Format.format("({0})", cond || "[%a-zA-Z0-9\-\\_\\s,]+");

            url = url.replace(new RegExp(paramsInMatchString[i]), matcher);
        }

        //we want to match the whole string, so include the anchors
        return new RegExp("^" + url + "$");
    }
});
/**
 * @author Ed Spencer
 * @private
 *
 * The Router is an ordered set of route definitions that decode a url into a controller function to execute. Each
 * route defines a type of url to match, along with the controller function to call if it is matched. The Router is
 * usually managed exclusively by an {@link Ext.app.Application Application}, which also uses a
 * {@link Ext.app.History History} instance to find out when the browser's url has changed.
 *
 * Routes are almost always defined inside a {@link Ext.app.Controller Controller}, as opposed to on the Router itself.
 * End-developers should not usually need to interact directly with the Router as the Application and Controller
 * classes manage everything automatically. See the {@link Ext.app.Controller Controller documentation} for more
 * information on specifying routes.
 */
Ext.define('Ext.app.Router', {
    requires: ['Ext.app.Route'],

    config: {
        /**
         * @cfg {Array} routes The set of routes contained within this Router. Read only
         */
        routes: [],

        /**
         * @cfg {Object} defaults Default configuration options for each Route connected to this Router.
         */
        defaults: {
            action: 'index'
        }
    },

    constructor: function(config) {
        this.initConfig(config);
    },

    /**
     * Connects a url-based route to a controller/action pair plus additional params
     * @param {String} url The url to recognize
     */
    connect: function(url, params) {
        params = Ext.apply({url: url}, params || {}, this.getDefaults());
        var route = Ext.create('Ext.app.Route', params);

        this.getRoutes().push(route);

        return route;
    },

    /**
     * Recognizes a url string connected to the Router, return the controller/action pair plus any additional
     * config associated with it
     * @param {String} url The url to recognize
     * @return {Object/undefined} If the url was recognized, the controller and action to call, else undefined
     */
    recognize: function(url) {
        var routes = this.getRoutes(),
            length = routes.length,
            i, result;

        for (i = 0; i < length; i++) {
            result = routes[i].recognize(url);

            if (result !== undefined) {
                return result;
            }
        }

        return undefined;
    },

    /**
     * Convenience method which just calls the supplied function with the Router instance. Example usage:
<pre><code>
Ext.Router.draw(function(map) {
    map.connect('activate/:token', {controller: 'users', action: 'activate'});
    map.connect('home',            {controller: 'index', action: 'home'});
});
</code></pre>
     * @param {Function} fn The fn to call
     */
    draw: function(fn) {
        fn.call(this, this);
    },

    /**
     * @private
     */
    clear: function() {
        this.setRoutes([]);
    }
}, function() {
});

/**
 * @private
 */
Ext.define('Ext.behavior.Behavior', {
    constructor: function(component) {
        this.component = component;

        component.on('destroy', 'onComponentDestroy', this);
    },

    onComponentDestroy: Ext.emptyFn
});

/**
 * @singleton
 *
 * This class is used to create JsonP requests. JsonP is a mechanism that allows for making requests for data cross
 * domain. More information is available [here](http://en.wikipedia.org/wiki/JSONP).
 *
 * ## Example
 *
 *     @example preview
 *     Ext.Viewport.add({
 *         xtype: 'button',
 *         text: 'Make JsonP Request',
 *         centered: true,
 *         handler: function(button) {
 *             // Mask the viewport
 *             Ext.Viewport.mask();
 *
 *             // Remove the button
 *             button.destroy();
 *
 *             // Make the JsonP request
 *             Ext.data.JsonP.request({
 *                 url: 'http://free.worldweatheronline.com/feed/weather.ashx',
 *                 callbackKey: 'callback',
 *                 params: {
 *                     key: '23f6a0ab24185952101705',
 *                     q: '94301', // Palo Alto
 *                     format: 'json',
 *                     num_of_days: 5
 *                 },
 *                 success: function(result) {
 *                     // Unmask the viewport
 *                     Ext.Viewport.unmask();
 *
 *                     // Get the weather data from the json object result
 *                     var weather = result.data.weather;
 *                     if (weather) {
 *                         // Style the viewport html, and set the html of the max temperature
 *                         Ext.Viewport.setStyleHtmlContent(true);
 *                         Ext.Viewport.setHtml('The temperature in Palo Alto is <b>' + weather[0].tempMaxF + '° F<b/>');
 *                     }
 *                 }
 *             });
 *         }
 *     });
 *
 * See the {@link #request} method for more details on making a JsonP request.
 */
Ext.define('Ext.data.JsonP', {
    alternateClassName: 'Ext.util.JSONP',

    /* Begin Definitions */

    singleton: true,

    statics: {
        requestCount: 0,
        requests: {}
    },

    /* End Definitions */

    /**
     * @property {Number} [timeout=30000]
     * A default timeout for any JsonP requests. If the request has not completed in this time the failure callback will
     * be fired. The timeout is in ms. Defaults to 30000.
     */
    timeout: 30000,

    /**
     * @property {Boolean} [disableCaching=true]
     * True to add a unique cache-buster param to requests. Defaults to true.
     */
    disableCaching: true,

    /**
     * @property {String} [disableCachingParam="_dc"]
     * Change the parameter which is sent went disabling caching through a cache buster. Defaults to '_dc'.
     */
    disableCachingParam: '_dc',

    /**
     * @property {String} [callbackKey="callback"]
     * Specifies the GET parameter that will be sent to the server containing the function name to be executed when the
     * request completes. Defaults to callback. Thus, a common request will be in the form of
     * url?callback=Ext.data.JsonP.callback1
     */
    callbackKey: 'callback',

    /**
     * Makes a JSONP request.
     * @param {Object} options An object which may contain the following properties. Note that options will take
     * priority over any defaults that are specified in the class.
     *
     * @param {String} options.url  The URL to request.
     * @param {Object} [options.params]  An object containing a series of key value pairs that will be sent along with the request.
     * @param {Number} [options.timeout]  See {@link #timeout}
     * @param {String} [options.callbackKey]  See {@link #callbackKey}
     * @param {String} [options.callbackName]  See {@link #callbackKey}
     *   The function name to use for this request. By default this name will be auto-generated: Ext.data.JsonP.callback1,
     *   Ext.data.JsonP.callback2, etc. Setting this option to "my_name" will force the function name to be
     *   Ext.data.JsonP.my_name. Use this if you want deterministic behavior, but be careful - the callbackName should be
     *   different in each JsonP request that you make.
     * @param {Boolean}  [options.disableCaching]  See {@link #disableCaching}
     * @param {String}   [options.disableCachingParam]  See {@link #disableCachingParam}
     * @param {Function} [options.success]  A function to execute if the request succeeds.
     * @param {Function} [options.failure]  A function to execute if the request fails.
     * @param {Function} [options.callback]  A function to execute when the request completes, whether it is a success or failure.
     * @param {Object}   [options.scope]  The scope in which to execute the callbacks: The "this" object for the
     *   callback function. Defaults to the browser window.
     *
     * @return {Object}  request An object containing the request details.
     */
    request: function(options){
        options = Ext.apply({}, options);

        if (!options.url) {
            Ext.Logger.error('A url must be specified for a JSONP request.');
        }

        var me = this,
            disableCaching = Ext.isDefined(options.disableCaching) ? options.disableCaching : me.disableCaching,
            cacheParam = options.disableCachingParam || me.disableCachingParam,
            id = ++me.statics().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.isSandboxed ? Ext.getUniqueGlobalNamespace() : 'Ext',
            request,
            script;

        params[callbackKey] = name + '.data.JsonP.' + callbackName;
        if (disableCaching) {
            params[cacheParam] = new Date().getTime();
        }

        script = me.createScript(url, params, options);

        me.statics().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 a request. If the request parameter is not specified all open requests will be aborted.
     * @param {Object/String} request The request to abort
     */
    abort: function(request){
        var requests = this.statics().requests,
            key;

        if (request) {
            if (!request.id) {
                request = requests[request];
            }
            this.abort(request);
        } else {
            for (key in requests) {
                if (requests.hasOwnProperty(key)) {
                    this.abort(requests[key]);
                }
            }
        }
    },

    /**
     * Sets up error handling for the script
     * @private
     * @param {Object} request The request
     */
    setupErrorHandling: function(request){
        request.script.onerror = Ext.bind(this.handleError, this, [request]);
    },

    /**
     * Handles any aborts when loading the script
     * @private
     * @param {Object} request The request
     */
    handleAbort: function(request){
        request.errorType = 'abort';
        this.handleResponse(null, request);
    },

    /**
     * Handles any script errors when loading the script
     * @private
     * @param {Object} request The request
     */
    handleError: function(request){
        request.errorType = 'error';
        this.handleResponse(null, request);
    },

    /**
     * Cleans up anu script handling errors
     * @private
     * @param {Object} request The request
     */
    cleanupErrorHandling: function(request){
        request.script.onerror = null;
    },

    /**
     * Handle any script timeouts
     * @private
     * @param {Object} request The request
     */
    handleTimeout: function(request){
        request.errorType = 'timeout';
        this.handleResponse(null, request);
    },

    /**
     * Handle a successful response
     * @private
     * @param {Object} result The result from the request
     * @param {Object} request The request
     */
    handleResponse: function(result, request){
        var success = true;

        if (request.timeout) {
            clearTimeout(request.timeout);
        }

        delete this[request.callbackName];
        delete this.statics()[request.id];

        this.cleanupErrorHandling(request);
        Ext.fly(request.script).destroy();

        if (request.errorType) {
            success = false;
            Ext.callback(request.failure, request.scope, [request.errorType]);
        } else {
            Ext.callback(request.success, request.scope, [result]);
        }
        Ext.callback(request.callback, request.scope, [success, result, request.errorType]);
    },

    /**
     * Create the script tag given the specified url, params and options. The options
     * parameter is passed to allow an override to access it.
     * @private
     * @param {String} url The url of the request
     * @param {Object} params Any extra params to be sent
     * @param {Object} options The object passed to {@link #request}.
     */
    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;
    },

    /**
     * Loads the script for the given request by appending it to the HEAD element. This is
     * its own method so that users can override it (as well as {@link #createScript}).
     * @private
     * @param request The request object.
     */
    loadScript: function (request) {
        Ext.getHead().appendChild(request.script);
    }
});

/**
 * @author Ed Spencer
 *
 * Represents a single read or write operation performed by a {@link Ext.data.proxy.Proxy Proxy}. Operation objects are
 * used to enable communication between Stores and Proxies. Application developers should rarely need to interact with
 * Operation objects directly.
 *
 * Note that when you define an Operation directly, you need to specify at least the {@link #model} configuration.
 *
 * Several Operations can be batched together in a {@link Ext.data.Batch batch}.
 */
Ext.define('Ext.data.Operation', {
    config: {
        /**
         * @cfg {Boolean} synchronous
         * True if this Operation is to be executed synchronously. This property is inspected by a
         * {@link Ext.data.Batch Batch} to see if a series of Operations can be executed in parallel or not.
         * @accessor
         * @private
         */
        synchronous: true,

        /**
         * @cfg {String} action
         * The action being performed by this Operation. Should be one of 'create', 'read', 'update' or 'destroy'.
         * @accessor
         */
        action: null,

        /**
         * @cfg {Ext.util.Filter[]} filters
         * Optional array of filter objects. Only applies to 'read' actions.
         * @accessor
         * @private
         */
        filters: null,

        /**
         * @cfg {Ext.util.Sorter[]} sorters
         * Optional array of sorter objects. Only applies to 'read' actions.
         * @accessor
         * @private
         */
        sorters: null,

        /**
         * @cfg {Ext.util.Grouper} grouper
         * Optional grouping configuration. Only applies to 'read' actions where grouping is desired.
         * @accessor
         * @private
         */
        grouper: null,

        /**
         * @cfg {Number} start
         * The start index (offset), used in paging when running a 'read' action.
         * @accessor
         * @private
         */
        start: null,

        /**
         * @cfg {Number} limit
         * The number of records to load. Used on 'read' actions when paging is being used.
         * @accessor
         * @private
         */
        limit: null,

        /**
         * @cfg {Ext.data.Batch} batch
         * The batch that this Operation is a part of.
         * @accessor
         * @private
         */
        batch: null,

        /**
         * @cfg {Function} callback
         * Function to execute when operation completed.
         * @cfg {Ext.data.Model[]} callback.records Array of records.
         * @cfg {Ext.data.Operation} callback.operation The Operation itself.
         * @cfg {Boolean} callback.success True when operation completed successfully.
         * @accessor
         * @private
         */
        callback: null,

        /**
         * @cfg {Object} scope
         * Scope for the {@link #callback} function.
         * @accessor
         * @private
         */
        scope: null,

        /**
         * @cfg {Ext.data.ResultSet} resultSet
         * The ResultSet for this operation.
         * @accessor
         */
        resultSet: null,

        /**
         * @cfg {Array} records
         * The records associated to this operation. Before an operation starts, these
         * are the records you are updating, creating, or destroying. After an operation
         * is completed, a Proxy usually sets these records on the Operation to become
         * the processed records. If you don't set these records on your operation in
         * your proxy, then the getter will return the ones defined on the {@link #resultSet}
         * @accessor
         */
        records: null,

        /**
         * @cfg {Ext.data.Request} request
         * The request used for this Operation. Operations don't usually care about Request and Response data, but in the
         * ServerProxy and any of its subclasses they may be useful for further processing.
         * @accessor
         */
        request: null,

        /**
         * @cfg {Object} response
         * The response that was gotten from the server if there was one.
         * @accessor
         */
        response: null,

        /**
         * @cfg {Boolean} withCredentials
         * This field is necessary when using cross-origin resource sharing.
         */
        withCredentials: null,

        /**
         * @cfg {Object} params
         * The params send along with this operation. These usually apply to a Server proxy if you are
         * creating your own custom proxy,
         * @accessor
         */
        params: null,
        url: null,
        page: null,
        node: null,

        /**
         * @cfg {Ext.data.Model} model
         * The Model that this Operation will be dealing with. This configuration is required when defining any Operation.
         * Since Operations take care of creating, updating, destroying and reading records, it needs access to the Model.
         */
        model: undefined,

        addRecords: false
    },

    /**
     * @property {Boolean} started
     * Read-only property tracking the start status of this Operation. Use {@link #isStarted}.
     * @private
     */
    started: false,

    /**
     * @property {Boolean} running
     * Read-only property tracking the run status of this Operation. Use {@link #isRunning}.
     * @private
     */
    running: false,

    /**
     * @property {Boolean} complete
     * Read-only property tracking the completion status of this Operation. Use {@link #isComplete}.
     * @private
     */
    complete: false,

    /**
     * @property {Boolean} success
     * Read-only property tracking whether the Operation was successful or not. This starts as undefined and is set to true
     * or false by the Proxy that is executing the Operation. It is also set to false by {@link #setException}. Use
     * {@link #wasSuccessful} to query success status.
     * @private
     */
    success: undefined,

    /**
     * @property {Boolean} exception
     * Read-only property tracking the exception status of this Operation. Use {@link #hasException} and see {@link #getError}.
     * @private
     */
    exception: false,

    /**
     * @property {String/Object} error
     * The error object passed when {@link #setException} was called. This could be any object or primitive.
     * @private
     */
    error: undefined,

    /**
     * Creates new Operation object.
     * @param {Object} config (optional) Config object.
     */
    constructor: function(config) {
        this.initConfig(config);
    },

    applyModel: function(model) {
        if (typeof model == 'string') {
            model = Ext.data.ModelManager.getModel(model);

            if (!model) {
                Ext.Logger.error('Model with name ' + arguments[0] + ' doesnt exist.');
            }
        }

        if (model && !model.prototype.isModel && Ext.isObject(model)) {
            model = Ext.data.ModelManager.registerType(model.storeId || model.id || Ext.id(), model);
        }

        if (!model) {
            Ext.Logger.warn('Unless you define your model using metadata, an Operation needs to have a model defined.');
        }

        return model;
    },

    getRecords: function() {
        var resultSet = this.getResultSet();
        return this._records || (resultSet ? resultSet.getRecords() : []);
    },

    /**
     * Marks the Operation as started.
     */
    setStarted: function() {
        this.started = true;
        this.running = true;
    },

    /**
     * Marks the Operation as completed.
     */
    setCompleted: function() {
        this.complete = true;
        this.running  = false;
    },

    /**
     * Marks the Operation as successful.
     */
    setSuccessful: function() {
        this.success = true;
    },

    /**
     * Marks the Operation as having experienced an exception. Can be supplied with an option error message/object.
     * @param {String/Object} error (optional) error string/object
     */
    setException: function(error) {
        this.exception = true;
        this.success = false;
        this.running = false;
        this.error = error;
    },

    /**
     * Returns true if this Operation encountered an exception (see also {@link #getError})
     * @return {Boolean} True if there was an exception
     */
    hasException: function() {
        return this.exception === true;
    },

    /**
     * Returns the error string or object that was set using {@link #setException}
     * @return {String/Object} The error object
     */
    getError: function() {
        return this.error;
    },
    
    /**
     * Returns true if the Operation has been started. Note that the Operation may have started AND completed, see
     * {@link #isRunning} to test if the Operation is currently running.
     * @return {Boolean} True if the Operation has started
     */
    isStarted: function() {
        return this.started === true;
    },

    /**
     * Returns true if the Operation has been started but has not yet completed.
     * @return {Boolean} True if the Operation is currently running
     */
    isRunning: function() {
        return this.running === true;
    },

    /**
     * Returns true if the Operation has been completed
     * @return {Boolean} True if the Operation is complete
     */
    isComplete: function() {
        return this.complete === true;
    },

    /**
     * Returns true if the Operation has completed and was successful
     * @return {Boolean} True if successful
     */
    wasSuccessful: function() {
        return this.isComplete() && this.success === true;
    },

    /**
     * Checks whether this operation should cause writing to occur.
     * @return {Boolean} Whether the operation should cause a write to occur.
     */
    allowWrite: function() {
        return this.getAction() != 'read';
    },

    process: function(action, resultSet, request, response) {
        if (resultSet.getSuccess() !== false) {
            this.setResponse(response);
            this.setResultSet(resultSet);
            this.setCompleted();
            this.setSuccessful();
        } else {
            return false;
        }

        return this['process' + Ext.String.capitalize(action)].call(this, resultSet, request, response);
    },

    processRead: function(resultSet) {
        var records = resultSet.getRecords(),
            processedRecords = [],
            Model = this.getModel(),
            ln = records.length,
            i, record;

        for (i = 0; i < ln; i++) {
            record = records[i];
            processedRecords.push(new Model(record.data, record.id, record.node));
        }

        this.setRecords(processedRecords);
        return true;
    },

    processCreate: function(resultSet) {
        var updatedRecords = resultSet.getRecords(),
            ln = updatedRecords.length,
            i, currentRecord, updatedRecord;

        for (i = 0; i < ln; i++) {
            updatedRecord = updatedRecords[i];
            currentRecord = this.findCurrentRecord(updatedRecord.clientId);

            if (currentRecord) {
                this.updateRecord(currentRecord, updatedRecord);
            }
            else {
                Ext.Logger.warn('Unable to match the record that came back from the server.');
            }
        }

        return true;
    },

    processUpdate: function(resultSet) {
        var updatedRecords = resultSet.getRecords(),
            currentRecords = this.getRecords(),
            ln = updatedRecords.length,
            i, currentRecord, updatedRecord;

        for (i = 0; i < ln; i++) {
            updatedRecord = updatedRecords[i];
            currentRecord = currentRecords[i];

            if (currentRecord) {
                this.updateRecord(currentRecord, updatedRecord);
            }
            else {
                Ext.Logger.warn('Unable to match the updated record that came back from the server.');
            }
        }

        return true;
    },

    processDestroy: function(resultSet) {
        var updatedRecords = resultSet.getRecords(),
            ln = updatedRecords.length,
            i, currentRecord, updatedRecord;

        for (i = 0; i < ln; i++) {
            updatedRecord = updatedRecords[i];
            currentRecord = this.findCurrentRecord(updatedRecord.id);

            if (currentRecord) {
                currentRecord.setIsErased(true);
                currentRecord.notifyStores('afterErase', currentRecord);
            }
            else {
                Ext.Logger.warn('Unable to match the destroyed record that came back from the server.');
            }
        }
    },

    findCurrentRecord: function(clientId) {
        var currentRecords = this.getRecords(),
            ln = currentRecords.length,
            i, currentRecord;

        for (i = 0; i < ln; i++) {
            currentRecord = currentRecords[i];
            if (currentRecord.getId() === clientId) {
                return currentRecord;
            }
        }
    },

    updateRecord: function(currentRecord, updatedRecord) {
        var recordData = updatedRecord.data,
            recordId = updatedRecord.id;

        currentRecord.beginEdit();

        currentRecord.set(recordData);
        if (recordId !== null) {
            currentRecord.setId(recordId);
        }

        // We call endEdit with silent: true because the commit below already makes
        // sure any store is notified of the record being updated.
        currentRecord.endEdit(true);
        
        currentRecord.commit();
    }
});

/**
 * @author Ed Spencer
 *
 * Simple class that represents a Request that will be made by any {@link Ext.data.proxy.Server} subclass.
 * All this class does is standardize the representation of a Request as used by any ServerProxy subclass,
 * it does not contain any actual logic or perform the request itself.
 */
Ext.define('Ext.data.Request', {
    config: {
        /**
         * @cfg {String} action
         * The name of the action this Request represents. Usually one of 'create', 'read', 'update' or 'destroy'.
         */
        action: null,

        /**
         * @cfg {Object} params
         * HTTP request params. The Proxy and its Writer have access to and can modify this object.
         */
        params: null,

        /**
         * @cfg {String} method
         * The HTTP method to use on this Request. Should be one of 'GET', 'POST', 'PUT' or 'DELETE'.
         */
        method: 'GET',

        /**
         * @cfg {String} url
         * The url to access on this Request.
         */
        url: null,

        /**
         * @cfg {Ext.data.Operation} operation
         * The operation this request belongs to.
         */
        operation: null,

        /**
         * @cfg {Ext.data.proxy.Proxy} proxy
         * The proxy this request belongs to.
         */
        proxy: null,

        /**
         * @cfg {Boolean} disableCaching
         * Wether or not to disable caching for this request.
         * Defaults to false.
         */
        disableCaching: false,

        /**
         * @cfg {Object} headers
         * Some requests (like XMLHttpRequests) want to send additional server headers.
         * This configuration can be set for those types of requests.
         */
        headers: {},

        /**
         * @cfg {String} callbackKey
         * Some requests (like JsonP) want to send an additional key that contains
         * the name of the callback function.
         */
        callbackKey: null,

        /**
         * @cfg {Ext.data.JsonP} jsonp
         * JsonP requests return a handle that might be useful in the callback function.
         */
        jsonP: null,

        /**
         * @cfg {Object} jsonData
         * This is used by some write actions to attach data to the request without encoding it
         * as a parameter.
         */
        jsonData: null,

        /**
         * @cfg {Object} xmlData
         * This is used by some write actions to attach data to the request without encoding it
         * as a parameter, but instead sending it as XML.
         */
        xmlData: null,

        /**
         * @cfg {Boolean} withCredentials
         * This field is necessary when using cross-origin resource sharing.
         */
        withCredentials: null,

        callback: null,
        scope: null,
        timeout: 30000,
        records: null,

        // The following two configurations are only used by Ext.data.proxy.Direct and are just
        // for being able to retrieve them after the request comes back from the server.
        directFn: null,
        args: null
    },

    /**
     * Creates the Request object.
     * @param {Object} [config] Config object.
     */
    constructor: function(config) {
        this.initConfig(config);
    }
});
/**
 * @author Ed Spencer
 *
 * Simple wrapper class that represents a set of records returned by a Proxy.
 */
Ext.define('Ext.data.ResultSet', {
    config: {
        /**
         * @cfg {Boolean} loaded
         * True if the records have already been loaded. This is only meaningful when dealing with
         * SQL-backed proxies.
         */
        loaded: true,

        /**
         * @cfg {Number} count
         * The number of records in this ResultSet. Note that total may differ from this number.
         */
        count: null,

        /**
         * @cfg {Number} total
         * The total number of records reported by the data source. This ResultSet may form a subset of
         * those records (see {@link #count}).
         */
        total: null,

        /**
         * @cfg {Boolean} success
         * True if the ResultSet loaded successfully, false if any errors were encountered.
         */
        success: false,

        /**
         * @cfg {Ext.data.Model[]} records (required)
         * The array of record instances.
         */
        records: null
    },

    /**
     * Creates the resultSet
     * @param {Object} [config] Config object.
     */
    constructor: function(config) {
        this.initConfig(config);
    },

    applyCount: function(count) {
        if (!count && count !== 0) {
            return this.getRecords().length;
        }
        return count;
    },
    
    /**
     * @private
     * Make sure we set the right count when new records have been sent in
     */
    updateRecords: function(records) {
        this.setCount(records.length);
    }
});
/**
 * @class Ext.data.SortTypes
 * This class defines a series of static methods that are used on a
 * {@link Ext.data.Field} for performing sorting. The methods cast the
 * underlying values into a data type that is appropriate for sorting on
 * that particular field.  If a {@link Ext.data.Field#type} is specified,
 * the sortType will be set to a sane default if the sortType is not
 * explicitly defined on the field. The sortType will make any necessary
 * modifications to the value and return it.
 * <ul>
 * <li><b>asText</b> - Removes any tags and converts the value to a string</li>
 * <li><b>asUCText</b> - Removes any tags and converts the value to an uppercase string</li>
 * <li><b>asUCText</b> - Converts the value to an uppercase string</li>
 * <li><b>asDate</b> - Converts the value into Unix epoch time</li>
 * <li><b>asFloat</b> - Converts the value to a floating point number</li>
 * <li><b>asInt</b> - Converts the value to an integer number</li>
 * </ul>
 * <p>
 * It is also possible to create a custom sortType that can be used throughout
 * an application.
 * <pre><code>
Ext.apply(Ext.data.SortTypes, {
    asPerson: function(person){
        // expects an object with a first and last name property
        return person.lastName.toUpperCase() + person.firstName.toLowerCase();
    }
});

Ext.define('Employee', {
    extend: 'Ext.data.Model',
    config: {
        fields: [{
            name: 'person',
            sortType: 'asPerson'
        }, {
            name: 'salary',
            type: 'float' // sortType set to asFloat
        }]
    }
});
 * </code></pre>
 * </p>
 * @singleton
 * @docauthor Evan Trimboli <evan@sencha.com>
 */
Ext.define('Ext.data.SortTypes', {
    singleton: true,

    /**
     * The regular expression used to strip tags
     * @type {RegExp}
     * @property
     */
    stripTagsRE : /<\/?[^>]+>/gi,

    /**
     * Default sort that does nothing
     * @param {Object} value The value being converted
     * @return {Object} The comparison value
     */
    none : function(value) {
        return value;
    },

    /**
     * Strips all HTML tags to sort on text only
     * @param {Object} value The value being converted
     * @return {String} The comparison value
     */
    asText : function(value) {
        return String(value).replace(this.stripTagsRE, "");
    },

    /**
     * Strips all HTML tags to sort on text only - Case insensitive
     * @param {Object} value The value being converted
     * @return {String} The comparison value
     */
    asUCText : function(value) {
        return String(value).toUpperCase().replace(this.stripTagsRE, "");
    },

    /**
     * Case insensitive string
     * @param {Object} value The value being converted
     * @return {String} The comparison value
     */
    asUCString : function(value) {
        return String(value).toUpperCase();
    },

    /**
     * Date sorting
     * @param {Object} value The value being converted
     * @return {Number} The comparison value
     */
    asDate : function(value) {
        if (!value) {
            return 0;
        }
        if (Ext.isDate(value)) {
            return value.getTime();
        }
        return Date.parse(String(value));
    },

    /**
     * Float sorting
     * @param {Object} value The value being converted
     * @return {Number} The comparison value
     */
    asFloat : function(value) {
        value = parseFloat(String(value).replace(/,/g, ""));
        return isNaN(value) ? 0 : value;
    },

    /**
     * Integer sorting
     * @param {Object} value The value being converted
     * @return {Number} The comparison value
     */
    asInt : function(value) {
        value = parseInt(String(value).replace(/,/g, ""), 10);
        return isNaN(value) ? 0 : value;
    }
});
/**
 * @class Ext.data.Types
 * <p>This is s static class containing the system-supplied data types which may be given to a {@link Ext.data.Field Field}.<p/>
 * <p>The properties in this class are used as type indicators in the {@link Ext.data.Field Field} class, so to
 * test whether a Field is of a certain type, compare the {@link Ext.data.Field#type type} property against properties
 * of this class.</p>
 * <p>Developers may add their own application-specific data types to this class. Definition names must be UPPERCASE.
 * each type definition must contain three properties:</p>
 * <div class="mdetail-params"><ul>
 * <li><code>convert</code> : <i>Function</i><div class="sub-desc">A function to convert raw data values from a data block into the data
 * to be stored in the Field. The function is passed the collowing parameters:
 * <div class="mdetail-params"><ul>
 * <li><b>v</b> : Mixed<div class="sub-desc">The data value as read by the Reader, if undefined will use
 * the configured <tt>{@link Ext.data.Field#defaultValue defaultValue}</tt>.</div></li>
 * <li><b>rec</b> : Mixed<div class="sub-desc">The data object containing the row as read by the Reader.
 * Depending on the Reader type, this could be an Array ({@link Ext.data.reader.Array ArrayReader}), an object
 * ({@link Ext.data.reader.Json JsonReader}), or an XML element.</div></li>
 * </ul></div></div></li>
 * <li><code>sortType</code> : <i>Function</i> <div class="sub-desc">A function to convert the stored data into comparable form, as defined by {@link Ext.data.SortTypes}.</div></li>
 * <li><code>type</code> : <i>String</i> <div class="sub-desc">A textual data type name.</div></li>
 * </ul></div>
 * <p>For example, to create a VELatLong field (See the Microsoft Bing Mapping API) containing the latitude/longitude value of a datapoint on a map from a JsonReader data block
 * which contained the properties <code>lat</code> and <code>long</code>, you would define a new data type like this:</p>
 *<pre><code>
// Add a new Field data type which stores a VELatLong object in the Record.
Ext.data.Types.VELATLONG = {
    convert: function(v, data) {
        return new VELatLong(data.lat, data.long);
    },
    sortType: function(v) {
        return v.Latitude;  // When sorting, order by latitude
    },
    type: 'VELatLong'
};
</code></pre>
 * <p>Then, when declaring a Model, use <pre><code>
var types = Ext.data.Types; // allow shorthand type access
Ext.define('Unit', {
    extend: 'Ext.data.Model',
    config: {
        fields: [
            { name: 'unitName', mapping: 'UnitName' },
            { name: 'curSpeed', mapping: 'CurSpeed', type: types.INT },
            { name: 'latitude', mapping: 'lat', type: types.FLOAT },
            { name: 'position', type: types.VELATLONG }
        ]
    }
});
</code></pre>
 * @singleton
 */
Ext.define('Ext.data.Types', {
    singleton: true,
    requires: ['Ext.data.SortTypes'],

    /**
     * @property {RegExp} stripRe
     * A regular expression for stripping non-numeric characters from a numeric value. Defaults to <tt>/[\$,%]/g</tt>.
     * This should be overridden for localization.
     */
    stripRe: /[\$,%]/g,
    dashesRe: /-/g,
    iso8601TestRe: /\d\dT\d\d/,
    iso8601SplitRe: /[- :T\.Z\+]/

}, function() {
    var Types = this,
        sortTypes = Ext.data.SortTypes;

    Ext.apply(Types, {
        /**
         * @property {Object} AUTO
         * This data type means that no conversion is applied to the raw data before it is placed into a Record.
         */
        AUTO: {
            convert: function(value) {
                return value;
            },
            sortType: sortTypes.none,
            type: 'auto'
        },

        /**
         * @property {Object} STRING
         * This data type means that the raw data is converted into a String before it is placed into a Record.
         */
        STRING: {
            convert: function(value) {
                // 'this' is the actual field that calls this convert method
                return (value === undefined || value === null)
                    ? (this.getAllowNull() ? null : '')
                    : String(value);
            },
            sortType: sortTypes.asUCString,
            type: 'string'
        },

        /**
         * @property {Object} INT
         * This data type means that the raw data is converted into an integer before it is placed into a Record.
         * <p>The synonym <code>INTEGER</code> is equivalent.</p>
         */
        INT: {
            convert: function(value) {
                return (value !== undefined && value !== null && value !== '')
                    ? ((typeof value === 'number')
                        ? parseInt(value, 10)
                        : parseInt(String(value).replace(Types.stripRe, ''), 10)
                    )
                    : (this.getAllowNull() ? null : 0);
            },
            sortType: sortTypes.none,
            type: 'int'
        },

        /**
         * @property {Object} FLOAT
         * This data type means that the raw data is converted into a number before it is placed into a Record.
         * <p>The synonym <code>NUMBER</code> is equivalent.</p>
         */
        FLOAT: {
            convert: function(value) {
                return (value !== undefined && value !== null && value !== '')
                    ? ((typeof value === 'number')
                        ? value
                        : parseFloat(String(value).replace(Types.stripRe, ''), 10)
                    )
                    : (this.getAllowNull() ? null : 0);
            },
            sortType: sortTypes.none,
            type: 'float'
        },

        /**
         * @property {Object} BOOL
         * <p>This data type means that the raw data is converted into a boolean before it is placed into
         * a Record. The string "true" and the number 1 are converted to boolean <code>true</code>.</p>
         * <p>The synonym <code>BOOLEAN</code> is equivalent.</p>
         */
        BOOL: {
            convert: function(value) {
                if ((value === undefined || value === null || value === '') && this.getAllowNull()) {
                    return null;
                }
                return value === true || value === 'true' || value == 1;
            },
            sortType: sortTypes.none,
            type: 'bool'
        },

        /**
         * @property {Object} DATE
         * This data type means that the raw data is converted into a Date before it is placed into a Record.
         * The date format is specified in the constructor of the {@link Ext.data.Field} to which this type is
         * being applied.
         */
        DATE: {
            convert: function(value) {
                var dateFormat = this.getDateFormat(),
                    parsed;

                if (!value) {
                    return null;
                }
                if (Ext.isDate(value)) {
                    return value;
                }
                if (dateFormat) {
                    if (dateFormat == 'timestamp') {
                        return new Date(value*1000);
                    }
                    if (dateFormat == 'time') {
                        return new Date(parseInt(value, 10));
                    }
                    return Ext.Date.parse(value, dateFormat);
                }

                parsed = new Date(Date.parse(value));
                if (isNaN(parsed)) {
                    // Dates with ISO 8601 format are not well supported by mobile devices, this can work around the issue.
                    if (Types.iso8601TestRe.test(value)) {
                        parsed = value.split(Types.iso8601SplitRe);
                        parsed = new Date(parsed[0], parsed[1]-1, parsed[2], parsed[3], parsed[4], parsed[5]);
                    }
                    if (isNaN(parsed)) {
                        // Dates with the format "2012-01-20" fail, but "2012/01/20" work in some browsers. We'll try and
                        // get around that.
                        parsed = new Date(Date.parse(value.replace(this.dashesRe, "/")));
                        if (isNaN(parsed)) {
                            Ext.Logger.warn("Cannot parse the passed value (" + value + ") into a valid date");
                        }
                    }
                }

                return isNaN(parsed) ? null : parsed;
            },
            sortType: sortTypes.asDate,
            type: 'date'
        }
    });

    Ext.apply(Types, {
        /**
         * @property {Object} BOOLEAN
         * <p>This data type means that the raw data is converted into a boolean before it is placed into
         * a Record. The string "true" and the number 1 are converted to boolean <code>true</code>.</p>
         * <p>The synonym <code>BOOL</code> is equivalent.</p>
         */
        BOOLEAN: this.BOOL,

        /**
         * @property {Object} INTEGER
         * This data type means that the raw data is converted into an integer before it is placed into a Record.
         * <p>The synonym <code>INT</code> is equivalent.</p>
         */
        INTEGER: this.INT,

        /**
         * @property {Object} NUMBER
         * This data type means that the raw data is converted into a number before it is placed into a Record.
         * <p>The synonym <code>FLOAT</code> is equivalent.</p>
         */
        NUMBER: this.FLOAT
    });
});

/**
 * @extends Object
 * @author Ed Spencer
 *
 * This singleton contains a set of validation functions that can be used to validate any type of data. They are most
 * often used in {@link Ext.data.Model Models}, where they are automatically set up and executed.
 */
Ext.define('Ext.data.Validations', {
    alternateClassName: 'Ext.data.validations',

    singleton: true,

    config: {
        /**
         * @property {String} presenceMessage
         * The default error message used when a presence validation fails.
         */
        presenceMessage: 'must be present',

        /**
         * @property {String} lengthMessage
         * The default error message used when a length validation fails.
         */
        lengthMessage: 'is the wrong length',

        /**
         * @property {Boolean} formatMessage
         * The default error message used when a format validation fails.
         */
        formatMessage: 'is the wrong format',

        /**
         * @property {String} inclusionMessage
         * The default error message used when an inclusion validation fails.
         */
        inclusionMessage: 'is not included in the list of acceptable values',

        /**
         * @property {String} exclusionMessage
         * The default error message used when an exclusion validation fails.
         */
        exclusionMessage: 'is not an acceptable value',

        /**
         * @property {String} emailMessage
         * The default error message used when an email validation fails
         */
        emailMessage: 'is not a valid email address'
    },

    constructor: function(config) {
        this.initConfig(config);
    },

    /**
     * Returns the configured error message for any of the validation types.
     * @param {String} type The type of validation you want to get the error message for.
     */
    getMessage: function(type) {
        var getterFn = this['get' + type[0].toUpperCase() + type.slice(1) + 'Message'];
        if (getterFn) {
            return getterFn.call(this);
        }
        return '';
    },

    /**
     * The regular expression used to validate email addresses
     * @property emailRe
     * @type RegExp
     */
    emailRe: /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,

    /**
     * Validates that the given value is present.
     * For example:
     *
     *     validations: [{type: 'presence', field: 'age'}]
     *
     * @param {Object} config Config object
     * @param {Object} value The value to validate
     * @return {Boolean} True if validation passed
     */
    presence: function(config, value) {
        if (value === undefined) {
            value = config;
        }

        //we need an additional check for zero here because zero is an acceptable form of present data
        return !!value || value === 0;
    },

    /**
     * Returns true if the given value is between the configured min and max values.
     * For example:
     *
     *     validations: [{type: 'length', field: 'name', min: 2}]
     *
     * @param {Object} config Config object
     * @param {String} value The value to validate
     * @return {Boolean} True if the value passes validation
     */
    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;
        }
    },

    /**
     * Validates that an email string is in the correct format
     * @param {Object} config Config object
     * @param {String} email The email address
     * @return {Boolean} True if the value passes validation
     */
    email: function(config, email) {
        return Ext.data.validations.emailRe.test(email);
    },

    /**
     * Returns true if the given value passes validation against the configured `matcher` regex.
     * For example:
     *
     *     validations: [{type: 'format', field: 'username', matcher: /([a-z]+)[0-9]{2,3}/}]
     *
     * @param {Object} config Config object
     * @param {String} value The value to validate
     * @return {Boolean} True if the value passes the format validation
     */
    format: function(config, value) {
        return !!(config.matcher && config.matcher.test(value));
    },

    /**
     * Validates that the given value is present in the configured `list`.
     * For example:
     *
     *     validations: [{type: 'inclusion', field: 'gender', list: ['Male', 'Female']}]
     *
     * @param {Object} config Config object
     * @param {String} value The value to validate
     * @return {Boolean} True if the value is present in the list
     */
    inclusion: function(config, value) {
        return config.list && Ext.Array.indexOf(config.list,value) != -1;
    },

    /**
     * Validates that the given value is present in the configured `list`.
     * For example:
     *
     *     validations: [{type: 'exclusion', field: 'username', list: ['Admin', 'Operator']}]
     *
     * @param {Object} config Config object
     * @param {String} value The value to validate
     * @return {Boolean} True if the value is not present in the list
     */
    exclusion: function(config, value) {
        return config.list && Ext.Array.indexOf(config.list,value) == -1;
    }
});
/**
 * @author Tommy Maintz
 *
 * This class is the simple default id generator for Model instances.
 *
 * An example of a configured simple generator would be:
 *
 *     Ext.define('MyApp.data.MyModel', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             identifier: {
 *                 type: 'simple',
 *                 prefix: 'ID_'
 *             }
 *         }
 *     });
 *     // assign id's of ID_1, ID_2, ID_3, etc.
 *
 */
Ext.define('Ext.data.identifier.Simple', {
    alias: 'data.identifier.simple',
    
    statics: {
        AUTO_ID: 1
    },

    config: {
        prefix: 'ext-record-'
    },

    constructor: function(config) {
        this.initConfig(config);
    },

    generate: function(record) {
        return this._prefix + this.self.AUTO_ID++;
    }
});
/**
 * @author Tommy Maintz
 *
 * This class generates UUID's according to RFC 4122. This class has a default id property.
 * This means that a single instance is shared unless the id property is overridden. Thus,
 * two {@link Ext.data.Model} instances configured like the following share one generator:
 *
 *     Ext.define('MyApp.data.MyModelX', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             identifier: 'uuid'
 *         }
 *     });
 *
 *     Ext.define('MyApp.data.MyModelY', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             identifier: 'uuid'
 *         }
 *     });
 *
 * This allows all models using this class to share a commonly configured instance.
 *
 * # Using Version 1 ("Sequential") UUID's
 *
 * If a server can provide a proper timestamp and a "cryptographic quality random number"
 * (as described in RFC 4122), the shared instance can be configured as follows:
 *
 *     Ext.data.identifier.Uuid.Global.reconfigure({
 *         version: 1,
 *         clockSeq: clock, // 14 random bits
 *         salt: salt,      // 48 secure random bits (the Node field)
 *         timestamp: ts    // timestamp per Section 4.1.4
 *     });
 *
 *     // or these values can be split into 32-bit chunks:
 *
 *     Ext.data.identifier.Uuid.Global.reconfigure({
 *         version: 1,
 *         clockSeq: clock,
 *         salt: { lo: saltLow32, hi: saltHigh32 },
 *         timestamp: { lo: timestampLow32, hi: timestamptHigh32 }
 *     });
 *
 * This approach improves the generator's uniqueness by providing a valid timestamp and
 * higher quality random data. Version 1 UUID's should not be used unless this information
 * can be provided by a server and care should be taken to avoid caching of this data.
 *
 * See http://www.ietf.org/rfc/rfc4122.txt for details.
 */
Ext.define('Ext.data.identifier.Uuid', {
    extend: 'Ext.data.identifier.Simple',

    alias: 'data.identifier.uuid',

    config: {
        /**
         * The id for this generator instance. By default all model instances share the same
         * UUID generator instance. By specifying an id other then 'uuid', a unique generator instance
         * will be created for the Model.
         */
        id: undefined,

        /**
         * @property {Number/Object} salt
         * When created, this value is a 48-bit number. For computation, this value is split
         * into 32-bit parts and stored in an object with `hi` and `lo` properties.
         */
        salt: null,

        /**
         * @property {Number/Object} timestamp
         * When created, this value is a 60-bit number. For computation, this value is split
         * into 32-bit parts and stored in an object with `hi` and `lo` properties.
         */
        timestamp: null,

        /**
         * @cfg {Number} version
         * The Version of UUID. Supported values are:
         *
         *  * 1 : Time-based, "sequential" UUID.
         *  * 4 : Pseudo-random UUID.
         *
         * The default is 4.
         */
        version: 4
    },

    applyId: function(id) {
        if (id === undefined) {
            return Ext.data.identifier.Uuid.Global;
        }
        return id;
    },

    constructor: function() {
        var me = this;
        me.callParent(arguments);
        me.parts = [];
        me.init();
    },

    /**
     * Reconfigures this generator given new config properties.
     */
    reconfigure: function(config) {
        this.setConfig(config);
        this.init();
    },

    generate: function () {
        var me = this,
            parts = me.parts,
            version = me.getVersion(),
            salt = me.getSalt(),
            time = me.getTimestamp();

        /*
           The magic decoder ring (derived from RFC 4122 Section 4.2.2):

           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           |                          time_low                             |
           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           |           time_mid            |  ver  |        time_hi        |
           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           |res|  clock_hi |   clock_low   |    salt 0   |M|     salt 1    |
           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
           |                         salt (2-5)                            |
           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                     time_mid      clock_hi (low 6 bits)
            time_low     | time_hi |clock_lo
                |        |     |   || salt[0]
                |        |     |   ||   | salt[1..5]
                v        v     v   vv   v v
                0badf00d-aced-1def-b123-dfad0badbeef
                              ^    ^     ^
                        version    |     multicast (low bit)
                                   |
                                reserved (upper 2 bits)
        */
        parts[0] = me.toHex(time.lo, 8);
        parts[1] = me.toHex(time.hi & 0xFFFF, 4);
        parts[2] = me.toHex(((time.hi >>> 16) & 0xFFF) | (version << 12), 4);
        parts[3] = me.toHex(0x80 | ((me.clockSeq >>> 8) & 0x3F), 2) +
                   me.toHex(me.clockSeq & 0xFF, 2);
        parts[4] = me.toHex(salt.hi, 4) + me.toHex(salt.lo, 8);

        if (version == 4) {
            me.init(); // just regenerate all the random values...
        } else {
            // sequentially increment the timestamp...
            ++time.lo;
            if (time.lo >= me.twoPow32) { // if (overflow)
                time.lo = 0;
                ++time.hi;
            }
        }

        return parts.join('-').toLowerCase();
    },

    /**
     * @private
     */
    init: function () {
        var me = this,
            salt = me.getSalt(),
            time = me.getTimestamp();

        if (me.getVersion() == 4) {
            // See RFC 4122 (Secion 4.4)
            //   o  If the state was unavailable (e.g., non-existent or corrupted),
            //      or the saved node ID is different than the current node ID,
            //      generate a random clock sequence value.
            me.clockSeq = me.rand(0, me.twoPow14-1);

            if (!salt) {
                salt = {};
                me.setSalt(salt);
            }

            if (!time) {
                time = {};
                me.setTimestamp(time);
            }

            // See RFC 4122 (Secion 4.4)
            salt.lo = me.rand(0, me.twoPow32-1);
            salt.hi = me.rand(0, me.twoPow16-1);
            time.lo = me.rand(0, me.twoPow32-1);
            time.hi = me.rand(0, me.twoPow28-1);
        } else {
            // this is run only once per-instance
            me.setSalt(me.split(me.getSalt()));
            me.setTimestamp(me.split(me.getTimestamp()));

            // Set multicast bit: "the least significant bit of the first octet of the
            // node ID" (nodeId = salt for this implementation):
            me.getSalt().hi |= 0x100;
        }
    },

    /**
     * Some private values used in methods on this class.
     * @private
     */
    twoPow14: Math.pow(2, 14),
    twoPow16: Math.pow(2, 16),
    twoPow28: Math.pow(2, 28),
    twoPow32: Math.pow(2, 32),

    /**
     * Converts a value into a hexadecimal value. Also allows for a maximum length
     * of the returned value.
     * @param value
     * @param length
     * @private
     */
    toHex: function(value, length) {
        var ret = value.toString(16);
        if (ret.length > length) {
            ret = ret.substring(ret.length - length); // right-most digits
        } else if (ret.length < length) {
            ret = Ext.String.leftPad(ret, length, '0');
        }
        return ret;
    },

    /**
     * Generates a random value with between a low and high.
     * @param lo
     * @param hi
     * @private
     */
    rand: function(lo, hi) {
        var v = Math.random() * (hi - lo + 1);
        return Math.floor(v) + lo;
    },

    /**
     * Splits a number into a low and high value.
     * @param bignum
     * @private
     */
    split: function(bignum) {
        if (typeof(bignum) == 'number') {
            var hi = Math.floor(bignum / this.twoPow32);
            return {
                lo: Math.floor(bignum - hi * this.twoPow32),
                hi: hi
            };
        }
        return bignum;
    }
}, function() {
    this.Global = new this({
        id: 'uuid'
    });
});
/**
 * @author Ed Spencer
 *
 * Base Writer class used by most subclasses of {@link Ext.data.proxy.Server}. This class is
 * responsible for taking a set of {@link Ext.data.Operation} objects and a {@link Ext.data.Request}
 * object and modifying that request based on the Operations.
 *
 * For example a Ext.data.writer.Json would format the Operations and their {@link Ext.data.Model}
 * instances based on the config options passed to the JsonWriter's constructor.
 *
 * Writers are not needed for any kind of local storage - whether via a
 * {@link Ext.data.proxy.WebStorage Web Storage proxy} (see {@link Ext.data.proxy.LocalStorage localStorage})
 * or just in memory via a {@link Ext.data.proxy.Memory MemoryProxy}.
 */
Ext.define('Ext.data.writer.Writer', {
    alias: 'writer.base',
    alternateClassName: ['Ext.data.DataWriter', 'Ext.data.Writer'],

    config: {
        /**
         * @cfg {Boolean} writeAllFields True to write all fields from the record to the server. If set to false it
         * will only send the fields that were modified. Defaults to <tt>true</tt>. Note that any fields that have
         * {@link Ext.data.Field#persist} set to false will still be ignored.
         */
        writeAllFields: true,

        /**
         * @cfg {String} nameProperty This property is used to read the key for each value that will be sent to the server.
         * For example:
         * <pre><code>
    Ext.define('Person', {
        extend: 'Ext.data.Model',
        fields: [{
            name: 'first',
            mapping: 'firstName'
        }, {
            name: 'last',
            mapping: 'lastName'
        }, {
            name: 'age'
        }]
    });
    new Ext.data.writer.Writer({
        writeAllFields: true,
        nameProperty: 'mapping'
    });

    // This will be sent to the server
    {
        firstName: 'first name value',
        lastName: 'last name value',
        age: 1
    }

         * </code></pre>
         * Defaults to <tt>name</tt>. If the value is not present, the field name will always be used.
         */
        nameProperty: 'name'
    },

    /**
     * Creates new Writer.
     * @param {Object} config (optional) Config object.
     */
    constructor: function(config) {
        this.initConfig(config);
    },

    /**
     * Prepares a Proxy's Ext.data.Request object
     * @param {Ext.data.Request} request The request object
     * @return {Ext.data.Request} The modified request object
     */
    write: function(request) {
        var operation = request.getOperation(),
            records   = operation.getRecords() || [],
            len       = records.length,
            i         = 0,
            data      = [];

        for (; i < len; i++) {
            data.push(this.getRecordData(records[i]));
        }
        return this.writeRecords(request, data);
    },

    writeDate: function(field, date) {
        var dateFormat = field.dateFormat || 'timestamp';
        switch (dateFormat) {
            case 'timestamp':
                return date.getTime()/1000;
            case 'time':
                return date.getTime();
            default:
                return Ext.Date.format(date, dateFormat);
        }
    },

    /**
     * Formats the data for each record before sending it to the server. This
     * method should be overridden to format the data in a way that differs from the default.
     * @param {Object} record The record that we are writing to the server.
     * @return {Object} An object literal of name/value keys to be written to the server.
     * By default this method returns the data property on the record.
     */
    getRecordData: function(record) {
        var isPhantom = record.phantom === true,
            writeAll = this.getWriteAllFields() || isPhantom,
            nameProperty = this.getNameProperty(),
            fields = record.getFields(),
            data = {},
            changes, name, field, key, value, fieldConfig;

        if (writeAll) {
            fields.each(function(field) {
                fieldConfig = field.config;
                if (fieldConfig.persist) {
                    name = fieldConfig[nameProperty] || fieldConfig.name;
                    value = record.get(fieldConfig.name);
                    if (fieldConfig.type.type == 'date') {
                        value = this.writeDate(fieldConfig, value);
                    }
                    data[name] = value;
                }
            }, this);
        } else {
            // Only write the changes
            changes = record.getChanges();
            for (key in changes) {
                if (changes.hasOwnProperty(key)) {
                    field = fields.get(key);
                    fieldConfig = field.config;
                    if (fieldConfig.persist) {
                        name = fieldConfig[nameProperty] || field.name;
                        value = changes[key];
                        if (fieldConfig.type.type == 'date') {
                            value = this.writeDate(fieldConfig, value);
                        }
                        data[name] = value;
                    }
                }
            }
            if (!isPhantom) {
                // always include the id for non phantoms
                data[record.idProperty] = record.getId();
            }
        }
        return data;
    }

    // Convert old properties in data into a config object
});

/**
 * @author Ed Spencer
 * @class Ext.data.writer.Xml

This class is used to write {@link Ext.data.Model} data to the server in an XML format.
The {@link #documentRoot} property is used to specify the root element in the XML document.
The {@link #record} option is used to specify the element name for each record that will make
up the XML document.

 * @markdown
 */
Ext.define('Ext.data.writer.Xml', {
    
    /* Begin Definitions */
    
    extend: 'Ext.data.writer.Writer',
    alternateClassName: 'Ext.data.XmlWriter',
    
    alias: 'writer.xml',
    
    /* End Definitions */

    config: {
        /**
         * @cfg {String} documentRoot The name of the root element of the document. Defaults to <tt>'xmlData'</tt>.
         * If there is more than 1 record and the root is not specified, the default document root will still be used
         * to ensure a valid XML document is created.
         */
        documentRoot: 'xmlData',

        /**
         * @cfg {String} defaultDocumentRoot The root to be used if {@link #documentRoot} is empty and a root is required
         * to form a valid XML document.
         */
        defaultDocumentRoot: 'xmlData',

        /**
         * @cfg {String} header A header to use in the XML document (such as setting the encoding or version).
         * Defaults to <tt>''</tt>.
         */
        header: '',

        /**
         * @cfg {String} record The name of the node to use for each record. Defaults to <tt>'record'</tt>.
         */
        record: 'record'
    },

    //inherit docs
    writeRecords: function(request, data) {
        var me = this,
            xml = [],
            i = 0,
            len = data.length,
            root = me.getDocumentRoot(),
            record = me.getRecord(),
            needsRoot = data.length !== 1,
            item,
            key;
            
        // may not exist
        xml.push(me.getHeader() || '');
        
        if (!root && needsRoot) {
            root = me.getDefaultDocumentRoot();
        }
        
        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.setXmlData(xml.join(''));
        return request;
    }
});
/**
 * Small utility class used internally to represent a Direct method.
 * @class Ext.direct.RemotingMethod
 * @private
 */
Ext.define('Ext.direct.RemotingMethod', {
    config: {
        name: null,
        params: null,
        formHandler: null,
        len: null,
        ordered: true
    },

    constructor: function(config) {
        this.initConfig(config);
    },

    applyParams: function(params) {
        if (Ext.isNumber(params)) {
            this.setLen(params);
        } else if (Ext.isArray(params)) {
            this.setOrdered(false);

            var ln = params.length,
                ret = [],
                i, param, name;

            for (i = 0; i < ln; i++) {
                param = params[i];
                name = Ext.isObject(param) ? param.name : param;
                ret.push(name);
            }

            return ret;
        }
    },

    getArgs: function(params, paramOrder, paramsAsHash) {
        var args = [],
            i, ln;

        if (this.getOrdered()) {
            if (this.getLen() > 0) {
                // If a paramOrder was specified, add the params into the argument list in that order.
                if (paramOrder) {
                    for (i = 0, ln = paramOrder.length; i < ln; i++) {
                        args.push(params[paramOrder[i]]);
                    }
                } else if (paramsAsHash) {
                    // If paramsAsHash was specified, add all the params as a single object argument.
                    args.push(params);
                }
            }
        } else {
            args.push(params);
        }

        return args;
    },

    /**
     * Takes the arguments for the Direct function and splits the arguments
     * from the scope and the callback.
     * @param {Array} args The arguments passed to the direct call
     * @return {Object} An object with 3 properties, args, callback & scope.
     */
    getCallData: function(args) {
        var me = this,
            data = null,
            len  = me.getLen(),
            params = me.getParams(),
            callback, scope, name;

        if (me.getOrdered()) {
            callback = args[len];
            scope = args[len + 1];
            if (len !== 0) {
                data = args.slice(0, len);
            }
        } else {
            data = Ext.apply({}, args[0]);
            callback = args[1];
            scope = args[2];

            for (name in data) {
                if (data.hasOwnProperty(name)) {
                    if (!Ext.Array.contains(params, name)) {
                        delete data[name];
                    }
                }
            }
        }

        return {
            data: data,
            callback: callback,
            scope: scope
        };
    }
});

/**
 * Supporting Class for Ext.Direct (not intended to be used directly).
 */
Ext.define('Ext.direct.Transaction', {
    alias: 'direct.transaction',
    alternateClassName: 'Ext.Direct.Transaction',

    statics: {
        TRANSACTION_ID: 0
    },

    config: {
        id: undefined,
        provider: null,
        retryCount: 0,
        args: null,
        action: null,
        method: null,
        data: null,
        callback: null,
        form: null
    },

    constructor: function(config) {
        this.initConfig(config);
    },

    applyId: function(id) {
        if (id === undefined) {
            id = ++this.self.TRANSACTION_ID;
        }
        return id;
    },

    updateId: function(id) {
        this.id = this.tid = id;
    },

    getTid: function() {
        return this.tid;
    },

    send: function(){
         this.getProvider().queueTransaction(this);
    },

    retry: function(){
        this.setRetryCount(this.getRetryCount() + 1);
        this.send();
    }
});
/**
 * This class encapsulates a _collection_ of DOM elements, providing methods to filter members, or to perform collective
 * actions upon the whole set.
 *
 * Although they are not listed, this class supports all of the methods of {@link Ext.dom.Element}. The methods from
 * these classes will be performed on all the elements in this collection.
 *
 * All methods return _this_ and can be chained.
 *
 * Usage:
 *
 *      var els = Ext.select("#some-el div.some-class", true);
 *      // or select directly from an existing element
 *      var el = Ext.get('some-el');
 *      el.select('div.some-class', true);
 *
 *      els.setWidth(100); // all elements become 100 width
 *      els.hide(true); // all elements fade out and hide
 *      // or
 *      els.setWidth(100).hide(true);
 */
Ext.define('Ext.dom.CompositeElement', {
    alternateClassName: 'Ext.CompositeElement',

    extend: 'Ext.dom.CompositeElementLite',

    // private
    getElement: function(el) {
        // In this case just return it, since we already have a reference to it
        return el;
    },

    // private
    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 {
            throw new Error("[Ext.select] Invalid selector specified: " + selector);
        }

        return (unique === true) ? new Ext.CompositeElement(elements) : new Ext.CompositeElementLite(elements);
    };
});

/**
 * @private
 */
Ext.define('Ext.event.Controller', {

    isFiring: false,

    listenerStack: null,

    constructor: function(info) {
        this.firingListeners = [];
        this.firingArguments = [];

        this.setInfo(info);

        return this;
    },

    setInfo: function(info) {
        this.info = info;
    },

    getInfo: function() {
        return this.info;
    },

    setListenerStacks: function(listenerStacks) {
        this.listenerStacks = listenerStacks;
    },

    fire: function(args, action) {
        var listenerStacks = this.listenerStacks,
            firingListeners = this.firingListeners,
            firingArguments = this.firingArguments,
            push = firingListeners.push,
            ln = listenerStacks.length,
            listeners, beforeListeners, currentListeners, afterListeners,
            isActionBefore = false,
            isActionAfter = false,
            i;

        firingListeners.length = 0;

        if (action) {
            if (action.order !== 'after') {
                isActionBefore = true;
            }
            else {
                isActionAfter = true;
            }
        }

        if (ln === 1) {
            listeners = listenerStacks[0].listeners;
            beforeListeners = listeners.before;
            currentListeners = listeners.current;
            afterListeners = listeners.after;

            if (beforeListeners.length > 0) {
                push.apply(firingListeners, beforeListeners);
            }

            if (isActionBefore) {
                push.call(firingListeners, action);
            }

            if (currentListeners.length > 0) {
                push.apply(firingListeners, currentListeners);
            }

            if (isActionAfter) {
                push.call(firingListeners, action);
            }

            if (afterListeners.length > 0) {
                push.apply(firingListeners, afterListeners);
            }
        }
        else {
            for (i = 0; i < ln; i++) {
                beforeListeners = listenerStacks[i].listeners.before;
                if (beforeListeners.length > 0) {
                    push.apply(firingListeners, beforeListeners);
                }
            }

            if (isActionBefore) {
                push.call(firingListeners, action);
            }

            for (i = 0; i < ln; i++) {
                currentListeners = listenerStacks[i].listeners.current;
                if (currentListeners.length > 0) {
                    push.apply(firingListeners, currentListeners);
                }
            }

            if (isActionAfter) {
                push.call(firingListeners, action);
            }

            for (i = 0; i < ln; i++) {
                afterListeners = listenerStacks[i].listeners.after;
                if (afterListeners.length > 0) {
                    push.apply(firingListeners, afterListeners);
                }
            }
        }

        if (firingListeners.length === 0) {
            return this;
        }

        if (!args) {
            args = [];
        }

        firingArguments.length = 0;
        firingArguments.push.apply(firingArguments, args);

        // Backwards compatibility
        firingArguments.push(null, this);

        this.doFire();

        return this;
    },

    doFire: function() {
        var firingListeners = this.firingListeners,
            firingArguments = this.firingArguments,
            optionsArgumentIndex = firingArguments.length - 2,
            i, ln, listener, options, fn, firingFn,
            boundFn, isLateBinding, scope, args, result;

        this.isPausing = false;
        this.isPaused = false;
        this.isStopped = false;
        this.isFiring = true;

        for (i = 0,ln = firingListeners.length; i < ln; i++) {
            listener = firingListeners[i];
            options = listener.options;
            fn = listener.fn;
            firingFn = listener.firingFn;
            boundFn = listener.boundFn;
            isLateBinding = listener.isLateBinding;
            scope = listener.scope;

            // Re-bind the callback if it has changed since the last time it's bound (overridden)
            if (isLateBinding && boundFn && boundFn !== scope[fn]) {
                boundFn = false;
                firingFn = false;
            }

            if (!boundFn) {
                if (isLateBinding) {
                    boundFn = scope[fn];

                    if (!boundFn) {
                        continue;
                    }
                }
                else {
                    boundFn = fn;
                }

                listener.boundFn = boundFn;
            }

            if (!firingFn) {
                firingFn = boundFn;

                if (options.buffer) {
                    firingFn = Ext.Function.createBuffered(firingFn, options.buffer, scope);
                }

                if (options.delay) {
                    firingFn = Ext.Function.createDelayed(firingFn, options.delay, scope);
                }

                listener.firingFn = firingFn;
            }

            firingArguments[optionsArgumentIndex] = options;

            args = firingArguments;

            if (options.args) {
                args = options.args.concat(args);
            }

            if (options.single === true) {
                listener.stack.remove(fn, scope, listener.order);
            }

            result = firingFn.apply(scope, args);

            if (result === false) {
                this.stop();
            }

            if (this.isStopped) {
                break;
            }

            if (this.isPausing) {
                this.isPaused = true;
                firingListeners.splice(0, i + 1);
                return;
            }
        }

        this.isFiring = false;
        this.listenerStacks = null;
        firingListeners.length = 0;
        firingArguments.length = 0;
        this.connectingController = null;
    },

    connect: function(controller) {
        this.connectingController = controller;
    },

    resume: function() {
        var connectingController = this.connectingController;

        this.isPausing = false;

        if (this.isPaused && this.firingListeners.length > 0) {
            this.isPaused = false;
            this.doFire();
        }

        if (connectingController) {
            connectingController.resume();
        }

        return this;
    },

    isInterrupted: function() {
        return this.isStopped || this.isPaused;
    },

    stop: function() {
        var connectingController = this.connectingController;

        this.isStopped = true;

        if (connectingController) {
            this.connectingController = null;
            connectingController.stop();
        }

        this.isFiring = false;

        this.listenerStacks = null;

        return this;
    },

    pause: function() {
        var connectingController = this.connectingController;

        this.isPausing = true;

        if (connectingController) {
            connectingController.pause();
        }

        return this;
    }
});

// Using @mixins to include all members of Ext.event.Touch
// into here to keep documentation simpler
/**
 * @mixins Ext.event.Touch
 *
 * Just as {@link Ext.dom.Element} wraps around a native DOM node, {@link Ext.event.Event} wraps the browser's native
 * event-object normalizing cross-browser differences such as mechanisms to stop event-propagation along with a method
 * to prevent default actions from taking place.
 *
 * Here is a simple example of how you use it:
 *
 *     @example preview
 *     Ext.Viewport.add({
 *         layout: 'fit',
 *         items: [
 *             {
 *                 docked: 'top',
 *                 xtype: 'toolbar',
 *                 title: 'Ext.event.Event example!'
 *             },
 *             {
 *                 id: 'logger',
 *                 styleHtmlContent: true,
 *                 html: 'Tap somewhere!',
 *                 padding: 5
 *             }
 *         ]
 *     });
 *
 *     Ext.Viewport.element.on({
 *         tap: function(e, node) {
 *             var string = '';
 *
 *             string += 'You tapped at: <strong>{ x: ' + e.pageX + ', y: ' + e.pageY + ' }</strong> <i>(e.pageX & e.pageY)</i>';
 *             string += '<hr />';
 *             string += 'The HTMLElement you tapped has the className of: <strong>' + e.target.className + '</strong> <i>(e.target)</i>';
 *             string += '<hr />';
 *             string += 'The HTMLElement which has the listener has a className of: <strong>' + e.getTarget().className + '</strong> <i>(e.getTarget())</i>';
 *
 *             Ext.getCmp('logger').setHtml(string);
 *         }
 *     });
 *
 * ## Recgonisers
 *
 * Sencha Touch includes a bunch of default event recognisers to know when a user taps, swipes, etc.
 *
 * For a full list of default recognisers, and more information, please view the {@link Ext.event.recognizer.Recognizer} documentation
 */
Ext.define('Ext.event.Event', {
    alternateClassName: 'Ext.EventObject',
    isStopped: false,

    set: function(name, value) {
        if (arguments.length === 1 && typeof name != 'string') {
            var info = name;

            for (name in info) {
                if (info.hasOwnProperty(name)) {
                    this[name] = info[name];
                }
            }
        }
        else {
            this[name] = info[name];
        }
    },

    /**
     * Stop the event (preventDefault and stopPropagation)
     */
    stopEvent: function() {
        return this.stopPropagation();
    },

    /**
     * Cancels bubbling of the event.
     */
    stopPropagation: function() {
        this.isStopped = true;

        return this;
    }
});

/**
 * @private
 */
Ext.define('Ext.event.ListenerStack', {

    currentOrder: 'current',

    length: 0,

    constructor: function() {
        this.listeners = {
            before: [],
            current: [],
            after: []
        };

        this.lateBindingMap = {};

        return this;
    },

    add: function(fn, scope, options, order) {
        var lateBindingMap = this.lateBindingMap,
            listeners = this.getAll(order),
            i = listeners.length,
            bindingMap, listener, id;

        if (typeof fn == 'string' && scope.isIdentifiable) {
            id = scope.getId();

            bindingMap = lateBindingMap[id];

            if (bindingMap) {
                if (bindingMap[fn]) {
                    return false;
                }
                else {
                    bindingMap[fn] = true;
                }
            }
            else {
                lateBindingMap[id] = bindingMap = {};
                bindingMap[fn] = true;
            }
        }
        else {
            if (i > 0) {
                while (i--) {
                    listener = listeners[i];

                    if (listener.fn === fn && listener.scope === scope) {
                        listener.options = options;
                        return false;
                    }
                }
            }
        }

        listener = this.create(fn, scope, options, order);

        if (options && options.prepend) {
            delete options.prepend;
            listeners.unshift(listener);
        }
        else {
            listeners.push(listener);
        }

        this.length++;

        return true;
    },

    getAt: function(index, order) {
        return this.getAll(order)[index];
    },

    getAll: function(order) {
        if (!order) {
            order = this.currentOrder;
        }

        return this.listeners[order];
    },

    count: function(order) {
        return this.getAll(order).length;
    },

    create: function(fn, scope, options, order) {
        return {
            stack: this,
            fn: fn,
            firingFn: false,
            boundFn: false,
            isLateBinding: typeof fn == 'string',
            scope: scope,
            options: options || {},
            order: order
        };
    },

    remove: function(fn, scope, order) {
        var listeners = this.getAll(order),
            i = listeners.length,
            isRemoved = false,
            lateBindingMap = this.lateBindingMap,
            listener, id;

        if (i > 0) {
            // Start from the end index, faster than looping from the
            // beginning for "single" listeners,
            // which are normally LIFO
            while (i--) {
                listener = listeners[i];

                if (listener.fn === fn && listener.scope === scope) {
                    listeners.splice(i, 1);
                    isRemoved = true;
                    this.length--;

                    if (typeof fn == 'string' && scope.isIdentifiable) {
                        id = scope.getId();

                        if (lateBindingMap[id] && lateBindingMap[id][fn]) {
                            delete lateBindingMap[id][fn];
                        }
                    }
                    break;
                }
            }
        }

        return isRemoved;
    }
});

/**
 * @private
 */
Ext.define('Ext.event.publisher.Publisher', {
    targetType: '',

    idSelectorRegex: /^#([\w\-]+)$/i,

    constructor: function() {
        var handledEvents = this.handledEvents,
            handledEventsMap,
            i, ln, event;

        handledEventsMap = this.handledEventsMap = {};

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

            handledEventsMap[event] = true;
        }

        this.subscribers = {};

        return this;
    },

    handles: function(eventName) {
        var map = this.handledEventsMap;

        return !!map[eventName] || !!map['*'] || eventName === '*';
    },

    getHandledEvents: function() {
        return this.handledEvents;
    },

    setDispatcher: function(dispatcher) {
        this.dispatcher = dispatcher;
    },

    subscribe: function() {
        return false;
    },

    unsubscribe: function() {
        return false;
    },

    unsubscribeAll: function() {
        delete this.subscribers;
        this.subscribers = {};

        return this;
    },

    notify: function() {
        return false;
    },

    getTargetType: function() {
        return this.targetType;
    },

    dispatch: function(target, eventName, args) {
        this.dispatcher.doDispatchEvent(this.targetType, target, eventName, args);
    }
});

/**
 * A base class for all event recognisers in Sencha Touch.
 *
 * Sencha Touch, by default, includes various different {@link Ext.event.recognizer.Recognizer} subclasses to recognise
 * events happening in your application.
 *
 * ## Default recognisers
 *
 * * {@link Ext.event.recognizer.Tap}
 * * {@link Ext.event.recognizer.DoubleTap}
 * * {@link Ext.event.recognizer.LongPress}
 * * {@link Ext.event.recognizer.Drag}
 * * {@link Ext.event.recognizer.HorizontalSwipe}
 * * {@link Ext.event.recognizer.Pinch}
 * * {@link Ext.event.recognizer.Rotate}
 *
 * ## Additional recognisers
 *
 * * {@link Ext.event.recognizer.VerticalSwipe}
 *
 * If you want to create custom recognisers, or disable recognisers in your Sencha Touch application, please refer to the
 * documentation in {@link Ext#setup}.
 *
 * @private
 */
Ext.define('Ext.event.recognizer.Recognizer', {
    mixins: ['Ext.mixin.Identifiable'],

    handledEvents: [],

    config: {
        onRecognized: Ext.emptyFn,
        onFailed: Ext.emptyFn,
        callbackScope: null
    },

    constructor: function(config) {
        this.initConfig(config);

        return this;
    },

    getHandledEvents: function() {
        return this.handledEvents;
    },

    onStart: Ext.emptyFn,

    onEnd: Ext.emptyFn,

    fail: function() {
        this.getOnFailed().apply(this.getCallbackScope(), arguments);

        return false;
    },

    fire: function() {
        this.getOnRecognized().apply(this.getCallbackScope(), arguments);
    }
});

/**
 * @private
 */
Ext.define('Ext.event.recognizer.Touch', {

    extend: 'Ext.event.recognizer.Recognizer',

    onTouchStart: Ext.emptyFn,

    onTouchMove: Ext.emptyFn,

    onTouchEnd: Ext.emptyFn
});

/**
 *
 */
Ext.define('Ext.fx.State', {

    isAnimatable: {
        'background-color'   : true,
        'background-image'   : true,
        'background-position': true,
        'border-bottom-color': true,
        'border-bottom-width': true,
        'border-color'       : true,
        'border-left-color'  : true,
        'border-left-width'  : true,
        'border-right-color' : true,
        'border-right-width' : true,
        'border-spacing'     : true,
        'border-top-color'   : true,
        'border-top-width'   : true,
        'border-width'       : true,
        'bottom'             : true,
        'color'              : true,
        'crop'               : true,
        'font-size'          : true,
        'font-weight'        : true,
        'height'             : true,
        'left'               : true,
        'letter-spacing'     : true,
        'line-height'        : true,
        'margin-bottom'      : true,
        'margin-left'        : true,
        'margin-right'       : true,
        'margin-top'         : true,
        'max-height'         : true,
        'max-width'          : true,
        'min-height'         : true,
        'min-width'          : true,
        'opacity'            : true,
        'outline-color'      : true,
        'outline-offset'     : true,
        'outline-width'      : true,
        'padding-bottom'     : true,
        'padding-left'       : true,
        'padding-right'      : true,
        'padding-top'        : true,
        'right'              : true,
        'text-indent'        : true,
        'text-shadow'        : true,
        'top'                : true,
        'vertical-align'     : true,
        'visibility'         : true,
        'width'              : true,
        'word-spacing'       : true,
        'z-index'            : true,
        'zoom'               : true,
        'transform'          : true
    },

    constructor: function(data) {
        this.data = {};

        this.set(data);
    },

    setConfig: function(data) {
        this.set(data);

        return this;
    },

    setRaw: function(data) {
        this.data = data;

        return this;
    },

    clear: function() {
        return this.setRaw({});
    },

    setTransform: function(name, value) {
        var data = this.data,
            isArray = Ext.isArray(value),
            transform = data.transform,
            ln, key;

        if (!transform) {
            transform = data.transform = {
                translateX: 0,
                translateY: 0,
                translateZ: 0,
                scaleX: 1,
                scaleY: 1,
                scaleZ: 1,
                rotate: 0,
                rotateX: 0,
                rotateY: 0,
                rotateZ: 0,
                skewX: 0,
                skewY: 0
            };
        }

        if (typeof name == 'string') {
            switch (name) {
                case 'translate':
                    if (isArray) {
                        ln = value.length;

                        if (ln == 0) { break; }

                        transform.translateX = value[0];

                        if (ln == 1) { break; }

                        transform.translateY = value[1];

                        if (ln == 2) { break; }

                        transform.translateZ = value[2];
                    }
                    else {
                        transform.translateX = value;
                    }
                    break;

                case 'rotate':
                    if (isArray) {
                        ln = value.length;

                        if (ln == 0) { break; }

                        transform.rotateX = value[0];

                        if (ln == 1) { break; }

                        transform.rotateY = value[1];

                        if (ln == 2) { break; }

                        transform.rotateZ = value[2];
                    }
                    else {
                        transform.rotate = value;
                    }
                    break;


                case 'scale':
                    if (isArray) {
                        ln = value.length;

                        if (ln == 0) { break; }

                        transform.scaleX = value[0];

                        if (ln == 1) { break; }

                        transform.scaleY = value[1];

                        if (ln == 2) { break; }

                        transform.scaleZ = value[2];
                    }
                    else {
                        transform.scaleX = value;
                        transform.scaleY = value;
                    }
                    break;

                case 'skew':
                    if (isArray) {
                        ln = value.length;

                        if (ln == 0) { break; }

                        transform.skewX = value[0];

                        if (ln == 1) { break; }

                        transform.skewY = value[1];
                    }
                    else {
                        transform.skewX = value;
                    }
                    break;

                default:
                    transform[name] = value;
            }
        }
        else {
            for (key in name) {
                if (name.hasOwnProperty(key)) {
                    value = name[key];

                    this.setTransform(key, value);
                }
            }
        }
    },

    set: function(name, value) {
        var data = this.data,
            key;

        if (typeof name != 'string') {
            for (key in name) {
                value = name[key];

                if (key === 'transform') {
                    this.setTransform(value);
                }
                else {
                    data[key] = value;
                }
            }
        }
        else {
            if (name === 'transform') {
                this.setTransform(value);
            }
            else {
                data[name] = value;
            }
        }

        return this;
    },

    unset: function(name) {
        var data = this.data;

        if (data.hasOwnProperty(name)) {
            delete data[name];
        }

        return this;
    },

    getData: function() {
        return this.data;
    }
});



/**
 * @private
 */
Ext.define('Ext.fx.easing.Abstract', {

    config: {
        startTime: 0,
        startValue: 0
    },

    isEasing: true,

    isEnded: false,

    constructor: function(config) {
        this.initConfig(config);

        return this;
    },

    applyStartTime: function(startTime) {
        if (!startTime) {
            startTime = Ext.Date.now();
        }

        return startTime;
    },

    updateStartTime: function(startTime) {
        this.reset();
    },

    reset: function() {
        this.isEnded = false;
    },

    getValue: Ext.emptyFn
});

/**
 * @private
 */
Ext.define('Ext.fx.easing.Bounce', {

    extend: 'Ext.fx.easing.Abstract',

    config: {
        springTension: 0.3,
        acceleration: 30,
        startVelocity: 0
    },

    getValue: function() {
        var deltaTime = Ext.Date.now() - this.getStartTime(),
            theta = (deltaTime / this.getAcceleration()),
            powTime = theta * Math.pow(Math.E, -this.getSpringTension() * theta);

        return this.getStartValue() + (this.getStartVelocity() * powTime);
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.easing.Linear', {

    extend: 'Ext.fx.easing.Abstract',

    alias: 'easing.linear',

    config: {
        duration: 0,
        endValue: 0
    },

    updateStartValue: function(startValue) {
        this.distance = this.getEndValue() - startValue;
    },

    updateEndValue: function(endValue) {
        this.distance = endValue - this.getStartValue();
    },

    getValue: function() {
        var deltaTime = Ext.Date.now() - this.getStartTime(),
            duration = this.getDuration();

        if (deltaTime > duration) {
            this.isEnded = true;
            return this.getEndValue();
        }
        else {
            return this.getStartValue() + ((deltaTime / duration) * this.distance);
        }
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.easing.Momentum', {

    extend: 'Ext.fx.easing.Abstract',

    config: {
        acceleration: 30,
        friction: 0,
        startVelocity: 0
    },

    alpha: 0,

    updateFriction: function(friction) {
        var theta = Math.log(1 - (friction / 10));

        this.theta = theta;

        this.alpha = theta / this.getAcceleration();
    },

    updateStartVelocity: function(velocity) {
        this.velocity = velocity * this.getAcceleration();
    },

    updateAcceleration: function(acceleration) {
        this.velocity = this.getStartVelocity() * acceleration;

        this.alpha = this.theta / acceleration;
    },

    getValue: function() {
        return this.getStartValue() - this.velocity * (1 - this.getFrictionFactor()) / this.theta;
    },

    getFrictionFactor: function() {
        var deltaTime = Ext.Date.now() - this.getStartTime();

        return Math.exp(deltaTime * this.alpha);
    },

    getVelocity: function() {
        return this.getFrictionFactor() * this.velocity;
    }
});

Ext.define('Ext.log.Base', {
    config: {},

    constructor: function(config) {
        this.initConfig(config);

        return this;
    }
});

/**
 * @class Ext.Logger
 * Logs messages to help with debugging.
 *
 * ## Example
 *
 *     Ext.Logger.deprecate('This method is no longer supported.');
 *
 * @singleton
 */
(function() {
var Logger = Ext.define('Ext.log.Logger', {

    extend: 'Ext.log.Base',

    statics: {
        defaultPriority: 'info',

        priorities: {
            /**
             * @method verbose
             * Convenience method for {@link #log} with priority 'verbose'
             */
            verbose:    0,
            /**
             * @method info
             * Convenience method for {@link #log} with priority 'info'
             */
            info:       1,
            /**
             * @method deprecate
             * Convenience method for {@link #log} with priority 'deprecate'
             */
            deprecate:  2,
            /**
             * @method warn
             * Convenience method for {@link #log} with priority 'warn'
             */
            warn:       3,
            /**
             * @method error
             * Convenience method for {@link #log} with priority 'error'
             */
            error:      4
        }
    },

    config: {
        enabled: true,
        minPriority: 'deprecate',
        writers: {}
    },

    /**
     * Logs a message to help with debugging
     * @param  {String} message  Message to log
     * @param  {Number} priority Priority of the log message
     */
    log: function(message, priority, callerId) {
        if (!this.getEnabled()) {
            return this;
        }

        var statics = Logger,
            priorities = statics.priorities,
            priorityValue = priorities[priority],
            caller = this.log.caller,
            callerDisplayName = '',
            writers = this.getWriters(),
            event, i, originalCaller;

        if (!priority) {
            priority = 'info';
        }

        if (priorities[this.getMinPriority()] > priorityValue) {
            return this;
        }

        if (!callerId) {
            callerId = 1;
        }

        if (Ext.isArray(message)) {
            message = message.join(" ");
        }
        else {
            message = String(message);
        }

        if (typeof callerId == 'number') {
            i = callerId;

            do {
                i--;

                caller = caller.caller;

                if (!caller) {
                    break;
                }

                if (!originalCaller) {
                    originalCaller = caller.caller;
                }

                if (i <= 0 && caller.displayName) {
                    break;
                }
            }
            while (caller !== originalCaller);

            callerDisplayName = Ext.getDisplayName(caller);
        }
        else {
            caller = caller.caller;
            callerDisplayName = Ext.getDisplayName(callerId) + '#' + caller.$name;
        }

        event = {
            time: Ext.Date.now(),
            priority: priorityValue,
            priorityName: priority,
            message: message,
            caller: caller,
            callerDisplayName: callerDisplayName
        };

        for (i in writers) {
            if (writers.hasOwnProperty(i)) {
                writers[i].write(Ext.merge({}, event));
            }
        }

        return this;
    }

}, function() {
    Ext.Object.each(this.priorities, function(priority) {
        this.override(priority, function(message, callerId) {
            if (!callerId) {
                callerId = 1;
            }

            if (typeof callerId == 'number') {
                callerId += 1;
            }

            this.log(message, priority, callerId);
        });
    }, this);
});

})();

Ext.define('Ext.log.filter.Filter', {
    extend: 'Ext.log.Base',

    accept: function(event) {
        return true;
    }
});

Ext.define('Ext.log.filter.Priority', {
    extend: 'Ext.log.filter.Filter',

    config: {
        minPriority: 1
    },

    accept: function(event) {
        return event.priority >= this.getMinPriority();
    }
});

Ext.define('Ext.log.formatter.Formatter', {
    extend: 'Ext.log.Base',

    config: {
        messageFormat: "{message}"
    },

    format: function(event) {
        return this.substitute(this.getMessageFormat(), event);
    },

    substitute: function(template, data) {
        var name, value;

        for (name in data) {
            if (data.hasOwnProperty(name)) {
                value = data[name];

                template = template.replace(new RegExp("\\{" + name + "\\}", "g"), value);
            }
        }

        return template;
    }
});

Ext.define('Ext.log.writer.Writer', {
    extend: 'Ext.log.Base',

    requires: ['Ext.log.formatter.Formatter'],

    config: {
        formatter: null,
        filters: {}
    },

    constructor: function() {
        this.activeFilters = [];

        return this.callParent(arguments);
    },

    updateFilters: function(filters) {
        var activeFilters = this.activeFilters,
            i, filter;

        activeFilters.length = 0;

        for (i in filters) {
            if (filters.hasOwnProperty(i)) {
                filter = filters[i];
                activeFilters.push(filter);
            }
        }
    },

    write: function(event) {
        var filters = this.activeFilters,
            formatter = this.getFormatter(),
            i, ln, filter;

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

            if (!filters[i].accept(event)) {
                return this;
            }
        }

        if (formatter) {
            event.message = formatter.format(event);
        }

        this.doWrite(event);

        return this;
    },

    // @private
    doWrite: Ext.emptyFn
});

/**
 * Base class for all mixins.
 * @private
 */
Ext.define('Ext.mixin.Mixin', {
    onClassExtended: function(cls, data) {
        var mixinConfig = data.mixinConfig,
            parentClassMixinConfig,
            beforeHooks, afterHooks;

        if (mixinConfig) {
            parentClassMixinConfig = cls.superclass.mixinConfig;

            if (parentClassMixinConfig) {
                mixinConfig = data.mixinConfig = Ext.merge({}, parentClassMixinConfig, mixinConfig);
            }

            data.mixinId = mixinConfig.id;

            beforeHooks = mixinConfig.beforeHooks;
            afterHooks = mixinConfig.hooks || mixinConfig.afterHooks;

            if (beforeHooks || afterHooks) {
                Ext.Function.interceptBefore(data, 'onClassMixedIn', function(targetClass) {
                    var mixin = this.prototype;

                    if (beforeHooks) {
                        Ext.Object.each(beforeHooks, function(from, to) {
                            targetClass.override(to, function() {
                                if (mixin[from].apply(this, arguments) !== false) {
                                    return this.callOverridden(arguments);
                                }
                            });
                        });
                    }

                    if (afterHooks) {
                        Ext.Object.each(afterHooks, function(from, to) {
                            targetClass.override(to, function() {
                                var ret = this.callOverridden(arguments);

                                mixin[from].apply(this, arguments);

                                return ret;
                            });
                        });
                    }
                });
            }
        }
    }
});

/**
 * Tracks what records are currently selected in a databound widget. This class is mixed in to {@link Ext.dataview.DataView} and
 * all subclasses.
 * @private
 */
Ext.define('Ext.mixin.Selectable', {
    extend: 'Ext.mixin.Mixin',

    mixinConfig: {
        id: 'selectable',
        hooks: {
            updateStore: 'updateStore'
        }
    },

    /**
     * @event beforeselectionchange
     * Fires before an item is selected
     * @param {Ext.mixin.Selectable} this
     * @preventable selectionchange
     * @deprecated 2.0.0 Please listen to the {@link #selectionchange} event with an order of `before` instead.
     */

    /**
     * @event selectionchange
     * Fires when a selection changes
     * @param {Ext.mixin.Selectable} this
     * @param {Ext.data.Model[]} records The records whose selection has changed
     */

    config: {
        /**
         * @cfg {Boolean} disableSelection <p><tt>true</tt> to disable selection.
         * This configuration will lock the selection model that the DataView uses.</p>
         * @accessor
         */
        disableSelection: null,

        /**
         * @cfg {String} mode
         * Modes of selection.
         * Valid values are SINGLE, SIMPLE, and MULTI. Defaults to 'SINGLE'
         * @accessor
         */
        mode: 'SINGLE',

        /**
         * @cfg {Boolean} allowDeselect
         * Allow users to deselect a record in a DataView, List or Grid. Only applicable when the Selectable's mode is
         * 'SINGLE'. Defaults to false.
         * @accessor
         */
        allowDeselect: false,

        /**
         * @cfg {Ext.data.Model} lastSelected
         * @private
         * @accessor
         */
        lastSelected: null,

        /**
         * @cfg {Ext.data.Model} lastFocused
         * @private
         * @accessor
         */
        lastFocused: null,

        /**
         * @cfg {Boolean} deselectOnContainerClick True to deselect current selection when the container body is
         * clicked. Defaults to true
         * @accessor
         */
        deselectOnContainerClick: true
    },

    modes: {
        SINGLE: true,
        SIMPLE: true,
        MULTI: true
    },

    selectableEventHooks: {
        addrecords: 'onSelectionStoreAdd',
        removerecords: 'onSelectionStoreRemove',
        updaterecord: 'onSelectionStoreUpdate',
        load: 'refreshSelection',
        refresh: 'refreshSelection'
    },

    constructor: function() {
        this.selected = new Ext.util.MixedCollection();
        this.callParent(arguments);
    },

    /**
     * @private
     */
    applyMode: function(mode) {
        mode = mode ? mode.toUpperCase() : 'SINGLE';
        // set to mode specified unless it doesnt exist, in that case
        // use single.
        return this.modes[mode] ? mode : 'SINGLE';
    },

    /**
     * @private
     */
    updateStore: function(newStore, oldStore) {
        var me = this,
            bindEvents = Ext.apply({}, me.selectableEventHooks, { scope: me });

        if (oldStore && Ext.isObject(oldStore) && oldStore.isStore) {
            if (oldStore.autoDestroy) {
                oldStore.destroy();
            }
            else {
                oldStore.un(bindEvents);
            }
        }

        if (newStore) {
            newStore.on(bindEvents);
            me.refreshSelection();
        }
    },

    /**
     * Selects all records.
     * @param {Boolean} silent True to suppress all select events.
     */
    selectAll: function(silent) {
        var me = this,
            selections = me.getStore().getRange(),
            ln = selections.length,
            i = 0;
        for (; i < ln; i++) {
            me.select(selections[i], true, silent);
        }
    },

    /**
     * Deselects all records.
     */
    deselectAll: function(supress) {
        var me = this,
            selections = me.getStore().getRange(),
            ln = selections.length,
            i = 0;

        for (; i < ln; i++) {
            me.deselect(selections, supress);
        }

        me.selected.clear();
        me.setLastSelected(null);
        me.setLastFocused(null);
    },

    // Provides differentiation of logic between MULTI, SIMPLE and SINGLE
    // selection modes.
    selectWithEvent: function(record) {
        var me = this,
            isSelected = me.isSelected(record);
        switch (me.getMode()) {
            case 'MULTI':
            case 'SIMPLE':
                if (isSelected) {
                    me.deselect(record);
                }
                else {
                    me.select(record, true);
                }
                break;
            case 'SINGLE':
                if (me.getAllowDeselect() && isSelected) {
                    // if allowDeselect is on and this record isSelected, deselect it
                    me.deselect(record);
                } else {
                    // select the record and do NOT maintain existing selections
                    me.select(record, false);
                }
                break;
        }
    },

    /**
     * Selects a range of rows if the selection model {@link Ext.mixin.Selectable#getDisableSelection is not locked}.
     * All rows in between startRow and endRow are also selected.
     * @param {Number} startRow The index of the first row in the range
     * @param {Number} endRow The index of the last row in the range
     * @param {Boolean} keepExisting (optional) True to retain existing selections
     */
    selectRange: function(startRecord, endRecord, keepExisting, dir) {
        var me = this,
            store = me.getStore(),
            startRow = store.indexOf(startRecord),
            endRow = store.indexOf(endRecord),
            selectedCount = 0,
            tmp, dontDeselect, i;

        if (me.getDisableSelection()) {
            return;
        }

        // swap values
        if (startRow > endRow) {
            tmp = endRow;
            endRow = startRow;
            startRow = tmp;
        }

        for (i = startRow; i <= endRow; i++) {
            if (me.isSelected(store.getAt(i))) {
                selectedCount++;
            }
        }

        if (!dir) {
            dontDeselect = -1;
        }
        else {
            dontDeselect = (dir == 'up') ? startRow : endRow;
        }

        for (i = startRow; i <= endRow; i++) {
            if (selectedCount == (endRow - startRow + 1)) {
                if (i != dontDeselect) {
                    me.deselect(i, true);
                }
            } else {
                me.select(i, true);
            }

        }
    },

    /**
     * Adds the given records to the currently selected set
     * @param {Ext.data.Model/Array/Number} records The records to select
     * @param {Boolean} keepExisting If true, the existing selection will be added to (if not, the old selection is replaced)
     * @param {Boolean} suppressEvent If true, the 'select' event will not be fired
     */
    select: function(records, keepExisting, suppressEvent) {
        var me = this,
            record;

        if (me.getDisableSelection()) {
            return;
        }

        if (typeof records === "number") {
            records = [me.getStore().getAt(records)];
        }

        if (!records) {
            return;
        }

        if (me.getMode() == "SINGLE" && records) {
            record = records.length ? records[0] : records;
            me.doSingleSelect(record, suppressEvent);
        } else {
            me.doMultiSelect(records, keepExisting, suppressEvent);
        }
    },

    /**
     * Selects a single record
     * @private
     */
    doSingleSelect: function(record, suppressEvent) {
        var me = this,
            selected = me.selected;

        if (me.getDisableSelection()) {
            return;
        }

        // already selected.
        // should we also check beforeselect?
        if (me.isSelected(record)) {
            return;
        }

        if (selected.getCount() > 0) {
            me.deselect(me.getLastSelected(), suppressEvent);
        }

        selected.add(record);
        me.setLastSelected(record);
        me.onItemSelect(record, suppressEvent);
        me.setLastFocused(record);
        me.fireSelectionChange(!suppressEvent);
    },

    /**
     * Selects a set of multiple records
     * @private
     */
    doMultiSelect: function(records, keepExisting, suppressEvent) {
        if (records === null || this.getDisableSelection()) {
            return;
        }
        records = !Ext.isArray(records) ? [records] : records;

        var me = this,
            selected = me.selected,
            ln = records.length,
            change = false,
            i = 0,
            record;

        if (!keepExisting && selected.getCount() > 0) {
            change = true;
            me.deselect(me.getSelection(), true);
        }
        for (; i < ln; i++) {
            record = records[i];
            if (keepExisting && me.isSelected(record)) {
                continue;
            }
            change = true;
            me.setLastSelected(record);
            selected.add(record);
            if (!suppressEvent) {
                me.setLastFocused(record);
            }

            me.onItemSelect(record, suppressEvent);
        }
        this.fireSelectionChange(change && !suppressEvent);
    },

    /**
     * Deselects the given record(s). If many records are currently selected, it will only deselect those you pass in.
     * @param {Number/Array/Ext.data.Model} records The record(s) to deselect. Can also be a number to reference by index
     * @param {Boolean} suppressEvent If true the deselect event will not be fired
     */
    deselect: function(records, suppressEvent) {
        var me = this;

        if (me.getDisableSelection()) {
            return;
        }

        records = Ext.isArray(records) ? records : [records];

        var selected = me.selected,
            change   = false,
            i        = 0,
            store    = me.getStore(),
            ln       = records.length,
            record;

        for (; i < ln; i++) {
            record = records[i];

            if (typeof record === 'number') {
                record = store.getAt(record);
            }

            if (selected.remove(record)) {
                if (me.getLastSelected() == record) {
                    me.setLastSelected(selected.last());
                }
                change = true;
            }
            if (record) {
                me.onItemDeselect(record, suppressEvent);
            }
        }
        me.fireSelectionChange(change && !suppressEvent);
    },

    /**
     * @param {Ext.data.Record} record
     * Set a record as the last focused record. This does NOT mean
     * that the record has been selected.
     */
    updateLastFocused: function(newRecord, oldRecord) {
        this.onLastFocusChanged(oldRecord, newRecord);
    },

    fireSelectionChange: function(fireEvent) {
        var me = this;
        if (fireEvent) {
                me.fireEvent('selectionchange', me, me.getSelection());
        }
    },

    /**
     * Returns an array of the currently selected records.
     * @return {Array} An array of selected records
     */
    getSelection: function() {
        return this.selected.getRange();
    },

    /**
     * Returns <tt>true</tt> if the specified row is selected.
     * @param {Ext.data.Model/Number} record The record or index of the record to check
     * @return {Boolean}
     */
    isSelected: function(record) {
        record = Ext.isNumber(record) ? this.getStore().getAt(record) : record;
        return this.selected.indexOf(record) !== -1;
    },

    /**
     * Returns true if there is a selected record.
     * @return {Boolean}
     */
    hasSelection: function() {
        return this.selected.getCount() > 0;
    },

    /**
     * @private
     */
    refreshSelection: function() {
        var me = this,
            selections = me.getSelection();

        me.deselectAll(true);
        if (selections.length) {
            me.select(selections, false, true);
        }
    },

    // when a store is cleared remove all selections
    // (if there were any)
    onSelectionStoreClear: function() {
        var me = this,
            selected = me.selected;
        if (selected.getCount() > 0) {
            selected.clear();
            me.setLastSelected(null);
            me.setLastFocused(null);
            me.fireSelectionChange(true);
        }
    },

    // prune records from the SelectionModel if
    // they were selected at the time they were
    // removed.
    onSelectionStoreRemove: function(store, record) {
        var me = this,
            selected = me.selected;

        if (me.getDisableSelection()) {
            return;
        }

        if (selected.remove(record)) {
            if (me.getLastSelected() == record) {
                me.setLastSelected(null);
            }
            if (me.getLastFocused() == record) {
                me.setLastFocused(null);
            }
            me.fireSelectionChange(true);
        }
    },

    /**
     * Returns the number of selections.
     * @return {Number}
     */
    getSelectionCount: function() {
        return this.selected.getCount();
    },

    onSelectionStoreAdd: Ext.emptyFn,
    onSelectionStoreUpdate: Ext.emptyFn,
    onItemSelect: Ext.emptyFn,
    onItemDeselect: Ext.emptyFn,
    onLastFocusChanged: Ext.emptyFn,
    onEditorKey: Ext.emptyFn
}, function() {
    /**
     * Selects a record instance by record instance or index.
     * @member Ext.mixin.Selectable
     * @method doSelect
     * @param {Ext.data.Model/Number} records An array of records or an index
     * @param {Boolean} keepExisting
     * @param {Boolean} suppressEvent Set to false to not fire a select event
     * @deprecated 2.0.0 Please use {@link #select} instead.
     */

    /**
     * Deselects a record instance by record instance or index.
     * @member Ext.mixin.Selectable
     * @method doDeselect
     * @param {Ext.data.Model/Number} records An array of records or an index
     * @param {Boolean} suppressEvent Set to false to not fire a deselect event
     * @deprecated 2.0.0 Please use {@link #deselect} instead.
     */

    /**
     * Returns the selection mode currently used by this Selectable
     * @member Ext.mixin.Selectable
     * @method getSelectionMode
     * @return {String} The current mode
     * @deprecated 2.0.0 Please use {@link #getMode} instead.
     */

    /**
     * Returns the array of previously selected items
     * @member Ext.mixin.Selectable
     * @method getLastSelected
     * @return {Array} The previous selection
     * @deprecated 2.0.0
     */

    /**
     * Returns true if the Selectable is currently locked
     * @member Ext.mixin.Selectable
     * @method isLocked
     * @return {Boolean} True if currently locked
     * @deprecated 2.0.0 Please use {@link #getDisableSelection} instead.
     */

    /**
     * This was an internal function accidentally exposed in 1.x and now deprecated. Calling it has no effect
     * @member Ext.mixin.Selectable
     * @method setLastFocused
     * @deprecated 2.0.0
     */

    /**
     * Deselects any currently selected records and clears all stored selections
     * @member Ext.mixin.Selectable
     * @method clearSelections
     * @deprecated 2.0.0 Please use {@link #deselectAll} instead.
     */

    /**
     * Returns the number of selections.
     * @member Ext.mixin.Selectable
     * @method getCount
     * @return {Number}
     * @deprecated 2.0.0 Please use {@link #getSelectionCount} instead.
     */

    /**
     * @cfg {Boolean} locked
     * @deprecated 2.0.0 Please use {@link #disableSelection} instead.
     */

});

/**
 * A Traversable mixin.
 * @private
 */
Ext.define('Ext.mixin.Traversable', {
    extend: 'Ext.mixin.Mixin',

    mixinConfig: {
        id: 'traversable'
    },

    setParent: function(parent) {
        this.parent = parent;

        return this;
    },

    /**
     * @member Ext.Component
     * Returns `true` if this component has a parent.
     * @return {Boolean} `true` if this component has a parent.
     */
    hasParent: function() {
        return Boolean(this.parent);
    },

    /**
     * @member Ext.Component
     * Returns the parent of this component, if it has one.
     * @return {Ext.Component} The parent of this component.
     */
    getParent: function() {
        return this.parent;
    },

    getAncestors: function() {
        var ancestors = [],
            parent = this.getParent();

        while (parent) {
            ancestors.push(parent);
            parent = parent.getParent();
        }

        return ancestors;
    },

    getAncestorIds: function() {
        var ancestorIds = [],
            parent = this.getParent();

        while (parent) {
            ancestorIds.push(parent.getId());
            parent = parent.getParent();
        }

        return ancestorIds;
    }
});

/**
 * The DelayedTask class provides a convenient way to "buffer" the execution of a method,
 * performing setTimeout where a new timeout cancels the old timeout. When called, the
 * task will wait the specified time period before executing. If durng that time period,
 * the task is called again, the original call will be cancelled. This continues so that
 * the function is only called a single time for each iteration.
 *
 * This method is especially useful for things like detecting whether a user has finished
 * typing in a text field. An example would be performing validation on a keypress. You can
 * use this class to buffer the keypress events for a certain number of milliseconds, and
 * perform only if they stop for that amount of time.
 *
 * Using {@link Ext.util.DelayedTask} is very simple:
 *
 *     //create the delayed task instance with our callback
 *     var task = Ext.create('Ext.util.DelayedTask', function() {
 *         console.log('callback!');
 *     });
 *
 *     task.delay(1500); //the callback function will now be called after 1500ms
 *
 *     task.cancel(); //the callback function will never be called now, unless we call delay() again
 *
 * ## Example
 *
 *     @example
 *     //create a textfield where we can listen to text
 *     var field = Ext.create('Ext.field.Text', {
 *         xtype: 'textfield',
 *         label: 'Length: 0'
 *     });
 *
 *     //add the textfield into a fieldset
 *     Ext.Viewport.add({
 *         xtype: 'formpanel',
 *         items: [{
 *             xtype: 'fieldset',
 *             items: [field],
 *             instructions: 'Type into the field and watch the count go up after 500ms.'
 *         }]
 *     });
 *
 *     //create our delayed task with a function that returns the fields length as the fields label
 *     var task = Ext.create('Ext.util.DelayedTask', function() {
 *         field.setLabel('Length: ' + field.getValue().length);
 *     });
 *
 *     // Wait 500ms before calling our function. If the user presses another key
 *     // during that 500ms, it will be cancelled and we'll wait another 500ms.
 *     field.on('keyup', function() {
 *         task.delay(500);
 *     });
 *
 * @constructor
 * The parameters to this constructor serve as defaults and are not required.
 * @param {Function} fn The default function to call.
 * @param {Object} scope The default scope (The `this` reference) in which the function is called. If
 * not specified, `this` will refer to the browser window.
 * @param {Array} args The default Array of arguments.
 */
Ext.define('Ext.util.DelayedTask', {
    config: {
        interval: null,
        delay: null,
        fn: null,
        scope: null,
        args: null
    },

    constructor: function(fn, scope, args) {
        var config = {
            fn: fn,
            scope: scope,
            args: args
        };

        this.initConfig(config);
    },

    /**
     * Cancels any pending timeout and queues a new one.
     * @param {Number} delay The milliseconds to delay
     * @param {Function} newFn Overrides the original function passed when instantiated.
     * @param {Object} newScope Overrides the original `scope` passed when instantiated. Remember that if no scope
     * is specified, `this` will refer to the browser window.
     * @param {Array} newArgs Overrides the original `args` passed when instantiated.
     */
    delay: function(delay, newFn, newScope, newArgs) {
        var me = this;

        //cancel any existing queued functions
        me.cancel();
            
        //set all the new configurations
        me.setConfig({
            delay: delay,
            fn: newFn,
            scope: newScope,
            args: newArgs
        });

        //create the callback method for this delayed task
        var call = function() {
            me.getFn().apply(me.getScope(), me.getArgs() || []);
            me.cancel();
        };

        me.setInterval(setInterval(call, me.getDelay()));
    },

    /**
     * Cancel the last queued timeout
     */
    cancel: function() {
        this.setInterval(null);
    },

    /**
     * @private
     * Clears the old interval
     */
    updateInterval: function(newInterval, oldInterval) {
        if (oldInterval) {
            clearInterval(oldInterval);
        }
    },

    /**
     * @private
     * Changes the value into an array if it isn't one.
     */
    applyArgs: function(config) {
        if (!Ext.isArray(config)) {
            config = [config];
        }

        return config;
    }
});

/**
 * Represents a filter that can be applied to a {@link Ext.util.MixedCollection MixedCollection}. Can either simply
 * filter on a property/value pair or pass in a filter function with custom logic. Filters are always used in the
 * context of MixedCollections, though {@link Ext.data.Store Store}s frequently create them when filtering and searching
 * on their records. Example usage:
 *
 *     // Set up a fictional MixedCollection containing a few people to filter on
 *     var allNames = new Ext.util.MixedCollection();
 *     allNames.addAll([
 *         { id: 1, name: 'Ed',    age: 25 },
 *         { id: 2, name: 'Jamie', age: 37 },
 *         { id: 3, name: 'Abe',   age: 32 },
 *         { id: 4, name: 'Aaron', age: 26 },
 *         { id: 5, name: 'David', age: 32 }
 *     ]);
 *
 *     var ageFilter = new Ext.util.Filter({
 *         property: 'age',
 *         value   : 32
 *     });
 *
 *     var longNameFilter = new Ext.util.Filter({
 *         filterFn: function(item) {
 *             return item.name.length > 4;
 *         }
 *     });
 *
 *     // a new MixedCollection with the 3 names longer than 4 characters
 *     var longNames = allNames.filter(longNameFilter);
 *
 *     // a new MixedCollection with the 2 people of age 24:
 *     var youngFolk = allNames.filter(ageFilter);
 */
Ext.define('Ext.util.Filter', {
    isFilter: true,

    config: {
        /**
         * @cfg {String} [property=null]
         * The property to filter on. Required unless a `filter` is passed
         */
        property: null,

        /**
         * @cfg {RegExp/Mixed} [value=null]
         * The value you want to match against. Can be a regular expression which will be used as matcher or any other
         * value.
         */
        value: null,

        /**
         * @cfg {Function} filterFn
         * A custom filter function which is passed each item in the {@link Ext.util.MixedCollection} in turn. Should
         * return true to accept each item or false to reject it
         */
        filterFn: Ext.emptyFn,

        /**
         * @cfg {Boolean} [anyMatch=false]
         * True to allow any match - no regex start/end line anchors will be added.
         */
        anyMatch: false,

        /**
         * @cfg {Boolean} [exactMatch=false]
         * True to force exact match (^ and $ characters added to the regex). Ignored if anyMatch is true.
         */
        exactMatch: false,

        /**
         * @cfg {Boolean} [caseSensitive=false]
         * True to make the regex case sensitive (adds 'i' switch to regex).
         */
        caseSensitive: false,

        /**
         * @cfg {String} [root=null]
         * Optional root property. This is mostly useful when filtering a Store, in which case we set the root to 'data'
         * to make the filter pull the {@link #property} out of the data object of each item
         */
        root: null,

        /**
         * @cfg {String} id
         * An optional id this filter can be keyed by in Collections. If no id is specified it will generate an id by
         * first trying a combination of property-value, and if none if these were specified (like when having a
         * filterFn) it will generate a random id.
         */
        id: undefined,

        /**
         * @cfg {Object} [scope=null]
         * The scope in which to run the filterFn
         */
        scope: null
    },

    applyId: function(id) {
        if (!id) {
            if (this.getProperty()) {
                id = this.getProperty() + '-' + String(this.getValue());
            }
            if (!id) {
                id = Ext.id(null, 'ext-filter-');
            }
        }

        return id;
    },

    /**
     * Creates new Filter.
     * @param {Object} config Config object
     */
    constructor: function(config) {
        this.initConfig(config);
    },

    applyFilterFn: function(filterFn) {
        if (filterFn === Ext.emptyFn) {
            filterFn = this.getInitialConfig('filter');
            if (filterFn) {
                return filterFn;
            }

            var value = this.getValue();
            if (!this.getProperty() && !value && value !== 0) {
                Ext.Logger.error('A Filter requires either a property and value, or a filterFn to be set');
                return Ext.emptyFn;
            }
            else {
                return this.createFilterFn();
            }
        }
        return filterFn;
    },

    /**
     * @private
     * Creates a filter function for the configured property/value/anyMatch/caseSensitive options for this Filter
     */
    createFilterFn: function() {
        var me       = this,
            matcher  = me.createValueMatcher();

        return function(item) {
            var root     = me.getRoot(),
                property = me.getProperty();

            if (root) {
                item = item[root];
            }

            return matcher.test(item[property]);
        };
    },

    /**
     * @private
     * Returns a regular expression based on the given value and matching options
     */
    createValueMatcher: function() {
        var me            = this,
            value         = me.getValue(),
            anyMatch      = me.getAnyMatch(),
            exactMatch    = me.getExactMatch(),
            caseSensitive = me.getCaseSensitive(),
            escapeRe      = Ext.String.escapeRegex;

        if (value === null || value === undefined || !value.exec) { // not a regex
            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;
    }
});

/**
 * Represents a 2D point with x and y properties, useful for comparison and instantiation
 * from an event:
 *
 *     var point = Ext.util.Point.fromEvent(e);
 *
 */
Ext.define('Ext.util.Point', {

    radianToDegreeConstant: 180 / Math.PI,

    statics: {
        /**
         * Returns a new instance of Ext.util.Point based on the pageX / pageY values of the given event
         * @static
         * @param {Event} e The event
         * @return Ext.util.Point
         */
        fromEvent: function(e) {
            var changedTouches = e.changedTouches,
                touch = (changedTouches && changedTouches.length > 0) ? changedTouches[0] : e;

            return this.fromTouch(touch);
        },

        /**
         * Returns a new instance of Ext.util.Point based on the pageX / pageY values of the given touch
         * @static
         * @param {Event} touch
         * @return Ext.util.Point
         */
        fromTouch: function(touch) {
            return new this(touch.pageX, touch.pageY);
        },

        /**
         * Returns a new point from an object that has 'x' and 'y' properties, if that object is not an instance
         * of Ext.util.Point. Otherwise, returns the given point itself.
         * @param object
         */
        from: function(object) {
            if (!object) {
                return new this(0, 0);
            }

            if (!(object instanceof this)) {
                return new this(object.x, object.y);
            }

            return object;
        }
    },

    /**
     * Creates point on 2D plane.
     * @param {Number} [x=0] X coordinate.
     * @param {Number} [y=0] Y coordinate.
     */
    constructor: function(x, y) {
        if (typeof x == 'undefined') {
            x = 0;
        }

        if (typeof y == 'undefined') {
            y = 0;
        }

        this.x = x;
        this.y = y;

        return this;
    },

    /**
     * Copy a new instance of this point
     * @return {Ext.util.Point} the new point
     */
    clone: function() {
        return new this.self(this.x, this.y);
    },

    /**
     * Clones this Point.
     * @deprecated 2.0.0 Please use {@link #clone} instead
     */
    copy: function() {
        return this.clone.apply(this, arguments);
    },

    /**
     * Copy the x and y values of another point / object to this point itself
     * @param {Ext.util.Point/Object} point
     * @return {Ext.util.Point} this This point
     */
    copyFrom: function(point) {
        this.x = point.x;
        this.y = point.y;

        return this;
    },

    /**
     * Returns a human-eye-friendly string that represents this point,
     * useful for debugging.
     * @return {String} For example `Point[12,8]`
     */
    toString: function() {
        return "Point[" + this.x + "," + this.y + "]";
    },

    /**
     * Compare this point and another point
     * @param {Ext.util.Point/Object} The point to compare with, either an instance
     * of Ext.util.Point or an object with x and y properties
     * @return {Boolean} Returns whether they are equivalent
     */
    equals: function(point) {
        return (this.x === point.x && this.y === point.y);
    },

    /**
     * Whether the given point is not away from this point within the given threshold amount
     * @param {Ext.util.Point/Object} The point to check with, either an instance
     * of Ext.util.Point or an object with x and y properties
     * @param {Object/Number} threshold Can be either an object with x and y properties or a number
     * @return {Boolean}
     */
    isCloseTo: function(point, threshold) {
        if (typeof threshold == 'number') {
            threshold = {x: threshold};
            threshold.y = threshold.x;
        }

        var x = point.x,
            y = point.y,
            thresholdX = threshold.x,
            thresholdY = threshold.y;

        return (this.x <= x + thresholdX && this.x >= x - thresholdX &&
                this.y <= y + thresholdY && this.y >= y - thresholdY);
    },

    /**
     * Returns true if this point is close to another one.
     * @deprecated 2.0.0 Please use {@link #isCloseTo} instead
     */
    isWithin: function() {
        return this.isCloseTo.apply(this, arguments);
    },

    /**
     * Translate this point by the given amounts
     * @param {Number} x Amount to translate in the x-axis
     * @param {Number} y Amount to translate in the y-axis
     * @return {Boolean}
     */
    translate: function(x, y) {
        this.x += x;
        this.y += y;

        return this;
    },

    /**
     * Compare this point with another point when the x and y values of both points are rounded. E.g:
     * [100.3,199.8] will equals to [100, 200]
     * @param {Ext.util.Point/Object} The point to compare with, either an instance
     * of Ext.util.Point or an object with x and y properties
     * @return {Boolean}
     */
    roundedEquals: function(point) {
        return (Math.round(this.x) === Math.round(point.x) &&
                Math.round(this.y) === Math.round(point.y));
    },

    getDistanceTo: function(point) {
        var deltaX = this.x - point.x,
            deltaY = this.y - point.y;

        return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
    },

    getAngleTo: function(point) {
        var deltaX = this.x - point.x,
            deltaY = this.y - point.y;

        return Math.atan2(deltaY, deltaX) * this.radianToDegreeConstant;
    }
});

/**
 * Represents a rectangular region and provides a number of utility methods
 * to compare regions.
 */
Ext.define('Ext.util.Region', {

    statics: {
        /**
         * @static
         * @param {Mixed} el A string, DomElement or Ext.Element representing an element
         * on the page.
         * @returns {Ext.util.Region} region
         * Retrieves an Ext.util.Region for a particular element.
         */
        getRegion: function(el) {
            return Ext.fly(el).getPageBox(true);
        },

        /**
         * @static
         * @param {Object} o An object with top, right, bottom, left properties
         * @return {Ext.util.Region} region The region constructed based on the passed object
         */
        from: function(o) {
            return new this(o.top, o.right, o.bottom, o.left);
        }
    },

    /**
     * Creates new Region.
     * @param {Number} top Top
     * @param {Number} right Right
     * @param {Number} bottom Bottom
     * @param {Number} left Left
     */
    constructor: function(t, r, b, l) {
        var me = this;
        me.top = t;
        me[1] = t;
        me.right = r;
        me.bottom = b;
        me.left = l;
        me[0] = l;
    },

    /**
     * Checks if this region completely contains the region that is passed in.
     * @param {Ext.util.Region} region
     */
    contains: function(region) {
        var me = this;
        return (region.left >= me.left &&
                region.right <= me.right &&
                region.top >= me.top &&
                region.bottom <= me.bottom);

    },

    /**
     * Checks if this region intersects the region passed in.
     * @param {Ext.util.Region} region
     * @return {Ext.util.Region/Boolean} Returns the intersected region or false if there is no intersection.
     */
    intersect: function(region) {
        var me = this,
            t = Math.max(me.top, region.top),
            r = Math.min(me.right, region.right),
            b = Math.min(me.bottom, region.bottom),
            l = Math.max(me.left, region.left);

        if (b > t && r > l) {
            return new Ext.util.Region(t, r, b, l);
        }
        else {
            return false;
        }
    },

    /**
     * Returns the smallest region that contains the current AND targetRegion.
     * @param {Ext.util.Region} region
     */
    union: function(region) {
        var me = this,
            t = Math.min(me.top, region.top),
            r = Math.max(me.right, region.right),
            b = Math.max(me.bottom, region.bottom),
            l = Math.min(me.left, region.left);

        return new Ext.util.Region(t, r, b, l);
    },

    /**
     * Modifies the current region to be constrained to the targetRegion.
     * @param {Ext.util.Region} targetRegion
     */
    constrainTo: function(r) {
        var me = this,
            constrain = Ext.util.Numbers.constrain;
        me.top = constrain(me.top, r.top, r.bottom);
        me.bottom = constrain(me.bottom, r.top, r.bottom);
        me.left = constrain(me.left, r.left, r.right);
        me.right = constrain(me.right, r.left, r.right);
        return me;
    },

    /**
     * Modifies the current region to be adjusted by offsets.
     * @param {Number} top top offset
     * @param {Number} right right offset
     * @param {Number} bottom bottom offset
     * @param {Number} left left offset
     */
    adjust: function(t, r, b, l) {
        var me = this;
        me.top += t;
        me.left += l;
        me.right += r;
        me.bottom += b;
        return me;
    },

    /**
     * Get the offset amount of a point outside the region
     * @param {String} axis optional
     * @param {Ext.util.Point} p the point
     * @return {Ext.util.Region}
     */
    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;
        }

    },

    /**
     * Get the offset amount on the x-axis
     * @param {Number} p the offset
     * @return {Number}
     */
    getOutOfBoundOffsetX: function(p) {
        if (p <= this.left) {
            return this.left - p;
        } else if (p >= this.right) {
            return this.right - p;
        }

        return 0;
    },

    /**
     * Get the offset amount on the y-axis
     * @param {Number} p the offset
     * @return {Number}
     */
    getOutOfBoundOffsetY: function(p) {
        if (p <= this.top) {
            return this.top - p;
        } else if (p >= this.bottom) {
            return this.bottom - p;
        }

        return 0;
    },

    /**
     * Check whether the point / offset is out of bound
     * @param {String} axis optional
     * @param {Ext.util.Point/Number} p the point / offset
     * @return {Boolean}
     */
    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));
        }
    },

    /**
     * Check whether the offset is out of bound in the x-axis
     * @param {Number} p the offset
     * @return {Boolean}
     */
    isOutOfBoundX: function(p) {
        return (p < this.left || p > this.right);
    },

    /**
     * Check whether the offset is out of bound in the y-axis
     * @param {Number} p the offset
     * @return {Boolean}
     */
    isOutOfBoundY: function(p) {
        return (p < this.top || p > this.bottom);
    },

    /*
     * Restrict a point within the region by a certain factor.
     * @param {String} axis Optional
     * @param {Ext.util.Point/Ext.util.Offset/Object} p
     * @param {Number} factor
     * @return {Ext.util.Point/Ext.util.Offset/Object/Number}
     */
    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);
            }
        }
    },

    /*
     * Restrict an offset within the region by a certain factor, on the x-axis
     * @param {Number} p
     * @param {Number} factor The factor, optional, defaults to 1
     * @return
     */
    restrictX: function(p, factor) {
        if (!factor) {
            factor = 1;
        }

        if (p <= this.left) {
            p -= (p - this.left) * factor;
        }
        else if (p >= this.right) {
            p -= (p - this.right) * factor;
        }
        return p;
    },

    /*
     * Restrict an offset within the region by a certain factor, on the y-axis
     * @param {Number} p
     * @param {Number} factor The factor, optional, defaults to 1
     */
    restrictY: function(p, factor) {
        if (!factor) {
            factor = 1;
        }

        if (p <= this.top) {
            p -= (p - this.top) * factor;
        }
        else if (p >= this.bottom) {
            p -= (p - this.bottom) * factor;
        }
        return p;
    },

    /*
     * Get the width / height of this region
     * @return {Object} an object with width and height properties
     */
    getSize: function() {
        return {
            width: this.right - this.left,
            height: this.bottom - this.top
        };
    },

    /**
     * Copy a new instance
     * @return {Ext.util.Region}
     */
    copy: function() {
        return new Ext.util.Region(this.top, this.right, this.bottom, this.left);
    },

    /**
     * Dump this to an eye-friendly string, great for debugging
     * @return {String} For example `Region[0,1,3,2]`
     */
    toString: function() {
        return "Region[" + this.top + "," + this.right + "," + this.bottom + "," + this.left + "]";
    },


    /**
     * Translate this region by the given offset amount
     * @param {Object} offset
     * @return {Ext.util.Region} this This Region
     */
    translateBy: function(offset) {
        this.left += offset.x;
        this.right += offset.x;
        this.top += offset.y;
        this.bottom += offset.y;

        return this;
    },

    /**
     * Round all the properties of this region
     * @return {Ext.util.Region} this This Region
     */
    round: function() {
        this.top = Math.round(this.top);
        this.right = Math.round(this.right);
        this.bottom = Math.round(this.bottom);
        this.left = Math.round(this.left);

        return this;
    },

    /**
     * Check whether this region is equivalent to the given region
     * @param {Ext.util.Region} region The region to compare with
     * @return {Boolean}
     */
    equals: function(region) {
        return (this.top == region.top && this.right == region.right && this.bottom == region.bottom && this.left == region.left)
    }
});

/**
 * Represents a single sorter that can be used as part of the sorters configuration in Ext.mixin.Sortable.
 */
Ext.define('Ext.util.Sorter', {
    isSorter: true,

    config: {
        /**
         * @cfg {String} property The property to sort by. Required unless `sorterFn` is provided
         */
        property: null,

        /**
         * @cfg {Function} sorterFn A specific sorter function to execute. Can be passed instead of {@link #property}
         */
        sorterFn: null,

        /**
         * @cfg {String} root Optional root property. This is mostly useful when sorting a Store, in which case we set the
         * root to 'data' to make the filter pull the {@link #property} out of the data object of each item
         */
        root: null,

        /**
         * @cfg {Function} transform A function that will be run on each value before
         * it is compared in the sorter. The function will receive a single argument,
         * the value.
         */
        transform: null,

        /**
         * @cfg {String} direction The direction to sort by.
         */
        direction: "ASC",

        /**
         * @cfg {Mixed} id An optional id this sorter can be keyed by in Collections. If
         * no id is specified it will use the property name used in this Sorter. If no
         * property is specified, e.g. when adding a custom sorter function we will generate
         * a random id.
         */
        id: undefined
    },

    constructor: function(config) {
        this.initConfig(config);
    },

    applySorterFn: function(sorterFn) {
        if (!sorterFn && !this.getProperty()) {
            Ext.Logger.error("A Sorter requires either a property or a sorterFn.");
        }
        return sorterFn;
    },

    applyProperty: function(property) {
        if (!property && !this.getSorterFn()) {
            Ext.Logger.error("A Sorter requires either a property or a sorterFn.");
        }
        return property;
    },

    applyId: function(id) {
        if (!id) {
            id = this.getProperty();
            if (!id) {
                id = Ext.id(null, 'ext-sorter-');
            }
        }

        return id;
    },

    /**
     * @private
     * Creates and returns a function which sorts an array by the given property and direction
     * @return {Function} A function which sorts by the property/direction combination provided
     */
    createSortFunction: function(sorterFn) {
        var me        = this,
            modifier  = me.getDirection().toUpperCase() == "DESC" ? -1 : 1;

        //create a comparison function. Takes 2 objects, returns 1 if object 1 is greater,
        //-1 if object 2 is greater or 0 if they are equal
        return function(o1, o2) {
            return modifier * sorterFn.call(me, o1, o2);
        };
    },

    /**
     * @private
     * Basic default sorter function that just compares the defined property of each object
     */
    defaultSortFn: function(item1, item2) {
        var me = this,
            transform = me._transform,
            root = me._root,
            value1, value2,
            property = me._property;

        if (root !== null) {
            item1 = item1[root];
            item2 = item2[root];
        }

        value1 = item1[property];
        value2 = item2[property];

        if (transform) {
            value1 = transform(value1);
            value2 = transform(value2);
        }

        return value1 > value2 ? 1 : (value1 < value2 ? -1 : 0);
    },

    updateDirection: function() {
        this.updateSortFn();
    },

    updateSortFn: function() {
        this.sort = this.createSortFunction(this.getSorterFn() || this.defaultSortFn);
    },

    /**
     * Toggles the direction of this Sorter. Note that when you call this function,
     * the Collection this Sorter is part of does not get refreshed automatically.
     */
    toggle: function() {
        this.setDirection(Ext.String.toggle(this.getDirection(), "ASC", "DESC"));
    }
});

/**
 * <p>General purpose inflector class that {@link #pluralize pluralizes}, {@link #singularize singularizes} and 
 * {@link #ordinalize ordinalizes} words. Sample usage:</p>
 * 
<pre><code>
//turning singular words into plurals
Ext.util.Inflector.pluralize('word'); //'words'
Ext.util.Inflector.pluralize('person'); //'people'
Ext.util.Inflector.pluralize('sheep'); //'sheep'

//turning plurals into singulars
Ext.util.Inflector.singularize('words'); //'word'
Ext.util.Inflector.singularize('people'); //'person'
Ext.util.Inflector.singularize('sheep'); //'sheep'

//ordinalizing numbers
Ext.util.Inflector.ordinalize(11); //"11th"
Ext.util.Inflector.ordinalize(21); //"21th"
Ext.util.Inflector.ordinalize(1043); //"1043rd"
</code></pre>
 * 
 * <p><u>Customization</u></p>
 * 
 * <p>The Inflector comes with a default set of US English pluralization rules. These can be augmented with additional
 * rules if the default rules do not meet your application's requirements, or swapped out entirely for other languages.
 * Here is how we might add a rule that pluralizes "ox" to "oxen":</p>
 * 
<pre><code>
Ext.util.Inflector.plural(/^(ox)$/i, "$1en");
</code></pre>
 * 
 * <p>Each rule consists of two items - a regular expression that matches one or more rules, and a replacement string.
 * In this case, the regular expression will only match the string "ox", and will replace that match with "oxen". 
 * Here's how we could add the inverse rule:</p>
 * 
<pre><code>
Ext.util.Inflector.singular(/^(ox)en$/i, "$1");
</code></pre>
 * 
 * <p>Note that the ox/oxen rules are present by default.</p>
 */
Ext.define('Ext.util.Inflector', {

    /* Begin Definitions */

    singleton: true,

    /* End Definitions */

    /**
     * @private
     * The registered plural tuples. Each item in the array should contain two items - the first must be a regular
     * expression that matchers the singular form of a word, the second must be a String that replaces the matched
     * part of the regular expression. This is managed by the {@link #plural} method.
     * @property plurals
     * @type Array
     */
    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"      ]
    ],
    
    /**
     * @private
     * The set of registered singular matchers. Each item in the array should contain two items - the first must be a 
     * regular expression that matches the plural form of a word, the second must be a String that replaces the 
     * matched part of the regular expression. This is managed by the {@link #singular} method.
     * @property singulars
     * @type Array
     */
    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),                                                            ""       ]
    ],
    
    /**
     * @private
     * The registered uncountable words
     * @property uncountable
     * @type Array
     */
     uncountable: [
        "sheep",
        "fish",
        "series",
        "species",
        "money",
        "rice",
        "information",
        "equipment",
        "grass",
        "mud",
        "offspring",
        "deer",
        "means"
    ],
    
    /**
     * Adds a new singularization rule to the Inflector. See the intro docs for more information
     * @param {RegExp} matcher The matcher regex
     * @param {String} replacer The replacement string, which can reference matches from the matcher argument
     */
    singular: function(matcher, replacer) {
        this.singulars.unshift([matcher, replacer]);
    },
    
    /**
     * Adds a new pluralization rule to the Inflector. See the intro docs for more information
     * @param {RegExp} matcher The matcher regex
     * @param {String} replacer The replacement string, which can reference matches from the matcher argument
     */
    plural: function(matcher, replacer) {
        this.plurals.unshift([matcher, replacer]);
    },
    
    /**
     * Removes all registered singularization rules
     */
    clearSingulars: function() {
        this.singulars = [];
    },
    
    /**
     * Removes all registered pluralization rules
     */
    clearPlurals: function() {
        this.plurals = [];
    },
    
    /**
     * Returns true if the given word is transnumeral (the word is its own singular and plural form - e.g. sheep, fish)
     * @param {String} word The word to test
     * @return {Boolean} True if the word is transnumeral
     */
    isTransnumeral: function(word) {
        return Ext.Array.indexOf(this.uncountable, word) != -1;
    },

    /**
     * Returns the pluralized form of a word (e.g. Ext.util.Inflector.pluralize('word') returns 'words')
     * @param {String} word The word to pluralize
     * @return {String} The pluralized form of the word
     */
    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;
    },
    
    /**
     * Returns the singularized form of a word (e.g. Ext.util.Inflector.singularize('words') returns 'word')
     * @param {String} word The word to singularize
     * @return {String} The singularized form of the 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;
    },
    
    /**
     * Returns the correct {@link Ext.data.Model Model} name for a given string. Mostly used internally by the data 
     * package
     * @param {String} word The word to classify
     * @return {String} The classified version of the word
     */
    classify: function(word) {
        return Ext.String.capitalize(this.singularize(word));
    },
    
    /**
     * Ordinalizes a given number by adding a prefix such as 'st', 'nd', 'rd' or 'th' based on the last digit of the 
     * number. 21 -> 21st, 22 -> 22nd, 23 -> 23rd, 24 -> 24th etc
     * @param {Number} number The number to ordinalize
     * @return {String} The ordinalized number
     */
    ordinalize: function(number) {
        var parsed = parseInt(number, 10),
            mod10  = parsed % 10,
            mod100 = parsed % 100;
        
        //11 through 13 are a special case
        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() {
    //aside from the rules above, there are a number of words that have irregular pluralization so we add them here
    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.Anim is used to excute simple animations defined in {@link Ext.anims}. The {@link #run} method can take any of the
 * properties defined below.
 *
 *     Ext.Anim.run(this, 'fade', {
 *         out: false,
 *         autoClear: true
 *     });
 *
 * When using {@link Ext.Anim#run}, ensure you require {@link Ext.Anim} in your application. Either do this using {@link Ext#require}:
 *
 *     Ext.requires('Ext.Anim');
 *
 * when using {@link Ext#setup}:
 *
 *     Ext.setup({
 *         requires: ['Ext.Anim'],
 *         onReady: function() {
 *             //do something
 *         }
 *     });
 *
 * or when using {@link Ext#application}:
 *
 *     Ext.application({
 *         requires: ['Ext.Anim'],
 *         launch: function() {
 *             //do something
 *         }
 *     });
 *
 * @singleton
 */
Ext.Anim = Ext.extend(Object, {
    isAnim: true,

    /**
     * @cfg {Boolean} disableAnimations
     * True to disable animations.
     */
    disableAnimations: false,

    defaultConfig: {
        /**
         * @cfg {Object} from
         * An object of CSS values which the animation begins with. If you define a CSS property here, you must also
         * define it in the {@link #to} config.
         */
        from: {},

        /**
         * @cfg {Object} to
         * An object of CSS values which the animation ends with. If you define a CSS property here, you must also
         * define it in the {@link #from} config.
         */
        to: {},

        /**
         * @cfg {Number} duration
         * Time in milliseconds for the animation to last.
         */
        duration: 250,

        /**
         * @cfg {Number} delay Time to delay before starting the animation.
         */
        delay: 0,

        /**
         * @cfg {String} easing
         * Valid values are 'ease', 'linear', ease-in', 'ease-out', 'ease-in-out' or a cubic-bezier curve as defined by CSS.
         */
        easing: 'ease-in-out',

        /**
         * @cfg {Boolean} autoClear
         * True to remove all custom CSS defined in the {@link #to} config when the animation is over.
         */
        autoClear: true,

        /**
         * @cfg {Boolean} out
         * True if you want the animation to slide out of the screen.
         */
        out: true,

        /**
         * @cfg {String} direction
         * Valid values are 'left', 'right', 'up', 'down' and null.
         */
        direction: null,

        /**
         * @cfg {Boolean} reverse
         * True to reverse the animation direction. For example, if the animation direction was set to 'left', it would
         * then use 'right'.
         */
        reverse: false
    },

    /**
     * @cfg {Function} before
     * Code to execute before starting the animation.
     */

    /**
     * @cfg {Function} after
     * Code to execute after the animation ends.
     */

    /**
     * @cfg {Object} scope
     * Scope to run the {@link #before} function in.
     */

    opposites: {
        'left': 'right',
        'right': 'left',
        'up': 'down',
        'down': 'up'
    },

    constructor: function(config) {
        config = Ext.apply({}, config || {}, this.defaultConfig);
        this.config = config;

        Ext.Anim.superclass.constructor.call(this);

        this.running = [];
    },

    initConfig: function(el, runConfig) {
        var me = this,
            config = Ext.apply({}, runConfig || {}, me.config);

        config.el = el = Ext.get(el);

        if (config.reverse && me.opposites[config.direction]) {
            config.direction = me.opposites[config.direction];
        }

        if (me.config.before) {
            me.config.before.call(config, el, config);
        }

        if (runConfig.before) {
            runConfig.before.call(config.scope || config, el, config);
        }

        return config;
    },

    run: function(el, config) {
        el = Ext.get(el);
        config = config || {};


        var me = this,
            style = el.dom.style,
            property,
            after = config.after;

        if (me.running[el.id]) {
            me.onTransitionEnd(null, el, {
                config: config,
                after: after
            });
        }

        config = this.initConfig(el, config);

        if (this.disableAnimations) {
            for (property in config.to) {
                if (!config.to.hasOwnProperty(property)) {
                    continue;
                }
                style[property] = config.to[property];
            }
            this.onTransitionEnd(null, el, {
                config: config,
                after: after
            });
            return me;
        }

        el.un('transitionend', me.onTransitionEnd, me);

        style.webkitTransitionDuration = '0ms';
        for (property in config.from) {
            if (!config.from.hasOwnProperty(property)) {
                continue;
            }
            style[property] = config.from[property];
        }

        setTimeout(function() {
            // If this element has been destroyed since the timeout started, do nothing
            if (!el.dom) {
                return;
            }

            // If this is a 3d animation we have to set the perspective on the parent
            if (config.is3d === true) {
                el.parent().setStyle({
                    // See https://sencha.jira.com/browse/TOUCH-1498
                    '-webkit-perspective': '1200',
                    '-webkit-transform-style': 'preserve-3d'
                });
            }

            style.webkitTransitionDuration = config.duration + 'ms';
            style.webkitTransitionProperty = 'all';
            style.webkitTransitionTimingFunction = config.easing;

            // Bind our listener that fires after the animation ends
            el.on('transitionend', me.onTransitionEnd, me, {
                single: true,
                config: config,
                after: after
            });

            for (property in config.to) {
                if (!config.to.hasOwnProperty(property)) {
                    continue;
                }
                style[property] = config.to[property];
            }
        }, config.delay || 5);

        me.running[el.id] = config;
        return me;
    },

    onTransitionEnd: function(ev, el, o) {
        el = Ext.get(el);

        if (this.running[el.id] === undefined) {
            return;
        }

        var style = el.dom.style,
            config = o.config,
            me = this,
            property;

        if (config.autoClear) {
            for (property in config.to) {
                if (!config.to.hasOwnProperty(property) || config[property] === false) {
                    continue;
                }
                style[property] = '';
            }
        }

        style.webkitTransitionDuration = null;
        style.webkitTransitionProperty = null;
        style.webkitTransitionTimingFunction = null;

        if (config.is3d) {
            el.parent().setStyle({
                '-webkit-perspective': '',
                '-webkit-transform-style': ''
            });
        }

        if (me.config.after) {
            me.config.after.call(config, el, config);
        }

        if (o.after) {
            o.after.call(config.scope || me, el, config);
        }

        delete me.running[el.id];
    }
});

Ext.Anim.seed = 1000;

/**
 * Used to run an animation on a specific element. Use the config argument to customize the animation
 * @param {Ext.Element/HTMLElement} el The element to animate
 * @param {String} anim The animation type, defined in {@link Ext.anims}
 * @param {Object} config The config object for the animation
 * @method run
 */
Ext.Anim.run = function(el, anim, config) {
    if (el.isComponent) {
        el = el.element;
    }

    config = config || {};

    if (anim.isAnim) {
        anim.run(el, config);
    }
    else {
        if (Ext.isObject(anim)) {
            if (config.before && anim.before) {
                config.before = Ext.createInterceptor(config.before, anim.before, anim.scope);
            }
            if (config.after && anim.after) {
                config.after = Ext.createInterceptor(config.after, anim.after, anim.scope);
            }
            config = Ext.apply({}, config, anim);
            anim = anim.type;
        }

        if (!Ext.anims[anim]) {
            throw anim + ' is not a valid animation type.';
        }
        else {
            // add el check to make sure dom exists.
            if (el && el.dom) {
                Ext.anims[anim].run(el, config);
            }
        }
    }
};

/**
 * @class Ext.anims
 * <p>Defines different types of animations. <strong>flip, cube, wipe</strong> animations do not work on Android.</p>
 * <p>Please refer to {@link Ext.Anim} on how to use animations.</p>
 * @singleton
 */
Ext.anims = {
    /**
     * Fade Animation
     */
    fade: new Ext.Anim({
        type: 'fade',
        before: function(el) {
            var fromOpacity = 1,
                toOpacity = 1,
                curZ = el.getStyle('z-index') == 'auto' ? 0 : el.getStyle('z-index'),
                zIndex = curZ;

            if (this.out) {
                toOpacity = 0;
            } else {
                zIndex = Math.abs(curZ) + 1;
                fromOpacity = 0;
            }

            this.from = {
                'opacity': fromOpacity,
                'z-index': zIndex
            };
            this.to = {
                'opacity': toOpacity,
                'z-index': zIndex
            };
        }
    }),

    /**
     * Slide Animation
     */
    slide: new Ext.Anim({
        direction: 'left',
        cover: false,
        reveal: false,
        opacity: false,
        'z-index': false,

        before: function(el) {
            var currentZIndex = el.getStyle('z-index') == 'auto' ? 0 : el.getStyle('z-index'),
                currentOpacity = el.getStyle('opacity'),
                zIndex = currentZIndex + 1,
                out = this.out,
                direction = this.direction,
                toX = 0,
                toY = 0,
                fromX = 0,
                fromY = 0,
                elH = el.getHeight(),
                elW = el.getWidth();

            if (direction == 'left' || direction == 'right') {
                if (out) {
                    toX = -elW;
                }
                else {
                    fromX = elW;
                }
            }
            else if (direction == 'up' || direction == 'down') {
                if (out) {
                    toY = -elH;
                }
                else {
                    fromY = elH;
                }
            }

            if (direction == 'right' || direction == 'down') {
                toY *= -1;
                toX *= -1;
                fromY *= -1;
                fromX *= -1;
            }

            if (this.cover && out) {
                toX = 0;
                toY = 0;
                zIndex = currentZIndex;
            }
            else if (this.reveal && !out) {
                fromX = 0;
                fromY = 0;
                zIndex = currentZIndex;
            }

            this.from = {
                '-webkit-transform': 'translate3d(' + fromX + 'px, ' + fromY + 'px, 0)',
                'z-index': zIndex,
                'opacity': currentOpacity - 0.01
            };
            this.to = {
                '-webkit-transform': 'translate3d(' + toX + 'px, ' + toY + 'px, 0)',
                'z-index': zIndex,
                'opacity': currentOpacity
            };
        }
    }),

    /**
     * Pop Animation
     */
    pop: new Ext.Anim({
        scaleOnExit: true,
        before: function(el) {
            var fromScale = 1,
                toScale = 1,
                fromOpacity = 1,
                toOpacity = 1,
                curZ = el.getStyle('z-index') == 'auto' ? 0 : el.getStyle('z-index'),
                fromZ = curZ,
                toZ = curZ;

            if (!this.out) {
                fromScale = 0.01;
                fromZ = curZ + 1;
                toZ = curZ + 1;
                fromOpacity = 0;
            }
            else {
                if (this.scaleOnExit) {
                    toScale = 0.01;
                    toOpacity = 0;
                } else {
                    toOpacity = 0.8;
                }
            }

            this.from = {
                '-webkit-transform': 'scale(' + fromScale + ')',
                '-webkit-transform-origin': '50% 50%',
                'opacity': fromOpacity,
                'z-index': fromZ
            };

            this.to = {
                '-webkit-transform': 'scale(' + toScale + ')',
                '-webkit-transform-origin': '50% 50%',
                'opacity': toOpacity,
                'z-index': toZ
            };
        }
    }),

    /**
     * Flip Animation
     */
    flip: new Ext.Anim({
        is3d: true,
        direction: 'left',
        before: function(el) {
            var rotateProp = 'Y',
                fromScale = 1,
                toScale = 1,
                fromRotate = 0,
                toRotate = 0;

            if (this.out) {
                toRotate = -180;
                toScale = 0.8;
            }
            else {
                fromRotate = 180;
                fromScale = 0.8;
            }

            if (this.direction == 'up' || this.direction == 'down') {
                rotateProp = 'X';
            }

            if (this.direction == 'right' || this.direction == 'left') {
                toRotate *= -1;
                fromRotate *= -1;
            }

            this.from = {
                '-webkit-transform': 'rotate' + rotateProp + '(' + fromRotate + 'deg) scale(' + fromScale + ')',
                '-webkit-backface-visibility': 'hidden'
            };
            this.to = {
                '-webkit-transform': 'rotate' + rotateProp + '(' + toRotate + 'deg) scale(' + toScale + ')',
                '-webkit-backface-visibility': 'hidden'
            };
        }
    }),

    /**
     * Cube Animation
     */
    cube: new Ext.Anim({
        is3d: true,
        direction: 'left',
        style: 'outer',
        before: function(el) {
            var origin = '0% 0%',
                fromRotate = 0,
                toRotate = 0,
                rotateProp = 'Y',
                fromZ = 0,
                toZ = 0,
                elW = el.getWidth(),
                elH = el.getHeight(),
                showTranslateZ = true,
                fromTranslate = ' translateX(0)',
                toTranslate = '';

            if (this.direction == 'left' || this.direction == 'right') {
                if (this.out) {
                    origin = '100% 100%';
                    toZ = elW;
                    toRotate = -90;
                } else {
                    origin = '0% 0%';
                    fromZ = elW;
                    fromRotate = 90;
                }
            } else if (this.direction == 'up' || this.direction == 'down') {
                rotateProp = 'X';
                if (this.out) {
                    origin = '100% 100%';
                    toZ = elH;
                    toRotate = 90;
                } else {
                    origin = '0% 0%';
                    fromZ = elH;
                    fromRotate = -90;
                }
            }

            if (this.direction == 'down' || this.direction == 'right') {
                fromRotate *= -1;
                toRotate *= -1;
                origin = (origin == '0% 0%') ? '100% 100%': '0% 0%';
            }

            if (this.style == 'inner') {
                fromZ *= -1;
                toZ *= -1;
                fromRotate *= -1;
                toRotate *= -1;

                if (!this.out) {
                    toTranslate = ' translateX(0px)';
                    origin = '0% 50%';
                } else {
                    toTranslate = fromTranslate;
                    origin = '100% 50%';
                }
            }

            this.from = {
                '-webkit-transform': 'rotate' + rotateProp + '(' + fromRotate + 'deg)' + (showTranslateZ ? ' translateZ(' + fromZ + 'px)': '') + fromTranslate,
                '-webkit-transform-origin': origin
            };
            this.to = {
                '-webkit-transform': 'rotate' + rotateProp + '(' + toRotate + 'deg) translateZ(' + toZ + 'px)' + toTranslate,
                '-webkit-transform-origin': origin
            };
        },
        duration: 250
    }),


    /**
     * Wipe Animation.
     * <p>Because of the amount of calculations involved, this animation is best used on small display
     * changes or specifically for phone environments. Does not currently accept any parameters.</p>
     */
    wipe: new Ext.Anim({
        before: function(el) {
            var curZ = el.getStyle('z-index'),
                zIndex,
                mask = '';

            if (!this.out) {
                zIndex = curZ + 1;
                mask = '-webkit-gradient(linear, left bottom, right bottom, from(transparent), to(#000), color-stop(66%, #000), color-stop(33%, transparent))';

                this.from = {
                    '-webkit-mask-image': mask,
                    '-webkit-mask-size': el.getWidth() * 3 + 'px ' + el.getHeight() + 'px',
                    'z-index': zIndex,
                    '-webkit-mask-position-x': 0
                };
                this.to = {
                    '-webkit-mask-image': mask,
                    '-webkit-mask-size': el.getWidth() * 3 + 'px ' + el.getHeight() + 'px',
                    'z-index': zIndex,
                    '-webkit-mask-position-x': -el.getWidth() * 2 + 'px'
                };
            }
        },
        duration: 500
    })
};

/**
 * This class compiles the XTemplate syntax into a function object. The function is used
 * like so:
 *
 *      function (out, values, parent, xindex, xcount) {
 *          // out is the output array to store results
 *          // values, parent, xindex and xcount have their historical meaning
 *      }
 *
 * @markdown
 * @private
 */
Ext.define('Ext.XTemplateCompiler', {
    extend: 'Ext.XTemplateParser',

    // Chrome really likes "new Function" to realize the code block (as in it is
    // 2x-3x faster to call it than using eval), but Firefox chokes on it badly.
    // IE and Opera are also fine with the "new Function" technique.
    useEval: Ext.isGecko,

    useFormat: true,

    propNameRe: /^[\w\d\$]*$/,

    compile: function (tpl) {
        var me = this,
            code = me.generate(tpl);

        // When using "new Function", we have to pass our "Ext" variable to it in order to
        // support sandboxing. If we did not, the generated function would use the global
        // "Ext", not the "Ext" from our sandbox (scope chain).
        //
        return me.useEval ? me.evalTpl(code) : (new Function('Ext', code))(Ext);
    },

    generate: function (tpl) {
        var me = this;

        me.body = [
            'var c0=values, p0=parent, n0=xcount, i0=xindex;\n'
        ];
        me.funcs = [
            'var fm=Ext.util.Format;' // note: Ext here is properly sandboxed
        ];
        me.switches = [];

        me.parse(tpl);

        me.funcs.push(
            (me.useEval ? '$=' : 'return') + ' function (' + me.fnArgs + ') {',
                me.body.join(''),
            '}'
        );

        var code = me.funcs.join('\n');

        return code;
    },

    //-----------------------------------
    // XTemplateParser callouts

    doText: function (text) {
        text = text.replace(this.aposRe, "\\'");
        text = text.replace(this.newLineRe, '\\n');
        this.body.push('out.push(\'', text, '\')\n');
    },

    doExpr: function (expr) {
        this.body.push('out.push(String(', expr, '))\n');
    },

    doTag: function (tag) {
        this.doExpr(this.parseTag(tag));
    },

    doElse: function () {
        this.body.push('} else {\n');
    },

    doEval: function (text) {
        this.body.push(text, '\n');
    },

    doIf: function (action, actions) {
        var me = this;

        // If it's just a propName, use it directly in the if
        if (me.propNameRe.test(action)) {
            me.body.push('if (', me.parseTag(action), ') {\n');
        }
        // Otherwise, it must be an expression, and needs to be returned from an fn which uses with(values)
        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 it's just a propName, use it directly in the else if
        if (me.propNameRe.test(action)) {
            me.body.push('} else if (', me.parseTag(action), ') {\n');
        }
        // Otherwise, it must be an expression, and needs to be returned from an fn which uses with(values)
        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 it's just a propName, use it directly in the switch
        if (me.propNameRe.test(action)) {
            me.body.push('switch (', me.parseTag(action), ') {\n');
        }
        // Otherwise, it must be an expression, and needs to be returned from an fn which uses with(values)
        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') {
            /*
            To exit a for loop we must restore the outer loop's context. The code looks
            like this (which goes with that produced by doFor:

                    for (...) { // the part generated by doFor
                        ...  // the body of the for loop

                        // ... any tpl for exec statement goes here...
                    }
                    parent = p1;
                    values = r2;
                    xcount = n1;
                    xindex = i1
            */
            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,'\n');
        } else if (type == 'if' || type == 'switch') {
            me.body.push('}\n');
        }
    },

    doFor: function (action, actions) {
        var me = this,
            s = me.addFn(action),
            L = me.level,
            up = L-1;

        /*
        We are trying to produce a block of code that looks like below. We use the nesting
        level to uniquely name the control variables.

            var c2 = f5.call(this, out, values, parent, xindex, xcount),
                    // c2 is the context object for the for loop
                a2 = Ext.isArray(c2),
                    // a2 is the isArray result for the context
                p2 = (parent=c1),
                    // p2 is the parent context (of the outer for loop)
                r2 = values
                    // r2 is the values object to

            // i2 is the loop index and n2 is the number (xcount) of this for loop
            for (var i2 = 0, n2 = a2 ? c2.length : (c2 ? 1 : 0), xcount = n2; i2 < n2; ++i2) {
                values=a2?c2[i2]:c2   // adjust special vars to inner scope
                xindex=i2+1           // xindex is 1-based

        The body of the loop is whatever comes between the tpl and /tpl statements (which
        is handled by doEnd).
        */

        me.body.push('var c',L,'=',s,me.callFn,', a',L,'=Ext.isArray(c',L,'),p',L,'=(parent=c',up,'),r',L,'=values\n',
            'for (var i',L,'=0,n',L,'=a',L,'?c',L,'.length:(c',L,'?1:0), xcount=n',L,';i',L,'<n'+L+';++i',L,'){\n',
            'values=a',L,'?c',L,'[i',L,']:c',L,'\n',
            'xindex=i',L,'+1\n');
    },

    doExec: function (action, actions) {
        var me = this,
            name = 'f' + me.funcs.length;

        me.funcs.push('function ' + name + '(' + me.fnArgs + ') {',
                            ' try { with(values) {',
                            '  ' + action,
                            ' }} catch(e) {}',
                      '}');

        me.body.push(name + me.callFn + '\n');
    },

    //-----------------------------------
    // Internal

    addFn: function (body) {
        var me = this,
            name = 'f' + me.funcs.length;

        if (body === '.') {
            me.funcs.push('function ' + name + '(' + me.fnArgs + ') {',
                            ' return values',
                       '}');
        } else if (body === '..') {
            me.funcs.push('function ' + name + '(' + me.fnArgs + ') {',
                            ' return parent',
                       '}');
        } else {
            me.funcs.push('function ' + name + '(' + me.fnArgs + ') {',
                            ' try { with(values) {',
                            '  return(' + body + ')',
                            ' }} catch(e) {}',
                       '}');
        }

        return name;
    },

    parseTag: function (tag) {
        var m = this.tagRe.exec(tag),
            name = m[1],
            format = m[2],
            args = m[3],
            math = m[4],
            v;

        // name = "." - Just use the values object.
        if (name == '.') {
            // filter to not include arrays/objects/nulls
            v = 'Ext.Array.indexOf(["string", "number", "boolean"], typeof values) > -1 || Ext.isDate(values) ? values : ""';
        }
        // name = "#" - Use the xindex
        else if (name == '#') {
            v = 'xindex';
        }
        else if (name.substr(0, 7) == "parent.") {
            v = name;
        }
        // name has a . in it - Use object literal notation, starting from values
        else if ((name.indexOf('.') !== -1) && (name.indexOf('-') === -1)) {

            v = "values." + name;
        }
        // name is a property of values
        else {
            v = "values['" + name + "']";
        }

        if (math) {
            v = '(' + v + math + ')';
        }

        if (format && this.useFormat) {
            args = args ? ',' + args : "";
            if (format.substr(0, 5) != "this.") {
                format = "fm." + format + '(';
            } else {
                format += '(';
            }
        } else {
            args = '';
            format = "(" + v + " === undefined ? '' : ";
        }

        return format + v + args + ')';
    },

    // @private
    evalTpl: function ($) {

        // We have to use eval to realize the code block and capture the inner func we also
        // don't want a deep scope chain. We only do this in Firefox and it is also unhappy
        // with eval containing a return statement, so instead we assign to "$" and return
        // that. Because we use "eval", we are automatically sandboxed properly.
        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';
    proto.callFn = '.call(this,' + proto.fnArgs + ')';
});
/**
 * @extends Object
 * @author Ed Spencer
 *
 * Fields are used to define what a Model is. They aren't instantiated directly - instead, when we create a class that
 * extends {@link Ext.data.Model}, it will automatically create a Field instance for each field configured in a {@link
 * Ext.data.Model Model}. For example, we might set up a model like this:
 *
 *     Ext.define('User', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: [
 *                 'name', 'email',
 *                 {name: 'age', type: 'int'},
 *                 {name: 'gender', type: 'string', defaultValue: 'Unknown'}
 *             ]
 *         }
 *     });
 *
 * Four fields will have been created for the User Model - name, email, age and gender. Note that we specified a couple
 * of different formats here; if we only pass in the string name of the field (as with name and email), the field is set
 * up with the 'auto' type. It's as if we'd done this instead:
 *
 *     Ext.define('User', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: [
 *                 {name: 'name', type: 'auto'},
 *                 {name: 'email', type: 'auto'},
 *                 {name: 'age', type: 'int'},
 *                 {name: 'gender', type: 'string', defaultValue: 'Unknown'}
 *             ]
 *         }
 *     });
 *
 * # Types and conversion
 *
 * The {@link #type} is important - it's used to automatically convert data passed to the field into the correct format.
 * In our example above, the name and email fields used the 'auto' type and will just accept anything that is passed
 * into them. The 'age' field had an 'int' type however, so if we passed 25.4 this would be rounded to 25.
 *
 * Sometimes a simple type isn't enough, or we want to perform some processing when we load a Field's data. We can do
 * this using a {@link #convert} function. Here, we're going to create a new field based on another:
 *
 *     Ext.define('User', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: [
 *                 'name', 'email',
 *                 {name: 'age', type: 'int'},
 *                 {name: 'gender', type: 'string', defaultValue: 'Unknown'},
 *
 *                 {
 *                     name: 'firstName',
 *                     convert: function(value, record) {
 *                         var fullName  = record.get('name'),
 *                             splits    = fullName.split(" "),
 *                             firstName = splits[0];
 *
 *                         return firstName;
 *                     }
 *                 }
 *             ]
 *         }
 *     });
 *
 * Now when we create a new User, the firstName is populated automatically based on the name:
 *
 *     var ed = Ext.create('User', {name: 'Ed Spencer'});
 *
 *     console.log(ed.get('firstName')); //logs 'Ed', based on our convert function
 *
 * In fact, if we log out all of the data inside ed, we'll see this:
 *
 *     console.log(ed.data);
 *
 *     //outputs this:
 *     {
 *         age: 0,
 *         email: "",
 *         firstName: "Ed",
 *         gender: "Unknown",
 *         name: "Ed Spencer"
 *     }
 *
 * The age field has been given a default of zero because we made it an int type. As an auto field, email has defaulted
 * to an empty string. When we registered the User model we set gender's {@link #defaultValue} to 'Unknown' so we see
 * that now. Let's correct that and satisfy ourselves that the types work as we expect:
 *
 *     ed.set('gender', 'Male');
 *     ed.get('gender'); //returns 'Male'
 *
 *     ed.set('age', 25.4);
 *     ed.get('age'); //returns 25 - we wanted an int, not a float, so no decimal places allowed
 */
Ext.define('Ext.data.Field', {
    requires: ['Ext.data.Types', 'Ext.data.SortTypes'],
    alias: 'data.field',

    isField: true,
    
    config: {
        /**
         * @cfg {String} name
         *
         * The name by which the field is referenced within the Model. This is referenced by, for example, the `dataIndex`
         * property in column definition objects passed to Ext.grid.property.HeaderContainer.
         *
         * Note: In the simplest case, if no properties other than `name` are required, a field definition may consist of
         * just a String for the field name.
         */
        name: null,

        /**
         * @cfg {String/Object} type
         *
         * The data type for automatic conversion from received data to the *stored* value if
         * `{@link Ext.data.Field#convert convert}` has not been specified. This may be specified as a string value.
         * Possible values are
         *
         * - auto (Default, implies no conversion)
         * - string
         * - int
         * - float
         * - boolean
         * - date
         *
         * This may also be specified by referencing a member of the {@link Ext.data.Types} class.
         *
         * Developers may create their own application-specific data types by defining new members of the {@link
         * Ext.data.Types} class.
         */
        type: 'auto',

        /**
         * @cfg {Function} convert
         *
         * A function which converts the value provided by the Reader into an object that will be stored in the Model.
         * It is passed the following parameters:
         *
         * - **v** : Mixed
         *
         *   The data value as read by the Reader, if undefined will use the configured `{@link Ext.data.Field#defaultValue
         *   defaultValue}`.
         *
         * - **rec** : Ext.data.Model
         *
         *   The data object containing the Model as read so far by the Reader. Note that the Model may not be fully populated
         *   at this point as the fields are read in the order that they are defined in your
         *   {@link Ext.data.Model#cfg-fields fields} array.
         *
         * Example of convert functions:
         *
         *     function fullName(v, record) {
         *         return record.name.last + ', ' + record.name.first;
         *     }
         *
         *     function location(v, record) {
         *         return !record.city ? '' : (record.city + ', ' + record.state);
         *     }
         *
         *     Ext.define('Dude', {
         *         extend: 'Ext.data.Model',
         *         fields: [
         *             {name: 'fullname',  convert: fullName},
         *             {name: 'firstname', mapping: 'name.first'},
         *             {name: 'lastname',  mapping: 'name.last'},
         *             {name: 'city', defaultValue: 'homeless'},
         *             'state',
         *             {name: 'location',  convert: location}
         *         ]
         *     });
         *
         *     // create the data store
         *     var store = Ext.create('Ext.data.Store', {
         *         reader: {
         *             type: 'json',
         *             model: 'Dude',
         *             idProperty: 'key',
         *             rootProperty: 'daRoot',
         *             totalProperty: 'total'
         *         }
         *     });
         *
         *     var myData = [
         *         { key: 1,
         *           name: { first: 'Fat',    last:  'Albert' }
         *           // notice no city, state provided in data2 object
         *         },
         *         { key: 2,
         *           name: { first: 'Barney', last:  'Rubble' },
         *           city: 'Bedrock', state: 'Stoneridge'
         *         },
         *         { key: 3,
         *           name: { first: 'Cliff',  last:  'Claven' },
         *           city: 'Boston',  state: 'MA'
         *         }
         *     ];
         */
        convert: undefined,

        /**
         * @cfg {String} dateFormat
         *
         * Used when converting received data into a Date when the {@link #type} is specified as `"date"`.
         *
         * A format string for the {@link Ext.Date#parse Ext.Date.parse} function, or "timestamp" if the value provided by
         * the Reader is a UNIX timestamp, or "time" if the value provided by the Reader is a javascript millisecond
         * timestamp. See {@link Ext.Date}.
         */
        dateFormat: null,

        /**
         * @cfg {Boolean} allowNull
         *
         * Use when converting received data into a boolean, string or number type (either int or float). If the value cannot be
         * parsed, null will be used if allowNull is true, otherwise the value will be 0. Defaults to true.
         */
        allowNull: true,

        /**
         * @cfg {Object} defaultValue
         *
         * The default value used **when a Model is being created by a {@link Ext.data.reader.Reader Reader}**
         * when the item referenced by the `{@link Ext.data.Field#mapping mapping}` does not exist in the data object
         * (i.e. undefined). Defaults to "".
         */
        defaultValue: undefined,

        /**
         * @cfg {String/Number} mapping
         *
         * (Optional) A path expression for use by the {@link Ext.data.reader.Reader} implementation that is creating the
         * {@link Ext.data.Model Model} to extract the Field value from the data object. If the path expression is the same
         * as the field name, the mapping may be omitted.
         *
         * The form of the mapping expression depends on the Reader being used.
         *
         * - {@link Ext.data.reader.Json}
         *
         *   The mapping is a string containing the javascript expression to reference the data from an element of the data2
         *   item's {@link Ext.data.reader.Json#rootProperty rootProperty} Array. Defaults to the field name.
         *
         * - {@link Ext.data.reader.Xml}
         *
         *   The mapping is an {@link Ext.DomQuery} path to the data item relative to the DOM element that represents the
         *   {@link Ext.data.reader.Xml#record record}. Defaults to the field name.
         *
         * - {@link Ext.data.reader.Array}
         *
         *   The mapping is a number indicating the Array index of the field's value. Defaults to the field specification's
         *   Array position.
         *
         * If a more complex value extraction strategy is required, then configure the Field with a {@link #convert}
         * function. This is passed the whole row object, and may interrogate it in whatever way is necessary in order to
         * return the desired data.
         */
        mapping: null,

        /**
         * @cfg {Function} sortType
         *
         * A function which converts a Field's value to a comparable value in order to ensure correct sort ordering.
         * Predefined functions are provided in {@link Ext.data.SortTypes}. A custom sort example:
         *
         *     // current sort     after sort we want
         *     // +-+------+          +-+------+
         *     // |1|First |          |1|First |
         *     // |2|Last  |          |3|Second|
         *     // |3|Second|          |2|Last  |
         *     // +-+------+          +-+------+
         *
         *     sortType: function(value) {
         *        switch (value.toLowerCase()) // native toLowerCase():
         *        {
         *           case 'first': return 1;
         *           case 'second': return 2;
         *           default: return 3;
         *        }
         *     }
         */
        sortType : undefined,

        /**
         * @cfg {String} sortDir
         *
         * Initial direction to sort (`"ASC"` or `"DESC"`). Defaults to `"ASC"`.
         */
        sortDir : "ASC",

        /**
         * @cfg {Boolean} allowBlank
         * @private
         *
         * Used for validating a {@link Ext.data.Model model}. Defaults to true. An empty value here will cause
         * {@link Ext.data.Model}.{@link Ext.data.Model#isValid isValid} to evaluate to false.
         */
        allowBlank : true,

        /**
         * @cfg {Boolean} persist
         *
         * False to exclude this field from the {@link Ext.data.Model#modified} fields in a model. This will also exclude
         * the field from being written using a {@link Ext.data.writer.Writer}. This option is useful when model fields are
         * used to keep state on the client but do not need to be persisted to the server. Defaults to true.
         */
        persist: true,

        // Used in LocalStorage stuff
        encode: null,
        decode: null
    },

    constructor : function(config) {
        // This adds support for just passing a string used as the field name
        if (Ext.isString(config)) {
            config = {name: config};
        }

        this.initConfig(config);
    },

    applyType: function(type) {
        var types = Ext.data.Types,
            autoType = types.AUTO;

        if (type) {
            if (Ext.isString(type)) {
                return types[type.toUpperCase()] || autoType;
            } else {
                // At this point we expect an actual type
                return type;
            }
        }

        return autoType;
    },

    updateType: function(newType, oldType) {
        var convert = this.getConvert();
        if (oldType && convert === oldType.convert) {
            this.setConvert(newType.convert);
        }
    },

    applySortType: function(sortType) {
        var sortTypes = Ext.data.SortTypes,
            type = this.getType(),
            defaultSortType = type.sortType;

        if (sortType) {
            if (Ext.isString(sortType)) {
                return sortTypes[sortType] || defaultSortType;
            } else {
                // At this point we expect a function
                return sortType;
            }
        }

        return defaultSortType;
    },

    applyConvert: function(convert) {
        var defaultConvert = this.getType().convert;
        if (convert && convert !== defaultConvert) {
            this._hasCustomConvert = true;
            return convert;
        } else {
            this._hasCustomConvert = false;
            return defaultConvert;
        }
    },

    hasCustomConvert: function() {
        return this._hasCustomConvert;
    }

});

/**
 * @author Tommy Maintz
 *
 * This class is a sequential id generator. A simple use of this class would be like so:
 *
 *     Ext.define('MyApp.data.MyModel', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             identifier: 'sequential'
 *         }
 *     });
 *     // assign id's of 1, 2, 3, etc.
 *
 * An example of a configured generator would be:
 *
 *     Ext.define('MyApp.data.MyModel', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             identifier: {
 *                 type: 'sequential',
 *                 prefix: 'ID_',
 *                 seed: 1000
 *             }
 *         }
 *     });
 *     // assign id's of ID_1000, ID_1001, ID_1002, etc.
 *
 */
Ext.define('Ext.data.identifier.Sequential', {
    extend: 'Ext.data.identifier.Simple',
    alias: 'data.identifier.sequential',

    config: {
        /**
         * @cfg {String} prefix
         * The string to place in front of the sequential number for each generated id. The
         * default is blank.
         */
        prefix: '',

        /**
         * @cfg {Number} seed
         * The number at which to start generating sequential id's. The default is 1.
         */
        seed: 1
    },

    constructor: function() {
        var me = this;
        me.callParent(arguments);
        me.parts = [me.getPrefix(), ''];
    },

    generate: function(record) {
        var me = this,
            parts = me.parts,
            seed = me.getSeed() + 1;

        me.setSeed(seed);
        parts[1] = seed;

        return parts.join('');
    }
});
/**
 * This class is used to write {@link Ext.data.Model} data to the server in a JSON format.
 * The {@link #allowSingle} configuration can be set to false to force the records to always be
 * encoded in an array, even if there is only a single record being sent.
 */
Ext.define('Ext.data.writer.Json', {
    extend: 'Ext.data.writer.Writer',
    alternateClassName: 'Ext.data.JsonWriter',
    alias: 'writer.json',

    config: {
        /**
         * @cfg {String} root
         * The key under which the records in this Writer will be placed. If you specify {@link #encode} to be true,
         * we default this to 'records'.
         *
         * Example generated request, using root: 'records':
         *
         *     {'records': [{name: 'my record'}, {name: 'another record'}]}
         *
         */
        root: undefined,

        /**
         * @cfg {Boolean} encode
         * True to use Ext.encode() on the data before sending. The encode option should only be set to true when a
         * {@link #root} is defined, because the values will be sent as part of the request parameters as opposed to
         * a raw post. The root will be the name of the parameter sent to the server.
         */
        encode: false,

        /**
         * @cfg {Boolean} allowSingle
         * False to ensure that records are always wrapped in an array, even if there is only one record being sent.
         * When there is more than one record, they will always be encoded into an array.
         *
         * Example:
         *
         *     // with allowSingle: true
         *     "root": {
         *         "first": "Mark",
         *         "last": "Corrigan"
         *     }
         *
         *     // with allowSingle: false
         *     "root": [{
         *         "first": "Mark",
         *         "last": "Corrigan"
         *     }]
         */
        allowSingle: true,

        encodeRequest: false
    },

    applyRoot: function(root) {
        if (!root && (this.getEncode() || this.getEncodeRequest())) {
            root = 'data';
        }
        return root;
    },

    //inherit docs
    writeRecords: function(request, data) {
        var root = this.getRoot(),
            params = request.getParams(),
            allowSingle = this.getAllowSingle(),
            jsonData;

        if (this.getAllowSingle() && data && data.length == 1) {
            // convert to single object format
            data = data[0];
        }

        if (this.getEncodeRequest()) {
            jsonData = request.getJsonData() || {};
            if (data && (data.length || (allowSingle && Ext.isObject(data)))) {
                jsonData[root] = data;
            }
            request.setJsonData(Ext.apply(jsonData, params || {}));
            request.setParams(null);
            request.setMethod('POST');
            return request;
        }

        if (!data || !(data.length || (allowSingle && Ext.isObject(data)))) {
            return request;
        }

        if (this.getEncode()) {
            if (root) {
                // sending as a param, need to encode
                params[root] = Ext.encode(data);
            } else {
                Ext.Logger.error('Must specify a root when using encode');
            }
        } else {
            // send as jsonData
            jsonData = request.getJsonData() || {};
            if (root) {
                jsonData[root] = data;
            } else {
                jsonData = data;
            }
            request.setJsonData(jsonData);
        }
        return request;
    }
});


/*
 * @allowSingle: true
 * @encodeRequest: false
 * Url: update.json?param1=test
 * {'field1': 'test': 'field2': 'test'}
 *
 * @allowSingle: false
 * @encodeRequest: false
 * Url: update.json?param1=test
 * [{'field1': 'test', 'field2': 'test'}]
 *
 * @allowSingle: true
 * @root: 'data'
 * @encodeRequest: true
 * Url: update.json
 * {
 *    'param1': 'test',
 *    'data': {'field1': 'test', 'field2': 'test'}
 * }
 *
 * @allowSingle: false
 * @root: 'data'
 * @encodeRequest: true
 * Url: update.json
 * {
 *     'param1': 'test',
 *     'data': [{'field1': 'test', 'field2': 'test'}]
 * }
 *
 * @allowSingle: true
 * @root: data
 * @encodeRequest: false
 * Url: update.json
 * param1=test&data={'field1': 'test', 'field2': 'test'}
 *
 * @allowSingle: false
 * @root: data
 * @encodeRequest: false
 * @ncode: true
 * Url: update.json
 * param1=test&data=[{'field1': 'test', 'field2': 'test'}]
 *
 * @allowSingle: true
 * @root: data
 * @encodeRequest: false
 * Url: update.json?param1=test&data={'field1': 'test', 'field2': 'test'}
 *
 * @allowSingle: false
 * @root: data
 * @encodeRequest: false
 * Url: update.json?param1=test&data=[{'field1': 'test', 'field2': 'test'}]
 */

/**
 * @private
 */
Ext.define('Ext.event.Dispatcher', {

    requires: [
        'Ext.event.ListenerStack',
        'Ext.event.Controller'
    ],

    statics: {
        getInstance: function() {
            if (!this.instance) {
                this.instance = new this();
            }

            return this.instance;
        },

        setInstance: function(instance) {
            this.instance = instance;

            return this;
        }
    },

    config: {
        publishers: {}
    },

    wildcard: '*',

    constructor: function(config) {
        this.listenerStacks = {};

        this.activePublishers = {};

        this.publishersCache = {};

        this.noActivePublishers = [];

        this.controller = null;

        this.initConfig(config);

        return this;
    },

    getListenerStack: function(targetType, target, eventName, createIfNotExist) {
        var listenerStacks = this.listenerStacks,
            map = listenerStacks[targetType],
            listenerStack;

        createIfNotExist = Boolean(createIfNotExist);

        if (!map) {
            if (createIfNotExist) {
                listenerStacks[targetType] = map = {};
            }
            else {
                return null;
            }
        }

        map = map[target];

        if (!map) {
            if (createIfNotExist) {
                listenerStacks[targetType][target] = map = {};
            }
            else {
                return null;
            }
        }

        listenerStack = map[eventName];

        if (!listenerStack) {
            if (createIfNotExist) {
                map[eventName] = listenerStack = new Ext.event.ListenerStack();
            }
            else {
                return null;
            }
        }

        return listenerStack;
    },

    getController: function(targetType, target, eventName, connectedController) {
        var controller = this.controller,
            info = {
                targetType: targetType,
                target: target,
                eventName: eventName
            };

        if (!controller) {
            this.controller = controller = new Ext.event.Controller();
        }

        if (controller.isFiring) {
            controller = new Ext.event.Controller();
        }

        controller.setInfo(info);

        if (connectedController && controller !== connectedController) {
            controller.connect(connectedController);
        }

        return controller;
    },

    applyPublishers: function(publishers) {
        var i, publisher;

        this.publishersCache = {};

        for (i in publishers) {
            if (publishers.hasOwnProperty(i)) {
                publisher = publishers[i];

                this.registerPublisher(publisher);
            }
        }

        return publishers;
    },

    registerPublisher: function(publisher) {
        var activePublishers = this.activePublishers,
            targetType = publisher.getTargetType(),
            publishers = activePublishers[targetType];

        if (!publishers) {
            activePublishers[targetType] = publishers = [];
        }

        publishers.push(publisher);

        publisher.setDispatcher(this);

        return this;
    },

    getCachedActivePublishers: function(targetType, eventName) {
        var cache = this.publishersCache,
            publishers;

        if ((publishers = cache[targetType]) && (publishers = publishers[eventName])) {
            return publishers;
        }

        return null;
    },

    cacheActivePublishers: function(targetType, eventName, publishers) {
        var cache = this.publishersCache;

        if (!cache[targetType]) {
            cache[targetType] = {};
        }

        cache[targetType][eventName] = publishers;

        return publishers;
    },

    getActivePublishers: function(targetType, eventName) {
        var publishers, activePublishers,
            i, ln, publisher;

        if ((publishers = this.getCachedActivePublishers(targetType, eventName))) {
            return publishers;
        }

        activePublishers = this.activePublishers[targetType];

        if (activePublishers) {
            publishers = [];

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

                if (publisher.handles(eventName)) {
                    publishers.push(publisher);
                }
            }
        }
        else {
            publishers = this.noActivePublishers;
        }

        return this.cacheActivePublishers(targetType, eventName, publishers);
    },

    hasListener: function(targetType, target, eventName) {
        var listenerStack = this.getListenerStack(targetType, target, eventName);

        if (listenerStack) {
            return listenerStack.count() > 0;
        }

        return false;
    },

    addListener: function(targetType, target, eventName) {
        var publishers = this.getActivePublishers(targetType, eventName),
            ln = publishers.length,
            i;

        if (ln > 0) {
            for (i = 0; i < ln; i++) {
                publishers[i].subscribe(target, eventName);
            }
        }

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

    doAddListener: function(targetType, target, eventName, fn, scope, options, order) {
        var listenerStack = this.getListenerStack(targetType, target, eventName, true);

        return listenerStack.add(fn, scope, options, order);
    },

    removeListener: function(targetType, target, eventName) {
        var publishers = this.getActivePublishers(targetType, eventName),
            ln = publishers.length,
            i;

        if (ln > 0) {
            for (i = 0; i < ln; i++) {
                publishers[i].unsubscribe(target, eventName);
            }
        }

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

    doRemoveListener: function(targetType, target, eventName, fn, scope, order) {
        var listenerStack = this.getListenerStack(targetType, target, eventName);

        if (listenerStack === null) {
            return false;
        }

        return listenerStack.remove(fn, scope, order);
    },

    clearListeners: function(targetType, target, eventName) {
        var listenerStacks = this.listenerStacks,
            ln = arguments.length,
            stacks, publishers, i, publisherGroup;

        if (ln === 3) {
            if (listenerStacks[targetType] && listenerStacks[targetType][target]) {
                this.removeListener(targetType, target, eventName);
                delete listenerStacks[targetType][target][eventName];
            }
        }
        else if (ln === 2) {
            if (listenerStacks[targetType]) {
                stacks = listenerStacks[targetType][target];

                if (stacks) {
                    for (eventName in stacks) {
                        if (stacks.hasOwnProperty(eventName)) {
                            publishers = this.getActivePublishers(targetType, eventName);

                            for (i = 0,ln = publishers.length; i < ln; i++) {
                                publishers[i].unsubscribe(target, eventName, true);
                            }
                        }
                    }

                    delete listenerStacks[targetType][target];
                }
            }
        }
        else if (ln === 1) {
            publishers = this.activePublishers[targetType];

            for (i = 0,ln = publishers.length; i < ln; i++) {
                publishers[i].unsubscribeAll();
            }

            delete listenerStacks[targetType];
        }
        else {
            publishers = this.activePublishers;

            for (targetType in publishers) {
                if (publishers.hasOwnProperty(targetType)) {
                    publisherGroup = publishers[targetType];

                    for (i = 0,ln = publisherGroup.length; i < ln; i++) {
                        publisherGroup[i].unsubscribeAll();
                    }
                }
            }

            delete this.listenerStacks;
            this.listenerStacks = {};
        }

        return this;
    },

    dispatchEvent: function(targetType, target, eventName) {
        var publishers = this.getActivePublishers(targetType, eventName),
            ln = publishers.length,
            i;

        if (ln > 0) {
            for (i = 0; i < ln; i++) {
                publishers[i].notify(target, eventName);
            }
        }

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

    doDispatchEvent: function(targetType, target, eventName, args, action, connectedController) {
        var listenerStack = this.getListenerStack(targetType, target, eventName),
            wildcardStacks = this.getWildcardListenerStacks(targetType, target, eventName),
            controller;

        if ((listenerStack === null || listenerStack.length == 0)) {
            if (wildcardStacks.length == 0 && !action) {
                return;
            }
        }
        else {
            wildcardStacks.push(listenerStack);
        }

        controller = this.getController(targetType, target, eventName, connectedController);
        controller.setListenerStacks(wildcardStacks);
        controller.fire(args, action);

        return !controller.isInterrupted();
    },

    getWildcardListenerStacks: function(targetType, target, eventName) {
        var stacks = [],
            wildcard = this.wildcard,
            isEventNameNotWildcard = eventName !== wildcard,
            isTargetNotWildcard = target !== wildcard,
            stack;

        if (isEventNameNotWildcard && (stack = this.getListenerStack(targetType, target, wildcard))) {
            stacks.push(stack);
        }

        if (isTargetNotWildcard && (stack = this.getListenerStack(targetType, wildcard, eventName))) {
            stacks.push(stack);
        }

        return stacks;
    }
});

/**
 * @private
 * @extends Object
 * DOM event. This class really extends Ext.event.Event, but for documentation
 * purposes it's members are listed inside Ext.event.Event.
 */
Ext.define('Ext.event.Dom', {
    extend: 'Ext.event.Event',

    constructor: function(event) {
        var target = event.target,
            touches;

        if (target && target.nodeType !== 1) {
            target = target.parentNode;
        }
        touches = event.changedTouches;
        if (touches) {
            touches = touches[0];
            this.pageX = touches.pageX;
            this.pageY = touches.pageY;
        }
        else {
            this.pageX = event.pageX;
            this.pageY = event.pageY;
        }

        this.browserEvent = this.event = event;
        this.target = this.delegatedTarget = target;
        this.type = event.type;

        this.timeStamp = this.time = event.timeStamp;

        if (typeof this.time != 'number') {
            this.time = new Date(this.time).getTime();
        }

        return this;
    },

    /**
     * @property {Number} distance
     * The distance of the event.
     *
     * **This is only available when the event type is `swipe` and `pinch`**
     */

    /**
     * @property {HTMLElement} target
     * The target HTMLElement for this event. For example; if you are listening to a tap event and you tap on a `<div>` element,
     * this will return that `<div>` element.
     */

    /**
     * @property {Number} pageX The browsers x coordinate of the event.
     */

    /**
     * @property {Number} pageY The browsers y coordinate of the event.
     */

    stopEvent: function() {
        this.preventDefault();

        return this.callParent();
    },

    /**
     * Prevents the browsers default handling of the event.
     */
    preventDefault: function() {
        this.browserEvent.preventDefault();
    },

    /**
     * Gets the x coordinate of the event.
     * @deprecated 2.0 Please use {@link #pageX} property directly.
     */
    getPageX: function() {
        return this.browserEvent.pageX;
    },

    /**
     * Gets the y coordinate of the event.
     * @deprecated 2.0 Please use {@link #pageX} property directly.
     */
    getPageY: function() {
        return this.browserEvent.pageY;
    },

    /**
     * Gets the X and Y coordinates of the event.
     * @deprecated 2.0 Please use the {@link #pageX} and {@link #pageY} properties directly.
     */
    getXY: function() {
        if (!this.xy) {
            this.xy = [this.getPageX(), this.getPageY()];
        }

        return this.xy;
    },

    /**
     * Gets the target for the event. Unlike {@link #target}, this returns the main element for your event. So if you are
     * listening to a tap event on Ext.Viewport.element, and you tap on an inner element of Ext.Viewport.element, this will
     * return Ext.Viewport.element.
     *
     * If you want the element you tapped on, then use {@link #target}.
     *
     * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
     * @param {Number/Mixed} maxDepth (optional) The max depth to
     search as a number or element (defaults to 10 || document.body)
     * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
     * @return {HTMLElement}
     */
    getTarget: function(selector, maxDepth, returnEl) {
        if (arguments.length === 0) {
            return this.delegatedTarget;
        }

        return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target);
    },

    /**
     * Returns the time of the event.
     * @return {Date}
     */
    getTime: function() {
        return this.time;
    },

    setDelegatedTarget: function(target) {
        this.delegatedTarget = target;
    },

    makeUnpreventable: function() {
        this.browserEvent.preventDefault = Ext.emptyFn;
    }
});

/**
 * @private
 * Touch event.
 */
Ext.define('Ext.event.Touch', {
    extend: 'Ext.event.Dom',

    requires: [
        'Ext.util.Point'
    ],

    constructor: function(event, info) {
        if (info) {
            this.set(info);
        }

        this.touchesMap = {};

        this.changedTouches = this.cloneTouches(event.changedTouches);
        this.touches = this.cloneTouches(event.touches);
        this.targetTouches = this.cloneTouches(event.targetTouches);

        return this.callParent([event]);
    },

    clone: function() {
        return new this.self(this);
    },

    setTargets: function(targetsMap) {
        this.doSetTargets(this.changedTouches, targetsMap);
        this.doSetTargets(this.touches, targetsMap);
        this.doSetTargets(this.targetTouches, targetsMap);
    },

    doSetTargets: function(touches, targetsMap) {
        var i, ln, touch, identifier, targets;

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

            identifier = touch.identifier;

            targets = targetsMap[identifier];

            if (targets) {
                touch.targets = targets;
            }
        }
    },

    cloneTouches: function(touches) {
        var map = this.touchesMap,
            clone = [],
            lastIdentifier = null,
            i, ln, touch, identifier;

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

            identifier = touch.identifier;

            // A quick fix for a bug found in Bada 1.0 where all touches have
            // idenfitier of 0
            if (lastIdentifier !== null && identifier === lastIdentifier) {
                identifier++;
            }

            lastIdentifier = identifier;

            if (!map[identifier]) {
                map[identifier] = {
                    pageX: touch.pageX,
                    pageY: touch.pageY,
                    identifier: identifier,
                    target: touch.target,
                    timeStamp: touch.timeStamp,
                    point: Ext.util.Point.fromTouch(touch),
                    targets: touch.targets
                };
            }

            clone[i] = map[identifier];
        }

        return clone;
    }
});

/**
 * @private
 */
Ext.define('Ext.event.publisher.ComponentPaint', {

    extend: 'Ext.event.publisher.Publisher',

    targetType: 'component',

    handledEvents: ['painted', 'erased'],

    eventNames: {
        painted: 'painted',
        erased: 'erased'
    },

    constructor: function() {
        this.callParent(arguments);

        this.hiddenQueue = {};
        this.renderedQueue = {};
    },

    getSubscribers: function(eventName, createIfNotExist) {
        var subscribers = this.subscribers;

        if (!subscribers.hasOwnProperty(eventName)) {
            if (!createIfNotExist) {
                return null;
            }

            subscribers[eventName] = {
                $length: 0
            };
        }

        return subscribers[eventName];
    },

    setDispatcher: function(dispatcher) {
        var targetType = this.targetType;

        dispatcher.doAddListener(targetType, '*', 'renderedchange', 'onBeforeComponentRenderedChange', this, null, 'before');
        dispatcher.doAddListener(targetType, '*', 'hiddenchange', 'onBeforeComponentHiddenChange', this, null, 'before');
        dispatcher.doAddListener(targetType, '*', 'renderedchange', 'onComponentRenderedChange', this, null, 'after');
        dispatcher.doAddListener(targetType, '*', 'hiddenchange', 'onComponentHiddenChange', this, null, 'after');

        return this.callParent(arguments);
    },

    subscribe: function(target, eventName) {
        var match = target.match(this.idSelectorRegex),
            subscribers,
            id;

        if (!match) {
            return false;
        }

        id = match[1];

        subscribers = this.getSubscribers(eventName, true);

        if (subscribers.hasOwnProperty(id)) {
            subscribers[id]++;
            return true;
        }

        subscribers[id] = 1;
        subscribers.$length++;

        return true;
    },

    unsubscribe: function(target, eventName, all) {
        var match = target.match(this.idSelectorRegex),
            subscribers,
            id;

        if (!match || !(subscribers = this.getSubscribers(eventName))) {
            return false;
        }

        id = match[1];

        if (!subscribers.hasOwnProperty(id) || (!all && --subscribers[id] > 0)) {
            return true;
        }

        delete subscribers[id];

        if (--subscribers.$length === 0) {
            delete this.subscribers[eventName];
        }

        return true;
    },

    onBeforeComponentRenderedChange: function(container, component, rendered) {
        var eventNames = this.eventNames,
            eventName = rendered ? eventNames.painted : eventNames.erased,
            subscribers = this.getSubscribers(eventName),
            queue;

        if (subscribers && subscribers.$length > 0) {
            this.renderedQueue[component.getId()] = queue = [];
            this.publish(subscribers, component, eventName, queue);
        }
    },

    onBeforeComponentHiddenChange: function(component, hidden) {
        var eventNames = this.eventNames,
            eventName = hidden ? eventNames.erased : eventNames.painted,
            subscribers = this.getSubscribers(eventName),
            queue;

        if (subscribers && subscribers.$length > 0) {
            this.hiddenQueue[component.getId()] = queue = [];
            this.publish(subscribers, component, eventName, queue);
        }
    },

    onComponentRenderedChange: function(container, component) {
        var renderedQueue = this.renderedQueue,
            id = component.getId(),
            queue;

        if (!renderedQueue.hasOwnProperty(id)) {
            return;
        }

        queue = renderedQueue[id];
        delete renderedQueue[id];

        if (queue.length > 0) {
            this.dispatchQueue(queue);
        }
    },

    onComponentHiddenChange: function(component) {
        var hiddenQueue = this.hiddenQueue,
            id = component.getId(),
            queue;

        if (!hiddenQueue.hasOwnProperty(id)) {
            return;
        }

        queue = hiddenQueue[id];
        delete hiddenQueue[id];

        if (queue.length > 0) {
            this.dispatchQueue(queue);
        }
    },

    dispatchQueue: function(dispatchingQueue) {
        var dispatcher = this.dispatcher,
            targetType = this.targetType,
            eventNames = this.eventNames,
            queue = dispatchingQueue.slice(),
            ln = queue.length,
            i, item, component, eventName, isPainted;

        dispatchingQueue.length = 0;

        if (ln > 0) {
            for (i = 0; i < ln; i++) {
                item = queue[i];
                component = item.component;
                eventName = item.eventName;
                isPainted = component.isPainted();

                if ((eventName === eventNames.painted && isPainted) || eventName === eventNames.erased && !isPainted) {
                    dispatcher.doDispatchEvent(targetType, '#' + item.id, eventName, [component]);
                }
            }
            queue.length = 0;
        }

    },

    publish: function(subscribers, component, eventName, dispatchingQueue) {
        var id = component.getId(),
            needsDispatching = false,
            eventNames, items, i, ln, isPainted;

        if (subscribers[id]) {
            eventNames = this.eventNames;

            isPainted = component.isPainted();

            if ((eventName === eventNames.painted && !isPainted) || eventName === eventNames.erased && isPainted) {
                needsDispatching = true;
            }
            else {
                return this;
            }
        }

        if (component.isContainer) {
            items = component.getItems().items;

            for (i = 0,ln = items.length; i < ln; i++) {
                this.publish(subscribers, items[i], eventName, dispatchingQueue);
            }
        }
        else if (component.isDecorator) {
            this.publish(subscribers, component.getComponent(), eventName, dispatchingQueue);
        }

        if (needsDispatching) {
            dispatchingQueue.push({
                id: id,
                eventName: eventName,
                component: component
            });
        }
    }
});

/**
 * @private
 */
Ext.define('Ext.event.publisher.Dom', {
    extend: 'Ext.event.publisher.Publisher',

    requires: [
        'Ext.env.Browser',
        'Ext.Element',
        'Ext.event.Dom'
    ],

    targetType: 'element',

    idOrClassSelectorRegex: /^([#|\.])([\w\-]+)$/,

    handledEvents: ['click', 'focus', 'blur',
                    'mousemove', 'mousedown', 'mouseup', 'mouseover', 'mouseout',
                    'keyup', 'keydown', 'keypress', 'submit',
                    'transitionend', 'animationstart', 'animationend'],

    classNameSplitRegex: /\s+/,

    SELECTOR_ALL: '*',

    constructor: function() {
        var eventNames = this.getHandledEvents(),
            eventNameMap = {},
            i, ln, eventName, vendorEventName;

        this.doBubbleEventsMap = {
            'click': true,
            'submit': true,
            'mousedown': true,
            'mousemove': true,
            'mouseup': true,
            'mouseover': true,
            'mouseout': true,
            'transitionend': true
        };

        this.onEvent = Ext.Function.bind(this.onEvent, this);

        for (i = 0,ln = eventNames.length; i < ln; i++) {
            eventName = eventNames[i];
            vendorEventName = this.getVendorEventName(eventName);
            eventNameMap[vendorEventName] = eventName;

            this.attachListener(vendorEventName);
        }

        this.eventNameMap = eventNameMap;

        return this.callParent();
    },

    getSubscribers: function(eventName) {
        var subscribers = this.subscribers,
            eventSubscribers = subscribers[eventName];

        if (!eventSubscribers) {
            eventSubscribers = subscribers[eventName] = {
                id: {
                    $length: 0
                },
                className: {
                    $length: 0
                },
                selector: [],
                all: 0,
                $length: 0
            }
        }

        return eventSubscribers;
    },

    getVendorEventName: function(eventName) {
        if (eventName === 'transitionend') {
            eventName = Ext.browser.getVendorProperyName('transitionEnd');
        }
        else if (eventName === 'animationstart') {
            eventName = Ext.browser.getVendorProperyName('animationStart');
        }
        else if (eventName === 'animationend') {
            eventName = Ext.browser.getVendorProperyName('animationEnd');
        }

        return eventName;
    },

    attachListener: function(eventName) {
        document.addEventListener(eventName, this.onEvent, !this.doesEventBubble(eventName));

        return this;
    },

    removeListener: function(eventName) {
        document.removeEventListener(eventName, this.onEvent, !this.doesEventBubble(eventName));

        return this;
    },

    doesEventBubble: function(eventName) {
        return !!this.doBubbleEventsMap[eventName];
    },

    subscribe: function(target, eventName) {
        if (!this.handles(eventName)) {
            return false;
        }

        var idOrClassSelectorMatch = target.match(this.idOrClassSelectorRegex),
            subscribers = this.getSubscribers(eventName),
            idSubscribers = subscribers.id,
            classNameSubscribers = subscribers.className,
            selectorSubscribers = subscribers.selector,
            type, value;

        if (idOrClassSelectorMatch !== null) {
            type = idOrClassSelectorMatch[1];
            value = idOrClassSelectorMatch[2];

            if (type === '#') {
                if (idSubscribers.hasOwnProperty(value)) {
                    idSubscribers[value]++;
                    return true;
                }

                idSubscribers[value] = 1;
                idSubscribers.$length++;
            }
            else {
                if (classNameSubscribers.hasOwnProperty(value)) {
                    classNameSubscribers[value]++;
                    return true;
                }

                classNameSubscribers[value] = 1;
                classNameSubscribers.$length++;
            }
        }
        else {
            if (target === this.SELECTOR_ALL) {
                subscribers.all++;
            }
            else {
                if (selectorSubscribers.hasOwnProperty(target)) {
                    selectorSubscribers[target]++;
                    return true;
                }

                selectorSubscribers[target] = 1;
                selectorSubscribers.push(target);
            }
        }

        subscribers.$length++;

        return true;
    },

    unsubscribe: function(target, eventName, all) {
        if (!this.handles(eventName)) {
            return false;
        }

        var idOrClassSelectorMatch = target.match(this.idOrClassSelectorRegex),
            subscribers = this.getSubscribers(eventName),
            idSubscribers = subscribers.id,
            classNameSubscribers = subscribers.className,
            selectorSubscribers = subscribers.selector,
            type, value;

        all = Boolean(all);

        if (idOrClassSelectorMatch !== null) {
            type = idOrClassSelectorMatch[1];
            value = idOrClassSelectorMatch[2];

            if (type === '#') {
                if (!idSubscribers.hasOwnProperty(value) || (!all && --idSubscribers[value] > 0)) {
                    return true;
                }

                delete idSubscribers[value];
                idSubscribers.$length--;
            }
            else {
                if (!classNameSubscribers.hasOwnProperty(value) || (!all && --classNameSubscribers[value] > 0)) {
                    return true;
                }

                delete classNameSubscribers[value];
                classNameSubscribers.$length--;
            }
        }
        else {
            if (target === this.SELECTOR_ALL) {
                if (all) {
                    subscribers.all = 0;
                }
                else {
                    subscribers.all--;
                }
            }
            else {
                if (!selectorSubscribers.hasOwnProperty(target) || (!all && --selectorSubscribers[target] > 0)) {
                    return true;
                }

                delete selectorSubscribers[target];
                Ext.Array.remove(selectorSubscribers, target);
            }
        }

        subscribers.$length--;

        return true;
    },

    getElementTarget: function(target) {
        if (target.nodeType !== 1) {
            target = target.parentNode;

            if (!target || target.nodeType !== 1) {
                return null;
            }
        }

        return target;
    },

    getBubblingTargets: function(target) {
        var targets = [];

        if (!target) {
            return targets;
        }

        do {
            targets[targets.length] = target;

            target = target.parentNode;
        } while (target && target.nodeType === 1);

        return targets;
    },

    dispatch: function(target, eventName, args) {
        args.push(args[0].target);
        this.callParent(arguments);
    },

    publish: function(eventName, targets, event) {
        var subscribers = this.getSubscribers(eventName),
            wildcardSubscribers;

        if (subscribers.$length === 0 || !this.doPublish(subscribers, eventName, targets, event)) {
            wildcardSubscribers = this.getSubscribers('*');

            if (wildcardSubscribers.$length > 0) {
                this.doPublish(wildcardSubscribers, eventName, targets, event);
            }
        }

        return this;
    },

    doPublish: function(subscribers, eventName, targets, event) {
        var idSubscribers = subscribers.id,
            classNameSubscribers = subscribers.className,
            selectorSubscribers = subscribers.selector,
            hasIdSubscribers = idSubscribers.$length > 0,
            hasClassNameSubscribers = classNameSubscribers.$length > 0,
            hasSelectorSubscribers = selectorSubscribers.length > 0,
            hasAllSubscribers = subscribers.all > 0,
            isClassNameHandled = {},
            args = [event],
            hasDispatched = false,
            classNameSplitRegex = this.classNameSplitRegex,
            i, ln, j, subLn, target, id, className, classNames, selector;

        for (i = 0,ln = targets.length; i < ln; i++) {
            target = targets[i];
            event.setDelegatedTarget(target);

            if (hasIdSubscribers) {
                id = target.id;

                if (id) {
                    if (idSubscribers.hasOwnProperty(id)) {
                        hasDispatched = true;
                        this.dispatch('#' + id, eventName, args);
                    }
                }
            }

            if (hasClassNameSubscribers) {
                className = target.className;

                if (className) {
                    classNames = className.split(classNameSplitRegex);

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

                        if (!isClassNameHandled[className]) {
                            isClassNameHandled[className] = true;

                            if (classNameSubscribers.hasOwnProperty(className)) {
                                hasDispatched = true;
                                this.dispatch('.' + className, eventName, args);
                            }
                        }
                    }
                }
            }

            // Stop propagation
            if (event.isStopped) {
                return hasDispatched;
            }
        }

        if (hasAllSubscribers && !hasDispatched) {
            event.setDelegatedTarget(event.browserEvent.target);
            hasDispatched = true;
            this.dispatch(this.ALL_SELECTOR, eventName, args);
            if (event.isStopped) {
                return hasDispatched;
            }
        }

        if (hasSelectorSubscribers) {
            for (j = 0,subLn = targets.length; j < subLn; j++) {
                target = targets[j];

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

                    if (this.matchesSelector(target, selector)) {
                        event.setDelegatedTarget(target);
                        hasDispatched = true;
                        this.dispatch(selector, eventName, args);
                    }

                    if (event.isStopped) {
                        return hasDispatched;
                    }
                }
            }
        }

        return hasDispatched;
    },

    matchesSelector: function(element, selector) {
        if ('webkitMatchesSelector' in element) {
            return element.webkitMatchesSelector(selector);
        }

        return Ext.DomQuery.is(element, selector);
    },

    onEvent: function(e) {
        var eventName = this.eventNameMap[e.type];

        if (!eventName || this.getSubscribersCount(eventName) === 0) {
            return;
        }

        var target = this.getElementTarget(e.target),
            targets;

        if (!target) {
            return;
        }

        if (this.doesEventBubble(eventName)) {
            targets = this.getBubblingTargets(target);
        }
        else {
            targets = [target];
        }

        this.publish(eventName, targets, new Ext.event.Dom(e));
    },

    hasSubscriber: function(target, eventName) {
        if (!this.handles(eventName)) {
            return false;
        }

        var match = target.match(this.idOrClassSelectorRegex),
            subscribers = this.getSubscribers(eventName),
            type, value;

        if (match !== null) {
            type = match[1];
            value = match[2];

            if (type === '#') {
                return subscribers.id.hasOwnProperty(value);
            }
            else {
                return subscribers.className.hasOwnProperty(value);
            }
        }
        else {
            return (subscribers.selector.hasOwnProperty(target) && Ext.Array.indexOf(subscribers.selector, target) !== -1);
        }

        return false;
    },

    getSubscribersCount: function(eventName) {
        if (!this.handles(eventName)) {
            return 0;
        }

        return this.getSubscribers(eventName).$length + this.getSubscribers('*').$length;
    }

});

/**
 * @private
 */
Ext.define('Ext.event.publisher.TouchGesture', {

    extend: 'Ext.event.publisher.Dom',

    requires: [
        'Ext.util.Point',
        'Ext.event.Touch'
    ],

    handledEvents: ['touchstart', 'touchmove', 'touchend', 'touchcancel'],

    moveEventName: 'touchmove',

    config: {
        moveThrottle: 1,
        buffering: {
            enabled: false,
            interval: 10
        },
        recognizers: {}
    },

    currentTouchesCount: 0,

    constructor: function(config) {
        this.processEvents = Ext.Function.bind(this.processEvents, this);

        this.eventProcessors = {
            touchstart: this.onTouchStart,
            touchmove: this.onTouchMove,
            touchend: this.onTouchEnd,
            touchcancel: this.onTouchEnd
        };

        this.eventToRecognizerMap = {};

        this.activeRecognizers = [];

        this.currentRecognizers = [];

        this.currentTargets = {};

        this.currentTouches = {};

        this.buffer = [];

        this.initConfig(config);

        return this.callParent();
    },

    applyBuffering: function(buffering) {
        if (buffering.enabled === true) {
            this.bufferTimer = setInterval(this.processEvents, buffering.interval);
        }
        else {
            clearInterval(this.bufferTimer);
        }

        return buffering;
    },

    applyRecognizers: function(recognizers) {
        var i, recognizer;

        for (i in recognizers) {
            if (recognizers.hasOwnProperty(i)) {
                recognizer = recognizers[i];

                this.registerRecognizer(recognizer);
            }
        }

        return recognizers;
    },

    handles: function(eventName) {
        return this.callParent(arguments) || this.eventToRecognizerMap.hasOwnProperty(eventName);
    },

    doesEventBubble: function() {
        // All touch events bubble
        return true;
    },

    eventLogs: [],

    onEvent: function(e) {
        var buffering = this.getBuffering();

        e = new Ext.event.Touch(e);

        if (buffering.enabled) {
            this.buffer.push(e);
        }
        else {
            this.processEvent(e);
        }
    },

    processEvents: function() {
        var buffer = this.buffer,
            ln = buffer.length,
            moveEvents = [],
            events, event, i;

        if (ln > 0) {
            events = buffer.slice(0);
            buffer.length = 0;

            for (i = 0; i < ln; i++) {
                event = events[i];
                if (event.type === this.moveEventName) {
                    moveEvents.push(event);
                }
                else {
                    if (moveEvents.length > 0) {
                        this.processEvent(this.mergeEvents(moveEvents));
                        moveEvents.length = 0;
                    }

                    this.processEvent(event);
                }
            }

            if (moveEvents.length > 0) {
                this.processEvent(this.mergeEvents(moveEvents));
                moveEvents.length = 0;
            }
        }
    },

    mergeEvents: function(events) {
        var changedTouchesLists = [],
            ln = events.length,
            i, event, targetEvent;

        targetEvent = events[ln - 1];

        if (ln === 1) {
            return targetEvent;
        }

        for (i = 0; i < ln; i++) {
            event = events[i];
            changedTouchesLists.push(event.changedTouches);
        }

        targetEvent.changedTouches = this.mergeTouchLists(changedTouchesLists);

        return targetEvent;
    },

    mergeTouchLists: function(touchLists) {
        var touches = {},
            list = [],
            i, ln, touchList, j, subLn, touch, identifier;

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

            for (j = 0,subLn = touchList.length; j < subLn; j++) {
                touch = touchList[j];
                identifier = touch.identifier;
                touches[identifier] = touch;
            }
        }

        for (identifier in touches) {
            if (touches.hasOwnProperty(identifier)) {
                list.push(touches[identifier]);
            }
        }

        return list;
    },

    registerRecognizer: function(recognizer) {
        var map = this.eventToRecognizerMap,
            activeRecognizers = this.activeRecognizers,
            handledEvents = recognizer.getHandledEvents(),
            i, ln, eventName;

        recognizer.setOnRecognized(this.onRecognized);
        recognizer.setCallbackScope(this);

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

            map[eventName] = recognizer;
        }

        activeRecognizers.push(recognizer);

        return this;
    },

    onRecognized: function(eventName, e, touches, info) {
        var targetGroups = [],
            ln = touches.length,
            targets, i, touch;

        if (ln === 1) {
            return this.publish(eventName, touches[0].targets, e, info);
        }

        for (i = 0; i < ln; i++) {
            touch = touches[i];
            targetGroups.push(touch.targets);
        }

        targets = this.getCommonTargets(targetGroups);

        this.publish(eventName, targets, e, info);
    },

    publish: function(eventName, targets, event, info) {
        event.set(info);

        return this.callParent([eventName, targets, event]);
    },

    getCommonTargets: function(targetGroups) {
        var firstTargetGroup = targetGroups[0],
            ln = targetGroups.length;

        if (ln === 1) {
            return firstTargetGroup;
        }

        var commonTargets = [],
            i = 1,
            target, targets, j;

        while (true) {
            target = firstTargetGroup[firstTargetGroup.length - i];

            if (!target) {
                return commonTargets;
            }

            for (j = 1; j < ln; j++) {
                targets = targetGroups[j];

                if (targets[targets.length - i] !== target) {
                    return commonTargets;
                }
            }

            commonTargets.unshift(target);
            i++;
        }

        return commonTargets;
    },

    invokeRecognizers: function(methodName, e) {
        var recognizers = this.activeRecognizers,
            ln = recognizers.length,
            i, recognizer;

        if (methodName === 'onStart') {
            for (i = 0; i < ln; i++) {
                recognizers[i].isActive = true;
            }
        }

        for (i = 0; i < ln; i++) {
            recognizer = recognizers[i];
            if (recognizer.isActive && recognizer[methodName].call(recognizer, e) === false) {
                recognizer.isActive = false;
            }
        }
    },

    getActiveRecognizers: function() {
        return this.activeRecognizers;
    },

    processEvent: function(e) {
        this.eventProcessors[e.type].call(this, e);
    },

    onTouchStart: function(e) {
        var currentTargets = this.currentTargets,
            currentTouches = this.currentTouches,
            currentTouchesCount = this.currentTouchesCount,
            changedTouches = e.changedTouches,
            touches = e.touches,
            touchesLn = touches.length,
            currentIdentifiers = {},
            ln = changedTouches.length,
            i, touch, identifier, fakeEndEvent;

        currentTouchesCount += ln;

        if (currentTouchesCount > touchesLn) {
            for (i = 0; i < touchesLn; i++) {
                touch = touches[i];
                identifier = touch.identifier;
                currentIdentifiers[identifier] = true;
            }

            for (identifier in currentTouches) {
                if (currentTouches.hasOwnProperty(identifier)) {
                    if (!currentIdentifiers[identifier]) {
                        currentTouchesCount--;
                        fakeEndEvent = e.clone();
                        touch = currentTouches[identifier];
                        touch.targets = this.getBubblingTargets(this.getElementTarget(touch.target));
                        fakeEndEvent.changedTouches = [touch];
                        this.onTouchEnd(fakeEndEvent);
                    }
                }
            }

            // Fix for a bug found in Motorola Droid X (Gingerbread) and similar
            // where there are 2 touchstarts but just one touchend
            if (currentTouchesCount > touchesLn) {
                return;
            }
        }

        for (i = 0; i < ln; i++) {
            touch = changedTouches[i];
            identifier = touch.identifier;

            if (!currentTouches.hasOwnProperty(identifier)) {
                this.currentTouchesCount++;
            }

            currentTouches[identifier] = touch;
            currentTargets[identifier] = this.getBubblingTargets(this.getElementTarget(touch.target));
        }

        e.setTargets(currentTargets);

        for (i = 0; i < ln; i++) {
            touch = changedTouches[i];

            this.publish('touchstart', touch.targets, e, {touch: touch});
        }

        if (!this.isStarted) {
            this.isStarted = true;
            this.invokeRecognizers('onStart', e);
        }

        this.invokeRecognizers('onTouchStart', e);
    },

    onTouchMove: function(e) {
        if (!this.isStarted) {
            return;
        }

        var currentTargets = this.currentTargets,
            currentTouches = this.currentTouches,
            moveThrottle = this.getMoveThrottle(),
            changedTouches = e.changedTouches,
            stillTouchesCount = 0,
            i, ln, touch, point, oldPoint, identifier;

        e.setTargets(currentTargets);

        for (i = 0,ln = changedTouches.length; i < ln; i++) {
            touch = changedTouches[i];
            identifier = touch.identifier;
            point = touch.point;

            oldPoint = currentTouches[identifier].point;

            if (moveThrottle && point.isCloseTo(oldPoint, moveThrottle)) {
                stillTouchesCount++;
                continue;
            }

            currentTouches[identifier] = touch;

            this.publish('touchmove', touch.targets, e, {touch: touch});
        }

        if (stillTouchesCount < ln) {
            this.invokeRecognizers('onTouchMove', e);
        }
    },

    onTouchEnd: function(e) {
        if (!this.isStarted) {
            return;
        }

        var currentTargets = this.currentTargets,
            currentTouches = this.currentTouches,
            changedTouches = e.changedTouches,
            ln = changedTouches.length,
            isEnded, identifier, i, touch;

        e.setTargets(currentTargets);

        this.currentTouchesCount -= ln;

        isEnded = (this.currentTouchesCount === 0);

        if (isEnded) {
            this.isStarted = false;
        }

        for (i = 0; i < ln; i++) {
            touch = changedTouches[i];
            identifier = touch.identifier;

            delete currentTouches[identifier];
            delete currentTargets[identifier];

            this.publish('touchend', touch.targets, e, {touch: touch});
        }

        this.invokeRecognizers('onTouchEnd', e);

        if (isEnded) {
            this.invokeRecognizers('onEnd', e);
        }
    }

}, function() {
    if (!Ext.feature.has.Touch) {
        this.override({
            moveEventName: 'mousemove',

            map: {
                mouseToTouch: {
                    mousedown: 'touchstart',
                    mousemove: 'touchmove',
                    mouseup: 'touchend'
                },

                touchToMouse: {
                    touchstart: 'mousedown',
                    touchmove: 'mousemove',
                    touchend: 'mouseup'
                }
            },

            attachListener: function(eventName) {
                eventName = this.map.touchToMouse[eventName];

                if (!eventName) {
                    return;
                }

                return this.callOverridden([eventName]);
            },

            lastEventType: null,

            onEvent: function(e) {
                if ('button' in e && e.button !== 0) {
                    return;
                }

                var type = e.type,
                    touchList = [e];

                // Temporary fix for a recent Chrome bugs where events don't seem to bubble up to document
                // when the element is being animated
                // with webkit-transition (2 mousedowns without any mouseup)
                if (type === 'mousedown' && this.lastEventType && this.lastEventType !== 'mouseup') {
                    var fixedEvent = document.createEvent("MouseEvent");
                        fixedEvent.initMouseEvent('mouseup', e.bubbles, e.cancelable,
                            document.defaultView, e.detail, e.screenX, e.screenY, e.clientX,
                            e.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, e.metaKey,
                            e.button, e.relatedTarget);

                    this.onEvent(fixedEvent);
                }

                if (type !== 'mousemove') {
                    this.lastEventType = type;
                }

                e.identifier = 1;
                e.touches = (type !== 'mouseup') ? touchList : [];
                e.targetTouches = (type !== 'mouseup') ? touchList : [];
                e.changedTouches = touchList;

                return this.callOverridden([e]);
            },

            processEvent: function(e) {
                this.eventProcessors[this.map.mouseToTouch[e.type]].call(this, e);
            }
        });
    }
});

/**
 * @private
 */
Ext.define('Ext.event.recognizer.MultiTouch', {
    extend: 'Ext.event.recognizer.Touch',

    requiredTouchesCount: 2,

    isTracking: false,

    isStarted: false,

    onTouchStart: function(e) {
        var requiredTouchesCount = this.requiredTouchesCount,
            touches = e.touches,
            touchesCount = touches.length;

        if (touchesCount === requiredTouchesCount) {
            this.start(e);
        }
        else if (touchesCount > requiredTouchesCount) {
            this.end(e);
        }
    },

    onTouchEnd: function(e) {
        this.end(e);
    },

    start: function() {
        if (!this.isTracking) {
            this.isTracking = true;
            this.isStarted = false;
        }
    },

    end: function(e) {
        if (this.isTracking) {
            this.isTracking = false;

            if (this.isStarted) {
                this.isStarted = false;

                this.fireEnd(e);
            }
        }
    }
});

/**
 * A event recogniser which knows when you pinch.
 * 
 * @private
 */
Ext.define('Ext.event.recognizer.Pinch', {
    extend: 'Ext.event.recognizer.MultiTouch',

    requiredTouchesCount: 2,

    handledEvents: ['pinchstart', 'pinch', 'pinchend'],

    /**
     * @member Ext.dom.Element
     * @event pinchstart
     * Fired once when a pinch has started.
     * @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
     * @param {HTMLElement} node The target of the event.
     * @param {Object} options The options object passed to Ext.util.Observable.addListener.
     */
    
    /**
     * @member Ext.dom.Element
     * @event pinch
     * Fires continuously when there is pinching (the touch must move for this to be fired).
     * @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
     * @param {HTMLElement} node The target of the event.
     * @param {Object} options The options object passed to Ext.util.Observable.addListener.
     */
    
    /**
     * @member Ext.dom.Element
     * @event pinchend
     * Fires when a pinch has ended.
     * @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
     * @param {HTMLElement} node The target of the event.
     * @param {Object} options The options object passed to Ext.util.Observable.addListener.
     */
    
    /**
     * @property {Number} scale
     * The scape of a pinch event.
     *
     * **This is only available when the event type is `pinch`**
     * @member Ext.event.Event
     */

    startDistance: 0,

    lastTouches: null,

    onTouchMove: function(e) {
        if (!this.isTracking) {
            return;
        }

        var touches = Array.prototype.slice.call(e.touches),
            firstPoint, secondPoint, distance;

        firstPoint = touches[0].point;
        secondPoint = touches[1].point;

        distance = firstPoint.getDistanceTo(secondPoint);

        if (distance === 0) {
            return;
        }

        if (!this.isStarted) {

            this.isStarted = true;

            this.startDistance = distance;

            this.fire('pinchstart', e, touches, {
                touches: touches,
                distance: distance,
                scale: 1
            });
        }
        else {
            this.fire('pinch', e, touches, {
                touches: touches,
                distance: distance,
                scale: distance / this.startDistance
            });
        }

        this.lastTouches = touches;
    },

    fireEnd: function(e) {
        this.fire('pinchend', e, this.lastTouches);
    },

    fail: function() {
        return this.callParent(arguments);
    }
});

/**
 * A simple event recogniser which knows when you rotate.
 * 
 * @private
 */
Ext.define('Ext.event.recognizer.Rotate', {
    extend: 'Ext.event.recognizer.MultiTouch',

    requiredTouchesCount: 2,

    handledEvents: ['rotatestart', 'rotate', 'rotateend'],

    /**
     * @member Ext.dom.Element
     * @event rotatestart
     * Fired once when a rotation has started.
     * @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
     * @param {HTMLElement} node The target of the event.
     * @param {Object} options The options object passed to Ext.util.Observable.addListener.
     */
    
    /**
     * @member Ext.dom.Element
     * @event rotate
     * Fires continuously when there is rotation (the touch must move for this to be fired).
     * When listening to this, ensure you know about the {@link Ext.event.Event#angle} and {@link Ext.event.Event#rotation}
     * properties in the `event` object.
     * @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
     * @param {HTMLElement} node The target of the event.
     * @param {Object} options The options object passed to Ext.util.Observable.addListener.
     */
    
    /**
     * @member Ext.dom.Element
     * @event rotateend
     * Fires when a rotation event has ended.
     * @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
     * @param {HTMLElement} node The target of the event.
     * @param {Object} options The options object passed to Ext.util.Observable.addListener.
     */

    /**
     * @property {Number} angle
     * The angle of the rotation.
     * 
     * **This is only available when the event type is `rotate`**
     * @member Ext.event.Event
     */
    
    /**
     * @property {Number} rotation
     * A amount of rotation, since the start of the event.
     * 
     * **This is only available when the event type is `rotate`**
     * @member Ext.event.Event
     */

    startAngle: 0,

    lastTouches: null,

    lastAngle: null,

    onTouchMove: function(e) {
        if (!this.isTracking) {
            return;
        }

        var touches = Array.prototype.slice.call(e.touches),
            lastAngle = this.lastAngle,
            firstPoint, secondPoint, angle, nextAngle, previousAngle, diff;

        firstPoint = touches[0].point;
        secondPoint = touches[1].point;

        angle = firstPoint.getAngleTo(secondPoint);

        if (lastAngle !== null) {
            diff = Math.abs(lastAngle - angle);
            nextAngle = angle + 360;
            previousAngle = angle - 360;

            if (Math.abs(nextAngle - lastAngle) < diff) {
                angle = nextAngle;
            }
            else if (Math.abs(previousAngle - lastAngle) < diff) {
                angle = previousAngle;
            }
        }

        this.lastAngle = angle;

        if (!this.isStarted) {
            this.isStarted = true;

            this.startAngle = angle;

            this.fire('rotatestart', e, touches, {
                touches: touches,
                angle: angle,
                rotation: 0
            });
        }
        else {
            this.fire('rotate', e, touches, {
                touches: touches,
                angle: angle,
                rotation: angle - this.startAngle
            });
        }

        this.lastTouches = touches;
    },

    fireEnd: function(e) {
        this.lastAngle = null;
        this.fire('rotateend', e, this.lastTouches);
    }
});

/**
 * @private
 */
Ext.define('Ext.event.recognizer.SingleTouch', {
    extend: 'Ext.event.recognizer.Touch',

    inheritableStatics: {
        NOT_SINGLE_TOUCH: 0x01,
        TOUCH_MOVED:  0x02
    },

    onTouchStart: function(e) {
        if (e.touches.length > 1) {
            return this.fail(this.self.NOT_SINGLE_TOUCH);
        }
    }
});


/**
 * A simple event recogniser which knows when you double tap.
 * 
 * @private
 */
Ext.define('Ext.event.recognizer.DoubleTap', {

    extend: 'Ext.event.recognizer.SingleTouch',

    config: {
        maxDuration: 300
    },

    handledEvents: ['singletap', 'doubletap'],

    /**
     * @member Ext.dom.Element
     * @event singletap
     * Fires when there is a single tap.
     * @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
     * @param {HTMLElement} node The target of the event.
     * @param {Object} options The options object passed to Ext.util.Observable.addListener.
     */
    
    /**
     * @member Ext.dom.Element
     * @event doubletap
     * Fires when there is a double tap.
     * @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
     * @param {HTMLElement} node The target of the event.
     * @param {Object} options The options object passed to Ext.util.Observable.addListener.
     */

    singleTapTimer: null,

    onTouchStart: function(e) {
        if (this.callParent(arguments) === false) {
            return false;
        }

        this.startTime = e.time;
        clearTimeout(this.singleTapTimer);
    },

    onTouchMove: function() {
        return this.fail(this.self.TOUCH_MOVED);
    },

    onEnd: function(e) {
        var me = this,
            maxDuration = this.getMaxDuration(),
            touch = e.changedTouches[0],
            time = e.time,
            lastTapTime = this.lastTapTime,
            duration;

        this.lastTapTime = time;

        if (lastTapTime) {
            duration = time - lastTapTime;

            if (duration <= maxDuration) {
                this.lastTapTime = 0;

                this.fire('doubletap', e, [touch], {
                    touch: touch,
                    duration: duration
                });

                return;
            }
        }

        if (time - this.startTime > maxDuration) {
            this.fireSingleTap(e, touch);
        }
        else {
            this.singleTapTimer = setTimeout(function() {
                me.fireSingleTap(e, touch);
            }, maxDuration);
        }
    },

    fireSingleTap: function(e, touch) {
        this.fire('singletap', e, [touch], {
            touch: touch
        });
    }
});

/**
 * A simple event recogniser which knows when you drag.
 * 
 * @private
 */
Ext.define('Ext.event.recognizer.Drag', {
    extend: 'Ext.event.recognizer.SingleTouch',

    isStarted: false,

    startPoint: null,

    previousPoint: null,

    lastPoint: null,

    handledEvents: ['dragstart', 'drag', 'dragend'],

    /**
     * @member Ext.dom.Element
     * @event dragstart
     * Fired once when a drag has started.
     * @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
     * @param {HTMLElement} node The target of the event.
     * @param {Object} options The options object passed to Ext.util.Observable.addListener.
     */

    /**
     * @member Ext.dom.Element
     * @event drag
     * Fires continuously when there is dragging (the touch must move for this to be fired).
     * @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
     * @param {HTMLElement} node The target of the event.
     * @param {Object} options The options object passed to Ext.util.Observable.addListener.
     */
    
    /**
     * @member Ext.dom.Element
     * @event dragend
     * Fires when a drag has ended.
     * @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
     * @param {HTMLElement} node The target of the event.
     * @param {Object} options The options object passed to Ext.util.Observable.addListener.
     */

    onTouchStart: function(e) {
        var startTouches,
            startTouch;

        if (this.callParent(arguments) === false) {
            if (this.isStarted && this.lastMoveEvent !== null) {
                this.onTouchEnd(this.lastMoveEvent);
            }
            return false;
        }

        this.startTouches = startTouches = e.changedTouches;
        this.startTouch = startTouch = startTouches[0];
        this.startPoint = startTouch.point;
    },

    onTouchMove: function(e) {
        var touches = e.changedTouches,
            touch = touches[0],
            point = touch.point,
            time = e.time;

        if (this.lastPoint) {
            this.previousPoint = this.lastPoint;
        }

        if (this.lastTime) {
            this.previousTime = this.lastTime;
        }

        this.lastTime = time;
        this.lastPoint = point;
        this.lastMoveEvent = e;

        if (!this.isStarted) {
            this.isStarted = true;

            this.startTime = time;
            this.previousTime = time;

            this.previousPoint = this.startPoint;

            this.fire('dragstart', e, this.startTouches, this.getInfo(e, this.startTouch));
        }
        else {
            this.fire('drag', e, touches, this.getInfo(e, touch));
        }
    },

    onTouchEnd: function(e) {
        if (this.isStarted) {
            var touches = e.changedTouches,
                touch = touches[0],
                point = touch.point;

            this.isStarted = false;

            this.lastPoint = point;

            this.fire('dragend', e, touches, this.getInfo(e, touch));

            this.startTime = 0;
            this.previousTime = 0;
            this.lastTime = 0;

            this.startPoint = null;
            this.previousPoint = null;
            this.lastPoint = null;
            this.lastMoveEvent = null;
        }
    },

    getInfo: function(e, touch) {
        var time = e.time,
            startPoint = this.startPoint,
            previousPoint = this.previousPoint,
            startTime = this.startTime,
            previousTime = this.previousTime,
            point = this.lastPoint,
            deltaX = point.x - startPoint.x,
            deltaY = point.y - startPoint.y,
            info = {
                touch: touch,
                startX: startPoint.x,
                startY: startPoint.y,
                previousX: previousPoint.x,
                previousY: previousPoint.y,
                pageX: point.x,
                pageY: point.y,
                deltaX: deltaX,
                deltaY: deltaY,
                absDeltaX: Math.abs(deltaX),
                absDeltaY: Math.abs(deltaY),
                previousDeltaX: point.x - previousPoint.x,
                previousDeltaY: point.y - previousPoint.y,
                time: time,
                startTime: startTime,
                previousTime: previousTime,
                deltaTime: time - startTime,
                previousDeltaTime: time - previousTime
            };

        return info;
    }
});

/**
 * A event recogniser which knows when you tap and hold for more than 1 second.
 *
 * @private
 */
Ext.define('Ext.event.recognizer.LongPress', {
    extend: 'Ext.event.recognizer.SingleTouch',

    inheritableStatics: {
        DURATION_NOT_ENOUGH: 0x20
    },

    config: {
        minDuration: 1000
    },

    handledEvents: ['longpress'],

    /**
     * @member Ext.dom.Element
     * @event longpress
     * Fires when you touch and hold still for more than 1 second
     * @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
     * @param {HTMLElement} node The target of the event.
     * @param {Object} options The options object passed to Ext.util.Observable.addListener.
     */

    /**
     * @member Ext.dom.Element
     * @event taphold
     * @deprecated 2.0.0 Please add listener to 'longpress' event instead
     * @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
     * @param {HTMLElement} node The target of the event.
     * @param {Object} options The options object passed to Ext.util.Observable.addListener.
     */

    fireLongPress: function(e) {
        var touch = e.changedTouches[0];

        this.fire('longpress', e, [touch], {
            touch: touch,
            duration: this.getMinDuration()
        });

        this.isLongPress = true;
    },

    onTouchStart: function(e) {
        var me = this;

        if (this.callParent(arguments) === false) {
            return false;
        }

        this.isLongPress = false;

        this.timer = setTimeout(function() {
            me.fireLongPress(e);
        }, this.getMinDuration());
    },

    onTouchMove: function() {
        return this.fail(this.self.TOUCH_MOVED);
    },

    onTouchEnd: function() {
        if (!this.isLongPress) {
            return this.fail(this.self.DURATION_NOT_ENOUGH);
        }
    },

    fail: function() {
        clearTimeout(this.timer);

        return this.callParent(arguments);
    }

}, function() {
});

/**
 * A simple event recogniser which knows when you tap.
 *
 * @private
 */
Ext.define('Ext.event.recognizer.Tap', {

    handledEvents: ['tap'],

    /**
     * @member Ext.dom.Element
     * @event tap
     * Fires when you tap
     * @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
     * @param {HTMLElement} node The target of the event.
     * @param {Object} options The options object passed to Ext.util.Observable.addListener.
     */

    /**
     * @member Ext.dom.Element
     * @event tapstart
     * @deprecated 2.0.0 Please listener to 'touchstart' event instead
     * @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
     * @param {HTMLElement} node The target of the event.
     * @param {Object} options The options object passed to Ext.util.Observable.addListener.
     */

    /**
     * @member Ext.dom.Element
     * @event tapcancel
     * @deprecated 2.0.0 Please listener to 'touchmove' event instead
     * @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
     * @param {HTMLElement} node The target of the event.
     * @param {Object} options The options object passed to Ext.util.Observable.addListener.
     */
    extend: 'Ext.event.recognizer.SingleTouch',

    onTouchMove: function() {
        return this.fail(this.self.TOUCH_MOVED);
    },

    onTouchEnd: function(e) {
        var touch = e.changedTouches[0];

        this.fire('tap', e, [touch]);
    }

}, function() {
});

/**
 * @class Ext.Date
 * @mixins Ext.DateExtras
 * A set of useful static methods to deal with date.
 *
 * **Please note:** Unless you require `Ext.DateExtras`, only the {@link #now} method will be available. You **MUST**
 * require `Ext.DateExtras` to use the other methods available below.
 *
 * Usage with {@link Ext#setup}:
 *
 *     Ext.setup({
 *         requires: 'Ext.DateExtras',
 *         onReady: function() {
 *             var date = new Date();
 *             alert(Ext.Date.format(date, 'j/d/Y'));
 *         }
 *     });
 *
 * The date parsing and formatting syntax contains a subset of
 * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
 * supported will provide results equivalent to their PHP versions.
 *
 * The following is a list of all currently supported formats:
 * <pre class="">
Format  Description                                                               Example returned values
------  -----------------------------------------------------------------------   -----------------------
  d     Day of the month, 2 digits with leading zeros                             01 to 31
  D     A short textual representation of the day of the week                     Mon to Sun
  j     Day of the month without leading zeros                                    1 to 31
  l     A full textual representation of the day of the week                      Sunday to Saturday
  N     ISO-8601 numeric representation of the day of the week                    1 (for Monday) through 7 (for Sunday)
  S     English ordinal suffix for the day of the month, 2 characters             st, nd, rd or th. Works well with j
  w     Numeric representation of the day of the week                             0 (for Sunday) to 6 (for Saturday)
  z     The day of the year (starting from 0)                                     0 to 364 (365 in leap years)
  W     ISO-8601 week number of year, weeks starting on Monday                    01 to 53
  F     A full textual representation of a month, such as January or March        January to December
  m     Numeric representation of a month, with leading zeros                     01 to 12
  M     A short textual representation of a month                                 Jan to Dec
  n     Numeric representation of a month, without leading zeros                  1 to 12
  t     Number of days in the given month                                         28 to 31
  L     Whether it&#39;s a leap year                                                  1 if it is a leap year, 0 otherwise.
  o     ISO-8601 year number (identical to (Y), but if the ISO week number (W)    Examples: 1998 or 2004
        belongs to the previous or next year, that year is used instead)
  Y     A full numeric representation of a year, 4 digits                         Examples: 1999 or 2003
  y     A two digit representation of a year                                      Examples: 99 or 03
  a     Lowercase Ante meridiem and Post meridiem                                 am or pm
  A     Uppercase Ante meridiem and Post meridiem                                 AM or PM
  g     12-hour format of an hour without leading zeros                           1 to 12
  G     24-hour format of an hour without leading zeros                           0 to 23
  h     12-hour format of an hour with leading zeros                              01 to 12
  H     24-hour format of an hour with leading zeros                              00 to 23
  i     Minutes, with leading zeros                                               00 to 59
  s     Seconds, with leading zeros                                               00 to 59
  u     Decimal fraction of a second                                              Examples:
        (minimum 1 digit, arbitrary number of digits allowed)                     001 (i.e. 0.001s) or
                                                                                  100 (i.e. 0.100s) or
                                                                                  999 (i.e. 0.999s) or
                                                                                  999876543210 (i.e. 0.999876543210s)
  O     Difference to Greenwich time (GMT) in hours and minutes                   Example: +1030
  P     Difference to Greenwich time (GMT) with colon between hours and minutes   Example: -08:00
  T     Timezone abbreviation of the machine running the code                     Examples: EST, MDT, PDT ...
  Z     Timezone offset in seconds (negative if west of UTC, positive if east)    -43200 to 50400
  c     ISO 8601 date
        Notes:                                                                    Examples:
        1) If unspecified, the month / day defaults to the current month / day,   1991 or
           the time defaults to midnight, while the timezone defaults to the      1992-10 or
           browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
           and minutes. The "T" delimiter, seconds, milliseconds and timezone     1994-08-19T16:20+01:00 or
           are optional.                                                          1995-07-18T17:21:28-02:00 or
        2) The decimal fraction of a second, if specified, must contain at        1996-06-17T18:22:29.98765+03:00 or
           least 1 digit (there is no limit to the maximum number                 1997-05-16T19:23:30,12345-0400 or
           of digits allowed), and may be delimited by either a '.' or a ','      1998-04-15T20:24:31.2468Z or
        Refer to the examples on the right for the various levels of              1999-03-14T20:24:32Z or
        date-time granularity which are supported, or see                         2000-02-13T21:25:33
        http://www.w3.org/TR/NOTE-datetime for more info.                         2001-01-12 22:26:34
  U     Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)                1193432466 or -2138434463
  MS    Microsoft AJAX serialized dates                                           \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
                                                                                  \/Date(1238606590509+0800)\/
</pre>
 *
 * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
 * <pre><code>
// Sample date:
// 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'

var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
console.log(Ext.Date.format(dt, 'Y-m-d'));                          // 2007-01-10
console.log(Ext.Date.format(dt, 'F j, Y, g:i a'));                  // January 10, 2007, 3:05 pm
console.log(Ext.Date.format(dt, 'l, \\t\\he jS \\of F Y h:i:s A')); // Wednesday, the 10th of January 2007 03:05:01 PM
</code></pre>
 *
 * Here are some standard date/time patterns that you might find helpful.  They
 * are not part of the source of Ext.Date, but to use them you can simply copy this
 * block of code into any script that is included after Ext.Date and they will also become
 * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
 * <pre><code>
Ext.Date.patterns = {
    ISO8601Long:"Y-m-d H:i:s",
    ISO8601Short:"Y-m-d",
    ShortDate: "n/j/Y",
    LongDate: "l, F d, Y",
    FullDateTime: "l, F d, Y g:i:s A",
    MonthDay: "F d",
    ShortTime: "g:i A",
    LongTime: "g:i:s A",
    SortableDateTime: "Y-m-d\\TH:i:s",
    UniversalSortableDateTime: "Y-m-d H:i:sO",
    YearMonth: "F, Y"
};
</code></pre>
 *
 * Example usage:
 * <pre><code>
var dt = new Date();
console.log(Ext.Date.format(dt, Ext.Date.patterns.ShortDate));
</code></pre>
 * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
 * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
 * @singleton
 */

/*
 * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
 * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
 * They generate precompiled functions from format patterns instead of parsing and
 * processing each pattern every time a date is formatted. These functions are available
 * on every Date object.
 */

(function() {

// create private copy of Ext's Ext.util.Format.format() method
// - to remove unnecessary dependency
// - to resolve namespace conflict with MS-Ajax's implementation
function xf(format) {
    var args = Array.prototype.slice.call(arguments, 1);
    return format.replace(/\{(\d+)\}/g, function(m, i) {
        return args[i];
    });
}

/**
 * Extra methods to be mixed into Ext.Date.
 *
 * Require this class to get Ext.Date with all the methods listed below.
 *
 * Using Ext.setup:
 *
 *     Ext.setup({
 *         requires: 'Ext.DateExtras',
 *         onReady: function() {
 *             var date = new Date();
 *             alert(Ext.Date.format(date, 'j/d/Y'));
 *         }
 *     });
 *
 * Using Ext.application:
 *
 *     Ext.application({
 *         requires: 'Ext.DateExtras',
 *         launch: function() {
 *             var date = new Date();
 *             alert(Ext.Date.format(date, 'j/d/Y'));
 *         }
 *     });
 *
 * @singleton
 */
Ext.DateExtras = {
    /**
     * Returns the current timestamp
     * @return {Date} The current timestamp
     * @method
     */
    now: Date.now || function() {
        return +new Date();
    },

    /**
     * Returns the number of milliseconds between two dates
     * @param {Date} dateA The first date
     * @param {Date} dateB (optional) The second date, defaults to now
     * @return {Number} The difference in milliseconds
     */
    getElapsed: function(dateA, dateB) {
        return Math.abs(dateA - (dateB || new Date()));
    },

    /**
     * Global flag which determines if strict date parsing should be used.
     * Strict date parsing will not roll-over invalid dates, which is the
     * default behaviour of javascript Date objects.
     * (see {@link #parse} for more information)
     * Defaults to <tt>false</tt>.
     * @type Boolean
    */
    useStrict: false,

    // private
    formatCodeToRegex: function(character, currentGroup) {
        // Note: currentGroup - position in regex result array (see notes for Ext.Date.parseCodes below)
        var p = utilDate.parseCodes[character];

        if (p) {
          p = typeof p == 'function'? p() : p;
          utilDate.parseCodes[character] = p; // reassign function result to prevent repeated execution
        }

        return p ? Ext.applyIf({
          c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
        }, p) : {
            g: 0,
            c: null,
            s: Ext.String.escapeRegex(character) // treat unrecognised characters as literals
        };
    },

    /**
     * <p>An object hash in which each property is a date parsing function. The property name is the
     * format string which that function parses.</p>
     * <p>This object is automatically populated with date parsing functions as
     * date formats are requested for Ext standard formatting strings.</p>
     * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
     * may be used as a format string to {@link #parse}.<p>
     * <p>Example:</p><pre><code>
Ext.Date.parseFunctions['x-date-format'] = myDateParser;
</code></pre>
     * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
     * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
     * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
     * (i.e. prevent javascript Date "rollover") (The default must be false).
     * Invalid date strings should return null when parsed.</div></li>
     * </ul></div></p>
     * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
     * formatting function must be placed into the {@link #formatFunctions} property.
     * @property parseFunctions
     * @type Object
     */
    parseFunctions: {
        "MS": function(input, strict) {
            // note: the timezone offset is ignored since the MS Ajax server sends
            // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
            var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
            var r = (input || '').match(re);
            return r? new Date(((r[1] || '') + r[2]) * 1) : null;
        }
    },
    parseRegexes: [],

    /**
     * <p>An object hash in which each property is a date formatting function. The property name is the
     * format string which corresponds to the produced formatted date string.</p>
     * <p>This object is automatically populated with date formatting functions as
     * date formats are requested for Ext standard formatting strings.</p>
     * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
     * may be used as a format string to {@link #format}. Example:</p><pre><code>
Ext.Date.formatFunctions['x-date-format'] = myDateFormatter;
</code></pre>
     * <p>A formatting function should return a string representation of the passed Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
     * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
     * </ul></div></p>
     * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
     * parsing function must be placed into the {@link #parseFunctions} property.
     * @property formatFunctions
     * @type Object
     */
    formatFunctions: {
        "MS": function() {
            // UTC milliseconds since Unix epoch (MS-AJAX serialized date format (MRSF))
            return '\\/Date(' + this.getTime() + ')\\/';
        }
    },

    y2kYear : 50,

    /**
     * Date interval constant
     * @type String
     */
    MILLI : "ms",

    /**
     * Date interval constant
     * @type String
     */
    SECOND : "s",

    /**
     * Date interval constant
     * @type String
     */
    MINUTE : "mi",

    /** Date interval constant
     * @type String
     */
    HOUR : "h",

    /**
     * Date interval constant
     * @type String
     */
    DAY : "d",

    /**
     * Date interval constant
     * @type String
     */
    MONTH : "mo",

    /**
     * Date interval constant
     * @type String
     */
    YEAR : "y",

    /**
     * <p>An object hash containing default date values used during date parsing.</p>
     * <p>The following properties are available:<div class="mdetail-params"><ul>
     * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
     * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
     * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
     * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
     * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
     * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
     * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
     * </ul></div></p>
     * <p>Override these properties to customize the default date values used by the {@link #parse} method.</p>
     * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
     * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
     * It is the responsiblity of the developer to account for this.</b></p>
     * Example Usage:
     * <pre><code>
// set default day value to the first day of the month
Ext.Date.defaults.d = 1;

// parse a February date string containing only year and month values.
// setting the default day value to 1 prevents weird date rollover issues
// when attempting to parse the following date string on, for example, March 31st 2009.
Ext.Date.parse('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
</code></pre>
     * @property defaults
     * @type Object
     */
    defaults: {},

    /**
     * An array of textual day names.
     * Override these values for international dates.
     * Example:
     * <pre><code>
Ext.Date.dayNames = [
    'SundayInYourLang',
    'MondayInYourLang',
    ...
];
</code></pre>
     * @type Array
     */
    dayNames : [
        "Sunday",
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "Saturday"
    ],

    /**
     * An array of textual month names.
     * Override these values for international dates.
     * Example:
     * <pre><code>
Ext.Date.monthNames = [
    'JanInYourLang',
    'FebInYourLang',
    ...
];
</code></pre>
     * @type Array
     */
    monthNames : [
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December"
    ],

    /**
     * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
     * Override these values for international dates.
     * Example:
     * <pre><code>
Ext.Date.monthNumbers = {
    'ShortJanNameInYourLang':0,
    'ShortFebNameInYourLang':1,
    ...
};
</code></pre>
     * @type Object
     */
    monthNumbers : {
        Jan:0,
        Feb:1,
        Mar:2,
        Apr:3,
        May:4,
        Jun:5,
        Jul:6,
        Aug:7,
        Sep:8,
        Oct:9,
        Nov:10,
        Dec:11
    },
    /**
     * <p>The date format string that the {@link Ext.util.Format#date} function uses.
     * See {@link Ext.Date} for details.</p>
     * <p>This defaults to <code>m/d/Y</code>, but may be overridden in a locale file.</p>
     * @property defaultFormat
     * @type String
     */
    defaultFormat : "m/d/Y",
    /**
     * Get the short month name for the given month number.
     * Override this function for international dates.
     * @param {Number} month A zero-based javascript month number.
     * @return {String} The short month name.
     */
    getShortMonthName : function(month) {
        return utilDate.monthNames[month].substring(0, 3);
    },

    /**
     * Get the short day name for the given day number.
     * Override this function for international dates.
     * @param {Number} day A zero-based javascript day number.
     * @return {String} The short day name.
     */
    getShortDayName : function(day) {
        return utilDate.dayNames[day].substring(0, 3);
    },

    /**
     * Get the zero-based javascript month number for the given short/full month name.
     * Override this function for international dates.
     * @param {String} name The short/full month name.
     * @return {Number} The zero-based javascript month number.
     */
    getMonthNumber : function(name) {
        // handle camel casing for english month names (since the keys for the Ext.Date.monthNumbers hash are case sensitive)
        return utilDate.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
    },

    /**
     * The base format-code to formatting-function hashmap used by the {@link #format} method.
     * Formatting functions are strings (or functions which return strings) which
     * will return the appropriate value when evaluated in the context of the Date object
     * from which the {@link #format} method is called.
     * Add to / override these mappings for custom date formatting.
     * Note: Ext.Date.format() treats characters as literals if an appropriate mapping cannot be found.
     * Example:
     * <pre><code>
Ext.Date.formatCodes.x = "Ext.util.Format.leftPad(this.getDate(), 2, '0')";
console.log(Ext.Date.format(new Date(), 'X'); // returns the current day of the month
</code></pre>
     * @type Object
     */
    formatCodes : {
        d: "Ext.String.leftPad(this.getDate(), 2, '0')",
        D: "Ext.Date.getShortDayName(this.getDay())", // get localised short day name
        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())", // get localised short month name
        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() { // ISO-8601 -- GMT format
            for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
                var e = c.charAt(i);
                code.push(e == "T" ? "'T'" : utilDate.getFormatCode(e)); // treat T as a character literal
            }
            return code.join(" + ");
        },
        /*
        c: function() { // ISO-8601 -- UTC format
            return [
              "this.getUTCFullYear()", "'-'",
              "Ext.util.Format.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
              "Ext.util.Format.leftPad(this.getUTCDate(), 2, '0')",
              "'T'",
              "Ext.util.Format.leftPad(this.getUTCHours(), 2, '0')", "':'",
              "Ext.util.Format.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
              "Ext.util.Format.leftPad(this.getUTCSeconds(), 2, '0')",
              "'Z'"
            ].join(" + ");
        },
        */

        U: "Math.round(this.getTime() / 1000)"
    },

    /**
     * Checks if the passed Date parameters will cause a javascript Date "rollover".
     * @param {Number} year 4-digit year
     * @param {Number} month 1-based month-of-year
     * @param {Number} day Day of month
     * @param {Number} hour (optional) Hour
     * @param {Number} minute (optional) Minute
     * @param {Number} second (optional) Second
     * @param {Number} millisecond (optional) Millisecond
     * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
     */
    isValid : function(y, m, d, h, i, s, ms) {
        // setup defaults
        h = h || 0;
        i = i || 0;
        s = s || 0;
        ms = ms || 0;

        // Special handling for year < 100
        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();
    },

    /**
     * Parses the passed string using the specified date format.
     * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
     * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
     * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
     * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
     * Keep in mind that the input date string must precisely match the specified format string
     * in order for the parse operation to be successful (failed parse operations return a null value).
     * <p>Example:</p><pre><code>
//dt = Fri May 25 2007 (current date)
var dt = new Date();

//dt = Thu May 25 2006 (today&#39;s month/day in 2006)
dt = Ext.Date.parse("2006", "Y");

//dt = Sun Jan 15 2006 (all date parts specified)
dt = Ext.Date.parse("2006-01-15", "Y-m-d");

//dt = Sun Jan 15 2006 15:20:01
dt = Ext.Date.parse("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");

// attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
dt = Ext.Date.parse("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
</code></pre>
     * @param {String} input The raw date string.
     * @param {String} format The expected date string format.
     * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
                        (defaults to false). Invalid date strings will return null when parsed.
     * @return {Date} The parsed Date.
     */
    parse : function(input, format, strict) {
        var p = utilDate.parseFunctions;
        if (p[format] == null) {
            utilDate.createParser(format);
        }
        return p[format](input, Ext.isDefined(strict) ? strict : utilDate.useStrict);
    },

    // Backwards compat
    parseDate: function(input, format, strict){
        return utilDate.parse(input, format, strict);
    },


    // private
    getFormatCode : function(character) {
        var f = utilDate.formatCodes[character];

        if (f) {
          f = typeof f == 'function'? f() : f;
          utilDate.formatCodes[character] = f; // reassign function result to prevent repeated execution
        }

        // note: unknown characters are treated as literals
        return f || ("'" + Ext.String.escape(character) + "'");
    },

    // private
    createFormat : function(format) {
        var code = [],
            special = false,
            ch = '';

        for (var 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('+'));
    },

    // private
    createParser : (function() {
        var code = [
            "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
                "def = Ext.Date.defaults,",
                "results = String(input).match(Ext.Date.parseRegexes[{0}]);", // either null, or an array of matched strings

            "if(results){",
                "{1}",

                "if(u != null){", // i.e. unix time is defined
                    "v = new Date(u * 1000);", // give top priority to UNIX time
                "}else{",
                    // create Date object representing midnight of the current day;
                    // this will provide us with our date defaults
                    // (note: clearTime() handles Daylight Saving Time automatically)
                    "dt = Ext.Date.clearTime(new Date);",

                    // date calculations (note: these calculations create a dependency on Ext.Number.from())
                    "y = Ext.Number.from(y, Ext.Number.from(def.y, dt.getFullYear()));",
                    "m = Ext.Number.from(m, Ext.Number.from(def.m - 1, dt.getMonth()));",
                    "d = Ext.Number.from(d, Ext.Number.from(def.d, dt.getDate()));",

                    // time calculations (note: these calculations create a dependency on Ext.Number.from())
                    "h  = Ext.Number.from(h, Ext.Number.from(def.h, dt.getHours()));",
                    "i  = Ext.Number.from(i, Ext.Number.from(def.i, dt.getMinutes()));",
                    "s  = Ext.Number.from(s, Ext.Number.from(def.s, dt.getSeconds()));",
                    "ms = Ext.Number.from(ms, Ext.Number.from(def.ms, dt.getMilliseconds()));",

                    "if(z >= 0 && y >= 0){",
                        // both the year and zero-based day of year are defined and >= 0.
                        // these 2 values alone provide sufficient info to create a full date object

                        // create Date object representing January 1st for the given year
                        // handle years < 100 appropriately
                        "v = Ext.Date.add(new Date(y < 100 ? 100 : y, 0, 1, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",

                        // then add day of year, checking for Date "rollover" if necessary
                        "v = !strict? v : (strict === true && (z <= 364 || (Ext.Date.isLeapYear(v) && z <= 365))? Ext.Date.add(v, Ext.Date.DAY, z) : null);",
                    "}else if(strict === true && !Ext.Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
                        "v = null;", // invalid date, so return null
                    "}else{",
                        // plain old Date object
                        // handle years < 100 properly
                        "v = Ext.Date.add(new Date(y < 100 ? 100 : y, m, d, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
                    "}",
                "}",
            "}",

            "if(v){",
                // favour UTC offset over GMT offset
                "if(zz != null){",
                    // reset to UTC, then add offset
                    "v = Ext.Date.add(v, Ext.Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
                "}else if(o){",
                    // reset to GMT, then add offset
                    "v = Ext.Date.add(v, Ext.Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
                "}",
            "}",

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

        return function(format) {
            var regexNum = utilDate.parseRegexes.length,
                currentGroup = 1,
                calc = [],
                regex = [],
                special = false,
                ch = "";

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

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

    // private
    parseCodes : {
        /*
         * Notes:
         * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
         * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
         * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
         */
        d: {
            g:1,
            c:"d = parseInt(results[{0}], 10);\n",
            s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
        },
        j: {
            g:1,
            c:"d = parseInt(results[{0}], 10);\n",
            s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
        },
        D: function() {
            for (var a = [], i = 0; i < 7; a.push(utilDate.getShortDayName(i)), ++i); // get localised short day names
            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]" // ISO-8601 day number (1 (monday) - 7 (sunday))
        },
        S: {
            g:0,
            c:null,
            s:"(?:st|nd|rd|th)"
        },
        w: {
            g:0,
            c:null,
            s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
        },
        z: {
            g:1,
            c:"z = parseInt(results[{0}], 10);\n",
            s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
        },
        W: {
            g:0,
            c:null,
            s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
        },
        F: function() {
            return {
                g:1,
                c:"m = parseInt(Ext.Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
                s:"(" + utilDate.monthNames.join("|") + ")"
            };
        },
        M: function() {
            for (var a = [], i = 0; i < 12; a.push(utilDate.getShortMonthName(i)), ++i); // get localised short month names
            return Ext.applyIf({
                s:"(" + a.join("|") + ")"
            }, utilDate.formatCodeToRegex("F"));
        },
        m: {
            g:1,
            c:"m = parseInt(results[{0}], 10) - 1;\n",
            s:"(\\d{2})" // month number with leading zeros (01 - 12)
        },
        n: {
            g:1,
            c:"m = parseInt(results[{0}], 10) - 1;\n",
            s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
        },
        t: {
            g:0,
            c:null,
            s:"(?:\\d{2})" // no. of days in the month (28 - 31)
        },
        L: {
            g:0,
            c:null,
            s:"(?:1|0)"
        },
        o: function() {
            return utilDate.formatCodeToRegex("Y");
        },
        Y: {
            g:1,
            c:"y = parseInt(results[{0}], 10);\n",
            s:"(\\d{4})" // 4-digit year
        },
        y: {
            g:1,
            c:"var ty = parseInt(results[{0}], 10);\n"
                + "y = ty > Ext.Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
            s:"(\\d{1,2})"
        },
        /*
         * In the am/pm parsing routines, we allow both upper and lower case
         * even though it doesn't exactly match the spec. It gives much more flexibility
         * in being able to specify case insensitive regexes.
         */
        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)"
        },
        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)"
        },
        g: function() {
            return utilDate.formatCodeToRegex("G");
        },
        G: {
            g:1,
            c:"h = parseInt(results[{0}], 10);\n",
            s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
        },
        h: function() {
            return utilDate.formatCodeToRegex("H");
        },
        H: {
            g:1,
            c:"h = parseInt(results[{0}], 10);\n",
            s:"(\\d{2})" //  24-hr format of an hour with leading zeroes (00 - 23)
        },
        i: {
            g:1,
            c:"i = parseInt(results[{0}], 10);\n",
            s:"(\\d{2})" // minutes with leading zeros (00 - 59)
        },
        s: {
            g:1,
            c:"s = parseInt(results[{0}], 10);\n",
            s:"(\\d{2})" // seconds with leading zeros (00 - 59)
        },
        u: {
            g:1,
            c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
            s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
        },
        O: {
            g:1,
            c:[
                "o = results[{0}];",
                "var sn = o.substring(0,1),", // get + / - sign
                    "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
                    "mn = o.substring(3,5) % 60;", // get minutes
                "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" // -12hrs <= GMT offset <= 14hrs
            ].join("\n"),
            s: "([+\-]\\d{4})" // GMT offset in hrs and mins
        },
        P: {
            g:1,
            c:[
                "o = results[{0}];",
                "var sn = o.substring(0,1),", // get + / - sign
                    "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
                    "mn = o.substring(4,6) % 60;", // get minutes
                "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" // -12hrs <= GMT offset <= 14hrs
            ].join("\n"),
            s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
        },
        T: {
            g:0,
            c:null,
            s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
        },
        Z: {
            g:1,
            c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
                  + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
            s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
        },
        c: function() {
            var calc = [],
                arr = [
                    utilDate.formatCodeToRegex("Y", 1), // year
                    utilDate.formatCodeToRegex("m", 2), // month
                    utilDate.formatCodeToRegex("d", 3), // day
                    utilDate.formatCodeToRegex("h", 4), // hour
                    utilDate.formatCodeToRegex("i", 5), // minute
                    utilDate.formatCodeToRegex("s", 6), // second
                    {c:"ms = results[7] || '0'; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n"}, // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
                    {c:[ // allow either "Z" (i.e. UTC) or "-0530" or "+08:00" (i.e. UTC offset) timezone delimiters. assumes local timezone if no timezone is specified
                        "if(results[8]) {", // timezone specified
                            "if(results[8] == 'Z'){",
                                "zz = 0;", // UTC
                            "}else if (results[8].indexOf(':') > -1){",
                                utilDate.formatCodeToRegex("P", 8).c, // timezone offset with colon separator
                            "}else{",
                                utilDate.formatCodeToRegex("O", 8).c, // timezone offset without colon separator
                            "}",
                        "}"
                    ].join('\n')}
                ];

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

            return {
                g:1,
                c:calc.join(""),
                s:[
                    arr[0].s, // year (required)
                    "(?:", "-", arr[1].s, // month (optional)
                        "(?:", "-", arr[2].s, // day (optional)
                            "(?:",
                                "(?:T| )?", // time delimiter -- either a "T" or a single blank space
                                arr[3].s, ":", arr[4].s,  // hour AND minute, delimited by a single colon (optional). MUST be preceded by either a "T" or a single blank space
                                "(?::", arr[5].s, ")?", // seconds (optional)
                                "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
                                "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
                            ")?",
                        ")?",
                    ")?"
                ].join("")
            };
        },
        U: {
            g:1,
            c:"u = parseInt(results[{0}], 10);\n",
            s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
        }
    },

    //Old Ext.Date prototype methods.
    // private
    dateFormat: function(date, format) {
        return utilDate.format(date, format);
    },

    /**
     * Formats a date given the supplied format string.
     * @param {Date} date The date to format
     * @param {String} format The format string
     * @return {String} The formatted date
     */
    format: function(date, format) {
        if (utilDate.formatFunctions[format] == null) {
            utilDate.createFormat(format);
        }
        var result = utilDate.formatFunctions[format].call(date);
        return result + '';
    },

    /**
     * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
     *
     * Note: The date string returned by the javascript Date object's toString() method varies
     * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
     * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
     * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
     * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
     * from the GMT offset portion of the date string.
     * @param {Date} date The date
     * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
     */
    getTimezone : function(date) {
        // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
        //
        // Opera  : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
        // Safari : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone (same as FF)
        // FF     : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
        // IE     : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
        // IE     : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
        //
        // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
        // step 1: (?:\((.*)\) -- find timezone in parentheses
        // step 2: ([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?) -- if nothing was found in step 1, find timezone from timezone offset portion of date string
        // step 3: remove all non uppercase characters found in step 1 and 2
        return date.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
    },

    /**
     * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
     * @param {Date} date The date
     * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
     * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
     */
    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");
    },

    /**
     * Get the numeric day number of the year, adjusted for leap year.
     * @param {Date} date The date
     * @return {Number} 0 to 364 (365 in leap years).
     */
    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;
    },

    /**
     * Get the numeric ISO-8601 week number of the year.
     * (equivalent to the format specifier 'W', but without a leading zero).
     * @param {Date} date The date
     * @return {Number} 1 to 53
     * @method
     */
    getWeekOfYear : (function() {
        // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
        var ms1d = 864e5, // milliseconds in a day
            ms7d = 7 * ms1d; // milliseconds in a week

        return function(date) { // return a closure so constants get calculated only once
            var DC3 = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate() + 3) / ms1d, // an Absolute Day Number
                AWN = Math.floor(DC3 / 7), // an Absolute Week Number
                Wyr = new Date(AWN * ms7d).getUTCFullYear();

            return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
        };
    })(),

    /**
     * Checks if the current date falls within a leap year.
     * @param {Date} date The date
     * @return {Boolean} True if the current date falls within a leap year, false otherwise.
     */
    isLeapYear : function(date) {
        var year = date.getFullYear();
        return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
    },

    /**
     * Get the first day of the current month, adjusted for leap year.  The returned value
     * is the numeric day index within the week (0-6) which can be used in conjunction with
     * the {@link #monthNames} array to retrieve the textual day name.
     * Example:
     * <pre><code>
var dt = new Date('1/10/2007'),
    firstDay = Ext.Date.getFirstDayOfMonth(dt);
console.log(Ext.Date.dayNames[firstDay]); //output: 'Monday'
     * </code></pre>
     * @param {Date} date The date
     * @return {Number} The day number (0-6).
     */
    getFirstDayOfMonth : function(date) {
        var day = (date.getDay() - (date.getDate() - 1)) % 7;
        return (day < 0) ? (day + 7) : day;
    },

    /**
     * Get the last day of the current month, adjusted for leap year.  The returned value
     * is the numeric day index within the week (0-6) which can be used in conjunction with
     * the {@link #monthNames} array to retrieve the textual day name.
     * Example:
     * <pre><code>
var dt = new Date('1/10/2007'),
    lastDay = Ext.Date.getLastDayOfMonth(dt);
console.log(Ext.Date.dayNames[lastDay]); //output: 'Wednesday'
     * </code></pre>
     * @param {Date} date The date
     * @return {Number} The day number (0-6).
     */
    getLastDayOfMonth : function(date) {
        return utilDate.getLastDateOfMonth(date).getDay();
    },


    /**
     * Get the date of the first day of the month in which this date resides.
     * @param {Date} date The date
     * @return {Date}
     */
    getFirstDateOfMonth : function(date) {
        return new Date(date.getFullYear(), date.getMonth(), 1);
    },

    /**
     * Get the date of the last day of the month in which this date resides.
     * @param {Date} date The date
     * @return {Date}
     */
    getLastDateOfMonth : function(date) {
        return new Date(date.getFullYear(), date.getMonth(), utilDate.getDaysInMonth(date));
    },

    /**
     * Get the number of days in the current month, adjusted for leap year.
     * @param {Date} date The date
     * @return {Number} The number of days in the month.
     * @method
     */
    getDaysInMonth: (function() {
        var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

        return function(date) { // return a closure for efficiency
            var m = date.getMonth();

            return m == 1 && utilDate.isLeapYear(date) ? 29 : daysInMonth[m];
        };
    })(),

    /**
     * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
     * @param {Date} date The date
     * @return {String} 'st, 'nd', 'rd' or 'th'.
     */
    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";
        }
    },

    /**
     * Creates and returns a new Date instance with the exact same date value as the called instance.
     * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
     * variable will also be changed.  When the intention is to create a new variable that will not
     * modify the original instance, you should create a clone.
     *
     * Example of correctly cloning a date:
     * <pre><code>
//wrong way:
var orig = new Date('10/1/2006');
var copy = orig;
copy.setDate(5);
console.log(orig);  //returns 'Thu Oct 05 2006'!

//correct way:
var orig = new Date('10/1/2006'),
    copy = Ext.Date.clone(orig);
copy.setDate(5);
console.log(orig);  //returns 'Thu Oct 01 2006'
     * </code></pre>
     * @param {Date} date The date
     * @return {Date} The new Date instance.
     */
    clone : function(date) {
        return new Date(date.getTime());
    },

    /**
     * Checks if the current date is affected by Daylight Saving Time (DST).
     * @param {Date} date The date
     * @return {Boolean} True if the current date is affected by DST.
     */
    isDST : function(date) {
        // adapted from http://sencha.com/forum/showthread.php?p=247172#post247172
        // courtesy of @geoffrey.mcgill
        return new Date(date.getFullYear(), 0, 1).getTimezoneOffset() != date.getTimezoneOffset();
    },

    /**
     * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
     * automatically adjusting for Daylight Saving Time (DST) where applicable.
     * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
     * @param {Date} date The date
     * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
     * @return {Date} this or the clone.
     */
    clearTime : function(date, clone) {
        if (clone) {
            return Ext.Date.clearTime(Ext.Date.clone(date));
        }

        // get current date before clearing time
        var d = date.getDate();

        // clear time
        date.setHours(0);
        date.setMinutes(0);
        date.setSeconds(0);
        date.setMilliseconds(0);

        if (date.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
            // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
            // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule

            // increment hour until cloned date == current date
            for (var 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;
    },

    /**
     * Provides a convenient method for performing basic date arithmetic. This method
     * does not modify the Date instance being called - it creates and returns
     * a new Date instance containing the resulting date value.
     *
     * Examples:
     * <pre><code>
// Basic usage:
var dt = Ext.Date.add(new Date('10/29/2006'), Ext.Date.DAY, 5);
console.log(dt); //returns 'Fri Nov 03 2006 00:00:00'

// Negative values will be subtracted:
var dt2 = Ext.Date.add(new Date('10/1/2006'), Ext.Date.DAY, -5);
console.log(dt2); //returns 'Tue Sep 26 2006 00:00:00'

     * </code></pre>
     *
     * @param {Date} date The date to modify
     * @param {String} interval A valid date interval enum value.
     * @param {Number} value The amount to add to the current date.
     * @return {Date} The new Date instance.
     */
    add : function(date, interval, value) {
        var d = Ext.Date.clone(date),
            Date = Ext.Date;
        if (!interval || value === 0) return d;

        switch(interval.toLowerCase()) {
            case Ext.Date.MILLI:
                d.setMilliseconds(d.getMilliseconds() + value);
                break;
            case Ext.Date.SECOND:
                d.setSeconds(d.getSeconds() + value);
                break;
            case Ext.Date.MINUTE:
                d.setMinutes(d.getMinutes() + value);
                break;
            case Ext.Date.HOUR:
                d.setHours(d.getHours() + value);
                break;
            case Ext.Date.DAY:
                d.setDate(d.getDate() + value);
                break;
            case Ext.Date.MONTH:
                var day = date.getDate();
                if (day > 28) {
                    day = Math.min(day, Ext.Date.getLastDateOfMonth(Ext.Date.add(Ext.Date.getFirstDateOfMonth(date), 'mo', value)).getDate());
                }
                d.setDate(day);
                d.setMonth(date.getMonth() + value);
                break;
            case Ext.Date.YEAR:
                d.setFullYear(date.getFullYear() + value);
                break;
        }
        return d;
    },

    /**
     * Checks if a date falls on or between the given start and end dates.
     * @param {Date} date The date to check
     * @param {Date} start Start date
     * @param {Date} end End date
     * @return {Boolean} true if this date falls on or between the given start and end dates.
     */
    between : function(date, start, end) {
        var t = date.getTime();
        return start.getTime() <= t && t <= end.getTime();
    }
};

var utilDate = Ext.DateExtras;

Ext.apply(Ext.Date, utilDate);


})();



/**
 * @private
 */
Ext.define('Ext.fx.Easing', {
    requires: ['Ext.fx.easing.Linear'],

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

/**
 * @class Ext.fx.easing.BoundMomentum
 *
 * This easing is typically used for {@link Ext.scroll.Scroller}. It's a combination of
 * {@link Ext.fx.easing.Momentum} and {@link Ext.fx.easing.Bounce}, which emulates deceleration when the animated element
 * is still within its boundary, then bouncing back (snapping) when it's out-of-bound.
 */

Ext.define('Ext.fx.easing.BoundMomentum', {
    extend: 'Ext.fx.easing.Abstract',

    requires: [
        'Ext.fx.easing.Momentum',
        'Ext.fx.easing.Bounce'
    ],

    config: {
        /**
         * @cfg {Object} momentum
         * A valid config object for {@link Ext.fx.easing.Momentum}
         * @accessor
         */
        momentum: null,

        /**
         * @cfg {Object} bounce
         * A valid config object for {@link Ext.fx.easing.Bounce}
         * @accessor
         */
        bounce: null,

        minMomentumValue: 0,

        maxMomentumValue: 0,

        /**
         * @cfg {Number} minVelocity
         * The minimum velocity to end this easing
         * @accessor
         */
        minVelocity: 0.01,

        /**
         * @cfg {Number} startVelocity
         * The start velocity
         * @accessor
         */
        startVelocity: 0
    },

    applyMomentum: function(config, currentEasing) {
        return Ext.factory(config, Ext.fx.easing.Momentum, currentEasing);
    },

    applyBounce: function(config, currentEasing) {
        return Ext.factory(config, Ext.fx.easing.Bounce, currentEasing);
    },

    updateStartTime: function(startTime) {
        this.getMomentum().setStartTime(startTime);

        this.callParent(arguments);
    },

    updateStartVelocity: function(startVelocity) {
        this.getMomentum().setStartVelocity(startVelocity);
    },

    updateStartValue: function(startValue) {
        this.getMomentum().setStartValue(startValue);
    },

    reset: function() {
        this.lastValue = null;

        this.isBouncingBack = false;

        this.isOutOfBound = false;

        return this.callParent(arguments);
    },

    getValue: function() {
        var momentum = this.getMomentum(),
            bounce = this.getBounce(),
            startVelocity = momentum.getStartVelocity(),
            direction = startVelocity > 0 ? 1 : -1,
            minValue = this.getMinMomentumValue(),
            maxValue = this.getMaxMomentumValue(),
            boundedValue = (direction == 1) ? maxValue : minValue,
            lastValue = this.lastValue,
            value, velocity;

        if (startVelocity === 0) {
            return this.getStartValue();
        }

        if (!this.isOutOfBound) {
            value = momentum.getValue();
            velocity = momentum.getVelocity();

            if (Math.abs(velocity) < this.getMinVelocity()) {
                this.isEnded = true;
            }

            if (value >= minValue && value <= maxValue) {
                return value;
            }

            this.isOutOfBound = true;

            bounce.setStartTime(Ext.Date.now())
                  .setStartVelocity(velocity)
                  .setStartValue(boundedValue);
        }

        value = bounce.getValue();

        if (!this.isEnded) {
            if (!this.isBouncingBack) {
                if (lastValue !== null) {
                    if ((direction == 1 && value < lastValue) || (direction == -1 && value > lastValue)) {
                        this.isBouncingBack = true;
                    }
                }
            }
            else {
                if (Math.round(value) == boundedValue) {
                    this.isEnded = true;
                }
            }
        }

        this.lastValue = value;

        return value;
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.easing.EaseIn', {
    extend: 'Ext.fx.easing.Linear',

    alias: 'easing.ease-in',

    config: {
        exponent: 4,
        duration: 1500
    },

    getValue: function() {
        var deltaTime = Ext.Date.now() - this.getStartTime(),
            duration = this.getDuration(),
            startValue = this.getStartValue(),
            endValue = this.getEndValue(),
            distance = this.distance,
            theta = deltaTime / duration,
            thetaEnd = Math.pow(theta, this.getExponent()),
            currentValue = startValue + (thetaEnd * distance);

        if (deltaTime >= duration) {
            this.isEnded = true;
            return endValue;
        }

        return currentValue;
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.easing.EaseOut', {
    extend: 'Ext.fx.easing.Linear',

    alias: 'easing.ease-out',

    config: {
        exponent: 4,
        duration: 1500
    },

    getValue: function() {
        var deltaTime = Ext.Date.now() - this.getStartTime(),
            duration = this.getDuration(),
            startValue = this.getStartValue(),
            endValue = this.getEndValue(),
            distance = this.distance,
            theta = deltaTime / duration,
            thetaC = 1 - theta,
            thetaEnd = 1 - Math.pow(thetaC, this.getExponent()),
            currentValue = startValue + (thetaEnd * distance);

        if (deltaTime >= duration) {
            this.isEnded = true;
            return endValue;
        }

        return currentValue;
    }
});

Ext.define('Ext.log.formatter.Default', {
    extend: 'Ext.log.formatter.Formatter',

    config: {
        messageFormat: "[{priorityName}][{callerDisplayName}] {message}"
    },

    format: function(event) {
        var event = Ext.merge({}, event, {
                priorityName: event.priorityName.toUpperCase()
            });

        return this.callParent([event]);
    }
});

Ext.define('Ext.log.formatter.Identity', {
    extend: 'Ext.log.formatter.Default',

    config: {
        messageFormat: "[{osIdentity}][{browserIdentity}][{timestamp}][{priorityName}][{callerDisplayName}] {message}"
    },

    format: function(event) {
        event.timestamp = Ext.Date.toString();
        event.browserIdentity = Ext.browser.name + ' ' + Ext.browser.version;
        event.osIdentity = Ext.os.name + ' ' + Ext.os.version;

        return this.callParent(arguments);
    }
});

Ext.define('Ext.log.writer.Console', {

    extend: 'Ext.log.writer.Writer',

    config: {
        throwOnErrors: true,
        throwOnWarnings: false
    },

    doWrite: function(event) {
        var message = event.message,
            priority = event.priorityName,
            consoleMethod;

        if (priority === 'error' && this.getThrowOnErrors()) {
            throw new Error(message);
        }

        if (typeof console !== 'undefined') {
            consoleMethod = priority;

            if (consoleMethod === 'deprecate') {
                consoleMethod = 'warn';
            }

            if (consoleMethod === 'warn' && this.getThrowOnWarnings()) {
                throw new Error(message);
            }

            if (!(consoleMethod in console)) {
                consoleMethod = 'log';
            }

            console[consoleMethod](message);
        }
    }
});

Ext.define('Ext.log.writer.DocumentTitle', {

    extend: 'Ext.log.writer.Writer',

    doWrite: function(event) {
        var message = event.message;

        document.title = message;
    }
});

/**
 * @private
 */
Ext.define('Ext.mixin.Filterable', {
    extend: 'Ext.mixin.Mixin',

    requires: [
        'Ext.util.Filter'
    ],

    mixinConfig: {
        id: 'filterable'
    },

    config: {
        /**
         * @cfg {Array} filters
         * An array with filters. A filter can be an instance of Ext.util.Filter,
         * an object representing an Ext.util.Filter configuration, or a filter function.
         */
        filters: null,

        /**
         * @cfg {String} filterRoot
         * The root inside each item in which the properties exist that we want to filter on.
         * This is useful for filtering records in which the data exists inside a 'data' property.
         */
        filterRoot: null
    },

    /**
     * @property {Boolean} dirtyFilterFn
     * A flag indicating wether the currently cashed filter function is still valid. Read-only.
     */
    dirtyFilterFn: false,

    /**
     * @property currentSortFn
     * This is the cached sorting function which is a generated function that calls all the
     * configured sorters in the correct order. This is a read-only property.
     */
    filterFn: null,

    /**
     * @property {Boolean} filtered
     * A read-only flag indicating if this object is filtered
     */
    filtered: false,

    applyFilters: function(filters, collection) {
        if (!collection) {
            collection = this.createFiltersCollection();
        }

        collection.clear();

        this.filtered = false;
        this.dirtyFilterFn = true;

        if (filters) {
            this.addFilters(filters);
        }

        return collection;
    },

    createFiltersCollection: function() {
        this._filters = Ext.create('Ext.util.Collection', function(filter) {
            return filter.getId();
        });
        return this._filters;
    },

    /**
     * This method adds a filter.
     * @param {Ext.util.Sorter/Function/Object} filter Can be an instance of Ext.util.Filter,
     * an object representing an Ext.util.Filter configuration, or a filter function.
     */
    addFilter: function(filter) {
        this.addFilters([filter]);
    },

    /**
     * This method adds all the filters in a passed array.
     * @param {Array} filters An array with filters. A filter can be an instance of Ext.util.Filter,
     * an object representing an Ext.util.Filter configuration, or a filter function.
     */
    addFilters: function(filters) {
        var currentFilters = this.getFilters();
        return this.insertFilters(currentFilters ? currentFilters.length : 0, filters);
    },

    /**
     * This method adds a filter at a given index.
     * @param {Number} index The index at which to insert the filter.
     * @param {Ext.util.Sorter/Function/Object} filter Can be an instance of Ext.util.Filter,
     * an object representing an Ext.util.Filter configuration, or a filter function.
     */
    insertFilter: function(index, filter) {
        return this.insertFilters(index, [filter]);
    },

    /**
     * This method inserts all the filters in the passed array at the given index.
     * @param {Number} index The index at which to insert the filters.
     * @param {Array} filters Each filter can be an instance of Ext.util.Filter,
     * an object representing an Ext.util.Filter configuration, or a filter function.
     */
    insertFilters: function(index, filters) {
        // We begin by making sure we are dealing with an array of sorters
        if (!Ext.isArray(filters)) {
            filters = [filters];
        }

        var ln = filters.length,
            filterRoot = this.getFilterRoot(),
            currentFilters = this.getFilters(),
            newFilters = [],
            filterConfig, i, filter;

        if (!currentFilters) {
            currentFilters = this.createFiltersCollection();
        }

        // We first have to convert every sorter into a proper Sorter instance
        for (i = 0; i < ln; i++) {
            filter = filters[i];
            filterConfig = {
                root: filterRoot
            };

            if (Ext.isFunction(filter)) {
                filterConfig.filterFn = filter;
            }
            // If we are dealing with an object, we assume its a Sorter configuration. In this case
            // we create an instance of Sorter passing this configuration.
            else if (Ext.isObject(filter)) {
                if (!filter.isFilter) {
                    if (filter.fn) {
                        filter.filterFn = filter.fn;
                        delete filter.fn;
                    }

                    filterConfig = Ext.apply(filterConfig, filter);
                }
                else {
                    newFilters.push(filter);
                    if (!filter.getRoot()) {
                        filter.setRoot(filterRoot);
                    }
                    continue;
                }
            }
            // Finally we get to the point where it has to be invalid
            else {
                Ext.Logger.warn('Invalid filter specified:', filter);
            }

            // If a sorter config was created, make it an instance
            filter = Ext.create('Ext.util.Filter', filterConfig);
            newFilters.push(filter);
        }

        // Now lets add the newly created sorters.
        for (i = 0, ln = newFilters.length; i < ln; i++) {
            currentFilters.insert(index + i, newFilters[i]);
        }

        this.dirtyFilterFn = true;

        if (currentFilters.length) {
            this.filtered = true;
        }

        return currentFilters;
    },

    /**
     * This method removes all the filters in a passed array.
     * @param {Array} filters Each value in the array can be a string (property name),
     * function (sorterFn), an object containing a property and value keys or
     * {@link Ext.util.Sorter Sorter} instance.
     */
    removeFilters: function(filters) {
        // We begin by making sure we are dealing with an array of sorters
        if (!Ext.isArray(filters)) {
            filters = [filters];
        }

        var ln = filters.length,
            currentFilters = this.getFilters(),
            i, filter;

        for (i = 0; i < ln; i++) {
            filter = filters[i];

            if (typeof filter === 'string') {
                currentFilters.each(function(item) {
                    if (item.getProperty() === filter) {
                        currentFilters.remove(item);
                    }
                });
            }
            else if (typeof filter === 'function') {
                currentFilters.each(function(item) {
                    if (item.getFilterFn() === filter) {
                        currentFilters.remove(item);
                    }
                });
            }
            else {
                if (filter.isFilter) {
                    currentFilters.remove(filter);
                }
                else if (filter.property !== undefined && filter.value !== undefined) {
                    currentFilters.each(function(item) {
                        if (item.getProperty() === filter.property && item.getValue() === filter.value) {
                            currentFilters.remove(item);
                        }
                    });
                }
            }
        }

        if (!currentFilters.length) {
            this.filtered = false;
        }
    },

    /**
     * This updates the cached sortFn based on the current sorters.
     * @return {Function} sortFn The generated sort function.
     * @private
     */
    updateFilterFn: function() {
        var filters = this.getFilters().items;

        this.filterFn = function(item) {
            var isMatch = true,
                length = filters.length,
                i;

            for (i = 0; i < length; i++) {
                var filter = filters[i],
                    fn     = filter.getFilterFn(),
                    scope  = filter.getScope() || this;

                isMatch = isMatch && fn.call(scope, item);
            }

            return isMatch;
        };

        this.dirtyFilterFn = false;
        return this.filterFn;
    },

    /**
     * This method will sort an array based on the currently configured {@link Ext.data.Store#sorters sorters}.
     * @param {Array} data The array you want to have sorted
     * @return {Array} The array you passed after it is sorted
     */
    filter: function(data) {
        return this.getFilters().length ? Ext.Array.filter(data, this.getFilterFn()) : data;
    },

    isFiltered: function(item) {
        return this.getFilters().length ? !this.getFilterFn()(item) : false;
    },

    /**
     * Returns an up to date sort function.
     * @return {Function} sortFn The sort function.
     */
    getFilterFn: function() {
        if (this.dirtyFilterFn) {
            return this.updateFilterFn();
        }
        return this.filterFn;
    }
});

/**
 * Mixin that provides a common interface for publishing events. Classes using this mixin can use the {@link #fireEvent}
 * and {@link #fireAction} methods to notify listeners of events on the class.
 *
 * Classes can also define a {@link #listeners} config to add an event hanler to the current object. See
 * {@link #addListener} for more details.
 *
 * ## Example
 *
 *     Ext.define('Employee', {
 *         mixins: ['Ext.mixin.Observable'],
 *
 *         config: {
 *             fullName: ''
 *         },
 *
 *         constructor: function(config) {
 *             this.initConfig(config);  // We need to initialize the config options when the class is instantiated
 *         },
 *
 *         quitJob: function() {
 *              this.fireEvent('quit');
 *         }
 *     });
 *
 *     var newEmployee = Ext.create('Employee', {
 *
 *         fullName: 'Ed Spencer',
 *
 *         listeners: {
 *             quit: function() { // This function will be called when the 'quit' event is fired
 *                 // By default, "this" will be the object that fired the event.
 *                 console.log(this.getFullName() + " has quit!");
 *             }
 *         }
 *     });
 *
 *     newEmployee.quitJob(); // Will log 'Ed Spencer has quit!'
 */
Ext.define('Ext.mixin.Observable', {

    requires: ['Ext.event.Dispatcher'],

    extend: 'Ext.mixin.Mixin',

    mixins: ['Ext.mixin.Identifiable'],

    mixinConfig: {
        id: 'observable',
        hooks: {
            destroy: 'destroy'
        }
    },

    alternateClassName: 'Ext.util.Observable',

    // @private
    isObservable: true,

    observableType: 'observable',

    validIdRegex: /^([\w\-]+)$/,

    observableIdPrefix: '#',

    listenerOptionsRegex: /^(?:delegate|single|delay|buffer|args|prepend)$/,

    config: {
        /**
         * @cfg {Object} listeners
         * A config object containing one or more event handlers to be added to this object during initialization. This
         * should be a valid listeners config object as specified in the {@link #addListener} example for attaching
         * multiple handlers at once.
         * @accessor
         */
        listeners: null,

        /**
         * @cfg {String/String[]} bubbleEvents The event name to bubble, or an Array of event names.
         * @accessor
         */
        bubbleEvents: null
    },

    constructor: function(config) {
        this.initConfig(config);
    },

    applyListeners: function(listeners) {
        if (listeners) {
            this.addListener(listeners);
        }
    },

    applyBubbleEvents: function(bubbleEvents) {
        if (bubbleEvents) {
            this.enableBubble(bubbleEvents);
        }
    },

    getOptimizedObservableId: function() {
        return this.observableId;
    },

    getObservableId: function() {
        if (!this.observableId) {
            var id = this.getUniqueId();

            if (!id.match(this.validIdRegex)) {
                Ext.Logger.error("Invalid unique id of '" + id + "' for this object", this);
            }

            this.observableId = this.observableIdPrefix + id;

            this.getObservableId = this.getOptimizedObservableId;
        }

        return this.observableId;
    },

    getOptimizedEventDispatcher: function() {
        return this.eventDispatcher;
    },

    getEventDispatcher: function() {
        if (!this.eventDispatcher) {
            this.eventDispatcher = Ext.event.Dispatcher.getInstance();
            this.getEventDispatcher = this.getOptimizedEventDispatcher;

            this.getListeners();
            this.getBubbleEvents();
        }

        return this.eventDispatcher;
    },

    getManagedListeners: function(object, eventName) {
        var id = object.getUniqueId(),
            managedListeners = this.managedListeners;

        if (!managedListeners) {
            this.managedListeners = managedListeners = {};
        }

        if (!managedListeners[id]) {
            managedListeners[id] = {};
            object.doAddListener('destroy', 'clearManagedListeners', this, {
                single: true,
                args: [object]
            });
        }

        if (!managedListeners[id][eventName]) {
            managedListeners[id][eventName] = [];
        }

        return managedListeners[id][eventName];
    },

    getUsedSelectors: function() {
        var selectors = this.usedSelectors;

        if (!selectors) {
            selectors = this.usedSelectors = [];
            selectors.$map = {};
        }

        return selectors;
    },

    /**
     * Fires the specified event with the passed parameters (minus the event name, plus the `options` object passed
     * to {@link #addListener}).
     *
     * An event may be set to bubble up an Observable parent hierarchy by calling {@link #enableBubble}.
     *
     * @param {String} eventName The name of the event to fire.
     * @param {Object...} args Variable number of parameters are passed to handlers.
     * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
     */
    fireEvent: function(eventName) {
        var args = Array.prototype.slice.call(arguments, 1);

        return this.doFireEvent(eventName, args);
    },

    /**
     * Fires the specified event with the passed parameters and execute a function (action)
     * at the end if there are no listeners that return false.
     *
     * @param {String} eventName The name of the event to fire.
     * @param {Array} args Arguments to pass to handers
     * @param {Function} fn Action
     * @param {Object} scope scope of fn
     */
    fireAction: function(eventName, args, fn, scope, options, order) {
        var fnType = typeof fn,
            action;

        if (args === undefined) {
            args = [];
        }

        if (fnType != 'undefined') {
            action = {
                fn: fn,
                isLateBinding: fnType == 'string',
                scope: scope || this,
                options: options || {},
                order: order
            };
        }

        return this.doFireEvent(eventName, args, action);
    },

    doFireEvent: function(eventName, args, action, connectedController) {
        if (this.eventFiringSuspended) {
            return;
        }

        var id = this.getObservableId(),
            dispatcher = this.getEventDispatcher();

        return dispatcher.dispatchEvent(this.observableType, id, eventName, args, action, connectedController);
    },

    /**
     * @private
     * @param name
     * @param fn
     * @param scope
     * @param options
     */
    doAddListener: function(name, fn, scope, options, order) {
        var isManaged = (scope && scope !== this && scope.isIdentifiable),
            usedSelectors = this.getUsedSelectors(),
            usedSelectorsMap = usedSelectors.$map,
            selector = this.getObservableId(),
            isAdded, managedListeners, delegate;

        if (!options) {
            options = {};
        }

        if (!scope) {
            scope = this;
        }

        if (options.delegate) {
            delegate = options.delegate;
            // See https://sencha.jira.com/browse/TOUCH-1579
            selector += ' ' + delegate;
        }

        if (!(selector in usedSelectorsMap)) {
            usedSelectorsMap[selector] = true;
            usedSelectors.push(selector);
        }

        isAdded = this.addDispatcherListener(selector, name, fn, scope, options, order);

        if (isAdded && isManaged) {
            managedListeners = this.getManagedListeners(scope, name);
            managedListeners.push({
                delegate: delegate,
                scope: scope,
                fn: fn,
                order: order
            });
        }

        return isAdded;
    },

    addDispatcherListener: function(selector, name, fn, scope, options, order) {
        return this.getEventDispatcher().addListener(this.observableType, selector, name, fn, scope, options, order);
    },

    doRemoveListener: function(name, fn, scope, options, order) {
        var isManaged = (scope && scope !== this && scope.isIdentifiable),
            selector = this.getObservableId(),
            isRemoved,
            managedListeners, i, ln, listener, delegate;

        if (options && options.delegate) {
            delegate = options.delegate;
            // See https://sencha.jira.com/browse/TOUCH-1579
            selector += ' ' + delegate;
        }

        if (!scope) {
            scope = this;
        }

        isRemoved = this.removeDispatcherListener(selector, name, fn, scope, order);

        if (isRemoved && isManaged) {
            managedListeners = this.getManagedListeners(scope, name);

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

                if (listener.fn === fn && listener.scope === scope && listener.delegate === delegate && listener.order === order) {
                    managedListeners.splice(i, 1);
                    break;
                }
            }
        }

        return isRemoved;
    },

    removeDispatcherListener: function(selector, name, fn, scope, order) {
        return this.getEventDispatcher().removeListener(this.observableType, selector, name, fn, scope, order);
    },

    clearManagedListeners: function(object) {
        var managedListeners = this.managedListeners,
            id, namedListeners, listeners, eventName, i, ln, listener, options;

        if (!managedListeners) {
            return this;
        }

        if (object) {
            if (typeof object != 'string') {
                id = object.getUniqueId();
            }
            else {
                id = object;
            }

            namedListeners = managedListeners[id];

            for (eventName in namedListeners) {
                if (namedListeners.hasOwnProperty(eventName)) {
                    listeners = namedListeners[eventName];

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

                        options = {};

                        if (listener.delegate) {
                            options.delegate = listener.delegate;
                        }

                        if (this.doRemoveListener(eventName, listener.fn, listener.scope, options, listener.order)) {
                            i--;
                            ln--;
                        }
                    }
                }
            }

            delete managedListeners[id];
            return this;
        }

        for (id in managedListeners) {
            if (managedListeners.hasOwnProperty(id)) {
                this.clearManagedListeners(id);
            }
        }
    },

    /**
     * @private
     * @param operation
     * @param eventName
     * @param fn
     * @param scope
     * @param options
     * @param order
     */
    changeListener: function(actionFn, eventName, fn, scope, options, order) {
        var eventNames,
            listeners,
            listenerOptionsRegex,
            actualOptions,
            name, value, i, ln, listener, valueType;

        if (typeof fn != 'undefined') {
            // Support for array format to add multiple listeners
            if (typeof eventName != 'string') {
                for (i = 0,ln = eventName.length; i < ln; i++) {
                    name = eventName[i];

                    actionFn.call(this, name, fn, scope, options, order);
                }

                return this;
            }

            actionFn.call(this, eventName, fn, scope, options, order);
        }
        else if (Ext.isArray(eventName)) {
            listeners = eventName;

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

                actionFn.call(this, listener.event, listener.fn, listener.scope, listener, listener.order);
            }
        }
        else {
            listenerOptionsRegex = this.listenerOptionsRegex;
            options = eventName;
            eventNames = [];
            listeners = [];
            actualOptions = {};

            for (name in options) {
                value = options[name];

                if (name === 'scope') {
                    scope = value;
                    continue;
                }
                else if (name === 'order') {
                    order = value;
                    continue;
                }

                if (!listenerOptionsRegex.test(name)) {
                    valueType = typeof value;

                    if (valueType != 'string' && valueType != 'function') {
                        actionFn.call(this, name, value.fn, value.scope || scope, value, value.order || order);
                        continue;
                    }

                    eventNames.push(name);
                    listeners.push(value);
                }
                else {
                    actualOptions[name] = value;
                }
            }

            for (i = 0,ln = eventNames.length; i < ln; i++) {
                actionFn.call(this, eventNames[i], listeners[i], scope, actualOptions, order);
            }
        }

    },

    /**
     * Appends an event handler to this object.
     *
     * ## Combining Options
     *
     * Using the options argument, it is possible to combine different types of listeners:
     *
     * A delayed, one-time listener.
     *
     *     container.on('tap', this.handleTap, this, {
     *         single: true,
     *         delay: 100
     *     });
     *
     * ## Attaching multiple handlers in 1 call
     *
     * The method also allows for a single argument to be passed which is a config object containing properties which
     * specify multiple events. For example:
     *
     *     container.on({
     *         tap  : this.onTap,
     *         swipe: this.onSwipe,
     *
     *         scope: this // Important. Ensure "this" is correct during handler execution
     *     });
     *
     * One can also specify options for each event handler separately:
     *
     *     container.on({
     *         tap  : { fn: this.onTap, scope: this, single: true },
     *         swipe: { fn: button.onSwipe, scope: button }
     *     });
     *
     * @param {String} eventName The name of the event to listen for. May also be an object who's property names are
     * event names.
     * @param {Function} fn The method the event invokes.  Will be called with arguments given to
     * {@link #fireEvent} plus the `options` parameter described below.
     * @param {Object} [scope] The scope (`this` reference) in which the handler function is executed. **If
     * omitted, defaults to the object which fired the event.**
     * @param {Object} [options] An object containing handler configuration.
     *
     * This object may contain any of the following properties:
     *
     * - **scope** : Object
     *
     *   The scope (`this` reference) in which the handler function is executed. **If omitted, defaults to the object
     *   which fired the event.**
     *
     * - **delay** : Number
     *
     *   The number of milliseconds to delay the invocation of the handler after the event fires.
     *
     * - **single** : Boolean
     *
     *   True to add a handler to handle just the next firing of the event, and then remove itself.
     *
     * - **order** : String
     *
     *   The order of when the listener should be added into the listener queue.
     *
     *   If you set an order of `before` and the event you are listening to is preventable, you can return `false` and it will stop the event.
     *
     *   Available options are `before`, `current` and `after`. Defaults to `current`.
     *
     * - **buffer** : Number
     *
     *   Causes the handler to be delayed by the specified number of milliseconds. If the event fires again within that
     *   time, the original handler is _not_ invoked, but the new handler is scheduled in its place.
     *
     * - **delegate** : String
     *
     *   Uses {@link Ext.ComponentQuery} to delegate events to a specified query selector within this item.
     *
     *       // Create a container with a two children; a button and a toolbar
     *       var container = Ext.create('Ext.Container', {
     *           items: [
     *               {
     *                  xtype: 'toolbar',
     *                  docked: 'top',
     *                  title: 'My Toolbar'
     *               },
     *               {
     *                  xtype: 'button',
     *                  text: 'My Button'
     *               }
     *           ]
     *       });
     *
     *       container.on({
     *           // Ext.Buttons have an xtype of 'button', so we use that are a selector for our delegate
     *           delegate: 'button',
     *
     *           tap: function() {
     *               alert('Button tapped!');
     *           }
     *       });
     *
     * @param {String} [order='current'] The order of when the listener should be added into the listener queue.
     * Possible values are `before`, `current` and `after`.
     */
    addListener: function(eventName, fn, scope, options, order) {
        return this.changeListener(this.doAddListener, eventName, fn, scope, options, order);
    },

    /**
     * Appends a before-event handler.  Returning `false` from the handler will stop the event.
     *
     * Same as {@link #addListener} with `order` set to `'before'`.
     *
     * @param {String} eventName The name of the event to listen for.
     * @param {Function} fn The method the event invokes.
     * @param {Object} [scope] The scope for `fn`.
     * @param {Object} [options] An object containing handler configuration.
     */
    addBeforeListener: function(eventName, fn, scope, options) {
        return this.addListener(eventName, fn, scope, options, 'before');
    },

    /**
     * Appends an after-event handler.
     *
     * Same as {@link #addListener} with `order` set to `'after'`.
     *
     * @param {String} eventName The name of the event to listen for.
     * @param {Function} fn The method the event invokes.
     * @param {Object} [scope] The scope for `fn`.
     * @param {Object} [options] An object containing handler configuration.
     */
    addAfterListener: function(eventName, fn, scope, options) {
        return this.addListener(eventName, fn, scope, options, 'after');
    },

    /**
     * Removes an event handler.
     *
     * @param {String} eventName The type of event the handler was associated with.
     * @param {Function} fn The handler to remove. **This must be a reference to the function passed into the
     * {@link #addListener} call.**
     * @param {Object} [scope] The scope originally specified for the handler. It must be the same as the
     * scope argument specified in the original call to {@link #addListener} or the listener will not be removed.
     * @param {Object} [options] Extra options object. See {@link #addListener} for details.
     * @param {String} [order='current'] The order of the listener to remove.
     * Possible values are `before`, `current` and `after`.
     */
    removeListener: function(eventName, fn, scope, options, order) {
        return this.changeListener(this.doRemoveListener, eventName, fn, scope, options, order);
    },

    /**
     * Removes a before-event handler.
     *
     * Same as {@link #removeListener} with `order` set to `'before'`.
     *
     * @param {String} eventName The name of the event the handler was associated with.
     * @param {Function} fn The handler to remove.
     * @param {Object} [scope] The scope originally specified for `fn`.
     * @param {Object} [options] Extra options object.
     */
    removeBeforeListener: function(eventName, fn, scope, options) {
        return this.removeListener(eventName, fn, scope, options, 'before');
    },

    /**
     * Removes a before-event handler.
     *
     * Same as {@link #removeListener} with `order` set to `'after'`.
     *
     * @param {String} eventName The name of the event the handler was associated with.
     * @param {Function} fn The handler to remove.
     * @param {Object} [scope] The scope originally specified for `fn`.
     * @param {Object} [options] Extra options object.
     */
    removeAfterListener: function(eventName, fn, scope, options) {
        return this.removeListener(eventName, fn, scope, options, 'after');
    },

    /**
     * Removes all listeners for this object.
     */
    clearListeners: function() {
        var usedSelectors = this.getUsedSelectors(),
            dispatcher = this.getEventDispatcher(),
            i, ln, selector;

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

            dispatcher.clearListeners(this.observableType, selector);
        }
    },

    /**
     * Checks to see if this object has any listeners for a specified event
     *
     * @param {String} eventName The name of the event to check for
     * @return {Boolean} True if the event is being listened for, else false
     */
    hasListener: function(eventName) {
        return this.getEventDispatcher().hasListener(this.observableType, this.getObservableId(), eventName);
    },

    /**
     * Suspends the firing of all events. (see {@link #resumeEvents})
     *
     * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
     * after the {@link #resumeEvents} call instead of discarding all suspended events.
     */
    suspendEvents: function(queueSuspended) {
        this.eventFiringSuspended = true;
    },

    /**
     * Resumes firing events (see {@link #suspendEvents}).
     *
     * If events were suspended using the `queueSuspended` parameter, then all events fired
     * during event suspension will be sent to any listeners now.
     */
    resumeEvents: function() {
        this.eventFiringSuspended = false;
    },

    /**
     * Relays selected events from the specified Observable as if the events were fired by <code><b>this</b></code>.
     * @param {Object} object The Observable whose events this object is to relay.
     * @param {String/Array/Object} events Array of event names to relay.
     */
    relayEvents: function(object, events, prefix) {
        var i, ln, oldName, newName;

        if (typeof prefix == 'undefined') {
            prefix = '';
        }

        if (typeof events == 'string') {
            events = [events];
        }

        if (Ext.isArray(events)) {
            for (i = 0,ln = events.length; i < ln; i++) {
                oldName = events[i];
                newName = prefix + oldName;

                object.addListener(oldName, this.createEventRelayer(newName), this);
            }
        }
        else {
            for (oldName in events) {
                if (events.hasOwnProperty(oldName)) {
                    newName = prefix + events[oldName];

                    object.addListener(oldName, this.createEventRelayer(newName), this);
                }
            }
        }

        return this;
    },

    /**
     * @private
     * @param args
     * @param fn
     */
    relayEvent: function(args, fn, scope, options, order) {
        var fnType = typeof fn,
            controller = args[args.length - 1],
            eventName = controller.getInfo().eventName,
            action;

        args = Array.prototype.slice.call(args, 0, -2);
        args[0] = this;

        if (fnType != 'undefined') {
            action = {
                fn: fn,
                scope: scope || this,
                options: options || {},
                order: order,
                isLateBinding: fnType == 'string'
            };
        }

        return this.doFireEvent(eventName, args, action, controller);
    },

    /**
     * @private
     * Creates an event handling function which refires the event from this object as the passed event name.
     * @param newName
     * @returns {Function}
     */
    createEventRelayer: function(newName){
        return function() {
            return this.doFireEvent(newName, Array.prototype.slice.call(arguments, 0, -2));
        }
    },

    /**
     * Enables events fired by this Observable to bubble up an owner hierarchy by calling `this.getBubbleTarget()` if
     * present. There is no implementation in the Observable base class.
     *
     * @param {String/String[]} events The event name to bubble, or an Array of event names.
     */
    enableBubble: function(events) {
        var isBubblingEnabled = this.isBubblingEnabled,
            i, ln, name;

        if (!isBubblingEnabled) {
            isBubblingEnabled = this.isBubblingEnabled = {};
        }

        if (typeof events == 'string') {
            events = Ext.Array.clone(arguments);
        }

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

            if (!isBubblingEnabled[name]) {
                isBubblingEnabled[name] = true;
                this.addListener(name, this.createEventBubbler(name), this);
            }
        }
    },

    createEventBubbler: function(name) {
        return function doBubbleEvent() {
            var bubbleTarget = ('getBubbleTarget' in this) ? this.getBubbleTarget() : null;

            if (bubbleTarget && bubbleTarget !== this && bubbleTarget.isObservable) {
                bubbleTarget.fireAction(name, Array.prototype.slice.call(arguments, 0, -2), doBubbleEvent, bubbleTarget, null, 'after');
            }
        }
    },

    getBubbleTarget: function() {
        return false;
    },

    destroy: function() {
        if (this.observableId) {
            this.fireEvent('destroy', this);
            this.clearListeners();
            this.clearManagedListeners();
        }
    },

    addEvents: Ext.emptyFn

}, function() {
    this.createAlias({
        /**
         * @method
         * Alias for {@link #addListener}.
         * @inheritdoc Ext.mixin.Observable#addListener
         */
        on: 'addListener',
        /**
         * @method
         * Alias for {@link #removeListener}.
         * @inheritdoc Ext.mixin.Observable#removeListener
         */
        un: 'removeListener',
        /**
         * @method
         * Alias for {@link #addBeforeListener}.
         * @inheritdoc Ext.mixin.Observable#addBeforeListener
         */
        onBefore: 'addBeforeListener',
        /**
         * @method
         * Alias for {@link #addAfterListener}.
         * @inheritdoc Ext.mixin.Observable#addAfterListener
         */
        onAfter: 'addAfterListener',
        /**
         * @method
         * Alias for {@link #removeBeforeListener}.
         * @inheritdoc Ext.mixin.Observable#removeBeforeListener
         */
        unBefore: 'removeBeforeListener',
        /**
         * @method
         * Alias for {@link #removeAfterListener}.
         * @inheritdoc Ext.mixin.Observable#removeAfterListener
         */
        unAfter: 'removeAfterListener'
    });

});

/**
 * @private
 */
Ext.define('Ext.Evented', {

    alternateClassName: 'Ext.EventedBase',

    mixins: ['Ext.mixin.Observable'],

    statics: {
        generateSetter: function(nameMap) {
            var internalName = nameMap.internal,
                applyName = nameMap.apply,
                changeEventName = nameMap.changeEvent,
                doSetName = nameMap.doSet;

            return function(value) {
                var initialized = this.initialized,
                    oldValue = this[internalName],
                    applier = this[applyName];

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

                    if (typeof value == 'undefined') {
                        return this;
                    }
                }

                if (value !== oldValue) {
                    if (initialized) {
                        this.fireAction(changeEventName, [this, value, oldValue], this.doSet, this, {
                            nameMap: nameMap
                        });
                    }
                    else {
                        this[internalName] = value;
                        this[doSetName].call(this, value, oldValue);
                    }
                }

                return this;
            }
        }
    },

    initialized: false,

    constructor: function(config) {
        this.initialConfig = config;
        this.initialize();
    },

    initialize: function() {
        this.initConfig(this.initialConfig);
        this.initialized = true;
    },

    doSet: function(me, value, oldValue, options) {
        var nameMap = options.nameMap;

        me[nameMap.internal] = value;
        me[nameMap.doSet].call(this, value, oldValue);
    },

    onClassExtended: function(Class, data) {
        if (!data.hasOwnProperty('eventedConfig')) {
            return;
        }

        var ExtClass = Ext.Class,
            config = data.config,
            eventedConfig = data.eventedConfig,
            name, nameMap;

        data.config = (config) ? Ext.applyIf(config, eventedConfig) : eventedConfig;

        /*
         * These are generated setters for eventedConfig
         *
         * If the component is initialized, it invokes fireAction to fire the event as well,
         * which indicate something has changed. Otherwise, it just executes the action
         * (happens during initialization)
         *
         * This is helpful when we only want the event to be fired for subsequent changes.
         * Also it's a major performance improvement for instantiation when fired events
         * are mostly useless since there's no listeners
         */
        for (name in eventedConfig) {
            if (eventedConfig.hasOwnProperty(name)) {
                nameMap = ExtClass.getConfigNameMap(name);

                data[nameMap.set] = this.generateSetter(nameMap);
            }
        }
    }
});

/**
 * @private
 * This is the abstract class for {@link Ext.Component}.
 *
 * This should never be overriden.
 */
Ext.define('Ext.AbstractComponent', {
    extend: 'Ext.Evented',

    onClassExtended: function(Class, members) {
        if (!members.hasOwnProperty('cachedConfig')) {
            return;
        }

        var prototype = Class.prototype,
            config = members.config,
            cachedConfig = members.cachedConfig,
            cachedConfigList = prototype.cachedConfigList,
            hasCachedConfig = prototype.hasCachedConfig,
            name, value;

        delete members.cachedConfig;

        prototype.cachedConfigList = cachedConfigList = (cachedConfigList) ? cachedConfigList.slice() : [];
        prototype.hasCachedConfig = hasCachedConfig = (hasCachedConfig) ? Ext.Object.chain(hasCachedConfig) : {};

        if (!config) {
            members.config = config = {};
        }

        for (name in cachedConfig) {
            if (cachedConfig.hasOwnProperty(name)) {
                value = cachedConfig[name];

                if (!hasCachedConfig[name]) {
                    hasCachedConfig[name] = true;
                    cachedConfigList.push(name);
                }

                config[name] = value;
            }
        }
    },

    getElementConfig: Ext.emptyFn,

    referenceAttributeName: 'reference',

    referenceSelector: '[reference]',

    /**
     * @private
     * Significantly improve instantiation time for Component with multiple references
     * Ext.Element instance of the reference domNode is only created the very first time
     * it's ever used
     */
    addReferenceNode: function(name, domNode) {
        Ext.Object.defineProperty(this, name, {
            get: function() {
                var reference;

                delete this[name];
                this[name] = reference = new Ext.Element(domNode);
                return reference;
            },
            configurable: true
        });
    },

    initElement: function() {
        var prototype = this.self.prototype,
            id = this.getId(),
            referenceList = [],
            cleanAttributes = true,
            referenceAttributeName = this.referenceAttributeName,
            needsOptimization = false,
            renderTemplate, renderElement, element,
            referenceNodes, i, ln, referenceNode, reference,
            configNameCache, defaultConfig, cachedConfigList, initConfigList, initConfigMap, configList,
            elements, name, nameMap, internalName;

        if (prototype.hasOwnProperty('renderTemplate')) {
            renderTemplate = this.renderTemplate.cloneNode(true);
            renderElement = renderTemplate.firstChild;
        }
        else {
            cleanAttributes = false;
            needsOptimization = true;
            renderTemplate = document.createDocumentFragment();
            renderElement = Ext.Element.create(this.getElementConfig(), true);
            renderTemplate.appendChild(renderElement);
        }

        referenceNodes = renderTemplate.querySelectorAll(this.referenceSelector);

        for (i = 0,ln = referenceNodes.length; i < ln; i++) {
            referenceNode = referenceNodes[i];
            reference = referenceNode.getAttribute(referenceAttributeName);

            if (cleanAttributes) {
                referenceNode.removeAttribute(referenceAttributeName);
            }

            if (reference == 'element') {
                referenceNode.id = id;
                this.element = element = new Ext.Element(referenceNode);
            }
            else {
                this.addReferenceNode(reference, referenceNode);
            }

            referenceList.push(reference);
        }

        this.referenceList = referenceList;

        if (!this.innerElement) {
            this.innerElement = element;
        }

        if (renderElement === element.dom) {
            this.renderElement = element;
        }
        else {
            this.addReferenceNode('renderElement', renderElement);
        }

        // This happens only *once* per class, during the very first instantiation
        // to optimize renderTemplate based on cachedConfig
        if (needsOptimization) {
            configNameCache = Ext.Class.configNameCache;
            defaultConfig = this.config;
            cachedConfigList = this.cachedConfigList;
            initConfigList = this.initConfigList;
            initConfigMap = this.initConfigMap;
            configList = [];

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

                if (initConfigMap[name]) {
                    initConfigMap[name] = false;
                    Ext.Array.remove(initConfigList, name);
                }

                if (defaultConfig[name] !== null) {
                    configList.push(name);
                    this[nameMap.get] = this[nameMap.initGet];
                }
            }

            for (i = 0,ln = configList.length; i < ln; i++) {
                name = configList[i];
                nameMap = configNameCache[name];
                internalName = nameMap.internal;

                this[internalName] = null;
                this[nameMap.set].call(this, defaultConfig[name]);
                delete this[nameMap.get];

                prototype[internalName] = this[internalName];
            }

            renderElement = this.renderElement.dom;
            prototype.renderTemplate = renderTemplate = document.createDocumentFragment();
            renderTemplate.appendChild(renderElement.cloneNode(true));

            elements = renderTemplate.querySelectorAll('[id]');

            for (i = 0,ln = elements.length; i < ln; i++) {
                element = elements[i];
                element.removeAttribute('id');
            }

            for (i = 0,ln = referenceList.length; i < ln; i++) {
                reference = referenceList[i];
                this[reference].dom.removeAttribute('reference');
            }
        }

        return this;
    }
});

/**
 * @author Ed Spencer
 *
 * Controllers are responsible for responding to events that occur within your app. If your app contains a Logout
 * {@link Ext.Button button} that your user can tap on, a Controller would listen to the Button's tap event and take
 * the appropriate action. It allows the View classes to handle the display of data and the Model classes to handle the
 * loading and saving of data - the Controller is the glue that binds them together.
 *
 * ## Relation to Ext.app.Application
 *
 * Controllers exist within the context of an {@link Ext.app.Application Application}. An Application usually consists
 * of a number of Controllers, each of which handle a specific part of the app. For example, an Application that
 * handles the orders for an online shopping site might have controllers for Orders, Customers and Products.
 *
 * All of the Controllers that an Application uses are specified in the Application's
 * {@link Ext.app.Application#controllers} config. The Application automatically instantiates each Controller and keeps
 * references to each, so it is unusual to need to instantiate Controllers directly. By convention each Controller is
 * named after the thing (usually the Model) that it deals with primarily, usually in the plural - for example if your
 * app is called 'MyApp' and you have a Controller that manages Products, convention is to create a
 * MyApp.controller.Products class in the file app/controller/Products.js.
 *
 * ## Refs and Control
 *
 * The centerpiece of Controllers is the twin configurations {@link #refs} and {@link #cfg-control}. These are used to
 * easily gain references to Components inside your app and to take action on them based on events that they fire.
 * Let's look at {@link #refs} first:
 *
 * ### Refs
 *
 * Refs leverage the powerful {@link Ext.ComponentQuery ComponentQuery} syntax to easily locate Components on your
 * page. We can define as many refs as we like for each Controller, for example here we define a ref called 'nav' that
 * finds a Component on the page with the ID 'mainNav'. We then use that ref in the addLogoutButton beneath it:
 *
 *     Ext.define('MyApp.controller.Main', {
 *         extend: 'Ext.app.Controller',
 *
 *         config: {
 *             refs: {
 *                 nav: '#mainNav'
 *             }
 *         },
 *
 *         addLogoutButton: function() {
 *             this.getNav().add({
 *                 text: 'Logout'
 *             });
 *         }
 *     });
 *
 * Usually, a ref is just a key/value pair - the key ('nav' in this case) is the name of the reference that will be
 * generated, the value ('#mainNav' in this case) is the {@link Ext.ComponentQuery ComponentQuery} selector that will
 * be used to find the Component.
 *
 * Underneath that, we have created a simple function called addLogoutButton which uses this ref via its generated
 * 'getNav' function. These getter functions are generated based on the refs you define and always follow the same
 * format - 'get' followed by the capitalized ref name. In this case we're treating the nav reference as though it's a
 * {@link Ext.Toolbar Toolbar}, and adding a Logout button to it when our function is called. This ref would recognize
 * a Toolbar like this:
 *
 *     Ext.create('Ext.Toolbar', {
 *         id: 'mainNav',
 *
 *         items: [
 *             {
 *                 text: 'Some Button'
 *             }
 *         ]
 *     });
 *
 * Assuming this Toolbar has already been created by the time we run our 'addLogoutButton' function (we'll see how that
 * is invoked later), it will get the second button added to it.
 *
 * ### Advanced Refs
 *
 * Refs can also be passed a couple of additional options, beyond name and selector. These are autoCreate and xtype,
 * which are almost always used together:
 *
 *     Ext.define('MyApp.controller.Main', {
 *         extend: 'Ext.app.Controller',
 *
 *         config: {
 *             refs: {
 *                 nav: '#mainNav',
 *
 *                 infoPanel: {
 *                     selector: 'tabpanel panel[name=fish] infopanel',
 *                     xtype: 'infopanel',
 *                     autoCreate: true
 *                 }
 *             }
 *         }
 *     });
 *
 * We've added a second ref to our Controller. Again the name is the key, 'infoPanel' in this case, but this time we've
 * passed an object as the value instead. This time we've used a slightly more complex selector query - in this example
 * imagine that your app contains a {@link Ext.tab.Panel tab panel} and that one of the items in the tab panel has been
 * given the name 'fish'. Our selector matches any Component with the xtype 'infopanel' inside that tab panel item.
 *
 * The difference here is that if that infopanel does not exist already inside the 'fish' panel, it will be
 * automatically created when you call this.getInfoPanel inside your Controller. The Controller is able to do this
 * because we provided the xtype to instantiate with in the event that the selector did not return anything.
 *
 * ### Control
 *
 * The sister config to {@link #refs} is {@link #cfg-control}. {@link #cfg-control Control} is the means by which your listen
 * to events fired by Components and have your Controller react in some way. Control accepts both ComponentQuery
 * selectors and refs as its keys, and listener objects as values - for example:
 *
 *     Ext.define('MyApp.controller.Main', {
 *         extend: 'Ext.app.Controller',
 *
 *         config: {
 *             control: {
 *                 loginButton: {
 *                     tap: 'doLogin'
 *                 },
 *                 'button[action=logout]': {
 *                     tap: 'doLogout'
 *                 }
 *             },
 *
 *             refs: {
 *                 loginButton: 'button[action=login]'
 *             }
 *         },
 *
 *         doLogin: function() {
 *             //called whenever the Login button is tapped
 *         },
 *
 *         doLogout: function() {
 *             //called whenever any Button with action=logout is tapped
 *         }
 *     });
 *
 * Here we have set up two control declarations - one for our loginButton ref and the other for any Button on the page
 * that has been given the action 'logout'. For each declaration we passed in a single event handler - in each case
 * listening for the 'tap' event, specifying the action that should be called when that Button fires the tap event.
 * Note that we specified the 'doLogin' and 'doLogout' methods as strings inside the control block - this is important.
 *
 * You can listen to as many events as you like in each control declaration, and mix and match ComponentQuery selectors
 * and refs as the keys.
 *
 * ## Routes
 *
 * As of Sencha Touch 2, Controllers can now directly specify which routes they are interested in. This enables us to
 * provide history support within our app, as well as the ability to deeply link to any part of the application that we
 * provide a route for.
 *
 * For example, let's say we have a Controller responsible for logging in and viewing user profiles, and want to make
 * those screens accessible via urls. We could achieve that like this:
 *
 *     Ext.define('MyApp.controller.Users', {
 *         extend: 'Ext.app.Controller',
 *
 *         config: {
 *             routes: {
 *                 'login': 'showLogin',
 *                 'user/:id': 'showUserById'
 *             },
 *
 *             refs: {
 *                 main: '#mainTabPanel'
 *             }
 *         },
 *
 *         //uses our 'main' ref above to add a loginpanel to our main TabPanel (note that
 *         //'loginpanel' is a custom xtype created for this application)
 *         showLogin: function() {
 *             this.getMain().add({
 *                 xtype: 'loginpanel'
 *             });
 *         },
 *
 *         //Loads the User then adds a 'userprofile' view to the main TabPanel
 *         showUserById: function(id) {
 *             MyApp.model.User.load(id, {
 *                 scope: this,
 *                 success: function(user) {
 *                     this.getMain().add({
 *                         xtype: 'userprofile',
 *                         user: user
 *                     });
 *                 }
 *             });
 *         }
 *     });
 *
 * The routes we specified above simply map the contents of the browser address bar to a Controller function to call
 * when that route is matched. The routes can be simple text like the login route, which matches against
 * http://myapp.com/#login, or contain wildcards like the 'user/:id' route, which matches urls like
 * http://myapp.com/#user/123. Whenever the address changes the Controller automatically calls the function specified.
 *
 * Note that in the showUserById function we had to first load the User instance. Whenever you use a route, the
 * function that is called by that route is completely responsible for loading its data and restoring state. This is
 * because your user could either send that url to another person or simply refresh the page, which we wipe clear any
 * cached data you had already loaded. There is a more thorough discussion of restoring state with routes in the
 * application architecture guides.
 *
 * ## Advanced Usage
 *
 * See <a href="#!/guide/controllers">the Controllers guide</a> for advanced Controller usage including before filters
 * and customizing for different devices.
 * 
 * @aside guide controllers
 * @aside guide apps_intro
 */
Ext.define('Ext.app.Controller', {
    mixins: {
        observable: "Ext.mixin.Observable"
    },

    config: {
        /**
         * @cfg {Object} refs A collection of named {@link Ext.ComponentQuery ComponentQuery} selectors that makes it
         * easy to get references to key Components on your page. Example usage:
         *
         *     refs: {
         *         main: '#mainTabPanel',
         *         loginButton: '#loginWindow button[action=login]',
         *
         *         infoPanel: {
         *             selector: 'infopanel',
         *             xtype: 'infopanel',
         *             autoCreate: true
         *         }
         *     }
         *
         * The first two are simple ComponentQuery selectors, the third (infoPanel) also passes in the autoCreate and
         * xtype options, which will first run the ComponentQuery to see if a Component matching that selector exists
         * on the page. If not, it will automatically create one using the xtype provided:
         *
         *     someControllerFunction: function() {
         *         //if the info panel didn't exist before, calling its getter will instantiate
         *         //it automatically and return the new instance
         *         this.getInfoPanel().show();
         *     }
         *
         * @accessor
         */
        refs: {},

        /**
         * @cfg {Object} routes Provides a mapping of urls to Controller actions. Whenever the specified url is matched
         * in the address bar, the specified Controller action is called. Example usage:
         *
         *     routes: {
         *         'login': 'showLogin',
         *         'users/:id': 'showUserById'
         *     }
         *
         * The first route will match against http://myapp.com/#login and call the Controller's showLogin function. The
         * second route contains a wildcard (':id') and will match all urls like http://myapp.com/#users/123, calling
         * the showUserById function with the matched ID as the first argument.
         *
         * @accessor
         */
        routes: {},

        /**
         * @cfg {Object} control Provides a mapping of Controller functions that should be called whenever certain
         * Component events are fired. The Components can be specified using {@link Ext.ComponentQuery ComponentQuery}
         * selectors or {@link #refs}. Example usage:
         *
         *     control: {
         *         'button[action=logout]': {
         *             tap: 'doLogout'
         *         },
         *         main: {
         *             activeitemchange: 'doUpdate'
         *         }
         *     }
         *
         * The first item uses a ComponentQuery selector to run the Controller's doLogout function whenever any Button
         * with action=logout is tapped on. The second calls the Controller's doUpdate function whenever the
         * activeitemchange event is fired by the Component referenced by our 'main' ref. In this case main is a tab
         * panel (see {@link #refs} for how to set that reference up).
         *
         * @accessor
         */
        control: {},

        /**
         * @cfg {Object} before Provides a mapping of Controller functions to filter functions that are run before them
         * when dispatched to from a route. These are usually used to run pre-processing functions like authentication
         * before a certain function is executed. They are only called when dispatching from a route. Example usage:
         *
         *     Ext.define('MyApp.controller.Products', {
         *         config: {
         *             before: {
         *                 editProduct: 'authenticate'
         *             },
         *
         *             routes: {
         *                 'product/edit/:id': 'editProduct'
         *             }
         *         },
         *
         *         //this is not directly because our before filter is called first
         *         editProduct: function() {
         *             //... performs the product editing logic
         *         },
         *
         *         //this is run before editProduct
         *         authenticate: function(action) {
         *             MyApp.authenticate({
         *                 success: function() {
         *                     action.resume();
         *                 },
         *                 failure: function() {
         *                     Ext.Msg.alert('Not Logged In', "You can't do that, you're not logged in");
         *                 }
         *             });
         *         }
         *     });
         *
         * @accessor
         */
        before: {},

        /**
         * @cfg {Ext.app.Application} application The Application instance this Controller is attached to. This is
         * automatically provided when using the MVC architecture so should rarely need to be set directly.
         * @accessor
         */
        application: {},
        
        /**
         * @cfg {Array} stores The set of stores to load for this Application. Each store is expected to
         * exist inside the *app/store* directory and define a class following the convention
         * AppName.store.StoreName. For example, in the code below, the *AppName.store.Users* class will be loaded.
         * Note that we are able to specify either the full class name (as with *AppName.store.Groups*) or just the
         * final part of the class name and leave Application to automatically prepend *AppName.store.’* to each:
         *
         *     stores: [
         *         'Users',
         *         'AppName.store.Groups',
         *         'SomeCustomNamespace.store.Orders'
         *     ]
         * @accessor
         */
        stores: [],

        /**
         * @cfg {Array} models The set of models to load for this Application. Each model is expected to exist inside the
         * *app/model* directory and define a class following the convention AppName.model.ModelName. For example, in the
         * code below, the classes *AppName.model.User*, *AppName.model.Group* and *AppName.model.Product* will be loaded.
         * Note that we are able to specify either the full class name (as with *AppName.model.Product*) or just the
         * final part of the class name and leave Application to automatically prepend *AppName.model.* to each:
         *
         *     models: [
         *         'User',
         *         'Group',
         *         'AppName.model.Product',
         *         'SomeCustomNamespace.model.Order'
         *     ]
         * @accessor
         */
        models: [],

        /**
         * @cfg {Array} views The set of views to load for this Application. Each view is expected to exist inside the
         * *app/view* directory and define a class following the convention AppName.view.ViewName. For example, in the
         * code below, the classes *AppName.view.Users*, *AppName.view.Groups* and *AppName.view.Products* will be loaded.
         * Note that we are able to specify either the full class name (as with *AppName.view.Products*) or just the
         * final part of the class name and leave Application to automatically prepend *AppName.view.* to each:
         *
         *     views: [
         *         'Users',
         *         'Groups',
         *         'AppName.view.Products',
         *         'SomeCustomNamespace.view.Orders'
         *     ]
         * @accessor
         */
        views: []
    },

    /**
     * Constructs a new Controller instance
     */
    constructor: function(config) {
        this.initConfig(config);

        this.mixins.observable.constructor.call(this, config);
    },

    /**
     * Called by the Controller's {@link #application} to initialize the Controller. This is always called before the
     * {@link Ext.app.Application Application} launches, giving the Controller a chance to run any pre-launch logic.
     * See also {@link #launch}, which is called after the {@link Ext.app.Application#launch Application's launch function}
     */
    init: Ext.emptyFn,

    /**
     * Called by the Controller's {@link #application} immediately after the Application's own
     * {@link Ext.app.Application#launch launch function} has been called. This is usually a good place to run any
     * logic that has to run after the app UI is initialized. See also {@link #init}, which is called before the
     * {@link Ext.app.Application#launch Application's launch function}.
     */
    launch: Ext.emptyFn,

    /**
     * Convenient way to redirect to a new url. See {@link Ext.app.Application#redirectTo} for full usage information
     */
    redirectTo: function(place) {
        return this.getApplication().redirectTo(place);
    },

    /**
     * @private
     * Executes an Ext.app.Action by giving it the correct before filters and kicking off execution
     */
    execute: function(action, skipFilters) {
        action.setBeforeFilters(this.getBefore()[action.getAction()]);
        action.execute();
    },

    /**
     * @private
     * Massages the before filters into an array of function references for each controller action
     */
    applyBefore: function(before) {
        var filters, name, length, i;

        for (name in before) {
            filters = Ext.Array.from(before[name]);
            length  = filters.length;

            for (i = 0; i < length; i++) {
                filters[i] = this[filters[i]];
            }

            before[name] = filters;
        }

        return before;
    },

    /**
     * @private
     */
    applyControl: function(config) {
        this.control(config, this);

        return config;
    },

    /**
     * @private
     */
    applyRefs: function(refs) {
        if (Ext.isArray(refs)) {
            Ext.Logger.deprecate("In Sencha Touch 2 the refs config accepts an object but you have passed it an array.");
        }

        this.ref(refs);

        return refs;
    },

    /**
     * @private
     * Adds any routes specified in this Controller to the global Application router
     */
    applyRoutes: function(routes) {
        var app    = this instanceof Ext.app.Application ? this : this.getApplication(),
            router = app.getRouter(),
            route, url, config;

        for (url in routes) {
            route = routes[url];

            config = {
                controller: this.$className
            };

            if (Ext.isString(route)) {
                config.action = route;
            } else {
                Ext.apply(config, route);
            }

            router.connect(url, config);
        }

        return routes;
    },
    
    /**
     * @private
     * As a convenience developers can locally qualify store names (e.g. 'MyStore' vs
     * 'MyApp.store.MyStore'). This just makes sure everything ends up fully qualified
     */
    applyStores: function(stores) {
        return this.getFullyQualified(stores, 'store');
    },

    /**
     * @private
     * As a convenience developers can locally qualify model names (e.g. 'MyModel' vs
     * 'MyApp.model.MyModel'). This just makes sure everything ends up fully qualified
     */
    applyModels: function(models) {
        return this.getFullyQualified(models, 'model');
    },

    /**
     * @private
     * As a convenience developers can locally qualify view names (e.g. 'MyView' vs
     * 'MyApp.view.MyView'). This just makes sure everything ends up fully qualified
     */
    applyViews: function(views) {
        return this.getFullyQualified(views, 'view');
    },
    
    /**
     * @private
     * Returns the fully qualified name for any class name variant. This is used to find the FQ name for the model, 
     * view, controller, store and profiles listed in a Controller or Application.
     * @param {String[]} items The array of strings to get the FQ name for
     * @param {String} namespace If the name happens to be an application class, add it to this namespace
     * @return {String} The fully-qualified name of the class
     */
    getFullyQualified: function(items, namespace) {
        var length  = items.length,
            appName = this.getApplication().getName(),
            name, i;

        for (i = 0; i < length; i++) {
            name = items[i];

            //we check name === appName to allow MyApp.profile.MyApp to exist
            if (Ext.isString(name) && (Ext.Loader.getPrefix(name) === "" || name === appName)) {
                items[i] = appName + '.' + namespace + '.' + name;
            }
        }

        return items;
    },

    /**
     * @private
     */
    control: function(selectors) {
        this.getApplication().control(selectors, this);
    },

    /**
     * @private
     * 1.x-inspired ref implementation
     */
    ref: function(refs) {
        var refName, getterName, selector, info;

        for (refName in refs) {
            selector = refs[refName];
            getterName = "get" + Ext.String.capitalize(refName);

            if (!this[getterName]) {
                if (Ext.isString(refs[refName])) {
                    info = {
                        ref: refName,
                        selector: selector
                    };
                } else {
                    info = refs[refName];
                }

                this[getterName] = Ext.Function.pass(this.getRef, [refName, info], this);
            }

            this.references = this.references || [];
            this.references.push(refName.toLowerCase());
        }
    },

    /**
     * @private
     */
    getRef: function(ref, info, config) {
        this.refCache = this.refCache || {};
        info = info || {};
        config = config || {};

        Ext.apply(info, config);

        if (info.forceCreate) {
            return Ext.ComponentManager.create(info, 'component');
        }

        var me = this,
            cached = me.refCache[ref];

        if (!cached) {
            me.refCache[ref] = cached = Ext.ComponentQuery.query(info.selector)[0];
            if (!cached && info.autoCreate) {
                me.refCache[ref] = cached = Ext.ComponentManager.create(info, 'component');
            }
            if (cached) {
                cached.on('destroy', function() {
                    me.refCache[ref] = null;
                });
            }
        }

        return cached;
    },

    /**
     * @private
     */
    hasRef: function(ref) {
        return this.references && this.references.indexOf(ref.toLowerCase()) !== -1;
    },

}, function() {
});

/**
 * @author Ed Spencer
 * @private
 *
 * Manages the stack of {@link Ext.app.Action} instances that have been decoded, pushes new urls into the browser's
 * location object and listens for changes in url, firing the {@link #change} event when a change is detected.
 *
 * This is tied to an {@link Ext.app.Application Application} instance. The Application performs all of the
 * interactions with the History object, no additional integration should be required.
 */
Ext.define('Ext.app.History', {
    mixins: ['Ext.mixin.Observable'],

    /**
     * @event change
     * Fires when a change in browser url is detected
     * @param {String} url The new url, after the hash (e.g. http://myapp.com/#someUrl returns 'someUrl')
     */

    config: {
        /**
         * @cfg {Array} actions The stack of {@link Ext.app.Action action} instances that have occured so far
         */
        actions: [],

        /**
         * @cfg {Boolean} updateUrl True to automatically update the browser's url when {@link #add} is called
         */
        updateUrl: true,

        /**
         * @cfg {String} token The current token as read from the browser's location object
         */
        token: ''
    },

    constructor: function(config) {
        if (Ext.feature.has.History) {
            window.addEventListener('hashchange', Ext.bind(this.detectStateChange, this));
        }
        else {
            setInterval(Ext.bind(this.detectStateChange, this), 50);
        }

        this.initConfig(config);
    },

    /**
     * Adds an {@link Ext.app.Action Action} to the stack, optionally updating the browser's url and firing the
     * {@link #change} event.
     * @param {Ext.app.Action} action The Action to add to the stack
     * @param {Boolean} silent Cancels the firing of the {@link #change} event if true
     */
    add: function(action, silent) {
        this.getActions().push(Ext.factory(action, Ext.app.Action));

        var url = action.getUrl();

        if (this.getUpdateUrl()) {
            // history.pushState({}, action.getTitle(), "#" + action.getUrl());
            this.setToken(url);
            window.location.hash = url;
        }

        if (silent !== true) {
            this.fireEvent('change', url);
        }

        this.setToken(url);
    },

    /**
     * @private
     */
    back: function() {
        this.getActions().pop().run();
    },

    /**
     * @private
     */
    applyToken: function(token) {
        return token[0] == '#' ? token.substr(1) : token;
    },

    /**
     * @private
     */
    detectStateChange: function() {
        var newToken = this.applyToken(window.location.hash),
            oldToken = this.getToken();

        if (newToken != oldToken) {
            this.onStateChange();
            this.setToken(newToken);
        }
    },

    /**
     * @private
     */
    onStateChange: function() {
        this.fireEvent('change', window.location.hash.substr(1));
    }
});

/**
 * @author Ed Spencer
 *
 * A Profile represents a range of devices that fall under a common category. For the vast majority of apps that use
 * device profiles, the app defines a Phone profile and a Tablet profile. Doing this enables you to easily customize
 * the experience for the different sized screens offered by those device types.
 *
 * Only one Profile can be active at a time, and each Profile defines a simple {@link #isActive} function that should
 * return either true or false. The first Profile to return true from its isActive function is set as your Application's
 * {@link Ext.app.Application#currentProfile current profile}.
 *
 * A Profile can define any number of {@link #models}, {@link #views}, {@link #controllers} and {@link #stores} which
 * will be loaded if the Profile is activated. It can also define a {@link #launch} function that will be called after
 * all of its dependencies have been loaded, just before the {@link Ext.app.Application#launch application launch}
 * function is called.
 *
 * ## Sample Usage
 *
 * First you need to tell your Application about your Profile(s):
 *
 *     Ext.application({
 *         name: 'MyApp',
 *         profiles: ['Phone', 'Tablet']
 *     });
 *
 * This will load app/profile/Phone.js and app/profile/Tablet.js. Here's how we might define the Phone profile:
 *
 *     Ext.define('MyApp.profile.Phone', {
 *         extend: 'Ext.app.Profile',
 *
 *         views: ['Main'],
 *
 *         isActive: function() {
 *             return Ext.os.is.Phone;
 *         }
 *     });
 *
 * The isActive function returns true if we detect that we are running on a phone device. If that is the case the
 * Application will set this Profile active and load the 'Main' view specified in the Profile's {@link #views} config.
 *
 * ## Class Specializations
 *
 * Because Profiles are specializations of an application, all of the models, views, controllers and stores defined
 * in a Profile are expected to be namespaced under the name of the Profile. Here's an expanded form of the example
 * above:
 *
 *     Ext.define('MyApp.profile.Phone', {
 *         extend: 'Ext.app.Profile',
 *
 *         views: ['Main'],
 *         controllers: ['Signup'],
 *         models: ['MyApp.model.Group'],
 *
 *         isActive: function() {
 *             return Ext.os.is.Phone;
 *         }
 *     });
 *
 * In this case, the Profile is going to load *app/view/phone/Main.js*, *app/controller/phone/Signup.js* and
 * *app/model/Group.js*. Notice that in each of the first two cases the name of the profile ('phone' in this case) was
 * injected into the class names. In the third case we specified the full Model name (for Group) so the Profile name
 * was not injected.
 *
 * For a fuller understanding of the ideas behind Profiles and how best to use them in your app, we suggest you read
 * the <a href="#!/guide/profiles">device profiles guide</a>.
 * 
 * @aside guide profiles
 */
Ext.define('Ext.app.Profile', {
    mixins: {
        observable: "Ext.mixin.Observable"
    },

    config: {
        /**
         * @cfg {String} namespace The namespace that this Profile's classes can be found in. Defaults to the lowercased
         * Profile {@link #name}, for example a Profile called MyApp.profile.Phone will by default have a 'phone'
         * namespace, which means that this Profile's additional models, stores, views and controllers will be loaded
         * from the MyApp.model.phone.*, MyApp.store.phone.*, MyApp.view.phone.* and MyApp.controller.phone.* namespaces
         * respectively.
         * @accessor
         */
        namespace: 'auto',

        /**
         * @cfg {String} name The name of this Profile. Defaults to the last section of the class name (e.g. a profile
         * called MyApp.profile.Phone will default the name to 'Phone').
         * @accessor
         */
        name: 'auto',

        /**
         * @cfg {Array} controllers Any additional {@link Ext.app.Application#controllers Controllers} to load for this
         * profile. Note that each item here will be prepended with the Profile namespace when loaded. Example usage:
         *
         *     controllers: [
         *         'Users',
         *         'MyApp.controller.Products'
         *     ]
         *
         * This will load *MyApp.controller.tablet.Users* and *MyApp.controller.Products*.
         * @accessor
         */
        controllers: [],

        /**
         * @cfg {Array} models Any additional {@link Ext.app.Application#models Models} to load for this profile. Note
         * that each item here will be prepended with the Profile namespace when loaded. Example usage:
         *
         *     models: [
         *         'Group',
         *         'MyApp.model.User'
         *     ]
         *
         * This will load *MyApp.model.tablet.Group* and *MyApp.model.User*.
         * @accessor
         */
        models: [],

        /**
         * @cfg {Array} views Any additional {@link Ext.app.Application#views views} to load for this profile. Note
         * that each item here will be prepended with the Profile namespace when loaded. Example usage:
         *
         *     views: [
         *         'Main',
         *         'MyApp.view.Login'
         *     ]
         *
         * This will load *MyApp.view.tablet.Main* and *MyApp.view.Login*.
         * @accessor
         */
        views: [],

        /**
         * @cfg {Array} stores Any additional {@link Ext.app.Application#stores Stores} to load for this profile. Note
         * that each item here will be prepended with the Profile namespace when loaded. Example usage:
         *
         *     stores: [
         *         'Users',
         *         'MyApp.store.Products'
         *     ]
         *
         * This will load *MyApp.store.tablet.Users* and *MyApp.store.Products*.
         * @accessor
         */
        stores: [],

        /**
         * @cfg {Ext.app.Application} application The {@link Ext.app.Application Application} instance that this
         * Profile is bound to. This is set automatically. Read only.
         * @accessor
         */
        application: null
    },

    /**
     * Creates a new Profile instance
     */
    constructor: function(config) {
        this.initConfig(config);

        this.mixins.observable.constructor.apply(this, arguments);
    },

    /**
     * Determines whether or not this Profile is active on the device isActive is executed on. Should return true if
     * this profile is meant to be active on this device, false otherwise. Each Profile should implement this function
     * (the default implementation just returns false).
     * @return {Boolean} True if this Profile should be activated on the device it is running on, false otherwise
     */
    isActive: function() {
        return false;
    },

    /**
     * @method
     * The launch function is called by the {@link Ext.app.Application Application} if this Profile's {@link #isActive}
     * function returned true. This is typically the best place to run any profile-specific app launch code. Example
     * usage:
     *
     *     launch: function() {
     *         Ext.create('MyApp.view.tablet.Main');
     *     }
     */
    launch: Ext.emptyFn,

    /**
     * @private
     */
    applyNamespace: function(name) {
        if (name == 'auto') {
            name = this.getName();
        }

        return name.toLowerCase();
    },

    /**
     * @private
     */
    applyName: function(name) {
        if (name == 'auto') {
            var pieces = this.$className.split('.');
            name = pieces[pieces.length - 1];
        }

        return name;
    },

    /**
     * @private
     * Computes the full class names of any specified model, view, controller and store dependencies, returns them in
     * an object map for easy loading
     */
    getDependencies: function() {
        var allClasses = [],
            format = Ext.String.format,
            appName = this.getApplication().getName(),
            namespace = this.getNamespace(),
            map = {
                model: this.getModels(),
                view: this.getViews(),
                controller: this.getControllers(),
                store: this.getStores()
            },
            classType, classNames, fullyQualified;

        for (classType in map) {
            classNames = [];

            Ext.each(map[classType], function(className) {
                if (Ext.isString(className)) {

                    //if there is a '.' anywhere in the string we treat it as fully qualified, if not compute the
                    //namespaced classname below
                    if (className.match("\\.")) {
                        fullyQualified = className;
                    } else {
                        fullyQualified = format('{0}.{1}.{2}.{3}', appName, classType, namespace, className);
                    }

                    classNames.push(fullyQualified);
                    allClasses.push(fullyQualified);
                }
            });

            map[classType] = classNames;
        }

        map.all = allClasses;

        return map;
    }
});
/**
 * @author Ed Spencer
 *
 * Ext.app.Application defines the set of {@link Ext.data.Model Models}, {@link Ext.app.Controller Controllers},
 * {@link Ext.app.Profile Profiles}, {@link Ext.data.Store Stores} and {@link Ext.Component Views} that an application
 * consists of. It automatically loads all of those dependencies and can optionally specify a {@link #launch} function
 * that will be called when everthing is ready.
 *
 * Sample usage:
 *
 *     Ext.application({
 *         name: 'MyApp',
 *
 *         models: ['User', 'Group'],
 *         stores: ['Users'],
 *         controllers: ['Users'],
 *         views: ['Main', 'ShowUser'],
 *
 *         launch: function() {
 *             Ext.create('MyApp.view.Main');
 *         }
 *     });
 *
 * Creating an Application instance is the only time in Sencha Touch 2 that we don't use Ext.create to create the new
 * instance. Instead, the {@link Ext#application} function instantiates an Ext.app.Application internally,
 * automatically loading the Ext.app.Application class if it is not present on the page already and hooking in to
 * {@link Ext#onReady} before creating the instance itself. An alternative is to use Ext.create inside an Ext.onReady
 * callback, but Ext.application is preferred.
 *
 * ## Dependencies
 *
 * Application follows a simple convention when it comes to specifying the controllers, views, models, stores and
 * profiles it requires. By default it expects each of them to be found inside the *app/controller*, *app/view*,
 * *app/model*, *app/store* and *app/profile* directories in your app - if you follow this convention you can just
 * specify the last part of each class name and Application will figure out the rest for you:
 *
 *     Ext.application({
 *         name: 'MyApp',
 *
 *         controllers: ['Users'],
 *         models: ['User', 'Group'],
 *         stores: ['Users'],
 *         views: ['Main', 'ShowUser']
 *     });
 *
 * The example above will load 6 files:
 *
 * * app/model/User.js
 * * app/model/Group.js
 * * app/store/Users.js
 * * app/controller/Users.js
 * * app/view/Main.js
 * * app/view/ShowUser.js
 *
 * ### Nested Dependencies
 *
 * For larger apps it's common to split the models, views and controllers into subfolders so keep the project
 * organized. This is especially true of views - it's not unheard of for large apps to have over a hundred separate
 * view classes so organizing them into folders can make maintenance much simpler.
 *
 * To specify dependencies in subfolders just use a period (".") to specify the folder:
 *
 *     Ext.application({
 *         name: 'MyApp',
 *
 *         controllers: ['Users', 'nested.MyController'],
 *         views: ['products.Show', 'products.Edit', 'user.Login']
 *     });
 *
 * In this case these 5 files will be loaded:
 *
 * * app/controller/Users.js
 * * app/controller/nested/MyController.js
 * * app/view/products/Show.js
 * * app/view/products/Edit.js
 * * app/view/user/Login.js
 *
 * Note that we can mix and match within each configuration here - for each model, view, controller, profile or store
 * you can specify either just the final part of the class name (if you follow the directory conventions), or the full
 * class name.
 *
 * ### External Dependencies
 *
 * Finally, we can specify application dependencies from outside our application by fully-qualifying the classes we
 * want to load. A common use case for this is sharing authentication logic between multiple applications. Perhaps you
 * have several apps that login via a common user database and you want to share that code between them. An easy way to
 * do this is to create a folder alongside your app folder and then add its contents as dependencies for your app.
 *
 * For example, let's say our shared login code contains a login controller, a user model and a login form view. We
 * want to use all of these in our application:
 *
 *     Ext.Loader.setPath({
 *         'Auth': 'Auth'
 *     });
 *
 *     Ext.application({
 *         views: ['Auth.view.LoginForm', 'Welcome'],
 *         controllers: ['Auth.controller.Sessions', 'Main'],
 *         models: ['Auth.model.User']
 *     });
 *
 * This will load the following files:
 *
 * * Auth/view/LoginForm.js
 * * Auth/controller/Sessions.js
 * * Auth/model/User.js
 * * app/view/Welcome.js
 * * app/controller/Main.js
 *
 * The first three were loaded from outside our application, the last two from the application itself. Note how we can
 * still mix and match application files and external dependency files.
 *
 * Note that to enable the loading of external dependencies we just have to tell the Loader where to find those files,
 * which is what we do with the Ext.Loader.setPath call above. In this case we're telling the Loader to find any class
 * starting with the 'Auth' namespace inside our 'Auth' folder. This means we can drop our common Auth code into our
 * application alongside the app folder and the framework will be able to figure out how to load everything.
 *
 * ## Launching
 *
 * Each Application can define a {@link Ext.app.Application#launch launch} function, which is called as soon as all of
 * your app's classes have been loaded and the app is ready to be launched. This is usually the best place to put any
 * application startup logic, typically creating the main view structure for your app.
 *
 * In addition to the Application launch function, there are two other places you can put app startup logic. Firstly,
 * each Controller is able to define an {@link Ext.app.Controller#init init} function, which is called before the
 * Application launch function. Secondly, if you are using Device Profiles, each Profile can define a
 * {@link Ext.app.Profile#launch launch} function, which is called after the Controller init functions but before the
 * Application launch function.
 *
 * Note that only the active Profile has its launch function called - for example if you define profiles for Phone and
 * Tablet and then launch the app on a tablet, only the Tablet Profile's launch function is called.
 *
 * 1. Controller#init functions called
 * 2. Profile#launch function called
 * 3. Application#launch function called
 * 4. Controller#launch functions called
 *
 * When using Profiles it is common to place most of the bootup logic inside the Profile launch function because each
 * Profile has a different set of views that need to be constructed at startup.
 *
 * ## Adding to Home Screen
 *
 * iOS devices allow your users to add your app to their home screen for easy access. iOS allows you to customize
 * several aspects of this, including the icon that will appear on the home screen and the startup image. These can be
 * specified in the Ext.application setup block:
 *
 *     Ext.application({
 *         name: 'MyApp',
 *
 *         {@link #icon}: 'resources/img/icon.png',
 *         {@link #glossOnIcon}: false,
 *         {@link #phoneStartupScreen}: 'resources/img/phone_startup.png',
 *         {@link #tabletStartupScreen}: 'resources/img/tablet_startup.png'
 *     });
 *
 * When the user adds your app to the home screen, your resources/img/icon.png file will be used as the application
 * icon. We also used the {@link #glossOnIcon} configuration to turn off the gloss effect that is automatically added
 * to icons in iOS. Finally we used the {@link #phoneStartupScreen} and {@link #tabletStartupScreen} configurations to
 * provide the images that will be displayed while your application is starting up. See also {@link #phoneIcon},
 * {@link #tabletIcon} and {@link #statusBarStyle}.
 *
 * ## Find out more
 *
 * If you are not already familiar with writing applications with Sencha Touch 2 we recommend reading the
 * <a href="#!/guide/apps_intro">intro to applications</a> guide, which lays out the core principles of writing apps
 * with Sencha Touch 2.
 *
 * @aside guide apps_intro
 * @aside guide first_app
 */
Ext.define('Ext.app.Application', {
    extend: 'Ext.app.Controller',

    requires: [
        'Ext.app.History',
        'Ext.app.Profile',
        'Ext.app.Router',
        'Ext.app.Action'
    ],

    config: {
        /**
         * @cfg {String/Object} icon Path to the .png image file to use when your app is added to the home screen on an
         * iOS device. When passed in as a String, the same icon will be used for both phone and tablet devices. To set
         * different icons for tablets and phones see the {@link #tabletIcon} and {@link #phoneIcon} configs.
         */

        /**
         * @cfg {String} tabletIcon Path to the .png image file to use when your app is added to the home screen on an
         * iOS **tablet** device (iPad).
         */

        /**
         * @cfg {String} phoneIcon Path to the .png image file to use when your app is added to the home screen on an
         * iOS **phone** device (iPhone or iPod).
         */

        /**
         * @cfg {Boolean} glossOnIcon If set to false, the 'gloss' effect added to home screen {@link #icon icons} on
         * iOS devices will be removed.
         */

        /**
         * @cfg {String} statusBarStyle Allows you to set the style of the status bar when your app is added to the
         * home screen on iOS devices. Defaults to 'black'. Alternative is to set to 'black-translucent', which turns
         * the status bar semi-transparent and overlaps the app content. This is usually not a good option for web apps
         */

        /**
         * @cfg {String} phoneStartupScreen Path to the .png image file that will be displayed while the app is
         * starting up once it has been added to the home screen of an iOS phone device (iPhone or iPod). This .png
         * file should be 320px wide and 460px high.
         */

        /**
         * @cfg {String} tabletStartupScreen Path to the .png image file that will be displayed while the app is
         * starting up once it has been added to the home screen of an iOS tablet device (iPad). This .png file should
         * be 768px wide and 1004px high.
         */

        /**
         * @cfg {Array} profiles The set of profiles to load for this Application. Each profile is expected to
         * exist inside the *app/profile* directory and define a class following the convention
         * AppName.profile.ProfileName. For example, in the code below, the classes *AppName.profile.Phone*
         * and *AppName.profile.Tablet* will be loaded. Note that we are able to specify
         * either the full class name (as with *AppName.profile.Tablet*) or just the final part of the class name
         * and leave Application to automatically prepend *AppName.profile.’* to each:
         *
         *     profiles: [
         *         'Phone',
         *         'AppName.profile.Tablet',
         *         'SomeCustomNamespace.profile.Desktop'
         *     ]
         * @accessor
         */
        profiles: [],

        /**
         * @cfg {Array} controllers The set of controllers to load for this Application. Each controller is expected to
         * exist inside the *app/controller* directory and define a class following the convention
         * AppName.controller.ControllerName. For example, in the code below, the classes *AppName.controller.Users*,
         * *AppName.controller.Groups* and *AppName.controller.Products* will be loaded. Note that we are able to specify
         * either the full class name (as with *AppName.controller.Products*) or just the final part of the class name
         * and leave Application to automatically prepend *AppName.controller.’* to each:
         *
         *     controllers: [
         *         'Users',
         *         'Groups',
         *         'AppName.controller.Products',
         *         'SomeCustomNamespace.controller.Orders'
         *     ]
         * @accessor
         */
        controllers: [],

        /**
         * @cfg {Ext.app.History} history The global {@link Ext.app.History History} instance attached to this
         * Application. Read only
         * @accessor
         */
        history: {},

        /**
         * @cfg {String} name The name of the Application. This should be a single word without spaces or periods
         * because it is used as the Application's global namespace. All classes in your application should be
         * namespaced undef the Application's name - for example if your application name is 'MyApp', your classes
         * should be named 'MyApp.model.User', 'MyApp.controller.Users', 'MyApp.view.Main' etc
         * @accessor
         */
        name: null,

        /**
         * @cfg {String} appFolder The path to the directory which contains all application's classes.
         * This path will be registered via {@link Ext.Loader#setPath} for the namespace specified in the {@link #name name} config.
         * Defaults to 'app'
         * @accessor
         */
        appFolder : 'app',

        /**
         * @cfg {Ext.app.Router} router The global {@link Ext.app.Router Router} instance attached to this Application.
         * Read only.
         * @accessor
         */
        router: {},

        /**
         * @cfg
         * @private
         * Used internally as the collection of instantiated controllers. Use {@link #getController} instead
         * @accessor
         */
        controllerInstances: [],

        /**
         * @cfg
         * @private
         * Used internally as the collection of instantiated profiles
         * @accessor
         */
        profileInstances: [],

        /**
         * @cfg {Ext.app.Profile} currentProfile The {@link Ext.app.Profile Profile} that is currently active for the
         * Application. This is set once, automatically by the Application before launch. Read only.
         * @accessor
         */
        currentProfile: null,

        /**
         * @cfg {Function} launch An optional function that will be called when the Application is ready to be
         * launched. This is normally used to render any initial UI required by your application
         * @accessor
         */
        launch: Ext.emptyFn,

        /**
         * @private
         * @cfg {Boolean} enableLoader Private config to disable loading of Profiles at application construct time.
         * This is used by Sencha's unit test suite to test Application.js in isolation and is likely to be removed
         * in favor of a more pleasing solution by the time you use it.
         * @accessor
         */
        enableLoader: true
    },

    /**
     * Constructs a new Application instance
     */
    constructor: function(config) {
        config = config || {};

        Ext.applyIf(config, {
            application: this
        });

        this.initConfig(config);

        //it's common to pass in functions to an application but because they are not predictable config names they
        //aren't ordinarily placed onto this so we need to do it manually
        for (var key in config) {
            this[key] = config[key];
        }


        if (this.getEnableLoader() !== false) {
            Ext.require(this.getProfiles(), this.onProfilesLoaded, this);
        }
    },

    /**
     * Dispatches a given {@link Ext.app.Action} to the relevant Controller instance. This is not usually called
     * directly by the developer, instead Sencha Touch's History support picks up on changes to the browser's url
     * and calls dispatch automatically.
     * @param {Ext.app.Action} action The action to dispatch
     * @param {Boolean} addToHistory True by default, sets the browser's url to the action's url
     */
    dispatch: function(action, addToHistory) {
        action = action || {};
        Ext.applyIf(action, {
            application: this
        });

        action = Ext.factory(action, Ext.app.Action);

        if (action) {
            var profile    = this.getCurrentProfile(),
                profileNS  = profile ? profile.getNamespace() : undefined,
                controller = this.getController(action.getController(), profileNS);

            if (controller) {
                if (addToHistory !== false) {
                    this.getHistory().add(action, true);
                }

                controller.execute(action);
            }
        }
    },

    /**
     * Redirects the browser to the given url. This only affects the url after the #. You can pass in either a String
     * or a Model instance - if a Model instance is defined its {@link Ext.data.Model#toUrl toUrl} function is called,
     * which returns a string representing the url for that model. Internally, this uses your application's
     * {@link Ext.app.Router Router} to decode the url into a matching controller action and then calls
     * {@link #dispatch}.
     * @param {String/Ext.data.Model} url The String url to redirect to
     */
    redirectTo: function(url) {
        if (Ext.data && Ext.data.Model && url instanceof Ext.data.Model) {
            var record = url;

            url = record.toUrl();
        }

        var decoded = this.getRouter().recognize(url);

        if (decoded) {
            decoded.url = url;
            if (record) {
                decoded.data = {};
                decoded.data.record = record;
            }
            return this.dispatch(decoded);
        }
    },

    /**
     * @private
     * (documented on Controller's control config)
     */
    control: function(selectors, controller) {
        //if the controller is not defined, use this instead (the application instance)
        controller = controller || this;

        var dispatcher = this.getEventDispatcher(),
            refs = (controller) ? controller.getRefs() : {},
            selector, eventName, listener, listeners, ref;

        for (selector in selectors) {
            if (selectors.hasOwnProperty(selector)) {
                listeners = selectors[selector];
                ref = refs[selector];

                //refs can be used in place of selectors
                if (ref) {
                    selector = ref.selector || ref;
                }
                for (eventName in listeners) {
                    listener = listeners[eventName];

                    if (Ext.isString(listener)) {
                        listener = controller[listener];
                    }

                    dispatcher.addListener('component', selector, eventName, listener, controller);
                }
            }
        }
    },

    /**
     * @private
     * Returns the Controller instance for the given controller name
     * @param {String} name The name of the Controller
     * @param {String} profileName Optional profile name. If passed, this is the same as calling
     * getController('profileName.controllerName')
     */
    getController: function(name, profileName) {
        var instances = this.getControllerInstances(),
            appName   = this.getName(),
            format    = Ext.String.format,
            topLevelName;

        if (name instanceof Ext.app.Controller) {
            return name;
        }

        if (instances[name]) {
            return instances[name];
        } else {
            topLevelName = format("{0}.controller.{1}", appName, name);
            profileName  = format("{0}.controller.{1}.{2}", appName, profileName, name);

            return instances[profileName] || instances[topLevelName];
        }
    },

    /**
     * @private
     * Callback that is invoked when all of the configured Profiles have been loaded. Detects the current profile and
     * gathers any additional dependencies from that profile, then loads all of those dependencies.
     */
    onProfilesLoaded: function() {
        var profiles  = this.getProfiles(),
            length    = profiles.length,
            instances = [],
            requires  = this.gatherDependencies(),
            current, i, profileDeps;

        for (i = 0; i < length; i++) {
            instances[i] = Ext.create(profiles[i], {
                application: this
            });

            /*
             * Note that we actually require all of the dependencies for all Profiles - this is so that we can produce
             * a single build file that will work on all defined Profiles. Although the other classes will be loaded,
             * the correct Profile will still be identified and the other classes ignored. While this feels somewhat
             * inefficient, the majority of the bulk of an application is likely to be the framework itself. The bigger
             * the app though, the bigger the effect of this inefficiency so ideally we will create a way to create and
             * load Profile-specific builds in a future release.
             */
            profileDeps = instances[i].getDependencies();
            requires = requires.concat(profileDeps.all);

            if (instances[i].isActive() && !current) {
                current = instances[i];

                this.setCurrentProfile(current);

                this.setControllers(this.getControllers().concat(profileDeps.controller));
                this.setModels(this.getModels().concat(profileDeps.model));
                this.setViews(this.getViews().concat(profileDeps.view));
                this.setStores(this.getStores().concat(profileDeps.store));
            }
        }

        this.setProfileInstances(instances);
        Ext.require(requires, this.loadControllerDependencies, this);
    },

    /**
     * @private
     * Controllers can also specify dependencies, so we grab them all here and require them
     */
    loadControllerDependencies: function() {
        this.instantiateStores();
        this.instantiateControllers();

        var controllers = this.getControllerInstances(),
            classes = [],
            i, controller, name;

        for (name in controllers) {
            controller = controllers[name];

            classes = classes.concat(controller.getModels().concat(controller.getViews()).concat(controller.getStores()));
        }

        Ext.require(classes, this.onDependenciesLoaded, this);
    },

    /**
     * @private
     * Callback that is invoked when all of the Application, Controller and Profile dependencies have been loaded.
     * Launches the controllers, then the profile and application
     */
    onDependenciesLoaded: function() {
        var me = this,
            profile = this.getCurrentProfile(),
            launcher = this.getLaunch(),
            controllers, name;


        controllers = this.getControllerInstances();

        for (name in controllers) {
            controllers[name].init(this);
        }

        if (profile) {
            profile.launch();
        }

        launcher.call(me);

        for (name in controllers) {
            if (controllers[name] && !(controllers[name] instanceof Ext.app.Controller)) {
                Ext.Logger.warn("The controller '" + name + "' doesn't have a launch method. Are you sure it extends from Ext.app.Controller?");
            } else {
                controllers[name].launch(this);
            }
        }

        me.redirectTo(window.location.hash.substr(1));
    },

    /**
     * @private
     * Gathers up all of the previously computed MVCS dependencies into a single array that we can pass to Ext.require
     */
    gatherDependencies: function() {
        var classes = this.getModels().concat(this.getViews()).concat(this.getControllers());

        Ext.each(this.getStores(), function(storeName) {
            if (Ext.isString(storeName)) {
                classes.push(storeName);
            }
        }, this);

        return classes;
    },

    /**
     * @private
     * Should be called after dependencies are loaded, instantiates all of the Stores specified in the {@link #stores}
     * config. For each item in the stores array we make sure the Store is instantiated. When strings are specified,
     * the corresponding app/store/StoreName.js was loaded so we now instantiate a MyApp.store.StoreName, giving it the
     * id StoreName.
     */
    instantiateStores: function() {
        var stores  = this.getStores(),
            length  = stores.length,
            store, storeClass, storeName, splits, i;

        for (i = 0; i < length; i++) {
            store = stores[i];

            if (Ext.data && Ext.data.Store && !(store instanceof Ext.data.Store)) {
                if (Ext.isString(store)) {
                    storeName = store;
                    storeClass = Ext.ClassManager.classes[store];

                    store = {
                        xclass: store
                    };

                    //we don't want to wipe out a configured storeId in the app's Store subclass so need
                    //to check for this first
                    if (storeClass.prototype.defaultConfig.storeId === undefined) {
                        splits = storeName.split('.');
                        store.id = splits[splits.length - 1];
                    }
                }

                stores[i] = Ext.factory(store, Ext.data.Store);
            }
        }

        this.setStores(stores);
    },

    /**
     * @private
     * Called once all of our controllers have been loaded
     */
    instantiateControllers: function() {
        var controllerNames = this.getControllers(),
            instances = {},
            length = controllerNames.length,
            name, i;

        for (i = 0; i < length; i++) {
            name = controllerNames[i];

            instances[name] = Ext.create(name, {
                application: this
            });
        }

        return this.setControllerInstances(instances);
    },

    /**
     * @private
     * As a convenience developers can locally qualify controller names (e.g. 'MyController' vs
     * 'MyApp.controller.MyController'). This just makes sure everything ends up fully qualified
     */
    applyControllers: function(controllers) {
        return this.getFullyQualified(controllers, 'controller');
    },

    /**
     * @private
     * As a convenience developers can locally qualify profile names (e.g. 'MyProfile' vs
     * 'MyApp.profile.MyProfile'). This just makes sure everything ends up fully qualified
     */
    applyProfiles: function(profiles) {
        return this.getFullyQualified(profiles, 'profile');
    },

    /**
     * @private
     * Checks that the name configuration has any whitespace, and trims them if found.
     */
    applyName: function(name) {
        var oldName;
        if (name && name.match(/ /g)) {
            oldName = name;
            name = name.replace(/ /g, "");

            Ext.Logger.warn('Attempting to create an application with a name which contains whitespace ("' + oldName + '"). Renamed to "' + name + '".');
        }

        return name;
    },

    /**
     * @private
     * Makes sure the app namespace exists, sets the `app` property of the namespace to this application and sets its
     * loading path (checks to make sure the path hadn't already been set via Ext.Loader.setPath)
     */
    updateName: function(newName) {
        Ext.ClassManager.setNamespace(newName + '.app', this);

        if (!Ext.Loader.config.paths[newName]) {
            Ext.Loader.setPath(newName, this.getAppFolder());
        }
    },

    /**
     * @private
     */
    applyRouter: function(config) {
        return Ext.factory(config, Ext.app.Router, this.getRouter());
    },

    /**
     * @private
     */
    applyHistory: function(config) {
        var history = Ext.factory(config, Ext.app.History, this.getHistory());

        history.on('change', this.onHistoryChange, this);

        return history;
    },

    /**
     * @private
     */
    onHistoryChange: function(url) {
        this.dispatch(this.getRouter().recognize(url), false);
    }
}, function() {
});

/**
 * @author Ed Spencer
 * @class Ext.data.Batch
 *
 * <p>Provides a mechanism to run one or more {@link Ext.data.Operation operations} in a given order. Fires the 'operationcomplete' event
 * after the completion of each Operation, and the 'complete' event when all Operations have been successfully executed. Fires an 'exception'
 * event if any of the Operations encounter an exception.</p>
 *
 * <p>Usually these are only used internally by {@link Ext.data.proxy.Proxy} classes</p>
 *
 */
Ext.define('Ext.data.Batch', {
    mixins: {
        observable: 'Ext.mixin.Observable'
    },

    config: {
        /**
         * @cfg {Boolean} autoStart true to immediately start processing the batch as soon as it is constructed (defaults to false)
         */
        autoStart: false,

        /**
         * @cfg {Boolean} pauseOnException true to automatically pause the execution of the batch if any operation encounters an exception (defaults to true)
         */
        pauseOnException: true,

        /**
         * @cfg {Ext.data.Proxy} proxy The proxy this Batch belongs to. Used to make the requests for each operation in the Batch.
         */
        proxy: null
    },

    /**
     * The index of the current operation being executed
     * @property current
     * @type Number
     */
    current: -1,

    /**
     * The total number of operations in this batch. Read only
     * @property total
     * @type Number
     */
    total: 0,

    /**
     * True if the batch is currently running
     * @property isRunning
     * @type Boolean
     */
    isRunning: false,

    /**
     * True if this batch has been executed completely
     * @property isComplete
     * @type Boolean
     */
    isComplete: false,

    /**
     * True if this batch has encountered an exception. This is cleared at the start of each operation
     * @property hasException
     * @type Boolean
     */
    hasException: false,

    /**
     * @event complete
     * Fired when all operations of this batch have been completed
     * @param {Ext.data.Batch} batch The batch object
     * @param {Object} operation The last operation that was executed
     */

    /**
     * @event exception
     * Fired when a operation encountered an exception
     * @param {Ext.data.Batch} batch The batch object
     * @param {Object} operation The operation that encountered the exception
     */

    /**
     * @event operationcomplete
     * Fired when each operation of the batch completes
     * @param {Ext.data.Batch} batch The batch object
     * @param {Object} operation The operation that just completed
     */

    /**
     * Creates new Batch object.
     * @param {Object} config (optional) Config object
     */
    constructor: function(config) {
        var me = this;

        me.initConfig(config);

        /**
         * Ordered array of operations that will be executed by this batch
         * @property {Ext.data.Operation[]} operations
         */
        me.operations = [];
    },

    /**
     * Adds a new operation to this batch
     * @param {Object} operation The {@link Ext.data.Operation Operation} object
     */
    add: function(operation) {
        this.total++;

        operation.setBatch(this);

        this.operations.push(operation);
    },

    /**
     * Kicks off the execution of the batch, continuing from the next operation if the previous
     * operation encountered an exception, or if execution was paused
     */
    start: function() {
        this.hasException = false;
        this.isRunning = true;

        this.runNextOperation();
    },

    /**
     * @private
     * Runs the next operation, relative to this.current.
     */
    runNextOperation: function() {
        this.runOperation(this.current + 1);
    },

    /**
     * Pauses execution of the batch, but does not cancel the current operation
     */
    pause: function() {
        this.isRunning = false;
    },

    /**
     * Executes a operation by its numeric index
     * @param {Number} index The operation index to run
     */
    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.fireEvent('exception', me, operation);
                } else {
                    me.fireEvent('operationcomplete', me, operation);
                }

                if (hasException && me.getPauseOnException()) {
                    me.pause();
                } else {
                    operation.setCompleted();
                    me.runNextOperation();
                }
            };

            operation.setStarted();

            me.getProxy()[operation.getAction()](operation, onProxyReturn, me);
        }
    }
});
/**
 * The Connection class encapsulates a connection to the page's originating domain, allowing requests to be made either
 * to a configured URL, or to a URL specified at request time.
 *
 * Requests made by this class are asynchronous, and will return immediately. No data from the server will be available
 * to the statement immediately following the {@link #request} call. To process returned data, use a success callback
 * in the request options object, or an {@link #requestcomplete event listener}.
 *
 * # File Uploads
 *
 * File uploads are not performed using normal "Ajax" techniques, that is they are not performed using XMLHttpRequests.
 * Instead the form is submitted in the standard manner with the DOM &lt;form&gt; element temporarily modified to have its
 * target set to refer to a dynamically generated, hidden &lt;iframe&gt; which is inserted into the document but removed
 * after the return data has been gathered.
 *
 * The server response is parsed by the browser to create the document for the IFRAME. If the server is using JSON to
 * send the return object, then the Content-Type header must be set to "text/html" in order to tell the browser to
 * insert the text unchanged into the document body.
 *
 * Characters which are significant to an HTML parser must be sent as HTML entities, so encode `<` as `&lt;`, `&` as
 * `&amp;` etc.
 *
 * The response text is retrieved from the document, and a fake XMLHttpRequest object is created containing a
 * responseText property in order to conform to the requirements of event handlers and callbacks.
 *
 * Be aware that file upload packets are sent with the content type multipart/form and some server technologies
 * (notably JEE) may require some custom processing in order to retrieve parameter names and parameter values from the
 * packet content.
 *
 * Also note that it's not possible to check the response code of the hidden iframe, so the success handler will ALWAYS fire.
 */
Ext.define('Ext.data.Connection', {
    mixins: {
        observable: 'Ext.util.Observable'
    },

    statics: {
        requestId: 0
    },

    config: {
        /**
         * @cfg {String} url
         * The default URL to be used for requests to the server.
         * @accessor
         */
        url: null,

        async: true,

        /**
         * @cfg {String} [method=undefined]
         * The default HTTP method to be used for requests. Note that this is case-sensitive and
         * should be all caps.
         *
         * Defaults to undefined; if not set but params are present will use "POST", otherwise "GET".
         */
        method: null,

        username: '',
        password: '',

        /**
         * @cfg {Boolean} disableCaching
         * True to add a unique cache-buster param to GET requests.
         * @accessor
         */
        disableCaching: true,

        /**
         * @cfg {String} disableCachingParam
         * Change the parameter which is sent went disabling caching through a cache buster.
         * @accessor
         */
        disableCachingParam: '_dc',

        /**
         * @cfg {Number} timeout
         * The timeout in milliseconds to be used for requests.
         * @accessor
         */
        timeout : 30000,

        /**
         * @cfg {Object} extraParams
         * Any parameters to be appended to the request.
         * @accessor
         */
        extraParams: null,

        /**
         * @cfg {Object} defaultHeaders
         * An object containing request headers which are added to each request made by this object.
         * @accessor
         */
        defaultHeaders: null,

        useDefaultHeader : true,
        defaultPostHeader : 'application/x-www-form-urlencoded; charset=UTF-8',
        useDefaultXhrHeader : true,
        defaultXhrHeader : 'XMLHttpRequest',

        autoAbort: false
    },

    textAreaRe: /textarea/i,
    multiPartRe: /multipart\/form-data/i,
    lineBreakRe: /\r\n/g,

    constructor : function(config) {
        this.initConfig(config);

        /**
         * @event beforerequest
         * Fires before a network request is made to retrieve a data object.
         * @param {Ext.data.Connection} conn This Connection object.
         * @param {Object} options The options config object passed to the {@link #request} method.
         */
        /**
         * @event requestcomplete
         * Fires if the request was successfully completed.
         * @param {Ext.data.Connection} conn This Connection object.
         * @param {Object} response The XHR object containing the response data.
         * See [The XMLHttpRequest Object](http://www.w3.org/TR/XMLHttpRequest/) for details.
         * @param {Object} options The options config object passed to the {@link #request} method.
         */
        /**
         * @event requestexception
         * Fires if an error HTTP status was returned from the server.
         * See [HTTP Status Code Definitions](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)
         * for details of HTTP status codes.
         * @param {Ext.data.Connection} conn This Connection object.
         * @param {Object} response The XHR object containing the response data.
         * See [The XMLHttpRequest Object](http://www.w3.org/TR/XMLHttpRequest/) for details.
         * @param {Object} options The options config object passed to the {@link #request} method.
         */
        this.requests = {};
    },

    /**
     * Sends an HTTP request to a remote server.
     *
     * **Important:** Ajax server requests are asynchronous, and this call will
     * return before the response has been received. Process any returned data
     * in a callback function.
     *
     *     Ext.Ajax.request({
     *         url: 'ajax_demo/sample.json',
     *         success: function(response, opts) {
     *             var obj = Ext.decode(response.responseText);
     *             console.dir(obj);
     *         },
     *         failure: function(response, opts) {
     *             console.log('server-side failure with status code ' + response.status);
     *         }
     *     });
     *
     * To execute a callback function in the correct scope, use the `scope` option.
     *
     * @param {Object} options An object which may contain the following properties:
     *
     * (The options object may also contain any other property which might be needed to perform
     * postprocessing in a callback because it is passed to callback functions.)
     *
     * @param {String/Function} options.url The URL to which to send the request, or a function
     * to call which returns a URL string. The scope of the function is specified by the `scope` option.
     * Defaults to the configured `url`.
     *
     * @param {Object/String/Function} options.params An object containing properties which are
     * used as parameters to the request, a url encoded string or a function to call to get either. The scope
     * of the function is specified by the `scope` option.
     *
     * @param {String} options.method The HTTP method to use
     * for the request. Defaults to the configured method, or if no method was configured,
     * "GET" if no parameters are being sent, and "POST" if parameters are being sent.  Note that
     * the method name is case-sensitive and should be all caps.
     *
     * @param {Function} options.callback The function to be called upon receipt of the HTTP response.
     * The callback is called regardless of success or failure and is passed the following parameters:
     * @param {Object} options.callback.options The parameter to the request call.
     * @param {Boolean} options.callback.success True if the request succeeded.
     * @param {Object} options.callback.response The XMLHttpRequest object containing the response data.
     * See [www.w3.org/TR/XMLHttpRequest/](http://www.w3.org/TR/XMLHttpRequest/) for details about
     * accessing elements of the response.
     *
     * @param {Function} options.success The function to be called upon success of the request.
     * The callback is passed the following parameters:
     * @param {Object} options.success.response The XMLHttpRequest object containing the response data.
     * @param {Object} options.success.options The parameter to the request call.
     *
     * @param {Function} options.failure The function to be called upon success of the request.
     * The callback is passed the following parameters:
     * @param {Object} options.failure.response The XMLHttpRequest object containing the response data.
     * @param {Object} options.failure.options The parameter to the request call.
     *
     * @param {Object} options.scope The scope in which to execute the callbacks: The "this" object for
     * the callback function. If the `url`, or `params` options were specified as functions from which to
     * draw values, then this also serves as the scope for those function calls. Defaults to the browser
     * window.
     *
     * @param {Number} options.timeout The timeout in milliseconds to be used for this request.
     * Defaults to 30 seconds.
     *
     * @param {HTMLElement/HTMLElement/String} options.form The `<form>` Element or the id of the `<form>`
     * to pull parameters from.
     *
     * @param {Boolean} options.isUpload **Only meaningful when used with the `form` option.**
     *
     * True if the form object is a file upload (will be set automatically if the form was configured
     * with **`enctype`** `"multipart/form-data"`).
     *
     * File uploads are not performed using normal "Ajax" techniques, that is they are **not**
     * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
     * DOM `<form>` element temporarily modified to have its [target][] set to refer to a dynamically
     * generated, hidden `<iframe>` which is inserted into the document but removed after the return data
     * has been gathered.
     *
     * The server response is parsed by the browser to create the document for the IFRAME. If the
     * server is using JSON to send the return object, then the [Content-Type][] header must be set to
     * "text/html" in order to tell the browser to insert the text unchanged into the document body.
     *
     * The response text is retrieved from the document, and a fake XMLHttpRequest object is created
     * containing a `responseText` property in order to conform to the requirements of event handlers
     * and callbacks.
     *
     * Be aware that file upload packets are sent with the content type [multipart/form][] and some server
     * technologies (notably JEE) may require some custom processing in order to retrieve parameter names
     * and parameter values from the packet content.
     *
     * [target]: http://www.w3.org/TR/REC-html40/present/frames.html#adef-target
     * [Content-Type]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17
     * [multipart/form]: http://www.faqs.org/rfcs/rfc2388.html
     *
     * @param {Object} options.headers Request headers to set for the request.
     *
     * @param {Object} options.xmlData XML document to use for the post. Note: This will be used instead
     * of params for the post data. Any params will be appended to the URL.
     *
     * @param {Object/String} options.jsonData JSON data to use as the post. Note: This will be used
     * instead of params for the post data. Any params will be appended to the URL.
     *
     * @param {Boolean} options.disableCaching True to add a unique cache-buster param to GET requests.
     *
     * @return {Object} The request object. This may be used to cancel the request.
     */
    request : function(options) {
        options = options || {};
        var me = this,
            scope = options.scope || window,
            username = options.username || me.getUsername(),
            password = options.password || me.getPassword() || '',
            async, requestOptions, request, headers, xhr;

        if (me.fireEvent('beforerequest', me, options) !== false) {
            requestOptions = me.setOptions(options, scope);

            if (this.isFormUpload(options) === true) {
                this.upload(options.form, requestOptions.url, requestOptions.data, options);
                return null;
            }

            // if autoabort is set, cancel the current transactions
            if (options.autoAbort === true || me.getAutoAbort()) {
                me.abort();
            }

            // create a connection object
            xhr = this.getXhrInstance();

            async = options.async !== false ? (options.async || me.getAsync()) : false;

            // open the request
            if (username) {
                xhr.open(requestOptions.method, requestOptions.url, async, username, password);
            } else {
                xhr.open(requestOptions.method, requestOptions.url, async);
            }

            headers = me.setupHeaders(xhr, options, requestOptions.data, requestOptions.params);

            // create the transaction object
            request = {
                id: ++this.self.requestId,
                xhr: xhr,
                headers: headers,
                options: options,
                async: async,
                timeout: setTimeout(function() {
                    request.timedout = true;
                    me.abort(request);
                }, options.timeout || me.getTimeout())
            };
            me.requests[request.id] = request;

            // bind our statechange listener
            if (async) {
                xhr.onreadystatechange = Ext.Function.bind(me.onStateChange, me, [request]);
            }

            // start the request!
            xhr.send(requestOptions.data);
            if (!async) {
                return this.onComplete(request);
            }
            return request;
        } else {
            Ext.callback(options.callback, options.scope, [options, undefined, undefined]);
            return null;
        }
    },

    /**
     * Uploads a form using a hidden iframe.
     * @param {String/HTMLElement/Ext.Element} form The form to upload
     * @param {String} url The url to post to
     * @param {String} params Any extra parameters to pass
     * @param {Object} options The initial options
     */
    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;

        /*
         * Originally this behaviour was modified for Opera 10 to apply the secure URL after
         * the frame had been added to the document. It seems this has since been corrected in
         * Opera so the behaviour has been reverted, the URL will be set before being added.
         */
        Ext.fly(frame).set({
            id: id,
            name: id,
            cls: Ext.baseCSSPrefix + 'hide-display',
            src: Ext.SSL_SECURE_URL
        });

        document.body.appendChild(frame);

        // This is required so that IE doesn't pop the response up in a new window.
        if (document.frames) {
            document.frames[id].name = id;
        }

        Ext.fly(form).set({
            target: id,
            method: 'POST',
            enctype: encoding,
            encoding: encoding,
            action: url || buf.action
        });

        // add dynamic params
        if (params) {
            Ext.iterate(Ext.Object.fromQueryString(params), function(name, value) {
                if (Ext.isArray(value)) {
                    Ext.each(value, function(v) {
                        addField(name, v);
                    });
                } else {
                    addField(name, value);
                }
            });
        }

        Ext.fly(frame).on('load', Ext.Function.bind(this.onUploadComplete, this, [frame, options]), null, {single: true});
        form.submit();

        Ext.fly(form).set(buf);
        Ext.each(hiddens, function(h) {
            Ext.removeNode(h);
        });
    },

    onUploadComplete: function(frame, options) {
        var me = this,
            // bogus response object
            response = {
                responseText: '',
                responseXML: null
            }, doc, firstChild;

        try {
            doc = frame.contentWindow.document || frame.contentDocument || window.frames[id].document;
            if (doc) {
                if (doc.body) {
                    if (this.textAreaRe.test((firstChild = doc.body.firstChild || {}).tagName)) { // json response wrapped in textarea
                        response.responseText = firstChild.value;
                    } else {
                        response.responseText = doc.body.innerHTML;
                    }
                }
                //in IE the document may still have a body even if returns XML.
                response.responseXML = doc.XMLDocument || doc;
            }
        } catch (e) {
        }

        me.fireEvent('requestcomplete', me, response, options);

        Ext.callback(options.success, options.scope, [response, options]);
        Ext.callback(options.callback, options.scope, [options, true, response]);

        setTimeout(function() {
            Ext.removeNode(frame);
        }, 100);
    },

    /**
     * Detects whether the form is intended to be used for an upload.
     * @private
     */
    isFormUpload: function(options) {
        var form = this.getForm(options);
        if (form) {
            return (options.isUpload || (this.multiPartRe).test(form.getAttribute('enctype')));
        }
        return false;
    },

    /**
     * Gets the form object from options.
     * @private
     * @param {Object} options The request options
     * @return {HTMLElement} The form, null if not passed
     */
    getForm: function(options) {
        return Ext.getDom(options.form) || null;
    },

    /**
     * Sets various options such as the url, params for the request
     * @param {Object} options The initial options
     * @param {Object} scope The scope to execute in
     * @return {Object} The params for the request
     */
    setOptions: function(options, scope) {
        var me = this,
            params = options.params || {},
            extraParams = me.getExtraParams(),
            urlParams = options.urlParams,
            url = options.url || me.getUrl(),
            jsonData = options.jsonData,
            method,
            disableCache,
            data;

        // allow params to be a method that returns the params object
        if (Ext.isFunction(params)) {
            params = params.call(scope, options);
        }

        // allow url to be a method that returns the actual url
        if (Ext.isFunction(url)) {
            url = url.call(scope, options);
        }

        url = this.setupUrl(options, url);

        if (!url) {
            Ext.Logger.error('No URL specified');
        }

        // check for xml or json data, and make sure json data is encoded
        data = options.rawData || options.xmlData || jsonData || null;
        if (jsonData && !Ext.isPrimitive(jsonData)) {
            data = Ext.encode(data);
        }

        // make sure params are a url encoded string and include any extraParams if specified
        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);

        // decide the proper method for this request
        method = (options.method || me.getMethod() || ((params || data) ? 'POST' : 'GET')).toUpperCase();
        this.setupMethod(options, method);


        disableCache = options.disableCaching !== false ? (options.disableCaching || me.getDisableCaching()) : false;
        // if the method is get append date to prevent caching
        if (method === 'GET' && disableCache) {
            url = Ext.urlAppend(url, (options.disableCachingParam || me.getDisableCachingParam()) + '=' + (new Date().getTime()));
        }

        // if the method is get or there is json/xml data append the params to the url
        if ((method == 'GET' || data) && params) {
            url = Ext.urlAppend(url, params);
            params = null;
        }

        // allow params to be forced into the url
        if (urlParams) {
            url = Ext.urlAppend(url, urlParams);
        }

        return {
            url: url,
            method: method,
            data: data || params || null
        };
    },

    /**
     * Template method for overriding url
     * @private
     * @param {Object} options
     * @param {String} url
     * @return {String} The modified url
     */
    setupUrl: function(options, url) {
        var form = this.getForm(options);
        if (form) {
            url = url || form.action;
        }
        return url;
    },


    /**
     * Template method for overriding params
     * @private
     * @param {Object} options
     * @param {String} params
     * @return {String} The modified params
     */
    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;
    },

    /**
     * Template method for overriding method
     * @private
     * @param {Object} options
     * @param {String} method
     * @return {String} The modified method
     */
    setupMethod: function(options, method) {
        if (this.isFormUpload(options)) {
            return 'POST';
        }
        return method;
    },

    /**
     * Setup all the headers for the request
     * @private
     * @param {Object} xhr The xhr object
     * @param {Object} options The options for the request
     * @param {Object} data The data for the request
     * @param {Object} params The params for the request
     */
    setupHeaders: function(xhr, options, data, params) {
        var me = this,
            headers = Ext.apply({}, options.headers || {}, me.getDefaultHeaders() || {}),
            contentType = me.getDefaultPostHeader(),
            jsonData = options.jsonData,
            xmlData = options.xmlData,
            key,
            header;

        if (!headers['Content-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['Content-Type'] = contentType;
        }

        if (me.getUseDefaultXhrHeader() && !headers['X-Requested-With']) {
            headers['X-Requested-With'] = me.getDefaultXhrHeader();
        }
        // set up all the request headers on the xhr object
        try {
            for (key in headers) {
                if (headers.hasOwnProperty(key)) {
                    header = headers[key];
                    xhr.setRequestHeader(key, header);
                }

            }
        } catch(e) {
            me.fireEvent('exception', key, header);
        }

        if (options.withCredentials) {
            xhr.withCredentials = options.withCredentials;
        }

        return headers;
    },

    /**
     * Creates the appropriate XHR transport for the browser.
     * @private
     */
    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;
    })(),

    /**
     * Determines whether this object has a request outstanding.
     * @param {Object} request The request to check
     * @return {Boolean} True if there is an outstanding request.
     */
    isLoading : function(request) {
        if (!(request && request.xhr)) {
            return false;
        }
        // if there is a connection and readyState is not 0 or 4
        var state = request.xhr.readyState;
        return !(state === 0 || state == 4);
    },

    /**
     * Aborts any outstanding request.
     * @param {Object} request (Optional) defaults to the last request
     */
    abort : function(request) {
        var me = this,
            requests = me.requests,
            id;

        if (request && me.isLoading(request)) {
            /*
             * Clear out the onreadystatechange here, this allows us
             * greater control, the browser may/may not fire the function
             * depending on a series of conditions.
             */
            request.xhr.onreadystatechange = null;
            request.xhr.abort();
            me.clearTimeout(request);
            if (!request.timedout) {
                request.aborted = true;
            }
            me.onComplete(request);
            me.cleanup(request);
        } else if (!request) {
            for (id in requests) {
                if (requests.hasOwnProperty(id)) {
                    me.abort(requests[id]);
                }
            }
        }
    },
    
    /**
     * Aborts all outstanding requests
     */
    abortAll: function() {
        this.abort();
    },

    /**
     * Fires when the state of the xhr changes
     * @private
     * @param {Object} request The request
     */
    onStateChange : function(request) {
        if (request.xhr.readyState == 4) {
            this.clearTimeout(request);
            this.onComplete(request);
            this.cleanup(request);
        }
    },

    /**
     * Clears the timeout on the request
     * @private
     * @param {Object} The request
     */
    clearTimeout: function(request) {
        clearTimeout(request.timeout);
        delete request.timeout;
    },

    /**
     * Cleans up any left over information from the request
     * @private
     * @param {Object} The request
     */
    cleanup: function(request) {
        request.xhr = null;
        delete request.xhr;
    },

    /**
     * To be called when the request has come back from the server
     * @private
     * @param {Object} request
     * @return {Object} The response
     */
    onComplete : function(request) {
        var me = this,
            options = request.options,
            result,
            success,
            response;

        try {
            result = me.parseStatus(request.xhr.status);
            
            if (request.timedout) {
                result.success = false;
            }
        } catch (e) {
            // in some browsers we can't access the status if the readyState is not 4, so the request has failed
            result = {
                success : false,
                isException : false
            };
        }
        success = 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;
    },

    /**
     * Checks if the response status was successful
     * @param {Number} status The status code
     * @return {Object} An object containing success/status state
     */
    parseStatus: function(status) {
        // see: https://prototype.lighthouseapp.com/projects/8886/tickets/129-ie-mangles-http-response-status-code-204-to-1223
        status = status == 1223 ? 204 : status;

        var success = (status >= 200 && status < 300) || status == 304 || status == 0,
            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
        };
    },

    /**
     * Creates the response object
     * @private
     * @param {Object} request
     */
    createResponse : function(request) {
        var xhr = request.xhr,
            headers = {},
            lines, count, line, index, key, response;

        //we need to make this check here because if a request times out an exception is thrown
        //when calling getAllResponseHeaders() because the response never came back to populate it
        if (request.timedout || request.aborted) {
            request.success = false;
            lines = [];
        } else {
            lines = xhr.getAllResponseHeaders().replace(this.lineBreakRe, '\n').split('\n');
        }

        count = lines.length;

        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;
            },
            responseText : xhr.responseText,
            responseXML : xhr.responseXML
        };

        // If we don't explicitly tear down the xhr reference, IE6/IE7 will hold this in the closure of the
        // functions created with getResponseHeader/getAllResponseHeaders
        xhr = null;
        return response;
    },

    /**
     * Creates the exception object
     * @private
     * @param {Object} request
     */
    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
        };
    }
});

/**
 * A singleton instance of an {@link Ext.data.Connection}. This class
 * is used to communicate with your server side code. It can be used as follows:
 *
 *     Ext.Ajax.request({
 *         url: 'page.php',
 *         params: {
 *             id: 1
 *         },
 *         success: function(response){
 *             var text = response.responseText;
 *             // process server response here
 *         }
 *     });
 *
 * Default options for all requests can be set by changing a property on the Ext.Ajax class:
 *
 *     Ext.Ajax.timeout = 60000; // 60 seconds
 *
 * Any options specified in the request method for the Ajax request will override any
 * defaults set on the Ext.Ajax class. In the code sample below, the timeout for the
 * request will be 60 seconds.
 *  
 *     Ext.Ajax.timeout = 120000; // 120 seconds
 *     Ext.Ajax.request({
 *         url: 'page.aspx',
 *         timeout: 60000
 *     });
 *
 * In general, this class will be used for all Ajax requests in your application.
 * The main reason for creating a separate {@link Ext.data.Connection} is for a
 * series of requests that share common settings that are different to all other
 * requests in the application.
 * 
 * @aside guide ajax
 */
Ext.define('Ext.Ajax', {
    extend: 'Ext.data.Connection',
    singleton: true,

    /**
     * @property {Boolean} autoAbort
     * Whether a new request should abort any pending requests.
     */
    autoAbort : false
});
/**
 * @author Ed Spencer
 *
 * Readers are used to interpret data to be loaded into a {@link Ext.data.Model Model} instance or a {@link
 * Ext.data.Store Store} - often in response to an AJAX request. In general there is usually no need to create
 * a Reader instance directly, since a Reader is almost always used together with a {@link Ext.data.proxy.Proxy Proxy},
 * and is configured using the Proxy's {@link Ext.data.proxy.Proxy#cfg-reader reader} configuration property:
 *
 *     Ext.create('Ext.data.Store', {
 *         model: 'User',
 *         proxy: {
 *             type: 'ajax',
 *             url : 'users.json',
 *             reader: {
 *                 type: 'json',
 *                 rootPorperty: 'users'
 *             }
 *         },
 *     });
 *
 * The above reader is configured to consume a JSON string that looks something like this:
 *
 *     {
 *         "success": true,
 *         "users": [
 *             { "name": "User 1" },
 *             { "name": "User 2" }
 *         ]
 *     }
 *
 *
 * # Loading Nested Data
 *
 * Readers have the ability to automatically load deeply-nested data objects based on the {@link Ext.data.association.Association
 * associations} configured on each Model. Below is an example demonstrating the flexibility of these associations in a
 * fictional CRM system which manages a User, their Orders, OrderItems and Products. First we'll define the models:
 *
 *     Ext.define("User", {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: [
 *                 'id', 'name'
 *             ],
 *
 *             hasMany: {model: 'Order', name: 'orders'},
 *
 *             proxy: {
 *                 type: 'rest',
 *                 url : 'users.json',
 *                 reader: {
 *                     type: 'json',
 *                     root: 'users'
 *                 }
 *             }
 *         }
 *     });
 *
 *     Ext.define("Order", {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: [
 *                 'id', 'total'
 *             ],
 *
 *             hasMany  : {model: 'OrderItem', name: 'orderItems', associationKey: 'order_items'},
 *             belongsTo: 'User'
 *         }
 *     });
 *
 *     Ext.define("OrderItem", {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: [
 *                 'id', 'price', 'quantity', 'order_id', 'product_id'
 *             ],
 *
 *             belongsTo: ['Order', {model: 'Product', associationKey: 'product'}]
 *         }
 *     });
 *
 *     Ext.define("Product", {
 *         extend: 'Ext.data.Model',
 *         fields: [
 *             'id', 'name'
 *         ],
 *
 *         hasMany: 'OrderItem'
 *     });
 *
 * This may be a lot to take in - basically a User has many Orders, each of which is composed of several OrderItems.
 * Finally, each OrderItem has a single Product. This allows us to consume data like this:
 *
 *     {
 *         "users": [
 *             {
 *                 "id": 123,
 *                 "name": "Ed",
 *                 "orders": [
 *                     {
 *                         "id": 50,
 *                         "total": 100,
 *                         "order_items": [
 *                             {
 *                                 "id"      : 20,
 *                                 "price"   : 40,
 *                                 "quantity": 2,
 *                                 "product" : {
 *                                     "id": 1000,
 *                                     "name": "MacBook Pro"
 *                                 }
 *                             },
 *                             {
 *                                 "id"      : 21,
 *                                 "price"   : 20,
 *                                 "quantity": 3,
 *                                 "product" : {
 *                                     "id": 1001,
 *                                     "name": "iPhone"
 *                                 }
 *                             }
 *                         ]
 *                     }
 *                 ]
 *             }
 *         ]
 *     }
 *
 * The JSON response is deeply nested - it returns all Users (in this case just 1 for simplicity's sake), all of the
 * Orders for each User (again just 1 in this case), all of the OrderItems for each Order (2 order items in this case),
 * and finally the Product associated with each OrderItem. Now we can read the data and use it as follows:
 *
 *     var store = Ext.create('Ext.data.Store', {
 *         model: "User"
 *     });
 *
 *     store.load({
 *         callback: function() {
 *             //the user that was loaded
 *             var user = store.first();
 *
 *             console.log("Orders for " + user.get('name') + ":")
 *
 *             //iterate over the Orders for each User
 *             user.orders().each(function(order) {
 *                 console.log("Order ID: " + order.getId() + ", which contains items:");
 *
 *                 //iterate over the OrderItems for each Order
 *                 order.orderItems().each(function(orderItem) {
 *                     //we know that the Product data is already loaded, so we can use the synchronous getProduct
 *                     //usually, we would use the asynchronous version (see {@link Ext.data.association.BelongsTo})
 *                     var product = orderItem.getProduct();
 *
 *                     console.log(orderItem.get('quantity') + ' orders of ' + product.get('name'));
 *                 });
 *             });
 *         }
 *     });
 *
 * Running the code above results in the following:
 *
 *     Orders for Ed:
 *     Order ID: 50, which contains items:
 *     2 orders of MacBook Pro
 *     3 orders of iPhone
 */
Ext.define('Ext.data.reader.Reader', {
    requires: [
        'Ext.data.ResultSet'
    ],
    alternateClassName: ['Ext.data.Reader', 'Ext.data.DataReader'],

    mixins: ['Ext.mixin.Observable'],

    // private
    isReader: true,

    config: {
        /**
         * @cfg {String} idProperty
         * Name of the property within a row object that contains a record identifier value. Defaults to The id of the
         * model. If an idProperty is explicitly specified it will override that of the one specified on the model
         */
        idProperty: undefined,

        /**
         * @cfg {String} clientIdProperty
         * The name of the property with a response that contains the existing client side id for a record that we are reading.
         */
        clientIdProperty: 'clientId',

        /**
         * @cfg {String} totalProperty
         * Name of the property from which to retrieve the total number of records in the dataset. This is only needed if
         * the whole dataset is not passed in one go, but is being paged from the remote server. Defaults to total.
         */
        totalProperty: 'total',

        /**
         * @cfg {String} successProperty
         * Name of the property from which to retrieve the success attribute. Defaults to success. See
         * {@link Ext.data.proxy.Server}.{@link Ext.data.proxy.Server#exception exception} for additional information.
         */
        successProperty: 'success',

        /**
         * @cfg {String} messageProperty
         * The name of the property which contains a response message. This property is optional.
         */
        messageProperty: null,

        /**
         * @cfg {String} rootProperty
         * The name of the property which contains the Array of row objects.  For JSON reader it's dot-separated list
         * of property names.  For XML reader it's a CSS selector.  For array reader it's not applicable.
         *
         * By default the natural root of the data will be used.  The root Json array, the root XML element, or the array.
         *
         * The data packet value for this property should be an empty array to clear the data or show no data.
         */
        rootProperty: '',

        /**
         * @cfg {Boolean} implicitIncludes
         * True to automatically parse models nested within other models in a response object. See the
         * Ext.data.reader.Reader intro docs for full explanation. Defaults to true.
         */
        implicitIncludes: true,

        model: undefined
    },

    constructor: function(config) {
        this.initConfig(config);
    },

    /**
     * @property {Object} metaData
     * The raw meta data that was most recently read, if any. Meta data can include existing
     * Reader config options like {@link #idProperty}, {@link #totalProperty}, etc. that get
     * automatically applied to the Reader, and those can still be accessed directly from the Reader
     * if needed. However, meta data is also often used to pass other custom data to be processed
     * by application code. For example, it is common when reconfiguring the data model of a grid to
     * also pass a corresponding column model config to be applied to the grid. Any such data will
     * not get applied to the Reader directly (it just gets passed through and is ignored by Ext).
     * This metaData property gives you access to all meta data that was passed, including any such
     * custom data ignored by the reader.
     *
     * This is a read-only property, and it will get replaced each time a new meta data object is
     * passed to the reader.
     */

    fieldCount: 0,

    applyModel: function(model) {
        if (typeof model == 'string') {
            model = Ext.data.ModelManager.getModel(model);

            if (!model) {
                Ext.Logger.error('Model with name ' + arguments[0] + ' doesnt exist.');
            }
        }

        if (model && !model.prototype.isModel && Ext.isObject(model)) {
            model = Ext.data.ModelManager.registerType(model.storeId || model.id || Ext.id(), model);
        }

        return model;
    },

    applyIdProperty: function(idProperty) {
        if (!idProperty && this.getModel()) {
            idProperty = this.getModel().getIdProperty();
        }
        return idProperty;
    },

    updateModel: function(model) {
        if (model) {
            if (!this.getIdProperty()) {
                this.setIdProperty(model.getIdProperty());
            }
            this.buildExtractors();
        }
    },

    createAccessor: Ext.emptyFn,
    createFieldAccessExpression: function() {
        return 'undefined';
    },

    /**
     * @private
     * This builds optimized functions for retrieving record data and meta data from an object.
     * Subclasses may need to implement their own getRoot function.
     */
    buildExtractors: function() {
        if (!this.getModel()) {
            return;
        }

        var me          = this,
            totalProp   = me.getTotalProperty(),
            successProp = me.getSuccessProperty(),
            messageProp = me.getMessageProperty();

        //build the extractors for all the meta data
        if (totalProp) {
            me.getTotal = me.createAccessor(totalProp);
        }

        if (successProp) {
            me.getSuccess = me.createAccessor(successProp);
        }

        if (messageProp) {
            me.getMessage = me.createAccessor(messageProp);
        }

        me.extractRecordData = me.buildRecordDataExtractor();
    },

    /**
     * @private
     * Return a function which will read a raw row object in the format this Reader accepts, and populates
     * a record's data object with converted data values.
     *
     * The returned function must be passed the following parameters:
     *
     * - dest A record's empty data object into which the new field value properties are injected.
     * - source A raw row data object of whatever type this Reader consumes
     * - record The record which is being populated.
     *
     */
    buildRecordDataExtractor: function() {
        var me = this,
            model = me.getModel(),
            fields = model.getFields(),
            ln = fields.length,
            fieldVarName = [],
            clientIdProp = me.getModel().getClientIdProperty(),
            prefix = '__field',
            code = [
                'var me = this,\n',
                '    fields = me.getModel().getFields(),\n',
                '    idProperty = me.getIdProperty(),\n',
                '    idPropertyIsFn = (typeof idProperty == "function"),',
                '    value,\n',
                '    internalId'
            ], i, field, varName, fieldName;

        fields = fields.items;

        for (i = 0; i < ln; i++) {
            field = fields[i];
            fieldName = field.getName();
            if (fieldName === model.getIdProperty()) {
                fieldVarName[i] = 'idField';
            } else {
                fieldVarName[i] = prefix + i;
            }
            code.push(',\n    ', fieldVarName[i], ' = fields.get("', field.getName(), '")');
        }

        code.push(';\n\n    return function(source) {\n        var dest = {};\n');
        code.push('        if (idPropertyIsFn) {\n');
        code.push('            idField.setMapping(idProperty);\n');
        code.push('        }\n');

        for (i = 0; i < ln; i++) {
            field = fields[i];
            varName = fieldVarName[i];
            fieldName = field.getName();
            if (fieldName === model.getIdProperty() && field.getMapping() === null) {
                field.setMapping(this.getIdProperty());
            }
            // createFieldAccessExpression must be implemented in subclasses to extract data from the source object in the correct way.
            code.push('        dest["' + field.getName() + '"]', ' = ', me.createFieldAccessExpression(field, varName, 'source'), ';\n');
        }

        // set the client id as the internalId of the record.
        // clientId handles the case where a client side record did not previously exist on the server,
        // so the server is passing back a client id that can be used to pair the server side record up with the client record
        if (clientIdProp) {
            code.push('        internalId = ' + me.createFieldAccessExpression(Ext.create('Ext.data.Field', {name: clientIdProp}), null, 'source') + ';\n');
            code.push('        if (internalId !== undefined) {\n');
            code.push('            dest["_clientId"] = internalId;\n        }\n');
        }

        code.push('        return dest;\n');
        code.push('    };');

        // Here we are creating a new Function and invoking it immediately in the scope of this Reader
        // It declares several vars capturing the configured context of this Reader, and returns a function
        // which, when passed a record data object, a raw data row in the format this Reader is configured to read,
        // and the record which is being created, will populate the record's data object from the raw row data.
        return Ext.functionFactory(code.join('')).call(me);
    },

    getFields: function() {
        return this.getModel().getFields().items;
    },

    /**
     * @private
     * By default this function just returns what is passed to it. It can be overridden in a subclass
     * to return something else. See XmlReader for an example.
     * @param {Object} data The data object
     * @return {Object} The normalized data object
     */
    getData: function(data) {
        return data;
    },

    /**
     * Takes a raw response object (as passed to this.read) and returns the useful data segment of it.
     * This must be implemented by each subclass
     * @param {Object} response The response object
     * @return {Object} The useful data from the response
     */
    getResponseData: function(response) {
        return response;
    },

    /**
     * @private
     * This will usually need to be implemented in a subclass. Given a generic data object (the type depends on the type
     * of data we are reading), this function should return the object as configured by the Reader's 'root' meta data config.
     * See XmlReader's getRoot implementation for an example. By default the same data object will simply be returned.
     * @param {Object} data The data object
     * @return {Object} The same data object
     */
    getRoot: function(data) {
        return data;
    },

    /**
     * Reads the given response object. This method normalizes the different types of response object that may be passed
     * to it, before handing off the reading of records to the {@link #readRecords} function.
     * @param {Object} response The response object. This may be either an XMLHttpRequest object or a plain JS object
     * @return {Ext.data.ResultSet} The parsed ResultSet object
     */
    read: function(response) {
        var data = response,
            Model = this.getModel(),
            resultSet, records, i, ln, record;

        if (response) {
            data = this.getResponseData(response);
        }

        if (data) {
            resultSet = this.readRecords(data);
            records = resultSet.getRecords();
            for (i = 0, ln = records.length; i < ln; i++) {
                record = records[i];
                records[i] = new Model(record.data, record.id, record.node);
            }
            return resultSet;
        } else {
            return this.nullResultSet;
        }
    },

    process: function(response) {
        var data = response;

        if (response) {
            data = this.getResponseData(response);
        }

        if (data) {
            return this.readRecords(data);
        } else {
            return this.nullResultSet;
        }
    },

    /**
     * Abstracts common functionality used by all Reader subclasses. Each subclass is expected to call this function
     * before running its own logic and returning the Ext.data.ResultSet instance. For most Readers additional
     * processing should not be needed.
     * @param {Object} data The raw data object
     * @return {Ext.data.ResultSet} A ResultSet object
     */
    readRecords: function(data) {
        var me  = this;

        /**
         * @property {Object} rawData
         * The raw data object that was last passed to readRecords. Stored for further processing if needed
         */
        me.rawData = data;

        data = me.getData(data);

        if (data.metaData) {
            me.onMetaChange(data.metaData);
        }

        if (!me.getModel()) {
            Ext.Logger.warn('In order to read record data, a Reader needs to have a Model defined on it.');
        }

        // If we pass an array as the data, we dont use getRoot on the data.
        // Instead the root equals to the data.
        var root    = Ext.isArray(data) ? data : me.getRoot(data),
            success = true,
            recordCount = 0,
            total, value, records, message;

        if (me.getTotalProperty()) {
            value = parseInt(me.getTotal(data), 10);
            if (!isNaN(value)) {
                total = value;
            }
        }

        if (me.getSuccessProperty()) {
            value = me.getSuccess(data);
            if (value === false || value === 'false') {
                success = false;
            }
        }

        if (me.getMessageProperty()) {
            message = me.getMessage(data);
        }

        if (root) {
            records = me.extractData(root);
            recordCount = records.length;
        } else {
            recordCount = 0;
            records = [];
        }

        return new Ext.data.ResultSet({
            total  : total,
            count  : recordCount,
            records: records,
            success: success,
            message: message
        });
    },

    /**
     * Returns extracted, type-cast rows of data.
     * @param {Object[]/Object} root from server response
     * @private
     */
    extractData : function(root) {
        var me = this,
            records = [],
            length  = root.length,
            model = me.getModel(),
            idProperty = model.getIdProperty(),
            fieldsCollection = model.getFields(),
            node, i, data, id, clientId;

        /*
         * We check here whether the fields are dirty since the last read.
         * This works around an issue when a Model is used for both a Tree and another
         * source, because the tree decorates the model with extra fields and it causes
         * issues because the readers aren't notified.
         */
        if (fieldsCollection.isDirty) {
            me.buildExtractors(true);
            delete fieldsCollection.isDirty;
        }

        if (!root.length && Ext.isObject(root)) {
            root = [root];
            length = 1;
        }

        for (i = 0; i < length; i++) {
            clientId = null;
            id = null;

            node = root[i];
            data = me.extractRecordData(node);

            if (data._clientId !== undefined) {
                clientId = data._clientId;
                delete data._clientId;
            }

            if (data[idProperty] !== undefined) {
                id = data[idProperty];
            }

            if (me.getImplicitIncludes()) {
                 me.readAssociated(data, node);
            }

            records.push({
                clientId: clientId,
                id: id,
                data: data,
                node: node
            });
        }

        return records;
    },

    /**
     * @private
     * Loads a record's associations from the data object. This prepopulates hasMany and belongsTo associations
     * on the record provided.
     * @param {Ext.data.Model} record The record to load associations for
     * @param {Object} data The data object
     * @return {String} Return value description
     */
    readAssociated: function(data, node) {
        var associations = this.getModel().associations.items,
            i            = 0,
            length       = associations.length,
            association, associationData, associationKey;

        for (; i < length; i++) {
            association     = associations[i];
            associationKey  = association.getAssociationKey();
            associationData = this.getAssociatedDataRoot(node, associationKey);

            if (associationData) {
                data[associationKey] = associationData;
            }
        }
    },

    /**
     * @private
     * Used internally by `readAssociated`. Given a data object (which could be json, xml etc) for a specific
     * record, this should return the relevant part of that data for the given association name. This is only really
     * needed to support the XML Reader, which has to do a query to get the associated data object
     * @param {Object} data The raw data object
     * @param {String} associationName The name of the association to get data for (uses associationKey if present)
     * @return {Object} The root
     */
    getAssociatedDataRoot: function(data, associationName) {
        return data[associationName];
    },

    /**
     * @private
     * Reconfigures the meta data tied to this Reader
     */
    onMetaChange : function(meta) {
        var fields = meta.fields,
            me = this,
            newModel, config, idProperty;

        // save off the raw meta data
        me.metaData = meta;

        // set any reader-specific configs from meta if available
        if (meta.rootProperty !== undefined) {
            me.setRootProperty(meta.rootProperty);
        }
        else if (meta.root !== undefined) {
            me.setRootProperty(meta.root);
        }

        if (meta.idProperty !== undefined) {
            me.setIdProperty(meta.idProperty);
        }
        if (meta.totalProperty !== undefined) {
            me.setTotalProperty(meta.totalProperty);
        }
        if (meta.successProperty !== undefined) {
            me.setSuccessProperty(meta.successProperty);
        }
        if (meta.messageProperty !== undefined) {
            me.setMessageProperty(meta.messageProperty);
        }

        if (fields) {
            if (me.getModel()) {
                me.getModel().setFields(fields);
                me.buildExtractors();
            }
            else {
                idProperty = me.getIdProperty();
                config = {fields: fields};

                if (idProperty) {
                    config.idProperty = idProperty;
                }

                newModel = Ext.define("Ext.data.reader.MetaModel" + Ext.id(), {
                    extend: 'Ext.data.Model',
                    config: config
                });

                me.setModel(newModel);
            }
        }
        else {
            me.buildExtractors();
        }
    }


    // Convert old properties in data into a config object
}, function() {
    Ext.apply(this.prototype, {
        // Private. Empty ResultSet to return when response is falsy (null|undefined|empty string)
        nullResultSet: new Ext.data.ResultSet({
            total  : 0,
            count  : 0,
            records: [],
            success: true
        })
    });

});

/**
 * The JSON Reader is used by a Proxy to read a server response that is sent back in JSON format. This usually happens
 * as a result of loading a Store - for example we might create something like this:
 *
 *     Ext.define('User', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: ['id', 'name', 'email']
 *         }
 *     });
 *
 *     var store = Ext.create('Ext.data.Store', {
 *         model: 'User',
 *         proxy: {
 *             type: 'ajax',
 *             url : 'users.json',
 *             reader: {
 *                 type: 'json'
 *             }
 *         }
 *     });
 *
 * The example above creates a 'User' model. Models are explained in the {@link Ext.data.Model Model} docs if you're not
 * already familiar with them.
 *
 * We created the simplest type of JSON Reader possible by simply telling our {@link Ext.data.Store Store}'s {@link
 * Ext.data.proxy.Proxy Proxy} that we want a JSON Reader. The Store automatically passes the configured model to the
 * Store, so it is as if we passed this instead:
 *
 *     reader: {
 *         type : 'json',
 *         model: 'User'
 *     }
 *
 * The reader we set up is ready to read data from our server - at the moment it will accept a response like this:
 *
 *     [
 *         {
 *             "id": 1,
 *             "name": "Ed Spencer",
 *             "email": "ed@sencha.com"
 *         },
 *         {
 *             "id": 2,
 *             "name": "Abe Elias",
 *             "email": "abe@sencha.com"
 *         }
 *     ]
 *
 * ## Reading other JSON formats
 *
 * If you already have your JSON format defined and it doesn't look quite like what we have above, you can usually pass
 * JsonReader a couple of configuration options to make it parse your format. For example, we can use the
 * {@link #rootProperty} configuration to parse data that comes back like this:
 *
 *     {
 *         "users": [
 *            {
 *                "id": 1,
 *                "name": "Ed Spencer",
 *                "email": "ed@sencha.com"
 *            },
 *            {
 *                "id": 2,
 *                "name": "Abe Elias",
 *                "email": "abe@sencha.com"
 *            }
 *         ]
 *     }
 *
 * To parse this we just pass in a {@link #rootProperty} configuration that matches the 'users' above:
 *
 *     reader: {
 *         type: 'json',
 *         rootProperty: 'users'
 *     }
 *
 * Sometimes the JSON structure is even more complicated. Document databases like CouchDB often provide metadata around
 * each record inside a nested structure like this:
 *
 *     {
 *         "total": 122,
 *         "offset": 0,
 *         "users": [
 *             {
 *                 "id": "ed-spencer-1",
 *                 "value": 1,
 *                 "user": {
 *                     "id": 1,
 *                     "name": "Ed Spencer",
 *                     "email": "ed@sencha.com"
 *                 }
 *             }
 *         ]
 *     }
 *
 * In the case above the record data is nested an additional level inside the "users" array as each "user" item has
 * additional metadata surrounding it ('id' and 'value' in this case). To parse data out of each "user" item in the JSON
 * above we need to specify the {@link #record} configuration like this:
 *
 *     reader: {
 *         type: 'json',
 *         record: 'user',
 *         rootProperty: 'users'
 *     }
 *
 * ## Response MetaData
 *
 * The server can return metadata in its response, in addition to the record data, that describe attributes
 * of the data set itself or are used to reconfigure the Reader. To pass metadata in the response you simply
 * add a `metaData` attribute to the root of the response data. The metaData attribute can contain anything,
 * but supports a specific set of properties that are handled by the Reader if they are present:
 *
 * - {@link #idProperty}: property name for the primary key field of the data
 * - {@link #rootProperty}: the property name of the root response node containing the record data
 * - {@link #totalProperty}: property name for the total number of records in the data
 * - {@link #successProperty}: property name for the success status of the response
 * - {@link #messageProperty}: property name for an optional response message
 * - {@link Ext.data.Model#cfg-fields fields}: Config used to reconfigure the Model's fields before converting the
 *   response data into records
 *
 * An initial Reader configuration containing all of these properties might look like this ("fields" would be
 * included in the Model definition, not shown):
 *
 *     reader: {
 *         type: 'json',
 *         idProperty: 'id',
 *         rootProperty: 'root',
 *         totalProperty: 'total',
 *         successProperty: 'success',
 *         messageProperty: 'message'
 *     }
 *
 * If you were to pass a response object containing attributes different from those initially defined above, you could
 * use the `metaData` attribute to reconifgure the Reader on the fly. For example:
 *
 *     {
 *         "count": 1,
 *         "ok": true,
 *         "msg": "Users found",
 *         "users": [{
 *             "userId": 123,
 *             "name": "Ed Spencer",
 *             "email": "ed@sencha.com"
 *         }],
 *         "metaData": {
 *             "idProperty": 'userId',
 *             "rootProperty": "users",
 *             "totalProperty": 'count',
 *             "successProperty": 'ok',
 *             "messageProperty": 'msg'
 *         }
 *     }
 *
 * You can also place any other arbitrary data you need into the `metaData` attribute which will be ignored by the Reader,
 * but will be accessible via the Reader's {@link #metaData} property. Application code can then process the passed
 * metadata in any way it chooses.
 *
 * A simple example for how this can be used would be customizing the fields for a Model that is bound to a grid. By passing
 * the `fields` property the Model will be automatically updated by the Reader internally, but that change will not be
 * reflected automatically in the grid unless you also update the column configuration. You could do this manually, or you
 * could simply pass a standard grid column config object as part of the `metaData` attribute
 * and then pass that along to the grid. Here's a very simple example for how that could be accomplished:
 *
 *     // response format:
 *     {
 *         ...
 *         "metaData": {
 *             "fields": [
 *                 { "name": "userId", "type": "int" },
 *                 { "name": "name", "type": "string" },
 *                 { "name": "birthday", "type": "date", "dateFormat": "Y-j-m" },
 *             ],
 *             "columns": [
 *                 { "text": "User ID", "dataIndex": "userId", "width": 40 },
 *                 { "text": "User Name", "dataIndex": "name", "flex": 1 },
 *                 { "text": "Birthday", "dataIndex": "birthday", "flex": 1, "format": 'Y-j-m', "xtype": "datecolumn" }
 *             ]
 *         }
 *     }
 */
Ext.define('Ext.data.reader.Json', {
    extend: 'Ext.data.reader.Reader',
    alternateClassName: 'Ext.data.JsonReader',
    alias : 'reader.json',

    config: {
        /**
         * @cfg {String} [record=null]
         * The optional location within the JSON response that the record data itself can be found at. See the
         * JsonReader intro docs for more details. This is not often needed.
         */
        record: null,

        /**
         * @cfg {Boolean} [useSimpleAccessors=false]
         * True to ensure that field names/mappings are treated as literals when reading values. Defalts to false. For
         * example, by default, using the mapping "foo.bar.baz" will try and read a property foo from the root, then a
         * property bar from foo, then a property baz from bar. Setting the simple accessors to true will read the
         * property with the name "foo.bar.baz" direct from the root object.
         */
        useSimpleAccessors: false
    },

    objectRe: /[\[\.]/,

    //inherit docs
    getResponseData: function(response) {
        var responseText = response;

        // Handle an XMLHttpRequest object
        if (response && response.responseText) {
            responseText = response.responseText;
        }

        // Handle the case where data has already been decoded
        if (typeof responseText !== 'string') {
            return responseText;
        }

        var data;
        try {
            data = Ext.decode(responseText);
        }
        catch (ex) {
            /**
             * @event exception Fires whenever the reader is unable to parse a response.
             * @param {Ext.data.reader.Xml} reader A reference to this reader
             * @param {XMLHttpRequest} response The XMLHttpRequest response object.
             * @param {String} error The error message.
             */
            this.fireEvent('exception', this, response, 'Unable to parse the JSON returned by the server: ' + ex.toString());
            Ext.Logger.warn('Unable to parse the JSON returned by the server: ' + ex.toString());
        }
        if (!data) {
            this.fireEvent('exception', this, response, 'JSON object not found');

            Ext.Logger.error('JSON object not found');
        }

        return data;
    },

    // inherit docs
    buildExtractors: function() {
        var me = this,
            root = me.getRootProperty();

        me.callParent(arguments);

        if (root) {
            me.rootAccessor = me.createAccessor(root);
        } else {
            delete me.rootAccessor;
        }
    },

    /**
     * We create this method because root is now a config so getRoot is already defined, but in the old
     * data package getRoot was passed a data argument and it would return the data inside of the root
     * property. This method handles both cases.
     * @param data (Optional)
     * @return {String/Object} Returns the config root value if this method was called without passing
     * data. Else it returns the object in the data bound to the root.
     * @private
     */
    getRoot: function(data) {
        var fieldsCollection = this.getModel().getFields();

        /*
         * We check here whether the fields are dirty since the last read.
         * This works around an issue when a Model is used for both a Tree and another
         * source, because the tree decorates the model with extra fields and it causes
         * issues because the readers aren't notified.
         */
        if (fieldsCollection.isDirty) {
            this.buildExtractors(true);
            delete fieldsCollection.isDirty;
        }

        if (this.rootAccessor) {
            return this.rootAccessor.call(this, data);
        } else {
            return data;
        }
    },

    /**
     * @private
     * We're just preparing the data for the superclass by pulling out the record objects we want. If a {@link #record}
     * was specified we have to pull those out of the larger JSON object, which is most of what this function is doing
     * @param {Object} root The JSON root node
     * @return {Ext.data.Model[]} The records
     */
    extractData: function(root) {
        var recordName = this.getRecord(),
            data = [],
            length, i;

        if (recordName) {
            length = root.length;

            if (!length && Ext.isObject(root)) {
                length = 1;
                root = [root];
            }

            for (i = 0; i < length; i++) {
                data[i] = root[i][recordName];
            }
        } else {
            data = root;
        }
        return this.callParent([data]);
    },

    /**
     * @private
     * Returns an accessor function for the given property string. Gives support for properties such as the following:
     * 'someProperty'
     * 'some.property'
     * 'some["property"]'
     * This is used by buildExtractors to create optimized extractor functions when casting raw data into model instances.
     */
    createAccessor: function() {
        var re = /[\[\.]/;

        return function(expr) {
            if (Ext.isEmpty(expr)) {
                return Ext.emptyFn;
            }
            if (Ext.isFunction(expr)) {
                return expr;
            }
            if (this.getUseSimpleAccessors() !== true) {
                var i = String(expr).search(re);
                if (i >= 0) {
                    return Ext.functionFactory('obj', 'var value; try {value = obj' + (i > 0 ? '.' : '') + expr + '} catch(e) {}; return value;');
                }
            }
            return function(obj) {
                return obj[expr];
            };
        };
    }(),

    /**
     * @private
     * Returns an accessor expression for the passed Field. Gives support for properties such as the following:
     * 'someProperty'
     * 'some.property'
     * 'some["property"]'
     * This is used by buildExtractors to create optimized on extractor function which converts raw data into model instances.
     */
    createFieldAccessExpression: function(field, fieldVarName, dataName) {
        var me     = this,
            re     = me.objectRe,
            hasMap = (field.getMapping() !== null),
            map    = hasMap ? field.getMapping() : field.getName(),
            result, operatorSearch;

        if (typeof map === 'function') {
            result = fieldVarName + '.getMapping()(' + dataName + ', this)';
        }
        else if (me.getUseSimpleAccessors() === true || ((operatorSearch = String(map).search(re)) < 0)) {
            if (!hasMap || isNaN(map)) {
                // If we don't provide a mapping, we may have a field name that is numeric
                map = '"' + map + '"';
            }
            result = dataName + "[" + map + "]";
        }
        else {
            result = dataName + (operatorSearch > 0 ? '.' : '') + map;
        }

        return result;
    }
});

/**
 * @author Ed Spencer
 *
 * Proxies are used by {@link Ext.data.Store Stores} to handle the loading and saving of {@link Ext.data.Model Model}
 * data. Usually developers will not need to create or interact with proxies directly.
 *
 * # Types of Proxy
 *
 * There are two main types of Proxy - {@link Ext.data.proxy.Client Client} and {@link Ext.data.proxy.Server Server}.
 * The Client proxies save their data locally and include the following subclasses:
 *
 * - {@link Ext.data.proxy.LocalStorage LocalStorageProxy} - saves its data to localStorage if the browser supports it
 * - {@link Ext.data.proxy.Memory MemoryProxy} - holds data in memory only, any data is lost when the page is refreshed
 *
 * The Server proxies save their data by sending requests to some remote server. These proxies include:
 *
 * - {@link Ext.data.proxy.Ajax Ajax} - sends requests to a server on the same domain
 * - {@link Ext.data.proxy.JsonP JsonP} - uses JSON-P to send requests to a server on a different domain
 *
 * Proxies operate on the principle that all operations performed are either Create, Read, Update or Delete. These four
 * operations are mapped to the methods {@link #create}, {@link #read}, {@link #update} and {@link #destroy}
 * respectively. Each Proxy subclass implements these functions.
 *
 * The CRUD methods each expect an {@link Ext.data.Operation Operation} object as the sole argument. The Operation
 * encapsulates information about the action the Store wishes to perform, the {@link Ext.data.Model model} instances
 * that are to be modified, etc. See the {@link Ext.data.Operation Operation} documentation for more details. Each CRUD
 * method also accepts a callback function to be called asynchronously on completion.
 *
 * Proxies also support batching of Operations via a {@link Ext.data.Batch batch} object, invoked by the {@link #batch}
 * method.
 */
Ext.define('Ext.data.proxy.Proxy', {
    extend: 'Ext.Evented',

    alias: 'proxy.proxy',

    alternateClassName: ['Ext.data.DataProxy', 'Ext.data.Proxy'],

    requires: [
        'Ext.data.reader.Json',
        'Ext.data.writer.Json'
    ],

    uses: [
        'Ext.data.Batch',
        'Ext.data.Operation',
        'Ext.data.Model'
    ],

    config: {
        /**
         * @cfg {String} batchOrder
         * Comma-separated ordering 'create', 'update' and 'destroy' actions when batching. Override this to set a different
         * order for the batched CRUD actions to be executed in. Defaults to 'create,update,destroy'.
         * @accessor
         */
        batchOrder: 'create,update,destroy',

        /**
         * @cfg {Boolean} batchActions
         * True to batch actions of a particular type when synchronizing the store. Defaults to true.
         * @accessor
         */
        batchActions: true,

        /**
         * @cfg {String/Ext.data.Model} model
         * The name of the Model to tie to this Proxy. Can be either the string name of the Model, or a reference to the
         * Model constructor. Required.
         * @accessor
         */
        model: null,

        /**
         * @cfg {Object/String/Ext.data.reader.Reader} reader
         * The Ext.data.reader.Reader to use to decode the server's response or data read from client. This can either be a
         * Reader instance, a config object or just a valid Reader type name (e.g. 'json', 'xml').
         * @accessor
         */
        reader: {
            type: 'json'
        },

        /**
         * @cfg {Object/String/Ext.data.writer.Writer} writer
         * The Ext.data.writer.Writer to use to encode any request sent to the server or saved to client. This can either be
         * a Writer instance, a config object or just a valid Writer type name (e.g. 'json', 'xml').
         * @accessor
         */
        writer: {
            type: 'json'
        }
    },

    isProxy: true,

    applyModel: function(model) {
        if (typeof model == 'string') {
            model = Ext.data.ModelManager.getModel(model);

            if (!model) {
                Ext.Logger.error('Model with name ' + arguments[0] + ' doesnt exist.');
            }
        }

        if (model && !model.prototype.isModel && Ext.isObject(model)) {
            model = Ext.data.ModelManager.registerType(model.storeId || model.id || Ext.id(), model);
        }

        return model;
    },

    updateModel: function(model) {
        if (model) {
            var reader = this.getReader();
            if (reader && !reader.getModel()) {
                reader.setModel(model);
            }
        }
    },

    applyReader: function(reader, currentReader) {
        return Ext.factory(reader, Ext.data.Reader, currentReader, 'reader');
    },

    updateReader: function(reader) {
        if (reader) {
            var model = this.getModel();
            if (!model) {
                model = reader.getModel();
                if (model) {
                    this.setModel(model);
                }
            } else {
                reader.setModel(model);
            }

            if (reader.onMetaChange) {
                 reader.onMetaChange = Ext.Function.createSequence(reader.onMetaChange, this.onMetaChange, this);
            }
        }
    },

    onMetaChange: function(data) {
        var model = this.getReader().getModel();
        if (!this.getModel() && model) {
            this.setModel(model);
        }

        /**
         * @event metachange
         * Fires whenever the server has sent back new metadata to reconfigure the Reader.
         * @param {Ext.data.Proxy} this
         * @param {Object} data The metadata sent back from the server
         */
        this.fireEvent('metachange', this, data);
    },

    applyWriter: function(writer, currentWriter) {
        return Ext.factory(writer, Ext.data.Writer, currentWriter, 'writer');
    },

    /**
     * Performs the given create operation. If you override this method in a custom Proxy, remember to always call the provided
     * callback method when you are done with your operation.
     * @param {Ext.data.Operation} operation The Operation to perform
     * @param {Function} callback Callback function to be called when the Operation has completed (whether successful or not)
     * @param {Object} scope Scope to execute the callback function in
     * @method
     */
    create: Ext.emptyFn,

    /**
     * Performs the given read operation. If you override this method in a custom Proxy, remember to always call the provided
     * callback method when you are done with your operation.
     * @param {Ext.data.Operation} operation The Operation to perform
     * @param {Function} callback Callback function to be called when the Operation has completed (whether successful or not)
     * @param {Object} scope Scope to execute the callback function in
     * @method
     */
    read: Ext.emptyFn,

    /**
     * Performs the given update operation. If you override this method in a custom Proxy, remember to always call the provided
     * callback method when you are done with your operation.
     * @param {Ext.data.Operation} operation The Operation to perform
     * @param {Function} callback Callback function to be called when the Operation has completed (whether successful or not)
     * @param {Object} scope Scope to execute the callback function in
     * @method
     */
    update: Ext.emptyFn,

    /**
     * Performs the given destroy operation. If you override this method in a custom Proxy, remember to always call the provided
     * callback method when you are done with your operation.
     * @param {Ext.data.Operation} operation The Operation to perform
     * @param {Function} callback Callback function to be called when the Operation has completed (whether successful or not)
     * @param {Object} scope Scope to execute the callback function in
     * @method
     */
    destroy: Ext.emptyFn,

    onDestroy: function() {
        Ext.destroy(this.getReader(), this.getWriter());
    },

    /**
     * Performs a batch of {@link Ext.data.Operation Operations}, in the order specified by {@link #batchOrder}. Used
     * internally by {@link Ext.data.Store}'s {@link Ext.data.Store#sync sync} method. Example usage:
     *
     *     myProxy.batch({
     *         create : [myModel1, myModel2],
     *         update : [myModel3],
     *         destroy: [myModel4, myModel5]
     *     });
     *
     * Where the myModel* above are {@link Ext.data.Model Model} instances - in this case 1 and 2 are new instances and
     * have not been saved before, 3 has been saved previously but needs to be updated, and 4 and 5 have already been
     * saved but should now be destroyed.
     *
     * @param {Object} options Object containing one or more properties supported by the batch method:
     *
     * @param {Object} options.operations Object containing the Model instances to act upon, keyed by action name
     *
     * @param {Object} [options.listeners] Event listeners object passed straight through to the Batch -
     * see {@link Ext.data.Batch} for details
     *
     * @param {Ext.data.Batch/Object} [options.batch] A {@link Ext.data.Batch} object (or batch config to apply
     * to the created batch). If unspecified a default batch will be auto-created.
     *
     * @param {Function} [options.callback] The function to be called upon completion of processing the batch.
     * The callback is called regardless of success or failure and is passed the following parameters:
     * @param {Ext.data.Batch} options.callback.batch The {@link Ext.data.Batch batch} that was processed,
     * containing all operations in their current state after processing
     * @param {Object} options.callback.options The options argument that was originally passed into batch
     *
     * @param {Function} [options.success] The function to be called upon successful completion of the batch. The
     * success function is called only if no exceptions were reported in any operations. If one or more exceptions
     * occurred then the `failure` function will be called instead. The success function is called
     * with the following parameters:
     * @param {Ext.data.Batch} options.success.batch The {@link Ext.data.Batch batch} that was processed,
     * containing all operations in their current state after processing
     * @param {Object} options.success.options The options argument that was originally passed into batch
     *
     * @param {Function} [options.failure] The function to be called upon unsuccessful completion of the batch. The
     * failure function is called when one or more operations returns an exception during processing (even if some
     * operations were also successful). The failure function is called with the following parameters:
     * @param {Ext.data.Batch} options.failure.batch The {@link Ext.data.Batch batch} that was processed,
     * containing all operations in their current state after processing
     * @param {Object} options.failure.options The options argument that was originally passed into batch
     *
     * @param {Object} [options.scope] The scope in which to execute any callbacks (i.e. the `this` object inside
     * the callback, success and/or failure functions). Defaults to the proxy.
     *
     * @return {Ext.data.Batch} The newly created Batch
     */
    batch: function(options, /* deprecated */listeners) {
        var me = this,
            useBatch = me.getBatchActions(),
            model = this.getModel(),
            batch,
            records;

        if (options.operations === undefined) {
            // the old-style (operations, listeners) signature was called
            // so convert to the single options argument syntax
            options = {
                operations: options,
                batch: {
                    listeners: listeners
                }
            };

            Ext.Logger.deprecate('Passes old-style signature to Proxy.batch (operations, listeners). Please convert to single options argument syntax.');
        }

        if (options.batch) {
             if (options.batch.isBatch) {
                 options.batch.setProxy(me);
             } else {
                 options.batch.proxy = me;
             }
        } 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));

        Ext.each(me.getBatchOrder().split(','), function(action) {
             records = options.operations[action];
             if (records) {
                 if (useBatch) {
                     batch.add(new Ext.data.Operation({
                         action: action,
                         records: records,
                         model: model
                     }));
                 } else {
                     Ext.each(records, function(record) {
                         batch.add(new Ext.data.Operation({
                             action : action,
                             records: [record],
                             model: model
                         }));
                     });
                 }
             }
        }, me);

        batch.start();
        return batch;
    },

    /**
      * @private
      * The internal callback that the proxy uses to call any specified user callbacks after completion of a 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]);
         }
    }

}, function() {
    // Ext.data2.proxy.ProxyMgr.registerType('proxy', this);

    //backwards compatibility
    // Ext.data.DataProxy = this;
    // Ext.deprecate('platform', '2.0', function() {
    //     Ext.data2.DataProxy = this;
    // }, this);
});

/**
 * @author Ed Spencer
 *
 * Base class for any client-side storage. Used as a superclass for {@link Ext.data.proxy.Memory Memory} and
 * {@link Ext.data.proxy.WebStorage Web Storage} proxies. Do not use directly, use one of the subclasses instead.
 * @private
 */
Ext.define('Ext.data.proxy.Client', {
    extend: 'Ext.data.proxy.Proxy',
    alternateClassName: 'Ext.proxy.ClientProxy',

    /**
     * Abstract function that must be implemented by each ClientProxy subclass. This should purge all record data
     * from the client side storage, as well as removing any supporting data (such as lists of record IDs)
     */
    clear: function() {
        Ext.Logger.error("The Ext.data.proxy.Client subclass that you are using has not defined a 'clear' function. See src/data/ClientProxy.js for details.");
    }
});

/**
 * @author Ed Spencer
 *
 * In-memory proxy. This proxy simply uses a local variable for data storage/retrieval, so its contents are lost on
 * every page refresh.
 *
 * Usually this Proxy isn't used directly, serving instead as a helper to a {@link Ext.data.Store Store} where a reader
 * is required to load data. For example, say we have a Store for a User model and have some inline data we want to
 * load, but this data isn't in quite the right format: we can use a MemoryProxy with a JsonReader to read it into our
 * Store:
 *
 *     //this is the model we will be using in the store
 *     Ext.define('User', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: [
 *                 {name: 'id',    type: 'int'},
 *                 {name: 'name',  type: 'string'},
 *                 {name: 'phone', type: 'string', mapping: 'phoneNumber'}
 *             ]
 *         }
 *     });
 *
 *     //this data does not line up to our model fields - the phone field is called phoneNumber
 *     var data = {
 *         users: [
 *             {
 *                 id: 1,
 *                 name: 'Ed Spencer',
 *                 phoneNumber: '555 1234'
 *             },
 *             {
 *                 id: 2,
 *                 name: 'Abe Elias',
 *                 phoneNumber: '666 1234'
 *             }
 *         ]
 *     };
 *
 *     //note how we set the 'root' in the reader to match the data structure above
 *     var store = Ext.create('Ext.data.Store', {
 *         autoLoad: true,
 *         model: 'User',
 *         data : data,
 *         proxy: {
 *             type: 'memory',
 *             reader: {
 *                 type: 'json',
 *                 root: 'users'
 *             }
 *         }
 *     });
 */
Ext.define('Ext.data.proxy.Memory', {
    extend: 'Ext.data.proxy.Client',
    alias: 'proxy.memory',
    alternateClassName: 'Ext.data.MemoryProxy',

    isMemoryProxy: true,

    config: {
        /**
         * @cfg {Object} data
         * Optional data to pass to configured Reader.
         */
        data: []
    },

    /**
     * @private
     * Fake processing function to commit the records, set the current operation
     * to successful and call the callback if provided. This function is shared
     * by the create, update and destroy methods to perform the bare minimum
     * processing required for the proxy to register a result from the action.
     */
    finishOperation: function(operation, callback, scope) {
        if (operation) {
            var i = 0,
                recs = operation.getRecords(),
                len = recs.length;

            for (i; i < len; i++) {
                recs[i].commit();
            }
            operation.setSuccessful();

            Ext.callback(callback, scope || this, [operation]);
        }
    },

    /**
     * Currently this is a hard-coded method that simply commits any records and sets the operation to successful,
     * then calls the callback function, if provided. It is essentially mocking a server call in memory, but since
     * there is no real back end in this case there's not much else to do. This method can be easily overridden to
     * implement more complex logic if needed.
     * @param {Ext.data.Operation} operation The Operation to perform
     * @param {Function} callback Callback function to be called when the Operation has completed (whether
     * successful or not)
     * @param {Object} scope Scope to execute the callback function in
     * @method
     */
    create: function() {
        this.finishOperation.apply(this, arguments);
    },

    /**
     * Currently this is a hard-coded method that simply commits any records and sets the operation to successful,
     * then calls the callback function, if provided. It is essentially mocking a server call in memory, but since
     * there is no real back end in this case there's not much else to do. This method can be easily overridden to
     * implement more complex logic if needed.
     * @param {Ext.data.Operation} operation The Operation to perform
     * @param {Function} callback Callback function to be called when the Operation has completed (whether
     * successful or not)
     * @param {Object} scope Scope to execute the callback function in
     * @method
     */
    update: function() {
        this.finishOperation.apply(this, arguments);
    },

    /**
     * Currently this is a hard-coded method that simply commits any records and sets the operation to successful,
     * then calls the callback function, if provided. It is essentially mocking a server call in memory, but since
     * there is no real back end in this case there's not much else to do. This method can be easily overridden to
     * implement more complex logic if needed.
     * @param {Ext.data.Operation} operation The Operation to perform
     * @param {Function} callback Callback function to be called when the Operation has completed (whether
     * successful or not)
     * @param {Object} scope Scope to execute the callback function in
     * @method
     */
    destroy: function() {
        this.finishOperation.apply(this, arguments);
    },

    /**
     * Reads data from the configured {@link #data} object. Uses the Proxy's {@link #reader}, if present.
     * @param {Ext.data.Operation} operation The read Operation
     * @param {Function} callback The callback to call when reading has completed
     * @param {Object} scope The scope to call the callback function in
     */
    read: function(operation, callback, scope) {
        var me     = this,
            reader = me.getReader();

        if (operation.process('read', reader.process(me.getData())) === false) {
            this.fireEvent('exception', this, null, operation);
        }

        Ext.callback(callback, scope || me, [operation]);
    },

    clear: Ext.emptyFn
});

/**
 * @author Ed Spencer
 *
 * ServerProxy is a superclass of {@link Ext.data.proxy.JsonP JsonPProxy} and {@link Ext.data.proxy.Ajax AjaxProxy}, and
 * would not usually be used directly.
 * @private
 */
Ext.define('Ext.data.proxy.Server', {
    extend: 'Ext.data.proxy.Proxy',
    alias : 'proxy.server',
    alternateClassName: 'Ext.data.ServerProxy',
    requires  : ['Ext.data.Request'],

    config: {
        /**
         * @cfg {String} url
         * The URL from which to request the data object.
         */
        url: null,

        /**
         * @cfg {String} pageParam
         * The name of the 'page' parameter to send in a request. Defaults to 'page'. Set this to false if you don't
         * want to send a page parameter.
         */
        pageParam: 'page',

        /**
         * @cfg {String} startParam
         * The name of the 'start' parameter to send in a request. Defaults to 'start'. Set this to false if you don't
         * want to send a start parameter.
         */
        startParam: 'start',

        /**
         * @cfg {String} limitParam
         * The name of the 'limit' parameter to send in a request. Defaults to 'limit'. Set this to false if you don't
         * want to send a limit parameter.
         */
        limitParam: 'limit',

        /**
         * @cfg {String} groupParam
         * The name of the 'group' parameter to send in a request. Defaults to 'group'. Set this to false if you don't
         * want to send a group parameter.
         */
        groupParam: 'group',

        /**
         * @cfg {String} sortParam
         * The name of the 'sort' parameter to send in a request. Defaults to 'sort'. Set this to undefined if you don't
         * want to send a sort parameter.
         */
        sortParam: 'sort',

        /**
         * @cfg {String} filterParam
         * The name of the 'filter' parameter to send in a request. Defaults to 'filter'. Set this to undefined if you don't
         * want to send a filter parameter.
         */
        filterParam: 'filter',

        /**
         * @cfg {String} directionParam
         * The name of the direction parameter to send in a request. **This is only used when simpleSortMode is set to
         * true.** Defaults to 'dir'.
         */
        directionParam: 'dir',

        /**
         * @cfg {Boolean} enablePagingParams This can be set to false if you want to prevent the paging params to be
         * sent along with the requests made by this proxy.
         */
        enablePagingParams: true,

        /**
         * @cfg {Boolean} simpleSortMode
         * Enabling simpleSortMode in conjunction with remoteSort will only send one sort property and a direction when a
         * remote sort is requested. The directionParam and sortParam will be sent with the property name and either 'ASC'
         * or 'DESC'.
         */
        simpleSortMode: false,

        /**
         * @cfg {Boolean} noCache
         * Disable caching by adding a unique parameter name to the request. Set to false to allow caching. Defaults to true.
         */
        noCache : true,

        /**
         * @cfg {String} cacheString
         * The name of the cache param added to the url when using noCache. Defaults to "_dc".
         */
        cacheString: "_dc",

        /**
         * @cfg {Number} timeout
         * The number of milliseconds to wait for a response. Defaults to 30000 milliseconds (30 seconds).
         */
        timeout : 30000,

        /**
         * @cfg {Object} api
         * Specific urls to call on CRUD action methods "create", "read", "update" and "destroy". Defaults to:
         *
         *     api: {
         *         create  : undefined,
         *         read    : undefined,
         *         update  : undefined,
         *         destroy : undefined
         *     }
         *
         * The url is built based upon the action being executed [create|read|update|destroy] using the commensurate
         * {@link #api} property, or if undefined default to the configured
         * {@link Ext.data.Store}.{@link Ext.data.proxy.Server#url url}.
         *
         * For example:
         *
         *     api: {
         *         create  : '/controller/new',
         *         read    : '/controller/load',
         *         update  : '/controller/update',
         *         destroy : '/controller/destroy_action'
         *     }
         *
         * If the specific URL for a given CRUD action is undefined, the CRUD action request will be directed to the
         * configured {@link Ext.data.proxy.Server#url url}.
         */
        api: {
            create  : undefined,
            read    : undefined,
            update  : undefined,
            destroy : undefined
        },

        /**
         * @cfg {Object} extraParams
         * Extra parameters that will be included on every request. Individual requests with params of the same name
         * will override these params when they are in conflict.
         */
        extraParams: {}
    },

    constructor: function(config) {
        config = config || {};
        if (config.nocache !== undefined) {
            config.noCache = config.nocache;
            Ext.Logger.warn('nocache configuration on Ext.data.proxy.Server has been deprecated. Please use noCache.');
        }
        this.callParent([config]);
    },

    //in a ServerProxy all four CRUD operations are executed in the same manner, so we delegate to doRequest in each case
    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);
    },

    /**
     * Sets a value in the underlying {@link #extraParams}.
     * @param {String} name The key for the new value
     * @param {Object} value The value
     */
    setExtraParam: function(name, value) {
        this.getExtraParams()[name] = value;
    },

    /**
     * Creates and returns an Ext.data.Request object based on the options passed by the {@link Ext.data.Store Store}
     * that this Proxy is attached to.
     * @param {Ext.data.Operation} operation The {@link Ext.data.Operation Operation} object to execute
     * @return {Ext.data.Request} The request object
     */
    buildRequest: function(operation) {
        var me = this,
            params = Ext.applyIf(operation.getParams() || {}, me.getExtraParams() || {}),
            request;

        //copy any sorters, filters etc into the params so they can be sent over the wire
        params = Ext.applyIf(params, me.getParams(operation));

        request = Ext.create('Ext.data.Request', {
            params   : params,
            action   : operation.getAction(),
            records  : operation.getRecords(),
            url      : operation.getUrl(),
            operation: operation,
            proxy    : me
        });

        request.setUrl(me.buildUrl(request));
        operation.setRequest(request);

        return request;
    },

    /**
     * This method handles the processing of the response and is usually overridden by subclasses to
     * do additional processing.
     * @param {Boolean} success Wether or not this request was successful
     * @param {Ext.data.Operation} operation The operation we made this request for
     * @param {Ext.data.Request} request The request that was made
     * @param {Object} response The response that we got
     * @param {Function} callback The callback to be fired onces the response is processed
     * @param {Object} scope The scope in which we call the callback
     * @protected
     */
    processResponse: function(success, operation, request, response, callback, scope) {
        var me = this,
            action = operation.getAction(),
            reader, resultSet;

        if (success === true) {
            reader = me.getReader();

            try {
                resultSet = reader.process(response);
            } catch(e) {
                operation.setException(e.message);

                me.fireEvent('exception', this, response, operation);
                return;
            }

            // This could happen if the model was configured using metaData
            if (!operation.getModel()) {
                operation.setModel(this.getModel());
            }

            if (operation.process(action, resultSet, request, response) === false) {
                this.fireEvent('exception', this, response, operation);
            }
        } else {
            me.setException(operation, response);
            /**
             * @event exception
             * Fires when the server returns an exception
             * @param {Ext.data.proxy.Proxy} this
             * @param {Object} response The response from the AJAX request
             * @param {Ext.data.Operation} operation The operation that triggered request
             */
            me.fireEvent('exception', this, response, operation);
        }

        //this callback is the one that was passed to the 'read' or 'write' function above
        if (typeof callback == 'function') {
            callback.call(scope || me, operation);
        }

        me.afterRequest(request, success);
    },

    /**
     * Sets up an exception on the operation
     * @private
     * @param {Ext.data.Operation} operation The operation
     * @param {Object} response The response
     */
    setException: function(operation, response) {
        operation.setException({
            status: response.status,
            statusText: response.statusText
        });
    },

    /**
     * Encode any values being sent to the server. Can be overridden in subclasses.
     * @private
     * @param {Array} value An array of sorters/filters.
     * @return {Object} The encoded value
     */
    applyEncoding: function(value) {
        return Ext.encode(value);
    },

    /**
     * Encodes the array of {@link Ext.util.Sorter} objects into a string to be sent in the request url. By default,
     * this simply JSON-encodes the sorter data
     * @param {Ext.util.Sorter[]} sorters The array of {@link Ext.util.Sorter Sorter} objects
     * @return {String} The encoded sorters
     */
    encodeSorters: function(sorters) {
        var min = [],
            length = sorters.length,
            i = 0;

        for (; i < length; i++) {
            min[i] = {
                property : sorters[i].getProperty(),
                direction: sorters[i].getDirection()
            };
        }
        return this.applyEncoding(min);

    },

    /**
     * Encodes the array of {@link Ext.util.Filter} objects into a string to be sent in the request url. By default,
     * this simply JSON-encodes the filter data
     * @param {Ext.util.Filter[]} filters The array of {@link Ext.util.Filter Filter} objects
     * @return {String} The encoded filters
     */
    encodeFilters: function(filters) {
        var min = [],
            length = filters.length,
            i = 0;

        for (; i < length; i++) {
            min[i] = {
                property: filters[i].getProperty(),
                value   : filters[i].getValue()
            };
        }
        return this.applyEncoding(min);
    },

    /**
     * @private
     * Copy any sorters, filters etc into the params so they can be sent over the wire
     */
    getParams: function(operation) {
        var me = this,
            params = {},
            grouper = operation.getGrouper(),
            sorters = operation.getSorters(),
            filters = operation.getFilters(),
            page = operation.getPage(),
            start = operation.getStart(),
            limit = operation.getLimit(),

            simpleSortMode = me.getSimpleSortMode(),

            pageParam = me.getPageParam(),
            startParam = me.getStartParam(),
            limitParam = me.getLimitParam(),
            groupParam = me.getGroupParam(),
            sortParam = me.getSortParam(),
            filterParam = me.getFilterParam(),
            directionParam = me.getDirectionParam();

        if (me.getEnablePagingParams()) {
            if (pageParam && page !== null) {
                params[pageParam] = page;
            }

            if (startParam && start !== null) {
                params[startParam] = start;
            }

            if (limitParam && limit !== null) {
                params[limitParam] = limit;
            }
        }

        if (groupParam && grouper) {
            // Grouper is a subclass of sorter, so we can just use the sorter method
            params[groupParam] = me.encodeSorters([grouper]);
        }

        if (sortParam && sorters && sorters.length > 0) {
            if (simpleSortMode) {
                params[sortParam] = sorters[0].getProperty();
                params[directionParam] = sorters[0].getDirection();
            } else {
                params[sortParam] = me.encodeSorters(sorters);
            }
        }

        if (filterParam && filters && filters.length > 0) {
            params[filterParam] = me.encodeFilters(filters);
        }

        return params;
    },

    /**
     * Generates a url based on a given Ext.data.Request object. By default, ServerProxy's buildUrl will add the
     * cache-buster param to the end of the url. Subclasses may need to perform additional modifications to the url.
     * @param {Ext.data.Request} request The request object
     * @return {String} The url
     */
    buildUrl: function(request) {
        var me = this,
            url = me.getUrl(request);

        if (!url) {
            Ext.Logger.error("You are using a ServerProxy but have not supplied it with a url.");
        }

        if (me.getNoCache()) {
            url = Ext.urlAppend(url, Ext.String.format("{0}={1}", me.getCacheString(), Ext.Date.now()));
        }

        return url;
    },

    /**
     * Get the url for the request taking into account the order of priority,
     * - The request
     * - The api
     * - The url
     * @private
     * @param {Ext.data.Request} request The request
     * @return {String} The url
     */
    getUrl: function(request) {
        return request ? request.getUrl() || this.getApi()[request.getAction()] || this._url : this._url;
    },

    /**
     * In ServerProxy subclasses, the {@link #create}, {@link #read}, {@link #update} and {@link #destroy} methods all
     * pass through to doRequest. Each ServerProxy subclass must implement the doRequest method - see {@link
     * Ext.data.proxy.JsonP} and {@link Ext.data.proxy.Ajax} for examples. This method carries the same signature as
     * each of the methods that delegate to it.
     *
     * @param {Ext.data.Operation} operation The Ext.data.Operation object
     * @param {Function} callback The callback function to call when the Operation has completed
     * @param {Object} scope The scope in which to execute the callback
     * @protected
     * @template
     */
    doRequest: function(operation, callback, scope) {
        Ext.Logger.error("The doRequest function has not been implemented on your Ext.data.proxy.Server subclass. See src/data/ServerProxy.js for details");
    },

    /**
     * Optional callback function which can be used to clean up after a request has been completed.
     * @param {Ext.data.Request} request The Request object
     * @param {Boolean} success True if the request was successful
     * @method
     */
    afterRequest: Ext.emptyFn
});

/**
 * @author Ed Spencer
 *
 * The JsonP proxy is useful when you need to load data from a domain other than the one your application is running on. If
 * your application is running on http://domainA.com it cannot use {@link Ext.data.proxy.Ajax Ajax} to load its data
 * from http://domainB.com because cross-domain ajax requests are prohibited by the browser.
 *
 * We can get around this using a JsonP proxy. JsonP proxy injects a `<script>` tag into the DOM whenever an AJAX request
 * would usually be made. Let's say we want to load data from http://domainB.com/users - the script tag that would be
 * injected might look like this:
 *
 *     <script src="http://domainB.com/users?callback=someCallback"></script>
 *
 * When we inject the tag above, the browser makes a request to that url and includes the response as if it was any
 * other type of JavaScript include. By passing a callback in the url above, we're telling domainB's server that we want
 * to be notified when the result comes in and that it should call our callback function with the data it sends back. So
 * long as the server formats the response to look like this, everything will work:
 *
 *     someCallback({
 *         users: [
 *             {
 *                 id: 1,
 *                 name: "Ed Spencer",
 *                 email: "ed@sencha.com"
 *             }
 *         ]
 *     });
 *
 * As soon as the script finishes loading, the 'someCallback' function that we passed in the url is called with the JSON
 * object that the server returned.
 *
 * JsonP proxy takes care of all of this automatically. It formats the url you pass, adding the callback parameter
 * automatically. It even creates a temporary callback function, waits for it to be called and then puts the data into
 * the Proxy making it look just like you loaded it through a normal {@link Ext.data.proxy.Ajax AjaxProxy}. Here's how
 * we might set that up:
 *
 *     Ext.define('User', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: ['id', 'name', 'email']
 *         }
 *     });
 *
 *     var store = Ext.create('Ext.data.Store', {
 *         model: 'User',
 *         proxy: {
 *             type: 'jsonp',
 *             url : 'http://domainB.com/users'
 *         }
 *     });
 *
 *     store.load();
 *
 * That's all we need to do - JsonP proxy takes care of the rest. In this case the Proxy will have injected a script tag
 * like this:
 *
 *     <script src="http://domainB.com/users?callback=callback1"></script>
 *
 * # Customization
 *
 * This script tag can be customized using the {@link #callbackKey} configuration. For example:
 *
 *     var store = Ext.create('Ext.data.Store', {
 *         model: 'User',
 *         proxy: {
 *             type: 'jsonp',
 *             url : 'http://domainB.com/users',
 *             callbackKey: 'theCallbackFunction'
 *         }
 *     });
 *
 *     store.load();
 *
 * Would inject a script tag like this:
 *
 *     <script src="http://domainB.com/users?theCallbackFunction=callback1"></script>
 *
 * # Implementing on the server side
 *
 * The remote server side needs to be configured to return data in this format. Here are suggestions for how you might
 * achieve this using Java, PHP and ASP.net:
 *
 * Java:
 *
 *     boolean jsonP = false;
 *     String cb = request.getParameter("callback");
 *     if (cb != null) {
 *         jsonP = true;
 *         response.setContentType("text/javascript");
 *     } else {
 *         response.setContentType("application/x-json");
 *     }
 *     Writer out = response.getWriter();
 *     if (jsonP) {
 *         out.write(cb + "(");
 *     }
 *     out.print(dataBlock.toJsonString());
 *     if (jsonP) {
 *         out.write(");");
 *     }
 *
 * PHP:
 *
 *     $callback = $_REQUEST['callback'];
 *
 *     // Create the output object.
 *     $output = array('a' => 'Apple', 'b' => 'Banana');
 *
 *     //start output
 *     if ($callback) {
 *         header('Content-Type: text/javascript');
 *         echo $callback . '(' . json_encode($output) . ');';
 *     } else {
 *         header('Content-Type: application/x-json');
 *         echo json_encode($output);
 *     }
 *
 * ASP.net:
 *
 *     String jsonString = "{success: true}";
 *     String cb = Request.Params.Get("callback");
 *     String responseString = "";
 *     if (!String.IsNullOrEmpty(cb)) {
 *         responseString = cb + "(" + jsonString + ")";
 *     } else {
 *         responseString = jsonString;
 *     }
 *     Response.Write(responseString);
 */
Ext.define('Ext.data.proxy.JsonP', {
    extend: 'Ext.data.proxy.Server',
    alternateClassName: 'Ext.data.ScriptTagProxy',
    alias: ['proxy.jsonp', 'proxy.scripttag'],
    requires: ['Ext.data.JsonP'],

    config: {
        defaultWriterType: 'base',

        /**
         * @cfg {String} callbackKey
         * See {@link Ext.data.JsonP#callbackKey}.
         * @accessor
         */
        callbackKey : 'callback',

        /**
         * @cfg {String} recordParam
         * The param name to use when passing records to the server (e.g. 'records=someEncodedRecordString'). Defaults to
         * 'records'
         * @accessor
         */
        recordParam: 'records',

        /**
         * @cfg {Boolean} autoAppendParams
         * True to automatically append the request's params to the generated url. Defaults to true
         * @accessor
         */
        autoAppendParams: true
    },

    /**
     * Performs the read request to the remote domain. JsonP proxy does not actually create an Ajax request,
     * instead we write out a `<script>` tag based on the configuration of the internal Ext.data.Request object
     * @param {Ext.data.Operation} operation The {@link Ext.data.Operation Operation} object to execute
     * @param {Function} callback A callback function to execute when the Operation has been completed
     * @param {Object} scope The scope to execute the callback in
     * @protected
     */
    doRequest: function(operation, callback, scope) {
        //generate the unique IDs for this request
        var me      = this,
            writer  = me.getWriter(),
            request = me.buildRequest(operation),
            params  = request.getParams();

        if (operation.allowWrite()) {
            request = writer.write(request);
        }

        // apply JsonP proxy-specific attributes to the Request
        request.setConfig({
            callbackKey: me.getCallbackKey(),
            timeout: me.getTimeout(),
            scope: me,
            callback: me.createRequestCallback(request, operation, callback, scope)
        });

        // Prevent doubling up because the params are already added to the url in buildUrl
        if (me.getAutoAppendParams()) {
            request.setParams({});
        }

        request.setJsonP(Ext.data.JsonP.request(request.getCurrentConfig()));

        // Set the params back once we have made the request though
        request.setParams(params);

        operation.setStarted();

        me.lastRequest = request;

        return request;
    },

    /**
     * @private
     * Creates and returns the function that is called when the request has completed. The returned function
     * should accept a Response object, which contains the response to be read by the configured Reader.
     * The third argument is the callback that should be called after the request has been completed and the Reader has decoded
     * the response. This callback will typically be the callback passed by a store, e.g. in proxy.read(operation, theCallback, scope)
     * theCallback refers to the callback argument received by this function.
     * See {@link #doRequest} for details.
     * @param {Ext.data.Request} request The Request object
     * @param {Ext.data.Operation} operation The Operation being executed
     * @param {Function} callback The callback function to be called when the request completes. This is usually the callback
     * passed to doRequest
     * @param {Object} scope The scope in which to execute the callback function
     * @return {Function} The callback function
     */
    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);
        };
    },

    // inherit docs
    setException: function(operation, response) {
        operation.setException(operation.getRequest().getJsonP().errorType);
    },


    /**
     * Generates a url based on a given Ext.data.Request object. Adds the params and callback function name to the url
     * @param {Ext.data.Request} request The request object
     * @return {String} The url
     */
    buildUrl: function(request) {
        var me      = this,
            url     = me.callParent(arguments),
            params  = Ext.apply({}, request.getParams()),
            filters = params.filters,
            records,
            filter, i, value;

        delete params.filters;

        if (me.getAutoAppendParams()) {
            url = Ext.urlAppend(url, Ext.Object.toQueryString(params));
        }

        if (filters && filters.length) {
            for (i = 0; i < filters.length; i++) {
                filter = filters[i];
                value = filter.getValue();
                if (value) {
                    url = Ext.urlAppend(url, filter.getProperty() + "=" + value);
                }
            }
        }

        //if there are any records present, append them to the url also
        records = request.getRecords();

        if (Ext.isArray(records) && records.length > 0) {
            url = Ext.urlAppend(url, Ext.String.format("{0}={1}", me.getRecordParam(), me.encodeRecords(records)));
        }

        return url;
    },

    //inherit docs
    destroy: function() {
        this.abort();
        this.callParent(arguments);
    },

    /**
     * Aborts the current server request if one is currently running
     */
    abort: function() {
        var lastRequest = this.lastRequest;
        if (lastRequest) {
            Ext.data.JsonP.abort(lastRequest.getJsonP());
        }
    },

    /**
     * Encodes an array of records into a string suitable to be appended to the script src url. This is broken out into
     * its own function so that it can be easily overridden.
     * @param {Ext.data.Model[]} records The records array
     * @return {String} The encoded records string
     */
    encodeRecords: function(records) {
        var encoded = "",
            i = 0,
            len = records.length;

        for (; i < len; i++) {
            encoded += Ext.Object.toQueryString(records[i].getData());
        }

        return encoded;
    }
});

/**
 * @author Ed Spencer
 *
 * WebStorageProxy is simply a superclass for the {@link Ext.data.proxy.LocalStorage LocalStorage} proxy. It uses the
 * new HTML5 key/value client-side storage objects to save {@link Ext.data.Model model instances} for offline use.
 * @private
 */
Ext.define('Ext.data.proxy.WebStorage', {
    extend: 'Ext.data.proxy.Client',
    alternateClassName: 'Ext.data.WebStorageProxy',

    requires: 'Ext.Date',

    config: {
        /**
         * @cfg {String} id
         * The unique ID used as the key in which all record data are stored in the local storage object.
         */
        id: undefined,

        // WebStorage proxies dont use readers and writers
        reader: null,
        writer: null
    },

    /**
     * Creates the proxy, throws an error if local storage is not supported in the current browser.
     * @param {Object} config (optional) Config object.
     */
    constructor: function(config) {
        this.callParent(arguments);

        /**
         * @property {Object} cache
         * Cached map of records already retrieved by this Proxy. Ensures that the same instance is always retrieved.
         */
        this.cache = {};

        if (this.getStorageObject() === undefined) {
            Ext.Logger.error("Local Storage is not supported in this browser, please use another type of data proxy");
        }
    },

    updateModel: function(model) {
        if (!this.getId()) {
            this.setId(model.modelName);
        }
    },

    //inherit docs
    create: function(operation, callback, scope) {
        var records = operation.getRecords(),
            length  = records.length,
            ids     = this.getIds(),
            id, record, i;

        operation.setStarted();

        for (i = 0; i < length; i++) {
            record = records[i];

            if (record.phantom) {
                record.phantom = false;
                id = this.getNextId();
            } else {
                id = record.getId();
            }

            this.setRecord(record, id);
            ids.push(id);
        }

        this.setIds(ids);

        operation.setCompleted();
        operation.setSuccessful();

        if (typeof callback == 'function') {
            callback.call(scope || this, operation);
        }
    },

    //inherit docs
    read: function(operation, callback, scope) {
        var records    = [],
            ids        = this.getIds(),
            model      = this.getModel(),
            idProperty = model.getIdProperty(),
            params     = operation.getParams() || {},
            length     = ids.length,
            i, record;

        //read a single record
        if (params[idProperty] !== undefined) {
            record = this.getRecord(params[idProperty]);
            if (record) {
                records.push(record);
                operation.setSuccessful();
            }
        } else {
            for (i = 0; i < length; i++) {
                records.push(this.getRecord(ids[i]));
            }
            operation.setSuccessful();
        }

        operation.setCompleted();

        operation.setResultSet(Ext.create('Ext.data.ResultSet', {
            records: records,
            total  : records.length,
            loaded : true
        }));
        operation.setRecords(records);

        if (typeof callback == 'function') {
            callback.call(scope || this, operation);
        }
    },

    //inherit docs
    update: function(operation, callback, scope) {
        var records = operation.getRecords(),
            length  = records.length,
            ids     = this.getIds(),
            record, id, i;

        operation.setStarted();

        for (i = 0; i < length; i++) {
            record = records[i];
            this.setRecord(record);

            //we need to update the set of ids here because it's possible that a non-phantom record was added
            //to this proxy - in which case the record's id would never have been added via the normal 'create' call
            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);
        }
    },

    //inherit
    destroy: function(operation, callback, scope) {
        var records = operation.getRecords(),
            length  = records.length,
            ids     = this.getIds(),

            //newIds is a copy of ids, from which we remove the destroyed records
            newIds  = [].concat(ids),
            i;

        for (i = 0; i < length; i++) {
            Ext.Array.remove(newIds, records[i].getId());
            this.removeRecord(records[i], false);
        }

        this.setIds(newIds);

        operation.setCompleted();
        operation.setSuccessful();

        if (typeof callback == 'function') {
            callback.call(scope || this, operation);
        }
    },

    /**
     * @private
     * Fetches a model instance from the Proxy by ID. Runs each field's decode function (if present) to decode the data.
     * @param {String} id The record's unique ID
     * @return {Ext.data.Model} The model instance or undefined if the record did not exist in the storage.
     */
    getRecord: function(id) {
        if (this.cache[id] === undefined) {
            var recordKey = this.getRecordKey(id),
                item = this.getStorageObject().getItem(recordKey),
                data    = {},
                Model   = this.getModel(),
                fields  = Model.getFields().items,
                length  = fields.length,
                i, field, name, record, rawData, dateFormat;

            if (!item) {
                return;
            }

            rawData = Ext.decode(item);

            for (i = 0; i < length; i++) {
                field = fields[i];
                name  = field.getName();

                if (typeof field.getDecode() == 'function') {
                    data[name] = field.getDecode()(rawData[name]);
                } else {
                    if (field.getType().type == 'date') {
                        dateFormat = field.getDateFormat();
                        if (dateFormat) {
                            data[name] = Ext.Date.parse(rawData[name], dateFormat);
                        } else {
                            data[name] = new Date(rawData[name]);
                        }
                    } else {
                        data[name] = rawData[name];
                    }
                }
            }

            record = new Model(data, id);
            this.cache[id] = record;
        }

        return this.cache[id];
    },

    /**
     * Saves the given record in the Proxy. Runs each field's encode function (if present) to encode the data.
     * @param {Ext.data.Model} record The model instance
     * @param {String} [id] The id to save the record under (defaults to the value of the record's getId() function)
     */
    setRecord: function(record, id) {
        if (id) {
            record.setId(id);
        } else {
            id = record.getId();
        }

        var me = this,
            rawData = record.getData(),
            data    = {},
            Model   = me.getModel(),
            fields  = Model.getFields().items,
            length  = fields.length,
            i = 0,
            field, name, obj, key, dateFormat;

        for (; i < length; i++) {
            field = fields[i];
            name  = field.getName();

            if (typeof field.getEncode() == 'function') {
                data[name] = field.getEncode()(rawData[name], record);
            } else {
                if (field.getType().type == 'date' && Ext.isDate(rawData[name])) {
                    dateFormat = field.getDateFormat();
                    if (dateFormat) {
                        data[name] = Ext.Date.format(rawData[name], dateFormat);
                    } else {
                        data[name] = rawData[name].getTime();
                    }
                } else {
                    data[name] = rawData[name];
                }
            }
        }

        obj = me.getStorageObject();
        key = me.getRecordKey(id);

        //keep the cache up to date
        me.cache[id] = record;

        //iPad bug requires that we remove the item before setting it
        obj.removeItem(key);
        obj.setItem(key, Ext.encode(data));
    },

    /**
     * @private
     * Physically removes a given record from the local storage. Used internally by {@link #destroy}, which you should
     * use instead because it updates the list of currently-stored record ids
     * @param {String/Number/Ext.data.Model} id The id of the record to remove, or an Ext.data.Model instance
     */
    removeRecord: function(id, updateIds) {
        var me = this,
            ids;

        if (id.isModel) {
            id = id.getId();
        }

        if (updateIds !== false) {
            ids = me.getIds();
            Ext.Array.remove(ids, id);
            me.setIds(ids);
        }

        me.getStorageObject().removeItem(me.getRecordKey(id));
    },

    /**
     * @private
     * Given the id of a record, returns a unique string based on that id and the id of this proxy. This is used when
     * storing data in the local storage object and should prevent naming collisions.
     * @param {String/Number/Ext.data.Model} id The record id, or a Model instance
     * @return {String} The unique key for this record
     */
    getRecordKey: function(id) {
        if (id.isModel) {
            id = id.getId();
        }

        return Ext.String.format("{0}-{1}", this.getId(), id);
    },

    /**
     * @private
     * Returns the unique key used to store the current record counter for this proxy. This is used internally when
     * realizing models (creating them when they used to be phantoms), in order to give each model instance a unique id.
     * @return {String} The counter key
     */
    getRecordCounterKey: function() {
        return Ext.String.format("{0}-counter", this.getId());
    },

    /**
     * @private
     * Returns the array of record IDs stored in this Proxy
     * @return {Number[]} The record IDs. Each is cast as a Number
     */
    getIds: function() {
        var ids    = (this.getStorageObject().getItem(this.getId()) || "").split(","),
            length = ids.length,
            i;

        if (length == 1 && ids[0] === "") {
            ids = [];
        } else {
            for (i = 0; i < length; i++) {
                ids[i] = parseInt(ids[i], 10);
            }
        }

        return ids;
    },

    /**
     * @private
     * Saves the array of ids representing the set of all records in the Proxy
     * @param {Number[]} ids The ids to set
     */
    setIds: function(ids) {
        var obj = this.getStorageObject(),
            str = ids.join(","),
            id  = this.getId(),
            key = this.getRecordCounterKey();

        obj.removeItem(id);

        if (Ext.isEmpty(str)) {
            obj.removeItem(key);
        } else {
            obj.setItem(id, str);
        }
    },

    /**
     * @private
     * Returns the next numerical ID that can be used when realizing a model instance (see getRecordCounterKey).
     * Increments the counter.
     * @return {Number} The id
     */
    getNextId: function() {
        var obj  = this.getStorageObject(),
            key  = this.getRecordCounterKey(),
            last = obj.getItem(key),
            ids, id;

        if (last === null) {
            ids = this.getIds();
            last = ids[ids.length - 1] || 0;
        }

        id = parseInt(last, 10) + 1;
        obj.setItem(key, id);

        return id;
    },

    /**
     * @private
     * Sets up the Proxy by claiming the key in the storage object that corresponds to the unique id of this Proxy. Called
     * automatically by the constructor, this should not need to be called again unless {@link #clear} has been called.
     */
    initialize: function() {
        this.callParent(arguments);
        var storageObject = this.getStorageObject();
        storageObject.setItem(this.getId(), storageObject.getItem(this.getId()) || "");
    },

    /**
     * Destroys all records stored in the proxy and removes all keys and values used to support the proxy from the
     * storage object.
     */
    clear: function() {
        var obj = this.getStorageObject(),
            ids = this.getIds(),
            len = ids.length,
            i;

        //remove all the records
        for (i = 0; i < len; i++) {
            this.removeRecord(ids[i]);
        }

        //remove the supporting objects
        obj.removeItem(this.getRecordCounterKey());
        obj.removeItem(this.getId());
    },

    /**
     * @private
     * Abstract function which should return the storage object that data will be saved to. This must be implemented
     * in each subclass.
     * @return {Object} The storage object
     */
    getStorageObject: function() {
        Ext.Logger.error("The getStorageObject function has not been defined in your Ext.data.proxy.WebStorage subclass");
    }
});

/**
 * @author Ed Spencer
 *
 * The LocalStorageProxy uses the new HTML5 localStorage API to save {@link Ext.data.Model Model} data locally on the
 * client browser. HTML5 localStorage is a key-value store (e.g. cannot save complex objects like JSON), so
 * LocalStorageProxy automatically serializes and deserializes data when saving and retrieving it.
 *
 * localStorage is extremely useful for saving user-specific information without needing to build server-side
 * infrastructure to support it. Let's imagine we're writing a Twitter search application and want to save the user's
 * searches locally so they can easily perform a saved search again later. We'd start by creating a Search model:
 *
 *     Ext.define('Search', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: ['id', 'query'],
 *             proxy: {
 *                 type: 'localstorage',
 *                 id  : 'twitter-Searches'
 *             }
 *         }
 *     });
 *
 * Our Search model contains just two fields - id and query - plus a Proxy definition. The only configuration we need to
 * pass to the LocalStorage proxy is an {@link #id}. This is important as it separates the Model data in this Proxy from
 * all others. The localStorage API puts all data into a single shared namespace, so by setting an id we enable
 * LocalStorageProxy to manage the saved Search data.
 *
 * Saving our data into localStorage is easy and would usually be done with a {@link Ext.data.Store Store}:
 *
 *     //our Store automatically picks up the LocalStorageProxy defined on the Search model
 *     var store = Ext.create('Ext.data.Store', {
 *         model: "Search"
 *     });
 *
 *     //loads any existing Search data from localStorage
 *     store.load();
 *
 *     //now add some Searches
 *     store.add({query: 'Sencha Touch'});
 *     store.add({query: 'Ext JS'});
 *
 *     //finally, save our Search data to localStorage
 *     store.sync();
 *
 * The LocalStorageProxy automatically gives our new Searches an id when we call store.sync(). It encodes the Model data
 * and places it into localStorage. We can also save directly to localStorage, bypassing the Store altogether:
 *
 *     var search = Ext.create('Search', {query: 'Sencha Animator'});
 *
 *     //uses the configured LocalStorageProxy to save the new Search to localStorage
 *     search.save();
 *
 * # Limitations
 *
 * If this proxy is used in a browser where local storage is not supported, the constructor will throw an error. A local
 * storage proxy requires a unique ID which is used as a key in which all record data are stored in the local storage
 * object.
 *
 * It's important to supply this unique ID as it cannot be reliably determined otherwise. If no id is provided but the
 * attached store has a storeId, the storeId will be used. If neither option is presented the proxy will throw an error.
 */
Ext.define('Ext.data.proxy.LocalStorage', {
    extend: 'Ext.data.proxy.WebStorage',
    alias: 'proxy.localstorage',
    alternateClassName: 'Ext.data.LocalStorageProxy',

    //inherit docs
    getStorageObject: function() {
        return window.localStorage;
    }
});
/**
 * @author Ed Spencer
 *
 * Proxy which uses HTML5 session storage as its data storage/retrieval mechanism. If this proxy is used in a browser
 * where session storage is not supported, the constructor will throw an error. A session storage proxy requires a
 * unique ID which is used as a key in which all record data are stored in the session storage object.
 *
 * It's important to supply this unique ID as it cannot be reliably determined otherwise. If no id is provided but the
 * attached store has a storeId, the storeId will be used. If neither option is presented the proxy will throw an error.
 *
 * Proxies are almost always used with a {@link Ext.data.Store store}:
 *
 *     new Ext.data.Store({
 *         proxy: {
 *             type: 'sessionstorage',
 *             id  : 'myProxyKey'
 *         }
 *     });
 *
 * Alternatively you can instantiate the Proxy directly:
 *
 *     new Ext.data.proxy.SessionStorage({
 *         id  : 'myOtherProxyKey'
 *     });
 *
 * Note that session storage is different to local storage (see {@link Ext.data.proxy.LocalStorage}) - if a browser
 * session is ended (e.g. by closing the browser) then all data in a SessionStorageProxy are lost. Browser restarts
 * don't affect the {@link Ext.data.proxy.LocalStorage} - the data are preserved.
 */
Ext.define('Ext.data.proxy.SessionStorage', {
    extend: 'Ext.data.proxy.WebStorage',
    alias: 'proxy.sessionstorage',
    alternateClassName: 'Ext.data.SessionStorageProxy',

    //inherit docs
    getStorageObject: function() {
        return window.sessionStorage;
    }
});

/**
 * @author Ed Spencer
 * @class Ext.data.reader.Array
 *
 * <p>Data reader class to create an Array of {@link Ext.data.Model} objects from an Array.
 * Each element of that Array represents a row of data fields. The
 * fields are pulled into a Record object using as a subscript, the <code>mapping</code> property
 * of the field definition if it exists, or the field's ordinal position in the definition.</p>
 *
 * <p><u>Example code:</u></p>
 *
<pre><code>
Employee = Ext.define('Employee', {
    extend: 'Ext.data.Model',
    config: {
        fields: [
            'id',
            {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
            {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
        ]
    }
});

var myReader = new Ext.data.reader.Array({
    model: 'Employee'
}, Employee);
</code></pre>
 *
 * <p>This would consume an Array like this:</p>
 *
<pre><code>
[ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
</code></pre>
 *
 * @constructor
 * Create a new ArrayReader
 * @param {Object} meta Metadata configuration options.
 */
Ext.define('Ext.data.reader.Array', {
    extend: 'Ext.data.reader.Json',
    alternateClassName: 'Ext.data.ArrayReader',
    alias : 'reader.array',

    // For Array Reader, methods in the base which use these properties must not see the defaults
    config: {
        totalProperty: undefined,
        successProperty: undefined
    },

    getResponseData: function(data) {
        return data;
    },

    /**
     * @private
     * Returns an accessor expression for the passed Field from an Array using either the Field's mapping, or
     * its ordinal position in the fields collsction as the index.
     * This is used by buildExtractors to create optimized on extractor function which converts raw data into model instances.
     */
    createFieldAccessExpression: function(field, fieldVarName, dataName) {
        var me     = this,
            mapping = field.getMapping(),
            index  = (mapping == null) ? me.getModel().getFields().indexOf(field) : mapping,
            result;

        if (typeof index === 'function') {
            result = fieldVarName + '.getMapping()(' + dataName + ', this)';
        } else {
            if (isNaN(index)) {
                index = '"' + index + '"';
            }
            result = dataName + "[" + index + "]";
        }
        return result;
    }
});
/**
 * @author Ed Spencer
 * @class Ext.data.reader.Xml
 * @extends Ext.data.reader.Reader
 *
 * <p>The XML Reader is used by a Proxy to read a server response that is sent back in XML format. This usually
 * happens as a result of loading a Store - for example we might create something like this:</p>
 *
<pre><code>
Ext.define('User', {
    extend: 'Ext.data.Model',
    config: {
        fields: ['id', 'name', 'email']
    }
});

var store = Ext.create('Ext.data.Store', {
    model: 'User',
    proxy: {
        type: 'ajax',
        url : 'users.xml',
        reader: {
            type: 'xml',
            record: 'user'
        }
    }
});
</code></pre>
 *
 * <p>The example above creates a 'User' model. Models are explained in the {@link Ext.data.Model Model} docs if you're
 * not already familiar with them.</p>
 *
 * <p>We created the simplest type of XML Reader possible by simply telling our {@link Ext.data.Store Store}'s
 * {@link Ext.data.proxy.Proxy Proxy} that we want a XML Reader. The Store automatically passes the configured model to the
 * Store, so it is as if we passed this instead:
 *
<pre><code>
reader: {
    type : 'xml',
    model: 'User',
    record: 'user'
}
</code></pre>
 *
 * <p>The reader we set up is ready to read data from our server - at the moment it will accept a response like this:</p>
 *
<pre><code>
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;user&gt;
    &lt;id&gt;1&lt;/id&gt;
    &lt;name&gt;Ed Spencer&lt;/name&gt;
    &lt;email&gt;ed@sencha.com&lt;/email&gt;
&lt;/user&gt;
&lt;user&gt;
    &lt;id&gt;2&lt;/id&gt;
    &lt;name&gt;Abe Elias&lt;/name&gt;
    &lt;email&gt;abe@sencha.com&lt;/email&gt;
&lt;/user&gt;
</code></pre>
 *
 * <p>The XML Reader uses the configured {@link #record} option to pull out the data for each record - in this case we
 * set record to 'user', so each &lt;user&gt; above will be converted into a User model.</p>
 *
 * <p><u>Reading other XML formats</u></p>
 *
 * <p>If you already have your XML format defined and it doesn't look quite like what we have above, you can usually
 * pass XmlReader a couple of configuration options to make it parse your format. For example, we can use the
 * {@link #rootProperty} configuration to parse data that comes back like this:</p>
 *
<pre><code>
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;users&gt;
    &lt;user&gt;
        &lt;id&gt;1&lt;/id&gt;
        &lt;name&gt;Ed Spencer&lt;/name&gt;
        &lt;email&gt;ed@sencha.com&lt;/email&gt;
    &lt;/user&gt;
    &lt;user&gt;
        &lt;id&gt;2&lt;/id&gt;
        &lt;name&gt;Abe Elias&lt;/name&gt;
        &lt;email&gt;abe@sencha.com&lt;/email&gt;
    &lt;/user&gt;
&lt;/users&gt;
</code></pre>
 *
 * <p>To parse this we just pass in a {@link #rootProperty} configuration that matches the 'users' above:</p>
 *
<pre><code>
reader: {
    type: 'xml',
    record: 'user',
    rootProperty: 'users'
}
</code></pre>
 *
 * <p>Note that XmlReader doesn't care whether your {@link #rootProperty} and {@link #record} elements are nested deep 
 * inside a larger structure, so a response like this will still work:
 *
<pre><code>
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;deeply&gt;
    &lt;nested&gt;
        &lt;xml&gt;
            &lt;users&gt;
                &lt;user&gt;
                    &lt;id&gt;1&lt;/id&gt;
                    &lt;name&gt;Ed Spencer&lt;/name&gt;
                    &lt;email&gt;ed@sencha.com&lt;/email&gt;
                &lt;/user&gt;
                &lt;user&gt;
                    &lt;id&gt;2&lt;/id&gt;
                    &lt;name&gt;Abe Elias&lt;/name&gt;
                    &lt;email&gt;abe@sencha.com&lt;/email&gt;
                &lt;/user&gt;
            &lt;/users&gt;
        &lt;/xml&gt;
    &lt;/nested&gt;
&lt;/deeply&gt;
</code></pre>
 *
 * <p><u>Response metadata</u></p>
 *
 * <p>The server can return additional data in its response, such as the {@link #totalProperty total number of records}
 * and the {@link #successProperty success status of the response}. These are typically included in the XML response
 * like this:</p>
 *
<pre><code>
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;total&gt;100&lt;/total&gt;
&lt;success&gt;true&lt;/success&gt;
&lt;users&gt;
    &lt;user&gt;
        &lt;id&gt;1&lt;/id&gt;
        &lt;name&gt;Ed Spencer&lt;/name&gt;
        &lt;email&gt;ed@sencha.com&lt;/email&gt;
    &lt;/user&gt;
    &lt;user&gt;
        &lt;id&gt;2&lt;/id&gt;
        &lt;name&gt;Abe Elias&lt;/name&gt;
        &lt;email&gt;abe@sencha.com&lt;/email&gt;
    &lt;/user&gt;
&lt;/users&gt;
</code></pre>
 *
 * <p>If these properties are present in the XML response they can be parsed out by the XmlReader and used by the
 * Store that loaded it. We can set up the names of these properties by specifying a final pair of configuration
 * options:</p>
 *
<pre><code>
reader: {
    type: 'xml',
    rootProperty: 'users',
    totalProperty  : 'total',
    successProperty: 'success'
}
</code></pre>
 *
 * <p>These final options are not necessary to make the Reader work, but can be useful when the server needs to report
 * an error or if it needs to indicate that there is a lot of data available of which only a subset is currently being
 * returned.</p>
 *
 * <p><u>Response format</u></p>
 *
 * <p><b>Note:</b> in order for the browser to parse a returned XML document, the Content-Type header in the HTTP
 * response must be set to "text/xml" or "application/xml". This is very important - the XmlReader will not
 * work correctly otherwise.</p>
 */
Ext.define('Ext.data.reader.Xml', {
    extend: 'Ext.data.reader.Reader',
    alternateClassName: 'Ext.data.XmlReader',
    alias : 'reader.xml',

    config: {
        /**
         * @cfg {String} record The DomQuery path to the repeated element which contains record information.
         */
        record: null
    },

    /**
     * @private
     * Creates a function to return some particular key of data from a response. The totalProperty and
     * successProperty are treated as special cases for type casting, everything else is just a simple selector.
     * @param {String} expr
     * @return {Function}
     */
    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 && node.firstChild) {
            return node.firstChild.nodeValue;
        }
        return undefined;
    },

    //inherit docs
    getResponseData: function(response) {
        // Check to see if the response is already an xml node.
        if (response.nodeType === 1) {
            return response;
        }

        var xml = response.responseXML;

        if (!xml) {
            /**
             * @event exception Fires whenever the reader is unable to parse a response.
             * @param {Ext.data.reader.Xml} reader A reference to this reader
             * @param {XMLHttpRequest} response The XMLHttpRequest response object.
             * @param {String} error The error message.
             */
            this.fireEvent('exception', this, response, 'XML data not found in the response');

            Ext.Logger.warn('XML data not found in the response');
        }

        return xml;
    },

    /**
     * Normalizes the data object
     * @param {Object} data The raw data object
     * @return {Object} Returns the documentElement property of the data object if present, or the same object if not
     */
    getData: function(data) {
        return data.documentElement || data;
    },

    /**
     * @private
     * Given an XML object, returns the Element that represents the root as configured by the Reader's meta data
     * @param {Object} data The XML data object
     * @return {XMLElement} The root node element
     */
    getRoot: function(data) {
        var nodeName = data.nodeName,
            root     = this.getRootProperty();

        if (!root || (nodeName && nodeName == root)) {
            return data;
        } else if (Ext.DomQuery.isXml(data)) {
            // This fix ensures we have XML data
            // Related to TreeStore calling getRoot with the root node, which isn't XML
            // Probably should be resolved in TreeStore at some point
            return Ext.DomQuery.selectNode(root, data);
        }
    },

    /**
     * @private
     * We're just preparing the data for the superclass by pulling out the record nodes we want
     * @param {XMLElement} root The XML root node
     * @return {Ext.data.Model[]} The records
     */
    extractData: function(root) {
        var recordName = this.getRecord();

        if (!recordName) {
            Ext.Logger.error('Record is a required parameter');
        }

        if (recordName != root.nodeName) {
            root = Ext.DomQuery.select(recordName, root);
        } else {
            root = [root];
        }
        return this.callParent([root]);
    },

    /**
     * @private
     * See Ext.data.reader.Reader's getAssociatedDataRoot docs
     * @param {Object} data The raw data object
     * @param {String} associationName The name of the association to get data for (uses associationKey if present)
     * @return {XMLElement} The root
     */
    getAssociatedDataRoot: function(data, associationName) {
        return Ext.DomQuery.select(associationName, data)[0];
    },

    /**
     * Parses an XML document and returns a ResultSet containing the model instances
     * @param {Object} doc Parsed XML document
     * @return {Ext.data.ResultSet} The parsed result set
     */
    readRecords: function(doc) {
        //it's possible that we get passed an array here by associations. Make sure we strip that out (see Ext.data.reader.Reader#readAssociated)
        if (Ext.isArray(doc)) {
            doc = doc[0];
        }
        return this.callParent([doc]);
    },

    /**
     * @private
     * Returns an accessor expression for the passed Field from an XML element using either the Field's mapping, or
     * its ordinal position in the fields collsction as the index.
     * This is used by buildExtractors to create optimized on extractor function which converts raw data into model instances.
     */
    createFieldAccessExpression: function(field, fieldVarName, dataName) {
        var selector = field.getMapping() || field.getName(),
            result;

        if (typeof selector === 'function') {
            result = fieldVarName + '.getMapping()(' + dataName + ', this)';
        } else {
            result = 'me.getNodeValue(Ext.DomQuery.selectNode("' + selector + '", ' + dataName + '))';
        }
        return result;
    }
});

/**
 * Ext.direct.Provider is an abstract class meant to be extended.
 *
 * For example Ext JS implements the following subclasses:
 *
 *     Provider
 *     |
 *     +---{@link Ext.direct.JsonProvider JsonProvider}
 *         |
 *         +---{@link Ext.direct.PollingProvider PollingProvider}
 *         |
 *         +---{@link Ext.direct.RemotingProvider RemotingProvider}
 *
 * @abstract
 */
Ext.define('Ext.direct.Provider', {
    alias: 'direct.provider',

    mixins: {
        observable: 'Ext.util.Observable'
    },

    config: {
        /**
         * @cfg {String} id
         * The unique id of the provider (defaults to an auto-assigned id).
         * You should assign an id if you need to be able to access the provider later and you do
         * not have an object reference available, for example:
         *
         *     Ext.direct.Manager.addProvider({
         *         type: 'polling',
         *         url:  'php/poll.php',
         *         id:   'poll-provider'
         *     });
         *     var p = {@link Ext.direct.Manager}.{@link Ext.direct.Manager#getProvider getProvider}('poll-provider');
         *     p.disconnect();
         *
         */
        id: undefined
    },

    /**
     * @event connect
     * Fires when the Provider connects to the server-side
     * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.
     */

    /**
     * @event disconnect
     * Fires when the Provider disconnects from the server-side
     * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.
     */

    /**
     * @event data
     * Fires when the Provider receives data from the server-side
     * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.
     * @param {Ext.direct.Event} e The Ext.direct.Event type that occurred.
     */

    /**
     * @event exception
     * Fires when the Provider receives an exception from the server-side
     */

    constructor : function(config){
        this.initConfig(config);
    },

    applyId: function(id) {
        if (id === undefined) {
            id = this.getUniqueId();
        }
        return id;
    },

    /**
     * Returns whether or not the server-side is currently connected.
     * Abstract method for subclasses to implement.
     */
    isConnected: function() {
        return false;
    },

    /**
     * Abstract methods for subclasses to implement.
     * @method
     */
    connect: Ext.emptyFn,

    /**
     * Abstract methods for subclasses to implement.
     * @method
     */
    disconnect: Ext.emptyFn
});

/**
 * @class Ext.direct.JsonProvider

A base provider for communicating using JSON. This is an abstract class
and should not be instanced directly.

 * @markdown
 * @abstract
 */

Ext.define('Ext.direct.JsonProvider', {
    extend: 'Ext.direct.Provider',

    alias: 'direct.jsonprovider',

    uses: ['Ext.direct.ExceptionEvent'],

    /**
     * Parse the JSON response
     * @private
     * @param {Object} response The XHR response object
     * @return {Object} The data in the response.
     */
    parseResponse: function(response) {
        if (!Ext.isEmpty(response.responseText)) {
            if (Ext.isObject(response.responseText)) {
                return response.responseText;
            }
            return Ext.decode(response.responseText);
        }
        return null;
    },

    /**
     * Creates a set of events based on the XHR response
     * @private
     * @param {Object} response The XHR response
     * @return {Ext.direct.Event[]} An array of Ext.direct.Event
     */
    createEvents: function(response) {
        var data = null,
            events = [],
            i = 0,
            ln, event;

        try {
            data = this.parseResponse(response);
        } catch(e) {
            event = Ext.create('Ext.direct.ExceptionEvent', {
                data: e,
                xhr: response,
                code: Ext.direct.Manager.exceptions.PARSE,
                message: 'Error parsing json response: \n\n ' + data
            });
            return [event];
        }

        if (Ext.isArray(data)) {
            for (ln = data.length; i < ln; ++i) {
                events.push(this.createEvent(data[i]));
            }
        } else {
            events.push(this.createEvent(data));
        }
        return events;
    },

    /**
     * Create an event from a response object
     * @param {Object} response The XHR response object
     * @return {Ext.direct.Event} The event
     */
    createEvent: function(response) {
        return Ext.create('direct.' + response.type, response);
    }
});
/**
 * @class Ext.direct.PollingProvider
 *
 * <p>Provides for repetitive polling of the server at distinct {@link #interval intervals}.
 * The initial request for data originates from the client, and then is responded to by the
 * server.</p>
 *
 * <p>All configurations for the PollingProvider should be generated by the server-side
 * API portion of the Ext.Direct stack.</p>
 *
 * <p>An instance of PollingProvider may be created directly via the new keyword or by simply
 * specifying <tt>type = 'polling'</tt>.  For example:</p>
 * <pre><code>
var pollA = Ext.create('Ext.direct.PollingProvider', {
    type:'polling',
    url: 'php/pollA.php',
});
Ext.direct.Manager.addProvider(pollA);
pollA.disconnect();

Ext.direct.Manager.addProvider(
    {
        type:'polling',
        url: 'php/pollB.php',
        id: 'pollB-provider'
    }
);
var pollB = Ext.direct.Manager.getProvider('pollB-provider');
 * </code></pre>
 */
Ext.define('Ext.direct.PollingProvider', {
    extend: 'Ext.direct.JsonProvider',
    alias: 'direct.pollingprovider',

    uses: ['Ext.direct.ExceptionEvent'],

    requires: ['Ext.Ajax', 'Ext.util.DelayedTask'],

    config: {
        /**
         * @cfg {Number} interval
         * How often to poll the server-side in milliseconds. Defaults to every 3 seconds.
         */
        interval: 3000,

        /**
         * @cfg {Object} baseParams
         * An object containing properties which are to be sent as parameters on every polling request
         */
        baseParams: null,

        /**
         * @cfg {String/Function} url
         * The url which the PollingProvider should contact with each request. This can also be
         * an imported Ext.Direct method which will accept the baseParams as its only argument.
         */
        url: null
    },

    /**
     * @event beforepoll
     * Fired immediately before a poll takes place, an event handler can return false
     * in order to cancel the poll.
     * @param {Ext.direct.PollingProvider} this
     */

    /**
     * @event poll
     * This event has not yet been implemented.
     * @param {Ext.direct.PollingProvider} this
     */

    // inherited
    isConnected: function() {
        return !!this.pollTask;
    },

    /**
     * Connect to the server-side and begin the polling process. To handle each
     * response subscribe to the data event.
     */
    connect: function() {
        var me = this,
            url = me.getUrl(),
            baseParams = me.getBaseParams();

        if (url && !me.pollTask) {
            me.pollTask = setInterval(function() {
                if (me.fireEvent('beforepoll', me) !== false) {
                    if (Ext.isFunction(url)) {
                        url(baseParams);
                    } else {
                        Ext.Ajax.request({
                            url: url,
                            callback: me.onData,
                            scope: me,
                            params: baseParams
                        });
                    }
                }
            }, me.getInterval());
            me.fireEvent('connect', me);
        } else if (!url) {
            Ext.Error.raise('Error initializing PollingProvider, no url configured.');
        }
    },

    /**
     * Disconnect from the server-side and stop the polling process. The disconnect
     * event will be fired on a successful disconnect.
     */
    disconnect: function() {
        var me = this;

        if (me.pollTask) {
            clearInterval(me.pollTask);
            delete me.pollTask;
            me.fireEvent('disconnect', me);
        }
    },

    // private
    onData: function(opt, success, response) {
        var me = this,
            i = 0,
            len,
            events;

        if (success) {
            events = me.createEvents(response);
            for (len = events.length; i < len; ++i) {
                me.fireEvent('data', me, events[i]);
            }
        } else {
            me.fireEvent('data', me, Ext.create('Ext.direct.ExceptionEvent', {
                data: null,
                code: Ext.direct.Manager.exceptions.TRANSPORT,
                message: 'Unable to connect to the server.',
                xhr: response
            }));
        }
    }
});
/**
 * @private
 */
Ext.define('Ext.fx.animation.Abstract', {

    extend: 'Ext.Evented',

    isAnimation: true,

    requires: [
        'Ext.fx.State'
    ],

    config: {
        name: '',

        element: null,

        before: null,

        from: {},

        to: {},

        after: null,

        states: {},

        duration:  300,

        easing: 'linear',

        iteration: 1,

        direction: 'normal',

        delay: 0,

        onBeforeStart: null,

        onEnd: null,

        onBeforeEnd: null,

        scope: null,

        reverse: null,

        preserveEndState: false,

        replacePrevious: true
    },

    STATE_FROM: '0%',

    STATE_TO: '100%',

    DIRECTION_UP: 'up',

    DIRECTION_DOWN: 'down',

    DIRECTION_LEFT: 'left',

    DIRECTION_RIGHT: 'right',

    stateNameRegex: /^(?:[\d\.]+)%$/,

    constructor: function() {
        this.states = {};

        this.callParent(arguments);

        return this;
    },

    applyElement: function(element) {
        return Ext.get(element);
    },

    applyBefore: function(before, current) {
        if (before) {
            return Ext.factory(before, Ext.fx.State, current);
        }
    },

    applyAfter: function(after, current) {
        if (after) {
            return Ext.factory(after, Ext.fx.State, current);
        }
    },

    setFrom: function(from) {
        return this.setState(this.STATE_FROM, from);
    },

    setTo: function(to) {
        return this.setState(this.STATE_TO, to);
    },

    getFrom: function() {
        return this.getState(this.STATE_FROM);
    },

    getTo: function() {
        return this.getState(this.STATE_TO);
    },

    setStates: function(states) {
        var validNameRegex = this.stateNameRegex,
            name;

        for (name in states) {
            if (validNameRegex.test(name)) {
                this.setState(name, states[name]);
            }
        }

        return this;
    },

    getStates: function() {
        return this.states;
    },

    setState: function(name, state) {
        var states = this.getStates(),
            stateInstance;

        stateInstance = Ext.factory(state, Ext.fx.State, states[name]);

        if (stateInstance) {
            states[name] = stateInstance;
        }
        else if (name === this.STATE_TO) {
            Ext.Logger.error("Setting and invalid '100%' / 'to' state of: " + state);
        }

        return this;
    },

    getState: function(name) {
        return this.getStates()[name];
    },

    getData: function() {
        var states = this.getStates(),
            statesData = {},
            before = this.getBefore(),
            after = this.getAfter(),
            from = states[this.STATE_FROM],
            to = states[this.STATE_TO],
            fromData = from.getData(),
            toData = to.getData(),
            data, name, state;

        for (name in states) {
            if (states.hasOwnProperty(name)) {
                state = states[name];
                data = state.getData();
                statesData[name] = data;
            }
        }

        if (Ext.os.is.Android2) {
            statesData['0.0001%'] = fromData;
        }

        return {
            before: before ? before.getData() : {},
            after: after ? after.getData() : {},
            states: statesData,
            from: fromData,
            to: toData,
            duration: this.getDuration(),
            iteration: this.getIteration(),
            direction: this.getDirection(),
            easing: this.getEasing(),
            delay: this.getDelay(),
            onEnd: this.getOnEnd(),
            onBeforeEnd: this.getOnBeforeEnd(),
            onBeforeStart: this.getOnBeforeStart(),
            scope: this.getScope(),
            preserveEndState: this.getPreserveEndState(),
            replacePrevious: this.getReplacePrevious()
        };
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.animation.Cube', {
    extend: 'Ext.fx.animation.Abstract',

    alias: 'animation.cube',

    config: {
        // @inherit
        before: {
//            'transform-style': 'preserve-3d'
        },

        after: {},

        /**
         * @cfg {String} direction The direction of which the slide animates
         * @accessor
         */
        direction: 'right',

        out: false
    },

//    getData: function() {
//        var to = this.getTo(),
//            from = this.getFrom(),
//            out  = this.getOut(),
//            direction  = this.getDirection(),
//            el = this.getElement(),
//            elW = el.getWidth(),
//            elH = el.getHeight(),
//            halfWidth = (elW / 2),
//            halfHeight = (elH / 2),
//            fromTransform = {},
//            toTransform = {},
//            originalFromTransform = {
//                rotateY: 0,
//                translateX: 0,
//                translateZ: 0
//            },
//            originalToTransform = {
//                rotateY: 90,
//                translateX: halfWidth,
//                translateZ: halfWidth
//            },
//            originalVerticalFromTransform = {
//                rotateX: 0,
//                translateY: 0,
//                translateZ: 0
//            },
//            originalVerticalToTransform = {
//                rotateX: 90,
//                translateY: halfHeight,
//                translateZ: halfHeight
//            },
//            tempTransform;
//
//        if (direction == "left" || direction == "right") {
//            if (out) {
//                toTransform = originalToTransform;
//                fromTransform = originalFromTransform;
//            } else {
//                toTransform = originalFromTransform;
//                fromTransform = originalToTransform;
//                fromTransform.rotateY *= -1;
//                fromTransform.translateX *= -1;
//            }
//
//            if (direction === 'right') {
//                tempTransform = fromTransform;
//                fromTransform = toTransform;
//                toTransform = tempTransform;
//            }
//        }
//
//        if (direction == "up" || direction == "down") {
//            if (out) {
//                toTransform = originalVerticalFromTransform;
//                fromTransform = {
//                    rotateX: -90,
//                    translateY: halfHeight,
//                    translateZ: halfHeight
//                };
//            } else {
//                fromTransform = originalVerticalFromTransform;
//                toTransform = {
//                    rotateX: 90,
//                    translateY: -halfHeight,
//                    translateZ: halfHeight
//                };
//            }
//
//            if (direction == "up") {
//                tempTransform = fromTransform;
//                fromTransform = toTransform;
//                toTransform = tempTransform;
//            }
//        }
//
//        from.set('transform', fromTransform);
//        to.set('transform', toTransform);
//
//        return this.callParent(arguments);
//    },

    getData: function() {
        var to = this.getTo(),
            from = this.getFrom(),
            before = this.getBefore(),
            after = this.getAfter(),
            out  = this.getOut(),
            direction  = this.getDirection(),
            el = this.getElement(),
            elW = el.getWidth(),
            elH = el.getHeight(),
            origin = out ? '100% 100%' : '0% 0%',
            fromOpacity = 1,
            toOpacity = 1,
            transformFrom = {
                rotateY: 0,
                translateZ: 0,
            },
            transformTo = {
                rotateY: 0,
                translateZ: 0
            };

        if (direction == "left" || direction == "right") {
            if (out) {
                toOpacity = 0.5;
                transformTo.translateZ = elW;
                transformTo.rotateY = -90;
            } else {
                fromOpacity = 0.5;
                transformFrom.translateZ = elW;
                transformFrom.rotateY = 90;
            }
        }

        before['transform-origin'] = origin;
        after['transform-origin'] = null;

        to.set('transform', transformTo);
        from.set('transform', transformFrom);

        from.set('opacity', fromOpacity);
        to.set('opacity', toOpacity);

        return this.callParent(arguments);
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.animation.Fade', {
    extend: 'Ext.fx.animation.Abstract',

    alternateClassName: 'Ext.fx.animation.FadeIn',

    alias: ['animation.fade', 'animation.fadeIn'],

    config: {
        /**
         * @cfg {Boolean} out True if you want to make this animation fade out, instead of fade in.
         * @accessor
         */

        out: false,

        before: {
            display: null,
            opacity: 0
        },

        after: {
            opacity: null
        },
        reverse: null
    },

    updateOut: function(newOut) {
        var to   = this.getTo(),
            from = this.getFrom();

        if (newOut) {
            from.set('opacity', 1);
            to.set('opacity',   0);
        } else {
            from.set('opacity', 0);
            to.set('opacity',   1);
        }
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.animation.FadeOut', {
    extend: 'Ext.fx.animation.Fade',
    alias: 'animation.fadeOut',

    config: {
        // @hide
        out: true,

        before: {}
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.animation.Flip', {
    extend: 'Ext.fx.animation.Abstract',

    alias: 'animation.flip',

    config: {
        easing: 'ease-in',

        /**
         * @cfg {String} direction The direction of which the slide animates
         * @accessor
         */
        direction: 'right',

        half: false,

        out: null
    },

    getData: function() {
        var from = this.getFrom(),
            to = this.getTo(),
            direction = this.getDirection(),
            out = this.getOut(),
            half = this.getHalf(),
            rotate = (half) ? 90 : 180,
            fromScale = 1,
            toScale = 1,
            fromRotateX = 0,
            fromRotateY = 0,
            toRotateX = 0,
            toRotateY = 0;


        if (out) {
            toScale = 0.8;
        }
        else {
            fromScale = 0.8;
        }

        switch (direction) {
            case this.DIRECTION_UP:
                if (out) {
                    toRotateX = rotate;
                }
                else {
                    fromRotateX = -rotate;
                }
                break;

            case this.DIRECTION_DOWN:
                if (out) {
                    toRotateX = -rotate;
                }
                else {
                    fromRotateX = rotate;
                }
                break;

            case this.DIRECTION_RIGHT:
                if (out) {
                    toRotateY = -rotate;
                }
                else {
                    fromRotateY = rotate;
                }
                break;

            case this.DIRECTION_LEFT:
                if (out) {
                    toRotateY = -rotate;
                }
                else {
                    fromRotateY = rotate;
                }
                break;
        }

        from.setTransform({
            rotateX: fromRotateX,
            rotateY: fromRotateY,
            scale: fromScale
        });

        to.setTransform({
            rotateX: toRotateX,
            rotateY: toRotateY,
            scale: toScale
        });

        return this.callParent(arguments);
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.animation.Pop', {
    extend: 'Ext.fx.animation.Abstract',

    alias: ['animation.pop', 'animation.popIn'],

    alternateClassName: 'Ext.fx.animation.PopIn',

    config: {
        /**
         * @cfg {Boolean} out True if you want to make this animation pop out, instead of pop in.
         * @accessor
         */
        out: false,

        before: {
            display: null,
            opacity: 0
        },
        after: {
            opacity: null
        }
    },

    getData: function() {
        var to = this.getTo(),
            from = this.getFrom(),
            out = this.getOut();

        if (out) {
            from.set('opacity', 1);
            from.setTransform({
                scale: 1
            });

            to.set('opacity', 0);
            to.setTransform({
                scale: 0
            });
        }
        else {
            from.set('opacity', 0);
            from.setTransform({
                scale: 0
            });

            to.set('opacity', 1);
            to.setTransform({
                scale: 1
            });
        }

        return this.callParent(arguments);
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.animation.PopOut', {
    extend: 'Ext.fx.animation.Pop',

    alias: 'animation.popOut',

    config: {
        // @hide
        out: true,

        before: {}
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.animation.Slide', {

    extend: 'Ext.fx.animation.Abstract',

    alternateClassName: 'Ext.fx.animation.SlideIn',

    alias: ['animation.slide', 'animation.slideIn'],

    config: {
        /**
         * @cfg {String} direction The direction of which the slide animates
         * @accessor
         */
        direction: 'left',

        /**
         * @cfg {Boolean} out True if you want to make this animation slide out, instead of slide in.
         * @accessor
         */
        out: false,

        /**
         * @cfg {Number} offset The offset that the animation should go offscreen before entering (or when exiting)
         * @accessor
         */
        offset: 0,

        // @inherit
        easing: 'auto',

        containerBox: 'auto',

        elementBox: 'auto',

        isElementBoxFit: true,

        useCssTransform: true
    },

    reverseDirectionMap: {
        up: 'down',
        down: 'up',
        left: 'right',
        right: 'left'
    },

    applyEasing: function(easing) {
        if (easing === 'auto') {
            return 'ease-' + ((this.getOut()) ? 'in' : 'out');
        }

        return easing;
    },

    getContainerBox: function() {
        var box = this._containerBox;

        if (box === 'auto') {
            box = this.getElement().getParent().getPageBox();
        }

        return box;
    },

    getElementBox: function() {
        var box = this._elementBox;

        if (this.getIsElementBoxFit()) {
            return this.getContainerBox();
        }

        if (box === 'auto') {
            box = this.getElement().getPageBox();
        }

        return box;
    },

    getData: function() {
        var elementBox = this.getElementBox(),
            containerBox = this.getContainerBox(),
            box = elementBox ? elementBox : containerBox,
            from = this.getFrom(),
            to = this.getTo(),
            out = this.getOut(),
            offset = this.getOffset(),
            direction = this.getDirection(),
            useCssTransform = this.getUseCssTransform(),
            reverse = this.getReverse(),
            translateX = 0,
            translateY = 0,
            fromX, fromY, toX, toY;

        if (reverse) {
            direction = this.reverseDirectionMap[direction];
        }

        switch (direction) {
            case this.DIRECTION_UP:
                if (out) {
                    translateY = containerBox.top - box.top - box.height - offset;
                }
                else {
                    translateY = containerBox.bottom - box.bottom + box.height + offset;
                }

                break;

            case this.DIRECTION_DOWN:
                if (out) {
                    translateY = containerBox.bottom - box.bottom + box.height + offset;
                }
                else {
                    translateY = containerBox.top - box.height - box.top - offset;
                }

                break;

            case this.DIRECTION_RIGHT:
                if (out) {
                    translateX = containerBox.right - box.right + box.width + offset;
                }
                else {
                    translateX = containerBox.left - box.left - box.width - offset;
                }

                break;

            case this.DIRECTION_LEFT:
                if (out) {
                    translateX = containerBox.left - box.left - box.width - offset;
                }
                else {
                    translateX = containerBox.right - box.right + box.width + offset;
                }

                break;
        }

        fromX = (out) ? 0 : translateX;
        fromY = (out) ? 0 : translateY;

        if (useCssTransform) {
            from.setTransform({
                translateX: fromX,
                translateY: fromY
            });
        }
        else {
            from.set('left', fromX);
            from.set('top', fromY);
        }

        toX = (out) ? translateX : 0;
        toY = (out) ? translateY : 0;

        if (useCssTransform) {
            to.setTransform({
                translateX: toX,
                translateY: toY
            });
        }
        else {
            to.set('left', toX);
            to.set('top', toY);
        }

        return this.callParent(arguments);
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.animation.SlideOut', {
    extend: 'Ext.fx.animation.Slide',
    alias: ['animation.slideOut'],

    config: {
        // @hide
        out: true
    }
});

/**
 * @author Jacky Nguyen <jacky@sencha.com>
 */
Ext.define('Ext.fx.Animation', {

    requires: [
        'Ext.fx.animation.Slide',
        'Ext.fx.animation.SlideOut',
        'Ext.fx.animation.Fade',
        'Ext.fx.animation.FadeOut',
        'Ext.fx.animation.Flip',
        'Ext.fx.animation.Pop',
        'Ext.fx.animation.PopOut',
        'Ext.fx.animation.Cube'
    ],

    constructor: function(config) {
        var defaultClass = Ext.fx.animation.Abstract,
            type;

        if (typeof config == 'string') {
            type = config;
            config = {};
        }
        else if (config && config.type) {
            type = config.type;
        }

        if (type) {
            if (Ext.os.is.Android2) {
                if (type == 'pop') {
                    type = 'fade';
                }
                if (type == 'popIn') {
                    type = 'fadeIn';
                }
                if (type == 'popOut') {
                    type = 'fadeOut';
                }
            }
            defaultClass = Ext.ClassManager.getByAlias('animation.' + type);

            if (!defaultClass) {
                Ext.Logger.error("Invalid animation type of: '" + type + "'");
            }
        }

        return Ext.factory(config, defaultClass);
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.layout.card.Abstract', {
    extend: 'Ext.Evented',
    isAnimation: true,

    config: {
        direction: 'left',

        duration: null,

        reverse: null,

        layout: null
    },

    updateLayout: function() {
        this.enable();
    },

    enable: function() {
        var layout = this.getLayout();

        if (layout) {
            layout.onBefore('activeitemchange', 'onActiveItemChange', this);
        }
    },

    disable: function() {
        var layout = this.getLayout();

        if (this.isAnimating) {
            this.stopAnimation();
        }

        if (layout) {
            layout.unBefore('activeitemchange', 'onActiveItemChange', this);
        }
    },

    onActiveItemChange: Ext.emptyFn,

    destroy: function() {
        var layout = this.getLayout();

        if (this.isAnimating) {
            this.stopAnimation();
        }

        if (layout) {
            layout.unBefore('activeitemchange', 'onActiveItemChange', this);
        }
        this.setLayout(null);
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.layout.card.Scroll', {
    extend: 'Ext.fx.layout.card.Abstract',

    requires: [
        'Ext.fx.easing.Linear'
    ],

    alias: 'fx.layout.card.scroll',

    config: {
        duration: 150
    },

    constructor: function(config) {
        this.initConfig(config);

        this.doAnimationFrame = Ext.Function.bind(this.doAnimationFrame, this);
    },

    getEasing: function() {
        var easing = this.easing;

        if (!easing) {
            this.easing = easing = new Ext.fx.easing.Linear();
        }

        return easing;
    },

    updateDuration: function(duration) {
        this.getEasing().setDuration(duration);
    },

    onActiveItemChange: function(cardLayout, newItem, oldItem, options, controller) {
        var direction = this.getDirection(),
            easing = this.getEasing(),
            containerElement, inElement, outElement, containerWidth, containerHeight, reverse;

        if (newItem && oldItem) {
            if (this.isAnimating) {
                this.stopAnimation();
            }

            containerElement = this.getLayout().container.innerElement;
            containerWidth = containerElement.getWidth();
            containerHeight = containerElement.getHeight();

            inElement = newItem.renderElement;
            outElement = oldItem.renderElement;

            this.oldItem = oldItem;
            this.newItem = newItem;
            this.currentEventController = controller;
            this.containerElement = containerElement;
            this.isReverse = reverse = this.getReverse();

            newItem.show();

            if (direction == 'right') {
                direction = 'left';
                this.isReverse = reverse = !reverse;
            }
            else if (direction == 'down') {
                direction = 'up';
                this.isReverse = reverse = !reverse;
            }

            if (direction == 'left') {
                if (reverse) {
                    easing.setConfig({
                        startValue: containerWidth,
                        endValue: 0
                    });

                    containerElement.dom.scrollLeft = containerWidth;
                    outElement.setLeft(containerWidth);
                }
                else {
                    easing.setConfig({
                        startValue: 0,
                        endValue: containerWidth
                    });

                    inElement.setLeft(containerWidth);
                }
            }
            else {
                if (reverse) {
                    easing.setConfig({
                        startValue: containerHeight,
                        endValue: 0
                    });

                    containerElement.dom.scrollTop = containerHeight;
                    outElement.setTop(containerHeight);
                }
                else {
                    easing.setConfig({
                        startValue: 0,
                        endValue: containerHeight
                    });

                    inElement.setTop(containerHeight);
                }
            }

            this.startAnimation();

            controller.pause();
        }
    },

    startAnimation: function() {
        this.isAnimating = true;
        this.getEasing().setStartTime(Date.now());
        this.timer = setInterval(this.doAnimationFrame, 20);
        this.doAnimationFrame();
    },

    doAnimationFrame: function() {
        var easing = this.getEasing(),
            direction = this.getDirection(),
            scroll = 'scrollTop',
            value;

        if (direction == 'left' || direction == 'right') {
            scroll = 'scrollLeft';
        }

        if (easing.isEnded) {
            this.stopAnimation();
        }
        else {
            value = easing.getValue();
            this.containerElement.dom[scroll] = value;
        }
    },

    stopAnimation: function() {
        var direction = this.getDirection(),
            scroll = 'setTop';

        if (direction == 'left' || direction == 'right') {
            scroll = 'setLeft';
        }

        this.currentEventController.resume();

        if (this.isReverse) {
            this.oldItem.renderElement[scroll](null);
        }
        else {
            this.newItem.renderElement[scroll](null);
        }

        clearInterval(this.timer);
        this.isAnimating = false;
        this.fireEvent('animationend', this);
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.layout.card.Style', {

    extend: 'Ext.fx.layout.card.Abstract',

    requires: [
        'Ext.fx.Animation'
    ],

    config: {
        inAnimation: {
            before: {
                visibility: null
            },
            preserveEndState: false,
            replacePrevious: true
        },

        outAnimation: {
            preserveEndState: false,
            replacePrevious: true
        }
    },

    constructor: function(config) {
        var inAnimation, outAnimation;

        this.initConfig(config);

        this.endAnimationCounter = 0;

        inAnimation = this.getInAnimation();
        outAnimation = this.getOutAnimation();

        inAnimation.on('animationend', 'incrementEnd', this);
        outAnimation.on('animationend', 'incrementEnd', this);


        inAnimation.setConfig(config);
        outAnimation.setConfig(config);
    },

    incrementEnd: function() {
        this.endAnimationCounter++;

        if (this.endAnimationCounter > 1) {
            this.endAnimationCounter = 0;
            this.fireEvent('animationend', this);
        }
    },

    applyInAnimation: function(animation, inAnimation) {
        return Ext.factory(animation, Ext.fx.Animation, inAnimation);
    },

    applyOutAnimation: function(animation, outAnimation) {
        return Ext.factory(animation, Ext.fx.Animation, outAnimation);
    },

    updateInAnimation: function(animation) {
        animation.setScope(this);
    },

    updateOutAnimation: function(animation) {
        animation.setScope(this);
    },

    onActiveItemChange: function(cardLayout, newItem, oldItem, options, controller) {
        var inAnimation = this.getInAnimation(),
            outAnimation = this.getOutAnimation(),
            inElement, outElement;

        if (newItem && oldItem && oldItem.isPainted()) {
            inElement = newItem.renderElement;
            outElement = oldItem.renderElement;

            inAnimation.setElement(inElement);
            outAnimation.setElement(outElement);

            outAnimation.setOnBeforeEnd(function(element, interrupted) {
                if (interrupted || Ext.Animator.hasRunningAnimations(element)) {
                    controller.firingArguments[1] = null;
                    controller.firingArguments[2] = null;
                }
            });
            outAnimation.setOnEnd(function() {
                controller.resume();
            });

            inElement.dom.style.setProperty('visibility', 'hidden', '!important');
            newItem.show();

            Ext.Animator.run([outAnimation, inAnimation]);
            controller.pause();
        }
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.layout.card.Cover', {
    extend: 'Ext.fx.layout.card.Style',

    alias: 'fx.layout.card.cover',

    config: {
        reverse: null,

        inAnimation: {
            before: {
                'z-index': 100
            },
            after: {
                'z-index': 0
            },
            type: 'slide',
            easing: 'ease-out'
        },
        outAnimation: {
            easing: 'ease-out',
            from: {
                opacity: 0.99
            },
            to: {
                opacity: 1
            },
            out: true
        }
    },

    updateReverse: function(reverse) {
        this.getInAnimation().setReverse(reverse);
        this.getOutAnimation().setReverse(reverse);
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.layout.card.Cube', {
    extend: 'Ext.fx.layout.card.Style',

    alias: 'fx.layout.card.cube',

    config: {
        reverse: null,
        inAnimation: {
            type: 'cube'
        },
        outAnimation: {
            type: 'cube',
            out: true
        }
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.layout.card.Fade', {
    extend: 'Ext.fx.layout.card.Style',

    alias: 'fx.layout.card.fade',

    config: {
        reverse: null,
        
        inAnimation: {
            type: 'fade',
            easing: 'ease-out'
        },
        outAnimation: {
            type: 'fade',
            easing: 'ease-out',
            out: true
        }
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.layout.card.Flip', {
    extend: 'Ext.fx.layout.card.Style',

    alias: 'fx.layout.card.flip',

    config: {
        duration: 500,

        inAnimation: {
            type: 'flip',
            half: true,
            easing: 'ease-out',
            before: {
                'backface-visibility': 'hidden'
            },
            after: {
                'backface-visibility': null
            }
        },
        outAnimation: {
            type: 'flip',
            half: true,
            easing: 'ease-in',
            before: {
                'backface-visibility': 'hidden'
            },
            after: {
                'backface-visibility': null
            },
            out: true
        }
    },

    updateDuration: function(duration) {
        var halfDuration = duration / 2,
            inAnimation = this.getInAnimation(),
            outAnimation = this.getOutAnimation();

        inAnimation.setDelay(halfDuration);
        inAnimation.setDuration(halfDuration);
        outAnimation.setDuration(halfDuration);
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.layout.card.Pop', {
    extend: 'Ext.fx.layout.card.Style',

    alias: 'fx.layout.card.pop',

    config: {
        duration: 500,

        inAnimation: {
            type: 'pop',
            easing: 'ease-out'
        },
        outAnimation: {
            type: 'pop',
            easing: 'ease-in',
            out: true
        }
    },

    updateDuration: function(duration) {
        var halfDuration = duration / 2,
            inAnimation = this.getInAnimation(),
            outAnimation = this.getOutAnimation();

        inAnimation.setDelay(halfDuration);
        inAnimation.setDuration(halfDuration);
        outAnimation.setDuration(halfDuration);
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.layout.card.Reveal', {
    extend: 'Ext.fx.layout.card.Style',

    alias: 'fx.layout.card.reveal',

    config: {
        inAnimation: {
            easing: 'ease-out',
            from: {
                opacity: 0.99
            },
            to: {
                opacity: 1
            }
        },
        outAnimation: {
            before: {
                'z-index': 100
            },
            after: {
                'z-index': 0
            },
            type: 'slide',
            easing: 'ease-out',
            out: true
        }
    },

    updateReverse: function(reverse) {
        this.getInAnimation().setReverse(reverse);
        this.getOutAnimation().setReverse(reverse);
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.layout.card.Slide', {
    extend: 'Ext.fx.layout.card.Style',

    alias: 'fx.layout.card.slide',

    config: {
        inAnimation: {
            type: 'slide',
            easing: 'ease-out'
        },
        outAnimation: {
            type: 'slide',
            easing: 'ease-out',
            out: true
        }
    },

    updateReverse: function(reverse) {
        this.getInAnimation().setReverse(reverse);
        this.getOutAnimation().setReverse(reverse);
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.layout.Card', {
    requires: [
        'Ext.fx.layout.card.Slide',
        'Ext.fx.layout.card.Cover',
        'Ext.fx.layout.card.Reveal',
        'Ext.fx.layout.card.Fade',
        'Ext.fx.layout.card.Flip',
        'Ext.fx.layout.card.Pop',
        'Ext.fx.layout.card.Cube',
        'Ext.fx.layout.card.Scroll'
    ],

    constructor: function(config) {
        var defaultClass = Ext.fx.layout.card.Abstract,
            type;

        if (!config) {
            return null;
        }

        if (typeof config == 'string') {
            type = config;

            config = {};
        }
        else if (config.type) {
            type = config.type;
        }

        config.elementBox = false;

        if (type) {

            if (Ext.os.is.Android2) {
                // In Android 2 we only support scroll and fade. Otherwise force it to slide.
                if (type != 'fade') {
                    type = 'scroll';
                }
            }
            else if (type === 'slide' && Ext.browser.is.ChromeMobile) {
                type = 'scroll';
            }

            defaultClass = Ext.ClassManager.getByAlias('fx.layout.card.' + type);

            if (!defaultClass) {
                Ext.Logger.error("Unknown card animation type: '" + type + "'");
            }
        }

        return Ext.factory(config, defaultClass);
    }
});

/**
 * @private
 */
Ext.define('Ext.fx.runner.Css', {
    extend: 'Ext.Evented',

    requires: [
        'Ext.fx.Animation'
    ],

    prefixedProperties: {
        'transform'                 : true,
        'transform-origin'          : true,
        'perspective'               : true,
        'transform-style'           : true,
        'transition'                : true,
        'transition-property'       : true,
        'transition-duration'       : true,
        'transition-timing-function': true,
        'transition-delay'          : true,
        'animation'                 : true,
        'animation-name'            : true,
        'animation-duration'        : true,
        'animation-iteration-count' : true,
        'animation-direction'       : true,
        'animation-timing-function' : true,
        'animation-delay'           : true
    },

    lengthProperties: {
        'top'                : true,
        'right'              : true,
        'bottom'             : true,
        'left'               : true,
        'width'              : true,
        'height'             : true,
        'max-height'         : true,
        'max-width'          : true,
        'min-height'         : true,
        'min-width'          : true,
        'margin-bottom'      : true,
        'margin-left'        : true,
        'margin-right'       : true,
        'margin-top'         : true,
        'padding-bottom'     : true,
        'padding-left'       : true,
        'padding-right'      : true,
        'padding-top'        : true,
        'border-bottom-width': true,
        'border-left-width'  : true,
        'border-right-width' : true,
        'border-spacing'     : true,
        'border-top-width'   : true,
        'border-width'       : true,
        'outline-width'      : true,
        'letter-spacing'     : true,
        'line-height'        : true,
        'text-indent'        : true,
        'word-spacing'       : true,
        'font-size'          : true,
        'translate'          : true,
        'translateX'         : true,
        'translateY'         : true,
        'translateZ'         : true,
        'translate3d'        : true
    },

    durationProperties: {
        'transition-duration'   : true,
        'transition-delay'      : true,
        'animation-duration'    : true,
        'animation-delay'       : true
    },

    angleProperties: {
        rotate     : true,
        rotateX    : true,
        rotateY    : true,
        rotateZ    : true,
        skew       : true,
        skewX      : true,
        skewY      : true
    },

    lengthUnitRegex: /([a-z%]*)$/,

    DEFAULT_UNIT_LENGTH: 'px',

    DEFAULT_UNIT_ANGLE: 'deg',

    DEFAULT_UNIT_DURATION: 'ms',

    formattedNameCache: {},

    constructor: function() {
        var supports3dTransform = Ext.feature.has.Css3dTransforms;

        if (supports3dTransform) {
            this.transformMethods = ['translateX', 'translateY', 'translateZ', 'rotate', 'rotateX', 'rotateY', 'rotateZ', 'skewX', 'skewY', 'scaleX', 'scaleY', 'scaleZ'];
        }
        else {
            this.transformMethods = ['translateX', 'translateY', 'rotate', 'skewX', 'skewY', 'scaleX', 'scaleY'];
        }

        this.vendorPrefix = Ext.browser.getStyleDashPrefix();

        this.ruleStylesCache = {};

        return this;
    },

    getStyleSheet: function() {
        var styleSheet = this.styleSheet,
            styleElement, styleSheets;

        if (!styleSheet) {
            styleElement = document.createElement('style');
            styleElement.type = 'text/css';

            (document.head || document.getElementsByTagName('head')[0]).appendChild(styleElement);

            styleSheets = document.styleSheets;

            this.styleSheet = styleSheet = styleSheets[styleSheets.length - 1];
        }

        return styleSheet;
    },

    applyRules: function(selectors) {
        var styleSheet = this.getStyleSheet(),
            ruleStylesCache = this.ruleStylesCache,
            rules = styleSheet.cssRules,
            selector, properties, ruleStyle,
            ruleStyleCache, rulesLength, name, value;

        for (selector in selectors) {
            properties = selectors[selector];

            ruleStyle = ruleStylesCache[selector];

            if (ruleStyle === undefined) {
                rulesLength = rules.length;
                styleSheet.insertRule(selector + '{}', rulesLength);
                ruleStyle = ruleStylesCache[selector] = rules.item(rulesLength).style;
            }

            ruleStyleCache = ruleStyle.$cache;

            if (!ruleStyleCache) {
                ruleStyleCache = ruleStyle.$cache = {};
            }

            for (name in properties) {
                value = this.formatValue(properties[name], name);
                name = this.formatName(name);

                if (ruleStyleCache[name] !== value) {
                    ruleStyleCache[name] = value;
//                    console.log(name + " " + value);

                    if (value === null) {
                        ruleStyle.removeProperty(name);
                    }
                    else {
                        ruleStyle.setProperty(name, value, 'important');
                    }
                }
            }
        }

        return this;
    },

    applyStyles: function(styles) {
        var id, element, elementStyle, properties, name, value;

        for (id in styles) {
//            console.log("-> ["+id+"]", "APPLY======================");
            element = document.getElementById(id);

            if (!element) {
                return this;
            }

            elementStyle = element.style;

            properties = styles[id];

            for (name in properties) {
                value = this.formatValue(properties[name], name);
                name = this.formatName(name);

//                console.log("->-> ["+id+"]", name, value);

                if (value === null) {
                    elementStyle.removeProperty(name);
                }
                else {
                    elementStyle.setProperty(name, value, 'important');
                }
            }
        }

        return this;
    },

    formatName: function(name) {
        var cache = this.formattedNameCache,
            formattedName = cache[name];

        if (!formattedName) {
            if (this.prefixedProperties[name]) {
                formattedName = this.vendorPrefix + name;
            }
            else {
                formattedName = name;
            }

            cache[name] = formattedName;
        }

        return formattedName;
    },

    formatValue: function(value, name) {
        var type = typeof value,
            lengthUnit = this.DEFAULT_UNIT_LENGTH,
            transformMethods,
            method, i, ln,
            transformValues, values, unit;

        if (type == 'string') {
            if (this.lengthProperties[name]) {
                unit = value.match(this.lengthUnitRegex)[1];

                if (unit.length > 0) {
                    if (unit !== lengthUnit) {
                        Ext.Logger.error("Length unit: '" + unit + "' in value: '" + value + "' of property: '" + name + "' is not " +
                            "valid for animation. Only 'px' is allowed");
                    }
                }
                else {
                    return value + lengthUnit;
                }
            }

            return value;
        }
        else if (type == 'number') {
            if (value == 0) {
                return '0';
            }

            if (this.lengthProperties[name]) {
                return value + lengthUnit;
            }

            if (this.angleProperties[name]) {
                return value + this.DEFAULT_UNIT_ANGLE;
            }

            if (this.durationProperties[name]) {
                return value + this.DEFAULT_UNIT_DURATION;
            }
        }
        else if (name === 'transform') {
            transformMethods = this.transformMethods;
            transformValues = [];

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

                transformValues.push(method + '(' + this.formatValue(value[method], method) + ')');
            }

            return transformValues.join(' ');
        }
        else if (Ext.isArray(value)) {
            values = [];

            for (i = 0,ln = value.length; i < ln; i++) {
                values.push(this.formatValue(value[i], name));
            }

            return (values.length > 0) ? values.join(', ') : 'none';
        }

        return value;
    }
});

/**
 * @author Jacky Nguyen <jacky@sencha.com>
 * @private
 */
Ext.define('Ext.fx.runner.CssTransition', {
    extend: 'Ext.fx.runner.Css',

    listenersAttached: false,

    constructor: function() {
        this.runningAnimationsData = {};

        return this.callParent(arguments);
    },

    attachListeners: function() {
        this.listenersAttached = true;
        this.getEventDispatcher().addListener('element', '*', 'transitionend', 'onTransitionEnd', this);
    },

    onTransitionEnd: function(e) {
        var target = e.target,
            id = target.id;

        if (id && this.runningAnimationsData.hasOwnProperty(id)) {
            this.refreshRunningAnimationsData(Ext.get(target), [e.browserEvent.propertyName]);
        }
    },

    onAnimationEnd: function(element, data, animation, isInterrupted, isReplaced) {
        var id = element.getId(),
            runningData = this.runningAnimationsData[id],
            runningNameMap = runningData.nameMap,
            endRules = {},
            endData = {},
            toPropertyNames, i, ln, name;

        endRules[id] = endData;

        if (data.onBeforeEnd) {
            data.onBeforeEnd.call(data.scope || this, element, isInterrupted);
        }

        animation.fireEvent('animationbeforeend', animation, element, isInterrupted);
        this.fireEvent('animationbeforeend', this, animation, element, isInterrupted);

        if (isReplaced || (!isInterrupted && !data.preserveEndState)) {
            toPropertyNames = data.toPropertyNames;

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

                if (!runningNameMap.hasOwnProperty(name)) {
                    endData[name] = null;
                }
            }
        }

        if (data.after) {
            Ext.merge(endData, data.after);
        }

        this.applyStyles(endRules);

        if (data.onEnd) {
            data.onEnd.call(data.scope || this, element, isInterrupted);
        }

        animation.fireEvent('animationend', animation, element, isInterrupted);
        this.fireEvent('animationend', this, animation, element, isInterrupted);
    },

    onAllAnimationsEnd: function(element) {
        var id = element.getId(),
            endRules = {};

        delete this.runningAnimationsData[id];

        endRules[id] = {
            'transition-property': null,
            'transition-duration': null,
            'transition-timing-function': null,
            'transition-delay': null
        };

        this.applyStyles(endRules);
        this.fireEvent('animationallend', this, element);
    },

    hasRunningAnimations: function(element) {
        var id = element.getId(),
            runningAnimationsData = this.runningAnimationsData;

        return runningAnimationsData.hasOwnProperty(id) && runningAnimationsData[id].sessions.length > 0;
    },

    refreshRunningAnimationsData: function(element, propertyNames, interrupt, replace) {
        var id = element.getId(),
            runningAnimationsData = this.runningAnimationsData,
            runningData = runningAnimationsData[id],
            nameMap = runningData.nameMap,
            nameList = runningData.nameList,
            sessions = runningData.sessions,
            ln, j, subLn, name,
            i, session, map, list,
            hasCompletedSession = false;

        interrupt = Boolean(interrupt);
        replace = Boolean(replace);

        if (!sessions) {
            return this;
        }

        ln = sessions.length;

        if (ln === 0) {
            return this;
        }

        if (replace) {
            runningData.nameMap = {};
            nameList.length = 0;

            for (i = 0; i < ln; i++) {
                session = sessions[i];
                this.onAnimationEnd(element, session.data, session.animation, interrupt, replace);
            }

            sessions.length = 0;
        }
        else {
            for (i = 0; i < ln; i++) {
                session = sessions[i];
                map = session.map;
                list = session.list;

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

                    if (map[name]) {
                        delete map[name];
                        Ext.Array.remove(list, name);
                        session.length--;
                        if (--nameMap[name] == 0) {
                            delete nameMap[name];
                            Ext.Array.remove(nameList, name);
                        }
                    }
                }

                if (session.length == 0) {
                    sessions.splice(i, 1);
                    i--;
                    ln--;

                    hasCompletedSession = true;
                    this.onAnimationEnd(element, session.data, session.animation, interrupt);
                }
            }
        }

        if (!replace && !interrupt && sessions.length == 0 && hasCompletedSession) {
            this.onAllAnimationsEnd(element);
        }
    },

    getRunningData: function(id) {
        var runningAnimationsData = this.runningAnimationsData;

        if (!runningAnimationsData.hasOwnProperty(id)) {
            runningAnimationsData[id] = {
                nameMap: {},
                nameList: [],
                sessions: []
            };
        }

        return runningAnimationsData[id];
    },

    getTestElement: function() {
        var testElement = this.testElement,
            iframe, iframeDocument, iframeStyle;

        if (!testElement) {
            iframe = document.createElement('iframe');
            iframeStyle = iframe.style;
            iframeStyle.setProperty('visibility', 'hidden', 'important');
            iframeStyle.setProperty('width', '0px', 'important');
            iframeStyle.setProperty('height', '0px', 'important');
            iframeStyle.setProperty('position', 'absolute', 'important');
            iframeStyle.setProperty('border', '0px', 'important');
            iframeStyle.setProperty('zIndex', '-1000', 'important');

            document.body.appendChild(iframe);
            iframeDocument = iframe.contentDocument;

            iframeDocument.open();
            iframeDocument.writeln('</body>');
            iframeDocument.close();

            this.testElement = testElement = iframeDocument.createElement('div');
            testElement.style.setProperty('position', 'absolute', '!important');
            iframeDocument.body.appendChild(testElement);
            this.testElementComputedStyle = window.getComputedStyle(testElement);
        }

        return testElement;
    },

    getCssStyleValue: function(name, value) {
        var testElement = this.getTestElement(),
            computedStyle = this.testElementComputedStyle,
            style = testElement.style;

        style.setProperty(name, value);
        value = computedStyle.getPropertyValue(name);
        style.removeProperty(name);

        return value;
    },

    run: function(animations) {
        var me = this,
            isLengthPropertyMap = this.lengthProperties,
            fromData = {},
            toData = {},
            data = {},
            element, elementId, from, to, before,
            fromPropertyNames, toPropertyNames,
            doApplyTo, message,
            runningData,
            i, j, ln, animation, propertiesLength, sessionNameMap,
            computedStyle, formattedName, name, toFormattedValue,
            computedValue, fromFormattedValue, isLengthProperty,
            runningNameMap, runningNameList, runningSessions;

        if (!this.listenersAttached) {
            this.attachListeners();
        }

        animations = Ext.Array.from(animations);

        for (i = 0,ln = animations.length; i < ln; i++) {
            animation = animations[i];
            animation = Ext.factory(animation, Ext.fx.Animation);
            element = animation.getElement();

            computedStyle = window.getComputedStyle(element.dom);

            elementId = element.getId();

            data = Ext.merge({}, animation.getData());

            if (animation.onBeforeStart) {
                animation.onBeforeStart.call(animation.scope || this, element);
                animation.fireEvent('animationstart', animation);
                this.fireEvent('animationstart', this, animation);
            }

            data[elementId] = data;

            before = data.before;
            from = data.from;
            to = data.to;

            data.fromPropertyNames = fromPropertyNames = [];
            data.toPropertyNames = toPropertyNames = [];

            for (name in to) {
                if (to.hasOwnProperty(name)) {
                    to[name] = toFormattedValue = this.formatValue(to[name], name);
                    formattedName = this.formatName(name);
                    isLengthProperty = isLengthPropertyMap.hasOwnProperty(name);

                    if (!isLengthProperty) {
                        toFormattedValue = this.getCssStyleValue(formattedName, toFormattedValue);
                    }

                    if (from.hasOwnProperty(name)) {
                        from[name] = fromFormattedValue = this.formatValue(from[name], name);

                        if (!isLengthProperty) {
                            fromFormattedValue = this.getCssStyleValue(formattedName, fromFormattedValue);
                        }

                        if (toFormattedValue !== fromFormattedValue) {
                            fromPropertyNames.push(formattedName);
                            toPropertyNames.push(formattedName);
                        }
                    }
                    else {
                        computedValue = computedStyle.getPropertyValue(formattedName);

                        if (toFormattedValue !== computedValue) {
                            toPropertyNames.push(formattedName);
                        }
                    }
                }
            }

            propertiesLength = toPropertyNames.length;

            if (propertiesLength === 0) {
                this.onAnimationEnd(element, data, animation);
                continue;
            }

            runningData = this.getRunningData(elementId);
            runningSessions = runningData.sessions;

            if (runningSessions.length > 0) {
                this.refreshRunningAnimationsData(
                    element, Ext.Array.merge(fromPropertyNames, toPropertyNames), true, data.replacePrevious
                );
            }

            runningNameMap = runningData.nameMap;
            runningNameList = runningData.nameList;

            sessionNameMap = {};
            for (j = 0; j < propertiesLength; j++) {
                name = toPropertyNames[j];
                sessionNameMap[name] = true;

                if (!runningNameMap.hasOwnProperty(name)) {
                    runningNameMap[name] = 1;
                    runningNameList.push(name);
                }
                else {
                    runningNameMap[name]++;
                }
            }

            runningSessions.push({
                element: element,
                map: sessionNameMap,
                list: toPropertyNames.slice(),
                length: propertiesLength,
                data: data,
                animation: animation
            });

            fromData[elementId] = from = Ext.apply(Ext.Object.chain(before), from);

            if (runningNameList.length > 0) {
                fromPropertyNames = Ext.Array.difference(runningNameList, fromPropertyNames);
                toPropertyNames = Ext.Array.merge(fromPropertyNames, toPropertyNames);
                from['transition-property'] = fromPropertyNames;
            }

            toData[elementId] = to = Ext.Object.chain(to);

            to['transition-property'] = toPropertyNames;
            to['transition-duration'] = data.duration;
            to['transition-timing-function'] = data.easing;
            to['transition-delay'] = data.delay;

            animation.startTime = Date.now();
        }

        message = this.$className;

        this.applyStyles(fromData);

        doApplyTo = function(e) {
            if (e.data === message && e.source === window) {
                window.removeEventListener('message', doApplyTo, false);
                me.applyStyles(toData);
            }
        };

        window.addEventListener('message', doApplyTo, false);
        window.postMessage(message, '*');
    }
});

/**
 * @class Ext.fx.Runner
 * @private
 */
Ext.define('Ext.fx.Runner', {
    requires: [
        'Ext.fx.runner.CssTransition'
//        'Ext.fx.runner.CssAnimation'
    ],

    constructor: function() {
        return new Ext.fx.runner.CssTransition();
    }
});

(function(clsPrefix) {

/**
 * The Default Layout is the layout that all other layouts inherit from. The main capability it provides is docking,
 * which means that every other layout can also provide docking support. It's unusual to use Default layout directly,
 * instead it's much more common to use one of the sub classes:
 *
 * * {@link Ext.layout.HBox hbox layout}
 * * {@link Ext.layout.VBox vbox layout}
 * * {@link Ext.layout.Card card layout}
 * * {@link Ext.layout.Fit fit layout}
 *
 * For a full overview of layouts check out the [Layout Guide](#!/guide/layouts).
 *
 * ## Docking
 *
 * Docking enables you to place additional Components at the top, right, bottom or left edges of the parent Container,
 * resizing the other items as necessary. For example, let's say we're using an {@link Ext.layout.HBox hbox layout}
 * with a couple of items and we want to add a banner to the top so that we end up with something like this:
 *
 * {@img ../guides/layouts/docktop.jpg}
 *
 * This is simple to achieve with the `docked: 'top'` configuration below. We can dock as many of the items as we like,
 * to either the top, right, bottom or left edges of the Container:
 *
 *     Ext.create('Ext.Container', {
 *         fullscreen: true,
 *         layout: 'hbox',
 *         items: [
 *             {
 *                 docked: 'top',
 *                 height: 20,
 *                 html: 'This is docked to the top'
 *             },
 *             {
 *                 html: 'message list',
 *                 flex: 1
 *             },
 *             {
 *                 html: 'message preview',
 *                 flex: 2
 *             }
 *         ]
 *     });
 *
 * Similarly, to dock something to the left of a layout (a {@link Ext.layout.VBox vbox} in this case), such as the
 * following:
 *
 * {@img ../guides/layouts/dockleft.jpg}
 *
 * We can simply dock to the left:
 *
 *     Ext.create('Ext.Container', {
 *         fullscreen: true,
 *         layout: 'vbox',
 *         items: [
 *             {
 *                 docked: 'left',
 *                 width: 100,
 *                 html: 'This is docked to the left'
 *             },
 *             {
 *                 html: 'message list',
 *                 flex: 1
 *             },
 *             {
 *                 html: 'message preview',
 *                 flex: 2
 *             }
 *         ]
 *     });
 *
 * We can also dock to the bottom and right and use other layouts than hbox and vbox ({@link Ext.layout.Card card} and
 * {@link Ext.layout.Fit fit} layouts both accept docking too).
 */
Ext.define('Ext.layout.Default', {
    extend: 'Ext.Evented',

    alternateClassName: ['Ext.layout.AutoContainerLayout', 'Ext.layout.ContainerLayout'],

    alias: ['layout.auto', 'layout.default'],

    isLayout: true,

    hasDockedItemsCls: clsPrefix + 'hasdocked',

    centeredItemCls: clsPrefix + 'centered',

    floatingItemCls: clsPrefix + 'floating',

    dockingWrapperCls: clsPrefix + 'docking',

    dockingInnerCls: clsPrefix + 'docking-inner',

    maskCls: clsPrefix + 'mask',

    positionMap: {
        top: 'start',
        left: 'start',
        bottom: 'end',
        right: 'end'
    },

    positionDirectionMap: {
        top: 'vertical',
        bottom: 'vertical',
        left: 'horizontal',
        right: 'horizontal'
    },

    DIRECTION_VERTICAL: 'vertical',

    DIRECTION_HORIZONTAL: 'horizontal',

    POSITION_START: 'start',

    POSITION_END: 'end',

    config: {
        /**
         * @cfg {Ext.fx.layout.Card} animation Layout animation configuration
         * Controls how layout transitions are animated.  Currently only available for
         * Card Layouts
         * @accessor
         */
        animation: null
    },

    constructor: function(container, config) {
        this.container = container;

        this.innerItems = [];

        this.centeringWrappers = {};

        this.initConfig(config);
    },

    reapply: Ext.emptyFn,

    unapply: Ext.emptyFn,

    onItemAdd: function() {
        this.doItemAdd.apply(this, arguments);
    },

    onItemRemove: function() {
        this.doItemRemove.apply(this, arguments);
    },

    onItemMove: function() {
        this.doItemMove.apply(this, arguments);
    },

    onItemCenteredChange: function() {
        this.doItemCenteredChange.apply(this, arguments);
    },

    onItemFloatingChange: function() {
        this.doItemFloatingChange.apply(this, arguments);
    },

    onItemDockedChange: function() {
        this.doItemDockedChange.apply(this, arguments);
    },

    /**
     * @private
     */
    doItemAdd: function(item, index) {
        var docked = item.getDocked();

        if (docked !== null) {
            this.dockItem(item, docked);
        }
        else if (item.isCentered()) {
            this.centerItem(item, index);
        }
        else {
            this.insertItem(item, index);
        }

        if (item.isFloating()) {
            this.onItemFloatingChange(item, true);
        }
    },

    /**
     * @private
     */
    doItemRemove: function(item) {
        if (item.isDocked()) {
            this.undockItem(item);
        }
        else if (item.isCentered()) {
            this.uncenterItem(item);
        }

        Ext.Array.remove(this.innerItems, item);

        this.container.innerElement.dom.removeChild(item.renderElement.dom);
    },

    /**
     * @private
     */
    doItemMove: function(item, toIndex, fromIndex) {
        if (item.isCentered()) {
            item.setZIndex((toIndex + 1) * 2);
        }
        else {
            if (item.isFloating()) {
                item.setZIndex((toIndex + 1) * 2);
            }
            this.insertItem(item, toIndex);
        }
    },

    /**
     * @private
     */
    doItemCenteredChange: function(item, centered) {
        if (centered) {
            this.centerItem(item);
        }
        else {
            this.uncenterItem(item);
        }
    },

    /**
     * @private
     */
    doItemFloatingChange: function(item, floating) {
        var element = item.element,
            floatingItemCls = this.floatingItemCls;

        if (floating) {
            if (item.getZIndex() === null) {
                item.setZIndex((this.container.indexOf(item) + 1) * 2);
            }
            element.addCls(floatingItemCls);
        }
        else {
            item.setZIndex(null);
            element.removeCls(floatingItemCls);
        }
    },

    /**
     * @private
     */
    doItemDockedChange: function(item, docked, oldDocked) {
        if (oldDocked) {
            this.undockItem(item, oldDocked);
        }

        if (docked) {
            this.dockItem(item, docked);
        }
    },

    centerItem: function(item) {
        this.insertItem(item, 0);

        if (item.getZIndex() === null) {
            item.setZIndex((this.container.indexOf(item) + 1) * 2);
        }

        this.createCenteringWrapper(item);

        // Mainly for styling
        item.element.addCls(this.floatingItemCls);
    },

    uncenterItem: function(item) {
        this.destroyCenteringWrapper(item);
        item.setZIndex(null);
        this.insertItem(item, this.container.indexOf(item));

        // Mainly for styling
        item.element.removeCls(this.floatingItemCls);
    },

    dockItem: function(item, position) {
        var container = this.container,
            itemRenderElement = item.renderElement,
            itemElement = item.element,
            dockingInnerElement = this.dockingInnerElement;

        if (!dockingInnerElement) {
            container.setUseBodyElement(true);
            this.dockingInnerElement = dockingInnerElement = container.bodyElement;
        }

        this.getDockingWrapper(position);

        if (this.positionMap[position] === this.POSITION_START) {
            itemRenderElement.insertBefore(dockingInnerElement);
        }
        else {
            itemRenderElement.insertAfter(dockingInnerElement);
        }

        itemElement.addCls(clsPrefix + 'docked-' + position);
    },

    undockItem: function(item, docked) {
        this.insertItem(item, this.container.indexOf(item));
        item.element.removeCls(clsPrefix + 'docked-' + docked);
    },

    getDockingWrapper: function(position) {
        var currentDockingDirection = this.currentDockingDirection,
            direction = this.positionDirectionMap[position],
            dockingWrapper = this.dockingWrapper;

        if (currentDockingDirection !== direction) {
            this.currentDockingDirection = direction;
            this.dockingWrapper = dockingWrapper = this.createDockingWrapper(direction);
        }

        return dockingWrapper;
    },

    createDockingWrapper: function(direction) {
        return this.dockingInnerElement.wrap({
            classList: [this.dockingWrapperCls + '-' + direction]
        }, true);
    },

    createCenteringWrapper: function(item) {
        var id = item.getId(),
            wrappers = this.centeringWrappers,
            renderElement = item.renderElement,
            wrapper;

        wrappers[id] = wrapper = renderElement.wrap({
            className: this.centeredItemCls
        });

        return wrapper;
    },

    destroyCenteringWrapper: function(item) {
        var id = item.getId(),
            wrappers = this.centeringWrappers,
            renderElement = item.renderElement,
            wrapper = wrappers[id];

        renderElement.unwrap();
        wrapper.destroy();
        delete wrappers[id];

        return this;
    },

    insertItem: function(item, index) {
       var container = this.container,
           items = container.getItems().items,
           innerItems = this.innerItems,
           containerDom = container.innerElement.dom,
           itemDom = item.renderElement.dom,
           relativeItem, relativeItemDom, domIndex;

       if (container.has(item)) {
           Ext.Array.remove(innerItems, item);
       }

       if (typeof index == 'number') {
           // Retrieve the *logical* relativeItem reference to insertBefore
           relativeItem = items[index];

           // If it is the item itself, get the next sibling
           if (relativeItem === item) {
               relativeItem = items[++index];
           }

           // Continue finding the relativeItem that is neither currently centered nor docked
           while (relativeItem && (relativeItem.isCentered() || relativeItem.isDocked())) {
               relativeItem = items[++index];
           }

           if (relativeItem) {
               // Retrieve the *physical* index of that relativeItem
               domIndex = innerItems.indexOf(relativeItem);

               if (domIndex !== -1) {
                   while (relativeItem && (relativeItem.isCentered() || relativeItem.isDocked())) {
                       relativeItem = innerItems[++domIndex];
                   }

                   if (relativeItem) {
                       innerItems.splice(domIndex, 0, item);

                       relativeItemDom = relativeItem.renderElement.dom;
                       containerDom.insertBefore(itemDom, relativeItemDom);

                       return this;
                   }
               }
           }
       }

       innerItems.push(item);
       containerDom.appendChild(itemDom);

       return this;
   }
});

})(Ext.baseCSSPrefix);

/**
 * AbstractBox is a superclass for the two box layouts:
 *
 * * {@link Ext.layout.HBox hbox}
 * * {@link Ext.layout.VBox vbox}
 *
 * AbstractBox itself is never used directly, but its subclasses provide flexible arrangement of child components
 * inside a {@link Ext.Container Container}. For a full overview of layouts check out the
 * [Layout Guide](#!/guide/layouts).
 *
 * ## Horizontal Box
 *
 * HBox allows you to easily lay out child components horizontally. It can size items based on a fixed width or a
 * fraction of the total width available, enabling you to achieve flexible layouts that expand or contract to fill the
 * space available.
 *
 * {@img ../guides/layouts/hbox.jpg}
 *
 * See the {@link Ext.layout.HBox HBox layout docs} for more information on using hboxes.
 *
 * ## Vertical Box
 *
 * VBox allows you to easily lay out child components verticaly. It can size items based on a fixed height or a
 * fraction of the total height available, enabling you to achieve flexible layouts that expand or contract to fill the
 * space available.
 *
 * {@img ../guides/layouts/vbox.jpg}
 *
 * See the {@link Ext.layout.VBox VBox layout docs} for more information on using vboxes.
 */
Ext.define('Ext.layout.AbstractBox', {
    extend: 'Ext.layout.Default',

    config: {
        /**
         * @cfg {String} align
         * Controls how the child items of the container are aligned. Acceptable configuration values for this property are:
         *
         * - **top** : **Default** child items are aligned vertically at the **top** of the container
         * - **middle** : child items are aligned vertically in the **middle** of the container
         * - **stretch** : child items are stretched vertically to fill the height of the container
         * - **stretchmax** : child items are stretched vertically to the height of the largest item.
         * @accessor
         */
        align: 'stretch',

        /**
         * @cfg {String} pack
         * Controls how the child items of the container are packed together. Acceptable configuration values
         * for this property are:
         *
         * - ** start ** : child items are packed together at left side of container
         * - ** center ** : child items are packed together at mid-width of container
         * - ** end ** : child items are packed together at right side of container
         * @accessor
         */
        pack: null
    },

    flexItemCls: Ext.baseCSSPrefix + 'layout-box-item',

    positionMap: {
        middle: 'center',
        left: 'start',
        top: 'start',
        right: 'end',
        bottom: 'end'
    },

    constructor: function(container) {
        this.callParent(arguments);

        container.innerElement.addCls(this.cls);

        container.on(this.sizeChangeEventName, 'onItemSizeChange', this, {
            delegate: '> component'
        });
    },

    reapply: function() {
        this.container.innerElement.addCls(this.cls);

        this.updatePack(this.getPack());
        this.updateAlign(this.getAlign());
    },

    unapply: function() {
        this.container.innerElement.removeCls(this.cls);

        this.updatePack(null);
        this.updateAlign(null);
    },

    /**
     * @private
     */
    doItemAdd: function(item, index) {
        this.callParent(arguments);

        if (item.isInnerItem()) {
            var size = item.getConfig(this.sizePropertyName),
                config = item.config;

            if (!size && ('flex' in config)) {
                this.setItemFlex(item, config.flex);
            }
        }
    },

    /**
     * @private
     */
    doItemRemove: function(item) {
        if (item.isInnerItem()) {
            this.setItemFlex(item, null);
        }

        this.callParent(arguments);
    },

    onItemSizeChange: function(item) {
        this.setItemFlex(item, null);
    },

    /**
     * @private
     */
    doItemCenteredChange: function(item, centered) {
        if (centered) {
            this.setItemFlex(item, null);
        }

        this.callParent(arguments);
    },

    /**
     * @private
     */
    doItemFloatingChange: function(item, floating) {
        if (floating) {
            this.setItemFlex(item, null);
        }

        this.callParent(arguments);
    },

    /**
     * @private
     */
    doItemDockedChange: function(item, docked) {
        if (docked) {
            this.setItemFlex(item, null);
        }

        this.callParent(arguments);
    },

    redrawContainer: function() {
        var container = this.container,
            renderedTo = container.renderElement.dom.parentNode;

        if (renderedTo && renderedTo.nodeType !== 11) {
            container.innerElement.redraw();
        }
    },

    /**
     * Sets the flex of an item in this box layout.
     * @param {Ext.Component} item The item of this layout which you want to update the flex of.
     * @param {Number} flex The flex to set on this method
     */
    setItemFlex: function(item, flex) {
        var element = item.element,
            flexItemCls = this.flexItemCls;

        if (flex) {
            element.addCls(flexItemCls);
        }
        else if (element.hasCls(flexItemCls)) {
            this.redrawContainer();
            element.removeCls(flexItemCls);
        }

        element.dom.style.webkitBoxFlex = flex;
    },

    convertPosition: function(position) {
        if (this.positionMap.hasOwnProperty(position)) {
            return this.positionMap[position];
        }

        return position;
    },

    applyAlign: function(align) {
        return this.convertPosition(align);
    },

    updateAlign: function(align) {
        this.container.innerElement.dom.style.webkitBoxAlign = align;
    },

    applyPack: function(pack) {
         return this.convertPosition(pack);
    },

    updatePack: function(pack) {
        this.container.innerElement.dom.style.webkitBoxPack = pack;
    }
});

/**
 * 
 * Fit Layout is probably the simplest layout available. All it does is make a child component fit to the full size of
 * its parent Container.
 * 
 * {@img ../guides/layouts/fit.jpg}
 * 
 * For example, if you have a parent Container that is 200px by 200px and give it a single child component and a 'fit'
 * layout, the child component will also be 200px by 200px:
 * 
 *     var panel = Ext.create('Ext.Panel', {
 *         width: 200,
 *         height: 200,
 *         layout: 'fit',
 *     
 *         items: {
 *             xtype: 'panel',
 *             html: 'Also 200px by 200px'
 *         }
 *     });
 *     
 *     Ext.Viewport.add(panel);
 * 
 * For a more detailed overview of what layouts are and the types of layouts shipped with Sencha Touch 2, check out the
 * [Layout Guide](#!/guide/layouts).
 */
Ext.define('Ext.layout.Fit', {
    extend: 'Ext.layout.Default',
    alternateClassName: 'Ext.layout.FitLayout',

    alias: 'layout.fit',

    cls: Ext.baseCSSPrefix + 'layout-fit',

    itemCls: Ext.baseCSSPrefix + 'layout-fit-item',

    constructor: function(container) {
        this.callParent(arguments);

        this.apply();
    },

    apply: function() {
        this.container.innerElement.addCls(this.cls);
    },

    reapply: function() {
        this.apply();
    },

    unapply: function() {
        this.container.innerElement.removeCls(this.cls);
    },

    doItemAdd: function(item, index) {
        if (item.isInnerItem()) {
            item.addCls(this.itemCls);
        }

        this.callParent(arguments);
    },

    /**
     * @private
     */
    doItemRemove: function(item) {
        if (item.isInnerItem()) {
            item.removeCls(this.itemCls);
        }

        this.callParent(arguments);
    }
});

/**
 *
 * Sometimes you want to show several screens worth of information but you've only got a small screen to work with.
 * TabPanels and Carousels both enable you to see one screen of many at a time, and underneath they both use a Card
 * Layout.
 *
 * Card Layout takes the size of the Container it is applied to and sizes the currently active item to fill the
 * Container completely. It then hides the rest of the items, allowing you to change which one is currently visible but
 * only showing one at once:
 *
 * {@img ../guides/layouts/card.jpg}
 *
 *
 * Here the gray box is our Container, and the blue box inside it is the currently active card. The three other cards
 * are hidden from view, but can be swapped in later. While it's not too common to create Card layouts directly, you
 * can do so like this:
 *
 *     var panel = Ext.create('Ext.Panel', {
 *         layout: 'card',
 *         items: [
 *             {
 *                 html: "First Item"
 *             },
 *             {
 *                 html: "Second Item"
 *             },
 *             {
 *                 html: "Third Item"
 *             },
 *             {
 *                 html: "Fourth Item"
 *             }
 *         ]
 *     });
 *
 *     panel.{@link Ext.Container#setActiveItem setActiveItem}(1);
 *
 * Here we create a Panel with a Card Layout and later set the second item active (the active item index is zero-based,
 * so 1 corresponds to the second item). Normally you're better off using a {@link Ext.tab.Panel tab panel} or a
 * {@link Ext.carousel.Carousel carousel}.
 *
 * For a more detailed overview of what layouts are and the types of layouts shipped with Sencha Touch 2, check out the
 * [Layout Guide](#!/guide/layouts).
 */
Ext.define('Ext.layout.Card', {
    extend: 'Ext.layout.Fit',
    alternateClassName: 'Ext.layout.CardLayout',

    isCard: true,

    /**
     * @event activeitemchange
     * @preventable doActiveItemChange
     * Fires when an card is made active
     * @param {Ext.layout.Card} this The layout instance
     * @param {Mixed} newActiveItem The new active item
     * @param {Mixed} oldActiveItem The old active item
     */

    requires: [
        'Ext.fx.layout.Card'
    ],

    alias: 'layout.card',

    cls: Ext.baseCSSPrefix + 'layout-card',

    itemCls: Ext.baseCSSPrefix + 'layout-card-item',

    constructor: function() {
        this.callParent(arguments);
        this.container.onInitialized(this.onContainerInitialized, this);
    },

    /**
     * @private
     */
    applyAnimation: function(animation) {
        return new Ext.fx.layout.Card(animation);
    },

    /**
     * @private
     */
    updateAnimation: function(animation, oldAnimation) {
        if (animation && animation.isAnimation) {
            animation.setLayout(this);
        }

        if (oldAnimation) {
            oldAnimation.destroy();
        }
    },

    /**
     * @private
     */
    doItemAdd: function(item, index) {
        if (item.isInnerItem()) {
            item.hide();
        }

        this.callParent(arguments);
    },

    /**
     * @private
     */
    doItemRemove: function(item, index, destroy) {
        this.callParent(arguments);

        if (!destroy && item.isInnerItem()) {
            item.show();
        }
    },

    onContainerInitialized: function(container) {
        var activeItem = container.getActiveItem();

        if (activeItem) {
            activeItem.show();
        }

        container.on('activeitemchange', 'onContainerActiveItemChange', this);
    },

    /**
     * @private
     */
    onContainerActiveItemChange: function(container) {
        this.relayEvent(arguments, 'doActiveItemChange');
    },

    /**
     * @private
     */
    doActiveItemChange: function(me, newActiveItem, oldActiveItem) {
        if (oldActiveItem) {
            oldActiveItem.hide();
        }

        if (newActiveItem) {
            newActiveItem.show();
        }
    },

    doItemDockedChange: function(item, docked) {
        var element = item.element;
        // See https://sencha.jira.com/browse/TOUCH-1508
        if (docked) {
            element.removeCls(this.itemCls);
        }
        else {
            element.addCls(this.itemCls);
        }

        this.callParent(arguments);
    }
});

/**
 * The HBox (short for horizontal box) layout makes it easy to position items horizontally in a 
 * {@link Ext.Container Container}. It can size items based on a fixed width or a fraction of the total width 
 * available.
 * 
 * For example, an email client might have a list of messages pinned to the left, taking say one third of the available
 * width, and a message viewing panel in the rest of the screen. We can achieve this with hbox layout's *flex* config:
 * 
 *     @example 
 *     Ext.create('Ext.Container', {
 *         fullscreen: true,
 *         layout: 'hbox',
 *         items: [
 *             {
 *                 html: 'message list',
 *                 style: 'background-color: #5E99CC;',
 *                 flex: 1
 *             },
 *             {
 *                 html: 'message preview',
 *                 style: 'background-color: #759E60;',
 *                 flex: 2
 *             }
 *         ]
 *     });
 * 
 * This will give us two boxes - one that's one third of the available width, the other being two thirds of the 
 * available width:
 * 
 * {@img ../guides/layouts/hbox.jpg}
 * 
 * We can also specify fixed widths for child items, or mix fixed widths and flexes. For example, here we have 3 items 
 * - one on each side with flex: 1, and one in the center with a fixed width of 100px:
 * 
 *     @example
 *     Ext.create('Ext.Container', {
 *         fullscreen: true,
 *         layout: 'hbox',
 *         items: [
 *             {
 *                 html: 'Left item',
 *                 style: 'background-color: #759E60;',
 *                 flex: 1
 *             },
 *             {
 *                 html: 'Center item',
 *                 width: 100
 *             },
 *             {
 *                 html: 'Right item',
 *                 style: 'background-color: #5E99CC;',
 *                 flex: 1
 *             }
 *         ]
 *     });
 * 
 * Which gives us an effect like this:
 * 
 * {@img ../guides/layouts/hboxfixed.jpg}
 * 
 * For a more detailed overview of what layouts are and the types of layouts shipped with Sencha Touch 2, check out the
 * [Layout Guide](#!/guide/layouts).
 */
Ext.define('Ext.layout.HBox', {
    extend: 'Ext.layout.AbstractBox',
    alternateClassName: 'Ext.layout.HBoxLayout',

    alias: 'layout.hbox',

    sizePropertyName: 'width',

    sizeChangeEventName: 'widthchange',

    cls: Ext.baseCSSPrefix + 'layout-hbox'
});



/**
 * The VBox (short for vertical box) layout makes it easy to position items horizontally in a 
 * {@link Ext.Container Container}. It can size items based on a fixed height or a fraction of the total height
 * available.
 * 
 * For example, let's say we want a banner to take one third of the available height, and an information panel in the
 * rest of the screen. We can achieve this with vbox layout's *flex* config:
 * 
 *     @example
 *     Ext.create('Ext.Container', {
 *         fullscreen: true,
 *         layout: 'vbox',
 *         items: [
 *             {
 *                 html: 'Awesome banner',
 *                 style: 'background-color: #759E60;',
 *                 flex: 1
 *             },
 *             {
 *                 html: 'Some wonderful information',
 *                 style: 'background-color: #5E99CC;',
 *                 flex: 2
 *             }
 *         ]
 *     });
 * 
 * This will give us two boxes - one that's one third of the available height, the other being two thirds of the 
 * available height:
 * 
 * {@img ../guides/layouts/vbox.jpg}
 * 
 * We can also specify fixed heights for child items, or mix fixed heights and flexes. For example, here we have 3 
 * items - one at the top and bottom with flex: 1, and one in the center with a fixed width of 100px:
 * 
 *     @example preview portrait
 *     Ext.create('Ext.Container', {
 *         fullscreen: true,
 *         layout: 'vbox',
 *         items: [
 *             {
 *                 html: 'Top item',
 *                 style: 'background-color: #5E99CC;',
 *                 flex: 1
 *             },
 *             {
 *                 html: 'Center item',
 *                 height: 100
 *             },
 *             {
 *                 html: 'Bottom item',
 *                 style: 'background-color: #759E60;',
 *                 flex: 1
 *             }
 *         ]
 *     });
 * 
 * Which gives us an effect like this:
 * 
 * {@img ../guides/layouts/vboxfixed.jpg}
 * 
 * For a more detailed overview of what layouts are and the types of layouts shipped with Sencha Touch 2, check out the
 * [Layout Guide](#!/guide/layouts).
 * 
 */
Ext.define('Ext.layout.VBox', {
    extend: 'Ext.layout.AbstractBox',
    alternateClassName: 'Ext.layout.VBoxLayout',

    alias: 'layout.vbox',

    sizePropertyName: 'height',

    sizeChangeEventName: 'heightchange',

    cls: Ext.baseCSSPrefix + 'layout-vbox'
});
/**
 * Factory class which returns an instance of the provided layout.
 */
Ext.define('Ext.layout.Layout', {

    requires: [
        'Ext.layout.Fit',
        'Ext.layout.Card',
        'Ext.layout.HBox',
        'Ext.layout.VBox'
    ],

    /**
     * Creates a new Layout for the specified container using the config object's layout to determine
     * layout to instantiate.
     * @param {Ext.Container} container A configuration object for the Component you wish to create.
     * @param {Object} [config] The alias to provide the Layout type; if none is
     * specified, Ext.layout.Default will be used.
     * @return {Ext.layout.Default} The newly instantiated Layout.
     */
    constructor: function(container, config) {
        var layoutClass = Ext.layout.Default,
            type, layout;

        if (typeof config == 'string') {
            type = config;
            config = {};
        }
        else if ('type' in config) {
            type = config.type;
        }

        if (type) {
            layoutClass = Ext.ClassManager.getByAlias('layout.' + type);

            if (!layoutClass) {
                Ext.Logger.error("Unknown layout type of: '" + type + "'");
            }
        }

        return new layoutClass(container, config);
    }
});


Ext.define('Ext.log.writer.Remote', {
    extend: 'Ext.log.writer.Writer',

    requires: [
        'Ext.Ajax'
    ],

    config: {
        batchSendDelay: 100,
        onFailureRetryDelay: 500,
        url: ''
    },

    isSending: false,

    sendingTimer: null,

    constructor: function() {
        this.queue = [];

        this.send = Ext.Function.bind(this.send, this);

        return this.callParent(arguments);
    },

    doWrite: function(event) {
        var queue = this.queue;
        queue.push(event.message);

        if (!this.isSending && this.sendingTimer === null) {
            this.sendingTimer = setTimeout(this.send, this.getBatchSendDelay());
        }
    },

    send: function() {
        var queue = this.queue,
            messages = queue.slice();

        queue.length = 0;

        this.sendingTimer = null;

        if (messages.length > 0) {
            this.doSend(messages);
        }
    },

    doSend: function(messages) {
        var me = this;

        me.isSending = true;

        Ext.Ajax.request({
            url: me.getUrl(),
            method: 'POST',
            params: {
                messages: messages.join("\n")
            },
            success: function(){
                me.isSending = false;
                me.send();
            },
            failure: function() {
                setTimeout(function() {
                    me.doSend(messages);
                }, me.getOnFailureRetryDelay());
            },
            scope: me
        });
    }
});

/**
 * @private
 */
Ext.define('Ext.mixin.Sortable', {
    extend: 'Ext.mixin.Mixin',

    requires: [
        'Ext.util.Sorter'
    ],

    mixinConfig: {
        id: 'sortable'
    },

    config: {
        /**
         * @cfg {Array} sorters
         * An array with sorters. A sorter can be an instance of Ext.util.Sorter, a string
         * indicating a property name, an object representing an Ext.util.Sorter configuration,
         * or a sort function.
         */
        sorters: null,

        /**
         * @cfg {String} defaultSortDirection
         * The default sort direction to use if one is not specified (defaults to "ASC")
         */
        defaultSortDirection: "ASC",

        /**
         * @cfg {String} sortRoot
         * The root inside each item in which the properties exist that we want to sort on.
         * This is useful for sorting records in which the data exists inside a 'data' property.
         */
        sortRoot: null
    },

    /**
     * @property {Boolean} dirtySortFn
     * A flag indicating wether the currently cashed sort function is still valid. Read-only.
     */
    dirtySortFn: false,

    /**
     * @property currentSortFn
     * This is the cached sorting function which is a generated function that calls all the
     * configured sorters in the correct order. This is a read-only property.
     */
    sortFn: null,

    /**
     * @property {Boolean} sorted
     * A read-only flag indicating if this object is sorted
     */
    sorted: false,

    applySorters: function(sorters, collection) {
        if (!collection) {
            collection = this.createSortersCollection();
        }

        collection.clear();
        this.sorted = false;

        if (sorters) {
            this.addSorters(sorters);
        }

        return collection;
    },

    createSortersCollection: function() {
        this._sorters = Ext.create('Ext.util.Collection', function(sorter) {
            return sorter.getId();
        });
        return this._sorters;
    },

    /**
     * This method adds a sorter.
     * @param {Ext.util.Sorter/String/Function/Object} sorter Can be an instance of
     * Ext.util.Sorter, a string indicating a property name, an object representing an Ext.util.Sorter
     * configuration, or a sort function.
     * @param {String} defaultDirection The default direction for each sorter in the array. Defaults
     * to the value of {@link #defaultSortDirection}. Can be either 'ASC' or 'DESC'.
     */
    addSorter: function(sorter, defaultDirection) {
        this.addSorters([sorter], defaultDirection);
    },

    /**
     * This method adds all the sorters in a passed array.
     * @param {Array} sorters An array with sorters. A sorter can be an instance of Ext.util.Sorter, a string
     * indicating a property name, an object representing an Ext.util.Sorter configuration,
     * or a sort function.
     * @param {String} defaultDirection The default direction for each sorter in the array. Defaults
     * to the value of {@link #defaultSortDirection}. Can be either 'ASC' or 'DESC'.
     */
    addSorters: function(sorters, defaultDirection) {
        var currentSorters = this.getSorters();
        return this.insertSorters(currentSorters ? currentSorters.length : 0, sorters, defaultDirection);
    },

    /**
     * This method adds a sorter at a given index.
     * @param {Number} index The index at which to insert the sorter.
     * @param {Ext.util.Sorter/String/Function/Object} sorter Can be an instance of Ext.util.Sorter,
     * a string indicating a property name, an object representing an Ext.util.Sorter configuration,
     * or a sort function.
     * @param {String} defaultDirection The default direction for each sorter in the array. Defaults
     * to the value of {@link #defaultSortDirection}. Can be either 'ASC' or 'DESC'.
     */
    insertSorter: function(index, sorter, defaultDirection) {
        return this.insertSorters(index, [sorter], defaultDirection);
    },

    /**
     * This method inserts all the sorters in the passed array at the given index.
     * @param {Number} index The index at which to insert the sorters.
     * @param {Array} sorters Can be an instance of Ext.util.Sorter, a string indicating a property name,
     * an object representing an Ext.util.Sorter configuration, or a sort function.
     * @param {String} defaultDirection The default direction for each sorter in the array. Defaults
     * to the value of {@link #defaultSortDirection}. Can be either 'ASC' or 'DESC'.
     */
    insertSorters: function(index, sorters, defaultDirection) {
        // We begin by making sure we are dealing with an array of sorters
        if (!Ext.isArray(sorters)) {
            sorters = [sorters];
        }

        var ln = sorters.length,
            direction = defaultDirection || this.getDefaultSortDirection(),
            sortRoot = this.getSortRoot(),
            currentSorters = this.getSorters(),
            newSorters = [],
            sorterConfig, i, sorter, currentSorter;

        if (!currentSorters) {
            // This will guarantee that we get the collection
            currentSorters = this.createSortersCollection();
        }

        // We first have to convert every sorter into a proper Sorter instance
        for (i = 0; i < ln; i++) {
            sorter = sorters[i];
            sorterConfig = {
                direction: direction,
                root: sortRoot
            };

            // If we are dealing with a string we assume it is a property they want to sort on.
            if (typeof sorter === 'string') {
                currentSorter = currentSorters.get(sorter);

                if (!currentSorter) {
                    sorterConfig.property = sorter;
                } else {
                    if (defaultDirection !== undefined) {
                        currentSorter.setDirection(defaultDirection);
                    } else {
                        // If we already have a sorter for this property we just toggle its direction.
                        currentSorter.toggle();
                    }
                    continue;
                }
            }
            // If it is a function, we assume its a sorting function.
            else if (Ext.isFunction(sorter)) {
                sorterConfig.sorterFn = sorter;
            }
            // If we are dealing with an object, we assume its a Sorter configuration. In this case
            // we create an instance of Sorter passing this configuration.
            else if (Ext.isObject(sorter)) {
                if (!sorter.isSorter) {
                    if (sorter.fn) {
                        sorter.sorterFn = sorter.fn;
                        delete sorter.fn;
                    }

                    sorterConfig = Ext.apply(sorterConfig, sorter);
                }
                else {
                    newSorters.push(sorter);
                    if (!sorter.getRoot()) {
                        sorter.setRoot(sortRoot);
                    }
                    continue;
                }
            }
            // Finally we get to the point where it has to be invalid
            else {
                Ext.Logger.warn('Invalid sorter specified:', sorter);
            }

            // If a sorter config was created, make it an instance
            sorter = Ext.create('Ext.util.Sorter', sorterConfig);
            newSorters.push(sorter);
        }

        // Now lets add the newly created sorters.
        for (i = 0, ln = newSorters.length; i < ln; i++) {
            currentSorters.insert(index + i, newSorters[i]);
        }

        this.dirtySortFn = true;

        if (currentSorters.length) {
            this.sorted = true;
        }
        return currentSorters;
    },

    /**
     * This method removes a sorter.
     * @param {Ext.util.Sorter/String/Function/Object} sorter Can be an instance of Ext.util.Sorter,
     * a string indicating a property name, an object representing an Ext.util.Sorter configuration,
     * or a sort function.
     */
    removeSorter: function(sorter) {
        return this.removeSorters([sorter]);
    },

    /**
     * This method removes all the sorters in a passed array.
     * @param {Array} sorters Each value in the array can be a string (property name),
     * function (sorterFn) or {@link Ext.util.Sorter Sorter} instance.
     */
    removeSorters: function(sorters) {
        // We begin by making sure we are dealing with an array of sorters
        if (!Ext.isArray(sorters)) {
            sorters = [sorters];
        }

        var ln = sorters.length,
            currentSorters = this.getSorters(),
            i, sorter;

        for (i = 0; i < ln; i++) {
            sorter = sorters[i];

            if (typeof sorter === 'string') {
                currentSorters.removeAtKey(sorter);
            }
            else if (typeof sorter === 'function') {
                currentSorters.each(function(item) {
                    if (item.getSorterFn() === sorter) {
                        currentSorters.remove(item);
                    }
                });
            }
            else if (sorter.isSorter) {
                currentSorters.remove(sorter);
            }
        }

        if (!currentSorters.length) {
            this.sorted = false;
        }
    },

    /**
     * This updates the cached sortFn based on the current sorters.
     * @return {Function} sortFn The generated sort function.
     * @private
     */
    updateSortFn: function() {
        var sorters = this.getSorters().items;

        this.sortFn = function(r1, r2) {
            var ln = sorters.length,
                result, i;

            // We loop over each sorter and check if r1 should be before or after r2
            for (i = 0; i < ln; i++) {
                result = sorters[i].sort.call(this, r1, r2);

                // If the result is -1 or 1 at this point it means that the sort is done.
                // Only if they are equal (0) we continue to see if a next sort function
                // actually might find a winner.
                if (result !== 0) {
                    break;
                }
            }

            return result;
        };

        this.dirtySortFn = false;
        return this.sortFn;
    },

    /**
     * Returns an up to date sort function.
     * @return {Function} sortFn The sort function.
     */
    getSortFn: function() {
        if (this.dirtySortFn) {
            return this.updateSortFn();
        }
        return this.sortFn;
    },

    /**
     * This method will sort an array based on the currently configured {@link #sorters}.
     * @param {Array} data The array you want to have sorted
     * @return {Array} data The array you passed after it is sorted
     */
    sort: function(data) {
        Ext.Array.sort(data, this.getSortFn());
        return data;
    },

    /**
     * This method returns the index that a given item would be inserted into a given array based
     * on the current sorters.
     * @param {Array} items The array that you want to insert the item into.
     * @param {Mixed} item The item that you want to insert into the items array.
     * @returns {Number} index The index for the given item in the given array based on the current sorters.
     */
    findInsertionIndex: function(items, item, sortFn) {
        var start = 0,
            end   = items.length - 1,
            sorterFn = sortFn || this.getSortFn(),
            middle,
            comparison;

        while (start <= end) {
            middle = (start + end) >> 1;
            comparison = sorterFn(item, items[middle]);
            if (comparison >= 0) {
                start = middle + 1;
            } else if (comparison < 0) {
                end = middle - 1;
            }
        }

        return start;
    }
});

/**
 * @private
 */
Ext.define('Ext.util.AbstractMixedCollection', {
    requires: ['Ext.util.Filter'],

    mixins: {
        observable: 'Ext.util.Observable'
    },

    /**
     * @event clear
     * Fires when the collection is cleared.
     */

    /**
     * @event add
     * Fires when an item is added to the collection.
     * @param {Number} index The index at which the item was added.
     * @param {Object} o The item added.
     * @param {String} key The key associated with the added item.
     */

    /**
     * @event replace
     * Fires when an item is replaced in the collection.
     * @param {String} key he key associated with the new added.
     * @param {Object} old The item being replaced.
     * @param {Object} new The new item.
     */

    /**
     * @event remove
     * Fires when an item is removed from the collection.
     * @param {Object} o The item being removed.
     * @param {String} key (optional) The key associated with the removed item.
     */

    constructor: function(allowFunctions, keyFn) {
        var me = this;

        me.items = [];
        me.map = {};
        me.keys = [];
        me.length = 0;

        me.allowFunctions = allowFunctions === true;

        if (keyFn) {
            me.getKey = keyFn;
        }

        me.mixins.observable.constructor.call(me);
    },

    /**
     * @cfg {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
     * function should add function references to the collection. Defaults to
     * <tt>false</tt>.
     */
    allowFunctions : false,

    /**
     * Adds an item to the collection. Fires the {@link #event-add} event when complete.
     * @param {String} key <p>The key to associate with the item, or the new item.</p>
     * <p>If a {@link #getKey} implementation was specified for this MixedCollection,
     * or if the key of the stored items is in a property called <tt><b>id</b></tt>,
     * the MixedCollection will be able to <i>derive</i> the key for the new item.
     * In this case just pass the new item in this parameter.</p>
     * @param {Object} o The item to add.
     * @return {Object} The item added.
     */
    add: function(key, obj){
        var me = this,
            myObj = obj,
            myKey = key,
            old;

        if (arguments.length == 1) {
            myObj = myKey;
            myKey = me.getKey(myObj);
        }
        if (typeof myKey != 'undefined' && myKey !== null) {
            old = me.map[myKey];
            if (typeof old != 'undefined') {
                return me.replace(myKey, myObj);
            }
            me.map[myKey] = myObj;
        }
        me.length++;
        me.items.push(myObj);
        me.keys.push(myKey);
        me.fireEvent('add', me.length - 1, myObj, myKey);
        return myObj;
    },

    /**
      * MixedCollection has a generic way to fetch keys if you implement getKey.  The default implementation
      * simply returns <b><code>item.id</code></b> but you can provide your own implementation
      * to return a different value as in the following examples:<pre><code>
// normal way
var mc = new Ext.util.MixedCollection();
mc.add(someEl.dom.id, someEl);
mc.add(otherEl.dom.id, otherEl);
//and so on

// using getKey
var mc = new Ext.util.MixedCollection();
mc.getKey = function(el){
   return el.dom.id;
};
mc.add(someEl);
mc.add(otherEl);

// or via the constructor
var mc = new Ext.util.MixedCollection(false, function(el){
   return el.dom.id;
});
mc.add(someEl);
mc.add(otherEl);
     * </code></pre>
     * @param {Object} item The item for which to find the key.
     * @return {Object} The key for the passed item.
     */
    getKey: function(o){
         return o.id;
    },

    /**
     * Replaces an item in the collection. Fires the {@link #event-replace} event when complete.
     * @param {String} key <p>The key associated with the item to replace, or the replacement item.</p>
     * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key
     * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection
     * will be able to <i>derive</i> the key of the replacement item. If you want to replace an item
     * with one having the same key value, then just pass the replacement item in this parameter.</p>
     * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate
     * with that key.
     * @return {Object}  The new item.
     */
    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);
        }
        index = me.indexOfKey(key);
        me.items[index] = o;
        me.map[key] = o;
        me.fireEvent('replace', key, old, o);
        return o;
    },

    /**
     * Adds all elements of an Array or an Object to the collection.
     * @param {Object/Array} objs An Object containing properties which will be added
     * to the collection, or an Array of values, each of which are added to the collection.
     * Functions references will be added to the collection if <code>{@link #allowFunctions}</code>
     * has been set to <tt>true</tt>.
     */
    addAll: function(objs){
        var me = this,
            i = 0,
            args,
            len,
            key;

        if (arguments.length > 1 || Ext.isArray(objs)) {
            args = arguments.length > 1 ? arguments : objs;
            for (len = args.length; i < len; i++) {
                me.add(args[i]);
            }
        } else {
            for (key in objs) {
                if (objs.hasOwnProperty(key)) {
                    if (me.allowFunctions || typeof objs[key] != 'function') {
                        me.add(key, objs[key]);
                    }
                }
            }
        }
    },

    /**
     * Executes the specified function once for every item in the collection, passing the following arguments:
     * <div class="mdetail-params"><ul>
     * <li><b>item</b> : Mixed<p class="sub-desc">The collection item</p></li>
     * <li><b>index</b> : Number<p class="sub-desc">The item's index</p></li>
     * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>
     * </ul></div>
     * The function should return a boolean value. Returning false from the function will stop the iteration.
     * @param {Function} fn The function to execute for each item.
     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current item in the iteration.
     */
    each: function(fn, scope){
        var items = [].concat(this.items), // each safe for removal
            i = 0,
            len = items.length,
            item;

        for (; i < len; i++) {
            item = items[i];
            if (fn.call(scope || item, item, i, len) === false) {
                break;
            }
        }
    },

    /**
     * Executes the specified function once for every key in the collection, passing each
     * key, and its associated item as the first two parameters.
     * @param {Function} fn The function to execute for each item.
     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
     */
    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);
        }
    },

    /**
     * Returns the first item in the collection which elicits a true return value from the
     * passed selection function.
     * @param {Function} fn The selection function to execute for each item.
     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
     * @return {Object} The first item in the collection which returned true from the selection function.
     */
    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;
    },

    /**
     * Inserts an item at the specified index in the collection. Fires the {@link #event-add} event when complete.
     * @param {Number} index The index to insert the item at.
     * @param {String} key The key to associate with the new item, or the item itself.
     * @param {Object} o (optional) If the second parameter was a key, the new item.
     * @return {Object} The item inserted.
     */
    insert: function(index, key, obj){
        var me = this,
            myKey = key,
            myObj = obj;

        if (arguments.length == 2) {
            myObj = myKey;
            myKey = me.getKey(myObj);
        }
        if (me.containsKey(myKey)) {
            me.suspendEvents();
            me.removeAtKey(myKey);
            me.resumeEvents();
        }
        if (index >= me.length) {
            return me.add(myKey, myObj);
        }
        me.length++;
        Ext.Array.splice(me.items, index, 0, myObj);
        if (typeof myKey != 'undefined' && myKey !== null) {
            me.map[myKey] = myObj;
        }
        Ext.Array.splice(me.keys, index, 0, myKey);
        me.fireEvent('add', index, myObj, myKey);
        return myObj;
    },

    /**
     * Remove an item from the collection.
     * @param {Object} o The item to remove.
     * @return {Object} The item removed or false if no item was removed.
     */
    remove: function(o){
        return this.removeAt(this.indexOf(o));
    },

    /**
     * Remove all items in the passed array from the collection.
     * @param {Array} items An array of items to be removed.
     * @return {Ext.util.MixedCollection} this object
     */
    removeAll: function(items){
        Ext.each(items || [], function(item) {
            this.remove(item);
        }, this);

        return this;
    },

    /**
     * Remove an item from a specified index in the collection. Fires the {@link #event-remove} event when complete.
     * @param {Number} index The index within the collection of the item to remove.
     * @return {Object} The item removed or false if no item was removed.
     */
    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);
            me.fireEvent('remove', o, key);
            return o;
        }
        return false;
    },

    /**
     * Removed an item associated with the passed key fom the collection.
     * @param {String} key The key of the item to remove.
     * @return {Object} The item removed or false if no item was removed.
     */
    removeAtKey: function(key){
        return this.removeAt(this.indexOfKey(key));
    },

    /**
     * Returns the number of items in the collection.
     * @return {Number} the number of items in the collection.
     */
    getCount: function(){
        return this.length;
    },

    /**
     * Returns index within the collection of the passed Object.
     * @param {Object} o The item to find the index of.
     * @return {Number} index of the item. Returns -1 if not found.
     */
    indexOf: function(o){
        return Ext.Array.indexOf(this.items, o);
    },

    /**
     * Returns index within the collection of the passed key.
     * @param {String} key The key to find the index of.
     * @return {Number} index of the key.
     */
    indexOfKey: function(key){
        return Ext.Array.indexOf(this.keys, key);
    },

    /**
     * Returns the item associated with the passed key OR index.
     * Key has priority over index.  This is the equivalent
     * of calling {@link #getByKey} first, then if nothing matched calling {@link #getAt}.
     * @param {String/Number} key The key or index of the item.
     * @return {Object} If the item is found, returns the item.  If the item was not found, returns <tt>undefined</tt>.
     * If an item was found, but is a Class, returns <tt>null</tt>.
     */
    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; // for prototype!
    },

    /**
     * Returns the item at the specified index.
     * @param {Number} index The index of the item.
     * @return {Object} The item at the specified index.
     */
    getAt: function(index) {
        return this.items[index];
    },

    /**
     * Returns the item associated with the passed key.
     * @param {String/Number} key The key of the item.
     * @return {Object} The item associated with the passed key.
     */
    getByKey: function(key) {
        return this.map[key];
    },

    /**
     * Returns true if the collection contains the passed Object as an item.
     * @param {Object} o  The Object to look for in the collection.
     * @return {Boolean} True if the collection contains the Object as an item.
     */
    contains: function(o){
        return Ext.Array.contains(this.items, o);
    },

    /**
     * Returns true if the collection contains the passed Object as a key.
     * @param {String} key The key to look for in the collection.
     * @return {Boolean} True if the collection contains the Object as a key.
     */
    containsKey: function(key){
        return typeof this.map[key] != 'undefined';
    },

    /**
     * Removes all items from the collection.  Fires the {@link #event-clear} event when complete.
     */
    clear: function(){
        var me = this;

        me.length = 0;
        me.items = [];
        me.keys = [];
        me.map = {};
        me.fireEvent('clear');
    },

    /**
     * Returns the first item in the collection.
     * @return {Object} the first item in the collection..
     */
    first: function() {
        return this.items[0];
    },

    /**
     * Returns the last item in the collection.
     * @return {Object} the last item in the collection..
     */
    last: function() {
        return this.items[this.length - 1];
    },

    /**
     * Collects all of the values of the given property and returns their sum
     * @param {String} property The property to sum by
     * @param {String} root Optional 'root' property to extract the first argument from. This is used mainly when
     * summing fields in records, where the fields are all stored inside the 'data' object
     * @param {Number} start (optional) The record index to start at (defaults to <tt>0</tt>)
     * @param {Number} end (optional) The record index to end at (defaults to <tt>-1</tt>)
     * @return {Number} The total
     */
    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;
    },

    /**
     * Collects unique values of a particular property in this MixedCollection
     * @param {String} property The property to collect on
     * @param {String} root Optional 'root' property to extract the first argument from. This is used mainly when
     * summing fields in records, where the fields are all stored inside the 'data' object
     * @param {Boolean} allowBlank (optional) Pass true to allow null, undefined or empty string values
     * @return {Array} The unique values
     */
    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;
    },

    /**
     * @private
     * Extracts all of the given property values from the items in the MC. Mainly used as a supporting method for
     * functions like sum and collect.
     * @param {String} property The property to extract
     * @param {String} root Optional 'root' property to extract the first argument from. This is used mainly when
     * extracting field data from Model instances, where the fields are stored inside the 'data' object
     * @return {Array} The extracted values
     */
    extractValues: function(property, root) {
        var values = this.items;

        if (root) {
            values = Ext.Array.pluck(values, root);
        }

        return Ext.Array.pluck(values, property);
    },

    /**
     * Returns a range of items in this collection
     * @param {Number} startIndex (optional) The starting index. Defaults to 0.
     * @param {Number} endIndex (optional) The ending index. Defaults to the last item.
     * @return {Array} An array of items
     */
    getRange: function(start, end){
        var me = this,
            items = me.items,
            range = [],
            i;

        if (items.length < 1) {
            return range;
        }

        start = start || 0;
        end = Math.min(typeof end == 'undefined' ? me.length - 1 : end, me.length - 1);
        if (start <= end) {
            for (i = start; i <= end; i++) {
                range[range.length] = items[i];
            }
        } else {
            for (i = start; i >= end; i--) {
                range[range.length] = items[i];
            }
        }
        return range;
    },

    /**
     * <p>Filters the objects in this collection by a set of {@link Ext.util.Filter Filter}s, or by a single
     * property/value pair with optional parameters for substring matching and case sensitivity. See
     * {@link Ext.util.Filter Filter} for an example of using Filter objects (preferred). Alternatively,
     * MixedCollection can be easily filtered by property like this:</p>
<pre><code>
//create a simple store with a few people defined
var people = new Ext.util.MixedCollection();
people.addAll([
    {id: 1, age: 25, name: 'Ed'},
    {id: 2, age: 24, name: 'Tommy'},
    {id: 3, age: 24, name: 'Arne'},
    {id: 4, age: 26, name: 'Aaron'}
]);

//a new MixedCollection containing only the items where age == 24
var middleAged = people.filter('age', 24);
</code></pre>
     *
     *
     * @param {Ext.util.Filter[]/String} property A property on your objects, or an array of {@link Ext.util.Filter Filter} objects
     * @param {String/RegExp} value Either string that the property values
     * should start with or a RegExp to test against the property
     * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
     * @param {Boolean} caseSensitive (optional) True for case sensitive comparison (defaults to False).
     * @return {Ext.util.MixedCollection} The new filtered collection
     */
    filter: function(property, value, anyMatch, caseSensitive) {
        var filters = [],
            filterFn;

        //support for the simple case of filtering by property/value
        if (Ext.isString(property)) {
            filters.push(Ext.create('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);
        }

        //at this point we have an array of zero or more Ext.util.Filter objects to filter with,
        //so here we construct a function that combines these filters by ANDing them together
        filterFn = function(record) {
            var isMatch = true,
                length = filters.length,
                i;

            for (i = 0; i < length; i++) {
                var filter = filters[i],
                    fn     = filter.getFilterFn(),
                    scope  = filter.getScope();

                isMatch = isMatch && fn.call(scope, record);
            }

            return isMatch;
        };

        return this.filterBy(filterFn);
    },

    /**
     * Filter by a function. Returns a <i>new</i> collection that has been filtered.
     * The passed function will be called with each object in the collection.
     * If the function returns true, the value is included otherwise it is filtered.
     * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
     * @return {Ext.util.MixedCollection} The new filtered collection
     */
    filterBy: function(fn, scope) {
        var me = this,
            newMC  = new this.self(),
            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]);
            }
        }

        return newMC;
    },

    /**
     * Finds the index of the first matching object in this collection by a specific property/value.
     * @param {String} property The name of a property on your objects.
     * @param {String/RegExp} value A string that the property values
     * should start with or a RegExp to test against the property.
     * @param {Number} start (optional) The index to start searching at (defaults to 0).
     * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning.
     * @param {Boolean} caseSensitive (optional) True for case sensitive comparison.
     * @return {Number} The matched index or -1
     */
    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);
    },

    /**
     * Find the index of the first matching object in this collection by a function.
     * If the function returns <i>true</i> it is considered a match.
     * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).
     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
     * @param {Number} start (optional) The index to start searching at (defaults to 0).
     * @return {Number} The matched index or -1
     */
    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;
    },

    /**
     * Returns a regular expression based on the given value and matching options. This is used internally for finding and filtering,
     * and by Ext.data.Store#filter
     * @private
     * @param {String} value The value to create the regex for. This is escaped using Ext.escapeRe
     * @param {Boolean} anyMatch True to allow any match - no regex start/end line anchors will be added. Defaults to false
     * @param {Boolean} caseSensitive True to make the regex case sensitive (adds 'i' switch to regex). Defaults to false.
     * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. Ignored if anyMatch is true.
     */
    createValueMatcher: function(value, anyMatch, caseSensitive, exactMatch) {
        if (!value.exec) { // not a regex
            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;
    },

    /**
     * Creates a shallow copy of this collection
     * @return {Ext.util.MixedCollection}
     */
    clone: function() {
        var me = this,
            copy = new this.self(),
            keys = me.keys,
            items = me.items,
            i = 0,
            len = items.length;

        for(; i < len; i++){
            copy.add(keys[i], items[i]);
        }
        copy.getKey = me.getKey;
        return copy;
    }
});

/**
 * @private
 */
Ext.define('Ext.util.Collection', {
    /**
     * @cfg {Object[]} filters
     * Array of {@link Ext.util.Filter Filters} for this collection.
     */

    /**
     * @cfg {Object[]} sorters
     * Array of {@link Ext.util.Sorter Sorters} for this collection.
     */

    config: {
        autoFilter: true,
        autoSort: true
    },

    mixins: {
        sortable: 'Ext.mixin.Sortable',
        filterable: 'Ext.mixin.Filterable'
    },

    constructor: function(keyFn, config) {
        var me = this;

        /**
         * @property {Array} [all=[]]
         * An array containing all the items (unsorted, unfiltered)
         */
        me.all = [];

        /**
         * @property {Array} [items=[]]
         * An array containing the filtered items (sorted)
         */
        me.items = [];

        /**
         * @property {Array} [keys=[]]
         * An array containing all the filtered keys (sorted)
         */
        me.keys = [];

        /**
         * @property {Object} [indices={}]
         * An object used as map to get a sorted and filtered index of an item
         */
        me.indices = {};

        /**
         * @property {Object} [map={}]
         * An object used as map to get an object based on its key
         */
        me.map = {};

        /**
         * @property {Number} [length=0]
         * The count of items in the collection filtered and sorted
         */
        me.length = 0;

        if (keyFn) {
            me.getKey = keyFn;
        }

        this.initConfig(config);
    },

    updateAutoSort: function(autoSort, oldAutoSort) {
        if (oldAutoSort === false && autoSort && this.items.length) {
            this.sort();
        }
    },

    updateAutoFilter: function(autoFilter, oldAutoFilter) {
        if (oldAutoFilter === false && autoFilter && this.all.length) {
            this.runFilters();
        }
    },

    insertSorters: function() {
        // We override the insertSorters method that exists on the Sortable mixin. This method always
        // gets called whenever you add or insert a new sorter. We do this because we actually want
        // to sort right after this happens.
        this.mixins.sortable.insertSorters.apply(this, arguments);
        if (this.getAutoSort() && this.items.length) {
            this.sort();
        }
        return this;
    },

    removeSorters: function(sorters) {
        // We override the removeSorters method that exists on the Sortable mixin. This method always
        // gets called whenever you remove a sorter. If we are still sorted after we removed this sorter,
        // then we have to resort the whole collection.
        this.mixins.sortable.removeSorters.call(this, sorters);
        if (this.sorted && this.getAutoSort() && this.items.length) {
            this.sort();
        }
        return this;
    },

    applyFilters: function(filters) {
        var collection = this.mixins.filterable.applyFilters.call(this, filters);
        if (!filters && this.all.length && this.getAutoFilter()) {
            this.filter();
        }
        return collection;
    },

    addFilters: function(filters) {
        // We override the insertFilters method that exists on the Filterable mixin. This method always
        // gets called whenever you add or insert a new filter. We do this because we actually want
        // to filter right after this happens.
        this.mixins.filterable.addFilters.call(this, filters);
        if (this.items.length && this.getAutoFilter()) {
            this.runFilters();
        }
        return this;
    },

    removeFilters: function(filters) {
        // We override the removeFilters method that exists on the Filterable mixin. This method always
        // gets called whenever you remove a filter. If we are still filtered after we removed this filter,
        // then we have to refilter the whole collection.
        this.mixins.filterable.removeFilters.call(this, filters);
        if (this.filtered && this.all.length && this.getAutoFilter()) {
            this.filter();
        }
        return this;
    },

    filter: function(property, value, anyMatch, caseSensitive) {
        // Support for the simple case of filtering by property/value
        if (property) {
            if (Ext.isString(property)) {
                this.addFilters({
                    property     : property,
                    value        : value,
                    anyMatch     : anyMatch,
                    caseSensitive: caseSensitive
                });
                return this.items;
            }
            else {
                this.addFilters(property);
                return this.items;
            }
        }

        this.items = this.mixins.filterable.filter.call(this, this.all.slice());
        this.updateAfterFilter();

        if (this.sorted && this.getAutoSort()) {
            this.sort();
        }
    },

    runFilters: function() {
        this.items = this.mixins.filterable.filter.call(this, this.items);
        this.updateAfterFilter();
    },

    updateAfterFilter: function() {
        var items = this.items,
            keys = this.keys,
            indices = this.indices = {},
            ln = items.length,
            i, item, key;

        keys.length = 0;

        for (i = 0; i < ln; i++) {
            item = items[i];
            key = this.getKey(item);
            indices[key] = i;
            keys[i] = key;
        }

        this.length = items.length;
        this.dirtyIndices = false;
    },

    sort: function(sorters, defaultDirection) {
        var items = this.items,
            keys = this.keys,
            indices = this.indices,
            ln = items.length,
            i, item, key;

        // If we pass sorters to this method we have to add them first.
        // Because adding a sorter automatically sorts the items collection
        // we can just return items after we have added the sorters
        if (sorters) {
            this.addSorters(sorters, defaultDirection);
            return this.items;
        }

        // We save the keys temporarily on each item
        for (i = 0; i < ln; i++) {
            items[i]._current_key = keys[i];
        }

        // Now we sort our items array
        this.handleSort(items);

        // And finally we update our keys and indices
        for (i = 0; i < ln; i++) {
            item = items[i];
            key = item._current_key;

            keys[i] = key;
            indices[key] = i;

            delete item._current_key;
        }

        this.dirtyIndices = true;
    },

    handleSort: function(items) {
        this.mixins.sortable.sort.call(this, items);
    },

    /**
     * Adds an item to the collection. Fires the {@link #add} event when complete.
     * @param {String} key
     *
     * The key to associate with the item, or the new item.
     *
     * If a {@link #getKey} implementation was specified for this MixedCollection, or if the key of the stored items is
     * in a property called **id**, the MixedCollection will be able to _derive_ the key for the new item. In this case
     * just pass the new item in this parameter.
     * @param {Object} item The item to add.
     * @return {Object} The item added.
     */
    add: function(key, item) {
        var me = this,
            filtered = this.filtered,
            sorted = this.sorted,
            all = this.all,
            items = this.items,
            keys = this.keys,
            indices = this.indices,
            filterable = this.mixins.filterable,
            currentLength = items.length,
            index = currentLength;

        if (arguments.length == 1) {
            item = key;
            key = me.getKey(item);
        }

        if (typeof key != 'undefined' && key !== null) {
            if (typeof me.map[key] != 'undefined') {
                return me.replace(key, item);
            }
            me.map[key] = item;
        }

        all.push(item);

        if (filtered && this.getAutoFilter() && filterable.isFiltered.call(me, item)) {
            return null;
        }

        me.length++;

        if (sorted && this.getAutoSort()) {
            index = this.findInsertionIndex(items, item);
        }

        if (index !== currentLength) {
            this.dirtyIndices = true;

            Ext.Array.splice(keys, index, 0, key);
            Ext.Array.splice(items, index, 0, item);
        } else {
            indices[key] = currentLength;

            keys.push(key);
            items.push(item);
        }

        return item;
    },

    /**
     * MixedCollection has a generic way to fetch keys if you implement getKey. The default implementation simply
     * returns **`item.id`** but you can provide your own implementation to return a different value as in the following
     * examples:
     *
     *     // normal way
     *     var mc = new Ext.util.MixedCollection();
     *     mc.add(someEl.dom.id, someEl);
     *     mc.add(otherEl.dom.id, otherEl);
     *     //and so on
     *
     *     // using getKey
     *     var mc = new Ext.util.MixedCollection();
     *     mc.getKey = function(el){
     *        return el.dom.id;
     *     };
     *     mc.add(someEl);
     *     mc.add(otherEl);
     *
     *     // or via the constructor
     *     var mc = new Ext.util.MixedCollection(false, function(el){
     *        return el.dom.id;
     *     });
     *     mc.add(someEl);
     *     mc.add(otherEl);
     * @param {Object} item The item for which to find the key.
     * @return {Object} The key for the passed item.
     */
    getKey: function(item) {
         return item.id;
    },

    /**
     * Replaces an item in the collection. Fires the {@link #replace} event when complete.
     * @param {String} oldKey
     *
     * The key associated with the item to replace, or the replacement item.
     *
     * If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key of your stored items is
     * in a property called **id**, then the MixedCollection will be able to _derive_ the key of the replacement item.
     * If you want to replace an item with one having the same key value, then just pass the replacement item in this
     * parameter.
     * @param {Object} item {Object} item (optional) If the first parameter passed was a key, the item to associate with
     * that key.
     * @return {Object} The new item.
     */
    replace: function(oldKey, item) {
        var me = this,
            sorted = me.sorted,
            filtered = me.filtered,
            filterable = me.mixins.filterable,
            items = me.items,
            keys = me.keys,
            all = me.all,
            map = me.map,
            returnItem = null,
            oldItemsLn = items.length,
            oldItem, index, newKey;

        if (arguments.length == 1) {
            item = oldKey;
            oldKey = newKey = me.getKey(item);
        } else {
            newKey = me.getKey(item);
        }

        oldItem = map[oldKey];
        if (typeof oldKey == 'undefined' || oldKey === null || typeof oldItem == 'undefined') {
             return me.add(newKey, item);
        }

        me.map[newKey] = item;
        if (newKey !== oldKey) {
            delete me.map[oldKey];
        }

        if (sorted && me.getAutoSort()) {
            Ext.Array.remove(items, oldItem);
            Ext.Array.remove(keys, oldKey);
            Ext.Array.remove(all, oldItem);

            all.push(item);

            me.dirtyIndices = true;

            if (filtered && me.getAutoFilter()) {
                // If the item is now filtered we check if it was not filtered
                // before. If that is the case then we subtract from the length
                if (filterable.isFiltered.call(me, item)) {
                    if (oldItemsLn !== items.length) {
                        me.length--;
                    }
                    return null;
                }
                // If the item was filtered, but now it is not anymore then we
                // add to the length
                else if (oldItemsLn === items.length) {
                    me.length++;
                    returnItem = item;
                }
            }

            index = this.findInsertionIndex(items, item);

            Ext.Array.splice(keys, index, 0, newKey);
            Ext.Array.splice(items, index, 0, item);
        } else {
            if (filtered) {
                if (me.getAutoFilter() && filterable.isFiltered.call(me, item)) {
                    if (items.indexOf(oldItem) !== -1) {
                        Ext.Array.remove(items, oldItem);
                        Ext.Array.remove(keys, oldKey);
                        me.length--;
                        me.dirtyIndices = true;
                    }
                    return null;
                }
                else if (items.indexOf(oldItem) === -1) {
                    items.push(item);
                    keys.push(newKey);
                    me.indices[newKey] = me.length;
                    me.length++;
                    return item;
                }
            }

            index = me.items.indexOf(oldItem);

            keys[index] = newKey;
            items[index] = item;
            this.dirtyIndices = true;
        }

        return returnItem;
    },

    /**
     * Adds all elements of an Array or an Object to the collection.
     * @param {Object/Array} objs An Object containing properties which will be added to the collection, or an Array of
     * values, each of which are added to the collection. Functions references will be added to the collection if {@link
     * Ext.util.MixedCollection#allowFunctions allowFunctions} has been set to true.
     */
    addAll: function(addItems) {
        var me = this,
            filtered = me.filtered,
            sorted = me.sorted,
            all = me.all,
            items = me.items,
            keys = me.keys,
            map = me.map,
            autoFilter = me.getAutoFilter(),
            autoSort = me.getAutoSort(),
            newKeys = [],
            newItems = [],
            filterable = me.mixins.filterable,
            addedItems = [],
            ln, key, i, item;

        if (Ext.isObject(addItems)) {
            for (key in addItems) {
                if (addItems.hasOwnProperty(key)) {
                    newItems.push(items[key]);
                    newKeys.push(key);
                }
            }
        } else {
            newItems = addItems;
            ln = addItems.length;
            for (i = 0; i < ln; i++) {
                newKeys.push(me.getKey(addItems[i]));
            }
        }

        for (i = 0; i < ln; i++) {
            key = newKeys[i];
            item = newItems[i];

            if (typeof key != 'undefined' && key !== null) {
                if (typeof map[key] != 'undefined') {
                    me.replace(key, item);
                    continue;
                }
                map[key] = item;
            }

            all.push(item);

            if (filtered && autoFilter && filterable.isFiltered.call(me, item)) {
                continue;
            }

            me.length++;

            keys.push(key);
            items.push(item);

            addedItems.push(item);
        }

        if (addedItems.length) {
            me.dirtyIndices = true;

            if (sorted && autoSort) {
                me.sort();
            }

            return addedItems;
        }

        return null;
    },

    /**
     * Executes the specified function once for every item in the collection, passing the following arguments:
     *
     *   - **item** : Mixed
     *
     * The collection item
     *
     *   - **index** : Number
     *
     * The item's index
     *
     *   - **length** : Number
     *
     * The total number of items in the collection
     *
     * The function should return a boolean value. Returning false from the function will stop the iteration.
     * @param {Function} fn The function to execute for each item.
     * @param {Object} scope The scope (`this` reference) in which the function is executed. Defaults to the current
     * item in the iteration.
     */
    each: function(fn, scope) {
        var items = this.items.slice(), // each safe for removal
            i = 0,
            len = items.length,
            item;

        for (; i < len; i++) {
            item = items[i];
            if (fn.call(scope || item, item, i, len) === false) {
                break;
            }
        }
    },

    /**
     * Executes the specified function once for every key in the collection, passing each key, and its associated item
     * as the first two parameters.
     * @param {Function} fn The function to execute for each item.
     * @param {Object} scope The scope (`this` reference) in which the function is executed. Defaults to the browser
     * window.
     */
    eachKey: function(fn, scope) {
        var keys = this.keys,
            items = this.items,
            ln = keys.length, i;

        for (i = 0; i < ln; i++) {
            fn.call(scope || window, keys[i], items[i], i, ln);
        }
    },

    /**
     * Returns the first item in the collection which elicits a true return value from the passed selection function.
     * @param {Function} fn The selection function to execute for each item.
     * @param {Object} scope The scope (`this` reference) in which the function is executed. Defaults to the browser
     * window.
     * @return {Object} The first item in the collection which returned true from the selection function.
     */
    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;
    },

    /**
     * Filter by a function. Returns a _new_ collection that has been filtered. The passed function will be called with
     * each object in the collection. If the function returns true, the value is included otherwise it is filtered.
     * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
     * @param {Object} scope The scope (`this` reference) in which the function is executed. Defaults to this
     * MixedCollection.
     * @return {Ext.util.MixedCollection} The new filtered collection
     */
    filterBy: function(fn, scope) {
        var me = this,
            newCollection = new this.self(),
            keys   = me.keys,
            items  = me.all,
            length = items.length,
            i;

        newCollection.getKey = me.getKey;

        for (i = 0; i < length; i++) {
            if (fn.call(scope || me, items[i], keys[i])) {
                newCollection.add(keys[i], items[i]);
            }
        }

        return newCollection;
    },

    /**
     * Inserts an item at the specified index in the collection. Fires the {@link #add} event when complete.
     * @param {Number} index The index to insert the item at.
     * @param {String} key The key to associate with the new item, or the item itself.
     * @param {Object} item If the second parameter was a key, the new item.
     * @return {Object} The item inserted.
     */
    insert: function(index, key, item) {
        var me = this,
            sorted = this.sorted,
            filtered = this.filtered;

        if (arguments.length == 2) {
            item = key;
            key = me.getKey(item);
        }

        if (me.containsKey(key)) {
            me.removeAtKey(key);
        }

        if (index >= me.length || (sorted && me.getAutoSort())) {
            return me.add(key, item);
        }

        this.all.push(item);

        if (typeof key != 'undefined' && key !== null) {
            me.map[key] = item;
        }

        if (filtered && this.getAutoFilter() && filterable.isFiltered.call(me, item)) {
            return null;
        }

        me.length++;

        Ext.Array.splice(me.items, index, 0, item);
        Ext.Array.splice(me.keys, index, 0, key);

        me.dirtyIndices = true;

        return item;
    },

    insertAll: function(index, insertItems) {
        if (index >= this.items.length || (this.sorted && this.getAutoSort())) {
            return this.addAll(insertItems);
        }

        var me = this,
            filtered = this.filtered,
            sorted = this.sorted,
            all = this.all,
            items = this.items,
            keys = this.keys,
            map = this.map,
            autoFilter = this.getAutoFilter(),
            autoSort = this.getAutoSort(),
            newKeys = [],
            newItems = [],
            addedItems = [],
            filterable = this.mixins.filterable,
            insertedUnfilteredItem = false,
            ln, key, i, item;

        if (sorted && this.getAutoSort()) {
            Ext.Logger.error('Inserting a collection of items into a sorted Collection is invalid. Please just add these items or remove the sorters.');
        }

        if (Ext.isObject(insertItems)) {
            for (key in insertItems) {
                if (insertItems.hasOwnProperty(key)) {
                    newItems.push(items[key]);
                    newKeys.push(key);
                }
            }
        } else {
            newItems = insertItems;
            ln = insertItems.length;
            for (i = 0; i < ln; i++) {
                newKeys.push(me.getKey(insertItems[i]));
            }
        }

        for (i = 0; i < ln; i++) {
            key = newKeys[i];
            item = newItems[i];

            if (typeof key != 'undefined' && key !== null) {
                if (me.containsKey(key)) {
                    me.removeAtKey(key);
                }
                map[key] = item;
            }

            all.push(item);

            if (filtered && autoFilter && filterable.isFiltered.call(me, item)) {
                continue;
            }

            me.length++;

            Ext.Array.splice(items, index + i, 0, item);
            Ext.Array.splice(keys, index + i, 0, key);

            insertedUnfilteredItem = true;
            addedItems.push(item);
        }

        if (insertedUnfilteredItem) {
            this.dirtyIndices = true;

            if (sorted && autoSort) {
                this.sort();
            }

            return addedItems;
        }

        return null;
    },

    /**
     * Remove an item from the collection.
     * @param {Object} item The item to remove.
     * @return {Object} The item removed or false if no item was removed.
     */
    remove: function(item) {
        var index = this.items.indexOf(item);
        if (index === -1) {
            Ext.Array.remove(this.all, item);
            return item;
        }
        return this.removeAt(this.items.indexOf(item));
    },

    /**
     * Remove all items in the passed array from the collection.
     * @param {Array} items An array of items to be removed.
     * @return {Ext.util.MixedCollection} this object
     */
    removeAll: function(items) {
        if (items) {
            var ln = items.length, i;

            for (i = 0; i < ln; i++) {
                this.remove(items[i]);
            }
        }

        return this;
    },

    /**
     * Remove an item from a specified index in the collection. Fires the {@link #remove} event when complete.
     * @param {Number} index The index within the collection of the item to remove.
     * @return {Object} The item removed or false if no item was removed.
     */
    removeAt: function(index) {
        var me = this,
            items = me.items,
            keys = me.keys,
            all = this.all,
            item, key;

        if (index < me.length && index >= 0) {
            item = items[index];
            key = keys[index];

            if (typeof key != 'undefined') {
                delete me.map[key];
            }

            Ext.Array.erase(items, index, 1);
            Ext.Array.erase(keys, index, 1);
            Ext.Array.remove(all, item);

            me.length--;

            this.dirtyIndices = true;

            return item;
        }

        return false;
    },

    /**
     * Removed an item associated with the passed key fom the collection.
     * @param {String} key The key of the item to remove.
     * @return {Object} The item removed or false if no item was removed.
     */
    removeAtKey: function(key) {
        return this.removeAt(this.indexOfKey(key));
    },

    /**
     * Returns the number of items in the collection.
     * @return {Number} the number of items in the collection.
     */
    getCount: function() {
        return this.length;
    },

    /**
     * Returns index within the collection of the passed Object.
     * @param {Object} item The item to find the index of.
     * @return {Number} index of the item. Returns -1 if not found.
     */
    indexOf: function(item) {
        if (this.dirtyIndices) {
            this.updateIndices();
        }

        var index = this.indices[this.getKey(item)];
        return (index === undefined) ? -1 : index;
    },

    /**
     * Returns index within the collection of the passed key.
     * @param {String} key The key to find the index of.
     * @return {Number} index of the key.
     */
    indexOfKey: function(key) {
        if (this.dirtyIndices) {
            this.updateIndices();
        }

        var index = this.indices[key];
        return (index === undefined) ? -1 : index;
    },

    updateIndices: function() {
        var items = this.items,
            ln = items.length,
            indices = this.indices = {},
            i, item, key;

        for (i = 0; i < ln; i++) {
            item = items[i];
            key = this.getKey(item);
            indices[key] = i;
        }

        this.dirtyIndices = false;
    },

    /**
     * Returns the item associated with the passed key OR index. Key has priority over index. This is the equivalent of
     * calling {@link #getByKey} first, then if nothing matched calling {@link #getAt}.
     * @param {String/Number} key The key or index of the item.
     * @return {Object} If the item is found, returns the item. If the item was not found, returns undefined. If an item
     * was found, but is a Class, returns null.
     */
    get: function(key) {
        var me = this,
            fromMap = me.map[key],
            item;

        if (fromMap !== undefined) {
            item = fromMap;
        }
        else if (typeof key == 'number') {
            item = me.items[key];
        }

        return typeof item != 'function' || me.getAllowFunctions() ? item : null; // for prototype!
    },

    /**
     * Returns the item at the specified index.
     * @param {Number} index The index of the item.
     * @return {Object} The item at the specified index.
     */
    getAt: function(index) {
        return this.items[index];
    },

    /**
     * Returns the item associated with the passed key.
     * @param {String/Number} key The key of the item.
     * @return {Object} The item associated with the passed key.
     */
    getByKey: function(key) {
        return this.map[key];
    },

    /**
     * Returns true if the collection contains the passed Object as an item.
     * @param {Object} item The Object to look for in the collection.
     * @return {Boolean} True if the collection contains the Object as an item.
     */
    contains: function(item) {
        var key = this.getKey(item);
        if (key) {
            return this.containsKey(key);
        } else {
            return Ext.Array.contains(this.items, item);
        }
    },

    /**
     * Returns true if the collection contains the passed Object as a key.
     * @param {String} key The key to look for in the collection.
     * @return {Boolean} True if the collection contains the Object as a key.
     */
    containsKey: function(key) {
        return typeof this.map[key] != 'undefined';
    },

    /**
     * Removes all items from the collection. Fires the {@link #clear} event when complete.
     */
    clear: function(){
        var me = this;

        me.length = 0;
        me.items.length = 0;
        me.keys.length = 0;
        me.all.length = 0;
        me.map = {};
    },

    /**
     * Returns the first item in the collection.
     * @return {Object} the first item in the collection..
     */
    first: function() {
        return this.items[0];
    },

    /**
     * Returns the last item in the collection.
     * @return {Object} the last item in the collection..
     */
    last: function() {
        return this.items[this.length - 1];
    },

    /**
     * Returns a range of items in this collection
     * @param {Number} startIndex The starting index. Defaults to 0.
     * @param {Number} endIndex The ending index. Defaults to the last item.
     * @return {Array} An array of items
     */
    getRange: function(start, end) {
        var me = this,
            items = me.items,
            range = [],
            i;

        if (items.length < 1) {
            return range;
        }

        start = start || 0;
        end = Math.min(typeof end == 'undefined' ? me.length - 1 : end, me.length - 1);
        if (start <= end) {
            for (i = start; i <= end; i++) {
                range[range.length] = items[i];
            }
        } else {
            for (i = start; i >= end; i--) {
                range[range.length] = items[i];
            }
        }

        return range;
    },

    /**
     * Find the index of the first matching object in this collection by a function. If the function returns _true_ it
     * is considered a match.
     * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).
     * @param {Object} scope The scope (`this` reference) in which the function is executed. Defaults to this
     * MixedCollection.
     * @param {Number} start The index to start searching at (defaults to 0).
     * @return {Number} The matched index or -1
     */
    findIndexBy: function(fn, scope, start) {
        var me = this,
            keys = me.keys,
            items = me.items,
            i = start || 0,
            ln = items.length;

        for (; i < ln; i++) {
            if (fn.call(scope || me, items[i], keys[i])) {
                return i;
            }
        }

        return -1;
    },

    /**
     * Creates a shallow copy of this collection
     * @return {Ext.util.MixedCollection}
     */
    clone: function() {
        var me = this,
            copy = new this.self(),
            keys = me.keys,
            items = me.items,
            i = 0,
            ln = items.length;

        for(; i < ln; i++) {
            copy.add(keys[i], items[i]);
        }

        copy.getKey = me.getKey;
        return copy;
    }
});

/**
 * @docauthor Evan Trimboli <evan@sencha.com>
 *
 * Contains a collection of all stores that are created that have an identifier. An identifier can be assigned by
 * setting the {@link Ext.data.Store#storeId storeId} property. When a store is in the StoreManager, it can be
 * referred to via it's identifier:
 *
 *     Ext.create('Ext.data.Store', {
 *         model: 'SomeModel',
 *         storeId: 'myStore'
 *     });
 *
 *     var store = Ext.data.StoreManager.lookup('myStore');
 *
 * Also note that the {@link #lookup} method is aliased to {@link Ext#getStore} for convenience.
 *
 * If a store is registered with the StoreManager, you can also refer to the store by it's identifier when registering
 * it with any Component that consumes data from a store:
 *
 *     Ext.create('Ext.data.Store', {
 *         model: 'SomeModel',
 *         storeId: 'myStore'
 *     });
 *
 *     Ext.create('Ext.view.View', {
 *         store: 'myStore',
 *         // other configuration here
 *     });
 */
Ext.define('Ext.data.StoreManager', {
    extend: 'Ext.util.Collection',
    alternateClassName: ['Ext.StoreMgr', 'Ext.data.StoreMgr', 'Ext.StoreManager'],
    singleton: true,
    uses: ['Ext.data.ArrayStore'],

    /**
     * Registers one or more Stores with the StoreManager. You do not normally need to register stores manually. Any
     * store initialized with a {@link Ext.data.Store#storeId} will be auto-registered.
     * @param {Ext.data.Store...} stores Any number of Store instances
     */
    register : function() {
        for (var i = 0, s; (s = arguments[i]); i++) {
            this.add(s);
        }
    },

    /**
     * Unregisters one or more Stores with the StoreManager
     * @param {String/Object...} stores Any number of Store instances or ID-s
     */
    unregister : function() {
        for (var i = 0, s; (s = arguments[i]); i++) {
            this.remove(this.lookup(s));
        }
    },

    /**
     * Gets a registered Store by id
     * @param {String/Object} store The id of the Store, or a Store instance, or a store configuration
     * @return {Ext.data.Store}
     */
    lookup : function(store) {
        // handle the case when we are given an array or an array of arrays.
        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 Ext.create('Ext.data.ArrayStore', {
                data  : data,
                fields: fields,
                // See https://sencha.jira.com/browse/TOUCH-1541
                autoDestroy: true,
                autoCreated: true,
                expanded: expand
            });
        }

        if (Ext.isString(store)) {
            // store id
            return this.get(store);
        } else {
            // store instance or store config
            if (store instanceof Ext.data.Store) {
                return store;
            } else {
                return Ext.factory(store, Ext.data.Store, null, 'store');
            }
        }
    },

    // getKey implementation for MixedCollection
    getKey : function(o) {
         return o.getStoreId();
    }
}, function() {
    /**
     * Creates a new store for the given id and config, then registers it with the {@link Ext.data.StoreManager Store Mananger}.
     * Sample usage:
     *
     *     Ext.regStore('AllUsers', {
     *         model: 'User'
     *     });
     *
     *     // the store can now easily be used throughout the application
     *     new Ext.List({
     *         store: 'AllUsers',
     *         ... other config
     *     });
     *
     * @param {String} id The id to set on the new store
     * @param {Object} config The store config
     * @member Ext
     * @method regStore
     */
    Ext.regStore = function(name, config) {
        var store;

        if (Ext.isObject(name)) {
            config = name;
        } else {
            if (config instanceof Ext.data.Store) {
                config.setStoreId(name);
            } else {
                config.storeId = name;
            }
        }

        if (config instanceof Ext.data.Store) {
            store = config;
        } else {
            store = Ext.create('Ext.data.Store', config);
        }

        return Ext.data.StoreManager.register(store);
    };

    /**
     * Shortcut to {@link Ext.data.StoreManager#lookup}.
     * @member Ext
     * @method getStore
     * @alias Ext.data.StoreManager#lookup
     */
    Ext.getStore = function(name) {
        return Ext.data.StoreManager.lookup(name);
    };
});

/**
 * Ext.Direct aims to streamline communication between the client and server by providing a single interface that
 * reduces the amount of common code typically required to validate data and handle returned data packets (reading data,
 * error conditions, etc).
 *
 * The Ext.direct namespace includes several classes for a closer integration with the server-side. The Ext.data
 * namespace also includes classes for working with Ext.data.Stores which are backed by data from an Ext.Direct method.
 *
 * # Specification
 *
 * For additional information consult the [Ext.Direct Specification][1].
 *
 * # Providers
 *
 * Ext.Direct uses a provider architecture, where one or more providers are used to transport data to and from the
 * server. There are several providers that exist in the core at the moment:
 *
 * - {@link Ext.direct.JsonProvider JsonProvider} for simple JSON operations
 * - {@link Ext.direct.PollingProvider PollingProvider} for repeated requests
 * - {@link Ext.direct.RemotingProvider RemotingProvider} exposes server side on the client.
 *
 * A provider does not need to be invoked directly, providers are added via {@link Ext.direct.Manager}.{@link #addProvider}.
 *
 * # Router
 *
 * Ext.Direct utilizes a "router" on the server to direct requests from the client to the appropriate server-side
 * method. Because the Ext.Direct API is completely platform-agnostic, you could completely swap out a Java based server
 * solution and replace it with one that uses C# without changing the client side JavaScript at all.
 *
 * # Server side events
 *
 * Custom events from the server may be handled by the client by adding listeners, for example:
 *
 *     {"type":"event","name":"message","data":"Successfully polled at: 11:19:30 am"}
 *
 *     // add a handler for a 'message' event sent by the server
 *     Ext.direct.Manager.on('message', function(e){
 *         out.append(String.format('<p><i>{0}</i></p>', e.data));
 *         out.el.scrollTo('t', 100000, true);
 *     });
 *
 *    [1]: http://sencha.com/products/extjs/extdirect
 *
 * @singleton
 * @alternateClassName Ext.Direct
 */
Ext.define('Ext.direct.Manager', {
    singleton: true,

    mixins: {
        observable: 'Ext.util.Observable'
    },

    requires: ['Ext.util.Collection'],

    alternateClassName: 'Ext.Direct',

    statics: {
        exceptions: {
            TRANSPORT: 'xhr',
            PARSE: 'parse',
            LOGIN: 'login',
            SERVER: 'exception'
        }
    },

    /**
     * @event event
     * Fires after an event.
     * @param {Ext.direct.Event} e The Ext.direct.Event type that occurred.
     * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.
     */

    /**
     * @event exception
     * Fires after an event exception.
     * @param {Ext.direct.Event} e The event type that occurred.
     */

    constructor: function() {
        var me = this;

        me.transactions = Ext.create('Ext.util.Collection', this.getKey);
        me.providers = Ext.create('Ext.util.Collection', this.getKey);
    },

    getKey: function(item) {
        return item.getId();
    },

    /**
     * Adds an Ext.Direct Provider and creates the proxy or stub methods to execute server-side methods. If the provider
     * is not already connected, it will auto-connect.
     *
     *     Ext.direct.Manager.addProvider({
     *         type: "remoting",       // create a {@link Ext.direct.RemotingProvider}
     *         url: "php\/router.php", // url to connect to the Ext.Direct server-side router.
     *         actions: {              // each property within the actions object represents a Class
     *             TestAction: [       // array of methods within each server side Class
     *             {
     *                 name: "doEcho", // name of method
     *                 len: 1
     *             },{
     *                 name: "multiply",
     *                 len: 1
     *             },{
     *                 name: "doForm",
     *                 formHandler: true,  // handle form on server with Ext.Direct.Transaction
     *                 len: 1
     *             }]
     *         },
     *         namespace: "myApplication", // namespace to create the Remoting Provider in
     *     });
     *
     * @param {Ext.direct.Provider/Object...} provider
     * Accepts any number of Provider descriptions (an instance or config object for
     * a Provider). Each Provider description instructs Ext.Directhow to create
     * client-side stub methods.
     */
    addProvider : function(provider) {
        var me = this,
            args = Ext.toArray(arguments),
            i = 0, ln;

        if (args.length > 1) {
            for (ln = args.length; i < ln; ++i) {
                me.addProvider(args[i]);
            }
            return;
        }

        // if provider has not already been instantiated
        if (!provider.isProvider) {
            provider = Ext.create('direct.' + provider.type + 'provider', provider);
        }
        me.providers.add(provider);
        provider.on('data', me.onProviderData, me);

        if (!provider.isConnected()) {
            provider.connect();
        }

        return provider;
    },

    /**
     * Retrieves a {@link Ext.direct.Provider provider} by the **{@link Ext.direct.Provider#id id}** specified when the
     * provider is {@link #addProvider added}.
     * @param {String/Ext.direct.Provider} id The id of the provider, or the provider instance.
     */
    getProvider : function(id){
        return id.isProvider ? id : this.providers.get(id);
    },

    /**
     * Removes the provider.
     * @param {String/Ext.direct.Provider} provider The provider instance or the id of the provider.
     * @return {Ext.direct.Provider} The provider, null if not found.
     */
    removeProvider : function(provider) {
        var me = this,
            providers = me.providers;

        provider = provider.isProvider ? provider : providers.get(provider);

        if (provider) {
            provider.un('data', me.onProviderData, me);
            providers.remove(provider);
            return provider;
        }
        return null;
    },

    /**
     * Adds a transaction to the manager.
     * @private
     * @param {Ext.direct.Transaction} transaction The transaction to add
     * @return {Ext.direct.Transaction} transaction
     */
    addTransaction: function(transaction) {
        this.transactions.add(transaction);
        return transaction;
    },

    /**
     * Removes a transaction from the manager.
     * @private
     * @param {String/Ext.direct.Transaction} transaction The transaction/id of transaction to remove
     * @return {Ext.direct.Transaction} transaction
     */
    removeTransaction: function(transaction) {
        transaction = this.getTransaction(transaction);
        this.transactions.remove(transaction);
        return transaction;
    },

    /**
     * Gets a transaction
     * @private
     * @param {String/Ext.direct.Transaction} transaction The transaction/id of transaction to get
     * @return {Ext.direct.Transaction}
     */
    getTransaction: function(transaction) {
        return Ext.isObject(transaction) ? transaction : this.transactions.get(transaction);
    },

    onProviderData : function(provider, event) {
        var me = this,
            i = 0, ln,
            name;

        if (Ext.isArray(event)) {
            for (ln = event.length; i < ln; ++i) {
                me.onProviderData(provider, event[i]);
            }
            return;
        }

        name = event.getName();

        if (name && name != 'event' && name != 'exception') {
            me.fireEvent(name, event);
        } else if (event.getStatus() === false) {
            me.fireEvent('exception', event);
        }

        me.fireEvent('event', event, provider);
    },

    /**
     * Parses a direct function. It may be passed in a string format, for example:
     * "MyApp.Person.read".
     * @protected
     * @param {String/Function} fn The direct function
     * @return {Function} The function to use in the direct call. Null if not found
     */
    parseMethod: function(fn) {
        if (Ext.isString(fn)) {
            var parts = fn.split('.'),
                i = 0,
                ln = parts.length,
                current = window;

            while (current && i < ln) {
                current = current[parts[i]];
                ++i;
            }
            fn = Ext.isFunction(current) ? current : null;
        }
        return fn || null;
    }
});

/**
 * This class is used to send requests to the server using {@link Ext.direct.Manager Ext.Direct}. When a
 * request is made, the transport mechanism is handed off to the appropriate
 * {@link Ext.direct.RemotingProvider Provider} to complete the call.
 *
 * # Specifying the function
 *
 * This proxy expects a Direct remoting method to be passed in order to be able to complete requests.
 * This can be done by specifying the {@link #directFn} configuration. This will use the same direct
 * method for all requests. Alternatively, you can provide an {@link #api} configuration. This
 * allows you to specify a different remoting method for each CRUD action.
 *
 * # Parameters
 *
 * This proxy provides options to help configure which parameters will be sent to the server.
 * By specifying the {@link #paramsAsHash} option, it will send an object literal containing each
 * of the passed parameters. The {@link #paramOrder} option can be used to specify the order in which
 * the remoting method parameters are passed.
 *
 * # Example Usage
 *
 *     Ext.define('User', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: ['firstName', 'lastName'],
 *             proxy: {
 *                 type: 'direct',
 *                 directFn: MyApp.getUsers,
 *                 paramOrder: 'id' // Tells the proxy to pass the id as the first parameter to the remoting method.
 *             }
 *         }
 *     });
 *     User.load(1);
 */
Ext.define('Ext.data.proxy.Direct', {
    extend: 'Ext.data.proxy.Server',
    alternateClassName: 'Ext.data.DirectProxy',
    alias: 'proxy.direct',
    requires: ['Ext.direct.Manager'],

    config: {
        /**
         * @cfg {String/String[]} paramOrder
         * Defaults to undefined. A list of params to be executed server side.  Specify the params in the order in
         * which they must be executed on the server-side as either (1) an Array of String values, or (2) a String
         * of params delimited by either whitespace, comma, or pipe. For example, any of the following would be
         * acceptable:
         *
         *     paramOrder: ['param1','param2','param3']
         *     paramOrder: 'param1 param2 param3'
         *     paramOrder: 'param1,param2,param3'
         *     paramOrder: 'param1|param2|param'
         */
        paramOrder: undefined,

        /**
         * @cfg {Boolean} paramsAsHash
         * Send parameters as a collection of named arguments.
         * Providing a {@link #paramOrder} nullifies this configuration.
         */
        paramsAsHash: true,

        /**
         * @cfg {Function/String} directFn
         * Function to call when executing a request. directFn is a simple alternative to defining the api configuration-parameter
         * for Store's which will not implement a full CRUD api. The directFn may also be a string reference to the fully qualified
         * name of the function, for example: 'MyApp.company.GetProfile'. This can be useful when using dynamic loading. The string
         * will be looked up when the proxy is created.
         */
        directFn : undefined,

        /**
         * @cfg {Object} api
         * The same as {@link Ext.data.proxy.Server#api}, however instead of providing urls, you should provide a direct
         * function call. See {@link #directFn}.
         */
        api: null,

        /**
         * @cfg {Object} extraParams
         * Extra parameters that will be included on every read request. Individual requests with params
         * of the same name will override these params when they are in conflict.
         */
        extraParams: null
    },

    // private
    paramOrderRe: /[\s,|]/,

    applyParamOrder: function(paramOrder) {
        if (Ext.isString(paramOrder)) {
            paramOrder = paramOrder.split(this.paramOrderRe);
        }
        return paramOrder;
    },

    applyDirectFn: function(directFn) {
        return Ext.direct.Manager.parseMethod(fn);
    },

    applyApi: function(api) {
        var fn;

        if (api && Ext.isObject(api)) {
            for (fn in api) {
                if (api.hasOwnProperty(fn)) {
                    api[fn] = Ext.direct.Manager.parseMethod(api[fn]);
                }
            }
        }

        return api;
    },

    doRequest: function(operation, callback, scope) {
        var me = this,
            writer = me.getWriter(),
            request = me.buildRequest(operation, callback, scope),
            api = me.getApi(),
            fn = api && api[request.getAction()] || me.getDirectFn(),
            params = request.getParams(),
            args = [],
            method;

        if (!fn) {
            Ext.Error.raise('No direct function specified for this proxy');
        }

        request = writer.write(request);

        if (operation.getAction() == 'read') {
            // We need to pass params
            method = fn.directCfg.method;
            args = method.getArgs(params, me.getParamOrder(), me.getParamsAsHash());
        } else {
            args.push(request.getJsonData());
        }

        args.push(me.createRequestCallback(request, operation, callback, scope), me);

        request.setConfig({
            args: args,
            directFn: fn
        });

        fn.apply(window, args);
    },

    /*
     * Inherit docs. We don't apply any encoding here because
     * all of the direct requests go out as jsonData
     */
    applyEncoding: function(value) {
        return value;
    },

    createRequestCallback: function(request, operation, callback, scope) {
        var me = this;

        return function(data, event) {
            me.processResponse(event.getStatus(), operation, request, event, callback, scope);
        };
    },

    // inherit docs
    extractResponseData: function(response) {
        var result = response.getResult();
        return Ext.isDefined(result) ? result : response.getData();
    },

    // inherit docs
    setException: function(operation, response) {
        operation.setException(response.getMessage());
    },

    // inherit docs
    buildUrl: function() {
        return '';
    }
});

/**
 * @class Ext.direct.Event
 * A base class for all Ext.direct events. An event is
 * created after some kind of interaction with the server.
 * The event class is essentially just a data structure
 * to hold a Direct response.
 */
Ext.define('Ext.direct.Event', {
    alias: 'direct.event',

    requires: ['Ext.direct.Manager'],

    config: {
        status: true,

        /**
         * @cfg {Object} data The raw data for this event.
         * @accessor
         */
        data: null,

        /**
         * @cfg {String} name The name of this Event.
         * @eccessor
         */
        name: 'event',

        xhr: null,

        code: null,

        message: '',

        result: null,

        transaction: null
    },

    constructor: function(config) {
        this.initConfig(config)
    }
});
/**
 * @class Ext.direct.RemotingEvent
 * An event that is fired when data is received from a
 * {@link Ext.direct.RemotingProvider}. Contains a method to the
 * related transaction for the direct request, see {@link #getTransaction}
 */
Ext.define('Ext.direct.RemotingEvent', {
    extend: 'Ext.direct.Event',

    alias: 'direct.rpc',

    config: {
        name: 'remoting',
        tid: null,
        transaction: null
    },

    /**
     * Get the transaction associated with this event.
     * @return {Ext.direct.Transaction} The transaction
     */
    getTransaction: function() {
        return this._transaction || Ext.direct.Manager.getTransaction(this.getTid());
    }
});
/**
 * @class Ext.direct.ExceptionEvent
 * An event that is fired when an exception is received from a {@link Ext.direct.RemotingProvider}
 */
Ext.define('Ext.direct.ExceptionEvent', {

    extend: 'Ext.direct.RemotingEvent',

    alias: 'direct.exception',

    config: {
        status: false,
        name: 'exception',
        error: null
    }
});

/**
 * Reusable data formatting functions
 */
Ext.define('Ext.util.Format', {
    requires: [
        'Ext.DateExtras'
    ],

    singleton: true,

    /**
     * The global default date format.
     */
    defaultDateFormat: 'm/d/Y',

    escapeRe: /('|\\)/g,
    trimRe: /^[\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,
    formatRe: /\{(\d+)\}/g,
    escapeRegexRe: /([-.*+?^${}()|[\]\/\\])/g,
    dashesRe: /-/g,
    iso8601TestRe: /\d\dT\d\d/,
    iso8601SplitRe: /[- :T\.Z\+]/,

    /**
     * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
     * @param {String} value The string to truncate
     * @param {Number} length The maximum length to allow before truncating
     * @param {Boolean} word True to try to find a common word break
     * @return {String} The converted text
     */
    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;
    },

    /**
     * Escapes the passed string for use in a regular expression
     * @param {String} str
     * @return {String}
     */
    escapeRegex: function(s) {
        return s.replace(Ext.util.Format.escapeRegexRe, "\\$1");
    },

    /**
     * Escapes the passed string for ' and \
     * @param {String} string The string to escape
     * @return {String} The escaped string
     */
    escape: function(string) {
        return string.replace(Ext.util.Format.escapeRe, "\\$1");
    },

    /**
     * Utility function that allows you to easily switch a string between two alternating values.  The passed value
     * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
     * they are already different, the first value passed in is returned.  Note that this method returns the new value
     * but does not change the current string.
     * <pre><code>
    // alternate sort directions
    sort = Ext.util.Format.toggle(sort, 'ASC', 'DESC');

    // instead of conditional logic:
    sort = (sort == 'ASC' ? 'DESC' : 'ASC');
       </code></pre>
     * @param {String} string The current string
     * @param {String} value The value to compare to the current string
     * @param {String} other The new value to use if the string already equals the first value passed in
     * @return {String} The new value
     */
    toggle: function(string, value, other) {
        return string == value ? other : value;
    },

    /**
     * Trims whitespace from either end of a string, leaving spaces within the string intact.  Example:
     * <pre><code>
    var s = '  foo bar  ';
    alert('-' + s + '-');         //alerts "- foo bar -"
    alert('-' + Ext.util.Format.trim(s) + '-');  //alerts "-foo bar-"
       </code></pre>
     * @param {String} string The string to escape
     * @return {String} The trimmed string
     */
    trim: function(string) {
        return string.replace(Ext.util.Format.trimRe, "");
    },

    /**
     * Pads the left side of a string with a specified character.  This is especially useful
     * for normalizing number and date strings.  Example usage:
     *
     * <pre><code>
var s = Ext.util.Format.leftPad('123', 5, '0');
// s now contains the string: '00123'
       </code></pre>
     * @param {String} string The original string
     * @param {Number} size The total length of the output string
     * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
     * @return {String} The padded string
     */
    leftPad: function (val, size, ch) {
        var result = String(val);
        ch = ch || " ";
        while (result.length < size) {
            result = ch + result;
        }
        return result;
    },

    /**
     * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
     * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
     * <pre><code>
var cls = 'my-class', text = 'Some text';
var s = Ext.util.Format.format('&lt;div class="{0}">{1}&lt;/div>', cls, text);
// s now contains the string: '&lt;div class="my-class">Some text&lt;/div>'
       </code></pre>
     * @param {String} string The tokenized string to be formatted
     * @param {String...} values The values to replace token {0}, {1}, etc
     * @return {String} The formatted string
     */
    format: function (format) {
        var args = Ext.toArray(arguments, 1);
        return format.replace(Ext.util.Format.formatRe, function(m, i) {
            return args[i];
        });
    },

    /**
     * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
     * @param {String} value The string to encode
     * @return {String} The encoded text
     */
    htmlEncode: function(value) {
        return ! value ? value: String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
    },

    /**
     * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
     * @param {String} value The string to decode
     * @return {String} The decoded text
     */
    htmlDecode: function(value) {
        return ! value ? value: String(value).replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"').replace(/&amp;/g, "&");
    },

    /**
     * Parse a value into a formatted date using the specified format pattern.
     * @param {String/Date} value The value to format (Strings must conform to the format expected by the javascript
     * Date object's <a href="http://www.w3schools.com/jsref/jsref_parse.asp">parse()</a> method)
     * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
     * @return {String} The formatted date string
     */
    date: function(value, format) {
        var date = value;
        if (!value) {
            return "";
        }
        if (!Ext.isDate(value)) {
            date = new Date(Date.parse(value));
            if (isNaN(date)) {
                // Dates with ISO 8601 format are not well supported by mobile devices, this can work around the issue.
                if (this.iso8601TestRe.test(value)) {
                    date = value.split(this.iso8601SplitRe);
                    date = new Date(date[0], date[1]-1, date[2], date[3], date[4], date[5]);
                }
                if (isNaN(date)) {
                    // Dates with the format "2012-01-20" fail, but "2012/01/20" work in some browsers. We'll try and
                    // get around that.
                    date = new Date(Date.parse(value.replace(this.dashesRe, "/")));
                    if (isNaN(date)) {
                        Ext.Logger.error("Cannot parse the passed value " + value + " into a valid date");
                    }
                }
            }
            value = date;
        }
        return Ext.Date.format(value, format || Ext.util.Format.defaultDateFormat);
    }
});

/**
 * Represents an HTML fragment template. Templates may be {@link #compile precompiled} for greater performance.
 *
 * An instance of this class may be created by passing to the constructor either a single argument, or multiple
 * arguments:
 *
 * # Single argument: String/Array
 *
 * The single argument may be either a String or an Array:
 *
 * - String:
 *
 *       var t = new Ext.Template("<div>Hello {0}.</div>");
 *       t.{@link #append}('some-element', ['foo']);
 *
 * - Array:
 *
 *   An Array will be combined with `join('')`.
 *
 *       var t = new Ext.Template([
 *           '<div name="{id}">',
 *               '<span class="{cls}">{name:trim} {value:ellipsis(10)}</span>',
 *           '</div>',
 *       ]);
 *       t.{@link #compile}();
 *       t.{@link #append}('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
 *
 * # Multiple arguments: String, Object, Array, ...
 *
 * Multiple arguments will be combined with `join('')`.
 *
 *     var t = new Ext.Template(
 *         '<div name="{id}">',
 *             '<span class="{cls}">{name} {value}</span>',
 *         '</div>',
 *         // a configuration object:
 *         {
 *             compiled: true,      // {@link #compile} immediately
 *         }
 *     );
 *
 * # Notes
 *
 * - For a list of available format functions, see {@link Ext.util.Format}.
 * - `disableFormats` reduces `{@link #apply}` time when no formatting is required.
 */
Ext.define('Ext.Template', {

    /* Begin Definitions */

    requires: ['Ext.dom.Helper', 'Ext.util.Format'],

    inheritableStatics: {
        /**
         * Creates a template from the passed element's value (_display:none_ textarea, preferred) or innerHTML.
         * @param {String/HTMLElement} el A DOM element or its id
         * @param {Object} config (optional) Config object
         * @return {Ext.Template} The created template
         * @static
         * @inheritable
         */
        from: function(el, config) {
            el = Ext.getDom(el);
            return new this(el.value || el.innerHTML, config || '');
        }
    },

    /* End Definitions */

    /**
     * Creates new template.
     *
     * @param {String...} html List of strings to be concatenated into template.
     * Alternatively an array of strings can be given, but then no config object may be passed.
     * @param {Object} config (optional) Config object
     */
    constructor: function(html) {
        var me = this,
            args = arguments,
            buffer = [],
            i = 0,
            length = args.length,
            value;

        me.initialConfig = {};

        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);
                }
            }
            html = buffer.join('');
        } else {
            if (Ext.isArray(html)) {
                buffer.push(html.join(''));
            } else {
                buffer.push(html);
            }
        }

        // @private
        me.html = buffer.join('');

        if (me.compiled) {
            me.compile();
        }
    },

    /**
     * @property {Boolean} isTemplate
     * `true` in this class to identify an objact as an instantiated Template, or subclass thereof.
     */
    isTemplate: true,

    /**
     * @cfg {Boolean} compiled
     * True to immediately compile the template. Defaults to false.
     */

    /**
     * @cfg {Boolean} disableFormats
     * True to disable format functions in the template. If the template doesn't contain
     * format functions, setting disableFormats to true will reduce apply time. Defaults to false.
     */
    disableFormats: false,

    re: /\{([\w\-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,

    /**
     * Returns an HTML fragment of this template with the specified values applied.
     *
     * @param {Object/Array} values The template values. Can be an array if your params are numeric:
     *
     *     var tpl = new Ext.Template('Name: {0}, Age: {1}');
     *     tpl.apply(['John', 25]);
     *
     * or an object:
     *
     *     var tpl = new Ext.Template('Name: {name}, Age: {age}');
     *     tpl.apply({name: 'John', age: 25});
     *
     * @return {String} The HTML fragment
     */
    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;
    },

    /**
     * Appends the result of this template to the provided output array.
     * @param {Object/Array} values The template values. See {@link #apply}.
     * @param {Array} out The array to which output is pushed.
     * @return {Array} The given out array.
     */
    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;
    },

    /**
     * @method applyTemplate
     * @member Ext.Template
     * Alias for {@link #apply}.
     * @inheritdoc Ext.Template#apply
     */
    applyTemplate: function () {
        return this.apply.apply(this, arguments);
    },

    /**
     * Sets the HTML used as the template and optionally compiles it.
     * @param {String} html
     * @param {Boolean} compile (optional) True to compile the template.
     * @return {Ext.Template} this
     */
    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, returnEl) {
        el = Ext.getDom(el);
        var newNode = Ext.DomHelper.insertHtml(where, el, this.apply(values));
        return returnEl ? Ext.get(newNode, true) : 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) {
        el = Ext.getDom(el);
        el.innerHTML = this.apply(values);
        return returnElement ? Ext.get(el.firstChild, true) : el.firstChild;
    }
});
/**
 * 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.DataView}.
 *
 * 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.
 * - While processing an array, the special variable {#} will provide the current array index + 1 (starts at 1, not 0).
 *
 * Examples:
 *
 *     <tpl for=".">...</tpl>       // loop through array at root node
 *     <tpl for="foo">...</tpl>     // loop through array at foo node
 *     <tpl for="foo.bar">...</tpl> // loop through array at foo.bar node
 *
 * Using the sample data above:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Kids: ',
 *         '<tpl for=".">',       // process the data.kids node
 *             '<p>{#}. {name}</p>',  // use current array index to autonumber
 *         '</tpl></p>'
 *     );
 *     tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
 *
 * 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">',     // interrogate the kids property within the data
 *             '<p>{name}</p>',
 *         '</tpl></p>'
 *     );
 *     tpl.overwrite(panel.body, data);  // pass the root node of the data object
 *
 * 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);
 *
 * # 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">',  // <-- Note that the > is encoded
 *                 '<p>{#}: {name}</p>',  // <-- Auto-number each item
 *                 '<p>In 5 Years: {age+5}</p>',  // <-- Basic math
 *                 '<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 looping template, the index of the loop you are in (1-based).
 * - **xcount**: If you are in a looping template, the total length of the array you are looping.
 *
 * 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 == 'Sara Grace';
 *             },
 *             isBaby: function(age){
 *                return age < 1;
 *             }
 *         }
 *     );
 *     tpl.overwrite(panel.body, data);
 */
Ext.define('Ext.XTemplate', {
    extend: 'Ext.Template',

    requires: 'Ext.XTemplateCompiler',

    /**
     * @cfg {Boolean} compiled
     * Only applies to {@link Ext.Template}, XTemplates are compiled automatically on the
     * first call to {@link #apply} or {@link #applyOut}.
     */

    apply: function(values) {
        return this.applyOut(values, []).join('');
    },

    applyOut: function(values, out) {
        var me = this,
            compiler;

        if (!me.fn) {
            compiler = new Ext.XTemplateCompiler({
                useFormat: me.disableFormats !== true
            });

            me.fn = compiler.compile(me.html);
        }

        try {
            me.fn.call(me, out, values, {}, 1, 1);
        } catch (e) {
            Ext.Logger.error(e.message);
        }

        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', {
         *          someTpl: [
         *              'tpl text here'
         *          ]
         *      });
         *
         * 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).
         *
         * @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
         */
        getTpl: function (instance, name) {
            var tpl = instance[name], // go for it! 99% of the time we will get it!
                proto;

            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
                    instance[name] = tpl;
                } else { // must be somewhere in the prototype chain
                    for (proto = instance.self.prototype; proto; proto = proto.superclass) {
                        if (proto.hasOwnProperty(name)) {
                            proto[name] = tpl;
                            break;
                        }
                    }
                }
            }
            // else !tpl (no such tpl) or the tpl is an instance already... either way, tpl
            // is ready to return

            return tpl || null;
        }
    }
});

/**
 * Provides a cross browser class for retrieving location information.
 *
 * Based on the [Geolocation API Specification](http://dev.w3.org/geo/api/spec-source.html)
 *
 * When instantiated, by default this class immediately begins tracking location information,
 * firing a {@link #locationupdate} event when new location information is available.  To disable this
 * location tracking (which may be battery intensive on mobile devices), set {@link #autoUpdate} to false.
 *
 * When this is done, only calls to {@link #updateLocation} will trigger a location retrieval.
 *
 * A {@link #locationerror} event is raised when an error occurs retrieving the location, either due to a user
 * denying the application access to it, or the browser not supporting it.
 *
 * The below code shows a GeoLocation making a single retrieval of location information.
 *
 *     var geo = new Ext.util.GeoLocation({
 *         autoUpdate: false,
 *         listeners: {
 *             locationupdate: function(geo) {
 *                 alert('New latitude: ' + geo.getLatitude());
 *             },
 *             locationerror: function(geo, bTimeout, bPermissionDenied, bLocationUnavailable, message) {
 *                 if(bTimeout){
 *                     alert('Timeout occurred.');
 *                 } else {
 *                     alert('Error occurred.');
 *                 }
 *             }
 *         }
 *     });
 *     geo.updateLocation();
 */
Ext.define('Ext.util.GeoLocation', {
    extend: 'Ext.Evented',

    config: {
        /**
         * @event locationerror
         * Raised when a location retrieval operation failed.<br/>
         * In the case of calling updateLocation, this event will be raised only once.<br/>
         * If {@link #autoUpdate} is set to true, this event could be raised repeatedly.
         * The first error is relative to the moment {@link #autoUpdate} was set to true
         * (or this {@link Ext.util.GeoLocation} was initialized with the {@link #autoUpdate} config option set to true).
         * Subsequent errors are relative to the moment when the device determines that it's position has changed.
         * @param {Ext.util.GeoLocation} this
         * @param {Boolean} timeout
         * Boolean indicating a timeout occurred
         * @param {Boolean} permissionDenied
         * Boolean indicating the user denied the location request
         * @param {Boolean} locationUnavailable
         * Boolean indicating that the location of the device could not be determined.<br/>
         * For instance, one or more of the location providers used in the location acquisition
         * process reported an internal error that caused the process to fail entirely.
         * @param {String} message
         * An error message describing the details of the error encountered.<br/>
         * This attribute is primarily intended for debugging and should not be used
         * directly in an application user interface.
         */

        /**
         * @event locationupdate
         * Raised when a location retrieval operation has been completed successfully.
         * @param {Ext.util.GeoLocation} this
         * Retrieve the current location information from the GeoLocation object by using the read-only
         * properties latitude, longitude, accuracy, altitude, altitudeAccuracy, heading, and speed.
         */

        /**
         * @cfg {Boolean} autoUpdate
         * When set to true, continually monitor the location of the device (beginning immediately)
         * and fire {@link #locationupdate}/{@link #locationerror} events.
         * When using google gears, if the user denies access or another error occurs, this will be reset to false.
         */
        autoUpdate: true,

        /**
         * Read-only property representing the last retrieved
         * geographical coordinate specified in degrees.
         * @type Number
         */
        latitude: null,

        /**
         * Read-only property representing the last retrieved
         * geographical coordinate specified in degrees.
         * @type Number
         */
        longitude: null,

        /**
         * Read-only property representing the last retrieved
         * accuracy level of the latitude and longitude coordinates,
         * specified in meters.<br/>
         * This will always be a non-negative number.<br/>
         * This corresponds to a 95% confidence level.
         * @type Number
         */
        accuracy: null,

        /**
         * Read-only property representing the last retrieved
         * height of the position, specified in meters above the ellipsoid
         * <a href="http://dev.w3.org/geo/api/spec-source.html#ref-wgs">[WGS84]</a>.
         * @type Number
         */
        altitude: null,

        /**
         * Read-only property representing the last retrieved
         * accuracy level of the altitude coordinate, specified in meters.<br/>
         * If altitude is not null then this will be a non-negative number.
         * Otherwise this returns null.<br/>
         * This corresponds to a 95% confidence level.
         * @type Number
         */
        altitudeAccuracy: null,

        /**
         * Read-only property representing the last retrieved
         * direction of travel of the hosting device,
         * specified in non-negative degrees between 0 and 359,
         * counting clockwise relative to the true north.<br/>
         * If speed is 0 (device is stationary), then this returns NaN
         * @type Number
         */
        heading: null,

        /**
         * Read-only property representing the last retrieved
         * current ground speed of the device, specified in meters per second.<br/>
         * If this feature is unsupported by the device, this returns null.<br/>
         * If the device is stationary, this returns 0,
         * otherwise it returns a non-negative number.
         * @type Number
         */
        speed: null,

        /**
         * Read-only property representing when the last retrieved
         * positioning information was acquired by the device.
         * @type Date
         */
        timestamp: null,

        //PositionOptions interface
        /**
         * @cfg {Boolean} allowHighAccuracy
         * When set to true, provide a hint that the application would like to receive
         * the best possible results. This may result in slower response times or increased power consumption.
         * The user might also deny this capability, or the device might not be able to provide more accurate
         * results than if this option was set to false.
         */
        allowHighAccuracy: false,

        /**
         * @cfg {Number} timeout
         * The maximum number of milliseconds allowed to elapse between a location update operation
         * and the corresponding {@link #locationupdate} event being raised.  If a location was not successfully
         * acquired before the given timeout elapses (and no other internal errors have occurred in this interval),
         * then a {@link #locationerror} event will be raised indicating a timeout as the cause.<br/>
         * Note that the time that is spent obtaining the user permission is <b>not</b> included in the period
         * covered by the timeout.  The timeout attribute only applies to the location acquisition operation.<br/>
         * In the case of calling updateLocation, the {@link #locationerror} event will be raised only once.<br/>
         * If {@link #autoUpdate} is set to true, the {@link #locationerror} event could be raised repeatedly.
         * The first timeout is relative to the moment {@link #autoUpdate} was set to true
         * (or this {@link Ext.util.GeoLocation} was initialized with the {@link #autoUpdate} config option set to true).
         * Subsequent timeouts are relative to the moment when the device determines that it's position has changed.
         */

        timeout: Infinity,

        /**
         * @cfg {Number} maximumAge
         * This option indicates that the application is willing to accept cached location information whose age
         * is no greater than the specified time in milliseconds. If maximumAge is set to 0, an attempt to retrieve
         * new location information is made immediately.<br/>
         * Setting the maximumAge to Infinity returns a cached position regardless of its age.<br/>
         * If the device does not have cached location information available whose age is no
         * greater than the specified maximumAge, then it must acquire new location information.<br/>
         * For example, if location information no older than 10 minutes is required, set this property to 600000.
         */
        maximumAge: 0,

        // @private
        provider : undefined
    },

    updateMaximumAge: function() {
        if (this.watchOperation) {
            this.updateWatchOperation();
        }
    },

    updateTimeout: function() {
        if (this.watchOperation) {
            this.updateWatchOperation();
        }
    },

    updateAllowHighAccuracy: function() {
        if (this.watchOperation) {
            this.updateWatchOperation();
        }
    },

    applyProvider: function(config) {
        if (Ext.feature.has.Geolocation) {
            if (!config) {
                if (navigator && navigator.geolocation) {
                    config = navigator.geolocation;
                }
                else if (window.google) {
                    config = google.gears.factory.create('beta.geolocation');
                }
            }
        }
        else {
            this.fireEvent('locationerror', this, false, false, true, 'This device does not support Geolocation.');
        }
        return config;
    },

    updateAutoUpdate: function(newAutoUpdate, oldAutoUpdate) {
        var me = this,
            provider = me.getProvider();

        if (oldAutoUpdate && provider) {
            clearInterval(me.watchOperationId);
            me.watchOperationId = null;
        }

        if (newAutoUpdate) {
            if (!provider) {
                me.fireEvent('locationerror', me, false, false, true, null);
                return;
            }

            try {
                me.updateWatchOperation();
            }
            catch(e) {
                me.fireEvent('locationerror', me, false, false, true, e.message);
            }
        }
    },

    // @private
    updateWatchOperation: function() {
        var me = this,
            provider = me.getProvider();

        // The native watchPosition method is currently broken in iOS5...

        if (me.watchOperationId) {
            clearInterval(me.watchOperationId);
        }

        function pollPosition() {
            provider.getCurrentPosition(
                Ext.bind(me.fireUpdate, me),
                Ext.bind(me.fireError, me),
                me.parseOptions()
            );
        }

        pollPosition();
        me.watchOperationId = setInterval(pollPosition, 10000);
    },

    /**
     * Executes a onetime location update operation,
     * raising either a {@link #locationupdate} or {@link #locationerror} event.<br/>
     * Does not interfere with or restart ongoing location monitoring.
     * @param {Function} callback
     * A callback method to be called when the location retrieval has been completed.<br/>
     * Will be called on both success and failure.<br/>
     * The method will be passed one parameter, {@link Ext.util.GeoLocation} (<b>this</b> reference),
     * set to null on failure.
     * <pre><code>
     geo.updateLocation(function (geo) {
     alert('Latitude: ' + (geo != null ? geo.latitude : 'failed'));
     });
     </code></pre>
     * @param {Object} scope (optional)
     * (optional) The scope (<b>this</b> reference) in which the handler function is executed.
     * <b>If omitted, defaults to the object which fired the event.</b>
     * <!--positonOptions undocumented param, see W3C spec-->
     */
    updateLocation: function(callback, scope, positionOptions) {
        var me = this,
            provider = me.getProvider();

        var failFunction = function(message, error) {
            if (error) {
                me.fireError(error);
            }
            else {
                me.fireEvent('locationerror', me, false, false, true, message);
            }
            if (callback) {
                callback.call(scope || me, null, me); //last parameter for legacy purposes
            }
        };

        if (!provider) {
            failFunction(null);
            return;
        }

        try {
            provider.getCurrentPosition(
                //success callback
                function(position) {
                    me.fireUpdate(position);
                    if (callback) {
                        callback.call(scope || me, me, me); //last parameter for legacy purposes
                    }
                },
                //error callback
                function(error) {
                    failFunction(null, error);
                },
                positionOptions || me.parseOptions()
            );
        }
        catch(e) {
            failFunction(e.message);
        }
    },

    // @private
    fireUpdate: function(position) {
        var me = this,
            coords = position.coords;

        me.setConfig({
            timestamp: position.timestamp,
            latitude: coords.latitude,
            longitude: coords.longitude,
            accuracy: coords.accuracy,
            altitude: coords.altitude,
            altitudeAccuracy: coords.altitudeAccuracy,
            heading: coords.heading,
            speed: coords.speed
        });

        me.fireEvent('locationupdate', me);
    },

    // @private
    fireError: function(error) {
        var errorCode = error.code;
        this.fireEvent('locationerror', this,
            errorCode == error.TIMEOUT,
            errorCode == error.PERMISSION_DENIED,
            errorCode == error.POSITION_UNAVAILABLE,
            error.message == undefined ? null : error.message
        );
    },

    // @private
    parseOptions: function() {
        var timeout = this.getTimeout(),
            ret = {
                maximumAge: this.getMaximumAge(),
                allowHighAccuracy: this.getAllowHighAccuracy()
            };

        //Google doesn't like Infinity
        if (timeout !== Infinity) {
            ret.timeout = timeout;
        }
        return ret;
    },

    destroy : function() {
        this.updateAutoUpdate(null);
    }
});

/**
 * @private
 */
Ext.define('Ext.util.Grouper', {

    /* Begin Definitions */

    extend: 'Ext.util.Sorter',

    isGrouper: true,
    
    config: {
        groupFn: null,

        /**
         * @cfg {String} sortProperty You can define this configuration if you want the groups to be sorted
         * on something other then the group string returned by the groupFn.
         * @param item1
         * @param item2
         */
        sortProperty: null,

        /**
         * @cfg
         * Grouper has a custom sorterFn that cannot be overriden by the user. If a property has been defined
         * on this grouper, we use the default sorterFn, else we sort based on the returned group string.
         * @param item1
         * @param item2
         * @private
         * @hide
         */
        sorterFn: function(item1, item2) {
            var property = this.getSortProperty(),
                groupFn, group1, group2, modifier;

            groupFn = this.getGroupFn();
            group1 = groupFn.call(this, item1);
            group2 = groupFn.call(this, item2);

            if (property) {
                if (group1 !== group2) {
                    return this.defaultSortFn.call(this, item1, item2);
                } else {
                    return 0;
                }
            }
            return (group1 > group2) ? 1 : ((group1 < group2) ? -1 : 0);
        }
    },

    /**
     * @private
     * Basic default sorter function that just compares the defined property of each object
     */
    defaultSortFn: function(item1, item2) {
        var me = this,
            transform = me._transform,
            root = me._root,
            value1, value2,
            property = me._sortProperty;

        if (root !== null) {
            item1 = item1[root];
            item2 = item2[root];
        }

        value1 = item1[property];
        value2 = item2[property];

        if (transform) {
            value1 = transform(value1);
            value2 = transform(value2);
        }

        return value1 > value2 ? 1 : (value1 < value2 ? -1 : 0);
    },

    updateProperty: function(property) {
        this.setGroupFn(this.standardGroupFn);
    },

    standardGroupFn: function(item) {
        var root = this.getRoot(),
            property = this.getProperty(),
            data = item;

        if (root) {
            data = item[root];
        }

        return data[property];
    },

    getGroupString: function(item) {
        var group = this.getGroupFn().call(this, item);
        return typeof group != 'undefined' ? group.toString() : '';
    }
});
/**
Represents a collection of a set of key and value pairs. Each key in the HashMap must be unique, the same 
key cannot exist twice. Access to items is provided via the key only. Sample usage:

    var map = Ext.create('Ext.util.HashMap');
    map.add('key1', 1);
    map.add('key2', 2);
    map.add('key3', 3);

    map.each(function(key, value, length){
        console.log(key, value, length);
    });

The HashMap is an unordered class, there is no guarantee when iterating over the items that they will be in 
any particular order. If this is required, then use a {@link Ext.util.MixedCollection}.

 */
Ext.define('Ext.util.HashMap', {
    mixins: {
        observable: 'Ext.util.Observable'
    },
    
    /**
     * @cfg {Function} keyFn
     * A function that is used to retrieve a default key for a passed object.
     * A default is provided that returns the **id** property on the object.
     * This function is only used if the add method is called with a single argument.
     */

    /**
     * Creates new HashMap.
     * @param {Object} config The configuration options
     */
    constructor: function(config) {
        /**
         * @event add
         * Fires when a new item is added to the hash
         * @param {Ext.util.HashMap} this.
         * @param {String} key The key of the added item.
         * @param {Object} value The value of the added item.
         */
        /**
         * @event clear
         * Fires when the hash is cleared.
         * @param {Ext.util.HashMap} this.
         */
        /**
         * @event remove
         * Fires when an item is removed from the hash.
         * @param {Ext.util.HashMap} this.
         * @param {String} key The key of the removed item.
         * @param {Object} value The value of the removed item.
         */
        /**
         * @event replace
         * Fires when an item is replaced in the hash.
         * @param {Ext.util.HashMap} this.
         * @param {String} key The key of the replaced item.
         * @param {Object} value The new value for the item.
         * @param {Object} old The old value for the item.
         */

        this.callParent();

        this.mixins.observable.constructor.call(this);

        this.clear(true);
    },

    /**
     * Gets the number of items in the hash.
     * @return {Number} The number of items in the hash.
     */
    getCount: function() {
        return this.length;
    },

    /**
     * Implementation for being able to extract the key from an object if only
     * a single argument is passed.
     * @private
     * @param {String} key The key
     * @param {Object} value The value
     * @return {Array} [key, value]
     */
    getData: function(key, value) {
        // if we have no value, it means we need to get the key from the object
        if (value === undefined) {
            value = key;
            key = this.getKey(value);
        }

        return [key, value];
    },

    /**
     * Extracts the key from an object. This is a default implementation, it may be overridden
     * @private
     * @param {Object} o The object to get the key from
     * @return {String} The key to use.
     */
    getKey: function(o) {
        return o.id;
    },

    /**
     * Add a new item to the hash. An exception will be thrown if the key already exists.
     * @param {String} key The key of the new item.
     * @param {Object} value The value of the new item.
     * @return {Object} The value of the new item added.
     */
    add: function(key, value) {
        var me = this,
            data;

        if (me.containsKey(key)) {
            throw new Error('This key already exists in the HashMap');
        }

        data = this.getData(key, value);
        key = data[0];
        value = data[1];
        me.map[key] = value;
        ++me.length;
        me.fireEvent('add', me, key, value);
        return value;
    },

    /**
     * Replaces an item in the hash. If the key doesn't exist, the
     * {@link #method-add} method will be used.
     * @param {String} key The key of the item.
     * @param {Object} value The new value for the item.
     * @return {Object} The new value of the item.
     */
    replace: function(key, value) {
        var me = this,
            map = me.map,
            old;

        if (!me.containsKey(key)) {
            me.add(key, value);
        }
        old = map[key];
        map[key] = value;
        me.fireEvent('replace', me, key, value, old);
        return value;
    },

    /**
     * Remove an item from the hash.
     * @param {Object} o The value of the item to remove.
     * @return {Boolean} True if the item was successfully removed.
     */
    remove: function(o) {
        var key = this.findKey(o);
        if (key !== undefined) {
            return this.removeByKey(key);
        }
        return false;
    },

    /**
     * Remove an item from the hash.
     * @param {String} key The key to remove.
     * @return {Boolean} True if the item was successfully removed.
     */
    removeByKey: function(key) {
        var me = this,
            value;

        if (me.containsKey(key)) {
            value = me.map[key];
            delete me.map[key];
            --me.length;
            me.fireEvent('remove', me, key, value);
            return true;
        }
        return false;
    },

    /**
     * Retrieves an item with a particular key.
     * @param {String} key The key to lookup.
     * @return {Object} The value at that key. If it doesn't exist, <tt>undefined</tt> is returned.
     */
    get: function(key) {
        return this.map[key];
    },

    /**
     * Removes all items from the hash.
     * @return {Ext.util.HashMap} this
     */
    clear: function(/* private */ initial) {
        var me = this;
        me.map = {};
        me.length = 0;
        if (initial !== true) {
            me.fireEvent('clear', me);
        }
        return me;
    },

    /**
     * Checks whether a key exists in the hash.
     * @param {String} key The key to check for.
     * @return {Boolean} True if they key exists in the hash.
     */
    containsKey: function(key) {
        return this.map[key] !== undefined;
    },

    /**
     * Checks whether a value exists in the hash.
     * @param {Object} value The value to check for.
     * @return {Boolean} True if the value exists in the dictionary.
     */
    contains: function(value) {
        return this.containsKey(this.findKey(value));
    },

    /**
     * Return all of the keys in the hash.
     * @return {Array} An array of keys.
     */
    getKeys: function() {
        return this.getArray(true);
    },

    /**
     * Return all of the values in the hash.
     * @return {Array} An array of values.
     */
    getValues: function() {
        return this.getArray(false);
    },

    /**
     * Gets either the keys/values in an array from the hash.
     * @private
     * @param {Boolean} isKey True to extract the keys, otherwise, the value
     * @return {Array} An array of either keys/values from the hash.
     */
    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;
    },

    /**
     * Executes the specified function once for each item in the hash.
     * Returning false from the function will cease iteration.
     *
     * The paramaters passed to the function are:
     * <div class="mdetail-params"><ul>
     * <li><b>key</b> : String<p class="sub-desc">The key of the item</p></li>
     * <li><b>value</b> : Number<p class="sub-desc">The value of the item</p></li>
     * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the hash</p></li>
     * </ul></div>
     * @param {Function} fn The function to execute.
     * @param {Object} scope The scope to execute in. Defaults to <tt>this</tt>.
     * @return {Ext.util.HashMap} this
     */
    each: function(fn, scope) {
        // copy items so they may be removed during iteration.
        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;
    },

    /**
     * Performs a shallow copy on this hash.
     * @return {Ext.util.HashMap} The new hash object.
     */
    clone: function() {
        var hash = new Ext.util.HashMap(),
            map = this.map,
            key;

        hash.suspendEvents();
        for (key in map) {
            if (map.hasOwnProperty(key)) {
                hash.add(key, map[key]);
            }
        }
        hash.resumeEvents();
        return hash;
    },

    /**
     * @private
     * Find the key for a value.
     * @param {Object} value The value to find.
     * @return {Object} The value of the item. Returns <tt>undefined</tt> if not found.
     */
    findKey: function(value) {
        var key,
            map = this.map;

        for (key in map) {
            if (map.hasOwnProperty(key) && map[key] === value) {
                return key;
            }
        }
        return undefined;
    }
});
/**
 * @private
 */
Ext.define('Ext.AbstractManager', {

    /* Begin Definitions */

    requires: ['Ext.util.HashMap'],

    /* End Definitions */

    typeName: 'type',

    constructor: function(config) {
        Ext.apply(this, config || {});

        /**
         * @property {Ext.util.HashMap} all
         * Contains all of the items currently managed
         */
        this.all = Ext.create('Ext.util.HashMap');

        this.types = {};
    },

    /**
     * Returns an item by id.
     * For additional details see {@link Ext.util.HashMap#get}.
     * @param {String} id The id of the item
     * @return {Object} The item, undefined if not found.
     */
    get : function(id) {
        return this.all.get(id);
    },

    /**
     * Registers an item to be managed
     * @param {Object} item The item to register
     */
    register: function(item) {
        this.all.add(item);
    },

    /**
     * Unregisters an item by removing it from this manager
     * @param {Object} item The item to unregister
     */
    unregister: function(item) {
        this.all.remove(item);
    },

    /**
     * Registers a new item constructor, keyed by a type key.
     * @param {String} type The mnemonic string by which the class may be looked up.
     * @param {Function} cls The new instance class.
     */
    registerType : function(type, cls) {
        this.types[type] = cls;
        cls[this.typeName] = type;
    },

    /**
     * Checks if an item type is registered.
     * @param {String} type The mnemonic string by which the class may be looked up
     * @return {Boolean} Whether the type is registered.
     */
    isRegistered : function(type){
        return this.types[type] !== undefined;
    },

    /**
     * Creates and returns an instance of whatever this manager manages, based on the supplied type and
     * config object.
     * @param {Object} config The config object
     * @param {String} defaultType If no type is discovered in the config object, we fall back to this type
     * @return {Object} The instance of whatever this manager is managing
     */
    create: function(config, defaultType) {
        var type        = config[this.typeName] || config.type || defaultType,
            Constructor = this.types[type];

        if (Constructor == undefined) {
            Ext.Error.raise("The '" + type + "' type has not been registered with this manager");
        }

        return new Constructor(config);
    },

    /**
     * Registers a function that will be called when an item with the specified id is added to the manager.
     * This will happen on instantiation.
     * @param {String} id The item id
     * @param {Function} fn The callback function. Called with a single parameter, the item.
     * @param {Object} scope The scope (this reference) in which the callback is executed.
     * Defaults to the item.
     */
    onAvailable : function(id, fn, scope){
        var all = this.all,
            item;

        if (all.containsKey(id)) {
            item = all.get(id);
            fn.call(scope || item, item);
        } else {
            all.on('add', function(map, key, item){
                if (key == id) {
                    fn.call(scope || item, item);
                    all.un('add', fn, scope);
                }
            });
        }
    },

    /**
     * Executes the specified function once for each item in the collection.
     * @param {Function} fn The function to execute.
     * @param {String} fn.key The key of the item
     * @param {Number} fn.value The value of the item
     * @param {Number} fn.length The total number of items in the collection
     * @param {Boolean} fn.return False to cease iteration.
     * @param {Object} scope The scope to execute in. Defaults to `this`.
     */
    each: function(fn, scope){
        this.all.each(fn, scope || this);
    },

    /**
     * Gets the number of items in the collection.
     * @return {Number} The number of items in the collection.
     */
    getCount: function(){
        return this.all.getCount();
    }
});

/**
 * @author Ed Spencer

The ModelManager keeps track of all {@link Ext.data.Model} types defined in your application.

__Creating Model Instances__

Model instances can be created by using the {@link Ext#create Ext.create} method. Ext.create replaces
the deprecated {@link #create Ext.ModelManager.create} method. It is also possible to create a model instance
this by using the Model type directly. The following 3 snippets are equivalent:

    Ext.define('User', {
        extend: 'Ext.data.Model',
        config: {
            fields: ['first', 'last']
        }
    });

    // method 1, create using Ext.create (recommended)
    Ext.create('User', {
        first: 'Ed',
        last: 'Spencer'
    });

    // method 2, create through the manager (deprecated)
    Ext.ModelManager.create({
        first: 'Ed',
        last: 'Spencer'
    }, 'User');

    // method 3, create on the type directly
    new User({
        first: 'Ed',
        last: 'Spencer'
    });

__Accessing Model Types__

A reference to a Model type can be obtained by using the {@link #getModel} function. Since models types
are normal classes, you can access the type directly. The following snippets are equivalent:

    Ext.define('User', {
        extend: 'Ext.data.Model',
        config: {
            fields: ['first', 'last']
        }
    });

    // method 1, access model type through the manager
    var UserType = Ext.ModelManager.getModel('User');

    // method 2, reference the type directly
    var UserType = User;

 * @markdown
 */
Ext.define('Ext.data.ModelManager', {
    extend: 'Ext.AbstractManager',
    alternateClassName: ['Ext.ModelMgr', 'Ext.ModelManager'],

    singleton: true,

    /**
     * @property defaultProxyType
     * @removed 2.0.0
     */

    /**
     * @property associationStack
     * @removed 2.0.0
     */

    modelNamespace: null,

    /**
     * Registers a model definition. All model plugins marked with isDefault: true are bootstrapped
     * immediately, as are any addition plugins defined in the model config.
     */
    registerType: function(name, config) {
        var proto = config.prototype,
            model;

        if (proto && proto.isModel) {
            // registering an already defined model
            model = config;
        } else {
            config = {
                extend: config.extend || 'Ext.data.Model',
                config: config
            };
            model = Ext.define(name, config);
        }
        this.types[name] = model;
        return model;
    },

    onModelDefined: Ext.emptyFn,

    // /**
    //  * @private
    //  * Private callback called whenever a model has just been defined. This sets up any associations
    //  * that were waiting for the given model to be defined
    //  * @param {Function} model The model that was just created
    //  */
    // 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);
    //     }
    // },
    //
    // /**
    //  * Registers an association where one of the models defined doesn't exist yet.
    //  * The ModelManager will check when new models are registered if it can link them
    //  * together
    //  * @private
    //  * @param {Ext.data.association.Association} association The association
    //  */
    // registerDeferredAssociation: function(association){
    //     this.associationStack.push(association);
    // },

    /**
     * Returns the {@link Ext.data.Model} for a given model name
     * @param {String/Object} id The id of the model or the model instance.
     * @return {Ext.data.Model} a model class.
     */
    getModel: function(id) {
        var model = id;
        if (typeof model == 'string') {
            model = this.types[model];
            if (!model && this.modelNamespace) {
                model = this.types[this.modelNamespace + '.' + model];
            }
        }
        return model;
    },

    /**
     * Creates a new instance of a Model using the given data.
     *
     * This method is deprecated.  Use {@link Ext#create Ext.create} instead.  For example:
     *
     *     Ext.create('User', {
     *         first: 'Ed',
     *         last: 'Spencer'
     *     });
     *
     * @param {Object} data Data to initialize the Model's fields with
     * @param {String} name The name of the model to create
     * @param {Number} id (Optional) unique id of the Model instance (see {@link Ext.data.Model})
     */
    create: function(config, name, id) {
        var con = typeof name == 'function' ? name : this.types[name || config.name];
        return new con(config, id);
    }
}, function() {

    /**
     * Old way for creating Model classes.  Instead use:
     *
     *     Ext.define("MyModel", {
     *         extend: "Ext.data.Model",
     *         fields: []
     *     });
     *
     * @param {String} name Name of the Model class.
     * @param {Object} config A configuration object for the Model you wish to create.
     * @return {Ext.data.Model} The newly registered Model
     * @member Ext
     * @deprecated 2.0.0 Please use {@link Ext#define} instead.
     */
    Ext.regModel = function() {
        Ext.Logger.deprecate('Ext.regModel has been deprecated. Models can now be created by ' +
            'extending Ext.data.Model: Ext.define("MyModel", {extend: "Ext.data.Model", fields: []});.');
        return this.ModelManager.registerType.apply(this.ModelManager, arguments);
    };
});

/**
 * @class Ext.data.NodeInterface
 * This class is meant to be used as a set of methods that are applied to the prototype of a
 * Record to decorate it with a Node API. This means that models used in conjunction with a tree
 * will have all of the tree related methods available on the model. In general this class will
 * not be used directly by the developer. This class also creates extra fields on the model if
 * they do not exist, to help maintain the tree state and UI. These fields are:
 * <ul>
 * <li>parentId</li>
 * <li>index</li>
 * <li>depth</li>
 * <li>expanded</li>
 * <li>expandable</li>
 * <li>checked</li>
 * <li>leaf</li>
 * <li>cls</li>
 * <li>iconCls</li>
 * <li>root</li>
 * <li>isLast</li>
 * <li>isFirst</li>
 * <li>allowDrop</li>
 * <li>allowDrag</li>
 * <li>loaded</li>
 * <li>loading</li>
 * <li>href</li>
 * <li>hrefTarget</li>
 * <li>qtip</li>
 * <li>qtitle</li>
 * </ul>
 *
 */
Ext.define('Ext.data.NodeInterface', {
    requires: ['Ext.data.Field', 'Ext.data.ModelManager'],

    alternateClassName: 'Ext.data.Node',

    /**
     * @property nextSibling
     * A reference to this node's next sibling node. `null` if this node does not have a next sibling.
     */

    /**
     * @property previousSibling
     * A reference to this node's previous sibling node. `null` if this node does not have a previous sibling.
     */

    /**
     * @property parentNode
     * A reference to this node's parent node. `null` if this node is the root node.
     */

    /**
     * @property lastChild
     * A reference to this node's last child node. `null` if this node has no children.
     */

    /**
     * @property firstChild
     * A reference to this node's first child node. `null` if this node has no children.
     */

    /**
     * @property childNodes
     * An array of this nodes children.  Array will be empty if this node has no chidren.
     */

    statics: {
        /**
         * This method allows you to decorate a Record's prototype to implement the NodeInterface.
         * This adds a set of methods, new events, new properties and new fields on every Record
         * with the same Model as the passed Record.
         * @param {Ext.data.Model} record The Record you want to decorate the prototype of.
         * @static
         */
        decorate: function(record) {
            if (!record.isNode) {
                // Apply the methods and fields to the prototype
                var mgr = Ext.data.ModelManager,
                    modelName = record.modelName,
                    modelClass = mgr.getModel(modelName),
                    newFields = [],
                    i, newField, len;

                // Start by adding the NodeInterface methods to the Model's prototype
                modelClass.override(this.getPrototypeBody());

                newFields = this.applyFields(modelClass, [
                    {name: 'parentId',   type: 'string',  defaultValue: null},
                    {name: 'index',      type: 'int',     defaultValue: 0},
                    {name: 'depth',      type: 'int',     defaultValue: 0},
                    {name: 'expanded',   type: 'bool',    defaultValue: false, persist: false},
                    {name: 'expandable', type: 'bool',    defaultValue: true, persist: false},
                    {name: 'checked',    type: 'auto',    defaultValue: null},
                    {name: 'leaf',       type: 'bool',    defaultValue: false, persist: false},
                    {name: 'cls',        type: 'string',  defaultValue: null, persist: false},
                    {name: 'iconCls',    type: 'string',  defaultValue: null, persist: false},
                    {name: 'root',       type: 'boolean', defaultValue: false, persist: false},
                    {name: 'isLast',     type: 'boolean', defaultValue: false, persist: false},
                    {name: 'isFirst',    type: 'boolean', defaultValue: false, persist: false},
                    {name: 'allowDrop',  type: 'boolean', defaultValue: true, persist: false},
                    {name: 'allowDrag',  type: 'boolean', defaultValue: true, persist: false},
                    {name: 'loaded',     type: 'boolean', defaultValue: false, persist: false},
                    {name: 'loading',    type: 'boolean', defaultValue: false, persist: false},
                    {name: 'href',       type: 'string',  defaultValue: null, persist: false},
                    {name: 'hrefTarget', type: 'string',  defaultValue: null, persist: false},
                    {name: 'qtip',       type: 'string',  defaultValue: null, persist: false},
                    {name: 'qtitle',     type: 'string',  defaultValue: null, persist: false}
                ]);

                len = newFields.length;

                // We set a dirty flag on the fields collection of the model. Any reader that
                // will read in data for this model will update their extractor functions.
                modelClass.getFields().isDirty = true;

                // Set default values
                for (i = 0; i < len; ++i) {
                    newField = newFields[i];
                    if (record.get(newField.getName()) === undefined) {
                        record.data[newField.getName()] = newField.getDefaultValue();
                    }
                }
            }

            if (!record.isDecorated) {
                record.isDecorated = true;

                Ext.applyIf(record, {
                    firstChild: null,
                    lastChild: null,
                    parentNode: null,
                    previousSibling: null,
                    nextSibling: null,
                    childNodes: []
                });

                record.enableBubble([
                    /**
                     * @event append
                     * Fires when a new child node is appended
                     * @param {Ext.data.NodeInterface} this This node
                     * @param {Ext.data.NodeInterface} node The newly appended node
                     * @param {Number} index The index of the newly appended node
                     */
                    "append",

                    /**
                     * @event remove
                     * Fires when a child node is removed
                     * @param {Ext.data.NodeInterface} this This node
                     * @param {Ext.data.NodeInterface} node The removed node
                     */
                    "remove",

                    /**
                     * @event move
                     * Fires when this node is moved to a new location in the tree
                     * @param {Ext.data.NodeInterface} this This node
                     * @param {Ext.data.NodeInterface} oldParent The old parent of this node
                     * @param {Ext.data.NodeInterface} newParent The new parent of this node
                     * @param {Number} index The index it was moved to
                     */
                    "move",

                    /**
                     * @event insert
                     * Fires when a new child node is inserted.
                     * @param {Ext.data.NodeInterface} this This node
                     * @param {Ext.data.NodeInterface} node The child node inserted
                     * @param {Ext.data.NodeInterface} refNode The child node the node was inserted before
                     */
                    "insert",

                    /**
                     * @event beforeappend
                     * Fires before a new child is appended, return false to cancel the append.
                     * @param {Ext.data.NodeInterface} this This node
                     * @param {Ext.data.NodeInterface} node The child node to be appended
                     */
                    "beforeappend",

                    /**
                     * @event beforeremove
                     * Fires before a child is removed, return false to cancel the remove.
                     * @param {Ext.data.NodeInterface} this This node
                     * @param {Ext.data.NodeInterface} node The child node to be removed
                     */
                    "beforeremove",

                    /**
                     * @event beforemove
                     * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
                     * @param {Ext.data.NodeInterface} this This node
                     * @param {Ext.data.NodeInterface} oldParent The parent of this node
                     * @param {Ext.data.NodeInterface} newParent The new parent this node is moving to
                     * @param {Number} index The index it is being moved to
                     */
                    "beforemove",

                     /**
                      * @event beforeinsert
                      * Fires before a new child is inserted, return false to cancel the insert.
                      * @param {Ext.data.NodeInterface} this This node
                      * @param {Ext.data.NodeInterface} node The child node to be inserted
                      * @param {Ext.data.NodeInterface} refNode The child node the node is being inserted before
                      */
                    "beforeinsert",

                    /**
                     * @event expand
                     * Fires when this node is expanded.
                     * @param {Ext.data.NodeInterface} this The expanding node
                     */
                    "expand",

                    /**
                     * @event collapse
                     * Fires when this node is collapsed.
                     * @param {Ext.data.NodeInterface} this The collapsing node
                     */
                    "collapse",

                    /**
                     * @event beforeexpand
                     * Fires before this node is expanded.
                     * @param {Ext.data.NodeInterface} this The expanding node
                     */
                    "beforeexpand",

                    /**
                     * @event beforecollapse
                     * Fires before this node is collapsed.
                     * @param {Ext.data.NodeInterface} this The collapsing node
                     */
                    "beforecollapse",

                    /**
                     * @event sort
                     * Fires when this node's childNodes are sorted.
                     * @param {Ext.data.NodeInterface} this This node.
                     * @param {Ext.data.NodeInterface[]} childNodes The childNodes of this node.
                     */
                    "sort",

                    'load'
                ]);
            }

            return record;
        },

        applyFields: function(modelClass, addFields) {
            var modelPrototype = modelClass.prototype,
                fields = modelPrototype.fields,
                keys = fields.keys,
                ln = addFields.length,
                addField, i,
                newFields = [];

            for (i = 0; i < ln; i++) {
                addField = addFields[i];
                if (!Ext.Array.contains(keys, addField.name)) {
                    addField = Ext.create('Ext.data.Field', addField);

                    newFields.push(addField);
                    fields.add(addField);
                }
            }

            return newFields;
        },

        getPrototypeBody: function() {
            return {
                isNode: true,

                /**
                 * Ensures that the passed object is an instance of a Record with the NodeInterface applied
                 * @return {Boolean}
                 * @private
                 */
                createNode: function(node) {
                    if (Ext.isObject(node) && !node.isModel) {
                        node = Ext.data.ModelManager.create(node, this.modelName);
                    }
                    // Make sure the node implements the node interface
                    return Ext.data.NodeInterface.decorate(node);
                },

                /**
                 * Returns true if this node is a leaf
                 * @return {Boolean}
                 */
                isLeaf : function() {
                    return this.get('leaf') === true;
                },

                /**
                 * Sets the first child of this node
                 * @private
                 * @param {Ext.data.NodeInterface} node
                 */
                setFirstChild : function(node) {
                    this.firstChild = node;
                },

                /**
                 * Sets the last child of this node
                 * @private
                 * @param {Ext.data.NodeInterface} node
                 */
                setLastChild : function(node) {
                    this.lastChild = node;
                },

                /**
                 * Updates general data of this node like isFirst, isLast, depth. This
                 * method is internally called after a node is moved. This shouldn't
                 * have to be called by the developer unless they are creating custom
                 * Tree plugins.
                 * @return {Boolean}
                 */
                updateInfo: function(silent) {
                    var me = this,
                        parentNode = me.parentNode,
                        isFirst = (!parentNode ? true : parentNode.firstChild == me),
                        isLast = (!parentNode ? true : parentNode.lastChild == me),
                        depth = 0,
                        parent = me,
                        children = me.childNodes,
                        ln = children.length,
                        i;

                    while (parent.parentNode) {
                        ++depth;
                        parent = parent.parentNode;
                    }

                    me.beginEdit();
                    me.set({
                        isFirst: isFirst,
                        isLast: isLast,
                        depth: depth,
                        index: parentNode ? parentNode.indexOf(me) : 0,
                        parentId: parentNode ? parentNode.getId() : null
                    });
                    me.endEdit(silent);
                    if (silent) {
                        me.commit(silent);
                    }

                    for (i = 0; i < ln; i++) {
                        children[i].updateInfo(silent);
                    }
                },

                /**
                 * Returns true if this node is the last child of its parent
                 * @return {Boolean}
                 */
                isLast : function() {
                   return this.get('isLast');
                },

                /**
                 * Returns true if this node is the first child of its parent
                 * @return {Boolean}
                 */
                isFirst : function() {
                   return this.get('isFirst');
                },

                /**
                 * Returns true if this node has one or more child nodes, else false.
                 * @return {Boolean}
                 */
                hasChildNodes : function() {
                    return !this.isLeaf() && this.childNodes.length > 0;
                },

                /**
                 * Returns true if this node has one or more child nodes, or if the <tt>expandable</tt>
                 * node attribute is explicitly specified as true, otherwise returns false.
                 * @return {Boolean}
                 */
                isExpandable : function() {
                    var me = this;

                    if (me.get('expandable')) {
                        return !(me.isLeaf() || (me.isLoaded() && !me.hasChildNodes()));
                    }
                    return false;
                },

                /**
                 * <p>Insert node(s) as the last child node of this node.</p>
                 * <p>If the node was previously a child node of another parent node, it will be removed from that node first.</p>
                 * @param {Ext.data.NodeInterface/Ext.data.NodeInterface[]} node The node or Array of nodes to append
                 * @return {Ext.data.NodeInterface} The appended node if single append, or null if an array was passed
                 */
                appendChild : function(node, suppressEvents, suppressNodeUpdate) {
                    var me = this,
                        i, ln,
                        index,
                        oldParent,
                        ps;

                    // if passed an array or multiple args do them one by one
                    if (Ext.isArray(node)) {
                        for (i = 0, ln = node.length; i < ln; i++) {
                            me.appendChild(node[i]);
                        }
                    } else {
                        // Make sure it is a record
                        node = me.createNode(node);

                        if (suppressEvents !== true && me.fireEvent("beforeappend", me, node) === false) {
                            return false;
                        }

                        index = me.childNodes.length;
                        oldParent = node.parentNode;

                        // it's a move, make sure we move it cleanly
                        if (oldParent) {
                            if (suppressEvents !== true && node.fireEvent("beforemove", node, oldParent, me, index) === false) {
                                return false;
                            }
                            oldParent.removeChild(node, null, false, true);
                        }

                        index = me.childNodes.length;
                        if (index === 0) {
                            me.setFirstChild(node);
                        }

                        me.childNodes.push(node);
                        node.parentNode = me;
                        node.nextSibling = null;

                        me.setLastChild(node);

                        ps = me.childNodes[index - 1];
                        if (ps) {
                            node.previousSibling = ps;
                            ps.nextSibling = node;
                            ps.updateInfo(suppressNodeUpdate);
                        } else {
                            node.previousSibling = null;
                        }

                        node.updateInfo(suppressNodeUpdate);

                        // As soon as we append a child to this node, we are loaded
                        if (!me.isLoaded()) {
                            me.set('loaded', true);
                        }
                        // If this node didnt have any childnodes before, update myself
                        else if (me.childNodes.length === 1) {
                            me.set('loaded', me.isLoaded());
                        }

                        if (suppressEvents !== true) {
                            me.fireEvent("append", me, node, index);

                            if (oldParent) {
                                node.fireEvent("move", node, oldParent, me, index);
                            }
                        }

                        return node;
                    }
                },

                /**
                 * Returns the bubble target for this node
                 * @private
                 * @return {Object} The bubble target
                 */
                getBubbleTarget: function() {
                    return this.parentNode;
                },

                /**
                 * Removes a child node from this node.
                 * @param {Ext.data.NodeInterface} node The node to remove
                 * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
                 * @return {Ext.data.NodeInterface} The removed node
                 */
                removeChild : function(node, destroy, suppressEvents, suppressNodeUpdate) {
                    var me = this,
                        index = me.indexOf(node);

                    if (index == -1 || (suppressEvents !== true && me.fireEvent("beforeremove", me, node) === false)) {
                        return false;
                    }

                    // remove it from childNodes collection
                    Ext.Array.erase(me.childNodes, index, 1);

                    // update child refs
                    if (me.firstChild == node) {
                        me.setFirstChild(node.nextSibling);
                    }
                    if (me.lastChild == node) {
                        me.setLastChild(node.previousSibling);
                    }

                    if (suppressEvents !== true) {
                        me.fireEvent("remove", me, node);
                    }

                    // update siblings
                    if (node.previousSibling) {
                        node.previousSibling.nextSibling = node.nextSibling;
                        node.previousSibling.updateInfo(suppressNodeUpdate);
                    }
                    if (node.nextSibling) {
                        node.nextSibling.previousSibling = node.previousSibling;
                        node.nextSibling.updateInfo(suppressNodeUpdate);
                    }

                    // If this node suddenly doesnt have childnodes anymore, update myself
                    if (!me.childNodes.length) {
                        me.set('loaded', me.isLoaded());
                    }

                    if (destroy) {
                        node.destroy(true);
                    } else {
                        node.clear();
                    }

                    return node;
                },

                /**
                 * Creates a copy (clone) of this Node.
                 * @param {String} id (optional) A new id, defaults to this Node's id.
                 * @param {Boolean} deep (optional) <p>If passed as <code>true</code>, all child Nodes are recursively copied into the new Node.</p>
                 * <p>If omitted or false, the copy will have no child Nodes.</p>
                 * @return {Ext.data.NodeInterface} A copy of this Node.
                 */
                copy: function(newId, deep) {
                    var me = this,
                        result = me.callOverridden(arguments),
                        len = me.childNodes ? me.childNodes.length : 0,
                        i;

                    // Move child nodes across to the copy if required
                    if (deep) {
                        for (i = 0; i < len; i++) {
                            result.appendChild(me.childNodes[i].copy(true));
                        }
                    }
                    return result;
                },

                /**
                 * Clear the node.
                 * @private
                 * @param {Boolean} destroy True to destroy the node.
                 */
                clear : function(destroy) {
                    var me = this;

                    // clear any references from the node
                    me.parentNode = me.previousSibling = me.nextSibling = null;
                    if (destroy) {
                        me.firstChild = me.lastChild = null;
                    }
                },

                /**
                 * Destroys the node.
                 */
                destroy : function(silent) {
                    /*
                     * Silent is to be used in a number of cases
                     * 1) When setRoot is called.
                     * 2) When destroy on the tree is called
                     * 3) For destroying child nodes on a node
                     */
                    var me = this,
                        options = me.destroyOptions;

                    if (silent === true) {
                        me.clear(true);
                        Ext.each(me.childNodes, function(n) {
                            n.destroy(true);
                        });
                        me.childNodes = null;
                        delete me.destroyOptions;
                        me.callOverridden([options]);
                    } else {
                        me.destroyOptions = silent;
                        // overridden method will be called, since remove will end up calling destroy(true);
                        me.remove(true);
                    }
                },

                /**
                 * Inserts the first node before the second node in this nodes childNodes collection.
                 * @param {Ext.data.NodeInterface} node The node to insert
                 * @param {Ext.data.NodeInterface} refNode The node to insert before (if null the node is appended)
                 * @return {Ext.data.NodeInterface} The inserted node
                 */
                insertBefore : function(node, refNode, suppressEvents) {
                    var me = this,
                        index     = me.indexOf(refNode),
                        oldParent = node.parentNode,
                        refIndex  = index,
                        ps;

                    if (!refNode) { // like standard Dom, refNode can be null for append
                        return me.appendChild(node);
                    }

                    // nothing to do
                    if (node == refNode) {
                        return false;
                    }

                    // Make sure it is a record with the NodeInterface
                    node = me.createNode(node);

                    if (suppressEvents !== true && me.fireEvent("beforeinsert", me, node, refNode) === false) {
                        return false;
                    }

                    // when moving internally, indexes will change after remove
                    if (oldParent == me && me.indexOf(node) < index) {
                        refIndex--;
                    }

                    // it's a move, make sure we move it cleanly
                    if (oldParent) {
                        if (suppressEvents !== true && node.fireEvent("beforemove", node, oldParent, me, index, refNode) === false) {
                            return false;
                        }
                        oldParent.removeChild(node);
                    }

                    if (refIndex === 0) {
                        me.setFirstChild(node);
                    }

                    Ext.Array.splice(me.childNodes, refIndex, 0, node);
                    node.parentNode = me;

                    node.nextSibling = refNode;
                    refNode.previousSibling = node;

                    ps = me.childNodes[refIndex - 1];
                    if (ps) {
                        node.previousSibling = ps;
                        ps.nextSibling = node;
                        ps.updateInfo();
                    } else {
                        node.previousSibling = null;
                    }

                    node.updateInfo();

                    if (!me.isLoaded()) {
                        me.set('loaded', true);
                    }
                    // If this node didnt have any childnodes before, update myself
                    else if (me.childNodes.length === 1) {
                        me.set('loaded', me.isLoaded());
                    }

                    if (suppressEvents !== true) {
                        me.fireEvent("insert", me, node, refNode);

                        if (oldParent) {
                            node.fireEvent("move", node, oldParent, me, refIndex, refNode);
                        }
                    }

                    return node;
                },

                /**
                 * Insert a node into this node
                 * @param {Number} index The zero-based index to insert the node at
                 * @param {Ext.data.Model} node The node to insert
                 * @return {Ext.data.Model} The record you just inserted
                 */
                insertChild: function(index, node) {
                    var sibling = this.childNodes[index];
                    if (sibling) {
                        return this.insertBefore(node, sibling);
                    }
                    else {
                        return this.appendChild(node);
                    }
                },

                /**
                 * Removes this node from its parent
                 * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
                 * @return {Ext.data.NodeInterface} this
                 */
                remove : function(destroy, suppressEvents) {
                    var parentNode = this.parentNode;

                    if (parentNode) {
                        parentNode.removeChild(this, destroy, suppressEvents, true);
                    }
                    return this;
                },

                /**
                 * Removes all child nodes from this node.
                 * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
                 * @return {Ext.data.NodeInterface} this
                 */
                removeAll : function(destroy, suppressEvents) {
                    var cn = this.childNodes,
                        n;

                    while ((n = cn[0])) {
                        this.removeChild(n, destroy, suppressEvents);
                    }
                    return this;
                },

                /**
                 * Returns the child node at the specified index.
                 * @param {Number} index
                 * @return {Ext.data.NodeInterface}
                 */
                getChildAt : function(index) {
                    return this.childNodes[index];
                },

                /**
                 * Replaces one child node in this node with another.
                 * @param {Ext.data.NodeInterface} newChild The replacement node
                 * @param {Ext.data.NodeInterface} oldChild The node to replace
                 * @return {Ext.data.NodeInterface} The replaced node
                 */
                replaceChild : function(newChild, oldChild, suppressEvents) {
                    var s = oldChild ? oldChild.nextSibling : null;

                    this.removeChild(oldChild, suppressEvents);
                    this.insertBefore(newChild, s, suppressEvents);
                    return oldChild;
                },

                /**
                 * Returns the index of a child node
                 * @param {Ext.data.NodeInterface} node
                 * @return {Number} The index of the node or -1 if it was not found
                 */
                indexOf : function(child) {
                    return Ext.Array.indexOf(this.childNodes, child);
                },

                /**
                 * Gets the hierarchical path from the root of the current node.
                 * @param {String} field (optional) The field to construct the path from. Defaults to the model idProperty.
                 * @param {String} separator (optional) A separator to use. Defaults to `'/'`.
                 * @return {String} The node path
                 */
                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);
                },

                /**
                 * Returns depth of this node (the root node has a depth of 0)
                 * @return {Number}
                 */
                getDepth : function() {
                    return this.get('depth');
                },

                /**
                 * Bubbles up the tree from this node, calling the specified function with each node. The arguments to the function
                 * will be the args provided or the current node. If the function returns false at any point,
                 * the bubble is stopped.
                 * @param {Function} fn The function to call
                 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node.
                 * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
                 */
                bubble : function(fn, scope, args) {
                    var p = this;
                    while (p) {
                        if (fn.apply(scope || p, args || [p]) === false) {
                            break;
                        }
                        p = p.parentNode;
                    }
                },


                /**
                 * Cascades down the tree from this node, calling the specified function with each node. The arguments to the function
                 * will be the args provided or the current node. If the function returns false at any point,
                 * the cascade is stopped on that branch.
                 * @param {Function} fn The function to call
                 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node.
                 * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
                 */
                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);
                        }
                    }
                },

                /**
                 * Interates the child nodes of this node, calling the specified function with each node. The arguments to the function
                 * will be the args provided or the current node. If the function returns false at any point,
                 * the iteration stops.
                 * @param {Function} fn The function to call
                 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node in the iteration.
                 * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
                 */
                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;
                        }
                    }
                },

                /**
                 * Finds the first child that has the attribute with the specified value.
                 * @param {String} attribute The attribute name
                 * @param {Object} value The value to search for
                 * @param {Boolean} deep (Optional) True to search through nodes deeper than the immediate children
                 * @return {Ext.data.NodeInterface} The found child or null if none was found
                 */
                findChild : function(attribute, value, deep) {
                    return this.findChildBy(function() {
                        return this.get(attribute) == value;
                    }, null, deep);
                },

                /**
                 * Finds the first child by a custom function. The child matches if the function passed returns <code>true</code>.
                 * @param {Function} fn A function which must return <code>true</code> if the passed Node is the required Node.
                 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Node being tested.
                 * @param {Boolean} deep (Optional) True to search through nodes deeper than the immediate children
                 * @return {Ext.data.NodeInterface} The found child or null if none was found
                 */
                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;
                },

                /**
                 * Returns true if this node is an ancestor (at any point) of the passed node.
                 * @param {Ext.data.NodeInterface} node
                 * @return {Boolean}
                 */
                contains : function(node) {
                    return node.isAncestor(this);
                },

                /**
                 * Returns true if the passed node is an ancestor (at any point) of this node.
                 * @param {Ext.data.NodeInterface} node
                 * @return {Boolean}
                 */
                isAncestor : function(node) {
                    var p = this.parentNode;
                    while (p) {
                        if (p == node) {
                            return true;
                        }
                        p = p.parentNode;
                    }
                    return false;
                },

                /**
                 * Sorts this nodes children using the supplied sort function.
                 * @param {Function} sortFn A function which, when passed two Nodes, returns -1, 0 or 1 depending upon required sort order.
                 * @param {Boolean} recursive Whether or not to apply this sort recursively
                 * @param {Boolean} suppressEvent Set to true to not fire a sort event.
                 */
                sort: function(sortFn, recursive, suppressEvent) {
                    var cs  = this.childNodes,
                        ln = cs.length,
                        i, n;

                    if (ln > 0) {
                        Ext.Array.sort(cs, sortFn);
                        for (i = 0; i < ln; i++) {
                            n = cs[i];
                            n.previousSibling = cs[i-1];
                            n.nextSibling = cs[i+1];

                            if (i === 0) {
                                this.setFirstChild(n);
                            }
                            if (i == ln - 1) {
                                this.setLastChild(n);
                            }

                            n.updateInfo(suppressEvent);

                            if (recursive && !n.isLeaf()) {
                                n.sort(sortFn, true, true);
                            }
                        }

                        this.notifyStores('afterEdit', ['sorted'], {sorted: 'sorted'});

                        if (suppressEvent !== true) {
                            this.fireEvent('sort', this, cs);
                        }
                    }
                },

                /**
                 * Returns true if this node is expaned
                 * @return {Boolean}
                 */
                isExpanded: function() {
                    return this.get('expanded');
                },

                /**
                 * Returns true if this node is loaded
                 * @return {Boolean}
                 */
                isLoaded: function() {
                    return this.get('loaded');
                },

                /**
                 * Returns true if this node is loading
                 * @return {Boolean}
                 */
                isLoading: function() {
                    return this.get('loading');
                },

                /**
                 * Returns true if this node is the root node
                 * @return {Boolean}
                 */
                isRoot: function() {
                    return !this.parentNode;
                },

                /**
                 * Returns true if this node is visible
                 * @return {Boolean}
                 */
                isVisible: function() {
                    var parent = this.parentNode;
                    while (parent) {
                        if (!parent.isExpanded()) {
                            return false;
                        }
                        parent = parent.parentNode;
                    }
                    return true;
                },

                /**
                 * Expand this node.
                 * @param {Function} recursive (Optional) True to recursively expand all the children
                 * @param {Function} callback (Optional) The function to execute once the expand completes
                 * @param {Object} scope (Optional) The scope to run the callback in
                 */
                expand: function(recursive, callback, scope) {
                    var me = this;

                    if (!me.isLeaf()) {
                        if (me.isLoading()) {
                            me.on('expand', function() {
                                me.expand(recursive, callback, scope);
                            }, me, {single: true});
                        }
                        else {
                            if (!me.isExpanded()) {
                                // The TreeStore actually listens for the beforeexpand method and checks
                                // whether we have to asynchronously load the children from the server
                                // first. Thats why we pass a callback function to the event that the
                                // store can call once it has loaded and parsed all the children.
                                me.fireAction('expand', [this], function() {
                                    me.set('expanded', true);
                                    Ext.callback(callback, scope || me, [me.childNodes]);
                                });
                            }
                            else {
                                Ext.callback(callback, scope || me, [me.childNodes]);
                            }
                        }
                    } else {
                        Ext.callback(callback, scope || me);
                    }
                },

                /**
                 * Collapse this node.
                 * @param {Function} recursive (Optional) True to recursively collapse all the children
                 * @param {Function} callback (Optional) The function to execute once the collapse completes
                 * @param {Object} scope (Optional) The scope to run the callback in
                 */
                collapse: function(recursive, callback, scope) {
                    var me = this;

                    // First we start by checking if this node is a parent
                    if (!me.isLeaf() && me.isExpanded()) {
                        this.fireAction('collapse', [me], function() {
                            me.set('expanded', false);
                            Ext.callback(callback, scope || me, [me.childNodes]);
                        });
                    } else {
                        Ext.callback(callback, scope || me, [me.childNodes]);
                    }
                }
            };
        }
    }
});

/**
 * @author Ed Spencer
 *
 * Associations enable you to express relationships between different {@link Ext.data.Model Models}. Let's say we're
 * writing an ecommerce system where Users can make Orders - there's a relationship between these Models that we can
 * express like this:
 *
 *     Ext.define('MyApp.model.User', {
 *         extend: 'Ext.data.Model',
 *
 *         config: {
 *             fields: ['id', 'name', 'email'],
 *             hasMany: {
 *                 model: 'MyApp.model.Order',
 *                 name: 'orders'
 *             }
 *         }
 *     });
 *
 *     Ext.define('MyApp.model.Order', {
 *         extend: 'Ext.data.Model',
 *
 *         config: {
 *             fields: ['id', 'user_id', 'status', 'price'],
 *             belongsTo: 'MyApp.model.User'
 *         }
 *     });
 *
 * We've set up two models - User and Order - and told them about each other. You can set up as many associations on
 * each Model as you need using the two default types - {@link Ext.data.association.HasMany hasMany} and
 * {@link Ext.data.association.BelongsTo belongsTo}. There's much more detail on the usage of each of those inside their
 * documentation pages. If you're not familiar with Models already, {@link Ext.data.Model there is plenty on those too}.
 *
 * ## Further Reading
 *
 * <ul style="list-style-type: disc; padding-left: 20px;">
 *   <li>{@link Ext.data.association.HasMany hasMany associations}</li>
 *   <li>{@link Ext.data.association.BelongsTo belongsTo associations}</li>
 *   <li>{@link Ext.data.association.HasOne hasOne associations}</li>
 *   <li>{@link Ext.data.Model using Models}</li>
 * </ul>
 *
 * ### Self-associating Models
 *
 * We can also have models that create parent/child associations between the same type. Below is an example, where
 * groups can be nested inside other groups:
 *
 *     // Server Data
 *     {
 *         "groups": {
 *             "id": 10,
 *             "parent_id": 100,
 *             "name": "Main Group",
 *             "parent_group": {
 *                 "id": 100,
 *                 "parent_id": null,
 *                 "name": "Parent Group"
 *             },
 *             "child_groups": [{
 *                 "id": 2,
 *                 "parent_id": 10,
 *                 "name": "Child Group 1"
 *             },{
 *                 "id": 3,
 *                 "parent_id": 10,
 *                 "name": "Child Group 2"
 *             },{
 *                 "id": 4,
 *                 "parent_id": 10,
 *                 "name": "Child Group 3"
 *             }]
 *         }
 *     }
 *
 *     // Client code
 *     Ext.define('MyApp.model.Group', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: ['id', 'parent_id', 'name'],
 *             proxy: {
 *                 type: 'ajax',
 *                 url: 'data.json',
 *                 reader: {
 *                     type: 'json',
 *                     root: 'groups'
 *                 }
 *             },
 *             associations: [{
 *                 type: 'hasMany',
 *                 model: 'MyApp.model.Group',
 *                 primaryKey: 'id',
 *                 foreignKey: 'parent_id',
 *                 autoLoad: true,
 *                 associationKey: 'child_groups' // read child data from child_groups
 *             }, {
 *                 type: 'belongsTo',
 *                 model: 'MyApp.model.Group',
 *                 primaryKey: 'id',
 *                 foreignKey: 'parent_id',
 *                 associationKey: 'parent_group' // read parent data from parent_group
 *             }]
 *         }
 *     });
 *
 *
 *     Ext.onReady(function(){
 *         MyApp.model.Group.load(10, {
 *             success: function(group){
 *                 console.log(group.getGroup().get('name'));
 *
 *                 group.groups().each(function(rec){
 *                     console.log(rec.get('name'));
 *                 });
 *             }
 *         });
 *
 *     });
 */
Ext.define('Ext.data.association.Association', {
    alternateClassName: 'Ext.data.Association',

    requires: ['Ext.data.ModelManager'],

    config: {
        /**
         * @cfg {Ext.data.Model/String} ownerModel The full class name or reference to the class that owns this
         * associations. This is a required configuration on every association.
         * @accessor
         */
        ownerModel: null,

        /*
         * @cfg {String} ownerName The name for the owner model. This defaults to the last part
         * of the class name of the {@link #ownerModel}.
         */
        ownerName: undefined,

        /**
         * @cfg {String} associatedModel The full class name or reference to the class that the {@link #ownerModel}
         * is being associated with. This is a required configuration on every association.
         * @accessor
         */
        associatedModel: null,

        /**
         * @cfg {String} associatedName The name for the associated model. This defaults to the last part
         * of the class name of the {@link #associatedModel}.
         * @accessor
         */
        associatedName: undefined,


        /**
         * @cfg {String} associationKey The name of the property in the data to read the association from.
         * Defaults to the name of the associated model.
         */
        associationKey: undefined,

        /**
         * @cfg {String} primaryKey The name of the primary key on the associated model. Defaults to 'id'.
         * In general this will be the {@link Ext.data.Model#idProperty} of the Model.
         */
        primaryKey: 'id',

        /**
         * @cfg {Ext.data.reader.Reader} reader A special reader to read associated data
         */
        reader: null,

        /**
         * @cfg {String} type The type configuration can be used when creating associations using a configuration object.
         * Use 'hasMany' to create a HasMany association
         * <pre><code>
    associations: [{
        type: 'hasMany',
        model: 'User'
    }]
         * </code></pre>
         */
        type: null,

        name: undefined
    },

    statics: {
        create: function(association) {
            if (!association.isAssociation) {
                if (Ext.isString(association)) {
                    association = {
                        type: association
                    };
                }
                association.type = association.type.toLowerCase();
                return Ext.factory(association, Ext.data.association.Association, null, 'association');
            }

            return association;
        }
    },

    /**
     * Creates the Association object.
     * @param {Object} config (optional) Config object.
     */
    constructor: function(config) {
        this.initConfig(config);
    },

    applyName: function(name) {
        if (!name) {
            name = this.getAssociatedName();
        }
        return name;
    },

    applyOwnerModel: function(ownerName) {
        var ownerModel = Ext.data.ModelManager.getModel(ownerName);
        if (ownerModel === undefined) {
            Ext.Logger.error('The configured ownerModel was not valid (you tried ' + ownerName + ')');
        }
        return ownerModel;
    },

    applyOwnerName: function(ownerName) {
        if (!ownerName) {
            ownerName = this.getOwnerModel().modelName;
        }
        ownerName = ownerName.slice(ownerName.lastIndexOf('.')+1);
        return ownerName;
    },

    updateOwnerModel: function(ownerModel) {
        this.setOwnerName(ownerModel.modelName);
    },

    applyAssociatedModel: function(associatedName) {
        var associatedModel = Ext.data.ModelManager.types[associatedName];
        if (associatedModel === undefined) {
            Ext.Logger.error('The configured associatedModel was not valid (you tried ' + associatedName + ')');
        }
        return associatedModel;
    },

    applyAssociatedName: function(associatedName) {
        if (!associatedName) {
            associatedName = this.getAssociatedModel().modelName;
        }
        associatedName = associatedName.slice(associatedName.lastIndexOf('.')+1);
        return associatedName;
    },

    updateAssociatedModel: function(associatedModel) {
        this.setAssociatedName(associatedModel.modelName);
    },

    applyReader: function(reader) {
        if (reader) {
            if (Ext.isString(reader)) {
                reader = {
                    type: reader
                };
            }

            if (!reader.isReader) {
                Ext.applyIf(reader, {
                    type: 'json'
                });
            }
        }

        return Ext.factory(reader, Ext.data.Reader, this.getReader(), 'reader');
    },

    updateReader: function(reader) {
        reader.setModel(this.getAssociatedModel());
    }

    // Convert old properties in data into a config object
});

/**
 * @author Ed Spencer
 * @class Ext.data.association.BelongsTo
 * @extends Ext.data.association.Association
 *
 * Represents a many to one association with another model. The owner model is expected to have
 * a foreign key which references the primary key of the associated model:
 *
 *     Ext.define('Category', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: [
 *                 { name: 'id',   type: 'int' },
 *                 { name: 'name', type: 'string' }
 *             ]
 *         }
 *     });
 *
 *     Ext.define('Product', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: [
 *                 { name: 'id',          type: 'int' },
 *                 { name: 'category_id', type: 'int' },
 *                 { name: 'name',        type: 'string' }
 *             ],
 *             // we can use the belongsTo shortcut on the model to create a belongsTo association
 *             associations: { type: 'belongsTo', model: 'Category' }
 *         }
 *     });
 *
 * In the example above we have created models for Products and Categories, and linked them together
 * by saying that each Product belongs to a Category. This automatically links each Product to a Category
 * based on the Product's category_id, and provides new functions on the Product model:
 *
 * ## Generated getter function
 *
 * The first function that is added to the owner model is a getter function:
 *
 *     var product = new Product({
 *         id: 100,
 *         category_id: 20,
 *         name: 'Sneakers'
 *     });
 *
 *     product.getCategory(function(category, operation) {
 *         // do something with the category object
 *         alert(category.get('id')); // alerts 20
 *     }, this);
 *
 * The getCategory function was created on the Product model when we defined the association. This uses the
 * Category's configured {@link Ext.data.proxy.Proxy proxy} to load the Category asynchronously, calling the provided
 * callback when it has loaded.
 *
 * The new getCategory function will also accept an object containing success, failure and callback properties
 * - callback will always be called, success will only be called if the associated model was loaded successfully
 * and failure will only be called if the associatied model could not be loaded:
 *
 *     product.getCategory({
 *         reload: true, // force a reload if the owner model is already cached
 *         callback: function(category, operation) {}, // a function that will always be called
 *         success : function(category, operation) {}, // a function that will only be called if the load succeeded
 *         failure : function(category, operation) {}, // a function that will only be called if the load did not succeed
 *         scope   : this // optionally pass in a scope object to execute the callbacks in
 *     });
 *
 * In each case above the callbacks are called with two arguments - the associated model instance and the
 * {@link Ext.data.Operation operation} object that was executed to load that instance. The Operation object is
 * useful when the instance could not be loaded.
 *
 * Once the getter has been called on the model, it will be cached if the getter is called a second time. To
 * force the model to reload, specify reload: true in the options object.
 *
 * ## Generated setter function
 *
 * The second generated function sets the associated model instance - if only a single argument is passed to
 * the setter then the following two calls are identical:
 *
 *     // this call...
 *     product.setCategory(10);
 *
 *     // is equivalent to this call:
 *     product.set('category_id', 10);
 *
 * An instance of the owner model can also be passed as a parameter.
 *
 * If we pass in a second argument, the model will be automatically saved and the second argument passed to
 * the owner model's {@link Ext.data.Model#save save} method:
 *
 *     product.setCategory(10, function(product, operation) {
 *         // the product has been saved
 *         alert(product.get('category_id')); //now alerts 10
 *     });
 *
 *     //alternative syntax:
 *     product.setCategory(10, {
 *         callback: function(product, operation), // a function that will always be called
 *         success : function(product, operation), // a function that will only be called if the load succeeded
 *         failure : function(product, operation), // a function that will only be called if the load did not succeed
 *         scope   : this //optionally pass in a scope object to execute the callbacks in
 *     })
 *
 * ## Customisation
 *
 * Associations reflect on the models they are linking to automatically set up properties such as the
 * {@link #primaryKey} and {@link #foreignKey}. These can alternatively be specified:
 *
 *     Ext.define('Product', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: [ // ...
 *             ],
 * 
 *             associations: [
 *                 { type: 'belongsTo', model: 'Category', primaryKey: 'unique_id', foreignKey: 'cat_id' }
 *             ]
 *         }
 *     });
 *
 * Here we replaced the default primary key (defaults to 'id') and foreign key (calculated as 'category_id')
 * with our own settings. Usually this will not be needed.
 */
Ext.define('Ext.data.association.BelongsTo', {
    extend: 'Ext.data.association.Association',
    alternateClassName: 'Ext.data.BelongsToAssociation',
    alias: 'association.belongsto',

    config: {
        /**
         * @cfg {String} foreignKey The name of the foreign key on the owner model that links it to the associated
         * model. Defaults to the lowercased name of the associated model plus "_id", e.g. an association with a
         * model called Product would set up a product_id foreign key.
         *
         *     Ext.define('Order', {
         *         extend: 'Ext.data.Model',
         *         fields: ['id', 'date'],
         *         hasMany: 'Product'
         *     });
         *
         *     Ext.define('Product', {
         *         extend: 'Ext.data.Model',
         *         fields: ['id', 'name', 'order_id'], // refers to the id of the order that this product belongs to
         *         belongsTo: 'Group'
         *     });
         *     var product = new Product({
         *         id: 1,
         *         name: 'Product 1',
         *         order_id: 22
         *     }, 1);
         *     product.getOrder(); // Will make a call to the server asking for order_id 22
         *
         */
        foreignKey: undefined,

        /**
         * @cfg {String} getterName The name of the getter function that will be added to the local model's prototype.
         * Defaults to 'get' + the name of the foreign model, e.g. getCategory
         */
        getterName: undefined,

        /**
         * @cfg {String} setterName The name of the setter function that will be added to the local model's prototype.
         * Defaults to 'set' + the name of the foreign model, e.g. setCategory
         */
        setterName: undefined,

        instanceName: undefined
    },

    applyForeignKey: function(foreignKey) {
        if (!foreignKey) {
            foreignKey = this.getAssociatedName().toLowerCase() + '_id';
        }
        return foreignKey;
    },

    updateForeignKey: function(foreignKey, oldForeignKey) {
        var fields = this.getOwnerModel().getFields(),
            field = fields.get(foreignKey);

        if (!field) {
            field = new Ext.data.Field({
                name: foreignKey
            });
            fields.add(field);
            fields.isDirty = true;
        }

        if (oldForeignKey) {
            field = fields.get(oldForeignKey);
            if (field) {
                fields.isDirty = true;
                fields.remove(field);
            }
        }
    },

    applyInstanceName: function(instanceName) {
        if (!instanceName) {
            instanceName = this.getAssociatedName() + 'BelongsToInstance';
        }
        return instanceName;
    },

    applyAssociationKey: function(associationKey) {
        if (!associationKey) {
            var associatedName = this.getAssociatedName();
            associationKey = associatedName[0].toLowerCase() + associatedName.slice(1);
        }
        return associationKey;
    },

    applyGetterName: function(getterName) {
        if (!getterName) {
            getterName = 'get' + this.getAssociatedName();
        }
        return getterName;
    },

    applySetterName: function(setterName) {
        if (!setterName) {
            setterName = 'set' + this.getAssociatedName();
        }
        return setterName;
    },

    updateGetterName: function(getterName, oldGetterName) {
        var ownerProto = this.getOwnerModel().prototype;
        if (oldGetterName) {
            delete ownerProto[oldGetterName];
        }
        if (getterName) {
            ownerProto[getterName] = this.createGetter();
        }
    },

    updateSetterName: function(setterName, oldSetterName) {
        var ownerProto = this.getOwnerModel().prototype;
        if (oldSetterName) {
            delete ownerProto[oldSetterName];
        }
        if (setterName) {
            ownerProto[setterName] = this.createSetter();
        }
    },

    /**
     * @private
     * Returns a setter function to be placed on the owner model's prototype
     * @return {Function} The setter function
     */
    createSetter: function() {
        var me = this,
            foreignKey = me.getForeignKey();

        //'this' refers to the Model instance inside this function
        return function(value, options, scope) {
            // If we pass in an instance, pull the id out
            if (value && value.isModel) {
                value = value.getId();
            }
            this.set(foreignKey, value);

            if (Ext.isFunction(options)) {
                options = {
                    callback: options,
                    scope: scope || this
                };
            }

            if (Ext.isObject(options)) {
                return this.save(options);
            }
        };
    },

    /**
     * @private
     * Returns a getter function to be placed on the owner model's prototype. We cache the loaded instance
     * the first time it is loaded so that subsequent calls to the getter always receive the same reference.
     * @return {Function} The getter function
     */
    createGetter: function() {
        var me              = this,
            associatedModel = me.getAssociatedModel(),
            foreignKey      = me.getForeignKey(),
            instanceName    = me.getInstanceName();

        //'this' refers to the Model instance inside this function
        return function(options, scope) {
            options = options || {};

            var model = this,
                foreignKeyId = model.get(foreignKey),
                success,
                instance,
                args;

            if (options.reload === true || model[instanceName] === undefined) {
                if (typeof options == 'function') {
                    options = {
                        callback: options,
                        scope: scope || model
                    };
                }

                // Overwrite the success handler so we can assign the current instance
                success = options.success;
                options.success = function(rec) {
                    model[instanceName] = rec;
                    if (success) {
                        success.call(this, arguments);
                    }
                };

                associatedModel.load(foreignKeyId, options);
            } else {
                instance = model[instanceName];
                args = [instance];
                scope = 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 associated data
     * @private
     * @param {Ext.data.Model} record The record we're writing to
     * @param {Ext.data.reader.Reader} reader The reader for the associated model
     * @param {Object} associationData The raw associated data
     */
    read: function(record, reader, associationData){
        record[this.getInstanceName()] = reader.read([associationData]).getRecords()[0];
    }
});

/**
 * @author Ed Spencer
 * @class Ext.data.association.HasMany
 * @extends Ext.data.association.Association
 *
 * <p>Represents a one-to-many relationship between two models. Usually created indirectly via a model definition:</p>
 *
<pre><code>
Ext.define('Product', {
    extend: 'Ext.data.Model',
    config: {
        fields: [
            {name: 'id',      type: 'int'},
            {name: 'user_id', type: 'int'},
            {name: 'name',    type: 'string'}
        ]
    }
});

Ext.define('User', {
    extend: 'Ext.data.Model',
    config: {
        fields: [
            {name: 'id',   type: 'int'},
            {name: 'name', type: 'string'}
        ],
        // we can use the hasMany shortcut on the model to create a hasMany association
        hasMany: {model: 'Product', name: 'products'}
    }
});
</pre></code>
*
 * <p>Above we created Product and User models, and linked them by saying that a User hasMany Products. This gives
 * us a new function on every User instance, in this case the function is called 'products' because that is the name
 * we specified in the association configuration above.</p>
 *
 * <p>This new function returns a specialized {@link Ext.data.Store Store} which is automatically filtered to load
 * only Products for the given model instance:</p>
 *
<pre><code>
//first, we load up a User with id of 1
var user = Ext.create('User', {id: 1, name: 'Ed'});

//the user.products function was created automatically by the association and returns a {@link Ext.data.Store Store}
//the created store is automatically scoped to the set of Products for the User with id of 1
var products = user.products();

//we still have all of the usual Store functions, for example it's easy to add a Product for this User
products.add({
    name: 'Another Product'
});

//saves the changes to the store - this automatically sets the new Product's user_id to 1 before saving
products.sync();
</code></pre>
 *
 * <p>The new Store is only instantiated the first time you call products() to conserve memory and processing time,
 * though calling products() a second time returns the same store instance.</p>
 *
 * <p><u>Custom filtering</u></p>
 *
 * <p>The Store is automatically furnished with a filter - by default this filter tells the store to only return
 * records where the associated model's foreign key matches the owner model's primary key. For example, if a User
 * with ID = 100 hasMany Products, the filter loads only Products with user_id == 100.</p>
 *
 * <p>Sometimes we want to filter by another field - for example in the case of a Twitter search application we may
 * have models for Search and Tweet:</p>
 *
<pre><code>
Ext.define('Search', {
    extend: 'Ext.data.Model',
    config: {
        fields: [
            'id', 'query'
        ],

        hasMany: {
            model: 'Tweet',
            name : 'tweets',
            filterProperty: 'query'
        }
    }
});

Ext.define('Tweet', {
    extend: 'Ext.data.Model',
    config: {
        fields: [
            'id', 'text', 'from_user'
        ]
    }
});

//returns a Store filtered by the filterProperty
var store = new Search({query: 'Sencha Touch'}).tweets();
</code></pre>
 *
 * <p>The tweets association above is filtered by the query property by setting the {@link #filterProperty}, and is
 * equivalent to this:</p>
 *
<pre><code>
var store = Ext.create('Ext.data.Store', {
    model: 'Tweet',
    filters: [
        {
            property: 'query',
            value   : 'Sencha Touch'
        }
    ]
});
</code></pre>
 */
Ext.define('Ext.data.association.HasMany', {
    extend: 'Ext.data.association.Association',
    alternateClassName: 'Ext.data.HasManyAssociation',
    requires: ['Ext.util.Inflector'],

    alias: 'association.hasmany',

    config: {
        /**
         * @cfg {String} foreignKey The name of the foreign key on the associated model that links it to the owner
         * model. Defaults to the lowercased name of the owner model plus "_id", e.g. an association with a
         * model called Group hasMany Users would create 'group_id' as the foreign key. When the remote store is loaded,
         * the store is automatically filtered so that only records with a matching foreign key are included in the
         * resulting child store. This can be overridden by specifying the {@link #filterProperty}.
         * <pre><code>
    Ext.define('Group', {
        extend: 'Ext.data.Model',
        fields: ['id', 'name'],
        hasMany: 'User'
    });

    Ext.define('User', {
        extend: 'Ext.data.Model',
        fields: ['id', 'name', 'group_id'], // refers to the id of the group that this user belongs to
        belongsTo: 'Group'
    });
         * </code></pre>
         */
        foreignKey: undefined,

        /**
         * @cfg {String} name The name of the function to create on the owner model to retrieve the child store.
         * If not specified, the pluralized name of the child model is used.
         * <pre><code>
        // This will create a users() method on any Group model instance
        Ext.define('Group', {
            extend: 'Ext.data.Model',
            fields: ['id', 'name'],
            hasMany: 'User'
        });
        var group = new Group();
        console.log(group.users());

        // The method to retrieve the users will now be getUserList
        Ext.define('Group', {
            extend: 'Ext.data.Model',
            fields: ['id', 'name'],
            hasMany: {model: 'User', name: 'getUserList'}
        });
        var group = new Group();
        console.log(group.getUserList());
         * </code></pre>
         */

        /**
         * @cfg {Object} store Optional configuration object that will be passed to the generated Store. Defaults to
         * an empty Object.
         */
        store: undefined,

        /**
         * @cfg {String} storeName Optional The name of the store by which you can reference it on this class as a property.
         */
        storeName: undefined,

        /**
         * @cfg {String} filterProperty Optionally overrides the default filter that is set up on the associated Store. If
         * this is not set, a filter is automatically created which filters the association based on the configured
         * {@link #foreignKey}. See intro docs for more details. Defaults to null.
         */
        filterProperty: null,

        /**
         * @cfg {Boolean} autoLoad True to automatically load the related store from a remote source when instantiated.
         * Defaults to <tt>false</tt>.
         */
        autoLoad: false
    },

    constructor: function(config) {
        config = config || {};

        if (config.storeConfig) {
            Ext.Logger.warn('storeConfig is deprecated on an association. Instead use the store configuration.');
            config.store = config.storeConfig;
            delete config.storeConfig;
        }

        this.callParent([config]);
    },

    applyName: function(name) {
        if (!name) {
            name = Ext.util.Inflector.pluralize(this.getAssociatedName().toLowerCase());
        }
        return name;
    },

    applyStoreName: function(name) {
        if (!name) {
            name = this.getName() + 'Store';
        }
        return name;
    },

    applyForeignKey: function(foreignKey) {
        if (!foreignKey) {
            foreignKey = this.getOwnerName().toLowerCase() + '_id';
        }
        return foreignKey;
    },

    applyAssociationKey: function(associationKey) {
        if (!associationKey) {
            var associatedName = this.getAssociatedName();
            associationKey = Ext.util.Inflector.pluralize(associatedName[0].toLowerCase() + associatedName.slice(1));
        }
        return associationKey;
    },

    updateForeignKey: function(foreignKey, oldForeignKey) {
        var fields = this.getAssociatedModel().getFields(),
            field = fields.get(foreignKey);

        if (!field) {
            field = new Ext.data.Field({
                name: foreignKey
            });
            fields.add(field);
            fields.isDirty = true;
        }

        if (oldForeignKey) {
            field = fields.get(oldForeignKey);
            if (field) {
                fields.remove(field);
                fields.isDirty = true;
            }
        }
    },

    /**
     * @private
     * Creates a function that returns an Ext.data.Store which is configured to load a set of data filtered
     * by the owner model's primary key - e.g. in a hasMany association where Group hasMany Users, this function
     * returns a Store configured to return the filtered set of a single Group's Users.
     * @return {Function} The store-generating function
     */
    applyStore: function(storeConfig) {
        var me = this,
            association     = me,
            associatedModel = me.getAssociatedModel(),
            storeName       = me.getStoreName(),
            foreignKey      = me.getForeignKey(),
            primaryKey      = me.getPrimaryKey(),
            filterProperty  = me.getFilterProperty(),
            autoLoad        = me.getAutoLoad();

        return function() {
            var me = this,
                config, filter,
                modelDefaults = {};

            if (me[storeName] === undefined) {
                if (filterProperty) {
                    filter = {
                        property  : filterProperty,
                        value     : me.get(filterProperty),
                        exactMatch: true
                    };
                } else {
                    filter = {
                        property  : foreignKey,
                        value     : me.get(primaryKey),
                        exactMatch: true
                    };
                }

                modelDefaults[foreignKey] = me.get(primaryKey);

                config = Ext.apply({}, storeConfig, {
                    model        : associatedModel,
                    filters      : [filter],
                    remoteFilter : true,
                    modelDefaults: modelDefaults
                });

                me[storeName] = Ext.create('Ext.data.Store', config);
                if (autoLoad) {
                    me[storeName].load(function(records, operation) {
                        association.updateInverseInstances(me);
                    });
                }
            }

            return me[storeName];
        };
    },

    updateStore: function(store) {
        this.getOwnerModel().prototype[this.getName()] = store;
    },

    /**
     * Read associated data
     * @private
     * @param {Ext.data.Model} record The record we're writing to
     * @param {Ext.data.reader.Reader} reader The reader for the associated model
     * @param {Object} associationData The raw associated data
     */
    read: function(record, reader, associationData) {
        var store = record[this.getName()](),
            records = reader.read(associationData).getRecords(),
            inverse;

        store.add(records);
        this.updateInverseInstances(record);
    },

    updateInverseInstances: function(record) {
        var store = record[this.getName()]();

        //now that we've added the related records to the hasMany association, set the inverse belongsTo
        //association on each of them if it exists
        inverse = this.getAssociatedModel().associations.findBy(function(assoc) {
            return assoc.getType() === 'belongsTo' && assoc.getAssociatedModel().$className === record.$className;
        });

        //if the inverse association was found, set it now on each record we've just created
        if (inverse) {
            store.each(function(associatedRecord) {
                associatedRecord[inverse.getInstanceName()] = record;
            });
        }
    }

});

/**
 * @class Ext.data.association.HasOne
 * @extends Ext.data.association.Association
 *
 * Represents a one to one association with another model. The owner model is expected to have
 * a foreign key which references the primary key of the associated model:
 *
 *     Ext.define('Person', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: [
 *                 { name: 'id', type: 'int' },
 *                 { name: 'name', type: 'string' },
 *                 { name: 'address_id', type: 'int'}
 *             ],
 *
 *             // we can use the hasOne shortcut on the model to create a hasOne association
 *             associations: { type: 'hasOne', model: 'Address' }
 *         }
 *     });
 *
 *     Ext.define('Address', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: [
 *                 { name: 'id', type: 'int' },
 *                 { name: 'number', type: 'string' },
 *                 { name: 'street', type: 'string' },
 *                 { name: 'city', type: 'string' },
 *                 { name: 'zip', type: 'string' },
 *             ]
 *         }
 *     });
 *
 * In the example above we have created models for People and Addresses, and linked them together
 * by saying that each Person has a single Address. This automatically links each Person to an Address
 * based on the Persons address_id, and provides new functions on the Person model:
 *
 * ## Generated getter function
 *
 * The first function that is added to the owner model is a getter function:
 *
 *     var person = Ext.create('Person', {
 *         id: 100,
 *         address_id: 20,
 *         name: 'John Smith'
 *     });
 *
 *     person.getAddress(function(address, operation) {
 *         // do something with the address object
 *         alert(address.get('id')); // alerts 20
 *     }, this);
 *
 * The getAddress function was created on the Person model when we defined the association. This uses the
 * Persons configured {@link Ext.data.proxy.Proxy proxy} to load the Address asynchronously, calling the provided
 * callback when it has loaded.
 *
 * The new getAddress function will also accept an object containing success, failure and callback properties
 * - callback will always be called, success will only be called if the associated model was loaded successfully
 * and failure will only be called if the associatied model could not be loaded:
 *
 *     person.getAddress({
 *         reload: true, // force a reload if the owner model is already cached
 *         callback: function(address, operation) {}, // a function that will always be called
 *         success : function(address, operation) {}, // a function that will only be called if the load succeeded
 *         failure : function(address, operation) {}, // a function that will only be called if the load did not succeed
 *         scope   : this // optionally pass in a scope object to execute the callbacks in
 *     });
 *
 * In each case above the callbacks are called with two arguments - the associated model instance and the
 * {@link Ext.data.Operation operation} object that was executed to load that instance. The Operation object is
 * useful when the instance could not be loaded.
 *
 * Once the getter has been called on the model, it will be cached if the getter is called a second time. To
 * force the model to reload, specify reload: true in the options object.
 *
 * ## Generated setter function
 *
 * The second generated function sets the associated model instance - if only a single argument is passed to
 * the setter then the following two calls are identical:
 *
 *     // this call...
 *     person.setAddress(10);
 *
 *     // is equivalent to this call:
 *     person.set('address_id', 10);
 *
 * An instance of the owner model can also be passed as a parameter.
 *
 * If we pass in a second argument, the model will be automatically saved and the second argument passed to
 * the owner model's {@link Ext.data.Model#save save} method:
 *
 *     person.setAddress(10, function(address, operation) {
 *         // the address has been saved
 *         alert(address.get('address_id')); //now alerts 10
 *     });
 *
 *     //alternative syntax:
 *     person.setAddress(10, {
 *         callback: function(address, operation), // a function that will always be called
 *         success : function(address, operation), // a function that will only be called if the load succeeded
 *         failure : function(address, operation), // a function that will only be called if the load did not succeed
 *         scope   : this //optionally pass in a scope object to execute the callbacks in
 *     })
 *
 * ## Customisation
 *
 * Associations reflect on the models they are linking to automatically set up properties such as the
 * {@link #primaryKey} and {@link #foreignKey}. These can alternatively be specified:
 *
 *     Ext.define('Person', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: [
 *                 // ...
 *             ],
 *
 *             associations: [
 *                 { type: 'hasOne', model: 'Address', primaryKey: 'unique_id', foreignKey: 'addr_id' }
 *             ]
 *         }
 *     });
 *
 * Here we replaced the default primary key (defaults to 'id') and foreign key (calculated as 'address_id')
 * with our own settings. Usually this will not be needed.
 */
Ext.define('Ext.data.association.HasOne', {
    extend: 'Ext.data.association.Association',
    alternateClassName: 'Ext.data.HasOneAssociation',

    alias: 'association.hasone',

    config: {
        /**
         * @cfg {String} foreignKey The name of the foreign key on the owner model that links it to the associated
         * model. Defaults to the lowercased name of the associated model plus "_id", e.g. an association with a
         * model called Person would set up a address_id foreign key.
         *
         *     Ext.define('Person', {
         *         extend: 'Ext.data.Model',
         *         fields: ['id', 'name', 'address_id'], // refers to the id of the address object
         *         hasOne: 'Address'
         *     });
         *
         *     Ext.define('Address', {
         *         extend: 'Ext.data.Model',
         *         fields: ['id', 'number', 'street', 'city', 'zip'],
         *         belongsTo: 'Person'
         *     });
         *     var Person = new Person({
         *         id: 1,
         *         name: 'John Smith',
         *         address_id: 13
         *     }, 1);
         *     person.getAddress(); // Will make a call to the server asking for address_id 13
         *
         */
        foreignKey: undefined,

        /**
         * @cfg {String} getterName The name of the getter function that will be added to the local model's prototype.
         * Defaults to 'get' + the name of the foreign model, e.g. getAddress
         */
        getterName: undefined,

        /**
         * @cfg {String} setterName The name of the setter function that will be added to the local model's prototype.
         * Defaults to 'set' + the name of the foreign model, e.g. setAddress
         */
        setterName: undefined,

        instanceName: undefined
    },

    applyForeignKey: function(foreignKey) {
        if (!foreignKey) {
            foreignKey = this.getAssociatedName().toLowerCase() + '_id';
        }
        return foreignKey;
    },

    updateForeignKey: function(foreignKey, oldForeignKey) {
        var fields = this.getAssociatedModel().getFields(),
            field = fields.get(foreignKey);

        if (!field) {
            field = new Ext.data.Field({
                name: foreignKey
            });
            fields.add(field);
            fields.isDirty = true;
        }

        if (oldForeignKey) {
            field = fields.get(oldForeignKey);
            if (field) {
                fields.remove(field);
                fields.isDirty = true;
            }
        }
    },

    applyInstanceName: function(instanceName) {
        if (!instanceName) {
            instanceName = this.getAssociatedName() + 'BelongsToInstance';
        }
        return instanceName;
    },

    applyAssociationKey: function(associationKey) {
        if (!associationKey) {
            var associatedName = this.getAssociatedName();
            associationKey = associatedName[0].toLowerCase() + associatedName.slice(1);
        }
        return associationKey;
    },

    applyGetterName: function(getterName) {
        if (!getterName) {
            getterName = 'get' + this.getAssociatedName();
        }
        return getterName;
    },

    applySetterName: function(setterName) {
        if (!setterName) {
            setterName = 'set' + this.getAssociatedName();
        }
        return setterName;
    },

    updateGetterName: function(getterName, oldGetterName) {
        var ownerProto = this.getOwnerModel().prototype;
        if (oldGetterName) {
            delete ownerProto[oldGetterName];
        }
        if (getterName) {
            ownerProto[getterName] = this.createGetter();
        }
    },

    updateSetterName: function(setterName, oldSetterName) {
        var ownerProto = this.getOwnerModel().prototype;
        if (oldSetterName) {
            delete ownerProto[oldSetterName];
        }
        if (setterName) {
            ownerProto[setterName] = this.createSetter();
        }
    },

    /**
     * @private
     * Returns a setter function to be placed on the owner model's prototype
     * @return {Function} The setter function
     */
    createSetter: function() {
        var me              = this,
            foreignKey      = me.getForeignKey(),
            instanceName    = me.getInstanceName(),
            associatedModel = me.getAssociatedModel();

        //'this' refers to the Model instance inside this function
        return function(value, options, scope) {
            var Model = Ext.data.Model,
                record;

            if (value && value.isModel) {
                value = value.getId();
            }

            this.set(foreignKey, value);

            record = Model.cache.get(Model.generateCacheId(associatedModel.modelName, value));
            if (record) {
                this[instanceName] = record;
            }

            if (Ext.isFunction(options)) {
                options = {
                    callback: options,
                    scope: scope || this
                };
            }

            if (Ext.isObject(options)) {
                return this.save(options);
            }
        };
    },

    /**
     * @private
     * Returns a getter function to be placed on the owner model's prototype. We cache the loaded instance
     * the first time it is loaded so that subsequent calls to the getter always receive the same reference.
     * @return {Function} The getter function
     */
    createGetter: function() {
        var me              = this,
            associatedModel = me.getAssociatedModel(),
            foreignKey      = me.getForeignKey(),
            instanceName    = me.getInstanceName();

        //'this' refers to the Model instance inside this function
        return function(options, scope) {
            options = options || {};

            var model = this,
                foreignKeyId = model.get(foreignKey),
                success, instance, args;

            if (options.reload === true || model[instanceName] === undefined) {
                if (typeof options == 'function') {
                    options = {
                        callback: options,
                        scope: scope || model
                    };
                }

                // Overwrite the success handler so we can assign the current instance
                success = options.success;
                options.success = function(rec){
                    model[instanceName] = rec;
                    if (success) {
                        success.call(this, arguments);
                    }
                };

                associatedModel.load(foreignKeyId, options);
            } else {
                instance = model[instanceName];
                args = [instance];
                scope = 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 associated data
     * @private
     * @param {Ext.data.Model} record The record we're writing to
     * @param {Ext.data.reader.Reader} reader The reader for the associated model
     * @param {Object} associationData The raw associated data
     */
    read: function(record, reader, associationData) {
        var inverse = this.getAssociatedModel().associations.findBy(function(assoc) {
            return assoc.getType() === 'belongsTo' && assoc.getAssociatedName() === record.$className;
        }), newRecord = reader.read([associationData]).getRecords()[0];

        record[this.getInstanceName()] = newRecord;

        //if the inverse association was found, set it now on each record we've just created
        if (inverse) {
            newRecord[inverse.getInstanceName()] = record;
        }
    }
});

/**
 * @class Ext.util.LineSegment
 *
 * Utility class that represents a line segment, constructed by two {@link Ext.util.Point}
 */
Ext.define('Ext.util.LineSegment', {
    requires: ['Ext.util.Point'],

    /**
     * Creates new LineSegment out of two points.
     * @param {Ext.util.Point} point1
     * @param {Ext.util.Point} point2
     */
    constructor: function(point1, point2) {
        var Point = Ext.util.Point;

        this.point1 = Point.from(point1);
        this.point2 = Point.from(point2);
    },

    /**
     * Returns the point where two lines intersect.
     * @param {Ext.util.LineSegment} lineSegment The line to intersect with.
     * @return {Ext.util.Point}
     */
    intersects: function(lineSegment) {
        var point1 = this.point1,
            point2 = this.point2,
            point3 = lineSegment.point1,
            point4 = lineSegment.point2,
            x1 = point1.x,
            x2 = point2.x,
            x3 = point3.x,
            x4 = point4.x,
            y1 = point1.y,
            y2 = point2.y,
            y3 = point3.y,
            y4 = point4.y,
            d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4),
            xi, yi;

        if (d == 0) {
            return null;
        }

        xi = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d;
        yi = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d;

        if (xi < Math.min(x1, x2) || xi > Math.max(x1, x2)
            || xi < Math.min(x3, x4) || xi > Math.max(x3, x4)
            || yi < Math.min(y1, y2) || yi > Math.max(y1, y2)
            || yi < Math.min(y3, y4) || yi > Math.max(y3, y4)) {
            return null;
        }

        return new Ext.util.Point(xi, yi);
    },

    /**
     * Returns string representation of the line. Useful for debugging.
     * @return {String} For example `Point[12,8] Point[0,0]`
     */
    toString: function() {
        return this.point1.toString() + " " + this.point2.toString();
    }
});

/**
 * @private
 */
Ext.define('Ext.util.SizeMonitor', {

    extend: 'Ext.Evented',

    config: {
        element: null,

        detectorCls: Ext.baseCSSPrefix + 'size-change-detector',

        callback: Ext.emptyFn,

        scope: null,

        args: []
    },

    constructor: function(config) {
        this.initConfig(config);

        this.doFireSizeChangeEvent = Ext.Function.bind(this.doFireSizeChangeEvent, this);

        var me = this,
            element = this.getElement().dom,
            cls = this.getDetectorCls(),
            expandDetector = Ext.Element.create({
                classList: [cls, cls + '-expand'],
                children: [{}]
            }, true),
            shrinkDetector = Ext.Element.create({
                classList: [cls, cls + '-shrink'],
                children: [{}]
            }, true),
            expandListener = function(e) {
                me.onDetectorScroll('expand', e);
            },
            shrinkListener = function(e) {
                me.onDetectorScroll('shrink', e);
            };

        element.appendChild(expandDetector);
        element.appendChild(shrinkDetector);

        expandDetector.addEventListener('scroll', expandListener, true);
        shrinkDetector.addEventListener('scroll', shrinkListener, true);

        this.detectors = {
            expand: expandDetector,
            shrink: shrinkDetector
        };

        this.position = {
            expand: {
                left: 0,
                top: 0
            },
            shrink: {
                left: 0,
                top: 0
            }
        };

        this.listeners = {
            expand: expandListener,
            shrink: shrinkListener
        };

        this.refresh();
    },

    applyElement: function(element) {
        if (element) {
            return Ext.get(element);
        }
    },

    refreshPosition: function(name) {
        var detector = this.detectors[name],
            position = this.position[name],
            left, top;

        position.left = left = detector.scrollWidth - detector.offsetWidth;
        position.top = top = detector.scrollHeight - detector.offsetHeight;

        detector.scrollLeft = left;
        detector.scrollTop = top;
    },

    refresh: function() {
        this.refreshPosition('expand');
        this.refreshPosition('shrink');
    },

    onDetectorScroll: function(name) {
        var detector = this.detectors[name],
            position = this.position[name];

        if (detector.scrollLeft !== position.left || detector.scrollTop !== position.top) {
            this.refresh();
            this.fireSizeChangeEvent();
        }
    },

    fireSizeChangeEvent: function() {
        clearTimeout(this.sizeChangeThrottleTimer);

        this.sizeChangeThrottleTimer = setTimeout(this.doFireSizeChangeEvent, 1);
    },

    doFireSizeChangeEvent: function() {
        this.getCallback().apply(this.getScope(), this.getArgs());
    },

    destroyDetector: function(name) {
        var detector = this.detectors[name],
            listener = this.listeners[name];

        detector.removeEventListener('scroll', listener, true);
        Ext.removeNode(detector);
    },

    destroy: function() {
        this.callParent(arguments);

        this.destroyDetector('expand');
        this.destroyDetector('shrink');

        delete this.listeners;
        delete this.detectors;
    }
});

/**
 * @private
 */
Ext.define('Ext.event.publisher.ComponentSize', {

    extend: 'Ext.event.publisher.Publisher',

    requires: [
        'Ext.ComponentManager',
        'Ext.util.SizeMonitor'
    ],

    targetType: 'component',

    handledEvents: ['resize'],

    constructor: function() {
        this.callParent(arguments);

        this.sizeMonitors = {};
    },

    subscribe: function(target) {
        var match = target.match(this.idSelectorRegex),
            subscribers = this.subscribers,
            sizeMonitors = this.sizeMonitors,
            dispatcher = this.dispatcher,
            targetType = this.targetType,
            component;

        if (!match) {
            return false;
        }

        if (!subscribers.hasOwnProperty(target)) {
            subscribers[target] = 0;

            dispatcher.addListener(targetType, target, 'painted', 'onComponentPainted', this, null, 'before');

            component = Ext.ComponentManager.get(match[1]);

            if (!component) {
                Ext.Logger.error("Adding a listener to the 'resize' event of a non-existing component");
            }

            sizeMonitors[target] = new Ext.util.SizeMonitor({
                element: component.element,
                callback: this.onComponentSizeChange,
                scope: this,
                args: [this, target]
            });
        }

        subscribers[target]++;
        return true;
    },

    unsubscribe: function(target, eventName, all) {
        var match = target.match(this.idSelectorRegex),
            subscribers = this.subscribers,
            dispatcher = this.dispatcher,
            targetType = this.targetType,
            sizeMonitors = this.sizeMonitors;

        if (!match) {
            return false;
        }

        if (!subscribers.hasOwnProperty(target) || (!all && --subscribers[target] > 0)) {
            return true;
        }

        sizeMonitors[target].destroy();
        delete sizeMonitors[target];

        dispatcher.removeListener(targetType, target, 'painted', 'onComponentPainted', this, 'before');

        delete subscribers[target];
        return true;
    },

    onComponentPainted: function(component) {
        var observableId = component.getObservableId(),
            sizeMonitor = this.sizeMonitors[observableId];

        sizeMonitor.refresh();
    },

    onComponentSizeChange: function(component, observableId) {
        this.dispatcher.doDispatchEvent(this.targetType, observableId, 'resize', [component]);
    }
});

/**
 * @docauthor Tommy Maintz <tommy@sencha.com>
 *
 * A mixin which allows a data component to be sorted. This is used by e.g. {@link Ext.data.Store} and {@link Ext.data.TreeStore}.
 *
 * **NOTE**: This mixin is mainly for internal library use and most users should not need to use it directly. It
 * is more likely you will want to use one of the component classes that import this mixin, such as
 * {@link Ext.data.Store} or {@link Ext.data.TreeStore}.
 */
Ext.define("Ext.util.Sortable", {
    /**
     * @property {Boolean} isSortable
     * Flag denoting that this object is sortable. Always true.
     */
    isSortable: true,
    
    /**
     * @property {String} defaultSortDirection
     * The default sort direction to use if one is not specified (defaults to "ASC")
     */
    defaultSortDirection: "ASC",
    
    requires: [
        'Ext.util.Sorter'
    ],

    /**
     * @property {String} sortRoot
     * The property in each item that contains the data to sort.
     */    
    
    /**
     * Performs initialization of this mixin. Component classes using this mixin should call this method during their
     * own initialization.
     */
    initSortable: function() {
        var me = this,
            sorters = me.sorters;
        
        /**
         * @property {Ext.util.MixedCollection} sorters
         * The collection of {@link Ext.util.Sorter Sorters} currently applied to this Store
         */
        me.sorters = Ext.create('Ext.util.AbstractMixedCollection', false, function(item) {
            return item.id || item.property;
        });
        
        if (sorters) {
            me.sorters.addAll(me.decodeSorters(sorters));
        }
    },

    /**
     * Sorts the data in the Store by one or more of its properties. Example usage:
     *
     *     //sort by a single field
     *     myStore.sort('myField', 'DESC');
     *
     *     //sorting by multiple fields
     *     myStore.sort([
     *         {
     *             property : 'age',
     *             direction: 'ASC'
     *         },
     *         {
     *             property : 'name',
     *             direction: 'DESC'
     *         }
     *     ]);
     *
     * Internally, Store converts the passed arguments into an array of {@link Ext.util.Sorter} instances, and delegates
     * the actual sorting to its internal {@link Ext.util.MixedCollection}.
     *
     * When passing a single string argument to sort, Store maintains a ASC/DESC toggler per field, so this code:
     *
     *     store.sort('myField');
     *     store.sort('myField');
     *
     * Is equivalent to this code, because Store handles the toggling automatically:
     *
     *     store.sort('myField', 'ASC');
     *     store.sort('myField', 'DESC');
     *
     * @param {String/Ext.util.Sorter[]} sorters Either a string name of one of the fields in this Store's configured
     * {@link Ext.data.Model Model}, or an array of sorter configurations.
     * @param {String} direction The overall direction to sort the data by. Defaults to "ASC".
     * @return {Ext.util.Sorter[]}
     */
    sort: function(sorters, direction, where, doSort) {
        var me = this,
            sorter, sorterFn,
            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') {
                    sorters = me.sorters.clone().items;
                    
                    me.sorters.clear();
                    me.sorters.addAll(newSorters);
                    me.sorters.addAll(sorters);
                }
                else {
                    me.sorters.addAll(newSorters);
                }
            }
            else {
                me.sorters.clear();
                me.sorters.addAll(newSorters);
            }
            
            if (doSort !== false) {
                me.onBeforeSort(newSorters);
            }
        }
        
        if (doSort !== false) {
            sorters = me.sorters.items;
            if (sorters.length) {
                //construct an amalgamated sorter function which combines all of the Sorters passed
                sorterFn = function(r1, r2) {
                    var result = sorters[0].sort(r1, r2),
                        length = sorters.length,
                        i;

                        //if we have more than one sorter, OR any additional sorter functions together
                        for (i = 1; i < length; i++) {
                            result = result || sorters[i].sort.call(this, r1, r2);
                        }

                    return result;
                };

                me.doSort(sorterFn);                
            }
        }
        
        return sorters;
    },
    
    onBeforeSort: Ext.emptyFn,
        
    /**
     * @private
     * Normalizes an array of sorter objects, ensuring that they are all Ext.util.Sorter instances
     * @param {Array} sorters The sorters array
     * @return {Array} Array of Ext.util.Sorter objects
     */
    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;
                }

                //support a function to be passed as a sorter definition
                if (typeof config == 'function') {
                    config = {
                        sorterFn: config
                    };
                }

                // ensure sortType gets pushed on if necessary
                if (fields && !config.transform) {
                    field = fields.get(config.property);
                    config.transform = field ? field.sortType : undefined;
                }
                sorters[i] = Ext.create('Ext.util.Sorter', config);
            }
        }

        return sorters;
    },
    
    getSorters: function() {
        return this.sorters.items;
    }
});
/**
 * Represents a collection of a set of key and value pairs. Each key in the MixedCollection must be unique, the same key
 * cannot exist twice. This collection is ordered, items in the collection can be accessed by index or via the key.
 * Newly added items are added to the end of the collection. This class is similar to {@link Ext.util.HashMap} however
 * it is heavier and provides more functionality. Sample usage:
 *
 *     var coll = new Ext.util.MixedCollection();
 *     coll.add('key1', 'val1');
 *     coll.add('key2', 'val2');
 *     coll.add('key3', 'val3');
 *
 *     console.log(coll.get('key1')); // prints 'val1'
 *     console.log(coll.indexOfKey('key3')); // prints 2
 *
 * The MixedCollection also has support for sorting and filtering of the values in the collection.
 *
 *     var coll = new Ext.util.MixedCollection();
 *     coll.add('key1', 100);
 *     coll.add('key2', -100);
 *     coll.add('key3', 17);
 *     coll.add('key4', 0);
 *     var biggerThanZero = coll.filterBy(function(value){
 *         return value > 0;
 *     });
 *     console.log(biggerThanZero.getCount()); // prints 2
 */
Ext.define('Ext.util.MixedCollection', {
    extend: 'Ext.util.AbstractMixedCollection',
    mixins: {
        sortable: 'Ext.util.Sortable'
    },

    /**
     * @event sort
     * Fires whenever MixedCollection is sorted
     * @param {Ext.util.MixedCollection} this
     */

    /**
     * @constructor
     * @param {Boolean} allowFunctions Specify `true` if the {@link #addAll}
     * function should add function references to the collection. (defaults to `false`)
     * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
     * and return the key value for that item.  This is used when available to look up the key on items that
     * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
     * equivalent to providing an implementation for the {@link #getKey} method.
     */
    constructor: function() {
        var me = this;
        me.callParent(arguments);
        me.mixins.sortable.initSortable.call(me);
    },

    doSort: function(sorterFn) {
        this.sortBy(sorterFn);
    },

    /**
     * @private
     * Performs the actual sorting based on a direction and a sorting function. Internally,
     * this creates a temporary array of all items in the MixedCollection, sorts it and then writes
     * the sorted array data back into this.items and this.keys
     * @param {String} property Property to sort by ('key', 'value', or 'index')
     * @param {String} dir (optional) Direction to sort 'ASC' or 'DESC'. Defaults to 'ASC'.
     * @param {Function} fn (optional) Comparison function that defines the sort order.
     * Defaults to sorting by numeric value.
     */
    _sort: function(property, dir, fn){
        var me = this,
            i, len,
            dsc   = String(dir).toUpperCase() == 'DESC' ? -1 : 1,

            //this is a temporary array used to apply the sorting function
            c     = [],
            keys  = me.keys,
            items = me.items;

        //default to a simple sorter function if one is not provided
        fn = fn || function(a, b) {
            return a - b;
        };

        //copy all the items into a temporary array, which we will sort
        for(i = 0, len = items.length; i < len; i++){
            c[c.length] = {
                key  : keys[i],
                value: items[i],
                index: i
            };
        }

        //sort the temporary array
        Ext.Array.sort(c, function(a, b){
            var v = fn(a[property], b[property]) * dsc;
            if(v === 0){
                v = (a.index < b.index ? -1 : 1);
            }
            return v;
        });

        //copy the temporary array back into the main this.items and this.keys objects
        for(i = 0, len = c.length; i < len; i++){
            items[i] = c[i].value;
            keys[i]  = c[i].key;
        }

        me.fireEvent('sort', me);
    },

    /**
     * Sorts the collection by a single sorter function
     * @param {Function} sorterFn The function to sort by
     */
    sortBy: function(sorterFn) {
        var me     = this,
            items  = me.items,
            keys   = me.keys,
            length = items.length,
            temp   = [],
            i;

        //first we create a copy of the items array so that we can sort it
        for (i = 0; i < length; i++) {
            temp[i] = {
                key  : keys[i],
                value: items[i],
                index: i
            };
        }

        Ext.Array.sort(temp, function(a, b) {
            var v = sorterFn(a.value, b.value);
            if (v === 0) {
                v = (a.index < b.index ? -1 : 1);
            }

            return v;
        });

        //copy the temporary array back into the main this.items and this.keys objects
        for (i = 0; i < length; i++) {
            items[i] = temp[i].value;
            keys[i]  = temp[i].key;
        }

        me.fireEvent('sort', me, items, keys);
    },

    /**
     * Reorders each of the items based on a mapping from old index to new index. Internally this just translates into a
     * sort. The 'sort' event is fired whenever reordering has occured.
     * @param {Object} mapping Mapping from old item index to new item index
     */
    reorder: function(mapping) {
        var me = this,
            items = me.items,
            index = 0,
            length = items.length,
            order = [],
            remaining = [],
            oldIndex;

        me.suspendEvents();

        //object of {oldPosition: newPosition} reversed to {newPosition: oldPosition}
        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);
    },

    /**
     * Sorts this collection by **key**s.
     * @param {String} direction 'ASC' or 'DESC'. Defaults to 'ASC'.
     * @param {Function} fn Comparison function that defines the sort order. Defaults to sorting by case insensitive
     * string.
     */
    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);
        });
    }
});

/**
 * @private
 */
Ext.define('Ext.ItemCollection', {
    extend: 'Ext.util.MixedCollection',

    getKey: function(item) {
        return item.getItemId();
    },

    has: function(item) {
        return this.map.hasOwnProperty(item.getId());
    }
});

/**
 * @author Ed Spencer
 * @class Ext.data.Error
 *
 * <p>This is used when validating a record. The validate method will return an Ext.data.Errors collection
 * containing Ext.data.Error instances. Each error has a field and a message.</p>
 *
 * <p>Usually this class does not need to be instantiated directly - instances are instead created
 * automatically when {@link Ext.data.Model#validate validate} on a model instance.</p>
 */

Ext.define('Ext.data.Error', {
    extend: 'Ext.util.MixedCollection',

    config: {
        /**
         * @cfg {String} field
         * The name of the field this error belongs to.
         */
        field: null,

        /**
         * @cfg {String} message
         * The message containing the description of the error.
         */
        message: ''
    },

    constructor: function(config) {
        this.initConfig(config);
    }
});

/**
 * @author Ed Spencer
 * @class Ext.data.Errors
 * @extends Ext.util.Collection
 *
 * <p>Wraps a collection of validation error responses and provides convenient functions for
 * accessing and errors for specific fields.</p>
 *
 * <p>Usually this class does not need to be instantiated directly - instances are instead created
 * automatically when {@link Ext.data.Model#validate validate} on a model instance:</p>
 *
<pre><code>
//validate some existing model instance - in this case it returned 2 failures messages
var errors = myModel.validate();

errors.isValid(); //false

errors.length; //2
errors.getByField('name');  // [{field: 'name',  message: 'must be present'}]
errors.getByField('title'); // [{field: 'title', message: 'is too short'}]
</code></pre>
 */
Ext.define('Ext.data.Errors', {
    extend: 'Ext.util.Collection',

    requires: 'Ext.data.Error',

    /**
     * Returns true if there are no errors in the collection
     * @return {Boolean}
     */
    isValid: function() {
        return this.length === 0;
    },

    /**
     * Returns all of the errors for the given field
     * @param {String} fieldName The field to get errors for
     * @return {Object[]} All errors for the given field
     */
    getByField: function(fieldName) {
        var errors = [],
            error, i;

        for (i = 0; i < this.length; i++) {
            error = this.items[i];

            if (error.getField() == fieldName) {
                errors.push(error);
            }
        }

        return errors;
    },
    
    add: function() {
        var obj = arguments.length == 1 ? arguments[0] : arguments[1];
        
        if (!(obj instanceof Ext.data.Error)) {
            obj = Ext.create('Ext.data.Error', {
                field: obj.field || obj.name,
                message: obj.error || obj.message
            });
        }
        
        return this.callParent([obj]);
    }
});

/**
 * @author Ed Spencer
 *
 * AjaxProxy is one of the most widely-used ways of getting data into your application. It uses AJAX
 * requests to load data from the server, usually to be placed into a {@link Ext.data.Store Store}.
 * Let's take a look at a typical setup. Here we're going to set up a Store that has an AjaxProxy.
 * To prepare, we'll also set up a {@link Ext.data.Model Model}:
 *
 *     Ext.define('User', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: ['id', 'name', 'email']
 *         }
 *     });
 *
 *     // The Store contains the AjaxProxy as an inline configuration
 *     var store = Ext.create('Ext.data.Store', {
 *         model: 'User',
 *         proxy: {
 *             type: 'ajax',
 *             url : 'users.json'
 *         }
 *     });
 *
 *     store.load();
 *
 * Our example is going to load user data into a Store, so we start off by defining a
 * {@link Ext.data.Model Model} with the fields that we expect the server to return. Next we set up
 * the Store itself, along with a {@link Ext.data.Store#proxy proxy} configuration. This
 * configuration was automatically turned into an Ext.data.proxy.Ajax instance, with the url we
 * specified being passed into AjaxProxy's constructor. It's as if we'd done this:
 *
 *     Ext.create('Ext.data.proxy.Ajax', {
 *         config: {
 *             url: 'users.json',
 *             model: 'User',
 *             reader: 'json'
 *         }
 *     });
 *
 * A couple of extra configurations appeared here - {@link #model} and {@link #reader}. These are
 * set by default when we create the proxy via the Store - the Store already knows about the Model,
 * and Proxy's default {@link Ext.data.reader.Reader Reader} is {@link Ext.data.reader.Json JsonReader}.
 *
 * Now when we call store.load(), the AjaxProxy springs into action, making a request to the url we
 * configured ('users.json' in this case). As we're performing a read, it sends a GET request to
 * that url (see {@link #actionMethods} to customize this - by default any kind of read will be sent
 * as a GET request and any kind of write will be sent as a POST request).
 *
 * ## Limitations
 *
 * AjaxProxy cannot be used to retrieve data from other domains. If your application is running on
 * http://domainA.com it cannot load data from http://domainB.com because browsers have a built-in
 * security policy that prohibits domains talking to each other via AJAX.
 *
 * If you need to read data from another domain and can't set up a proxy server (some software that
 * runs on your own domain's web server and transparently forwards requests to http://domainB.com,
 * making it look like they actually came from http://domainA.com), you can use
 * {@link Ext.data.proxy.JsonP} and a technique known as JSON-P (JSON with Padding), which can help
 * you get around the problem so long as the server on http://domainB.com is set up to support
 * JSON-P responses. See {@link Ext.data.proxy.JsonP JsonPProxy}'s introduction docs for more details.
 *
 * ## Readers and Writers
 *
 * AjaxProxy can be configured to use any type of {@link Ext.data.reader.Reader Reader} to decode
 * the server's response. If no Reader is supplied, AjaxProxy will default to using a
 * {@link Ext.data.reader.Json JsonReader}. Reader configuration can be passed in as a simple
 * object, which the Proxy automatically turns into a {@link Ext.data.reader.Reader Reader} instance:
 *
 *     var proxy = Ext.create('Ext.data.proxy.Ajax', {
 *         config: {
 *             model: 'User',
 *             reader: {
 *                 type: 'xml',
 *                 root: 'users'
 *             }
 *         }
 *     });
 *
 *     proxy.getReader(); //returns an {@link Ext.data.reader.Xml XmlReader} instance based on the config we supplied
 *
 * ## Url generation
 *
 * AjaxProxy automatically inserts any sorting, filtering, paging and grouping options into the url
 * it generates for each request. These are controlled with the following configuration options:
 *
 * - {@link #pageParam} - controls how the page number is sent to the server (see also
 *   {@link #startParam} and {@link #limitParam})
 * - {@link #sortParam} - controls how sort information is sent to the server
 * - {@link #groupParam} - controls how grouping information is sent to the server
 * - {@link #filterParam} - controls how filter information is sent to the server
 *
 * Each request sent by AjaxProxy is described by an {@link Ext.data.Operation Operation}. To see
 * how we can customize the generated urls, let's say we're loading the Proxy with the following
 * Operation:
 *
 *     var operation = Ext.create('Ext.data.Operation', {
 *         action: 'read',
 *         page  : 2
 *     });
 *
 * Now we'll issue the request for this Operation by calling {@link #read}:
 *
 *     var proxy = Ext.create('Ext.data.proxy.Ajax', {
 *         url: '/users'
 *     });
 *
 *     proxy.read(operation); // GET /users?page=2
 *
 * Easy enough - the Proxy just copied the page property from the Operation. We can customize how
 * this page data is sent to the server:
 *
 *     var proxy = Ext.create('Ext.data.proxy.Ajax', {
 *         url: '/users',
 *         pagePage: 'pageNumber'
 *     });
 *
 *     proxy.read(operation); // GET /users?pageNumber=2
 *
 * Alternatively, our Operation could have been configured to send start and limit parameters
 * instead of page:
 *
 *     var operation = Ext.create('Ext.data.Operation', {
 *         action: 'read',
 *         start : 50,
 *         limit : 25
 *     });
 *
 *     var proxy = Ext.create('Ext.data.proxy.Ajax', {
 *         url: '/users'
 *     });
 *
 *     proxy.read(operation); // GET /users?start=50&limit;=25
 *
 * Again we can customize this url:
 *
 *     var proxy = Ext.create('Ext.data.proxy.Ajax', {
 *         url: '/users',
 *         startParam: 'startIndex',
 *         limitParam: 'limitIndex'
 *     });
 *
 *     proxy.read(operation); // GET /users?startIndex=50&limitIndex;=25
 *
 * AjaxProxy will also send sort and filter information to the server. Let's take a look at how this
 * looks with a more expressive Operation object:
 *
 *     var operation = Ext.create('Ext.data.Operation', {
 *         action: 'read',
 *         sorters: [
 *             Ext.create('Ext.util.Sorter', {
 *                 property : 'name',
 *                 direction: 'ASC'
 *             }),
 *             Ext.create('Ext.util.Sorter', {
 *                 property : 'age',
 *                 direction: 'DESC'
 *             })
 *         ],
 *         filters: [
 *             Ext.create('Ext.util.Filter', {
 *                 property: 'eyeColor',
 *                 value   : 'brown'
 *             })
 *         ]
 *     });
 *
 * This is the type of object that is generated internally when loading a {@link Ext.data.Store Store}
 * with sorters and filters defined. By default the AjaxProxy will JSON encode the sorters and
 * filters, resulting in something like this (note that the url is escaped before sending the
 * request, but is left unescaped here for clarity):
 *
 *     var proxy = Ext.create('Ext.data.proxy.Ajax', {
 *         url: '/users'
 *     });
 *
 *     proxy.read(operation); // GET /users?sort=[{"property":"name","direction":"ASC"},{"property":"age","direction":"DESC"}]&filter;=[{"property":"eyeColor","value":"brown"}]
 *
 * We can again customize how this is created by supplying a few configuration options. Let's say
 * our server is set up to receive sorting information is a format like "sortBy=name#ASC,age#DESC".
 * We can configure AjaxProxy to provide that format like this:
 *
 *      var proxy = Ext.create('Ext.data.proxy.Ajax', {
 *          url: '/users',
 *          sortParam: 'sortBy',
 *          filterParam: 'filterBy',
 *
 *          // our custom implementation of sorter encoding - turns our sorters into "name#ASC,age#DESC"
 *          encodeSorters: function(sorters) {
 *              var length   = sorters.length,
 *                  sortStrs = [],
 *                  sorter, i;
 *
 *              for (i = 0; i < length; i++) {
 *                  sorter = sorters[i];
 *
 *                  sortStrs[i] = sorter.property + '#' + sorter.direction
 *              }
 *
 *              return sortStrs.join(",");
 *          }
 *      });
 *
 *      proxy.read(operation); // GET /users?sortBy=name#ASC,age#DESC&filterBy;=[{"property":"eyeColor","value":"brown"}]
 *
 * We can also provide a custom {@link #encodeFilters} function to encode our filters.
 *
 * @constructor
 * Note that if this HttpProxy is being used by a {@link Ext.data.Store Store}, then the Store's
 * call to {@link Ext.data.Store#method-load load} will override any specified callback and params
 * options. In this case, use the {@link Ext.data.Store Store}'s events to modify parameters, or
 * react to loading events.
 *
 * @param {Object} config (optional) Config object.
 * If an options parameter is passed, the singleton {@link Ext.Ajax} object will be used to
 * make the request.
 */
Ext.define('Ext.data.proxy.Ajax', {
    extend: 'Ext.data.proxy.Server',

    requires: ['Ext.util.MixedCollection', 'Ext.Ajax'],
    alias: 'proxy.ajax',
    alternateClassName: ['Ext.data.HttpProxy', 'Ext.data.AjaxProxy'],

    config: {
        /**
         * @property {Object} actionMethods
         * Mapping of action name to HTTP request method. In the basic AjaxProxy these are set to
         * 'GET' for 'read' actions and 'POST' for 'create', 'update' and 'destroy' actions.
         * The {@link Ext.data.proxy.Rest} maps these to the correct RESTful methods.
         */
        actionMethods: {
            create : 'POST',
            read   : 'GET',
            update : 'POST',
            destroy: 'POST'
        },

        /**
         * @cfg {Object} headers
         * Any headers to add to the Ajax request. Defaults to undefined.
         */
        headers: {},

        /**
         * @cfg {Boolean} withCredentials
         * This configuration is sometimes necessary when using cross-origin resource sharing.
         * @accessor
         */
        withCredentials: false
    },

    /**
     * Performs Ajax request.
     * @protected
     */
    doRequest: function(operation, callback, scope) {
        var writer  = this.getWriter(),
            request = this.buildRequest(operation);

        request.setConfig({
            headers        : this.getHeaders(),
            timeout        : this.getTimeout(),
            method         : this.getMethod(request),
            callback       : this.createRequestCallback(request, operation, callback, scope),
            scope          : this
        });

        if (operation.getWithCredentials() || this.getWithCredentials()) {
            request.setWithCredentials(true);
        }

        // We now always have the writer prepare the request
        request = writer.write(request);

        Ext.Ajax.request(request.getCurrentConfig());

        return request;
    },

    /**
     * Returns the HTTP method name for a given request. By default this returns based on a lookup on
     * {@link #actionMethods}.
     * @param {Ext.data.Request} request The request object
     * @return {String} The HTTP method to use (should be one of 'GET', 'POST', 'PUT' or 'DELETE')
     */
    getMethod: function(request) {
        return this.getActionMethods()[request.getAction()];
    },

    /**
     * @private
     * @param {Ext.data.Request} request The Request object
     * @param {Ext.data.Operation} operation The Operation being executed
     * @param {Function} callback The callback function to be called when the request completes.
     *        This is usually the callback passed to doRequest
     * @param {Object} scope The scope in which to execute the callback function
     * @return {Function} The callback function
     */
    createRequestCallback: function(request, operation, callback, scope) {
        var me = this;

        return function(options, success, response) {
            me.processResponse(success, operation, request, response, callback, scope);
        };
    }
});

/**
 * @author Ed Spencer
 *
 * A Model represents some object that your application manages. For example, one might define a Model for Users,
 * Products, Cars, or any other real-world object that we want to model in the system. Models are registered via the
 * {@link Ext.data.ModelManager model manager}, and are used by {@link Ext.data.Store stores}, which are in turn used by many
 * of the data-bound components in Ext.
 *
 * Models are defined as a set of fields and any arbitrary methods and properties relevant to the model. For example:
 *
 *     Ext.define('User', {
 *         extend: 'Ext.data.Model',
 *
 *         config: {
 *             fields: [
 *                 {name: 'name',  type: 'string'},
 *                 {name: 'age',   type: 'int'},
 *                 {name: 'phone', type: 'string'},
 *                 {name: 'alive', type: 'boolean', defaultValue: true}
 *             ]
 *         },
 *
 *         changeName: function() {
 *             var oldName = this.get('name'),
 *                 newName = oldName + " The Barbarian";
 *
 *             this.set('name', newName);
 *         }
 *     });
 *
 * The fields array is turned into a {@link Ext.util.MixedCollection MixedCollection} automatically by the {@link
 * Ext.data.ModelManager ModelManager}, and all other functions and properties are copied to the new Model's prototype.
 *
 * Now we can create instances of our User model and call any model logic we defined:
 *
 *     var user = Ext.create('User', {
 *         name : 'Conan',
 *         age  : 24,
 *         phone: '555-555-5555'
 *     });
 *
 *     user.changeName();
 *     user.get('name'); //returns "Conan The Barbarian"
 *
 * # Validations
 *
 * Models have built-in support for validations, which are executed against the validator functions in {@link
 * Ext.data.validations} ({@link Ext.data.validations see all validation functions}). Validations are easy to add to
 * models:
 *
 *     Ext.define('User', {
 *         extend: 'Ext.data.Model',
 *
 *         config: {
 *             fields: [
 *                 {name: 'name',     type: 'string'},
 *                 {name: 'age',      type: 'int'},
 *                 {name: 'phone',    type: 'string'},
 *                 {name: 'gender',   type: 'string'},
 *                 {name: 'username', type: 'string'},
 *                 {name: 'alive',    type: 'boolean', defaultValue: true}
 *             ],
 *
 *             validations: [
 *                 {type: 'presence',  field: 'age'},
 *                 {type: 'length',    field: 'name',     min: 2},
 *                 {type: 'inclusion', field: 'gender',   list: ['Male', 'Female']},
 *                 {type: 'exclusion', field: 'username', list: ['Admin', 'Operator']},
 *                 {type: 'format',    field: 'username', matcher: /([a-z]+)[0-9]{2,3}/}
 *             ]
 *         }
 *     });
 *
 * The validations can be run by simply calling the {@link #validate} function, which returns a {@link Ext.data.Errors}
 * object:
 *
 *     var instance = Ext.create('User', {
 *         name: 'Ed',
 *         gender: 'Male',
 *         username: 'edspencer'
 *     });
 *
 *     var errors = instance.validate();
 *
 * # Associations
 *
 * Models can have associations with other Models via {@link Ext.data.association.HasOne},
 * {@link Ext.data.association.BelongsTo belongsTo} and {@link Ext.data.association.HasMany hasMany} associations.
 * For example, let's say we're writing a blog administration application which deals with Users, Posts and Comments.
 * We can express the relationships between these models like this:
 *
 *     Ext.define('Post', {
 *         extend: 'Ext.data.Model',
 *
 *         config: {
 *             fields: ['id', 'user_id'],
 *             belongsTo: 'User',
 *             hasMany  : {model: 'Comment', name: 'comments'}
 *         }
 *     });
 *
 *     Ext.define('Comment', {
 *         extend: 'Ext.data.Model',
 *
 *         config: {
 *             fields: ['id', 'user_id', 'post_id'],
 *             belongsTo: 'Post'
 *         }
 *     });
 *
 *     Ext.define('User', {
 *         extend: 'Ext.data.Model',
 *
 *         config: {
 *             fields: ['id'],
 *             hasMany: [
 *                 'Post',
 *                 {model: 'Comment', name: 'comments'}
 *             ]
 *         }
 *     });
 *
 * See the docs for {@link Ext.data.association.HasOne}, {@link Ext.data.association.BelongsTo} and
 * {@link Ext.data.association.HasMany} for details on the usage and configuration of associations.
 * Note that associations can also be specified like this:
 *
 *     Ext.define('User', {
 *         extend: 'Ext.data.Model',
 *
 *         config: {
 *             fields: ['id'],
 *             associations: [
 *                 {type: 'hasMany', model: 'Post',    name: 'posts'},
 *                 {type: 'hasMany', model: 'Comment', name: 'comments'}
 *             ]
 *         }
 *     });
 *
 * # Using a Proxy
 *
 * Models are great for representing types of data and relationships, but sooner or later we're going to want to load or
 * save that data somewhere. All loading and saving of data is handled via a {@link Ext.data.proxy.Proxy Proxy}, which
 * can be set directly on the Model:
 *
 *     Ext.define('User', {
 *         extend: 'Ext.data.Model',
 *
 *         config: {
 *             fields: ['id', 'name', 'email'],
 *             proxy: {
 *                 type: 'rest',
 *                 url : '/users'
 *             }
 *         }
 *     });
 *
 * Here we've set up a {@link Ext.data.proxy.Rest Rest Proxy}, which knows how to load and save data to and from a
 * RESTful backend. Let's see how this works:
 *
 *     var user = Ext.create('User', {name: 'Ed Spencer', email: 'ed@sencha.com'});
 *
 *     user.save(); //POST /users
 *
 * Calling {@link #save} on the new Model instance tells the configured RestProxy that we wish to persist this Model's
 * data onto our server. RestProxy figures out that this Model hasn't been saved before because it doesn't have an id,
 * and performs the appropriate action - in this case issuing a POST request to the url we configured (/users). We
 * configure any Proxy on any Model and always follow this API - see {@link Ext.data.proxy.Proxy} for a full list.
 *
 * Loading data via the Proxy is equally easy:
 *
 *     //get a reference to the User model class
 *     var User = Ext.ModelManager.getModel('User');
 *
 *     //Uses the configured RestProxy to make a GET request to /users/123
 *     User.load(123, {
 *         success: function(user) {
 *             console.log(user.getId()); //logs 123
 *         }
 *     });
 *
 * Models can also be updated and destroyed easily:
 *
 *     //the user Model we loaded in the last snippet:
 *     user.set('name', 'Edward Spencer');
 *
 *     //tells the Proxy to save the Model. In this case it will perform a PUT request to /users/123 as this Model already has an id
 *     user.save({
 *         success: function() {
 *             console.log('The User was updated');
 *         }
 *     });
 *
 *     //tells the Proxy to destroy the Model. Performs a DELETE request to /users/123
 *     user.destroy({
 *         success: function() {
 *             console.log('The User was destroyed!');
 *         }
 *     });
 *
 * # Usage in Stores
 *
 * It is very common to want to load a set of Model instances to be displayed and manipulated in the UI. We do this by
 * creating a {@link Ext.data.Store Store}:
 *
 *     var store = Ext.create('Ext.data.Store', {
 *         model: 'User'
 *     });
 *
 *     //uses the Proxy we set up on Model to load the Store data
 *     store.load();
 *
 * A Store is just a collection of Model instances - usually loaded from a server somewhere. Store can also maintain a
 * set of added, updated and removed Model instances to be synchronized with the server via the Proxy. See the {@link
 * Ext.data.Store Store docs} for more information on Stores.
 *
 * @constructor
 * Creates new Model instance.
 * @param {Object} data An object containing keys corresponding to this model's fields, and their associated values
 * @param {Number} id (optional) Unique ID to assign to this model instance
 */
Ext.define('Ext.data.Model', {
    alternateClassName: 'Ext.data.Record',

    mixins: {
        observable: 'Ext.mixin.Observable'
    },

    isModel: true,

    requires: [
        'Ext.util.Collection',
        'Ext.data.Field',
        'Ext.data.identifier.Simple',
        'Ext.data.ModelManager',
        'Ext.data.proxy.Ajax',
        'Ext.data.association.HasMany',
        'Ext.data.association.BelongsTo',
        'Ext.data.association.HasOne',
        'Ext.data.Errors'
    ],

    config: {
        /**
         * @cfg {String} idProperty
         * The name of the field treated as this Model's unique id. Defaults to 'id'. Note that this field
         * needs to have a type of 'auto'. Setting the field type to anything else will be undone by the
         * framework. This is because new records that are created without an id, will have one generated.
         * This generated id is a string.
         */
        idProperty: 'id',

        data: null,

        /**
         * @cfg {Object[]/String[]} fields
         * The field definitions for all instances of this Model. **Note:** this does not set the *values* of each
         * field on an instance, it sets the collection of fields itself. Sample usage:
         *
         *     Ext.define('MyApp.model.User', {
         *         extend: 'Ext.data.Model',
         *
         *         config: {
         *             fields: [
         *                 'id',
         *                 {name: 'age', type: 'int'},
         *                 {name: 'taxRate', type: 'float'}
         *             ]
         *         }
         *     });
         * @accessor
         */
        fields: undefined,

        /**
         * @cfg {Object[]} validations
         * An array of {@link Ext.data.Validations validations} for this model.
         */
        validations: null,

        /**
         * @cfg {Object[]} associations
         * An array of {@link Ext.data.association.Association associations} for this model.
         */
        associations: null,

        /**
         * @cfg {String/Object/String[]/Object[]} hasMany
         * One or more {@link Ext.data.association.HasMany HasMany associations} for this model.
         */
        hasMany: null,

        /**
         * @cfg {String/Object/String[]/Object[]} hasOne
         * One or more {@link Ext.data.association.HasOne HasOne associations} for this model.
         */
        hasOne: null,

        /**
         * @cfg {String/Object/String[]/Object[]} belongsTo
         * One or more {@link Ext.data.association.BelongsTo BelongsTo associations} for this model.
         */
        belongsTo: null,

        /**
         * @cfg {Object/Ext.data.Proxy} proxy
         * The string type of the default Model Proxy. Defaults to null.
         * @accessor
         */
        proxy: null,

        identifier: {
            type: 'simple'
        },

        /**
         * @cfg {String} clientIdProperty
         * The name of a property that is used for submitting this Model's unique client-side identifier
         * to the server when multiple phantom records are saved as part of the same {@link Ext.data.Operation Operation}.
         * In such a case, the server response should include the client id for each record
         * so that the server response data can be used to update the client-side records if necessary.
         * This property cannot have the same name as any of this Model's fields.
         * Defaults to 'clientId'.
         * @accessor
         */
        clientIdProperty: 'clientId',

        /**
         * @method getIsErased Returns true if the record has been erased on the server.
         */
        isErased: false
    },

    staticConfigs: [
        'idProperty',
        'fields',
        'validations',
        'associations',
        'hasMany',
        'hasOne',
        'belongsTo',
        'clientIdProperty',
        'identifier',
        'proxy'
    ],

    statics: {
        EDIT   : 'edit',
        REJECT : 'reject',
        COMMIT : 'commit',

        generateProxyMethod: function(name) {
            return function() {
                var prototype = this.prototype;

                return prototype[name].apply(prototype, arguments);
            };
        },

        generateCacheId: function(record, id) {
            var modelName;

            if (record && record.isModel) {
                modelName = record.modelName;
                if (id === undefined) {
                    id = record.getId();
                }
            } else {
                modelName = record;
            }

            return modelName.replace(/\./g, '-').toLowerCase() + '-' + id;
        }
    },

    inheritableStatics: {
        /**
         * Asynchronously loads a model instance by id. Sample usage:
         *
         *     MyApp.User = Ext.define('User', {
         *         extend: 'Ext.data.Model',
         *         fields: [
         *             {name: 'id', type: 'int'},
         *             {name: 'name', type: 'string'}
         *         ]
         *     });
         *
         *     MyApp.User.load(10, {
         *         scope: this,
         *         failure: function(record, operation) {
         *             //do something if the load failed
         *         },
         *         success: function(record, operation) {
         *             //do something if the load succeeded
         *         },
         *         callback: function(record, operation) {
         *             //do something whether the load succeeded or failed
         *         }
         *     });
         *
         * @param {Number} id The id of the model to load
         * @param {Object} config (optional) config object containing success, failure and callback functions, plus
         * optional scope
         * @static
         * @inheritable
         */
        load: function(id, config, scope) {
            var proxy = this.getProxy(),
                idProperty = this.getIdProperty(),
                record = null,
                params = {},
                callback, operation;

            scope = scope || (config && config.scope) || this;
            if (Ext.isFunction(config)) {
                config = {
                    callback: config,
                    scope: scope
                };
            }

            params[idProperty] = id;
            config = Ext.apply({}, config);
            config = Ext.applyIf(config, {
                action: 'read',
                params: params,
                model: this
            });

            operation  = Ext.create('Ext.data.Operation', config);

            if (!proxy) {
                Ext.Logger.error('You are trying to load a model that doesn\'t have a Proxy specified');
            }

            callback = function(operation) {
                if (operation.wasSuccessful()) {
                    record = operation.getRecords()[0];
                    Ext.callback(config.success, scope, [record, operation]);
                } else {
                    Ext.callback(config.failure, scope, [record, operation]);
                }
                Ext.callback(config.callback, scope, [record, operation]);
            };

            proxy.read(operation, callback, this);
        }
    },

    /**
     * @property {Boolean} editing
     * Internal flag used to track whether or not the model instance is currently being edited. Read-only.
     */
    editing : false,

    /**
     * @property {Boolean} dirty
     * True if this Record has been modified. Read-only.
     */
    dirty : false,

    /**
     * @property {Boolean} phantom
     * True when the record does not yet exist in a server-side database (see {@link #setDirty}).
     * Any record which has a real database pk set as its id property is NOT a phantom -- it's real.
     */
    phantom : false,

    constructor: function(data, id, raw, convertedData) {
        var me = this,
            cached = null,
            idProperty = this.getIdProperty();

        /**
         * @property {Object} modified key/value pairs of all fields whose values have changed.
         * The value is the original value for the field.
         */
        me.modified = {};

        /**
         * @property {Object} raw The raw data used to create this model if created via a reader.
         */
        me.raw = raw || data || {};

        /**
         * @property {Array} stores
         * An array of {@link Ext.data.Store} objects that this record is bound to.
         */
        me.stores = [];

        data = data || convertedData || {};

        // We begin by checking if an id is passed to the constructor. If this is the case we override
        // any possible id value that was passed in the data.
        if (id || id === 0) {
            // Lets skip using set here since it's so much faster
            data[idProperty] = me.internalId = id;
        }

        id = me.data[idProperty];
        if (id || id === 0) {
            cached = Ext.data.Model.cache.get(Ext.data.Model.generateCacheId(this, id));
            if (cached) {
                return cached.mergeData(convertedData || data || {});
            }
        }

        if (convertedData) {
            me.setConvertedData(data);
        } else {
            me.setData(data);
        }

        // If it does not have an id at this point, we generate it using the id strategy. This means
        // that we will treat this record as a phantom record from now on
        id = me.data[idProperty];
        if (!id && id !== 0) {
            me.data[idProperty] = me.internalId = me.id = me.getIdentifier().generate(me);
            me.phantom = true;

            if (this.associations.length) {
                this.handleInlineAssociationData(data);
            }
        } else {
            me.id = me.getIdentifier().generate(me);
        }

        Ext.data.Model.cache.add(me);

        if (this.init && typeof this.init == 'function') {
            this.init();
        }
    },

    /**
     * Private function that is used when you create a record that already exists in the model cache.
     * In this case we loop over each field, and apply any data to the current instance that is not already
     * marked as being dirty on that instance.
     * @param data
     * @private
     */
    mergeData: function(rawData) {
        var me = this,
            fields = me.getFields().items,
            ln = fields.length,
            data = me.data,
            i, field, fieldName, value, convert;

        for (i = 0; i < ln; i++) {
            field = fields[i];
            fieldName = field.getName();
            convert = field.getConvert();
            value = rawData[fieldName];

            if (value !== undefined && !me.isModified(fieldName)) {
                if (convert) {
                    value = convert.call(field, value, me);
                }

                data[fieldName] = value;
            }
        }

        return this;
    },

    /**
     * This method is used to set the data for this Record instance.
     * Note that the existing data is removed. If a field is not specified
     * in the passed data it will use the field's default value. If a convert
     * method is specified for the field it will be called on the value.
     * @param rawData
     * @return {Ext.data.Model} this This Record
     */
    setData: function(rawData) {
        var fields = this.fields.items,
            ln = fields.length,
            isArray = Ext.isArray(rawData),
            data = this._data = this.data = {},
            i, field, name, value, convert, id;

        if (!rawData) {
            return this;
        }

        for (i = 0; i < ln; i++) {
            field = fields[i];
            name = field.getName();
            convert = field.getConvert();

            if (isArray) {
                value = rawData[i];
            }
            else {
                value = rawData[name];
                if (typeof value == 'undefined') {
                    value = field.getDefaultValue();
                }
            }

            if (convert) {
                value = convert.call(field, value, this);
            }

            data[name] = value;
        }

        id = this.getId();
        if (this.associations.length && (id || id === 0)) {
            this.handleInlineAssociationData(rawData);
        }

        return this;
    },

    handleInlineAssociationData: function(data) {
        var associations = this.associations.items,
            ln = associations.length,
            rawData = this.raw,
            i, association, associationData, reader, proxy, associationKey;

        for (i = 0; i < ln; i++) {
            association = associations[i];
            associationKey = association.getAssociationKey();
            associationData = data[associationKey];

            if (!associationData) {
                associationData = rawData[associationKey];
            }

            if (associationData) {
                reader = association.getReader();
                if (!reader) {
                    proxy = association.getAssociatedModel().getProxy();
                    // if the associated model has a Reader already, use that, otherwise attempt to create a sensible one
                    if (proxy) {
                        reader = proxy.getReader();
                    } else {
                        reader = new Ext.data.JsonReader({
                            model: association.getAssociatedModel()
                        });
                    }
                }
                association.read(this, reader, associationData);
            }
        }
    },

    /**
     * Sets the model instance's id field to the given id.
     * @param {Number/String} id The new id
     */
    setId: function(id) {
        var currentId = this.getId();

        // Lets use the direct property instead of getter here
        this.set(this.getIdProperty(), id);

        // We don't update the this.id since we don't want to break listeners that already
        // exist on the record instance.
        this.internalId = id;

        Ext.data.Model.cache.replace(currentId, this);
    },

    /**
     * Returns the unique ID allocated to this model instance as defined by {@link #idProperty}.
     * @return {Number/String} The id
     */
    getId: function() {
        // Lets use the direct property instead of getter here
        return this.get(this.getIdProperty());
    },

    /**
     * This sets the data directly without converting and applying default values.
     * This method is used when a Record gets instantiated by a Reader. Only use
     * this when you are sure you are passing correctly converted data.
     * @param data
     * @return this This Record
     */
    setConvertedData: function(data) {
        this._data = this.data = data;
        return this;
    },

    /**
     * Returns the value of the given field
     * @param {String} fieldName The field to fetch the value for
     * @return {Object} The value
     */
    get: function(fieldName) {
        return this.data[fieldName];
    },

    /**
     * Sets the given field to the given value, marks the instance as dirty
     * @param {String/Object} fieldName The field to set, or an object containing key/value pairs
     * @param {Object} value The value to set
     */
    set: function(fieldName, value) {
        var me = this,
            // We are using the fields map since it saves lots of function calls
            fieldMap = me.fields.map,
            modified = me.modified,
            notEditing = !me.editing,
            associations = me.associations.items,
            modifiedCount = 0,
            modifiedFieldNames = [],
            field, key, i, currentValue, ln, convert;

        /*
         * If we're passed an object, iterate over that object. NOTE: we pull out fields with a convert function and
         * set those last so that all other possible data is set before the convert function is called
         */
        if (arguments.length == 1) {
            for (key in fieldName) {
                if (fieldName.hasOwnProperty(key)) {
                    //here we check for the custom convert function. Note that if a field doesn't have a convert function,
                    //we default it to its type's convert function, so we have to check that here. This feels rather dirty.
                    field = fieldMap[key];
                    if (field && field.hasCustomConvert()) {
                        modifiedFieldNames.push(key);
                        continue;
                    }

                    if (!modifiedCount && notEditing) {
                        me.beginEdit();
                    }
                    ++modifiedCount;
                    me.set(key, fieldName[key]);
                }
            }

            ln = modifiedFieldNames.length;
            if (ln) {
                if (!modifiedCount && notEditing) {
                    me.beginEdit();
                }
                modifiedCount += ln;
                for (i = 0; i < ln; i++) {
                    field = modifiedFieldNames[i];
                    me.set(field, fieldName[field]);
                }
            }

            if (notEditing && modifiedCount) {
                me.endEdit(false, modifiedFieldNames);
            }
        } else {
            field = fieldMap[fieldName];
            convert = field && field.getConvert();
            if (convert) {
                value = convert.call(field, value, me);
            }

            currentValue = me.data[fieldName];
            me.data[fieldName] = value;

            if (field && !me.isEqual(currentValue, value)) {
                if (modified.hasOwnProperty(fieldName)) {
                    if (me.isEqual(modified[fieldName], value)) {
                        // the original value in me.modified equals the new value, so the
                        // field is no longer modified
                        delete modified[fieldName];
                        // we might have removed the last modified field, so check to see if
                        // there are any modified fields remaining and correct me.dirty:
                        me.dirty = false;
                        for (key in modified) {
                            if (modified.hasOwnProperty(key)) {
                                me.dirty = true;
                                break;
                            }
                        }
                    }
                } else {
                    me.dirty = true;
                    // We only go one level back?
                    modified[fieldName] = currentValue;
                }
            }

            if (notEditing) {
                me.afterEdit([fieldName], modified);
            }
        }
    },

    /**
     * Checks if two values are equal, taking into account certain
     * special factors, for example dates.
     * @private
     * @param {Object} a The first value
     * @param {Object} b The second value
     * @return {Boolean} True if the values are equal
     */
    isEqual: function(a, b){
        if (Ext.isDate(a) && Ext.isDate(b)) {
            return a.getTime() === b.getTime();
        }
        return a === b;
    },

    /**
     * Begins an edit. While in edit mode, no events (e.g.. the `update` event) are relayed to the containing store.
     * When an edit has begun, it must be followed by either {@link #endEdit} or {@link #cancelEdit}.
     */
    beginEdit: function() {
        var me = this;
        if (!me.editing) {
            me.editing      = true;

            // We save the current states of dirty, data and modified so that when we
            // cancel the edit, we can put it back to this state
            me.dirtySave    = me.dirty;
            me.dataSave     = Ext.apply({}, me.data);
            me.modifiedSave = Ext.apply({}, me.modified);
        }
    },

    /**
     * Cancels all changes made in the current edit operation.
     */
    cancelEdit: function() {
        var me = this;
        if (me.editing) {
            me.editing = false;

            // Reset the modified state, nothing changed since the edit began
            me.modified = me.modifiedSave;
            me.data = me.dataSave;
            me.dirty = me.dirtySave;

            // Delete the saved states
            delete me.modifiedSave;
            delete me.dataSave;
            delete me.dirtySave;
        }
    },

    /**
     * Ends an edit. If any data was modified, the containing store is notified (ie, the store's `update` event will
     * fire).
     * @param {Boolean} silent True to not notify the store of the change
     * @param {String[]} modifiedFieldNames Array of field names changed during edit.
     */
    endEdit: function(silent, modifiedFieldNames) {
        var me = this;

        if (me.editing) {
            me.editing = false;

            if (silent !== true && (me.changedWhileEditing())) {
                me.afterEdit(modifiedFieldNames || Ext.Object.getKeys(this.modified), this.modified);
            }

            delete me.modifiedSave;
            delete me.dataSave;
            delete me.dirtySave;
        }
    },

    /**
     * Checks if the underlying data has changed during an edit. This doesn't necessarily
     * mean the record is dirty, however we still need to notify the store since it may need
     * to update any views.
     * @private
     * @return {Boolean} True if the underlying data has changed during an edit.
     */
    changedWhileEditing: function() {
        var me = this,
            saved = me.dataSave,
            data = me.data,
            key;

        for (key in data) {
            if (data.hasOwnProperty(key)) {
                if (!me.isEqual(data[key], saved[key])) {
                    return true;
                }
            }
        }

        return false;
    },

    /**
     * Gets a hash of only the fields that have been modified since this Model was created or commited.
     * @return {Object}
     */
    getChanges : function() {
        var modified = this.modified,
            changes  = {},
            field;

        for (field in modified) {
            if (modified.hasOwnProperty(field)) {
                changes[field] = this.get(field);
            }
        }

        return changes;
    },

    /**
     * Returns true if the passed field name has been `{@link #modified}` since the load or last commit.
     * @param {String} fieldName {@link Ext.data.Field#name}
     * @return {Boolean}
     */
    isModified : function(fieldName) {
        return this.modified.hasOwnProperty(fieldName);
    },

    /**
     * Saves the model instance using the configured proxy.
     * @param {Object/Function} options Options to pass to the proxy. Config object for {@link Ext.data.Operation}.
     * If you pass a function, this will automatically become the callback method.
     * @param {Object} scope The scope to run your callback method in. This is only used if you passed a function
     * as the first argument.
     * @return {Ext.data.Model} The Model instance
     */
    save: function(options, scope) {
        var me     = this,
            action = me.phantom ? 'create' : 'update',
            proxy  = me.getProxy(),
            operation,
            callback;

        if (!proxy) {
            Ext.Logger.error('You are trying to save a model instance that doesn\'t have a Proxy specified');
        }

        options = options || {};
        scope = scope || me;

        if (Ext.isFunction(options)) {
            options = {
                callback: options,
                scope: scope
            };
        }

        Ext.applyIf(options, {
            records: [me],
            action : action,
            model: me.self
        });

        operation = Ext.create('Ext.data.Operation', options);

        callback = function(operation) {
            if (operation.wasSuccessful()) {
                Ext.callback(options.success, scope, [me, operation]);
            } else {
                Ext.callback(options.failure, scope, [me, operation]);
            }

            Ext.callback(options.callback, scope, [me, operation]);
        };

        proxy[action](operation, callback, me);

        return me;
    },

    /**
     * Destroys the record using the configured proxy. This will create a 'destroy' operation.
     * Note that this doesn't destroy this instance after the server comes back with a response.
     * It will however call afterErase on any Stores it is joined to. Stores by default will
     * automatically remove this instance from their data collection.
     * @param {Object} options Options to pass to the proxy. Config object for {@link Ext.data.Operation}.
     * @return {Ext.data.Model} The Model instance
     */
    erase: function(options, scope) {
        var me     = this,
            proxy  = this.getProxy(),
            operation,
            callback;

        if (!proxy) {
            Ext.Logger.error('You are trying to erase a model instance that doesn\'t have a Proxy specified');
        }

        options = options || {};
        scope = scope || me;

        if (Ext.isFunction(options)) {
            options = {
                callback: options,
                scope: scope
            };
        }

        Ext.applyIf(options, {
            records: [me],
            action : 'destroy',
            model: this.self
        });

        operation = Ext.create('Ext.data.Operation', options);

        callback = function(operation) {
            if (operation.wasSuccessful()) {
                Ext.callback(options.success, scope, [me, operation]);
            } else {
                Ext.callback(options.failure, scope, [me, operation]);
            }

            Ext.callback(options.callback, scope, [me, operation]);
        };

        proxy.destroy(operation, callback, me);

        return me;
    },

    /**
     * Usually called by the {@link Ext.data.Store} to which this model instance has been {@link #join joined}. Rejects
     * all changes made to the model instance since either creation, or the last commit operation. Modified fields are
     * reverted to their original values.
     *
     * Developers should subscribe to the {@link Ext.data.Store#update} event to have their code notified of reject
     * operations.
     *
     * @param {Boolean} silent (optional) True to skip notification of the owning store of the change.
     * Defaults to false.
     */
    reject: function(silent) {
        var me = this,
            modified = me.modified,
            field;

        for (field in modified) {
            if (modified.hasOwnProperty(field)) {
                if (typeof modified[field] != "function") {
                    me.data[field] = modified[field];
                }
            }
        }

        me.dirty = false;
        me.editing = false;
        me.modified = {};

        if (silent !== true) {
            me.afterReject();
        }
    },

    /**
     * Usually called by the {@link Ext.data.Store} which owns the model instance. Commits all changes made to the
     * instance since either creation or the last commit operation.
     *
     * Developers should subscribe to the {@link Ext.data.Store#update} event to have their code notified of commit
     * operations.
     *
     * @param {Boolean} silent (optional) True to skip notification of the owning store of the change.
     * Defaults to false.
     */
    commit: function(silent) {
        var me = this,
            modified = this.modified;

        me.phantom = me.dirty = me.editing = false;
        me.modified = {};

        if (silent !== true) {
            me.afterCommit(modified);
        }
    },

    /**
     * @private
     * If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
     * afterEdit method is called
     * @param {String[]} modifiedFieldNames Array of field names changed during edit.
     */
    afterEdit : function(modifiedFieldNames, modified) {
        this.notifyStores('afterEdit', modifiedFieldNames, modified);
    },

    /**
     * @private
     * If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
     * afterReject method is called
     */
    afterReject : function() {
        this.notifyStores("afterReject");
    },

    /**
     * @private
     * If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
     * afterCommit method is called
     */
    afterCommit: function(modified) {
        this.notifyStores('afterCommit', Ext.Object.getKeys(modified || {}), modified);
    },

    /**
     * @private
     * Helper function used by afterEdit, afterReject and afterCommit. Calls the given method on the
     * {@link Ext.data.Store store} that this instance has {@link #join joined}, if any. The store function
     * will always be called with the model instance as its single argument.
     * @param {String} fn The function to call on the store
     */
    notifyStores: function(fn) {
        var args = Ext.Array.clone(arguments),
            stores = this.stores,
            ln = stores.length,
            i, store;

        args[0] = this;
        for (i = 0; i < ln; ++i) {
            store = stores[i];
            if (store !== undefined && typeof store[fn] == "function") {
                store[fn].apply(store, args);
            }
        }
    },

    /**
     * Creates a copy (clone) of this Model instance.
     *
     * @param {String} id A new id. If you don't specify this a new id will be generated for you.
     * To generate a phantom instance with a new id use:
     *
     *     var rec = record.copy(); // clone the record with a new id
     *
     * @return {Ext.data.Model}
     */
    copy: function(newId) {
        var me = this,
            idProperty = me.getIdProperty(),
            raw = Ext.apply({}, me.raw),
            data = Ext.apply({}, me.data);

        delete raw[idProperty];
        delete data[idProperty];

        return new me.self(null, newId, raw, data);
    },

    /**
     * Returns an object containing the data set on this record. This method also allows you to
     * retrieve all the associated data. Note that if you should always use this method if you
     * need all the associated data, since the data property on the record instance is not
     * ensured to be updated at all times.
     * @param {Boolean} includeAssociated True to include the associated data.
     * @return {Object} The data
     */
    getData: function(includeAssociated) {
        var data = this.data;

        if (includeAssociated === true) {
            Ext.apply(data, this.getAssociatedData());
        }

        return data;
    },

    /**
     * Gets all of the data from this Models *loaded* associations. It does this recursively - for example if we have a
     * User which hasMany Orders, and each Order hasMany OrderItems, it will return an object like this:
     *
     *     {
     *         orders: [
     *             {
     *                 id: 123,
     *                 status: 'shipped',
     *                 orderItems: [
     *                     ...
     *                 ]
     *             }
     *         ]
     *     }
     *
     * @return {Object} The nested data set for the Model's loaded associations
     */
    getAssociatedData: function() {
        return this.prepareAssociatedData(this, [], null);
    },

    /**
     * @private
     * This complex-looking method takes a given Model instance and returns an object containing all data from
     * all of that Model's *loaded* associations. See {@link #getAssociatedData}
     * @param {Ext.data.Model} record The Model instance
     * @param {String[]} ids PRIVATE. The set of Model instance internalIds that have already been loaded
     * @param {String} associationType (optional) The name of the type of association to limit to.
     * @return {Object} The nested data set for the Model's loaded associations
     */
    prepareAssociatedData: function(record, ids, associationType) {
        //we keep track of all of the internalIds of the models that we have loaded so far in here
        var associations     = record.associations.items,
            associationCount = associations.length,
            associationData  = {},
            associatedStore, associationName, associatedRecords, associatedRecord,
            associatedRecordCount, association, id, i, j, type, allow;

        for (i = 0; i < associationCount; i++) {
            association = associations[i];
            associationName = association.getName();
            type = association.getType();
            allow = true;

            if (associationType) {
                allow = type == associationType;
            }

            if (allow && type.toLowerCase() == 'hasmany') {
                //this is the hasMany store filled with the associated data
                associatedStore = record[association.getStoreName()];

                //we will use this to contain each associated record's data
                associationData[associationName] = [];

                //if it's loaded, put it into the association data
                if (associatedStore && associatedStore.getCount() > 0) {
                    associatedRecords = associatedStore.data.items;
                    associatedRecordCount = associatedRecords.length;

                    //now we're finally iterating over the records in the association. We do this recursively
                    for (j = 0; j < associatedRecordCount; j++) {
                        associatedRecord = associatedRecords[j];
                        // Use the id, since it is prefixed with the model name, guaranteed to be unique
                        id = associatedRecord.id;

                        //when we load the associations for a specific model instance we add it to the set of loaded ids so that
                        //we don't load it twice. If we don't do this, we can fall into endless recursive loading failures.
                        if (Ext.Array.indexOf(ids, id) == -1) {
                            ids.push(id);

                            associationData[associationName][j] = associatedRecord.getData();
                            Ext.apply(associationData[associationName][j], this.prepareAssociatedData(associatedRecord, ids, type));
                        }
                    }
                }
            } else if (allow && (type.toLowerCase() == 'belongsto' || type.toLowerCase() == 'hasone')) {
                associatedRecord = record[association.getInstanceName()];
                if (associatedRecord !== undefined) {
                    id = associatedRecord.id;
                    if (Ext.Array.indexOf(ids, id) === -1) {
                        ids.push(id);
                        associationData[associationName] = associatedRecord.getData();
                        Ext.apply(associationData[associationName], this.prepareAssociatedData(associatedRecord, ids, type));
                    }
                }
            }
        }

        return associationData;
    },

    /**
     * By joining this model to an instance of a class, this model will automatically try to
     * call certain template methods on that instance (afterEdit, afterCommit, afterErase).
     * For example, a Store calls join and unjoin whenever you add or remove a record to it's data collection.
     * This way a Store can get notified of any changes made to this record.
     * This functionality is usually only required when creating custom components.
     * @param {Ext.data.Store} store The store to which this model has been added.
     */
    join: function(store) {
        Ext.Array.include(this.stores, store);
    },

    /**
     * This unjoins this record from an instance of a class. Look at the documentation for {@link #join}
     * for more information about joining records to class instances.
     * @param {Ext.data.Store} store The store from which this model has been removed.
     */
    unjoin: function(store) {
        Ext.Array.remove(this.stores, store);
    },

    /**
     * Marks this **Record** as `{@link #dirty}`. This method is used interally when adding `{@link #phantom}` records
     * to a {@link Ext.data.proxy.Server#writer writer enabled store}.
     *
     * Marking a record `{@link #dirty}` causes the phantom to be returned by {@link Ext.data.Store#getUpdatedRecords}
     * where it will have a create action composed for it during {@link Ext.data.Model#save model save} operations.
     */
    setDirty : function() {
        var me = this,
            name;

        me.dirty = true;

        me.fields.each(function(field) {
            if (field.getPersist()) {
                name = field.getName();
                me.modified[name] = me.get(name);
            }
        });
    },

    /**
     * Validates the current data against all of its configured {@link #cfg-validations}.
     * @return {Ext.data.Errors} The errors object
     */
    validate: function() {
        var errors      = Ext.create('Ext.data.Errors'),
            validations = this.getValidations().items,
            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(Ext.create('Ext.data.Error', {
                        field  : field,
                        message: validation.message || validators.getMessage(type)
                    }));
                }
            }
        }

        return errors;
    },

    /**
     * Checks if the model is valid. See {@link #validate}.
     * @return {Boolean} True if the model is valid.
     */
    isValid: function(){
        return this.validate().isValid();
    },

    /**
     * Returns a url-suitable string for this model instance. By default this just returns the name of the Model class
     * followed by the instance ID - for example an instance of MyApp.model.User with ID 123 will return 'user/123'.
     * @return {String} The url string for this model instance
     */
    toUrl: function() {
        var pieces = this.$className.split('.'),
            name   = pieces[pieces.length - 1].toLowerCase();

        return name + '/' + this.getId();
    },

    /**
     * Destroys this model instance. Note that this doesn't do a 'destroy' operation. If you want to destroy
     * the record in your localstorage or on the server you should use the {@link #erase} method.
     */
    destroy: function() {
        var me = this;
        me.notifyStores('afterErase', me);
        Ext.data.Model.cache.remove(me);
        me.data = me._data = me.raw = me.stores = me.modified = null;
        me.callParent(arguments);
    },

    markDirty : function() {
        if (Ext.isDefined(Ext.Logger)) {
            Ext.Logger.deprecate('Ext.data.Model: markDirty has been deprecated. Use setDirty instead.');
        }
        return this.setDirty.apply(this, arguments);
    },

    applyProxy: function(proxy, currentProxy) {
        return Ext.factory(proxy, Ext.data.Proxy, currentProxy, 'proxy');
    },

    updateProxy: function(proxy) {
        if (proxy) {
            proxy.setModel(this.self);
        }
    },

    applyAssociations: function(associations) {
        if (associations) {
            this.addAssociations(associations, 'hasMany');
        }
    },

    applyBelongsTo: function(belongsTo) {
        if (belongsTo) {
            this.addAssociations(belongsTo, 'belongsTo');
        }
    },

    applyHasMany: function(hasMany) {
        if (hasMany) {
            this.addAssociations(hasMany, 'hasMany');
        }
    },

    applyHasOne: function(hasOne) {
        if (hasOne) {
            this.addAssociations(hasOne, 'hasOne');
        }
    },

    addAssociations: function(associations, defaultType) {
        var ln, i, association,
            name = this.self.modelName,
            associationsCollection = this.self.associations,
            onCreatedFn;

        associations = Ext.Array.from(associations);

        for (i = 0, ln = associations.length; i < ln; i++) {
            association = associations[i];
            if (!Ext.isObject(association)) {
                association = {model: association};
            }

            Ext.applyIf(association, {
                type: defaultType,
                ownerModel: name,
                associatedModel: association.model
            });

            delete association.model;

            onCreatedFn = Ext.Function.bind(function(associationName) {
                associationsCollection.add(Ext.data.association.Association.create(this));
            }, association);

            Ext.ClassManager.onCreated(onCreatedFn, this, (typeof association.associatedModel === 'string') ? association.associatedModel : Ext.getClassName(association.associatedModel));
        }
    },

    applyValidations: function(validations) {
        if (validations) {
            if (!Ext.isArray(validations)) {
                validations = [validations];
            }
            this.addValidations(validations);
        }
    },

    addValidations: function(validations) {
        this.self.validations.addAll(validations);
    },

    /**
     * @method setFields
     * Updates the collection of Fields that all instances of this Model use. **Does not** update field values in a Model
     * instance (use {@link #set} for that), instead this updates which fields are available on the Model class. This
     * is normally used when creating or updating Model definitions dynamically, for example if you allow your users to
     * define their own Models and save the fields configuration to a database, this method allows you to change those
     * fields later.
     */

    applyFields: function(fields) {
        var superFields = this.superclass.fields;
        if (superFields) {
            fields = superFields.items.concat(fields || []);
        }
        return fields || [];
    },

    updateFields: function(fields) {
        var ln = fields.length,
            me = this,
            prototype = me.self.prototype,
            idProperty = this.getIdProperty(),
            idField, fieldsCollection, field, i;

        /**
         * @property {Ext.util.MixedCollection} fields
         * The fields defined on this model.
         */
        fieldsCollection = me._fields = me.fields = new Ext.util.Collection(prototype.getFieldName);

        for (i = 0; i < ln; i++) {
            field = fields[i];
            if (!field.isField) {
                field = new Ext.data.Field(fields[i]);
            }
            fieldsCollection.add(field);
        }

        // We want every Model to have an id property field
        idField = fieldsCollection.get(idProperty);
        if (!idField) {
            fieldsCollection.add(new Ext.data.Field(idProperty));
        } else {
            idField.setType('auto');
        }

        fieldsCollection.addSorter(prototype.sortConvertFields);
    },

    applyIdentifier: function(identifier) {
        if (typeof identifier === 'string') {
            identifier = {
                type: identifier
            };
        }
        return Ext.factory(identifier, Ext.data.identifier.Simple, this.getIdentifier(), 'data.identifier');
    },

    /**
     * This method is used by the fields collection to retrieve the key for a field
     * based on it's name.
     * @param field
     * @private
     */
    getFieldName: function(field) {
        return field.getName();
    },

    /**
     * This method is being used to sort the fields based on their convert method. If
     * a field has a custom convert method, we ensure its more to the bottom of the collection.
     * @param field1
     * @param field2
     * @private
     */
    sortConvertFields: function(field1, field2) {
        var f1SpecialConvert = field1.hasCustomConvert(),
            f2SpecialConvert = field2.hasCustomConvert();

        if (f1SpecialConvert && !f2SpecialConvert) {
            return 1;
        }
        if (!f1SpecialConvert && f2SpecialConvert) {
            return -1;
        }
        return 0;
    },

    /**
     * @private
     */
    onClassExtended: function(cls, data, hooks) {
        var onBeforeClassCreated = hooks.onBeforeCreated,
            Model = this,
            prototype = Model.prototype,
            configNameCache = Ext.Class.configNameCache,
            staticConfigs = prototype.staticConfigs.concat(data.staticConfigs || []),
            defaultConfig = prototype.config,
            config = data.config || {},
            key;

        // Convert old properties in data into a config object
        data.config = config;

        hooks.onBeforeCreated = function(cls, data) {
            var dependencies = [],
                prototype = cls.prototype,
                statics = {},
                config = prototype.config,
                staticConfigsLn = staticConfigs.length,
                copyMethods = ['set', 'get'],
                copyMethodsLn = copyMethods.length,
                associations = config.associations || [],
                name = Ext.getClassName(cls),
                key, methodName, i, j, ln;

            // Create static setters and getters for each config option
            for (i = 0; i < staticConfigsLn; i++) {
                key = staticConfigs[i];

                for (j = 0; j < copyMethodsLn; j++) {
                    methodName = configNameCache[key][copyMethods[j]];
                    if (methodName in prototype) {
                        statics[methodName] = Model.generateProxyMethod(methodName);
                    }
                }
            }

            cls.addStatics(statics);

            // Save modelName on class and its prototype
            cls.modelName = name;
            prototype.modelName = name;

            // Take out dependencies on other associations and the proxy type
            if (config.belongsTo) {
                dependencies.push('association.belongsto');
            }
            if (config.hasMany) {
                dependencies.push('association.hasmany');
            }
            if (config.hasOne) {
                dependencies.push('association.hasone');
            }

            for (i = 0,ln = associations.length; i < ln; ++i) {
                dependencies.push('association.' + associations[i].type.toLowerCase());
            }

            if (config.proxy) {
                if (typeof config.proxy === 'string') {
                    dependencies.push('proxy.' + config.proxy);
                }
                else if (typeof config.proxy.type === 'string') {
                    dependencies.push('proxy.' + config.proxy.type);
                }
            }

            if (config.validations) {
                dependencies.push('Ext.data.Validations');
            }

            Ext.require(dependencies, function() {
                Ext.Function.interceptBefore(hooks, 'onCreated', function() {
                    Ext.data.ModelManager.registerType(name, cls);

                    var superCls = cls.prototype.superclass;

                    /**
                     * @property {Ext.util.Collection} associations
                     * The associations defined on this model.
                     */
                    cls.prototype.associations = cls.associations = cls.prototype._associations = (superCls && superCls.associations)
                        ? superCls.associations.clone()
                        : new Ext.util.Collection(function(association) {
                            return association.getName();
                        });

                    /**
                     * @property {Ext.util.Collection} validations
                     * The validations defined on this model.
                     */
                    cls.prototype.validations = cls.validations = cls.prototype._validations = (superCls && superCls.validations)
                        ? superCls.validations.clone()
                        : new Ext.util.Collection(function(validation) {
                            return validation.field || validation.name;
                        });

                    cls.prototype = Ext.Object.chain(cls.prototype);
                    cls.prototype.initConfig.call(cls.prototype, config);

                    delete cls.prototype.initConfig;
                });

                onBeforeClassCreated.call(Model, cls, data, hooks);
            });
        };
    }
}, function() {
    this.cache = new Ext.util.Collection(this.generateCacheId);
});

/**
 * @author Ed Spencer
 * @class Ext.data.Store
 *
 * The Store class encapsulates a client side cache of {@link Ext.data.Model Model} objects. Stores load
 * data via a {@link Ext.data.proxy.Proxy Proxy}, and also provide functions for {@link #sort sorting},
 * {@link #filter filtering} and querying the {@link Ext.data.Model model} instances contained within it.
 *
 * Creating a Store is easy - we just tell it the Model and the Proxy to use to load and save its data:
 *
 *     // Set up a {@link Ext.data.Model model} to use in our Store
 *     Ext.define('User', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: [
 *                 {name: 'firstName', type: 'string'},
 *                 {name: 'lastName',  type: 'string'},
 *                 {name: 'age',       type: 'int'},
 *                 {name: 'eyeColor',  type: 'string'}
 *             ]
 *         }
 *     });
 *
 *     var myStore = Ext.create('Ext.data.Store', {
 *         model: 'User',
 *         proxy: {
 *             type: 'ajax',
 *             url : '/users.json',
 *             reader: {
 *                 type: 'json',
 *                 rootProperty: 'users'
 *             }
 *         },
 *         autoLoad: true
 *     });
 *
 * In the example above we configured an AJAX proxy to load data from the url '/users.json'. We told our Proxy
 * to use a {@link Ext.data.reader.Json JsonReader} to parse the response from the server into Model object -
 * {@link Ext.data.reader.Json see the docs on JsonReader} for details.
 *
 * ## Inline data
 *
 * Stores can also load data inline. Internally, Store converts each of the objects we pass in as {@link #cfg-data}
 * into Model instances:
 *
 *     Ext.create('Ext.data.Store', {
 *         model: 'User',
 *         data : [
 *             {firstName: 'Ed',    lastName: 'Spencer'},
 *             {firstName: 'Tommy', lastName: 'Maintz'},
 *             {firstName: 'Aaron', lastName: 'Conran'},
 *             {firstName: 'Jamie', lastName: 'Avins'}
 *         ]
 *     });
 *
 * Loading inline data using the method above is great if the data is in the correct format already (e.g. it doesn't need
 * to be processed by a {@link Ext.data.reader.Reader reader}). If your inline data requires processing to decode the data structure,
 * use a {@link Ext.data.proxy.Memory MemoryProxy} instead (see the {@link Ext.data.proxy.Memory MemoryProxy} docs for an example).
 *
 * Additional data can also be loaded locally using {@link #method-add}.
 *
 * ## Loading Nested Data
 *
 * Applications often need to load sets of associated data - for example a CRM system might load a User and her Orders.
 * Instead of issuing an AJAX request for the User and a series of additional AJAX requests for each Order, we can load a nested dataset
 * and allow the Reader to automatically populate the associated models. Below is a brief example, see the {@link Ext.data.reader.Reader} intro
 * docs for a full explanation:
 *
 *     var store = Ext.create('Ext.data.Store', {
 *         autoLoad: true,
 *         model: "User",
 *         proxy: {
 *             type: 'ajax',
 *             url : 'users.json',
 *             reader: {
 *                 type: 'json',
 *                 rootProperty: 'users'
 *             }
 *         }
 *     });
 *
 * Which would consume a response like this:
 *
 *     {
 *         "users": [
 *             {
 *                 "id": 1,
 *                 "name": "Ed",
 *                 "orders": [
 *                     {
 *                         "id": 10,
 *                         "total": 10.76,
 *                         "status": "invoiced"
 *                     },
 *                     {
 *                         "id": 11,
 *                         "total": 13.45,
 *                         "status": "shipped"
 *                     }
 *                 ]
 *             }
 *         ]
 *     }
 *
 * See the {@link Ext.data.reader.Reader} intro docs for a full explanation.
 *
 * ## Filtering and Sorting
 *
 * Stores can be sorted and filtered - in both cases either remotely or locally. The {@link #sorters} and {@link #filters} are
 * held inside {@link Ext.util.MixedCollection MixedCollection} instances to make them easy to manage. Usually it is sufficient to
 * either just specify sorters and filters in the Store configuration or call {@link #sort} or {@link #filter}:
 *
 *     var store = Ext.create('Ext.data.Store', {
 *         model: 'User',
 *         sorters: [
 *             {
 *                 property : 'age',
 *                 direction: 'DESC'
 *             },
 *             {
 *                 property : 'firstName',
 *                 direction: 'ASC'
 *             }
 *         ],
 *
 *         filters: [
 *             {
 *                 property: 'firstName',
 *                 value   : /Ed/
 *             }
 *         ]
 *     });
 *
 * The new Store will keep the configured sorters and filters in the MixedCollection instances mentioned above. By default, sorting
 * and filtering are both performed locally by the Store - see {@link #remoteSort} and {@link #remoteFilter} to allow the server to
 * perform these operations instead.
 *
 * Filtering and sorting after the Store has been instantiated is also easy. Calling {@link #filter} adds another filter to the Store
 * and automatically filters the dataset (calling {@link #filter} with no arguments simply re-applies all existing filters). Note that by
 * default your sorters are automatically reapplied if using local sorting.
 *
     store.filter('eyeColor', 'Brown');
 *
 * Change the sorting at any time by calling {@link #sort}:
 *
     store.sort('height', 'ASC');
 *
 * Note that all existing sorters will be removed in favor of the new sorter data (if {@link #sort} is called with no arguments,
 * the existing sorters are just reapplied instead of being removed). To keep existing sorters and add new ones, just add them
 * to the MixedCollection:
 *
 *     store.sorters.add(new Ext.util.Sorter({
 *         property : 'shoeSize',
 *         direction: 'ASC'
 *     }));
 *
 *     store.sort();
 *
 * ## Registering with StoreManager
 *
 * Any Store that is instantiated with a {@link #storeId} will automatically be registed with the {@link Ext.data.StoreManager StoreManager}.
 * This makes it easy to reuse the same store in multiple views:
 *
 *     //this store can be used several times
 *     Ext.create('Ext.data.Store', {
 *         model: 'User',
 *         storeId: 'usersStore'
 *     });
 *
 *     new Ext.List({
 *         store: 'usersStore',
 *
 *         //other config goes here
 *     });
 *
 *     new Ext.view.View({
 *         store: 'usersStore',
 *
 *         //other config goes here
 *     });
 *
 * ## Further Reading
 *
 * Stores are backed up by an ecosystem of classes that enables their operation. To gain a full understanding of these
 * pieces and how they fit together, see:
 *
 * <ul style="list-style-type: disc; padding-left: 25px">
 *     <li>{@link Ext.data.proxy.Proxy Proxy} - overview of what Proxies are and how they are used</li>
 *     <li>{@link Ext.data.Model Model} - the core class in the data package</li>
 *     <li>{@link Ext.data.reader.Reader Reader} - used by any subclass of {@link Ext.data.proxy.Server ServerProxy} to read a response</li>
 * </ul>
 *
 */
Ext.define('Ext.data.Store', {
    alias: 'store.store',

    extend: 'Ext.Evented',

    requires: [
        'Ext.util.Collection',
        'Ext.data.Operation',
        'Ext.data.proxy.Memory',
        'Ext.data.Model',
        'Ext.data.StoreManager',
        'Ext.util.Grouper'
    ],

    /**
     * @event addrecords
     * Fired when one or more new Model instances have been added to this Store. You should listen
     * for this event if you have to update a representation of the records in this store in your UI.
     * If you need the indices of the records that were added please use the store.indexOf(record) method.
     * @param {Ext.data.Store} store The store
     * @param {Ext.data.Model[]} records The Model instances that were added
     */

    /**
     * @event removerecords
     * Fired when one or more Model instances have been removed from this Store. You should listen
     * for this event if you have to update a representation of the records in this store in your UI.
     * @param {Ext.data.Store} store The Store object
     * @param {Ext.data.Model[]} records The Model instances that was removed
     * @param {Number[]} indices The indices of the records that were removed. These indices already
     * take into account any potential earlier records that you remove. This means that if you loop
     * over the records, you can get its current index in your data representation from this array.
     */

    /**
     * @event updaterecord
     * Fires when a Model instance has been updated
     * @param {Ext.data.Store} this
     * @param {Ext.data.Model} record The Model instance that was updated
     * @param {Number} newIndex If the update changed the index of the record (due to sorting for example), then
     * this gives you the new index in the store.
     * @param {Number} oldIndex If the update changed the index of the record (due to sorting for example), then
     * this gives you the old index in the store.
     * @param {Array} modifiedFieldNames An array containing the field names that have been modified since the
     * record was committed or created
     * @param {Object} modifiedValues An object where each key represents a field name that had it's value modified,
     * and where the value represents the old value for that field. To get the new value in a listener
     * you should use the {@link Ext.data.Model#get get} method.
     */

    /**
     * @event update
     * @inheritdoc Ext.data.Store#updaterecord
     * @removed 2.0 Listen to #updaterecord instead.
     */

    /**
     * @event refresh
     * Fires whenever the records in the Store have changed in a way that your representation of the records
     * need to be entirely refreshed.
     * @param {Ext.data.Store} this The data store
     * @param {Ext.util.Collection} data The data collection containing all the records
     */

    /**
     * @event beforeload
     * Fires before a request is made for a new data object. If the beforeload handler returns false the load
     * action will be canceled. Note that you should not listen for this event in order to refresh the
     * data view. Use the {@link #refresh} event for this instead.
     * @param {Ext.data.Store} store This Store
     * @param {Ext.data.Operation} operation The Ext.data.Operation object that will be passed to the Proxy to
     * load the Store
     */

    /**
     * @event load
     * Fires whenever records have been loaded into the store. Note that you should not listen
     * for this event in order to refresh the data view. Use the {@link #refresh} event for this instead.
     * @param {Ext.data.Store} this
     * @param {Ext.util.Grouper[]} records An array of records
     * @param {Boolean} successful True if the operation was successful.
     * @param {Ext.data.Operation} operation The associated operation
     */

    /**
     * @event write
     * Fires whenever a successful write has been made via the configured {@link #proxy Proxy}
     * @param {Ext.data.Store} store This Store
     * @param {Ext.data.Operation} operation The {@link Ext.data.Operation Operation} object that was used in
     * the write
     */

    /**
     * @event beforesync
     * Fired before a call to {@link #sync} is executed. Return false from any listener to cancel the synv
     * @param {Object} options Hash of all records to be synchronized, broken down into create, update and destroy
     */

    /**
     * @event clear
     * Fired after the {@link #removeAll} method is called. Note that you should not listen for this event in order
     * to refresh the data view. Use the {@link #refresh} event for this instead.
     * @param {Ext.data.Store} this
     */

    statics: {
        create: function(store) {
            if (!store.isStore) {
                if (!store.type) {
                    store.type = 'store';
                }
                store = Ext.createByAlias('store.' + store.type, store);
            }
            return store;
        }
    },

    isStore: true,

    config: {
        /**
         * @cfg {String} storeId
         * Unique identifier for this store. If present, this Store will be registered with the {@link Ext.data.StoreManager},
         * making it easy to reuse elsewhere.
         * @accessor
         */
        storeId: undefined,

        /**
         * @cfg {Object[]/Ext.data.Model[]} data
         * Array of Model instances or data objects to load locally. See "Inline data" above for details.
         * @accessor
         */
        data: null,

        /**
         * @cfg {Boolean/Object} autoLoad
         * If data is not specified, and if autoLoad is true or an Object, this store's load method is automatically called
         * after creation. If the value of autoLoad is an Object, this Object will be passed to the store's load method.
         * Defaults to false.
         * @accessor
         */
        autoLoad: null,

        /**
         * @cfg {Boolean} autoSync
         * True to automatically sync the Store with its Proxy after every edit to one of its Records. Defaults to false.
         * @accessor
         */
        autoSync: false,

        /**
         * @cfg {String} model
         * Name of the {@link Ext.data.Model Model} associated with this store.
         * The string is used as an argument for {@link Ext.ModelManager#getModel}.
         * @accessor
         */
        model: undefined,

        /**
         * @cfg {String/Ext.data.proxy.Proxy/Object} proxy The Proxy to use for this Store. This can be either a string, a config
         * object or a Proxy instance - see {@link #setProxy} for details.
         * @accessor
         */
        proxy: undefined,

        /**
         * @cfg {Object[]} fields
         * This may be used in place of specifying a {@link #model} configuration. The fields should be a
         * set of {@link Ext.data.Field} configuration objects. The store will automatically create a {@link Ext.data.Model}
         * with these fields. In general this configuration option should be avoided, it exists for the purposes of
         * backwards compatibility. For anything more complicated, such as specifying a particular id property or
         * associations, a {@link Ext.data.Model} should be defined and specified for the {@link #model}
         * config.
         * @accessor
         */
        fields: null,

        /**
         * @cfg {Boolean} remoteSort
         * True to defer any sorting operation to the server. If false, sorting is done locally on the client. Defaults to <tt>false</tt>.
         *
         * If this is set to `true`, you will have to manually call the {@link #method-load} method after you {@link #method-sort}, to retrieve the sorted
         * data from the server.
         * @accessor
         */
        remoteSort: false,

        /**
         * @cfg {Boolean} remoteFilter
         * True to defer any filtering operation to the server. If false, filtering is done locally on the client. Defaults to `false`.
         *
         * If this is set to `true`, you will have to manually call the {@link #method-load} method after you {@link #method-filter} to retrieve the filtered
         * data from the server.
         * @accessor
         */
        remoteFilter: false,

        /**
         * @cfg {Boolean} remoteGroup
         * True to defer any grouping operation to the server. If false, grouping is done locally on the client. Defaults to <tt>false</tt>
         * @accessor
         */
        remoteGroup: false,

        /**
         * @cfg {Object[]} filters
         * Array of {@link Ext.util.Filter Filters} for this store. This configuration is handled by the
         * {@link Ext.mixin.Filterable Filterable} mixin of the {@link Ext.util.Collection data} collection.
         * @accessor
         */
        filters: null,

        /**
         * @cfg {Object[]} sorters
         * Array of {@link Ext.util.Sorter Sorters} for this store. This configuration is handled by the
         * {@link Ext.mixin.Sortable Sortable} mixin of the {@link Ext.util.Collection data} collection.
         * @accessor
         */
        sorters: null,

        /**
         * @cfg {Object[]} grouper
         * A configuration object for this Store's {@link Ext.util.Grouper grouper}.
         *
         * For example, to group a store's items by the first letter of the last name:
         *
         *     Ext.define('People', {
         *         extend: 'Ext.data.Store',
         *
         *         config: {
         *             fields: ['first_name', 'last_name'],
         *
         *             grouper: {
         *                 groupFn: function(record) {
         *                     return record.get('last_name').substr(0, 1);
         *                 },
         *                 sortProperty: 'last_name'
         *             }
         *         }
         *     });
         *
         * @accessor
         */
        grouper: null,

        /**
         * @cfg {String} groupField
         * The (optional) field by which to group data in the store. Internally, grouping is very similar to sorting - the
         * groupField and {@link #groupDir} are injected as the first sorter (see {@link #sort}). Stores support a single
         * level of grouping, and groups can be fetched via the {@link #getGroups} method.
         * @accessor
         */
        groupField: null,

        /**
         * @cfg {String} groupDir
         * The direction in which sorting should be applied when grouping. If you specify a grouper by using the {@link #groupField}
         * configuration, this will automatically default to "ASC" - the other supported value is "DESC"
         * @accessor
         */
        groupDir: null,

        /**
         * @cfg {Function} getGroupString This function will be passed to the {@link #grouper} configuration as it's groupFn.
         * Note that this configuration is deprecated and grouper: {groupFn: yourFunction}} is preferred.
         * @deprecated
         * @accessor
         */
        getGroupString: null,

        /**
         * @cfg {Number} pageSize
         * The number of records considered to form a 'page'. This is used to power the built-in
         * paging using the nextPage and previousPage functions. Defaults to 25.
         * @accessor
         */
        pageSize: 25,

        /**
         * @cfg {Number} totalCount The total number of records in the full dataset, as indicated by a server. If the
         * server-side dataset contains 5000 records but only returns pages of 50 at a time, totalCount will be set to
         * 5000 and {@link #getCount} will return 50
         */
        totalCount: null,

        /**
         * @cfg {Boolean} clearOnPageLoad True to empty the store when loading another page via {@link #loadPage},
         * {@link #nextPage} or {@link #previousPage} (defaults to true). Setting to false keeps existing records, allowing
         * large data sets to be loaded one page at a time but rendered all together.
         * @accessor
         */
        clearOnPageLoad: true,

        modelDefaults: {},

        /**
         * @cfg {Boolean} autoDestroy This is a private configuration used in the framework whether this Store
         * can be destroyed.
         * @private
         */
        autoDestroy: false,

        /**
         * @cfg {Boolean} syncRemovedRecords This configuration allows you to disable the synchronization of
         * removed records on this Store. By default, when you call removeAll or remove, records will be added
         * to an internal removed array. When you then sync the Store, we send a destroy request for these records.
         * If you don't want this to happen, you can set this configuration to false.
         */
        syncRemovedRecords: true
    },

    /**
     * @property {Number} currentPage
     * The page that the Store has most recently loaded (see {@link #loadPage})
     */
    currentPage: 1,

    constructor: function(config) {
        config = config || {};

        this.data = this._data = this.createDataCollection();

        this.data.setSortRoot('data');
        this.data.setFilterRoot('data');

        this.removed = [];

        if (config.id && !config.storeId) {
            config.storeId = config.id;
            delete config.id;
        }


        this.initConfig(config);
    },

    createDataCollection: function() {
        return new Ext.util.Collection(function(record) {
            return record.getId();
        });
    },

    applyStoreId: function(storeId) {
        if (storeId === undefined || storeId === null) {
            storeId = this.getUniqueId();
        }
        return storeId;
    },

    updateStoreId: function(storeId, oldStoreId) {
        if (oldStoreId) {
            Ext.data.StoreManager.unregister(this);
        }
        if (storeId) {
            Ext.data.StoreManager.register(this);
        }
    },

    applyModel: function(model) {
        if (typeof model == 'string') {
            var registeredModel = Ext.data.ModelManager.getModel(model);
            if (!registeredModel) {
                Ext.Logger.error('Model with name "' + model + '" does not exist.');
            }
            model = registeredModel;
        }

        if (model && !model.prototype.isModel && Ext.isObject(model)) {
            model = Ext.data.ModelManager.registerType(model.storeId || model.id || Ext.id(), model);
        }

        if (!model) {
            var fields = this.getFields(),
                data = this.config.data;

            if (!fields && data && data.length) {
                fields = Ext.Object.getKeys(data[0]);
            }

            if (fields) {
                model = Ext.define('Ext.data.Store.ImplicitModel-' + (this.getStoreId() || Ext.id()), {
                    extend: 'Ext.data.Model',
                    config: {
                        fields: fields,
                        proxy: this.getProxy()
                    }
                });

                this.implicitModel = true;
            }
        }
        if (!model && this.getProxy()) {
            model = this.getProxy().getModel();
        }

        if (!model) {
            Ext.Logger.warn('Unless you define your model through metadata, a store needs to have a model defined on either itself or on its proxy');
        }

        return model;
    },

    updateModel: function(model) {
        var proxy = this.getProxy();

        if (proxy && !proxy.getModel()) {
            proxy.setModel(model);
        }
    },

    applyProxy: function(proxy, currentProxy) {
        proxy = Ext.factory(proxy, Ext.data.Proxy, currentProxy, 'proxy');

        if (!proxy && this.getModel()) {
            proxy = this.getModel().getProxy();

            if (!proxy) {
                proxy = new Ext.data.proxy.Memory({
                    model: this.getModel()
                });
            }
        }

        return proxy;
    },

    updateProxy: function(proxy) {
        if (proxy) {
            if (!proxy.getModel()) {
                proxy.setModel(this.getModel());
            }
            proxy.on('metachange', this.onMetaChange, this);
        }
    },

    /**
     * We are using applyData so that we can return nothing and prevent the this.data
     * property to be overridden.
     * @param data
     */
    applyData: function(data) {
        var me = this,
            proxy;
        if (data) {
            proxy = me.getProxy();
            if (proxy instanceof Ext.data.proxy.Memory) {
                proxy.setData(data);
                me.load();
            } else {
                // We make it silent because we don't want to fire a refresh event
                me.removeAll(true);

                // This means we have to fire a clear event though
                me.fireEvent('clear', me);

                // We don't want to fire addrecords event since we will be firing
                // a refresh event later which will already take care of updating
                // any views bound to this store
                me.suspendEvents();
                me.add(data);
                me.resumeEvents();

                // We set this to true so isAutoLoading to try
                me.dataLoaded = true;
            }
        } else {
            me.removeAll(true);

            // This means we have to fire a clear event though
            me.fireEvent('clear', me);
        }

        me.fireEvent('refresh', me, me.data);
    },

    clearData: function() {
        this.setData(null);
    },

    addData: function(data) {
        var reader = this.getProxy().getReader(),
            resultSet = reader.read(data),
            records = resultSet.getRecords();

        this.add(records);
    },

    updateAutoLoad: function(autoLoad) {
        var proxy = this.getProxy();
        if (autoLoad && (proxy && !proxy.isMemoryProxy)) {
            this.load(Ext.isObject(autoLoad) ? autoLoad : null);
        }
    },

    /**
     * Returns true if the Store is set to autoLoad or is a type which loads upon instantiation.
     * @return {Boolean}
     */
    isAutoLoading: function() {
        var proxy = this.getProxy();
        return (this.getAutoLoad() || (proxy && proxy.isMemoryProxy) || this.dataLoaded);
    },

    updateGroupField: function(groupField) {
        var grouper = this.getGrouper();
        if (groupField) {
            if (!grouper) {
                this.setGrouper({
                    property: groupField,
                    direction: this.getGroupDir() || 'ASC'
                });
            } else {
                grouper.setProperty(groupField);
            }
        } else if (grouper) {
            this.setGrouper(null);
        }
    },

    updateGroupDir: function(groupDir) {
        var grouper = this.getGrouper();
        if (grouper) {
            grouper.setDirection(groupDir);
        }
    },

    applyGetGroupString: function(getGroupStringFn) {
        var grouper = this.getGrouper();
        if (getGroupStringFn) {
            Ext.Logger.warn('Specifying getGroupString on a store has been deprecated. Please use grouper: {groupFn: yourFunction}');

            if (grouper) {
                grouper.setGroupFn(getGroupStringFn);
            } else {
                this.setGrouper({
                    groupFn: getGroupStringFn
                });
            }
        } else if (grouper) {
            this.setGrouper(null);
        }
    },

    applyGrouper: function(grouper) {
        if (typeof grouper == 'string') {
            grouper = {
                property: grouper
            };
        }
        else if (typeof grouper == 'function') {
            grouper = {
                groupFn: grouper
            };
        }

        grouper = Ext.factory(grouper, Ext.util.Grouper, this.getGrouper());
        return grouper;
    },

    updateGrouper: function(grouper, oldGrouper) {
        var data = this.data;
        if (oldGrouper) {
            data.removeSorter(oldGrouper);
            if (!grouper) {
                data.getSorters().removeSorter('isGrouper');
            }
        }
        if (grouper) {
            data.insertSorter(0, grouper);
            if (!oldGrouper) {
                data.getSorters().addSorter({
                    direction: 'DESC',
                    property: 'isGrouper',
                    transform: function(value) {
                        return (value === true) ? 1 : -1;
                    }
                });
            }
        }
    },

    /**
     * This method tells you if this store has a grouper defined on it.
     * @return {Boolean} true if this store has a grouper defined.
     */
    isGrouped: function() {
        return !!this.getGrouped();
    },

    updateSorters: function(sorters) {
        var grouper = this.getGrouper(),
            data = this.data,
            autoSort = data.getAutoSort();

        // While we remove/add sorters we don't want to automatically sort because we still need
        // to apply any field sortTypes as transforms on the Sorters after we have added them.
        data.setAutoSort(false);

        data.setSorters(sorters);
        if (grouper) {
            data.insertSorter(0, grouper);
        }

        this.updateSortTypes();

        // Now we put back autoSort on the Collection to the value it had before. If it was
        // auto sorted, setting this back will cause it to sort right away.
        data.setAutoSort(autoSort);
    },

    updateSortTypes: function() {
        var model = this.getModel(),
            fields = model && model.getFields(),
            data = this.data;

        // We loop over each sorter and set it's transform method to the every field's sortType.
        if (fields) {
            data.getSorters().each(function(sorter) {
                var property = sorter.getProperty(),
                    field;

                if (!sorter.isGrouper && property && !sorter.getTransform()) {
                    field = fields.get(property);
                    if (field) {
                        sorter.setTransform(field.getSortType());
                    }
                }
            });
        }
    },

    updateFilters: function(filters) {
        this.data.setFilters(filters);
    },

    /**
     * Adds Model instance to the Store. This method accepts either:
     *
     * - An array of Model instances or Model configuration objects.
     * - Any number of Model instance or Model configuration object arguments.
     *
     * The new Model instances will be added at the end of the existing collection.
     *
     * Sample usage:
     *
     *     myStore.add({some: 'data2'}, {some: 'other data2'});
     *
     * @param {Ext.data.Model[]/Ext.data.Model...} model An array of Model instances
     * or Model configuration objects, or variable number of Model instance or config arguments.
     * @return {Ext.data.Model[]} The model instances that were added
     */
    add: function(records) {
        //accept both a single-argument array of records, or any number of record arguments
        if (!Ext.isArray(records)) {
            records = Array.prototype.slice.apply(arguments);
        }

        return this.insert(this.data.length, records);
    },

    /**
     * Inserts Model instances into the Store at the given index and fires the {@link #add} event.
     * See also <code>{@link #add}</code>.
     * @param {Number} index The start index at which to insert the passed Records.
     * @param {Ext.data.Model[]} records An Array of Ext.data.Model objects to add to the cache.
     */
    insert: function(index, records) {
        if (!Ext.isArray(records)) {
            records = Array.prototype.slice.call(arguments, 1);
        }

        var me = this,
            sync = false,
            ln = records.length,
            Model = this.getModel(),
            modelDefaults = me.getModelDefaults(),
            i, record, added = false;

        records = records.slice();

        for (i = 0; i < ln; i++) {
            record = records[i];
            if (!record.isModel) {
                record = new Model(record);
            }
            // If we are adding a record that is already an instance which was still in the
            // removed array, then we remove it from the removed array
            else if (this.removed.indexOf(record) != -1) {
                Ext.Array.remove(this.removed, record);
            }

            record.set(modelDefaults);

            // reassign the model in the array in case it wasn't created yet
            records[i] = record;
            record.join(me);

            // If this is a newly created record, then we might want to sync it later
            sync = sync || (record.phantom === true);
        }

        // Now we insert all these records in one go to the collection. Saves many function
        // calls to data.insert. Does however create two loops over the records we are adding.
        if (ln === 1) {
            added = this.data.insert(index, records[0]);
            if (added) {
                added = [added];
            }
        } else {
            added = this.data.insertAll(index, records);
        }

        if (added) {
            me.fireEvent('addrecords', me, added);
        }

        if (me.getAutoSync() && sync) {
            me.sync();
        }

        return records;
    },

    /**
     * Removes the given record from the Store, firing the 'removerecords' event passing all the instances that are removed.
     * @param {Ext.data.Model/Ext.data.Model[]} records Model instance or array of instances to remove
     */
    remove: function(records) {
        if (records.isModel) {
            records = [records];
        }

        var me = this,
            sync = false,
            i = 0,
            autoSync = this.getAutoSync(),
            ln = records.length,
            indices = [],
            removed = [],
            isPhantom,
            items = me.data.items,
            record, index, j;

        for (; i < ln; i++) {
            record = records[i];

            if (me.data.contains(record)) {
                isPhantom = (record.phantom === true);

                index = items.indexOf(record);
                if (index !== -1) {
                    removed.push(record);
                    indices.push(index);
                }

                if (!isPhantom && me.getSyncRemovedRecords()) {
                     // don't push phantom records onto removed
                     me.removed.push(record);
                }

                record.unjoin(me);

                me.data.remove(record);
                sync = sync || !isPhantom;
            }
        }

        me.fireEvent('removerecords', me, removed, indices);

        if (autoSync && sync) {
            me.sync();
        }
    },

    /**
     * Removes the model instance at the given index
     * @param {Number} index The record index
     */
    removeAt: function(index) {
        var record = this.getAt(index);

        if (record) {
            this.remove(record);
        }
    },

    /**
     * Remove all items from the store.
     * @param {Boolean} silent Prevent the `clear` event from being fired.
     */
    removeAll: function(silent) {
        if (silent !== true) {
            this.fireAction('clear', [this], 'doRemoveAll');
        } else {
            this.doRemoveAll.call(this, true);
        }
    },

    doRemoveAll: function(silent) {
        var me = this;
        me.data.each(function(record) {
            record.unjoin(me);
        });
        if (me.getSyncRemovedRecords()) {
            me.removed = me.removed.concat(me.data.items);
        }
        me.data.clear();

        if (silent !== true) {
            me.fireEvent('refresh', me, me.data);
        }

        if (me.getAutoSync()) {
            this.sync();
        }
    },

    /**
     * Calls the specified function for each of the {@link Ext.data.Model Records} in the cache.
     * @param {Function} fn The function to call. The {@link Ext.data.Model Record} is passed as the first parameter.
     * Returning <tt>false</tt> aborts and exits the iteration.
     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed.
     * Defaults to the current {@link Ext.data.Model Record} in the iteration.
     */
    each: function(fn, scope) {
        this.data.each(fn, scope);
    },

    /**
     * Gets the number of cached records.
     * If using paging, this may not be the total size of the dataset.
     * @return {Number} The number of Records in the Store's cache.
     */
    getCount: function() {
        return this.data.length || 0;
    },

    /**
     * Get the Record at the specified index.
     * @param {Number} index The index of the Record to find.
     * @return {Ext.data.Model} The Record at the passed index. Returns undefined if not found.
     */
    getAt: function(index) {
        return this.data.getAt(index);
    },

    /**
     * Returns a range of Records between specified indices.
     * @param {Number} startIndex (optional) The starting index (defaults to 0)
     * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
     * @return {Ext.data.Model[]} An array of Records
     */
    getRange: function(start, end) {
        return this.data.getRange(start, end);
    },

    /**
     * Get the Record with the specified id.
     * @param {String} id The id of the Record to find.
     * @return {Ext.data.Model} The Record with the passed id. Returns undefined if not found.
     */
    getById: function(id) {
        return this.data.findBy(function(record) {
            return record.getId() == id;
        });
    },

    /**
     * Get the index within the cache of the passed Record.
     * @param {Ext.data.Model} record The Ext.data.Model object to find.
     * @return {Number} The index of the passed Record. Returns -1 if not found.
     */
    indexOf: function(record) {
        return this.data.indexOf(record);
    },

    /**
     * Get the index within the cache of the Record with the passed id.
     * @param {String} id The id of the Record to find.
     * @return {Number} The index of the Record. Returns -1 if not found.
     */
    indexOfId: function(id) {
        return this.data.indexOfKey(id);
    },

    /**
     * @private
     * A model instance should call this method on the Store it has been {@link Ext.data.Model#join joined} to.
     * @param {Ext.data.Model} record The model instance that was edited
     * @param {String[]} modifiedFieldNames Array of field names changed during edit.
     */
    afterEdit: function(record, modifiedFieldNames, modified) {
        var me = this,
            data = me.data,
            currentId = modified[record.getIdProperty()] || record.getId(),
            currentIndex = data.keys.indexOf(currentId),
            newIndex;

        if (currentIndex === -1 && data.map[currentId] === undefined) {
            return;
        }

        if (me.getAutoSync()) {
            me.sync();
        }

        if (currentId !== record.getId()) {
            data.replace(currentId, record);
        } else {
            data.replace(record);
        }

        newIndex = data.indexOf(record);
        if (currentIndex === -1 && newIndex !== -1) {
            me.fireEvent('addrecords', me, [record]);
        }
        else if (currentIndex !== -1 && newIndex === -1) {
            me.fireEvent('removerecords', me, [record], [currentIndex]);
        }
        else if (newIndex !== -1) {
            me.fireEvent('updaterecord', me, record, newIndex, currentIndex, modifiedFieldNames, modified);
        }
    },

    /**
     * @private
     * A model instance should call this method on the Store it has been {@link Ext.data.Model#join joined} to..
     * @param {Ext.data.Model} record The model instance that was edited
     */
    afterReject: function(record) {
        var index = this.data.indexOf(record);
        this.fireEvent('updaterecord', this, record, index, index, [], {});
    },

    /**
     * @private
     * A model instance should call this method on the Store it has been {@link Ext.data.Model#join joined} to.
     * @param {Ext.data.Model} record The model instance that was edited
     */
    afterCommit: function(record, modifiedFieldNames, modified) {
        var me = this,
            data = me.data,
            currentId = modified[record.getIdProperty()] || record.getId(),
            currentIndex = data.keys.indexOf(currentId),
            newIndex;

        if (currentIndex === -1 && data.map[currentId] === undefined) {
            return;
        }

        if (currentId !== record.getId()) {
            data.replace(currentId, record);
        } else {
            data.replace(record);
        }

        newIndex = data.indexOf(record);
        if (currentIndex === -1 && newIndex !== -1) {
            me.fireEvent('addrecords', me, [record]);
        }
        else if (currentIndex !== -1 && newIndex === -1) {
            me.fireEvent('removerecords', me, [record], [currentIndex]);
        }
        else if (newIndex !== -1) {
            me.fireEvent('updaterecord', me, record, newIndex, currentIndex, modifiedFieldNames, modified);
        }
    },

    /**
     * This gets called by a record after is gets erased from the server.
     * @param record
     * @private
     */
    afterErase: function(record) {
        var me = this,
            data = me.data,
            index = data.indexOf(record);

        if (index !== -1) {
            data.remove(record);
            me.fireEvent('removerecords', me, [record], [index]);
        }
    },

    updateRemoteFilter: function(remoteFilter) {
        this.data.setAutoFilter(!remoteFilter);
    },

    updateRemoteSort: function(remoteSort) {
        this.data.setAutoSort(!remoteSort);
    },

    /**
     * Sorts the data in the Store by one or more of its properties. Example usage:
     *
     *     // sort by a single field
     *     myStore.sort('myField', 'DESC');
     *
     *     // sorting by multiple fields
     *     myStore.sort([
     *         {
     *             property : 'age',
     *             direction: 'ASC'
     *         },
     *         {
     *             property : 'name',
     *             direction: 'DESC'
     *         }
     *     ]);
     *
     * Internally, Store converts the passed arguments into an array of {@link Ext.util.Sorter} instances, and delegates
     * the actual sorting to its internal {@link Ext.util.Collection}.
     *
     * When passing a single string argument to sort, Store maintains a ASC/DESC toggler per field, so this code:
     *
     *     store.sort('myField');
     *     store.sort('myField');
     *
     * is equivalent to this code:
     *
     *     store.sort('myField', 'ASC');
     *     store.sort('myField', 'DESC');
     *
     * because Store handles the toggling automatically.
     *
     * If the {@link #remoteSort} configuration has been set to true, you will have to manually call the {@link #method-load}
     * method after you sort to retrieve the sorted data from the server.
     *
     * @param {String/Ext.util.Sorter[]} sorters Either a string name of one of the fields in this Store's configured
     * {@link Ext.data.Model Model}, or an array of sorter configurations.
     * @param {String} defaultDirection The default overall direction to sort the data by. Defaults to "ASC".
     * @param {String} where (Optional) This can be either 'prepend' or 'append'. If you leave this undefined
     * it will clear the current sorters.
     */
    sort: function(sorters, defaultDirection, where) {
        var data = this.data,
            grouper = this.getGrouper(),
            autoSort = data.getAutoSort();

        if (sorters) {
            // While we are adding sorters we dont want to sort right away
            // since we need to update sortTypes on the sorters.
            data.setAutoSort(false);
            if (typeof where === 'string') {
                if (where == 'prepend') {
                    data.insertSorters(grouper ? 1 : 0, sorters, defaultDirection);
                } else {
                    data.addSorters(sorters, defaultDirection);
                }
            } else {
                data.setSorters(null);
                if (grouper) {
                    data.addSorters(grouper);
                }
                data.addSorters(sorters, defaultDirection);
            }
            this.updateSortTypes();
            // Setting back autoSort to true (if it was like that before) will
            // instantly sort the data again.
            data.setAutoSort(autoSort);
        }

        if (!this.getRemoteSort()) {
            // If we havent added any new sorters we have to manually call sort
            if (!sorters) {
                this.data.sort();
            }

            this.fireEvent('sort', this, this.data, this.data.getSorters());
            if (data.length) {
                this.fireEvent('refresh', this, this.data);
            }
        }
    },

    /**
     * Filters the loaded set of records by a given set of filters.
     *
     * Filtering by single field:
     *
     *     store.filter("email", /\.com$/);
     *
     * Using multiple filters:
     *
     *     store.filter([
     *         {property: "email", value: /\.com$/},
     *         {filterFn: function(item) { return item.get("age") > 10; }}
     *     ]);
     *
     * Using Ext.util.Filter instances instead of config objects
     * (note that we need to specify the {@link Ext.util.Filter#root root} config option in this case):
     *
     *     store.filter([
     *         Ext.create('Ext.util.Filter', {property: "email", value: /\.com$/, root: 'data'}),
     *         Ext.create('Ext.util.Filter', {filterFn: function(item) { return item.get("age") > 10; }, root: 'data'})
     *     ]);
     *
     * If the {@link #remoteFilter} configuration has been set to true, you will have to manually call the {@link #method-load}
     * method after you filter to retrieve the filtered data from the server.
     *
     * @param {Object[]/Ext.util.Filter[]/String} filters The set of filters to apply to the data.
     * These are stored internally on the store, but the filtering itself is done on the Store's
     * {@link Ext.util.MixedCollection MixedCollection}. See MixedCollection's
     * {@link Ext.util.MixedCollection#filter filter} method for filter syntax.
     * Alternatively, pass in a property string.
     * @param {String} [value] value to filter by (only if using a property string as the first argument).
     * @param {Boolean} [anyMatch=false] True to allow any match, false to anchor regex beginning with `^`.
     * @param {Boolean} [caseSensitive=false] True to make the filtering regex case sensitive.
     */
    filter: function(property, value, anyMatch, caseSensitive) {
        var data = this.data,
            ln = data.length;

        if (this.getRemoteFilter()) {
            if (property) {
                if (Ext.isString(property)) {
                    data.addFilters({
                        property     : property,
                        value        : value,
                        anyMatch     : anyMatch,
                        caseSensitive: caseSensitive
                    });
                }
                else if (Ext.isArray(property) || property.isFilter) {
                    data.addFilters(property);
                }
            }
        } else {
            data.filter(property, value);
            this.fireEvent('filter', this, data, data.getFilters());

            if (data.length !== ln) {
                this.fireEvent('refresh', this, data);
            }
        }
    },

    /**
     * Filter by a function. The specified function will be called for each
     * Record in this Store. If the function returns <tt>true</tt> the Record is included,
     * otherwise it is filtered out.
     * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
     * <li><b>record</b> : Ext.data.Model<p class="sub-desc">The {@link Ext.data.Model record}
     * to test for filtering. Access field values using {@link Ext.data.Model#get}.</p></li>
     * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
     * </ul>
     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this Store.
     */
    filterBy: function(fn, scope) {
        var me = this,
            data = me.data,
            ln = data.length;

        data.filter({
            filterFn: fn,
            scope: scope
        });

        this.fireEvent('filter', this, data, data.getFilters());

        if (data.length !== ln) {
            this.fireEvent('refresh', this, data);
        }
    },

    /**
     * Query the cached records in this Store using a filtering function. The specified function
     * will be called with each record in this Store. If the function returns <tt>true</tt> the record is
     * included in the results.
     * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
     * <li><b>record</b> : Ext.data.Model<p class="sub-desc">The {@link Ext.data.Model record}
     * to test for filtering. Access field values using {@link Ext.data.Model#get}.</p></li>
     * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
     * </ul>
     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this Store.
     * @return {Ext.util.MixedCollection} Returns an Ext.util.MixedCollection of the matched records
     **/
    queryBy: function(fn, scope) {
        return this.data.filterBy(fn, scope || this);
    },

    /**
     * Reverts to a view of the Record cache with no filtering applied.
     * @param {Boolean} [suppressEvent=false] True to clear silently without firing the `refresh` event.
     */
    clearFilter: function(suppressEvent) {
        var ln = this.data.length;
        if (suppressEvent) {
            this.suspendEvents();
        }
        this.data.setFilters(null);
        if (suppressEvent) {
            this.resumeEvents();
        } else if (ln !== this.data.length) {
            this.fireEvent('refresh', this, this.data);
        }
    },

    /**
     * Returns true if this store is currently filtered
     * @return {Boolean}
     */
    isFiltered: function() {
        return this.data.filtered;
    },

    getSorters: function() {
        var sorters = this.data.getSorters();
        return (sorters) ? sorters.items : [];
    },

    getFilters: function() {
        var filters = this.data.getFilters();
        return (filters) ? filters.items : [];
    },

    /**
     * Returns an array containing the result of applying the grouper to the records in this store. See {@link #groupField},
     * {@link #groupDir} and {@link #grouper}. Example for a store containing records with a color field:
     *
     *     var myStore = Ext.create('Ext.data.Store', {
     *         groupField: 'color',
     *         groupDir  : 'DESC'
     *     });
     *
     *     myStore.getGroups(); //returns:
     *     [
     *        {
     *            name: 'yellow',
     *            children: [
     *                //all records where the color field is 'yellow'
     *            ]
     *        },
     *        {
     *            name: 'red',
     *            children: [
     *                //all records where the color field is 'red'
     *            ]
     *        }
     *     ]
     *
     * @param {String} groupName (Optional) Pass in an optional groupName argument to access a specific group as defined by {@link #grouper}
     * @return {Object/Object[]} The grouped data
     */
    getGroups: function(requestGroupString) {
        var records = this.data.items,
            length = records.length,
            grouper = this.getGrouper(),
            groups = [],
            pointers = {},
            record,
            groupStr,
            group,
            i;

        if (!grouper) {
            Ext.Logger.error('Trying to get groups for a store that has no grouper');
        }

        for (i = 0; i < length; i++) {
            record = records[i];
            groupStr = grouper.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;
    },

    getGroupString: function(record) {
        var grouper = this.getGrouper();
        if (grouper) {
            return grouper.getGroupString(record);
        }
        return null;
    },

    /**
     * Finds the index of the first matching Record in this store by a specific field value.
     * @param {String} fieldName The name of the Record field to test.
     * @param {String/RegExp} value Either a string that the field value
     * should begin with, or a RegExp to test against the field.
     * @param {Number} startIndex (optional) The index to start searching at
     * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
     * @param {Boolean} caseSensitive (optional) True for case sensitive comparison
     * @param {Boolean} exactMatch (optional) True to force exact match (^ and $ characters added to the regex). Defaults to false.
     * @return {Number} The matched index or -1
     */
    find: function(fieldName, value, startIndex, anyMatch, caseSensitive, exactMatch) {
        var filter = Ext.create('Ext.util.Filter', {
            property: fieldName,
            value: value,
            anyMatch: anyMatch,
            caseSensitive: caseSensitive,
            exactMatch: exactMatch,
            root: 'data'
        });
        return this.data.findIndexBy(filter.getFilterFn(), null, startIndex);
    },

    /**
     * Finds the first matching Record in this store by a specific field value.
     * @param {String} fieldName The name of the Record field to test.
     * @param {String/RegExp} value Either a string that the field value
     * should begin with, or a RegExp to test against the field.
     * @param {Number} startIndex (optional) The index to start searching at
     * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
     * @param {Boolean} caseSensitive (optional) True for case sensitive comparison
     * @param {Boolean} exactMatch (optional) True to force exact match (^ and $ characters added to the regex). Defaults to false.
     * @return {Ext.data.Model} The matched record or null
     */
    findRecord: function() {
        var me = this,
            index = me.find.apply(me, arguments);
        return index !== -1 ? me.getAt(index) : null;
    },

    /**
     * Finds the index of the first matching Record in this store by a specific field value.
     * @param {String} fieldName The name of the Record field to test.
     * @param {Object} value The value to match the field against.
     * @param {Number} startIndex (optional) The index to start searching at
     * @return {Number} The matched index or -1
     */
    findExact: function(fieldName, value, startIndex) {
        return this.data.findIndexBy(function(record) {
            return record.get(fieldName) === value;
        }, this, startIndex);
    },

    /**
     * Find the index of the first matching Record in this Store by a function.
     * If the function returns <tt>true</tt> it is considered a match.
     * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
     * <li><b>record</b> : Ext.data.Model<p class="sub-desc">The {@link Ext.data.Model record}
     * to test for filtering. Access field values using {@link Ext.data.Model#get}.</p></li>
     * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
     * </ul>
     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this Store.
     * @param {Number} startIndex (optional) The index to start searching at
     * @return {Number} The matched index or -1
     */
    findBy: function(fn, scope, startIndex) {
        return this.data.findIndexBy(fn, scope, startIndex);
    },

    /**
     * Loads data into the Store via the configured {@link #proxy}. This uses the Proxy to make an
     * asynchronous call to whatever storage backend the Proxy uses, automatically adding the retrieved
     * instances into the Store and calling an optional callback if required. Example usage:
     *
     *     store.load({
     *         callback: function(records, operation, success) {
     *             // the {@link Ext.data.Operation operation} object contains all of the details of the load operation
     *             console.log(records);
     *         },
     *         scope: this
     *     });
     *
     * If only the callback and scope options need to be specified, then one can call it simply like so:
     *
     *     store.load(function(records, operation, success) {
     *         console.log('loaded records');
     *     }, this);
     *
     * @param {Object/Function} [options] config object, passed into the Ext.data.Operation object before loading.
     * @param {Object} [scope] Scope for the function.
     */
    load: function(options, scope) {
        var me = this,
            operation,
            currentPage = me.currentPage,
            pageSize = me.getPageSize();

        options = options || {};

        if (Ext.isFunction(options)) {
            options = {
                callback: options,
                scope: scope || this
            };
        }

        if (me.getRemoteSort()) {
            options.sorters = options.sorters || this.getSorters();
        }

        if (me.getRemoteFilter()) {
            options.filters = options.filters || this.getFilters();
        }

        if (me.getRemoteGroup()) {
            options.grouper = options.grouper || this.getGrouper();
        }

        Ext.applyIf(options, {
            page: currentPage,
            start: (currentPage - 1) * pageSize,
            limit: pageSize,
            addRecords: false,
            action: 'read',
            model: this.getModel()
        });

        operation = Ext.create('Ext.data.Operation', options);

        if (me.fireEvent('beforeload', me, operation) !== false) {
            me.loading = true;
            me.getProxy().read(operation, me.onProxyLoad, me);
        }

        return me;
    },

    /**
     * Returns true if the Store is currently performing a load operation
     * @return {Boolean} True if the Store is currently loading
     */
    isLoading: function() {
        return Boolean(this.loading);
    },

    /**
     * Returns true if the Store has been loaded.
     * @return {Boolean} True if the Store has been loaded
     */
    isLoaded: function() {
        return Boolean(this.loaded);
    },

    /**
     * Synchronizes the Store with its Proxy. This asks the Proxy to batch together any new, updated
     * and deleted records in the store, updating the Store's internal representation of the records
     * as each operation completes.
     */
    sync: function() {
        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', this, operations) !== false) {
            me.getProxy().batch({
                operations: operations,
                listeners: me.getBatchListeners()
            });
        }

        return {
            added: toCreate,
            updated: toUpdate,
            removed: toDestroy
        };
    },

    /**
     * Convenience function for getting the first model instance in the store
     * @return {Ext.data.Model/undefined} The first model instance in the store, or undefined
     */
    first: function() {
        return this.data.first();
    },

    /**
     * Convenience function for getting the last model instance in the store
     * @return {Ext.data.Model/undefined} The last model instance in the store, or undefined
     */
    last: function() {
        return this.data.last();
    },

    /**
     * Sums the value of <tt>property</tt> for each {@link Ext.data.Model record} between <tt>start</tt>
     * and <tt>end</tt> and returns the result.
     * @param {String} field The field in each record
     * @return {Number} The sum
     */
    sum: function(field) {
        var total = 0,
            i = 0,
            records = this.data.items,
            len = records.length;

        for (; i < len; ++i) {
            total += records[i].get(field);
        }

        return total;
    },

    /**
     * Gets the minimum value in the store.
     * @param {String} field The field in each record
     * @return {Object} The minimum value, if no items exist, undefined.
     */
    min: function(field) {
        var i = 1,
            records = this.data.items,
            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;
    },

    /**
     * Gets the maximum value in the store.
     * @param {String} field The field in each record
     * @return {Object} The maximum value, if no items exist, undefined.
     */
    max: function(field) {
        var i = 1,
            records = this.data.items,
            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;
    },

    /**
     * Gets the average value in the store.
     * @param {String} field The field in each record you want to get the average for.
     * @return {Object} The average value, if no items exist, 0.
     */
    average: function(field) {
        var i = 0,
            records = this.data.items,
            len = records.length,
            sum = 0;

        if (records.length > 0) {
            for (; i < len; ++i) {
                sum += records[i].get(field);
            }
            return sum / len;
        }
        return 0;
    },

    /**
     * @private
     * Returns an object which is passed in as the listeners argument to proxy.batch inside this.sync.
     * This is broken out into a separate function to allow for customisation of the listeners
     * @return {Object} The listeners object
     */
    getBatchListeners: function() {
        return {
            scope: this,
            exception: this.onBatchException,
            complete: this.onBatchComplete
        };
    },

    /**
     * @private
     * Attached as the 'complete' event listener to a proxy's Batch object. Iterates over the batch operations
     * and updates the Store's internal data MixedCollection.
     */
    onBatchComplete: function(batch) {
        var me = this,
            operations = batch.operations,
            length = operations.length,
            i;

        for (i = 0; i < length; i++) {
            me.onProxyWrite(operations[i]);
        }
    },

    onBatchException: function(batch, operation) {
        // //decide what to do... could continue with the next operation
        // batch.start();
        //
        // //or retry the last operation
        // batch.retry();
    },

    /**
     * @private
     * Called internally when a Proxy has completed a load request
     */
    onProxyLoad: function(operation) {
        var me = this,
            records = operation.getRecords(),
            resultSet = operation.getResultSet(),
            successful = operation.wasSuccessful();

        if (resultSet) {
            me.setTotalCount(resultSet.getTotal());
        }

        if (successful) {
            if (operation.getAddRecords() !== true) {
                me.data.each(function(record) {
                    record.unjoin(me);
                });
                me.data.clear();

                // This means we have to fire a clear event though
                me.fireEvent('clear', this);
            }

            if (records && records.length) {
                // Now lets add the records without firing an addrecords event
                me.suspendEvents();
                me.add(records);
                me.resumeEvents();
            }

            // And finally fire a refresh event so any bound view can fully refresh itself
            me.fireEvent('refresh', this, this.data);
        }

        me.loaded = true;
        me.loading = false;
        me.fireEvent('load', this, records, successful);

        //this is a callback that would have been passed to the 'read' function and is optional
        Ext.callback(operation.getCallback(), operation.getScope() || me, [records, operation, successful]);
    },

    /**
     * @private
     * Callback for any write Operation over the Proxy. Updates the Store's MixedCollection to reflect
     * the updates provided by the Proxy
     */
    onProxyWrite: function(operation) {
        var me = this,
            success = operation.wasSuccessful(),
            records = operation.getRecords();

        switch (operation.getAction()) {
            case 'create':
                me.onCreateRecords(records, operation, success);
                break;
            case 'update':
                me.onUpdateRecords(records, operation, success);
                break;
            case 'destroy':
                me.onDestroyRecords(records, operation, success);
                break;
        }

        if (success) {
            me.fireEvent('write', me, operation);
        }
        //this is a callback that would have been passed to the 'create', 'update' or 'destroy' function and is optional
        Ext.callback(operation.getCallback(), operation.getScope() || me, [records, operation, success]);
    },

    // These methods are now just template methods since updating the records etc is all taken care of
    // by the operation itself.
    onCreateRecords: function(records, operation, success) {},
    onUpdateRecords: function(records, operation, success) {},

    onDestroyRecords: function(records, operation, success) {
        this.removed = [];
    },

    onMetaChange: function(data) {
        var model = this.getProxy().getModel();
        if (!this.getModel() && model) {
            this.setModel(model);
        }

        /**
         * @event metachange
         * Fires whenever the server has sent back new metadata to reconfigure the Reader.
         * @param {Ext.data.Store} this
         * @param {Object} data The metadata sent back from the server
         */
        this.fireEvent('metachange', this, data);
    },

    /**
     * Returns all Model instances that are either currently a phantom (e.g. have no id), or have an ID but have not
     * yet been saved on this Store (this happens when adding a non-phantom record from another Store into this one).
     * @return {Ext.data.Model[]} The Model instances.
     */
    getNewRecords: function() {
        return this.data.filterBy(function(item) {
            // only want phantom records that are valid
            return item.phantom === true && item.isValid();
        }).items;
    },

    /**
     * Returns all Model instances that have been updated in the Store but not yet synchronized with the Proxy.
     * @return {Ext.data.Model[]} The updated Model instances.
     */
    getUpdatedRecords: function() {
        return this.data.filterBy(function(item) {
            // only want dirty records, not phantoms that are valid
            return item.dirty === true && item.phantom !== true && item.isValid();
        }).items;
    },

    /**
     * Returns any records that have been removed from the store but not yet destroyed on the proxy.
     * @return {Ext.data.Model[]} The removed Model instances
     */
    getRemovedRecords: function() {
        return this.removed;
    },

    // PAGING METHODS
    /**
     * Loads a given 'page' of data by setting the start and limit values appropriately. Internally this just causes a normal
     * load operation, passing in calculated 'start' and 'limit' params
     * @param {Number} page The number of the page to load
     * @param {Object} options See options for {@link #method-load}
     */
    loadPage: function(page, options, scope) {
        if (typeof options === 'function') {
            options = {
                callback: options,
                scope: scope || this
            };

        }
        var me = this,
            pageSize = me.getPageSize(),
            clearOnPageLoad = me.getClearOnPageLoad();

        options = Ext.apply({}, options);

        me.currentPage = page;

        me.load(Ext.applyIf(options, {
            page: page,
            start: (page - 1) * pageSize,
            limit: pageSize,
            addRecords: !clearOnPageLoad
        }));
    },

    /**
     * Loads the next 'page' in the current data set
     * @param {Object} options See options for {@link #method-load}
     */
    nextPage: function(options) {
        this.loadPage(this.currentPage + 1, options);
    },

    /**
     * Loads the previous 'page' in the current data set
     * @param {Object} options See options for {@link #method-load}
     */
    previousPage: function(options) {
        this.loadPage(this.currentPage - 1, options);
    }

});

/**
 * @author Ed Spencer
 *
 * Small helper class to make creating {@link Ext.data.Store}s from Array data easier. An ArrayStore will be
 * automatically configured with a {@link Ext.data.reader.Array}.
 *
 * A store configuration would be something like:
 *
 *     var store = Ext.create('Ext.data.ArrayStore', {
 *         // store configs
 *         autoDestroy: true,
 *         storeId: 'myStore',
 *         // reader configs
 *         idIndex: 0,
 *         fields: [
 *            'company',
 *            {name: 'price', type: 'float'},
 *            {name: 'change', type: 'float'},
 *            {name: 'pctChange', type: 'float'},
 *            {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
 *         ]
 *     });
 *
 * This store is configured to consume a returned object of the form:
 *
 *     var myData = [
 *         ['3m Co',71.72,0.02,0.03,'9/1 12:00am'],
 *         ['Alcoa Inc',29.01,0.42,1.47,'9/1 12:00am'],
 *         ['Boeing Co.',75.43,0.53,0.71,'9/1 12:00am'],
 *         ['Hewlett-Packard Co.',36.53,-0.03,-0.08,'9/1 12:00am'],
 *         ['Wal-Mart Stores, Inc.',45.45,0.73,1.63,'9/1 12:00am']
 *     ];
 *
 * An object literal of this form could also be used as the {@link #data} config option.
 *
 * **Note:** Although not listed here, this class accepts all of the configuration options of
 * **{@link Ext.data.reader.Array ArrayReader}**.
 */
Ext.define('Ext.data.ArrayStore', {
    extend: 'Ext.data.Store',
    alias: 'store.array',
    uses: ['Ext.data.reader.Array'],

    config: {
        proxy: {
            type: 'memory',
            reader: 'array'
        }
    },

    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() {
    // backwards compat
    Ext.data.SimpleStore = Ext.data.ArrayStore;
    // Ext.reg('simplestore', Ext.data.SimpleStore);
});

/**
 * Small helper class to create an {@link Ext.data.Store} configured with an {@link Ext.data.proxy.Direct}
 * and {@link Ext.data.reader.Json} to make interacting with an {@link Ext.direct.Manager} server-side
 * {@link Ext.direct.Provider Provider} easier. To create a different proxy/reader combination create a basic
 * {@link Ext.data.Store} configured as needed.
 *
 * Since configurations are deeply merged with the standard configuration, you can override certain proxy and
 * reader configurations like this:
 *
 *     Ext.create('Ext.data.DirectStore', {
 *         proxy: {
 *             paramsAsHash: true,
 *             directFn: someDirectFn,
 *             simpleSortMode: true
 *             reader: {
 *                 rootProperty: 'results',
 *                 idProperty: '_id'
 *             }
 *         }
 *     });
 *
 */
Ext.define('Ext.data.DirectStore', {
    extend: 'Ext.data.Store',
    alias: 'store.direct',
    requires: ['Ext.data.proxy.Direct'],

    config: {
        proxy: {
            type: 'direct',
            reader: {
                type: 'json'
            }
        }
    }
});

/**
 * @author Ed Spencer
 * @class Ext.data.JsonStore
 * @extends Ext.data.Store
 * @private
 *
 * <p>Small helper class to make creating {@link Ext.data.Store}s from JSON data easier.
 * A JsonStore will be automatically configured with a {@link Ext.data.reader.Json}.</p>
 *
 * <p>A store configuration would be something like:</p>
 *
<pre><code>
var store = new Ext.data.JsonStore({
    // store configs
    autoDestroy: true,
    storeId: 'myStore'

    proxy: {
        type: 'ajax',
        url: 'get-images.php',
        reader: {
            type: 'json',
            root: 'images',
            idProperty: 'name'
        }
    },

    //alternatively, a {@link Ext.data.Model} name can be given (see {@link Ext.data.Store} for an example)
    fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
});
</code></pre>
 *
 * <p>This store is configured to consume a returned object of the form:<pre><code>
{
    images: [
        {name: 'Image one', url:'/GetImage.php?id=1', size:46.5, lastmod: new Date(2007, 10, 29)},
        {name: 'Image Two', url:'/GetImage.php?id=2', size:43.2, lastmod: new Date(2007, 10, 30)}
    ]
}
</code></pre>
 *
 * <p>An object literal of this form could also be used as the {@link #data} config option.</p>
 *
 * @xtype jsonstore
 */
Ext.define('Ext.data.JsonStore',  {
    extend: 'Ext.data.Store',
    alias: 'store.json',

    config: {
        proxy: {
            type: 'ajax',
            reader: 'json',
            writer: 'json'
        }
    }
});

/**
 * @private
 */
Ext.define('Ext.data.NodeStore', {
    extend: 'Ext.data.Store',
    alias: 'store.node',
    requires: ['Ext.data.NodeInterface'],

    config: {
        /**
         * @cfg {Ext.data.Model} node The Record you want to bind this Store to. Note that
         * this record will be decorated with the Ext.data.NodeInterface if this is not the
         * case yet.
         * @accessor
         */
        node: null,

        /**
         * @cfg {Boolean} recursive Set this to true if you want this NodeStore to represent
         * all the descendents of the node in its flat data collection. This is useful for
         * rendering a tree structure to a DataView and is being used internally by
         * the TreeView. Any records that are moved, removed, inserted or appended to the
         * node at any depth below the node this store is bound to will be automatically
         * updated in this Store's internal flat data structure.
         * @accessor
         */
        recursive: false,

        /**
         * @cfg {Boolean} rootVisible False to not include the root node in this Stores collection.
         * @accessor
         */
        rootVisible: false,

        sorters: undefined,
        filters: undefined,

        /**
         * @cfg {Boolean} folderSort
         * Set to true to automatically prepend a leaf sorter. Defaults to `undefined`.
         */
        folderSort: false
    },

    afterEdit: function(record, modifiedFields) {
        if (modifiedFields) {
            if (modifiedFields.indexOf('loaded') !== -1) {
                return this.add(this.retrieveChildNodes(record));
            }
            if (modifiedFields.indexOf('expanded') !== -1) {
                return this.filter();
            }
            if (modifiedFields.indexOf('sorted') !== -1) {
                return this.sort();
            }
        }
        this.callParent(arguments);
    },

    onNodeAppend: function(parent, node) {
        this.add([node].concat(this.retrieveChildNodes(node)));
    },

    onNodeInsert: function(parent, node) {
        this.add([node].concat(this.retrieveChildNodes(node)));
    },

    onNodeRemove: function(parent, node) {
        this.remove([node].concat(this.retrieveChildNodes(node)));
    },

    onNodeSort: function() {
        this.sort();
    },

    updateFolderSort: function(folderSort) {
        if (folderSort) {
            this.setGrouper(function(node) {
                if (node.isLeaf()) {
                    return 1;
                }
                return 0;
            });
        } else {
            this.setGrouper(null);
        }
    },

    createDataCollection: function() {
        var collection = this.callParent();
        collection.handleSort = Ext.Function.bind(this.handleTreeSort, this, [collection], true);
        collection.findInsertionIndex = Ext.Function.bind(this.handleTreeInsertionIndex, this, [collection, collection.findInsertionIndex], true);
        return collection;
    },

    handleTreeInsertionIndex: function(items, item, collection, originalFn) {
        return originalFn.call(collection, items, item, this.treeSortFn);
    },

    handleTreeSort: function(data) {
        Ext.Array.sort(data, this.treeSortFn);
        return data;
    },

    /**
     * This is a custom tree sorting algorithm. It uses the index property on each node to determine
     * how to sort siblings. It uses the depth property plus the index to create a weight for each node.
     * This weight algorithm has the limitation of not being able to go more then 80 levels in depth, or
     * more then 10k nodes per parent. The end result is a flat collection being correctly sorted based
     * on this one single sort function.
     * @param node1
     * @param node2
     * @private
     */
    treeSortFn: function(node1, node2) {
        // A shortcut for siblings
        if (node1.parentNode === node2.parentNode) {
            return (node1.data.index < node2.data.index) ? -1 : 1;
        }

        // @NOTE: with the following algorithm we can only go 80 levels deep in the tree
        // and each node can contain 10000 direct children max
        var weight1 = 0,
            weight2 = 0,
            parent1 = node1,
            parent2 = node2;

        while (parent1) {
            weight1 += (Math.pow(10, (parent1.data.depth+1) * -4) * (parent1.data.index+1));
            parent1 = parent1.parentNode;
        }
        while (parent2) {
            weight2 += (Math.pow(10, (parent2.data.depth+1) * -4) * (parent2.data.index+1));
            parent2 = parent2.parentNode;
        }

        if (weight1 > weight2) {
            return 1;
        } else if (weight1 < weight2) {
            return -1;
        }
        return (node1.data.index > node2.data.index) ? 1 : -1;
    },

    applyFilters: function(filters) {
        var me = this;
        return function(item) {
            return me.isVisible(item);
        };
    },

    applyProxy: function(proxy) {
        if (proxy) {
            Ext.Logger.warn("A NodeStore cannot be bound to a proxy. Instead bind it to a record " +
                            "decorated with the NodeInterface by setting the node config.");
        }
    },

    applyNode: function(node) {
        if (node) {
            node = Ext.data.NodeInterface.decorate(node);
        }
        return node;
    },

    updateNode: function(node, oldNode) {
        if (oldNode) {
            oldNode.un({
                append  : 'onNodeAppend',
                insert  : 'onNodeInsert',
                remove  : 'onNodeRemove',
                load    : 'onNodeLoad',
                scope: this
            });
            oldNode.unjoin(this);
        }

        if (node) {
            node.on({
                scope   : this,
                append  : 'onNodeAppend',
                insert  : 'onNodeInsert',
                remove  : 'onNodeRemove',
                load    : 'onNodeLoad'
            });

            node.join(this);

            var data = [];
            if (node.childNodes.length) {
                data = data.concat(this.retrieveChildNodes(node));
            }
            if (this.getRootVisible()) {
                data.push(node);
            } else if (node.isLoaded() || node.isLoading()) {
                node.set('expanded', true);
            }

            this.data.clear();
            this.fireEvent('clear', this);

            this.suspendEvents();
            this.add(data);
            this.resumeEvents();

            this.fireEvent('refresh', this, this.data);
        }
    },

    /**
     * Private method used to deeply retrieve the children of a record without recursion.
     * @private
     * @param parent
     */
    retrieveChildNodes: function(root) {
        var node = this.getNode(),
            recursive = this.getRecursive(),
            added = [],
            child = root;

        if (!root.childNodes.length || (!recursive && root !== node)) {
            return added;
        }

        if (!recursive) {
            return root.childNodes;
        }

        while (child) {
            if (child._added) {
                delete child._added;
                if (child === root) {
                    break;
                } else {
                    child = child.nextSibling || child.parentNode;
                }
            } else {
                if (child !== root) {
                    added.push(child);
                }
                if (child.firstChild) {
                    child._added = true;
                    child = child.firstChild;
                } else {
                    child = child.nextSibling || child.parentNode;
                }
            }
        }

        return added;
    },

    isVisible: function(node) {
        var parent = node.parentNode;
        while (parent) {
            if (!parent.isExpanded()) {
                return false;
            }

            //we need to check this because for a nodestore the node is not likely to be the root
            //so we stop going up the chain when we hit the original node as we don't care about any
            //ancestors above the configured node
            if (parent === this.getNode()) {
                break;
            }

            parent = parent.parentNode;
        }
        return true;
    }
});

/**
 * The TreeStore is a store implementation that allows for nested data.
 *
 * It provides convenience methods for loading nodes, as well as the ability to use
 * the hierarchical tree structure combined with a store. This class also relays many events from
 * the Tree for convenience.
 *
 * # Using Models
 *
 * If no Model is specified, an implicit model will be created that implements {@link Ext.data.NodeInterface}.
 * The standard Tree fields will also be copied onto the Model for maintaining their state. These fields are listed
 * in the {@link Ext.data.NodeInterface} documentation.
 *
 * # Reading Nested Data
 *
 * For the tree to read nested data, the {@link Ext.data.reader.Reader} must be configured with a root property,
 * so the reader can find nested data for each node. If a root is not specified, it will default to
 * 'children'.
 */
Ext.define('Ext.data.TreeStore', {
    extend: 'Ext.data.NodeStore',
    alias: 'store.tree',

    config: {
        /**
         * @cfg {Ext.data.Model/Ext.data.NodeInterface/Object} root
         * The root node for this store. For example:
         *
         *     root: {
         *         expanded: true,
         *         text: "My Root",
         *         children: [
         *             { text: "Child 1", leaf: true },
         *             { text: "Child 2", expanded: true, children: [
         *                 { text: "GrandChild", leaf: true }
         *             ] }
         *         ]
         *     }
         *
         * Setting the `root` config option is the same as calling {@link #setRootNode}.
         * @accessor
         */
        root: undefined,

        /**
         * @cfg {Boolean} clearOnLoad
         * Remove previously existing child nodes before loading. Default to true.
         * @accessor
         */
        clearOnLoad : true,

        /**
         * @cfg {String} nodeParam
         * The name of the parameter sent to the server which contains the identifier of the node.
         * Defaults to 'node'.
         * @accessor
         */
        nodeParam: 'node',

        /**
         * @cfg {String} defaultRootId
         * The default root id. Defaults to 'root'
         * @accessor
         */
        defaultRootId: 'root',

        /**
         * @cfg {String} defaultRootProperty
         * The root property to specify on the reader if one is not explicitly defined.
         * @accessor
         */
        defaultRootProperty: 'children',

        /**
         * @cfg {Boolean} recursive
         * @private
         * @hide
         */
        recursive: true

        /**
         * @cfg {Object} node
         * @private
         * @hide
         */
    },

    applyProxy: function() {
        return Ext.data.Store.prototype.applyProxy.apply(this, arguments);
    },

    applyRoot: function(root) {
        var me = this;
        root = root || {};
        root = Ext.apply({}, root);

        if (!root.isModel) {
            Ext.applyIf(root, {
                id: me.getDefaultRootId(),
                text: 'Root',
                allowDrag: false
            });

            root = Ext.data.ModelManager.create(root, me.getModel());
        }

        Ext.data.NodeInterface.decorate(root);
        root.set(root.raw);

        return root;
    },

    handleTreeInsertionIndex: function(items, item, collection, originalFn) {
        if (item.parentNode) {
            item.parentNode.sort(collection.getSortFn(), true, true);
        }
        return this.callParent(arguments);
    },

    handleTreeSort: function(data, collection) {
        if (this._sorting) {
            return data;
        }

        this._sorting = true;
        this.getNode().sort(collection.getSortFn(), true, true);
        delete this._sorting;
        return this.callParent(arguments);
    },

    updateRoot: function(root, oldRoot) {
        if (oldRoot) {
            oldRoot.unBefore({
                expand: 'onNodeBeforeExpand',
                scope: this
            });
            oldRoot.unjoin(this);
        }

        root.onBefore({
            expand: 'onNodeBeforeExpand',
            scope: this
        });

        this.onNodeAppend(null, root);
        this.setNode(root);

        if (!root.isLoaded() && !root.isLoading() && root.isExpanded()) {
            this.load({
                node: root
            });
        }

        /**
         * @event rootchange
         * Fires whenever the root node changes on this TreeStore.
         * @param {Ext.data.TreeStore} store This tree Store
         * @param {Ext.data.Model} newRoot The new root node
         * @param {Ext.data.Model} oldRoot The old root node
         */
        this.fireEvent('rootchange', this, root, oldRoot);
    },

    /**
     * Returns the record node by id
     * @return {Ext.data.NodeInterface}
     */
    getNodeById: function(id) {
        return this.data.getByKey(id);
    },

    onNodeBeforeExpand: function(node, options, e) {
        if (node.isLoading()) {
            e.pause();
            this.on('load', function() {
                e.resume();
            }, this, {single: true});
        }
        else if (!node.isLoaded()) {
            e.pause();
            this.load({
                node: node,
                callback: function() {
                    e.resume();
                }
            });
        }
    },

    onNodeAppend: function(parent, node) {
        var proxy = this.getProxy(),
            reader = proxy.getReader(),
            data = node.raw,
            records = [],
            rootProperty = reader.getRootProperty(),
            dataRoot, processedData, i, ln;

        if (!node.isLeaf()) {
            dataRoot = reader.getRoot(data);
            if (dataRoot) {
                processedData = reader.extractData(dataRoot);
                for (i = 0, ln = processedData.length; i < ln; i++) {
                    if (processedData[i].node[rootProperty]) {
                        processedData[i].data[rootProperty] = processedData[i].node[rootProperty];
                    }
                    records.push(processedData[i].data);
                }
                if (records.length) {
                    this.fillNode(node, records);
                }
                delete data[rootProperty];
            }
        }
    },

    updateAutoLoad: function(autoLoad) {
        if (autoLoad) {
            var root = this.getRoot();
            if (!root.isLoaded() && !root.isLoading()) {
                this.load({node: root});
            }
        }
    },

    /**
     * Loads the Store using its configured {@link #proxy}.
     * @param {Object} options (Optional) config object. This is passed into the {@link Ext.data.Operation Operation}
     * object that is created and then sent to the proxy's {@link Ext.data.proxy.Proxy#read} function.
     * The options can also contain a node, which indicates which node is to be loaded. If not specified, it will
     * default to the root node.
     */
    load: function(options) {
        options = options || {};
        options.params = options.params || {};

        var me = this,
            node = options.node = options.node || me.getRoot();

        options.params[me.getNodeParam()] = node.getId();

        if (me.getClearOnLoad()) {
            node.removeAll(true);
        }
        node.set('loading', true);

        return me.callParent([options]);
    },

    updateProxy: function(proxy) {
        this.callParent(arguments);

        var reader = proxy.getReader();
        if (!reader.getRootProperty()) {
            reader.setRootProperty(this.getDefaultRootProperty());
            reader.buildExtractors();
        }
    },

    // inherit docs
    removeAll: function() {
        this.getRootNode().removeAll(true);
        this.callParent(arguments);
    },

    // inherit docs
    onProxyLoad: function(operation) {
        var me = this,
            records = operation.getRecords(),
            successful = operation.wasSuccessful(),
            node = operation.getNode();

        node.beginEdit();
        node.set('loading', false);
        if (successful) {
            records = me.fillNode(node, records);
        }
        node.endEdit();

        node.fireEvent('load', node, records, successful);

        me.loading = false;
        me.fireEvent('load', this, records, successful);

        //this is a callback that would have been passed to the 'read' function and is optional
        Ext.callback(operation.getCallback(), operation.getScope() || me, [records, operation, successful]);
    },

    /**
     * Fills a node with a series of child records.
     * @private
     * @param {Ext.data.NodeInterface} node The node to fill
     * @param {Ext.data.Model[]} records The records to add
     */
    fillNode: function(node, records) {
        var ln = records ? records.length : 0,
            i, child;

        for (i = 0; i < ln; i++) {
            // true/true to suppress any events fired by the node, or the new child node
            child = node.appendChild(records[i], true, true);
            this.onNodeAppend(node, child);
        }
        node.set('loaded', true);

        return records;
    }

});

/**
 * @author Ed Spencer
 *
 * The Rest proxy is a specialization of the {@link Ext.data.proxy.Ajax AjaxProxy} which simply maps the four actions
 * (create, read, update and destroy) to RESTful HTTP verbs. For example, let's set up a {@link Ext.data.Model Model}
 * with an inline Rest proxy
 *
 *     Ext.define('User', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: ['id', 'name', 'email'],
 *
 *             proxy: {
 *                 type: 'rest',
 *                 url : '/users'
 *             }
 *         }
 *     });
 *
 * Now we can create a new User instance and save it via the Rest proxy. Doing this will cause the Proxy to send a POST
 * request to '/users':
 *
 *     var user = Ext.create('User', {name: 'Ed Spencer', email: 'ed@sencha.com'});
 *
 *     user.save(); //POST /users
 *
 * Let's expand this a little and provide a callback for the {@link Ext.data.Model#save} call to update the Model once
 * it has been created. We'll assume the creation went successfully and that the server gave this user an ID of 123:
 *
 *     user.save({
 *         success: function(user) {
 *             user.set('name', 'Khan Noonien Singh');
 *
 *             user.save(); //PUT /users/123
 *         }
 *     });
 *
 * Now that we're no longer creating a new Model instance, the request method is changed to an HTTP PUT, targeting the
 * relevant url for that user. Now let's delete this user, which will use the DELETE method:
 *
 *         user.destroy(); //DELETE /users/123
 *
 * Finally, when we perform a load of a Model or Store, Rest proxy will use the GET method:
 *
 *     //1. Load via Store
 *
 *     //the Store automatically picks up the Proxy from the User model
 *     var store = Ext.create('Ext.data.Store', {
 *         model: 'User'
 *     });
 *
 *     store.load(); //GET /users
 *
 *     //2. Load directly from the Model
 *
 *     //GET /users/123
 *     Ext.ModelManager.getModel('User').load(123, {
 *         success: function(user) {
 *             console.log(user.getId()); //outputs 123
 *         }
 *     });
 *
 * # Url generation
 *
 * The Rest proxy is able to automatically generate the urls above based on two configuration options - {@link #appendId} and
 * {@link #format}. If appendId is true (it is by default) then Rest proxy will automatically append the ID of the Model
 * instance in question to the configured url, resulting in the '/users/123' that we saw above.
 *
 * If the request is not for a specific Model instance (e.g. loading a Store), the url is not appended with an id.
 * The Rest proxy will automatically insert a '/' before the ID if one is not already present.
 *
 *     new Ext.data.proxy.Rest({
 *         url: '/users',
 *         appendId: true //default
 *     });
 *
 *     // Collection url: /users
 *     // Instance url  : /users/123
 *
 * The Rest proxy can also optionally append a format string to the end of any generated url:
 *
 *     new Ext.data.proxy.Rest({
 *         url: '/users',
 *         format: 'json'
 *     });
 *
 *     // Collection url: /users.json
 *     // Instance url  : /users/123.json
 *
 * If further customization is needed, simply implement the {@link #buildUrl} method and add your custom generated url
 * onto the {@link Ext.data.Request Request} object that is passed to buildUrl. See [Rest proxy's implementation][1] for
 * an example of how to achieve this.
 *
 * Note that Rest proxy inherits from {@link Ext.data.proxy.Ajax AjaxProxy}, which already injects all of the sorter,
 * filter, group and paging options into the generated url. See the {@link Ext.data.proxy.Ajax AjaxProxy docs} for more
 * details.
 *
 * [1]: source/Rest.html#Ext-data-proxy-Rest-method-buildUrl
 */
Ext.define('Ext.data.proxy.Rest', {
    extend: 'Ext.data.proxy.Ajax',
    alternateClassName: 'Ext.data.RestProxy',
    alias : 'proxy.rest',

    config: {
        /**
         * @cfg {Boolean} appendId
         * True to automatically append the ID of a Model instance when performing a request based on that single instance.
         * See Rest proxy intro docs for more details. Defaults to true.
         */
        appendId: true,

        /**
         * @cfg {String} format
         * Optional data format to send to the server when making any request (e.g. 'json'). See the Rest proxy intro docs
         * for full details. Defaults to null.
         */
        format: null,

        /**
         * @cfg {Boolean} batchActions
         * True to batch actions of a particular type when synchronizing the store. Defaults to false.
         */
        batchActions: false,

        actionMethods: {
            create : 'POST',
            read   : 'GET',
            update : 'PUT',
            destroy: 'DELETE'
        }
    },

    /**
     * Specialized version of buildUrl that incorporates the {@link #appendId} and {@link #format} options into the
     * generated url. Override this to provide further customizations, but remember to call the superclass buildUrl so
     * that additional parameters like the cache buster string are appended.
     * @param {Object} request
     */
    buildUrl: function(request) {
        var me        = this,
            operation = request.getOperation(),
            records   = operation.getRecords() || [],
            record    = records[0],
            model     = me.getModel(),
            idProperty= model.getIdProperty(),
            format    = me.getFormat(),
            url       = me.getUrl(request),
            params    = request.getParams() || {},
            id        = (record && !record.phantom) ? record.getId() : params[idProperty];

        if (me.getAppendId() && id) {
            if (!url.match(/\/$/)) {
                url += '/';
            }
            url += id;
            delete params[idProperty];
        }

        if (format) {
            if (!url.match(/\.$/)) {
                url += '.';
            }

            url += format;
        }

        request.setUrl(url);

        return me.callParent([request]);
    }
});

/**
 * @class Ext.direct.RemotingProvider
 *
 * <p>The {@link Ext.direct.RemotingProvider RemotingProvider} exposes access to
 * server side methods on the client (a remote procedure call (RPC) type of
 * connection where the client can initiate a procedure on the server).</p>
 *
 * <p>This allows for code to be organized in a fashion that is maintainable,
 * while providing a clear path between client and server, something that is
 * not always apparent when using URLs.</p>
 *
 * <p>To accomplish this the server-side needs to describe what classes and methods
 * are available on the client-side. This configuration will typically be
 * outputted by the server-side Ext.Direct stack when the API description is built.</p>
 */
Ext.define('Ext.direct.RemotingProvider', {
    alias: 'direct.remotingprovider',

    extend: 'Ext.direct.JsonProvider',

    requires: [
        'Ext.util.MixedCollection',
        'Ext.util.DelayedTask',
        'Ext.direct.Transaction',
        'Ext.direct.RemotingMethod'
    ],

    config: {
        /**
         * @cfg {String/Object} namespace
         * Namespace for the Remoting Provider (defaults to the browser global scope of <i>window</i>).
         * Explicitly specify the namespace Object, or specify a String to have a
         * {@link Ext#namespace namespace created} implicitly.
         */
        namespace: undefined,

        /**
         * @cfg {String} url
         * <b>Required</b>. The url to connect to the {@link Ext.direct.Manager} server-side router.
         */
        url: null,

        /**
         * @cfg {String} enableUrlEncode
         * Specify which param will hold the arguments for the method.
         * Defaults to <tt>'data'</tt>.
         */
        enableUrlEncode: null,

        /**
         * @cfg {Number/Boolean} enableBuffer
         * <p><tt>true</tt> or <tt>false</tt> to enable or disable combining of method
         * calls. If a number is specified this is the amount of time in milliseconds
         * to wait before sending a batched request.</p>
         * <br><p>Calls which are received within the specified timeframe will be
         * concatenated together and sent in a single request, optimizing the
         * application by reducing the amount of round trips that have to be made
         * to the server.</p>
         */
        enableBuffer: 10,

        /**
         * @cfg {Number} maxRetries
         * Number of times to re-attempt delivery on failure of a call.
         */
        maxRetries: 1,

        /**
         * @cfg {Number} timeout
         * The timeout to use for each request.
         */
        timeout: undefined,

        /**
         * @cfg {Object} actions
         * Object literal defining the server side actions and methods. For example, if
         * the Provider is configured with:
         *
         *    actions: { // each property within the 'actions' object represents a server side Class
         *        // array of methods within each server side Class to be stubbed out on client
         *        TestAction: [{
         *            name: "doEcho",
         *            len: 1
         *        }, {
         *            "name": "multiply", // name of method
         *            "len": 2            // The number of parameters that will be used to create an
         *                                // array of data to send to the server side function.
         *                                // Ensure the server sends back a Number, not a String.
         *        }, {
         *            name: "doForm",
         *            formHandler: true, // direct the client to use specialized form handling method
         *            len: 1
         *        }]
         *    }
         *
         * Note that a Store is not required, a server method can be called at any time.
         * In the following example a <b>client side</b> handler is used to call the
         * server side method "multiply" in the server-side "TestAction" Class:
         *
         *    TestAction.multiply(
         *        2, 4, // pass two arguments to server, so specify len=2
         *        // callback function after the server is called
         *        //     result: the result returned by the server
         *        //     e: Ext.direct.RemotingEvent object
         *        function(result, e){
         *            var t = e.getTransaction();
         *            var action = t.action; // server side Class called
         *            var method = t.method; // server side method called
         *            if (e.getStatus()) {
         *                var answer = Ext.encode(result); // 8
         *
         *            } else {
         *                var msg = e.getMessage(); // failure message
         *            }
         *        }
         *    );
         *
         * In the example above, the server side "multiply" function will be passed two
         * arguments (2 and 4).  The "multiply" method should return the value 8 which will be
         * available as the <tt>result</tt> in the example above.
         */
        actions: {}
    },

    /**
     * @event beforecall
     * Fires immediately before the client-side sends off the RPC call.
     * By returning false from an event handler you can prevent the call from
     * executing.
     * @param {Ext.direct.RemotingProvider} provider
     * @param {Ext.direct.Transaction} transaction
     * @param {Object} meta The meta data
     */

    /**
     * @event call
     * Fires immediately after the request to the server-side is sent. This does
     * NOT fire after the response has come back from the call.
     * @param {Ext.direct.RemotingProvider} provider
     * @param {Ext.direct.Transaction} transaction
     * @param {Object} meta The meta data
     */

    constructor : function(config) {
        var me = this;

        me.callParent(arguments);

        me.transactions = Ext.create('Ext.util.Collection', function(item) {
            return item.getId();
        });
        me.callBuffer = [];
    },

    applyNamespace: function(namespace) {
        if (Ext.isString(namespace)) {
            return Ext.ns(namespace);
        }
        return namespace || window;
    },

    /**
     * Initialize the API
     * @private
     */
    initAPI : function() {
        var actions = this.getActions(),
            namespace = this.getNamespace(),
            action, cls, methods,
            i, ln, method;

        for (action in actions) {
            if (actions.hasOwnProperty(action)) {
                cls = namespace[action];
                if (!cls) {
                    cls = namespace[action] = {};
                }
                methods = actions[action];

                for (i = 0, ln = methods.length; i < ln; ++i) {
                    method = Ext.create('Ext.direct.RemotingMethod', methods[i]);
                    cls[method.getName()] = this.createHandler(action, method);
                }
            }
        }
    },

    /**
     * Create a handler function for a direct call.
     * @private
     * @param {String} action The action the call is for
     * @param {Object} method The details of the method
     * @return {Function} A JS function that will kick off the call
     */
    createHandler : function(action, method) {
        var me = this,
            handler;

        if (!method.getFormHandler()) {
            handler = function() {
                me.configureRequest(action, method, Array.prototype.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;
    },

    // inherit docs
    isConnected: function() {
        return !!this.connected;
    },

    // inherit docs
    connect: function() {
        var me = this;

        if (me.getUrl()) {
            me.initAPI();
            me.connected = true;
            me.fireEvent('connect', me);
        } else {
            Ext.Error.raise('Error initializing RemotingProvider, no url configured.');
        }
    },

    // inherit docs
    disconnect: function() {
        var me = this;

        if (me.connected) {
            me.connected = false;
            me.fireEvent('disconnect', me);
        }
    },

    /**
     * Run any callbacks related to the transaction.
     * @private
     * @param {Ext.direct.Transaction} transaction The transaction
     * @param {Ext.direct.Event} event The event
     */
    runCallback: function(transaction, event) {
        var success = !!event.getStatus(),
            functionName = success ? 'success' : 'failure',
            callback = transaction && transaction.getCallback(),
            result;

        if (callback) {
            // this doesnt make any sense. why do we have both result and data?
            // result = Ext.isDefined(event.getResult()) ? event.result : event.data;
            result = event.getResult();
            if (Ext.isFunction(callback)) {
                callback(result, event, success);
            } else {
                Ext.callback(callback[functionName], callback.scope, [result, event, success]);
                Ext.callback(callback.callback, callback.scope, [result, event, success]);
            }
        }
    },

    /**
     * React to the ajax request being completed
     * @private
     */
    onData: function(options, success, response) {
        var me = this,
            i = 0,
            ln, events, event,
            transaction, transactions;

        if (success) {
            events = me.createEvents(response);
            for (ln = events.length; i < ln; ++i) {
                event = events[i];
                transaction = me.getTransaction(event);
                me.fireEvent('data', me, event);
                if (transaction) {
                    me.runCallback(transaction, event, true);
                    Ext.direct.Manager.removeTransaction(transaction);
                }
            }
        } else {
            transactions = [].concat(options.transaction);
            for (ln = transactions.length; i < ln; ++i) {
                transaction = me.getTransaction(transactions[i]);
                if (transaction && transaction.getRetryCount() < me.getMaxRetries()) {
                    transaction.retry();
                } else {
                    event = Ext.create('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.runCallback(transaction, event, false);
                        Ext.direct.Manager.removeTransaction(transaction);
                    }
                }
            }
        }
    },

    /**
     * Get transaction from XHR options
     * @private
     * @param {Object} options The options sent to the Ajax request
     * @return {Ext.direct.Transaction} The transaction, null if not found
     */
    getTransaction: function(options) {
        return options && options.getTid() ? Ext.direct.Manager.getTransaction(options.getTid()) : null;
    },

    /**
     * Configure a direct request
     * @private
     * @param {String} action The action being executed
     * @param {Object} method The being executed
     */
    configureRequest: function(action, method, args) {
        var me = this,
            callData = method.getCallData(args),
            data = callData.data,
            callback = callData.callback,
            scope = callData.scope,
            transaction;

        transaction = Ext.create('Ext.direct.Transaction', {
            provider: me,
            args: args,
            action: action,
            method: method.getName(),
            data: data,
            callback: scope && Ext.isFunction(callback) ? Ext.Function.bind(callback, scope) : callback
        });

        if (me.fireEvent('beforecall', me, transaction, method) !== false) {
            Ext.direct.Manager.addTransaction(transaction);
            me.queueTransaction(transaction);
            me.fireEvent('call', me, transaction, method);
        }
    },

    /**
     * Gets the Ajax call info for a transaction
     * @private
     * @param {Ext.direct.Transaction} transaction The transaction
     * @return {Object} The call params
     */
    getCallData: function(transaction) {
        return {
            action: transaction.getAction(),
            method: transaction.getMethod(),
            data: transaction.getData(),
            type: 'rpc',
            tid: transaction.getId()
        };
    },

    /**
     * Sends a request to the server
     * @private
     * @param {Object/Array} data The data to send
     */
    sendRequest : function(data) {
        var me = this,
            request = {
                url: me.getUrl(),
                callback: me.onData,
                scope: me,
                transaction: data,
                timeout: me.getTimeout()
            }, callData,
            enableUrlEncode = me.getEnableUrlEncode(),
            i = 0,
            ln, params;


        if (Ext.isArray(data)) {
            callData = [];
            for (ln = data.length; i < ln; ++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);
    },

    /**
     * Add a new transaction to the queue
     * @private
     * @param {Ext.direct.Transaction} transaction The transaction
     */
    queueTransaction: function(transaction) {
        var me = this,
            enableBuffer = me.getEnableBuffer();

        if (transaction.getForm()) {
            me.sendFormRequest(transaction);
            return;
        }

        me.callBuffer.push(transaction);
        if (enableBuffer) {
            if (!me.callTask) {
                me.callTask = Ext.create('Ext.util.DelayedTask', me.combineAndSend, me);
            }
            me.callTask.delay(Ext.isNumber(enableBuffer) ? enableBuffer : 10);
        } else {
            me.combineAndSend();
        }
    },

    /**
     * Combine any buffered requests and send them off
     * @private
     */
    combineAndSend : function() {
        var buffer = this.callBuffer,
            ln = buffer.length;

        if (ln > 0) {
            this.sendRequest(ln == 1 ? buffer[0] : buffer);
            this.callBuffer = [];
        }
    }

//    /**
//     * Configure a form submission request
//     * @private
//     * @param {String} action The action being executed
//     * @param {Object} method The method being executed
//     * @param {HTMLElement} form The form being submitted
//     * @param {Function} callback (optional) A callback to run after the form submits
//     * @param {Object} scope (optional) A scope to execute the callback in
//     */
//    configureFormRequest : function(action, method, form, callback, scope){
//        var me = this,
//            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
//            }),
//            isUpload,
//            params;
//
//        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)
//            };
//
//            // change made from typeof callback check to callback.params
//            // to support addl param passing in DirectSubmit EAC 6/2
//            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);
//        }
//    },
//
//    /**
//     * Sends a form request
//     * @private
//     * @param {Ext.direct.Transaction} transaction The transaction to send
//     */
//    sendFormRequest: function(transaction){
//        Ext.Ajax.request({
//            url: this.url,
//            params: transaction.params,
//            callback: this.onData,
//            scope: this,
//            form: transaction.form,
//            isUpload: transaction.isUpload,
//            transaction: transaction
//        });
//    }

});

/**
 * A wrapper class which can be applied to any element. Fires a "tap" event while
 * touching the device. The interval between firings may be specified in the config but
 * defaults to 20 milliseconds.
 */
Ext.define('Ext.util.TapRepeater', {
    requires: ['Ext.DateExtras'],

    mixins: {
        observable: 'Ext.mixin.Observable'
    },

    /**
     * @event touchstart
     * Fires when the touch is started.
     * @param {Ext.util.TapRepeater} this
     * @param {Ext.event.Event} e
     */

    /**
     * @event tap
     * Fires on a specified interval during the time the element is pressed.
     * @param {Ext.util.TapRepeater} this
     * @param {Ext.event.Event} e
     */

    /**
     * @event touchend
     * Fires when the touch is ended.
     * @param {Ext.util.TapRepeater} this
     * @param {Ext.event.Event} e
     */

    config: {
        el: null,
        accelerate: true,
        interval: 10,
        delay: 250,
        preventDefault: true,
        stopDefault: false,
        timer: 0,
        pressCls: null
    },

    /**
     * Creates new TapRepeater.
     * @param {Mixed} el The element to listen on
     * @param {Object} config
     */
    constructor: function(config) {
        var me = this;
        for (var configName in config) {
            if (me.self.prototype.config && !(configName in me.self.prototype.config)) {
                me[configName] = config[configName];
                Ext.Logger.warn('Applied config as instance property: "' + configName + '"', me);
            }
        }
        me.initConfig(config);
    },

    updateEl: function(newEl, oldEl) {
        var eventCfg = {
                touchstart: 'onTouchStart',
                touchend: 'onTouchEnd',
                tap: 'eventOptions',
                scope: this
            };
        if (oldEl) {
            oldEl.un(eventCfg)
        }
        newEl.on(eventCfg);
    },

    // @private
    eventOptions: function(e) {
        if (this.getPreventDefault()) {
            e.preventDefault();
        }
        if (this.getStopDefault()) {
            e.stopEvent();
        }
    },

    // @private
    destroy: function() {
        this.clearListeners();
        Ext.destroy(this.el);
    },

    // @private
    onTouchStart: function(e) {
        var me = this,
            pressCls = me.getPressCls();
        clearTimeout(me.getTimer());
        if (pressCls) {
            me.getEl().addCls(pressCls);
        }
        me.tapStartTime = new Date();

        me.fireEvent('touchstart', me, e);
        me.fireEvent('tap', me, e);

        // Do not honor delay or interval if acceleration wanted.
        if (me.getAccelerate()) {
            me.delay = 400;
        }
        me.setTimer(Ext.defer(me.tap, me.getDelay() || me.getInterval(), me, [e]));
    },

    // @private
    tap: function(e) {
        var me = this;
        me.fireEvent('tap', me, e);
        me.setTimer(Ext.defer(me.tap, me.getAccelerate() ? me.easeOutExpo(Ext.Date.getElapsed(me.tapStartTime),
            400,
            -390,
            12000) : me.getInterval(), me, [e]));
    },

    // Easing calculation
    // @private
    easeOutExpo: function(t, b, c, d) {
        return (t == d) ? b + c : c * ( - Math.pow(2, -10 * t / d) + 1) + b;
    },

    // @private
    onTouchEnd: function(e) {
        var me = this;
        clearTimeout(me.getTimer());
        me.getEl().removeCls(me.getPressCls());
        me.fireEvent('touchend', me, e);
    }
});

/**
 * @private
 *
 * The abstract class. Sub-classes are expected, at the very least, to implement translation logics inside
 * the 'translate' method
 */
Ext.define('Ext.util.translatable.Abstract', {
    extend: 'Ext.Evented',

    requires: ['Ext.fx.easing.Linear'],

    config: {
        element: null,

        easing: null,

        easingX: null,

        easingY: null,

        fps: 60
    },

    /**
     * @event animationstart
     * Fires whenever the animation is started
     * @param {Ext.util.translatable.Abstract} this
     * @param {Number} x The current translation on the x axis
     * @param {Number} y The current translation on the y axis
     */

    /**
     * @event animationframe
     * Fires for each animation frame
     * @param {Ext.util.translatable.Abstract} this
     * @param {Number} x The new translation on the x axis
     * @param {Number} y The new translation on the y axis
     */

    /**
     * @event animationend
     * Fires whenever the animation is ended
     * @param {Ext.util.translatable.Abstract} this
     * @param {Number} x The current translation on the x axis
     * @param {Number} y The current translation on the y axis
     */

    constructor: function(config) {
        var element;

        this.doAnimationFrame = Ext.Function.bind(this.doAnimationFrame, this);

        this.x = 0;

        this.y = 0;

        this.activeEasingX = null;

        this.activeEasingY = null;

        this.initialConfig = config;

        if (config && config.element) {
            element = config.element;
            this.setElement(element);
        }
    },

    applyElement: function(element) {
        if (!element) {
            return;
        }

        return Ext.get(element);
    },

    updateElement: function(element) {
        this.initConfig(this.initialConfig);
        this.refresh();
    },

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

    applyEasing: function(easing) {
        if (!this.getEasingX()) {
            this.setEasingX(this.factoryEasing(easing));
        }

        if (!this.getEasingY()) {
            this.setEasingY(this.factoryEasing(easing));
        }
    },

    applyEasingX: function(easing) {
        return this.factoryEasing(easing);
    },

    applyEasingY: function(easing) {
        return this.factoryEasing(easing);
    },

    updateFps: function(fps) {
        this.animationInterval = 1000 / fps;
    },

    doTranslate: function(x, y) {
        if (typeof x == 'number') {
            this.x = x;
        }

        if (typeof y == 'number') {
            this.y = y;
        }

        return this;
    },

    translate: function(x, y, animation) {
        if (Ext.isObject(x)) {
            throw new Error();
        }

        this.stopAnimation();

        if (animation !== undefined) {
            return this.translateAnimated(x, y, animation);
        }

        return this.doTranslate(x, y);
    },

    animate: function(easingX, easingY) {
        this.activeEasingX = easingX;
        this.activeEasingY = easingY;

        this.isAnimating = true;
        this.animationTimer = setInterval(this.doAnimationFrame, this.animationInterval);

        this.fireEvent('animationstart', this, this.x, this.y);

        return this;
    },

    translateAnimated: function(x, y, animation) {
        if (Ext.isObject(x)) {
            throw new Error();
        }

        if (!Ext.isObject(animation)) {
            animation = {};
        }

        var now = Ext.Date.now(),
            easing = animation.easing,
            easingX = (typeof x == 'number') ? (animation.easingX || this.getEasingX() || easing || true) : null,
            easingY = (typeof y == 'number') ? (animation.easingY || this.getEasingY() || easing || true) : null;

        if (easingX) {
            easingX = this.factoryEasing(easingX);
            easingX.setStartTime(now);
            easingX.setStartValue(this.x);
            easingX.setEndValue(x);

            if ('duration' in animation) {
                easingX.setDuration(animation.duration);
            }
        }

        if (easingY) {
            easingY = this.factoryEasing(easingY);
            easingY.setStartTime(now);
            easingY.setStartValue(this.y);
            easingY.setEndValue(y);

            if ('duration' in animation) {
                easingY.setDuration(animation.duration);
            }
        }

        return this.animate(easingX, easingY);
    },

    doAnimationFrame: function() {
        if (!this.isAnimating) {
            return;
        }

        var easingX = this.activeEasingX,
            easingY = this.activeEasingY,
            x, y;

        if (easingX === null && easingY === null) {
            this.stopAnimation();
            return;
        }

        if (easingX !== null) {
            this.x = x = Math.round(easingX.getValue());

            if (easingX.isEnded) {
                this.activeEasingX = null;
                this.fireEvent('axisanimationend', this, 'x', x);
            }
        }
        else {
            x = this.x;
        }

        if (easingY !== null) {
            this.y = y = Math.round(easingY.getValue());

            if (easingY.isEnded) {
                this.activeEasingY = null;
                this.fireEvent('axisanimationend', this, 'y', y);
            }
        }
        else {
            y = this.y;
        }

        this.doTranslate(x, y);
        this.fireEvent('animationframe', this, x, y);
    },

    stopAnimation: function() {
        if (!this.isAnimating) {
            return;
        }

        this.activeEasingX = null;
        this.activeEasingY = null;

        this.isAnimating = false;

        clearInterval(this.animationTimer);
        this.fireEvent('animationend', this, this.x, this.y);
    },

    refresh: function() {
        this.translate(this.x, this.y);
    }
});

/**
 * @private
 *
 * CSS Transform implementation
 */
Ext.define('Ext.util.translatable.CssTransform', {
    extend: 'Ext.util.translatable.Abstract',

    doTranslate: function(x, y) {
        var domStyle = this.getElement().dom.style;

        if (typeof x != 'number') {
            x = this.x;
        }

        if (typeof y != 'number') {
            y = this.y;
        }

        domStyle.webkitTransform = 'translate3d(' + x + 'px, ' + y + 'px, 0px)';

        return this.callParent(arguments);
    },

    destroy: function() {
        var element = this.getElement();

        if (element && !element.isDestroyed) {
            element.dom.style.webkitTransform = null;
        }

        this.callParent(arguments);
    }
});

/**
 * @private
 *
 * Scroll position implementation
 */
Ext.define('Ext.util.translatable.ScrollPosition', {
    extend: 'Ext.util.translatable.Abstract',

    wrapperWidth: 0,

    wrapperHeight: 0,

    baseCls: 'x-translatable',

    config: {
        useWrapper: true
    },

    getWrapper: function() {
        var wrapper = this.wrapper,
            baseCls = this.baseCls,
            element = this.getElement(),
            nestedStretcher, container;

        if (!wrapper) {
            container = element.getParent();

            if (!container) {
                return null;
            }

            if (this.getUseWrapper()) {
                wrapper = element.wrap({
                    className: baseCls + '-wrapper'
                }, true);
            }
            else {
                wrapper = container.dom;
            }

            wrapper.appendChild(Ext.Element.create({
                className: baseCls + '-stretcher'
            }, true));

            this.nestedStretcher = nestedStretcher = Ext.Element.create({
                className: baseCls + '-nested-stretcher'
            }, true);

            element.appendChild(nestedStretcher);

            element.addCls(baseCls);
            container.addCls(baseCls + '-container');

            this.container = container;
            this.wrapper = wrapper;

            this.refresh();
        }

        return wrapper;
    },

    doTranslate: function(x, y) {
        var wrapper = this.getWrapper();

        if (wrapper) {
            if (typeof x == 'number') {
                wrapper.scrollLeft = this.wrapperWidth - x;
            }

            if (typeof y == 'number') {
                wrapper.scrollTop = this.wrapperHeight - y;
            }
        }

        return this.callParent(arguments);
    },

    refresh: function() {
        var wrapper = this.getWrapper();

        if (wrapper) {
            this.wrapperWidth = wrapper.offsetWidth;
            this.wrapperHeight = wrapper.offsetHeight;

            this.callParent(arguments);
        }
    },

    destroy: function() {
        var element = this.getElement(),
            baseCls = this.baseCls;

        if (this.wrapper) {
            if (this.getUseWrapper()) {
                element.unwrap();
            }

            this.container.removeCls(baseCls + '-container');
            element.removeCls(baseCls);
            element.removeChild(this.nestedStretcher);
        }

        this.callParent(arguments);
    }

});

/**
 * The utility class to abstract different implementations to have the best performance when applying 2D translation
 * on any DOM element.
 *
 * @private
 */
Ext.define('Ext.util.Translatable', {
    requires: [
        'Ext.util.translatable.CssTransform',
        'Ext.util.translatable.ScrollPosition'
    ],

    constructor: function(config) {
        var namespace = Ext.util.translatable,
            CssTransform = namespace.CssTransform,
            ScrollPosition = namespace.ScrollPosition,
            classReference;

        if (typeof config == 'object' && 'translationMethod' in config) {
            if (config.translationMethod === 'scrollposition') {
                classReference = ScrollPosition;
            }
            else if (config.translationMethod === 'csstransform') {
                classReference = CssTransform;
            }
        }

        if (!classReference) {
            if (Ext.os.is.Android2 || Ext.browser.is.ChromeMobile) {
                classReference = ScrollPosition;
            }
            else {
                classReference = CssTransform;
            }
        }

        return new classReference(config);
    }
});

/**
 * @private
 */
Ext.define('Ext.behavior.Translatable', {

    extend: 'Ext.behavior.Behavior',

    requires: [
        'Ext.util.Translatable'
    ],

    constructor: function() {
        this.listeners = {
            painted: 'onComponentPainted',
            scope: this
        };

        this.callParent(arguments);
    },

    onComponentPainted: function() {
        this.translatable.refresh();
    },

    setConfig: function(config) {
        var translatable = this.translatable,
            component = this.component;

        if (config) {
            if (!translatable) {
                this.translatable = translatable = new Ext.util.Translatable(config);
                translatable.setElement(component.renderElement);
                translatable.on('destroy', 'onTranslatableDestroy', this);

                if (component.isPainted()) {
                    this.onComponentPainted(component);
                }

                component.on(this.listeners);
            }
            else if (Ext.isObject(config)) {
                translatable.setConfig(config);
            }
        }
        else if (translatable) {
            translatable.destroy();
        }

        return this;
    },

    getTranslatable: function() {
        return this.translatable;
    },

    onTranslatableDestroy: function() {
        var component = this.component;

        delete this.translatable;
        component.un(this.listeners);
    },

    onComponentDestroy: function() {
        var translatable = this.translatable;

        if (translatable) {
            translatable.destroy();
        }
    }
});

/**
 * @class Ext.scroll.Scroller
 * @author Jacky Nguyen <jacky@sencha.com>
 *
 * Momentum scrolling is one of the most important part of the framework's UI layer. In Sencha Touch there are
 * several scroller implementations so we can have the best performance on all mobile devices and browsers.
 *
 * Scroller settings can be changed using the {@link Ext.Container#scrollable scrollable} configuration in
 * {@link Ext.Container}. Anything you pass to that method will be passed to the scroller when it is
 * instanciated in your container.
 *
 * Please note that the {@link Ext.Container#getScrollable} method returns an instance of {@link Ext.scroll.View}.
 * So if you need to get access to the scroller after your container has been instansiated, you must used the
 * {@link Ext.scroll.View#getScroller} method.
 *
 *     //lets assume container is a container you have
 *     //created which is scrollable
 *     container.getScrollable.getScroller().setFps(10);
 *
 * ## Example
 *
 * Here is a simple example of how to adjust the scroller settings when using a {@link Ext.Container} (or anything
 * that extends it).
 *
 *     @example
 *     var container = Ext.create('Ext.Container', {
 *         fullscreen: true,
 *         html: 'This container is scrollable!',
 *         scrollable: {
 *             direction: 'vertical'
 *         }
 *     });
 *
 * As you can see, we are passing the {@link #direction} configuration into the scroller instance in our container.
 *
 * You can pass any of the configs below in that {@link Ext.Container#scrollable scrollable} configuration and it will
 * just work.
 *
 * Go ahead and try it in the live code editor above!
 */
Ext.define('Ext.scroll.Scroller', {

    extend: 'Ext.Evented',

    requires: [
        'Ext.fx.easing.BoundMomentum',
        'Ext.fx.easing.EaseOut',
        'Ext.util.SizeMonitor',
        'Ext.util.Translatable'
    ],

    /**
     * @event maxpositionchange
     * Fires whenever the maximum position has changed
     * @param {Ext.scroll.Scroller} this
     * @param {Number} maxPosition The new maximum position
     */

    /**
     * @event refresh
     * Fires whenever the Scroller is refreshed
     * @param {Ext.scroll.Scroller} this
     */

    /**
     * @event scrollstart
     * Fires whenever the scrolling is started
     * @param {Ext.scroll.Scroller} this
     * @param {Number} x The current x position
     * @param {Number} y The current y position
     */

    /**
     * @event scrollend
     * Fires whenever the scrolling is ended
     * @param {Ext.scroll.Scroller} this
     * @param {Number} x The current x position
     * @param {Number} y The current y position
     */

    /**
     * @event scroll
     * Fires whenever the Scroller is scrolled
     * @param {Ext.scroll.Scroller} this
     * @param {Number} x The new x position
     * @param {Number} y The new y position
     */

    config: {
        /**
         * @cfg
         * @private
         */
        element: null,

        /**
         * @cfg {String} direction
         * Possible values: 'auto', 'vertical', 'horizontal', or 'both'
         * @accessor
         */
        direction: 'auto',

        /**
         * @cfg
         * @private
         */
        translationMethod: 'auto',

        /**
         * @cfg
         * @private
         */
        fps: 'auto',

        /**
         * @cfg {Boolean} disabled
         * Whether or not this component is disabled
         * @accessor
         */
        disabled: null,

        /**
         * @cfg {Boolean} directionLock
         * True to lock the direction of the scroller when the user starts scrolling.
         * This is useful when putting a scroller inside a scroller or a {@link Ext.Carousel}.
         * @accessor
         */
        directionLock: false,

        /**
         * @cfg {Object} momentumEasing
         * A valid config for {@link Ext.fx.easing.BoundMomentum}. The default value is:
         *
         *     {
         *         momentum: {
         *             acceleration: 30,
         *             friction: 0.5
         *         },
         *         bounce: {
         *             acceleration: 30,
         *             springTension: 0.3
         *         }
         *     }
         *
         * Note that supplied object will be recursively merged with the default object. For example: you can simply
         * pass this to change the momentum acceleration only
         *
         *     {
         *         momentum: {
         *             acceleration: 10
         *         }
         *     }
         *
         * @accessor
         */
        momentumEasing: {
            momentum: {
                acceleration: 30,
                friction: 0.5
            },

            bounce: {
                acceleration: 30,
                springTension: 0.3
            },

            minVelocity: 1
        },

        /**
         * @cfg
         * @private
         */
        bounceEasing: {
            duration: 400
        },

        /**
         * @cfg
         * @private
         */
        outOfBoundRestrictFactor: 0.5,

        /**
         * @cfg
         * @private
         */
        startMomentumResetTime: 300,

        /**
         * @cfg
         * @private
         */
        maxAbsoluteVelocity: 6,

        /**
         * @cfg
         * @private
         */
        containerSize: 'auto',

        /**
         * @cfg
         * @private
         */
        containerScrollSize: 'auto',

        /**
         * @cfg
         * @private
         */
        size: 'auto',

        /**
         * @cfg
         * @private
         */
        autoRefresh: true,

        /**
         * @cfg {Object} initialOffset
         *
         */
        initialOffset: {
            x: 0,
            y: 0
        },

        /**
         * @cfg {Number/Object} slotSnapSize
         * The size of each slot to snap to in 'px', can be either an object with x and y values, i.e:
         *
         *      {
         *          x: 50,
         *          y: 100
         *      }
         *
         * or a number value to be used for both directions. For example: a value of '`50`' will be treated as:
         *
         *      {
         *          x: 50,
         *          y: 50
         *      }
         *
         * @accessor
         */
        slotSnapSize: {
            x: 0,
            y: 0
        },

        /**
         * @cfg
         * @private
         */
        slotSnapOffset: {
            x: 0,
            y: 0
        },

        slotSnapEasing: {
            duration: 150
        }
    },

    cls: Ext.baseCSSPrefix + 'scroll-scroller',

    containerCls: Ext.baseCSSPrefix + 'scroll-container',

    dragStartTime: 0,

    dragEndTime: 0,

    isDragging: false,

    isAnimating: false,

    /**
     * @private
     */
    constructor: function(config) {
        var element = config && config.element;

        this.doAnimationFrame = Ext.Function.bind(this.doAnimationFrame, this);
        this.stopAnimation = Ext.Function.bind(this.stopAnimation, this);

        this.listeners = {
            scope: this,
            touchstart: 'onTouchStart',
            touchend: 'onTouchEnd',
            dragstart: 'onDragStart',
            drag: 'onDrag',
            dragend: 'onDragEnd'
        };

        this.minPosition = { x: 0, y: 0 };

        this.startPosition = { x: 0, y: 0 };

        this.size = { x: 0, y: 0 };

        this.position = { x: 0, y: 0 };

        this.velocity = { x: 0, y: 0 };

        this.isAxisEnabledFlags = { x: false, y: false };

        this.flickStartPosition = { x: 0, y: 0 };

        this.flickStartTime = { x: 0, y: 0 };

        this.lastDragPosition = { x: 0, y: 0 };

        this.dragDirection = { x: 0, y: 0};

        this.initialConfig = config;

        if (element) {
            this.setElement(element);
        }

        return this;
    },

    /**
     * @private
     */
    applyElement: function(element) {
        if (!element) {
            return;
        }

        return Ext.get(element);
    },

    /**
     * @private
     */
    updateElement: function(element) {
        this.initialize();

        element.addCls(this.cls);

        if (!this.getDisabled()) {
            this.attachListeneners();
        }

        this.onConfigUpdate(['containerSize', 'size'], 'refreshMaxPosition');

        this.on('maxpositionchange', 'snapToBoundary');
        this.on('minpositionchange', 'snapToBoundary');

        return this;
    },

    getTranslatable: function() {
        if (!this.hasOwnProperty('translatable')) {
            var bounceEasing = this.getBounceEasing();

            this.translatable = new Ext.util.Translatable({
                translationMethod: this.getTranslationMethod(),
                element: this.getElement(),
                easingX: bounceEasing.x,
                easingY: bounceEasing.y,
                useWrapper: false,
                listeners: {
                    animationframe: 'onAnimationFrame',
                    animationend: 'onAnimationEnd',
                    axisanimationend: 'onAxisAnimationEnd',
                    scope: this
                }
            });
        }

        return this.translatable;
    },

    updateFps: function(fps) {
        if (fps !== 'auto') {
            this.getTranslatable().setFps(fps);
        }
    },

    /**
     * @private
     */
    attachListeneners: function() {
        this.getContainer().on(this.listeners);
    },

    /**
     * @private
     */
    detachListeners: function() {
        this.getContainer().un(this.listeners);
    },

    /**
     * @private
     */
    updateDisabled: function(disabled) {
        if (disabled) {
            this.detachListeners();
        }
        else {
            this.attachListeneners();
        }
    },

    updateInitialOffset: function(initialOffset) {
        if (typeof initialOffset == 'number') {
            initialOffset = {
                x: initialOffset,
                y: initialOffset
            };
        }

        var position = this.position,
            x, y;

        position.x = x = initialOffset.x;
        position.y = y = initialOffset.y;

        this.getTranslatable().doTranslate(-x, -y);
    },

    /**
     * @private
     */
    applyDirection: function(direction) {
        var minPosition = this.getMinPosition(),
            maxPosition = this.getMaxPosition(),
            isHorizontal, isVertical;

        this.givenDirection = direction;

        if (direction === 'auto') {
            isHorizontal = maxPosition.x > minPosition.x;
            isVertical = maxPosition.y > minPosition.y;

            if (isHorizontal && isVertical) {
                direction = 'both';
            }
            else if (isHorizontal) {
                direction = 'horizontal';
            }
            else {
                direction = 'vertical';
            }
        }

        return direction;
    },

    /**
     * @private
     */
    updateDirection: function(direction) {
        var isAxisEnabled = this.isAxisEnabledFlags;

        isAxisEnabled.x = (direction === 'both' || direction === 'horizontal');
        isAxisEnabled.y = (direction === 'both' || direction === 'vertical');
    },

    /**
     * Returns true if a specified axis is enabled
     * @param {String} axis The axis to check (`x` or `y`).
     * @return {Boolean} True if the axis is enabled
     */
    isAxisEnabled: function(axis) {
        this.getDirection();

        return this.isAxisEnabledFlags[axis];
    },

    /**
     * @private
     */
    applyMomentumEasing: function(easing) {
        var defaultClass = Ext.fx.easing.BoundMomentum;

        return {
            x: Ext.factory(easing, defaultClass),
            y: Ext.factory(easing, defaultClass)
        };
    },

    /**
     * @private
     */
    applyBounceEasing: function(easing) {
        var defaultClass = Ext.fx.easing.EaseOut;

        return {
            x: Ext.factory(easing, defaultClass),
            y: Ext.factory(easing, defaultClass)
        };
    },

    /**
     * @private
     */
    applySlotSnapEasing: function(easing) {
        var defaultClass = Ext.fx.easing.EaseOut;

        return {
            x: Ext.factory(easing, defaultClass),
            y: Ext.factory(easing, defaultClass)
        };
    },

    /**
     * @private
     */
    getMinPosition: function() {
        var minPosition = this.minPosition;

        if (!minPosition) {
            this.minPosition = minPosition = {
                x: 0,
                y: 0
            };

            this.fireEvent('minpositionchange', this, minPosition);
        }

        return minPosition;
    },

    /**
     * @private
     */
    getMaxPosition: function() {
        var maxPosition = this.maxPosition,
            size, containerSize;

        if (!maxPosition) {
            size = this.getSize();
            containerSize = this.getContainerSize();

            this.maxPosition = maxPosition = {
                x: Math.max(0, size.x - containerSize.x),
                y: Math.max(0, size.y - containerSize.y)
            };

            this.fireEvent('maxpositionchange', this, maxPosition);
        }

        return maxPosition;
    },

    /**
     * @private
     */
    refreshMaxPosition: function() {
        this.maxPosition = null;
        this.getMaxPosition();
    },

    /**
     * @private
     */
    applyContainerSize: function(size) {
        var containerDom, x, y;

        this.givenContainerSize = size;

        if (size === 'auto') {
            containerDom = this.getContainer().dom;

            x = containerDom.offsetWidth;
            y = containerDom.offsetHeight;
        }
        else {
            x = size.x;
            y = size.y;
        }

        return {
            x: x,
            y: y
        };
    },

    /**
     * @private
     */
    applySize: function(size) {
        var dom, x, y;

        this.givenSize = size;

        if (size === 'auto') {
            dom = this.getElement().dom;

            x = dom.offsetWidth;
            y = dom.offsetHeight;
        }
        else {
            x = size.x;
            y = size.y;
        }

        return {
            x: x,
            y: y
        };
    },

    /**
     * @private
     */
    applyContainerScrollSize: function(size) {
        var containerDom, x, y;

        this.givenContainerScrollSize = size;

        if (size === 'auto') {
            containerDom = this.getContainer().dom;

            x = containerDom.scrollWidth;
            y = containerDom.scrollHeight;
        }
        else {
            x = size.x;
            y = size.y;
        }

        return {
            x: x,
            y: y
        };
    },

    /**
     * @private
     */
    updateAutoRefresh: function(autoRefresh) {
        var SizeMonitor = Ext.util.SizeMonitor,
            sizeMonitors;

        if (autoRefresh) {
            this.sizeMonitors = {
                element: new SizeMonitor({
                    element: this.getElement(),
                    callback: this.doRefresh,
                    scope: this
                }),
                container: new SizeMonitor({
                    element: this.getContainer(),
                    callback: this.doRefresh,
                    scope: this
                })
            };
        }
        else {
            sizeMonitors = this.sizeMonitors;

            if (sizeMonitors) {
                sizeMonitors.element.destroy();
                sizeMonitors.container.destroy();
            }
        }
    },

    applySlotSnapSize: function(snapSize) {
        if (typeof snapSize == 'number') {
            return {
                x: snapSize,
                y: snapSize
            }
        }

        return snapSize;
    },

    applySlotSnapOffset: function(snapOffset) {
        if (typeof snapOffset == 'number') {
            return {
                x: snapOffset,
                y: snapOffset
            }
        }

        return snapOffset;
    },

    /**
     * @private
     * Returns the container for this scroller
     */
    getContainer: function() {
        var container = this.container;

        if (!container) {
            this.container = container = this.getElement().getParent();
            if (!container) {
                Ext.Logger.error("Making an element scrollable that doesn't have any container");
            }
            container.addCls(this.containerCls);
        }

        return container;
    },

    /**
     * @private
     */
    doRefresh: function() {
        this.stopAnimation();

        this.getTranslatable().refresh();
        this.setSize(this.givenSize);
        this.setContainerSize(this.givenContainerSize);
        this.setContainerScrollSize(this.givenContainerScrollSize);
        this.setDirection(this.givenDirection);

        this.fireEvent('refresh', this);
    },

    /**
     * @private
     * @return {Ext.scroll.Scroller} this
     */
    refresh: function() {
        var sizeMonitors = this.sizeMonitors;

        if (sizeMonitors) {
            sizeMonitors.element.refresh();
            sizeMonitors.container.refresh();
        }

        this.doRefresh();

        return this;
    },

    /**
     * Scrolls to the given location
     *
     * @param {Number} x The scroll position on the x axis
     * @param {Number} y The scroll position on the y axis
     * @param {Boolean/Object} animation (Optional) Whether or not to animate the scrolling to the new position
     *
     * @return {Ext.scroll.Scroller} this
     */
    scrollTo: function(x, y, animation) {

        var translatable = this.getTranslatable(),
            position = this.position,
            positionChanged = false,
            translationX, translationY;

        if (this.isAxisEnabled('x')) {
            if (typeof x != 'number') {
                x = position.x;
            }
            else {
                if (position.x !== x) {
                    position.x = x;
                    positionChanged = true;
                }
            }

            translationX = -x;
        }

        if (this.isAxisEnabled('y')) {
            if (typeof y != 'number') {
                y = position.y;
            }
            else {
                if (position.y !== y) {
                    position.y = y;
                    positionChanged = true;
                }
            }

            translationY = -y;
        }

        if (positionChanged) {
            if (animation !== undefined) {
                translatable.translateAnimated(translationX, translationY, animation);
            }
            else {
                this.fireEvent('scroll', this, position.x, position.y);
                translatable.doTranslate(translationX, translationY);
            }
        }

        return this;
    },

    /**
     * @private
     */
    scrollToTop: function(animation) {
        var initialOffset = this.getInitialOffset();

        return this.scrollTo(initialOffset.x, initialOffset.y, animation);
    },

    /**
     * Scrolls to the end of the scrollable view
     * @return {Ext.scroll.Scroller} this
     */
    scrollToEnd: function(animation) {
        return this.scrollTo(0, this.getSize().y - this.getContainerSize().y, animation);
    },

    /**
     * Change the scroll offset by the given amount
     * @param {Number} x The offset to scroll by on the x axis
     * @param {Number} y The offset to scroll by on the y axis
     * @return {Ext.scroll.Scroller} this
     */
    scrollBy: function(x, y, animation) {
        var position = this.position;

        x = (typeof x == 'number') ? x + position.x : null;
        y = (typeof y == 'number') ? y + position.y : null;

        return this.scrollTo(x, y, animation);
    },

    /**
     * @private
     */
    onTouchStart: function() {
        this.isTouching = true;
        this.stopAnimation();
    },

    /**
     * @private
     */
    onTouchEnd: function() {
        var position = this.position;

        this.isTouching = false;

        if (!this.isDragging && this.snapToSlot()) {
            this.fireEvent('scrollstart', this, position.x, position.y);
        }
    },

    /**
     * @private
     */
    onDragStart: function(e) {
        var direction = this.getDirection(),
            absDeltaX = e.absDeltaX,
            absDeltaY = e.absDeltaY,
            directionLock = this.getDirectionLock(),
            startPosition = this.startPosition,
            flickStartPosition = this.flickStartPosition,
            flickStartTime = this.flickStartTime,
            lastDragPosition = this.lastDragPosition,
            currentPosition = this.position,
            dragDirection = this.dragDirection,
            x = currentPosition.x,
            y = currentPosition.y,
            now = Ext.Date.now();

        this.isDragging = true;

        if (directionLock && direction !== 'both') {
            if ((direction === 'horizontal' && absDeltaX > absDeltaY)
                    || (direction === 'vertical' && absDeltaY > absDeltaX)) {
                e.stopPropagation();
            }
            else {
                this.isDragging = false;
                return;
            }
        }

        lastDragPosition.x = x;
        lastDragPosition.y = y;

        flickStartPosition.x = x;
        flickStartPosition.y = y;

        startPosition.x = x;
        startPosition.y = y;

        flickStartTime.x = now;
        flickStartTime.y = now;

        dragDirection.x = 0;
        dragDirection.y = 0;

        this.dragStartTime = now;

        this.isDragging = true;

        this.fireEvent('scrollstart', this, x, y);
    },

    /**
     * @private
     */
    onAxisDrag: function(axis, delta) {
        if (!this.isAxisEnabled(axis)) {
            return;
        }

        var flickStartPosition = this.flickStartPosition,
            flickStartTime = this.flickStartTime,
            lastDragPosition = this.lastDragPosition,
            dragDirection = this.dragDirection,
            old = this.position[axis],
            min = this.getMinPosition()[axis],
            max = this.getMaxPosition()[axis],
            start = this.startPosition[axis],
            last = lastDragPosition[axis],
            current = start - delta,
            lastDirection = dragDirection[axis],
            restrictFactor = this.getOutOfBoundRestrictFactor(),
            startMomentumResetTime = this.getStartMomentumResetTime(),
            now = Ext.Date.now(),
            distance;

        if (current < min) {
            current *= restrictFactor;
        }
        else if (current > max) {
            distance = current - max;
            current = max + distance * restrictFactor;
        }

        if (current > last) {
            dragDirection[axis] = 1;
        }
        else if (current < last) {
            dragDirection[axis] = -1;
        }

        if ((lastDirection !== 0 && (dragDirection[axis] !== lastDirection))
                || (now - flickStartTime[axis]) > startMomentumResetTime) {
            flickStartPosition[axis] = old;
            flickStartTime[axis] = now;
        }

        lastDragPosition[axis] = current;
    },

    /**
     * @private
     */
    onDrag: function(e) {
        if (!this.isDragging) {
            return;
        }

        var lastDragPosition = this.lastDragPosition;

        this.onAxisDrag('x', e.deltaX);
        this.onAxisDrag('y', e.deltaY);

        this.scrollTo(lastDragPosition.x, lastDragPosition.y);
    },

    /**
     * @private
     */
    onDragEnd: function(e) {
        var easingX, easingY;

        if (!this.isDragging) {
            return;
        }

        this.dragEndTime = Ext.Date.now();

        this.onDrag(e);

        this.isDragging = false;

        easingX = this.getAnimationEasing('x');
        easingY = this.getAnimationEasing('y');

        if (easingX || easingY) {
            this.getTranslatable().animate(easingX, easingY);
        }
        else {
            this.onScrollEnd();
        }
    },

    /**
     * @private
     */
    getAnimationEasing: function(axis) {
        if (!this.isAxisEnabled(axis)) {
            return null;
        }

        var currentPosition = this.position[axis],
            flickStartPosition = this.flickStartPosition[axis],
            flickStartTime = this.flickStartTime[axis],
            minPosition = this.getMinPosition()[axis],
            maxPosition = this.getMaxPosition()[axis],
            maxAbsVelocity = this.getMaxAbsoluteVelocity(),
            boundValue = null,
            dragEndTime = this.dragEndTime,
            easing, velocity, duration;

        if (currentPosition < minPosition) {
            boundValue = minPosition;
        }
        else if (currentPosition > maxPosition) {
            boundValue = maxPosition;
        }

        // Out of bound, to be pulled back
        if (boundValue !== null) {
            easing = this.getBounceEasing()[axis];
            easing.setConfig({
                startTime: dragEndTime,
                startValue: -currentPosition,
                endValue: -boundValue
            });

            return easing;
        }

        // Still within boundary, start deceleration
        duration = dragEndTime - flickStartTime;

        if (duration === 0) {
            return null;
        }

        velocity = (currentPosition - flickStartPosition) / (dragEndTime - flickStartTime);

        if (velocity === 0) {
            return null;
        }

        if (velocity < -maxAbsVelocity) {
            velocity = -maxAbsVelocity;
        }
        else if (velocity > maxAbsVelocity) {
            velocity = maxAbsVelocity;
        }

        easing = this.getMomentumEasing()[axis];
        easing.setConfig({
            startTime: dragEndTime,
            startValue: -currentPosition,
            startVelocity: -velocity,
            minMomentumValue: -maxPosition,
            maxMomentumValue: 0
        });

        return easing;
    },

    /**
     * @private
     */
    onAnimationFrame: function(translatable, x, y) {
        var position = this.position;

        position.x = -x;
        position.y = -y;

        this.fireEvent('scroll', this, position.x, position.y);
    },

    onAxisAnimationEnd: function(axis) {

    },

    /**
     * @private
     */
    onAnimationEnd: function() {
        this.snapToBoundary();
        this.onScrollEnd();
    },

    /**
     * @private
     * Stops the animation of the scroller at any time.
     */
    stopAnimation: function() {
        this.getTranslatable().stopAnimation();
    },

    /**
     * @private
     */
    onScrollEnd: function() {
        var position = this.position;

        if (this.isTouching || !this.snapToSlot()) {
            this.fireEvent('scrollend', this, position.x, position.y);
        }
    },

    /**
     * @private
     */
    snapToSlot: function() {
        var snapX = this.getSnapPosition('x'),
            snapY = this.getSnapPosition('y'),
            easing = this.getSlotSnapEasing();

        if (snapX !== null || snapY !== null) {
            this.scrollTo(snapX, snapY, {
                easingX: easing.x,
                easingY: easing.y
            });

            return true;
        }

        return false;
    },

    /**
     * @private
     */
    getSnapPosition: function(axis) {
        var snapSize = this.getSlotSnapSize()[axis],
            snapPosition = null,
            position, snapOffset, maxPosition, mod;

        if (snapSize !== 0 && this.isAxisEnabled(axis)) {
            position = this.position[axis];
            snapOffset = this.getSlotSnapOffset()[axis];
            maxPosition = this.getMaxPosition()[axis];

            mod = (position - snapOffset) % snapSize;

            if (mod !== 0) {
                if (Math.abs(mod) > snapSize / 2) {
                    snapPosition = position + ((mod > 0) ? snapSize - mod : mod - snapSize);

                    if (snapPosition > maxPosition) {
                        snapPosition = position - mod;
                    }
                }
                else {
                    snapPosition = position - mod;
                }
            }
        }

        return snapPosition;
    },

    /**
     * @private
     */
    snapToBoundary: function() {
        var position = this.position,
            minPosition = this.getMinPosition(),
            maxPosition = this.getMaxPosition(),
            minX = minPosition.x,
            minY = minPosition.y,
            maxX = maxPosition.x,
            maxY = maxPosition.y,
            x = Math.round(position.x),
            y = Math.round(position.y);

        if (x < minX) {
            x = minX;
        }
        else if (x > maxX) {
            x = maxX;
        }

        if (y < minY) {
            y = minY;
        }
        else if (y > maxY) {
            y = maxY;
        }

        this.scrollTo(x, y);
    },

    destroy: function() {
        var element = this.getElement(),
            sizeMonitors = this.sizeMonitors;

        if (sizeMonitors) {
            sizeMonitors.element.destroy();
            sizeMonitors.container.destroy();
        }

        if (element && !element.isDestroyed) {
            element.removeCls(this.cls);
            this.getContainer().removeCls(this.containerCls);
        }

        Ext.destroy(this.translatable);

        this.callParent(arguments);
    }

}, function() {
});

/**
 * A core util class to bring Draggable behavior to any DOM element
 */
Ext.define('Ext.util.Draggable', {
    isDraggable: true,

    mixins: [
        'Ext.mixin.Observable'
    ],

    requires: [
        'Ext.util.SizeMonitor',
        'Ext.util.Translatable'
    ],

    /**
     * @event dragstart
     * @preventable initDragStart
     * Fires whenever the component starts to be dragged
     * @param {Ext.util.Draggable} this
     * @param {Ext.event.Event} e the event object
     * @param {Number} offsetX The current offset value on the x axis
     * @param {Number} offsetY The current offset value on the y axis
     */

    /**
     * @event drag
     * Fires whenever the component is dragged
     * @param {Ext.util.Draggable} this
     * @param {Ext.event.Event} e the event object
     * @param {Number} offsetX The new offset value on the x axis
     * @param {Number} offsetY The new offset value on the y axis
     */

    /**
     * @event dragend
     * Fires whenever the component is dragged
     * @param {Ext.util.Draggable} this
     * @param {Ext.event.Event} e the event object
     * @param {Number} offsetX The current offset value on the x axis
     * @param {Number} offsetY The current offset value on the y axis
     */

    config: {
        cls: Ext.baseCSSPrefix + 'draggable',

        draggingCls: Ext.baseCSSPrefix + 'dragging',

        element: null,

        constraint: 'container',

        disabled: null,

        /**
         * @cfg {String} direction
         * Possible values: 'vertical', 'horizontal', or 'both'
         * @accessor
         */
        direction: 'both',

        translatable: {}
    },

    DIRECTION_BOTH: 'both',

    DIRECTION_VERTICAL: 'vertical',

    DIRECTION_HORIZONTAL: 'horizontal',

    /**
     * Creates new Draggable.
     * @param {Object} config The configuration object for this Draggable.
     */
    constructor: function(config) {
        var element;

        this.sizeMonitors = {};

        this.extraConstraint = {};

        this.initialConfig = config;

        this.offset = {
            x: 0,
            y: 0
        };

        this.listeners = {
            dragstart: 'onDragStart',
            drag     : 'onDrag',
            dragend  : 'onDragEnd',

            scope: this
        };

        if (config && config.element) {
            element = config.element;
            delete config.element;

            this.setElement(element);
        }

        return this;
    },

    applyElement: function(element) {
        if (!element) {
            return;
        }

        return Ext.get(element);
    },

    updateElement: function(element) {
        element.on(this.listeners);

        this.sizeMonitors.element = new Ext.util.SizeMonitor({
            element: element,
            callback: this.doRefresh,
            scope: this
        });

        this.initConfig(this.initialConfig);
    },

    updateCls: function(cls) {
        this.getElement().addCls(cls);
    },

    applyTranslatable: function(translatable, currentInstance) {
        translatable = Ext.factory(translatable, Ext.util.Translatable, currentInstance);
        translatable.setElement(this.getElement());

        return translatable;
    },

    setExtraConstraint: function(constraint) {
        this.extraConstraint = constraint || {};

        this.refreshConstraint();

        return this;
    },

    addExtraConstraint: function(constraint) {
        Ext.merge(this.extraConstraint, constraint);

        this.refreshConstraint();

        return this;
    },

    applyConstraint: function(newConstraint, currentConstraint) {
        this.currentConstraint = newConstraint;

        if (newConstraint === 'container') {
            return Ext.merge(this.getContainerConstraint(), this.extraConstraint);
        }

        return Ext.merge({}, this.extraConstraint, newConstraint);
    },

    updateConstraint: function() {
        this.refreshOffset();
    },

    getContainerConstraint: function() {
        var container = this.getContainer();

        if (!container) {
            return {
                min: { x: -Infinity, y: -Infinity },
                max: { x: Infinity, y: Infinity }
            };
        }

        var dom = this.getElement().dom,
            containerDom = container.dom,
            width = dom.offsetWidth,
            height = dom.offsetHeight,
            containerWidth = containerDom.offsetWidth,
            containerHeight = containerDom.offsetHeight;

        return {
            min: { x: 0, y: 0 },
            max: { x: containerWidth - width, y: containerHeight - height }
        };
    },

    getContainer: function() {
        var container = this.container;

        if (!container) {
            container = this.getElement().getParent();

            if (container) {
                this.sizeMonitors.container = new Ext.util.SizeMonitor({
                    element: container,
                    callback: this.doRefresh,
                    scope: this
                });

                this.container = container;
            }
        }

        return container;
    },

    detachListeners: function() {
        this.getElement().un(this.listeners);
    },

    isAxisEnabled: function(axis) {
        var direction = this.getDirection();

        if (axis === 'x') {
            return (direction === this.DIRECTION_BOTH || direction === this.DIRECTION_HORIZONTAL);
        }

        return (direction === this.DIRECTION_BOTH || direction === this.DIRECTION_VERTICAL);
    },

    onDragStart: function(e) {
        if (this.getDisabled()) {
            return false;
        }

        var offset = this.offset;

        this.fireAction('dragstart', [this, e, offset.x, offset.y], this.initDragStart);
    },

    initDragStart: function(me, e, offsetX, offsetY) {
        this.dragStartOffset = {
            x: offsetX,
            y: offsetY
        };

        this.isDragging = true;

        this.getElement().addCls(this.getDraggingCls());
    },

    onDrag: function(e) {
        if (!this.isDragging) {
            return;
        }

        var startOffset = this.dragStartOffset;

        this.fireAction('drag', [this, e, startOffset.x + e.deltaX, startOffset.y + e.deltaY], this.doDrag);
    },

    doDrag: function(me, e, offsetX, offsetY) {
        me.setOffset(offsetX, offsetY);
    },

    onDragEnd: function(e) {
        if (!this.isDragging) {
            return;
        }

        this.onDrag(e);

        this.isDragging = false;

        this.getElement().removeCls(this.getDraggingCls());

        this.fireEvent('dragend', this, e, this.offset.x, this.offset.y);
    },

    setOffset: function(x, y, animation) {
        var currentOffset = this.offset,
            constraint = this.getConstraint(),
            minOffset = constraint.min,
            maxOffset = constraint.max,
            min = Math.min,
            max = Math.max;

        if (this.isAxisEnabled('x') && typeof x == 'number') {
            x = min(max(x, minOffset.x), maxOffset.x);
        }
        else {
            x = currentOffset.x;
        }

        if (this.isAxisEnabled('y') && typeof y == 'number') {
            y = min(max(y, minOffset.y), maxOffset.y);
        }
        else {
            y = currentOffset.y;
        }

        currentOffset.x = x;
        currentOffset.y = y;

        this.getTranslatable().translate(x, y, animation);
    },

    getOffset: function() {
        return this.offset;
    },

    refreshConstraint: function() {
        this.setConstraint(this.currentConstraint);
    },

    refreshOffset: function() {
        var offset = this.offset;

        this.setOffset(offset.x, offset.y);
    },

    doRefresh: function() {
        this.refreshConstraint();
        this.getTranslatable().refresh();
        this.refreshOffset();
    },

    refresh: function() {
        var sizeMonitors = this.sizeMonitors;

        if (sizeMonitors.element) {
            sizeMonitors.element.refresh();
        }

        if (sizeMonitors.container) {
            sizeMonitors.container.refresh();
        }

        this.doRefresh();
    },

    /**
     * Enable the Draggable.
     * @return {Ext.util.Draggable} This Draggable instance
     */
    enable: function() {
        return this.setDisabled(false);
    },

    /**
     * Disable the Draggable.
     * @return {Ext.util.Draggable} This Draggable instance
     */
    disable: function() {
        return this.setDisabled(true);
    },

    destroy: function() {
        var sizeMonitors = this.sizeMonitors,
            translatable = this.getTranslatable();

        if (sizeMonitors.element) {
            sizeMonitors.element.destroy();
        }

        if (sizeMonitors.container) {
            sizeMonitors.container.destroy();
        }

        var element = this.getElement();
        if (element && !element.isDestroyed) {
            element.removeCls(this.getCls());
        }

        this.detachListeners();

        if (translatable) {
            translatable.destroy();
        }
    }

}, function() {
});


/**
 * @private
 */
Ext.define('Ext.behavior.Draggable', {

    extend: 'Ext.behavior.Behavior',

    requires: [
        'Ext.util.Draggable'
    ],

    constructor: function() {
        this.listeners = {
            painted: 'onComponentPainted',
            scope: this
        };

        this.callParent(arguments);
    },

    onComponentPainted: function() {
        this.draggable.refresh();
    },

    setConfig: function(config) {
        var draggable = this.draggable,
            component = this.component;

        if (config) {
            if (!draggable) {
                component.setTranslatable(true);
                this.draggable = draggable = new Ext.util.Draggable(config);
                draggable.setTranslatable(component.getTranslatable());
                draggable.setElement(component.renderElement);
                draggable.on('destroy', 'onDraggableDestroy', this);

                if (component.isPainted()) {
                    this.onComponentPainted(component);
                }

                component.on(this.listeners);
            }
            else if (Ext.isObject(config)) {
                draggable.setConfig(config);
            }
        }
        else if (draggable) {
            draggable.destroy();
        }

        return this;
    },

    getDraggable: function() {
        return this.draggable;
    },

    onDraggableDestroy: function() {
        var component = this.component;

        delete this.draggable;
        component.un(this.listeners);
    },

    onComponentDestroy: function() {
        var draggable = this.draggable;

        if (draggable) {
            draggable.destroy();
        }
    }
});

(function(clsPrefix) {

/**
 * Most of the visual classes you interact with in Sencha Touch are Components. Every Component in Sencha Touch is a
 * subclass of Ext.Component, which means they can all:
 *
 * * Render themselves onto the page using a template
 * * Show and hide themselves at any time
 * * Center themselves on the screen
 * * Enable and disable themselves
 *
 * They can also do a few more advanced things:
 *
 * * Float above other components (windows, message boxes and overlays)
 * * Change size and position on the screen with animation
 * * Dock other Components inside themselves (useful for toolbars)
 * * Align to other components, allow themselves to be dragged around, make their content scrollable & more
 *
 * ## Available Components
 *
 * There are many components available in Sencha Touch, separated into 4 main groups:
 *
 * ### Navigation components
 * * {@link Ext.Toolbar}
 * * {@link Ext.Button}
 * * {@link Ext.TitleBar}
 * * {@link Ext.SegmentedButton}
 * * {@link Ext.Title}
 * * {@link Ext.Spacer}
 *
 * ### Store-bound components
 * * {@link Ext.dataview.DataView}
 * * {@link Ext.Carousel}
 * * {@link Ext.List}
 * * {@link Ext.NestedList}
 *
 * ### Form components
 * * {@link Ext.form.Panel}
 * * {@link Ext.form.FieldSet}
 * * {@link Ext.field.Checkbox}
 * * {@link Ext.field.Hidden}
 * * {@link Ext.field.Slider}
 * * {@link Ext.field.Text}
 * * {@link Ext.picker.Picker}
 * * {@link Ext.picker.Date}
 *
 * ### General components
 * * {@link Ext.Panel}
 * * {@link Ext.tab.Panel}
 * * {@link Ext.Viewport Ext.Viewport}
 * * {@link Ext.Img}
 * * {@link Ext.Map}
 * * {@link Ext.Audio}
 * * {@link Ext.Video}
 * * {@link Ext.Sheet}
 * * {@link Ext.ActionSheet}
 * * {@link Ext.MessageBox}
 *
 *
 * ## Instantiating Components
 *
 * Components are created the same way as all other classes in Sencha Touch - using Ext.create. Here's how we can
 * create a Text field:
 *
 *     var panel = Ext.create('Ext.Panel', {
 *         html: 'This is my panel'
 *     });
 *
 * This will create a {@link Ext.Panel Panel} instance, configured with some basic HTML content. A Panel is just a
 * simple Component that can render HTML and also contain other items. In this case we've created a Panel instance but
 * it won't show up on the screen yet because items are not rendered immediately after being instantiated. This allows
 * us to create some components and move them around before rendering and laying them out, which is a good deal faster
 * than moving them after rendering.
 *
 * To show this panel on the screen now we can simply add it to the global Viewport:
 *
 *     Ext.Viewport.add(panel);
 *
 * Panels are also Containers, which means they can contain other Components, arranged by a layout. Let's revisit the
 * above example now, this time creating a panel with two child Components and a hbox layout:
 *
 *     @example
 *     var panel = Ext.create('Ext.Panel', {
 *         layout: 'hbox',
 *
 *         items: [
 *             {
 *                 xtype: 'panel',
 *                 flex: 1,
 *                 html: 'Left Panel, 1/3rd of total size',
 *                  style: 'background-color: #5E99CC;'
 *             },
 *             {
 *                 xtype: 'panel',
 *                 flex: 2,
 *                 html: 'Right Panel, 2/3rds of total size',
 *                  style: 'background-color: #759E60;'
 *             }
 *         ]
 *     });
 *
 *     Ext.Viewport.add(panel);
 *
 * This time we created 3 Panels - the first one is created just as before but the inner two are declared inline using
 * an xtype. Xtype is a convenient way of creating Components without having to go through the process of using
 * Ext.create and specifying the full class name, instead you can just provide the xtype for the class inside an object
 * and the framework will create the components for you.
 *
 * We also specified a layout for the top level panel - in this case hbox, which splits the horizontal width of the
 * parent panel based on the 'flex' of each child. For example, if the parent Panel above is 300px wide then the first
 * child will be flexed to 100px wide and the second to 200px because the first one was given flex: 1 and the second
 * flex: 2.
 *
 * ## Using xtype
 *
 * xtype is an easy way to create Components without using the full class name. This is especially useful when creating
 * a {@link Ext.Container Container} that contains child Components. An xtype is simply a shorthand way of specifying a
 * Component - for example you can use xtype: 'panel' instead of typing out Ext.panel.Panel.
 *
 * Sample usage:
 *
 *     @example miniphone
 *     Ext.create('Ext.Container', {
 *         fullscreen: true,
 *         layout: 'fit',
 *
 *         items: [
 *             {
 *                 xtype: 'panel',
 *                 html: 'This panel is created by xtype'
 *             },
 *             {
 *                 xtype: 'toolbar',
 *                 title: 'So is the toolbar',
 *                 docked: 'top'
 *             }
 *         ]
 *     });
 *
 *
 * ### Common xtypes
 *
 * These are the xtypes that are most commonly used. For an exhaustive list please see the
 * <a href="#!/guide/components">Components Guide</a>
 *
 * <pre>
 xtype                   Class
 -----------------       ---------------------
 actionsheet             Ext.ActionSheet
 audio                   Ext.Audio
 button                  Ext.Button
 image                   Ext.Img
 label                   Ext.Label
 loadmask                Ext.LoadMask
 map                     Ext.Map
 panel                   Ext.Panel
 segmentedbutton         Ext.SegmentedButton
 sheet                   Ext.Sheet
 spacer                  Ext.Spacer
 title                   Ext.Title
 toolbar                 Ext.Toolbar
 video                   Ext.Video
 carousel                Ext.carousel.Carousel
 navigationview          Ext.navigation.View
 datepicker              Ext.picker.Date
 picker                  Ext.picker.Picker
 slider                  Ext.slider.Slider
 thumb                   Ext.slider.Thumb
 tabpanel                Ext.tab.Panel
 viewport                Ext.viewport.Default

 DataView Components
 ---------------------------------------------
 dataview                Ext.dataview.DataView
 list                    Ext.dataview.List
 nestedlist              Ext.dataview.NestedList

 Form Components
 ---------------------------------------------
 checkboxfield           Ext.field.Checkbox
 datepickerfield         Ext.field.DatePicker
 emailfield              Ext.field.Email
 hiddenfield             Ext.field.Hidden
 numberfield             Ext.field.Number
 passwordfield           Ext.field.Password
 radiofield              Ext.field.Radio
 searchfield             Ext.field.Search
 selectfield             Ext.field.Select
 sliderfield             Ext.field.Slider
 spinnerfield            Ext.field.Spinner
 textfield               Ext.field.Text
 textareafield           Ext.field.TextArea
 togglefield             Ext.field.Toggle
 urlfield                Ext.field.Url
 fieldset                Ext.form.FieldSet
 formpanel               Ext.form.Panel
 * </pre>
 *
 * ## Configuring Components
 *
 * Whenever you create a new Component you can pass in configuration options. All of the configurations for a given
 * Component are listed in the "Config options" section of its class docs page. You can pass in any number of
 * configuration options when you instantiate the Component, and modify any of them at any point later. For example, we
 *  can easily modify the {@link Ext.Panel#html html content} of a Panel after creating it:
 *
 *     @example miniphone
 *     //we can configure the HTML when we instantiate the Component
 *     var panel = Ext.create('Ext.Panel', {
 *         fullscreen: true,
 *         html: 'This is a Panel'
 *     });
 *
 *     //we can update the HTML later using the setHtml method:
 *     panel.setHtml('Some new HTML');
 *
 *     //we can retrieve the current HTML using the getHtml method:
 *     alert(panel.getHtml()); //alerts "Some new HTML"
 *
 * Every config has a getter method and a setter method - these are automatically generated and always follow the same
 * pattern. For example, a config called 'html' will receive getHtml and setHtml methods, a config called defaultType
 * will receive getDefaultType and setDefaultType methods, and so on.
 *
 * ## Further Reading
 *
 * See the [Component & Container Guide](#!/guide/components) for more information, and check out the
 * {@link Ext.Container} class docs also.
 *
 * @aside guide components
 * @aside guide events
 *
 */
Ext.define('Ext.Component', {

    extend: 'Ext.AbstractComponent',

    alternateClassName: 'Ext.lib.Component',

    mixins: ['Ext.mixin.Traversable'],

    requires: [
        'Ext.ComponentManager',
        'Ext.XTemplate',
        'Ext.dom.Element',
        'Ext.behavior.Translatable',
        'Ext.behavior.Draggable'
    ],

    /**
     * @cfg {String} xtype
     * The `xtype` configuration option can be used to optimize Component creation and rendering. It serves as a
     * shortcut to the full componet name. For example, the component `Ext.button.Button` has an xtype of `button`.
     *
     * You can define your own xtype on a custom {@link Ext.Component component} by specifying the
     * {@link Ext.Class#alias alias} config option with a prefix of `widget`. For example:
     *
     *     Ext.define('PressMeButton', {
     *         extend: 'Ext.button.Button',
     *         alias: 'widget.pressmebutton',
     *         text: 'Press Me'
     *     })
     *
     * Any Component can be created implicitly as an object config with an xtype specified, allowing it to be
     * declared and passed into the rendering pipeline without actually being instantiated as an object. Not only is
     * rendering deferred, but the actual creation of the object itself is also deferred, saving memory and resources
     * until they are actually needed. In complex, nested layouts containing many Components, this can make a
     * noticeable improvement in performance.
     *
     *     // Explicit creation of contained Components:
     *     var panel = new Ext.Panel({
     *        ...
     *        items: [
     *           Ext.create('Ext.button.Button', {
     *              text: 'OK'
     *           })
     *        ]
     *     };
     *
     *     // Implicit creation using xtype:
     *     var panel = new Ext.Panel({
     *        ...
     *        items: [{
     *           xtype: 'button',
     *           text: 'OK'
     *        }]
     *     };
     *
     * In the first example, the button will always be created immediately during the panel's initialization. With
     * many added Components, this approach could potentially slow the rendering of the page. In the second example,
     * the button will not be created or rendered until the panel is actually displayed in the browser. If the panel
     * is never displayed (for example, if it is a tab that remains hidden) then the button will never be created and
     * will never consume any resources whatsoever.
     */
    xtype: 'component',

    observableType: 'component',

    cachedConfig: {
        /**
         * @cfg {Number} flex
         * The flex of this item *if* this item item is inside a {@link Ext.layout.HBox} or {@link Ext.layout.VBox}
         * layout.
         *
         * You can also update the flex of a component dynamically using the {@link Ext.layout.AbstractBox#setItemFlex}
         * method.
         * @accessor
         */

        /**
         * @cfg {String} baseCls
         * The base CSS class to apply to this components's element. This will also be prepended to
         * other elements within this component. To add specific styling for sub-classes, use the {@link #cls} config.
         * @accessor
         */
        baseCls: null,

        /**
         * @cfg {String/String[]} cls The CSS class to add to this component's element, in addition to the {@link #baseCls}
         * @accessor
         */
        cls: null,

        /**
         * @cfg {String} floatingCls The CSS class to add to this component when it is floatable.
         * @accessor
         */
        floatingCls: null,

        /**
         * @cfg {String} hiddenCls The CSS class to add to the component when it is hidden
         * @accessor
         */
        hiddenCls: clsPrefix + 'item-hidden',

        /**
         * @cfg {String} ui The ui to be used on this Component
         */
        ui: null,

        /**
         * @cfg {Number/String} margin The margin to use on this Component. Can be specified as a number (in which case
         * all edges get the same margin) or a CSS string like '5 10 10 10'
         * @accessor
         */
        margin: null,

        /**
         * @cfg {Number/String} padding The padding to use on this Component. Can be specified as a number (in which
         * case all edges get the same padding) or a CSS string like '5 10 10 10'
         * @accessor
         */
        padding: null,

        /**
         * @cfg {Number/String} border The border to use on this Component. Can be specified as a number (in which
         * case all edges get the same border width) or a CSS string like '5 10 10 10'
         * @accessor
         */
        border: null,

        /**
         * @cfg {String} styleHtmlCls
         * The class that is added to the content target when you set styleHtmlContent to true.
         * @accessor
         */
        styleHtmlCls: clsPrefix + 'html',

        /**
         * @cfg {Boolean} [styleHtmlContent=false]
         * True to automatically style the html inside the content target of this component (body for panels).
         * @accessor
         */
        styleHtmlContent: null
    },

    eventedConfig: {
        /**
         * @cfg {Number/String} left
         * The absolute left position of this Component; must be a valid CSS length value, e.g: `300`, `100px`, `30%`, etc.
         * Explicitly setting this value will make this Component become 'floating', which means its layout will no
         * longer be affected by the Container that it resides in.
         * @accessor
         * @evented
         */
        left: null,

        /**
         * @cfg {Number/String} top
         * The absolute top position of this Component; must be a valid CSS length value, e.g: `300`, `100px`, `30%`, etc.
         * Explicitly setting this value will make this Component become 'floating', which means its layout will no
         * longer be affected by the Container that it resides in.
         * @accessor
         * @evented
         */
        top: null,

        /**
         * @cfg {Number/String} right
         * The absolute right position of this Component; must be a valid CSS length value, e.g: `300`, `100px`, `30%`, etc.
         * Explicitly setting this value will make this Component become 'floating', which means its layout will no
         * longer be affected by the Container that it resides in.
         * @accessor
         * @evented
         */
        right: null,

        /**
         * @cfg {Number/String} bottom
         * The absolute bottom position of this Component; must be a valid CSS length value, e.g: `300`, `100px`, `30%`, etc.
         * Explicitly setting this value will make this Component become 'floating', which means its layout will no
         * longer be affected by the Container that it resides in.
         * @accessor
         * @evented
         */
        bottom: null,

        /**
         * @cfg {Number/String} width
         * The width of this Component; must be a valid CSS length value, e.g: `300`, `100px`, `30%`, etc.
         * By default, if this is not explicitly set, this Component's element will simply have its own natual size.
         * @accessor
         * @evented
         */
        width: null,

        /**
         * @cfg {Number/String} height
         * The height of this Component; must be a valid CSS length value, e.g: `300`, `100px`, `30%`, etc.
         * By default, if this is not explicitly set, this Component's element will simply have its own natual size.
         * @accessor
         * @evented
         */
        height: null,

        /**
         * @cfg {Number/String} minWidth
         * The minimum width of this Component; must be a valid CSS length value, e.g: `300`, `100px`, `30%`, etc.
         * @accessor
         * @evented
         */
        minWidth: null,

        /**
         * @cfg {Number/String} minHeight
         * The minimum height of this Component; must be a valid CSS length value, e.g: `300`, `100px`, `30%`, etc.
         * @accessor
         * @evented
         */
        minHeight: null,

        /**
         * @cfg {Number/String} maxWidth
         * The maximum width of this Component; must be a valid CSS length value, e.g: `300`, `100px`, `30%`, etc.
         * Note that this config will not apply if the Component is 'floating' (absolutely positioned or centered)
         * @accessor
         * @evented
         */
        maxWidth: null,

        /**
         * @cfg {Number/String} maxHeight
         * The maximum height of this Component; must be a valid CSS length value, e.g: `300`, `100px`, `30%`, etc.
         * Note that this config will not apply if the Component is 'floating' (absolutely positioned or centered)
         * @accessor
         * @evented
         */
        maxHeight: null,

        /**
         * @cfg {String} docked
         * The dock position of this component in its container. Can be 'left', 'top', 'right' or 'bottom'.
         *
         * <b>Notes</b>
         *
         * You must use a HTML5 doctype for {@link #docked} `bottom` to work. To do this, simply add the following code to the HTML file:
         *
         *     <!doctype html>
         *
         * So your index.html file should look a little like this:
         *
         *     <!doctype html>
         *     <html>
         *         <head>
         *             <title>MY application title</title>
         *             ...
         *
         * @accessor
         * @evented
         */
        docked: null,

        /**
         * @cfg {Boolean} centered
         * Whether or not this Component is absolutely centered inside its Container
         * @accessor
         * @evented
         */
        centered: null,

        /**
         * @cfg {Boolean} hidden
         * Whether or not this Component is hidden (its CSS `display` property is set to `none`)
         * @accessor
         * @evented
         */
        hidden: null,

        /**
         * @cfg {Boolean} disabled
         * Whether or not this component is disabled
         * @accessor
         * @evented
         */
        disabled: null
    },

    config: {
        /**
         * @cfg {String/Object} style Optional CSS styles that will be rendered into an inline style attribute when the
         * Component is rendered
         * @accessor
         */
        style: null,

        /**
         * @cfg {String/Ext.Element/HTMLElement} html Optional HTML content to render inside this Component, or a reference
         * to an existing element on the page.
         * @accessor
         */
        html: null,

        /**
         * @cfg {Object} draggable Configuration options to make this Component draggable
         * @accessor
         */
        draggable: null,

        /**
         * @cfg {Object} translatable
         * @private
         * @accessor
         */
        translatable: null,

        /**
         * @cfg {Ext.Element} renderTo Optional element to render this Component to. Usually this is not needed because
         * a Component is normally full screen or automatically rendered inside another {@link Ext.Container Container}
         * @accessor
         */
        renderTo: null,

        /**
         * @cfg {Number} zIndex The z-index to give this Component when it is rendered
         * @accessor
         */
        zIndex: null,

        /**
         * @cfg {String/String[]/Ext.Template[]/Ext.XTemplate[]} tpl
         * A {@link String}, {@link Ext.Template}, {@link Ext.XTemplate} or an {@link Array} of strings to form an {@link Ext.XTemplate}.
         * Used in conjunction with the {@link #data} and {@link #tplWriteMode} configurations.
         *
         * <b>Note</b>
         * The {@link #data} configuration <i>must</i> be set for any content to be shown in the component when using this configuration.
         * @accessor
         */
        tpl: null,

        /**
         * @cfg {String/Mixed} enterAnimation
         * Animation effect to apply when the Component is being shown.  Typically you want to use an
         * inbound animation type such as 'fadeIn' or 'slideIn'.
         * @deprecated 2.0.0 Please use {@link #showAnimation} instead.
         * @accessor
         */
        enterAnimation: null,

        /**
         * @cfg {String/Mixed} exitAnimation
         * Animation effect to apply when the Component is being hidden.
         * @deprecated 2.0.0 Please use {@link #hideAnimation} instead.  Typically you want to use an
         * outbound animation type such as 'fadeOut' or 'slideOut'.
         * @accessor
         */
        exitAnimation: null,

        /**
         * @cfg {String/Mixed} showAnimation
         * Animation effect to apply when the Component is being shown.  Typically you want to use an
         * inbound animation type such as 'fadeIn' or 'slideIn'.
         * @accessor
         */
        showAnimation: null,

        /**
         * @cfg {String/Mixed} hideAnimation
         * Animation effect to apply when the Component is being hidden.  Typically you want to use an
         * outbound animation type such as 'fadeOut' or 'slideOut'.
         * @accessor
         */
        hideAnimation: null,

        /**
         * @cfg {Mixed} renderTpl
         * <p>An {@link Ext.XTemplate XTemplate} used to create the internal structure inside this Component's
         * encapsulating Element.</p>
         * <p>You do not normally need to specify this. For the base classes {@link Ext.Component}
         * and {@link Ext.Container}, this defaults to <b><code>null</code></b> which means that they will be initially rendered
         * with no internal structure; they render their {@link #getEl Element} empty. The more specialized ExtJS and Touch classes
         * which use a more complex DOM structure, provide their own template definitions.</p>
         * <p>This is intended to allow the developer to create application-specific utility Components with customized
         * internal structure.</p>
         * @accessor
         */

        /**
         * @cfg {String} tplWriteMode The Ext.(X)Template method to use when
         * updating the content area of the Component. Defaults to <code>'overwrite'</code>
         * (see <code>{@link Ext.XTemplate#overwrite}</code>).
         * @accessor
         */
        tplWriteMode: 'overwrite',

        /**
         * @cfg {Mixed} data
         * The initial set of data to apply to the <code>{@link #tpl}</code> to
         * update the content area of the Component.
         * @accessor
         */
        data: null,

        /**
         * @cfg {String} disabledCls The CSS class to add to the component when it is disabled
         * @accessor
         */
        disabledCls: clsPrefix + 'item-disabled',

        /**
         * @cfg {Ext.Element/HTMLElement/String} contentEl The configured element will automatically be
         * added as the content of this component. When you pass a string, we expect it to be an element id.
         * If the content element is hidden, we will automatically show it.
         * @accessor
         */
        contentEl: null,

        /**
         * @cfg {String} itemId
         * @accessor
         */
        itemId: undefined,

        /**
         * @cfg {Ext.data.Model} record A model instance which updates the Component's html based on it's tpl. Similar to the data
         * configuration, but tied to to a record to make allow dynamic updates.  This must be a model
         * instance and not a configuration of one.
         * @accessor
         */
        record: null,

        /**
         * @cfg {Object/Array} plugins
         * @accessor
         * An object or array of objects that will provide custom functionality for this component.  The only
         * requirement for a valid plugin is that it contain an init method that accepts a reference of type Ext.Component.
         *
         * When a component is created, if any plugins are available, the component will call the init method on each
         * plugin, passing a reference to itself.  Each plugin can then call methods or respond to events on the
         * component as needed to provide its functionality.
         *
         * For examples of plugins, see Ext.plugin.PullRefresh and Ext.plugin.ListPaging
         *
         * ## Example code
         *
         * A plugin by alias:
         *
         *     Ext.create('Ext.dataview.List', {
         *         config: {
         *             plugins: 'listpaging',
         *             itemTpl: '<div class="item">{title}</div>',
         *             store: 'Items'
         *         }
         *     });
         *
         * Multiple plugins by alias:
         *
         *     Ext.create('Ext.dataview.List', {
         *         config: {
         *             plugins: ['listpaging', 'pullrefresh'],
         *             itemTpl: '<div class="item">{title}</div>',
         *             store: 'Items'
         *         }
         *     });
         *
         * Single plugin by class name with config options:
         *
         *     Ext.create('Ext.dataview.List', {
         *         config: {
         *             plugins: {
         *                 xclass: 'Ext.plugin.ListPaging', // Reference plugin by class
         *                 autoPaging: true
         *             },
         *
         *             itemTpl: '<div class="item">{title}</div>',
         *             store: 'Items'
         *         }
         *     });
         *
         * Multiple plugins by class name with config options:
         *
         *     Ext.create('Ext.dataview.List', {
         *         config: {
         *             plugins: [
         *                 {
         *                     xclass: 'Ext.plugin.PullRefresh',
         *                     pullRefreshText: 'Pull to refresh...'
         *                 },
         *                 {
         *                     xclass: 'Ext.plugin.ListPaging',
         *                     autoPaging: true
         *                 }
         *             ],
         *
         *             itemTpl: '<div class="item">{title}</div>',
         *             store: 'Items'
         *         }
         *     });
         *
         */
        plugins: null
    },

    /**
     * @event painted
     * Fires whenever this Component actually becomes visible (painted) on the screen. This is useful when you need to
     * perform 'read' operations on the DOM element, i.e: calculating natural sizes and positioning. If you just need
     * perform post-initialization tasks that don't rely on the fact that the element must be rendered and visible on
     * the screen, listen to {@link #initialize}
     *
     * Note: This event is not available to be used with event delegation. Instead 'painted' only fires if you explicily
     * add at least one listener to it, due to performance reason.
     *
     * @param {Ext.Component} this The component instance
     */

    /**
     * @event erased
     * The opposite event of {@link #painted}. This event fires whenever this Component are no longer visible
     * (erased) on the screen. This might be triggered either when this Component {@link #hidden} config is set to
     * `true`, or when its Element is taken out of the document's DOM tree. This is normally useful to perform cleaning
     * up after what you have set up from listening to {@link #painted} event.
     *
     * Note: This event is not available to be used with event delegation. Instead 'erased' only fires if you explicily
     * add at least one listener to it, due to performance reason.
     *
     * @param {Ext.Component} this The component instance
     */

    /**
     * @event resize
     * Important note: For the best performance on mobile devices, use this only when you absolutely need to monitor
     * a Component's size.
     *
     * Note: This event is not available to be used with event delegation. Instead 'resize' only fires if you explicily
     * add at least one listener to it, due to performance reason.
     *
     * @param {Ext.Component} this The component instance
     */

    /**
     * @event show
     * Fires whenever the Component is shown
     * @param {Ext.Component} this The component instance
     */

    /**
     * @event hide
     * Fires whenever the Component is hidden
     * @param {Ext.Component} this The component instance
     */

    /**
     * @event fullscreen
     * Fires whenever a Component with the fullscreen config is instantiated
     * @param {Ext.Component} this The component instance
     */

    /**
     * @event floatingchange
     * Fires whenever there is a change in the floating status of a component
     * @param {Ext.Component} this The component instance
     * @param {Boolean} floating The component's new floating state
     */

    /**
     * @event beforeorientationchange
     * @removed 2.0.0 This event is now only available onBefore the Viewport's {@link Ext.Viewport#orientationchange}
     */

    /**
     * @event orientationchange
     * @removed 2.0.0 This event is now only available on the Viewport's {@link Ext.Viewport#orientationchange}
     */

    /**
     * @private
     */
    listenerOptionsRegex: /^(?:delegate|single|delay|buffer|args|prepend|element)$/,

    /**
     * @private
     */
    alignmentRegex: /^([a-z]+)-([a-z]+)(\?)?$/,

    /**
     * @private
     */
    isComponent: true,

    /**
     * @private
     */
    floating: false,

    /**
     * @private
     */
    rendered: false,

    /**
     * @readonly
     */
    dockPositions: {
        top: true,
        right: true,
        bottom: true,
        left: true
    },

    innerElement: null,

    element: null,

    template: [],

    /**
     * Creates new Component.
     * @param {Object} config The standard configuration object.
     */
    constructor: function(config) {
        var me = this,
            currentConfig = me.config,
            id;

        me.onInitializedListeners = [];
        me.initialConfig = config;

        if (config !== undefined && 'id' in config) {
            id = config.id;
        }
        else if ('id' in currentConfig) {
            id = currentConfig.id;
        }
        else {
            id = me.getId();
        }

        me.id = id;
        me.setId(id);

        Ext.ComponentManager.register(me);

        me.initElement();

        me.initConfig(me.initialConfig);

        me.initialize();

        me.triggerInitialized();

        /**
         * Force the component to take up 100% width and height available, by adding it to {@link Ext.Viewport}.
         * @cfg {Boolean} fullscreen
         */
        if ('fullscreen' in me.config) {
            me.fireEvent('fullscreen', me);
        }

        me.fireEvent('initialize', me);
    },

    beforeInitConfig: function(config) {
        this.beforeInitialize.apply(this, arguments);
    },

    /**
     * @private
     */
    beforeInitialize: Ext.emptyFn,

    /**
     * Allows addition of behavior to the rendering phase.
     * @protected
     * @template
     */
    initialize: Ext.emptyFn,

    getTemplate: function() {
        return this.template;
    },

    getElementConfig: function() {
        return {
            reference: 'element',
            children: this.getTemplate()
        };
    },

    /**
     * @private
     */
    triggerInitialized: function() {
        var listeners = this.onInitializedListeners,
            ln = listeners.length,
            listener, i;

        if (!this.initialized) {
            this.initialized = true;

            if (ln > 0) {
                for (i = 0; i < ln; i++) {
                    listener = listeners[i];
                    listener.fn.call(listener.scope, this);
                }

                listeners.length = 0;
            }
        }
    },

    /**
     * @private
     * @param fn
     * @param scope
     */
    onInitialized: function(fn, scope) {
        var listeners = this.onInitializedListeners;

        if (!scope) {
            scope = this;
        }

        if (this.initialized) {
            fn.call(scope, this);
        }
        else {
            listeners.push({
                fn: fn,
                scope: scope
            });
        }
    },

    renderTo: function(container, insertBeforeElement) {
        var dom = this.renderElement.dom,
            containerDom = Ext.getDom(container),
            insertBeforeChildDom = Ext.getDom(insertBeforeElement);

        if (containerDom) {
            if (insertBeforeChildDom) {
                containerDom.insertBefore(dom, insertBeforeChildDom);
            }
            else {
                containerDom.appendChild(dom);
            }

            this.setRendered(Boolean(dom.offsetParent));
        }
    },

    setParent: function(parent) {
        var currentParent = this.parent;

        if (parent && currentParent && currentParent !== parent) {
            currentParent.remove(this, false);
        }

        this.parent = parent;

        return this;
    },

    applyPlugins: function(config) {
        var ln, i, configObj;

        if (!config) {
            return config;
        }

        config = [].concat(config);

        for (i = 0, ln = config.length; i < ln; i++) {
            configObj = config[i];
            config[i] = Ext.factory(configObj, 'Ext.plugin.Plugin', null, 'plugin');
        }

        return config;
    },

    updatePlugins: function(newPlugins, oldPlugins) {
        var ln, i;

        if (newPlugins) {
            for (i = 0, ln = newPlugins.length; i < ln; i++) {
                newPlugins[i].init(this);
            }
        }

        if (oldPlugins) {
            for (i = 0, ln = oldPlugins.length; i < ln; i++) {
                Ext.destroy(oldPlugins[i]);
            }
        }
    },

    updateRenderTo: function(newContainer) {
        this.renderTo(newContainer);
    },

    updateStyle: function(style) {
        this.element.dom.style.cssText += style;
    },

    updateBorder: function(border) {
        this.element.setBorder(border);
    },

    updatePadding: function(padding) {
       this.innerElement.setPadding(padding);
    },

    updateMargin: function(margin) {
        this.element.setMargin(margin);
    },

    updateUi: function(newUi, oldUi) {
        var baseCls = this.getBaseCls();

        if (baseCls) {
            if (oldUi) {
                this.element.removeCls(oldUi, baseCls);
            }

            if (newUi) {
                this.element.addCls(newUi, baseCls);
            }
        }
    },

    applyBaseCls: function(baseCls) {
        return baseCls || clsPrefix + this.xtype;
    },

    updateBaseCls: function(newBaseCls, oldBaseCls) {
        var me = this,
            ui = me.getUi();

        if (newBaseCls) {
            this.element.addCls(newBaseCls);

            if (ui) {
                this.element.addCls(newBaseCls, null, ui);
            }
        }

        if (oldBaseCls) {
            this.element.removeCls(oldBaseCls);

            if (ui) {
                this.element.removeCls(oldBaseCls, null, ui);
            }
        }
    },

    /**
     * Adds a CSS class (or classes) to this Component's rendered element
     * @param {String} cls The CSS class to add
     * @param {String} prefix Optional prefix to add to each class
     * @param {String} suffix Optional suffix to add to each class
     */
    addCls: function(cls, prefix, suffix) {
        var oldCls = this.getCls(),
            newCls = (oldCls) ? oldCls.slice() : [],
            ln, i, cachedCls;

        prefix = prefix || '';
        suffix = suffix || '';

        if (typeof cls == "string") {
            cls = [cls];
        }

        ln = cls.length;

        //check if there is currently nothing in the array and we dont need to add a prefix or a suffix.
        //if true, we can just set the newCls value to the cls property, because that is what the value will be
        //if false, we need to loop through each and add them to the newCls array
        if (!newCls.length && prefix === '' && suffix === '') {
            newCls = cls;
        } else {
            for (i = 0; i < ln; i++) {
                cachedCls = prefix + cls[i] + suffix;
                if (newCls.indexOf(cachedCls) == -1) {
                    newCls.push(cachedCls);
                }
            }
        }

        this.setCls(newCls);
    },

    /**
     * Removes the given CSS class(es) from this Component's rendered element
     * @param {String} cls The class(es) to remove
     * @param {String} prefix Optional prefix to prepend before each class
     * @param {String} suffix Optional suffix to append to each class
     */
    removeCls: function(cls, prefix, suffix) {
        var oldCls = this.getCls(),
            newCls = (oldCls) ? oldCls.slice() : [],
            ln, i;

        prefix = prefix || '';
        suffix = suffix || '';

        if (typeof cls == "string") {
            newCls = Ext.Array.remove(newCls, prefix + cls + suffix);
        } else {
            ln = cls.length;
            for (i = 0; i < ln; i++) {
                newCls = Ext.Array.remove(newCls, prefix + cls[i] + suffix);
            }
        }

        this.setCls(newCls);
    },

    /**
     * Replaces specified classes with the newly specified classes.
     * It uses the {@link #addCls} and {@link #removeCls} methdos, so if the class(es) you are removing don't exist, it will
     * still add the new classes.
     * @param {String} oldCls The class(es) to remove
     * @param {String} newCls The class(es) to add
     * @param {String} prefix Optional prefix to prepend before each class
     * @param {String} suffix Optional suffix to append to each class
     */
    replaceCls: function(oldCls, newCls, prefix, suffix) {
        // We could have just called {@link #removeCls} and {@link #addCls}, but that would mean {@link #updateCls}
        // would get called twice, which would have performance implications because it will update the dom.

        var cls = this.getCls(),
            array = (cls) ? cls.slice() : [],
            ln, i, cachedCls;

        prefix = prefix || '';
        suffix = suffix || '';

        //remove all oldCls
        if (typeof oldCls == "string") {
            array = Ext.Array.remove(array, prefix + oldCls + suffix);
        } else if (oldCls) {
            ln = oldCls.length;
            for (i = 0; i < ln; i++) {
                array = Ext.Array.remove(array, prefix + oldCls[i] + suffix);
            }
        }

        //add all newCls
        if (typeof newCls == "string") {
            array.push(prefix + newCls + suffix);
        } else if (newCls) {
            ln = newCls.length;

            //check if there is currently nothing in the array and we dont need to add a prefix or a suffix.
            //if true, we can just set the array value to the newCls property, because that is what the value will be
            //if false, we need to loop through each and add them to the array
            if (!array.length && prefix === '' && suffix === '') {
                array = newCls;
            } else {
                for (i = 0; i < ln; i++) {
                    cachedCls = prefix + newCls[i] + suffix;
                    if (array.indexOf(cachedCls) == -1) {
                        array.push(cachedCls);
                    }
                }
            }
        }

        this.setCls(array);
    },

    /**
     * @private
     * Checks if the cls is a string. If it is, changed it into an array
     */
    applyCls: function(cls) {
        if (typeof cls == "string") {
            cls = [cls];
        }

        //reset it back to null if there is nothing.
        if (!cls || !cls.length) {
            cls = null;
        }

        return cls;
    },

    /**
     * @private
     * All cls methods directly report to the {@link #cls} configuration, so anytime it changes, {@link #updateCls} will be called
     */
    updateCls: function(newCls, oldCls) {
        this.element.replaceCls(oldCls, newCls);
    },

    /**
     * Updates the {@link #styleHtmlCls} configuration
     */
    updateStyleHtmlCls: function(newHtmlCls, oldHtmlCls) {
        var innerHtmlElement = this.innerHtmlElement,
            innerElement = this.innerElement;

        if (this.getStyleHtmlContent() && oldHtmlCls) {
            if (innerHtmlElement) {
                innerHtmlElement.replaceCls(oldHtmlCls, newHtmlCls);
            } else {
                innerElement.replaceCls(oldHtmlCls, newHtmlCls);
            }
        }
    },

    applyStyleHtmlContent: function(config) {
        return Boolean(config);
    },

    updateStyleHtmlContent: function(styleHtmlContent) {
        var htmlCls = this.getStyleHtmlCls(),
            innerElement = this.innerElement,
            innerHtmlElement = this.innerHtmlElement;

        if (styleHtmlContent) {
            if (innerHtmlElement) {
                innerHtmlElement.addCls(htmlCls);
            } else {
                innerElement.addCls(htmlCls);
            }
        } else {
            if (innerHtmlElement) {
                innerHtmlElement.removeCls(htmlCls);
            } else {
                innerElement.addCls(htmlCls);
            }
        }
    },

    applyContentEl: function(contentEl) {
        if (contentEl) {
            return Ext.get(contentEl);
        }
    },

    updateContentEl: function(newContentEl, oldContentEl) {
        if (oldContentEl) {
            oldContentEl.hide();
            Ext.getBody().append(oldContentEl);
        }

        if (newContentEl) {
            this.setHtml(newContentEl.dom);
            newContentEl.show();
        }
    },

    /**
     * Returns the height and width of the Component
     * @return {Object} The current height and width of the Component
     */
    getSize: function() {
        return {
            width: this.getWidth(),
            height: this.getHeight()
        };
    },

    isCentered: function() {
        return Boolean(this.getCentered());
    },

    isFloating: function() {
        return this.floating;
    },

    isDocked: function() {
        return Boolean(this.getDocked());
    },

    isInnerItem: function() {
        var me = this;
        return !me.isCentered() && !me.isFloating() && !me.isDocked();
    },

    filterPositionValue: function(value) {
        if (value === '' || value === 'auto') {
            value = null;
        }

        return value;
    },

    applyTop: function(top) {
        return this.filterPositionValue(top);
    },

    applyRight: function(right) {
        return this.filterPositionValue(right);
    },

    applyBottom: function(bottom) {
        return this.filterPositionValue(bottom);
    },

    applyLeft: function(left) {
        return this.filterPositionValue(left);
    },

    doSetTop: function(top) {
        this.updateFloating();
        this.element.setTop(top);
    },

    doSetRight: function(right) {
        this.updateFloating();
        this.element.setRight(right);
    },

    doSetBottom: function(bottom) {
        this.updateFloating();
        this.element.setBottom(bottom);
    },

    doSetLeft: function(left) {
        this.updateFloating();
        this.element.setLeft(left);
    },

    doSetWidth: function(width) {
        this.element.setWidth(width);
    },

    doSetHeight: function(height) {
        this.element.setHeight(height);
    },

    doSetMinWidth: function(width) {
        this.element.setMinWidth(width);
    },

    doSetMinHeight: function(height) {
        this.element.setMinHeight(height);
    },

    doSetMaxWidth: function(width) {
        this.element.setMaxWidth(width);
    },

    doSetMaxHeight: function(height) {
        this.element.setMaxHeight(height);
    },

    applyCentered: function(centered) {
        centered = Boolean(centered);

        if (centered) {
            if (this.isFloating()) {
                this.resetFloating();
            }

            if (this.isDocked()) {
                this.setDocked(false);
            }
        }

        return centered;
    },

    doSetCentered: Ext.emptyFn,

    applyDocked: function(docked) {
        if (docked) {
            if (!this.dockPositions[docked]) {
                Ext.Logger.error("Invalid docking position of '" + docked + "', must be either 'top', 'right', 'bottom', " +
                    "'left' or `null` (for no docking)", this);
                return;
            }

            if (this.isFloating()) {
                this.resetFloating();
            }

            if (this.isCentered()) {
                this.setCentered(false);
            }
        }

        return docked;
    },

    doSetDocked: Ext.emptyFn,

    /**
     * Resets {@link #top}, {@link #right}, {@link #bottom} and {@link #left} configurations to `null`, which
     * will unfloat this component.
     */
    resetFloating: function() {
        this.setTop(null);
        this.setRight(null);
        this.setBottom(null);
        this.setLeft(null);
    },

    updateFloating: function() {
        var floating = true,
            floatingCls = this.getFloatingCls();

        if (this.getTop() === null && this.getBottom() === null && this.getRight() === null && this.getLeft() === null) {
            floating = false;
        }

        if (floating !== this.floating) {
            if (floating) {
                if (this.isCentered()) {
                    this.setCentered(false);
                }

                if (this.isDocked()) {
                    this.setDocked(false);
                }

                if (floatingCls) {
                    this.addCls(floatingCls);
                }
            } else if (floatingCls) {
                this.removeCls(floatingCls);
            }

            this.floating = floating;
            this.fireEvent('floatingchange', this, floating);
        }
    },

    /**
     * Updates the floatingCls if the component is currently floating
     * @private
     */
    updateFloatingCls: function(newFloatingCls, oldFloatingCls) {
        if (this.isFloating()) {
            this.replaceCls(oldFloatingCls, newFloatingCls);
        }
    },

    applyDisabled: function(disabled) {
        return Boolean(disabled);
    },

    doSetDisabled: function(disabled) {
        this.element[disabled ? 'addCls' : 'removeCls'](this.getDisabledCls());
    },

    updateDisabledCls: function(newDisabledCls, oldDisabledCls) {
        if (this.isDisabled()) {
            this.element.replaceCls(oldDisabledCls, newDisabledCls);
        }
    },

    /**
     * Disables this Component
     */
    disable: function() {
       this.setDisabled(true);
    },

    /**
     * Enables this Component
     */
    enable: function() {
        this.setDisabled(false);
    },

    /**
     * Returns true if this Component is currently disabled
     * @return {Boolean} True if currently disabled
     */
    isDisabled: function() {
        return this.getDisabled();
    },

    applyZIndex: function(zIndex) {
        if (zIndex !== null) {
            zIndex = Number(zIndex);

            if (isNaN(zIndex)) {
                zIndex = null;
            }
        }

        return zIndex;
    },

    updateZIndex: function(zIndex) {
        var domStyle = this.element.dom.style;

        if (zIndex !== null) {
            domStyle.setProperty('z-index', zIndex, 'important');
        }
        else {
            domStyle.removeProperty('z-index');
        }
    },

    getInnerHtmlElement: function() {
        var innerHtmlElement = this.innerHtmlElement,
            styleHtmlCls = this.getStyleHtmlCls();

        if (!innerHtmlElement || !innerHtmlElement.dom || !innerHtmlElement.dom.parentNode) {
            this.innerHtmlElement = innerHtmlElement = this.innerElement.createChild({ cls: 'x-innerhtml ' });

            if (this.getStyleHtmlContent()) {
                this.innerHtmlElement.addCls(styleHtmlCls);
                this.innerElement.removeCls(styleHtmlCls);
            }
        }

        return innerHtmlElement;
    },

    updateHtml: function(html) {
        var innerHtmlElement = this.getInnerHtmlElement();

        if (Ext.isElement(html)){
            innerHtmlElement.setHtml('');
            innerHtmlElement.append(html);
        }
        else {
            innerHtmlElement.setHtml(html);
        }
    },

    applyHidden: function(hidden) {
        return Boolean(hidden);
    },

    doSetHidden: function(hidden) {
        var element = this.renderElement;

        if (hidden) {
            element.hide();
        }
        else {
            element.show();
        }

        if (this.element) {
            this.element[hidden ? 'addCls' : 'removeCls'](this.getHiddenCls());
        }

        this.fireEvent(hidden ? 'hide' : 'show', this);
    },

    updateHiddenCls: function(newHiddenCls, oldHiddenCls) {
        if (this.isHidden()) {
            this.element.replaceCls(oldHiddenCls, newHiddenCls);
        }
    },

    /**
     * Returns true if this Component is currently hidden
     * @return {Boolean} True if currently hidden
     */
    isHidden: function() {
        return this.getHidden();
    },

    /**
     * Hides this Component
     */
    hide: function(animation) {
        if (!this.getHidden()) {
            if (animation === undefined || (animation && animation.isComponent)) {
                animation = this.getHideAnimation();
            }
            if (animation) {
                if (animation === true) {
                    animation = 'fadeOut';
                }
                this.onBefore({
                    hiddenchange: 'animateFn',
                    scope: this,
                    single: true,
                    args: [animation]
                });
            }
            this.setHidden(true);
        }
        return this;
    },

    /**
     * Shows this component
     */
    show: function(animation) {
        var hidden = this.getHidden();
        if (hidden || hidden === null) {
            if (animation === undefined || (animation && !animation.isComponent)) {
                animation = this.getShowAnimation();
            }

            if (animation) {
                if (animation === true) {
                    animation = 'fadeIn';
                }

                this.onBefore({
                    hiddenchange: 'animateFn',
                    scope: this,
                    single: true,
                    args: [animation]
                });
            }

            this.setHidden(false);
        }

        return this;
    },

    animateFn: function(animation, component, newState, oldState, options, controller) {
        if (animation) {
            var anim = new Ext.fx.Animation(animation);

            anim.setElement(component.element);

            if (newState) {
                anim.setOnEnd(function() {
                    controller.resume();
                });

                controller.pause();
            }

            Ext.Animator.run(anim);
        }
    },

    /**
     * @private
     */
    setVisibility: function(isVisible) {
        this.renderElement.setVisibility(isVisible);
    },

    /**
     * @private
     */
    isRendered: function() {
        return this.rendered;
    },

    /**
     * @private
     */
    isPainted: function() {
        return this.renderElement.isPainted();
    },

    /**
     * @private
     */
    applyTpl: function(config) {
        return (Ext.isObject(config) && config.isTemplate) ? config : new Ext.XTemplate(config);
    },

    applyData: function(data) {
        if (Ext.isObject(data)) {
            return Ext.apply({}, data);
        }

        return data;
    },

    /**
     * @private
     */
    updateData: function(newData) {
        var me = this;
        if (newData) {
            var tpl = me.getTpl(),
                tplWriteMode = me.getTplWriteMode();

            if (tpl) {
                tpl[tplWriteMode](me.getInnerHtmlElement(), newData);
            }

            /**
             * @event updatedata
             * Fires whenever the data of the component is updated
             * @param {Ext.Component} this The component instance
             * @param {Object} newData The new data
             */
            this.fireEvent('updatedata', me, newData);
        }
    },

    applyRecord: function(config) {
        if (config && Ext.isObject(config) && config.isModel) {
            return config;
        }
        return  null;
    },

    updateRecord: function(newRecord, oldRecord) {
        var me = this;

        if (oldRecord) {
            oldRecord.unjoin(me);
        }

        if (!newRecord) {
            me.updateData('');
        }
        else {
            newRecord.join(me);
            me.updateData(newRecord.getData(true));
        }
    },

    // @private Used to handle joining of a record to a tpl
    afterEdit: function() {
        this.updateRecord(this.getRecord());
    },

    // @private Used to handle joining of a record to a tpl
    afterErase: function() {
        this.setRecord(null);
    },

    applyItemId: function(itemId) {
        return itemId || this.getId();
    },

    /**
     * <p>Tests whether or not this Component is of a specific xtype. This can test whether this Component is descended
     * from the xtype (default) or whether it is directly of the xtype specified (shallow = true).</p>
     * <p><b>If using your own subclasses, be aware that a Component must register its own xtype
     * to participate in determination of inherited xtypes.</b></p>
     * <p>For a list of all available xtypes, see the {@link Ext.Component} header.</p>
     * <p>Example usage:</p>
     * <pre><code>
var t = new Ext.field.Text();
var isText = t.isXType('textfield');        // true
var isBoxSubclass = t.isXType('field');       // true, descended from Ext.field.Field
var isBoxInstance = t.isXType('field', true); // false, not a direct Ext.field.Field instance
</code></pre>
     * @param {String} xtype The xtype to check for this Component
     * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is
     * the default), or true to check whether this Component is directly of the specified xtype.
     * @return {Boolean} True if this component descends from the specified xtype, false otherwise.
     */
    isXType: function(xtype, shallow) {
        if (shallow) {
            return this.xtypes.indexOf(xtype) != -1;
        }

        return Boolean(this.xtypesMap[xtype]);
    },

    /**
     * <p>Returns this Component's xtype hierarchy as a slash-delimited string. For a list of all
     * available xtypes, see the {@link Ext.Component} header.</p>
     * <p><b>If using your own subclasses, be aware that a Component must register its own xtype
     * to participate in determination of inherited xtypes.</b></p>
     * <p>Example usage:</p>
     * <pre><code>
var t = new Ext.field.Text();
alert(t.getXTypes());  // alerts 'component/field/textfield'
</code></pre>
     * @return {String} The xtype hierarchy string
     */
    getXTypes: function() {
        return this.xtypesChain.join('/');
    },

    getDraggableBehavior: function() {
        var behavior = this.draggableBehavior;

        if (!behavior) {
            behavior = this.draggableBehavior = new Ext.behavior.Draggable(this);
        }

        return behavior;
    },

    applyDraggable: function(config) {
        this.getDraggableBehavior().setConfig(config);
    },

    getDraggable: function() {
        return this.getDraggableBehavior().getDraggable();
    },

    getTranslatableBehavior: function() {
        var behavior = this.translatableBehavior;

        if (!behavior) {
            behavior = this.translatableBehavior = new Ext.behavior.Translatable(this);
        }

        return behavior;
    },

    applyTranslatable: function(config) {
        this.getTranslatableBehavior().setConfig(config);
    },

    getTranslatable: function() {
        return this.getTranslatableBehavior().getTranslatable();
    },

    translateAxis: function(axis, value, animation) {
        var x, y;

        if (axis === 'x') {
            x = value;
        }
        else {
            y = value;
        }

        return this.translate(x, y, animation);
    },

    translate: function() {
        var translatable = this.getTranslatable();

        if (!translatable) {
            this.setTranslatable(true);
            translatable = this.getTranslatable();
        }

        translatable.translate.apply(translatable, arguments);
    },

    /**
     * @private
     * @param rendered
     */
    setRendered: function(rendered) {
        var wasRendered = this.rendered;

        if (rendered !== wasRendered) {
            this.rendered = rendered;

            return true;
        }

        return false;
    },

    /**
     * Sets the size of the Component
     * @param {Number} width The new width for the Component
     * @param {Number} height The new height for the Component
     */
    setSize: function(width, height) {
        if (width != undefined) {
            this.setWidth(width);
        }
        if (height != undefined) {
            this.setHeight(height);
        }
    },

    //@private
    doAddListener: function(name, fn, scope, options, order) {
        if (options && 'element' in options) {
            if (this.referenceList.indexOf(options.element) === -1) {
                Ext.Logger.error("Adding event listener with an invalid element reference of '" + options.element +
                    "' for this component. Available values are: '" + this.referenceList.join("', '") + "'", this);
            }

            // The default scope is this component
            this[options.element].doAddListener(name, fn, scope || this, options, order);
        }

        return this.callParent(arguments);
    },

    //@private
    doRemoveListener: function(name, fn, scope, options, order) {
        if (options && 'element' in options) {
            if (this.referenceList.indexOf(options.element) === -1) {
                Ext.Logger.error("Removing event listener with an invalid element reference of '" + options.element +
                    "' for this component. Available values are: '" + this.referenceList.join('", "') + "'", this);
            }

            // The default scope is this component
            this[options.element].doRemoveListener(name, fn, scope || this, options, order);
        }

        return this.callParent(arguments);
    },

    /**
     * Shows this component along side a specified component.
     *
     * If you specify no alignment, it will automatically position the specified component relative to this component.
     *
     * The following are all of the supported alignment positions:
     *
     *     Value  Description
     *     -----  -----------------------------
     *     tl     The top left corner
     *     t      The center of the top edge
     *     tr     The top right corner
     *     l      The center of the left edge
     *     c      In the center of the element
     *     r      The center of the right edge
     *     bl     The bottom left corner
     *     b      The center of the bottom edge
     *     br     The bottom right corner
     *
     * ## Example
     *
     *     // show `comp` by `otherComp` using the default positioning ("tc-bc", non-constrained)
     *     comp.showBy(otherComp);
     *
     *     // align the top left corner of `comp` with the top right corner of `otherComp` (constrained to viewport)
     *     comp.showBy(otherComp, "tr?");
     *
     *     // align the bottom right corner of `comp` with the center left edge of `otherComp`
     *     comp.showBy(otherComp, "br-l?");
     *
     * @param {Ext.Component} component The target component to show this component by
     * @param {String} alignment (Optional) The specific alignment.
     */
    showBy: function(component, alignment) {
        var args = Ext.Array.from(arguments);

        var viewport = Ext.Viewport,
            parent = this.getParent();

        this.setVisibility(false);

        if (parent !== viewport) {
            viewport.add(this);
        }

        this.show();

        this.on('erased', 'onShowByErased', this, { single: true });
        viewport.on('resize', 'refreshShowBy', this, { args: [component, alignment] });

        this.currentShowByArgs = args;

        this.alignTo(component, alignment);
        this.setVisibility(true);
    },

    refreshShowBy: function(component, alignment) {
        this.alignTo(component, alignment);
    },

    /**
     * @private
     * @param component
     */
    onShowByErased: function() {
        Ext.Viewport.un('resize', 'refreshShowBy', this);
    },

    /**
     * @private
     */
    alignTo: function(component, alignment) {
        var alignToElement = component.isComponent ? component.renderElement : component,
            element = this.renderElement,
            alignToBox = alignToElement.getPageBox(),
            constrainBox = this.getParent().element.getPageBox(),
            box = element.getPageBox(),
            alignToHeight = alignToBox.height,
            alignToWidth = alignToBox.width,
            height = box.height,
            width = box.width;

        // Keep off the sides...
        constrainBox.bottom -= 5;
        constrainBox.height -= 10;
        constrainBox.left += 5;
        constrainBox.right -= 5;
        constrainBox.top += 5;
        constrainBox.width -= 10;

        if (!alignment || alignment === 'auto') {
            if (constrainBox.bottom - alignToBox.bottom < height) {
                if (alignToBox.top - constrainBox.top < height) {
                    if (alignToBox.left - constrainBox.left < width) {
                        alignment = 'cl-cr?';
                    }
                    else {
                        alignment = 'cr-cl?';
                    }
                }
                else {
                    alignment = 'bc-tc?';
                }
            }
            else {
                alignment = 'tc-bc?';
            }
        }

        var matches = alignment.match(this.alignmentRegex);
        if (!matches) {
            Ext.Logger.error("Invalid alignment value of '" + alignment + "'");
        }

        var from = matches[1].split(''),
            to = matches[2].split(''),
            constrained = (matches[3] === '?'),
            fromVertical = from[0],
            fromHorizontal = from[1] || fromVertical,
            toVertical = to[0],
            toHorizontal = to[1] || toVertical,
            top = alignToBox.top,
            left = alignToBox.left,
            halfAlignHeight = alignToHeight / 2,
            halfAlignWidth = alignToWidth / 2,
            halfWidth = width / 2,
            halfHeight = height / 2,
            maxLeft, maxTop;

        switch (fromVertical) {
            case 't':
                switch (toVertical) {
                    case 'c':
                        top += halfAlignHeight;
                        break;
                    case 'b':
                        top += alignToHeight;
                }
                break;

            case 'b':
                switch (toVertical) {
                    case 'c':
                        top -= (height - halfAlignHeight);
                        break;
                    case 't':
                        top -= height;
                        break;
                    case 'b':
                        top -= height - alignToHeight;
                }
                break;

            case 'c':
                switch (toVertical) {
                    case 't':
                        top -= halfHeight;
                        break;
                    case 'c':
                        top -= (halfHeight - halfAlignHeight);
                        break;
                    case 'b':
                        top -= (halfHeight - alignToHeight);
                }
                break;
        }

        switch (fromHorizontal) {
            case 'l':
                switch (toHorizontal) {
                    case 'c':
                        left += halfAlignHeight;
                        break;
                    case 'r':
                        left += alignToWidth;
                }
                break;

            case 'r':
                switch (toHorizontal) {
                    case 'r':
                        left -= (width - alignToWidth);
                        break;
                    case 'c':
                        left -= (width - halfWidth);
                        break;
                    case 'l':
                        left -= width;
                }
                break;

            case 'c':
                switch (toHorizontal) {
                    case 'l':
                        left -= halfWidth;
                        break;
                    case 'c':
                        left -= (halfWidth - halfAlignWidth);
                        break;
                    case 'r':
                        left -= (halfWidth - alignToWidth);
                }
                break;
        }

        if (constrained) {
            maxLeft = (constrainBox.left + constrainBox.width) - width;
            maxTop = (constrainBox.top + constrainBox.height) - height;

            left = Math.max(constrainBox.left, Math.min(maxLeft, left));
            top = Math.max(constrainBox.top, Math.min(maxTop, top));
        }

        this.setLeft(left);
        this.setTop(top);
    },

    /**
     * <p>Walks up the <code>ownerCt</code> axis looking for an ancestor Container which matches
     * the passed simple selector.</p>
     * <p>Example:<pre><code>
var owningTabPanel = grid.up('tabpanel');
</code></pre>
     * @param {String} selector Optional. The simple selector to test.
     * @return {Ext.Container} The matching ancestor Container (or <code>undefined</code> if no match was found).
     */
    up: function(selector) {
        var result = this.parent;

        if (selector) {
            for (; result; result = result.parent) {
                if (Ext.ComponentQuery.is(result, selector)) {
                    return result;
                }
            }
        }
        return result;
    },

    //@inherit
    getBubbleTarget: function() {
        return this.getParent();
    },

    /**
     * Destroys this Component. If it is currently added to a Container it will first be removed from that Container.
     * All Ext.Element references are also deleted and the Component is de-registered from Ext.ComponentManager
     */
    destroy: function() {
        this.destroy = Ext.emptyFn;

        var parent = this.getParent(),
            referenceList = this.referenceList,
            i, ln, reference;

        // Remove this component itself from the container if it's currently contained
        if (parent) {
            parent.remove(this, false);
        }

        // Destroy all element references
        for (i = 0, ln = referenceList.length; i < ln; i++) {
            reference = referenceList[i];
            this[reference].destroy();
            delete this[reference];
        }

        this.setRecord(null);

        Ext.destroy(this.innerHtmlElement, this.getTranslatable());
        Ext.ComponentManager.unregister(this);

        this.callParent();
    }

    // Convert old properties in data into a config object

}, function() {
});

})(Ext.baseCSSPrefix);

/**
 * {@link Ext.Button} is a simple class to display a button in Sencha Touch. There are various
 * different styles of {@link Ext.Button} you can create by using the {@link #icon},
 * {@link #iconCls}, {@link #iconAlign}, {@link #iconMask}, {@link #ui}, and {@link #text}
 * configurations.
 *
 * ## Simple Button
 *
 * Here is an {@link Ext.Button} is it's simplist form:
 *
 *     @example miniphone
 *     var button = Ext.create('Ext.Button', {
 *         text: 'Button'
 *     });
 *     Ext.Viewport.add(button);
 *
 * ## Icons
 *
 * You can also create a {@link Ext.Button} with just an icon using the {@link #iconCls}
 * configuration:
 *
 *     @example miniphone
 *     var button = Ext.create('Ext.Button', {
 *         iconCls: 'refresh',
 *         iconMask: true
 *     });
 *     Ext.Viewport.add(button);
 *
 * Note that the {@link #iconMask} configuration is required when you want to use any of the
 * bundled Pictos icons.
 *
 * Here are the included icons available (if {@link Global_CSS#$include-default-icons $include-default-icons}
 * is set to true):
 *
 * - action
 * - add
 * - arrow_down
 * - arrow_left
 * - arrow_right
 * - arrow_up
 * - compose
 * - delete
 * - organize
 * - refresh
 * - reply
 * - search
 * - settings
 * - star
 * - trash
 * - maps
 * - locate
 * - home
 *
 * You can also use other pictos icons by using the {@link Global_CSS#pictos-iconmask pictos-iconmask} mixin in your SASS.
 *
 * ## Badges
 *
 * Buttons can also have a badge on them, by using the {@link #badgeText} configuration:
 *
 *     @example
 *     Ext.create('Ext.Container', {
 *         fullscreen: true,
 *         items: {
 *             xtype: 'button',
 *             text: 'My Button',
 *             badgeText: '2'
 *         }
 *     });
 *
 * ## UI
 *
 * Buttons also come with a range of different default UIs. Here are the included UIs
 * available (if {@link #$include-button-uis $include-button-uis} is set to true):
 *
 * - **normal** - a basic gray button
 * - **back** - a back button
 * - **forward** - a forward button
 * - **round** - a round button
 * - **action** - shaded using the {@link Global_CSS#$base-color $base-color} (dark blue by default)
 * - **decline** - red
 * - **confirm** - green
 *
 * And setting them is very simple:
 *
 *     var uiButton = Ext.create('Ext.Button', {
 *         text: 'My Button',
 *         ui: 'action'
 *     });
 *
 * And how they look:
 *
 *     @example miniphone preview
 *     Ext.create('Ext.Container', {
 *         fullscreen: true,
 *         padding: 4,
 *         defaults: {
 *             xtype: 'button',
 *             margin: 5
 *         },
 *         layout: {
 *             type: 'vbox',
 *             align: 'center'
 *         },
 *         items: [
 *             { ui: 'normal', text: 'normal' },
 *             { ui: 'round', text: 'round' },
 *             { ui: 'action', text: 'action' },
 *             { ui: 'decline', text: 'decline' },
 *             { ui: 'confirm', text: 'confirm' }
 *         ]
 *     });
 *
 * Note that the default {@link #ui} is **normal**.
 *
 * You can also use the {@link #sencha-button-ui sencha-button-ui} CSS Mixin to create your own UIs.
 *
 * ## Examples
 *
 * This example shows a bunch of icons on the screen in two toolbars. When you click on the center
 * button, it switches the iconCls on every button on the page.
 *
 *     @example preview
 *     Ext.createWidget('container', {
 *         fullscreen: true,
 *         layout: {
 *             type: 'vbox',
 *             pack:'center',
 *             align: 'center'
 *         },
 *         items: [
 *             {
 *                 xtype: 'button',
 *                 text: 'Change iconCls',
 *                 handler: function() {
 *                     // classes for all the icons to loop through.
 *                     var availableIconCls = [
 *                         'action', 'add', 'arrow_down', 'arrow_left',
 *                         'arrow_right', 'arrow_up', 'compose', 'delete',
 *                         'organize', 'refresh', 'reply', 'search',
 *                         'settings', 'star', 'trash', 'maps', 'locate',
 *                         'home'
 *                     ];
 *                     // get the text of this button,
 *                     // so we know which button we don't want to change
 *                     var text = this.getText();
 *
 *                     // use ComponentQuery to find all buttons on the page
 *                     // and loop through all of them
 *                     Ext.Array.forEach(Ext.ComponentQuery.query('button'), function(button) {
 *                         // if the button is the change iconCls button, continue
 *                         if (button.getText() == text) {
 *                             return;
 *                         }
 *
 *                         // get the index of the new available iconCls
 *                         var index = availableIconCls.indexOf(button.getIconCls()) + 1;
 *
 *                         // update the iconCls of the button with the next iconCls, if one exists.
 *                         // if not, use the first one
 *                         button.setIconCls(availableIconCls[(index == availableIconCls.length) ? 0 : index]);
 *                     });
 *                 }
 *             },
 *             {
 *                 xtype: 'toolbar',
 *                 docked: 'top',
 *                 defaults: {
 *                     iconMask: true
 *                 },
 *                 items: [
 *                     { xtype: 'spacer' },
 *                     { iconCls: 'action' },
 *                     { iconCls: 'add' },
 *                     { iconCls: 'arrow_down' },
 *                     { iconCls: 'arrow_left' },
 *                     { iconCls: 'arrow_up' },
 *                     { iconCls: 'compose' },
 *                     { iconCls: 'delete' },
 *                     { iconCls: 'organize' },
 *                     { iconCls: 'refresh' },
 *                     { xtype: 'spacer' }
 *                 ]
 *             },
 *             {
 *                 xtype: 'toolbar',
 *                 docked: 'bottom',
 *                 ui: 'light',
 *                 defaults: {
 *                     iconMask: true
 *                 },
 *                 items: [
 *                     { xtype: 'spacer' },
 *                     { iconCls: 'reply' },
 *                     { iconCls: 'search' },
 *                     { iconCls: 'settings' },
 *                     { iconCls: 'star' },
 *                     { iconCls: 'trash' },
 *                     { iconCls: 'maps' },
 *                     { iconCls: 'locate' },
 *                     { iconCls: 'home' },
 *                     { xtype: 'spacer' }
 *                 ]
 *             }
 *         ]
 *     });
 *
 */
Ext.define('Ext.Button', {
    extend: 'Ext.Component',

    xtype: 'button',

    /**
     * @event tap
     * @preventable doTap
     * Fires whenever a button is tapped
     * @param {Ext.Button} this The item added to the Container
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event release
     * @preventable doRelease
     * Fires whenever the button is released
     * @param {Ext.Button} this The item added to the Container
     * @param {Ext.EventObject} e The event object
     */

    cachedConfig: {
        /**
         * @cfg {String} pressedCls
         * The CSS class to add to the Button when it is pressed.
         * @accessor
         */
        pressedCls: Ext.baseCSSPrefix + 'button-pressing',

        /**
         * @cfg {String} badgeCls
         * The CSS class to add to the Button's badge, if it has one.
         * @accessor
         */
        badgeCls: Ext.baseCSSPrefix + 'badge',

        /**
         * @cfg {String} hasBadgeCls
         * The CSS class to add to the Button if it has a badge (note that this goes on the
         * Button element itself, not on the badge element).
         * @private
         * @accessor
         */
        hasBadgeCls: Ext.baseCSSPrefix + 'hasbadge',

        /**
         * @cfg {String} labelCls
         * The CSS class to add to the field's label element.
         * @accessor
         */
        labelCls: Ext.baseCSSPrefix + 'button-label',

        /**
         * @cfg {String} iconMaskCls
         * The CSS class to add to the icon element as allowed by {@link #iconMask}.
         * @accessor
         */
        iconMaskCls: Ext.baseCSSPrefix + 'icon-mask'
    },

    config: {
        /**
         * @cfg {String} badgeText
         * Optional badge text.
         * @accessor
         */
        badgeText: null,

        /**
         * @cfg {String} text
         * The Button text.
         * @accessor
         */
        text: null,

        /**
         * @cfg {String} iconCls
         * Optional CSS class to add to the icon element. This is useful if you want to use a CSS
         * background image to create your Button icon.
         * @accessor
         */
        iconCls: null,

        /**
         * @cfg {String} icon
         * Url to the icon image to use if you want an icon to appear on your button.
         * @accessor
         */
        icon: null,

        /**
         * @cfg {String} iconAlign
         * The position within the Button to render the icon Options are: `top`, `right`, `botom`, `left` and `center` (when you have
         * no {@link #text} set).
         * @accessor
         */
        iconAlign: 'left',

        /**
         * @cfg {Number/Boolean} pressedDelay
         * The amount of delay between the tapstart and the moment we add the pressedCls (in milliseconds).
         * Settings this to true defaults to 100ms.
         */
        pressedDelay: 0,

        /**
         * @cfg {Boolean} iconMask
         * Whether or not to mask the icon with the {@link #iconMaskCls} configuration.
         * This is needed if you want to use any of the bundled pictos icons in the Sencha Touch SASS.
         * @accessor
         */
        iconMask: null,

        /**
         * @cfg {Function} handler
         * The handler function to run when the Button is tapped on.
         * @accessor
         */
        handler: null,

        /**
         * @cfg {Object} scope
         * The scope to fire the configured {@link #handler} in.
         * @accessor
         */
        scope: null,

        /**
         * @cfg {String} autoEvent
         * Optional event name that will be fired instead of 'tap' when the Button is tapped on.
         * @accessor
         */
        autoEvent: null,

        /**
         * @cfg {String} ui
         * The ui style to render this button with. The valid default options are:
         * 'normal', 'back', 'round', 'action', 'confirm' and 'forward'.
         * @accessor
         */
        ui: 'normal',

        /**
         * @cfg {String} html The html to put in this button.
         *
         * If you want to just add text, please use the {@link #text} configuration
         */

        // @inherit
        baseCls: Ext.baseCSSPrefix + 'button'
    },

    template: [
        {
            tag: 'span',
            reference: 'badgeElement',
            hidden: true
        },
        {
            tag: 'span',
            className: Ext.baseCSSPrefix + 'button-icon',
            reference: 'iconElement',
            hidden: true
        },
        {
            tag: 'span',
            reference: 'textElement',
            hidden: true
        }
    ],

    initialize: function() {
        this.callParent();

        this.element.on({
            scope      : this,
            tap        : 'onTap',
            touchstart : 'onPress',
            touchmove  : 'onTouchMove',
            touchend   : 'onRelease'
        });
    },

    /**
     * @private
     */
    updateBadgeText: function(badgeText) {
        var element = this.element,
            badgeElement = this.badgeElement;

        if (badgeText) {
            badgeElement.show();
            badgeElement.setText(badgeText);
        }
        else {
            badgeElement.hide();
        }

        element[(badgeText) ? 'addCls' : 'removeCls'](this.getHasBadgeCls());
    },

    /**
     * @private
     */
    updateText: function(text) {
        var textElement = this.textElement;

        if (text) {
            textElement.show();
            textElement.setHtml(text);
        }
        else {
            textElement.hide();
        }
    },

    /**
     * @private
     */
    updateHtml: function(html) {
        var textElement = this.textElement;

        if (html) {
            textElement.show();
            textElement.setHtml(html);
        }
        else {
            textElement.hide();
        }
    },

    /**
     * @private
     */
    updateBadgeCls: function(badgeCls, oldBadgeCls) {
        this.badgeElement.replaceCls(oldBadgeCls, badgeCls);
    },

    /**
     * @private
     */
    updateHasBadgeCls: function(hasBadgeCls, oldHasBadgeCls) {
        var element = this.element;

        if (element.hasCls(oldHasBadgeCls)) {
            element.replaceCls(oldHasBadgeCls, hasBadgeCls);
        }
    },

    /**
     * @private
     */
    updateLabelCls: function(labelCls, oldLabelCls) {
        this.textElement.replaceCls(oldLabelCls, labelCls);
    },

    /**
     * @private
     */
    updatePressedCls: function(pressedCls, oldPressedCls) {
        var element = this.element;

        if (element.hasCls(oldPressedCls)) {
            element.replaceCls(oldPressedCls, pressedCls);
        }
    },

    /**
     * @private
     */
    updateIcon: function(icon) {
        var me = this,
            element = me.iconElement;

        if (icon) {
            me.showIconElement();
            element.setStyle('background-image', icon ? 'url(' + icon + ')' : '');
            me.refreshIconAlign();
            me.refreshIconMask();
        }
        else {
            me.hideIconElement();
            me.setIconAlign(false);
        }
    },

    /**
     * @private
     */
    updateIconCls: function(iconCls, oldIconCls) {
        var me = this,
            element = me.iconElement;

        if (iconCls) {
            me.showIconElement();
            element.replaceCls(oldIconCls, iconCls);
            me.refreshIconAlign();
            me.refreshIconMask();
        }
        else {
            me.hideIconElement();
            me.setIconAlign(false);
        }
    },

    /**
     * @private
     */
    updateIconAlign: function(alignment, oldAlignment) {
        var element = this.element,
            baseCls = Ext.baseCSSPrefix + 'iconalign-';

        if (!this.getText()) {
            alignment = "center";
        }

        element.removeCls(baseCls + "center");
        element.removeCls(baseCls + oldAlignment);
        if (this.getIcon() || this.getIconCls()) {
            element.addCls(baseCls + alignment);
        }
    },

    refreshIconAlign: function() {
        this.updateIconAlign(this.getIconAlign());
    },

    /**
     * @private
     */
    updateIconMaskCls: function(iconMaskCls, oldIconMaskCls) {
        var element = this.iconElement;

        if (this.getIconMask()) {
            element.replaceCls(oldIconMaskCls, iconMaskCls);
        }
    },

    /**
     * @private
     */
    updateIconMask: function(iconMask) {
        this.iconElement[iconMask ? "addCls" : "removeCls"](this.getIconMaskCls());
    },

    refreshIconMask: function() {
        this.updateIconMask(this.getIconMask());
    },

    applyAutoEvent: function(autoEvent) {
        var me = this;

        if (typeof autoEvent == 'string') {
            autoEvent = {
                name : autoEvent,
                scope: me.scope || me
            };
        }

        return autoEvent;
    },

    /**
     * @private
     */
    updateAutoEvent: function(autoEvent) {
        var name  = autoEvent.name,
            scope = autoEvent.scope;

        this.setHandler(function() {
            scope.fireEvent(name, scope, this);
        });

        this.setScope(scope);
    },

    /**
     * Used by icon and iconCls configurations to hide the icon element.
     * We do this because Tab needs to change the visibility of the icon, not make
     * it display:none
     * @private
     */
    hideIconElement: function() {
        this.iconElement.hide();
    },

    /**
     * Used by icon and iconCls configurations to show the icon element.
     * We do this because Tab needs to change the visibility of the icon, not make
     * it display:node
     * @private
     */
    showIconElement: function() {
        this.iconElement.show();
    },

    /**
     * We override this to check for '{ui}-back'. This is because if you have a UI of back, you need to actually add two class names.
     * The ui class, and the back class:
     *
     * `ui: 'action-back'` would turn into:
     *
     * `class="x-button-action x-button-back"`
     *
     * But `ui: 'action' would turn into:
     *
     * `class="x-button-action"`
     *
     * So we just split it up into an array and add both of them as a UI, when it has `back`.
     * @private
     */
    applyUi: function(config) {
        if (config && Ext.isString(config)) {
            var array  = config.split('-');
            if (array && (array[1] == "back" || array[1] == "forward")) {
                return array;
            }
        }

        return config;
    },

    getUi: function() {
        //Now that the UI can sometimes be an array, we need to check if it an array and return the proper value.
        var ui = this._ui;
        if (Ext.isArray(ui)) {
            return ui.join('-');
        }
        return ui;
    },

    applyPressedDelay: function(delay) {
        if (Ext.isNumber(delay)) {
            return delay;
        }
        return (delay) ? 100 : 0;
    },

    // @private
    onPress: function() {
        var element = this.element,
            pressedDelay = this.getPressedDelay(),
            pressedCls = this.getPressedCls();

        if (!this.getDisabled()) {
            this.isPressed = true;

            if (this.hasOwnProperty('releasedTimeout')) {
                clearTimeout(this.releasedTimeout);
                delete this.releasedTimeout;
            }

            if (pressedDelay > 0) {
                this.pressedTimeout = setTimeout(function() {
                    if (element) {
                        element.addCls(pressedCls);
                    }
                }, pressedDelay);
            }
            else {
                element.addCls(pressedCls);
            }
        }
    },

    onTouchMove: function(e) {
        return;
    },

    // @private
    onRelease: function(e) {
        this.fireAction('release', [this, e], 'doRelease');
    },

    // @private
    doRelease: function(me, e) {
        if (!me.isPressed) {
            return;
        }

        me.isPressed = false;

        if (me.hasOwnProperty('pressedTimeout')) {
            clearTimeout(me.pressedTimeout);
            delete me.pressedTimeout;
        }

        me.releasedTimeout = setTimeout(function() {
            if (me && me.element) {
                me.element.removeCls(me.getPressedCls());
            }
        }, 10);
    },

    // @private
    onTap: function(e) {
        if (this.getDisabled()) {
            return false;
        }

        this.fireAction('tap', [this, e], 'doTap');
    },

    /**
     * @private
     */
    doTap: function(me, e) {
        var handler = me.getHandler(),
            scope = me.getScope() || me;

        if (!handler) {
            return;
        }

        if (typeof handler == 'string') {
            handler = scope[handler];
        }

        //this is done so if you hide the button in the handler, the tap event will not fire on the new element
        //where the button was.
        e.preventDefault();

        handler.apply(scope, arguments);
    }
}, function() {
});

/**
 * @class Ext.Decorator
 * @extend Ext.Component
 *
 * In a few words, a Decorator is a Component that wraps around another Component. A typical example of a Decorator is a
 * {@link Ext.field.Field Field}. A form field is nothing more than a decorator around another component, and gives the
 * component a label, as well as extra styling to make it look good in a form.
 *
 * A Decorator can be thought of as a lightweight Container that has only one child item, and no layout overhead.
 * The look and feel of decorators can be styled purely in CSS.
 *
 * Another powerful feature that Decorator provides is config proxying. For example: all config items of a
 * {@link Ext.slider.Slider Slider} also exist in a {@link Ext.field.Slider Slider Field} for API convenience.
 * The {@link Ext.field.Slider Slider Field} simply proxies all corresponding getters and setters
 * to the actual {@link Ext.slider.Slider Slider} instance. Writing out all the setters and getters to do that is a tedious task
 * and a waste of code space. Instead, when you sub-class Ext.Decorator, all you need to do is to specify those config items
 * that you want to proxy to the Component using a special 'proxyConfig' class property. Here's how it may look like
 * in a Slider Field class:
 *
 *     Ext.define('My.field.Slider', {
 *         extend: 'Ext.Decorator',
 *
 *         config: {
 *             component: {
 *                 xtype: 'slider'
 *             }
 *         },
 *
 *         proxyConfig: {
 *             minValue: 0,
 *             maxValue: 100,
 *             increment: 1
 *         }
 *
 *         // ...
 *     });
 *
 * Once `My.field.Slider` class is created, it will have all setters and getters methods for all items listed in `proxyConfig`
 * automatically generated. These methods all proxy to the same method names that exist within the Component instance.
 */
Ext.define('Ext.Decorator', {
    extend: 'Ext.Component',

    isDecorator: true,

    config: {
        /**
         * @cfg {Object} component The config object to factory the Component that this Decorator wraps around
         */
        component: {}
    },

    statics: {
        generateProxySetter: function(name) {
            return function(value) {
                var component = this.getComponent();
                component[name].call(component, value);

                return this;
            }
        },
        generateProxyGetter: function(name) {
            return function() {
                var component = this.getComponent();
                return component[name].call(component);
            }
        }
    },

    onClassExtended: function(Class, members) {
        if (!members.hasOwnProperty('proxyConfig')) {
            return;
        }

        var ExtClass = Ext.Class,
            proxyConfig = members.proxyConfig,
            config = members.config;

        members.config = (config) ? Ext.applyIf(config, proxyConfig) : proxyConfig;

        var name, nameMap, setName, getName;

        for (name in proxyConfig) {
            if (proxyConfig.hasOwnProperty(name)) {
                nameMap = ExtClass.getConfigNameMap(name);
                setName = nameMap.set;
                getName = nameMap.get;

                members[setName] = this.generateProxySetter(setName);
                members[getName] = this.generateProxyGetter(getName);
            }
        }
    },

    // @private
    applyComponent: function(config) {
        return Ext.factory(config, Ext.Component);
    },

    // @private
    updateComponent: function(newComponent, oldComponent) {
        if (oldComponent) {
            if (this.isRendered() && oldComponent.setRendered(false)) {
                oldComponent.fireAction('renderedchange', [this, oldComponent, false],
                    'doUnsetComponent', this, { args: [oldComponent] });
            }
            else {
                this.doUnsetComponent(oldComponent);
            }
        }

        if (newComponent) {
            if (this.isRendered() && newComponent.setRendered(true)) {
                newComponent.fireAction('renderedchange', [this, newComponent, true],
                    'doSetComponent', this, { args: [newComponent] });
            }
            else {
                this.doSetComponent(newComponent);
            }
        }
    },

    // @private
    doUnsetComponent: function(component) {
        if (component.renderElement.dom) {
            this.innerElement.dom.removeChild(component.renderElement.dom);
        }
    },

    // @private
    doSetComponent: function(component) {
        if (component.renderElement.dom) {
            this.innerElement.dom.appendChild(component.renderElement.dom);
        }
    },

    // @private
    setRendered: function(rendered) {
        var component;

        if (this.callParent(arguments)) {
            component = this.getComponent();

            if (component) {
                component.setRendered(rendered);
            }

            return true;
        }

        return false;
    },

    // @private
    setDisabled: function(disabled) {
        this.callParent(arguments);
        this.getComponent().setDisabled(disabled);
    },

    destroy: function() {
        Ext.destroy(this.getComponent());
        this.callParent();
    }
});

/**
 * This is a simple way to add an image of any size to your application and have it participate in the layout system
 * like any other component. This component typically takes between 1 and 3 configurations - a {@link #src}, and
 * optionally a {@link #height} and a {@link #width}:
 *
 *     @example miniphone
 *     var img = Ext.create('Ext.Img', {
 *         src: 'http://www.sencha.com/assets/images/sencha-avatar-64x64.png',
 *         height: 64,
 *         width: 64
 *     });
 *     Ext.Viewport.add(img);
 *
 * It's also easy to add an image into a panel or other container using its xtype:
 *
 *     @example miniphone
 *     Ext.create('Ext.Panel', {
 *         fullscreen: true,
 *         layout: 'hbox',
 *         items: [
 *             {
 *                 xtype: 'image',
 *                 src: 'http://www.sencha.com/assets/images/sencha-avatar-64x64.png',
 *                 flex: 1
 *             },
 *             {
 *                 xtype: 'panel',
 *                 flex: 2,
 *                 html: 'Sencha Inc.<br/>1700 Seaport Boulevard Suite 120, Redwood City, CA'
 *             }
 *         ]
 *     });
 *
 * Here we created a panel which contains an image (a profile picture in this case) and a text area to allow the user
 * to enter profile information about themselves. In this case we used an {@link Ext.layout.HBox hbox layout} and
 * flexed the image to take up one third of the width and the text area to take two thirds of the width. See the
 * {@link Ext.layout.HBox hbox docs} for more information on flexing items.
 */
Ext.define('Ext.Img', {
    extend: 'Ext.Component',
    xtype: ['image', 'img'],

    /**
     * @event tap
     * Fires whenever the component is tapped
     * @param {Ext.Img} this The Image instance
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event load
     * Fires when the image is loaded
     * @param {Ext.Img} this The Image instance
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event error
     * Fires if an error occured when trying to load the image
     * @param {Ext.Img} this The Image instance
     * @param {Ext.EventObject} e The event object
     */

    config: {
        /**
         * @cfg {String} src The source of this image
         * @accessor
         */
        src: null,

        // @inherit
        baseCls: Ext.baseCSSPrefix + 'img',

        mode: 'background'
    },

    beforeInitialize: function() {
        var me = this;
        me.onLoad = Ext.Function.bind(me.onLoad, me);
        me.onError = Ext.Function.bind(me.onError, me);
    },

    initialize: function() {
        var me = this;
        me.callParent();

        me.relayEvents(me.renderElement, '*');

        me.element.on({
            tap: 'onTap',
            scope: me
        });
    },

    hide: function() {
        this.callParent();
        this.hiddenSrc = this.hiddenSrc || this.getSrc();
        this.setSrc(null);
    },

    show: function() {
        this.callParent();
        if (this.hiddenSrc) {
            this.setSrc(this.hiddenSrc);
            delete this.hiddenSrc;
        }
    },

    updateMode: function(mode) {
        if (mode === 'background') {
            if (this.imageElement) {
                this.imageElement.destroy();
                delete this.imageElement;
                this.updateSrc(this.getSrc());
            }
        }
        else {
            this.imageElement = this.element.createChild({ tag: 'img' });
        }
    },

    onTap: function(e) {
        this.fireEvent('tap', this, e);
    },

    onAfterRender: function() {
        this.updateSrc(this.getSrc());
    },

    /**
     * @private
     */
    updateSrc: function(newSrc) {
        var me = this,
            dom;

        if (me.getMode() === 'background') {
            dom = this.imageObject || new Image();
        }
        else {
            dom = me.imageElement.dom;
        }

        this.imageObject = dom;

        dom.setAttribute('src', Ext.isString(newSrc) ? newSrc : '');
        dom.addEventListener('load', me.onLoad, false);
        dom.addEventListener('error', me.onError, false);
    },

    detachListeners: function() {
        var dom = this.imageObject;

        if (dom) {
            dom.removeEventListener('load', this.onLoad, false);
            dom.removeEventListener('error', this.onError, false);
        }
    },

    onLoad : function(e) {
        this.detachListeners();

        if (this.getMode() === 'background') {
            this.element.dom.style.backgroundImage = 'url("' + this.imageObject.src + '")';
        }

        this.fireEvent('load', this, e);
    },

    onError : function(e) {
        this.detachListeners();
        this.fireEvent('error', this, e);
    },

    doSetWidth: function(width) {
        var sizingElement = (this.getMode() === 'background') ? this.element : this.imageElement;

        sizingElement.setWidth(width);

        this.callParent(arguments);
    },

    doSetHeight: function(height) {
        var sizingElement = (this.getMode() === 'background') ? this.element : this.imageElement;

        sizingElement.setHeight(height);

        this.callParent(arguments);
    },

    destroy: function() {
        this.detachListeners();

        Ext.destroy(this.imageObject);
        delete this.imageObject;

        this.callParent();
    }
});

/**
 * A simple label component which allows you to insert content using {@link #html} configuration.
 *
 *     @example miniphone
 *     Ext.Viewport.add({
 *         xtype: 'label',
 *         html: 'My label!'
 *     });
 */
Ext.define('Ext.Label', {
    extend: 'Ext.Component',
    xtype: 'label',

    config: {
        /**
         * @cfg {String} html
         * The label of this component.
         */
    }
});

/**
 * Wraps a Google Map in an Ext.Component using the [Google Maps API](http://code.google.com/apis/maps/documentation/v3/introduction.html).
 *
 * To use this component you must include an additional JavaScript file from Google:
 *
 *     <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=true"></script>
 *
 * ## Example
 *
 *     Ext.Viewport.add({
 *         xtype: 'map',
 *         useCurrentLocation: true
 *     });
 *
 */
Ext.define('Ext.Map', {
    extend: 'Ext.Component',
    xtype : 'map',
    requires: ['Ext.util.GeoLocation'],

    isMap: true,

    config: {
        /**
         * @event maprender
         * @param {Ext.Map} this
         * @param {google.maps.Map} map The rendered google.map.Map instance
         */

        /**
         * @event centerchange
         * @param {Ext.Map} this
         * @param {google.maps.Map} map The rendered google.map.Map instance
         * @param {google.maps.LatLng} center The current LatLng center of the map
         */

        /**
         * @event typechange
         * @param {Ext.Map} this
         * @param {google.maps.Map} map The rendered google.map.Map instance
         * @param {Number} mapType The current display type of the map
         */

        /**
         * @event zoomchange
         * @param {Ext.Map} this
         * @param {google.maps.Map} map The rendered google.map.Map instance
         * @param {Number} zoomLevel The current zoom level of the map
         */

        /**
         * @cfg {String} baseCls
         * The base CSS class to apply to the Maps's element
         * @accessor
         */
        baseCls: Ext.baseCSSPrefix + 'map',

        /**
         * @cfg {Boolean/Ext.util.GeoLocation} useCurrentLocation
         * Pass in true to center the map based on the geolocation coordinates or pass a
         * {@link Ext.util.GeoLocation GeoLocation} config to have more control over your GeoLocation options
         * @accessor
         */
        useCurrentLocation: false,

        /**
         * @cfg {google.maps.Map} map
         * The wrapped map.
         * @accessor
         */
        map: null,

        /**
         * @cfg {Ext.util.GeoLocation} geo
         * @accessor
         */
        geo: null,

        /**
         * @cfg {Object} mapOptions
         * MapOptions as specified by the Google Documentation:
         * http://code.google.com/apis/maps/documentation/v3/reference.html
         * @accessor
         */
        mapOptions: {}
    },

    constructor: function() {
        this.callParent(arguments);
        this.options= {};
        this.element.setVisibilityMode(Ext.Element.OFFSETS);

        if (!(window.google || {}).maps) {
            this.setHtml('Google Maps API is required');
        }
    },

    initialize: function() {
        this.callParent();
        this.on({
            painted: 'doResize',
            scope: this
        });
        this.element.on('touchstart', 'onTouchStart', this);
    },

    onTouchStart: function(e) {
        e.makeUnpreventable();
    },

    applyMapOptions: function(options) {
        return Ext.merge({}, this.options, options);
    },

    updateMapOptions: function(newOptions) {
        var gm = (window.google || {}).maps,
            map = this.getMap();

        if (gm && map) {
            map.setOptions(newOptions);
        }
    },

    getMapOptions: function() {
        return Ext.merge({}, this.options);
    },

    updateUseCurrentLocation: function(useCurrentLocation) {
        this.setGeo(useCurrentLocation);
        if (!useCurrentLocation) {
            this.renderMap();
        }
    },

    applyGeo: function(config) {
        return Ext.factory(config, Ext.util.GeoLocation, this.getGeo());
    },

    updateGeo: function(newGeo, oldGeo) {
        var events = {
            locationupdate : 'onGeoUpdate',
            locationerror : 'onGeoError',
            scope : this
        };

        if (oldGeo) {
            oldGeo.un(events);
        }

        if (newGeo) {
            newGeo.on(events);
            newGeo.updateLocation();
        }
    },

    doResize: function() {
        var gm = (window.google || {}).maps,
            map = this.getMap();

        if (gm && map) {
            gm.event.trigger(map, "resize");
        }
    },

    // @private
    renderMap: function() {
        var me = this,
            gm = (window.google || {}).maps,
            element = me.element,
            mapOptions = me.getMapOptions(),
            map = me.getMap(),
            event;

        if (gm) {
            if (Ext.os.is.iPad) {
                Ext.merge({
                    navigationControlOptions: {
                        style: gm.NavigationControlStyle.ZOOM_PAN
                    }
                }, mapOptions);
            }

            mapOptions = Ext.merge({
                zoom: 12,
                mapTypeId: gm.MapTypeId.ROADMAP
            }, mapOptions);

            // This is done separately from the above merge so we don't have to instantiate
            // a new LatLng if we don't need to
            if (!mapOptions.hasOwnProperty('center')) {
                mapOptions.center = new gm.LatLng(37.381592, -122.135672); // Palo Alto
            }

            if (element.dom.firstChild) {
                Ext.fly(element.dom.firstChild).destroy();
            }

            if (map) {
                gm.event.clearInstanceListeners(map);
            }

            me.setMap(new gm.Map(element.dom, mapOptions));
            map = me.getMap();

            //Track zoomLevel and mapType changes
            event = gm.event;
            event.addListener(map, 'zoom_changed', Ext.bind(me.onZoomChange, me));
            event.addListener(map, 'maptypeid_changed', Ext.bind(me.onTypeChange, me));
            event.addListener(map, 'center_changed', Ext.bind(me.onCenterChange, me));

            me.fireEvent('maprender', me, map);
        }
    },

    // @private
    onGeoUpdate: function(geo) {
        if (geo) {
            this.setMapCenter(new google.maps.LatLng(geo.getLatitude(), geo.getLongitude()));
        }
    },

    // @private
    onGeoError: Ext.emptyFn,

    /**
     * Moves the map center to the designated coordinates hash of the form:
     *
     *     { latitude: 37.381592, longitude: -122.135672 }
     *
     * or a google.maps.LatLng object representing to the target location.
     *
     * @param {Object/google.maps.LatLng} coordinates Object representing the desired Latitude and
     * longitude upon which to center the map.
     */
    setMapCenter: function(coordinates) {
        var me = this,
            map = me.getMap(),
            gm = (window.google || {}).maps;

        if (gm) {
            if (!me.isPainted()) {
                me.un('painted', 'setMapCenter', this);
                me.on('painted', 'setMapCenter', this, { single: true, args: [coordinates] });
                return;
            }
            coordinates = coordinates || new gm.LatLng(37.381592, -122.135672);

            if (coordinates && !(coordinates instanceof gm.LatLng) && 'longitude' in coordinates) {
                coordinates = new gm.LatLng(coordinates.latitude, coordinates.longitude);
            }

            if (!map) {
                me.renderMap();
                map = me.getMap();
            }

            if (map && coordinates instanceof gm.LatLng) {
                map.panTo(coordinates);
            }
            else {
                this.options = Ext.apply(this.getMapOptions(), {
                    center: coordinates
                });
            }
        }
    },

    // @private
    onZoomChange : function() {
        var mapOptions = this.getMapOptions(),
            map = this.getMap(),
            zoom;

        zoom = (map && map.getZoom) ? map.getZoom() : mapOptions.zoom || 10;

        this.options = Ext.apply(mapOptions, {
            zoom: zoom
        });

        this.fireEvent('zoomchange', this, map, zoom);
    },

    // @private
    onTypeChange : function() {
        var mapOptions = this.getMapOptions(),
            map = this.getMap(),
            mapTypeId;

        mapTypeId = (map && map.getMapTypeId) ? map.getMapTypeId() : mapOptions.mapTypeId;

        this.options = Ext.apply(mapOptions, {
            mapTypeId: mapTypeId
        });

        this.fireEvent('typechange', this, map, mapTypeId);
    },

    // @private
    onCenterChange: function() {
        var mapOptions = this.getMapOptions(),
            map = this.getMap(),
            center;

        center = (map && map.getCenter) ? map.getCenter() : mapOptions.center;

        this.options = Ext.apply(mapOptions, {
            center: center
        });

        this.fireEvent('centerchange', this, map, center);

    },

    // @private
    destroy: function() {
        Ext.destroy(this.getGeo());
        var map = this.getMap();

        if (map && (window.google || {}).maps) {
            google.maps.event.clearInstanceListeners(map);
        }

        this.callParent();
    }
}, function() {
});

/**
 * A simple class used to mask any {@link Ext.Container}.
 *
 * This should rarely be used directly, instead look at the {@link Ext.Container#masked} configuration.
 *
 * ## Example
 *
 *     @example miniphone
 *     // Create our container
 *     var container = Ext.create('Ext.Container', {
 *         html: 'My container!'
 *     });
 *
 *     // Add the container to the Viewport
 *     Ext.Viewport.add(container);
 *
 *     // Mask the container
 *     container.setMasked(true);
 */
Ext.define('Ext.Mask', {
    extend: 'Ext.Component',
    xtype: 'mask',

    config: {
        // @inherit
        baseCls: Ext.baseCSSPrefix + 'mask',

        /**
         * @cfg {Boolean} transparent True to make this mask transparent.
         */
        transparent: false,

        /**
         * @cfg
         * @hide
         */
        top: 0,

        /**
         * @cfg
         * @hide
         */
        left: 0,

        /**
         * @cfg
         * @hide
         */
        right: 0,

        /**
         * @cfg
         * @hide
         */
        bottom: 0
    },

    /**
     * @event tap
     * A tap event fired when a user taps on this mask
     * @param {Ext.Mask} this The mask instance
     * @param {Ext.EventObject} e The event object
     */
    initialize: function() {
        this.callParent();

        this.on({
            painted: 'onPainted',
            erased: 'onErased'
        })
    },

    onPainted: function() {
        this.element.on('*', 'onEvent', this);
    },

    onErased: function() {
        this.element.un('*', 'onEvent', this);
    },

    onEvent: function(e) {
        var controller = arguments[arguments.length - 1];

        if (controller.info.eventName === 'tap') {
            this.fireEvent('tap', this, e);
            return false;
        }

        if (e && e.stopEvent) {
            e.stopEvent();
        }

        return false;
    },

    updateTransparent: function(newTransparent) {
        this[newTransparent ? 'addCls' : 'removeCls'](this.getBaseCls() + '-transparent');
    }
});

/**
 * A simple class used to mask any {@link Ext.Container}.
 *
 * This should rarely be used directly, instead look at the {@link Ext.Container#masked} configuration.
 *
 * ## Example
 *
 *     @example miniphone
 *     Ext.Viewport.add({
 *         masked: {
 *            xtype: 'loadmask'
 *         }
 *     });
 *
 * You can customize the loading {@link #message} and whether or not you want to show the {@link #indicator}:
 *
 *     @example miniphone
 *     Ext.Viewport.add({
 *         masked: {
 *            xtype: 'loadmask',
 *            message: 'A message..',
 *            indicator: false
 *         }
 *     });
 *
 */
Ext.define('Ext.LoadMask', {
    extend: 'Ext.Mask',
    xtype: 'loadmask',

    config: {
        /**
         * @cfg {String} message
         * The text to display in a centered loading message box.
         * @accessor
         */
        message: 'Loading...',

        /**
         * @cfg {String} messageCls
         * The CSS class to apply to the loading message element.
         * @accessor
         */
        messageCls: Ext.baseCSSPrefix + 'mask-message',

        /**
         * @cfg {Boolean} indicator
         * True to show the loading indicator on this {@link Ext.LoadMask}.
         * @accessor
         */
        indicator: true,

        // @inherit
        listeners: {
            painted: 'onPainted',
            erased: 'onErased'
        }
    },

    getTemplate: function() {
        var prefix = Ext.baseCSSPrefix;

        return [
            {
                //it needs an inner so it can be centered within the mask, and have a background
                reference: 'innerElement',
                cls: prefix + 'mask-inner',
                children: [
                    //the elements required for the CSS loading {@link #indicator}
                    {
                        reference: 'indicatorElement',
                        cls: prefix + 'loading-spinner-outer',
                        children: [
                            {
                                cls: prefix + 'loading-spinner',
                                children: [
                                    { tag: 'span', cls: prefix + 'loading-top' },
                                    { tag: 'span', cls: prefix + 'loading-right' },
                                    { tag: 'span', cls: prefix + 'loading-bottom' },
                                    { tag: 'span', cls: prefix + 'loading-left' }
                                ]
                            }
                        ]
                    },
                    //the element used to display the {@link #message}
                    {
                        reference: 'messageElement'
                    }
                ]
            }
        ];
    },

    /**
     * Updates the message element with the new value of the {@link #message} configuration
     * @private
     */
    updateMessage: function(newMessage) {
        this.messageElement.setHtml(newMessage);
    },

    /**
     * Replaces the cls of the message element with the value of the {@link #messageCls} configuration.
     * @private
     */
    updateMessageCls: function(newMessageCls, oldMessageCls) {
        this.messageElement.replaceCls(oldMessageCls, newMessageCls);
    },

    /**
     * Shows/hides the loading indicator when the {@link #indicator} configuration is changed.
     * @private
     */
    updateIndicator: function(newIndicator) {
        this[newIndicator ? 'removeCls' : 'addCls'](Ext.baseCSSPrefix + 'indicator-hidden');
    },

    onPainted: function() {
        this.getParent().on({
            scope: this,
            resize: this.refreshPosition
        });

        this.refreshPosition();
    },

    onErased: function() {
        this.getParent().un({
            scope: this,
            resize: this.refreshPosition
        });
    },

    /**
     * @private
     * Updates the location of the indicator
     */
    refreshPosition: function() {
        var parent = this.getParent(),
            scrollable = parent.getScrollable(),
            scroller = (scrollable) ? scrollable.getScroller() : null,
            offset = (scroller) ? scroller.position : { x: 0, y: 0 },
            parentSize = parent.element.getSize(),
            innerSize = this.element.getSize();

        this.innerElement.setStyle({
            marginTop: Math.round(parentSize.height - innerSize.height + (offset.y * 2)) + 'px',
            marginLeft: Math.round(parentSize.width - innerSize.width + offset.x) + 'px'
        });
    }
}, function() {
});

/**
 * Provides a base class for audio/visual controls. Should not be used directly.
 *
 * Please see the {@link Ext.Audio} and {@link Ext.Video} classes for more information.
 * @private
 */
Ext.define('Ext.Media', {
    extend: 'Ext.Component',
    xtype: 'media',

    /**
     * @event play
     * Fires whenever the media is played
     * @param {Ext.Media} this
     */

    /**
     * @event paused
     * Fires whenever the media is pause
     * @param {Ext.Media} this
     * @param {Number} time The time at which the media was paused at in seconds
     */

    /**
     * @event ended
     * Fires whenever the media playback has ended
     * @param {Ext.Media} this
     * @param {Number} time The time at which the media ended at in seconds
     */

    /**
     * @event stop
     * Fires whenever the media is stopped.
     * The pause event will also fire after the stop event if the media is currently playing.
     * The timeupdate event will also fire after the stop event regardless of playing status.
     * @param {Ext.Media} this
     */

    /**
     * @event volumechange
     * Fires whenever the volume is changed
     * @param {Ext.Media} this
     * @param {Number} volume The volume level from 0 to 1
     */

    /**
     * @event mutedchange
     * Fires whenever the muted status is changed.
     * The volumechange event will also fire after the mutedchange event fires.
     * @param {Ext.Media} this
     * @param {Boolean} muted The muted status
     */

    /**
     * @event timeupdate
     * Fires when the media is playing every 15 to 250ms.
     * @param {Ext.Media} this
     * @param {Number} time The current time in seconds
     */

    config: {
        /**
         * @cfg {String} url
         * Location of the media to play.
         * @accessor
         */
        url: '',

        /**
         * @cfg {Boolean} enableControls
         * Set this to false to turn off the native media controls.
         * Defaults to false when you are on Android, as it doesnt support controls.
         * @accessor
         */
        enableControls: Ext.os.is.Android ? false : true,

        /**
         * @cfg {Boolean} autoResume
         * Will automatically start playing the media when the container is activated.
         * @accessor
         */
        autoResume: false,

        /**
         * @cfg {Boolean} autoPause
         * Will automatically pause the media when the container is deactivated.
         * @accessor
         */
        autoPause: true,

        /**
         * @cfg {Boolean} preload
         * Will begin preloading the media immediately.
         * @accessor
         */
        preload: true,

        /**
         * @cfg {Boolean} loop
         * Will loop the media forever.
         * @accessor
         */
        loop: false,

        /**
         * @cfg {Ext.Element} media
         * A reference to the underlying audio/video element.
         * @accessor
         */
        media: null,

        // @private
        playing: false,

        /**
         * @cfg {Number} volume
         * The volume of the media from 0.0 to 1.0. Default is 1.
         * @accessor
         */
        volume: 1,

        /**
         * @cfg {Boolean} muted
         * Whether or not the media is muted. This will also set the volume to zero. Default is false.
         * @accessor
         */
        muted: false
    },

    initialize: function() {
        var me = this;
        me.callParent();

        me.on({
            scope: me,

            activate  : me.onActivate,
            deactivate: me.onDeactivate
        });

        me.addMediaListener({
            play         : 'onPlay',
            pause        : 'onPause',
            ended        : 'onEnd',
            volumechange : 'onVolumeChange',
            timeupdate   : 'onTimeUpdate'
        });
    },

    addMediaListener: function(event, fn) {
        var me   = this,
            dom  = me.media.dom,
            bind = Ext.Function.bind;

        if (!Ext.isObject(event)) {
            var oldEvent = event;
            event = {};
            event[oldEvent] = fn;
        }

        Ext.Object.each(event, function(e, fn) {
            if (typeof fn !== 'function') {
                fn = me[fn];
            }

            if (typeof fn == 'function') {
                fn = bind(fn, me);

                dom.addEventListener(e, fn);
            }
        });
    },

    onPlay: function() {
        this.fireEvent('play', this);
    },

    onPause: function() {
        this.fireEvent('pause', this, this.getCurrentTime());
    },

    onEnd: function() {
        this.fireEvent('ended', this, this.getCurrentTime());
    },

    onVolumeChange: function() {
        this.fireEvent('volumechange', this, this.media.dom.volume);
    },

    onTimeUpdate: function() {
        this.fireEvent('timeupdate', this, this.getCurrentTime());
    },

    /**
     * Returns if the media is currently playing
     * @return {Boolean} playing True if the media is playing
     */
    isPlaying: function() {
        return this.getPlaying();
    },

    // @private
    onActivate: function() {
        var me = this;

        if (me.getAutoResume() && !me.isPlaying()) {
            me.play();
        }
    },

    // @private
    onDeactivate: function() {
        var me = this;

        if (me.getAutoResume() && me.isPlaying()) {
            me.pause();
        }
    },

    /**
     * Sets the URL of the media element. If the media element already exists, it is update the src attribute of the
     * element. If it is currently playing, it will start the new video.
     */
    updateUrl: function(newUrl) {
        var dom = this.media.dom;

        //when changing the src, we must call load:
        //http://developer.apple.com/library/safari/#documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/ControllingMediaWithJavaScript/ControllingMediaWithJavaScript.html
        dom.src = newUrl;
        dom.load();

        if (this.getPlaying()) {
            this.play();
        }
    },

    /**
     * Updates the controls of the video element.
     */
    updateEnableControls: function(enableControls) {
        this.media.dom.controls = enableControls ? 'controls' : false;
    },

    /**
     * Updates the loop setting of the media element.
     */
    updateLoop: function(loop) {
        this.media.dom.loop = loop ? 'loop' : false;
    },

    /**
     * Starts or resumes media playback
     */
    play: function() {
        this.media.dom.play();
        this.setPlaying(true);
    },

    /**
     * Pauses media playback
     */
    pause: function() {
        this.media.dom.pause();
        this.setPlaying(false);
    },

    /**
     * Toggles the media playback state
     */
    toggle: function() {
        if (this.isPlaying()) {
            this.pause();
        } else {
            this.play();
        }
    },

    /**
     * Stops media playback and returns to the beginning
     */
    stop: function() {
        var me = this;

        me.setCurrentTime(0);
        me.fireEvent('stop', me);
        me.pause();
    },

    //@private
    updateVolume: function(volume) {
        this.media.dom.volume = volume;
    },

    //@private
    updateMuted: function(muted) {
        this.fireEvent('mutedchange', this, muted);

        this.media.dom.muted = muted;
    },

    /**
     * Returns the current time of the media in seconds;
     */
    getCurrentTime: function() {
        return this.media.dom.currentTime;
    },

    /*
     * Set the current time of the media.
     * @param {Number} time The time in seconds
     */
    setCurrentTime: function(time) {
        this.media.dom.currentTime = time;

        return time;
    },

    /**
     * Returns the duration of the media in seconds;
     */
    getDuration: function() {
        return this.media.dom.duration;
    },

    destroy: function() {
        var me = this;
        Ext.Object.each(event, function(e, fn) {
            if (typeof fn !== 'function') {
                fn = me[fn];
            }

            if (typeof fn == 'function') {
                fn = bind(fn, me);

                dom.removeEventListener(e, fn);
            }
        });
    }
});

/**
 * {@link Ext.Audio} is a simple class which provides a container for the [HTML5 Audio element](http://www.w3schools.com/html5/tag_audio.asp).
 *
 * ## Recommended File Types/Compression:
 * * Uncompressed WAV and AIF audio
 * * MP3 audio
 * * AAC-LC
 * * HE-AAC audio
 *
 * ## Notes
 * On Android devices, the audio tags controls do not show. You must use the {@link #method-play}, {@link #pause} and
 * {@link #toggle} methods to control the audio (example below).
 *
 * ## Examples
 *
 * Here is an example of the {@link Ext.Audio} component in a fullscreen container:
 *
 *     @example preview
 *     Ext.create('Ext.Container', {
 *         fullscreen: true,
 *         layout: {
 *             type : 'vbox',
 *             pack : 'center',
 *             align: 'stretch'
 *         },
 *         items: [
 *             {
 *                 xtype : 'toolbar',
 *                 docked: 'top',
 *                 title : 'Ext.Audio'
 *             },
 *             {
 *                 xtype: 'audio',
 *                 url  : 'touch/examples/audio/crash.mp3'
 *             }
 *         ]
 *     });
 *
 * You can also set the {@link #hidden} configuration of the {@link Ext.Audio} component to true by default,
 * and then control the audio by using the {@link #method-play}, {@link #pause} and {@link #toggle} methods:
 *
 *     @example preview
 *     Ext.create('Ext.Container', {
 *         fullscreen: true,
 *         layout: {
 *             type: 'vbox',
 *             pack: 'center'
 *         },
 *         items: [
 *             {
 *                 xtype : 'toolbar',
 *                 docked: 'top',
 *                 title : 'Ext.Audio'
 *             },
 *             {
 *                 xtype: 'toolbar',
 *                 docked: 'bottom',
 *                 defaults: {
 *                     xtype: 'button',
 *                     handler: function() {
 *                         var container = this.getParent().getParent(),
 *                             // use ComponentQuery to get the audio component (using its xtype)
 *                             audio = container.down('audio');
 *
 *                         audio.toggle();
 *                         this.setText(audio.isPlaying() ? 'Pause' : 'Play');
 *                     }
 *                 },
 *                 items: [
 *                     { text: 'Play', flex: 1 }
 *                 ]
 *             },
 *             {
 *                 html: 'Hidden audio!',
 *                 styleHtmlContent: true
 *             },
 *             {
 *                 xtype : 'audio',
 *                 hidden: true,
 *                 url   : 'touch/examples/audio/crash.mp3'
 *             }
 *         ]
 *     });
 *
 */
Ext.define('Ext.Audio', {
    extend: 'Ext.Media',
    xtype : 'audio',

    config: {
        // @inherited
        cls: Ext.baseCSSPrefix + 'audio'

        /**
         * @cfg {String} url
         * The location of the audio to play.
         *
         * ### Recommended file types are:
         * * Uncompressed WAV and AIF audio
         * * MP3 audio
         * * AAC-LC
         * * HE-AAC audio
         * @accessor
         * @markdown
         */
    },

    // @private
    onActivate: function() {
        var me = this;

        me.callParent();

        if (Ext.os.is.Phone) {
            me.element.show();
        }
    },

    // @private
    onDeactivate: function() {
        var me = this;

        me.callParent();

        if (Ext.os.is.Phone) {
            me.element.hide();
        }
    },

    template: [{
        reference: 'media',
        preload: 'auto',
        tag: 'audio',
        cls: Ext.baseCSSPrefix + 'component'
    }]
});

/**
The {@link Ext.Spacer} component is generally used to put space between items in {@link Ext.Toolbar} components.

## Examples

By default the {@link #flex} configuration is set to 1:

    @example miniphone preview
    Ext.create('Ext.Container', {
        fullscreen: true,
        items: [
            {
                xtype : 'toolbar',
                docked: 'top',
                items: [
                    {
                        xtype: 'button',
                        text : 'Button One'
                    },
                    {
                        xtype: 'spacer'
                    },
                    {
                        xtype: 'button',
                        text : 'Button Two'
                    }
                ]
            }
        ]
    });

Alternatively you can just set the {@link #width} configuration which will get the {@link Ext.Spacer} a fixed width:

    @example preview
    Ext.create('Ext.Container', {
        fullscreen: true,
        layout: {
            type: 'vbox',
            pack: 'center',
            align: 'stretch'
        },
        items: [
            {
                xtype : 'toolbar',
                docked: 'top',
                items: [
                    {
                        xtype: 'button',
                        text : 'Button One'
                    },
                    {
                        xtype: 'spacer',
                        width: 50
                    },
                    {
                        xtype: 'button',
                        text : 'Button Two'
                    }
                ]
            },
            {
                xtype: 'container',
                items: [
                    {
                        xtype: 'button',
                        text : 'Change Ext.Spacer width',
                        handler: function() {
                            //get the spacer using ComponentQuery
                            var spacer = Ext.ComponentQuery.query('spacer')[0],
                                from = 10,
                                to = 250;

                            //set the width to a random number
                            spacer.setWidth(Math.floor(Math.random() * (to - from + 1) + from));
                        }
                    }
                ]
            }
        ]
    });

You can also insert multiple {@link Ext.Spacer}'s:

    @example preview
    Ext.create('Ext.Container', {
        fullscreen: true,
        items: [
            {
                xtype : 'toolbar',
                docked: 'top',
                items: [
                    {
                        xtype: 'button',
                        text : 'Button One'
                    },
                    {
                        xtype: 'spacer'
                    },
                    {
                        xtype: 'button',
                        text : 'Button Two'
                    },
                    {
                        xtype: 'spacer',
                        width: 20
                    },
                    {
                        xtype: 'button',
                        text : 'Button Three'
                    }
                ]
            }
        ]
    });
 */
Ext.define('Ext.Spacer', {
    extend: 'Ext.Component',
    alias : 'widget.spacer',

    config: {
        /**
         * @cfg {Number} flex
         * The flex value of this spacer. This defaults to 1, if no width has been set.
         * @accessor
         */
        
        /**
         * @cfg {Number} width
         * The width of this spacer. If this is set, the value of {@link #flex} will be ignored.
         * @accessor
         */
    },

    // @private
    constructor: function(config) {
        config = config || {};

        if (!config.width) {
            config.flex = 1;
        }

        this.callParent([config]);
    }
});

/**
 * {@link Ext.Title} is used for the {@link Ext.Toolbar#title} configuration in the {@link Ext.Toolbar} component.
 * @private
 */
Ext.define('Ext.Title', {
    extend: 'Ext.Component',
    xtype: 'title',

    config: {
        // @inherit
        baseCls: 'x-title',

        /**
         * @cfg {String} title The title text
         */
        title: ''
    },

    // @private
    updateTitle: function(newTitle) {
        this.setHtml(newTitle);
    }
});

/**
 * Provides a simple Container for HTML5 Video.
 *
 * ## Notes
 *
 * - There are quite a few issues with the `<video>` tag on Android devices. On Android 2+, the video will
 * appear and play on first attempt, but any attempt afterwards will not work.
 *
 * ## Useful Properties
 *
 * - {@link #url}
 * - {@link #autoPause}
 * - {@link #autoResume}
 *
 * ## Useful Methods
 *
 * - {@link #pause}
 * - {@link #method-play}
 * - {@link #toggle}
 *
 * ## Example
 *
 *     var panel = new Ext.Panel({
 *         fullscreen: true,
 *         items: [
 *             {
 *                 xtype    : 'video',
 *                 x        : 600,
 *                 y        : 300,
 *                 width    : 175,
 *                 height   : 98,
 *                 url      : "porsche911.mov",
 *                 posterUrl: 'porsche.png'
 *             }
 *         ]
 *     });
 *
 */
Ext.define('Ext.Video', {
    extend: 'Ext.Media',
    xtype: 'video',

    config: {
        /**
         * @cfg {String/Array} urls
         * Location of the video to play. This should be in H.264 format and in a .mov file format.
         * @accessor
         */

        /**
         * @cfg {String} posterUrl
         * Location of a poster image to be shown before showing the video.
         * @accessor
         */
        posterUrl: null,

        // @inherited
        cls: Ext.baseCSSPrefix + 'video'
    },

    template: [{
        /**
         * @property {Ext.dom.Element} ghost
         * @private
         */
        reference: 'ghost',
        classList: [Ext.baseCSSPrefix + 'video-ghost']
    }, {
        tag: 'video',
        reference: 'media',
        classList: [Ext.baseCSSPrefix + 'media']
    }],

    initialize: function() {
        var me = this;

        me.callParent();

        me.media.hide();

        me.onBefore({
            erased: 'onErased',
            scope: me
        });

        me.ghost.on({
            tap: 'onGhostTap',
            scope: me
        });

        me.media.on({
            pause: 'onPause',
            scope: me
        });

        if (Ext.os.is.Android4 || Ext.os.is.iPad) {
            this.isInlineVideo = true;
        }
    },

    applyUrl: function(url) {
        return [].concat(url);
    },

    updateUrl: function(newUrl) {
        var me = this,
            media = me.media,
            newLn = newUrl.length,
            existingSources = media.query('source'),
            oldLn = existingSources.length,
            i;


        for (i = 0; i < oldLn; i++) {
            Ext.fly(existingSources[i]).destroy();
        }

        for (i = 0; i < newLn; i++) {
            media.appendChild(Ext.Element.create({
                tag: 'source',
                src: newUrl[i]
            }));
        }

        if (me.getPlaying()) {
            me.play();
        }
    },

    onErased: function() {
        this.pause();
        this.media.setTop(-2000);
        this.ghost.show();
    },

    /**
     * @private
     * Called when the {@link #ghost} element is tapped.
     */
    onGhostTap: function() {
        var me = this,
            media = this.media,
            ghost = this.ghost;

        media.show();
        if (Ext.os.is.Android2) {
            setTimeout(function() {
                me.play();
                setTimeout(function() {
                    media.hide();
                }, 10);
            }, 10);
        } else {
            // Browsers which support native video tag display only, move the media down so
            // we can control the Viewport
            ghost.hide();
            me.play();
            setTimeout(function() {
                me.play();
            }, 10);
        }
    },

    // native video tag display only only, move the media down so we can control the Viewport
    onPause: function() {
        this.callParent(arguments);
        if (!this.isInlineVideo) {
            this.media.setTop(-2000);
            this.ghost.show();
        }
    },

    // native video tag display only only, move the media down so we can control the Viewport
    onPlay: function() {
        this.callParent(arguments);
        this.media.setTop(0);
    },

    /**
     * Updates the URL to the poster, even if it is rendered.
     * @param {Object} newUrl
     */
    updatePosterUrl: function(newUrl) {
        var ghost = this.ghost;
        if (ghost) {
            ghost.setStyle('background-image', 'url(' + newUrl + ')');
        }
    }
});

/**
 * A private utility class used by Ext.Carousel to create indicators.
 * @private
 */
Ext.define('Ext.carousel.Indicator', {
    extend: 'Ext.Component',
    xtype : 'carouselindicator',
    alternateClassName: 'Ext.Carousel.Indicator',

    config: {
        // @inherit
        baseCls: Ext.baseCSSPrefix + 'carousel-indicator',

        direction: 'horizontal'
    },

    /**
     * @event previous
     * @param {Ext.carousel.Indicator} this
     * Fires when this indicator is tapped on the left half
     */

    /**
     * @event next
     * @param {Ext.carousel.Indicator} this
     * Fires when this indicator is tapped on the right half
     */

    initialize: function() {
        this.callParent();

        this.indicators = [];

        this.element.on({
            tap: 'onTap',
            scope: this
        });
    },

    updateDirection: function(newDirection, oldDirection) {
        var baseCls = this.getBaseCls();

        this.element.replaceCls(oldDirection, newDirection, baseCls);

        if (newDirection === 'horizontal') {
            this.setBottom(0);
            this.setRight(null);
        }
        else {
            this.setRight(0);
            this.setBottom(null);
        }
    },

    addIndicator: function() {
        this.indicators.push(this.element.createChild({
            tag: 'span'
        }));
    },

    removeIndicator: function() {
        var indicators = this.indicators;

        if (indicators.length > 0) {
            indicators.pop().destroy();
        }
    },

    setActiveIndex: function(index) {
        var indicators = this.indicators,
            currentActiveIndex = this.activeIndex,
            currentActiveItem = indicators[currentActiveIndex],
            activeItem = indicators[index],
            baseCls = this.getBaseCls();

        if (currentActiveItem) {
            currentActiveItem.removeCls(baseCls, null, 'active');
        }

        if (activeItem) {
            activeItem.addCls(baseCls, null, 'active');
        }

        this.activeIndex = index;

        return this;
    },

    // @private
    onTap: function(e) {
        var touch = e.touch,
            box = this.element.getPageBox(),
            centerX = box.left + (box.width / 2),
            centerY = box.top + (box.height / 2),
            direction = this.getDirection();

        if ((direction === 'horizontal' && touch.pageX >= centerX) || (direction === 'vertical' && touch.pageY >= centerY)) {
            this.fireEvent('next', this);
        }
        else {
            this.fireEvent('previous', this);
        }
    },

    destroy: function() {
        var indicators = this.indicators,
            i, ln, indicator;

        for (i = 0,ln = indicators.length; i < ln; i++) {
            indicator = indicators[i];
            indicator.destroy();
        }

        indicators.length = 0;

        this.callParent();
    }
});

/**
 * @private
 */
Ext.define('Ext.carousel.Item', {
    extend: 'Ext.Decorator',

    config: {
        baseCls: 'x-carousel-item',
        component: null,
        translatable: true
    }
});

/**
 * IndexBar is a component used to display a list of data (primarily an alphabet) which can then be used to quickly
 * navigate through a list (see {@link Ext.List}) of data. When a user taps on an item in the {@link Ext.IndexBar},
 * it will fire the {@link #index} event.
 *
 * ## Example:
 *
 * Here is an example of the usage in a {@link Ext.List}:
 *
 *     @example phone portrait preview
 *     Ext.define('Contact', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: ['firstName', 'lastName']
 *         }
 *     });
 *
 *     var store = new Ext.data.JsonStore({
 *        model: 'Contact',
 *        sorters: 'lastName',
 *
 *        grouper: {
 *            groupFn: function(record) {
 *                return record.get('lastName')[0];
 *            }
 *        },
 *
 *        data: [
 *            {firstName: 'Tommy',   lastName: 'Maintz'},
 *            {firstName: 'Rob',     lastName: 'Dougan'},
 *            {firstName: 'Ed',      lastName: 'Spencer'},
 *            {firstName: 'Jamie',   lastName: 'Avins'},
 *            {firstName: 'Aaron',   lastName: 'Conran'},
 *            {firstName: 'Dave',    lastName: 'Kaneda'},
 *            {firstName: 'Jacky',   lastName: 'Nguyen'},
 *            {firstName: 'Abraham', lastName: 'Elias'},
 *            {firstName: 'Jay',     lastName: 'Robinson'},
 *            {firstName: 'Nigel',   lastName: 'White'},
 *            {firstName: 'Don',     lastName: 'Griffin'},
 *            {firstName: 'Nico',    lastName: 'Ferrero'},
 *            {firstName: 'Nicolas', lastName: 'Belmonte'},
 *            {firstName: 'Jason',   lastName: 'Johnston'}
 *        ]
 *     });
 *
 *     var list = new Ext.List({
 *        fullscreen: true,
 *        itemTpl: '<div class="contact">{firstName} <strong>{lastName}</strong></div>',
 *
 *        grouped     : true,
 *        indexBar    : true,
 *        store: store,
 *        hideOnMaskTap: false
 *     });
 *
*/
Ext.define('Ext.dataview.IndexBar', {
    extend: 'Ext.Component',
    alternateClassName: 'Ext.IndexBar',

    /**
     * @event index
     * Fires when an item in the index bar display has been tapped.
     * @param {Ext.dataview.IndexBar} this The IndexBar instance
     * @param {String} html The HTML inside the tapped node.
     * @param {Ext.dom.Element} target The node on the indexbar that has been tapped.
     */

    config: {
        baseCls: Ext.baseCSSPrefix + 'indexbar',

        /**
         * @cfg {String} direction
         * Layout direction, can be either 'vertical' or 'horizontal'
         * @accessor
         */
        direction: 'vertical',

        /**
         * @cfg {Array} letters
         * The letters to show on the index bar.
         * @accessor
         */
        letters: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],

        ui: 'alphabet',

        /**
         * @cfg {String} listPrefix
         * The prefix string to be appended at the beginning of the list.
         * E.g: useful to add a "#" prefix before numbers.
         * @accessor
         */
        listPrefix: null
    },

    // @private
    itemCls: Ext.baseCSSPrefix + '',

    updateDirection: function(newDirection, oldDirection) {
        var baseCls = this.getBaseCls();

        this.element.replaceCls(baseCls + '-' + oldDirection, baseCls + '-' + newDirection);
    },

    getElementConfig: function() {
        return {
            reference: 'wrapper',
            classList: ['x-centered', 'x-indexbar-wrapper'],
            children: [this.callParent()]
        };
    },

    updateLetters: function(letters) {
        this.innerElement.setHtml('');

        if (letters) {
            var ln = letters.length,
                i;

            for (i = 0; i < ln; i++) {
                this.innerElement.createChild({
                    html: letters[i]
                });
            }
        }
    },

    updateListPrefix: function(listPrefix) {
        if (listPrefix && listPrefix.length) {
            this.innerElement.createChild({
                html: listPrefix
            }, 0);
        }
    },

    // @private
    initialize: function() {
        this.callParent();

        this.innerElement.on({
            touchstart: this.onTouchStart,
            touchend: this.onTouchEnd,
            touchmove: this.onTouchMove,
            scope: this
        });
    },

    // @private
    onTouchStart: function(e, t) {
        e.stopPropagation();
        this.innerElement.addCls(this.getBaseCls() + '-pressed');
        this.pageBox = this.innerElement.getPageBox();
        this.onTouchMove(e);
    },

    // @private
    onTouchEnd: function(e, t) {
        this.innerElement.removeCls(this.getBaseCls() + '-pressed');
    },

    // @private
    onTouchMove: function(e) {
        var point = Ext.util.Point.fromEvent(e),
            target,
            pageBox = this.pageBox;

        if (!pageBox) {
            pageBox = this.pageBox = this.el.getPageBox();
        }

        if (this.getDirection() === 'vertical') {
            if (point.y > pageBox.bottom || point.y < pageBox.top) {
                return;
            }
            target = Ext.Element.fromPoint(pageBox.left + (pageBox.width / 2), point.y);
        }
        else {
            if (point.x > pageBox.right || point.x < pageBox.left) {
                return;
            }
            target = Ext.Element.fromPoint(point.x, pageBox.top + (pageBox.height / 2));
        }

        if (target) {
            this.fireEvent('index', this, target.dom.innerHTML, target);
        }
    },

    destroy: function() {
        var me = this,
            elements = Array.prototype.slice.call(me.innerElement.dom.childNodes),
            ln = elements.length,
            i = 0;

        for (; i < ln; i++) {
            Ext.removeNode(elements[i]);
        }
        this.callParent();
    }

}, function() {
});

/**
 * @private - To be made a sample
 */
Ext.define('Ext.dataview.ListItemHeader', {
    extend: 'Ext.Component',
    xtype : 'listitemheader',

    config: {
        // @inherit
        baseCls: Ext.baseCSSPrefix + 'list-header',
        docked: 'top'
    }
});
/**
 * @private
 */
Ext.define('Ext.dataview.element.Container', {
    extend: 'Ext.Component',

    /**
     * @event itemtouchstart
     * Fires whenever an item is touched
     * @param {Ext.dataview.element.Container} this
     * @param {Ext.dataview.component.DataItem} item The item touched
     * @param {Number} index The index of the item touched
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event itemtouchmove
     * Fires whenever an item is moved
     * @param {Ext.dataview.element.Container} this
     * @param {Ext.dataview.component.DataItem} item The item moved
     * @param {Number} index The index of the item moved
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event itemtouchend
     * Fires whenever an item is touched
     * @param {Ext.dataview.element.Container} this
     * @param {Ext.dataview.component.DataItem} item The item touched
     * @param {Number} index The index of the item touched
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event itemtap
     * Fires whenever an item is tapped
     * @param {Ext.dataview.element.Container} this
     * @param {Ext.dataview.component.DataItem} item The item tapped
     * @param {Number} index The index of the item tapped
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event itemtaphold
     * Fires whenever an item is tapped
     * @param {Ext.dataview.element.Container} this
     * @param {Ext.dataview.component.DataItem} item The item tapped
     * @param {Number} index The index of the item tapped
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event itemdoubletap
     * Fires whenever an item is doubletapped
     * @param {Ext.dataview.element.Container} this
     * @param {Ext.dataview.component.DataItem} item The item doubletapped
     * @param {Number} index The index of the item doubletapped
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event itemswipe
     * Fires whenever an item is swiped
     * @param {Ext.dataview.element.Container} this
     * @param {Ext.dataview.component.DataItem} item The item swiped
     * @param {Number} index The index of the item swiped
     * @param {Ext.EventObject} e The event object
     */

    doInitialize: function() {
        this.element.on({
            touchstart: 'onItemTouchStart',
            touchend: 'onItemTouchEnd',
            tap: 'onItemTap',
            taphold: 'onItemTapHold',
            touchmove: 'onItemTouchMove',
            doubletap: 'onItemDoubleTap',
            swipe: 'onItemSwipe',
            delegate: '> div',
            scope: this
        });
    },

    //@private
    initialize: function() {
        this.callParent();
        this.doInitialize();
    },

    updateBaseCls: function(newBaseCls, oldBaseCls) {
        var me = this;

        me.callParent([newBaseCls + '-container', oldBaseCls]);
    },

    onItemTouchStart: function(e) {
        var me = this,
            target = e.getTarget(),
            index = me.getViewItems().indexOf(target);

        Ext.get(target).on({
            touchmove: 'onItemTouchMove',
            scope   : me,
            single: true
        });

        me.fireEvent('itemtouchstart', me, Ext.get(target), index, e);
    },

    onItemTouchEnd: function(e) {
        var me = this,
            target = e.getTarget(),
            index = me.getViewItems().indexOf(target);

        Ext.get(target).un({
            touchmove: 'onItemTouchMove',
            scope   : me
        });

        me.fireEvent('itemtouchend', me, Ext.get(target), index, e);
    },

    onItemTouchMove: function(e) {
        var me = this,
            target = e.getTarget(),
            index = me.getViewItems().indexOf(target);

        me.fireEvent('itemtouchmove', me, Ext.get(target), index, e);
    },

    onItemTap: function(e) {
        var me = this,
            target = e.getTarget(),
            index = me.getViewItems().indexOf(target);

        me.fireEvent('itemtap', me, Ext.get(target), index, e);
    },

    onItemTapHold: function(e) {
        var me     = this,
            target = e.getTarget(),
            index  = me.getViewItems().indexOf(target);

        me.fireEvent('itemtaphold', me, Ext.get(target), index, e);
    },

    onItemDoubleTap: function(e) {
        var me = this,
            target = e.getTarget(),
            index = me.getViewItems().indexOf(target);

        me.fireEvent('itemdoubletap', me, Ext.get(target), index, e);
    },

    onItemSwipe: function(e) {
        var me = this,
            target = e.getTarget(),
            index = me.getViewItems().indexOf(target);

        me.fireEvent('itemswipe', me,  Ext.get(target), index, e);
    },

    updateListItem: function(record, item) {
        var me = this,
            dataview = me.dataview,
            data = dataview.prepareData(record.getData(true), dataview.getStore().indexOf(record), record);
        item.innerHTML = me.dataview.getItemTpl().apply(data);
    },

    addListItem: function(index, record) {
        var me = this,
            dataview = me.dataview,
            data = dataview.prepareData(record.getData(true), dataview.getStore().indexOf(record), record),
            element = me.element,
            childNodes = element.dom.childNodes,
            ln = childNodes.length,
            wrapElement;

        wrapElement = Ext.Element.create(this.getItemElementConfig(index, data));

        if (!ln || index == ln) {
            wrapElement.appendTo(element);
        } else {
            wrapElement.insertBefore(childNodes[index]);
        }
    },

    getItemElementConfig: function(index, data) {
        var dataview = this.dataview,
            itemCls = dataview.getItemCls(),
            cls = dataview.getBaseCls() + '-item';

        if (itemCls) {
            cls += ' ' + itemCls;
        }
        return {
            cls: cls,
            html: dataview.getItemTpl().apply(data)
        };
    },

    doRemoveItemCls: function(cls) {
        var elements = this.getViewItems(),
            ln = elements.length,
            i = 0;

        for (; i < ln; i++) {
            Ext.fly(elements[i]).removeCls(cls);
        }
    },

    doAddItemCls: function(cls) {
        var elements = this.getViewItems(),
            ln = elements.length,
            i = 0;

        for (; i < ln; i++) {
            Ext.fly(elements[i]).addCls(cls);
        }
    },

    // Remove
    moveItemsToCache: function(from, to) {
        var me = this,
            items = me.getViewItems(),
            i = to - from,
            item;

        for (; i >= 0; i--) {
            item = items[from + i];
            item.parentNode.removeChild(item);
        }
        if (me.getViewItems().length == 0) {
            this.dataview.showEmptyText();
        }
    },

    // Add
    moveItemsFromCache: function(records) {
        var me = this,
            dataview = me.dataview,
            store = dataview.getStore(),
            ln = records.length,
            i, record;

        if (ln) {
            dataview.hideEmptyText();
        }

        for (i = 0; i < ln; i++) {
            records[i]._tmpIndex = store.indexOf(records[i]);
        }

        Ext.Array.sort(records, function(record1, record2) {
            return record1._tmpIndex > record2._tmpIndex ? 1 : -1;
        });

        for (i = 0; i < ln; i++) {
            record = records[i];
            me.addListItem(record._tmpIndex, record);
            delete record._tmpIndex;
        }
    },

    // Transform ChildNodes into a proper Array so we can do indexOf...
    getViewItems: function() {
        return Array.prototype.slice.call(this.element.dom.childNodes);
    },

    destroy: function() {
        var elements = this.getViewItems(),
            ln = elements.length,
            i = 0;

        for (; i < ln; i++) {
            Ext.removeNode(elements[i]);
        }
        this.callParent();
    }
});

/**
 * @private
*/
Ext.define('Ext.dataview.element.List', {
    extend: 'Ext.dataview.element.Container',

    updateBaseCls: function(newBaseCls) {
        var me = this;

        me.itemClsShortCache = newBaseCls + '-item';

        me.headerClsShortCache = newBaseCls + '-header';
        me.headerClsCache = '.' + me.headerClsShortCache;

        me.headerItemClsShortCache = newBaseCls + '-header-item';

        me.footerClsShortCache = newBaseCls + '-footer-item';
        me.footerClsCache = '.' + me.footerClsShortCache;

        me.labelClsShortCache = newBaseCls + '-item-label';
        me.labelClsCache = '.' + me.labelClsShortCache;

        me.disclosureClsShortCache = newBaseCls + '-disclosure';
        me.disclosureClsCache = '.' + me.disclosureClsShortCache;

        me.iconClsShortCache = newBaseCls + '-icon';
        me.iconClsCache = '.' + me.iconClsShortCache;

        this.callParent(arguments);
    },

    hiddenDisplayCache: Ext.baseCSSPrefix + 'hidden-display',

    getItemElementConfig: function(index, data) {
        var me = this,
            dataview = me.dataview,
            itemCls = dataview.getItemCls(),
            cls = me.itemClsShortCache,
            config, iconSrc;

        if (itemCls) {
            cls += ' ' + itemCls;
        }

        config = {
            cls: cls,
            children: [{
                cls: me.labelClsShortCache,
                html: dataview.getItemTpl().apply(data)
            }]
        };

        if (dataview.getIcon()) {
            iconSrc = data.iconSrc;
            config.children.push({
                cls: me.iconClsShortCache,
                style: 'background-image: ' + iconSrc ? 'url("' + newSrc + '")' : ''
            });
        }

        if (dataview.getOnItemDisclosure()) {
            config.children.push({
                cls: me.disclosureClsShortCache + ((data.disclosure === false) ? me.hiddenDisplayCache : '')
            });
        }
        return config;
    },

    updateListItem: function(record, item) {
        var me = this,
            dataview = me.dataview,
            extItem = Ext.fly(item),
            innerItem = extItem.down(me.labelClsCache, true),
            data = record.data,
            disclosure = data && data.hasOwnProperty('disclosure'),
            iconSrc = data && data.hasOwnProperty('iconSrc'),
            disclosureEl, iconEl;

        innerItem.innerHTML = dataview.getItemTpl().apply(data);

        if (disclosure && dataview.getOnItemDisclosure()) {
            disclosureEl = extItem.down(me.disclosureClsCache);
            disclosureEl[disclosure ? 'removeCls' : 'addCls'](me.hiddenDisplayCache);
        }

        if (dataview.getIcon()) {
            iconEl = extItem.down(me.iconClsCache, true);
            iconEl.style.backgroundImage = iconSrc ? 'url("' + iconSrc + '")' : '';
        }
    },

    doRemoveHeaders: function() {
        var me = this,
            headerClsShortCache = me.headerItemClsShortCache,
            existingHeaders = me.element.query(me.headerClsCache),
            existingHeadersLn = existingHeaders.length,
            i = 0,
            item;

        for (; i < existingHeadersLn; i++) {
            item = existingHeaders[i];
            Ext.fly(item.parentNode).removeCls(headerClsShortCache);
            Ext.removeNode(item);
        }
    },

    doRemoveFooterCls: function() {
        var me = this,
            footerClsShortCache = me.footerClsShortCache,
            existingFooters = me.element.query(me.footerClsCache),
            existingFootersLn = existingFooters.length,
            i = 0;

        for (; i < existingFootersLn; i++) {
            Ext.fly(existingFooters[i]).removeCls(footerClsShortCache);
        }
    },

    doAddHeader: function(item, html) {
        item = Ext.fly(item);
        item.insertFirst(Ext.Element.create({
            cls: this.headerClsShortCache,
            html: html
        }));
        item.addCls(this.headerItemClsShortCache);
    },

    destroy: function() {
        this.doRemoveHeaders();
        this.callParent();
    }
});

/**
 * @private
 */
Ext.define('Ext.event.publisher.ComponentDelegation', {
    extend: 'Ext.event.publisher.Publisher',

    requires: [
        'Ext.Component',
        'Ext.ComponentQuery'
    ],

    targetType: 'component',

    optimizedSelectorRegex: /^#([\w\-]+)((?:[\s]*)>(?:[\s]*)|(?:\s*))([\w\-]+)$/i,

    handledEvents: ['*'],

    getSubscribers: function(eventName, createIfNotExist) {
        var subscribers = this.subscribers,
            eventSubscribers = subscribers[eventName];

        if (!eventSubscribers && createIfNotExist) {
            eventSubscribers = subscribers[eventName] = {
                type: {
                    $length: 0
                },
                selector: [],
                $length: 0
            }
        }

        return eventSubscribers;
    },

    subscribe: function(target, eventName) {
        // Ignore id-only selectors since they are already handled
        if (this.idSelectorRegex.test(target)) {
            return false;
        }

        var optimizedSelector = target.match(this.optimizedSelectorRegex),
            subscribers = this.getSubscribers(eventName, true),
            typeSubscribers = subscribers.type,
            selectorSubscribers = subscribers.selector,
            id, isDescendant, type, map, subMap;

        if (optimizedSelector !== null) {
            id = optimizedSelector[1];
            isDescendant = optimizedSelector[2].indexOf('>') === -1;
            type = optimizedSelector[3];

            map = typeSubscribers[type];

            if (!map) {
                typeSubscribers[type] = map = {
                    descendents: {
                        $length: 0
                    },
                    children: {
                        $length: 0
                    },
                    $length: 0
                }
            }

            subMap = isDescendant ? map.descendents : map.children;

            if (subMap.hasOwnProperty(id)) {
                subMap[id]++;
                return true;
            }

            subMap[id] = 1;
            subMap.$length++;
            map.$length++;
            typeSubscribers.$length++;
        }
        else {
            if (selectorSubscribers.hasOwnProperty(target)) {
                selectorSubscribers[target]++;
                return true;
            }

            selectorSubscribers[target] = 1;
            selectorSubscribers.push(target);
        }

        subscribers.$length++;

        return true;
    },

    unsubscribe: function(target, eventName, all) {
        var subscribers = this.getSubscribers(eventName);

        if (!subscribers) {
            return false;
        }

        var match = target.match(this.optimizedSelectorRegex),
            typeSubscribers = subscribers.type,
            selectorSubscribers = subscribers.selector,
            id, isDescendant, type, map, subMap;

        all = Boolean(all);

        if (match !== null) {
            id = match[1];
            isDescendant = match[2].indexOf('>') === -1;
            type = match[3];

            map = typeSubscribers[type];

            if (!map) {
                return true;
            }

            subMap = isDescendant ? map.descendents : map.children;

            if (!subMap.hasOwnProperty(id) || (!all && --subMap[id] > 0)) {
                return true;
            }

            delete subMap[id];
            subMap.$length--;
            map.$length--;
            typeSubscribers.$length--;
        }
        else {
            if (!selectorSubscribers.hasOwnProperty(target) || (!all && --selectorSubscribers[target] > 0)) {
                return true;
            }

            delete selectorSubscribers[target];
            Ext.Array.remove(selectorSubscribers, target);
        }

        if (--subscribers.$length === 0) {
            delete this.subscribers[eventName];
        }

        return true;
    },

    notify: function(target, eventName) {
        var subscribers = this.getSubscribers(eventName),
            id, component;

        if (!subscribers || subscribers.$length === 0) {
            return false;
        }

        id = target.substr(1);
        component = Ext.ComponentManager.get(id);

        if (component) {
            this.dispatcher.doAddListener(this.targetType, target, eventName, 'publish', this, {
                args: [eventName, component]
            }, 'before');
        }
    },

    matchesSelector: function(component, selector) {
        return Ext.ComponentQuery.is(component, selector);
    },

    dispatch: function(target, eventName, args, connectedController) {
        this.dispatcher.doDispatchEvent(this.targetType, target, eventName, args, null, connectedController);
    },

    publish: function(eventName, component) {
        var subscribers = this.getSubscribers(eventName);

        if (!subscribers) {
            return;
        }

        var eventController = arguments[arguments.length - 1],
            typeSubscribers = subscribers.type,
            selectorSubscribers = subscribers.selector,
            args = Array.prototype.slice.call(arguments, 2, -2),
            types = component.xtypesChain,
            descendentsSubscribers, childrenSubscribers,
            parentId, ancestorIds, ancestorId, parentComponent,
            selector,
            i, ln, type, j, subLn;

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

            subscribers = typeSubscribers[type];

            if (subscribers && subscribers.$length > 0) {
                descendentsSubscribers = subscribers.descendents;

                if (descendentsSubscribers.$length > 0) {
                    if (!ancestorIds) {
                        ancestorIds = component.getAncestorIds();
                    }

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

                        if (descendentsSubscribers.hasOwnProperty(ancestorId)) {
                            this.dispatch('#' + ancestorId + ' ' + type, eventName, args, eventController);
                        }

                    }
                }

                childrenSubscribers = subscribers.children;

                if (childrenSubscribers.$length > 0) {
                    if (!parentId) {
                        if (ancestorIds) {
                            parentId = ancestorIds[0];
                        }
                        else {
                            parentComponent = component.getParent();
                            if (parentComponent) {
                                parentId = parentComponent.getId();
                            }
                        }
                    }

                    if (parentId) {
                        if (childrenSubscribers.hasOwnProperty(parentId)) {
                            this.dispatch('#' + parentId + ' > ' + type, eventName, args, eventController);
                        }
                    }
                }
            }
        }

        ln = selectorSubscribers.length;

        if (ln > 0) {
            for (i = 0; i < ln; i++) {
                selector = selectorSubscribers[i];

                if (this.matchesSelector(component, selector)) {
                    this.dispatch(selector, eventName, args, eventController);
                }
            }
        }
    }
});

/**
 * @private
 */
Ext.define('Ext.field.Input', {
    extend: 'Ext.Component',
    xtype : 'input',

    /**
     * @event clearicontap
     * Fires whenever the clear icon is tapped
     * @param {Ext.field.Input} this
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event masktap
     * @preventable doMaskTap
     * Fires whenever a mask is tapped
     * @param {Ext.field.Input} this
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event focus
     * @preventable doFocus
     * Fires whenever the input get focus
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event blur
     * @preventable doBlur
     * Fires whenever the input loses focus
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event click
     * Fires whenever the input is clicked
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event keyup
     * Fires whenever keyup is detected
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event paste
     * Fires whenever paste is detected
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event mousedown
     * Fires whenever the input has a mousedown occur
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @property {String} tag The el tag
     * @private
     */
    tag: 'input',

    cachedConfig: {
        /**
         * @cfg {String} cls The className to be applied to this input
         * @accessor
         */
        cls: Ext.baseCSSPrefix + 'form-field',

        /**
         * @cfg {String} focusCls The CSS class to use when the field receives focus
         * @accessor
         */
        focusCls: Ext.baseCSSPrefix + 'field-focus',

        // @private
        maskCls: Ext.baseCSSPrefix + 'field-mask',

        /**
         * True to use a mask on this field, or `auto` to automatically select when you should use it.
         * @cfg {String/Boolean} useMask
         * @private
         * @accessor
         */
        useMask: 'auto',

        /**
         * @cfg {String} type The type attribute for input fields -- e.g. radio, text, password, file (defaults
         * to 'text'). The types 'file' and 'password' must be used to render those field types currently -- there are
         * no separate Ext components for those.
         * @accessor
         */
        type: 'text',

        /**
         * @cfg {Boolean} checked <tt>true</tt> if the checkbox should render initially checked (defaults to <tt>false</tt>)
         * @accessor
         */
        checked: false
    },

    config: {
        // @inherit
        baseCls: Ext.baseCSSPrefix + 'field-input',

        /**
         * @cfg {String} name The field's HTML name attribute
         * <b>Note</b>: this property must be set if this field is to be automatically included with
         * {@link Ext.form.Panel#method-submit form submit()}.
         * @accessor
         */
        name: null,

        /**
         * @cfg {Mixed} value A value to initialize this field with (defaults to undefined).
         * @accessor
         */
        value: null,

        /**
         * @property {Boolean} <tt>True</tt> if the field currently has focus.
         * @accessor
         */
        isFocused: false,

        /**
         * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered,
         * not those which are built via applyTo (defaults to undefined).
         * @accessor
         */
        tabIndex: null,

        /**
         * @cfg {String} placeHolder A string value displayed in the input (if supported) when the control is empty.
         * @accessor
         */
        placeHolder: null,

        /**
         * @cfg {Number} minValue The minimum value that this Number field can accept (defaults to undefined, e.g. no minimium)
         * @accessor
         */
        minValue: null,

        /**
         * @cfg {Number} maxValue The maximum value that this Number field can accept (defaults to undefined, e.g. no maximum)
         * @accessor
         */
        maxValue: null,

        /**
         * @cfg {Number} stepValue The amount by which the field is incremented or decremented each time the spinner is tapped.
         * Defaults to undefined, which means that the field goes up or down by 1 each time the spinner is tapped
         * @accessor
         */
        stepValue: null,

        /**
         * @cfg {Number} maxLength The maximum number of permitted input characters (defaults to 0).
         * @accessor
         */
        maxLength: null,

        /**
         * True to set the field's DOM element autocomplete attribute to "on", false to set to "off". Defaults to undefined, leaving the attribute unset
         * @cfg {Boolean} autoComplete
         * @accessor
         */
        autoComplete: null,

        /**
         * True to set the field's DOM element autocapitalize attribute to "on", false to set to "off". Defaults to undefined, leaving the attribute unset
         * @cfg {Boolean} autoCapitalize
         * @accessor
         */
        autoCapitalize: null,

        /**
         * True to set the field DOM element autocorrect attribute to "on", false to set to "off". Defaults to undefined, leaving the attribute unset.
         * @cfg {Boolean} autoCorrect
         * @accessor
         */
        autoCorrect: null,

        /**
         * True to set the field DOM element readonly attribute to "true". Defaults to undefined, leaving the attribute unset.
         * @cfg {Boolean} readOnly
         * @accessor
         */
        readOnly: null,

        /**
         * Sets the field DOM element maxRows attribute. Defaults to undefined, leaving the attribute unset.
         * @cfg {Number} maxRows
         * @accessor
         */
        maxRows: null,

        /**
         * @cfg {Boolean} disabled True to disable the field (defaults to false).
         * <p>Be aware that conformant with the <a href="http://www.w3.org/TR/html401/interact/forms.html#h-17.12.1">HTML specification</a>,
         * disabled Fields will not be {@link Ext.form.Panel#method-submit submitted}.</p>
         * @accessor
         */

        /**
         * <p>The value that the Field had at the time it was last focused. This is the value that is passed
         * to the {@link Ext.field.Text#change} event which is fired if the value has been changed when the Field is blurred.</p>
         * <p><b>This will be undefined until the Field has been visited.</b> Compare {@link #originalValue}.</p>
         * @cfg {Mixed} startValue
         * @accessor
         */
        startValue: false
    },

    /**
     * @cfg {String/Number} originalValue The original value when the input is rendered
     * @private
     */

    // @private
    getTemplate: function() {
        var items = [
            {
                reference: 'input',
                tag: this.tag
            },
            {
                reference: 'clearIcon',
                cls: 'x-clear-icon'
            }
        ];

        items.push({
            reference: 'mask',
            classList: [this.config.maskCls]
        });

        return items;
    },

    // @inherit
    initElement: function() {
        var me = this;

        me.callParent();

        me.input.on({
            scope: me,

            keyup    : 'onKeyUp',
            focus    : 'onFocus',
            blur     : 'onBlur',
            paste    : 'onPaste'
        });

        me.mask.on({
            tap: 'onMaskTap',
            scope: me
        });

        if (me.clearIcon) {
            me.clearIcon.on({
                tap: 'onClearIconTap',
                scope: me
            });
        }
    },

    applyUseMask: function(useMask) {
        if (useMask === 'auto') {
            useMask = Ext.os.is.iOS && Ext.os.version.lt('5');
        }

        return Boolean(useMask);
    },

    /**
     * Updates the useMask configuration
     */
    updateUseMask: function(newUseMask) {
        this.mask[newUseMask ? 'show' : 'hide']();
    },

    /**
     * Helper method to update a specified attribute on the fieldEl, or remove the attribute all together
     * @private
     */
    updateFieldAttribute: function(attribute, newValue) {
        var input = this.input;

        if (newValue) {
            input.dom.setAttribute(attribute, newValue);
        } else {
            input.dom.removeAttribute(attribute);
        }
    },

    /**
     * Updates the {@link #cls} configuration
     */
    updateCls: function(newCls, oldCls) {
        this.input.addCls(Ext.baseCSSPrefix + 'input-el');
        this.input.replaceCls(oldCls, newCls);
    },

    /**
     * Updates the type attribute with the {@link #type} configuration
     * @private
     */
    updateType: function(newType, oldType) {
        var prefix = Ext.baseCSSPrefix + 'input-';

        this.input.replaceCls(prefix + oldType, prefix + newType);
        this.updateFieldAttribute('type', newType);
    },

    /**
     * Updates the name attribute with the {@link #name} configuration
     * @private
     */
    updateName: function(newName) {
        this.updateFieldAttribute('name', newName);
    },

    /**
     * Returns the field data value
     * @return {Mixed} value The field value
     */
    getValue: function() {
        var input = this.input;

        if (input) {
            this._value = input.dom.value;
        }

        return this._value;
    },

    // @private
    applyValue: function(value) {
        return (Ext.isEmpty(value)) ? '' : value;
    },

    /**
     * Updates the {@link #value} configuration
     * @private
     */
    updateValue: function(newValue) {
        var input = this.input;

        if (input) {
            input.dom.value = newValue;
        }
    },

    setValue: function(newValue) {
        this.updateValue(this.applyValue(newValue));
        return this;
    },

    // @private
    applyTabIndex: function(tabIndex) {
        if (tabIndex !== null && typeof tabIndex != 'number') {
            throw new Error("Ext.field.Field: [applyTabIndex] trying to pass a value which is not a number");
        }
        return tabIndex;
    },

    /**
     * Updates the tabIndex attribute with the {@link #tabIndex} configuration
     * @private
     */
    updateTabIndex: function(newTabIndex) {
        this.updateFieldAttribute('tabIndex', newTabIndex);
    },

    // @private
    testAutoFn: function(value) {
        return [true, 'on'].indexOf(value) !== -1;
    },

    applyMaxLength: function(maxLength) {
        if (maxLength !== null && typeof maxLength != 'number') {
            throw new Error("Ext.field.Text: [applyMaxLength] trying to pass a value which is not a number");
        }
        return maxLength;
    },

    /**
     * Updates the maxlength attribute with the {@link #maxLength} configuration
     * @private
     */
    updateMaxLength: function(newMaxLength) {
        this.updateFieldAttribute('maxlength', newMaxLength);
    },

    /**
     * Updates the placeholder attribute with the {@link #placeHolder} configuration
     * @private
     */
    updatePlaceHolder: function(newPlaceHolder) {
        this.updateFieldAttribute('placeholder', newPlaceHolder);
    },

    // @private
    applyAutoComplete: function(autoComplete) {
        return this.testAutoFn(autoComplete);
    },

    /**
     * Updates the autocomplete attribute with the {@link #autoComplete} configuration
     * @private
     */
    updateAutoComplete: function(newAutoComplete) {
        var value = newAutoComplete ? 'on' : 'off';
        this.updateFieldAttribute('autocomplete', value);
    },

    // @private
    applyAutoCapitalize: function(autoCapitalize) {
        return this.testAutoFn(autoCapitalize);
    },

    /**
     * Updates the autocapitalize attribute with the {@link #autoCapitalize} configuration
     * @private
     */
    updateAutoCapitalize: function(newAutoCapitalize) {
        var value = newAutoCapitalize ? 'on' : 'off';
        this.updateFieldAttribute('autocapitalize', value);
    },

    // @private
    applyAutoCorrect: function(autoCorrect) {
        return this.testAutoFn(autoCorrect);
    },

    /**
     * Updates the autocorrect attribute with the {@link #autoCorrect} configuration
     * @private
     */
    updateAutoCorrect: function(newAutoCorrect) {
        var value = newAutoCorrect ? 'on' : 'off';
        this.updateFieldAttribute('autocorrect', value);
    },

    /**
     * Updates the min attribute with the {@link #minValue} configuration
     * @private
     */
    updateMinValue: function(newMinValue) {
        this.updateFieldAttribute('min', newMinValue);
    },

    /**
     * Updates the max attribute with the {@link #maxValue} configuration
     * @private
     */
    updateMaxValue: function(newMaxValue) {
        this.updateFieldAttribute('max', newMaxValue);
    },

    /**
     * Updates the step attribute with the {@link #stepValue} configuration
     * @private
     */
    updateStepValue: function(newStepValue) {
        this.updateFieldAttribute('step', newStepValue);
    },

    // @private
    checkedRe: /^(true|1|on)/i,

    /**
     * Returns the checked value of this field
     * @return {Mixed} value The field value
     */
    getChecked: function() {
        var el = this.input,
            checked;

        if (el) {
            checked = el.dom.checked;
            this._checked = checked;
        }

        return checked;
    },

    // @private
    applyChecked: function(checked) {
        return !!this.checkedRe.test(String(checked));
    },

    setChecked: function(newChecked) {
        this.updateChecked(this.applyChecked(newChecked));
        this._checked = newChecked;
    },

    /**
     * Updates the autocorrect attribute with the {@link #autoCorrect} configuration
     * @private
     */
    updateChecked: function(newChecked) {
        this.input.dom.checked = newChecked;
    },

    /**
     * Updates the readonly attribute with the {@link #readOnly} configuration
     * @private
     */
    updateReadOnly: function(readOnly) {
        this.updateFieldAttribute('readonly', readOnly);
    },

    // @private
    applyMaxRows: function(maxRows) {
        if (maxRows !== null && typeof maxRows !== 'number') {
            throw new Error("Ext.field.Input: [applyMaxRows] trying to pass a value which is not a number");
        }

        return maxRows;
    },

    updateMaxRows: function(newRows) {
        this.updateFieldAttribute('rows', newRows);
    },

    // @inherit
    doSetDisabled: function(disabled) {
        this.callParent(arguments);

        this.input.dom.disabled = disabled;

        if (!disabled) {
            this.blur();
        }
    },

    /**
     * <p>Returns true if the value of this Field has been changed from its original value.
     * Will return false if the field is disabled or has not been rendered yet.</p>
     */
    isDirty: function() {
        if (this.getDisabled()) {
            return false;
        }

        return String(this.getValue()) !== String(this.originalValue);
    },

    /**
     * Resets the current field value to the originally loaded value and clears any validation messages.
     */
    reset: function() {
        this.setValue(this.originalValue);
    },

    // @private
    onMaskTap: function(e) {
        this.fireAction('masktap', [this, e], 'doMaskTap');
    },

    // @private
    doMaskTap: function(me, e) {
        if (me.getDisabled()) {
            return false;
        }

        me.maskCorrectionTimer = Ext.defer(me.showMask, 1000, me);
        me.hideMask();
    },

    // @private
    showMask: function(e) {
        if (this.mask) {
            this.mask.setStyle('display', 'block');
        }
    },

    // @private
    hideMask: function(e) {
        if (this.mask) {
            this.mask.setStyle('display', 'none');
        }
    },

    /**
     * Attempts to set the field as the active input focus.
     * @return {Ext.field.Input} this
     */
    focus: function() {
        var me = this,
            el = me.input;

        if (el && el.dom.focus) {
            el.dom.focus();
        }
        return me;
    },

    /**
     * Attempts to forcefully blur input focus for the field.
     * @return {Ext.field.Input} this
     */
    blur: function() {
        var me = this,
            el = this.input;

        if (el && el.dom.blur) {
            el.dom.blur();
        }
        return me;
    },

    /**
     * Attempts to forcefully select all the contents of the input field.
     * @return {Ext.field.Input} this
     */
    select: function() {
        var me = this,
            el = me.input;

        if (el && el.dom.setSelectionRange) {
            el.dom.setSelectionRange(0, 9999);
        }
        return me;
    },

    onFocus: function(e) {
        this.fireAction('focus', [e], 'doFocus');
    },

    // @private
    doFocus: function(e) {
        var me = this;

        if (me.mask) {
            if (me.maskCorrectionTimer) {
                clearTimeout(me.maskCorrectionTimer);
            }
            me.hideMask();
        }

        if (!me.getIsFocused()) {
            me.setIsFocused(true);
            me.setStartValue(me.getValue());
        }
    },

    onBlur: function(e) {
        this.fireAction('blur', [e], 'doBlur');
    },

    // @private
    doBlur: function(e) {
        var me         = this,
            value      = me.getValue(),
            startValue = me.getStartValue();

        me.setIsFocused(false);

        if (String(value) != String(startValue)) {
            me.onChange(me, value, startValue);
        }

        me.showMask();
    },

    // @private
    onClearIconTap: function(e) {
        var oldValue = this.getValue(),
            newValue;

        this.fireEvent('clearicontap', this, e);

        newValue = this.getValue();

        if (String(newValue) != String(oldValue)) {
            this.onChange(this, newValue, oldValue);
        }

        //focus the field after cleartap happens, but only on android.
        //this is to stop the keyboard from hiding. TOUCH-2064
        if (Ext.os.is.Android) {
            this.focus();
        }
    },

    onClick: function(e) {
        this.fireEvent('click', e);
    },

    onChange: function(me, value, startValue) {
        this.fireEvent('change', me, value, startValue);
    },

    onKeyUp: function(e) {
        this.fireEvent('keyup', e);
    },

    onPaste: function(e) {
        this.fireEvent('paste', e);
    },

    onMouseDown: function(e) {
        this.fireEvent('mousedown', e);
    }
});

/**
 * Field is the base class for all form fields used in Sencha Touch. It provides a lot of shared functionality to all
 * field subclasses (for example labels, simple validation, {@link #clearIcon clearing} and tab index management), but
 * is rarely used directly. Instead, it is much more common to use one of the field subclasses:
 *
<pre>
xtype            Class
---------------------------------------
textfield        {@link Ext.field.Text}
numberfield      {@link Ext.field.Number}
textareafield    {@link Ext.field.TextArea}
hiddenfield      {@link Ext.field.Hidden}
radiofield       {@link Ext.field.Radio}
checkboxfield    {@link Ext.field.Checkbox}
selectfield      {@link Ext.field.Select}
togglefield      {@link Ext.field.Toggle}
fieldset         {@link Ext.form.FieldSet}
</pre>
 *
 * Fields are normally used within the context of a form and/or fieldset. See the {@link Ext.form.Panel FormPanel}
 * and {@link Ext.form.FieldSet FieldSet} docs for examples on how to put those together, or the list of links above
 * for usage of individual field types. If you wish to create your own Field subclasses you can extend this class,
 * though it is sometimes more useful to extend {@link Ext.field.Text} as this provides additional text entry
 * functionality.
 */
Ext.define('Ext.field.Field', {
    extend: 'Ext.Decorator',
    alternateClassName: 'Ext.form.Field',
    xtype: 'field',
    requires: [
        'Ext.field.Input'
    ],

    /**
     * Set to true on all Ext.field.Field subclasses. This is used by {@link Ext.form.Panel#getValues} to determine which
     * components inside a form are fields.
     * @property isField
     * @type Boolean
     */
    isField: true,

    // @private
    isFormField: true,

    config: {
        // @inherit
        baseCls: Ext.baseCSSPrefix + 'field',

        /**
         * The label of this field
         * @cfg {String} label
         * @accessor
         */
        label: null,

        /**
         * @cfg {String} labelAlign The position to render the label relative to the field input.
         * Available options are: 'top', 'left', 'bottom' and 'right'
         * Defaults to 'left'
         * @accessor
         */
        labelAlign: 'left',

        /**
         * @cfg {Number/String} labelWidth The width to make this field's label.
         * @accessor
         */
        labelWidth: '30%',

        /**
         * @cfg {Boolean} clearIcon True to use a clear icon in this field
         * @accessor
         */
        clearIcon: null,

        /**
         * @cfg {Boolean} required True to make this field required. Note: this only causes a visual indication.
         * Doesn't prevent user from submitting the form.
         * @accessor
         */
        required: false,

        /**
         * <p>The label Element associated with this Field. <b>Only available if a {@link #label} is specified.</b></p>
<p>This has been deprecated.
         * @type Ext.Element
         * @property labelEl
         * @deprecated 2.0
         */

        /**
         * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password, file (defaults
         * to 'text'). The types 'file' and 'password' must be used to render those field types currently -- there are
         * no separate Ext components for those.
         * This is now deprecated. Please use 'input.type' instead.
         * @deprecated 2.0
         * @accessor
         */
        inputType: null,

        /**
         * @cfg {String} name The field's HTML name attribute.
         * <b>Note</b>: this property must be set if this field is to be automatically included with
         * {@link Ext.form.Panel#method-submit form submit()}.
         * @accessor
         */
        name: null,

        /**
         * @cfg {Mixed} value A value to initialize this field with.
         * @accessor
         */
        value: null,

        /**
         * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered,
         * not those which are built via applyTo.
         * @accessor
         */
        tabIndex: null

        /**
         * @cfg {Object} component The inner component for this field.
         */

        /**
         * @cfg {Boolean} fullscreen
         * @hide
         */
    },

    cachedConfig: {
        /**
         * @cfg {String} labelCls Optional CSS class to add to the Label element
         * @accessor
         */
        labelCls: null,

        /**
         * @cfg {String} requiredCls The className to be applied to this Field when the {@link #required} configuration is set to true
         * @accessor
         */
        requiredCls: Ext.baseCSSPrefix + 'field-required',

        /**
         * @cfg {String} inputCls CSS class to add to the input element of this fields {@link #component}
         */
        inputCls: null
    },

    /**
     * @cfg {Boolean} isFocused
     * True if this field is currently focused,
     * @private
     */

    getElementConfig: function() {
        var prefix = Ext.baseCSSPrefix;

        return {
            reference: 'element',
            className: 'x-container',
            children: [
                {
                    reference: 'label',
                    cls: prefix + 'form-label',
                    children: [{
                        reference: 'labelspan',
                        tag: 'span'
                    }]
                },
                {
                    reference: 'innerElement',
                    cls: prefix + 'component-outer'
                }
            ]
        };
    },

    // @private
    updateLabel: function(newLabel, oldLabel) {
        var renderElement = this.renderElement,
            prefix = Ext.baseCSSPrefix;

        if (newLabel) {
            this.labelspan.setHtml(newLabel);
            renderElement.addCls(prefix + 'field-labeled');
        } else {
            renderElement.removeCls(prefix + 'field-labeled');
        }
    },

    // @private
    updateLabelAlign: function(newLabelAlign, oldLabelAlign) {
        var renderElement = this.renderElement,
            prefix = Ext.baseCSSPrefix;

        if (newLabelAlign) {
            renderElement.addCls(prefix + 'label-align-' + newLabelAlign);

            if (newLabelAlign == "top") {
                this.label.setWidth('100%');
            } else {
                this.updateLabelWidth(this.getLabelWidth());
            }
        }

        if (oldLabelAlign) {
            renderElement.removeCls(prefix + 'label-align-' + oldLabelAlign);
        }
    },

    // @private
    updateLabelCls: function(newLabelCls, oldLabelCls) {
        if (newLabelCls) {
            this.label.addCls(newLabelCls);
        }

        if (oldLabelCls) {
            this.label.removeCls(oldLabelCls);
        }
    },

    // @private
    updateLabelWidth: function(newLabelWidth) {
        if (newLabelWidth) {
            if (this.getLabelAlign() == "top") {
                this.label.setWidth('100%');
            } else {
                this.label.setWidth(newLabelWidth);
            }
        }
    },

    /**
     * Updates the {@link #required} configuration
     * @private
     */
    updateRequired: function(newRequired) {
        this.renderElement[newRequired ? 'addCls' : 'removeCls'](this.getRequiredCls());
    },

    /**
     * Updates the {@link #required} configuration
     * @private
     */
    updateRequiredCls: function(newRequiredCls, oldRequiredCls) {
        if (this.getRequired()) {
            this.renderElement.replaceCls(oldRequiredCls, newRequiredCls);
        }
    },

    // @private
    initialize: function() {
        var me = this;
        me.callParent();

        me.doInitValue();
    },

    /**
     * @private
     */
    doInitValue: function() {
        /**
         * @property {Mixed} originalValue
         * The original value of the field as configured in the {@link #value} configuration.
         * setting is <code>true</code>.
         */
        this.originalValue = this.getInitialConfig().value;
    },

    /**
     * Resets the current field value to the originally loaded value and clears any validation messages.
     * @return {Ext.field.Field} this
     */
    reset: function() {
        this.setValue(this.originalValue);

        return this;
    },

    /**
     * <p>Returns true if the value of this Field has been changed from its {@link #originalValue}.
     * Will return false if the field is disabled or has not been rendered yet.</p>
     * @return {Boolean} True if this field has been changed from its original value (and
     * is not disabled), false otherwise.
     */
    isDirty: function() {
        return false;
    }
}, function() {
});

/**
The checkbox field is an enhanced version of the native browser checkbox and is great for enabling your user to
choose one or more items from a set (for example choosing toppings for a pizza order). It works like any other
{@link Ext.field.Field field} and is usually found in the context of a form:

## Example

    @example miniphone preview
    var form = Ext.create('Ext.form.Panel', {
        fullscreen: true,
        items: [
            {
                xtype: 'checkboxfield',
                name : 'tomato',
                label: 'Tomato',
                value: 'tomato',
                checked: true
            },
            {
                xtype: 'checkboxfield',
                name : 'salami',
                label: 'Salami'
            },
            {
                xtype: 'toolbar',
                docked: 'bottom',
                items: [
                    { xtype: 'spacer' },
                    {
                        text: 'getValues',
                        handler: function() {
                            var form = Ext.ComponentQuery.query('formpanel')[0],
                                values = form.getValues();

                            Ext.Msg.alert(null,
                                "Tomato: " + ((values.tomato) ? "yes" : "no")
                                + "<br />Salami: " + ((values.salami) ? "yes" : "no")
                            );
                        }
                    },
                    { xtype: 'spacer' }
                ]
            }
        ]
    });


The form above contains two check boxes - one for Tomato, one for Salami. We configured the Tomato checkbox to be
checked immediately on load, and the Salami checkbox to be unchecked. We also specified an optional text
{@link #value} that will be sent when we submit the form. We can get this value using the Form's
{@link Ext.form.Panel#getValues getValues} function, or have it sent as part of the data that is sent when the
form is submitted:

    form.getValues(); //contains a key called 'tomato' if the Tomato field is still checked
    form.submit(); //will send 'tomato' in the form submission data

 */
Ext.define('Ext.field.Checkbox', {
    extend: 'Ext.field.Field',
    alternateClassName: 'Ext.form.Checkbox',

    xtype: 'checkboxfield',
    qsaLeftRe: /[\[]/g,
    qsaRightRe: /[\]]/g,

    isCheckbox: true,

    /**
     * @event check
     * Fires when the checkbox is checked.
     * @param {Ext.field.Checkbox} this This checkbox
     * @param {Ext.EventObject} e This event object
     */

    /**
     * @event uncheck
     * Fires when the checkbox is unchecked.
     * @param {Ext.field.Checkbox} this This checkbox
     * @param {Ext.EventObject} e This event object
     */

    config: {
        // @inherit
        ui: 'checkbox',

        /**
         * @cfg {String} value The string value to submit if the item is in a checked state.
         * @accessor
         */
        value: '',

        /**
         * @cfg {Boolean} checked <tt>true</tt> if the checkbox should render initially checked
         * @accessor
         */
        checked: false,

        /**
         * @cfg {Number} tabIndex
         * @hide
         */
        tabIndex: -1,

        // @inherit
        component: {
            xtype   : 'input',
            type    : 'checkbox',
            useMask : true,
            cls     : Ext.baseCSSPrefix + 'input-checkbox'
        }
    },

    // @private
    initialize: function() {
        var me = this;

        me.callParent();

        me.getComponent().on({
            scope: me,
            order: 'before',
            masktap: 'onMaskTap'
        });
    },

    // @private
    doInitValue: function() {
        var me = this,
            initialConfig = me.getInitialConfig();

        // you can have a value or checked config, but checked get priority
        if (initialConfig.hasOwnProperty('value')) {
            me.originalState = initialConfig.value;
        }

        if (initialConfig.hasOwnProperty('checked')) {
            me.originalState = initialConfig.checked;
        }

        me.callParent(arguments);
    },

    // @private
    updateInputType: function(newInputType) {
        var component = this.getComponent();
        if (component) {
            component.setType(newInputType);
        }
    },

    // @private
    updateName: function(newName) {
        var component = this.getComponent();
        if (component) {
            component.setName(newName);
        }
    },

    /**
     * Returns the field checked value
     * @return {Mixed} The field value
     */
    getChecked: function() {
        // we need to get the latest value from the {@link #input} and then update the value
        this._checked = this.getComponent().getChecked();

        return this._checked;
    },

    getValue: function() {
        return this.getChecked();
    },

    setValue: function(value) {
        return this.setChecked(value);
    },

    setChecked: function(newChecked) {
        this.updateChecked(newChecked);
        this._checked = newChecked;
    },

    updateChecked: function(newChecked) {
        this.getComponent().setChecked(newChecked);
    },

    // @private
    onMaskTap: function(component, e) {
        var me = this,
            dom = component.input.dom;

        if (me.getDisabled()) {
            return false;
        }

        //we must manually update the input dom with the new checked value
        dom.checked = !dom.checked;

        //continue as normal, like a normal tap
        // this.onTap(component, e);

        //calling getchecked will sync the new checked value
        if (me.getChecked()) {
            me.fireEvent('check', me, e);
        }
        else {
            me.fireEvent('uncheck', me, e);
        }

        //return false so the mask does not disappear
        return false;
    },

    /**
     * @method
     * Method called when this {@link Ext.field.Checkbox} has been checked
     */
    doChecked: Ext.emptyFn,

    /**
     * @method
     * Method called when this {@link Ext.field.Checkbox} has been unchecked
     */
    doUnChecked: Ext.emptyFn,

    /**
     * Returns the checked state of the checkbox.
     * @return {Boolean} True if checked, else otherwise
     */
    isChecked: function() {
        return this.getChecked();
    },

    /**
     * Set the checked state of the checkbox to true
     * @return {Ext.field.Checkbox} This checkbox
     */
    check: function() {
        return this.setChecked(true);
    },

    /**
     * Set the checked state of the checkbox to false
     * @return {Ext.field.Checkbox} This checkbox
     */
    uncheck: function() {
        return this.setChecked(false);
    },

    getSameGroupFields: function() {
        var component = this.up('formpanel') || this.up('fieldset'),
            name = this.getName(),
            replaceLeft = this.qsaLeftRe,
            replaceRight = this.qsaRightRe,
            components = [],
            elements, element, i, ln;


        if (!component) {
            return null;
        }

        // This is to handle ComponentQuery's lack of handling [name=foo[bar]] properly
        name = name.replace(replaceLeft, '\\[');
        name = name.replace(replaceRight, '\\]');

        elements = Ext.query('[name=' + name + ']', component.element.dom);
        ln = elements.length;
        for (i = 0; i < ln; i++) {
            element = elements[i];
            element = Ext.fly(element).up('.x-field-' + element.getAttribute('type'));
            if (element && element.id) {
                components.push(Ext.getCmp(element.id));
            }
        }
        return components;
    },

    /**
     * Returns an array of values from the checkboxes in the group that are checked,
     * @return {Array}
     */
    getGroupValues: function() {
        var values = [];

        this.getSameGroupFields().forEach(function(field) {
            if (field.getChecked()) {
                values.push(field.getValue());
            }
        });

        return values;
    },

    /**
     * Set the status of all matched checkboxes in the same group to checked
     * @param {Array} values An array of values
     * @return {Ext.field.Checkbox} This checkbox
     */
    setGroupValues: function(values) {
        this.getSameGroupFields().forEach(function(field) {
            field.setChecked((values.indexOf(field.getValue()) !== -1));
        });

        return this;
    },

    /**
     * Resets the status of all matched checkboxes in the same group to checked
     * @return {Ext.field.Checkbox} This checkbox
     */
    resetGroupValues: function() {
        this.getSameGroupFields().forEach(function(field) {
            field.setChecked(field.originalState);
        });

        return this;
    },

    // @inherit
    reset: function() {
        this.setChecked(this.originalState);
        return this;
    }
});

/**
 * Hidden fields allow you to easily inject additional data into a {@link Ext.form.Panel form} without displaying
 * additional fields on the screen. This is often useful for sending dynamic or previously collected data back to the
 * server in the same request as the normal form submission. For example, here is how we might set up a form to send
 * back a hidden userId field:
 *
 *     @example
 *     var form = Ext.create('Ext.form.Panel', {
 *         fullscreen: true,
 *         items: [
 *             {
 *                 xtype: 'fieldset',
 *                 title: 'Enter your name',
 *                 items: [
 *                     {
 *                         xtype: 'hiddenfield',
 *                         name: 'userId',
 *                         value: 123
 *                     },
 *                     {
 *                         xtype: 'checkboxfield',
 *                         label: 'Enable notifications',
 *                         name: 'notifications'
 *                     }
 *                 ]
 *             }
 *         ]
 *     });
 *
 * In the form above we created two fields - a hidden field and a {@link Ext.field.Checkbox check box field}. Only the
 * check box will be visible, but both fields will be submitted. Hidden fields cannot be tabbed to - they are removed
 * from the tab index so when your user taps the next/previous field buttons the hidden field is skipped over.
 *
 * It's easy to read and update the value of a hidden field within a form. Using the example above, we can get a
 * reference to the hidden field and then set it to a new value in 2 lines of code:
 *
 *     var userId = form.down('hiddenfield')[0];
 *     userId.setValue(1234);
 */
Ext.define('Ext.field.Hidden', {
    extend: 'Ext.field.Field',
    alternateClassName: 'Ext.form.Hidden',
    xtype: 'hiddenfield',

    config: {
        // @inherit
        component: {
            xtype: 'input',
            type : 'hidden'
        },

        // @inherit
        ui: 'hidden',

        /**
         * @cfg hidden
         * @hide
         */
        hidden: true,

        /**
         * @cfg {Number} tabIndex
         * @hide
         */
        tabIndex: -1
    }

});

/**
 * The radio field is an enhanced version of the native browser radio controls and is a good way of allowing your user
 * to choose one option out of a selection of several (for example, choosing a favorite color):
 *
 *     @example
 *     var form = Ext.create('Ext.form.Panel', {
 *         fullscreen: true,
 *         items: [
 *             {
 *                 xtype: 'radiofield',
 *                 name : 'color',
 *                 value: 'red',
 *                 label: 'Red',
 *                 checked: true
 *             },
 *             {
 *                 xtype: 'radiofield',
 *                 name : 'color',
 *                 value: 'green',
 *                 label: 'Green'
 *             },
 *             {
 *                 xtype: 'radiofield',
 *                 name : 'color',
 *                 value: 'blue',
 *                 label: 'Blue'
 *             }
 *         ]
 *     });
 *
 * Above we created a simple form which allows the user to pick a color from the options red, green and blue. Because
 * we gave each of the fields above the same {@link #name}, the radio field ensures that only one of them can be
 * checked at a time. When we come to get the values out of the form again or submit it to the server, only 1 value
 * will be sent for each group of radio fields with the same name:
 *
 *     form.getValues(); //looks like {color: 'red'}
 *     form.submit(); //sends a single field back to the server (in this case color: red)
 *
 */
Ext.define('Ext.field.Radio', {
    extend: 'Ext.field.Checkbox',
    xtype: 'radiofield',
    alternateClassName: 'Ext.form.Radio',

    isRadio: true,

    config: {
        // @inherit
        ui: 'radio',

        // @inherit
        component: {
            type: 'radio',
            cls: Ext.baseCSSPrefix + 'input-radio'
        }
    },

    getValue: function() {
        return this._value;
    },

    setValue: function(value) {
        this._value = value;
        return this;
    },

    /**
     * Returns the selected value if this radio is part of a group (other radio fields with the same name, in the same FormPanel),
     * @return {String}
     */
    getGroupValue: function() {
        var fields = this.getSameGroupFields(),
            ln = fields.length,
            i = 0,
            field;

        for (; i < ln; i++) {
            field = fields[i];
            if (field.getChecked()) {
                return field.getValue();
            }
        }

        return null;
    },

    /**
     * Set the matched radio field's status (that has the same value as the given string) to checked
     * @param {String} value The value of the radio field to check
     * @return {Ext.field.Radio} The field that is checked
     */
    setGroupValue: function(value) {
        var fields = this.getSameGroupFields(),
            ln = fields.length,
            i = 0,
            field;

        for (; i < ln; i++) {
            field = fields[i];
            if (field.getValue() === value) {
                field.setChecked(true);
                return field;
            }
        }
    }
});

/**
 * The text field is the basis for most of the input fields in Sencha Touch. It provides a baseline of shared
 * functionality such as input validation, standard events, state management and look and feel. Typically we create
 * text fields inside a form, like this:
 *
 *     @example
 *     Ext.create('Ext.form.Panel', {
 *         fullscreen: true,
 *         items: [
 *             {
 *                 xtype: 'fieldset',
 *                 title: 'Enter your name',
 *                 items: [
 *                     {
 *                         xtype: 'textfield',
 *                         label: 'First Name',
 *                         name: 'firstName'
 *                     },
 *                     {
 *                         xtype: 'textfield',
 *                         label: 'Last Name',
 *                         name: 'lastName'
 *                     }
 *                 ]
 *             }
 *         ]
 *     });
 *
 * This creates two text fields inside a form. Text Fields can also be created outside of a Form, like this:
 *
 *     Ext.create('Ext.field.Text', {
 *         label: 'Your Name',
 *         value: 'Ed Spencer'
 *     });
 *
 * ## Configuring
 *
 * Text field offers several configuration options, including {@link #placeHolder}, {@link #maxLength},
 * {@link #autoComplete}, {@link #autoCapitalize} and {@link #autoCorrect}. For example, here is how we would configure
 * a text field to have a maximum length of 10 characters, with placeholder text that disappears when the field is
 * focused:
 *
 *     Ext.create('Ext.field.Text', {
 *         label: 'Username',
 *         maxLength: 10,
 *         placeHolder: 'Enter your username'
 *     });
 *
 * The autoComplete, autoCapitalize and autoCorrect configs simply set those attributes on the text field and allow the
 * native browser to provide those capabilities. For example, to enable auto complete and auto correct, simply
 * configure your text field like this:
 *
 *     Ext.create('Ext.field.Text', {
 *         label: 'Username',
 *         autoComplete: true,
 *         autoCorrect: true
 *     });
 *
 * These configurations will be picked up by the native browser, which will enable the options at the OS level.
 *
 * Text field inherits from {@link Ext.field.Field}, which is the base class for all fields in Sencha Touch and provides
 * a lot of shared functionality for all fields, including setting values, clearing and basic validation. See the
 * {@link Ext.field.Field} documentation to see how to leverage its capabilities.
 */
Ext.define('Ext.field.Text', {
    extend: 'Ext.field.Field',
    xtype: 'textfield',
    alternateClassName: 'Ext.form.Text',

    /**
     * @event focus
     * Fires when this field receives input focus
     * @param {Ext.field.Text} this This field
     * @param {Ext.event.Event} e
     */

    /**
     * @event blur
     * Fires when this field loses input focus
     * @param {Ext.field.Text} this This field
     * @param {Ext.event.Event} e
     */

    /**
     * @event paste
     * Fires when this field is pasted.
     * @param {Ext.field.Text} this This field
     * @param {Ext.event.Event} e
     */

    /**
     * @event mousedown
     * Fires when this field receives a mousedown
     * @param {Ext.field.Text} this This field
     * @param {Ext.event.Event} e
     */

    /**
     * @event keyup
     * @preventable doKeyUp
     * Fires when a key is released on the input element
     * @param {Ext.field.Text} this This field
     * @param {Ext.event.Event} e
     */

    /**
     * @event clearicontap
     * @preventable doClearIconTap
     * Fires when the clear icon is tapped
     * @param {Ext.field.Text} this This field
     * @param {Ext.event.Event} e
     */

    /**
     * @event change
     * Fires just before the field blurs if the field value has changed
     * @param {Ext.field.Text} this This field
     * @param {Mixed} newValue The new value
     * @param {Mixed} oldValue The original value
     */

    /**
     * @event action
     * @preventable doAction
     * Fires whenever the return key or go is pressed. FormPanel listeners
     * for this event, and submits itself whenever it fires. Also note
     * that this event bubbles up to parent containers.
     * @param {Ext.field.Text} this This field
     * @param {Mixed} e The key event object
     */

    config: {
        // @inherit
        ui: 'text',

        // @inherit
        clearIcon: true,

        /**
         * @cfg {String} placeHolder A string value displayed in the input (if supported) when the control is empty.
         * @accessor
         */
        placeHolder: null,

        /**
         * @cfg {Number} maxLength The maximum number of permitted input characters.
         * @accessor
         */
        maxLength: null,

        /**
         * True to set the field's DOM element autocomplete attribute to "on", false to set to "off".
         * @cfg {Boolean} autoComplete
         * @accessor
         */
        autoComplete: null,

        /**
         * True to set the field's DOM element autocapitalize attribute to "on", false to set to "off".
         * @cfg {Boolean} autoCapitalize
         * @accessor
         */
        autoCapitalize: null,

        /**
         * True to set the field DOM element autocorrect attribute to "on", false to set to "off".
         * @cfg {Boolean} autoCorrect
         * @accessor
         */
        autoCorrect: null,

        /**
         * True to set the field DOM element readonly attribute to true.
         * @cfg {Boolean} readOnly
         * @accessor
         */
        readOnly: null,

        /**
         * @cfg {Object} component The inner component for this field, which defaults to an input text.
         * @accessor
         */
        component: {
            xtype: 'input',
            type : 'text'
        }
    },

    // @private
    initialize: function() {
        var me = this;

        me.callParent();

        me.getComponent().on({
            scope: this,

            keyup       : 'onKeyUp',
            change      : 'onChange',
            focus       : 'onFocus',
            blur        : 'onBlur',
            paste       : 'onPaste',
            mousedown   : 'onMouseDown',
            clearicontap: 'onClearIconTap'
        });

        // set the originalValue of the textfield, if one exists
        me.originalValue = me.originalValue || "";
        me.getComponent().originalValue = me.originalValue;

        me.syncEmptyCls();
    },

    syncEmptyCls: function() {
        var empty = (this._value) ? this._value.length : false,
            cls = Ext.baseCSSPrefix + 'empty';

        if (empty) {
            this.removeCls(cls);
        } else {
            this.addCls(cls);
        }
    },

    // @private
    updateValue: function(newValue) {
        var component = this.getComponent();

        if (component) {
            component.setValue(newValue);
        }

        this[newValue ? 'showClearIcon' : 'hideClearIcon']();

        this.syncEmptyCls();
    },

    getValue: function() {
        var me = this;

        me._value = me.getComponent().getValue();

        me.syncEmptyCls();

        return me._value;
    },

    // @private
    updatePlaceHolder: function(newPlaceHolder) {
        this.getComponent().setPlaceHolder(newPlaceHolder);
    },

    // @private
    updateMaxLength: function(newMaxLength) {
        this.getComponent().setMaxLength(newMaxLength);
    },

    // @private
    updateAutoComplete: function(newAutoComplete) {
        this.getComponent().setAutoComplete(newAutoComplete);
    },

    // @private
    updateAutoCapitalize: function(newAutoCapitalize) {
        this.getComponent().setAutoCapitalize(newAutoCapitalize);
    },

    // @private
    updateAutoCorrect: function(newAutoCorrect) {
        this.getComponent().setAutoCorrect(newAutoCorrect);
    },

    // @private
    updateReadOnly: function(newReadOnly) {
        if (newReadOnly) {
            this.hideClearIcon();
        } else {
            this.showClearIcon();
        }

        this.getComponent().setReadOnly(newReadOnly);
    },

    // @private
    updateInputType: function(newInputType) {
        var component = this.getComponent();
        if (component) {
            component.setType(newInputType);
        }
    },

    // @private
    updateName: function(newName) {
        var component = this.getComponent();
        if (component) {
            component.setName(newName);
        }
    },

    // @private
    updateTabIndex: function(newTabIndex) {
        var component = this.getComponent();
        if (component) {
            component.setTabIndex(newTabIndex);
        }
    },

    /**
     * Updates the {@link #inputCls} configuration on this fields {@link #component}
     * @private
     */
    updateInputCls: function(newInputCls, oldInputCls) {
        var component = this.getComponent();
        if (component) {
            component.replaceCls(oldInputCls, newInputCls);
        }
    },

    // @inherit
    doSetDisabled: function(disabled) {
        var me = this;

        me.callParent(arguments);

        var component = me.getComponent();
        if (component) {
            component.setDisabled(disabled);
        }

        if (disabled) {
            me.hideClearIcon();
        } else {
            me.showClearIcon();
        }
    },

    // @private
    showClearIcon: function() {
        var me = this;

        if (!me.getDisabled() && !me.getReadOnly() && me.getValue() && me.getClearIcon()) {
            me.element.addCls(Ext.baseCSSPrefix + 'field-clearable');
        }

        return me;
    },

    // @private
    hideClearIcon: function() {
        if (this.getClearIcon()) {
            this.element.removeCls(Ext.baseCSSPrefix + 'field-clearable');
        }
    },

    onKeyUp: function(e) {
        this.fireAction('keyup', [this, e], 'doKeyUp');
    },

    /**
     * Called when a key has been pressed in the `<input>`
     * @private
     */
    doKeyUp: function(me, e) {
        // getValue to ensure that we are in sync with the dom
        var value = me.getValue();

        // show the {@link #clearIcon} if it is being used
        me[value ? 'showClearIcon' : 'hideClearIcon']();

        if (e.browserEvent.keyCode === 13) {
            me.fireAction('action', [me, e], 'doAction');
        }
    },

    doAction: function() {
        this.blur();
    },

    onClearIconTap: function(e) {
        this.fireAction('clearicontap', [this, e], 'doClearIconTap');
    },

    // @private
    doClearIconTap: function(me, e) {
        me.setValue('');

        //sync with the input
        me.getValue();
    },

    onChange: function(me, value, startValue) {
        me.fireEvent('change', this, value, startValue);
    },

    onFocus: function(e) {
        this.isFocused = true;
        this.fireEvent('focus', this, e);
    },

    onBlur: function(e) {
        var me = this;

        this.isFocused = false;

        me.fireEvent('blur', me, e);

        setTimeout(function() {
            me.isFocused = false;
        }, 50);
    },

    onPaste: function(e) {
        this.fireEvent('paste', this, e);
    },

    onMouseDown: function(e) {
        this.fireEvent('mousedown', this, e);
    },

    /**
     * Attempts to set the field as the active input focus.
     * @return {Ext.field.Text} This field
     */
    focus: function() {
        this.getComponent().focus();
        return this;
    },

    /**
     * Attempts to forcefully blur input focus for the field.
     * @return {Ext.field.Text} This field
     */
    blur: function() {
        this.getComponent().blur();
        return this;
    },

    /**
     * Attempts to forcefully select all the contents of the input field.
     * @return {Ext.field.Text} this
     */
    select: function() {
        this.getComponent().select();
        return this;
    },

    // @inherit
    reset: function() {
        this.getComponent().reset();

        //we need to call this to sync the input with this field
        this.getValue();

        this[this._value ? 'showClearIcon' : 'hideClearIcon']();
    },

    // @inherit
    isDirty: function() {
        var component = this.getComponent();
        if (component) {
            return component.isDirty();
        }
        return false;
    }
});


/**
 * The Email field creates an HTML5 email input and is usually created inside a form. Because it creates an HTML email
 * input field, most browsers will show a specialized virtual keyboard for email address input. Aside from that, the
 * email field is just a normal text field. Here's an example of how to use it in a form:
 *
 *     @example
 *     Ext.create('Ext.form.Panel', {
 *         fullscreen: true,
 *         items: [
 *             {
 *                 xtype: 'fieldset',
 *                 title: 'Register',
 *                 items: [
 *                     {
 *                         xtype: 'emailfield',
 *                         label: 'Email',
 *                         name: 'email'
 *                     },
 *                     {
 *                         xtype: 'passwordfield',
 *                         label: 'Password',
 *                         name: 'password'
 *                     }
 *                 ]
 *             }
 *         ]
 *     });
 *
 * Or on its own, outside of a form:
 *
 *     Ext.create('Ext.field.Email', {
 *         label: 'Email address',
 *         value: 'prefilled@email.com'
 *     });
 *
 * Because email field inherits from {@link Ext.field.Text textfield} it gains all of the functionality that text fields
 * provide, including getting and setting the value at runtime, validations and various events that are fired as the
 * user interacts with the component. Check out the {@link Ext.field.Text} docs to see the additional functionality
 * available.
 */
Ext.define('Ext.field.Email', {
    extend: 'Ext.field.Text',
    alternateClassName: 'Ext.form.Email',
    xtype: 'emailfield',

    config: {
        // @inherit
        component: {
	        type: 'email'
	    },

        // @inherit
        autoCapitalize: false
    }
});






/**
 * The Number field creates an HTML5 number input and is usually created inside a form. Because it creates an HTML
 * number input field, most browsers will show a specialized virtual keyboard for entering numbers. The Number field
 * only accepts numerical input and also provides additional spinner UI that increases or decreases the current value
 * by a configured {@link #stepValue step value}. Here's how we might use one in a form:
 *
 *     @example
 *     Ext.create('Ext.form.Panel', {
 *         fullscreen: true,
 *         items: [
 *             {
 *                 xtype: 'fieldset',
 *                 title: 'How old are you?',
 *                 items: [
 *                     {
 *                         xtype: 'numberfield',
 *                         label: 'Age',
 *                         minValue: 18,
 *                         maxValue: 150,
 *                         name: 'age'
 *                     }
 *                 ]
 *             }
 *         ]
 *     });
 *
 * Or on its own, outside of a form:
 *
 *     Ext.create('Ext.field.Number', {
 *         label: 'Age',
 *         value: '26'
 *     });
 *
 * ## minValue, maxValue and stepValue
 *
 * The {@link #minValue} and {@link #maxValue} configurations are self-explanatory and simply constrain the value
 * entered to the range specified by the configured min and max values. The other option exposed by this component
 * is {@link #stepValue}, which enables you to set how much the value changes every time the up and down spinners
 * are tapped on. For example, to create a salary field that ticks up and down by $1,000 each tap we can do this:
 *
 *     @example
 *     Ext.create('Ext.form.Panel', {
 *         fullscreen: true,
 *         items: [
 *             {
 *                 xtype: 'fieldset',
 *                 title: 'Are you rich yet?',
 *                 items: [
 *                     {
 *                         xtype: 'numberfield',
 *                         label: 'Salary',
 *                         value: 30000,
 *                         minValue: 25000,
 *                         maxValue: 50000,
 *                         stepValue: 1000
 *                     }
 *                 ]
 *             }
 *         ]
 *     });
 *
 * This creates a field that starts with a value of $30,000, steps up and down in $1,000 increments and will not go
 * beneath $25,000 or above $50,000.
 *
 * Because number field inherits from {@link Ext.field.Text textfield} it gains all of the functionality that text
 * fields provide, including getting and setting the value at runtime, validations and various events that are fired as
 * the user interacts with the component. Check out the {@link Ext.field.Text} docs to see the additional functionality
 * available.
 */
Ext.define('Ext.field.Number', {
    extend: 'Ext.field.Text',
    xtype: 'numberfield',
    alternateClassName: 'Ext.form.Number',

    config: {
        // @inherit
        component: {
            type: 'number'
        },

        // @inherit
        ui: 'number'
    },

    proxyConfig: {
        /**
         * @cfg {Number} minValue The minimum value that this Number field can accept
         * @accessor
         */
        minValue: null,

        /**
         * @cfg {Number} maxValue The maximum value that this Number field can accept
         * @accessor
         */
        maxValue: null,

        /**
         * @cfg {Number} stepValue The amount by which the field is incremented or decremented each time the spinner is tapped.
         * Defaults to undefined, which means that the field goes up or down by 1 each time the spinner is tapped
         * @accessor
         */
        stepValue: null
    },

    applyValue: function(value) {
        var minValue = this.getMinValue(),
            maxValue = this.getMaxValue();

        if (Ext.isNumber(minValue)) {
            value = Math.max(value, minValue);
        }

        if (Ext.isNumber(maxValue)) {
            value = Math.min(value, maxValue);
        }

        return parseFloat(value);
    },

    getValue: function() {
        var value = this.callParent();
        return parseFloat(value || 0);
    },

    doClearIconTap: function(me, e) {
        me.setValue(0);
    }
});

/**
 * The Password field creates a password input and is usually created inside a form. Because it creates a password
 * field, when the user enters text it will show up as stars. Aside from that, the password field is just a normal text
 * field. Here's an example of how to use it in a form:
 *
 *     @example
 *     Ext.create('Ext.form.Panel', {
 *         fullscreen: true,
 *         items: [
 *             {
 *                 xtype: 'fieldset',
 *                 title: 'Register',
 *                 items: [
 *                     {
 *                         xtype: 'emailfield',
 *                         label: 'Email',
 *                         name: 'email'
 *                     },
 *                     {
 *                         xtype: 'passwordfield',
 *                         label: 'Password',
 *                         name: 'password'
 *                     }
 *                 ]
 *             }
 *         ]
 *     });
 *
 * Or on its own, outside of a form:
 *
 *     Ext.create('Ext.field.Password', {
 *         label: 'Password',
 *         value: 'existingPassword'
 *     });
 *
 * Because the password field inherits from {@link Ext.field.Text textfield} it gains all of the functionality that text
 * fields provide, including getting and setting the value at runtime, validations and various events that are fired as
 * the user interacts with the component. Check out the {@link Ext.field.Text} docs to see the additional functionality
 * available.
 */
Ext.define('Ext.field.Password', {
    extend: 'Ext.field.Text',
    xtype: 'passwordfield',
    alternateClassName: 'Ext.form.Password',

    config: {
        // @inherit
        autoCapitalize: false,

        // @inherit
        component: {
	        type: 'password'
	    }
    }
});

/**
 * The Search field creates an HTML5 search input and is usually created inside a form. Because it creates an HTML
 * search input type, the visual styling of this input is slightly different to normal text input contrls (the corners
 * are rounded), though the virtual keyboard displayed by the operating system is the standard keyboard control.
 *
 * As with all other form fields in Sencha Touch, the search field gains a "clear" button that appears whenever there
 * is text entered into the form, and which removes that text when tapped.
 *
 *     @example
 *     Ext.create('Ext.form.Panel', {
 *         fullscreen: true,
 *         items: [
 *             {
 *                 xtype: 'fieldset',
 *                 title: 'Search',
 *                 items: [
 *                     {
 *                         xtype: 'searchfield',
 *                         label: 'Query',
 *                         name: 'query'
 *                     }
 *                 ]
 *             }
 *         ]
 *     });
 *
 * Or on its own, outside of a form:
 *
 *     Ext.create('Ext.field.Search', {
 *         label: 'Search:',
 *         value: 'query'
 *     });
 *
 * Because search field inherits from {@link Ext.field.Text textfield} it gains all of the functionality that text
 * fields provide, including getting and setting the value at runtime, validations and various events that are fired
 * as the user interacts with the component. Check out the {@link Ext.field.Text} docs to see the additional
 * functionality available.
 */
Ext.define('Ext.field.Search', {
    extend: 'Ext.field.Text',
    xtype: 'searchfield',
    alternateClassName: 'Ext.form.Search',

    config: {
        // @inherit
        component: {
	        type: 'search'
	    },

	    // @inherit
	    ui: 'search'
    }
});

/**
 * Wraps an HTML5 number field. Example usage:
 *
 *     @example miniphone
 *     var spinner = Ext.create('Ext.field.Spinner', {
 *         label: 'Spinner Field',
 *         minValue: 0,
 *         maxValue: 100,
 *         increment: 2,
 *         cycle: true
 *     });
 *     Ext.Viewport.add(spinner);
 *
 */
Ext.define('Ext.field.Spinner', {
    extend: 'Ext.field.Number',
    xtype: 'spinnerfield',
    alternateClassName: 'Ext.form.Spinner',
    requires: ['Ext.util.TapRepeater'],

    /**
     * @event spin
     * Fires when the value is changed via either spinner buttons
     * @param {Ext.field.Spinner} this
     * @param {Number} value
     * @param {String} direction 'up' or 'down'
     */

    /**
     * @event spindown
     * Fires when the value is changed via the spinner down button
     * @param {Ext.field.Spinner} this
     * @param {Number} value
     */

    /**
     * @event spinup
     * Fires when the value is changed via the spinner up button
     * @param {Ext.field.Spinner} this
     * @param {Number} value
     */

    /**
     * @event change
     * @hide
     */

    /**
     * @event updatedata
     * @hide
     */

    /**
     * @event action
     * @hide
     */

    config: {
        // @inherit
        cls: Ext.baseCSSPrefix + 'spinner',

        /**
         * @cfg {Number} [minValue=-infinity] The minimum allowed value.
         * @accessor
         */
        minValue: Number.NEGATIVE_INFINITY,

        /**
         * @cfg {Number} [maxValue=infinity] The maximum allowed value.
         * @accessor
         */
        maxValue: Number.MAX_VALUE,

        /**
         * @cfg {Number} increment Value that is added or subtracted from the current value when a spinner is used.
         * @accessor
         */
        increment: 0.1,

        /**
         * @cfg {Boolean} accelerateOnTapHold True if autorepeating should start slowly and accelerate.
         * @accessor
         */
        accelerateOnTapHold: true,

        /**
         * @cfg {Boolean} cycle When set to true, it will loop the values of a minimum or maximum is reached.
         * If the maximum value is reached, the value will be set to the minimum.
         * @accessor
         */
        cycle: false,

        /**
         * @cfg {Boolean} clearIcon
         * @hide
         * @accessor
         */
        clearIcon: false,

        /**
         * @cfg {Number} defaultValue The default value for this field when no value has been set. It is also used when
         *                            the value is set to `NaN`.
         */
        defaultValue: 0,

        /**
         * @cfg {Number} tabIndex
         * @hide
         */
        tabIndex: -1,

        // @inherit
        component: {
            disabled: true
        }
    },

    constructor: function() {
        this.callParent(arguments);

        if (!this.getValue()) {
            this.setValue(this.getDefaultValue());
        }
    },

    syncEmptyCls: Ext.emptyFn,

    /**
     * Updates the {@link #component} configuration
     */
    updateComponent: function(newComponent) {
        this.callParent(arguments);

        var innerElement = this.innerElement,
            cls = this.getCls();

        if (newComponent) {
            this.spinDownButton = Ext.Element.create({
                cls : cls + '-button ' + cls + '-button-down',
                html: '-'
            });

            innerElement.insertFirst(this.spinDownButton);

            this.spinUpButton = Ext.Element.create({
                cls : cls + '-button ' + cls + '-button-up',
                html: '+'
            });

            innerElement.appendChild(this.spinUpButton);

            this.downRepeater = this.createRepeater(this.spinDownButton, this.onSpinDown);
            this.upRepeater = this.createRepeater(this.spinUpButton,     this.onSpinUp);
        }
    },

    // @inherit
    applyValue: function(value) {
        value = parseFloat(value);
        if (isNaN(value) || value === null) {
            value = this.getDefaultValue();
        }

        //round the value to 1 decimal
        value = Math.round(value * 10) / 10;

        return this.callParent([value]);
    },

    // @private
    createRepeater: function(el, fn) {
        var me = this,
            repeater = Ext.create('Ext.util.TapRepeater', {
                el: el,
                accelerate: me.getAccelerateOnTapHold()
            });

        repeater.on({
            tap: fn,
            touchstart: 'onTouchStart',
            touchend: 'onTouchEnd',
            scope: me
        });

        return repeater;
    },

    // @private
    onSpinDown: function() {
        if (!this.getDisabled()) {
            this.spin(true);
        }
    },

    // @private
    onSpinUp: function() {
        if (!this.getDisabled()) {
            this.spin(false);
        }
    },

    // @private
    onTouchStart: function(repeater) {
        if (!this.getDisabled()) {
            repeater.getEl().addCls(Ext.baseCSSPrefix + 'button-pressed');
        }
    },

    // @private
    onTouchEnd: function(repeater) {
        repeater.getEl().removeCls(Ext.baseCSSPrefix + 'button-pressed');
    },

    // @private
    spin: function(down) {
        var me = this,
            originalValue = me.getValue(),
            increment = me.getIncrement(),
            direction = down ? 'down' : 'up',
            minValue = me.getMinValue(),
            maxValue = me.getMaxValue(),
            value;

        if (down) {
            value = originalValue - increment;
        }
        else {
            value = originalValue + increment;
        }

        //if cycle is true, then we need to check fi the value hasn't changed and we cycle the value
        if (me.getCycle()) {
            if (originalValue == minValue && value < minValue) {
                value = maxValue;
            }

            if (originalValue == maxValue && value > maxValue) {
                value = minValue;
            }
        }

        me.setValue(value);
        value = me.getValue();

        me.fireEvent('spin', me, value, direction);
        me.fireEvent('spin' + direction, me, value);
    },

    /**
     * @private
     */
    doSetDisabled: function(disabled) {
        Ext.Component.prototype.doSetDisabled.apply(this, arguments);
    },

    /**
     * @private
     */
    setDisabled: function() {
        Ext.Component.prototype.setDisabled.apply(this, arguments);
    },

    reset: function() {
        this.setValue(this.getDefaultValue());
    },

    // @private
    destroy: function() {
        var me = this;
        Ext.destroy(me.downRepeater, me.upRepeater, me.spinDownButton, me.spinUpButton);
        me.callParent(arguments);
    }
}, function() {
});

/**
 * @private
 */
Ext.define('Ext.field.TextAreaInput', {
    extend: 'Ext.field.Input',
    xtype : 'textareainput',

    tag: 'textarea'
});

/**
 * Creates an HTML textarea field on the page. This is useful whenever you need the user to enter large amounts of text
 * (i.e. more than a few words). Typically, text entry on mobile devices is not a pleasant experience for the user so
 * it's good to limit your use of text areas to only those occasions when freeform text is required or alternative
 * input methods like select boxes or radio buttons are not possible. Text Areas are usually created inside forms, like
 * this:
 *
 *     @example
 *     Ext.create('Ext.form.Panel', {
 *         fullscreen: true,
 *         items: [
 *             {
 *                 xtype: 'fieldset',
 *                 title: 'About you',
 *                 items: [
 *                     {
 *                         xtype: 'textfield',
 *                         label: 'Name',
 *                         name: 'name'
 *                     },
 *                     {
 *                         xtype: 'textareafield',
 *                         label: 'Bio',
 *                         maxRows: 4,
 *                         name: 'bio'
 *                     }
 *                 ]
 *             }
 *         ]
 *     });
 *
 * In the example above we're creating a form with a {@link Ext.field.Text text field} for the user's name and a text
 * area for their bio. We used the {@link #maxRows} configuration on the text area to tell it to grow to a maximum of 4
 * rows of text before it starts using a scroll bar inside the text area to scroll the text.
 *
 * We can also create a text area outside the context of a form, like this:
 *
 * This creates two text fields inside a form. Text Fields can also be created outside of a Form, like this:
 *
 *     Ext.create('Ext.field.TextArea', {
 *         label: 'About You',
 *         {@link #placeHolder}: 'Tell us about yourself...'
 *     });
 */
Ext.define('Ext.field.TextArea', {
    extend: 'Ext.field.Text',
    xtype: 'textareafield',
    requires: ['Ext.field.TextAreaInput'],
    alternateClassName: 'Ext.form.TextArea',

    config: {
        // @inherit
        ui: 'textarea',

        // @inherit
        autoCapitalize: false,

        // @inherit
        component: {
            xtype: 'textareainput'
        },

        /**
         * @cfg {Number} maxRows The maximum number of lines made visible by the input.
         * @accessor
         */
        maxRows: null
    },

    // @private
    updateMaxRows: function(newRows) {
        this.getComponent().setMaxRows(newRows);
    },

    doSetHeight: function(newHeight) {
        this.callParent(arguments);
        var component = this.getComponent();
        component.input.setHeight(newHeight);
    },

    doSetWidth: function(newWidth) {
        this.callParent(arguments);
        var component = this.getComponent();
        component.input.setWidth(newWidth);
    },

    /**
     * Called when a key has been pressed in the `<input>`
     * @private
     */
    doKeyUp: function(me) {
        // getValue to ensure that we are in sync with the dom
        var value = me.getValue();

        // show the {@link #clearIcon} if it is being used
        me[value ? 'showClearIcon' : 'hideClearIcon']();
    }
});

/**
 * The Url field creates an HTML5 url input and is usually created inside a form. Because it creates an HTML url input
 * field, most browsers will show a specialized virtual keyboard for web address input. Aside from that, the url field
 * is just a normal text field. Here's an example of how to use it in a form:
 *
 *     @example
 *     Ext.create('Ext.form.Panel', {
 *         fullscreen: true,
 *         items: [
 *             {
 *                 xtype: 'fieldset',
 *                 title: 'Add Bookmark',
 *                 items: [
 *                     {
 *                         xtype: 'urlfield',
 *                         label: 'Url',
 *                         name: 'url'
 *                     }
 *                 ]
 *             }
 *         ]
 *     });
 *
 * Or on its own, outside of a form:
 *
 *     Ext.create('Ext.field.Url', {
 *         label: 'Web address',
 *         value: 'http://sencha.com'
 *     });
 *
 * Because url field inherits from {@link Ext.field.Text textfield} it gains all of the functionality that text fields
 * provide, including getting and setting the value at runtime, validations and various events that are fired as the
 * user interacts with the component. Check out the {@link Ext.field.Text} docs to see the additional functionality
 * available.
 */
Ext.define('Ext.field.Url', {
    extend: 'Ext.field.Text',
    xtype: 'urlfield',
    alternateClassName: 'Ext.form.Url',

    config: {
        // @inherit
        autoCapitalize: false,

        // @inherit
        component: {
	        type: 'url'
	    }
    }
});

/**
 * Adds a Load More button at the bottom of the list. When the user presses this button,
 * the next page of data will be loaded into the store and appended to the List.
 *
 * By specifying `{@link #autoPaging}: true`, an 'infinite scroll' effect can be achieved,
 * i.e., the next page of content will load automatically when the user scrolls to the
 * bottom of the list.
 *
 * ## Example
 *
 *     Ext.create('Ext.dataview.List', {
 *
 *         store: Ext.create('TweetStore'),
 *
 *         plugins: [
 *             {
 *                 xclass: 'Ext.plugin.ListPaging',
 *                 autoPaging: true
 *             }
 *         ],
 *
 *         itemTpl: [
 *             '<img src="{profile_image_url}" />',
 *             '<div class="tweet">{text}</div>'
 *         ]
 *     });
 */
Ext.define('Ext.plugin.ListPaging', {
    extend: 'Ext.Component',
    alias: 'plugin.listpaging',

    config: {
        /**
         * @cfg {Boolean} autoPaging
         * True to automatically load the next page when you scroll to the bottom of the list.
         */
        autoPaging: false,

        /**
         * @cfg {String} loadMoreText The text used as the label of the Load More button.
         */
        loadMoreText: 'Load More...',

        /**
         * @cfg {String} noMoreRecordsText The text used as the label of the Load More button when the Store's
         * {@link Ext.data.Store#totalCount totalCount} indicates that all of the records available on the server are
         * already loaded
         */
        noMoreRecordsText: 'No More Records',

        /**
         * @private
         * @cfg {String} loadTpl The template used to render the load more text
         */
        loadTpl: [
            '<div class="{cssPrefix}loading-spinner" style="font-size: 180%; margin: 10px auto;">',
                 '<span class="{cssPrefix}loading-top"></span>',
                 '<span class="{cssPrefix}loading-right"></span>',
                 '<span class="{cssPrefix}loading-bottom"></span>',
                 '<span class="{cssPrefix}loading-left"></span>',
            '</div>',
            '<div class="{cssPrefix}list-paging-msg">{message}</div>'
        ].join(''),

        /**
         *
         */
        loadMoreCmp: {
            xtype: 'component',
            baseCls: Ext.baseCSSPrefix + 'list-paging'
        },

        /**
         * @private
         * @cfg {Boolean} loadMoreCmpAdded Indicates whether or not the load more component has been added to the List
         * yet.
         */
        loadMoreCmpAdded: false,

        /**
         * @private
         * @cfg {String} loadingCls The CSS class that is added to the {@link #loadMoreCmp} while the Store is loading
         */
        loadingCls: Ext.baseCSSPrefix + 'loading',

        /**
         * @private
         * @cfg {Ext.List} list Local reference to the List this plugin is bound to
         */
        list: null,

        /**
         * @private
         * @cfg {Ext.scroll.Scroller} scroller Local reference to the List's Scroller
         */
        scroller: null,

        /**
         * @private
         * @cfg {Boolean} loading True if the plugin has initiated a Store load that has not yet completed
         */
        loading: false
    },

    /**
     * @private
     * Sets up all of the references the plugin needs
     */
    init: function(list) {
        var scroller = list.getScrollable().getScroller(),
            store    = list.getStore();

        this.setList(list);
        this.setScroller(scroller);
        this.bindStore(list.getStore());

        // We provide our own load mask so if the Store is autoLoading already disable the List's mask straight away,
        // otherwise if the Store loads later allow the mask to show once then remove it thereafter
        if (store) {
            this.disableDataViewMask(store);
        }

        // The List's Store could change at any time so make sure we are informed when that happens
        list.updateStore = Ext.Function.createInterceptor(list.updateStore, this.bindStore, this);

        if (this.getAutoPaging()) {
            scroller.on({
                scrollend: this.onScrollEnd,
                scope: this
            });
        }
    },

    /**
     * @private
     */
    bindStore: function(newStore, oldStore) {
        if (oldStore) {
            oldStore.un({
                load: this.onStoreLoad,
                beforeload: this.onStoreBeforeLoad,
                scope: this
            });
        }

        if (newStore) {
            newStore.on({
                load: this.onStoreLoad,
                beforeload: this.onStoreBeforeLoad,
                scope: this
            });

//            this.disableDataViewMask(newStore);
        }
    },

    /**
     * @private
     * Removes the List/DataView's loading mask because we show our own in the plugin. The logic here disables the
     * loading mask immediately if the store is autoloading. If it's not autoloading, allow the mask to show the first
     * time the Store loads, then disable it and use the plugin's loading spinner.
     * @param {Ext.data.Store} store The store that is bound to the DataView
     */
    disableDataViewMask: function(store) {
        var list = this.getList();

        if (store.isAutoLoading()) {
            list.setLoadingText(null);
        } else {
            store.on({
                load: {
                    single: true,
                    fn: function() {
                        list.setLoadingText(null);
                    }
                }
            });
        }
    },

    /**
     * @private
     */
    applyLoadTpl: function(config) {
        return (Ext.isObject(config) && config.isTemplate) ? config : new Ext.XTemplate(config);
    },

    /**
     * @private
     */
    applyLoadMoreCmp: function(config) {
        config = Ext.merge(config, {
            html: this.getLoadTpl().apply({
                cssPrefix: Ext.baseCSSPrefix,
                message: this.getLoadMoreText()
            }),
            listeners: {
                tap: {
                    fn: this.loadNextPage,
                    scope: this,
                    element: 'element'
                }
            }
        });

        return Ext.factory(config, Ext.Component, this.getLoadMoreCmp());
    },

    /**
     * @private
     * If we're using autoPaging and detect that the user has scrolled to the bottom, kick off loading of the next page
     */
    onScrollEnd: function(scroller, x, y) {
        if (!this.getLoading() && y >= scroller.maxPosition.y) {
            if (!this.storeFullyLoaded()) {
                this.loadNextPage();
            }
        }
    },

    /**
     * @private
     * Makes sure we add/remove the loading CSS class while the Store is loading
     */
    updateLoading: function(isLoading) {
        var loadMoreCmp = this.getLoadMoreCmp(),
            loadMoreCls = this.getLoadingCls();

        if (isLoading) {
            loadMoreCmp.addCls(loadMoreCls);
        } else {
            loadMoreCmp.removeCls(loadMoreCls);
        }
    },

    /**
     * @private
     * If the Store is just about to load but it's currently empty, we hide the load more button because this is
     * usually an outcome of setting a new Store on the List so we don't want the load more button to flash while
     * the new Store loads
     */
    onStoreBeforeLoad: function(store) {
        if (store.getCount() === 0) {
            this.getLoadMoreCmp().hide();
        }
    },

    /**
     * @private
     */
    onStoreLoad: function(store) {
        var loadCmp  = this.addLoadMoreCmp(),
            template = this.getLoadTpl(),
            message  = this.storeFullyLoaded() ? this.getNoMoreRecordsText() : this.getLoadMoreText();

        this.getLoadMoreCmp().show();
        this.setLoading(false);

        //restores scroll position after a Store load
        if (this.scrollY) {
            this.getScroller().scrollTo(null, this.scrollY);
            delete this.scrollY;
        }

        //if we've reached the end of the data set, switch to the noMoreRecordsText
        loadCmp.setHtml(template.apply({
            cssPrefix: Ext.baseCSSPrefix,
            message: message
        }));
    },

    /**
     * @private
     * Because the attached List's inner list element is rendered after our init function is called,
     * we need to dynamically add the loadMoreCmp later. This does this once and caches the result.
     */
    addLoadMoreCmp: function() {
        var list = this.getList(),
            cmp  = this.getLoadMoreCmp();

        if (!this.getLoadMoreCmpAdded()) {
            list.add(cmp);
            this.setLoadMoreCmpAdded(true);
        }

        return cmp;
    },

    /**
     * @private
     * Returns true if the Store is detected as being fully loaded, or the server did not return a total count, which
     * means we're in 'infinite' mode
     * @return {Boolean}
     */
    storeFullyLoaded: function() {
        var store = this.getList().getStore(),
            total = store.getTotalCount();

        return total !== null ? store.getTotalCount() <= (store.currentPage * store.getPageSize()) : false;
    },

    /**
     * @private
     */
    loadNextPage: function() {
        var store = this.getList().getStore();

        this.setLoading(true);

        //keep a cache of the current scroll position as we'll need to reset it after the List is
        //updated with new data
        this.scrollY = this.getScroller().position.y;

        store.nextPage({ addRecords: true });
    }
});

/**
 * This plugin adds pull to refresh functionality to the List.
 *
 * ## Example
 *
 *     @example
 *     var store = Ext.create('Ext.data.Store', {
 *         fields: ['name', 'img', 'text'],
 *         data: [
 *             {
 *                 name: 'ariyahidayat',
 *                 img: 'https://si0.twimg.com/profile_images/1662373732/headshot_normal.jpg',
 *                 text: 'Read about the first exciting year of #PhantomJS development'
 *             },{
 *                 name: 'whereisthysting',
 *                 img: 'https://si0.twimg.com/profile_images/1628080808/Brendan_Coughran_normal.jpg',
 *                 text: 'The power of canvas http://glittle.org/blog/smiley-slider/'
 *             },{
 *                 name: 'philogb',
 *                 img: 'https://si0.twimg.com/profile_images/1249073521/ng_normal.png',
 *                 text: '@ikarienator nice burritos!'
 *             }
 *         ]
 *     });
 * 
 *     Ext.create('Ext.dataview.List', {
 *         fullscreen: true,
 *
 *         store: store,
 *
 *         plugins: [
 *             {
 *                 xclass: 'Ext.plugin.PullRefresh',
 *                 pullRefreshText: 'Pull down for more new Tweets!'
 *             }
 *         ],
 *
 *         itemTpl: [
 *             '<img src="{img}" alt='{name} photo' />',
 *             '<div class="tweet"><b>{name}:</b> {text}</div>'
 *         ]
 *     });
 */
Ext.define('Ext.plugin.PullRefresh', {
    extend: 'Ext.Component',
    alias: 'plugin.pullrefresh',
    requires: ['Ext.DateExtras'],

    config: {
        /*
         * @accessor
         */
        list: null,

        /*
         * @cfg {String} pullRefreshText The text that will be shown while you are pulling down.
         * @accessor
         */
        pullRefreshText: 'Pull down to refresh...',

        /*
         * @cfg {String} releaseRefreshText The text that will be shown after you have pulled down enough to show the release message.
         * @accessor
         */
        releaseRefreshText: 'Release to refresh...',

        /*
         * @cfg {String} loadingText The text that will be shown while the list is refreshing.
         * @accessor
         */
        loadingText: 'Loading...',

        /*
         * @cfg {Number} snappingAnimationDuration The duration for snapping back animation after the data has been refreshed
         * @accessor
         */
        snappingAnimationDuration: 150,

        /*
         * @cfg {Function} refreshFn The function that will be called to refresh the list.
         * If this is not defined, the store's load function will be called.
         * The refresh function gets called with a reference to this plugin instance.
         * @accessor
         */
        refreshFn: null,

        /*
         * @cfg {XTemplate/String/Array} pullTpl The template being used for the pull to refresh markup.
         * @accessor
         */
        pullTpl: [
            '<div class="x-list-pullrefresh">',
                '<div class="x-list-pullrefresh-arrow"></div>',
                '<div class="x-loading-spinner">',
                    '<span class="x-loading-top"></span>',
                    '<span class="x-loading-right"></span>',
                    '<span class="x-loading-bottom"></span>',
                    '<span class="x-loading-left"></span>',
                '</div>',
                '<div class="x-list-pullrefresh-wrap">',
                    '<h3 class="x-list-pullrefresh-message">{message}</h3>',
                    '<div class="x-list-pullrefresh-updated">Last Updated: <span>{lastUpdated:date("m/d/Y h:iA")}</span></div>',
                '</div>',
            '</div>'
        ].join('')
    },

    isRefreshing: false,
    currentViewState: '',

    initialize: function() {
        this.callParent();

        this.on({
            painted: 'onPainted',
            scope: this
        });
    },

    init: function(list) {
        var me = this,
            store = list.getStore(),
            pullTpl = me.getPullTpl(),
            element = me.element,
            scroller = list.getScrollable().getScroller();

        me.setList(list);
        me.lastUpdated = new Date();

        list.insert(0, me);

        // We provide our own load mask so if the Store is autoLoading already disable the List's mask straight away,
        // otherwise if the Store loads later allow the mask to show once then remove it thereafter
        if (store) {
            if (store.isAutoLoading()) {
                list.setLoadingText(null);
            } else {
                store.on({
                    load: {
                        single: true,
                        fn: function() {
                            list.setLoadingText(null);
                        }
                    }
                });
            }
        }

        pullTpl.overwrite(element, {
            message: me.getPullRefreshText(),
            lastUpdated: me.lastUpdated
        }, true);

        me.loadingElement = element.getFirstChild();
        me.messageEl = element.down('.x-list-pullrefresh-message');
        me.updatedEl = element.down('.x-list-pullrefresh-updated > span');

        me.maxScroller = scroller.getMaxPosition();

        scroller.on({
            maxpositionchange: me.setMaxScroller,
            scroll: me.onScrollChange,
            scope: me
        });
    },

    /**
     * @private
     * Attempts to load the newest posts via the attached List's Store's Proxy
     */
    fetchLatest: function() {
        var store = this.getList().getStore(),
            proxy = store.getProxy(),
            operation;

        operation = Ext.create('Ext.data.Operation', {
            page: 1,
            start: 0,
            model: store.getModel(),
            limit: store.getPageSize(),
            action: 'read',
            filters: store.getRemoteFilter() ? store.getFilters() : []
        });

        proxy.read(operation, this.onLatestFetched, this);
    },

    /**
     * @private
     * Called after fetchLatest has finished grabbing data. Matches any returned records against what is already in the
     * Store. If there is an overlap, updates the existing records with the new data and inserts the new items at the
     * front of the Store. If there is no overlap, insert the new records anyway and record that there's a break in the
     * timeline between the new and the old records.
     */
    onLatestFetched: function(operation) {
        var store      = this.getList().getStore(),
            oldRecords = store.getData(),
            newRecords = operation.getRecords(),
            length     = newRecords.length,
            toInsert   = [],
            newRecord, oldRecord, i;

        for (i = 0; i < length; i++) {
            newRecord = newRecords[i];
            oldRecord = oldRecords.getByKey(newRecord.getId());

            if (oldRecord) {
                oldRecord.set(newRecord.getData());
            } else {
                toInsert.push(newRecord);
            }

            oldRecord = undefined;
        }

        store.insert(0, toInsert);
    },

    onPainted: function() {
        this.pullHeight = this.loadingElement.getHeight();
    },

    setMaxScroller: function(scroller, position) {
        this.maxScroller = position;
    },

    onScrollChange: function(scroller, x, y) {
        if (y < 0) {
            this.onBounceTop(y);
        }
        if (y > this.maxScroller.y) {
            this.onBounceBottom(y);
        }
    },

    /**
     * @private
     */
    applyPullTpl: function(config) {
        return (Ext.isObject(config) && config.isTemplate) ? config : new Ext.XTemplate(config);
    },

    onBounceTop: function(y) {
        var me = this,
            list = me.getList(),
            scroller = list.getScrollable().getScroller();

        if (!me.isReleased) {
            if (!me.isRefreshing && -y >= me.pullHeight + 10) {
                me.isRefreshing = true;

                me.setViewState('release');

                scroller.getContainer().onBefore({
                    dragend: 'onScrollerDragEnd',
                    single: true,
                    scope: me
                });
            }
            else if (me.isRefreshing && -y < me.pullHeight + 10) {
                me.isRefreshing = false;
                me.setViewState('pull');
            }
        }
    },

    onScrollerDragEnd: function() {
        var me = this;

        if (me.isRefreshing) {
            var list = me.getList(),
                scroller = list.getScrollable().getScroller();

            scroller.minPosition.y = -me.pullHeight;
            scroller.on({
                scrollend: 'loadStore',
                single: true,
                scope: me
            });

            me.isReleased = true;
        }
    },

    loadStore: function() {
        var me = this,
            list = me.getList(),
            scroller = list.getScrollable().getScroller();

        me.setViewState('loading');
        me.isReleased = false;

        Ext.defer(function() {
            scroller.on({
                scrollend: function() {
                    if (me.getRefreshFn()) {
                        me.getRefreshFn().call(me, me);
                    } else {
                        me.fetchLatest();
                    }
                    me.resetRefreshState();
                },
                delay: 100,
                single: true,
                scope: me
            });
            scroller.minPosition.y = 0;
            scroller.scrollTo(null, 0, true);
        }, 500, me);
    },

    onBounceBottom: Ext.emptyFn,

    setViewState: function(state) {
        var me = this,
            prefix = Ext.baseCSSPrefix,
            messageEl = me.messageEl,
            loadingElement = me.loadingElement;

        if (state === me.currentViewState) {
            return me;
        }
        me.currentViewState = state;

        if (messageEl && loadingElement) {
            switch (state) {
                case 'pull':
                    messageEl.setHtml(me.getPullRefreshText());
                    loadingElement.removeCls([prefix + 'list-pullrefresh-release', prefix + 'list-pullrefresh-loading']);
                break;

                case 'release':
                    messageEl.setHtml(me.getReleaseRefreshText());
                    loadingElement.addCls(prefix + 'list-pullrefresh-release');
                break;

                case 'loading':
                    messageEl.setHtml(me.getLoadingText());
                    loadingElement.addCls(prefix + 'list-pullrefresh-loading');
                break;
            }
        }

        return me;
    },

    resetRefreshState: function() {
        var me = this;

        me.isRefreshing = false;
        me.lastUpdated = new Date();

        me.setViewState('pull');
        me.updatedEl.setHtml(Ext.util.Format.date(me.lastUpdated, "m/d/Y h:iA"));
    }
});

/**
 * @private
 */
Ext.define('Ext.scroll.indicator.Abstract', {
    extend: 'Ext.Component',

    config: {
        baseCls: 'x-scroll-indicator',

        axis: 'x',

        value: 0,

        length: null,

        hidden: true,

        ui: 'dark'
    },

    cachedConfig: {
        ratio: 1,

        barCls: 'x-scroll-bar',

        active: true
    },

    barElement: null,

    barLength: 0,

    gapLength: 0,

    getElementConfig: function() {
        return {
            reference: 'barElement',
            children: [this.callParent()]
        };
    },

    applyRatio: function(ratio) {
        if (isNaN(ratio)) {
            ratio = 1;
        }

        return ratio;
    },

    refresh: function() {
        var bar = this.barElement,
            barDom = bar.dom,
            ratio = this.getRatio(),
            axis = this.getAxis(),
            barLength = (axis === 'x') ? barDom.offsetWidth : barDom.offsetHeight,
            length = barLength * ratio;

        this.barLength = barLength;

        this.gapLength = barLength - length;

        this.setLength(length);

        this.updateValue(this.getValue());
    },

    updateBarCls: function(barCls) {
        this.barElement.addCls(barCls);
    },

    updateAxis: function(axis) {
        this.element.addCls(this.getBaseCls(), null, axis);
        this.barElement.addCls(this.getBarCls(), null, axis);
    },

    updateValue: function(value) {
        this.setOffset(this.gapLength * value);
    },

    updateActive: function(active) {
        this.barElement[active ? 'addCls' : 'removeCls']('active');
    },

    doSetHidden: function(hidden) {
        var elementDomStyle = this.element.dom.style;

        if (hidden) {
            elementDomStyle.opacity = '0';
        }
        else {
            elementDomStyle.opacity = '';
        }
    },

    updateLength: function(length) {
        var axis = this.getAxis(),
            element = this.element;

        if (axis === 'x') {
            element.setWidth(length);
        }
        else {
            element.setHeight(length);
        }
    },

    setOffset: function(offset) {
        var axis = this.getAxis(),
            element = this.element;

        if (axis === 'x') {
            element.setLeft(offset);
        }
        else {
            element.setTop(offset);
        }
    }
});

/**
 * @private
 */
Ext.define('Ext.scroll.indicator.CssTransform', {
    extend: 'Ext.scroll.indicator.Abstract',

    config: {
        cls: 'csstransform'
    },

    getElementConfig: function() {
        var config = this.callParent();

        config.children[0].children = [
            {
                reference: 'startElement'
            },
            {
                reference: 'middleElement'
            },
            {
                reference: 'endElement'
            }
        ];

        return config;
    },

    refresh: function() {
        var axis = this.getAxis(),
            startElementDom = this.startElement.dom,
            endElementDom = this.endElement.dom,
            middleElement = this.middleElement,
            startElementLength, endElementLength;

        if (axis === 'x') {
            startElementLength = startElementDom.offsetWidth;
            endElementLength = endElementDom.offsetWidth;
            middleElement.setLeft(startElementLength);
        }
        else {
            startElementLength = startElementDom.offsetHeight;
            endElementLength = endElementDom.offsetHeight;
            middleElement.setTop(startElementLength);
        }

        this.startElementLength = startElementLength;
        this.endElementLength = endElementLength;
        this.minLength = startElementLength + endElementLength;

        this.callParent();
    },

    applyLength: function(length) {
        return Math.round(Math.max(this.minLength, length));
    },

    updateLength: function(length) {
        var axis = this.getAxis(),
            endElementStyle = this.endElement.dom.style,
            middleElementStyle = this.middleElement.dom.style,
            endElementLength = this.endElementLength,
            endElementOffset = length - endElementLength,
            middleElementLength = endElementOffset - this.startElementLength;

        if (axis === 'x') {
            endElementStyle.webkitTransform = 'translate3d(' + endElementOffset + 'px, 0, 0)';
            middleElementStyle.webkitTransform = 'translate3d(0, 0, 0) scaleX(' + middleElementLength + ')';
        }
        else {
            endElementStyle.webkitTransform = 'translate3d(0, ' + endElementOffset + 'px, 0)';
            middleElementStyle.webkitTransform = 'translate3d(0, 0, 0) scaleY(' + middleElementLength + ')';
        }
    },

    updateValue: function(value) {
        var barLength = this.barLength,
            gapLength = this.gapLength,
            length = this.getLength(),
            newLength, offset, extra;

        if (value <= 0) {
            offset = 0;
            this.updateLength(this.applyLength(length + value * barLength));
        }
        else if (value >= 1) {
            extra = Math.round((value - 1) * barLength);
            newLength = this.applyLength(length - extra);
            extra = length - newLength;
            this.updateLength(newLength);
            offset = gapLength + extra;
        }
        else {
            offset = gapLength * value;
        }

        this.setOffset(offset);
    },

    setOffset: function(offset) {
        var axis = this.getAxis(),
            elementStyle = this.element.dom.style;

        offset = Math.round(offset);

        if (axis === 'x') {
            elementStyle.webkitTransform = 'translate3d(' + offset + 'px, 0, 0)';
        }
        else {
            elementStyle.webkitTransform = 'translate3d(0, ' + offset + 'px, 0)';
        }
    }
});

/**
 * @private
 */
Ext.define('Ext.scroll.indicator.Default', {
    extend: 'Ext.scroll.indicator.Abstract',

    config: {
        cls: 'default'
    },

    setOffset: function(offset) {
        var axis = this.getAxis(),
            domStyle = this.element.dom.style;

        if (axis === 'x') {
            domStyle.webkitTransform = 'translate3d(' + offset + 'px, 0, 0)';
        }
        else {
            domStyle.webkitTransform = 'translate3d(0, ' + offset + 'px, 0)';
        }
    },

    applyLength: function(length) {
        return Math.round(Math.max(0, length));
    },

    updateValue: function(value) {
        var barLength = this.barLength,
            gapLength = this.gapLength,
            length = this.getLength(),
            newLength, offset, extra;

        if (value <= 0) {
            offset = 0;
            this.updateLength(this.applyLength(length + value * barLength));
        }
        else if (value >= 1) {
            extra = Math.round((value - 1) * barLength);
            newLength = this.applyLength(length - extra);
            extra = length - newLength;
            this.updateLength(newLength);
            offset = gapLength + extra;
        }
        else {
            offset = gapLength * value;
        }

        this.setOffset(offset);
    }
});

/**
 * @private
 */
Ext.define('Ext.scroll.indicator.ScrollPosition', {
    extend: 'Ext.scroll.indicator.Abstract',

    config: {
        cls: 'scrollposition'
    },

    getElementConfig: function() {
        var config = this.callParent(arguments);

        config.children.unshift({
            className: 'x-scroll-bar-stretcher'
        });

        return config;
    },

    updateValue: function(value) {
        if (this.gapLength === 0) {
            if (value > 1) {
                value = value - 1;
            }

            this.setOffset(this.barLength * value);
        }
        else {
            this.setOffset(this.gapLength * value);
        }
    },

    setLength: function(length) {
        var axis = this.getAxis(),
            scrollOffset = this.barLength,
            barDom = this.barElement.dom,
            element = this.element;

        this.callParent(arguments);

        if (axis === 'x') {
            barDom.scrollLeft = scrollOffset;
            element.setLeft(scrollOffset);
        }
        else {
            barDom.scrollTop = scrollOffset;
            element.setTop(scrollOffset);
        }
    },

    setOffset: function(offset) {
        var axis = this.getAxis(),
            scrollOffset = this.barLength,
            barDom = this.barElement.dom;

        offset = scrollOffset - offset;

        if (axis === 'x') {
            barDom.scrollLeft = offset;
        }
        else {
            barDom.scrollTop = offset;
        }
    }
});

/**
 * @private
 */
Ext.define('Ext.scroll.Indicator', {
    requires: [
        'Ext.scroll.indicator.Default',
        'Ext.scroll.indicator.ScrollPosition',
        'Ext.scroll.indicator.CssTransform'
    ],

    alternateClassName: 'Ext.util.Indicator',

    constructor: function(config) {
        if (Ext.os.is.Android2 || Ext.browser.is.ChromeMobile) {
            return new Ext.scroll.indicator.ScrollPosition(config);
        }
        else if (Ext.os.is.iOS) {
            return new Ext.scroll.indicator.CssTransform(config);
        }
        else {
            return new Ext.scroll.indicator.Default(config);
        }
    }
});

/**
 * This is a simple container that is used to combile content and a {@link Ext.scroll.View} instance. It also
 * provides scroll indicators.
 *
 * 99% of the time all you need to use in this class is {@link #getScroller}.
 *
 * This should never should be extended.
 */
Ext.define('Ext.scroll.View', {
    extend: 'Ext.Evented',

    alternateClassName: 'Ext.util.ScrollView',

    requires: [
        'Ext.scroll.Scroller',
        'Ext.scroll.Indicator'
    ],

    config: {
        /**
         * @cfg {String} indicatorsUi
         * The style of the indicators of this view. Available options are `dark` or `light`.
         */
        indicatorsUi: 'dark',

        element: null,

        scroller: {},

        indicators: {
            x: {
                axis: 'x'
            },
            y: {
                axis: 'y'
            }
        },

        indicatorsHidingDelay: 100,

        cls: Ext.baseCSSPrefix + 'scroll-view'
    },

    /**
     * @method getScroller
     * Returns the scroller instance in this view. Checkout the documentation of {@link Ext.scroll.Scroller} and
     * {@link Ext.Container#getScrollable} for more information.
     * @return {Ext.scroll.View} The scroller
     */

    /**
     * @private
     */
    processConfig: function(config) {
        if (!config) {
            return null;
        }

        if (typeof config == 'string') {
            config = {
                direction: config
            };
        }

        config = Ext.merge({}, config);

        var scrollerConfig = config.scroller,
            name;

        if (!scrollerConfig) {
            config.scroller = scrollerConfig = {};
        }

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

        return config;
    },

    constructor: function(config) {
        config = this.processConfig(config);

        this.useIndicators = { x: true, y: true };

        this.doHideIndicators = Ext.Function.bind(this.doHideIndicators, this);

        this.initConfig(config);
    },

    setConfig: function(config) {
        return this.callParent([this.processConfig(config)]);
    },

    updateIndicatorsUi: function(newUi) {
        var indicators = this.getIndicators();
        indicators.x.setUi(newUi);
        indicators.y.setUi(newUi);
    },

    applyScroller: function(config, currentScroller) {
        return Ext.factory(config, Ext.scroll.Scroller, currentScroller);
    },

    applyIndicators: function(config, indicators) {
        var defaultClass = Ext.scroll.Indicator,
            useIndicators = this.useIndicators;

        if (!config) {
            config = {};
        }

        if (!config.x) {
            useIndicators.x = false;
            config.x = {};
        }

        if (!config.y) {
            useIndicators.y = false;
            config.y = {};
        }

        return {
            x: Ext.factory(config.x, defaultClass, indicators && indicators.x),
            y: Ext.factory(config.y, defaultClass, indicators && indicators.y)
        };
    },

    updateIndicators: function(indicators) {
        this.indicatorsGrid = Ext.Element.create({
            className: 'x-scroll-bar-grid-wrapper',
            children: [{
                className: 'x-scroll-bar-grid',
                children: [
                    {
                        children: [{}, {
                            children: [indicators.y.barElement]
                        }]
                    },
                    {
                        children: [{
                            children: [indicators.x.barElement]
                        }, {}]
                    }
                ]
            }]
        });
    },

    updateScroller: function(scroller) {
        scroller.on({
            scope: this,
            scrollstart: 'onScrollStart',
            scroll: 'onScroll',
            scrollend: 'onScrollEnd',
            refresh: 'refreshIndicators'
        });
    },

    isAxisEnabled: function(axis) {
        return this.getScroller().isAxisEnabled(axis) && this.useIndicators[axis];
    },

    applyElement: function(element) {
        if (element) {
            return Ext.get(element);
        }
    },

    updateElement: function(element) {
        var scrollerElement = element.getFirstChild().getFirstChild(),
            scroller = this.getScroller();

        element.addCls(this.getCls());
        element.insertFirst(this.indicatorsGrid);

        scroller.setElement(scrollerElement);

        this.refreshIndicators();

        return this;
    },

    showIndicators: function() {
        var indicators = this.getIndicators();

        if (this.hasOwnProperty('indicatorsHidingTimer')) {
            clearTimeout(this.indicatorsHidingTimer);
            delete this.indicatorsHidingTimer;
        }

        if (this.isAxisEnabled('x')) {
            indicators.x.show();
        }

        if (this.isAxisEnabled('y')) {
            indicators.y.show();
        }
    },

    hideIndicators: function() {
        var delay = this.getIndicatorsHidingDelay();

        if (delay > 0) {
            this.indicatorsHidingTimer = setTimeout(this.doHideIndicators, delay);
        }
        else {
            this.doHideIndicators();
        }
    },

    doHideIndicators: function() {
        var indicators = this.getIndicators();

        if (this.isAxisEnabled('x')) {
            indicators.x.hide();
        }

        if (this.isAxisEnabled('y')) {
            indicators.y.hide();
        }
    },

    onScrollStart: function() {
        this.onScroll.apply(this, arguments);
        this.showIndicators();
    },

    onScrollEnd: function() {
        this.hideIndicators();
    },

    onScroll: function(scroller, x, y) {
        this.setIndicatorValue('x', x);
        this.setIndicatorValue('y', y);

        if (this.isBenchmarking) {
            this.framesCount++;
        }
    },

    isBenchmarking: false,

    framesCount: 0,

    getCurrentFps: function() {
        var now = Date.now(),
            fps;

        if (!this.isBenchmarking) {
            this.isBenchmarking = true;
            fps = 0;
        }
        else {
            fps = Math.round(this.framesCount * 1000 / (now - this.framesCountStartTime));
        }

        this.framesCountStartTime = now;
        this.framesCount = 0;

        return fps;
    },

    setIndicatorValue: function(axis, scrollerPosition) {
        if (!this.isAxisEnabled(axis)) {
            return this;
        }

        var scroller = this.getScroller(),
            scrollerMaxPosition = scroller.getMaxPosition()[axis],
            scrollerContainerSize = scroller.getContainerSize()[axis],
            value;

        if (scrollerMaxPosition === 0) {
            value = scrollerPosition / scrollerContainerSize;

            if (scrollerPosition >= 0) {
                value += 1;
            }
        }
        else {
            if (scrollerPosition > scrollerMaxPosition) {
                value = 1 + ((scrollerPosition - scrollerMaxPosition) / scrollerContainerSize);
            }
            else if (scrollerPosition < 0) {
                value = scrollerPosition / scrollerContainerSize;
            }
            else {
                value = scrollerPosition / scrollerMaxPosition;
            }
        }

        this.getIndicators()[axis].setValue(value);
    },

    refreshIndicator: function(axis) {
        if (!this.isAxisEnabled(axis)) {
            return this;
        }

        var scroller = this.getScroller(),
            indicator = this.getIndicators()[axis],
            scrollerContainerSize = scroller.getContainerSize()[axis],
            scrollerSize = scroller.getSize()[axis],
            ratio = scrollerContainerSize / scrollerSize;

        indicator.setRatio(ratio);
        indicator.refresh();
    },

    refresh: function() {
        return this.getScroller().refresh();
    },

    refreshIndicators: function() {
        var indicators = this.getIndicators();

        indicators.x.setActive(this.isAxisEnabled('x'));
        indicators.y.setActive(this.isAxisEnabled('y'));

        this.refreshIndicator('x');
        this.refreshIndicator('y');
    },

    destroy: function() {
        var element = this.getElement(),
            indicators = this.getIndicators();

        if (element && !element.isDestroyed) {
            element.removeCls(this.getCls());
        }

        indicators.x.destroy();
        indicators.y.destroy();

        Ext.destroy(this.getScroller(), this.indicatorsGrid);
        delete this.indicatorsGrid;

        this.callParent(arguments);
    }
});

/**
 * @private
 */
Ext.define('Ext.behavior.Scrollable', {

    extend: 'Ext.behavior.Behavior',

    requires: [
        'Ext.scroll.View'
    ],

    constructor: function() {
        this.listeners = {
            painted: 'onComponentPainted',
            scope: this
        };

        this.callParent(arguments);
    },

    onComponentPainted: function() {
        this.scrollView.refresh();
    },

    setConfig: function(config) {
        var scrollView = this.scrollView,
            component = this.component,
            scrollViewElement, scrollContainer, scrollerElement;

        if (config) {
            if (!scrollView) {
                this.scrollView = scrollView = new Ext.scroll.View(config);
                scrollView.on('destroy', 'onScrollViewDestroy', this);

                component.setUseBodyElement(true);

                this.scrollerElement = scrollerElement = component.innerElement;
                this.scrollContainer = scrollContainer = scrollerElement.wrap();
                this.scrollViewElement = scrollViewElement = component.bodyElement;

                scrollView.setElement(scrollViewElement);

                if (component.isPainted()) {
                    this.onComponentPainted(component);
                }

                component.on(this.listeners);
            }
            else if (Ext.isObject(config)) {
                scrollView.setConfig(config);
            }
        }
        else if (scrollView) {
            scrollView.destroy();
        }

        return this;
    },

    getScrollView: function() {
        return this.scrollView;
    },

    onScrollViewDestroy: function() {
        var component = this.component,
            scrollerElement = this.scrollerElement;

        if (!scrollerElement.isDestroyed) {
            this.scrollerElement.unwrap();
        }

        this.scrollContainer.destroy();

        component.un(this.listeners);

        delete this.scrollerElement;
        delete this.scrollView;
        delete this.scrollContainer;
    },

    onComponentDestroy: function() {
        var scrollView = this.scrollView;

        if (scrollView) {
            scrollView.destroy();
        }
    }
});

/**
 * A Container has all of the abilities of {@link Ext.Component Component}, but lets you nest other Components inside
 * it. Applications are made up of lots of components, usually nested inside one another. Containers allow you to
 * render and arrange child Components inside them. Most apps have a single top-level Container called a Viewport,
 * which takes up the entire screen. Inside of this are child components, for example in a mail app the Viewport
 * Container's two children might be a message List and an email preview pane.
 *
 * Containers give the following extra functionality:
 *
 * * Adding child Components at instantiation and run time
 * * Removing child Components
 * * Specifying a [Layout](#!/guide/layouts)
 *
 * Layouts determine how the child Components should be laid out on the screen. In our mail app example we'd use an
 * HBox layout so that we can pin the email list to the left hand edge of the screen and allow the preview pane to
 * occupy the rest. There are several layouts in Sencha Touch 2, each of which help you achieve your desired
 * application structure, further explained in the [Layout guide](#!/guide/layouts).
 *
 * ## Adding Components to Containers
 *
 * As we mentioned above, Containers are special Components that can have child Components arranged by a Layout. One of
 * the code samples above showed how to create a Panel with 2 child Panels already defined inside it but it's easy to
 * do this at run time too:
 *
 *     @example miniphone
 *     //this is the Panel we'll be adding below
 *     var aboutPanel = Ext.create('Ext.Panel', {
 *         html: 'About this app'
 *     });
 *
 *     //this is the Panel we'll be adding to
 *     var mainPanel = Ext.create('Ext.Panel', {
 *         fullscreen: true,
 *
 *         layout: 'hbox',
 *         defaults: {
 *             flex: 1
 *         },
 *
 *         items: {
 *             html: 'First Panel',
 *             style: 'background-color: #5E99CC;'
 *         }
 *     });
 *
 *     //now we add the first panel inside the second
 *     mainPanel.add(aboutPanel);
 *
 * Here we created three Panels in total. First we made the aboutPanel, which we might use to tell the user a little
 * about the app. Then we create one called mainPanel, which already contains a third Panel in its
 * {@link Ext.Container#cfg-items items} configuration, with some dummy text ("First Panel"). Finally, we add the first
 * panel to the second by calling the {@link Ext.Container#method-add add} method on mainPanel.
 *
 * In this case we gave our mainPanel another hbox layout, but we also introduced some
 * {@link Ext.Container#defaults defaults}. These are applied to every item in the Panel, so in this case every child
 * inside mainPanel will be given a flex: 1 configuration. The effect of this is that when we first render the screen
 * only a single child is present inside mainPanel, so that child takes up the full width available to it. Once the
 * mainPanel.add line is called though, the aboutPanel is rendered inside of it and also given a flex of 1, which will
 * cause it and the first panel to both receive half the full width of the mainPanel.
 *
 * Likewise, it's easy to remove items from a Container:
 *
 *     mainPanel.remove(aboutPanel);
 *
 * After this line is run everything is back to how it was, with the first child panel once again taking up the full
 * width inside mainPanel.
 *
 * ## Further Reading
 *
 * See the [Component & Container Guide](#!/guide/components) for more information, and check out the
 * {@link Ext.Container} class docs also.
 */
Ext.define('Ext.Container', {
    extend: 'Ext.Component',

    alternateClassName: 'Ext.lib.Container',

    requires: [
        'Ext.layout.Layout',
        'Ext.ItemCollection',
        'Ext.behavior.Scrollable',
        'Ext.Mask'
    ],

    xtype: 'container',

    /**
     * @event add
     * Fires whenever item added to the Container
     * @param {Ext.Container} this The Container instance
     * @param {Object} item The item added to the Container
     * @param {Number} index The index of the item within the Container
     */

    /**
     * @event remove
     * Fires whenever item removed from the Container
     * @param {Ext.Container} this The Container instance
     * @param {Object} item The item removed from the Container
     * @param {Number} index The index of the item that was removed
     */

    /**
     * @event move
     * Fires whenever item moved within the Container
     * @param {Ext.Container} this The Container instance
     * @param {Object} item The item moved within the Container
     * @param {Number} toIndex The new index of the item
     * @param {Number} fromIndex The old index of the item
     */

    /**
     * @private
     * @event renderedchange
     * Fires whenever an item is rendered into a container or derendered
     * from a Container
     * @param {Ext.Container} this The Container instance
     * @param {Object} item The item in the Container
     * @param {Boolean} rendered The current rendered status of the item
     */

    /**
     * @event activate
     * Fires whenever item within the Container is activated
     * @param {Ext.Container} this The Container instance
     * @param {Object} newActiveItem The new active item within the container
     * @param {Object} oldActiveItem The old active item within the container
     */

    /**
     * @event deactivate
     * Fires whenever item within the Container is deactivated
     * @param {Ext.Container} this The Container instance
     * @param {Object} newActiveItem The new active item within the container
     * @param {Object} oldActiveItem The old active item within the container
     */

    eventedConfig: {
        /**
         * @cfg {Object/Number} activeItem The item from the {@link #cfg-items} collection that will be active first. This is
         * usually only meaningful in a {@link Ext.layout.Card card layout}, where only one item can be active at a
         * time
         * @accessor
         * @evented
         */
        activeItem: 0
    },

    config: {
        /**
         * @cfg {String/Object/Boolean} cardSwitchAnimation
         * @removed 2.0.0 Please use {@link Ext.layout.Card#animation} instead
         */

        /**
         * @cfg {Object/String} layout Configuration for this Container's layout. Example:
         *
         *    Ext.create('Ext.Container', {
         *        layout: {
         *            type: 'hbox',
         *            align: 'middle'
         *        },
         *        items: [
         *            {
         *                xtype: 'panel',
         *                flex: 1,
         *                style: 'background-color: red;'
         *            },
         *            {
         *                xtype: 'panel',
         *                flex: 2,
         *                style: 'background-color: green'
         *            }
         *        ]
         *    });
         *
         * See the layouts guide for more information
         *
         * @accessor
         */
        layout: null,

        /**
         * @cfg {Object} control Enables you to easily control Components inside this Container by listening to their
         * events and taking some action. For example, if we had a container with a nested Disable button, and we
         * wanted to hide the Container when the Disable button is tapped, we could do this:
         *
         *     Ext.create('Ext.Container', {
         *         control: {
         *            'button[text=Disable]': {
         *                tap: 'hideMe'
         *            }
         *         },
         *
         *         hideMe: function() {
         *             this.hide()
         *         }
         *     });
         *
         * We used a {@link Ext.ComponentQuery} selector to listen to the {@link Ext.Button#tap tap} event on any
         * {@link Ext.Button button} anywhere inside the Container that has the {@link Ext.Button#text text} 'Disable'.
         * Whenever a Component matching that selector fires the 'tap' event our hideMe function is called. hideMe is
         * called with scope: this (e.g. 'this' is the Container instance).
         *
         */
        control: {},

        /**
         * @cfg {Object} defaults A set of default configurations to apply to all child Components in this Container.
         * It's often useful to specify defaults when creating more than one items with similar configurations. For
         * example here we can specify that each child is a panel and avoid repeating the xtype declaration for each
         * one:
         *
         *    Ext.create('Ext.Container', {
         *        defaults: {
         *            xtype: 'panel'
         *        },
         *        items: [
         *            {
         *                html: 'Panel 1'
         *            },
         *            {
         *                html: 'Panel 2'
         *            }
         *        ]
         *    });
         *
         * @accessor
         */
        defaults: null,

        /**
         * @cfg {Array/Object} items The child items to add to this Container. This is usually an array of Component
         * configurations or instances, for example:
         *
         *    Ext.create('Ext.Container', {
         *        items: [
         *            {
         *                xtype: 'panel',
         *                html: 'This is an item'
         *            }
         *        ]
         *    });
         * @accessor
         */
        items: null,

        /**
         * @cfg {Boolean} autoDestroy If true, child items will be destroyed as soon as they are {@link #method-remove removed}
         * from this container
         * @accessor
         */
        autoDestroy: true,

        /** @cfg {String} defaultType
         * The default {@link Ext.Component xtype} of child Components to create in this Container when a child item
         * is specified as a raw configuration object, rather than as an instantiated Component.
         * @accessor
         */
        defaultType: null,

        /**
         * @cfg {Boolean/String/Object} scrollable
         * Configuration options to make this Container scrollable. Acceptable values are:
         *
         * * 'horizontal', 'vertical', 'both' to enabling scrolling for that direction.
         * * true/false to explicitly enable/disable scrolling.
         *
         * Alternatively, you can give it an object which is then passed to the scroller instance:
         *
         *     scrollable: {
         *         direction: 'vertical',
         *         directionLock: true
         *     }
         *
         * Please look at the {@link Ext.scroll.Scroller} documentation for more example on how to use this.
         * @accessor
         */
        scrollable: null,

        //@private
        useBodyElement: null,

        /**
         * @cfg {Boolean/Object/Ext.Mask/Ext.LoadMask} masked
         * A configuration to allow you to mask this container.
         * You can optionally pass an object block with and xtype of `loadmask`, and an optional `message` value to
         * display a loading mask. Please refer to the {@link Ext.LoadMask} component to see other configurations.
         *
         *     masked: {
         *         xtype: 'loadmask',
         *         message: 'My message'
         *     }
         *
         * Alternatively, you can just call the setter at any time with true/false to show/hide the mask"
         *
         *     setMasked(true); //show the mask
         *     setMasked(false); //hides the mask
         *
         * There are also two convience methods, {@link #method-mask} and {@link #unmask}, to allow you to mask and unmask
         * this container at any time.
         *
         * Remember, the Ext.Viewport is always a container, so if you want to mask your whole application at anytime,
         * can call:
         *
         *     Ext.Viewport.setMasked({
         *         xtype: 'loadmask',
         *         message: 'Hello'
         *     });
         *
         * @accessor
         */
        masked: null,

        /**
         * @cfg {Boolean} modal True to make this Container modal. This will create a mask underneath the Container
         * that covers its parent and does not allow the user to interact with any other Components until this
         * Container is dismissed
         * @accessor
         */
        modal: null,

        /**
         * @cfg {Boolean} hideOnMaskTap When using a {@link #modal} Component, setting this to true (the default) will
         * hide the modal mask and the Container when the mask is tapped on
         * @accessor
         */
        hideOnMaskTap: null
    },

    isContainer: true,

    delegateListeners: {
        delegate: '> component',
        centeredchange: 'onItemCenteredChange',
        dockedchange: 'onItemDockedChange',
        floatingchange: 'onItemFloatingChange'
    },

    constructor: function(config) {
        var me = this;

        me._items = me.items = new Ext.ItemCollection();
        me.innerItems = [];

        me.onItemAdd = me.onFirstItemAdd;

        me.callParent(arguments);
    },

    getElementConfig: function() {
        return {
            reference: 'element',
            className: 'x-container',
            children: [{
                reference: 'innerElement',
                className: 'x-inner'
            }]
        };
    },

    /**
     * Changes the {@link #masked} configuration when its setter is called, which will convert the value
     * into a proper object/instance of {@link Ext.Mask}/{@link Ext.LoadMask}. If a mask already exists,
     * it will use that instead.
     */
    applyMasked: function(masked, currentMask) {
        currentMask = Ext.factory(masked, Ext.Mask, currentMask);

        if (currentMask) {
            this.add(currentMask);
        }

        return currentMask;
    },

    /**
     * Convience method which calls {@link #setMasked} with a value of true (to show the mask). For additional
     * functionality, call the {@link #setMasked} function direction (See the {@link #masked} configuration documention
     * for more information).
     */
    mask: function(mask) {
        this.setMasked(mask || true);
    },

    /**
     * Convience method which calls {@link #setMasked} with a value of false (to hide the mask). For additional
     * functionality, call the {@link #setMasked} function direction (See the {@link #masked} configuration documention
     * for more information).
     */
    unmask: function() {
        this.setMasked(false);
    },

    applyModal: function(modal, currentMask) {
        if (!modal && !currentMask) {
            return;
        }

        return Ext.factory(modal, Ext.Mask, currentMask);
    },

    updateModal: function(newModal, oldModal) {
        var listeners = {
            painted: 'refreshModalMask',
            erased: 'destroyModalMask'
        };

        if (newModal) {
            this.on(listeners);
            newModal.on('destroy', 'onModalDestroy', this);
            if (this.getTop() === null && this.getBottom() === null && this.getRight() === null && this.getLeft() === null && !this.getCentered()) {
                Ext.Logger.warn("You have specified a modal config on a container that is neither centered nor has any positioning information.  Setting to top and left to 0 to compensate.");
                this.setTop(0);
                this.setLeft(0);
            }

            if (this.isPainted()) {
                this.refreshModalMask();
            }
        }
        else if (oldModal) {
            oldModal.un('destroy', 'onModalDestroy', this);
            this.un(listeners);
        }
    },

    onModalDestroy: function() {
        this.setModal(null);
    },

    refreshModalMask: function() {
        var mask = this.getModal(),
            container = this.getParent();

        if (!this.painted) {
            this.painted = true;

            if (mask) {
                container.insertBefore(mask, this);
                mask.setZIndex(this.getZIndex() - 1);
            }
        }
    },

    destroyModalMask: function() {
        var mask = this.getModal(),
            container = this.getParent();

        if (this.painted) {
            this.painted = false;

            if (mask) {
                container.remove(mask, false);
            }
        }
    },

    updateZIndex: function(zIndex) {
        var modal = this.getModal();

        this.callParent(arguments);

        if (modal) {
            modal.setZIndex(zIndex - 1);
        }
    },

    updateHideOnMaskTap: function(hideOnMaskTap) {
        var modal = this.getModal();

        if (modal) {
            modal[hideOnMaskTap ? 'on' : 'un'].call(modal, 'tap', 'hide', this);
        }
    },

    updateBaseCls: function(newBaseCls, oldBaseCls) {
        var me = this,
            ui = me.getUi();

        if (newBaseCls) {
            this.element.addCls(newBaseCls);
            this.innerElement.addCls(newBaseCls, null, 'inner');

            if (ui) {
                this.element.addCls(newBaseCls, null, ui);
            }
        }

        if (oldBaseCls) {
            this.element.removeCls(oldBaseCls);
            this.innerElement.removeCls(newBaseCls, null, 'inner');

            if (ui) {
                this.element.removeCls(oldBaseCls, null, ui);
            }
        }
    },

    updateUseBodyElement: function(useBodyElement) {
        if (useBodyElement) {
            this.bodyElement = this.innerElement.wrap({
                cls: 'x-body'
            });
            this.referenceList.push('bodyElement');
        }
    },

    applyItems: function(items, collection) {
        if (items) {
            this.getDefaultType();
            this.getDefaults();

            if (this.initialized && collection.length > 0) {
                this.removeAll();
            }

            this.add(items);
        }
    },

    /**
     * @private
     */
     applyControl: function(selectors) {
         var selector, key, listener, listeners;

         for (selector in selectors) {
             listeners = selectors[selector];

             for (key in listeners) {
                 listener = listeners[key];

                 if (Ext.isObject(listener)) {
                     listener.delegate = selector;
                 }
             }

             listeners.delegate = selector;

             this.addListener(listeners);
         }

         return selectors;
     },

    /**
     * Initialize layout and event listeners the very first time an item is added
     * @private
     */
    onFirstItemAdd: function() {
        delete this.onItemAdd;

        this.setLayout(new Ext.layout.Layout(this, this.getLayout() || 'default'));

        if (this.innerHtmlElement && !this.getHtml()) {
            this.innerHtmlElement.destroy();
            delete this.innerHtmlElement;
        }

        this.on(this.delegateListeners);

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

    updateLayout: function(newLayout, oldLayout) {
        if (oldLayout && oldLayout.isLayout) {
            Ext.Logger.error('Replacing a layout after one has already been initialized is not currently supported.');
        }
    },

    updateDefaultType: function(defaultType) {
        // Cache the direct reference to the default item class here for performance
        this.defaultItemClass = Ext.ClassManager.getByAlias('widget.' + defaultType);

        if (!this.defaultItemClass) {
            Ext.Logger.error("Invalid defaultType of: '" + defaultType + "', must be a valid component xtype");
        }
    },

    applyDefaults: function(defaults) {
        if (defaults) {
            this.factoryItem = this.factoryItemWithDefaults;
            return defaults;
        }
    },

    factoryItem: function(item) {
        if (!item) {
            Ext.Logger.error("Invalid item given: " + item + ", must be either the config object to factory a new item, " +
                "or an existing component instance");
        }

        return Ext.factory(item, this.defaultItemClass);
    },

    factoryItemWithDefaults: function(item) {
        if (!item) {
            Ext.Logger.error("Invalid item given: " + item + ", must be either the config object to factory a new item, " +
                "or an existing component instance");
        }

        var me = this,
            defaults = me.getDefaults(),
            instance;

        if (!defaults) {
            return Ext.factory(item, me.defaultItemClass);
        }

        // Existing instance
        if (item.isComponent) {
            instance = item;

            // Apply defaults only if this is not already an item of this container
            if (defaults && item.isInnerItem() && !me.has(instance)) {
                instance.setConfig(defaults, true);
            }
        }
        // Config object
        else {
            if (defaults && !item.ignoreDefaults) {
                // Note:
                // - defaults is only applied to inner items
                // - we merge the given config together with defaults into a new object so that the original object stays intact
                if (!(
                        item.hasOwnProperty('left') &&
                        item.hasOwnProperty('right') &&
                        item.hasOwnProperty('top') &&
                        item.hasOwnProperty('bottom') &&
                        item.hasOwnProperty('docked') &&
                        item.hasOwnProperty('centered')
                    )) {
                    item = Ext.mergeIf({}, item, defaults);
                }
            }

            instance = Ext.factory(item, me.defaultItemClass);
        }

        return instance;
    },

    /**
     * Adds one or more Components to this Container. Example:
     *
     *    var myPanel = Ext.create('Ext.Panel', {
     *        html: 'This will be added to a Container'
     *    });
     *
     *    myContainer.add([myPanel])
     *
     * @param {Array} newItems The new items to add to the Container
     */
    add: function(newItems) {
        var me = this,
            i, ln, item, newActiveItem;

        newItems = Ext.Array.from(newItems);

        ln = newItems.length;

        for (i = 0; i < ln; i++) {
            item = me.factoryItem(newItems[i]);
            this.doAdd(item);
            if (!newActiveItem && !this.getActiveItem() && this.innerItems.length > 0 && item.isInnerItem()) {
                newActiveItem = item;
            }
        }
        if (newActiveItem) {
            this.setActiveItem(newActiveItem);
        }

        return item;
    },

    /**
     * @private
     * @param item
     */
    doAdd: function(item) {
        var me = this,
            items = me.getItems(),
            index;

        if (!items.has(item)) {
            index = items.length;
            items.add(item);

            if (item.isInnerItem()) {
                me.insertInner(item);
            }

            item.setParent(me);

            me.onItemAdd(item, index);
        }
    },

    /**
     * Removes an item from this Container, optionally destroying it
     * @param {Object} item The item to remove
     * @param {Boolean} destroy Calls the Component's {@link Ext.Component#destroy destroy} method if true
     * @return {Ext.Component} this
     */
    remove: function(item, destroy) {
        var me = this,
            index = me.indexOf(item),
            innerItems = this.getInnerItems(),
            innerIndex;

        if (destroy === undefined) {
            destroy = me.getAutoDestroy();
        }

        if (index !== -1) {
            if (!this.removingAll && innerItems.length > 1 && item === this.getActiveItem()) {
                this.on({
                    activeitemchange: 'doRemove',
                    scope: this,
                    single: true,
                    order: 'after',
                    args: [item, index, destroy]
                });

                innerIndex = innerItems.indexOf(item);

                if (innerIndex === 0) {
                    this.setActiveItem(1);
                }
                else {
                    this.setActiveItem(0);
                }
            }
            else {
                this.doRemove(item, index, destroy);
            }
        }

        return me;
    },

    doRemove: function(item, index, destroy) {
        var me = this;

        me.items.remove(item);

        if (item.isInnerItem()) {
            me.removeInner(item);
        }

        me.onItemRemove(item, index, destroy);

        item.setParent(null);

        if (destroy) {
            item.destroy();
        }
    },

    /**
     * Removes all items currently in the Container, optionally destroying them all
     * @param {Boolean} destroy If true, {@link Ext.Component#destroy destroys} each removed Component
     * @param {Boolean} everything If true, completely remove all items including docked / centered and floating items
     * @return {Ext.Component} this
     */
    removeAll: function(destroy, everything) {
        var items = this.items,
            ln = items.length,
            i = 0,
            item;

        if (destroy === undefined) {
            destroy = this.getAutoDestroy();
        }

        everything = Boolean(everything);

        // removingAll flag is used so we don't unnecessarily change activeItem while removing all items.
        this.removingAll = true;

        for (; i < ln; i++) {
            item = items.getAt(i);

            if (item && (everything || item.isInnerItem())) {
                this.doRemove(item, i, destroy);

                i--;
                ln--;
            }
        }

        this.removingAll = false;

        return this;
    },

    /**
     * Returns the Component for a given index in the Container's {@link #property-items}
     * @param {Number} index The index of the Component to return
     * @return {Ext.Component} The item at the specified index, if found
     */
    getAt: function(index) {
        return this.items.getAt(index);
    },

    /**
     * Removes the Component at the specified index:
     *
     *     myContainer.removeAt(0); //removes the first item
     *
     * @param {Number} index The index of the Component to remove
     */
    removeAt: function(index) {
        var item = this.getAt(index);

        if (item) {
            this.remove(item);
        }

        return this;
    },

    /**
     * Removes an inner Component at the specified index:
     *
     *     myContainer.removeInnerAt(0); //removes the first item of the innerItems propety
     *
     * @param {Number} index The index of the Component to remove
     */
    removeInnerAt: function(index) {
        var item = this.getInnerItems()[index];

        if (item) {
            this.remove(item);
        }

        return this;
    },

    /**
     * @private
     */
    has: function(item) {
        return this.getItems().indexOf(item) != -1;
    },

    /**
     * @private
     */
    hasInnerItem: function(item) {
        return this.innerItems.indexOf(item) != -1;
    },

    /**
     * @private
     */
    indexOf: function(item) {
        return this.getItems().indexOf(item);
    },

    /**
     * @private
     * @param item
     * @param index
     */
    insertInner: function(item, index) {
        var items = this.getItems().items,
            innerItems = this.innerItems,
            currentInnerIndex = innerItems.indexOf(item),
            newInnerIndex = -1,
            nextSibling;

        if (currentInnerIndex !== -1) {
            innerItems.splice(currentInnerIndex, 1);
        }

        if (typeof index == 'number') {
            do {
                nextSibling = items[++index];
            } while (nextSibling && !nextSibling.isInnerItem());

            if (nextSibling) {
                newInnerIndex = innerItems.indexOf(nextSibling);
                innerItems.splice(newInnerIndex, 0, item);
            }
        }

        if (newInnerIndex === -1) {
            innerItems.push(item);
            newInnerIndex = innerItems.length - 1;
        }

        if (currentInnerIndex !== -1) {
            this.onInnerItemMove(item, newInnerIndex, currentInnerIndex);
        }

        return this;
    },

    onInnerItemMove: Ext.emptyFn,

    /**
     * @private
     * @param item
     */
    removeInner: function(item) {
        Ext.Array.remove(this.innerItems, item);

        return this;
    },

    /**
     * Adds a child Component at the given index. For example, here's how we can add a new item, making it the first
     * child Component of this Container:
     *
     *     myContainer.insert(0, {xtype: 'panel', html: 'new item'});
     *
     * @param {Number} index The index to insert the Component at
     * @param {Object} item The Component to insert
     */
    insert: function(index, item) {
        var me = this,
            i;

        if (Ext.isArray(item)) {
            for (i = item.length - 1; i >= 0; i--) {
                me.insert(index, item[i]);
            }

            return me;
        }

        item = this.factoryItem(item);

        this.doInsert(index, item);

        return item;
    },

    /**
     * @private
     * @param index
     * @param item
     */
    doInsert: function(index, item) {
        var me = this,
            items = me.items,
            itemsLength = items.length,
            currentIndex, isInnerItem;

        isInnerItem = item.isInnerItem();

        if (index > itemsLength) {
            index = itemsLength;
        }

        if (items[index - 1] === item) {
            return me;
        }

        currentIndex = me.indexOf(item);

        if (currentIndex !== -1) {
            if (currentIndex < index) {
                index -= 1;
            }

            items.removeAt(currentIndex);
        }
        else {
            item.setParent(me);
        }

        items.insert(index, item);

        if (isInnerItem) {
            me.insertInner(item, index);
        }

        if (currentIndex !== -1) {
            me.onItemMove(item, index, currentIndex);
        }
        else {
            me.onItemAdd(item, index);
        }
    },

    /**
     * @private
     */
    insertFirst: function(item) {
        return this.insert(0, item);
    },

    /**
     * @private
     */
    insertLast: function(item) {
        return this.insert(this.getItems().length, item);
    },

    /**
     * @private
     */
    insertBefore: function(item, relativeToItem) {
        var index = this.indexOf(relativeToItem);

        if (index !== -1) {
            this.insert(index, item);
        }
        return this;
    },

    /**
     * @private
     */
    insertAfter: function(item, relativeToItem) {
        var index = this.indexOf(relativeToItem);

        if (index !== -1) {
            this.insert(index + 1, item);
        }
        return this;
    },

    /**
     * @private
     */
    onItemAdd: function(item, index) {
        this.doItemLayoutAdd(item, index);

        if (this.initialized) {
            this.fireEvent('add', this, item, index);
        }
    },

    doItemLayoutAdd: function(item, index) {
        var layout = this.getLayout();

        if (this.isRendered() && item.setRendered(true)) {
            item.fireAction('renderedchange', [this, item, true], 'onItemAdd', layout, { args: [item, index] });
        }
        else {
            layout.onItemAdd(item, index);
        }
    },

    /**
     * @private
     */
    onItemRemove: function(item, index) {
        this.doItemLayoutRemove(item, index);

        this.fireEvent('remove', this, item, index);
    },

    doItemLayoutRemove: function(item, index) {
        var layout = this.getLayout();

        if (this.isRendered() && item.setRendered(false)) {
            item.fireAction('renderedchange', [this, item, false], 'onItemRemove', layout, { args: [item, index] });
        }
        else {
            layout.onItemRemove(item, index);
        }
    },

    /**
     * @private
     */
    onItemMove: function(item, toIndex, fromIndex) {
        if (item.isDocked()) {
            item.setDocked(null);
        }

        this.doItemLayoutMove(item, toIndex, fromIndex);

        this.fireEvent('move', this, item, toIndex, fromIndex);
    },

    doItemLayoutMove: function(item, toIndex, fromIndex) {
        this.getLayout().onItemMove(item, toIndex, fromIndex);
    },

    /**
     * @private
     */
    onItemCenteredChange: function(item, centered) {
        if (!item.isFloating() && !item.isDocked()) {
            if (centered) {
                this.removeInner(item);
            }
            else {
                this.insertInner(item, this.indexOf(item));
            }
        }

        this.getLayout().onItemCenteredChange(item, centered);
    },

    /**
     * @private
     */
    onItemFloatingChange: function(item, floating) {
        if (!item.isCentered() && !item.isDocked()) {
            if (floating) {
                this.removeInner(item);
            }
            else {
                this.insertInner(item, this.indexOf(item));
            }
        }

        this.getLayout().onItemFloatingChange(item, floating);
    },

    /**
     * @private
     */
    onItemDockedChange: function(item, docked, oldDocked) {
        if (!item.isCentered() && !item.isFloating()) {
            if (docked) {
                this.removeInner(item);
            }
            else {
                this.insertInner(item, this.indexOf(item));
            }
        }

        this.getLayout().onItemDockedChange(item, docked, oldDocked);
    },

    /**
     * Returns all inner {@link #property-items} of this container. `inner` means that the item is not `docked` or
     * `floating`.
     * @return {Array} The inner items of this container.
     */
    getInnerItems: function() {
        return this.innerItems;
    },

    /**
     * Returns all the {@link Ext.Component#docked} items in this container.
     * @return {Array} The docked items of this container
     */
    getDockedItems: function() {
        var items = this.getItems().items,
            dockedItems = [],
            ln = items.length,
            item, i;

        for (i = 0; i < ln; i++) {
            item = items[i];
            if (item.isDocked()) {
                dockedItems.push(item);
            }
        }

        return dockedItems;
    },

    /**
     * @private
     */
    applyActiveItem: function(activeItem, currentActiveItem) {
        var innerItems = this.getInnerItems();

        // Make sure the items are already initialized
        this.getItems();

        if (typeof activeItem == 'number') {
            activeItem = Math.max(0, Math.min(activeItem, innerItems.length - 1));
            activeItem = innerItems[activeItem];

            if (activeItem) {
                return activeItem;
            }
            else if (currentActiveItem) {
                return null;
            }
        }
        else if (activeItem) {
            if (!activeItem.isComponent) {
                activeItem = this.factoryItem(activeItem);
            }

            if (!activeItem.isInnerItem()) {
                Ext.Logger.error("Setting activeItem to be a non-inner item");
            }

            if (!this.has(activeItem)) {
                this.add(activeItem);
            }

            return activeItem;
        }
    },

    /**
     * Animates to the supplied activeItem with a specified animation. Currently this only works
     * with a Card layout.  This passed animation will override any default animations on the
     * container, for a single card switch. The animation will be destroyed when complete.
     * @param {Object/Number} activeItem The item or item index to make active
     * @param {Object/Ext.fx.layout.Card} animation Card animation configuration or instance
     */
    animateActiveItem: function(activeItem, animation) {
        var layout = this.getLayout(),
            defaultAnimation;

        if (this.activeItemAnimation) {
            this.activeItemAnimation.destroy();
        }
        this.activeItemAnimation = animation = new Ext.fx.layout.Card(animation);
        if (animation && layout.isCard) {
            animation.setLayout(layout);
            defaultAnimation = layout.getAnimation();
            if (defaultAnimation) {
                defaultAnimation.disable();
                animation.on('animationend', function() {
                    defaultAnimation.enable();
                    animation.destroy();
                }, this);
            }
        }
        return this.setActiveItem(activeItem);
    },

    /**
     * @private
     */
    doSetActiveItem: function(newActiveItem, oldActiveItem) {
        if (oldActiveItem) {
            oldActiveItem.fireEvent('deactivate', oldActiveItem, this, newActiveItem);
        }

        if (newActiveItem) {
            newActiveItem.fireEvent('activate', newActiveItem, this, oldActiveItem);
        }
    },

    /**
     * @private
     */
    setRendered: function(rendered) {
        if (this.callParent(arguments)) {
            var items = this.items.items,
                i, ln;

            for (i = 0,ln = items.length; i < ln; i++) {
                items[i].setRendered(rendered);
            }

            return true;
        }

        return false;
    },

    /**
     * @private
     */
    getScrollableBehavior: function() {
        var behavior = this.scrollableBehavior;

        if (!behavior) {
            behavior = this.scrollableBehavior = new Ext.behavior.Scrollable(this);
        }

        return behavior;
    },

    /**
     * @private
     */
    applyScrollable: function(config) {
        this.getScrollableBehavior().setConfig(config);
    },

    /**
     * Returns an the scrollable instance for this container, which is a {@link Ext.scroll.View} class.
     *
     * Please checkout the documentation for {@link Ext.scroll.View}, {@link Ext.scroll.View#getScroller}
     * and {@link Ext.scroll.Scroller} for more information.
     * @return {Ext.scroll.View} The scroll view
     */
    getScrollable: function() {
        return this.getScrollableBehavior().getScrollView();
    },

    // Used by ComponentQuery to retrieve all of the items
    // which can potentially be considered a child of this Container.
    // This should be overriden by components which have child items
    // that are not contained in items. For example dockedItems, menu, etc
    // @private
    getRefItems: function(deep) {
        var items = this.getItems().items.slice(),
            ln = items.length,
            i, item;

        if (deep) {
            for (i = 0; i < ln; i++) {
                item = items[i];

                if (item.getRefItems) {
                    items = items.concat(item.getRefItems(true));
                }
            }
        }

        return items;
    },

    /**
     * Examines this container's <code>{@link #property-items}</code> <b>property</b>
     * and gets a direct child component of this container.
     * @param {String/Number} component This parameter may be any of the following:
     * <div><ul class="mdetail-params">
     * <li>a <b><code>String</code></b> : representing the <code>itemId</code>
     * or <code>{@link Ext.Component#getId id}</code> of the child component </li>
     * <li>a <b><code>Number</code></b> : representing the position of the child component
     * within the <code>{@link #property-items}</code> <b>property</b></li>
     * </ul></div>
     * <p>For additional information see {@link Ext.util.MixedCollection#get}.
     * @return Ext.Component The component (if found).
     */
    getComponent: function(component) {
        if (Ext.isObject(component)) {
            component = component.getItemId();
        }

        return this.getItems().get(component);
    },

    /**
     * Finds a docked item of this container using a reference, id or an index of its location
     * in {@link #getDockedItems}.
     * @param {String/Number} component The id or index of the component to find
     * @return {Ext.Component/Boolean} The docked component, if found
     */
    getDockedComponent: function(component) {
        if (Ext.isObject(component)) {
            component = component.getItemId();
        }

        var dockedItems = this.getDockedItems(),
            ln = dockedItems.length,
            item, i;

        if (Ext.isNumber(component)) {
            return dockedItems[component];
        }

        for (i = 0; i < ln; i++) {
            item = dockedItems[i];
            if (item.id == component) {
                return item;
            }
        }

        return false;
    },

    /**
     * Retrieves all descendant components which match the passed selector.
     * Executes an Ext.ComponentQuery.query using this container as its root.
     * @param {String} selector Selector complying to an Ext.ComponentQuery selector
     * @return {Array} Ext.Component's which matched the selector
     */
    query: function(selector) {
        return Ext.ComponentQuery.query(selector, this);
    },

    /**
     * Retrieves the first direct child of this container which matches the passed selector.
     * The passed in selector must comply with an Ext.ComponentQuery selector.
     * @param {String} selector An Ext.ComponentQuery selector
     * @return Ext.Component
     */
    child: function(selector) {
        return this.query('> ' + selector)[0] || null;
    },

    /**
     * Retrieves the first descendant of this container which matches the passed selector.
     * The passed in selector must comply with an Ext.ComponentQuery selector.
     * @param {String} selector An Ext.ComponentQuery selector
     * @return Ext.Component
     */
    down: function(selector) {
        return this.query(selector)[0] || null;
    },



    destroy: function() {
        var modal = this.getModal();

        if (modal) {
            modal.destroy();
        }

        this.removeAll(true, true);
        Ext.destroy(this.getScrollable(), this.bodyElement);
        this.callParent();
    }

}, function() {
    this.addMember('defaultItemClass', this);

});



/**
 * Panel is a container that has specific functionality and structural components that make it the perfect building
 * block for application-oriented user interfaces.
 *
 * Panels are, by virtue of their inheritance from {@link Ext.Container}, capable of being configured with a {@link
 * Ext.Container#layout layout}, and containing child Components.
 *
 * When either specifying child {@link Ext.Container#cfg-items items} of a Panel, or dynamically {@link Ext.Container#method-add
 * adding} Components to a Panel, remember to consider how you wish the Panel to arrange those child elements, and
 * whether those child elements need to be sized using one of Ext's built-in `**{@link Ext.Container#layout layout}**`
 * schemes.
 *
 * ## Example:
 *
 *     @example miniphone preview
 *     var panel = Ext.create('Ext.Panel', {
 *         fullscreen: true,
 *
 *         items: [
 *             {
 *                 docked: 'top',
 *                 xtype: 'toolbar',
 *                 title: 'Standard Titlebar'
 *             },
 *             {
 *                 docked: 'top',
 *                 xtype: 'toolbar',
 *                 ui   : 'light',
 *                 items: [
 *                     {
 *                         text: 'Test Button'
 *                     }
 *                 ]
 *             }
 *         ],
 *
 *         html: 'Testing'
 *     });
 *
 */
Ext.define('Ext.Panel', {
    extend: 'Ext.Container',
    requires: ['Ext.util.LineSegment'],

    alternateClassName: 'Ext.lib.Panel',

    xtype: 'panel',

    isPanel: true,

    config: {
        baseCls: Ext.baseCSSPrefix + 'panel',

        /**
         * @cfg {Number/Boolean/String} bodyPadding
         * A shortcut for setting a padding style on the body element. The value can either be
         * a number to be applied to all sides, or a normal css string describing padding.
         * @deprecated 2.0.0
         */
        bodyPadding: null,

        /**
         * @cfg {Number/Boolean/String} bodyMargin
         * A shortcut for setting a margin style on the body element. The value can either be
         * a number to be applied to all sides, or a normal css string describing margins.
         * @deprecated 2.0.0
         */
        bodyMargin: null,

        /**
         * @cfg {Number/Boolean/String} bodyBorder
         * A shortcut for setting a border style on the body element. The value can either be
         * a number to be applied to all sides, or a normal css string describing borders.
         * @deprecated 2.0.0
         */
        bodyBorder: null
    },

    getElementConfig: function() {
        var config = this.callParent();

        config.children.push({
            reference: 'tipElement',
            className: 'x-anchor',
            hidden: true
        });

        return config;
    },

    applyBodyPadding: function(bodyPadding) {
        if (bodyPadding === true) {
            bodyPadding = 5;
        }

        bodyPadding = Ext.dom.Element.unitizeBox(bodyPadding);

        return bodyPadding;
    },

    updateBodyPadding: function(newBodyPadding) {
        this.element.setStyle('padding', newBodyPadding);
    },

    applyBodyMargin: function(bodyMargin) {
        if (bodyMargin === true) {
            bodyMargin = 5;
        }

        bodyMargin = Ext.dom.Element.unitizeBox(bodyMargin);

        return bodyMargin;
    },

    updateBodyMargin: function(newBodyMargin) {
        this.element.setStyle('margin', newBodyMargin);
    },

    applyBodyBorder: function(bodyBorder) {
        if (bodyBorder === true) {
            bodyBorder = 1;
        }

        bodyBorder = Ext.dom.Element.unitizeBox(bodyBorder);

        return bodyBorder;
    },

    updateBodyBorder: function(newBodyBorder) {
        this.element.setStyle('border-width', newBodyBorder);
    },

    alignTo: function(component) {
        var tipElement = this.tipElement;

        tipElement.hide();

        if (this.currentTipPosition) {
            tipElement.removeCls('x-anchor-' + this.currentTipPosition);
        }

        this.callParent(arguments);

        var LineSegment = Ext.util.LineSegment,
            alignToElement = component.isComponent ? component.renderElement : component,
            element = this.renderElement,
            alignToBox = alignToElement.getPageBox(),
            box = element.getPageBox(),
            left = box.left,
            top = box.top,
            right = box.right,
            bottom = box.bottom,
            centerX = left + (box.width / 2),
            centerY = top + (box.height / 2),
            leftTopPoint = { x: left, y: top },
            rightTopPoint = { x: right, y: top },
            leftBottomPoint = { x: left, y: bottom },
            rightBottomPoint = { x: right, y: bottom },
            boxCenterPoint = { x: centerX, y: centerY },
            alignToCenterX = alignToBox.left + (alignToBox.width / 2),
            alignToCenterY = alignToBox.top + (alignToBox.height / 2),
            alignToBoxCenterPoint = { x: alignToCenterX, y: alignToCenterY },
            centerLineSegment = new LineSegment(boxCenterPoint, alignToBoxCenterPoint),
            offsetLeft = 0,
            offsetTop = 0,
            tipSize, tipWidth, tipHeight, tipPosition, tipX, tipY;

        tipElement.setVisibility(false);
        tipElement.show();
        tipSize = tipElement.getSize();
        tipWidth = tipSize.width;
        tipHeight = tipSize.height;

        if (centerLineSegment.intersects(new LineSegment(leftTopPoint, rightTopPoint))) {
            tipX = Math.min(Math.max(alignToCenterX, left), right - (tipWidth / 2));
            tipY = top;
            offsetTop = tipHeight + 10;
            tipPosition = 'top';
        }
        else if (centerLineSegment.intersects(new LineSegment(leftTopPoint, leftBottomPoint))) {
            tipX = left;
            tipY = Math.min(Math.max(alignToCenterY + (tipWidth / 2), top), bottom);
            offsetLeft = tipHeight + 10;
            tipPosition = 'left';
        }
        else if (centerLineSegment.intersects(new LineSegment(leftBottomPoint, rightBottomPoint))) {
            tipX = Math.min(Math.max(alignToCenterX, left), right - (tipWidth / 2));
            tipY = bottom;
            offsetTop = -tipHeight - 10;
            tipPosition = 'bottom';
        }
        else if (centerLineSegment.intersects(new LineSegment(rightTopPoint, rightBottomPoint))) {
            tipX = right;
            tipY = Math.min(Math.max(alignToCenterY - (tipWidth / 2), top), bottom);
            offsetLeft = -tipHeight - 10;
            tipPosition = 'right';
        }

        if (tipX || tipY) {
            this.currentTipPosition = tipPosition;
            tipElement.addCls('x-anchor-' + tipPosition);
            tipElement.setLeft(tipX - left);
            tipElement.setTop(tipY - top);
            tipElement.setVisibility(true);

            this.setLeft(this.getLeft() + offsetLeft);
            this.setTop(this.getTop() + offsetTop);
        }
    }
});

/**
 * SegmentedButton is a container for a group of {@link Ext.Button}s. Generally a SegmentedButton would be
 * a child of a {@link Ext.Toolbar} and would be used to switch between different views.
 *
 * # Useful Properties:
 *
 * - {@link #allowMultiple}
 *
 * ## Example usage:
 *
 *     @example
 *     var segmentedButton = new Ext.SegmentedButton({
 *         allowMultiple: true,
 *         items: [
 *             {
 *                 text: 'Option 1'
 *             },
 *             {
 *                 text   : 'Option 2',
 *                 pressed: true
 *             },
 *             {
 *                 text: 'Option 3'
 *             }
 *         ],
 *         listeners: {
 *             toggle: function(container, button, pressed){
 *                 console.log("User toggled the '" + button.text + "' button: " + (pressed ? 'on' : 'off'));
 *             }
 *         }
 *     });
 *     Ext.Viewport.add(segmentedButton);
 *
 */
Ext.define('Ext.SegmentedButton', {
    extend: 'Ext.Container',
    xtype : 'segmentedbutton',
    requires: ['Ext.Button'],

    config: {
        // @inherited
        baseCls: Ext.baseCSSPrefix + 'segmentedbutton',

        /**
         * @cfg {String} pressedCls
         * CSS class when a button is in pressed state.
         * @accessor
         */
        pressedCls: Ext.baseCSSPrefix + 'button-pressed',

        /**
         * @cfg {Boolean} allowMultiple
         * Allow multiple pressed buttons.
         * @accessor
         */
        allowMultiple: false,

        /**
         * @cfg {Boolean} allowDepress
         * Allow toggling the pressed state of each button.
         * Defaults to true when `allowMultiple` is true.
         * @accessor
         */
        allowDepress: null,

        /**
         * @cfg {Array} pressedButtons
         * The pressed buttons for this segmented button.
         *
         * You can remove all pressed buttons by calling {@link #setPressedButtons} with an empty array.
         * @accessor
         */
        pressedButtons: null,

        // @inherit
        layout: {
            type : 'hbox',
            align: 'stretch'
        },

        // @inherited
        defaultType: 'button'
    },

    /**
     * @event toggle
     * Fires when any child button's pressed state has changed.
     * @param {Ext.SegmentedButton} this
     * @param {Ext.Button} button The toggled button
     * @param {Boolean} isPressed Boolean to indicate if the button was pressed or not
     */

    initialize: function() {
        var me = this;

        me.callParent();

        me.on({
            delegate: '> button',
            scope   : me,
            tap: 'onButtonRelease'
        });

        me.onAfter({
            delegate: '> button',
            scope   : me,
            hiddenchange: 'onButtonHiddenChange'
        });
    },

    updateAllowMultiple: function() {
        if (!this.initialized && !this.getInitialConfig().hasOwnProperty('allowDepress')) {
            this.setAllowDepress(true);
        }
    },

    /**
     * We override initItems so we can check for the pressed config.
     */
    applyItems: function() {
        var me = this,
            pressedButtons = [],
            ln, i, item, items;

        //call the parent first so the items get converted into a MixedCollection
        me.callParent(arguments);

        items = this.getItems();
        ln = items.length;

        for (i = 0; i < ln; i++) {
            item = items.items[i];
            if (item.getInitialConfig('pressed')) {
                pressedButtons.push(items.items[i]);
            }
        }

        me.updateFirstAndLastCls(items);

        me.setPressedButtons(pressedButtons);
    },

    /**
     * Button sets a timeout of 10ms to remove the {@link #pressedCls} on the release event.
     * We don't want this to happen, so lets return false and cancel the event.
     * @private
     */
    onButtonRelease: function(button) {
        var me             = this,
            pressedButtons = me.getPressedButtons(),
            buttons        = [],
            alreadyPressed;

        if (!me.getDisabled() && !button.getDisabled()) {
            //if we allow for multiple pressed buttons, use the existing pressed buttons
            if (me.getAllowMultiple()) {
                buttons = pressedButtons.concat(buttons);
            }

            alreadyPressed = (buttons.indexOf(button) !== -1) || (pressedButtons.indexOf(button) !== -1);

            //if we allow for depressing buttons, and the new pressed button is currently pressed, remove it
            if (alreadyPressed && me.getAllowDepress()) {
                Ext.Array.remove(buttons, button);
            } else if (!alreadyPressed || !me.getAllowDepress()) {
                buttons.push(button);
            }

            me.setPressedButtons(buttons);

            Ext.defer(function() {
                me.fireEvent('toggle', me, button, me.isPressed(button));
            }, 50);
        }
    },

    // @private
    onButtonHiddenChange: function() {
        this.updateFirstAndLastCls(this.getItems());
    },

    // @private
    updateFirstAndLastCls: function(items) {
        var ln = items.length,
            basePrefix = Ext.baseCSSPrefix,
            item, i;

        //add a first cls to the first non-hidden button
        for (i = 0; i < ln; i++) {
            item = items.items[i];
            if (!item.isHidden()) {
                item.addCls(basePrefix + 'first');
                break;
            }
        }

        //add a last cls to the last non-hidden button
        for (i = ln - 1; i >= 0; i--) {
            item = items.items[i];
            if (!item.isHidden()) {
                item.addCls(basePrefix + 'last');
                break;
            }
        }
    },

    /**
     * @private
     */
    applyPressedButtons: function(newButtons) {
        var me    = this,
            array = [],
            button, ln, i;

        if (Ext.isArray(newButtons)) {
            ln = newButtons.length;
            for (i = 0; i< ln; i++) {
                button = me.getComponent(newButtons[i]);
                if (button && array.indexOf(button) === -1) {
                    array.push(button);
                }
            }
        } else {
            button = me.getComponent(newButtons);
            if (button && array.indexOf(button) === -1) {
                array.push(button);
            }
        }

        return array;
    },

    /**
     * Updates the pressed buttons.
     * @private
     */
    updatePressedButtons: function(newButtons) {
        var me    = this,
            items = me.getItems(),
            pressedCls = me.getPressedCls(),
            item, button, ln, i;

        //loop through existing items and remove the pressed cls from them
        ln = items.length;
        for (i = 0; i < ln; i++) {
            item = items.items[i];
            item.removeCls([pressedCls, item.getPressedCls()]);
        }

        //loop through the new pressed buttons and add the pressed cls to them
        ln = newButtons.length;
        for (i = 0; i < ln; i++) {
            button = newButtons[i];
            button.addCls(pressedCls);
        }
    },

    /**
     * Returns true if a specified {@link Ext.Button} is pressed
     * @param {Ext.Button} button The button to check if pressed
     * @return {Boolean} pressed
     */
    isPressed: function(button) {
        var pressedButtons = this.getPressedButtons();
        return pressedButtons.indexOf(button) != -1;
    },

    /**
     *
     */
    doSetDisabled: function(disabled) {
        var me = this;

        me.items.each(function(item) {
            item.setDisabled(disabled);
        }, me);

        me.callParent(arguments);
    }
}, function() {
});

/**
 * A general sheet class. This renderable container provides base support for orientation-aware transitions for popup or
 * side-anchored sliding Panels.
 *
 * In most cases, you should use {@link Ext.ActionSheet}, {@link Ext.MessageBox}, {@link Ext.picker.Picker} or {@link Ext.picker.Date}.
 */
Ext.define('Ext.Sheet', {
    extend: 'Ext.Panel',

    xtype: 'sheet',

    requires: ['Ext.fx.Animation'],

    config: {
        // @inherited
        baseCls: Ext.baseCSSPrefix + 'sheet',

        /**
         * @inherit
         */
        hidden: true,

        /**
         * @inherit
         */
        modal: true,

        /**
         * @cfg {Boolean} centered
         * Whether or not this component is absolutely centered inside its container
         * @accessor
         * @evented
         */
        centered: true,

        /**
         * @cfg {Boolean} stretchX True to stretch this sheet horizontally.
         */
        stretchX: null,

        /**
         * @cfg {Boolean} stretchY True to stretch this sheet vertically.
         */
        stretchY: null,

        /**
         * @cfg {String} enter
         * The viewport side used as the enter point when shown (top, bottom, left, right)
         * Applies to sliding animation effects only. Defaults to 'bottom'
         */
        enter: 'bottom',

        /**
         * @cfg {String} exit
         * The viewport side used as the exit point when hidden (top, bottom, left, right)
         * Applies to sliding animation effects only. Defaults to 'bottom'
         */
        exit: 'bottom',

        /**
         * @inherit
         */
        showAnimation: !Ext.os.is.Android2 ? {
            type: 'slideIn',
            duration: 250,
            easing: 'ease-out'
        } : null,

        /**
         * @inherit
         */
        hideAnimation: !Ext.os.is.Android2 ? {
            type: 'slideOut',
            duration: 250,
            easing: 'ease-in'
        } : null
    },

    applyHideAnimation: function(config) {
        var exit = this.getExit(),
            direction = exit;

        if (exit === null) {
            return null;
        }

        if (config === true) {
            config = {
                type: 'slideOut'
            };
        }
        if (Ext.isString(config)) {
            config = {
                type: config
            };
        }
        var anim = Ext.factory(config, Ext.fx.Animation);

        if (anim) {
            if (exit == 'bottom') {
                direction = 'down';
            }
            if (exit == 'top') {
                direction = 'up';
            }
            anim.setDirection(direction);
        }
        return anim;
    },

    applyShowAnimation: function(config) {
        var enter = this.getEnter(),
            direction = enter;

        if (enter === null) {
            return null;
        }

        if (config === true) {
            config = {
                type: 'slideIn'
            };
        }
        if (Ext.isString(config)) {
            config = {
                type: config
            };
        }
        var anim = Ext.factory(config, Ext.fx.Animation);

        if (anim) {
            if (enter == 'bottom') {
                direction = 'down';
            }
            if (enter == 'top') {
                direction = 'up';
            }
            anim.setBefore({
                display: null
            });
            anim.setReverse(true);
            anim.setDirection(direction);
        }
        return anim;
    },

    updateStretchX: function(newStretchX) {
        this.getLeft();
        this.getRight();

        if (newStretchX) {
            this.setLeft(0);
            this.setRight(0);
        }
    },

    updateStretchY: function(newStretchY) {
        this.getTop();
        this.getBottom();

        if (newStretchY) {
            this.setTop(0);
            this.setBottom(0);
        }
    }
});

/**
 * {@link Ext.ActionSheet ActionSheets} are used to display a list of {@link Ext.Button buttons} in a popup dialog.
 *
 * The key difference between ActionSheet and {@link Ext.Sheet} is that ActionSheets are docked at the bottom of the
 * screen, and the {@link #defaultType} is set to {@link Ext.Button button}.
 *
 * ## Example
 *
 *     @example preview miniphone
 *     var actionSheet = Ext.create('Ext.ActionSheet', {
 *         items: [
 *             {
 *                 text: 'Delete draft',
 *                 ui  : 'decline'
 *             },
 *             {
 *                 text: 'Save draft'
 *             },
 *             {
 *                 text: 'Cancel',
 *                 ui  : 'confirm'
 *             }
 *         ]
 *     });
 *
 *     Ext.Viewport.add(actionSheet);
 *     actionSheet.show();
 *
 * As you can see from the code above, you no longer have to specify a `xtype` when creating buttons within a {@link Ext.ActionSheet ActionSheet},
 * because the {@link #defaultType} is set to {@link Ext.Button button}.
 *
 */
Ext.define('Ext.ActionSheet', {
    extend: 'Ext.Sheet',
    alias : 'widget.actionsheet',
    requires: ['Ext.Button'],

    config: {
        // @inherit
        baseCls: Ext.baseCSSPrefix + 'sheet-action',

        // @inherit
        left: 0,

        // @inherit
        right: 0,

        // @inherit
        bottom: 0,

        // @hide
        centered: false,

        // @inherit
        height: 'auto',

        // @inherit
        defaultType: 'button'
    }
});

/**
 * {@link Ext.TitleBar}'s are most commonly used as a docked item within an {@link Ext.Container}.
 *
 * The main difference between a {@link Ext.TitleBar} and an {@link Ext.Toolbar} is that
 * the {@link #title} configuration is **always** centered horiztonally in a {@link Ext.TitleBar} between
 * any items aligned left or right.
 *
 * You can also give items of a {@link Ext.TitleBar} an `align` configuration of `left` or `right`
 * which will dock them to the `left` or `right` of the bar.
 *
 * ## Examples
 *
 *     @example preview
 *     Ext.Viewport.add({
 *         xtype: 'titlebar',
 *         docked: 'top',
 *         title: 'Navigation',
 *         items: [
 *             {
 *                 iconCls: 'add',
 *                 iconMask: true,
 *                 align: 'left'
 *             },
 *             {
 *                 iconCls: 'home',
 *                 iconMask: true,
 *                 align: 'right'
 *             }
 *         ]
 *     });
 *
 *     Ext.Viewport.setStyleHtmlContent(true);
 *     Ext.Viewport.setHtml('This shows the title being centered and buttons using align <i>left</i> and <i>right</i>.');
 *
 * <br />
 *
 *     @example preview
 *     Ext.Viewport.add({
 *         xtype: 'titlebar',
 *         docked: 'top',
 *         title: 'Navigation',
 *         items: [
 *             {
 *                 align: 'left',
 *                 text: 'This button has a super long title'
 *             },
 *             {
 *                 iconCls: 'home',
 *                 iconMask: true,
 *                 align: 'right'
 *             }
 *         ]
 *     });
 *
 *     Ext.Viewport.setStyleHtmlContent(true);
 *     Ext.Viewport.setHtml('This shows how the title is automatically moved to the right when one of the aligned buttons is very wide.');
 *
 * <br />
 *
 *     @example preview
 *     Ext.Viewport.add({
 *         xtype: 'titlebar',
 *         docked: 'top',
 *         title: 'A very long title',
 *         items: [
 *             {
 *                 align: 'left',
 *                 text: 'This button has a super long title'
 *             },
 *             {
 *                 align: 'right',
 *                 text: 'Another button'
 *             },
 *         ]
 *     });
 *
 *     Ext.Viewport.setStyleHtmlContent(true);
 *     Ext.Viewport.setHtml('This shows how the title and buttons will automatically adjust their size when the width of the items are too wide..');
 *
 * The {@link #defaultType} of Toolbar's is {@link Ext.Button button}.
 */
Ext.define('Ext.TitleBar', {
    extend: 'Ext.Container',
    xtype: 'titlebar',

    requires: [
        'Ext.Button',
        'Ext.Title',
        'Ext.Spacer',
        'Ext.util.SizeMonitor'
    ],

    // private
    isToolbar: true,

    config: {
        // @inherit
        baseCls: Ext.baseCSSPrefix + 'toolbar',

        // @inherit
        cls: Ext.baseCSSPrefix + 'navigation-bar',

        /**
         * @cfg {String} ui
         * Style options for Toolbar. Either 'light' or 'dark'.
         * @accessor
         */
        ui: 'dark',

        /**
         * @cfg {String} title
         * The title of the toolbar.
         * @accessor
         */
        title: null,

        /**
         * @cfg {String} defaultType
         * The default xtype to create.
         * @accessor
         */
        defaultType: 'button',

        /**
         * @cfg
         * @hide
         */
        layout: {
            type: 'hbox'
        },

        /**
         * @cfg {Array/Object} items The child items to add to this TitleBar. The {@link #defaultType} of
         * a TitleBar is {@link Ext.Button}, so you do not need to specify an `xtype` if you are adding
         * buttons.
         *
         * You can also give items a `align` configuration which will align the item to the `left` or `right` of
         * the TitleBar.
         * @accessor
         */
        items: []
    },

    /**
     * The max button width in this toolbar
     * @private
     */
    maxButtonWidth: '40%',

    constructor: function() {
        this.refreshTitlePosition = Ext.Function.createThrottled(this.refreshTitlePosition, 50, this);

        this.callParent(arguments);
    },

    beforeInitialize: function() {
        this.applyItems = this.applyInitialItems;
    },

    initialize: function() {
        delete this.applyItems;

        this.doAdd = this.doBoxAdd;
        this.remove = this.doBoxRemove;
        this.doInsert = this.doBoxInsert;

        this.add(this.initialItems);
        delete this.initialItems;

        this.on({
            painted: 'onPainted',
            erased: 'onErased'
        });
    },

    applyInitialItems: function(items) {
        var SizeMonitor = Ext.util.SizeMonitor,
            defaults = this.getDefaults() || {},
            leftBox, rightBox, spacer;

        this.initialItems = items;

        this.leftBox = leftBox = this.add({
            xtype: 'container',
            style: 'position: relative',
            layout: {
                type: 'hbox',
                align: 'center'
            }
        });

        this.spacer = spacer = this.add({
            xtype: 'component',
            style: 'position: relative',
            flex: 1
        });

        this.rightBox = rightBox = this.add({
            xtype: 'container',
            style: 'position: relative',
            layout: {
                type: 'hbox',
                align: 'center'
            }
        });

        this.titleComponent = this.add({
            xtype: 'title',
            hidden: defaults.hidden,
            centered: true
        });

        this.sizeMonitors = {
            leftBox: new SizeMonitor({
                element: leftBox.renderElement,
                callback: this.refreshTitlePosition,
                scope: this
            }),
            spacer: new SizeMonitor({
                element: spacer.renderElement,
                callback: this.refreshTitlePosition,
                scope: this
            }),
            rightBox: new SizeMonitor({
                element: rightBox.renderElement,
                callback: this.refreshTitlePosition,
                scope: this
            })
        };
    },

    doBoxAdd: function(item) {
        if (item.config.align == 'right') {
            this.rightBox.add(item);
        }
        else {
            this.leftBox.add(item);
        }

        if (this.painted) {
            this.refreshTitlePosition();
        }
    },

    doBoxRemove: function(item) {
        if (item.config.align == 'right') {
            this.rightBox.remove(item);
        }
        else {
            this.leftBox.remove(item);
        }

        if (this.painted) {
            this.refreshTitlePosition();
        }
    },

    doBoxInsert: function(index, item) {
        if (item.config.align == 'right') {
            this.rightBox.add(item);
        }
        else {
            this.leftBox.add(item);
        }
    },

    onPainted: function() {
        var sizeMonitors = this.sizeMonitors;

        this.painted = true;
        this.refreshTitlePosition();

        sizeMonitors.leftBox.refresh();
        sizeMonitors.spacer.refresh();
        sizeMonitors.rightBox.refresh();
    },

    onErased: function() {
        this.painted = false;
    },

    getMaxButtonWidth: function() {
        var value = this.maxButtonWidth;

        //check if it is a percentage
        if (Ext.isString(this.maxButtonWidth)) {
            value = parseInt(value.replace('%', ''), 10);
            value = Math.round((this.element.getWidth() / 100) * value);
        }

        return value;
    },

    refreshTitlePosition: function() {
        var titleElement = this.titleComponent.renderElement;

        titleElement.setWidth(null);
        titleElement.setLeft(null);

        //set the min/max width of the left button
        var leftBox = this.leftBox,
            leftButton = leftBox.down('button'),
            leftBoxWidth, maxButtonWidth;

        if (leftButton) {
            leftButton.renderElement.setWidth('auto');

            leftBoxWidth = leftBox.renderElement.getWidth();
            maxButtonWidth = this.getMaxButtonWidth();

            if (leftBoxWidth > maxButtonWidth) {
                leftButton.renderElement.setWidth(maxButtonWidth);
            }
        }

        var spacerBox = this.spacer.renderElement.getPageBox(),
            titleBox = titleElement.getPageBox(),
            widthDiff = titleBox.width - spacerBox.width,
            titleLeft = titleBox.left,
            titleRight = titleBox.right,
            halfWidthDiff, leftDiff, rightDiff;

        if (widthDiff > 0) {
            titleElement.setWidth(spacerBox.width);
            halfWidthDiff = widthDiff / 2;
            titleLeft += halfWidthDiff;
            titleRight -= halfWidthDiff;
        }

        leftDiff = spacerBox.left - titleLeft;
        rightDiff = titleRight - spacerBox.right;

        if (leftDiff > 0) {
            titleElement.setLeft(leftDiff);
        }
        else if (rightDiff > 0) {
            titleElement.setLeft(-rightDiff);
        }

        titleElement.repaint();
    },

    // @private
    updateTitle: function(newTitle) {
        this.titleComponent.setTitle(newTitle);

        this.titleBox = null;

        if (this.painted) {
            this.refreshTitlePosition();
        }
    },

    destroy: function() {
        this.callParent();

        var sizeMonitors = this.sizeMonitors;

        sizeMonitors.leftBox.destroy();
        sizeMonitors.spacer.destroy();
        sizeMonitors.rightBox.destroy();
    }
});

/**
 * {@link Ext.Toolbar}s are most commonly used as docked items as within a {@link Ext.Container}. They can be docked either `top` or `bottom` using the {@link #docked} configuration.
 *
 * They allow you to insert items (normally {@link Ext.Button buttons}) and also add a {@link #title}.
 *
 * The {@link #defaultType} of {@link Ext.Toolbar} is {@link Ext.Button}.
 *
 * You can alterntively use {@link Ext.TitleBar} if you want the title to automatically adjust the size of its items.
 *
 * ## Examples
 *
 *     @example miniphone preview
 *     Ext.create('Ext.Container', {
 *         fullscreen: true,
 *         layout: {
 *             type: 'vbox',
 *             pack: 'center'
 *         },
 *         items: [
 *             {
 *                 xtype : 'toolbar',
 *                 docked: 'top',
 *                 title: 'My Toolbar'
 *             },
 *             {
 *                 xtype: 'container',
 *                 defaults: {
 *                     xtype: 'button',
 *                     margin: '10 10 0 10'
 *                 },
 *                 items: [
 *                     {
 *                         text: 'Toggle docked',
 *                         handler: function() {
 *                             var toolbar = Ext.ComponentQuery.query('toolbar')[0],
 *                                 newDocked = (toolbar.getDocked() == 'top') ? 'bottom' : 'top';
 *
 *                             toolbar.setDocked(newDocked);
 *                         }
 *                     },
 *                     {
 *                         text: 'Toggle UI',
 *                         handler: function() {
 *                             var toolbar = Ext.ComponentQuery.query('toolbar')[0],
 *                                 newUi = (toolbar.getUi() == 'light') ? 'dark' : 'light';
 *
 *                             toolbar.setUi(newUi);
 *                         }
 *                     },
 *                     {
 *                         text: 'Change title',
 *                         handler: function() {
 *                             var toolbar = Ext.ComponentQuery.query('toolbar')[0],
 *                                 titles = ['My Toolbar', 'Ext.Toolbar', 'Configurations are awesome!', 'Beautiful.'],
                                   //internally, the title configuration gets converted into a {@link Ext.Title} component,
                                   //so you must get the title configuration of that component
 *                                 title = toolbar.getTitle().getTitle(),
 *                                 newTitle = titles[titles.indexOf(title) + 1] || titles[0];
 *
 *                             toolbar.setTitle(newTitle);
 *                         }
 *                     }
 *                 ]
 *             }
 *         ]
 *     });
 *
 * ## Notes
 *
 * You must use a HTML5 doctype for {@link #docked} `bottom` to work. To do this, simply add the following code to the HTML file:
 *
 *     <!doctype html>
 *
 * So your index.html file should look a little like this:
 *
 *     <!doctype html>
 *     <html>
 *         <head>
 *             <title>MY application title</title>
 *             ...
 *
 */
Ext.define('Ext.Toolbar', {
    extend: 'Ext.Container',
    xtype : 'toolbar',

    requires: [
        'Ext.Button',
        'Ext.Title',
        'Ext.Spacer'
    ],

    // private
    isToolbar: true,

    config: {
        // @inherit
        baseCls: Ext.baseCSSPrefix + 'toolbar',

        /**
         * @cfg {String} ui
         * The ui for this {@link Ext.Toolbar}. Either 'light' or 'dark'. Cou can create more UIs by using using the CSS Mixin {@link #sencha-toolbar-ui}
         * @accessor
         */
        ui: 'dark',

        /**
         * @cfg {String/Ext.Title} title
         * The title of the toolbar.
         * @accessor
         */
        title: null,

        /**
         * @cfg {String} defaultType
         * The default xtype to create.
         * @accessor
         */
        defaultType: 'button',

        /**
         * @cfg {String} docked
         * The docked position for this {@link Ext.Toolbar}.
         * If you specify `left` or `right`, the {@link #layout} configuration will automatically change to a `vbox`. It's also
         * recommended to adjust the {@link #width} of the toolbar if you do this.
         * @accessor
         */

        /**
         * @cfg {Object/String} layout Configuration for this Container's layout. Example:
         *
         *    Ext.create('Ext.Container', {
         *        layout: {
         *            type: 'hbox',
         *            align: 'middle'
         *        },
         *        items: [
         *            {
         *                xtype: 'panel',
         *                flex: 1,
         *                style: 'background-color: red;'
         *            },
         *            {
         *                xtype: 'panel',
         *                flex: 2,
         *                style: 'background-color: green'
         *            }
         *        ]
         *    });
         *
         * See the layouts guide for more information
         *
         * Please note, if you set the {@link #docked} configuration to `left` or `right`, the default layout will change from the
         * `hbox` to a `vbox`.
         *
         * @accessor
         */
        layout: {
            type: 'hbox',
            align: 'center'
        }
    },

    constructor: function(config) {
        config = config || {};

        if (config.docked == "left" || config.docked == "right") {
            config.layout = {
                type: 'vbox',
                align: 'stretch'
            };
        }

        this.callParent([config]);
    },

    // @private
    applyTitle: function(title) {
        if (typeof title == 'string') {
            title = {
                title: title,
                centered: true
            };
        }

        return Ext.factory(title, Ext.Title, this.getTitle());
    },

    // @private
    updateTitle: function(newTitle, oldTitle) {
        if (newTitle) {
            this.add(newTitle);
            this.getLayout().setItemFlex(newTitle, 1);
        }

        if (oldTitle) {
            oldTitle.destroy();
        }
    },

    /**
     * Shows the title if it exists.
     */
    showTitle: function() {
        var title = this.getTitle();

        if (title) {
            title.show();
        }
    },

    /**
     * Hides the title if it exists.
     */
    hideTitle: function() {
        var title = this.getTitle();

        if (title) {
            title.hide();
        }
    }

    /**
     * Returns an {@link Ext.Title} component.
     * @member Ext.Toolbar
     * @method getTitle
     * @return {Ext.Title}
     */

    /**
     * Use this to update the {@link #title} configuration.
     * @member Ext.Toolbar
     * @method setTitle
     * @param {String/Ext.Title} title You can either pass a String, or a config/instance of Ext.Title
     */

}, function() {
});


/**
 * Utility class for generating different styles of message boxes. The framework provides a global singleton
 * {@link Ext.Msg} for common usage which you should use in most cases.
 *
 * If you want to use {@link Ext.MessageBox} directly, just think of it as a modal {@link Ext.Container}.
 *
 * Note that the MessageBox is asynchronous. Unlike a regular JavaScript `alert` (which will halt browser execution),
 * showing a MessageBox will not cause the code to stop. For this reason, if you have code that should only run _after_
 * some user feedback from the MessageBox, you must use a callback function (see the `fn` configuration option parameter
 * for the {@link #method-show show} method for more details).
 *
 * ## Example
 *
 *     @example preview
 *     Ext.Msg.alert('Title', 'The quick brown fox jumped over the lazy dog.', Ext.emptyFn);
 *
 * Checkout {@link Ext.Msg} for more examples.
 *
 */
Ext.define('Ext.MessageBox', {
    extend  : 'Ext.Sheet',
    requires: [
        'Ext.Toolbar',
        'Ext.field.Text',
        'Ext.field.TextArea'
    ],

    config: {
        // @inherit
        ui: 'dark',

        // @inherit
        baseCls: Ext.baseCSSPrefix + 'msgbox',

        /**
         * @cfg {String} iconCls
         * CSS class for the icon. The icon should be 40px x 40px.
         * @accessor
         */
        iconCls: null,

        // @inherit
        showAnimation: {
            type: 'popIn',
            duration: 250,
            easing: 'ease-out'
        },

        // @inherit
        hideAnimation: {
            type: 'popOut',
            duration: 250,
            easing: 'ease-out'
        },

        /**
         * Override the default zIndex so it is normally always above floating components.
         */
        zIndex: 10,

        /**
         * @cfg {Number} defaultTextHeight
         * The default height in pixels of the message box's multiline textarea if displayed.
         * @accessor
         */
        defaultTextHeight: 75,

        /**
         * @cfg {String} title
         * The title of this {@link Ext.MessageBox}.
         * @accessor
         */
        title: null,

        /**
         * @cfg {Array/Object} buttons
         * An array of buttons, or an object of a button to be displayed in the toolbar of this {@link Ext.MessageBox}.
         */
        buttons: null,

        /**
         * @cfg {String} message
         * The message to be displayed in the {@link Ext.MessageBox}.
         * @accessor
         */
        message: null,

        /**
         * @cfg {String} msg
         * The message to be displayed in the {@link Ext.MessageBox}.
         * @removed 2.0.0 Please use {@link #message} instead.
         */

        /**
         * @cfg {Object} prompt
         * The configuration to be passed if you want an {@link Ext.field.Text} or {@link Ext.field.TextArea} field
         * in your {@link Ext.MessageBox}.
         *
         * Pass an object with the property "multiline" with a value of true, if you want the prompt to use a TextArea.
         *
         * Alternatively, you can just pass in an object which has an xtype/xclass of another component.
         *
         *     prompt: {
         *         xtype: 'textarea',
         *         value: 'test'
         *     }
         *
         * @accessor
         */
        prompt: null,

        // @inherit
        layout: {
            type: 'vbox',
            pack: 'center'
        }
    },

    statics: {
        OK    : {text: 'OK',     itemId: 'ok',  ui: 'action'},
        YES   : {text: 'Yes',    itemId: 'yes', ui: 'action'},
        NO    : {text: 'No',     itemId: 'no'},
        CANCEL: {text: 'Cancel', itemId: 'cancel'},

        INFO    : Ext.baseCSSPrefix + 'msgbox-info',
        WARNING : Ext.baseCSSPrefix + 'msgbox-warning',
        QUESTION: Ext.baseCSSPrefix + 'msgbox-question',
        ERROR   : Ext.baseCSSPrefix + 'msgbox-error',

        OKCANCEL: [
            {text: 'Cancel', itemId: 'cancel'},
            {text: 'OK',     itemId: 'ok',  ui : 'action'}
        ],
        YESNOCANCEL: [
            {text: 'Cancel', itemId: 'cancel'},
            {text: 'No',     itemId: 'no'},
            {text: 'Yes',    itemId: 'yes', ui: 'action'}
        ],
        YESNO: [
            {text: 'No',  itemId: 'no'},
            {text: 'Yes', itemId: 'yes', ui: 'action'}
        ]
    },

    // @inherit
    constructor: function(config) {
        config = config || {};

        if (config.hasOwnProperty('promptConfig')) {
            Ext.Logger.deprecate("'promptConfig' config is deprecated, please use 'prompt' config instead", this);

            Ext.applyIf(config, {
                prompt: config.promptConfig
            });

            delete config.promptConfig;
        }

        if (config.hasOwnProperty('multiline') || config.hasOwnProperty('multiLine')) {
            config.prompt = config.prompt || {};
            Ext.applyIf(config.prompt, {
                multiLine: config.multiline || config.multiLine
            });

            delete config.multiline;
            delete config.multiLine;
        }

        this.callParent([config]);
    },

    /**
     * Creates a new {@link Ext.Toolbar} instance using {@link Ext#factory}
     * @private
     */
    applyTitle: function(config) {
        if (typeof config == "string") {
            config = {
                title: config
            };
        }

        Ext.applyIf(config, {
            docked: 'top',
            cls   : this.getBaseCls() + '-title'
        });

        return Ext.factory(config, Ext.Toolbar, this.getTitle());
    },

    /**
     * Adds the new {@link Ext.Toolbar} instance into this container
     * @private
     */
    updateTitle: function(newTitle) {
        if (newTitle) {
            this.add(newTitle);
        }
    },

    /**
     * Adds the new {@link Ext.Toolbar} instance into this container
     * @private
     */
    updateButtons: function(newButtons) {
        var me = this;

        if (newButtons) {
            if (me.buttonsToolbar) {
                me.buttonsToolbar.removeAll();
                me.buttonsToolbar.setItems(newButtons);
            } else {
                me.buttonsToolbar = Ext.create('Ext.Toolbar', {
                    docked     : 'bottom',
                    defaultType: 'button',
                    layout     : {
                        type: 'hbox',
                        pack: 'center'
                    },
                    ui         : me.getUi(),
                    cls        : me.getBaseCls() + '-buttons',
                    items      : newButtons
                });

                me.add(me.buttonsToolbar);
            }
        }
    },

    /**
     * @private
     */
    applyMessage: function(config) {
        config = {
            html : config,
            cls  : this.getBaseCls() + '-text'
        };

        return Ext.factory(config, Ext.Component, this._message);
    },

    /**
     * @private
     */
    updateMessage: function(newMessage) {
        if (newMessage) {
            this.add(newMessage);
        }
    },

    getMessage: function() {
        if (this._message) {
            return this._message.getHtml();
        }

        return null;
    },

    /**
     * @private
     */
    applyIconCls: function(config) {
        config = {
            xtype : 'component',
            docked: 'left',
            width : 40,
            height: 40,
            baseCls: Ext.baseCSSPrefix + 'icon',
            hidden: (config) ? false : true,
            cls: config
        };

        return Ext.factory(config, Ext.Component, this._iconCls);
    },

    /**
     * @private
     */
    updateIconCls: function(newIconCls, oldIconCls) {
        var me = this;

        //ensure the title and btuton elements are added first
        this.getTitle();
        this.getButtons();

        if (newIconCls && !oldIconCls) {
            this.add(newIconCls);
        } else {
            this.remove(oldIconCls);
        }
    },

    getIconCls: function() {
        var icon = this._iconCls,
            iconCls;

        if (icon) {
            iconCls = icon.getCls();
            return (iconCls) ? iconCls[0] : null;
        }

        return null;
    },

    /**
     * @private
     */
    applyPrompt: function(prompt) {
        if (prompt) {
            var config = {
                label: false
            };

            if (Ext.isObject(prompt)) {
                Ext.apply(config, prompt);
            }

            if (config.multiLine) {
                config.height = Ext.isNumber(config.multiLine) ? parseFloat(config.multiLine) : this.getDefaultTextHeight();
                return Ext.factory(config, Ext.field.TextArea, this.getPrompt());
            } else {
                return Ext.factory(config, Ext.field.Text, this.getPrompt());
            }
        }

        return prompt;
    },

    /**
     * @private
     */
    updatePrompt: function(newPrompt, oldPrompt) {
        if (newPrompt) {
            this.add(newPrompt);
        }

        if (oldPrompt) {
            this.remove(oldPrompt);
        }
    },

    // @private
    // pass `fn` config to show method instead
    onClick: function(button) {
        if (button) {
            var config = button.config.userConfig || {},
                initialConfig = button.getInitialConfig(),
                prompt = this.getPrompt();

            if (typeof config.fn == 'function') {
                this.on({
                    hiddenchange: function() {
                        config.fn.call(
                            config.scope || null,
                            initialConfig.itemId || initialConfig.text,
                            prompt ? prompt.getValue() : null,
                            config
                        );
                    },
                    single: true,
                    scope: this
                });
            }

            if (config.cls) {
                    this.el.removeCls(config.cls);
                }

            if (config.input) {
                config.input.dom.blur();
            }
        }

        this.hide();
    },

    /**
     * Displays the {@link Ext.MessageBox} with a specified configuration. All
     * display functions (e.g. {@link #method-prompt}, {@link #alert}, {@link #confirm})
     * on MessageBox call this function internally, although those calls
     * are basic shortcuts and do not support all of the config options allowed here.
     *
     * Example usage:
     *
     *     Ext.Msg.show({
     *        title: 'Address',
     *        message: 'Please enter your address:',
     *        width: 300,
     *        buttons: Ext.MessageBox.OKCANCEL,
     *        multiLine: true,
     *        prompt : { maxlength : 180, autocapitalize : true },
     *        fn: function(buttonId) {
     *            console.log('You pressed the "' + buttonId + '" button.');
     *        }
     *     });
     *
     * @param {Object} config An object with the following config options:
     *
     * @param {Object/Array} config.buttons
     * A button config object or Array of the same(e.g., `Ext.MessageBox.OKCANCEL` or `{text:'Foo', itemId:'cancel'}`),
     * or false to not show any buttons.
     *
     * Defaults to: `false`
     *
     * @param {String} config.cls
     * A custom CSS class to apply to the message box's container element.
     *
     * @param {Function} config.fn
     * A callback function which is called when the dialog is dismissed by clicking on the configured buttons.
     *
     * @param {String} config.fn.buttonId The itemId of the button pressed, one of: 'ok', 'yes', 'no', 'cancel'.
     * @param {String} config.fn.value Value of the input field if either `prompt` or `multiline` option is true.
     * @param {Object} config.fn.opt The config object passed to show.
     *
     * @param {Number} config.width
     * A fixed width for the MessageBox.
     *
     * Defaults to: `auto`
     *
     * @param {Number} config.height
     * A fixed height for the MessageBox.
     *
     * Defaults to: `auto`
     *
     * @param {Object} config.scope
     * The scope of the callback function
     *
     * @param {String} config.icon
     * A CSS class that provides a background image to be used as the body icon for the dialog
     * (e.g. Ext.MessageBox.WARNING or 'custom-class').
     *
     * @param {Boolean} config.modal
     * False to allow user interaction with the page while the message box is displayed.
     *
     * Defaults to: `true`
     *
     * @param {String} config.message
     * A string that will replace the existing message box body text.
     * Defaults to the XHTML-compliant non-breaking space character `&#160;`.
     *
     * @param {Number} config.defaultTextHeight
     * The default height in pixels of the message box's multiline textarea if displayed.
     *
     * Defaults to: `75`
     *
     * @param {Boolean} config.prompt
     * True to prompt the user to enter single-line text. Please view the {@link Ext.MessageBox#method-prompt} documentation in {@link Ext.MessageBox}.
     * for more information.
     *
     * Defaults to: `false`
     *
     * @param {Boolean} config.multiline
     * True to prompt the user to enter multi-line text.
     *
     * Defaults to: `false`
     *
     * @param {String} config.title
     * The title text.
     *
     * @param {String} config.value
     * The string value to set into the active textbox element if displayed.
     *
     * @return {Ext.MessageBox} this
     */
    show: function(initialConfig) {
        //if it has not been added to a container, add it to the Viewport.
        if (!this.getParent() && Ext.Viewport) {
            Ext.Viewport.add(this);
        }

        if (!initialConfig) {
            return this.callParent();
        }

        var config = Ext.Object.merge({}, {
            value: ''
        }, initialConfig);

        var buttons        = initialConfig.buttons || Ext.MessageBox.OK || [],
            buttonBarItems = [],
            userConfig     = initialConfig;

        Ext.each(buttons, function(buttonConfig) {
            if (!buttonConfig) {
                return;
            }

            buttonBarItems.push(Ext.apply({
                userConfig: userConfig,
                scope     : this,
                handler   : 'onClick'
            }, buttonConfig));
        }, this);

        config.buttons = buttonBarItems;

        if (config.promptConfig) {
            Ext.Logger.deprecate("'promptConfig' config is deprecated, please use 'prompt' config instead", this);
        }
        config.prompt = (config.promptConfig || config.prompt) || null;

        if (config.multiLine) {
            config.prompt = config.prompt || {};
            config.prompt.multiLine = config.multiLine;
            delete config.multiLine;
        }

        config = Ext.apply({
            iconCls: null,
            title: null,
            buttons: null,
            message: null,
            prompt: null
        }, config);

        this.setConfig(config);

        var prompt = this.getPrompt();
        if (prompt) {
            prompt.setValue(initialConfig.value || '');
        }

        this.callParent();

        return this;
    },

    /**
     * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript alert prompt). If
     * a callback function is passed it will be called after the user clicks the button, and the itemId of the button
     * that was clicked will be passed as the only parameter to the callback.
     *
     * @param {String} title The title bar text
     *
     * @param {String} message The message box body text
     *
     * @param {Function} fn
     * A callback function which is called when the dialog is dismissed by clicking on the configured buttons.
     *
     * @param {String} fn.buttonId The itemId of the button pressed, one of: 'ok', 'yes', 'no', 'cancel'.
     * @param {String} fn.value Value of the input field if either `prompt` or `multiline` option is true.
     * @param {Object} fn.opt The config object passed to show.
     *
     * @param {Object} scope The scope (`this` reference) in which the callback is executed.
     * Defaults to: the browser window
     *
     * @return {Ext.MessageBox} this
     */
    alert: function(title, message, fn, scope) {
        return this.show({
            title       : title,
            message     : message,
            buttons     : Ext.MessageBox.OK,
            promptConfig: false,
            fn          : function(buttonId) {
                if (fn) {
                    fn.call(scope, buttonId);
                }
            },
            scope: scope
        });
    },

    /**
     * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's confirm). If a callback
     * function is passed it will be called after the user clicks either button, and the id of the button that was
     * clicked will be passed as the only parameter to the callback (could also be the top-right close button).
     *
     * @param {String} title The title bar text
     *
     * @param {String} message The message box body text
     *
     * @param {Function} fn
     * A callback function which is called when the dialog is dismissed by clicking on the configured buttons.
     *
     * @param {String} fn.buttonId The itemId of the button pressed, one of: 'ok', 'yes', 'no', 'cancel'.
     * @param {String} fn.value Value of the input field if either `prompt` or `multiline` option is true.
     * @param {Object} fn.opt The config object passed to show.
     *
     * @param {Object} scope The scope (`this` reference) in which the callback is executed.
     *
     * Defaults to: the browser window
     *
     * @return {Ext.MessageBox} this
     */
    confirm: function(title, message, fn, scope) {
        return this.show({
            title       : title,
            message     : message,
            buttons     : Ext.MessageBox.YESNO,
            promptConfig: false,
            scope       : scope,
            fn: function(button) {
                if (fn) {
                    fn.call(scope, button);
                }
            }
        });
    },

    /**
     * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
     * JavaScript's prompt). The prompt can be a single-line or multi-line textbox. If a callback function is passed it
     * will be called after the user clicks either button, and the id of the button that was clicked (could also be the
     * top-right close button) and the text that was entered will be passed as the two parameters to the callback.
     *
     * Example usage:
     *
     *         Ext.Msg.prompt(
     *             'Welcome!',
     *             'What\'s your name going to be today?',
     *             function(buttonId, value) {
     *                 console.log(value)
     *             },
     *             null,
     *             false,
     *             null,
     *             { autocapitalize : true, placeholder : 'First-name please...' }
     *         );
     *
     * @param {String} title The title bar text
     *
     * @param {String} message The message box body text
     *
     * @param {Function} fn
     * A callback function which is called when the dialog is dismissed by clicking on the configured buttons.
     *
     * @param {String} fn.buttonId The itemId of the button pressed, one of: 'ok', 'yes', 'no', 'cancel'.
     * @param {String} fn.value Value of the input field if either `prompt` or `multiline` option is true.
     * @param {Object} fn.opt The config object passed to show.
     *
     * @param {Object} scope The scope (`this` reference) in which the callback is executed.
     *
     * Defaults to: the browser window.
     *
     * @param {Boolean/Number} multiLine True to create a multiline textbox using the defaultTextHeight property,
     * or the height in pixels to create the textbox.
     *
     * Defaults to: `false`
     *
     * @param {String} value Default value of the text input element.
     *
     * @param {Object} prompt
     * The configuration for the prompt. See the {@link Ext.MessageBox#cfg-prompt prompt} documentation in {@link Ext.MessageBox}
     * for more information.
     *
     * Defaults to: `true`
     *
     * @return {Ext.MessageBox} this
     */
    prompt: function(title, message, fn, scope, multiLine, value, prompt) {
        return this.show({
            title    : title,
            message  : message,
            buttons  : Ext.MessageBox.OKCANCEL,
            scope    : scope,
            prompt   : prompt || true,
            multiLine: multiLine,
            value    : value,
            fn: function(button, inputValue) {
                if (fn) {
                    fn.call(scope, button, inputValue);
                }
            }
        });
    }
}, function(MessageBox) {

    Ext.onSetup(function() {
        /**
         * @class Ext.Msg
         * @extends Ext.MessageBox
         * @singleton
         *
         * A global shared singleton instance of the {@link Ext.MessageBox} class.
         *
         * Allows for simple creation of various different alerts and notifications.
         *
         * ## Examples
         *
         * ### Alert
         * Use the {@link #alert} method to show a basic alert:
         *
         *     @example preview
         *     Ext.Msg.alert('Title', 'The quick brown fox jumped over the lazy dog.', Ext.emptyFn);
         *
         * ### Prompt
         * Use the {@link #method-prompt} method to show an alert which has a textfield:
         *
         *     @example preview
         *     Ext.Msg.prompt('Name', 'Please enter your name:', function(text) {
         *         // process text value and close...
         *     });
         *
         * ### Confirm
         * Use the {@link #confirm} method to show a confirmation alert (shows yes and no buttons).
         *
         *     @example preview
         *     Ext.Msg.confirm("Confirmation", "Are you sure you want to do that?", Ext.emptyFn);
         */
        Ext.Msg = new MessageBox;
    });
});


/**
 * @class Ext.carousel.Carousel
 * @author Jacky Nguyen <jacky@sencha.com>
 *
 * Carousels, like [tabs](#!/guide/tabs), are a great way to allow the user to swipe through multiple full-screen pages.
 * A Carousel shows only one of its pages at a time but allows you to swipe through with your finger.
 *
 * Carousels can be oriented either horizontally or vertically and are easy to configure - they just work like any other
 * Container. Here's how to set up a simple horizontal Carousel:
 *
 *     @example
 *     Ext.create('Ext.Carousel', {
 *         fullscreen: true,
 *
 *         defaults: {
 *             styleHtmlContent: true
 *         },
 *
 *         items: [
 *             {
 *                 html : 'Item 1',
 *                 style: 'background-color: #5E99CC'
 *             },
 *             {
 *                 html : 'Item 2',
 *                 style: 'background-color: #759E60'
 *             },
 *             {
 *                 html : 'Item 3'
 *             }
 *         ]
 *     });
 *
 * We can also make Carousels orient themselves vertically:
 *
 *     @example preview
 *     Ext.create('Ext.Carousel', {
 *         fullscreen: true,
 *         direction: 'vertical',
 *
 *         defaults: {
 *             styleHtmlContent: true
 *         },
 *
 *         items: [
 *             {
 *                 html : 'Item 1',
 *                 style: 'background-color: #759E60'
 *             },
 *             {
 *                 html : 'Item 2',
 *                 style: 'background-color: #5E99CC'
 *             }
 *         ]
 *     });
 *
 * ### Common Configurations
 * * {@link #ui} defines the style of the carousel
 * * {@link #direction} defines the direction of the carousel
 * * {@link #indicator} defines if the indicator show be shown
 *
 * ### Useful Methods
 * * {@link #next} moves to the next card
 * * {@link #previous} moves to the previous card
 * * {@link #setActiveItem} moves to the passed card
 *
 * ## Further Reading
 *
 * For more information about Carousels see the [Carousel guide](#!/guide/carousel).
 * 
 * @aside guide carousel
 */
Ext.define('Ext.carousel.Carousel', {
    extend: 'Ext.Container',

    alternateClassName: 'Ext.Carousel',

    xtype: 'carousel',

    requires: [
        'Ext.fx.easing.EaseOut',
        'Ext.carousel.Item',
        'Ext.carousel.Indicator'
    ],

    config: {
        /**
         * @cfg layout
         * Hide layout config in Carousel. It only causes confusion.
         * @accessor
         * @private
         */

        baseCls: 'x-carousel',

        /**
         * @cfg {String} direction
         * The direction of the Carousel, either 'horizontal' or 'vertical'
         * @accessor
         */
        direction: 'horizontal',

        directionLock: false,

        animation: {
            duration: 250,
            easing: {
                type: 'ease-out'
            }
        },

        /**
         * @cfg {Boolean} indicator
         * Provides an indicator while toggling between child items to let the user
         * know where they are in the card stack.
         * @accessor
         */
        indicator: true,

        /**
         * @cfg {String} ui
         * Style options for Carousel. Default is 'dark'. 'light' is also available.
         * @accessor
         */
        ui: 'dark',

        itemConfig: {},

        bufferSize: 1,

        itemLength: null
    },

    itemLength: 0,

    offset: 0,

    flickStartOffset: 0,

    flickStartTime: 0,

    dragDirection: 0,

    count: 0,

    painted: false,

    activeIndex: -1,

    beforeInitialize: function() {
        this.animationListeners = {
            animationframe: 'onActiveItemAnimationFrame',
            animationend: 'onActiveItemAnimationEnd',
            scope: this
        };

        this.element.on({
            dragstart: 'onDragStart',
            drag: 'onDrag',
            dragend: 'onDragEnd',
            scope: this
        });

        this.on({
            painted: 'onPainted',
            erased: 'onErased',
            resize: 'onSizeChange'
        });

        this.carouselItems = [];

        this.orderedCarouselItems = [];

        this.inactiveCarouselItems = [];

        this.hiddenTranslation = 0;
    },

    updateBufferSize: function(size) {
        var ItemClass = Ext.carousel.Item,
            total = size * 2 + 1,
            isRendered = this.isRendered(),
            innerElement = this.innerElement,
            items = this.carouselItems,
            ln = items.length,
            itemConfig = this.getItemConfig(),
            itemLength = this.getItemLength(),
            direction = this.getDirection(),
            setterName = direction === 'horizontal' ? 'setWidth' : 'setHeight',
            i, item;

        for (i = ln; i < total; i++) {
            item = Ext.factory(itemConfig, ItemClass);

            if (itemLength) {
                item[setterName].call(item, itemLength);
            }

            items.push(item);
            innerElement.append(item.renderElement);

            if (isRendered && item.setRendered(true)) {
                item.fireEvent('renderedchange', this, item, true);
            }
        }
    },

    getActiveCarouselItem: function() {
        return this.orderedCarouselItems[this.getBufferSize()];
    },

    setRendered: function(rendered) {
        var wasRendered = this.rendered;

        if (rendered !== wasRendered) {
            this.rendered = rendered;

            var items = this.items.items,
                carouselItems = this.carouselItems,
                i, ln, item;

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

                if (!item.isInnerItem()) {
                    item.setRendered(rendered);
                }
            }

            for (i = 0,ln = carouselItems.length; i < ln; i++) {
                carouselItems[i].setRendered(rendered);
            }

            return true;
        }

        return false;
    },

    onPainted: function() {
        this.painted = true;
        this.refresh();
        this.refreshCarouselItems();
    },

    onErased: function() {
        this.painted = false;
    },

    onSizeChange: function() {
        this.refreshSizing();
        this.refreshCarouselItems();
        this.refreshOffset();
    },

    onItemAdd: function(item, index) {
        this.callParent(arguments);

        var innerIndex = this.getInnerItems().indexOf(item),
            indicator = this.getIndicator();

        if (indicator && item.isInnerItem()) {
            indicator.addIndicator();
        }

        if (innerIndex <= this.getActiveIndex()) {
            this.refreshActiveIndex();
        }

        if (this.painted && this.isIndexDirty(innerIndex)) {
            this.refreshActiveItem();
        }
    },

    doItemLayoutAdd: function(item) {
        if (item.isInnerItem()) {
            return;
        }

        this.callParent(arguments);
    },

    onItemRemove: function(item, index) {
        this.callParent(arguments);

        var innerIndex = this.getInnerItems().indexOf(item),
            indicator = this.getIndicator(),
            carouselItems = this.carouselItems,
            i, ln, carouselItem;

        if (item.isInnerItem() && indicator) {
            indicator.removeIndicator();
        }

        if (innerIndex <= this.getActiveIndex()) {
            this.refreshActiveIndex();
        }

        if (this.isIndexDirty(innerIndex)) {
            for (i = 0,ln = carouselItems.length; i < ln; i++) {
                carouselItem = carouselItems[i];

                if (carouselItem.getComponent() === item) {
                    carouselItem.setComponent(null);
                }
            }

            if (this.painted) {
                this.refreshActiveItem();
            }
        }
    },

    doItemLayoutRemove: function(item) {
        if (item.isInnerItem()) {
            return;
        }

        this.callParent(arguments);
    },

    onInnerItemMove: function(item, toIndex, fromIndex) {
        if (this.painted && (this.isIndexDirty(toIndex) || this.isIndexDirty(fromIndex))) {
            this.refreshActiveItem();
        }
    },

    doItemLayoutMove: function(item) {
        if (item.isInnerItem()) {
            return;
        }

        this.callParent(arguments);
    },

    isIndexDirty: function(index) {
        var activeIndex = this.getActiveIndex(),
            bufferSize = this.getBufferSize();

        return (index >= activeIndex - bufferSize && index <= activeIndex + bufferSize);
    },

    onDragStart: function(e) {
        var direction = this.getDirection(),
            absDeltaX = e.absDeltaX,
            absDeltaY = e.absDeltaY,
            directionLock = this.getDirectionLock();

        this.isDragging = true;

        if (directionLock) {
            if ((direction === 'horizontal' && absDeltaX > absDeltaY)
                || (direction === 'vertical' && absDeltaY > absDeltaX)) {
                e.stopPropagation();
            }
            else {
                this.isDragging = false;
                return;
            }
        }

        if (this.isAnimating) {
            this.getActiveCarouselItem().getTranslatable().stopAnimation();
        }

        this.dragStartOffset = this.offset;
        this.dragDirection = 0;
    },

    onDrag: function(e) {
        if (!this.isDragging) {
            return;
        }

        var startOffset = this.dragStartOffset,
            direction = this.getDirection(),
            delta = direction === 'horizontal' ? e.deltaX : e.deltaY,
            lastOffset = this.offset,
            flickStartTime = this.flickStartTime,
            dragDirection = this.dragDirection,
            now = Ext.Date.now(),
            currentActiveIndex = this.getActiveIndex(),
            maxIndex = this.getMaxItemIndex(),
            lastDragDirection = dragDirection,
            offset;

        if ((currentActiveIndex === 0 && delta > 0) || (currentActiveIndex === maxIndex && delta < 0)) {
            delta *= 0.5;
        }

        offset = startOffset + delta;

        if (offset > lastOffset) {
            dragDirection = 1;
        }
        else if (offset < lastOffset) {
            dragDirection = -1;
        }

        if (dragDirection !== lastDragDirection || (now - flickStartTime) > 300) {
            this.flickStartOffset = lastOffset;
            this.flickStartTime = now;
        }

        this.dragDirection = dragDirection;

        this.setOffset(offset);
    },

    onDragEnd: function(e) {
        if (!this.isDragging) {
            return;
        }

        this.onDrag(e);

        this.isDragging = false;

        var now = Ext.Date.now(),
            itemLength = this.itemLength,
            threshold = itemLength / 2,
            offset = this.offset,
            activeIndex = this.getActiveIndex(),
            maxIndex = this.getMaxItemIndex(),
            animationDirection = 0,
            flickDistance = offset - this.flickStartOffset,
            flickDuration = now - this.flickStartTime,
            indicator = this.getIndicator(),
            velocity;

        if (flickDuration > 0 && Math.abs(flickDistance) >= 10) {
            velocity = flickDistance / flickDuration;

            if (Math.abs(velocity) >= 1) {
                if (velocity < 0 && activeIndex < maxIndex) {
                    animationDirection = -1;
                }
                else if (velocity > 0 && activeIndex > 0) {
                    animationDirection = 1;
                }
            }
        }

        if (animationDirection === 0) {
            if (activeIndex < maxIndex && offset < -threshold) {
                animationDirection = -1;
            }
            else if (activeIndex > 0 && offset > threshold) {
                animationDirection = 1;
            }
        }

        if (indicator) {
            indicator.setActiveIndex(activeIndex - animationDirection);
        }

        this.animationDirection = animationDirection;

        this.setOffsetAnimated(animationDirection * itemLength);
    },

    applyAnimation: function(animation) {
        animation.easing = Ext.factory(animation.easing, Ext.fx.easing.EaseOut);

        return animation;
    },

    updateDirection: function(direction) {
        var indicator = this.getIndicator();

        this.currentAxis = (direction === 'horizontal') ? 'x' : 'y';

        if (indicator) {
            indicator.setDirection(direction);
        }
    },

    setOffset: function(offset) {
        var orderedCarouselItems = this.orderedCarouselItems,
            bufferSize = this.getBufferSize(),
            activeItem = orderedCarouselItems[bufferSize],
            itemLength = this.itemLength,
            axis = this.currentAxis,
            nextItem, previousItem, distance, i;

        this.offset = offset;

        offset += this.itemOffset;

        if (activeItem) {
            activeItem.translateAxis(axis, offset);

            for (i = 1,distance = 0; i <= bufferSize; i++) {
                previousItem = orderedCarouselItems[bufferSize - i];

                if (previousItem) {
                    distance += itemLength;
                    previousItem.translateAxis(axis, offset - distance);
                }
            }

            for (i = 1,distance = 0; i <= bufferSize; i++) {
                nextItem = orderedCarouselItems[bufferSize + i];

                if (nextItem) {
                    distance += itemLength;
                    nextItem.translateAxis(axis, offset + distance);
                }
            }
        }

        return this;
    },

    setOffsetAnimated: function(offset) {
        var activeCarouselItem = this.orderedCarouselItems[this.getBufferSize()],
            indicator = this.getIndicator();

        if (indicator) {
            indicator.setActiveIndex(this.getActiveIndex() - this.animationDirection);
        }

        this.offset = offset;
        offset += this.itemOffset;

        if (activeCarouselItem) {
            this.isAnimating = true;

            activeCarouselItem.getTranslatable().on(this.animationListeners);
            activeCarouselItem.translateAxis(this.currentAxis, offset, this.getAnimation());
        }

        return this;
    },

    onActiveItemAnimationFrame: function(translatable) {
        var orderedCarouselItems = this.orderedCarouselItems,
            bufferSize = this.getBufferSize(),
            itemLength = this.itemLength,
            axis = this.currentAxis,
            offset = translatable[axis],
            previousItem, nextItem, i, distance;

        for (i = 1,distance = 0; i <= bufferSize; i++) {
            previousItem = orderedCarouselItems[bufferSize - i];

            if (previousItem) {
                distance += itemLength;
                previousItem.translateAxis(axis, offset - distance);
            }
        }

        for (i = 1,distance = 0; i <= bufferSize; i++) {
            nextItem = orderedCarouselItems[bufferSize + i];

            if (nextItem) {
                distance += itemLength;
                nextItem.translateAxis(axis, offset + distance);
            }
        }
    },

    onActiveItemAnimationEnd: function(translatable) {
        var currentActiveIndex = this.getActiveIndex(),
            animationDirection = this.animationDirection,
            axis = this.currentAxis,
            currentOffset = translatable[axis],
            itemLength = this.itemLength,
            offset;

        this.isAnimating = false;

        translatable.un(this.animationListeners);

        if (animationDirection === -1) {
            offset = itemLength + currentOffset;
        }
        else if (animationDirection === 1) {
            offset = currentOffset - itemLength;
        }
        else {
            offset = currentOffset;
        }

        offset -= this.itemOffset;
        this.offset = offset;
        this.setActiveItem(currentActiveIndex - animationDirection);
    },

    refresh: function() {
        this.refreshSizing();
        this.refreshActiveItem();
    },

    refreshSizing: function() {
        var element = this.element,
            itemLength = this.getItemLength(),
            itemOffset, containerSize;

        if (this.getDirection() === 'horizontal') {
            containerSize = element.getWidth();
        }
        else {
            containerSize = element.getHeight();
        }

        this.hiddenTranslation = -containerSize;

        if (itemLength === null) {
            itemLength = containerSize;
            itemOffset = 0;
        }
        else {
            itemOffset = (containerSize - itemLength) / 2;
        }

        this.itemLength = itemLength;
        this.itemOffset = itemOffset;
    },

    refreshOffset: function() {
        this.setOffset(this.offset);
    },

    refreshActiveItem: function() {
        this.doSetActiveItem(this.getActiveItem());
    },

    /**
     * Returns the index of the currently active card.
     * @return {Number} The index of the currently active card.
     */
    getActiveIndex: function() {
        return this.activeIndex;
    },

    refreshActiveIndex: function() {
        this.activeIndex = this.getInnerItemIndex(this.getActiveItem());
    },

    refreshCarouselItems: function() {
        var items = this.carouselItems,
            i, ln, item;

        for (i = 0,ln = items.length; i < ln; i++) {
            item = items[i];
            item.getTranslatable().refresh();
        }

        this.refreshInactiveCarouselItems();
    },

    refreshInactiveCarouselItems: function() {
        var items = this.inactiveCarouselItems,
            hiddenTranslation = this.hiddenTranslation,
            axis = this.currentAxis,
            i, ln, item;

        for (i = 0,ln = items.length; i < ln; i++) {
            item = items[i];
            item.translateAxis(axis, hiddenTranslation);
        }
    },

    getMaxItemIndex: function() {
        return this.innerItems.length - 1;
    },

    getInnerItemIndex: function(item) {
        return this.innerItems.indexOf(item);
    },

    getInnerItemAt: function(index) {
        return this.innerItems[index];
    },

    applyActiveItem: function() {
        var activeItem = this.callParent(arguments),
            activeIndex;

        if (activeItem) {
            activeIndex = this.getInnerItemIndex(activeItem);

            if (activeIndex !== -1) {
                this.activeIndex = activeIndex;
                return activeItem;
            }
        }
    },

    doSetActiveItem: function(activeItem) {
        var activeIndex = this.getActiveIndex(),
            maxIndex = this.getMaxItemIndex(),
            indicator = this.getIndicator(),
            bufferSize = this.getBufferSize(),
            carouselItems = this.carouselItems.slice(),
            orderedCarouselItems = this.orderedCarouselItems,
            visibleIndexes = {},
            visibleItems = {},
            visibleItem, component, id, i, index, ln, carouselItem;

        if (carouselItems.length === 0) {
            return;
        }

        this.callParent(arguments);

        orderedCarouselItems.length = 0;

        if (activeItem) {
            id = activeItem.getId();
            visibleItems[id] = activeItem;
            visibleIndexes[id] = bufferSize;

            if (activeIndex > 0) {
                for (i = 1; i <= bufferSize; i++) {
                    index = activeIndex - i;
                    if (index >= 0) {
                        visibleItem = this.getInnerItemAt(index);
                        id = visibleItem.getId();
                        visibleItems[id] = visibleItem;
                        visibleIndexes[id] = bufferSize - i;
                    }
                    else {
                        break;
                    }
                }
            }

            if (activeIndex < maxIndex) {
                for (i = 1; i <= bufferSize; i++) {
                    index = activeIndex + i;
                    if (index <= maxIndex) {
                        visibleItem = this.getInnerItemAt(index);
                        id = visibleItem.getId();
                        visibleItems[id] = visibleItem;
                        visibleIndexes[id] = bufferSize + i;
                    }
                    else {
                        break;
                    }
                }
            }

            for (i = 0,ln = carouselItems.length; i < ln; i++) {
                carouselItem = carouselItems[i];
                component = carouselItem.getComponent();

                if (component) {
                    id = component.getId();

                    if (visibleIndexes.hasOwnProperty(id)) {
                        carouselItems.splice(i, 1);
                        i--;
                        ln--;
                        delete visibleItems[id];
                        orderedCarouselItems[visibleIndexes[id]] = carouselItem;
                    }
                }
            }

            for (id in visibleItems) {
                if (visibleItems.hasOwnProperty(id)) {
                    visibleItem = visibleItems[id];
                    carouselItem = carouselItems.pop();
                    carouselItem.setComponent(visibleItem);
                    orderedCarouselItems[visibleIndexes[id]] = carouselItem;
                }
            }
        }

        this.inactiveCarouselItems.length = 0;
        this.inactiveCarouselItems = carouselItems;
        this.refreshOffset();
        this.refreshInactiveCarouselItems();

        if (indicator) {
            indicator.setActiveIndex(activeIndex);
        }
    },

    /**
     * Switches to the next card
     * @return {Ext.carousel.Carousel} this
     */
    next: function() {
        if (this.isAnimating) {
            this.getActiveCarouselItem().getTranslatable().stopAnimation();
            this.setOffset(0);
        }

        if (this.activeIndex === this.getMaxItemIndex()) {
            return this;
        }

        this.animationDirection = -1;
        this.setOffsetAnimated(-this.itemLength);
        return this;
    },

    /**
     * Switches to the previous card
     * @return {Ext.carousel.Carousel} this
     */
    previous: function() {
        if (this.isAnimating) {
            this.getActiveCarouselItem().getTranslatable().stopAnimation();
            this.setOffset(0);
        }

        if (this.activeIndex === 0) {
            return this;
        }

        this.animationDirection = 1;
        this.setOffsetAnimated(this.itemLength);
        return this;
    },

    // @private
    applyIndicator: function(indicator, currentIndicator) {
        return Ext.factory(indicator, Ext.carousel.Indicator, currentIndicator);
    },

    // @private
    updateIndicator: function(indicator) {
        if (indicator) {
            this.insertFirst(indicator);

            indicator.setUi(this.getUi());
            indicator.on({
                next: 'next',
                previous: 'previous',
                scope: this
            });
        }
    },

    destroy: function() {
        var carouselItems = this.carouselItems.slice();

        this.carouselItems.length = 0;

        Ext.destroy(carouselItems, this.getIndicator());

        this.callParent();
        delete this.carouselItems;
    }

}, function() {
});

/**
 * @class Ext.carousel.Infinite
 * @author Jacky Nguyen <jacky@sencha.com>
 * @private
 *
 * The true infinite implementation of Carousel, private for now until it's stable to be public
 */
Ext.define('Ext.carousel.Infinite', {
    extend: 'Ext.carousel.Carousel',

    config: {
        indicator: null,

        maxItemIndex: Infinity,

        innerItemConfig: {}
    },

    applyIndicator: function(indicator) {
        if (indicator) {
            Ext.Logger.error("'indicator' in Infinite Carousel implementation is not currently supported", this);
        }
        return;
    },

    updateBufferSize: function(size) {
        this.callParent(arguments);

        var total = size * 2 + 1,
            ln = this.innerItems.length,
            innerItemConfig = this.getInnerItemConfig(),
            i;

        for (i = ln; i < total; i++) {
            this.doAdd(this.factoryItem(innerItemConfig));
        }

        this.rebuildInnerIndexes();
        this.refreshActiveItem();
    },

    updateMaxItemIndex: function(maxIndex, oldMaxIndex) {
        if (oldMaxIndex !== undefined) {
            var activeIndex = this.getActiveIndex();

            if (activeIndex > maxIndex) {
                this.setActiveItem(maxIndex);
            }
            else {
                this.rebuildInnerIndexes(activeIndex);
                this.refreshActiveItem();
            }

        }
    },

    rebuildInnerIndexes: function(activeIndex) {
        var indexToItem = this.innerIndexToItem,
            idToIndex = this.innerIdToIndex,
            items = this.innerItems.slice(),
            ln = items.length,
            bufferSize = this.getBufferSize(),
            maxIndex = this.getMaxItemIndex(),
            changedIndexes = [],
            i, oldIndex, index, id, item;


        if (activeIndex === undefined) {
            this.innerIndexToItem = indexToItem = {};
            this.innerIdToIndex = idToIndex = {};

            for (i = 0; i < ln; i++) {
                item = items[i];
                id = item.getId();
                idToIndex[id] = i;
                indexToItem[i] = item;
                this.fireEvent('itemindexchange', this, item, i, -1);
            }
        }
        else {
            for (i = activeIndex - bufferSize; i <= activeIndex + bufferSize; i++) {
                if (i >= 0 && i <= maxIndex) {
                    if (indexToItem.hasOwnProperty(i)) {
                        Ext.Array.remove(items, indexToItem[i]);
                        continue;
                    }
                    changedIndexes.push(i);
                }
            }

            for (i = 0,ln = changedIndexes.length; i < ln; i++) {
                item = items[i];
                id = item.getId();
                index = changedIndexes[i];
                oldIndex = idToIndex[id];

                delete indexToItem[oldIndex];

                idToIndex[id] = index;
                indexToItem[index] = item;
                this.fireEvent('itemindexchange', this, item, index, oldIndex);
            }
        }
    },

    reset: function() {
        this.rebuildInnerIndexes();
        this.setActiveItem(0);
    },

    refreshItems: function() {
        var items = this.innerItems,
            idToIndex = this.innerIdToIndex,
            index, item, i, ln;

        for (i = 0,ln = items.length; i < ln; i++) {
            item = items[i];
            index = idToIndex[item.getId()];
            this.fireEvent('itemindexchange', this, item, index, -1);
        }
    },

    getInnerItemIndex: function(item) {
        var index = this.innerIdToIndex[item.getId()];

        return (typeof index == 'number') ? index : -1;
    },

    getInnerItemAt: function(index) {
        return this.innerIndexToItem[index];
    },

    applyActiveItem: function(activeItem) {
        this.getItems();
        this.getBufferSize();

        var maxIndex = this.getMaxItemIndex(),
            currentActiveIndex = this.getActiveIndex();

        if (typeof activeItem == 'number') {
            activeItem = Math.max(0, Math.min(activeItem, maxIndex));

            if (activeItem === currentActiveIndex) {
                return;
            }

            this.activeIndex = activeItem;

            this.rebuildInnerIndexes(activeItem);

            activeItem = this.getInnerItemAt(activeItem);
        }

        if (activeItem) {
            return this.callParent([activeItem]);
        }
    }
});

/**
 * A DataItem is a container for {@link Ext.dataview.DataView} with useComponents: true. It ties together
 * {@link Ext.data.Model records} to its contained Components via a {@link #dataMap dataMap} configuration.
 *
 * For example, lets say you have a `text configuration which, when applied, gets turned into an instance of an
 * Ext.Component. We want to update the {@link #html} of a sub-component when the 'text' field of the record gets
 * changed.
 *
 * As you can see below, it is simply a matter of setting the key of the object to be the getter of the config
 * (getText), and then give that property a value of an object, which then has 'setHtml' (the html setter) as the key,
 * and 'text' (the field name) as the value. You can continue this for a as many sub-components as you wish.
 *
 *        dataMap: {
 *           // When the record is updated, get the text configuration, and
 *           // call {@link #setHtml} with the 'text' field of the record.
 *           getText: {
 *              setHtml: 'text'
 *          },
 *
 *          // When the record is updated, get the userName configuration, and
 *          // call {@link #setHtml} with the 'from_user' field of the record.
 *          getUserName: {
 *              setHtml: 'from_user'
 *          },
 *
 *          // When the record is updated, get the avatar configuration, and
 *          // call `setSrc` with the 'profile_image_url' field of the record.
 *          getAvatar: {
 *              setSrc: 'profile_image_url'
 *          }
 *      },
 */

Ext.define('Ext.dataview.component.DataItem', {
    extend: 'Ext.Container',
    xtype : 'dataitem',

    config: {
        baseCls: Ext.baseCSSPrefix + 'data-item',

        defaultType: 'component',

        /**
         * @cfg {Ext.data.Model} record The model instance of this DataItem. It is controlled by the Component DataView
         * @accessor
         */
        record: null,

        /**
         * @cfg {String} itemCls
         * An additional CSS class to apply to items within the DataView.
         * @accessor
         */
        itemCls: null,

        /**
         * @cfg dataMap
         * The dataMap allows you to map {@link #record} fields to specific configurations in this component.
         *
         * For example, lets say you have a `text` configuration which, when applied, gets turned into an instance of an Ext.Component.
         * We want to update the {@link #html} of this component when the 'text' field of the record gets changed.
         * For example:
         *
         *      dataMap: {
         *          getText: {
         *              setHtml: 'text'
         *          }
         *      }
         *
         * In this example, it is simply a matter of setting the key of the object to be the getter of the config (getText), and then give that
         * property a value of an object, which then has 'setHtml' (the html setter) as the key, and 'text' (the field name) as the value.
         */
        dataMap: {},

        items: [{
            xtype: 'component'
        }]
    },

    updateBaseCls: function(newBaseCls, oldBaseCls) {
        var me = this;

        me.callParent(arguments);
    },

    updateItemCls: function(newCls, oldCls) {
        if (oldCls) {
            this.removeCls(oldCls);
        }
        if (newCls) {
            this.addCls(newCls);
        }
    },

    /**
     * Updates this container's child items, passing through the dataMap.
     * @param newRecord
     * @private
     */
    updateRecord: function(newRecord) {
        if (!newRecord) {
            return;
        }

        var me = this,
            dataview = me.config.dataview,
            data = dataview.prepareData(newRecord.getData(true), dataview.getStore().indexOf(newRecord), newRecord),
            items = me.getItems(),
            item = items.first(),
            dataMap = me.getDataMap(),
            componentName, component, setterMap, setterName;

        if (!item) {
            return;
        }
        for (componentName in dataMap) {
            setterMap = dataMap[componentName];
            component = me[componentName]();
            if (component) {
                for (setterName in setterMap) {
                    if (component[setterName]) {
                        component[setterName](data[setterMap[setterName]]);
                    }
                }
            }
        }

        /**
         * @event updatedata
         * Fires whenever the data of the DataItem is updated
         * @param {Ext.dataview.component.DataItem} this The DataItem instance
         * @param {Object} newData The new data
         */
        me.fireEvent('updatedata', me, data);

        // Bypassing setter because sometimes we pass the same object (different properties)
        item.updateData(data);
    }
});

/**
 * @private
 */
Ext.define('Ext.dataview.component.Container', {
    extend: 'Ext.Container',

    requires: [
        'Ext.dataview.component.DataItem'
    ],

    /**
     * @event itemtouchstart
     * Fires whenever an item is touched
     * @param {Ext.dataview.component.Container} this
     * @param {Ext.dataview.component.DataItem} item The item touched
     * @param {Number} index The index of the item touched
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event itemtouchmove
     * Fires whenever an item is moved
     * @param {Ext.dataview.component.Container} this
     * @param {Ext.dataview.component.DataItem} item The item moved
     * @param {Number} index The index of the item moved
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event itemtouchend
     * Fires whenever an item is touched
     * @param {Ext.dataview.component.Container} this
     * @param {Ext.dataview.component.DataItem} item The item touched
     * @param {Number} index The index of the item touched
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event itemtap
     * Fires whenever an item is tapped
     * @param {Ext.dataview.component.Container} this
     * @param {Ext.dataview.component.DataItem} item The item tapped
     * @param {Number} index The index of the item tapped
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event itemtaphold
     * Fires whenever an item is tapped
     * @param {Ext.dataview.component.Container} this
     * @param {Ext.dataview.component.DataItem} item The item tapped
     * @param {Number} index The index of the item tapped
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event itemdoubletap
     * Fires whenever an item is doubletapped
     * @param {Ext.dataview.component.Container} this
     * @param {Ext.dataview.component.DataItem} item The item doubletapped
     * @param {Number} index The index of the item doubletapped
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event itemswipe
     * Fires whenever an item is swiped
     * @param {Ext.dataview.component.Container} this
     * @param {Ext.dataview.component.DataItem} item The item swiped
     * @param {Number} index The index of the item swiped
     * @param {Ext.EventObject} e The event object
     */

    constructor: function() {
        this.itemCache = [];
        this.callParent(arguments);
    },

    //@private
    doInitialize: function() {
        this.innerElement.on({
            touchstart: 'onItemTouchStart',
            touchend: 'onItemTouchEnd',
            tap: 'onItemTap',
            touchmove: 'onItemTouchMove',
            doubletap: 'onItemDoubleTap',
            swipe: 'onItemSwipe',
            delegate: '> .' + Ext.baseCSSPrefix + 'data-item',
            scope: this
        });
    },

    //@private
    initialize: function() {
        this.callParent();
        this.doInitialize();
    },

    onItemTouchStart: function(e) {
        var me = this,
            target = e.getTarget(),
            item = Ext.getCmp(target.id);

        item.on({
            touchmove: 'onItemTouchMove',
            scope   : me,
            single: true
        });

        me.fireEvent('itemtouchstart', me, item, me.indexOf(item), e);
    },

    onItemTouchMove: function(e) {
        var me = this,
            target = e.getTarget(),
            item = Ext.getCmp(target.id);
        me.fireEvent('itemtouchmove', me, item, me.indexOf(item), e);
    },

    onItemTouchEnd: function(e) {
        var me = this,
            target = e.getTarget(),
            item = Ext.getCmp(target.id);

        item.un({
            touchmove: 'onItemTouchMove',
            scope   : me
        });

        me.fireEvent('itemtouchend', me, item, me.indexOf(item), e);
    },

    onItemTap: function(e) {
        var me = this,
            target = e.getTarget(),
            item = Ext.getCmp(target.id);
        me.fireEvent('itemtap', me, item, me.indexOf(item), e);
    },

    onItemTapHold: function(e) {
        var me = this,
            target = e.getTarget(),
            item = Ext.getCmp(target.id);
        me.fireEvent('itemtaphold', me, item, me.indexOf(item), e);
    },

    onItemDoubleTap: function(e) {
        var me = this,
            target = e.getTarget(),
            item = Ext.getCmp(target.id);
        me.fireEvent('itemdoubletap', me, item, me.indexOf(item), e);
    },

    onItemSwipe: function(e) {
        var me = this,
            target = e.getTarget(),
            item = Ext.getCmp(target.id);
        me.fireEvent('itemswipe', me, item, me.indexOf(item), e);
    },

    moveItemsToCache: function(from, to) {
        var me = this,
            dataview = me.dataview,
            maxItemCache = dataview.getMaxItemCache(),
            items = me.getViewItems(),
            itemCache = me.itemCache,
            cacheLn = itemCache.length,
            pressedCls = dataview.getPressedCls(),
            selectedCls = dataview.getSelectedCls(),
            i = to - from,
            item;

        for (; i >= 0; i--) {
            item = items[from + i];
            if (cacheLn !== maxItemCache) {
                me.remove(item, false);
                item.removeCls([pressedCls, selectedCls]);
                itemCache.push(item);
                cacheLn++;
            }
            else {
                item.destroy();
            }
        }

        if (me.getViewItems().length == 0) {
            this.dataview.showEmptyText();
        }
    },

    moveItemsFromCache: function(records) {
        var me = this,
            dataview = me.dataview,
            store = dataview.getStore(),
            ln = records.length,
            xtype = dataview.getDefaultType(),
            itemConfig = dataview.getItemConfig(),
            itemCache = me.itemCache,
            cacheLn = itemCache.length,
            items = [],
            i, item, record;

        if (ln) {
            dataview.hideEmptyText();
        }

        for (i = 0; i < ln; i++) {
            records[i]._tmpIndex = store.indexOf(records[i]);
        }

        Ext.Array.sort(records, function(record1, record2) {
            return record1._tmpIndex > record2._tmpIndex ? 1 : -1;
        });

        for (i = 0; i < ln; i++) {
            record = records[i];
            if (cacheLn) {
                cacheLn--;
                item = itemCache.pop();
                this.updateListItem(record, item);
            }
            else {
                item = me.getDataItemConfig(xtype, record, itemConfig);
            }
            this.insert(record._tmpIndex, item);
            delete record._tmpIndex;
        }
        return items;
    },

    getViewItems: function() {
        return this.getInnerItems();
    },

    updateListItem: function(record, item) {
        if (item.updateRecord) {
            item.updateRecord(record);
        }
    },

    getDataItemConfig: function(xtype, record, itemConfig) {
        var dataview = this.dataview;
        return {
            xtype: xtype,
            record: record,
            dataview: dataview,
            itemCls: dataview.getItemCls(),
            defaults: itemConfig
        };
    },

    doRemoveItemCls: function(cls) {
        var items = this.getViewItems(),
            ln = items.length,
            i = 0;

        for (; i < ln; i++) {
            items[i].removeCls(cls);
        }
    },

    doAddItemCls: function(cls) {
        var items = this.getViewItems(),
            ln = items.length,
            i = 0;

        for (; i < ln; i++) {
            items[i].addCls(cls);
        }
    },

    destroy: function() {
        var me = this,
            itemCache = me.itemCache,
            ln = itemCache.length,
            i = 0;

        for (; i < ln; i++) {
            itemCache[i].destroy();
        }
        this.callParent();
    }
});

/**
 * DataView makes it easy to create lots of components dynamically, usually based off a {@link Ext.data.Store Store}.
 * It's great for rendering lots of data from your server backend or any other data source and is what powers
 * components like {@link Ext.List}.
 *
 * Use DataView whenever you want to show sets of the same component many times, for examples in apps like these:
 *
 * - List of messages in an email app
 * - Showing latest news/tweets
 * - Tiled set of albums in an HTML5 music player
 *
 * # Creating a Simple DataView
 *
 * At its simplest, a DataView is just a Store full of data and a simple template that we use to render each item:
 *
 *     @example miniphone preview
 *     var touchTeam = Ext.create('Ext.DataView', {
 *         fullscreen: true,
 *         store: {
 *             fields: ['name', 'age'],
 *             data: [
 *                 {name: 'Jamie',  age: 100},
 *                 {name: 'Rob',   age: 21},
 *                 {name: 'Tommy', age: 24},
 *                 {name: 'Jacky', age: 24},
 *                 {name: 'Ed',   age: 26}
 *             ]
 *         },
 *
 *         itemTpl: '<div>{name} is {age} years old</div>'
 *     });
 *
 * Here we just defined everything inline so it's all local with nothing being loaded from a server. For each of the 5
 * data items defined in our Store, DataView will render a {@link Ext.Component Component} and pass in the name and age
 * data. The component will use the tpl we provided above, rendering the data in the curly bracket placeholders we
 * provided.
 *
 * Because DataView is integrated with Store, any changes to the Store are immediately reflected on the screen. For
 * example, if we add a new record to the Store it will be rendered into our DataView:
 *
 *     touchTeam.getStore().add({
 *         name: 'Abe Elias',
 *         age: 33
 *     });
 *
 * We didn't have to manually update the DataView, it's just automatically updated. The same happens if we modify one
 * of the existing records in the Store:
 *
 *     touchTeam.getStore().getAt(0).set('age', 42);
 *
 * This will get the first record in the Store (Jamie), change the age to 42 and automatically update what's on the
 * screen.
 *
 *     @example miniphone
 *     var touchTeam = Ext.create('Ext.DataView', {
 *         fullscreen: true,
 *         store: {
 *             fields: ['name', 'age'],
 *             data: [
 *                 {name: 'Jamie',  age: 100},
 *                 {name: 'Rob',   age: 21},
 *                 {name: 'Tommy', age: 24},
 *                 {name: 'Jacky', age: 24},
 *                 {name: 'Ed',   age: 26}
 *             ]
 *         },
 *
 *         itemTpl: '<div>{name} is {age} years old</div>'
 *     });
 *
 *     touchTeam.getStore().add({
 *         name: 'Abe Elias',
 *         age: 33
 *     });
 *
 *     touchTeam.getStore().getAt(0).set('age', 42);
 *
 * # Loading data from a server
 *
 * We often want to load data from our server or some other web service so that we don't have to hard code it all
 * locally. Let's say we want to load all of the latest tweets about Sencha Touch into a DataView, and for each one
 * render the user's profile picture, user name and tweet message. To do this all we have to do is modify the
 * {@link #store} and {@link #itemTpl} a little:
 *
 *     @example portrait
 *     Ext.create('Ext.DataView', {
 *         fullscreen: true,
 *         cls: 'twitterView',
 *         store: {
 *             autoLoad: true,
 *             fields: ['from_user', 'text', 'profile_image_url'],
 *
 *             proxy: {
 *                 type: 'jsonp',
 *                 url: 'http://search.twitter.com/search.json?q=Sencha Touch',
 *
 *                 reader: {
 *                     type: 'json',
 *                     root: 'results'
 *                 }
 *             }
 *         },
 *
 *         itemTpl: '<img src="{profile_image_url}" /><h2>{from_user}</h2><p>{text}</p><div style="clear: both"></div>'
 *     });
 *
 * The Store no longer has hard coded data, instead we've provided a {@link Ext.data.proxy.Proxy Proxy}, which fetches
 * the data for us. In this case we used a JSON-P proxy so that we can load from Twitter's JSON-P search API. We also
 * specified the fields present for each tweet, and used Store's {@link Ext.data.Store#autoLoad autoLoad} configuration
 * to load automatically. Finally, we configured a Reader to decode the response from Twitter, telling it to expect
 * JSON and that the tweets can be found in the 'results' part of the JSON response.
 *
 * The last thing we did is update our template to render the image, twitter username and message. All we need to do
 * now is add a little CSS to style the list the way we want it and we end up with a very basic twitter viewer. Click
 * the preview button on the example above to see it in action.
 *
 * @aside guide dataview
 */
Ext.define('Ext.dataview.DataView', {
    extend: 'Ext.Container',

    alternateClassName: 'Ext.DataView',

    mixins: ['Ext.mixin.Selectable'],

    xtype: 'dataview',

    requires: [
        'Ext.LoadMask',
        'Ext.data.StoreManager',
        'Ext.dataview.component.Container',
        'Ext.dataview.element.Container'
    ],

    /**
     * @event containertap
     * @removed 2.0.0
     */

    /**
     * @event itemtouchstart
     * Fires whenever an item is touched
     * @param {Ext.dataview.DataView} this
     * @param {Number} index The index of the item touched
     * @param {Ext.Element/Ext.dataview.component.DataItem} target The element or DataItem touched
     * @param {Ext.data.Model} record The record assosciated to the item
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event itemtouchmove
     * Fires whenever an item is moved
     * @param {Ext.dataview.DataView} this
     * @param {Number} index The index of the item moved
     * @param {Ext.Element/Ext.dataview.component.DataItem} target The element or DataItem moved
     * @param {Ext.data.Model} record The record assosciated to the item
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event itemtouchend
     * Fires whenever an item is touched
     * @param {Ext.dataview.DataView} this
     * @param {Number} index The index of the item touched
     * @param {Ext.Element/Ext.dataview.component.DataItem} target The element or DataItem touched
     * @param {Ext.data.Model} record The record assosciated to the item
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event itemtap
     * Fires whenever an item is tapped
     * @param {Ext.dataview.DataView} this
     * @param {Number} index The index of the item tapped
     * @param {Ext.Element/Ext.dataview.component.DataItem} target The element or DataItem tapped
     * @param {Ext.data.Model} record The record assosciated to the item
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event itemtaphold
     * Fires whenever an item's taphold event fires
     * @param {Ext.dataview.DataView} this
     * @param {Number} index The index of the item touched
     * @param {Ext.Element/Ext.dataview.component.DataItem} target The element or DataItem touched
     * @param {Ext.data.Model} record The record assosciated to the item
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event itemdoubletap
     * Fires whenever an item is doubletapped
     * @param {Ext.dataview.DataView} this
     * @param {Number} index The index of the item doubletapped
     * @param {Ext.Element/Ext.dataview.component.DataItem} target The element or DataItem doubletapped
     * @param {Ext.data.Model} record The record assosciated to the item
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event itemswipe
     * Fires whenever an item is swiped
     * @param {Ext.dataview.DataView} this
     * @param {Number} index The index of the item swiped
     * @param {Ext.Element/Ext.dataview.component.DataItem} target The element or DataItem swiped
     * @param {Ext.data.Model} record The record assosciated to the item
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event select
     * @preventable doItemSelect
     * Fires whenever an item is selected
     * @param {Ext.dataview.DataView} this
     * @param {Ext.data.Model} record The record assosciated to the item
     */

    /**
     * @event deselect
     * @preventable doItemDeselect
     * Fires whenever an item is deselected
     * @param {Ext.dataview.DataView} this
     * @param {Ext.data.Model} record The record assosciated to the item
     * @param {Boolean} supressed Flag to supress the event
     */

    /**
     * @event refresh
     * @preventable doRefresh
     * Fires whenever the DataView is refreshed
     * @param {Ext.dataview.DataView} this
     */

    /**
     * @hide
     * @event add
     */

    /**
     * @hide
     * @event remove
     */

    /**
     * @hide
     * @event move
     */

    config: {
        /**
         * @cfg layout
         * Hide layout config in DataView. It only causes confusion.
         * @accessor
         * @private
         */

        /**
         * @cfg {Ext.data.Store/Object} store
         * Can be either a Store instance or a configuration object that will be turned into a Store. The Store is used
         * to populate the set of items that will be rendered in the DataView. See the DataView intro documentation for
         * more information about the relationship between Store and DataView.
         * @accessor
         */
        store: null,

        // @inherit
        baseCls: Ext.baseCSSPrefix + 'dataview',

        /**
         * @cfg {String} emptyText
         * The text to display in the view when there is no data to display
         */
        emptyText: null,

        /**
         * @cfg {Boolean} deferEmptyText True to defer emptyText being applied until the store's first load
         */
        deferEmptyText: true,

        /**
         * @cfg {String/String[]/Ext.XTemplate} itemTpl
         * The tpl to use for each of the items displayed in this DataView.
         */
        itemTpl: '<div>{text}</div>',

        /**
         * @cfg {String} pressedCls
         * The CSS class to apply to an item on the view while it is being pressed.
         * @accessor
         */
        pressedCls: 'x-item-pressed',

        /**
         * @cfg {String} itemCls
         * An additional CSS class to apply to items within the DataView.
         * @accessor
         */
        itemCls: null,

        /**
         * @cfg {String} selectedCls
         * The CSS class to apply to an item on the view while it is selected.
         * @accessor
         */
        selectedCls: 'x-item-selected',

        /**
         * @cfg {String} triggerEvent
         * Determines what type of touch event causes an item to be selected.
         * Valid options are 'tap' and 'singletap'.
         * @accessor
         */
        triggerEvent: 'itemtap',

        /**
         * @cfg {String} triggerCtEvent
         * Determines what type of touch event is recognized as a touch on the container.
         * Valid options are 'tap' and 'singletap'.
         * @accessor
         */
        triggerCtEvent: 'tap',

        /**
         * @cfg {Boolean} deselectOnContainerClick
         * When set to true, tapping on the DataView's background (i.e. not on
         * an item in the DataView) will deselect any currently selected items.
         * @accessor
         */
        deselectOnContainerClick: true,

        // @inherit
        scrollable: true,

        /**
         * @cfg {Boolean/Object} inline
         * When set to true the items within the DataView will have their display set to inline-block
         * and be arranged horizontally. By default the items will wrap to the width of the DataView.
         * Passing an object with { wrap: false } will turn off this wrapping behavior and overflowed
         * items will need to be scrolled to horizontally.
         * @accessor
         */
        inline: null,

        /**
         * @cfg {Number} pressedDelay
         * The amount of delay between the tapstart and the moment we add the pressedCls.
         * Settings this to true defaults to 100ms.
         * @accessor
         */
        pressedDelay: 100,

        /**
         * @cfg {String} loadingText
         * A string to display during data load operations (defaults to 'Loading...').  If specified, this text will be
         * displayed in a loading div and the view's contents will be cleared while loading, otherwise the view's
         * contents will continue to display normally until the new data is loaded and the contents are replaced.
         */
        loadingText: 'Loading...',

        /**
         * @cfg {Boolean} useComponents
         * Flag the use a component based DataView implementation.  This allows the full use of components in the
         * DataView at the cost of some performance.
         *
         * Checkout the [DataView Guide](#!/guide/dataview) for more information on using this configuration.
         * @accessor
         */
        useComponents: null,

        /**
         * @cfg {Object} itemConfig
         * A configuration object that is passed to every item created by a component based DataView. Because each
         * item that a DataView renders is a Component, we can pass configuration options to each component to
         * easily customize how each child component behaves.
         * Note this is only used when useComponents is true.
         * @accessor
         */
        itemConfig: {},

        /**
         * @cfg {Number} maxItemCache
         * Maintains a cache of reusable components when using a component based DataView.  Improveing performance at
         * the cost of memory.
         * Note this is currently only used when useComponents is true.
         * @accessor
         */
        maxItemCache: 20,

        /**
         * @cfg {String} defaultType
         * The xtype used for the component based DataView. Defaults to dataitem.
         * Note this is only used when useComponents is true.
         * @accessor
         */
        defaultType: 'dataitem',

        /**
         * @cfg {String} scrollToTopOnRefresh
         * Scroll the DataView to the top when the DataView is refreshed
         * @accessor
         */
        scrollToTopOnRefresh: true
    },

    constructor: function(config) {
        var me = this;

        if (config && config.layout) {
            Ext.Logger.warn('Attempting to create a DataView with a layout. DataViews do not have a layout configuration as their items are laid out automatically.');
            delete config.layout;
        }

        me.hasLoadedStore = false;

        me.mixins.selectable.constructor.apply(me, arguments);

        me.callParent(arguments);
    },

    updateItemCls: function(newCls, oldCls) {
        var container = this.container;
        if (container) {
            if (oldCls) {
                container.doRemoveItemCls(oldCls);
            }
            if (newCls) {
                container.doAddItemCls(newCls);
            }
        }
    },

    storeEventHooks: {
        beforeload: 'onBeforeLoad',
        load: 'onLoad',
        refresh: 'refresh',
        addrecords: 'onStoreAdd',
        removerecords: 'onStoreRemove',
        updaterecord: 'onStoreUpdate'
    },

    initialize: function() {
        this.callParent();
        var me = this,
            container;

        me.on(me.getTriggerCtEvent(), me.onContainerTrigger, me);

        container = me.container = this.add(new Ext.dataview[me.getUseComponents() ? 'component' : 'element'].Container({
            baseCls: this.getBaseCls()
        }));
        container.dataview = me;

        me.on(me.getTriggerEvent(), me.onItemTrigger, me);

        container.on({
            itemtouchstart: 'onItemTouchStart',
            itemtouchend: 'onItemTouchEnd',
            itemtap: 'onItemTap',
            itemtaphold: 'onItemTapHold',
            itemtouchmove: 'onItemTouchMove',
            itemdoubletap: 'onItemDoubleTap',
            itemswipe: 'onItemSwipe',
            scope: me
        });

        if (this.getStore()) {
            this.refresh();
        }
    },

    applyInline: function(config) {
        if (Ext.isObject(config)) {
            config = Ext.apply({}, config);
        }
        return config;
    },

    updateInline: function(newInline, oldInline) {
        var baseCls = this.getBaseCls();
        if (oldInline) {
            this.removeCls([baseCls + '-inlineblock', baseCls + '-nowrap']);
        }
        if (newInline) {
            this.addCls(baseCls + '-inlineblock');
            if (Ext.isObject(newInline) && newInline.wrap === false) {
                this.addCls(baseCls + '-nowrap');
            }
            else {
                this.removeCls(baseCls + '-nowrap');
            }
        }
    },

    /**
     * Function which can be overridden to provide custom formatting for each Record that is used by this
     * DataView's {@link #tpl template} to render each node.
     * @param {Object/Object[]} data The raw data object that was used to create the Record.
     * @param {Number} recordIndex the index number of the Record being prepared for rendering.
     * @param {Ext.data.Model} record The Record being prepared for rendering.
     * @return {Array/Object} The formatted data in a format expected by the internal {@link #tpl template}'s overwrite() method.
     * (either an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}))
     */
    prepareData: function(data, index, record) {
        return data;
    },

    // apply to the selection model to maintain visual UI cues
    onContainerTrigger: function(e) {
        var me = this;
        if (e.target != me.element.dom) {
            return;
        }
        if (me.getDeselectOnContainerClick() && me.getStore()) {
            me.deselectAll();
        }
    },

    // apply to the selection model to maintain visual UI cues
    onItemTrigger: function(me, index) {
        this.selectWithEvent(this.getStore().getAt(index));
    },

    doAddPressedCls: function(record) {
        var me = this,
        item = me.container.getViewItems()[me.getStore().indexOf(record)];
        if (Ext.isElement(item)) {
            item = Ext.get(item);
        }
        if (item) {
            item.addCls(me.getPressedCls());
        }
    },

    onItemTouchStart: function(container, target, index, e) {
        var me = this,
            store = me.getStore(),
            record = store && store.getAt(index);

        me.fireAction('itemtouchstart', [me, index, target, record, e], 'doItemTouchStart');
    },

    doItemTouchStart: function(me, index, target, record) {
        var pressedDelay = me.getPressedDelay();

        if (record) {
            if (pressedDelay > 0) {
                me.pressedTimeout = Ext.defer(me.doAddPressedCls, pressedDelay, me, [record]);
            }
            else {
                me.doAddPressedCls(record);
            }
        }
    },

    onItemTouchEnd: function(container, target, index, e) {
        var me = this,
            store = me.getStore(),
            record = store && store.getAt(index);

        if (this.hasOwnProperty('pressedTimeout')) {
            clearTimeout(this.pressedTimeout);
            delete this.pressedTimeout;
        }

        if (record && target) {
            target.removeCls(me.getPressedCls());
        }

        me.fireEvent('itemtouchend', me, index, target, record, e);
    },

    onItemTouchMove: function(container, target, index, e) {
        var me = this,
            store = me.getStore(),
            record = store && store.getAt(index);

        if (me.hasOwnProperty('pressedTimeout')) {
            clearTimeout(me.pressedTimeout);
            delete me.pressedTimeout;
        }

        if (record && target) {
            target.removeCls(me.getPressedCls());
        }
        me.fireEvent('itemtouchmove', me, index, target, record, e);
    },

    onItemTap: function(container, target, index, e) {
        var me = this,
            store = me.getStore(),
            record = store && store.getAt(index);

        me.fireEvent('itemtap', me, index, target, record, e);
    },

    onItemTapHold: function(container, target, index, e) {
        var me = this,
            store = me.getStore(),
            record = store && store.getAt(index);

        me.fireEvent('itemtaphold', me, index, target, record, e);
    },

    onItemDoubleTap: function(container, target, index, e) {
        var me = this,
            store = me.getStore(),
            record = store && store.getAt(index);

        me.fireEvent('itemdoubletap', me, index, target, record, e);
    },

    onItemSwipe: function(container, target, index, e) {
        var me = this,
            store = me.getStore(),
            record = store && store.getAt(index);

        me.fireEvent('itemswipe', me, index, target, record, e);
    },

    // invoked by the selection model to maintain visual UI cues
    onItemSelect: function(record, suppressEvent) {
        var me = this;
        if (suppressEvent) {
            me.doItemSelect(me, record);
        } else {
            me.fireAction('select', [me, record], 'doItemSelect');
        }
    },

    // invoked by the selection model to maintain visual UI cues
    doItemSelect: function(me, record) {
        if (me.container) {
            var item = me.container.getViewItems()[me.getStore().indexOf(record)];
            if (Ext.isElement(item)) {
                item = Ext.get(item);
            }
            if (item) {
                item.removeCls(me.getPressedCls());
                item.addCls(me.getSelectedCls());
            }
        }
    },

    // invoked by the selection model to maintain visual UI cues
    onItemDeselect: function(record, suppressEvent) {
        var me = this;
        if (me.container) {
            if (suppressEvent) {
                me.doItemDeselect(me, record);
            }
            else {
                me.fireAction('deselect', [me, record, suppressEvent], 'doItemDeselect');
            }
        }
    },

    doItemDeselect: function(me, record) {
        var item = me.container.getViewItems()[me.getStore().indexOf(record)];

        if (Ext.isElement(item)) {
            item = Ext.get(item);
        }

        if (item) {
            item.removeCls([me.getPressedCls(), me.getSelectedCls()]);
        }
    },

    updateData: function(data) {
        var store = this.getStore();
        if (!store) {
            this.setStore(Ext.create('Ext.data.Store', {
                data: data
            }));
        } else {
            store.add(data);
        }
    },

    applyStore: function(store) {
        var me = this,
            bindEvents = Ext.apply({}, me.storeEventHooks, { scope: me }),
            proxy, reader;

        if (store) {
            store = Ext.data.StoreManager.lookup(store);
            if (store && Ext.isObject(store) && store.isStore) {
                store.on(bindEvents);
                proxy = store.getProxy();
                if (proxy) {
                    reader = proxy.getReader();
                    if (reader) {
                        reader.on('exception', 'handleException', this);
                    }
                }
            }
            else {
                Ext.Logger.warn("The specified Store cannot be found", this);
            }
        }

        return store;
    },

    /**
     * Method called when the Store's Reader throws an exception
     * @method handleException
     */
    handleException: function() {
        this.setMasked(false);
    },

    updateStore: function(newStore, oldStore) {
        var me = this,
            bindEvents = Ext.apply({}, me.storeEventHooks, { scope: me }),
            proxy, reader;

        if (oldStore && Ext.isObject(oldStore) && oldStore.isStore) {
            if (oldStore.autoDestroy) {
                oldStore.destroy();
            }
            else {
                oldStore.un(bindEvents);
                proxy = oldStore.getProxy();
                if (proxy) {
                    reader = proxy.getReader();
                    if (reader) {
                        reader.un('exception', 'handleException', this);
                    }
                }
            }
        }

        if (newStore) {
            if (newStore.isLoaded()) {
                this.hasLoadedStore = true;
            }

            if (newStore.isLoading()) {
                me.onBeforeLoad();
            }
            if (me.container) {
                me.refresh();
            }
        }
    },

    onBeforeLoad: function() {
        var scrollable = this.getScrollable();
        if (scrollable) {
            scrollable.getScroller().stopAnimation();
        }

        var loadingText = this.getLoadingText();
        if (loadingText) {
            this.setMasked({
                xtype: 'loadmask',
                message: loadingText
            });

            //disable scorlling while it is masked
            if (scrollable) {
                scrollable.getScroller().setDisabled(true);
            }
        }

        this.hideEmptyText();
    },

    updateEmptyText: function(newEmptyText) {
        var me = this;
        if (newEmptyText) {
            me.emptyTextCmp = me.add({
                xtype: 'component',
                cls: me.getBaseCls() + '-emptytext',
                html: newEmptyText,
                hidden: true
            });
        }
        else if (me.emptyTextCmp) {
            me.remove(me.emptyTextCmp, true);
            delete me.emptyTextCmp;
        }
    },

    onLoad: function(store) {
        var scrollable = this.getScrollable();

        //remove any masks on the store
        this.hasLoadedStore = true;
        this.setMasked(false);

        //enable the scroller again
        if (scrollable) {
            scrollable.getScroller().setDisabled(false);
        }
        if (!store.getCount()) {
            this.showEmptyText();
        }
    },

    /**
     * Refreshes the view by reloading the data from the store and re-rendering the template.
     */
    refresh: function() {
        var me = this,
            container = me.container;

        if (!me.getStore()) {
            if (!me.hasLoadedStore && !me.getDeferEmptyText()) {
                me.showEmptyText();
            }
            return;
        }
        if (container) {
            me.fireAction('refresh', [me], 'doRefresh');
        }
    },

    applyItemTpl: function(config) {
        return (Ext.isObject(config) && config.isTemplate) ? config : new Ext.XTemplate(config);
    },

    onAfterRender: function() {
        var me = this;
        me.callParent(arguments);
        me.updateStore(me.getStore());
    },

    getViewItems: function() {
        return this.container.getViewItems();
    },

    doRefresh: function(me) {
        var container = me.container,
            store = me.getStore(),
            records = store.getRange(),
            items = container.getViewItems(),
            recordsLn = records.length,
            itemsLn = items.length,
            deltaLn = recordsLn - itemsLn,
            scrollable = me.getScrollable(),
            i, item;

        if (this.getScrollToTopOnRefresh() && scrollable) {
            scrollable.getScroller().scrollToTop();
        }

        // No items, hide all the items from the collection.
        if (recordsLn < 1) {
            me.onStoreClear();
            return;
        }

        // Too many items, hide the unused ones
        if (deltaLn < 0) {
            container.moveItemsToCache(itemsLn + deltaLn, itemsLn - 1);
            // Items can changed, we need to refresh our references
            items = container.getViewItems();
            itemsLn = items.length;
        }
        // Not enough items, create new ones
        else if (deltaLn > 0) {
            container.moveItemsFromCache(store.getRange(itemsLn));
        }

        // Update Data and insert the new html for existing items
        for (i = 0; i < itemsLn; i++) {
            item = items[i];
            container.updateListItem(records[i], item);
        }
    },

    showEmptyText: function() {
        if (this.hasLoadedStore && this.getEmptyText()) {
            this.emptyTextCmp.show();
        }
    },

    hideEmptyText: function() {
        if (this.getEmptyText()) {
            this.emptyTextCmp.hide();
        }
    },

    onStoreClear: function() {
        var me = this,
            container = me.container,
            items = container.getViewItems();

        container.moveItemsToCache(0, items.length - 1);
        this.showEmptyText();
    },

    // private
    onStoreAdd: function(store, records) {
        if (records) {
            this.container.moveItemsFromCache(records);
        }
    },

    // private
    onStoreRemove: function(store, records, indices) {
        var container = this.container,
            ln = records.length,
            i;
        for (i = 0; i < ln; i++) {
            container.moveItemsToCache(indices[i], indices[i]);
        }
    },

    // private
    onStoreUpdate: function(store, record, newIndex, oldIndex) {
        var me = this,
            container = me.container;
        oldIndex = (typeof oldIndex === 'undefined') ? newIndex : oldIndex;


        if (oldIndex !== newIndex) {
            container.moveItemsToCache(oldIndex, oldIndex);
            container.moveItemsFromCache([record]);
        }
        else {
            // Bypassing setter because sometimes we pass the same record (different data)
            container.updateListItem(record, container.getViewItems()[newIndex]);
        }
    }
});

/**
 * List is a custom styled DataView which allows Grouping, Indexing, Icons, and a Disclosure.
 *
 * ## Example:
 *
 * Here is an example of the usage in a {@link Ext.List}:
 *
 *     @example miniphone preview
 *     Ext.define('Contact', {
 *        extend: 'Ext.data.Model',
 *        fields: ['firstName', 'lastName']
 *     });
 *
 *     var store = Ext.create('Ext.data.Store', {
 *        model: 'Contact',
 *        sorters: 'lastName',
 *
 *        grouper: {
 *            groupFn: function(record) {
 *                return record.get('lastName')[0];
 *            }
 *        },
 *
 *        data: [
 *            {firstName: 'Tommy',   lastName: 'Maintz'},
 *            {firstName: 'Rob',     lastName: 'Dougan'},
 *            {firstName: 'Ed',      lastName: 'Spencer'},
 *            {firstName: 'Jamie',   lastName: 'Avins'},
 *            {firstName: 'Aaron',   lastName: 'Conran'},
 *            {firstName: 'Dave',    lastName: 'Kaneda'},
 *            {firstName: 'Jacky',   lastName: 'Nguyen'},
 *            {firstName: 'Abraham', lastName: 'Elias'},
 *            {firstName: 'Jay',     lastName: 'Robinson'},
 *            {firstName: 'Nigel',   lastName: 'White'},
 *            {firstName: 'Don',     lastName: 'Griffin'},
 *            {firstName: 'Nico',    lastName: 'Ferrero'},
 *            {firstName: 'Nicolas', lastName: 'Belmonte'},
 *            {firstName: 'Jason',   lastName: 'Johnston'}
 *        ]
 *     });
 *
 *     Ext.create('Ext.List', {
 *        fullscreen: true,
 *        itemTpl: '<div class="contact">{firstName} <strong>{lastName}</strong></div>',
 *        store: store,
 *        grouped: true
 *     });
 *
*/
Ext.define('Ext.dataview.List', {
    alternateClassName: 'Ext.List',
    extend: 'Ext.dataview.DataView',
    xtype : 'list',

    requires: [
        'Ext.dataview.element.List',
        'Ext.dataview.IndexBar',
        'Ext.dataview.ListItemHeader'
    ],

    /**
     * @event disclose
     * @preventable doDisclose
     * Fires whenever a disclosure is handled
     * @param {Ext.dataview.List} this The List instance
     * @param {Ext.data.Model} record The record assisciated to the item
     * @param {HTMLElement} target The element doubletapped
     * @param {Number} index The index of the item doubletapped
     * @param {Ext.EventObject} e The event object
     */

    config: {
        /**
         * @cfg {Boolean/Object} indexBar
         * True to render an alphabet IndexBar docked on the right.
         * This can also be a config object that will be passed to {@link Ext.IndexBar}
         * (defaults to false)
         * @accessor
         */
        indexBar: false,

        icon: null,

        /**
         * @cfg {Boolean} clearSelectionOnDeactivate
         * @removed 2.0.0
         */

        /**
         * @cfg {Boolean} preventSelectionOnDisclose True to prevent the item selection when the user
         * taps a disclose icon. Defaults to <tt>true</tt>
         * @accessor
         */
        preventSelectionOnDisclose: true,

        // @inherit
        baseCls: Ext.baseCSSPrefix + 'list',

        /**
         * @cfg {Boolean} pinHeaders
         * Whether or not to pin headers on top of item groups while scrolling for an iPhone native list experience.
         * @accessor
         */
        pinHeaders: true,

        /**
         * @cfg {Boolean} grouped
         * Whether or not to group items in the provided Store with a header for each item.
         * @accessor
         */
        grouped: false,

        /**
         * @cfg {Boolean/Function/Object} onItemDisclosure
         * True to display a disclosure icon on each list item.
         * The list will still fire the disclose event, and the event can be stopped before itemtap.
         * By setting this config to a function, the function passed will be called when the disclosure
         * is tapped.
         * Finally you can specify an object with a 'scope' and 'handler'
         * property defined. This will also be bound to the tap event listener
         * and is useful when you want to change the scope of the handler.
         * @accessor
         */
        onItemDisclosure: null,

        /**
         * @cfg {String} ui
         * The style of this list. Available options are `normal` and `round`.
         */
        ui: 'normal'

        /**
         * @cfg {Boolean} useComponents
         * Flag the use a component based DataView implementation.  This allows the full use of components in the
         * DataView at the cost of some performance.
         *
         * Checkout the [DataView Guide](#!/guide/dataview) for more information on using this configuration.
         * @accessor
         * @private
         */

        /**
         * @cfg {Object} itemConfig
         * A configuration object that is passed to every item created by a component based DataView. Because each
         * item that a DataView renders is a Component, we can pass configuration options to each component to
         * easily customize how each child component behaves.
         * Note this is only used when useComponents is true.
         * @accessor
         * @private
         */

        /**
         * @cfg {Number} maxItemCache
         * Maintains a cache of reusable components when using a component based DataView.  Improveing performance at
         * the cost of memory.
         * Note this is currently only used when useComponents is true.
         * @accessor
         * @private
         */

        /**
         * @cfg {String} defaultType
         * The xtype used for the component based DataView. Defaults to dataitem.
         * Note this is only used when useComponents is true.
         * @accessor
         * @private
         */
    },

    constructor: function() {
        this.translateHeader = (Ext.os.is.Android2) ? this.translateHeaderCssPosition : this.translateHeaderTransform;
        this.callParent(arguments);
    },

    // apply to the selection model to maintain visual UI cues
    onItemTrigger: function(me, index, target, record, e) {
        if (!(this.getPreventSelectionOnDisclose() && Ext.fly(e.target).hasCls(this.getBaseCls() + '-disclosure'))) {
            this.callParent(arguments);
        }
    },

    initialize: function() {
        var me = this,
            container;

        me.on(me.getTriggerCtEvent(), me.onContainerTrigger, me);

        container = me.container = this.add(new Ext.dataview.element.List({
            baseCls: this.getBaseCls()
        }));
        container.dataview = me;

        me.on(me.getTriggerEvent(), me.onItemTrigger, me);

        container.element.on({
            delegate: '.' + this.getBaseCls() + '-disclosure',
            tap: 'handleItemDisclosure',
            scope: me
        });

        container.on({
            itemtouchstart: 'onItemTouchStart',
            itemtouchend: 'onItemTouchEnd',
            itemtap: 'onItemTap',
            itemtaphold: 'onItemTapHold',
            itemtouchmove: 'onItemTouchMove',
            itemdoubletap: 'onItemDoubleTap',
            itemswipe: 'onItemSwipe',
            scope: me
        });

        if (this.getStore()) {
            this.refresh();
        }
    },

    updateInline: function(newInline) {
        this.callParent(arguments);
        if (newInline) {
            this.setOnItemDisclosure(false);
            this.setIndexBar(false);
            this.setGrouped(false);
        }
    },

    applyIndexBar: function(indexBar) {
        return Ext.factory(indexBar, Ext.dataview.IndexBar, this.getIndexBar());
    },

    updateIndexBar: function(indexBar) {
        if (indexBar && this.getScrollable()) {
            this.indexBarElement = this.getScrollableBehavior().getScrollView().getElement().appendChild(indexBar.renderElement);

            indexBar.on({
                index: 'onIndex',
                scope: this
            });

            this.element.addCls(this.getBaseCls() + '-indexed');
        }
    },

    updateGrouped: function(grouped) {
        var baseCls = this.getBaseCls(),
            cls = baseCls + '-grouped',
            unCls = baseCls + '-ungrouped';

        if (grouped) {
            this.addCls(cls);
            this.removeCls(unCls);
            this.doRefreshHeaders();
            this.updatePinHeaders(this.getPinHeaders());
        }
        else {
            this.addCls(unCls);
            this.removeCls(cls);

            if (this.container) {
                this.container.doRemoveHeaders();
            }

            this.updatePinHeaders(null);
        }
    },

    updatePinHeaders: function(pinnedHeaders) {
        var scrollable = this.getScrollable(),
            scroller;

        if (scrollable) {
            scroller = scrollable.getScroller();
        }

        if (!scrollable) {
            return;
        }

        if (pinnedHeaders && this.getGrouped()) {
            scroller.on({
                refresh: 'doRefreshHeaders',
                scroll: 'onScroll',
                scope: this
            });

            if (!this.header || !this.header.renderElement.dom) {
                this.createHeader();
            }
        } else {
            scroller.un({
                refresh: 'doRefreshHeaders',
                scroll: 'onScroll',
                scope: this
            });

            if (this.header) {
                this.header.destroy();
            }
        }
    },

    createHeader: function() {
        var header,
            scrollable = this.getScrollable(),
            scroller, scrollView, scrollViewElement;

        if (scrollable) {
            scroller = scrollable.getScroller();
            scrollView = this.getScrollableBehavior().getScrollView();
            scrollViewElement = scrollView.getElement();
        }
        else {
            return;
        }

        this.header = header = Ext.create('Ext.dataview.ListItemHeader', {
            html: ' ',
            cls: 'x-list-header-swap'
        });
        scrollViewElement.dom.insertBefore(header.element.dom, scroller.getContainer().dom.nextSibling);
        this.translateHeader(1000);
    },

    // We need to use the same events as the DataView so we are sure we run AFTER the list populates
    refresh: function() {
        this.callParent();
        this.doRefreshHeaders();
    },

    onStoreAdd: function() {
        this.callParent(arguments);
        this.doRefreshHeaders();
    },

    onStoreRemove: function() {
        this.callParent(arguments);
        this.doRefreshHeaders();
    },

    onStoreUpdate: function() {
        this.callParent(arguments);
        this.doRefreshHeaders();
    },

    onStoreClear: function() {
        this.callParent();
        if (this.header) {
            this.header.destroy();
        }
        this.doRefreshHeaders();
    },

    // @private
    getClosestGroups : function() {
        var groups = this.pinHeaderInfo.offsets,
            scrollable = this.getScrollable(),
            ln = groups.length,
            i = 0,
            pos, group, current, next;

        if (scrollable) {
            pos = scrollable.getScroller().position;
        }
        else {
            return {
                current: 0,
                next: 0
            };
        }

        for (; i < ln; i++) {
            group = groups[i];
            if (group.offset > pos.y) {
                next = group;
                break;
            }
            current = group;
        }

        return {
            current: current,
            next: next
        };
    },

    doRefreshHeaders: function() {
        if (!this.getGrouped() || !this.container) {
            return false;
        }

        var headerIndices = this.findGroupHeaderIndices(),
            ln = headerIndices.length,
            items = this.container.getViewItems(),
            headerInfo = this.pinHeaderInfo = {offsets: []},
            headerOffsets = headerInfo.offsets,
            scrollable = this.getScrollable(),
            scroller, scrollPosition, i, headerItem, header;

        if (ln) {
            for (i = 0; i < ln; i++) {
                headerItem = items[headerIndices[i]];
                if (headerItem) {
                    header = this.getItemHeader(headerItem);

                    headerOffsets.push({
                        header: header,
                        offset: headerItem.offsetTop
                    });
                }
            }

            headerInfo.closest = this.getClosestGroups();
            this.setActiveGroup(headerInfo.closest.current);
            if (header) {
                headerInfo.headerHeight = Ext.fly(header).getHeight();
            }

            // Ensure the pinned header is positioned correctly
            if (scrollable) {
                scroller = scrollable.getScroller();
                scrollPosition = scroller.position;
                this.onScroll(scroller, scrollPosition.x, scrollPosition.y);
            }
        }
    },

    getItemHeader: function(item) {
        return item.childNodes[0];
    },

    onScroll: function(scroller, x, y) {
        var me = this,
            headerInfo = me.pinHeaderInfo,
            closest = headerInfo.closest,
            activeGroup = me.activeGroup,
            headerHeight = headerInfo.headerHeight,
            next, current;

        if (!closest) {
            return;
        }

        next = closest.next;
        current = closest.current;

        if (!this.header || !this.header.renderElement.dom) {
            this.createHeader();
        }

        if (y <= 0) {
            if (activeGroup) {
                me.setActiveGroup(false);
                closest.next = current;
            }
            this.translateHeader(1000);
            return;
        }
        else if ((next && y > next.offset) || (current && y < current.offset)) {
            closest = headerInfo.closest = this.getClosestGroups();
            next = closest.next;
            current = closest.current;
            this.setActiveGroup(current);
        }

        if (next && y > 0 && next.offset - y <= headerHeight) {
            var headerOffset = headerHeight - (next.offset - y);
            this.translateHeader(headerOffset);
        }
        else {
            this.translateHeader(null);
        }
    },

    translateHeaderTransform: function(offset) {
        this.header.renderElement.dom.style.webkitTransform = (offset === null) ? null : 'translate3d(0px, -' + offset + 'px, 0px)';
    },

    translateHeaderCssPosition: function(offset) {
        this.header.renderElement.dom.style.top = (offset === null) ? null : '-' + Math.round(offset) + 'px';
    },

    /**
     * Set the current active group
     * @param {Object} group The group to set active
     * @private
     */
    setActiveGroup : function(group) {
        var me = this,
            header = me.header;
        if (header) {
            if (group) {
                if (!me.activeGroup || me.activeGroup.header != group.header) {
                    header.show();

                    if (header.element) {
                        header.setHtml(group.header.innerHTML);
                    }
                }
            } else if (header && header.dom) {
                header.hide();
            }
        }

        this.activeGroup = group;
    },

    onIndex: function(indexBar, index) {
        var me = this,
            key = index.toLowerCase(),
            store = me.getStore(),
            groups = store.getGroups(),
            ln = groups.length,
            scrollable = me.getScrollable(),
            scroller, group, i, closest, id, item;

        if (scrollable) {
            scroller = me.getScrollable().getScroller();
        }
        else {
            return;
        }

        for (i = 0; i < ln; i++) {
            group = groups[i];
            id = group.name.toLowerCase();
            if (id == key || id > key) {
                closest = group;
                break;
            }
            else {
                closest = group;
            }
        }

        if (scrollable && closest) {
            item = me.container.getViewItems()[store.indexOf(closest.children[0])];

            //stop the scroller from scrolling
            scroller.stopAnimation();

            //make sure the new offsetTop is not out of bounds for the scroller
            var containerSize = scroller.getContainerSize().y,
                size = scroller.getSize().y,
                maxOffset = size - containerSize,
                offset = (item.offsetTop > maxOffset) ? maxOffset : item.offsetTop;

            scroller.scrollTo(0, offset);
        }
    },

    applyOnItemDisclosure: function(config) {
        if (Ext.isFunction(config)) {
            return {
                scope: this,
                handler: config
            };
        }
        return config;
    },

    handleItemDisclosure: function(e) {
        var me = this,
            item = e.getTarget().parentNode,
            index = me.container.getViewItems().indexOf(item),
            record = me.getStore().getAt(index);

        me.fireAction('disclose', [me, record, item, index, e], 'doDisclose');
    },

    doDisclose: function(me, record, item, index, e) {
        var onItemDisclosure = me.getOnItemDisclosure();

        if (onItemDisclosure && onItemDisclosure.handler) {
            onItemDisclosure.handler.call(me, record, item, index, e);
        }
    },

    findGroupHeaderIndices: function() {
        if (!this.getGrouped()) {
            return [];
        }
        var me = this,
            store = me.getStore();
        if (!store) {
            return [];
        }

        var container = me.container,
            groups = store.getGroups(),
            groupLn = groups.length,
            items = container.getViewItems(),
            newHeaderItems = [],
            footerClsShortCache = container.footerClsShortCache,
            i, firstGroupedRecord, index, item, lastGroup;

        container.doRemoveHeaders();
        container.doRemoveFooterCls();

        if (items.length) {
            for (i = 0; i < groupLn; i++) {
                firstGroupedRecord = groups[i].children[0];
                index = store.indexOf(firstGroupedRecord);
                item = items[index];
                container.doAddHeader(item, store.getGroupString(firstGroupedRecord));
                // Skip footer before the first Header
                if (i) {
                    Ext.fly(item.previousSibling).addCls(footerClsShortCache);
                }
                newHeaderItems.push(index);
            }
            // Add footer before the last item
            lastGroup = groups[--i].children;
            Ext.fly(items[store.indexOf(lastGroup[lastGroup.length - 1])]).addCls(footerClsShortCache);
        }

        return newHeaderItems;
    },

    destroy: function() {
        Ext.destroy(this.getIndexBar(), this.indexBarElement, this.header);
        this.callParent();
    }
});

/**
 * NestedList provides a miller column interface to navigate between nested sets
 * and provide a clean interface with limited screen real-estate.
 *
 *     @example miniphone preview
 *      var data = {
 *          text: 'Groceries',
 *          items: [{
 *              text: 'Drinks',
 *              items: [{
 *                  text: 'Water',
 *                  items: [{
 *                      text: 'Sparkling',
 *                      leaf: true
 *                  }, {
 *                      text: 'Still',
 *                      leaf: true
 *                  }]
 *              }, {
 *                  text: 'Coffee',
 *                  leaf: true
 *              }, {
 *                  text: 'Espresso',
 *                  leaf: true
 *              }, {
 *                  text: 'Redbull',
 *                  leaf: true
 *              }, {
 *                  text: 'Coke',
 *                  leaf: true
 *              }, {
 *                  text: 'Diet Coke',
 *                  leaf: true
 *              }]
 *          }, {
 *              text: 'Fruit',
 *              items: [{
 *                  text: 'Bananas',
 *                  leaf: true
 *              }, {
 *                  text: 'Lemon',
 *                  leaf: true
 *              }]
 *          }, {
 *              text: 'Snacks',
 *              items: [{
 *                  text: 'Nuts',
 *                  leaf: true
 *              }, {
 *                  text: 'Pretzels',
 *                  leaf: true
 *              }, {
 *                  text: 'Wasabi Peas',
 *                  leaf: true
 *              }]
 *          }]
 *      };
 *
 *      Ext.define('ListItem', {
 *          extend: 'Ext.data.Model',
 *          fields: [{
 *              name: 'text',
 *              type: 'string'
 *          }]
 *      });
 *
 *      var store = Ext.create('Ext.data.TreeStore', {
 *          model: 'ListItem',
 *          defaultRootProperty: 'items',
 *          root: data
 *      });
 *
 *      var nestedList = Ext.create('Ext.NestedList', {
 *          fullscreen: true,
 *          title: 'Groceries',
 *          displayField: 'text',
 *          store: store
 *      });
 *
 * @aside guide nested_list
 */
Ext.define('Ext.dataview.NestedList', {
    alternateClassName: 'Ext.NestedList',
    extend: 'Ext.Container',
    xtype : 'nestedlist',
    requires: [
        'Ext.List',
        'Ext.Toolbar',
        'Ext.Button',
        'Ext.XTemplate',
        'Ext.data.StoreManager',
        'Ext.data.NodeStore',
        'Ext.data.TreeStore'
    ],

    config: {
        // @inherit
        cls: Ext.baseCSSPrefix + 'nested-list',

        /**
         * @cfg {String/Object/Boolean} cardSwitchAnimation
         * @removed 2.0.0 please use {@link Ext.layout.Card#animation}
         */

        /**
         * @cfg {String} backText
         * The label to display for the back button. Defaults to "Back".
         * @accessor
         */
        backText: 'Back',

        /**
         * @cfg {Boolean} useTitleAsBackText
         * @accessor
         */
        useTitleAsBackText: true,

        /**
         * @cfg {Boolean} updateTitleText
         * Update the title with the currently selected category. Defaults to true.
         * @accessor
         */
        updateTitleText: true,

        /**
         * @cfg {String} displayField
         * Display field to use when setting item text and title.
         * This configuration is ignored when overriding getItemTextTpl or
         * getTitleTextTpl for the item text or title. (Defaults to 'text')
         * @accessor
         */
        displayField: 'text',

        /**
         * @cfg {String} loadingText
         * Loading text to display when a subtree is loading.
         * @accessor
         */
        loadingText: 'Loading...',

        /**
         * @cfg {String} emptyText
         * Empty text to display when a subtree is empty.
         * @accessor
         */
        emptyText: 'No items available.',

        /**
         * @cfg {Boolean/Function} onItemDisclosure
         * Maps to the Ext.List onItemDisclosure configuration for individual lists. (Defaults to false)
         * @accessor
         */
        onItemDisclosure: false,

        /**
         * @cfg {Boolean} allowDeselect
         * Set to true to alow the user to deselect leaf items via interaction.
         * Defaults to false.
         * @accessor
         */
        allowDeselect: false,

        /**
         * @deprecated 2.0.0 Please set the {@link #toolbar} configuration to `false` instead
         * @cfg {Boolean} useToolbar True to show the header toolbar.
         * @accessor
         */
        useToolbar: null,

        /**
         * @cfg {Ext.Toolbar/Object/Boolean} toolbar
         * The configuration to be used for the toolbar displayed in this nested list.
         * @accessor
         */
        toolbar: {
            docked: 'top',
            xtype: 'titlebar',
            ui: 'light',
            inline: true
        },

        /**
         * @cfg {String} title The title of the toolbar
         * @accessor
         */
        title: '',

        /**
         * @cfg {String} layout
         * @hide
         * @accessor
         */
        layout: {
            type: 'card',
            animation: {
                type: 'slide',
                duration: 250,
                direction: 'left'
            }
        },

        /**
         * @cfg {Ext.data.TreeStore} store The tree store to be used for this nested list.
         */
        store: null,

        /**
         * @cfg {Ext.Container} detailContainer The container of the detailCard.
         * @accessor
         */
        detailContainer: undefined,

        /**
         * @cfg {Ext.Component} detailCard to provide a final card for leaf nodes.
         * @accessor
         */
        detailCard: null,

        /**
         * @cfg {Object} backButton The configuration for the back button used in the nested list
         */
        backButton: {
            ui: 'back',
            hidden: true
        },

        /**
         * @cfg {Object} listConfig An optional config object which is merged with the default
         * configuration used to create each nested list
         */
        listConfig: null,

        // @private
        lastNode: null,

        // @private
        lastActiveList: null
    },

    /**
     * @event itemtap
     * Fires when a node is tapped on
     * @param {Ext.dataview.NestedList} this
     * @param {Ext.dataview.List} list The Ext.dataview.List that is currently active
     * @param {Number} index The index of the item tapped
     * @param {Ext.dom.Element} target The element tapped
     * @param {Ext.data.Record} record The record tapped
     * @param {Ext.event.Event} e The event object
     */

    /**
     * @event itemdoubletap
     * Fires when a node is double tapped on
     * @param {Ext.dataview.NestedList} this
     * @param {Ext.dataview.List} list The Ext.dataview.List that is currently active
     * @param {Number} index The index of the item that was tapped
     * @param {Object} item The item tapped
     * @param {Ext.event.Event} e The event object
     */

    /**
     * @event containertap
     * Fires when a tap occurs and it is not on a template node.
     * @param {Ext.dataview.NestedList} this
     * @param {Ext.dataview.List} list The Ext.dataview.List that is currently active
     * @param {Ext.event.Event} e The raw event object
     */

    /**
     * @event selectionchange
     * Fires when the selected nodes change.
     * @param {Ext.dataview.NestedList} this
     * @param {Ext.dataview.List} list The Ext.datavaie.List that is currently active
     * @param {Array} selections Array of the selected nodes
     */

    /**
     * @event beforeselect
     * Fires before a selection is made.
     * @param {Ext.dataview.NestedList} this
     * @param {Ext.dataview.List} list The Ext.dataview.List that is currently active
     * @param {HTMLElement} node The node to be selected
     * @param {Array} selections Array of currently selected nodes
     */

    /**
     * @event listchange
     * Fires when the user taps a list item
     * @param {Ext.dataview.NestedList} this
     * @param {Object} listitem The new active list
     */

    /**
     * @event leafitemtap
     * Fires when the user taps a leaf list item
     * @param {Ext.dataview.NestedList} this
     * @param {Ext.List} list The subList the item is on
     * @param {Number} index The index of the item tapped
     * @param {Ext.dom.Element} target The element tapped
     * @param {Ext.data.Record} record The record tapped
     * @param {Ext.event.Event} e The event
     */

    /**
     * @event back
     * @preventable doBack
     * Fires when the user taps Back
     * @param {Ext.dataview.NestedList} this
     * @param {HTMLElement} node The node to be selected
     * @param {Ext.dataview.List} lastActiveList The Ext.dataview.List that was last active
     * @param {Boolean} detailCardActive Flag set if the detail card is currently active
     */

    /**
     * @event beforeload
     * Fires before a request is made for a new data object.
     * @param {Ext.dataview.NestedList} this
     * @param {Ext.data.Store} store The store instance
     * @param {Ext.data.Operation} operation The Ext.data.Operation object that will be passed to the Proxy to
     * load the Store
     */

    /**
     * @event load
     * Fires whenever records have been loaded into the store.
     * @param {Ext.dataview.NestedList} this
     * @param {Ext.data.Store} store The store instance
     * @param {Ext.util.Grouper[]} records An array of records
     * @param {Boolean} successful True if the operation was successful.
     * @param {Ext.data.Operation} operation The associated operation
     */

    constructor: function(config) {
        if (Ext.isObject(config)) {
            if (config.getTitleTextTpl) {
                this.getTitleTextTpl = config.getTitleTextTpl;
            }
            if (config.getItemTextTpl) {
                this.getItemTextTpl = config.getItemTextTpl;
            }
        }
        this.callParent(arguments);
    },

    onItemInteraction: function() {
        if (this.isGoingTo) {
            return false;
        }
    },

    applyDetailContainer: function(config) {
        if (!config) {
            config = this;
        }

        return config;
    },

    updateDetailContainer: function(newContainer, oldContainer) {
        newContainer.onBefore('activeitemchange', 'onBeforeDetailContainerChange', this);
        newContainer.onAfter('activeitemchange', 'onDetailContainerChange', this);
    },

    onBeforeDetailContainerChange: function() {
        this.isGoingTo = true;
    },

    onDetailContainerChange: function() {
        this.isGoingTo = false;
    },

    /**
     * Called when an list item has been tapped
     * @param {Ext.List} list The subList the item is on
     * @param {Number} index The id of the item tapped
     * @param {Ext.Element} target The list item tapped
     * @param {Ext.data.Record} record The record whichw as tapped
     * @param {Ext.event.Event} e The event
     */
    onItemTap: function(list, index, target, record, e) {
        var me = this,
            store = list.getStore(),
            node = store.getAt(index);

        me.fireEvent('itemtap', this, list, index, target, record, e);
        if (node.isLeaf()) {
            me.fireEvent('leafitemtap', this, list, index, target, record, e);
            me.goToLeaf(node);
        }
        else {
            this.goToNode(node);
        }
    },

    onBeforeSelect: function() {
        this.fireEvent('beforeselect', [this, Array.prototype.slice.call(arguments)]);
    },

    onContainerTap: function() {
        this.fireEvent('containertap', [this, Array.prototype.slice.call(arguments)]);
    },

    onSelectionChange: function() {
        this.fireEvent('selectionchange', [this, Array.prototype.slice.call(arguments)]);
    },

    onItemDoubleTap: function() {
        this.fireEvent('itemdoubletap', [this, Array.prototype.slice.call(arguments)]);
    },

    onStoreBeforeLoad: function() {
        var loadingText = this.getLoadingText(),
            scrollable = this.getScrollable();

        if (loadingText) {
            this.setMasked({
                xtype: 'loadmask',
                message: loadingText
            });

            //disable scorlling while it is masked
            if (scrollable) {
                scrollable.getScroller().setDisabled(true);
            }
        }

        this.fireEvent('beforeload', [this, Array.prototype.slice.call(arguments)]);
    },

    onStoreLoad: function() {
        this.setMasked(false);
        this.fireEvent('load', [this, Array.prototype.slice.call(arguments)]);
    },

    /**
     * Called when the backButton has been tapped
     */
    onBackTap: function() {
        var me = this,
            node = me.getLastNode(),
            detailCard = me.getDetailCard(),
            detailCardActive = detailCard && me.getActiveItem() == detailCard,
            lastActiveList = me.getLastActiveList();

        this.fireAction('back', [this, node, lastActiveList, detailCardActive], 'doBack');
    },

    doBack: function(me, node, lastActiveList, detailCardActive) {
        var layout = me.getLayout(),
            animation = (layout) ? layout.getAnimation() : null;

        if (detailCardActive && lastActiveList) {
            if (animation) {
                animation.setReverse(true);
            }
            me.setActiveItem(lastActiveList);
            me.setLastNode(node.parentNode);
            me.syncToolbar();
        }
        else {
            this.goToNode(node.parentNode);
        }
    },

    updateData: function(data) {
        if (!this.getStore()) {
            this.setStore(new Ext.data.TreeStore({
                root: data
            }));
        }
    },

    applyStore: function(store) {
        if (store) {
            store = Ext.data.StoreManager.lookup(store);

            if (store && Ext.isObject(store) && store.isStore) {
                store.on({
                    scope: this,
                    load: 'onStoreLoad',
                    beforeload: 'onStoreBeforeLoad'
                });
            }
            else if (!store)  {
                Ext.Logger.warn("The specified Store cannot be found", this);
            }
        }

        return store;
    },

    updateStore: function(newStore, oldStore) {
        var me = this;
        if (oldStore && Ext.isObject(oldStore) && oldStore.isStore) {
            if (oldStore.autoDestroy) {
                oldStore.destroy();
            }
            oldStore.un({
                rootchange: 'onStoreRootChange',
                scope: this
            });
        }

        if (newStore) {
            me.goToNode(newStore.getRoot());
            newStore.on({
                rootchange: 'onStoreRootChange',
                scope: this
            });
        }
    },

    onStoreRootChange: function(store, node) {
        this.goToNode(node);
    },

    applyBackButton: function(config) {
        return Ext.factory(config, Ext.Button, this.getBackButton());
    },

    applyDetailCard: function(config) {
        return this.factoryItem(config);
    },

    updateBackButton: function(newButton, oldButton) {
        if (newButton) {
            var me = this;
            newButton.on('tap', me.onBackTap, me);
            newButton.setText(me.getBackText());
            me.getToolbar().insert(0, newButton);
        }
        else if (oldButton) {
            oldButton.destroy();
        }
    },

    applyToolbar: function(config) {
        return Ext.factory(config, Ext.TitleBar, this.getToolbar());
    },

    updateToolbar: function(newToolbar, oldToolbar) {
        var me = this;
        if (newToolbar) {
            newToolbar.setTitle(me.getTitle());
            if (!newToolbar.getParent()) {
                me.add(newToolbar);
            }
        }
        else if (oldToolbar) {
            oldToolbar.destroy();
        }
    },

    updateUseToolbar: function(newUseToolbar, oldUseToolbar) {
        if (!newUseToolbar) {
            this.setToolbar(false);
        }
    },

    updateTitle: function(newTitle) {
        var me = this,
            toolbar = me.getToolbar();
        if (toolbar) {
            if (me.getUpdateTitleText()) {
                toolbar.setTitle(newTitle);
            }
        }
    },

    /**
     * Override this method to provide custom template rendering of individual
     * nodes. The template will receive all data within the Record and will also
     * receive whether or not it is a leaf node.
     * @param {Ext.data.Record} node
     */
    getItemTextTpl: function(node) {
        return '{' + this.getDisplayField() + '}';
    },

    /**
     * Override this method to provide custom template rendering of titles/back
     * buttons when useTitleAsBackText is enabled.
     * @param {Ext.data.Record} node
     */
    getTitleTextTpl: function(node) {
        return '{' + this.getDisplayField() + '}';
    },

    /**
     * @private
     */
    renderTitleText: function(node, forBackButton) {
        if (!node.titleTpl) {
            node.titleTpl = Ext.create('Ext.XTemplate', this.getTitleTextTpl(node));
        }

        if (node.isRoot()) {
            var initialTitle = this.getInitialConfig('title');
            return (forBackButton && initialTitle === '') ? this.getInitialConfig('backText') : initialTitle;
        }

        return  node.titleTpl.applyTemplate(node.data);
    },

    /**
     * Method to handle going to a specific node within this nested list. Node must be part of the
     * internal {@link #store}.
     * @param {Ext.data.NodeInterface} node The specified node to navigate to
     */
    goToNode: function(node) {
        if (!node) {
            return;
        }

        var me = this,
            activeItem = me.getActiveItem(),
            detailCard = me.getDetailCard(),
            detailCardActive = detailCard && me.getActiveItem() == detailCard,
            reverse = me.goToNodeReverseAnimation(node),
            firstList = me.firstList,
            secondList = me.secondList,
            layout = me.getLayout(),
            animation = (layout) ? layout.getAnimation() : null,
            list;

        //if the node is a leaf, throw an error
        if (node.isLeaf()) {
            throw new Error('goToNode: passed a node which is a leaf.');
        }

        //if we are currently at the passed node, do nothing.
        if (node == me.getLastNode() && !detailCardActive) {
            return;
        }

        if (detailCardActive) {
            if (animation) {
                animation.setReverse(true);
            }
            me.setActiveItem(me.getLastActiveList());
        }
        else {
            if (firstList && secondList) {
                //firstList and secondList have both been created
                activeItem = me.getActiveItem();

                me.setLastActiveList(activeItem);
                list = (activeItem == firstList) ? secondList : firstList;
                list.getStore().setNode(node);
                node.expand();

                if (animation) {
                    animation.setReverse(reverse);
                }
                me.setActiveItem(list);
                list.deselectAll();
            }
            else if (firstList) {
                //only firstList has been created
                me.setLastActiveList(me.getActiveItem());
                me.setActiveItem(me.getList(node));
                me.secondList = me.getActiveItem();
            }
            else {
                //no lists have been created
                me.setActiveItem(me.getList(node));
                me.firstList = me.getActiveItem();
            }
        }

        me.fireEvent('listchange', this, me.getActiveItem());

        me.setLastNode(node);

        me.syncToolbar();
    },



    /**
     * The leaf you want to navigate to. You should pass a node instance.
     * @param {Ext.data.NodeInterface} node The specified node to navigate to
     */
    goToLeaf: function(node) {
        if (!node.isLeaf()) {
            throw new Error('goToLeaf: passed a node which is not a leaf.');
        }

        var me = this,
            card = me.getDetailCard(node),
            container = me.getDetailContainer(),
            sharedContainer = container == this,
            layout = me.getLayout(),
            animation = (layout) ? layout.getAnimation() : false;

        if (card) {
            if (container.getItems().indexOf(card) === -1) {
                container.add(card);
            }
            if (sharedContainer) {
                if (me.getActiveItem() instanceof Ext.dataview.List) {
                    me.setLastActiveList(me.getActiveItem());
                }
                me.setLastNode(node);
            }
            if (animation) {
                animation.setReverse(false);
            }
            container.setActiveItem(card);
            me.syncToolbar();
        }
    },

    /**
     * @private
     * Method which updates the {@link #backButton} and {@link #toolbar} with the latest information from
     * the current node.
     */
    syncToolbar: function(forceDetail) {
        var me = this,
            detailCard = me.getDetailCard(),
            node = me.getLastNode(),
            detailActive = forceDetail || (detailCard && (me.getActiveItem() == detailCard)),
            parentNode = (detailActive) ? node : node.parentNode,
            backButton = me.getBackButton();

        //show/hide the backButton, and update the backButton text, if one exists
        if (backButton) {
            backButton[parentNode ? 'show' : 'hide']();
            if (parentNode && me.getUseTitleAsBackText()) {
                backButton.setText(me.renderTitleText(node.parentNode, true));
            }
        }

        if (node) {
            me.setTitle(me.renderTitleText(node));
        }
    },

    updateBackText: function(newText) {
        this.getBackButton().setText(newText);
    },

    /**
     * @private
     * Returns true if the passed node should have a reverse animation from the previous current node
     * @param {Ext.data.NodeInterface} node
     */
    goToNodeReverseAnimation: function(node) {
        var me = this,
            lastNode = me.getLastNode();
        if (!lastNode) {
            return false;
        }

        return (!lastNode.contains(node) && lastNode.isAncestor(node)) ? true : false;
    },

    /**
     * @private
     * Returns the list config for a specified node.
     * @param {HTMLElement} node The node for the list config
     */
    getList: function(node) {
        var me = this,
            nodeStore = Ext.create('Ext.data.NodeStore', {
                recursive: false,
                node: node,
                rootVisible: false,
                model: me.getStore().getModel()
            });

        node.expand();

        return Ext.Object.merge({
            xtype: 'list',
            pressedDelay: 250,
            autoDestroy: true,
            store: nodeStore,
            onItemDisclosure: me.getOnItemDisclosure(),
            allowDeselect : me.getAllowDeselect(),
            listeners: [
                { event: 'itemdoubletap', fn: 'onItemDoubleTap', scope: me },
                { event: 'itemtap', fn: 'onItemInteraction', scope: me, order: 'before'},
                { event: 'itemtouchstart', fn: 'onItemInteraction', scope: me, order: 'before'},
                { event: 'itemtap', fn: 'onItemTap', scope: me },
                { event: 'beforeselect', fn: 'onBeforeSelect', scope: me },
                { event: 'containertap', fn: 'onContainerTap', scope: me },
                { event: 'selectionchange', fn: 'onSelectionChange', scope: me }
            ],
            itemTpl: '<span<tpl if="leaf == true"> class="x-list-item-leaf"</tpl>>' + me.getItemTextTpl(node) + '</span>'
        }, this.getListConfig());
    }

}, function() {
});


/**
 * A FieldSet is a great way to visually separate elements of a form. It's normally used when you have a form with
 * fields that can be divided into groups - for example a customer's billing details in one fieldset and their shipping
 * address in another. A fieldset can be used inside a form or on its own elsewhere in your app. Fieldsets can
 * optionally have a title at the top and instructions at the bottom. Here's how we might create a FieldSet inside a
 * form:
 *
 *     @example
 *     Ext.create('Ext.form.Panel', {
 *         fullscreen: true,
 *         items: [
 *             {
 *                 xtype: 'fieldset',
 *                 title: 'About You',
 *                 instructions: 'Tell us all about yourself',
 *                 items: [
 *                     {
 *                         xtype: 'textfield',
 *                         name : 'firstName',
 *                         label: 'First Name'
 *                     },
 *                     {
 *                         xtype: 'textfield',
 *                         name : 'lastName',
 *                         label: 'Last Name'
 *                     }
 *                 ]
 *             }
 *         ]
 *     });
 *
 * Above we created a {@link Ext.form.Panel form} with a fieldset that contains two text fields. In this case, all
 * of the form fields are in the same fieldset, but for longer forms we may choose to use multiple fieldsets. We also
 * configured a {@link #title} and {@link #instructions} to give the user more information on filling out the form if
 * required.
 */
Ext.define('Ext.form.FieldSet', {
    extend  : 'Ext.Container',
    alias   : 'widget.fieldset',
    requires: ['Ext.Title'],

    config: {
        // @inherit
        baseCls: Ext.baseCSSPrefix + 'form-fieldset',

        /**
         * @cfg {String} title Optional fieldset title, rendered just above the grouped fields
         * @accessor
         */
        title: null,

        /**
         * @cfg {String} instructions Optional fieldset instructions, rendered just below the grouped fields
         * @accessor
         */
        instructions: null
    },

    // @private
    applyTitle: function(title) {
        if (typeof title == 'string') {
            title = {title: title};
        }

        Ext.applyIf(title, {
            docked : 'top',
            baseCls: this.getBaseCls() + '-title'
        });

        return Ext.factory(title, Ext.Title, this.getTitle());
    },

    // @private
    updateTitle: function(newTitle, oldTitle) {
        if (newTitle) {
            this.add(newTitle);
        }
        if (oldTitle) {
            this.remove(oldTitle);
        }
    },

    // @private
    applyInstructions: function(instructions) {
        if (typeof instructions == 'string') {
            instructions = {title: instructions};
        }

        Ext.applyIf(instructions, {
            docked : 'bottom',
            baseCls: this.getBaseCls() + '-instructions'
        });

        return Ext.factory(instructions, Ext.Title, this.getInstructions());
    },

    // @private
    updateInstructions: function(newInstructions, oldInstructions) {
        if (newInstructions) {
            this.add(newInstructions);
        }
        if (oldInstructions) {
            this.remove(oldInstructions);
        }
    }
});

/**
 * The Form panel presents a set of form fields and provides convenient ways to load and save data. Usually a form
 * panel just contains the set of fields you want to display, ordered inside the items configuration like this:
 *
 *     @example
 *     var form = Ext.create('Ext.form.Panel', {
 *         fullscreen: true,
 *         items: [
 *             {
 *                 xtype: 'textfield',
 *                 name: 'name',
 *                 label: 'Name'
 *             },
 *             {
 *                 xtype: 'emailfield',
 *                 name: 'email',
 *                 label: 'Email'
 *             },
 *             {
 *                 xtype: 'passwordfield',
 *                 name: 'password',
 *                 label: 'Password'
 *             }
 *         ]
 *     });
 *
 * Here we just created a simple form panel which could be used as a registration form to sign up to your service. We
 * added a plain {@link Ext.field.Text text field} for the user's Name, an {@link Ext.field.Email email field} and
 * finally a {@link Ext.field.Password password field}. In each case we provided a {@link Ext.field.Field#name name}
 * config on the field so that we can identify it later on when we load and save data on the form.
 *
 * <h2>Loading data</h2>
 *
 * Using the form we created above, we can load data into it in a few different ways, the easiest is to use
 * {@link #setValues}:
 *
 *     form.setValues({
 *         name: 'Ed',
 *         email: 'ed@sencha.com',
 *         password: 'secret'
 *     });
 *
 * It's also easy to load {@link Ext.data.Model Model} instances into a form - let's say we have a User model and want
 * to load a particular instance into our form:
 *
 *     Ext.define('MyApp.model.User', {
 *         extend: 'Ext.data.Model',
 *         config: {
 *             fields: ['name', 'email', 'password']
 *         }
 *     });
 *
 *     var ed = Ext.create('MyApp.model.User', {
 *         name: 'Ed',
 *         email: 'ed@sencha.com',
 *         password: 'secret'
 *     });
 *
 *     form.setRecord(ed);
 *
 * <h2>Retrieving form data</h2>
 *
 * Getting data out of the form panel is simple and is usually achieve vai the {@link #getValues} method:
 *
 *     var values = form.getValues();
 *
 *     //values now looks like this:
 *     {
 *         name: 'Ed',
 *         email: 'ed@sencha.com',
 *         password: 'secret'
 *     }
 *
 * It's also possible to listen to the change events on individual fields to get more timely notification of changes
 * that the user is making. Here we expand on the example above with the User model, updating the model as soon as
 * any of the fields are changed:
 *
 *     var form = Ext.create('Ext.form.Panel', {
 *         listeners: {
 *             '> field': {
 *                 change: function(field, newValue, oldValue) {
 *                     ed.set(field.getName(), newValue);
 *                 }
 *             }
 *         },
 *         items: //as before
 *     });
 *
 * The above used a new capability of Touch 2.0, which enables you to specify listeners on child components of any
 * container. In this case, we attached a listener to the {@link Ext.field.Text#change change} event of each form
 * field that is a direct child of the form panel. Our listener gets the name of the field that fired the change event,
 * and updates our {@link Ext.data.Model Model} instance with the new value. For example, changing the email field
 * in the form will update the Model's email field.
 *
 * <h2>Submitting forms</h2>
 *
 * There are a few ways to submit form data. In our example above we have a Model instance that we have updated, giving
 * us the option to use the Model's {@link Ext.data.Model#save save} method to persist the changes back to our server,
 * without using a traditional form submission. Alternatively, we can send a normal browser form submit using the
 * {@link #method} method:
 *
 *     form.submit({
 *         url: 'url/to/submit/to',
 *         method: 'POST',
 *         success: function() {
 *             alert('form submitted successfully!');
 *         }
 *     });
 *
 * In this case we provided the url to submit the form to inside the submit call - alternatively you can just set the
 * {@link #url} configuration when you create the form. We can specify other parameters (see {@link #method} for a
 * full list), including callback functions for success and failure, which are called depending on whether or not the
 * form submission was successful. These functions are usually used to take some action in your app after your data
 * has been saved to the server side.
 *
 * @aside guide forms
 */
Ext.define('Ext.form.Panel', {
    alternateClassName: 'Ext.form.FormPanel',
    extend  : 'Ext.Panel',
    xtype   : 'formpanel',
    requires: ['Ext.XTemplate', 'Ext.field.Checkbox', 'Ext.Ajax'],

    /**
     * @event submit
     * @preventable doSubmit
     * Fires upon successful (Ajax-based) form submission
     * @param {Ext.form.Panel} this This FormPanel
     * @param {Object} result The result object as returned by the server
     * @param {Ext.EventObject} e The event object
     */

    /**
     * @event beforesubmit
     * @preventable doBeforeSubmit
     * Fires immediately preceding any Form submit action.
     * Implementations may adjust submitted form values or options prior to execution.
     * A return value of <tt>false</tt> from this listener will abort the submission
     * attempt (regardless of standardSubmit configuration)
     * @param {Ext.form.Panel} this This FormPanel
     * @param {Object} values A hash collection of the qualified form values about to be submitted
     * @param {Object} options Submission options hash (only available when standardSubmit is false)
     */

    /**
     * @event exception
     * Fires when either the Ajax HTTP request reports a failure OR the server returns a success:false
     * response in the result payload.
     * @param {Ext.form.Panel} this This FormPanel
     * @param {Object} result Either a failed Ext.data.Connection request object or a failed (logical) server
     * response payload.
     */

    config: {
        // @inherit
        baseCls: Ext.baseCSSPrefix + 'form',

        /**
         * @cfg {Boolean} standardSubmit
         * Wether or not we want to perform a standard form submit.
         * @accessor
         */
        standardSubmit: false,

        /**
         * @cfg {String} url
         * The default Url for submit actions
         * @accessor
         */
        url: null,

        /**
         * @cfg {Object} baseParams
         * Optional hash of params to be sent (when standardSubmit configuration is false) on every submit.
         * @accessor
         */
        baseParams : null,

        /**
         * @cfg {Object} submitOnAction
         * When this is set to true, the form will automatically submit itself whenever the 'action'
         * event fires on a field in this form. The action event usually fires whenever you press
         * go or enter inside a textfield.
         * @accessor
         */
        submitOnAction : true,

        /**
         * @cfg {Ext.data.Model} record The model instance of this form. Can by dynamically set at any time
         * @accessor
         */
        record: null,

        /**
         * @cfg {String} method
         * The method which this form will be submitted. `post` or `get`.
         */
        method: 'post',

        // @inherit
        scrollable: {
            translationMethod: 'scrollposition'
        }
    },

    getElementConfig: function() {
        var config = this.callParent();
        config.tag = "form";

        return config;
    },

    // @private
    initialize: function() {
        var me = this;
        me.callParent();

        me.on({
            action: 'onFieldAction',
            scope : me
        });

        me.element.on({
            submit: 'onSubmit',
            scope : me
        });
    },

    updateRecord: function(newRecord) {
        var fields, values, name;

        if (newRecord && (fields = newRecord.fields)) {
            values = this.getValues();
            for (name in values) {
                if (values.hasOwnProperty(name) && fields.containsKey(name)) {
                    newRecord.set(name, values[name]);
                }
            }
        }
        return this;
    },

    /**
     * Loads matching fields from a model instance into this form
     * @param {Ext.data.Model} instance The model instance
     * @return {Ext.form.Panel} This form
     */
    setRecord: function(record) {
        var me = this;

        if (record && record.data) {
            me.setValues(record.data);
        }

        me._record = record;

        return this;
    },

    // @private
    onSubmit: function(e) {
        var me = this;
        if (e && !me.getStandardSubmit()) {
            e.stopEvent();
        }

        me.fireAction('submit', [me, me.getValues(true), e], 'doSubmit');

    },

    doSubmit: function(me, values, e) {
        if (e) {
            e.stopEvent();
        }
    },

    // @private
    onFieldAction: function(field) {
        if (this.getSubmitOnAction()) {
            field.blur();
            this.submit();
        }
    },

    /**
     * Performs a Ajax-based submission of form values (if standardSubmit is false) or otherwise
     * executes a standard HTML Form submit action.
     *
     * @param {Object} options
     * The configuration when submiting this form.
     *
     * @param {String} options.url
     * The url for the action (defaults to the form's {@link #url}).
     *
     * @param {String} options.method
     * The form method to use (defaults to the form's {@link #method}, or POST if not defined).
     *
     * @param {String/Object} params
     * The params to pass when submitting this form (defaults to this forms {@link #baseParams}).
     * Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.
     *
     * @param {Object} headers
     * Request headers to set for the action
     *
     * @param {Boolean} autoAbort
     * `true` to abort any pending Ajax request prior to submission (defaults to false)
     * **Note:** Has no effect when {@link #standardSubmit} is enabled.
     *
     * @param {Boolean} options.submitDisabled
     * `true` to submit all fields regardless of disabled state (defaults to false).
     * Note: Has no effect when {@link #standardSubmit} is enabled.
     *
     * @param {String/Object} waitMsg
     * If specified, the value which is passed to the loading {@link #masked mask}. See {@link #masked} for
     * more information.
     *
     * @param {Function} options.success
     * The callback that will be invoked after a successful response. A response is successful if
     * a response is received from the server and is a JSON object where the success property is set
     * to true, {"success": true}
     *
     * The function is passed the following parameters:
     *
     * @param {Ext.form.Panel} options.success.form
     * The form that requested the action
     *
     * @param {Ext.form.Panel} options.success.result
     * The result object returned by the server as a result of the submit request.
     *
     * @param {Function} options.failure
     * The callback that will be invoked after a failed transaction attempt.
     *
     * The function is passed the following parameters:
     *
     * @param {Ext.form.Panel} options.failure.form
     * The {@link Ext.form.Panel} that requested the submit.
     *
     * @param {Ext.form.Panel} options.failure.result
     * The failed response or result object returned by the server which performed the operation.
     *
     * @param {Object} options.scope
     * The scope in which to call the callback functions (The this reference for the callback functions).=
     *
     * @return {Ext.data.Connection} The request object
     */
    submit: function(options) {
        var me = this,
            form = me.element.dom || {},
            formValues;

        options = Ext.apply({
            url : me.getUrl() || form.action,
            submit: false,
            method : me.getMethod() || form.method || 'post',
            autoAbort : false,
            params : null,
            waitMsg : null,
            headers : null,
            success : null,
            failure : null
        }, options || {});

        formValues = me.getValues(me.getStandardSubmit() || !options.submitDisabled);

        return me.fireAction('beforesubmit', [me, formValues, options], 'doBeforeSubmit');
    },

    doBeforeSubmit: function(me, formValues, options) {
        var form = me.element.dom || {};

        if (me.getStandardSubmit()) {
            if (options.url && Ext.isEmpty(form.action)) {
                form.action = options.url;
            }

            form.method = (options.method || form.method).toLowerCase();
            form.submit();
        }
        else {
            if (options.waitMsg) {
                me.setMasked(options.waitMsg);
            }

            return Ext.Ajax.request({
                url: options.url,
                method: options.method,
                rawData: Ext.urlEncode(Ext.apply(
                    Ext.apply({}, me.getBaseParams() || {}),
                    options.params || {},
                    formValues
                )),
                autoAbort: options.autoAbort,
                headers: Ext.apply(
                    {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
                    options.headers || {}
                ),
                scope: me,
                callback: function(callbackOptions, success, response) {
                    var me = this,
                        responseText = response.responseText,
                        failureFn;

                    me.setMasked(false);

                    failureFn = function() {
                        if (Ext.isFunction(options.failure)) {
                            options.failure.call(options.scope || me, me, response, responseText);
                        }
                        me.fireEvent('exception', me, response);
                    };

                    if (success) {
                        response = Ext.decode(responseText);
                        success = !!response.success;
                        if (success) {
                            if (Ext.isFunction(options.success)) {
                                options.success.call(options.scope || me, me, response, responseText);
                            }
                            me.fireEvent('submit', me, response);
                        } else {
                            failureFn();
                        }
                    }
                    else {
                        failureFn();
                    }
                }
            });
        }
    },

    /**
     * Sets the values of form fields in bulk. Example usage:
     *
     *     myForm.setValues({
     *         name: 'Ed',
     *         crazy: true,
     *         username: 'edspencer'
     *     });
     *
     * If there groups of checkbox fields with the same name, pass their values in an array. For example:
     *
     *     myForm.setValues({
     *         name: 'Jacky',
     *         crazy: false,
     *         hobbies: [
     *             'reading',
     *             'cooking',
     *             'gaming'
     *         ]
     *     });
     *
     * @param {Object} values field name => value mapping object
     * @return {Ext.form.Panel} This form
     */
    setValues: function(values) {
        var fields = this.getFields(),
            name, field, value;

        values = values || {};

        for (name in values) {
            if (values.hasOwnProperty(name)) {
                field = fields[name];
                value = values[name];
                if (field) {
                    if (Ext.isArray(field)) {
                        field.forEach(function(f) {
                            if (f.isRadio) {
                                f.setGroupValue(value);
                            } else if (Ext.isArray(values[name])) {
                                f.setChecked((value.indexOf(f.getValue()) != -1));
                            } else {
                                f.setChecked((value == f.getValue()));
                            }
                        });
                    } else {
                        if (field.setChecked) {
                            field.setChecked(value);
                        } else {
                            field.setValue(value);
                        }
                    }
                }
            }
        }

        return this;
    },

    /**
     * Returns an object containing the value of each field in the form, keyed to the field's name.
     * For groups of checkbox fields with the same name, it will be arrays of values. For examples:

     <pre><code>
     {
         name: "Jacky Nguyen", // From a TextField
         favorites: [
             'pizza',
             'noodle',
             'cake'
         ]
     }
     </code></pre>

     * @param {Boolean} enabled <tt>true</tt> to return only enabled fields
     * @return {Object} Object mapping field name to its value
     */
    getValues: function(enabled) {
        var fields = this.getFields(),
            values = {},
            field, name, ln, i;

        for (name in fields) {
            if (fields.hasOwnProperty(name)) {
                if (Ext.isArray(fields[name])) {
                    values[name] = [];

                    ln = fields[name].length;

                    for (i = 0; i < ln; i++) {
                        field = fields[name][i];

                        if (!field.getChecked) {
                            values[name] = field.getValue();

                            throw new Error("Ext.form.Panel: [getValues] You have multiple fields with the same 'name' configuration of '" + name + "' in your form panel (#" + this.id + ").");

                            break;
                        }

                        if (!(enabled && field.getDisabled())) {
                            if (field.isRadio) {
                                values[name] = field.getGroupValue();
                            } else {
                                values[name].push(field.getValue());
                            }
                        }


                    }
                } else {
                    field = fields[name];

                    if (!(enabled && field.getDisabled())) {
                        if (field.isCheckbox) {
                            values[name] = (field.getChecked()) ? field.getValue() : null;
                        } else {
                            values[name] = field.getValue();
                        }
                    }
                }
            }
        }

        return values;
    },

    /**
     * Resets all fields in the form back to their original values
     * @return {Ext.form.Panel} This form
     */
    reset: function() {
        this.getFieldsAsArray().forEach(function(field) {
            field.reset();
        });

        return this;
    },

    /**
     * A convenient method to enable all fields in this forms
     * @return {Ext.form.Panel} This form
     */
    enable: function() {
        this.getFieldsAsArray().forEach(function(field) {
            field.enable();
        });

        return this;
    },

    /**
     * A convenient method to disable all fields in this forms
     * @return {Ext.form.Panel} This form
     */
    disable: function() {
        this.getFieldsAsArray().forEach(function(field) {
            field.disable();
        });

        return this;
    },

    /**
     * @private
     */
    getFieldsAsArray: function() {
        var fields = [],
            getFieldsFrom = function(item) {
                if (item.isField) {
                    fields.push(item);
                }

                if (item.isContainer) {
                    item.getItems().each(getFieldsFrom);
                }
            };

        this.getItems().each(getFieldsFrom);

        return fields;
    },

    /**
     * @private
     * Returns all {@link Ext.field.Field field} instances inside this form
     * @param byName return only fields that match the given name, otherwise return all fields.
     * @return {Object/Array} All field instances, mapped by field name; or an array if byName is passed
     */
    getFields: function(byName) {
        var fields = {},
            itemName;

        var getFieldsFrom = function(item) {
            if (item.isField) {
                itemName = item.getName();

                if ((byName && itemName == byName) || typeof byName == 'undefined') {
                    if (fields.hasOwnProperty(itemName)) {
                        if (!Ext.isArray(fields[itemName])) {
                            fields[itemName] = [fields[itemName]];
                        }

                        fields[itemName].push(item);
                    } else {
                        fields[itemName] = item;
                    }
                }

            }

            if (item.isContainer) {
                item.items.each(getFieldsFrom);
            }
        };

        this.getItems().each(getFieldsFrom);

        return (byName) ? (fields[byName] || []) : fields;
    },

    /**
     * Returns an array of fields in this formpanel
     * @return {Ext.field.Field[]} An array of fields in this form panel
     * @private
     */
    getFieldsArray: function() {
        var fields = [];

        var getFieldsFrom = function(item) {
            if (item.isField) {
                fields.push(item);
            }

            if (item.isContainer) {
                item.items.each(getFieldsFrom);
            }
        };

        this.items.each(getFieldsFrom);

        return fields;
    },

    getFieldsFromItem: Ext.emptyFn,

    /**
     * Shows a generic/custom mask over a designated Element.
     * @param {String/Object} cfg Either a string message or a configuration object supporting
     * the following options:
     *
     *     {
     *         message : 'Please Wait',
     *         cls : 'form-mask'
     *     }
     *
     * @return {Ext.form.Panel} This form
     * @deprecated 2.0.0 Please use {@link #setMasked} instead.
     */
    showMask: function(cfg, target) {
        Ext.Logger.warn('showMask is now deprecated. Please use Ext.form.Panel#setMasked instead');

        cfg = Ext.isObject(cfg) ? cfg.message : cfg;

        if (cfg) {
            this.setMasked({
                xtype: 'loadmask',
                message: cfg
            });
        } else {
            this.setMasked(true);
        }

        return this;
    },

    /**
     * Hides a previously shown wait mask (See {@link #showMask})
     * @return {Ext.form.Panel} this
     * @deprecated 2.0.0 Please use {@link #unmask} or {@link #setMasked} instead.
     */
    hideMask: function() {
        this.setMasked(false);
        return this;
    },

    /**
     * Returns the currently focused field
     * @return {Ext.field.Field} The currently focused field, if one is focused or `null`.
     * @private
     */
    getFocusedField: function() {
        var fields = this.getFieldsArray(),
            ln = fields.length,
            field, i;

        for (i = 0; i < ln; i++) {
            field = fields[i];
            if (field.isFocused) {
                return field;
            }
        }

        return null;
    },

    /**
     * @private
     * @return {Boolean/Ext.field.Field} The next field if one exists, or false
     * @private
     */
    getNextField: function() {
        var fields = this.getFieldsArray(),
            focusedField = this.getFocusedField(),
            ln = fields.length,
            index;

        if (focusedField) {
            index = fields.indexOf(focusedField);

            if (index !== fields.length - 1) {
                index++;
                return fields[index];
            }
        }

        return false;
    },

    /**
     * Tries to focus the next field in the form, if there is currently a focused field.
     * @return {Boolean/Ext.field.Field} The next field that was focused, or false
     * @private
     */
    focusNextField: function() {
        var field = this.getNextField();
        if (field) {
            field.focus();
            return field;
        }

        return false;
    },

    /**
     * @private
     * @return {Boolean/Ext.field.Field} The next field if one exists, or false
     */
    getPreviousField: function() {
        var fields = this.getFieldsArray(),
            focusedField = this.getFocusedField(),
            ln = fields.length,
            index;

        if (focusedField) {
            index = fields.indexOf(focusedField);

            if (index !== 0) {
                index--;
                return fields[index];
            }
        }

        return false;
    },

    /**
     * Tries to focus the previous field in the form, if there is currently a focused field.
     * @return {Boolean/Ext.field.Field} The previous field that was focused, or false
     * @private
     */
    focusPreviousField: function() {
        var field = this.getPreviousField();
        if (field) {
            field.focus();
            return field;
        }

        return false;
    }
}, function() {

    /**
     * @member Ext.form.Panel
     * @method loadRecord
     * @deprecated 2.0.0 please use #setRecord
     */

    /**
     * @member Ext.form.Panel
     * @method loadModel
     * @deprecated 2.0.0 please use #setRecord
     */

    /**
     * @member Ext.form.Panel
     * @method load
     * @deprecated 2.0.0 please use #setRecord
     */
});

/**
 * This component is used in {@link Ext.navigation.View} to control animations in the toolbar. You should never need to
 * interact with the component directly, unless you are subclassing it.
 * @private
 * @author Robert Dougan <rob@sencha.com>
 */
Ext.define('Ext.navigation.Bar', {
    extend: 'Ext.Container',

    requires: [
        'Ext.Button',
        'Ext.TitleBar',
        'Ext.Spacer',
        'Ext.util.SizeMonitor'
    ],

    // private
    isToolbar: true,

    config: {
        // @inherit
        baseCls: Ext.baseCSSPrefix + 'toolbar',

        // @inherit
        cls: Ext.baseCSSPrefix + 'navigation-bar',

        /**
         * @cfg {String} ui
         * Style options for Toolbar. Either 'light' or 'dark'.
         * @accessor
         */
        ui: 'dark',

        /**
         * @cfg {String} title
         * The title of the toolbar. You should NEVER set this, it is used internally. You set the title of the
         * navigation bar by giving a navigation views children a title configuration.
         * @private
         * @accessor
         */
        title: null,

        /**
         * @cfg
         * @hide
         * @accessor
         */
        defaultType: 'button',

        /**
         * @cfg
         * @hide
         * @accessor
         */
        layout: {
            type: 'hbox'
        },

        /**
         * @cfg {Array/Object} items The child items to add to this NavigationBar. The {@link #cfg-defaultType} of
         * a NavigationBar is {@link Ext.Button}, so you do not need to specify an `xtype` if you are adding
         * buttons.
         *
         * You can also give items a `align` configuration which will align the item to the `left` or `right` of
         * the NavigationBar.
         * @hide
         * @accessor
         */

        /**
         * @cfg {String} defaultBackButtonText
         * The text to be displayed on the back button if:
         * a) The previous view does not have a title
         * b) The {@link #useTitleForBackButtonText} configuration is true.
         * @private
         * @accessor
         */
        defaultBackButtonText: 'Back',

        /**
         * @cfg {Object} animation
         * @private
         * @accessor
         */
        animation: {
            duration: 300
        },

        /**
         * @cfg {Boolean} useTitleForBackButtonText
         * Set to false if you always want to display the {@link #defaultBackButtonText} as the text
         * on the back button. True if you want to use the previous views title.
         * @private
         * @accessor
         */
        useTitleForBackButtonText: null,

        /**
         * @cfg {Ext.navigation.View} view A reference to the navigation view this bar is linked to.
         * @private
         * @accessor
         */
        view: null,

        /**
         * @cfg {Ext.Button/Object} backButton The configuration for the back button
         * @private
         * @accessor
         */
        backButton: {
            align: 'left',
            ui: 'back',
            hidden: true
        }
    },

    /**
     * The minmum back button width allowed.
     * @private
     */
    minBackButtonWidth: 80,

    constructor: function(config) {
        config = config || {};

        if (!config.items) {
            config.items = [];
        }

        this.callParent([config]);
    },

    beforeInitialize: function() {
        this.backButtonStack = [];
        this.lastAnimationProperties = {};
        this.animating = false;

        this.onSizeMonitorChange = Ext.Function.createThrottled(this.onSizeMonitorChange, 50, this);
    },

    initialize: function() {
        var me = this;

        me.on({
            painted: 'onPainted',
            erased: 'onErased'
        });

        me.onSizeMonitorChange();
    },

    /**
     * @private
     */
    updateView: function(newView, oldView) {
        var backButton = this.getBackButton(),
            innerItems, i, backButtonText;

        this.getItems();

        if (newView) {
            this.backButtonStack = [];

            //update the back button stack with the current inner items of the view
            innerItems = newView.getInnerItems();
            for (i = 0; i < innerItems.length; i++) {
                this.backButtonStack.push(innerItems[i].config.title || '&nbsp;');
            }

            this.titleComponent.setTitle(this.getTitleText());

            backButtonText = this.getBackButtonText();
            if (backButtonText) {
                backButton.setText(this.getBackButtonText());
                backButton.show();
            }
        }
    },

    /**
     * @private
     */
    onViewAdd: function(view, item, index) {
        var animation = view.getLayout().getAnimation();

        this.endAnimation();

        this.backButtonStack.push(item.config.title || '&nbsp;');

        this.refreshNavigationBarProxy();

        if (animation && animation.isAnimation && view.isPainted()) {
            if (this.backButtonStack.length > 1) {
                this.pushBackButtonAnimated(this.getBackButtonText());
            }
            this.pushTitleAnimated(this.getTitleText());
        } else {
            if (this.backButtonStack.length > 1) {
                this.pushBackButton(this.getBackButtonText());
            }
            this.pushTitle(this.getTitleText());
        }
    },

    /**
     * @private
     */
    onViewRemove: function(view, item, index) {
        var animation = view.getLayout().getAnimation();

        this.endAnimation();

        this.backButtonStack.pop();

        this.refreshNavigationBarProxy();

        if (animation && animation.isAnimation && view.isPainted()) {
            this.popBackButtonAnimated(this.getBackButtonText());
            this.popTitleAnimated(this.getTitleText());
        } else {
            this.popBackButton(this.getBackButtonText());
            this.popTitle(this.getTitleText());
        }
    },

    endAnimation: function() {
        var lastAnimationProperties = this.lastAnimationProperties,
            animation, el, key;

        if (lastAnimationProperties) {
            for (animation in lastAnimationProperties) {
                el = Ext.get(animation);

                for (key in lastAnimationProperties[animation].to) {
                    el.setStyle(key, lastAnimationProperties[animation][key]);
                }

                if (lastAnimationProperties[animation].onEnd) {
                    lastAnimationProperties[animation].onEnd.call(this);
                }
            }
        }
    },

    /**
     * @private
     */
    applyBackButton: function(config) {
        return Ext.factory(config, Ext.Button, this.getBackButton());
    },

    /**
     * @private
     */
    updateBackButton: function(newBackButton, oldBackButton) {
        if (oldBackButton) {
            this.remove(oldBackButton);
        }

        if (newBackButton) {
            this.add(newBackButton);

            newBackButton.on({
                scope: this,
                tap: this.onBackButtonTap
            });
        }
    },

    onBackButtonTap: function() {
        this.fireEvent('back', this);
    },

    updateUseTitleForBackButtonText: function(newUseTitleForBackButtonText) {
        var backButton = this.getBackButton();

        if (backButton) {
            backButton.setText(this.getBackButtonText());
        }

        this.onSizeMonitorChange();
    },

    onPainted: function() {
        this.painted = true;

        this.sizeMonitor.refresh();

        this.onSizeMonitorChange();
    },

    onErased: function() {
        this.painted = false;
    },

    applyItems: function(items) {
        var me = this;

        if (!me.initialized) {
            var defaults = me.getDefaults() || {},
                leftBox, rightBox, spacer;

            me.leftBox = leftBox = me.add({
                xtype: 'container',
                style: 'position: relative',
                layout: {
                    type: 'hbox',
                    align: 'center'
                }
            });
            me.spacer = spacer = me.add({
                xtype: 'component',
                style: 'position: relative',
                flex: 1
            });
            me.rightBox = rightBox = me.add({
                xtype: 'container',
                style: 'position: relative',
                layout: {
                    type: 'hbox',
                    align: 'center'
                }
            });
            me.titleComponent = me.add({
                xtype: 'title',
                hidden: defaults.hidden,
                centered: true
            });

            me.sizeMonitor = new Ext.util.SizeMonitor({
                element: me.element,
                callback: me.onSizeMonitorChange,
                scope: me
            });

            me.doAdd = me.doBoxAdd;
            me.doInsert = me.doBoxInsert;
        }

        me.callParent(arguments);
    },

    doBoxAdd: function(item) {
        if (item.config.align == 'right') {
            this.rightBox.add(item);
        }
        else {
            this.leftBox.add(item);
        }
    },

    doBoxInsert: function(index, item) {
        if (item.config.align == 'right') {
            this.rightBox.add(item);
        }
        else {
            this.leftBox.add(item);
        }
    },

    /**
     * Called when any size of this component changes.
     * It refreshes the navigation bar proxy so that the title and back button is in the correct location.
     * @private
     */
    onSizeMonitorChange: function() {
        if (!this.rendered) {
            return;
        }

        var backButton = this.getBackButton(),
            titleComponent = this.titleComponent;

        if (backButton && backButton.rendered) {
            backButton.setWidth(null);
        }

        this.refreshNavigationBarProxy();

        var properties = this.getNavigationBarProxyProperties();
        if (backButton && backButton.rendered) {
            backButton.setWidth(properties.backButton.width);
        }

        titleComponent.setStyle('-webkit-transform', null);
        titleComponent.setWidth(properties.title.width);
        titleComponent.element.setLeft(properties.title.left);
    },

    /**
     * Calculates and returns the position values needed for the back button when you are pushing a title.
     * @private
     */
    getBackButtonAnimationProperties: function() {
        var me = this,
            element = me.element,
            backButtonElement = me.getBackButton().element,
            titleElement = me.titleComponent.element,
            minButtonOffset = Math.min(element.getWidth() / 3, 200),
            proxyProperties = this.getNavigationBarProxyProperties(),
            buttonOffset, buttonGhostOffset;

        buttonOffset = titleElement.getX() - element.getX();
        buttonGhostOffset = element.getX() - backButtonElement.getX() - backButtonElement.getWidth();

        buttonOffset = Math.min(buttonOffset, minButtonOffset);

        return {
            element: {
                from: {
                    left: buttonOffset,
                    width: proxyProperties.backButton.width,
                    opacity: 0
                },
                to: {
                    left: 0,
                    width: proxyProperties.backButton.width,
                    opacity: 1
                }
            },

            ghost: {
                from: null,
                to: {
                    left: buttonGhostOffset,
                    opacity: 0
                }
            }
        };
    },

    /**
     * Calculates and returns the position values needed for the back button when you are popping a title.
     * @private
     */
    getBackButtonAnimationReverseProperties: function() {
        var me = this,
            element = me.element,
            backButtonElement = me.getBackButton().element,
            titleElement = me.titleComponent.element,
            minButtonGhostOffset = Math.min(element.getWidth() / 3, 200),
            proxyProperties = this.getNavigationBarProxyProperties(),
            buttonOffset, buttonGhostOffset;

        buttonOffset = element.getX() - backButtonElement.getX() - backButtonElement.getWidth();
        buttonGhostOffset = titleElement.getX() - backButtonElement.getWidth();

        buttonGhostOffset = Math.min(buttonGhostOffset, minButtonGhostOffset);

        return {
            element: {
                from: {
                    left: buttonOffset,
                    width: proxyProperties.backButton.width,
                    opacity: 0
                },
                to: {
                    left: 0,
                    width: proxyProperties.backButton.width,
                    opacity: 1
                }
            },

            ghost: {
                from: null,
                to: {
                    left: buttonGhostOffset,
                    opacity: 0
                }
            }
        };
    },

    /**
     * Calculates and returns the position values needed for the title when you are pushing a title.
     * @private
     */
    getTitleAnimationProperties: function() {
        var me = this,
            element = me.element,
            titleElement = me.titleComponent.element,
            proxyProperties = this.getNavigationBarProxyProperties(),
            titleOffset, titleGhostOffset;

        titleOffset = element.getWidth() - titleElement.getX();
        titleGhostOffset = element.getX() - titleElement.getX() + proxyProperties.backButton.width;

        if ((proxyProperties.backButton.left + titleElement.getWidth()) > titleElement.getX()) {
            titleGhostOffset = element.getX() - titleElement.getX() - titleElement.getWidth();
        }

        return {
            element: {
                from: {
                    left: titleOffset,
                    width: proxyProperties.title.width,
                    opacity: 0
                },
                to: {
                    left: proxyProperties.title.left,
                    width: proxyProperties.title.width,
                    opacity: 1
                }
            },
            ghost: {
                from: titleElement.getLeft(),
                to: {
                    left: titleGhostOffset,
                    opacity: 0
                }
            }
        };
    },

    /**
     * Calculates and returns the position values needed for the title when you are popping a title.
     * @private
     */
    getTitleAnimationReverseProperties: function() {
        var me = this,
            element = me.element,
            titleElement = me.titleComponent.element,
            proxyProperties = this.getNavigationBarProxyProperties(),
            ghostLeft = 0,
            titleOffset, titleGhostOffset;

        ghostLeft = titleElement.getLeft();
        titleElement.setLeft(0);

        titleOffset = element.getX() - titleElement.getX() + proxyProperties.backButton.width;
        titleGhostOffset = element.getX() + element.getWidth();

        if ((proxyProperties.backButton.left + titleElement.getWidth()) > titleElement.getX()) {
            titleOffset = element.getX() - titleElement.getX() - titleElement.getWidth();
        }

        return {
            element: {
                from: {
                    left: titleOffset,
                    width: proxyProperties.title.width,
                    opacity: 0
                },
                to: {
                    left: proxyProperties.title.left,
                    width: proxyProperties.title.width,
                    opacity: 1
                }
            },
            ghost: {
                from: ghostLeft,
                to: {
                    left: titleGhostOffset,
                    opacity: 0
                }
            }
        };
    },

    /**
     * Helper method used to animate elements.
     * You pass it an element, objects for the from and to positions an option onEnd callback called when the animation is over.
     * Normally this method is passed configurations returned from the methods such as {@link #getTitleAnimationReverseProperties} etc.
     * It is called from the {@link #pushBackButtonAnimated}, {@link #pushTitleAnimated}, {@link #popBackButtonAnimated} and {@link #popTitleAnimated}
     * methods.
     *
     * If the current device is Android, it will use top/left to animate.
     * If it is anything else, it will use transform.
     * @private
     */
    animate: function(component, element, from, to, onEnd) {
        var me = this,
            config = {
                element: element,
                easing: 'ease-in-out',
                duration: this.getAnimation().duration,
                replacePrevious: true,
                preserveEndState: true
            },
            animation;

        this.lastAnimationProperties[element.id] = {
            to: to,
            onEnd: onEnd
        };

        //reset the left of the element
        element.setLeft(0);

        if (Ext.os.is.Android) {
            if (from) {
                config.from = {
                    left: from.left,
                    opacity: from.opacity
                };

                if (from.width) {
                    config.from.width = from.width;
                }
            }

            if (to) {
                config.to = {
                    left: to.left,
                    opacity: to.opacity
                };

                if (to.width) {
                    config.to.width = to.width;
                }
            }
        } else {
            if (from) {
                config.from = {
                    transform: {
                        translateX: from.left
                    },
                    opacity: from.opacity
                };

                if (from.width) {
                    config.from.width = from.width;
                }
            }

            if (to) {
                config.to = {
                    transform: {
                        translateX: to.left
                    },
                    opacity: to.opacity
                };

                if (to.width) {
                    config.to.width = to.width;
                }
            }
        }

        fn = function() {
            if (onEnd) {
                onEnd.call(me);
            }

            if (component && Ext.isNumber(to.width)) {
                component.setWidth(to.width);
            }

            me.lastAnimationProperties = {};
        };

        animation = new Ext.fx.Animation(config);
        animation.on('animationend', fn, this);

        Ext.Animator.run(animation);
    },

    /**
     * Returns the text needed for the current back button at anytime.
     * @private
     */
    getBackButtonText: function() {
        var text = this.backButtonStack[this.backButtonStack.length - 2],
            useTitleForBackButtonText = this.getUseTitleForBackButtonText();

        if (!useTitleForBackButtonText) {
            if (text) {
                text = this.getDefaultBackButtonText();
            }
        }

        return text;
    },

    /**
     * Returns the text needed for the current title at anytime.
     * @private
     */
    getTitleText: function() {
        return this.backButtonStack[this.backButtonStack.length - 1];
    },

    /**
     * Pushes a back button into the bar with no animations
     * @private
     */
    pushBackButton: function(title) {
        var backButton = this.getBackButton();
        backButton.setText(title);
        backButton.show();

        var properties = this.getBackButtonAnimationProperties(),
            to = properties.element.to;

        if (to.left) {
            backButton.setLeft(to.left);
        }

        if (to.width) {
            backButton.setWidth(to.width);
        }
    },

    /**
     * Pushes a new back button into the bar with animations
     * @private
     */
    pushBackButtonAnimated: function(title) {
        var me = this;

        var backButton = me.getBackButton(),
            previousTitle = backButton.getText(),
            backButtonElement = backButton.element,
            properties = me.getBackButtonAnimationProperties(),
            buttonGhost;

        //if there is a previoustitle, there should be a buttonGhost. so create it.
        if (previousTitle) {
            buttonGhost = me.createProxy(backButton);
        }

        //update the back button, and make sure it is visible
        backButton.setText(this.getBackButtonText());
        backButton.show();

        //animate the backButton, which always has the new title
        me.animate(backButton, backButtonElement, properties.element.from, properties.element.to, function() {
            me.animating = false;
        });

        //if there is a buttonGhost, we must animate it too.
        if (buttonGhost) {
            me.animate(null, buttonGhost, properties.ghost.from, properties.ghost.to, function() {
                buttonGhost.destroy();
            });
        }
    },

    /**
     * Pops the back button with no animations
     * @private
     */
    popBackButton: function(title) {
        var backButton = this.getBackButton();

        backButton.setText(null);

        if (title) {
            backButton.setText(this.getBackButtonText());
        } else {
            backButton.hide();
        }

        var properties = this.getBackButtonAnimationReverseProperties(),
            to = properties.element.to;

        if (to.left) {
            backButton.setLeft(to.left);
        }

        if (to.width) {
            backButton.setWidth(to.width);
        }
    },

    /**
     * Pops the current back button with animations.
     * It will automatically know whether or not it should show the previous backButton or not. And proceed accordingly
     * @private
     */
    popBackButtonAnimated: function(title) {
        var me = this;

        var backButton = me.getBackButton(),
            previousTitle = backButton.getText(),
            backButtonElement = backButton.element,
            properties = me.getBackButtonAnimationReverseProperties(),
            buttonGhost;

        //if there is a previoustitle, there should be a buttonGhost. so create it.
        if (previousTitle) {
            buttonGhost = me.createProxy(backButton);
        }

        //update the back button, and make sure it is visible
        if (title && me.backButtonStack.length) {
            backButton.setText(this.getBackButtonText());
            backButton.show();

            me.animate(backButton, backButtonElement, properties.element.from, properties.element.to);
        } else {
            backButton.hide();
        }

        //if there is a buttonGhost, we must animate it too.
        if (buttonGhost) {
            me.animate(null, buttonGhost, properties.ghost.from, properties.ghost.to, function() {
                buttonGhost.destroy();

                if (!title) {
                    backButton.setText(null);
                }
            });
        }
    },

    /**
     * Pushes a new title into the bar without any animations
     * @private
     */
    pushTitle: function(newTitle) {
        var title = this.titleComponent,
            titleElement = title.element,
            properties = this.getTitleAnimationProperties(),
            to = properties.element.to;

        title.setTitle(newTitle);

        if (to.left) {
            titleElement.setLeft(to.left);
        }

        if (to.width) {
            title.setWidth(to.width);
        }
    },

    /**
     * Pushs a new title into the navigation bar, animating as it goes.
     * @private
     */
    pushTitleAnimated: function(newTitle) {
        var me = this;

        var backButton = me.getBackButton(),
            previousTitle = (backButton) ? backButton.getText() : null,
            title = me.titleComponent,
            titleElement = title.element,
            properties, titleGhost;

        //if there is a previoustitle, there should be a buttonGhost. so create it.
        if (previousTitle) {
            titleGhost = me.createProxy(title, true);
        }

        title.setTitle(newTitle);

        properties = me.getTitleAnimationProperties();

        //animate the new title
        me.animate(title, titleElement, properties.element.from, properties.element.to);

        //if there is a titleGhost, we must animate it too.
        if (titleGhost) {
            me.animate(null, titleGhost, properties.ghost.from, properties.ghost.to, function() {
                titleGhost.destroy();
            });
        }
    },

    /**
     * Pops the title without any animation.
     * Simply gets the correct positions for the title and sets it on the dom.
     * @private
     */
    popTitle: function(newTitle) {
        var title = this.titleComponent,
            titleElement = title.element,
            properties = this.getTitleAnimationReverseProperties(),
            to = properties.element.to;

        title.setTitle(newTitle);

        if (to.left) {
            titleElement.setLeft(to.left);
        }

        if (to.width) {
            title.setWidth(to.width);
        }
    },

    /**
     * Method which pops the current title and animates it. It will automatically know whether or not to use a titleGhost
     * element, and how to animate it.
     * @private
     */
    popTitleAnimated: function(newTitle) {
        var me = this;

        var backButton = me.getBackButton(),
            previousTitle = me.titleComponent.getTitle(),
            title = me.titleComponent,
            titleElement = title.element,
            properties = me.getTitleAnimationReverseProperties(),
            titleGhost;

        //if there is a previoustitle, there should be a buttonGhost. so create it.
        if (previousTitle) {
            titleGhost = me.createProxy(title, true);
        }

        title.setTitle(newTitle || '');

        //animate the new title
        me.animate(title, titleElement, properties.element.from, properties.element.to, function() {
            me.animating = false;
        });

        //if there is a titleGhost, we must animate it too.
        if (titleGhost) {
            me.animate(null, titleGhost, properties.ghost.from, properties.ghost.to, function() {
                titleGhost.destroy();
            });
        }
    },

    /**
     * This creates a proxy of the whole navigation bar and positions it out of the view.
     * This is used so we know where the back button and title needs to be at any time, either if we are
     * animating, not animating, or resizing.
     * @private
     */
    createNavigationBarProxy: function() {
        var proxy = this.proxy;

        if (proxy) {
            return;
        }

        //create a titlebar for the proxy
        this.proxy = proxy = Ext.create('Ext.TitleBar', {
            items: [
                {
                    xtype: 'button',
                    ui: 'back',
                    text: ''
                }
            ],
            title: this.backButtonStack[0]
        });

        proxy.backButton = proxy.down('button[ui=back]');

        //add the proxy to the body
        Ext.getBody().appendChild(proxy.element);

        proxy.element.setStyle('position', 'absolute');
        proxy.element.setStyle('visibility', 'hidden');
        proxy.element.setX(0);
        proxy.element.setY(-1000);
    },

    /**
     * A Simple helper method which returns the current positions and sizes of the title and back button
     * in the navigation bar proxy.
     * @private
     */
    getNavigationBarProxyProperties: function() {
        return {
            title: {
                left: this.proxy.titleComponent.element.getLeft(),
                width: this.proxy.titleComponent.element.getWidth()
            },
            backButton: {
                left: this.proxy.backButton.element.getLeft(),
                width: this.proxy.backButton.element.getWidth()
            }
        };
    },

    /**
     * Refreshes the navigation bar proxy with the latest data available in the backButtonStack.
     * @private
     */
    refreshNavigationBarProxy: function() {
        var proxy = this.proxy,
            element = this.element,
            backButtonStack = this.backButtonStack,
            title = backButtonStack[backButtonStack.length - 1],
            oldTitle = this.getBackButtonText();

        if (!proxy) {
            this.createNavigationBarProxy();
            proxy = this.proxy;
        }

        proxy.element.setWidth(element.getWidth());
        proxy.element.setHeight(element.getHeight());

        proxy.setTitle(title);

        if (oldTitle) {
            proxy.backButton.setText(oldTitle);
            proxy.backButton.show();
        } else {
            proxy.backButton.hide();
        }

        proxy.refreshTitlePosition();
    },

    /**
     * Handles removing back button stacks from this bar
     * @private
     */
    onBeforePop: function(count) {
        count--;
        for (var i = 0; i < count; i++) {
            this.backButtonStack.pop();
        }
    },

    /**
     * We override the hidden method because we don't want to remove it from the view using display:none. Instead we just position it off
     * the scren, much like the navigation bar proxy. This means that all animations, pushing, popping etc. all still work when if you hide/show
     * this bar at any time.
     * @private
     */
    doSetHidden: function(hidden) {
        if (!hidden) {
            this.element.setStyle({
                position: 'relative',
                top: 'auto',
                left: 'auto',
                width: 'auto'
            });
        } else {
            this.element.setStyle({
                position: 'absolute',
                top: '-1000px',
                left: '-1000px',
                width: this.element.getWidth() + 'px'
            });
        }
    },

    /**
     * Creates a proxy element of the passed element, and positions it in the same position, using absolute positioning.
     * The createNavigationBarProxy method uses this to create proxies of the backButton and the title elements.
     * @private
     */
    createProxy: function(component, useParent) {
        var element = (useParent) ? component.element.getParent() : component.element,
            ghost = Ext.get(element.id + '-proxy');

        if (!ghost) {
            ghost = element.dom.cloneNode(true);
            ghost.id = element.id + '-proxy';

            //insert it into the toolbar
            element.getParent().dom.appendChild(ghost);

            //set the x/y
            ghost = Ext.get(ghost);
            ghost.setStyle('position', 'absolute');
            ghost.setY(element.getY());
            ghost.setX(element.getX());
        }

        return ghost;
    }
});

/**
 * @author Robert Dougan <rob@sencha.com>
 *
 * NavigationView is basically a {@link Ext.Container} with a {@link Ext.layout.Card card} layout, so only one view
 * can be visible at a time. However, NavigationView also adds extra functionality on top of this to allow
 * you to `push` and `pop` views at any time. When you do this, your NavigationView will automatically animate
 * between your current active view, and the new view you want to `push`, or the previous view you want to `pop`.
 *
 * Using the NavigationView is very simple. Here is a basic example of it in action:
 *
 *     @example
 *     var view = Ext.create('Ext.NavigationView', {
 *         fullscreen: true,
 *
 *         items: [{
 *             title: 'First',
 *             items: [{
 *                 xtype: 'button',
 *                 text: 'Push a new view!',
 *                 handler: function() {
 *                     //use the push() method to push another view. It works much like
 *                     //add() or setActiveItem(). it accepts a view instance, or you can give it
 *                     //a view config.
 *                     view.push({
 *                         title: 'Second',
 *                         html: 'Second view!'
 *                     });
 *                 }
 *             }]
 *         }]
 *     });
 *
 * Now, here comes the fun part: you can push any view/item into the NavigationView, at any time, and it will
 * automatically handle the animations between the two views, including adding a back button (if necessary)
 * and showing the new title.
 *
 *     view.push({
 *         title: 'A new view',
 *         html: 'Some new content'
 *     });
 *
 * As you can see, it is as simple as calling the {@link #method-push} method, with a new view (instance or object). Done.
 *
 * You can also `pop` a view at any time. This will remove the top-most view from the NavigationView, and amimate back
 * to the previous view. You can do this using the {@link #method-pop} method (which requires no arguments).
 *
 *     view.pop();
 *
 * @aside guide navigation_view
 */
Ext.define('Ext.navigation.View', {
    extend: 'Ext.Container',
    alternateClassName: 'Ext.NavigationView',
    xtype: 'navigationview',
    requires: ['Ext.navigation.Bar'],

    config: {
        // @inherit
        baseCls: Ext.baseCSSPrefix + 'navigationview',

        /**
         * @cfg {Boolean/Object} navigationBar
         * The NavigationBar used in this navigation view. It defaults to be docked to the top.
         *
         * You can just pass in a normal object if you want to customize the NavigationBar. For example:
         *
         *     navigationBar: {
         *         ui: 'dark',
         *         docked: 'bottom'
         *     }
         *
         * You **cannot** specify a *title* property in this configuration. The title of the navigationBar is taken
         * from the configuration of this views children:
         *
         *     view.push({
         *         title: 'This views title which will be shown in the navigation bar',
         *         html: 'Some HTML'
         *     });
         *
         * @accessor
         */
        navigationBar: {
            docked: 'top'
        },

        /**
         * @cfg {String} defaultBackButtonText
         * The text to be displayed on the back button if:
         * a) The previous view does not have a title
         * b) The {@link #useTitleForBackButtonText} configuration is true.
         * @accessor
         */
        defaultBackButtonText: 'Back',

        /**
         * @cfg {Boolean} useTitleForBackButtonText
         * Set to false if you always want to display the {@link #defaultBackButtonText} as the text
         * on the back button. True if you want to use the previous views title.
         * @accessor
         */
        useTitleForBackButtonText: false,

        /**
         * @cfg {Array/Object} items The child items to add to this NavigationView. This is usually an array of Component
         * configurations or instances, for example:
         *
         *    Ext.create('Ext.Container', {
         *        items: [
         *            {
         *                xtype: 'panel',
         *                title: 'My title',
         *                html: 'This is an item'
         *            }
         *        ]
         *    });
         *
         * If you want a title to be displayed in the {@link #navigationBar}, you must specify a `title` configuration in your
         * view, like above.
         *
         * Note: only one view will be visible at a time. If you want to change to another view, use the {@link #method-push} or
         * {@link #setActiveItem} methods.
         * @accessor
         */

        /**
         * @cfg
         * @hide
         */
        layout: {
            type: 'card',
            animation: {
                duration: 300,
                easing: 'ease-out',
                type: 'slide',
                direction: 'left'
            }
        }

        // See https://sencha.jira.com/browse/TOUCH-1568
        // If you do, add to #navigationBar config docs:
        //
        //     If you want to add a button on the right of the NavigationBar,
        //     use the {@link #rightButton} configuration.
    },

    /**
     * @event push
     * Fires when a view is pushed into this navigation view
     * @param {Ext.navigation.View} this The component instance
     * @param {Mixed} view The view that has been pushed
     */

    /**
     * @event pop
     * Fires when a view is popped from this navigation view
     * @param {Ext.navigation.View} this The component instance
     * @param {Mixed} view The view that has been popped
     */

    // @private
    initialize: function() {
        //add a listener onto the back button in the navigationbar
        this.getNavigationBar().on({
            scope: this,
            back: this.onBackButtonTap
        });

        this.relayEvents(this, {
            add: 'push',
            remove: 'pop'
        });
    },

    /**
     * @private
     * Disable all animations on Android
     */
    applyLayout: function(config) {
        config = config || {};

        if (Ext.os.is.Android) {
            config.animation = false;
        }

        return config;
    },

    /**
     * @private
     * Called when the user taps on the back button
     */
    onBackButtonTap: function() {
        this.pop();

        this.fireEvent('back', this);
    },

    /**
     * Pushes a new view into this navigation view using the default animation that this view has.
     * @param {Object} view The view to push
     * @return {Ext.Component} The new item you just pushed
     */
    push: function(view) {
        return this.add(view);
    },

    /**
     * Removes the current active view from the stack and sets the previous view using the default animation
     * of this view.
     * @param {Number} count The number of views you want to pop
     * @return {Ext.Component} The new active item
     */
    pop: function(count) {
        if (this.onBeforePop(count)) {
            return this.doPop();
        }
    },

    /**
     * @private
     * Calculates whether it needs to remove any items from the stack when you are popping more than 1
     * item. If it does, it removes those views from the stack and returns `true`.
     * @return {Boolean} True if it has removed views
     */
    onBeforePop: function(count) {
        var me = this,
            innerItems = this.getInnerItems(),
            ln = innerItems.length,
            toRemove, last, i;

        //default to 1 pop
        if (!Ext.isNumber(count) || count < 1) {
            count = 1;
        }

        //check if we are trying to remove more items than we have
        count = Math.min(count, ln - 1);

        if (count) {
            //we need to reset the backButtonStack in the navigation bar
            me.getNavigationBar().onBeforePop(count);

            //get the items we need to remove from the view and remove theme
            toRemove = innerItems.splice(-count, count - 1);
            for (i = 0; i < toRemove.length; i++) {
                this.remove(toRemove[i]);
            }

            return true;
        }

        return false;
    },

    /**
     * @private
     */
    doPop: function(config) {
        var me = this,
            innerItems = this.getInnerItems(),
            layout = me.getLayout(),
            animation = layout.getAnimation();

        //set the new active item to be the new last item of the stack
        me.remove(innerItems[innerItems.length - 1]);

        return this.getActiveItem();
    },

    /**
     * Returns the previous item, if one exists.
     * @return {Mixed} The previous view
     */
    getPreviousItem: function() {
        var innerItems = this.getInnerItems();
        return innerItems[innerItems.length - 2];
    },

    /**
     * Updates the backbutton text accordingly in the {@link #navigationBar}
     * @private
     */
    updateUseTitleForBackButtonText: function(useTitleForBackButtonText) {
        var navigationBar = this.getNavigationBar();
        if (navigationBar) {
            navigationBar.setUseTitleForBackButtonText(useTitleForBackButtonText);
        }
    },

    // @private
    applyNavigationBar: function(config) {
        if (!config) {
            config = {
                hidden: true,
                docked: 'top'
            };
        }

        if (config.title) {
            delete config.title;
            Ext.Logger.warn("Ext.navigation.View: The 'navigationBar' configuration does not accept a 'title' property. You " +
                            "set the title of the navigationBar by giving this navigation view's children a 'title' property.");
        }

        config.view = this;

        return Ext.factory(config, Ext.navigation.Bar, this.getNavigationBar());
    },

    // @private
    updateNavigationBar: function(newNavigationBar, oldNavigationBar) {
        if (oldNavigationBar) {
            this.remove(oldNavigationBar);
        }

        if (newNavigationBar) {
            var layout = this.getLayout(),
                animation = (layout && layout.isLayout) ? layout.getAnimation() : false;

            if (animation && animation.isAnimation) {
                newNavigationBar.setAnimation(animation.config);
            }
            this.add(newNavigationBar);
        }
    },

    /**
     * @private
     */
    applyActiveItem: function(activeItem, currentActiveItem) {
        var innerItems = this.getInnerItems();

        // Make sure the items are already initialized
        this.getItems();

        // If we are not initialzed yet, we should set the active item to the last item in the stack
        if (!this.initialized) {
            activeItem = innerItems.length - 1;
        }

        if (typeof activeItem == 'number') {
            activeItem = Math.max(0, Math.min(activeItem, innerItems.length - 1));
            activeItem = innerItems[activeItem];

            if (activeItem) {
                return activeItem;
            }
            else if (currentActiveItem) {
                return null;
            }
        } else if (activeItem) {
            if (!activeItem.isComponent) {
                activeItem = this.factoryItem(activeItem);
            }

            if (!activeItem.isInnerItem()) {
                Ext.Logger.error("Setting activeItem to be a non-inner item");
            }

            if (!this.has(activeItem)) {
                this.add(activeItem);
            }

            return activeItem;
        }
    },

    remove: function(item, destroy) {
        var me = this,
            index = me.indexOf(item),
            innerItems = this.getInnerItems(),
            animation = this.getLayout().getAnimation(),
            innerIndex;

        if (destroy === undefined) {
            destroy = me.getAutoDestroy();
        }

        if (index !== -1) {
            if (!this.removingAll && innerItems.length > 1 && item === this.getActiveItem()) {
                this.on({
                    activeitemchange: 'doRemove',
                    scope: this,
                    single: true,
                    order: 'after',
                    args: [item, index, destroy]
                });

                innerIndex = innerItems.indexOf(item);

                if (innerIndex > 0) {
                    if (animation && animation.isAnimation) {
                        animation.setReverse(true);
                    }

                    this.setActiveItem(innerIndex - 1);

                    this.getNavigationBar().onViewRemove(this, innerItems[innerIndex], innerIndex);
                }
            }
            else {
                this.doRemove(item, index, destroy);
            }
        }

        return me;
    },

    /**
     * @private
     */
    doRemove: function() {
        var animation = this.getLayout().getAnimation();

        if (animation && animation.isAnimation) {
            animation.setReverse(false);
        }

        this.callParent(arguments);
    },

    /**
     * @private
     */
    onItemAdd: function(item, index) {
        this.doItemLayoutAdd(item, index);

        if (!this.isItemsInitializing && item.isInnerItem()) {
            this.setActiveItem(item);
            this.getNavigationBar().onViewAdd(this, item, index);
        }

        if (this.initialized) {
            this.fireEvent('add', this, item, index);
        }
    },

    /**
     * Resets the view by removing all items between the first and last item.
     * @return {Ext.Component} The view that is now active
     */
    reset: function() {
        this.pop(this.getInnerItems().length);
    }
});

/**
 * @private
 *
 * A general {@link Ext.picker.Picker} slot class.  Slots are used to organize multiple scrollable slots into
 * a single {@link Ext.picker.Picker}.
 *
 *     {
 *         name : 'limit_speed',
 *         title: 'Speed Limit',
 *         data : [
 *             {text: '50 KB/s', value: 50},
 *             {text: '100 KB/s', value: 100},
 *             {text: '200 KB/s', value: 200},
 *             {text: '300 KB/s', value: 300}
 *         ]
 *     }
 *
 * See the {@link Ext.picker.Picker} documentation on how to use slots.
 */
Ext.define('Ext.picker.Slot', {
    extend: 'Ext.DataView',
    xtype : 'pickerslot',
    alternateClassName: 'Ext.Picker.Slot',
    requires: [
        'Ext.XTemplate',
        'Ext.data.Store',
        'Ext.Component',
        'Ext.data.StoreManager'
    ],

    /**
     * @event slotpick
     * Fires whenever an slot is picked
     * @param {Ext.picker.Slot} this
     * @param {Mixed} value The value of the pick
     * @param {HTMLElement} node The node element of the pick
     */

    isSlot: true,

    config: {
        /**
         * @cfg {String} title
         * The title to use for this slot. Null for no title
         * @accessor
         */
        title: null,

        /**
         * @private
         * @cfg {Boolean} showTitle
         * @accessor
         */
        showTitle: true,

        /**
         * @private
         * @cfg {String} cls
         * The main component class
         * @accessor
         */
        cls: Ext.baseCSSPrefix + 'picker-slot',

        /**
         * @cfg {String} name
         * The name of this slot. This config option is required.
         * @accessor
         */
        name: null,

        /**
         * @cfg {Number} value The value of this slot
         * @accessor
         */
        value: null,

        /**
         * @cfg {Number} flex
         * @accessor
         * @hide
         */
        flex: 1,

        /**
         * @cfg {String} align
         * The horizontal alignment of the slot's contents. Valid values are "left", "center",
         * and "right". Defaults to "left".
         * @accessor
         */
        align: 'left',

        /**
         * @cfg {String} displayField
         * The display field in the store.
         * Defaults to 'text'.
         * @accessor
         */
        displayField: 'text',

        /**
         * @cfg {String} valueField
         * The value field in the store.
         * Defaults to 'value'.
         * @accessor
         */
        valueField: 'value',

        /**
         * @cfg {Object} scrollable
         * @accessor
         * @hide
         */
        scrollable: {
            direction: 'vertical',
            indicators: false,
            momentumEasing: {
                minVelocity: 2
            },
            slotSnapEasing: {
                duration: 100
            }
        }
    },

    constructor: function() {
        /**
         * @property selectedIndex
         * @type Number
         * The current selectedIndex of the picker slot
         * @private
         */
        this.selectedIndex = 0;

        /**
         * @property picker
         * @type Ext.picker.Picker
         * A reference to the owner Picker
         * @private
         */

        this.callParent(arguments);
    },

    /**
     * Sets the title for this dataview by creating element
     */
    applyTitle: function(title) {
        //check if the title isnt defined
        if (title) {
            //create a new title element
            title = Ext.create('Ext.Component', {
                cls: Ext.baseCSSPrefix + 'picker-slot-title',
                docked      : 'top',
                html        : title
            });
        }

        return title;
    },

    updateTitle: function(newTitle, oldTitle) {
        if (newTitle) {
            this.add(newTitle);
            this.setupBar();
        }

        if (oldTitle) {
            this.remove(oldTitle);
        }
    },

    updateShowTitle: function(showTitle) {
        var title = this.getTitle();
        if (title) {
            title[showTitle ? 'show' : 'hide']();

            this.setupBar();
        }
    },

    updateDisplayField: function(newDisplayField) {
        this.setItemTpl('<div class="' + Ext.baseCSSPrefix + 'picker-item {cls} <tpl if="extra">' + Ext.baseCSSPrefix + 'picker-invalid</tpl>">{' + newDisplayField + '}</div>');
    },

    /**
     * Updates the {@link #align} configuration
     */
    updateAlign: function(newAlign, oldAlign) {
        var element = this.element;
        element.addCls(Ext.baseCSSPrefix + 'picker-' + newAlign);
        element.removeCls(Ext.baseCSSPrefix + 'picker-' + oldAlign);
    },

    /**
     * Looks at the {@link #data} configuration and turns it into {@link #store}
     */
    applyData: function(data) {
        var parsedData = [],
            ln = data && data.length,
            i, item, obj;

        if (data && Ext.isArray(data) && ln) {
            for (i = 0; i < ln; i++) {
                item = data[i];
                obj = {};
                if (Ext.isArray(item)) {
                    obj[this.valueField] = item[0];
                    obj[this.displayField] = item[1];
                }
                else if (Ext.isString(item)) {
                    obj[this.valueField] = item;
                    obj[this.displayField] = item;
                }
                else if (Ext.isObject(item)) {
                    obj = item;
                }
                parsedData.push(obj);
            }
        }

        return data;
    },

    updateData: function(data) {
        this.setStore(Ext.create('Ext.data.Store', {
            model: 'x-textvalue',
            data : data
        }));
    },

    // @private
    initialize: function() {
        this.callParent();

        var scroller = this.getScrollable().getScroller();

        this.on({
            scope: this,
            painted: 'onPainted',
            itemtap: 'doItemTap'
        });

        scroller.on({
            scope: this,
            scrollend: 'onScrollEnd'
        });
    },

    // @private
    onPainted: function() {
        this.setupBar();
    },

    /**
     * Returns an instance of the owner picker
     * @private
     */
    getPicker: function() {
        if (!this.picker) {
            this.picker = this.getParent();
        }

        return this.picker;
    },

    // @private
    setupBar: function() {
        if (!this.rendered) {
            //if the component isnt rendered yet, there is no point in calculating the padding just eyt
            return;
        }

        var element = this.element,
            innerElement = this.innerElement,
            picker = this.getPicker(),
            bar = picker.bar,
            value = this.getValue(),
            showTitle = this.getShowTitle(),
            title = this.getTitle(),
            scrollable = this.getScrollable(),
            scroller = scrollable.getScroller(),
            titleHeight = 0,
            barY, elY, barHeight, padding, paddingBottom;

        barY = bar.getY();
        elY = element.getY();

        if (showTitle && title) {
            elY += title.element.getHeight();
        }

        barHeight = bar.getHeight();

        if (showTitle && title) {
            titleHeight = title.element.getHeight();
        }

        padding = Math.ceil((element.getHeight() - titleHeight - barHeight) / 2);
        this.slotPadding = padding;

        innerElement.setStyle({
            padding: padding + 'px 0 ' + (padding) + 'px'
        });

        scroller.refresh();
        scroller.setSlotSnapSize(barHeight);

        this.setValue(value);
    },

    // @private
    doItemTap: function(list, index, item, e) {
        var me = this;
        me.selectedIndex = index;
        me.selectedNode = item;
        me.scrollToItem(item, true);

        me.fireEvent('slotpick', me, me.getValue(), me.selectedNode);
    },

    // @private
    scrollToItem: function(item, animated) {
        var y = item.getY(),
            parentEl = item.parent(),
            parentY = parentEl.getY(),
            // padding = this.slotPadding,
            scrollView = this.getScrollable(),
            scroller = scrollView.getScroller(),
            difference;

        difference = y - parentY;

        scroller.scrollTo(0, difference, animated);
    },

    // @private
    onScrollEnd: function(scroller, x, y) {
        var me = this,
            index = Math.round(y / me.picker.bar.getHeight()),
            viewItems = me.getViewItems(),
            item = viewItems[index];

        if (item) {
            me.selectedIndex = index;
            me.selectedNode = item;

            me.fireEvent('slotpick', me, me.getValue(), me.selectedNode);
        }
    },

    /**
     * Returns the vlaue of this slot
     * @private
     */
    getValue: function() {
        var store = this.getStore(),
            record, value;

        if (!store) {
            return;
        }

        //if the value is ever false, that means we do not want to return anything
        if (this._value === false) {
            return null;
        }

        record = store.getAt(this.selectedIndex);

        value = record ? record.get(this.getValueField()) : null;
        this._value = value;

        return value;
    },

    /**
     * Sets the value of this slot
     * @private
     */
    setValue: function(value) {
        if (!Ext.isDefined(value)) {
            return;
        }

        if (!this.rendered || !value) {
            //we don't want to call this until the slot has been rendered
            this._value = value;
            return;
        }

        var store = this.getStore(),
            viewItems = this.getViewItems(),
            valueField = this.getValueField(),
            index, item;

        index = store.find(valueField, value);
        if (index != -1) {
            item = Ext.get(viewItems[index]);

            this.selectedIndex = index;
            this.scrollToItem(item);

            this._value = value;
        }
    },

    /**
     * Sets the value of this slot
     * @private
     */
    setValueAnimated: function(value) {
        if (!value) {
            return;
        }

        if (!this.rendered) {
            //we don't want to call this until the slot has been rendered
            this._value = value;
            return;
        }

        var store = this.getStore(),
            viewItems = this.getViewItems(),
            valueField = this.getValueField(),
            index, item;

        index = store.find(valueField, value);
        if (index != -1) {
            item = Ext.get(viewItems[index]);

            this.selectedIndex = index;
            this.scrollToItem(item, {
                duration: 100
            });

            this._value = value;
        }
    }
});

/**
A general picker class. {@link Ext.picker.Slot}s are used to organize multiple scrollable slots into a single picker. {@link #slots} is
the only necessary configuration.

The {@link #slots} configuration with a few key values:

 - **name:** The name of the slot (will be the key when using {@link #getValues} in this {@link Ext.picker.Picker})
 - **title:** The title of this slot (if {@link #useTitles} is set to true)
 - **data/store:** The data or store to use for this slot.

Remember, {@link Ext.picker.Slot} class extends from {@link Ext.dataview.DataView}.

## Examples

    @example miniphone preview
    var picker = Ext.create('Ext.Picker', {
        slots: [
            {
                name : 'limit_speed',
                title: 'Speed',
                data : [
                    {text: '50 KB/s', value: 50},
                    {text: '100 KB/s', value: 100},
                    {text: '200 KB/s', value: 200},
                    {text: '300 KB/s', value: 300}
                ]
            }
        ]
    });
    Ext.Viewport.add(picker);
    picker.show();

You can also customize the top toolbar on the {@link Ext.picker.Picker} by changing the {@link #doneButton} and {@link #cancelButton} configurations:

    @example miniphone preview
    var picker = Ext.create('Ext.Picker', {
        doneButton: 'I\'m done!',
        cancelButton: false,
        slots: [
            {
                name : 'limit_speed',
                title: 'Speed',
                data : [
                    {text: '50 KB/s', value: 50},
                    {text: '100 KB/s', value: 100},
                    {text: '200 KB/s', value: 200},
                    {text: '300 KB/s', value: 300}
                ]
            }
        ]
    });
    Ext.Viewport.add(picker);
    picker.show();

Or by passing a custom {@link #toolbar} configuration:

    @example miniphone preview
    var picker = Ext.create('Ext.Picker', {
        doneButton: false,
        cancelButton: false,
        toolbar: {
            ui: 'light',
            title: 'My Picker!'
        },
        slots: [
            {
                name : 'limit_speed',
                title: 'Speed',
                data : [
                    {text: '50 KB/s', value: 50},
                    {text: '100 KB/s', value: 100},
                    {text: '200 KB/s', value: 200},
                    {text: '300 KB/s', value: 300}
                ]
            }
        ]
    });
    Ext.Viewport.add(picker);
    picker.show();
 */
Ext.define('Ext.picker.Picker', {
    extend: 'Ext.Sheet',
    alias : 'widget.picker',
    alternateClassName: 'Ext.Picker',
    requires: ['Ext.picker.Slot', 'Ext.Toolbar', 'Ext.data.Model'],

    isPicker: true,

    /**
     * @event pick
     * Fired when a slot has been picked
     * @param {Ext.Picker} this This Picker
     * @param {Object} The values of this picker's slots, in {name:'value'} format
     * @param {Ext.Picker.Slot} slot An instance of Ext.Picker.Slot that has been picked
     */

    /**
     * @event change
     * Fired when the picked value has changed
     * @param {Ext.Picker} this This Picker
     * @param {Object} The values of this picker's slots, in {name:'value'} format
     */

    /**
     * @event cancel
     * Fired when the cancel button is tapped and the values are reverted back to
     * what they were
     * @param {Ext.Picker} this This Picker
     */

    config: {
        // @inherited
        cls: Ext.baseCSSPrefix + 'picker',

        /**
         * @cfg {String/Mixed} doneButton
         * Can be either:<ul>
         * <li>A {String} text to be used on the Done button</li>
         * <li>An {Object} as config for {@link Ext.Button}</li>
         * <li>false or null to hide it</li></ul>
         * @accessor
         */
        doneButton: true,

        /**
         * @cfg {String/Mixed} cancelButton
         * Can be either:<ul>
         * <li>A {String} text to be used on the Cancel button</li>
         * <li>An {Object} as config for {@link Ext.Button}</li>
         * <li>false or null to hide it</li></ul>
         * @accessor
         */
        cancelButton: true,

        /**
         * @cfg {Boolean} useTitles
         * Generate a title header for each individual slot and use
         * the title configuration of the slot.
         * @accessor
         */
        useTitles: false,

        /**
         * @cfg {Array} slots
         * An array of slot configurations.
         * <ul>
         *  <li>name - {String} - Name of the slot</li>
         *  <li>data - {Array} - An array of text/value pairs in the format {text: 'myKey', value: 'myValue'}</li>
         *  <li>title - {String} - Title of the slot. This is used in conjunction with useTitles: true.</li>
         * </ul>
         * @accessor
         */
        slots: null,

        /**
         * @cfg {String/Number} value The value to initialize the picker with
         * @accessor
         */
        value: null,

        /**
         * @cfg {Number} height
         * The height of the picker.
         * @accessor
         */
        height: 220,

        // @inherit
        layout: {
            type : 'hbox',
            align: 'stretch'
        },

        /**
         * @cfg
         * @hide
         */
        centered: false,

        // @inherit
        left : 0,

        // @inherit
        right: 0,

        // @inherit
        bottom: 0,

        // @private
        defaultType: 'pickerslot',

        /**
         * @cfg {Ext.TitleBar/Ext.Toolbar/Object} toolbar
         * The toolbar which contains the {@link #doneButton} and {@link #cancelButton} buttons.
         * You can override this if you wish, and add your own configurations. Just ensure that you take into account
         * the {@link #doneButton} and {@link #cancelButton} configurations.
         *
         * The default xtype is a {@link Ext.TitleBar}:
         *
         *     toolbar: {
         *         items: [
         *             {
         *                 xtype: 'button',
         *                 text: 'Left',
         *                 align: 'left'
         *             },
         *             {
         *                 xtype: 'button',
         *                 text: 'Right',
         *                 align: 'left'
         *             }
         *         ]
         *     }
         *
         * Or to use a {@link Ext.Toolbar instead}:
         *
         *     toolbar: {
         *         xtype: 'toolbar',
         *         items: [
         *             {
         *                 xtype: 'button',
         *                 text: 'Left'
         *             },
         *             {
         *                 xtype: 'button',
         *                 text: 'Left Two'
         *             }
         *         ]
         *     }
         *
         * @accessor
         */
        toolbar: true
    },

    initElement: function() {
        this.callParent(arguments);

        var me = this,
            clsPrefix = Ext.baseCSSPrefix,
            innerElement = this.innerElement;

        //insert the mask, and the picker bar
        this.mask = innerElement.createChild({
            cls: clsPrefix + 'picker-mask'
        });

        this.bar = this.mask.createChild({
            cls: clsPrefix + 'picker-bar'
        });

        me.on({
            scope   : this,
            delegate: 'pickerslot',
            slotpick: 'onSlotPick'
        });

        me.on({
            scope: this,
            show: 'onShow'
        });
    },

    /**
     * @private
     */
    applyToolbar: function(config) {
        if (config === true) {
            config = {};
        }

        Ext.applyIf(config, {
            docked: 'top'
        });

        return Ext.factory(config, 'Ext.TitleBar', this.getToolbar());
    },

    /**
     * @private
     */
    updateToolbar: function(newToolbar, oldToolbar) {
        if (newToolbar) {
            this.add(newToolbar);
        }

        if (oldToolbar) {
            this.remove(oldToolbar);
        }
    },

    /**
     * Updates the {@link #doneButton} configuration. Will change it into a button when appropriate, or just update the text if needed.
     */
    applyDoneButton: function(config) {
        if (config) {
            if (Ext.isBoolean(config)) {
                config = {};
            }

            if (typeof config == "string") {
                config = {
                    text: config
                };
            }

            Ext.applyIf(config, {
                ui: 'action',
                align: 'right',
                text: 'Done'
            });
        }

        return Ext.factory(config, 'Ext.Button', this.getDoneButton());
    },

    updateDoneButton: function(newDoneButton, oldDoneButton) {
        var toolbar = this.getToolbar();

        if (newDoneButton) {
            toolbar.add(newDoneButton);
            newDoneButton.on('tap', this.onDoneButtonTap, this);
        } else if (oldDoneButton) {
            toolbar.remove(oldDoneButton);
        }
    },

    /**
     * Updates the {@link #cancelButton} configuration. Will change it into a button when appropriate, or just update the text if needed.
     */
    applyCancelButton: function(config) {
        if (config) {
            if (Ext.isBoolean(config)) {
                config = {};
            }

            if (typeof config == "string") {
                config = {
                    text: config
                };
            }

            Ext.applyIf(config, {
                align: 'left',
                text: 'Cancel'
            });
        }

        return Ext.factory(config, 'Ext.Button', this.getCancelButton());
    },

    updateCancelButton: function(newCancelButton, oldCancelButton) {
        var toolbar = this.getToolbar();

        if (newCancelButton) {
            toolbar.add(newCancelButton);
            newCancelButton.on('tap', this.onCancelButtonTap, this);
        } else if (oldCancelButton) {
            toolbar.remove(oldCancelButton);
        }
    },

    /**
     *
     */
    updateUseTitles: function(useTitles) {
        var innerItems = this.getInnerItems(),
            ln = innerItems.length,
            cls = Ext.baseCSSPrefix + 'use-titles',
            i, innerItem;

        //add a cls onto the picker
        if (useTitles) {
            this.addCls(cls);
        } else {
            this.removeCls(cls);
        }

        //show the titme on each of the slots
        for (i = 0; i < ln; i++) {
            innerItem = innerItems[i];

            if (innerItem.isSlot) {
                innerItem.setShowTitle(useTitles);
            }
        }
    },

    applySlots: function(slots) {
        //loop through each of the slots and add a referece to this picker
        if (slots) {
            var ln = slots.length,
                i;

            for (i = 0; i < ln; i++) {
                slots[i].picker = this;
            }
        }

        return slots;
    },

    /**
     * Adds any new {@link #slots} to this picker, and removes existing {@link #slots}
     * @private
     */
    updateSlots: function(newSlots) {
        this.removeAll();

        if (newSlots) {
            this.add(newSlots);
        }

        this.updateUseTitles(this.getUseTitles());
    },

    /**
     * @private
     * Called when the done button has been tapped.
     */
    onDoneButtonTap: function() {
        // var anim = this.animSheet('exit');
        // Ext.apply(anim, {
        //     after: function() {
        //
        //     },
        //     scope: this
        // });
        this.fireEvent('change', this, this.getValue());
        this.hide();
    },

    /**
     * @private
     * Called when the cancel button has been tapped.
     */
    onCancelButtonTap: function() {
        // var anim = this.animSheet('exit');
        // Ext.apply(anim, {
        //     after: function() {
        //         // Set the value back to what it was previously
        //         this.setValue(this.values);
        //         this.fireEvent('cancel', this);
        //     },
        //     scope: this
        // });
        this.fireEvent('cancel', this);
        this.hide();
    },

    /**
     * @private
     * Called when a slot has been picked.
     */
    onSlotPick: function(slot, value, node) {
        this.fireEvent('pick', this, this.getValue(), slot);
    },

    onShow: function() {
        if (!this.isHidden()) {
            this.setValue(this._value);
        }
    },

    /**
     * Sets the values of the pickers slots
     * @param {Object} values The values in a {name:'value'} format
     * @param {Boolean} animated True to animate setting the values
     * @return {Ext.Picker} this This picker
     */
    setValue: function(values, animated) {
        var me = this,
            slots = me.getInnerItems(),
            ln = slots.length,
            key, slot, loopSlot, i;

        if (!values) {
            values = {};
            for (i = 0; i < ln; i++) {
                //set the value to false so the slot will return null when getValue is set
                values[slots[i].config.name] = false;
            }
        }

        for (key in values) {
            value = values[key];
            for (i = 0; i < slots.length; i++) {
                loopSlot = slots[i];
                if (loopSlot.config.name == key) {
                    slot = loopSlot;
                    break;
                }
            }

            if (slot) {
                if (animated) {
                    slot.setValueAnimated(value);
                } else {
                    slot.setValue(value);
                }
            }
        }

        me._value = values;
        me._values = values;

        return me;
    },

    setValueAnimated: function(values) {
        this.setValue(values, true);
    },

    /**
     * Returns the values of each of the pickers slots
     * @return {Object} The values of the pickers slots
     */
    getValue: function() {
        var parent = this.getParent(),
            values = {},
            items = this.getItems().items,
            ln = items.length,
            item, i;

        if (parent) {
            for (i = 0; i < ln; i++) {
                item = items[i];
                if (item && item.isSlot) {
                    values[item.getName()] = item.getValue();
                }
            }

            this._values = values;
        }

        return this._values;
    },

    /**
     * Returns the values of eaach of the pickers slots
     * @return {Object} The values of the pickers slots
     */
    getValues: function() {
        return this.getValue();
    },

    destroy: function() {
        this.callParent();
        Ext.destroy(this.mask, this.bar);
    }
}, function() {

    Ext.define('x-textvalue', {
        extend: 'Ext.data.Model',
        config: {
            fields: ['text', 'value']
        }
    });
});


/**
 * Simple Select field wrapper. Example usage:
 *
 *     @example
 *     Ext.create('Ext.form.Panel', {
 *         fullscreen: true,
 *         items: [
 *             {
 *                 xtype: 'fieldset',
 *                 title: 'Select',
 *                 items: [
 *                     {
 *                         xtype: 'selectfield',
 *                         label: 'Choose one',
 *                         options: [
 *                             {text: 'First Option',  value: 'first'},
 *                             {text: 'Second Option', value: 'second'},
 *                             {text: 'Third Option',  value: 'third'}
 *                         ]
 *                     }
 *                 ]
 *             }
 *         ]
 *     });
 *
 */
Ext.define('Ext.field.Select', {
    extend: 'Ext.field.Text',
    xtype: 'selectfield',
    alternateClassName: 'Ext.form.Select',
    requires: [
        'Ext.Panel',
        'Ext.picker.Picker',
        'Ext.data.Store',
        'Ext.data.StoreManager',
        'Ext.List'
    ],

    /**
     * @event change
     * Fires when an option selection has changed
     * @param {Ext.field.Select} this
     * @param {Mixed} newValue The new value
     * @param {Mixed} oldValue The old value
     */

    config: {
        // @inherit
        ui: 'select',

        /**
         * @cfg {Boolean} useClearIcon
         * @hide
         */

        /**
         * @cfg {String/Number} valueField The underlying {@link Ext.data.Field#name data value name} (or numeric Array index) to bind to this
         * Select control. (defaults to 'value')
         * @accessor
         */
        valueField: 'value',

        /**
         * @cfg {String/Number} displayField The underlying {@link Ext.data.Field#name data value name} (or numeric Array index) to bind to this
         * Select control. This resolved value is the visibly rendered value of the available selection options.
         * (defaults to 'text')
         * @accessor
         */
        displayField: 'text',

        /**
         * @cfg {Ext.data.Store} store (Optional) store instance used to provide selection options data.
         * @accessor
         */
        store: null,

        /**
         * @cfg {Array} options (Optional) An array of select options.
    <pre><code>
        [
            {text: 'First Option',  value: 'first'},
            {text: 'Second Option', value: 'second'},
            {text: 'Third Option',  value: 'third'}
        ]
    </code></pre>
         * Note: option object member names should correspond with defined {@link #valueField valueField} and {@link #displayField displayField} values.
         * This config will be ignore if a {@link #store store} instance is provided
         * @accessor
         */
        options: null,

        /**
         * @cfg {String} hiddenName Specify a hiddenName if you're using the {@link Ext.form.Panel#standardSubmit standardSubmit} option.
         * This name will be used to post the underlying value of the select to the server.
         * @accessor
         */
        hiddenName: null,

        /**
         * @cfg {Object} component
         * @accessor
         * @hide
         */
        component: {
            useMask: true
        },

        /**
         * @cfg {Boolean} clearIcon
         * @hide
         * @accessor
         */
        clearIcon: false,

        /**
         * @cfg {String/Boolean} usePicker
         * `true` if you want this component to always use a {@link Ext.picker.Picker}.
         * `false` if you want it to use a popup overlay {@link Ext.List}.
         * `auto` if you want to show a {@link Ext.picker.Picker} only on phones.
         */
        usePicker: 'auto',

        /**
         * @cfg {Object} defaultPhonePickerConfig
         * The default configuration for the picker component when you are on a phone
         */
        defaultPhonePickerConfig: null,

        /**
         * @cfg {Object} defaultTabletPickerConfig
         * The default configuration for the picker component when you are on a tablet
         */
        defaultTabletPickerConfig: null
    },

    // @private
    constructor: function(config) {
        config = config || {};

        if (!config.store) {
            config.store = true;
        }

        this.callParent([config]);
    },

    // @private
    initialize: function() {
        var me = this,
            component = me.getComponent();

        me.callParent();

        component.on({
            scope: me,
            masktap: 'onMaskTap'
        });

        component.input.dom.disabled = true;
    },

    /**
     * @private
     */
    updateDefaultPhonePickerConfig: function(newConfig) {
        var picker = this.picker;
        if (picker) {
            picker.setConfig(newConfig);
        }
    },

    /**
     * @private
     */
    updateDefaultTabletPickerConfig: function(newConfig) {
        var listPanel = this.listPanel;
        if (listPanel) {
            listPanel.setConfig(newConfig);
        }
    },

    /**
     * @private
     * Checks if the value is `auto`. If it is, it only uses the picker if the current device type
     * is a phone.
     */
    applyUsePicker: function(usePicker) {
        if (usePicker == "auto") {
            usePicker = (Ext.os.deviceType == 'Phone');
        }

        return Boolean(usePicker);
    },

    syncEmptyCls: Ext.emptyFn,

    /**
     * @private
     */
    applyValue: function(value) {
        var record = value,
            index;

        //we call this so that the options configruation gets intiailized, so that a store exists, and we can
        //find the correct value
        this.getOptions();

        if (!(value instanceof Ext.data.Model)) {
            index = this.getStore().find(this.getValueField(), value, null, null, null, true);

            if (index == -1) {
                index = this.getStore().find(this.getDisplayField(), value, null, null, null, true);
            }

            record = this.getStore().getAt(index);
        }

        return record;
    },

    updateValue: function(newValue, oldValue) {
        this.previousRecord = oldValue;
        this.record = newValue;

        this.callParent([newValue ? newValue.get(this.getDisplayField()) : '']);

        if (oldValue !== newValue) {
            this.fireEvent('change', this, newValue, oldValue);
        }
    },

    getValue: function() {
        var record = this.record;
        return (record) ? record.get(this.getValueField()) : null;
    },

    /**
     * Returns the current selected {@link Ext.data.Model record} instance selected in this field.
     * @return {Ext.data.Model} the record.
     */
    getRecord: function() {
        return this.record;
    },

    // @private
    getPhonePicker: function() {
        var config = this.getDefaultPhonePickerConfig();

        if (!this.picker) {
            this.picker = Ext.create('Ext.picker.Picker', Ext.apply({
                slots: [{
                    align       : 'center',
                    name        : this.getName(),
                    valueField  : this.getValueField(),
                    displayField: this.getDisplayField(),
                    value       : this.getValue(),
                    store       : this.getStore()
                }],
                listeners: {
                    change: this.onPickerChange,
                    scope: this
                }
            }, config));
        }

        return this.picker;
    },

    // @private
    getTabletPicker: function() {
        var config = this.getDefaultTabletPickerConfig();

        if (!this.listPanel) {
            this.listPanel = Ext.create('Ext.Panel', Ext.apply({
                centered: true,
                modal: true,
                cls: Ext.baseCSSPrefix + 'select-overlay',
                layout: 'fit',
                hideOnMaskTap: true,
                items: {
                    xtype: 'list',
                    store: this.getStore(),
                    itemTpl: '<span class="x-list-label">{' + this.getDisplayField() + '}</span>',
                    listeners: {
                        select : this.onListSelect,
                        itemtap: this.onListTap,
                        scope  : this
                    }
                }
            }, config));
        }

        return this.listPanel;
    },

    // @private
    onMaskTap: function() {
        if (this.getDisabled()) {
            return false;
        }

        this.showPicker();

        return false;
    },

    /**
     * Shows the picker for the select field, whether that is a {@link Ext.picker.Picker} or a simple
     * {@link Ext.List list}.
     */
    showPicker: function() {
        //check if the store is empty, if it is, return
        if (this.getStore().getCount() === 0) {
            return;
        }

        if (this.getReadOnly()) {
            return;
        }

        this.isFocused = true;

        //hide the keyboard
        //the causes https://sencha.jira.com/browse/TOUCH-1679
        // Ext.Viewport.hideKeyboard();

        if (this.getUsePicker()) {
            var picker = this.getPhonePicker(),
                name   = this.getName(),
                value  = {};

            value[name] = this.record.get(this.getValueField());
            picker.setValue(value);
            if (!picker.getParent()) {
                Ext.Viewport.add(picker);
            }
            picker.show();
        } else {
            var listPanel = this.getTabletPicker(),
                list = listPanel.down('list'),
                store = list.getStore(),
                index = store.find(this.getValueField(), this.getValue(), null, null, null, true),
                record = store.getAt((index == -1) ? 0 : index);

            if (!listPanel.getParent()) {
                Ext.Viewport.add(listPanel);
            }

            listPanel.showBy(this.getComponent());
            list.select(record, null, true);
        }
    },

    // @private
    onListSelect: function(item, record) {
        var me = this;
        if (record) {
            me.setValue(record);
        }
    },

    onListTap: function() {
        this.listPanel.hide({
            type : 'fade',
            out  : true,
            scope: this
        });
    },

    // @private
    onPickerChange: function(picker, value) {
        var me = this,
            newValue = value[me.getName()],
            store = me.getStore(),
            index = store.find(me.getValueField(), newValue, null, null, null, true),
            record = store.getAt(index);

        me.setValue(record);
    },

    /**
     * Updates the underlying &lt;options&gt; list with new values.
     * @param {Array} options An array of options configurations to insert or append.
<pre><code>
selectBox.setOptions(
    [   {text: 'First Option',  value: 'first'},
        {text: 'Second Option', value: 'second'},
        {text: 'Third Option',  value: 'third'}
    ]).setValue('third');
</code></pre>
     * Note: option object member names should correspond with defined {@link #valueField valueField} and
     * {@link #displayField displayField} values.
     * @return {Ext.field.Select} this
     */
    updateOptions: function(newOptions) {
        var store = this.getStore();

        if (!newOptions) {
            store.clearData();
        }
        else {
            store.setData(newOptions);
            this.onStoreDataChanged(store);
        }
    },

    applyStore: function(store) {
        if (store === true) {
            store = Ext.create('Ext.data.Store', {
                fields: [this.getValueField(), this.getDisplayField()]
            });
        }

        if (store) {
            store = Ext.data.StoreManager.lookup(store);

            store.on({
                scope: this,
                addrecords: this.onStoreDataChanged,
                removerecords: this.onStoreDataChanged,
                updaterecord: this.onStoreDataChanged,
                refresh: this.onStoreDataChanged
            });
        }

        return store;
    },

    updateStore: function(newStore) {
        if (newStore) {
            this.onStoreDataChanged(newStore);
        }
    },

    /**
     * Called when the internal {@link #store}'s data has changed
     */
    onStoreDataChanged: function(store) {
        var initialConfig = this.getInitialConfig(),
            value = this.getValue();

        if (Ext.isDefined(value)) {
            this.updateValue(this.applyValue(value));
        }

        if (this.getValue() === null) {
            if (initialConfig.hasOwnProperty('value')) {
                this.setValue(initialConfig.value);
            }

            if (this.getValue() === null) {
                if (store.getCount() > 0) {
                    this.setValue(store.getAt(0));
                }
            }
        }
    },

    /**
     * @private
     */
    doSetDisabled: function(disabled) {
        Ext.Component.prototype.doSetDisabled.apply(this, arguments);
    },

    /**
     * @private
     */
    setDisabled: function() {
        Ext.Component.prototype.setDisabled.apply(this, arguments);
    },

    /**
     * Resets the Select field to the value of the first record in the store.
     * @return {Ext.field.Select} this
     */
    reset: function() {
        var store = this.getStore(),
            record = (this.originalValue) ? this.originalValue : store.getAt(0);

        if (store && record) {
            this.setValue(record);
        }

        return this;
    },

    onFocus: function(e) {
        this.fireEvent('focus', this, e);

        this.isFocused = true;

        this.showPicker();
    },

    destroy: function() {
        this.callParent(arguments);
        Ext.destroy(this.listPanel, this.picker, this.hiddenField);
    }
});

/**
A date picker component which shows a Date Picker on the screen. This class extends from {@link Ext.picker.Picker}
and {@link Ext.Sheet} so it is a popup.

This component has no required configurations.

## Examples

    @example miniphone preview
    var datePicker = Ext.create('Ext.picker.Date');
    Ext.Viewport.add(datePicker);
    datePicker.show();

You may want to adjust the {@link #yearFrom} and {@link #yearTo} properties:

    @example miniphone preview
    var datePicker = Ext.create('Ext.picker.Date', {
        yearFrom: 2000,
        yearTo  : 2015
    });
    Ext.Viewport.add(datePicker);
    datePicker.show();

You can set the value of the {@link Ext.picker.Date} to the current date using `new Date()`:

    @example miniphone preview
    var datePicker = Ext.create('Ext.picker.Date', {
        value: new Date()
    });
    Ext.Viewport.add(datePicker);
    datePicker.show();

And you can hide the titles from each of the slots by using the {@link #useTitles} configuration:

    @example miniphone preview
    var datePicker = Ext.create('Ext.picker.Date', {
        useTitles: false
    });
    Ext.Viewport.add(datePicker);
    datePicker.show();

 */
Ext.define('Ext.picker.Date', {
    extend: 'Ext.picker.Picker',
    xtype: 'datepicker',
    alternateClassName: 'Ext.DatePicker',
    requires: ['Ext.DateExtras'],

    config: {
        /**
         * @cfg {Number} yearFrom
         * The start year for the date picker.
         * @accessor
         */
        yearFrom: 1980,

        /**
         * @cfg {Number} yearTo
         * The last year for the date picker.
         * @default the current year (new Date().getFullYear())
         * @accessor
         */
        yearTo: new Date().getFullYear(),

        /**
         * @cfg {String} monthText
         * The label to show for the month column.
         * @accessor
         */
        monthText: 'Month',

        /**
         * @cfg {String} dayText
         * The label to show for the day column.
         * @accessor
         */
        dayText: 'Day',

        /**
         * @cfg {String} yearText
         * The label to show for the year column.
         * @accessor
         */
        yearText: 'Year',

        /**
         * @cfg {Array} slotOrder
         * An array of strings that specifies the order of the slots.
         * @accessor
         */
        slotOrder: ['month', 'day', 'year']

        /**
         * @cfg {Object/Date} value
         * Default value for the field and the internal {@link Ext.picker.Date} component. Accepts an object of 'year',
         * 'month' and 'day' values, all of which should be numbers, or a {@link Date}.
         *
         * Examples:
         * {year: 1989, day: 1, month: 5} = 1st May 1989.
         * new Date() = current date
         * @accessor
         */

        /**
         * @cfg {Array} slots
         * @hide
         * @accessor
         */
    },

    initialize: function() {
        this.callParent();

        this.on({
            scope: this,
            delegate: '> slot',
            slotpick: this.onSlotPick
        });
    },

    setValue: function(value, animated) {
        if (Ext.isDate(value)) {
            value = {
                day  : value.getDate(),
                month: value.getMonth() + 1,
                year : value.getFullYear()
            };
        }

        this.callParent([value, animated]);
    },

    getValue: function() {
        var values = {},
            daysInMonth, day, month, year,
            items = this.getItems().items,
            ln = items.length,
            item, i;

        for (i = 0; i < ln; i++) {
            item = items[i];
            if (item instanceof Ext.picker.Slot) {
                values[item.getName()] = item.getValue();
            }
        }

        //if all the slots return null, we should not reutrn a date
        if (values.year === null && values.month === null && values.day === null) {
            return null;
        }

        year = Ext.isNumber(values.year) ? values.year : 1;
        month = Ext.isNumber(values.month) ? values.month : 1;
        day = Ext.isNumber(values.day) ? values.day : 1;

        if (month && year && month && day) {
            daysInMonth = this.getDaysInMonth(month, year);
        }
        day = (daysInMonth) ? Math.min(day, daysInMonth): day;

        return new Date(year, month - 1, day);
    },

    /**
     * Updates the yearFrom configuration
     */
    updateYearFrom: function() {
        if (this.initialized) {
            this.createSlots();
        }
    },

    /**
     * Updates the yearTo configuration
     */
    updateYearTo: function() {
        if (this.initialized) {
            this.createSlots();
        }
    },

    /**
     * Updates the monthText configuration
     */
    updateMonthText: function(newMonthText, oldMonthText) {
        var innerItems = this.getInnerItems,
            ln = innerItems.length,
            item, i;

        //loop through each of the current items and set the title on the correct slice
        if (this.initialized) {
            for (i = 0; i < ln; i++) {
                item = innerItems[i];

                if ((typeof item.title == "string" && item.title == oldMonthText) || (item.title.html == oldMonthText)) {
                    item.setTitle(newMonthText);
                }
            }
        }
    },

    /**
     * Updates the dayText configuraton
     */
    updateDayText: function(newDayText, oldDayText) {
        var innerItems = this.getInnerItems,
            ln = innerItems.length,
            item, i;

        //loop through each of the current items and set the title on the correct slice
        if (this.initialized) {
            for (i = 0; i < ln; i++) {
                item = innerItems[i];

                if ((typeof item.title == "string" && item.title == oldDayText) || (item.title.html == oldDayText)) {
                    item.setTitle(newDayText);
                }
            }
        }
    },

    /**
     * Updates the yearText configuration
     */
    updateYearText: function(yearText) {
        var innerItems = this.getInnerItems,
            ln = innerItems.length,
            item, i;

        //loop through each of the current items and set the title on the correct slice
        if (this.initialized) {
            for (i = 0; i < ln; i++) {
                item = innerItems[i];

                if (item.title == this.yearText) {
                    item.setTitle(yearText);
                }
            }
        }
    },

    // @private
    constructor: function() {
        this.callParent(arguments);
        this.createSlots();
    },

    /**
     * Generates all slots for all years specified by this component, and then sets them on the component
     * @private
     */
    createSlots: function() {
        var me        = this,
            slotOrder = this.getSlotOrder(),
            yearsFrom = me.getYearFrom(),
            yearsTo   = me.getYearTo(),
            years     = [],
            days      = [],
            months    = [],
            ln, tmp, i,
            daysInMonth;

        // swap values if user mixes them up.
        if (yearsFrom > yearsTo) {
            tmp = yearsFrom;
            yearsFrom = yearsTo;
            yearsTo = tmp;
        }

        for (i = yearsFrom; i <= yearsTo; i++) {
            years.push({
                text: i,
                value: i
            });
        }

        daysInMonth = this.getDaysInMonth(1, new Date().getFullYear());

        for (i = 0; i < daysInMonth; i++) {
            days.push({
                text: i + 1,
                value: i + 1
            });
        }

        for (i = 0, ln = Ext.Date.monthNames.length; i < ln; i++) {
            months.push({
                text: Ext.Date.monthNames[i],
                value: i + 1
            });
        }

        var slots = [];

        slotOrder.forEach(function(item) {
            slots.push(this.createSlot(item, days, months, years));
        }, this);

        me.setSlots(slots);
    },

    /**
     * Returns a slot config for a specified date.
     * @private
     */
    createSlot: function(name, days, months, years) {
        switch (name) {
            case 'year':
                return {
                    name: 'year',
                    align: 'center',
                    data: years,
                    title: this.getYearText(),
                    flex: 3
                };
            case 'month':
                return {
                    name: name,
                    align: 'right',
                    data: months,
                    title: this.getMonthText(),
                    flex: 4
                };
            case 'day':
                return {
                    name: 'day',
                    align: 'center',
                    data: days,
                    title: this.getDayText(),
                    flex: 2
                };
        }
    },

    onSlotPick: function() {
        var value = this.getValue(),
            slot = this.getDaySlot(),
            year = value.getFullYear(),
            month = value.getMonth(),
            days = [],
            selected = slot,
            daysInMonth, i;

        if (!value || !Ext.isDate(value) || !slot) {
            return;
        }

        //get the new days of the month for this new date
        daysInMonth = this.getDaysInMonth(month + 1, year);
        for (i = 0; i < daysInMonth; i++) {
            days.push({
                text: i + 1,
                value: i + 1
            });
        }

        // We dont need to update the slot days unless it has changed
        if (slot.getData().length == days.length) {
            return;
        }

        // Now we have the correct amounnt of days for the day slot, lets update it
        var store = slot.getStore(),
            viewItems = slot.getViewItems(),
            valueField = slot.getValueField(),
            index, item;

        slot.setData(days);

        index = store.find(valueField, value.getDate());
        if (index == -1) {
            return;
        }

        item = Ext.get(viewItems[index]);

        slot.selectedIndex = index;
        slot.scrollToItem(item);

        slot._value = value;
    },

    getDaySlot: function() {
        var innerItems = this.getInnerItems(),
            ln = innerItems.length,
            i, slot;

        if (this.daySlot) {
            return this.daySlot;
        }

        for (i = 0; i < ln; i++) {
            slot = innerItems[i];
            if (slot.isSlot && slot.getName() == "day") {
                this.daySlot = slot;
                return slot;
            }
        }

        return null;
    },

    // @private
    getDaysInMonth: function(month, year) {
        var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
        return month == 2 && this.isLeapYear(year) ? 29 : daysInMonth[month-1];
    },

    // @private
    isLeapYear: function(year) {
        return !!((year & 3) === 0 && (year % 100 || (year % 400 === 0 && year)));
    }
});

/**
This is a specialized field which shows a {@link Ext.picker.Date} when tapped. If it has a predefined value,
or a value is selected in the {@link Ext.picker.Date}, it will be displayed like a normal {@link Ext.field.Text}
(but not selectable/changable).

    Ext.create('Ext.field.DatePicker', {
        label: 'Birthday',
        value: new Date()
    });

{@link Ext.field.DatePicker} fields are very simple to implement, and have no required configurations.

## Examples

It can be very useful to set a default {@link #value} configuration on {@link Ext.field.DatePicker} fields. In
this example, we set the {@link #value} to be the current date. You can also use the {@link #setValue} method to
update the value at any time.

    @example miniphone preview
    Ext.create('Ext.form.Panel', {
        fullscreen: true,
        items: [
            {
                xtype: 'fieldset',
                items: [
                    {
                        xtype: 'datepickerfield',
                        label: 'Birthday',
                        name: 'birthday',
                        value: new Date()
                    }
                ]
            },
            {
                xtype: 'toolbar',
                docked: 'bottom',
                items: [
                    { xtype: 'spacer' },
                    {
                        text: 'setValue',
                        handler: function() {
                            var datePickerField = Ext.ComponentQuery.query('datepickerfield')[0];

                            var randomNumber = function(from, to) {
                                return Math.floor(Math.random() * (to - from + 1) + from);
                            };

                            datePickerField.setValue({
                                month: randomNumber(0, 11),
                                day  : randomNumber(0, 28),
                                year : randomNumber(1980, 2011)
                            });
                        }
                    },
                    { xtype: 'spacer' }
                ]
            }
        ]
    });

When you need to retrieve the date from the {@link Ext.field.DatePicker}, you can either use the {@link #getValue} or
{@link #getFormattedValue} methods:

    @example preview
    Ext.create('Ext.form.Panel', {
        fullscreen: true,
        items: [
            {
                xtype: 'fieldset',
                items: [
                    {
                        xtype: 'datepickerfield',
                        label: 'Birthday',
                        name: 'birthday',
                        value: new Date()
                    }
                ]
            },
            {
                xtype: 'toolbar',
                docked: 'bottom',
                items: [
                    {
                        text: 'getValue',
                        handler: function() {
                            var datePickerField = Ext.ComponentQuery.query('datepickerfield')[0];
                            Ext.Msg.alert(null, datePickerField.getValue());
                        }
                    },
                    { xtype: 'spacer' },
                    {
                        text: 'getFormattedValue',
                        handler: function() {
                            var datePickerField = Ext.ComponentQuery.query('datepickerfield')[0];
                            Ext.Msg.alert(null, datePickerField.getFormattedValue());
                        }
                    }
                ]
            }
        ]
    });


 */
Ext.define('Ext.field.DatePicker', {
    extend: 'Ext.field.Text',
    alternateClassName: 'Ext.form.DatePicker',
    xtype: 'datepickerfield',
    requires: [
        'Ext.picker.Date',
        'Ext.DateExtras'
    ],

    /**
     * @event change
     * Fires when a date is selected
     * @param {Ext.field.DatePicker} this
     * @param {Date} date The new date
     */

    config: {
        ui: 'select',

        /**
         * @cfg {Object/Ext.picker.Date} picker
         * An object that is used when creating the internal {@link Ext.picker.Date} component or a direct instance of {@link Ext.picker.Date}
         * Defaults to true
         * @accessor
         */
        picker: true,

        /**
         * @cfg {Boolean}
         * @hide
         * @accessor
         */
        clearIcon: false,

        /**
         * @cfg {Object/Date} value
         * Default value for the field and the internal {@link Ext.picker.Date} component. Accepts an object of 'year',
         * 'month' and 'day' values, all of which should be numbers, or a {@link Date}.
         *
         * Example: {year: 1989, day: 1, month: 5} = 1st May 1989 or new Date()
         * @accessor
         */

        /**
         * @cfg {Boolean} destroyPickerOnHide
         * Whether or not to destroy the picker widget on hide. This save memory if it's not used frequently,
         * but increase delay time on the next show due to re-instantiation. Defaults to false
         * @accessor
         */
        destroyPickerOnHide: false,

        /**
         * @cfg {String} dateFormat The format to be used when displaying the date in this field.
         * Accepts any valid date format. You can view formats over in the {@link Ext.Date} documentation.
         * Defaults to `Ext.util.Format.defaultDateFormat`.
         */
        dateFormat: null,

        /**
         * @cfg {Object}
         * @hide
         */
        component: {
            useMask: true
        }
    },

    initialize: function() {
        this.callParent();

        this.getComponent().on({
            scope: this,

            masktap: 'onMaskTap'
        });

        this.getComponent().input.dom.disabled = true;
    },

    syncEmptyCls: Ext.emptyFn,

    applyValue: function(value) {
        if (!Ext.isDate(value) && !Ext.isObject(value)) {
            value = null;
        }

        if (Ext.isObject(value)) {
            value = new Date(value.year, value.month - 1, value.day);
        }

        return value;
    },

    // @inherit
    updateValue: function(newValue) {
        var picker = this._picker;
        if (picker && picker.isPicker) {
            picker.setValue(newValue);
        }

        // Ext.Date.format expects a Date
        if (newValue !== null) {
            this.getComponent().setValue(Ext.Date.format(newValue, this.getDateFormat() || Ext.util.Format.defaultDateFormat));
        } else {
            this.getComponent().setValue('');
        }

        if (this._picker && this._picker instanceof Ext.picker.Date) {
            this._picker.setValue(newValue);
        }
    },

    /**
     * Updates the date format in the field.
     * @private
     */
    updateDateFormat: function(newDateFormat, oldDateFormat) {
        var value = this.getValue();
        if (newDateFormat != oldDateFormat && Ext.isDate(value) && this._picker && this._picker instanceof Ext.picker.Date) {
            this.getComponent().setValue(Ext.Date.format(value, newDateFormat || Ext.util.Format.defaultDateFormat));
        }
    },

    /**
     * Returns the {@link Date} value of this field.
     * If you wanted a formated date
     * @return {Date} The date selected
     */
    getValue: function() {
        if (this._picker && this._picker instanceof Ext.picker.Date) {
            return this._picker.getValue();
        }

        return this._value;
    },

    /**
     * Returns the value of the field formatted using the specified format. If it is not specified, it will default to
     * {@link #dateFormat} and then {@link Ext.util.Format#defaultDateFormat}.
     * @param {String} format The format to be returned
     * @return {String} The formatted date
     */
    getFormattedValue: function(format) {
        var value = this.getValue();
        return (Ext.isDate(value)) ? Ext.Date.format(value, format || this.getDateFormat() || Ext.util.Format.defaultDateFormat) : value;
    },

    applyPicker: function(picker, pickerInstance) {
        if (pickerInstance && pickerInstance.isPicker) {
            picker = pickerInstance.setConfig(picker);
        }

        return picker;
    },

    getPicker: function() {
        var picker = this._picker;

        if (picker && !picker.isPicker) {
            picker = Ext.factory(picker, Ext.picker.Date);
            picker.on({
                scope: this,

                change: 'onPickerChange',
                hide  : 'onPickerHide'
            });
            picker.hide();
            picker.setValue(this.getValue());
            Ext.Viewport.add(picker);
            this._picker = picker;
        }

        return picker;
    },

    /**
     * @private
     * Listener to the tap event of the mask element. Shows the internal DatePicker component when the button has been tapped.
     */
    onMaskTap: function() {
        if (this.getDisabled()) {
            return false;
        }

        if (this.getReadOnly()) {
            return false;
        }

        this.getPicker().show();

        return false;
    },

    /**
     * Called when the picker changes its value
     * @param {Ext.picker.Date} picker The date picker
     * @param {Object} value The new value from the date picker
     * @private
     */
    onPickerChange: function(picker, value) {
        var me = this;

        me.setValue(value);
        me.fireEvent('change', me, me.getValue());
    },

    /**
     * Destroys the picker when it is hidden, if
     * {@link Ext.field.DatePicker#destroyPickerOnHide destroyPickerOnHide} is set to true
     * @private
     */
    onPickerHide: function() {
        var picker = this.getPicker();

        if (this.getDestroyPickerOnHide() && picker) {
            picker.destroy();
            this.setPicker(null);
        }
    },

    reset: function() {
        this.setValue(this.originalValue);
    },

    // @private
    destroy: function() {
        var picker = this.getPicker();

        if (picker) {
            picker.destroy();
        }

        this.callParent(arguments);
    }
});

/**
 * @private
 * Utility class used by Ext.slider.Slider - should never need to be used directly.
 */
Ext.define('Ext.slider.Thumb', {
    extend: 'Ext.Component',
    xtype : 'thumb',

    config: {
        // @inherit
        baseCls: Ext.baseCSSPrefix + 'thumb',

        // @inherit
        draggable: {
            direction: 'horizontal'
        }
    },

    elementWidth: 0,

    initialize: function() {
        this.callParent();

        this.getDraggable().onBefore({
            dragstart: 'onDragStart',
            drag: 'onDrag',
            dragend: 'onDragEnd',
            scope: this
        });

        this.on('painted', 'onPainted');
    },

    onDragStart: function() {
        if (this.isDisabled()) {
            return false;
        }

        this.relayEvent(arguments);
    },

    onDrag: function() {
        if (this.isDisabled()) {
            return false;
        }

        this.relayEvent(arguments);
    },

    onDragEnd: function() {
        if (this.isDisabled()) {
            return false;
        }

        this.relayEvent(arguments);
    },

    onPainted: function() {
        this.elementWidth = this.element.dom.offsetWidth;
    },

    getElementWidth: function() {
        return this.elementWidth;
    }
});

/**
 * Utility class used by Ext.field.Slider.
 * @private
 */
Ext.define('Ext.slider.Slider', {
    extend: 'Ext.Container',
    xtype: 'slider',

    requires: [
        'Ext.slider.Thumb',
        'Ext.fx.easing.EaseOut'
    ],

    /**
    * @event change
    * Fires when the value changes
    * @param {Ext.field.Slider} this
    * @param {Ext.slider.Thumb} thumb The thumb being changed
    * @param {Number} newValue The new value
    * @param {Number} oldValue The old value
    */

    /**
    * @event dragstart
    * Fires when the slider thumb starts a drag
    * @param {Ext.field.Slider} this
    * @param {Ext.slider.Thumb} thumb The thumb being dragged
    * @param {Array} value The start value
    * @param {Ext.EventObject} e
    */

    /**
    * @event drag
    * Fires when the slider thumb starts a drag
    * @param {Ext.field.Slider} this
    * @param {Ext.slider.Thumb} thumb The thumb being dragged
    * @param {Ext.EventObject} e
    */

    /**
    * @event dragend
    * Fires when the slider thumb starts a drag
    * @param {Ext.field.Slider} this
    * @param {Ext.slider.Thumb} thumb The thumb being dragged
    * @param {Array} value The end value
    * @param {Ext.EventObject} e
    */
    config: {
        baseCls: 'x-slider',

        /**
         * @cfg {Object} thumbConfig The config object to factory {@link Ext.slider.Thumb} instances
         * @accessor
         */
        thumbConfig: {
            draggable: {
                translatable: {
                    easingX: {
                        duration: 300,
                        type: 'ease-out'
                    }
                }
            }
        },

        /**
         * @cfg {Number/Number[]} value The value(s) of this slider's thumbs. If you pass
         * a number, it will assume you have just 1 thumb.
         * @accessor
         */
        value: 0,

        /**
         * @cfg {Number} minValue The lowest value any thumb on this slider can be set to.
         * @accessor
         */
        minValue: 0,

        /**
         * @cfg {Number} maxValue The highest value any thumb on this slider can be set to.
         * @accessor
         */
        maxValue: 100,

        /**
         * @cfg {Number} increment The increment by which to snap each thumb when its value changes. Defaults to 1. Any thumb movement
         * will be snapped to the nearest value that is a multiple of the increment (e.g. if increment is 10 and the user
         * tries to move the thumb to 67, it will be snapped to 70 instead)
         * @accessor
         */
        increment: 1,

        /**
         * @cfg {Boolean} allowThumbsOverlapping Whether or not to allow multiple thumbs to overlap each other.
         * Setting this to true guarantees the ability to select every possible value in between {@link #minValue}
         * and {@link #maxValue} that satisfies {@link #increment}
         * @accessor
         */
        allowThumbsOverlapping: false,

        /**
         * @cfg {Boolean/Object} animation
         * @accessor
         */
        animation: true
    },

    /**
     * @cfg {Number/Number[]} values Alias to {@link #value}
     */

    elementWidth: 0,

    offsetValueRatio: 0,

    activeThumb: null,

    constructor: function(config) {
        config = config || {};

        if (config.hasOwnProperty('values')) {
            config.value = config.values;
        }

        this.callParent([config]);
    },

    // @private
    initialize: function() {
        var element = this.element;

        this.callParent();

        element.on({
            scope: this,
            tap: 'onTap'
        });

        this.on({
            scope: this,
            delegate: '> thumb',
            dragstart: 'onThumbDragStart',
            drag: 'onThumbDrag',
            dragend: 'onThumbDragEnd'
        });

        this.on({
            painted: 'refresh',
            resize: 'refresh'
        });
    },

    /**
     * @private
     */
    factoryThumb: function() {
        return Ext.factory(this.getThumbConfig(), Ext.slider.Thumb);
    },

    /**
     * Returns the Thumb instances bound to this Slider
     * @return {Ext.slider.Thumb[]} The thumb instances
     */
    getThumbs: function() {
        return this.innerItems;
    },

    /**
     * Returns the Thumb instance bound to this Slider
     * @param {Number} [index=0] The index of Thumb to return.
     * @return {Ext.slider.Thumb} The thumb instance
     */
    getThumb: function(index) {
        if (typeof index != 'number') {
            index = 0;
        }

        return this.innerItems[index];
    },

    refreshOffsetValueRatio: function() {
        var valueRange = this.getMaxValue() - this.getMinValue(),
            trackWidth = this.elementWidth - this.thumbWidth;

        this.offsetValueRatio = trackWidth / valueRange;
    },

    refreshElementWidth: function() {
        this.elementWidth = this.element.dom.offsetWidth;
        this.thumbWidth = this.getThumb(0).getElementWidth();
    },

    refresh: function() {
        this.refreshElementWidth();
        this.refreshValue();
    },

    setActiveThumb: function(thumb) {
        var oldActiveThumb = this.activeThumb;

        if (oldActiveThumb && oldActiveThumb !== thumb) {
            oldActiveThumb.setZIndex(null);
        }

        this.activeThumb = thumb;
        thumb.setZIndex(2);

        return this;
    },

    onThumbDragStart: function(thumb, e) {
        if (e.absDeltaX <= e.absDeltaY) {
            return false;
        }
        else {
            e.stopPropagation();
        }

        if (this.getAllowThumbsOverlapping()) {
            this.setActiveThumb(thumb);
        }

        this.dragStartValue = this.getValue()[this.getThumbIndex(thumb)];
        this.fireEvent('dragstart', this, thumb, this.dragStartValue, e);
    },

    onThumbDrag: function(thumb, e, offsetX) {
        var index = this.getThumbIndex(thumb),
            offsetValueRatio = this.offsetValueRatio,
            constrainedValue = this.constrainValue(offsetX / offsetValueRatio);

        e.stopPropagation();

        this.setIndexValue(index, constrainedValue);

        this.fireEvent('drag', this, thumb, this.getValue(), e);

        return false;
    },

    setIndexValue: function(index, value, animation) {
        var thumb = this.getThumb(index),
            values = this.getValue(),
            offsetValueRatio = this.offsetValueRatio,
            draggable = thumb.getDraggable();

        draggable.setOffset(value * offsetValueRatio, null, animation);

        values[index] = this.constrainValue(draggable.getOffset().x / offsetValueRatio);
    },

    onThumbDragEnd: function(thumb, e) {
        this.refreshThumbConstraints(thumb);
        var index = this.getThumbIndex(thumb),
            newValue = this.getValue()[index],
            oldValue = this.dragStartValue;

        this.fireEvent('dragend', this, thumb, this.getValue(), e);
        if (oldValue !== newValue) {
            this.fireEvent('change', this, thumb, newValue, oldValue);
        }
    },

    getThumbIndex: function(thumb) {
        return this.getThumbs().indexOf(thumb);
    },

    refreshThumbConstraints: function(thumb) {
        var allowThumbsOverlapping = this.getAllowThumbsOverlapping(),
            offsetX = thumb.getDraggable().getOffset().x,
            thumbs = this.getThumbs(),
            index = this.getThumbIndex(thumb),
            previousThumb = thumbs[index - 1],
            nextThumb = thumbs[index + 1],
            thumbWidth = this.thumbWidth;

        if (previousThumb) {
            previousThumb.getDraggable().addExtraConstraint({
                max: {
                    x: offsetX - ((allowThumbsOverlapping) ? 0 : thumbWidth)
                }
            });
        }

        if (nextThumb) {
            nextThumb.getDraggable().addExtraConstraint({
                min: {
                    x: offsetX + ((allowThumbsOverlapping) ? 0 : thumbWidth)
                }
            });
        }
    },

    // @private
    onTap: function(e) {
        if (this.isDisabled()) {
            return;
        }

        var targetElement = Ext.get(e.target);

        if (!targetElement || targetElement.hasCls('x-thumb')) {
            return;
        }

        var touchPointX = e.touch.point.x,
            element = this.element,
            elementX = element.getX(),
            offset = touchPointX - elementX - (this.thumbWidth / 2),
            value = this.constrainValue(offset / this.offsetValueRatio),
            values = this.getValue(),
            minDistance = Infinity,
            ln = values.length,
            i, absDistance, testValue, closestIndex, oldValue, thumb;

        if (ln === 1) {
            closestIndex = 0;
        }
        else {
            for (i = 0; i < ln; i++) {
                testValue = values[i];
                absDistance = Math.abs(testValue - value);

                if (absDistance < minDistance) {
                    minDistance = absDistance;
                    closestIndex = i;
                }
            }
        }

        oldValue = values[closestIndex];
        thumb = this.getThumb(closestIndex);

        this.setIndexValue(closestIndex, value, this.getAnimation());
        this.refreshThumbConstraints(thumb);

        if (oldValue !== value) {
            this.fireEvent('change', this, thumb, value, oldValue);
        }
    },

    // @private
    updateThumbs: function(newThumbs) {
        this.add(newThumbs);
    },

    applyValue: function(value) {
        var values = Ext.Array.from(value || 0),
            filteredValues = [],
            previousFilteredValue = this.getMinValue(),
            filteredValue, i, ln;

        for (i = 0,ln = values.length; i < ln; i++) {
            filteredValue = this.constrainValue(values[i]);

            if (filteredValue < previousFilteredValue) {
                Ext.Logger.warn("Invalid values of '"+Ext.encode(values)+"', values at smaller indexes must " +
                    "be smaller than or equal to values at greater indexes");
                filteredValue = previousFilteredValue;
            }

            filteredValues.push(filteredValue);

            previousFilteredValue = filteredValue;
        }

        return filteredValues;
    },

    /**
     * Updates the sliders thumbs with their new value(s)
     */
    updateValue: function(newValue, oldValue) {
        var thumbs = this.getThumbs(),
            ln = newValue.length,
            i;

        this.setThumbsCount(ln);

        for (i = 0; i < ln; i++) {
            thumbs[i].getDraggable().setExtraConstraint(null)
                                    .setOffset(newValue[i] * this.offsetValueRatio);
        }

        for (i = 0; i < ln; i++) {
            this.refreshThumbConstraints(thumbs[i]);
        }
    },

    /**
     * @private
     */
    refreshValue: function() {
        this.refreshOffsetValueRatio();

        this.setValue(this.getValue());
    },

    /**
     * @private
     * Takes a desired value of a thumb and returns the nearest snap value. e.g if minValue = 0, maxValue = 100, increment = 10 and we
     * pass a value of 67 here, the returned value will be 70. The returned number is constrained within {@link #minValue} and {@link #maxValue},
     * so in the above example 68 would be returned if {@link #maxValue} was set to 68.
     * @param {Number} value The value to snap
     * @return {Number} The snapped value
     */
    constrainValue: function(value) {
        var me = this,
            minValue  = me.getMinValue(),
            maxValue  = me.getMaxValue(),
            increment = me.getIncrement(),
            remainder;

        value = parseFloat(value);

        if (isNaN(value)) {
            value = minValue;
        }

        remainder = value % increment;
        value -= remainder;

        if (Math.abs(remainder) >= (increment / 2)) {
            value += (remainder > 0) ? increment : -increment;
        }

        value = Math.max(minValue, value);
        value = Math.min(maxValue, value);

        return value;
    },

    setThumbsCount: function(count) {
        var thumbs = this.getThumbs(),
            thumbsCount = thumbs.length,
            i, ln, thumb;

        if (thumbsCount > count) {
            for (i = 0,ln = thumbsCount - count; i < ln; i++) {
                thumb = thumbs[thumbs.length - 1];
                thumb.destroy();
            }
        }
        else if (thumbsCount < count) {
            for (i = 0,ln = count - thumbsCount; i < ln; i++) {
                this.add(this.factoryThumb());
            }
        }

        return this;
    },

    /**
     * Convience method. Calls {@link #setValue}
     */
    setValues: function(value) {
        this.setValue(value);
    },

    /**
     * Convience method. Calls {@link #getValue}
     */
    getValues: function() {
        return this.getValue();
    },

    // Sets the {@link #increment} configuration
    applyIncrement: function(increment) {
        if (increment === 0) {
            increment = 1;
        }

        return Math.abs(increment);
    },

    // @private
    updateAllowThumbsOverlapping: function(newValue, oldValue) {
        if (typeof oldValue != 'undefined') {
            this.refreshValue();
        }
    },

    // @private
    updateMinValue: function(newValue, oldValue) {
        if (typeof oldValue != 'undefined') {
            this.refreshValue();
        }
    },

    // @private
    updateMaxValue: function(newValue, oldValue) {
        if (typeof oldValue != 'undefined') {
            this.refreshValue();
        }
    },

    // @private
    updateIncrement: function(newValue, oldValue) {
        if (typeof oldValue != 'undefined') {
            this.refreshValue();
        }
    },

    doSetDisabled: function(disabled) {
        this.callParent(arguments);

        var items = this.getItems().items,
            ln = items.length,
            i;

        for (i = 0; i < ln; i++) {
            items[i].setDisabled(disabled);
        }
    }

}, function() {
});

/**
 * The slider is a way to allow the user to select a value from a given numerical range. You might use it for choosing
 * a percentage, combine two of them to get min and max values, or use three of them to specify the hex values for a
 * color. Each slider contains a single 'thumb' that can be dragged along the slider's length to change the value.
 * Sliders are equally useful inside {@link Ext.form.Panel forms} and standalone. Here's how to quickly create a
 * slider in form, in this case enabling a user to choose a percentage:
 *
 *     @example
 *     Ext.create('Ext.form.Panel', {
 *         fullscreen: true,
 *         items: [
 *             {
 *                 xtype: 'sliderfield',
 *                 label: 'Percentage',
 *                 value: 50,
 *                 minValue: 0,
 *                 maxValue: 100
 *             }
 *         ]
 *     });
 *
 * In this case we set a starting value of 50%, and defined the min and max values to be 0 and 100 respectively, giving
 * us a percentage slider. Because this is such a common use case, the defaults for {@link #minValue} and
 * {@link #maxValue} are already set to 0 and 100 so in the example above they could be removed.
 *
 * It's often useful to render sliders outside the context of a form panel too. In this example we create a slider that
 * allows a user to choose the waist measurement of a pair of jeans. Let's say the online store we're making this for
 * sells jeans with waist sizes from 24 inches to 60 inches in 2 inch increments - here's how we might achieve that:
 *
 *     @example
 *     Ext.create('Ext.form.Panel', {
 *         fullscreen: true,
 *         items: [
 *             {
 *                 xtype: 'sliderfield',
 *                 label: 'Waist Measurement',
 *                 minValue: 24,
 *                 maxValue: 60,
 *                 increment: 2,
 *                 value: 32
 *             }
 *         ]
 *     });
 *
 * Now that we've got our slider, we can ask it what value it currently has and listen to events that it fires. For
 * example, if we wanted our app to show different images for different sizes, we can listen to the {@link #change}
 * event to be informed whenever the slider is moved:
 *
 *     slider.on('change', function(field, newValue) {
 *         if (newValue[0] > 40) {
 *             imgComponent.setSrc('large.png')
 *         } else {
 *             imgComponent.setSrc('small.png');
 *         }
 *     }, this);
 *
 * Here we listened to the {@link #change} event on the slider and updated the background image of an
 * {@link Ext.Img image component} based on what size the user selected. Of course, you can use any logic inside your
 * event listener.
 */
Ext.define('Ext.field.Slider', {
    extend  : 'Ext.field.Field',
    xtype   : 'sliderfield',
    requires: ['Ext.slider.Slider'],
    alternateClassName: 'Ext.form.Slider',

    /**
     * @event change
     * Fires when an option selection has changed.
     * @param {Ext.field.Select} me
     * @param {Ext.slider.Thumb} thumb
     * @param {Number} newValue the new value of this thumb
     * @param {Number} oldValue the old value of this thumb
     */

    config: {
        // @inherit
        cls: Ext.baseCSSPrefix + 'slider-field',

        /**
         * @inherit
         */
        tabIndex: -1
    },

    proxyConfig: {
        /**
         * @cfg {Number/Number[]} value See {@link Ext.slider.Slider#value}
         * @accessor
         */
        value: 0,

        /**
         * @cfg {Number} minValue See {@link Ext.slider.Slider#minValue}
         * @accessor
         */
        minValue: 0,

        /**
         * @cfg {Number} maxValue See {@link Ext.slider.Slider#maxValue}
         * @accessor
         */
        maxValue: 100,

        /**
         * @cfg {Number} increment See {@link Ext.slider.Slider#increment}
         * @accessor
         */
        increment: 1
    },

    /**
     * @cfg {Number/Number[]} values See {@link Ext.slider.Slider#values}
     */

    constructor: function(config) {
        config = config || {};

        if (config.hasOwnProperty('values')) {
            config.value = config.values;
        }

        this.callParent([config]);
    },

    // @private
    initialize: function() {
        this.callParent();

        this.getComponent().on({
            scope: this,
            change: 'onSliderChange'
        });
    },

    // @private
    applyComponent: function(config) {
        return Ext.factory(config, Ext.slider.Slider);
    },

    onSliderChange: function(me, thumb, newValue, oldValue) {
        this.fireEvent('change', this, thumb, newValue, oldValue);
    },

    /**
     * Convience method. Calls {@link #setValue}
     */
    setValues: function(value) {
        this.setValue(value);
    },

    /**
     * Convience method. Calls {@link #getValue}
     */
    getValues: function() {
        return this.getValue();
    },

    // @inherit
    reset: function() {
        var config = this.config,
            initialValue = (this.config.hasOwnProperty('values')) ? config.values : config.value;

        this.setValue(initialValue);
    },

    doSetDisabled: function(disabled) {
        this.callParent(arguments);

        this.getComponent().setDisabled(disabled);
    }
});

/**
 * @private
 */
Ext.define('Ext.slider.Toggle', {
    extend: 'Ext.slider.Slider',

    config: {
        // @inherit
        baseCls: 'x-toggle',

        /**
         * @cfg {String} minValueCls CSS class added to the field when toggled to its minValue
         * @accessor
         */
        minValueCls: 'x-toggle-off',

        /**
         * @cfg {String} maxValueCls CSS class added to the field when toggled to its maxValue
         * @accessor
         */
        maxValueCls: 'x-toggle-on'
    },

    initialize: function() {
        this.callParent();

        this.on({
            change: 'onChange'
        });
    },

    applyMinValue: function() {
        return 0;
    },

    applyMaxValue: function() {
        return 1;
    },

    applyIncrement: function() {
        return 1;
    },

    setValue: function(newValue, oldValue) {
        this.callParent(arguments);
        this.onChange(this, this.getThumbs()[0], newValue, oldValue);
    },

    onChange: function(me, thumb, newValue, oldValue) {
        var isOn = newValue > 0,
            onCls = me.getMaxValueCls(),
            offCls = me.getMinValueCls();

        this.element.addCls(isOn ? onCls : offCls);
        this.element.removeCls(isOn ? offCls : onCls);
    }
});

/**
 * Specialized {@link Ext.field.Slider} with a single thumb which only supports two {@link #value values}.
 *
 * ## Examples
 *
 *     @example miniphone preview
 *     Ext.Viewport.add({
 *         xtype: 'togglefield',
 *         name: 'awesome',
 *         label: 'Are you awesome?',
 *         labelWidth: '40%'
 *     });
 *
 * Having a default value of 'toggled':
 *
 *     @example miniphone preview
 *     Ext.Viewport.add({
 *         xtype: 'togglefield',
 *         name: 'awesome',
 *         value: 1,
 *         label: 'Are you awesome?',
 *         labelWidth: '40%'
 *     });
 *
 * And using the {@link #value} {@link #toggle} method:
 *
 *     @example miniphone preview
 *     Ext.Viewport.add([
 *         {
 *             xtype: 'togglefield',
 *             name: 'awesome',
 *             value: 1,
 *             label: 'Are you awesome?',
 *             labelWidth: '40%'
 *         },
 *         {
 *             xtype: 'toolbar',
 *             docked: 'top',
 *             items: [
 *                 {
 *                     xtype: 'button',
 *                     text: 'Toggle',
 *                     flex: 1,
 *                     handler: function() {
 *                         Ext.ComponentQuery.query('togglefield')[0].toggle();
 *                     }
 *                 }
 *             ]
 *         }
 *     ]);
 */
Ext.define('Ext.field.Toggle', {
    extend: 'Ext.field.Slider',
    xtype : 'togglefield',
    alternateClassName: 'Ext.form.Toggle',
    requires: ['Ext.slider.Toggle'],

    config: {
        // @inherit
        cls: 'x-toggle-field'
    },

    proxyConfig: {
        /**
         * @cfg {String} minValueCls See {@link Ext.slider.Toggle#minValueCls}
         * @accessor
         */
        minValueCls: 'x-toggle-off',

        /**
         * @cfg {String} maxValueCls  See {@link Ext.slider.Toggle#maxValueCls}
         * @accessor
         */
        maxValueCls: 'x-toggle-on'
    },

    // @private
    applyComponent: function(config) {
        return Ext.factory(config, Ext.slider.Toggle);
    },

    /**
     * Sets the value of the toggle.
     * @param {Number} value **1** for toggled, **0** for untoggled.
     */
    setValue: function(newValue) {
        if (newValue === true) {
            newValue = 1;
        }

        this.getComponent().setValue(newValue);

        return this;
    },

    /**
     * Toggles the value of this toggle field.
     * @return this
     */
    toggle: function() {
        var value = this.getValue();
        this.setValue((value == 1) ? 0 : 1);

        return this;
    }
});

/**
 * Used in the {@link Ext.tab.Bar} component. This shouldn't be used directly, instead use
 * {@link Ext.tab.Bar} or {@link Ext.tab.Panel}.
 * @private
 */
Ext.define('Ext.tab.Tab', {
    extend: 'Ext.Button',
    xtype: 'tab',
    alternateClassName: 'Ext.Tab',

    // @private
    isTab: true,

    config: {
        // @inherit
        baseCls: Ext.baseCSSPrefix + 'tab',

        /**
         * @cfg {String} pressedCls
         * The CSS class to be applied to a Tab when it is pressed. Defaults to 'x-tab-pressed'.
         * Providing your own CSS for this class enables you to customize the pressed state.
         * @accessor
         */
        pressedCls: Ext.baseCSSPrefix + 'tab-pressed',

        /**
         * @cfg {String} activeCls
         * The CSS class to be applied to a Tab when it is active. Defaults to 'x-tab-active'.
         * Providing your own CSS for this class enables you to customize the active state.
         * @accessor
         */
        activeCls: Ext.baseCSSPrefix + 'tab-active',

        /**
         * @cfg {Boolean} active
         * Set this to true to have the tab be active by default.
         * @accessor
         */
        active: false,

        /**
         * @cfg {String} title
         * The title of the card that this tab is bound to.
         * @accessor
         */
        title: '&nbsp;'
    },

    // We need to override this so the iconElement is properly hidden using visibilty
    // when we render it.
    template: [
        {
            tag: 'span',
            reference: 'badgeElement',
            hidden: true
        },
        {
            tag: 'span',
            className: Ext.baseCSSPrefix + 'button-icon',
            reference: 'iconElement',
            style: 'visibility: hidden !important'
        },
        {
            tag: 'span',
            reference: 'textElement',
            hidden: true
        }
    ],

    /**
     * @event activate
     * Fires when a tab is activated
     * @param {Ext.tab.Tab} this
     */

    /**
     * @event deactivate
     * Fires when a tab is deactivated
     * @param {Ext.tab.Tab} this
     */

     // @inherit
    updateTitle: function(title) {
        this.setText(title);
    },

    // @inherit
    hideIconElement: function() {
        this.iconElement.dom.style.setProperty('visibility', 'hidden', '!important');
    },

    // @inherit
    showIconElement: function() {
        this.iconElement.dom.style.setProperty('visibility', 'visible', '!important');
    },

    // @inherit
    updateActive: function(active, oldActive) {
        var activeCls = this.getActiveCls();
        if (active && !oldActive) {
            this.element.addCls(activeCls);
            this.fireEvent('activate', this);
        } else if (oldActive) {
            this.element.removeCls(activeCls);
            this.fireEvent('deactivate', this);
        }
    }
}, function() {
    this.override({
        activate: function() {
            this.setActive(true);
        },

        deactivate: function() {
            this.setActive(false);
        }
    });
});

/**
 * Ext.tab.Bar is used internally by {@link Ext.tab.Panel} to create the bar of tabs that appears at the top of the tab
 * panel. It's unusual to use it directly, instead see the {@link Ext.tab.Panel tab panel docs} for usage instructions.
 *
 * Used in the {@link Ext.tab.Panel} component to display {@link Ext.tab.Tab} components.
 *
 * @private
 */
Ext.define('Ext.tab.Bar', {
    extend: 'Ext.Toolbar',
    alternateClassName: 'Ext.TabBar',
    xtype : 'tabbar',

    requires: ['Ext.tab.Tab'],

    config: {
        // @inherit
        baseCls: Ext.baseCSSPrefix + 'tabbar',

        // @private
        defaultType: 'tab',

        // @private
        layout: {
            type: 'hbox',
            align: 'middle'
        }
    },

    eventedConfig: {
        /**
         * @cfg {Ext.Component} activeTab
         * @accessor
         */
        activeTab: null
    },

    /**
     * @event tabchange
     * @param {Ext.tab.Bar} this
     * @param {Ext.tab.Tab} newTab The new Tab
     * @param {Ext.tab.Tab} oldTab The old Tab
     */

    initialize: function() {
        var me = this;
        me.callParent();

        me.on({
            tap: 'onTabTap',

            delegate: '> tab',
            scope   : me
        });
    },

    // @private
    onTabTap: function(tab) {
        this.setActiveTab(tab);
    },

    /**
     * @private
     */
    applyActiveTab: function(activeTab, oldActiveTab) {
        if (!activeTab && activeTab !== 0) {
            return;
        }

        var activeTabInstance = this.parseActiveTab(activeTab);

        if (!activeTabInstance) {
            if (oldActiveTab) {
                Ext.Logger.warn('Trying to set a non-existent activeTab');
            }
            return;
        }
        return activeTabInstance;
    },

    /**
     * @private
     * When docked to the top, pack left, when on the bottom pack center
     */
    doSetDocked: function(newDocked) {
        var layout = this.getLayout(),
            pack   = newDocked == 'bottom' ? 'center' : 'left';

        //layout isn't guaranteed to be instantiated so must test
        if (layout.isLayout) {
            layout.setPack(pack);
        } else {
            layout.pack = (layout && layout.pack) ? layout.pack : pack;
        }
    },

    /**
     * @private
     * Sets the active tab
     */
    doSetActiveTab: function(newTab, oldTab) {
        if (newTab) {
            newTab.setActive(true);
        }

        if (oldTab) {
            oldTab.setActive(false);
        }
    },

    /**
     * @private
     * Parses the active tab, which can be a number or string
     */
    parseActiveTab: function(tab) {
        //we need to call getItems to initialize the items, otherwise they will not exist yet.
        if (typeof tab == 'number') {
            return this.getInnerItems()[tab];
        }
        else if (typeof tab == 'string') {
            tab = Ext.getCmp(tab);
        }
        return tab;
    }
});

/**
 * Tab Panels are a great way to allow the user to switch between several pages that are all full screen. Each
 * Component in the Tab Panel gets its own Tab, which shows the Component when tapped on. Tabs can be positioned at
 * the top or the bottom of the Tab Panel, and can optionally accept title and icon configurations.
 *
 * Here's how we can set up a simple Tab Panel with tabs at the bottom. Use the controls at the top left of the example
 * to toggle between code mode and live preview mode (you can also edit the code and see your changes in the live
 * preview):
 *
 *     @example miniphone preview
 *     Ext.create('Ext.TabPanel', {
 *         fullscreen: true,
 *         tabBarPosition: 'bottom',
 *
 *         defaults: {
 *             styleHtmlContent: true
 *         },
 *
 *         items: [
 *             {
 *                 title: 'Home',
 *                 iconCls: 'home',
 *                 html: 'Home Screen'
 *             },
 *             {
 *                 title: 'Contact',
 *                 iconCls: 'user',
 *                 html: 'Contact Screen'
 *             }
 *         ]
 *     });
 * One tab was created for each of the {@link Ext.Panel panels} defined in the items array. Each tab automatically uses
 * the title and icon defined on the item configuration, and switches to that item when tapped on. We can also position
 * the tab bar at the top, which makes our Tab Panel look like this:
 *
 *     @example miniphone preview
 *     Ext.create('Ext.TabPanel', {
 *         fullscreen: true,
 *
 *         defaults: {
 *             styleHtmlContent: true
 *         },
 *
 *         items: [
 *             {
 *                 title: 'Home',
 *                 html: 'Home Screen'
 *             },
 *             {
 *                 title: 'Contact',
 *                 html: 'Contact Screen'
 *             }
 *         ]
 *     });
 *
 */
Ext.define('Ext.tab.Panel', {
    extend: 'Ext.Container',
    xtype : 'tabpanel',
    alternateClassName: 'Ext.TabPanel',

    requires: ['Ext.tab.Bar'],

    /**
     * @cfg {Object} layout
     * @hide
     */
    config: {
        /**
         * @cfg {String} ui
         * Sets the UI of this component.
         * Available values are: `light` and `dark`.
         * @accessor
         */
        ui: 'dark',

        /**
         * @cfg {Object} tabBar
         * An Ext.tab.Bar configuration.
         * @accessor
         */
        tabBar: true,

        /**
         * @cfg {String} tabBarPosition
         * The docked position for the {@link #tabBar} instance.
         * Possible values are 'top' and 'bottom'.
         * @accessor
         */
        tabBarPosition: 'top',

        // @inherit
        layout: {
            type: 'card',
            animation: {
                type: 'slide',
                direction: 'left'
            }
        },

        // @inherit
        cls: Ext.baseCSSPrefix + 'tabpanel'

        /**
         * @cfg {Boolean/String/Object} scrollable
         * @accessor
         * @hide
         */

        /**
         * @cfg {Boolean/String/Object} scroll
         * @hide
         */
    },

    delegateListeners: {
        delegate: '> component',
        centeredchange: 'onItemCenteredChange',
        dockedchange: 'onItemDockedChange',
        floatingchange: 'onItemFloatingChange',
        disabledchange: 'onItemDisabledChange'
    },

    initialize: function() {
        this.callParent();

        this.on({
            order: 'before',
            activetabchange: 'doTabChange',
            delegate: '> tabbar',
            scope   : this
        });
    },

    /**
     * Tab panels should not be scrollable. Instead, you should add scrollable to any item that
     * you want to scroll.
     * @private
     */
    applyScrollable: function() {
        return false;
    },

    /**
     * Updates the Ui for this component and the {@link #tabBar}.
     */
    updateUi: function(newUi, oldUi) {
        this.callParent(arguments);

        if (this.initialized) {
            this.getTabBar().setUi(newUi);
        }
    },

    /**
     * @private
     */
    doSetActiveItem: function(newActiveItem, oldActiveItem) {
        if (newActiveItem) {
            var items = this.getInnerItems(),
                oldIndex = items.indexOf(oldActiveItem),
                newIndex = items.indexOf(newActiveItem),
                reverse = oldIndex > newIndex,
                animation = this.getLayout().getAnimation(),
                tabBar = this.getTabBar(),
                oldTab = tabBar.parseActiveTab(oldIndex),
                newTab = tabBar.parseActiveTab(newIndex);

            if (animation && animation.setReverse) {
                animation.setReverse(reverse);
            }

            this.callParent(arguments);

            if (newIndex != -1) {
                this.getTabBar().setActiveTab(newIndex);

                if (oldTab) {
                    oldTab.setActive(false);
                }

                if (newTab) {
                    newTab.setActive(true);
                }
            }
        }
    },

    /**
     * Updates this container with the new active item.
     */
    doTabChange: function(tabBar, newTab) {
        this.setActiveItem(tabBar.indexOf(newTab));
    },

    /**
     * Creates a new {@link Ext.tab.Bar} instance using {@link Ext#factory}.
     * @private
     */
    applyTabBar: function(config) {
        if (config === true) {
            config = {};
        }

        if (config) {
            Ext.applyIf(config, {
                ui: this.getUi(),
                docked: this.getTabBarPosition()
            });
        }

        return Ext.factory(config, Ext.tab.Bar, this.getTabBar());
    },

    /**
     * Adds the new {@link Ext.tab.Bar} instance into this container.
     * @private
     */
    updateTabBar: function(newTabBar) {
        if (newTabBar) {
            this.add(newTabBar);
            this.setTabBarPosition(newTabBar.getDocked());
        }
    },

    /**
     * Updates the docked position of the {@link #tabBar}.
     * @private
     */
    updateTabBarPosition: function(position) {
        var tabBar = this.getTabBar();
        if (tabBar) {
            tabBar.setDocked(position);
        }
    },

    // @inherit
    onItemAdd: function(card) {
        var me = this;

        if (!card.isInnerItem()) {
            return me.callParent(arguments);
        }

        var tabBar             = me.getTabBar(),
            initialConfig      = card.getInitialConfig(),
            tabConfig          = initialConfig.tab || {},
            tabTitle           = initialConfig.title,
            tabIconCls         = initialConfig.iconCls,
            tabHidden          = initialConfig.hidden,
            tabDisabled        = initialConfig.disabled,
            tabBadgeText       = initialConfig.badgeText,
            innerItems         = me.getInnerItems(),
            index              = innerItems.indexOf(card),
            tabs               = tabBar.getItems(),
            cards              = me.getInnerItems(),
            currentTabInstance = (tabs.length >= cards.length) && tabs.getAt(index),
            tabInstance;

        if (tabTitle && !tabConfig.title) {
            tabConfig.title = tabTitle;
        }

        if (tabIconCls && !tabConfig.iconCls) {
            tabConfig.iconCls = tabIconCls;
        }

        if (tabHidden && !tabConfig.hidden) {
            tabConfig.hidden = tabHidden;
        }

        if (tabDisabled && !tabConfig.disabled) {
            tabConfig.disabled = tabDisabled;
        }

        if (tabBadgeText && !tabConfig.badgeText) {
            tabConfig.badgeText = tabBadgeText;
        }

        if (!currentTabInstance && !tabConfig.title && !tabConfig.iconCls) {
            if (!tabConfig.title && !tabConfig.iconCls) {
                Ext.Logger.error('Adding a card to a tab container without specifying any tab configuration');
            }
        }

        tabInstance = Ext.factory(tabConfig, Ext.tab.Tab, currentTabInstance);

        if (!currentTabInstance) {
            tabBar.insert(index, tabInstance);
        }

        card.tab = tabInstance;

        me.callParent(arguments);
    },

    /**
     * If an item gets enabled/disabled and it has an tab, we should also enable/disable that tab
     * @private
     */
    onItemDisabledChange: function(item, newDisabled) {
        if (item && item.tab) {
            item.tab.setDisabled(newDisabled);
        }
    },

    // @private
    onItemRemove: function(item, index) {
        this.getTabBar().remove(item.tab, this.getAutoDestroy());

        this.callParent(arguments);
    }
}, function() {
});

Ext.define('Ext.table.Cell', {
    extend: 'Ext.Container',

    xtype: 'tablecell',

    config: {
        baseCls: 'x-table-cell'
    },

    getElementConfig: function() {
        var config = this.callParent();

        config.children.length = 0;

        return config;
    }
});

Ext.define('Ext.table.Row', {
    extend: 'Ext.table.Cell',

    xtype: 'tablerow',

    config: {
        baseCls: 'x-table-row',
        defaultType: 'tablecell'
    }
});

Ext.define('Ext.table.Table', {
    extend: 'Ext.Container',

    requires: ['Ext.table.Row'],

    xtype: 'table',

    config: {
        baseCls: 'x-table',
        defaultType: 'tablerow'
    },

    cachedConfig: {
        fixedLayout: false
    },

    fixedLayoutCls: 'x-table-fixed',

    updateFixedLayout: function(fixedLayout) {
        this.innerElement[fixedLayout ? 'addCls' : 'removeCls'](this.fixedLayoutCls);
    }
});

/**
 * @private
 * Base class for iOS and Andorid viewports.
 */
Ext.define('Ext.viewport.Default', {
    extend: 'Ext.Container',

    xtype: 'viewport',

    PORTRAIT: 'portrait',

    LANDSCAPE: 'landscape',

    requires: [
        'Ext.LoadMask'
    ],

    /**
     * @event ready
     * Fires when the Viewport is in the DOM and ready
     * @param {Ext.Viewport} this
     */

    /**
     * @event maximize
     * Fires when the Viewport is maximized
     * @param {Ext.Viewport} this
     */

    /**
     * @event orientationchange
     * Fires when the Viewport orientation has changed
     * @param {Ext.Viewport} this
     * @param {String} newOrientation The new orientation
     * @param {Number} width The width of the Viewport
     * @param {Number} height The height of the Viewport
     */

    config: {
        /**
         * @cfg {Boolean} autoMaximize
         * Whether or not to always automatically maximize the viewport on first load and all subsequent orientation changes.
         *
         * This is set to `false` by default for a number of reasons:
         *
         * - Orientation change performance is drastically reduced when this is enabled, on all devices.
         * - On some devices (mostly Android) this can sometimes cause issues when the default browser zoom setting is changed.
         * - When wrapping your phone in a native shell, you may get a blank screen.
         *
         * @accessor
         */
        autoMaximize: false,

        /**
         * @private
         */
        autoBlurInput: true,

        /**
         * @cfg {Boolean} preventPanning
         * Whether or not to always prevent default panning behavior of the
         * browser's viewport
         * @accessor
         */
        preventPanning: true,

        /**
         * @cfg
         * @private
         */
        preventZooming: true,

        /**
         * @cfg
         * @private
         */
        autoRender: true,

        /**
         * @cfg {Object/String} layout Configuration for this Container's layout. Example:
         *
         *    Ext.create('Ext.Container', {
         *        layout: {
         *            type: 'hbox',
         *            align: 'middle'
         *        },
         *        items: [
         *            {
         *                xtype: 'panel',
         *                flex: 1,
         *                style: 'background-color: red;'
         *            },
         *            {
         *                xtype: 'panel',
         *                flex: 2,
         *                style: 'background-color: green'
         *            }
         *        ]
         *    });
         *
         * See the layouts guide for more information
         *
         * Defaults to {@link Ext.layout.Card card}
         * @accessor
         */
        layout: 'card',

        /**
         * @cfg
         * @private
         */
        width: '100%',

        /**
         * @cfg
         * @private
         */
        height: '100%'
    },

    /**
     * @property {Boolean} isReady
     * True if the DOM is ready
     */
    isReady: false,

    isViewport: true,

    isMaximizing: false,

    id: 'ext-viewport',

    isInputRegex: /^(input|textarea|select|a)$/i,

    focusedElement: null,

    /**
     * @private
     */
    fullscreenItemCls: Ext.baseCSSPrefix + 'fullscreen',

    constructor: function(config) {
        var bind = Ext.Function.bind;

        this.doPreventPanning = bind(this.doPreventPanning, this);
        this.doPreventZooming = bind(this.doPreventZooming, this);
        this.doBlurInput = bind(this.doBlurInput, this);

        this.maximizeOnEvents = ['ready', 'orientationchange'];

        this.orientation = this.determineOrientation();
        this.windowWidth = this.getWindowWidth();
        this.windowHeight = this.getWindowHeight();
        this.windowOuterHeight = this.getWindowOuterHeight();

        if (!this.stretchHeights) {
            this.stretchHeights = {};
        }

        this.callParent([config]);

        if (this.supportsOrientation()) {
            this.addWindowListener('orientationchange', bind(this.onOrientationChange, this));
        }
        else {
            this.addWindowListener('resize', bind(this.onResize, this));
        }

        document.addEventListener('focus', bind(this.onElementFocus, this), true);
        document.addEventListener('blur', bind(this.onElementBlur, this), true);

        Ext.onDocumentReady(this.onDomReady, this);

        this.on('ready', this.onReady, this, {single: true});

        this.getEventDispatcher().addListener('component', '*', 'fullscreen', 'onItemFullscreenChange', this);

        return this;
    },

    onDomReady: function() {
        this.isReady = true;
        this.updateSize();
        this.fireEvent('ready', this);
    },

    onReady: function() {
        if (this.getAutoRender()) {
            this.render();
        }
    },

    onElementFocus: function(e) {
        this.focusedElement = e.target;
    },

    onElementBlur: function() {
        this.focusedElement = null;
    },

    render: function() {
        if (!this.rendered) {
            var body = Ext.getBody(),
                clsPrefix = Ext.baseCSSPrefix,
                classList = [],
                osEnv = Ext.os,
                osName = osEnv.name.toLowerCase(),
                browserName = Ext.browser.name.toLowerCase(),
                osMajorVersion = osEnv.version.getMajor(),
                orientation = this.getOrientation();

            this.renderTo(body);

            classList.push(clsPrefix + osEnv.deviceType.toLowerCase());

            if (osEnv.is.iPad) {
                classList.push(clsPrefix + 'ipad');
            }

            classList.push(clsPrefix + osName);
            classList.push(clsPrefix + browserName);

            if (osMajorVersion) {
                classList.push(clsPrefix + osName + '-' + osMajorVersion);
            }

            if (osEnv.is.BlackBerry) {
                classList.push(clsPrefix + 'bb');
            }

            if (Ext.browser.is.Standalone) {
                classList.push(clsPrefix + 'standalone');
            }

            classList.push(clsPrefix + orientation);

            body.addCls(classList);
        }
    },

    applyAutoBlurInput: function(autoBlurInput) {
        var touchstart = (Ext.feature.has.Touch) ? 'touchstart' : 'mousedown';

        if (autoBlurInput) {
            this.addWindowListener(touchstart, this.doBlurInput, false);
        }
        else {
            this.removeWindowListener(touchstart, this.doBlurInput, false);
        }

        return autoBlurInput;
    },

    applyAutoMaximize: function(autoMaximize) {
        if (autoMaximize) {
            this.on('ready', 'doAutoMaximizeOnReady', this, { single: true });
            this.on('orientationchange', 'doAutoMaximizeOnOrientationChange', this);
        }
        else {
            this.un('ready', 'doAutoMaximizeOnReady', this);
            this.un('orientationchange', 'doAutoMaximizeOnOrientationChange', this);
        }

        return autoMaximize;
    },

    applyPreventPanning: function(preventPanning) {
        if (preventPanning) {
            this.addWindowListener('touchmove', this.doPreventPanning, false);
        }
        else {
            this.removeWindowListener('touchmove', this.doPreventPanning, false);
        }

        return preventPanning;
    },

    applyPreventZooming: function(preventZooming) {
        var touchstart = (Ext.feature.has.Touch) ? 'touchstart' : 'mousedown';

        if (preventZooming) {
            this.addWindowListener(touchstart, this.doPreventZooming, false);
        }
        else {
            this.removeWindowListener(touchstart, this.doPreventZooming, false);
        }

        return preventZooming;
    },

    doAutoMaximizeOnReady: function() {
        var controller = arguments[arguments.length - 1];

        controller.pause();

        this.isMaximizing = true;

        this.on('maximize', function() {
            this.isMaximizing = false;

            this.updateSize();

            controller.resume();

            this.fireEvent('ready', this);
        }, this, { single: true });

        this.maximize();
    },

    doAutoMaximizeOnOrientationChange: function() {
        var controller = arguments[arguments.length - 1],
            firingArguments = controller.firingArguments;

        controller.pause();

        this.isMaximizing = true;

        this.on('maximize', function() {
            this.isMaximizing = false;

            this.updateSize();

            firingArguments[1] = this.windowWidth;
            firingArguments[2] = this.windowHeight;

            controller.resume();
        }, this, { single: true });

        this.maximize();
    },

    doBlurInput: function(e) {
        var target = e.target,
            focusedElement = this.focusedElement;

        if (focusedElement && !this.isInputRegex.test(target.tagName)) {
            delete this.focusedElement;
            focusedElement.blur();
        }
    },

    doPreventPanning: function(e) {
        e.preventDefault();
    },

    doPreventZooming: function(e) {
        // Don't prevent right mouse event
        if ('button' in e && e.button !== 0) {
            return;
        }

        var target = e.target;

        if (target && target.nodeType === 1 && !this.isInputRegex.test(target.tagName)) {
            e.preventDefault();
        }
    },

    addWindowListener: function(eventName, fn, capturing) {
        window.addEventListener(eventName, fn, Boolean(capturing));
    },

    removeWindowListener: function(eventName, fn, capturing) {
        window.removeEventListener(eventName, fn, Boolean(capturing));
    },

    doAddListener: function(eventName, fn, scope, options) {
        if (eventName === 'ready' && this.isReady && !this.isMaximizing) {
            fn.call(scope);
            return this;
        }

        this.mixins.observable.doAddListener.apply(this, arguments);
    },

    supportsOrientation: function() {
        return Ext.feature.has.Orientation;
    },

    onResize: function() {
        var oldWidth = this.windowWidth,
            oldHeight = this.windowHeight,
            width = this.getWindowWidth(),
            height = this.getWindowHeight(),
            currentOrientation = this.getOrientation(),
            newOrientation = this.determineOrientation();

        if ((oldWidth !== width || oldHeight !== height) && currentOrientation !== newOrientation) {
            this.fireOrientationChangeEvent(newOrientation, currentOrientation);
        }
    },

    onOrientationChange: function() {
        var currentOrientation = this.getOrientation(),
            newOrientation = this.determineOrientation();

        if (newOrientation !== currentOrientation) {
            this.fireOrientationChangeEvent(newOrientation, currentOrientation);
        }
    },

    fireOrientationChangeEvent: function(newOrientation, oldOrientation) {
        var clsPrefix = Ext.baseCSSPrefix;
        Ext.getBody().replaceCls(clsPrefix + oldOrientation, clsPrefix + newOrientation);

        this.orientation = newOrientation;

        this.updateSize();
        this.fireEvent('orientationchange', this, newOrientation, this.windowWidth, this.windowHeight);
    },

    updateSize: function(width, height) {
        this.windowWidth = width !== undefined ? width : this.getWindowWidth();
        this.windowHeight = height !== undefined ? height : this.getWindowHeight();

        return this;
    },

    waitUntil: function(condition, onSatisfied, onTimeout, delay, timeoutDuration) {
        if (!delay) {
            delay = 50;
        }

        if (!timeoutDuration) {
            timeoutDuration = 2000;
        }

        var scope = this,
            elapse = 0;

        setTimeout(function repeat() {
            elapse += delay;

            if (condition.call(scope) === true) {
                if (onSatisfied) {
                    onSatisfied.call(scope);
                }
            }
            else {
                if (elapse >= timeoutDuration) {
                    if (onTimeout) {
                        onTimeout.call(scope);
                    }
                }
                else {
                    setTimeout(repeat, delay);
                }
            }
        }, delay);
    },

    maximize: function() {
        this.fireMaximizeEvent();
    },

    fireMaximizeEvent: function() {
        this.updateSize();
        this.fireEvent('maximize', this);
    },

    doSetHeight: function(height) {
        Ext.getBody().setHeight(height);

        this.callParent(arguments);
    },

    doSetWidth: function(width) {
        Ext.getBody().setWidth(width);

        this.callParent(arguments);
    },

    scrollToTop: function() {
        window.scrollTo(0, -1);
    },

    /**
     * Retrieves the document width.
     * @return {Number} width in pixels.
     */
    getWindowWidth: function() {
        return window.innerWidth;
    },

    /**
     * Retrieves the document height.
     * @return {Number} height in pixels.
     */
    getWindowHeight: function() {
        return window.innerHeight;
    },

    getWindowOuterHeight: function() {
        return window.outerHeight;
    },

    getWindowOrientation: function() {
        return window.orientation;
    },

    /**
     * Returns the current orientation.
     * @return {String} `portrait` or `landscape`
     */
    getOrientation: function() {
        return this.orientation;
    },

    getSize: function() {
        return {
            width: this.windowWidth,
            height: this.windowHeight
        };
    },

    determineOrientation: function() {
        var portrait = this.PORTRAIT,
            landscape = this.LANDSCAPE;

        if (this.supportsOrientation()) {
            if (this.getWindowOrientation() % 180 === 0) {
                return portrait;
            }

            return landscape;
        }
        else {
            if (this.getWindowHeight() >= this.getWindowWidth()) {
                return portrait;
            }

            return landscape;
        }
    },

    onItemFullscreenChange: function(item) {
        item.addCls(this.fullscreenItemCls);
        this.add(item);
    }
});

/**
 * @private
 * Android version of viewport.
 */
Ext.define('Ext.viewport.Android', {
    extend: 'Ext.viewport.Default',

    constructor: function() {
        this.on('orientationchange', 'doFireOrientationChangeEvent', this, { prepend: true });
        this.on('orientationchange', 'hideKeyboardIfNeeded', this, { prepend: true });

        return this.callParent(arguments);
    },

    getDummyInput: function() {
        var input = this.dummyInput,
            focusedElement = this.focusedElement,
            box = Ext.fly(focusedElement).getPageBox();

        if (!input) {
            this.dummyInput = input = document.createElement('input');
            input.style.position = 'absolute';
            input.style.opacity = '0';
            document.body.appendChild(input);
        }

        input.style.left = box.left + 'px';
        input.style.top = box.top + 'px';
        input.style.display = '';

        return input;
    },

    doBlurInput: function(e) {
        var target = e.target,
            focusedElement = this.focusedElement,
            dummy;

        if (focusedElement && !this.isInputRegex.test(target.tagName)) {
            dummy = this.getDummyInput();
            delete this.focusedElement;
            dummy.focus();

            setTimeout(function() {
                dummy.style.display = 'none';
            }, 100);
        }
    },

    hideKeyboardIfNeeded: function() {
        var eventController = arguments[arguments.length - 1],
            focusedElement = this.focusedElement;

        if (focusedElement) {
            delete this.focusedElement;
            eventController.pause();

            if (Ext.os.version.lt('4')) {
                focusedElement.style.display = 'none';
            }
            else {
                focusedElement.blur();
            }

            setTimeout(function() {
                focusedElement.style.display = '';
                eventController.resume();
            }, 1000);
        }
    },

    doFireOrientationChangeEvent: function() {
        var eventController = arguments[arguments.length - 1];

        this.orientationChanging = true;

        eventController.pause();

        this.waitUntil(function() {
            return this.getWindowOuterHeight() !== this.windowOuterHeight;
        }, function() {
            this.windowOuterHeight = this.getWindowOuterHeight();
            this.updateSize();

            eventController.firingArguments[1] = this.windowWidth;
            eventController.firingArguments[2] = this.windowHeight;
            eventController.resume();
            this.orientationChanging = false;

        }, function() {
            Ext.Logger.error("Timeout waiting for viewport's outerHeight to change before firing orientationchange", this);
        });

        return this;
    },

    applyAutoMaximize: function(autoMaximize) {
        this.callParent(arguments);

        this.on('add', 'fixSize', this, { single: true });
        if (!autoMaximize) {
            this.on('ready', 'fixSize', this, { single: true });
            this.onAfter('orientationchange', 'doFixSize', this);
        }
        else {
            this.un('ready', 'fixSize', this);
            this.unAfter('orientationchange', 'doFixSize', this);
        }
    },

    fixSize: function() {
        this.doFixSize();
    },

    doFixSize: function() {
        this.setHeight(this.getWindowHeight());
    },

    getActualWindowOuterHeight: function() {
        return Math.round(this.getWindowOuterHeight() / window.devicePixelRatio);
    },

    maximize: function() {
        var stretchHeights = this.stretchHeights,
            orientation = this.orientation,
            height;

        height = stretchHeights[orientation];

        if (!height) {
            stretchHeights[orientation] = height = this.getActualWindowOuterHeight();
        }

        if (!this.addressBarHeight) {
            this.addressBarHeight = height - this.getWindowHeight();
        }

        this.setHeight(height);

        var isHeightMaximized = Ext.Function.bind(this.isHeightMaximized, this, [height]);

        this.scrollToTop();
        this.waitUntil(isHeightMaximized, this.fireMaximizeEvent, this.fireMaximizeEvent);
    },

    isHeightMaximized: function(height) {
        this.scrollToTop();
        return this.getWindowHeight() === height;
    }

}, function() {
    if (!Ext.os.is.Android) {
        return;
    }

    var version = Ext.os.version,
        userAgent = Ext.browser.userAgent,
        // These Android devices have a nasty bug which causes JavaScript timers to be completely frozen
        // when the browser's viewport is being panned.
        isBuggy = /(htc|desire|incredible|ADR6300)/i.test(userAgent) && version.lt('2.3');

    if (isBuggy) {
        this.override({
            constructor: function(config) {
                if (!config) {
                    config = {};
                }

                config.autoMaximize = false;

                this.watchDogTick = Ext.Function.bind(this.watchDogTick, this);

                setInterval(this.watchDogTick, 1000);

                return this.callParent([config]);
            },

            watchDogTick: function() {
                this.watchDogLastTick = Ext.Date.now();
            },

            doPreventPanning: function() {
                var now = Ext.Date.now(),
                    lastTick = this.watchDogLastTick,
                    deltaTime = now - lastTick;

                // Timers are frozen
                if (deltaTime >= 2000) {
                    return;
                }

                return this.callParent(arguments);
            },

            doPreventZooming: function() {
                var now = Ext.Date.now(),
                    lastTick = this.watchDogLastTick,
                    deltaTime = now - lastTick;

                // Timers are frozen
                if (deltaTime >= 2000) {
                    return;
                }

                return this.callParent(arguments);
            }
        });
    }

    if (version.match('2')) {
        this.override({
            onReady: function() {
                this.addWindowListener('resize', Ext.Function.bind(this.onWindowResize, this));

                this.callParent(arguments);
            },

            scrollToTop: function() {
                document.body.scrollTop = 100;
            },

            onWindowResize: function() {
                var oldWidth = this.windowWidth,
                    oldHeight = this.windowHeight,
                    width = this.getWindowWidth(),
                    height = this.getWindowHeight();

                if (this.getAutoMaximize() && !this.isMaximizing && !this.orientationChanging
                    && window.scrollY === 0
                    && oldWidth === width
                    && height < oldHeight
                    && ((height >= oldHeight - this.addressBarHeight) || !this.focusedElement)) {
                        this.scrollToTop();
                }
            },

            fixSize: function() {
                var orientation = this.getOrientation(),
                    outerHeight = window.outerHeight,
                    outerWidth = window.outerWidth,
                    actualOuterHeight;

                // On some Android 2 devices such as the Kindle Fire, outerWidth and outerHeight are reported wrongly
                // when navigating from another page that has larger size.
                if (orientation === 'landscape' && (outerHeight < outerWidth)
                    || orientation === 'portrait' && (outerHeight >= outerWidth)) {
                    actualOuterHeight = this.getActualWindowOuterHeight();
                }
                else {
                    actualOuterHeight = this.getWindowHeight();
                }

                this.waitUntil(function() {
                    return actualOuterHeight > this.getWindowHeight();
                }, this.doFixSize, this.doFixSize, 50, 1000);
            }
        });
    }
    else if (version.gtEq('3.1')) {
        this.override({
            isHeightMaximized: function(height) {
                this.scrollToTop();
                return this.getWindowHeight() === height - 1;
            }
        });
    }
    else if (version.match('3')) {
        this.override({
            isHeightMaximized: function() {
                this.scrollToTop();
                return true;
            }
        })
    }

    if (version.gtEq('4')) {
        this.override({
            doBlurInput: Ext.emptyFn
        });
    }
});

/**
 * @private
 * iOS version of viewport.
 */
Ext.define('Ext.viewport.Ios', {
    extend: 'Ext.viewport.Default',

    isFullscreen: function() {
        return this.isHomeScreen();
    },

    isHomeScreen: function() {
        return window.navigator.standalone === true;
    },

    constructor: function() {
        this.callParent(arguments);

        if (this.getAutoMaximize() && !this.isFullscreen()) {
            this.addWindowListener('touchstart', Ext.Function.bind(this.onTouchStart, this));
        }
    },

    maximize: function() {
        if (this.isFullscreen()) {
            return this.callParent();
        }

        var stretchHeights = this.stretchHeights,
            orientation = this.orientation,
            currentHeight = this.getWindowHeight(),
            height = stretchHeights[orientation];

        if (window.scrollY > 0) {
            this.scrollToTop();

            if (!height) {
                stretchHeights[orientation] = height = this.getWindowHeight();
            }

            this.setHeight(height);
            this.fireMaximizeEvent();
        }
        else {
            if (!height) {
                height = this.getScreenHeight();
            }

            this.setHeight(height);

            this.waitUntil(function() {
                this.scrollToTop();
                return currentHeight !== this.getWindowHeight();
            }, function() {
                if (!stretchHeights[orientation]) {
                    height = stretchHeights[orientation] = this.getWindowHeight();
                    this.setHeight(height);
                }

                this.fireMaximizeEvent();
            }, function() {
                Ext.Logger.error("Timeout waiting for window.innerHeight to change", this);
                height = stretchHeights[orientation] = this.getWindowHeight();
                this.setHeight(height);
                this.fireMaximizeEvent();
            }, 50, 1000);
        }
    },

    getScreenHeight: function() {
        return window.screen[this.orientation === this.PORTRAIT ? 'height' : 'width'];
    },

    onElementFocus: function() {
        if (this.getAutoMaximize() && !this.isFullscreen()) {
            clearTimeout(this.scrollToTopTimer);
        }

        this.callParent(arguments);
    },

    onElementBlur: function() {
        if (this.getAutoMaximize() && !this.isFullscreen()) {
            this.scrollToTopTimer = setTimeout(this.scrollToTop, 500);
        }

        this.callParent(arguments);
    },

    onTouchStart: function() {
        if (this.focusedElement === null) {
            this.scrollToTop();
        }
    },

    scrollToTop: function() {
        window.scrollTo(0, 0);
    }

}, function() {
    if (!Ext.os.is.iOS) {
        return;
    }

    if (Ext.os.version.lt('3.2')) {
        this.override({
            constructor: function() {
                var stretchHeights = this.stretchHeights = {};

                stretchHeights[this.PORTRAIT] = 416;
                stretchHeights[this.LANDSCAPE] = 268;

                return this.callOverridden(arguments);
            }
        });
    }

    if (Ext.os.version.lt('5')) {
        this.override({
            fieldMaskClsTest: '-field-mask',

            doPreventZooming: function(e) {
                var target = e.target;

                if (target && target.nodeType === 1 &&
                    !this.isInputRegex.test(target.tagName) &&
                    target.className.indexOf(this.fieldMaskClsTest) == -1) {
                    e.preventDefault();
                }
            }
        });
    }

    if (Ext.os.is.iPad) {
        this.override({
            isFullscreen: function() {
                return true;
            }
        });
    }
});

/**
 * This class acts as a factory for environment-specific viewport implementations.
 *
 * Please refer to the {@link Ext.Viewport} documentation about using the global instance.
 * @private
 */
Ext.define('Ext.viewport.Viewport', {
    requires: [
        'Ext.viewport.Ios',
        'Ext.viewport.Android'
    ],

    constructor: function(config) {
        var osName = Ext.os.name,
            viewportName, viewport;

        switch (osName) {
            case 'Android':
                viewportName = 'Android';
                break;

            case 'iOS':
                viewportName = 'Ios';
                break;

            default:
                viewportName = 'Default';
        }

        viewport = Ext.create('Ext.viewport.' + viewportName, config);

        return viewport;
    }
});

// Docs for the singleton instance created by above factory:

/**
 * @class Ext.Viewport
 * @extends Ext.viewport.Default
 * @singleton
 *
 * Ext.Viewport is a instance created when you use {@link Ext#setup}. Because {@link Ext.Viewport} extends from
 * {@link Ext.Container}, it has as {@link #layout} (which defaults to {@link Ext.layout.Card}). This means you
 * can add items to it at any time, from anywhere in your code. The {@link Ext.Viewport} {@link #cfg-fullscreen}
 * configuration is `true` by default, so it will take up your whole screen.
 *
 *     @example raw
 *     Ext.setup({
 *         onReady: function() {
 *             Ext.Viewport.add({
 *                 xtype: 'container',
 *                 html: 'My new container!'
 *             });
 *         }
 *     });
 *
 * If you want to customize anything about this {@link Ext.Viewport} instance, you can do so by adding a property
 * called `viewport` into your {@link Ext#setup} object:
 *
 *     @example raw
 *     Ext.setup({
 *         viewport: {
 *             layout: 'vbox'
 *         },
 *         onReady: function() {
 *             //do something
 *         }
 *     });
 *
 * **Note** if you use {@link Ext#onReady}, this instance of {@link Ext.Viewport} will **not** be created. Though, in most cases,
 * you should **not** use {@link Ext#onReady}.
 */

/**
 * A base class used for both {@link Ext.event.recognizer.VerticalSwipe} and {@link Ext.event.recognizer.HorizontalSwipe}
 * event recognisers.
 *
 * @private
 */
Ext.define('Ext.event.recognizer.Swipe', {
    extend: 'Ext.event.recognizer.SingleTouch',

    handledEvents: ['swipe'],

    /**
     * @member Ext.dom.Element
     * @event swipe
     * Fires when there is a swipe
     * When listening to this, ensure you know about the {@link Ext.event.Event#direction} property in the `event` object.
     * @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
     * @param {HTMLElement} node The target of the event.
     * @param {Object} options The options object passed to Ext.util.Observable.addListener.
     */
    
    /**
     * @property {Number} direction
     * The direction of the swipe. Available options are:
     * 
     * * up
     * * down
     * * left
     * * right
     * 
     * Note: In order to recognise swiping up and down, you must enable the vertical swipe recogniser.
     * 
     * **This is only available when the event type is `swipe`**
     * @member Ext.event.Event
     */
    
    /**
     * @property {Number} duration
     * The duration of the swipe.
     *
     * **This is only available when the event type is `swipe`**
     * @member Ext.event.Event
     */

    inheritableStatics: {
        MAX_OFFSET_EXCEEDED: 0x10,
        MAX_DURATION_EXCEEDED: 0x11,
        DISTANCE_NOT_ENOUGH: 0x12
    },

    config: {
        minDistance: 80,
        maxOffset: 35,
        maxDuration: 1000
    },

    onTouchStart: function(e) {
        if (this.callParent(arguments) === false) {
            return false;
        }

        var touch = e.changedTouches[0];

        this.startTime = e.time;

        this.isHorizontal = true;
        this.isVertical = true;

        this.startX = touch.pageX;
        this.startY = touch.pageY;
    },

    onTouchMove: function(e) {
        var touch = e.changedTouches[0],
            x = touch.pageX,
            y = touch.pageY,
            absDeltaX = Math.abs(x - this.startX),
            absDeltaY = Math.abs(y - this.startY),
            time = e.time;

        if (time - this.startTime > this.getMaxDuration()) {
            return this.fail(this.self.MAX_DURATION_EXCEEDED);
        }

        if (this.isVertical && absDeltaX > this.getMaxOffset()) {
            this.isVertical = false;
        }

        if (this.isHorizontal && absDeltaY > this.getMaxOffset()) {
            this.isHorizontal = false;
        }

        if (!this.isHorizontal && !this.isVertical) {
            return this.fail(this.self.MAX_OFFSET_EXCEEDED);
        }
    },

    onTouchEnd: function(e) {
        if (this.onTouchMove(e) === false) {
            return false;
        }

        var touch = e.changedTouches[0],
            x = touch.pageX,
            y = touch.pageY,
            deltaX = x - this.startX,
            deltaY = y - this.startY,
            absDeltaX = Math.abs(deltaX),
            absDeltaY = Math.abs(deltaY),
            minDistance = this.getMinDistance(),
            duration = e.time - this.startTime,
            direction, distance;

        if (this.isVertical && absDeltaY < minDistance) {
            this.isVertical = false;
        }

        if (this.isHorizontal && absDeltaX < minDistance) {
            this.isHorizontal = false;
        }

        if (this.isHorizontal) {
            direction = (deltaX < 0) ? 'left' : 'right';
            distance = absDeltaX;
        }
        else if (this.isVertical) {
            direction = (deltaY < 0) ? 'up' : 'down';
            distance = absDeltaY;
        }
        else {
            return this.fail(this.self.DISTANCE_NOT_ENOUGH);
        }

        this.fire('swipe', e, [touch], {
            touch: touch,
            direction: direction,
            distance: distance,
            duration: duration
        });
    }
});

/**
 * A event recogniser created to recognise horitzontal swipe movements.
 * 
 * @private
 */
Ext.define('Ext.event.recognizer.HorizontalSwipe', {
    extend: 'Ext.event.recognizer.Swipe',

    handledEvents: ['swipe'],

    onTouchStart: function(e) {
        if (this.callParent(arguments) === false) {
            return false;
        }

        var touch = e.changedTouches[0];

        this.startTime = e.time;

        this.startX = touch.pageX;
        this.startY = touch.pageY;
    },

    onTouchMove: function(e) {
        var touch = e.changedTouches[0],
            y = touch.pageY,
            absDeltaY = Math.abs(y - this.startY),
            time = e.time,
            maxDuration = this.getMaxDuration(),
            maxOffset = this.getMaxOffset();

        if (time - this.startTime > maxDuration) {
            return this.fail(this.self.MAX_DURATION_EXCEEDED);
        }

        if (absDeltaY > maxOffset) {
            return this.fail(this.self.MAX_OFFSET_EXCEEDED);
        }
    },

    onTouchEnd: function(e) {
        if (this.onTouchMove(e) !== false) {
            var touch = e.changedTouches[0],
                x = touch.pageX,
                deltaX = x - this.startX,
                distance = Math.abs(deltaX),
                duration = e.time - this.startTime,
                minDistance = this.getMinDistance(),
                direction;

            if (distance < minDistance) {
                return this.fail(this.self.DISTANCE_NOT_ENOUGH);
            }

            direction = (deltaX < 0) ? 'left' : 'right';

            this.fire('swipe', e, [touch], {
                touch: touch,
                direction: direction,
                distance: distance,
                duration: duration
            });
        }
    }
});




