    // @tag foundation,core
    // @require ../Ext.js
    // @define Ext.Version

    /**
     * @class Ext.Version
     *
     * A utility class that wraps around a version number string and provides convenient methods
     * to perform comparisons. A version number is expressed in the following general format:
     *
     *     major[.minor[.patch[.build[release]]]]
     *
     * The `Version` instance holds various readonly properties that contain the digested form
     * of the version string. The numeric componnets of `major`, `minor`, `patch` and `build`
     * as well as the textual suffix called `release`.
     *
     * Not depicted in the above syntax are three possible prefixes used to control partial
     * matching. These are '^' (the default), '>' and '~'. These are discussed below.
     *
     * Examples:
     *
     *     var version = new Ext.Version('1.0.2beta'); // or maybe "1.0" or "1.2.3.4RC"
     *     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
     *
     * The understood values of `release` are assigned numberic equivalents for the sake of
     * comparsion. The order of these from smallest to largest is as follows:
     *
     *  * `"dev"`
     *  * `"alpha"` or `"a"`
     *  * `"beta"` or `"b"`
     *  * `"RC"` or `"rc"`
     *  * `"#"`
     *  * `"pl"` or `"p"`
     *
     * Any other (unrecognized) suffix is consider greater than any of these.
     *
     * ## Comparisons
     * There are two forms of comparison that are commonly needed: full and partial. Full
     * comparison is simpler and is also the default.
     *
     * Example:
     *
     *     var version = new Ext.Version('1.0.2beta');
     *
     *     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 (using a Number)
     *     console.log(version.match('1.0.2')); // True (using a String)
     *
     * These comparisons are ultimately implemented by {@link Ext.Version#compareTo compareTo}
     * which returns -1, 0 or 1 depending on whether the `Version' instance is less than, equal
     * to, or greater than the given "other" version.
     *
     * For example:
     *
     *      var n = version.compareTo('1.0.1');  // == 1  (because 1.0.2beta > 1.0.1)
     *
     *      n = version.compareTo('1.1');  // == -1
     *      n = version.compareTo(version); // == 0
     *
     * ### Partial Comparisons
     * By default, unspecified version number fields are filled with 0. In other words, the
     * version number fields are 0-padded on the right or a "lower bound". This produces the
     * most commonly used forms of comparsion:
     *
     *      var ver = new Version('4.2');
     *
     *      n = ver.compareTo('4.2.1'); // == -1  (4.2 promotes to 4.2.0 and is less than 4.2.1)
     *
     * There are two other ways to interpret comparisons of versions of different length. The
     * first of these is to change the padding on the right to be a large number (scuh as
     * Infinity) instead of 0. This has the effect of making the version an upper bound. For
     * example:
     *
     *      var ver = new Version('^4.2'); // NOTE: the '^' prefix used
     *
     *      n = ver.compreTo('4.3'); // == -1  (less than 4.3)
     *
     *      n = ver.compareTo('4.2'); // == 1   (greater than all 4.2's)
     *      n = ver.compareTo('4.2.1'); // == 1
     *      n = ver.compareTo('4.2.9'); // == 1
     *
     * The second way to interpret this comparison is to ignore the extra digits, making the
     * match a prefix match. For example:
     *
     *      var ver = new Version('~4.2'); // NOTE: the '~' prefix used
     *
     *      n = ver.compreTo('4.3'); // == -1
     *
     *      n = ver.compareTo('4.2'); // == 0
     *      n = ver.compareTo('4.2.1'); // == 0
     *
     * This final form can be useful when version numbers contain more components than are
     * important for certain comparisons. For example, the full version of Ext JS 4.2.1 is
     * "4.2.1.883" where 883 is the `build` number.
     *
     * This is how to create a "partial" `Version` and compare versions to it:
     *
     *      var version421ish = new Version('~4.2.1');
     *
     *      n = version421ish.compareTo('4.2.1.883'); // == 0
     *      n = version421ish.compareTo('4.2.1.2'); // == 0
     *      n = version421ish.compareTo('4.2.1'); // == 0
     *
     *      n = version421ish.compareTo('4.2'); // == 1
     *
     * In the above example, '4.2.1.2' compares as equal to '4.2.1' because digits beyond the
     * given "4.2.1" are ignored. However, '4.2' is less than the '4.2.1' prefix; its missing
     * digit is filled with 0.
     */
    (function() {

    // Current core version
    // also fix Ext-more.js
    var version = '4.2.0',
        // used by checkVersion to avoid temp arrays:
        checkVerTemp = [''],
        endOfVersionRe = /([^\d\.])/,
        notDigitsRe = /[^\d]/g,
        plusMinusRe = /[\-+]/g,
        stripRe = /\s/g,
        underscoreRe = /_/g,
        Version;

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

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

            /**
             * @property {String} [release=""]
             * The release level. The following values are understood:
             *
             *  * `"dev"`
             *  * `"alpha"` or `"a"`
             *  * `"beta"` or `"b"`
             *  * `"RC"` or `"rc"`
             *  * `"#"`
             *  * `"pl"` or `"p"`
             * @readonly
             */
            release: '',

            /**
             * @param {String/Number} version The version number.
             * @param {String} [defaultMode="^"] The padding mode (e.g., '^', '>' or '~').
             * This is ignored if the `version` contains an explicit mode prefix.
             * @return {Ext.Version} this
             */
            constructor: function (version, defaultMode) {
                var me = this,
                    padModes = me.padModes,
                    ch, i, pad, parts, release, releaseStartIndex, ver;

                if (version.isVersion) {
                    return version;
                }

                me.version = ver = String(version).toLowerCase().
                                        replace(underscoreRe, '.').replace(plusMinusRe, '');

                ch = ver.charAt(0);
                if (ch in padModes) {
                    ver = ver.substring(1);
                    pad = padModes[ch];
                } else {
                    pad = defaultMode ? padModes[defaultMode] : 0; // careful - NaN is falsey!
                }
                me.pad = pad;

                releaseStartIndex = ver.search(endOfVersionRe);
                me.shortVersion = ver;

                if (releaseStartIndex !== -1) {
                    me.release = release = ver.substr(releaseStartIndex, version.length);
                    me.shortVersion = ver.substr(0, releaseStartIndex);
                    release = Version.releaseValueMap[release] || release;
                }

                me.releaseValue = release || pad;
                me.shortVersion = me.shortVersion.replace(notDigitsRe, '');

                /**
                 * @property {Number[]} parts
                 * The split array of version number components found in the version string.
                 * For example, for "1.2.3", this would be `[1, 2, 3]`.
                 * @readonly
                 * @private
                 */
                me.parts = parts = ver.split('.');
                for (i = parts.length; i--; ) {
                    parts[i] = parseInt(parts[i], 10);
                }
                if (pad === Infinity) {
                    // have to add this to the end to create an upper bound:
                    parts.push(pad);
                }

                /**
                 * @property {Number} major
                 * The first numeric part of the version number string.
                 * @readonly
                 */
                me.major = parts[0] || pad;

                /**
                 * @property {Number} [minor]
                 * The second numeric part of the version number string.
                 * @readonly
                 */
                me.minor = parts[1] || pad;

                /**
                 * @property {Number} [patch]
                 * The third numeric part of the version number string.
                 * @readonly
                 */
                me.patch = parts[2] || pad;

                /**
                 * @property {Number} [build]
                 * The fourth numeric part of the version number string.
                 * @readonly
                 */
                me.build = parts[3] || pad;

                return me;
            },

            /**
             * Compares this version instance to the specified `other` version.
             *
             * @param {String/Number/Ext.Version} other The other version to which to compare.
             * @return {Number} -1 if this version is less than the target version, 1 if this
             * version is greater, and 0 if they are equal.
             */
            compareTo: function (other) {
                 // "lhs" == "left-hand-side"
                 // "rhs" == "right-hand-side"
                var me = this,
                    lhsPad = me.pad,
                    lhsParts = me.parts,
                    lhsLength = lhsParts.length,
                    rhsVersion = other.isVersion ? other : new Version(other),
                    rhsPad = rhsVersion.pad,
                    rhsParts = rhsVersion.parts,
                    rhsLength = rhsParts.length,
                    length = Math.max(lhsLength, rhsLength),
                    i, lhs, rhs;

                for (i = 0; i < length; i++) {
                    lhs = (i < lhsLength) ? lhsParts[i] : lhsPad;
                    rhs = (i < rhsLength) ? rhsParts[i] : rhsPad;

                    // When one or both of the values are NaN these tests produce false
                    // and we end up treating NaN as equal to anything.
                    if (lhs < rhs) {
                        return -1;
                    }
                    if (lhs > rhs) {
                        return 1;
                    }
                }

                // same comments about NaN apply here...
                lhs = me.releaseValue;
                rhs = rhsVersion.releaseValue;
                if (lhs < rhs) {
                    return -1;
                }
                if (lhs > rhs) {
                    return 1;
                }

                return 0;
            },

            /**
             * 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}
             */
            getMajor: function() {
                return this.major;
            },

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

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

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

            /**
             * Returns the release component text (e.g., "beta").
             * @return {String}
             */
            getRelease: function() {
                return this.release;
            },

            /**
             * Returns the release component value for comparison purposes.
             * @return {Number/String}
             */
            getReleaseValue: function() {
                return this.releaseValue;
            },

            /**
             * 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 this.compareTo(target) > 0;
            },

            /**
             * 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 this.compareTo(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 this.compareTo(target) < 0;
            },

            /**
             * 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 this.compareTo(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 this.compareTo(target) === 0;
            },

            /**
             * Returns whether this version matches the supplied argument. Example:
             *
             *     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
             *
             * @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() {
                var me = this;
                return [me.getMajor(), me.getMinor(), me.getPatch(), me.getBuild(), me.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/Ext.Version} target
             * @return {Boolean}
             */
            gt: function (target) {
                return this.compareTo(target) > 0;
            },

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

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

            /**
             * Convenient alias to {@link Ext.Version#isLessThanOrEqual isLessThanOrEqual}
             * @param {String/Number/Ext.Version} target
             * @return {Boolean}
             */
            ltEq: function (target) {
                return this.compareTo(target) <= 0;
            }
        });

        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 by ensuring the first parameter is a `Version`
             * instance and then calling the `compareTo` method.
             *
             * @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 ver = current.isVersion ? current : new Version(current);
                return ver.compareTo(target);
            }
        });

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

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

            /**
             * Set version number for the given package name.
             *
             * @param {String} packageName The package name, e.g. 'core', 'touch', 'ext'.
             * @param {String/Ext.Version} version The version, e.g. '1.2.3alpha', '2.4.0-dev'.
             * @return {Ext}
             */
            setVersion: function(packageName, version) {
                Ext.lastRegisteredVersion = Ext.versions[packageName] = new Version(version);
                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] The package name, e.g., 'core', 'touch', 'ext'.
             * @return {Ext.Version} The version.
             */
            getVersion: function(packageName) {
                if (packageName === undefined) {
                    return Ext.lastRegisteredVersion;
                }

                return Ext.versions[packageName];
            },

            /**
             * This method checks the registered package versions against the provided version
             * `specs`. A `spec` is either a string or an object indicating a boolean operator.
             * This method accepts either form or an array of these as the first argument. The
             * second argument applies only when the first is an array and indicates whether
             * all `specs` must match or just one.
             *
             * ## Package Version Specifications
             * The string form of a `spec` is used to indicate a version or range of versions
             * for a particular package. This form of `spec` consists of three (3) parts:
             *
             *  * Package name followed by "@". If not provided, the framework is assumed.
             *  * Minimum version.
             *  * Maximum version.
             *
             * At least one version number must be provided. If both minimum and maximum are
             * provided, these must be separated by a "-".
             *
             * Some examples of package version specifications:
             *
             *      4.2.2           (exactly version 4.2.2 of the framework)
             *      4.2.2+          (version 4.2.2 or higher of the framework)
             *      4.2.2-          (version 4.2.2 or higher of the framework)
             *      4.2.1 - 4.2.3   (versions from 4.2.1 up to 4.2.3 of the framework)
             *      - 4.2.2         (any version up to version 4.2.1 of the framework)
             *
             *      foo@1.0         (exactly version 1.0 of package "foo")
             *      foo@1.0-1.3     (versions 1.0 up to 1.3 of package "foo")
             *
             * **NOTE:** This syntax is the same as that used in Sencha Cmd's package
             * requirements declarations.
             *
             * ## Boolean Operator Specifications
             * Instead of a string, an object can be used to describe a boolean operation to
             * perform on one or more `specs`. The operator is either **`and`** or **`or`**
             * and can contain an optional **`not`**.
             *
             * For example:
             *
             *      {
             *          not: true,  // negates boolean result
             *          and: [
             *              '4.2.2',
             *              'foo@1.0.1 - 2.0.1'
             *          ]
             *      }
             *
             * Each element of the array can in turn be a string or object spec. In other
             * words, the value is passed to this method (recursively) as the first argument
             * so these two calls are equivalent:
             *
             *      Ext.checkVersion({
             *          not: true,  // negates boolean result
             *          and: [
             *              '4.2.2',
             *              'foo@1.0.1 - 2.0.1'
             *          ]
             *      });
             *
             *      !Ext.checkVersion([
             *              '4.2.2',
             *              'foo@1.0.1 - 2.0.1'
             *          ], true);
             *
             * ## Examples
             *
             *      // A specific framework version
             *      Ext.checkVersion('4.2.2');
             *
             *      // A range of framework versions:
             *      Ext.checkVersion('4.2.1-4.2.3');
             *
             *      // A specific version of a package:
             *      Ext.checkVersion('foo@1.0.1');
             *
             *      // A single spec that requires both a framework version and package
             *      // version range to match:
             *      Ext.checkVersion({
             *          and: [
             *              '4.2.2',
             *              'foo@1.0.1-1.0.2'
             *          ]
             *      });
             *
             *      // These checks can be nested:
             *      Ext.checkVersion({
             *          and: [
             *              '4.2.2',  // exactly version 4.2.2 of the framework *AND*
             *              {
             *                  // either (or both) of these package specs:
             *                  or: [
             *                      'foo@1.0.1-1.0.2',
             *                      'bar@3.0+'
             *                  ]
             *              }
             *          ]
             *      });
             *
             * ## Version Comparisons
             * Version comparsions are assumed to be "prefix" based. That is to say, `"foo@1.2"`
             * matches any version of "foo" that has a major version 1 and a minor version of 2.
             *
             * This also applies to ranges. For example `"foo@1.2-2.2"` matches all versions
             * of "foo" from 1.2 up to 2.2 regardless of the specific patch and build.
             *
             * ## Use in Overrides
             * This methods primary use is in support of conditional overrides on an
             * `Ext.define` declaration.
             *
             * @param {String/Array/Object} specs A version specification string, an object
             * containing `or` or `and` with a value that is equivalent to `specs` or an array
             * of either of these.
             * @param {Boolean} [matchAll=false] Pass `true` to require all specs to match.
             * @return {Boolean} True if `specs` matches the registered package versions.
             */
            checkVersion: function (specs, matchAll) {
                var isArray = Ext.isArray(specs),
                    compat = isArray ? specs : checkVerTemp,
                    length = compat.length,
                    versions = Ext.versions,
                    frameworkVer = versions.ext || versions.touch,
                    i, index, matches, minVer, maxVer, spec, range, ver;

                if (!isArray) {
                    checkVerTemp[0] = specs;
                }

                for (i = 0; i < length; ++i) {
                    if (!Ext.isString(spec = compat[i])) {
                        matches = Ext.checkVersion(spec.and || spec.or, !spec.or);
                        if (spec.not) {
                            matches = !matches;
                        }
                    } else {
                        if (spec.indexOf(' ') >= 0) {
                            spec = spec.replace(stripRe, '');
                        }

                        // For "name@..." syntax, we need to find the package by the given name
                        // as a registered package.
                        index = spec.indexOf('@');
                        if (index < 0) {
                            range = spec;
                            ver = frameworkVer;
                        } else {
                            if (!(ver = versions[spec.substring(0, index)])) {
                                // The package is not registered, so if we must matchAll then
                                // we are done - FAIL:
                                if (matchAll) {
                                    return false;
                                }
                                // Otherwise this spec is not a match so we can move on to the
                                // next...
                                continue;
                            }
                            range = spec.substring(index+1);
                        }

                        // Now look for a version, version range or partial range:
                        index = range.indexOf('-');
                        if (index < 0) {
                            // just a version or "1.0+"
                            if (range.charAt(index = range.length - 1) === '+') {
                                minVer = range.substring(0, index);
                                maxVer = null;
                            } else {
                                minVer = maxVer = range;
                            }
                        } else if (index > 0) {
                            // a range like "1.0-1.5" or "1.0-"
                            minVer = range.substring(0, index);
                            maxVer = range.substring(index+1); // may be empty
                        } else {
                            // an upper limit like "-1.5"
                            minVer = null;
                            maxVer = range.substring(index+1);
                        }

                        matches = true;
                        if (minVer) {
                            minVer = new Version(minVer, '~'); // prefix matching
                            matches = minVer.ltEq(ver);
                        }
                        if (matches && maxVer) {
                            maxVer = new Version(maxVer, '~'); // prefix matching
                            matches = maxVer.gtEq(ver);
                        }
                    } // string spec

                    if (matches) {
                        // spec matched and we are looking for any match, so we are GO!
                        if (!matchAll) {
                            return true;
                        }
                    } else if (matchAll) {
                        // spec does not match the registered package version
                        return false;
                    }
                }

                // In the loop above, for matchAll we return FALSE on mismatch, so getting
                // here with matchAll means we had no mismatches. On the other hand, if we
                // are !matchAll, we return TRUE on match and so we get here only if we found
                // no matches.
                return !!matchAll;
            },

            /**
             * 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 (`this`) if the closure
             */
            deprecate: function(packageName, since, closure, scope) {
                if (Version.compare(Ext.getVersion(packageName), since) < 1) {
                    closure.call(scope);
                }
            }
        }); // End Versioning

        Ext.setVersion('core', version);

    }());