    // @tag foundation,core
    // @require Base.js
    // @define Ext.Class

    /**
     * @author Jacky Nguyen <jacky@sencha.com>
     * @docauthor 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;

        // Creates a constructor that has nothing extra in its scope chain.
        function makeCtor (className) {
            function constructor () {
                // Opera has some problems returning from a constructor when Dragonfly isn't running. The || null seems to
                // be sufficient to stop it misbehaving. Known to be required against 10.53, 11.51 and 11.61.
                return this.constructor.apply(this, arguments) || null;
            }
            //<debug>
            if (className) {
                constructor.displayName = className;
            }
            //</debug>
            return constructor;
        }

        /**
         * @method constructor
         * Create 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, data);

            ExtClass.process(Class, data, onCreated);

            return Class;
        };

        Ext.apply(ExtClass, {
            /**
             * @private
             */
            onBeforeCreated: function(Class, data, hooks) {
                //<debug>
                Ext.classSystemMonitor && Ext.classSystemMonitor(Class, '>> Ext.Class#onBeforeCreated', arguments);
                //</debug>

                Class.addMembers(data);

                hooks.onCreated.call(Class, Class);

                //<debug>
                Ext.classSystemMonitor && Ext.classSystemMonitor(Class, '<< Ext.Class#onBeforeCreated', arguments);
                //</debug>
            },

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

                if (!Class) {
                    Class = makeCtor(
                        //<debug>
                        data.$className
                        //</debug>
                    );
                }

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

                return Class;
            },

            /**
             * @private
             */
            process: function(Class, data, onCreated) {
                var preprocessorStack = data.preprocessors || ExtClass.defaultPreprocessors,
                    registeredPreprocessors = this.preprocessors,
                    hooks = {
                        onBeforeCreated: this.onBeforeCreated
                    },
                    preprocessors = [],
                    preprocessor, preprocessorsProperties,
                    i, ln, j, subLn, preprocessorProperty;

                delete data.preprocessors;

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

                    if (typeof preprocessor == 'string') {
                        preprocessor = registeredPreprocessors[preprocessor];
                        preprocessorsProperties = preprocessor.properties;

                        if (preprocessorsProperties === true) {
                            preprocessors.push(preprocessor.fn);
                        }
                        else if (preprocessorsProperties) {
                            for (j = 0,subLn = preprocessorsProperties.length; j < subLn; j++) {
                                preprocessorProperty = preprocessorsProperties[j];

                                if (data.hasOwnProperty(preprocessorProperty)) {
                                    preprocessors.push(preprocessor.fn);
                                    break;
                                }
                            }
                        }
                    }
                    else {
                        preprocessors.push(preprocessor);
                    }
                }

                hooks.onCreated = onCreated ? onCreated : Ext.emptyFn;
                hooks.preprocessors = preprocessors;

                this.doProcess(Class, data, hooks);
            },

            doProcess: function(Class, data, hooks) {
                var me = this,
                    preprocessors = hooks.preprocessors,
                    preprocessor = preprocessors.shift(),
                    doProcess = me.doProcess;

                for ( ; preprocessor ; preprocessor = preprocessors.shift()) {
                    // Returning false signifies an asynchronous preprocessor - it will call doProcess when we can continue
                    if (preprocessor.call(me, Class, data, hooks, doProcess) === false) {
                        return;
                    }
                }
                hooks.onBeforeCreated.apply(me, arguments);
            },

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

            /**
             * Register a new pre-processor to be used during the class creation process
             *
             * @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);
             *         }
             *     });
             *
             * @param {Function} fn.cls The created class
             * @param {Object} fn.data The set of properties passed in {@link Ext.Class} constructor
             * @param {Function} fn.fn The callback function that **must** to be executed when this
             * pre-processor finishes, regardless of whether the processing is synchronous or aynchronous.
             * @return {Ext.Class} this
             * @private
             * @static
             */
            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
             *
             * @param {String} name
             * @return {Function} preprocessor
             * @private
             * @static
             */
            getPreprocessor: function(name) {
                return this.preprocessors[name];
            },

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

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

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

            /**
             * Set the default array stack of default pre-processors
             *
             * @private
             * @param {Array} preprocessors
             * @return {Ext.Class} this
             * @static
             */
            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);
             *         }
             *     }).setDefaultPreprocessorPosition('debug', 'last');
             *
             * @private
             * @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
             * @static
             */
            setDefaultPreprocessorPosition: function(name, offset, relativeName) {
                var defaultPreprocessors = this.defaultPreprocessors,
                    index;

                if (typeof offset == 'string') {
                    if (offset === 'first') {
                        defaultPreprocessors.unshift(name);

                        return this;
                    }
                    else if (offset === 'last') {
                        defaultPreprocessors.push(name);

                        return this;
                    }

                    offset = (offset === 'after') ? 1 : -1;
                }

                index = Ext.Array.indexOf(defaultPreprocessors, relativeName);

                if (index !== -1) {
                    Ext.Array.splice(defaultPreprocessors, Math.max(0, index + offset), 0, name);
                }

                return this;
            },

            configNameCache: {},

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

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

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

                return map;
            }
        });

        /**
         * @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, hooks) {
            //<debug>
            Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#extendPreProcessor', arguments);
            //</debug>

            var Base = Ext.Base,
                basePrototype = Base.prototype,
                extend = data.extend,
                Parent, parentPrototype, i;

            delete data.extend;

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

            parentPrototype = Parent.prototype;

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

            Class.extend(Parent);

            Class.triggerExtended.apply(Class, arguments);

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

        }, true);

        //<feature classSystem.statics>
        /**
         * @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) {
            //<debug>
            Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#staticsPreprocessor', arguments);
            //</debug>

            Class.addStatics(data.statics);

            delete data.statics;
        });
        //</feature>

        //<feature classSystem.inheritableStatics>
        /**
         * @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) {
            //<debug>
            Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#inheritableStaticsPreprocessor', arguments);
            //</debug>

            Class.addInheritableStatics(data.inheritableStatics);

            delete data.inheritableStatics;
        });
        //</feature>

        //<feature classSystem.config>
        /**
         * @cfg {Object} config
         * List of configuration options with their default values, for which automatically
         * accessor methods are generated.  For example:
         *
         *     Ext.define('SmartPhone', {
         *          config: {
         *              hasTouchScreen: false,
         *              operatingSystem: 'Other',
         *              price: 500
         *          },
         *          constructor: function(cfg) {
         *              this.initConfig(cfg);
         *          }
         *     });
         *
         *     var iPhone = new SmartPhone({
         *          hasTouchScreen: true,
         *          operatingSystem: 'iOS'
         *     });
         *
         *     iPhone.getPrice(); // 500;
         *     iPhone.getOperatingSystem(); // 'iOS'
         *     iPhone.getHasTouchScreen(); // true;
         *
         * NOTE for when configs are reference types, the getter and setter methods do not make copies.
         *
         * For example, when a config value is set, the reference is stored on the instance. All instances that set
         * the same reference type will share it.
         *
         * In the case of the getter, the value with either come from the prototype if the setter was never called or from
         * the instance as the last value passed to the setter.
         *
         * For some config properties, the value passed to the setter is transformed prior to being stored on the instance.
         */
        ExtClass.registerPreprocessor('config', function(Class, data) {
            //<debug>
            Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#configPreProcessor', arguments);
            //</debug>

            var config = data.config,
                prototype = Class.prototype;

            delete data.config;

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

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

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

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

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

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

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

                        return this;
                    };
                }

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

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

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

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

                        currentGetter = this[getName];

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

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

            Class.addConfig(config, true);
        });
        //</feature>

        //<feature classSystem.mixins>
        /**
         * @cfg {String[]/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', {
         *          mixins: ['CanSing']
         *     })
         *
         * In this case the Musician class will get a `sing` method from CanSing mixin.
         *
         * But what if the Musician already has a `sing` method? Or you want to mix
         * in two classes, both of which define `sing`?  In such a cases it's good
         * to define mixins as an object, where you assign a name to each mixin:
         *
         *     Ext.define('Musician', {
         *          mixins: {
         *              canSing: 'CanSing'
         *          },
         *
         *          sing: function() {
         *              // delegate singing operation to mixin
         *              this.mixins.canSing.sing.call(this);
         *          }
         *     })
         *
         * In this case the `sing` method of Musician will overwrite the
         * mixed in `sing` method. But you can access the original mixed in method
         * through special `mixins` property.
         */
        ExtClass.registerPreprocessor('mixins', function(Class, data, hooks) {
            //<debug>
            Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#mixinsPreprocessor', arguments);
            //</debug>

            var mixins = data.mixins,
                name, mixin, i, ln;

            delete data.mixins;

            Ext.Function.interceptBefore(hooks, 'onCreated', function() {
                //<debug>
                Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#mixinsPreprocessor#beforeCreated', arguments);
                //</debug>

                if (mixins instanceof Array) {
                    for (i = 0,ln = mixins.length; i < ln; i++) {
                        mixin = mixins[i];
                        name = mixin.prototype.mixinId || mixin.$className;

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

        //<feature classSystem.backwardsCompatible>
        // Backwards compatible
        Ext.extend = function(Class, Parent, members) {
            //<debug>
            Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#extend-backwards-compatible', arguments);
            //</debug>

            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'
                //<feature classSystem.statics>
                ,'statics'
                //</feature>
                //<feature classSystem.inheritableStatics>
                ,'inheritableStatics'
                //</feature>
                //<feature classSystem.mixins>
                ,'mixins'
                //</feature>
                //<feature classSystem.config>
                ,'config'
                //</feature>
            ];

            if (Class) {
                cls = new ExtClass(Class, members);
                // The 'constructor' is given as 'Class' but also needs to be on prototype
                cls.prototype.constructor = Class;
            } else {
                cls = new ExtClass(members);
            }

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

            return cls;
        };
        //</feature>
    }());