3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @author Jacky Nguyen <jacky@sencha.com>
17 * @docauthor Jacky Nguyen <jacky@sencha.com>
20 * Handles class creation throughout the framework. This is a low level factory that is used by Ext.ClassManager and generally
21 * should not be used directly. If you choose to use Ext.Class you will lose out on the namespace, aliasing and depency loading
22 * features made available by Ext.ClassManager. The only time you would use Ext.Class directly is to create an anonymous class.
24 * If you wish to create a class you should use {@link Ext#define Ext.define} which aliases
25 * {@link Ext.ClassManager#create Ext.ClassManager.create} to enable namespacing and dynamic dependency resolution.
27 * Ext.Class is the factory and **not** the superclass of everything. For the base class that **all** Ext classes inherit
28 * from, see {@link Ext.Base}.
34 baseStaticProperties = [],
37 for (baseStaticProperty in Base) {
38 if (Base.hasOwnProperty(baseStaticProperty)) {
39 baseStaticProperties.push(baseStaticProperty);
46 * @param {Object} classData An object represent the properties of this class
47 * @param {Function} createdFn (Optional) The callback function to be executed when this class is fully created.
48 * Note that the creation process can be asynchronous depending on the pre-processors used.
49 * @return {Ext.Base} The newly created class
51 Ext.Class = Class = function(newClass, classData, onClassCreated) {
52 if (typeof newClass != 'function') {
53 onClassCreated = classData;
55 newClass = function() {
56 return this.constructor.apply(this, arguments);
64 var preprocessorStack = classData.preprocessors || Class.getDefaultPreprocessors(),
65 registeredPreprocessors = Class.getPreprocessors(),
68 preprocessor, staticPropertyName, process, i, j, ln;
70 for (i = 0, ln = baseStaticProperties.length; i < ln; i++) {
71 staticPropertyName = baseStaticProperties[i];
72 newClass[staticPropertyName] = Base[staticPropertyName];
75 delete classData.preprocessors;
77 for (j = 0, ln = preprocessorStack.length; j < ln; j++) {
78 preprocessor = preprocessorStack[j];
80 if (typeof preprocessor == 'string') {
81 preprocessor = registeredPreprocessors[preprocessor];
83 if (!preprocessor.always) {
84 if (classData.hasOwnProperty(preprocessor.name)) {
85 preprocessors.push(preprocessor.fn);
89 preprocessors.push(preprocessor.fn);
93 preprocessors.push(preprocessor);
97 classData.onClassCreated = onClassCreated || Ext.emptyFn;
99 classData.onBeforeClassCreated = function(cls, data) {
100 onClassCreated = data.onClassCreated;
102 delete data.onBeforeClassCreated;
103 delete data.onClassCreated;
107 onClassCreated.call(cls, cls);
110 process = function(cls, data) {
111 preprocessor = preprocessors[index++];
114 data.onBeforeClassCreated.apply(this, arguments);
118 if (preprocessor.call(this, cls, data, process) !== false) {
119 process.apply(this, arguments);
123 process.call(Class, newClass, classData);
134 * Register a new pre-processor to be used during the class creation process
137 * @param {String} name The pre-processor's name
138 * @param {Function} fn The callback function to be executed. Typical format:
140 * function(cls, data, fn) {
143 * // Execute this when the processing is finished.
144 * // Asynchronous processing is perfectly ok
146 * fn.call(this, cls, data);
150 * @param {Function} fn.cls The created class
151 * @param {Object} fn.data The set of properties passed in {@link Ext.Class} constructor
152 * @param {Function} fn.fn The callback function that **must** to be executed when this pre-processor finishes,
153 * regardless of whether the processing is synchronous or aynchronous
155 * @return {Ext.Class} this
158 registerPreprocessor: function(name, fn, always) {
159 this.preprocessors[name] = {
161 always: always || false,
169 * Retrieve a pre-processor callback function by its name, which has been registered before
171 * @param {String} name
172 * @return {Function} preprocessor
175 getPreprocessor: function(name) {
176 return this.preprocessors[name];
179 getPreprocessors: function() {
180 return this.preprocessors;
184 * Retrieve the array stack of default pre-processors
186 * @return {Function[]} defaultPreprocessors
189 getDefaultPreprocessors: function() {
190 return this.defaultPreprocessors || [];
194 * Set the default array stack of default pre-processors
196 * @param {Function/Function[]} preprocessors
197 * @return {Ext.Class} this
200 setDefaultPreprocessors: function(preprocessors) {
201 this.defaultPreprocessors = Ext.Array.from(preprocessors);
207 * Inserts this pre-processor at a specific position in the stack, optionally relative to
208 * any existing pre-processor. For example:
210 * Ext.Class.registerPreprocessor('debug', function(cls, data, fn) {
214 * fn.call(this, cls, data);
216 * }).setDefaultPreprocessorPosition('debug', 'last');
218 * @param {String} name The pre-processor name. Note that it needs to be registered with
219 * {@link #registerPreprocessor registerPreprocessor} before this
220 * @param {String} offset The insertion position. Four possible values are:
221 * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
222 * @param {String} relativeName
223 * @return {Ext.Class} this
226 setDefaultPreprocessorPosition: function(name, offset, relativeName) {
227 var defaultPreprocessors = this.defaultPreprocessors,
230 if (typeof offset == 'string') {
231 if (offset === 'first') {
232 defaultPreprocessors.unshift(name);
236 else if (offset === 'last') {
237 defaultPreprocessors.push(name);
242 offset = (offset === 'after') ? 1 : -1;
245 index = Ext.Array.indexOf(defaultPreprocessors, relativeName);
248 Ext.Array.splice(defaultPreprocessors, Math.max(0, index + offset), 0, name);
256 * @cfg {String} extend
257 * The parent class that this class extends. For example:
259 * Ext.define('Person', {
260 * say: function(text) { alert(text); }
263 * Ext.define('Developer', {
265 * say: function(text) { this.callParent(["print "+text]); }
268 Class.registerPreprocessor('extend', function(cls, data) {
269 var extend = data.extend,
271 basePrototype = base.prototype,
272 prototype = function() {},
273 parent, i, k, ln, staticName, parentStatics,
274 parentPrototype, clsPrototype;
276 if (extend && extend !== Object) {
283 parentPrototype = parent.prototype;
285 prototype.prototype = parentPrototype;
286 clsPrototype = cls.prototype = new prototype();
288 if (!('$class' in parent)) {
289 for (i in basePrototype) {
290 if (!parentPrototype[i]) {
291 parentPrototype[i] = basePrototype[i];
296 clsPrototype.self = cls;
298 cls.superclass = clsPrototype.superclass = parentPrototype;
302 //<feature classSystem.inheritableStatics>
303 // Statics inheritance
304 parentStatics = parentPrototype.$inheritableStatics;
307 for (k = 0, ln = parentStatics.length; k < ln; k++) {
308 staticName = parentStatics[k];
310 if (!cls.hasOwnProperty(staticName)) {
311 cls[staticName] = parent[staticName];
317 //<feature classSystem.config>
318 // Merge the parent class' config object without referencing it
319 if (parentPrototype.config) {
320 clsPrototype.config = Ext.Object.merge({}, parentPrototype.config);
323 clsPrototype.config = {};
327 //<feature classSystem.onClassExtended>
328 if (clsPrototype.$onExtended) {
329 clsPrototype.$onExtended.call(cls, cls, data);
332 if (data.onClassExtended) {
333 clsPrototype.$onExtended = data.onClassExtended;
334 delete data.onClassExtended;
340 //<feature classSystem.statics>
342 * @cfg {Object} statics
343 * List of static methods for this class. For example:
345 * Ext.define('Computer', {
347 * factory: function(brand) {
348 * // 'this' in static methods refer to the class itself
349 * return new this(brand);
353 * constructor: function() { ... }
356 * var dellComputer = Computer.factory('Dell');
358 Class.registerPreprocessor('statics', function(cls, data) {
359 cls.addStatics(data.statics);
365 //<feature classSystem.inheritableStatics>
367 * @cfg {Object} inheritableStatics
368 * List of inheritable static methods for this class.
369 * Otherwise just like {@link #statics} but subclasses inherit these methods.
371 Class.registerPreprocessor('inheritableStatics', function(cls, data) {
372 cls.addInheritableStatics(data.inheritableStatics);
374 delete data.inheritableStatics;
378 //<feature classSystem.config>
380 * @cfg {Object} config
381 * List of configuration options with their default values, for which automatically
382 * accessor methods are generated. For example:
384 * Ext.define('SmartPhone', {
386 * hasTouchScreen: false,
387 * operatingSystem: 'Other',
390 * constructor: function(cfg) {
391 * this.initConfig(cfg);
395 * var iPhone = new SmartPhone({
396 * hasTouchScreen: true,
397 * operatingSystem: 'iOS'
400 * iPhone.getPrice(); // 500;
401 * iPhone.getOperatingSystem(); // 'iOS'
402 * iPhone.getHasTouchScreen(); // true;
403 * iPhone.hasTouchScreen(); // true
405 Class.registerPreprocessor('config', function(cls, data) {
406 var prototype = cls.prototype;
408 Ext.Object.each(data.config, function(name) {
409 var cName = name.charAt(0).toUpperCase() + name.substr(1),
411 apply = 'apply' + cName,
412 setter = 'set' + cName,
413 getter = 'get' + cName;
415 if (!(apply in prototype) && !data.hasOwnProperty(apply)) {
416 data[apply] = function(val) {
421 if (!(setter in prototype) && !data.hasOwnProperty(setter)) {
422 data[setter] = function(val) {
423 var ret = this[apply].call(this, val, this[pName]);
425 if (typeof ret != 'undefined') {
433 if (!(getter in prototype) && !data.hasOwnProperty(getter)) {
434 data[getter] = function() {
440 Ext.Object.merge(prototype.config, data.config);
445 //<feature classSystem.mixins>
447 * @cfg {Object} mixins
448 * List of classes to mix into this class. For example:
450 * Ext.define('CanSing', {
452 * alert("I'm on the highway to hell...")
456 * Ext.define('Musician', {
464 Class.registerPreprocessor('mixins', function(cls, data) {
465 var mixins = data.mixins,
470 Ext.Function.interceptBefore(data, 'onClassCreated', function(cls) {
471 if (mixins instanceof Array) {
472 for (i = 0,ln = mixins.length; i < ln; i++) {
474 name = mixin.prototype.mixinId || mixin.$className;
476 cls.mixin(name, mixin);
480 for (name in mixins) {
481 if (mixins.hasOwnProperty(name)) {
482 cls.mixin(name, mixins[name]);
491 Class.setDefaultPreprocessors([
493 //<feature classSystem.statics>
496 //<feature classSystem.inheritableStatics>
497 ,'inheritableStatics'
499 //<feature classSystem.config>
502 //<feature classSystem.mixins>
507 //<feature classSystem.backwardsCompatible>
508 // Backwards compatible
509 Ext.extend = function(subclass, superclass, members) {
510 if (arguments.length === 2 && Ext.isObject(superclass)) {
511 members = superclass;
512 superclass = subclass;
519 Ext.Error.raise("Attempting to extend from a class which has not been loaded on the page.");
522 members.extend = superclass;
523 members.preprocessors = [
525 //<feature classSystem.statics>
528 //<feature classSystem.inheritableStatics>
529 ,'inheritableStatics'
531 //<feature classSystem.mixins>
534 //<feature classSystem.config>
540 cls = new Class(subclass, members);
543 cls = new Class(members);
546 cls.prototype.override = function(o) {
548 if (o.hasOwnProperty(m)) {