PageRenderTime 42ms CodeModel.GetById 18ms app.highlight 17ms RepoModel.GetById 1ms app.codeStats 0ms

/ext-4.0.7/src/core/src/class/Class.js

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