/**
 * @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.compareTo('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.compareTo('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.
 */
/* eslint-disable indent */
(function() {
// @define Ext.Version
// @require Ext.String
    var // used by checkVersion to avoid temp arrays:
        checkVerTemp = [''],
        endOfVersionRe = /([^\d.])/,
        notDigitsRe = /[^\d]/g,
        plusMinusRe = /[-+]/g,
        stripRe = /\s/g,
        underscoreRe = /_/g,
        toolkitNames = { classic: 1, modern: 1 },
        Version;
 
    Ext.Version = Version = function(version, defaultMode) {
        var me = this,
            padModes = me.padModes,
            ch, i, pad, parts, release, releaseStartIndex, ver;
 
        if (version.isVersion) {
            version = version.version;
        }
 
        me.version = ver = String(version).toLowerCase().replace(underscoreRe, '.')
                                                        .replace(plusMinusRe, '');
 
        ch = ver.charAt(0);
 
        if (ch in padModes) {
            ver = ver.substring(1);
            pad = padModes[ch];
        }
        else {
            pad = defaultMode ? padModes[defaultMode] : 0; // 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 (= 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;
    };
 
    Version.prototype = {
        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: '',
 
        /**
         * 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 (= 0; i < length; i++) {
                lhs = (< lhsLength) ? lhsParts[i] : lhsPad;
                rhs = (< 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, {
        aliases: {
            from: {
                extjs: 'ext',
                core: 'core',
                touch: 'modern'
            },
            to: {
                ext: ['extjs'],
                'core': ['core'],
                modern: ['touch']
            }
        },
 
        /**
         * @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) {
            // eslint-disable-next-line max-len
            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);
        },
 
        set: function(collection, packageName, version) {
            var aliases = Version.aliases.to[packageName],
                ver = version.isVersion ? version : new Version(version),
                i;
 
            collection[packageName] = ver;
 
            if (aliases) {
                for (= aliases.length; i-- > 0;) {
                    collection[aliases[i]] = ver;
                }
            }
 
            return ver;
        }
    });
 
    /**
     * @class Ext
     */
    Ext.apply(Ext, {
        /**
         * @private
         */
        compatVersions: {},
 
        /**
         * @private
         * 
         * Object containing version information for all packages utilized by your 
         * application. 
         * 
         * For a public getter, please see `Ext.getVersion()`.
         */
        versions: {},
 
        /**
         * @private
         */
        lastRegisteredVersion: null,
 
        /**
         * Get the compatibility level (a version number) for the given package name. If
         * none has been registered with `Ext.setCompatVersion` then `Ext.getVersion` is
         * used to get the current version.
         *
         * @param {String} packageName The package name, e.g. 'core', 'touch', 'ext'.
         * @since 5.0.0
         * @private
         */
        getCompatVersion: function(packageName) {
            var versions = Ext.compatVersions,
                compat;
 
            if (!packageName) {
                compat = versions.ext || versions.touch || versions.core;
            }
            else {
                compat = versions[Version.aliases.from[packageName] || packageName];
            }
 
            return compat || Ext.getVersion(packageName);
        },
 
        /**
         * Set the compatibility level (a 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. '4.2'.
         * @since 5.0.0
         * @private
         */
        setCompatVersion: function(packageName, version) {
            Version.set(Ext.compatVersions, packageName, version);
        },
 
        /**
         * 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) {
            if (packageName in toolkitNames) {
                Ext.toolkit = packageName;
            }
 
            Ext.lastRegisteredVersion = Version.set(Ext.versions, packageName, version);
 
            return this;
        },
 
        /**
         * Get the version number of the supplied package name; will return the version of
         * the framework.
         *
         * @param {String} [packageName] The package name, e.g., 'core', 'touch', 'ext'.
         * @return {Ext.Version} The version.
         */
        getVersion: function(packageName) {
            var versions = Ext.versions;
 
            if (!packageName) {
                return versions.ext || versions.touch || versions.core;
            }
 
            return versions[Version.aliases.from[packageName] || 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)
         *      
         *      [email protected]         (exactly version 1.0 of package "foo")
         *      [email protected]     (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',
         *              '[email protected] - 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',
         *              '[email protected] - 2.0.1'
         *          ]
         *      });
         *
         *      !Ext.checkVersion([
         *              '4.2.2',
         *              '[email protected] - 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('[email protected]');
         * 
         *      // A single spec that requires both a framework version and package
         *      // version range to match:
         *      Ext.checkVersion({
         *          and: [
         *              '4.2.2',
         *              '[email protected]'
         *          ]
         *      });
         * 
         *      // 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: [
         *                      '[email protected]',
         *                      '[email protected]+'
         *                  ]
         *              }
         *          ]
         *      });
         * 
         * ## Version Comparisons
         * Version comparsions are assumed to be "prefix" based. That is to say, `"[email protected]"`
         * matches any version of "foo" that has a major version 1 and a minor version of 2.
         * 
         * This also applies to ranges. For example `"[email protected]"` 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),
                aliases = Version.aliases.from,
                compat = isArray ? specs : checkVerTemp,
                length = compat.length,
                versions = Ext.versions,
                frameworkVer = versions.ext || versions.touch,
                i, index, matches, minVer, maxVer, packageName, spec, range, ver;
 
            if (!isArray) {
                checkVerTemp[0] = specs;
            }
 
            for (= 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 {
                        packageName = spec.substring(0, index);
 
                        if (!(ver = versions[aliases[packageName] || packageName])) {
                            // 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
         * @private
         */
        deprecate: function(packageName, since, closure, scope) {
            if (Version.compare(Ext.getVersion(packageName), since) < 1) {
                closure.call(scope);
            }
        }
    }); // End Versioning
}());
 
// load the cmd-5 style app manifest metadata now, if available...
(function(manifest) {
    var packages = (manifest && manifest.packages) || {},
        compat = manifest && manifest.compatibility,
        name, pkg;
 
    for (name in packages) {
        pkg = packages[name];
 
        if (pkg && pkg.version) {
            Ext.setVersion(name, pkg.version);
        }
    }
 
    if (compat) {
        if (Ext.isString(compat)) {
            Ext.setCompatVersion('core', compat);
        }
        else {
            for (name in compat) {
                Ext.setCompatVersion(name, compat[name]);
            }
        }
    }
 
    if (!packages.ext && !packages.touch) {
        Ext.setVersion('ext', '6');
    }
})(Ext.manifest);