Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / source / Class.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5   <title>The source code</title>
6   <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8   <style type="text/css">
9     .highlight { display: block; background-color: #ddd; }
10   </style>
11   <script type="text/javascript">
12     function highlight() {
13       document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14     }
15   </script>
16 </head>
17 <body onload="prettyPrint(); highlight();">
18   <pre class="prettyprint lang-js"><span id='Ext-Class'>/**
19 </span> * @author Jacky Nguyen &lt;jacky@sencha.com&gt;
20  * @docauthor Jacky Nguyen &lt;jacky@sencha.com&gt;
21  * @class Ext.Class
22  *
23  * Handles class creation throughout the framework. This is a low level factory that is used by Ext.ClassManager and generally
24  * should not be used directly. If you choose to use Ext.Class you will lose out on the namespace, aliasing and depency loading
25  * features made available by Ext.ClassManager. The only time you would use Ext.Class directly is to create an anonymous class.
26  *
27  * If you wish to create a class you should use {@link Ext#define Ext.define} which aliases
28  * {@link Ext.ClassManager#create Ext.ClassManager.create} to enable namespacing and dynamic dependency resolution.
29  *
30  * Ext.Class is the factory and **not** the superclass of everything. For the base class that **all** Ext classes inherit
31  * from, see {@link Ext.Base}.
32  */
33 (function() {
34
35     var Class,
36         Base = Ext.Base,
37         baseStaticProperties = [],
38         baseStaticProperty;
39
40     for (baseStaticProperty in Base) {
41         if (Base.hasOwnProperty(baseStaticProperty)) {
42             baseStaticProperties.push(baseStaticProperty);
43         }
44     }
45
46 <span id='Ext-Class-method-constructor'>    /**
47 </span>     * @method constructor
48      * Creates new class.
49      * @param {Object} classData An object represent the properties of this class
50      * @param {Function} createdFn (Optional) The callback function to be executed when this class is fully created.
51      * Note that the creation process can be asynchronous depending on the pre-processors used.
52      * @return {Ext.Base} The newly created class
53      */
54     Ext.Class = Class = function(newClass, classData, onClassCreated) {
55         if (typeof newClass != 'function') {
56             onClassCreated = classData;
57             classData = newClass;
58             newClass = function() {
59                 return this.constructor.apply(this, arguments);
60             };
61         }
62
63         if (!classData) {
64             classData = {};
65         }
66
67         var preprocessorStack = classData.preprocessors || Class.getDefaultPreprocessors(),
68             registeredPreprocessors = Class.getPreprocessors(),
69             index = 0,
70             preprocessors = [],
71             preprocessor, staticPropertyName, process, i, j, ln;
72
73         for (i = 0, ln = baseStaticProperties.length; i &lt; ln; i++) {
74             staticPropertyName = baseStaticProperties[i];
75             newClass[staticPropertyName] = Base[staticPropertyName];
76         }
77
78         delete classData.preprocessors;
79
80         for (j = 0, ln = preprocessorStack.length; j &lt; ln; j++) {
81             preprocessor = preprocessorStack[j];
82
83             if (typeof preprocessor == 'string') {
84                 preprocessor = registeredPreprocessors[preprocessor];
85
86                 if (!preprocessor.always) {
87                     if (classData.hasOwnProperty(preprocessor.name)) {
88                         preprocessors.push(preprocessor.fn);
89                     }
90                 }
91                 else {
92                     preprocessors.push(preprocessor.fn);
93                 }
94             }
95             else {
96                 preprocessors.push(preprocessor);
97             }
98         }
99
100         classData.onClassCreated = onClassCreated || Ext.emptyFn;
101
102         classData.onBeforeClassCreated = function(cls, data) {
103             onClassCreated = data.onClassCreated;
104
105             delete data.onBeforeClassCreated;
106             delete data.onClassCreated;
107
108             cls.implement(data);
109
110             onClassCreated.call(cls, cls);
111         };
112
113         process = function(cls, data) {
114             preprocessor = preprocessors[index++];
115
116             if (!preprocessor) {
117                 data.onBeforeClassCreated.apply(this, arguments);
118                 return;
119             }
120
121             if (preprocessor.call(this, cls, data, process) !== false) {
122                 process.apply(this, arguments);
123             }
124         };
125
126         process.call(Class, newClass, classData);
127
128         return newClass;
129     };
130
131     Ext.apply(Class, {
132
133 <span id='Ext-Class-property-preprocessors'>        /** @private */
134 </span>        preprocessors: {},
135
136 <span id='Ext-Class-static-method-registerPreprocessor'>        /**
137 </span>         * Register a new pre-processor to be used during the class creation process
138          *
139          * @member Ext.Class
140          * @param {String} name The pre-processor's name
141          * @param {Function} fn The callback function to be executed. Typical format:
142          *
143          *     function(cls, data, fn) {
144          *         // Your code here
145          *
146          *         // Execute this when the processing is finished.
147          *         // Asynchronous processing is perfectly ok
148          *         if (fn) {
149          *             fn.call(this, cls, data);
150          *         }
151          *     });
152          *
153          * @param {Function} fn.cls The created class
154          * @param {Object} fn.data The set of properties passed in {@link Ext.Class} constructor
155          * @param {Function} fn.fn The callback function that **must** to be executed when this pre-processor finishes,
156          * regardless of whether the processing is synchronous or aynchronous
157          *
158          * @return {Ext.Class} this
159          * @static
160          */
161         registerPreprocessor: function(name, fn, always) {
162             this.preprocessors[name] = {
163                 name: name,
164                 always: always ||  false,
165                 fn: fn
166             };
167
168             return this;
169         },
170
171 <span id='Ext-Class-static-method-getPreprocessor'>        /**
172 </span>         * Retrieve a pre-processor callback function by its name, which has been registered before
173          *
174          * @param {String} name
175          * @return {Function} preprocessor
176          * @static
177          */
178         getPreprocessor: function(name) {
179             return this.preprocessors[name];
180         },
181
182         getPreprocessors: function() {
183             return this.preprocessors;
184         },
185
186 <span id='Ext-Class-static-method-getDefaultPreprocessors'>        /**
187 </span>         * Retrieve the array stack of default pre-processors
188          *
189          * @return {Function[]} defaultPreprocessors
190          * @static
191          */
192         getDefaultPreprocessors: function() {
193             return this.defaultPreprocessors || [];
194         },
195
196 <span id='Ext-Class-static-method-setDefaultPreprocessors'>        /**
197 </span>         * Set the default array stack of default pre-processors
198          *
199          * @param {Function/Function[]} preprocessors
200          * @return {Ext.Class} this
201          * @static
202          */
203         setDefaultPreprocessors: function(preprocessors) {
204             this.defaultPreprocessors = Ext.Array.from(preprocessors);
205
206             return this;
207         },
208
209 <span id='Ext-Class-static-method-setDefaultPreprocessorPosition'>        /**
210 </span>         * Inserts this pre-processor at a specific position in the stack, optionally relative to
211          * any existing pre-processor. For example:
212          *
213          *     Ext.Class.registerPreprocessor('debug', function(cls, data, fn) {
214          *         // Your code here
215          *
216          *         if (fn) {
217          *             fn.call(this, cls, data);
218          *         }
219          *     }).setDefaultPreprocessorPosition('debug', 'last');
220          *
221          * @param {String} name The pre-processor name. Note that it needs to be registered with
222          * {@link #registerPreprocessor registerPreprocessor} before this
223          * @param {String} offset The insertion position. Four possible values are:
224          * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
225          * @param {String} relativeName
226          * @return {Ext.Class} this
227          * @static
228          */
229         setDefaultPreprocessorPosition: function(name, offset, relativeName) {
230             var defaultPreprocessors = this.defaultPreprocessors,
231                 index;
232
233             if (typeof offset == 'string') {
234                 if (offset === 'first') {
235                     defaultPreprocessors.unshift(name);
236
237                     return this;
238                 }
239                 else if (offset === 'last') {
240                     defaultPreprocessors.push(name);
241
242                     return this;
243                 }
244
245                 offset = (offset === 'after') ? 1 : -1;
246             }
247
248             index = Ext.Array.indexOf(defaultPreprocessors, relativeName);
249
250             if (index !== -1) {
251                 Ext.Array.splice(defaultPreprocessors, Math.max(0, index + offset), 0, name);
252             }
253
254             return this;
255         }
256     });
257
258 <span id='Ext-Class-cfg-extend'>    /**
259 </span>     * @cfg {String} extend
260      * The parent class that this class extends. For example:
261      *
262      *     Ext.define('Person', {
263      *         say: function(text) { alert(text); }
264      *     });
265      *
266      *     Ext.define('Developer', {
267      *         extend: 'Person',
268      *         say: function(text) { this.callParent([&quot;print &quot;+text]); }
269      *     });
270      */
271     Class.registerPreprocessor('extend', function(cls, data) {
272         var extend = data.extend,
273             base = Ext.Base,
274             basePrototype = base.prototype,
275             prototype = function() {},
276             parent, i, k, ln, staticName, parentStatics,
277             parentPrototype, clsPrototype;
278
279         if (extend &amp;&amp; extend !== Object) {
280             parent = extend;
281         }
282         else {
283             parent = base;
284         }
285
286         parentPrototype = parent.prototype;
287
288         prototype.prototype = parentPrototype;
289         clsPrototype = cls.prototype = new prototype();
290
291         if (!('$class' in parent)) {
292             for (i in basePrototype) {
293                 if (!parentPrototype[i]) {
294                     parentPrototype[i] = basePrototype[i];
295                 }
296             }
297         }
298
299         clsPrototype.self = cls;
300
301         cls.superclass = clsPrototype.superclass = parentPrototype;
302
303         delete data.extend;
304
305         //&lt;feature classSystem.inheritableStatics&gt;
306         // Statics inheritance
307         parentStatics = parentPrototype.$inheritableStatics;
308
309         if (parentStatics) {
310             for (k = 0, ln = parentStatics.length; k &lt; ln; k++) {
311                 staticName = parentStatics[k];
312
313                 if (!cls.hasOwnProperty(staticName)) {
314                     cls[staticName] = parent[staticName];
315                 }
316             }
317         }
318         //&lt;/feature&gt;
319
320         //&lt;feature classSystem.config&gt;
321         // Merge the parent class' config object without referencing it
322         if (parentPrototype.config) {
323             clsPrototype.config = Ext.Object.merge({}, parentPrototype.config);
324         }
325         else {
326             clsPrototype.config = {};
327         }
328         //&lt;/feature&gt;
329
330         //&lt;feature classSystem.onClassExtended&gt;
331         if (clsPrototype.$onExtended) {
332             clsPrototype.$onExtended.call(cls, cls, data);
333         }
334
335         if (data.onClassExtended) {
336             clsPrototype.$onExtended = data.onClassExtended;
337             delete data.onClassExtended;
338         }
339         //&lt;/feature&gt;
340
341     }, true);
342
343     //&lt;feature classSystem.statics&gt;
344 <span id='Ext-Class-cfg-statics'>    /**
345 </span>     * @cfg {Object} statics
346      * List of static methods for this class. For example:
347      *
348      *     Ext.define('Computer', {
349      *          statics: {
350      *              factory: function(brand) {
351      *                  // 'this' in static methods refer to the class itself
352      *                  return new this(brand);
353      *              }
354      *          },
355      *
356      *          constructor: function() { ... }
357      *     });
358      *
359      *     var dellComputer = Computer.factory('Dell');
360      */
361     Class.registerPreprocessor('statics', function(cls, data) {
362         cls.addStatics(data.statics);
363
364         delete data.statics;
365     });
366     //&lt;/feature&gt;
367
368     //&lt;feature classSystem.inheritableStatics&gt;
369 <span id='Ext-Class-cfg-inheritableStatics'>    /**
370 </span>     * @cfg {Object} inheritableStatics
371      * List of inheritable static methods for this class.
372      * Otherwise just like {@link #statics} but subclasses inherit these methods.
373      */
374     Class.registerPreprocessor('inheritableStatics', function(cls, data) {
375         cls.addInheritableStatics(data.inheritableStatics);
376
377         delete data.inheritableStatics;
378     });
379     //&lt;/feature&gt;
380
381     //&lt;feature classSystem.config&gt;
382 <span id='Ext-Class-cfg-config'>    /**
383 </span>     * @cfg {Object} config
384      * List of configuration options with their default values, for which automatically
385      * accessor methods are generated.  For example:
386      *
387      *     Ext.define('SmartPhone', {
388      *          config: {
389      *              hasTouchScreen: false,
390      *              operatingSystem: 'Other',
391      *              price: 500
392      *          },
393      *          constructor: function(cfg) {
394      *              this.initConfig(cfg);
395      *          }
396      *     });
397      *
398      *     var iPhone = new SmartPhone({
399      *          hasTouchScreen: true,
400      *          operatingSystem: 'iOS'
401      *     });
402      *
403      *     iPhone.getPrice(); // 500;
404      *     iPhone.getOperatingSystem(); // 'iOS'
405      *     iPhone.getHasTouchScreen(); // true;
406      *     iPhone.hasTouchScreen(); // true
407      */
408     Class.registerPreprocessor('config', function(cls, data) {
409         var prototype = cls.prototype;
410
411         Ext.Object.each(data.config, function(name) {
412             var cName = name.charAt(0).toUpperCase() + name.substr(1),
413                 pName = name,
414                 apply = 'apply' + cName,
415                 setter = 'set' + cName,
416                 getter = 'get' + cName;
417
418             if (!(apply in prototype) &amp;&amp; !data.hasOwnProperty(apply)) {
419                 data[apply] = function(val) {
420                     return val;
421                 };
422             }
423
424             if (!(setter in prototype) &amp;&amp; !data.hasOwnProperty(setter)) {
425                 data[setter] = function(val) {
426                     var ret = this[apply].call(this, val, this[pName]);
427
428                     if (typeof ret != 'undefined') {
429                         this[pName] = ret;
430                     }
431
432                     return this;
433                 };
434             }
435
436             if (!(getter in prototype) &amp;&amp; !data.hasOwnProperty(getter)) {
437                 data[getter] = function() {
438                     return this[pName];
439                 };
440             }
441         });
442
443         Ext.Object.merge(prototype.config, data.config);
444         delete data.config;
445     });
446     //&lt;/feature&gt;
447
448     //&lt;feature classSystem.mixins&gt;
449 <span id='Ext-Class-cfg-mixins'>    /**
450 </span>     * @cfg {Object} mixins
451      * List of classes to mix into this class. For example:
452      *
453      *     Ext.define('CanSing', {
454      *          sing: function() {
455      *              alert(&quot;I'm on the highway to hell...&quot;)
456      *          }
457      *     });
458      *
459      *     Ext.define('Musician', {
460      *          extend: 'Person',
461      *
462      *          mixins: {
463      *              canSing: 'CanSing'
464      *          }
465      *     })
466      */
467     Class.registerPreprocessor('mixins', function(cls, data) {
468         var mixins = data.mixins,
469             name, mixin, i, ln;
470
471         delete data.mixins;
472
473         Ext.Function.interceptBefore(data, 'onClassCreated', function(cls) {
474             if (mixins instanceof Array) {
475                 for (i = 0,ln = mixins.length; i &lt; ln; i++) {
476                     mixin = mixins[i];
477                     name = mixin.prototype.mixinId || mixin.$className;
478
479                     cls.mixin(name, mixin);
480                 }
481             }
482             else {
483                 for (name in mixins) {
484                     if (mixins.hasOwnProperty(name)) {
485                         cls.mixin(name, mixins[name]);
486                     }
487                 }
488             }
489         });
490     });
491
492     //&lt;/feature&gt;
493
494     Class.setDefaultPreprocessors([
495         'extend'
496         //&lt;feature classSystem.statics&gt;
497         ,'statics'
498         //&lt;/feature&gt;
499         //&lt;feature classSystem.inheritableStatics&gt;
500         ,'inheritableStatics'
501         //&lt;/feature&gt;
502         //&lt;feature classSystem.config&gt;
503         ,'config'
504         //&lt;/feature&gt;
505         //&lt;feature classSystem.mixins&gt;
506         ,'mixins'
507         //&lt;/feature&gt;
508     ]);
509
510     //&lt;feature classSystem.backwardsCompatible&gt;
511     // Backwards compatible
512     Ext.extend = function(subclass, superclass, members) {
513         if (arguments.length === 2 &amp;&amp; Ext.isObject(superclass)) {
514             members = superclass;
515             superclass = subclass;
516             subclass = null;
517         }
518
519         var cls;
520
521         if (!superclass) {
522             Ext.Error.raise(&quot;Attempting to extend from a class which has not been loaded on the page.&quot;);
523         }
524
525         members.extend = superclass;
526         members.preprocessors = [
527             'extend'
528             //&lt;feature classSystem.statics&gt;
529             ,'statics'
530             //&lt;/feature&gt;
531             //&lt;feature classSystem.inheritableStatics&gt;
532             ,'inheritableStatics'
533             //&lt;/feature&gt;
534             //&lt;feature classSystem.mixins&gt;
535             ,'mixins'
536             //&lt;/feature&gt;
537             //&lt;feature classSystem.config&gt;
538             ,'config'
539             //&lt;/feature&gt;
540         ];
541
542         if (subclass) {
543             cls = new Class(subclass, members);
544         }
545         else {
546             cls = new Class(members);
547         }
548
549         cls.prototype.override = function(o) {
550             for (var m in o) {
551                 if (o.hasOwnProperty(m)) {
552                     this[m] = o[m];
553                 }
554             }
555         };
556
557         return cls;
558     };
559     //&lt;/feature&gt;
560
561 })();
562 </pre>
563 </body>
564 </html>