]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/base/base.js
Release 6.2.0beta4
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / base / base.js
1 /*
2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
5 version: 3.0.0
6 build: 1549
7 */
8 YUI.add('base-base', function(Y) {
9
10     /**
11      * The base module provides the Base class, which objects requiring attribute and custom event support can extend. 
12      * The module also provides two ways to reuse code - An augmentable Plugin.Host interface which provides plugin support 
13      * (which is augmented to the Base class) and Base.build which provides a way to 
14      * build custom classes using extensions.
15      *
16      * @module base
17      */
18
19     /**
20      * The base-base submodule provides the Base class without the Plugin support, provided by Plugin.Host, 
21      * and without the extension support provided by Base.build.
22      *
23      * @module base
24      * @submodule base-base
25      */
26     var O = Y.Object,
27         L = Y.Lang,
28         DOT = ".",
29         DESTROY = "destroy",
30         INIT = "init",
31         INITIALIZED = "initialized",
32         DESTROYED = "destroyed",
33         INITIALIZER = "initializer",
34         OBJECT_CONSTRUCTOR = Object.prototype.constructor,
35         DEEP = "deep",
36         SHALLOW = "shallow",
37         DESTRUCTOR = "destructor",
38
39         Attribute = Y.Attribute;
40
41     /**
42      * <p>
43      * A base class which objects requiring attributes and custom event support can 
44      * extend. Base also handles the chaining of initializer and destructor methods across 
45      * the hierarchy as part of object construction and destruction. Additionally, attributes configured 
46      * through the static <a href="#property_Base.ATTRS">ATTRS</a> property for each class 
47      * in the hierarchy will be initialized by Base.
48      * </p>
49      *
50      * <p>
51      * The static <a href="#property_Base.NAME">NAME</a> property of each class extending 
52      * from Base will be used as the identifier for the class, and is used by Base to prefix 
53      * all events fired by instances of that class.
54      * </p>
55      * @class Base
56      * @constructor
57      * @uses Attribute
58      * @uses Plugin.Host
59      *
60      * @param {Object} config Object with configuration property name/value pairs
61      */
62     function Base() {
63
64         Attribute.call(this);
65
66         // If Plugin.Host has been augmented [ through base-pluginhost ], setup it's
67         // initial state, but don't initialize Plugins yet. That's done after initialization.
68         var PluginHost = Y.Plugin && Y.Plugin.Host;  
69         if (this._initPlugins && PluginHost) {
70             PluginHost.call(this);
71         }
72
73         if (this._lazyAddAttrs !== false) { this._lazyAddAttrs = true; }
74
75         this.init.apply(this, arguments);
76     }
77
78     /**
79      * The list of properties which can be configured for 
80      * each attribute (e.g. setter, getter, writeOnce, readOnly etc.)
81      *
82      * @property Base._ATTR_CFG
83      * @type Array
84      * @static
85      * @private
86      */
87     Base._ATTR_CFG = Attribute._ATTR_CFG.concat("cloneDefaultValue");
88
89     /**
90      * <p>
91      * The string to be used to identify instances of 
92      * this class, for example in prefixing events.
93      * </p>
94      * <p>
95      * Classes extending Base, should define their own
96      * static NAME property, which should be camelCase by
97      * convention (e.g. MyClass.NAME = "myClass";).
98      * </p>
99      * @property Base.NAME
100      * @type String
101      * @static
102      */
103     Base.NAME = "base";
104
105     /**
106      * The default set of attributes which will be available for instances of this class, and 
107      * their configuration. In addition to the configuration properties listed by 
108      * Attribute's <a href="Attribute.html#method_addAttr">addAttr</a> method, the attribute 
109      * can also be configured with a "cloneDefaultValue" property, which defines how the statically
110      * defined value field should be protected ("shallow", "deep" and false are supported values). 
111      *
112      * By default if the value is an object literal or an array it will be "shallow" cloned, to 
113      * protect the default value.
114      *
115      * @property Base.ATTRS
116      * @type Object
117      * @static
118      */
119     Base.ATTRS = {
120         /**
121          * Flag indicating whether or not this object
122          * has been through the init lifecycle phase.
123          *
124          * @attribute initialized
125          * @readonly
126          * @default false
127          * @type boolean
128          */
129         initialized: {
130             readOnly:true,
131             value:false
132         },
133
134         /**
135          * Flag indicating whether or not this object
136          * has been through the destroy lifecycle phase.
137          *
138          * @attribute destroyed
139          * @readonly
140          * @default false
141          * @type boolean
142          */
143         destroyed: {
144             readOnly:true,
145             value:false
146         }
147     };
148
149     Base.prototype = {
150
151         /**
152          * Init lifecycle method, invoked during construction.
153          * Fires the init event prior to setting up attributes and 
154          * invoking initializers for the class hierarchy.
155          *
156          * @method init
157          * @final
158          * @chainable
159          * @param {Object} config Object with configuration property name/value pairs
160          * @return {Base} A reference to this object
161          */
162         init: function(config) {
163
164             /**
165              * The string used to identify the class of this object.
166              *
167              * @deprecated Use this.constructor.NAME
168              * @property name
169              * @type String
170              */
171             this._yuievt.config.prefix = this.name = this.constructor.NAME;
172
173             /**
174              * <p>
175              * Lifecycle event for the init phase, fired prior to initialization. 
176              * Invoking the preventDefault() method on the event object provided 
177              * to subscribers will prevent initialization from occuring.
178              * </p>
179              * <p>
180              * Subscribers to the "after" momemt of this event, will be notified
181              * after initialization of the object is complete (and therefore
182              * cannot prevent initialization).
183              * </p>
184              *
185              * @event init
186              * @preventable _defInitFn
187              * @param {EventFacade} e Event object, with a cfg property which 
188              * refers to the configuration object passed to the constructor.
189              */
190             this.publish(INIT, {
191                 queuable:false,
192                 defaultFn:this._defInitFn
193             });
194
195             if (config) {
196                 if (config.on) {
197                     this.on(config.on);
198                 }
199                 if (config.after) {
200                     this.after(config.after);
201                 }
202             }
203
204             this.fire(INIT, {cfg: config});
205
206             return this;
207         },
208
209         /**
210          * <p>
211          * Destroy lifecycle method. Fires the destroy
212          * event, prior to invoking destructors for the
213          * class hierarchy.
214          * </p>
215          * <p>
216          * Subscribers to the destroy
217          * event can invoke preventDefault on the event object, to prevent destruction
218          * from proceeding.
219          * </p>
220          * @method destroy
221          * @return {Base} A reference to this object
222          * @final
223          * @chainable
224          */
225         destroy: function() {
226
227             /**
228              * <p>
229              * Lifecycle event for the destroy phase, 
230              * fired prior to destruction. Invoking the preventDefault 
231              * method on the event object provided to subscribers will 
232              * prevent destruction from proceeding.
233              * </p>
234              * <p>
235              * Subscribers to the "after" moment of this event, will be notified
236              * after destruction is complete (and as a result cannot prevent
237              * destruction).
238              * </p>
239              * @event destroy
240              * @preventable _defDestroyFn
241              * @param {EventFacade} e Event object
242              */
243             this.publish(DESTROY, {
244                 queuable:false,
245                 defaultFn: this._defDestroyFn
246             });
247             this.fire(DESTROY);
248             return this;
249         },
250
251         /**
252          * Default init event handler
253          *
254          * @method _defInitFn
255          * @param {EventFacade} e Event object, with a cfg property which 
256          * refers to the configuration object passed to the constructor.
257          * @protected
258          */
259         _defInitFn : function(e) {
260             this._initHierarchy(e.cfg);
261             if (this._initPlugins) {
262                 // Need to initPlugins manually, to handle constructor parsing, static Plug parsing
263                 this._initPlugins(e.cfg);
264             }
265             this._set(INITIALIZED, true);
266         },
267
268         /**
269          * Default destroy event handler
270          *
271          * @method _defDestroyFn
272          * @param {EventFacade} e Event object
273          * @protected
274          */
275         _defDestroyFn : function(e) {
276             this._destroyHierarchy();
277             if (this._destroyPlugins) {
278                 this._destroyPlugins();
279             }
280             this._set(DESTROYED, true);
281         },
282
283         /**
284          * Returns the class hierarchy for this object, with Base being the last class in the array.
285          *
286          * @method _getClasses
287          * @protected
288          * @return {Function[]} An array of classes (constructor functions), making up the class hierarchy for this object.
289          * This value is cached the first time the method, or _getAttrCfgs, is invoked. Subsequent invocations return the 
290          * cached value.
291          */
292         _getClasses : function() {
293             if (!this._classes) {
294                 this._initHierarchyData();
295             }
296             return this._classes;
297         },
298
299         /**
300          * Returns an aggregated set of attribute configurations, by traversing the class hierarchy.
301          *
302          * @method _getAttrCfgs
303          * @protected
304          * @return {Object} The hash of attribute configurations, aggregated across classes in the hierarchy
305          * This value is cached the first time the method, or _getClasses, is invoked. Subsequent invocations return
306          * the cached value.
307          */
308         _getAttrCfgs : function() {
309             if (!this._attrs) {
310                 this._initHierarchyData();
311             }
312             return this._attrs;
313         },
314
315         /**
316          * A helper method used when processing ATTRS across the class hierarchy during 
317          * initialization. Returns a disposable object with the attributes defined for 
318          * the provided class, extracted from the set of all attributes passed in .
319          *
320          * @method _filterAttrCfs
321          * @private
322          *
323          * @param {Function} clazz The class for which the desired attributes are required.
324          * @param {Object} allCfgs The set of all attribute configurations for this instance. 
325          * Attributes will be removed from this set, if they belong to the filtered class, so
326          * that by the time all classes are processed, allCfgs will be empty.
327          * 
328          * @return {Object} The set of attributes belonging to the class passed in, in the form
329          * of an object with attribute name/configuration pairs.
330          */
331         _filterAttrCfgs : function(clazz, allCfgs) {
332             var cfgs = null, attr, attrs = clazz.ATTRS;
333
334             if (attrs) {
335                 for (attr in attrs) {
336                     if (attrs.hasOwnProperty(attr) && allCfgs[attr]) {
337                         cfgs = cfgs || {};
338                         cfgs[attr] = allCfgs[attr];
339                         delete allCfgs[attr];
340                     }
341                 }
342             }
343
344             return cfgs;
345         },
346
347         /**
348          * A helper method used by _getClasses and _getAttrCfgs, which determines both
349          * the array of classes and aggregate set of attribute configurations
350          * across the class hierarchy for the instance.
351          * 
352          * @method _initHierarchyData
353          * @private
354          */
355         _initHierarchyData : function() {
356             var c = this.constructor, 
357                 classes = [],
358                 attrs = [];
359
360             while (c) {
361                 // Add to classes
362                 classes[classes.length] = c;
363
364                 // Add to attributes
365                 if (c.ATTRS) {
366                     attrs[attrs.length] = c.ATTRS;
367                 }
368                 c = c.superclass ? c.superclass.constructor : null;
369             }
370
371             this._classes = classes;
372             this._attrs = this._aggregateAttrs(attrs);
373         },
374
375         /**
376          * A helper method, used by _initHierarchyData to aggregate 
377          * attribute configuration across the instances class hierarchy.
378          *
379          * The method will potect the attribute configuration value to protect the statically defined 
380          * default value in ATTRS if required (if the value is an object literal, array or the 
381          * attribute configuration has cloneDefaultValue set to shallow or deep).
382          *
383          * @method _aggregateAttrs
384          * @private
385          * @param {Array} allAttrs An array of ATTRS definitions across classes in the hierarchy 
386          * (subclass first, Base last)
387          * @return {Object} The aggregate set of ATTRS definitions for the instance
388          */
389         _aggregateAttrs : function(allAttrs) {
390             var attr,
391                 attrs,
392                 cfg,
393                 val,
394                 path,
395                 i, 
396                 clone, 
397                 cfgProps = Base._ATTR_CFG,
398                 aggAttrs = {};
399
400             if (allAttrs) {
401                 for (i = allAttrs.length-1; i >= 0; --i) {
402                     attrs = allAttrs[i];
403
404                     for (attr in attrs) {
405                         if (attrs.hasOwnProperty(attr)) {
406
407                             // Protect config passed in
408                             cfg = Y.mix({}, attrs[attr], true, cfgProps);
409
410                             val = cfg.value;
411                             clone = cfg.cloneDefaultValue;
412
413                             if (val) {
414                                 if ( (clone === undefined && (OBJECT_CONSTRUCTOR === val.constructor || L.isArray(val))) || clone === DEEP || clone === true) {
415                                     cfg.value = Y.clone(val);
416                                 } else if (clone === SHALLOW) {
417                                     cfg.value = Y.merge(val);
418                                 }
419                                 // else if (clone === false), don't clone the static default value. 
420                                 // It's intended to be used by reference.
421                             }
422
423                             path = null;
424                             if (attr.indexOf(DOT) !== -1) {
425                                 path = attr.split(DOT);
426                                 attr = path.shift();
427                             }
428
429                             if (path && aggAttrs[attr] && aggAttrs[attr].value) {
430                                 O.setValue(aggAttrs[attr].value, path, val);
431                             } else if (!path){
432                                 if (!aggAttrs[attr]) {
433                                     aggAttrs[attr] = cfg;
434                                 } else {
435                                     Y.mix(aggAttrs[attr], cfg, true, cfgProps);
436                                 }
437                             }
438                         }
439                     }
440                 }
441             }
442
443             return aggAttrs;
444         },
445
446         /**
447          * Initializes the class hierarchy for the instance, which includes 
448          * initializing attributes for each class defined in the class's 
449          * static <a href="#property_Base.ATTRS">ATTRS</a> property and 
450          * invoking the initializer method on the prototype of each class in the hierarchy.
451          *
452          * @method _initHierarchy
453          * @param {Object} userVals Object with configuration property name/value pairs
454          * @private
455          */
456         _initHierarchy : function(userVals) {
457             var lazy = this._lazyAddAttrs,
458                 constr,
459                 constrProto,
460                 ci,
461                 ei,
462                 el,
463                 classes = this._getClasses(),
464                 attrCfgs = this._getAttrCfgs();
465
466             for (ci = classes.length-1; ci >= 0; ci--) {
467
468                 constr = classes[ci];
469                 constrProto = constr.prototype;
470
471                 if (constr._yuibuild && constr._yuibuild.exts && !constr._yuibuild.dynamic) {
472                     for (ei = 0, el = constr._yuibuild.exts.length; ei < el; ei++) {
473                         constr._yuibuild.exts[ei].apply(this, arguments);
474                     }
475                 }
476
477                 this.addAttrs(this._filterAttrCfgs(constr, attrCfgs), userVals, lazy);
478
479                 if (constrProto.hasOwnProperty(INITIALIZER)) {
480                     constrProto.initializer.apply(this, arguments);
481                 }
482             }
483         },
484
485         /**
486          * Destroys the class hierarchy for this instance by invoking
487          * the descructor method on the prototype of each class in the hierarchy.
488          *
489          * @method _destroyHierarchy
490          * @private
491          */
492         _destroyHierarchy : function() {
493             var constr,
494                 constrProto,
495                 ci, cl,
496                 classes = this._getClasses();
497
498             for (ci = 0, cl = classes.length; ci < cl; ci++) {
499                 constr = classes[ci];
500                 constrProto = constr.prototype;
501                 if (constrProto.hasOwnProperty(DESTRUCTOR)) {
502                     constrProto.destructor.apply(this, arguments);
503                 }
504             }
505         },
506
507         /**
508          * Default toString implementation. Provides the constructor NAME
509          * and the instance ID.
510          *
511          * @method toString
512          * @return {String} String representation for this object
513          */
514         toString: function() {
515             return this.constructor.NAME + "[" + Y.stamp(this) + "]";
516         }
517     };
518
519     // Straightup augment, no wrapper functions
520     Y.mix(Base, Attribute, false, null, 1);
521
522     // Fix constructor
523     Base.prototype.constructor = Base;
524
525     Y.Base = Base;
526
527     // Fix constructor
528     Base.prototype.constructor = Base;
529
530
531 }, '3.0.0' ,{requires:['attribute-base']});
532 YUI.add('base-pluginhost', function(Y) {
533
534     /**
535      * The base-pluginhost submodule adds Plugin support to Base, by augmenting Base with 
536      * Plugin.Host and setting up static (class level) Base.plug and Base.unplug methods.
537      *
538      * @module base
539      * @submodule base-pluginhost
540      * @for Base
541      */
542
543     var Base = Y.Base,
544         PluginHost = Y.Plugin.Host;
545
546     Y.mix(Base, PluginHost, false, null, 1);
547
548     /**
549      * Alias for <a href="Plugin.Host.html#method_Plugin.Host.plug">Plugin.Host.plug</a>. See aliased 
550      * method for argument and return value details.
551      *
552      * @method Base.plug
553      * @static
554      */
555     Base.plug = PluginHost.plug;
556
557     /**
558      * Alias for <a href="Plugin.Host.html#method_Plugin.Host.unplug">Plugin.Host.unplug</a>. See the 
559      * aliased method for argument and return value details.
560      *
561      * @method Base.unplug
562      * @static
563      */
564     Base.unplug = PluginHost.unplug;
565
566
567 }, '3.0.0' ,{requires:['base-base', 'pluginhost']});
568 YUI.add('base-build', function(Y) {
569
570     /**
571      * The base-build submodule provides Base.build functionality, which
572      * can be used to create custom classes, by aggregating extensions onto 
573      * a main class.
574      *
575      * @module base
576      * @submodule base-build
577      * @for Base
578      */
579
580     var Base = Y.Base,
581         L = Y.Lang;
582
583     /**
584      * The build configuration for the Base class.
585      *
586      * Defines the static fields which need to be aggregated
587      * when the Base class is used as the main class passed to 
588      * the <a href="#method_Base.build">Base.build</a> method.
589      *
590      * @property Base._buildCfg
591      * @type Object
592      * @static
593      * @final
594      * @private
595      */
596     Base._buildCfg = {
597         aggregates : ["ATTRS", "_PLUG", "_UNPLUG"]
598     };
599
600     /**
601      * <p>
602      * Builds a custom constructor function (class) from the
603      * main function, and array of extension functions (classes)
604      * provided. The NAME field for the constructor function is 
605      * defined by the first argument passed in.
606      * </p>
607      * <p>
608      * The cfg object supports the following properties
609      * </p>
610      * <dl>
611      *    <dt>dynamic &#60;boolean&#62;</dt>
612      *    <dd>
613      *    <p>If true (default), a completely new class
614      *    is created which extends the main class, and acts as the 
615      *    host on which the extension classes are augmented.</p>
616      *    <p>If false, the extensions classes are augmented directly to
617      *    the main class, modifying the main class' prototype.</p>
618      *    </dd>
619      *    <dt>aggregates &#60;String[]&#62;</dt>
620      *    <dd>An array of static property names, which will get aggregated
621      *    on to the built class, in addition to the default properties build 
622      *    will always aggregate as defined by the main class' static _buildCfg
623      *    property.
624      *    </dd>
625      * </dl>
626      *
627      * @method Base.build
628      * @static
629      * @param {Function} name The name of the new class. Used to defined the NAME property for the new class.
630      * @param {Function} main The main class on which to base the built class
631      * @param {Function[]} extensions The set of extension classes which will be
632      * augmented/aggregated to the built class.
633      * @param {Object} cfg Optional. Build configuration for the class (see description).
634      * @return {Function} A custom class, created from the provided main and extension classes
635      */
636     Base.build = function(name, main, extensions, cfg) {
637
638         var build = Base.build,
639             builtClass = build._getClass(main, cfg),
640             aggregates = build._getAggregates(main, cfg),
641             dynamic = builtClass._yuibuild.dynamic,
642             i, l, val, extClass;
643
644         // Shallow isolate aggregates
645         if (dynamic) {
646             if (aggregates) {
647                 for (i = 0, l = aggregates.length; i < l; ++i) {
648                     val = aggregates[i];
649                     if (main.hasOwnProperty(val)) {
650                         builtClass[val] = L.isArray(main[val]) ? [] : {};
651                     }
652                 }
653                 Y.aggregate(builtClass, main, true, aggregates);
654             }
655         }
656
657         // Augment/Aggregate
658         for (i = 0, l = extensions.length; i < l; i++) {
659             extClass = extensions[i];
660
661             if (aggregates) {
662                 Y.aggregate(builtClass, extClass, true, aggregates);
663             }
664
665             // Old augment
666             Y.mix(builtClass, extClass, true, null, 1);
667
668             builtClass._yuibuild.exts.push(extClass);
669         }
670
671         builtClass.prototype.hasImpl = build._hasImpl;
672
673         if (dynamic) {
674             builtClass.NAME = name;
675             builtClass.prototype.constructor = builtClass;
676         }
677
678         return builtClass;
679     };
680
681     Y.mix(Base.build, {
682
683         _template: function(main) {
684
685             function BuiltClass() {
686
687                 BuiltClass.superclass.constructor.apply(this, arguments);
688
689                 var f = BuiltClass._yuibuild.exts, 
690                     l = f.length,
691                     i;
692
693                 for (i = 0; i < l; i++) {
694                     f[i].apply(this, arguments);
695                 }
696
697                 return this;
698             }
699             Y.extend(BuiltClass, main);
700
701             return BuiltClass;
702         },
703
704         _hasImpl : function(extClass) {
705             var classes = this._getClasses();
706             for (var i = 0, l = classes.length; i < l; i++) {
707                 var cls = classes[i];
708                  
709                 if (cls._yuibuild) {
710                     var exts = cls._yuibuild.exts,
711                         ll = exts.length,
712                         j;
713     
714                     for (j = 0; j < ll; j++) {
715                         if (exts[j] === extClass) {
716                             return true;
717                         }
718                     }
719                 }
720             }
721             return false;
722         },
723
724         _getClass : function(main, cfg) {
725
726            var dynamic = (cfg && false === cfg.dynamic) ? false : true,
727                 builtClass = (dynamic) ? Base.build._template(main) : main;
728
729             builtClass._yuibuild = {
730                 id: null,
731                 exts : [],
732                 dynamic : dynamic
733             };
734
735             return builtClass;
736         },
737
738         _getAggregates : function(main, cfg) {
739             var aggr = [],
740                 cfgAggr = (cfg && cfg.aggregates),
741                 c = main,
742                 classAggr;
743
744             while (c && c.prototype) {
745                 classAggr = c._buildCfg && c._buildCfg.aggregates;
746                 if (classAggr) {
747                     aggr = aggr.concat(classAggr);
748                 }
749                 c = c.superclass ? c.superclass.constructor : null;
750             }
751
752             if (cfgAggr) {
753                 aggr = aggr.concat(cfgAggr);
754             }
755
756             return aggr;
757         }
758     });
759
760
761 }, '3.0.0' ,{requires:['base-base']});
762
763
764 YUI.add('base', function(Y){}, '3.0.0' ,{use:['base-base', 'base-pluginhost', 'base-build']});
765