NOBUG: Fixed file access permissions
[moodle.git] / lib / yuilib / 3.13.0 / base-build / base-build.js
blob4347048df76924aea2215ab4d4dc183e893c1977
1 /*
2 YUI 3.13.0 (build 508226d)
3 Copyright 2013 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
8 YUI.add('base-build', function (Y, NAME) {
10     /**
11      * The base-build submodule provides Base.build functionality, which
12      * can be used to create custom classes, by aggregating extensions onto
13      * a main class.
14      *
15      * @module base
16      * @submodule base-build
17      * @for Base
18      */
19     var BaseCore = Y.BaseCore,
20         Base     = Y.Base,
21         L        = Y.Lang,
23         INITIALIZER = "initializer",
24         DESTRUCTOR  = "destructor",
25         AGGREGATES  = ["_PLUG", "_UNPLUG"],
27         build;
29     // Utility function used in `_buildCfg` to aggregate array values into a new
30     // array from the sender constructor to the receiver constructor.
31     function arrayAggregator(prop, r, s) {
32         if (s[prop]) {
33             r[prop] = (r[prop] || []).concat(s[prop]);
34         }
35     }
37     // Utility function used in `_buildCfg` to aggregate `_ATTR_CFG` array
38     // values from the sender constructor into a new array on receiver's
39     // constructor, and clear the cached hash.
40     function attrCfgAggregator(prop, r, s) {
41         if (s._ATTR_CFG) {
42             // Clear cached hash.
43             r._ATTR_CFG_HASH = null;
45             arrayAggregator.apply(null, arguments);
46         }
47     }
49     // Utility function used in `_buildCfg` to aggregate ATTRS configs from one
50     // the sender constructor to the receiver constructor.
51     function attrsAggregator(prop, r, s) {
52         BaseCore.modifyAttrs(r, s.ATTRS);
53     }
55     Base._build = function(name, main, extensions, px, sx, cfg) {
57         var build = Base._build,
59             builtClass = build._ctor(main, cfg),
60             buildCfg = build._cfg(main, cfg, extensions),
62             _mixCust = build._mixCust,
64             dynamic = builtClass._yuibuild.dynamic,
66             i, l, extClass, extProto,
67             initializer,
68             destructor;
70         // Augment/Aggregate
71         for (i = 0, l = extensions.length; i < l; i++) {
72             extClass = extensions[i];
74             extProto = extClass.prototype;
76             initializer = extProto[INITIALIZER];
77             destructor = extProto[DESTRUCTOR];
78             delete extProto[INITIALIZER];
79             delete extProto[DESTRUCTOR];
81             // Prototype, old non-displacing augment
82             Y.mix(builtClass, extClass, true, null, 1);
84             // Custom Statics
85             _mixCust(builtClass, extClass, buildCfg);
87             if (initializer) {
88                 extProto[INITIALIZER] = initializer;
89             }
91             if (destructor) {
92                 extProto[DESTRUCTOR] = destructor;
93             }
95             builtClass._yuibuild.exts.push(extClass);
96         }
98         if (px) {
99             Y.mix(builtClass.prototype, px, true);
100         }
102         if (sx) {
103             Y.mix(builtClass, build._clean(sx, buildCfg), true);
104             _mixCust(builtClass, sx, buildCfg);
105         }
107         builtClass.prototype.hasImpl = build._impl;
109         if (dynamic) {
110             builtClass.NAME = name;
111             builtClass.prototype.constructor = builtClass;
113             // Carry along the reference to `modifyAttrs()` from `main`.
114             builtClass.modifyAttrs = main.modifyAttrs;
115         }
117         return builtClass;
118     };
120     build = Base._build;
122     Y.mix(build, {
124         _mixCust: function(r, s, cfg) {
126             var aggregates,
127                 custom,
128                 statics,
129                 aggr,
130                 l,
131                 i;
133             if (cfg) {
134                 aggregates = cfg.aggregates;
135                 custom = cfg.custom;
136                 statics = cfg.statics;
137             }
139             if (statics) {
140                 Y.mix(r, s, true, statics);
141             }
143             if (aggregates) {
144                 for (i = 0, l = aggregates.length; i < l; i++) {
145                     aggr = aggregates[i];
146                     if (!r.hasOwnProperty(aggr) && s.hasOwnProperty(aggr)) {
147                         r[aggr] = L.isArray(s[aggr]) ? [] : {};
148                     }
149                     Y.aggregate(r, s, true, [aggr]);
150                 }
151             }
153             if (custom) {
154                 for (i in custom) {
155                     if (custom.hasOwnProperty(i)) {
156                         custom[i](i, r, s);
157                     }
158                 }
159             }
161         },
163         _tmpl: function(main) {
165             function BuiltClass() {
166                 BuiltClass.superclass.constructor.apply(this, arguments);
167             }
168             Y.extend(BuiltClass, main);
170             return BuiltClass;
171         },
173         _impl : function(extClass) {
174             var classes = this._getClasses(), i, l, cls, exts, ll, j;
175             for (i = 0, l = classes.length; i < l; i++) {
176                 cls = classes[i];
177                 if (cls._yuibuild) {
178                     exts = cls._yuibuild.exts;
179                     ll = exts.length;
181                     for (j = 0; j < ll; j++) {
182                         if (exts[j] === extClass) {
183                             return true;
184                         }
185                     }
186                 }
187             }
188             return false;
189         },
191         _ctor : function(main, cfg) {
193            var dynamic = (cfg && false === cfg.dynamic) ? false : true,
194                builtClass = (dynamic) ? build._tmpl(main) : main,
195                buildCfg = builtClass._yuibuild;
197             if (!buildCfg) {
198                 buildCfg = builtClass._yuibuild = {};
199             }
201             buildCfg.id = buildCfg.id || null;
202             buildCfg.exts = buildCfg.exts || [];
203             buildCfg.dynamic = dynamic;
205             return builtClass;
206         },
208         _cfg : function(main, cfg, exts) {
209             var aggr = [],
210                 cust = {},
211                 statics = [],
212                 buildCfg,
213                 cfgAggr = (cfg && cfg.aggregates),
214                 cfgCustBuild = (cfg && cfg.custom),
215                 cfgStatics = (cfg && cfg.statics),
216                 c = main,
217                 i,
218                 l;
220             // Prototype Chain
221             while (c && c.prototype) {
222                 buildCfg = c._buildCfg;
223                 if (buildCfg) {
224                     if (buildCfg.aggregates) {
225                         aggr = aggr.concat(buildCfg.aggregates);
226                     }
227                     if (buildCfg.custom) {
228                         Y.mix(cust, buildCfg.custom, true);
229                     }
230                     if (buildCfg.statics) {
231                         statics = statics.concat(buildCfg.statics);
232                     }
233                 }
234                 c = c.superclass ? c.superclass.constructor : null;
235             }
237             // Exts
238             if (exts) {
239                 for (i = 0, l = exts.length; i < l; i++) {
240                     c = exts[i];
241                     buildCfg = c._buildCfg;
242                     if (buildCfg) {
243                         if (buildCfg.aggregates) {
244                             aggr = aggr.concat(buildCfg.aggregates);
245                         }
246                         if (buildCfg.custom) {
247                             Y.mix(cust, buildCfg.custom, true);
248                         }
249                         if (buildCfg.statics) {
250                             statics = statics.concat(buildCfg.statics);
251                         }
252                     }
253                 }
254             }
256             if (cfgAggr) {
257                 aggr = aggr.concat(cfgAggr);
258             }
260             if (cfgCustBuild) {
261                 Y.mix(cust, cfg.cfgBuild, true);
262             }
264             if (cfgStatics) {
265                 statics = statics.concat(cfgStatics);
266             }
268             return {
269                 aggregates: aggr,
270                 custom: cust,
271                 statics: statics
272             };
273         },
275         _clean : function(sx, cfg) {
276             var prop, i, l, sxclone = Y.merge(sx),
277                 aggregates = cfg.aggregates,
278                 custom = cfg.custom;
280             for (prop in custom) {
281                 if (sxclone.hasOwnProperty(prop)) {
282                     delete sxclone[prop];
283                 }
284             }
286             for (i = 0, l = aggregates.length; i < l; i++) {
287                 prop = aggregates[i];
288                 if (sxclone.hasOwnProperty(prop)) {
289                     delete sxclone[prop];
290                 }
291             }
293             return sxclone;
294         }
295     });
297     /**
298      * <p>
299      * Builds a custom constructor function (class) from the
300      * main function, and array of extension functions (classes)
301      * provided. The NAME field for the constructor function is
302      * defined by the first argument passed in.
303      * </p>
304      * <p>
305      * The cfg object supports the following properties
306      * </p>
307      * <dl>
308      *    <dt>dynamic &#60;boolean&#62;</dt>
309      *    <dd>
310      *    <p>If true (default), a completely new class
311      *    is created which extends the main class, and acts as the
312      *    host on which the extension classes are augmented.</p>
313      *    <p>If false, the extensions classes are augmented directly to
314      *    the main class, modifying the main class' prototype.</p>
315      *    </dd>
316      *    <dt>aggregates &#60;String[]&#62;</dt>
317      *    <dd>An array of static property names, which will get aggregated
318      *    on to the built class, in addition to the default properties build
319      *    will always aggregate as defined by the main class' static _buildCfg
320      *    property.
321      *    </dd>
322      * </dl>
323      *
324      * @method build
325      * @deprecated Use the more convenient Base.create and Base.mix methods instead
326      * @static
327      * @param {Function} name The name of the new class. Used to define the NAME property for the new class.
328      * @param {Function} main The main class on which to base the built class
329      * @param {Function[]} extensions The set of extension classes which will be
330      * augmented/aggregated to the built class.
331      * @param {Object} cfg Optional. Build configuration for the class (see description).
332      * @return {Function} A custom class, created from the provided main and extension classes
333      */
334     Base.build = function(name, main, extensions, cfg) {
335         return build(name, main, extensions, null, null, cfg);
336     };
338     /**
339      * Creates a new class (constructor function) which extends the base class passed in as the second argument,
340      * and mixes in the array of extensions provided.
341      *
342      * Prototype properties or methods can be added to the new class, using the px argument (similar to Y.extend).
343      *
344      * Static properties or methods can be added to the new class, using the sx argument (similar to Y.extend).
345      *
346      * **NOTE FOR COMPONENT DEVELOPERS**: Both the `base` class, and `extensions` can define static a `_buildCfg`
347      * property, which acts as class creation meta-data, and drives how special static properties from the base
348      * class, or extensions should be copied, aggregated or (custom) mixed into the newly created class.
349      *
350      * The `_buildCfg` property is a hash with 3 supported properties: `statics`, `aggregates` and `custom`, e.g:
351      *
352      *     // If the Base/Main class is the thing introducing the property:
353      *
354      *     MyBaseClass._buildCfg = {
355      *
356      *        // Static properties/methods to copy (Alias) to the built class.
357      *        statics: ["CopyThisMethod", "CopyThisProperty"],
358      *
359      *        // Static props to aggregate onto the built class.
360      *        aggregates: ["AggregateThisProperty"],
361      *
362      *        // Static properties which need custom handling (e.g. deep merge etc.)
363      *        custom: {
364      *           "CustomProperty" : function(property, Receiver, Supplier) {
365      *              ...
366      *              var triggers = Receiver.CustomProperty.triggers;
367      *              Receiver.CustomProperty.triggers = triggers.concat(Supplier.CustomProperty.triggers);
368      *              ...
369      *           }
370      *        }
371      *     };
372      *
373      *     MyBaseClass.CopyThisMethod = function() {...};
374      *     MyBaseClass.CopyThisProperty = "foo";
375      *     MyBaseClass.AggregateThisProperty = {...};
376      *     MyBaseClass.CustomProperty = {
377      *        triggers: [...]
378      *     }
379      *
380      *     // Or, if the Extension is the thing introducing the property:
381      *
382      *     MyExtension._buildCfg = {
383      *         statics : ...
384      *         aggregates : ...
385      *         custom : ...
386      *     }
387      *
388      * This way, when users pass your base or extension class to `Y.Base.create` or `Y.Base.mix`, they don't need to
389      * know which properties need special handling. `Y.Base` has a buildCfg which defines `ATTRS` for custom mix handling
390      * (to protect the static config objects), and `Y.Widget` has a buildCfg which specifies `HTML_PARSER` for
391      * straight up aggregation.
392      *
393      * @method create
394      * @static
395      * @param {String} name The name of the newly created class. Used to define the NAME property for the new class.
396      * @param {Function} main The base class which the new class should extend.
397      * This class needs to be Base or a class derived from base (e.g. Widget).
398      * @param {Function[]} extensions The list of extensions which will be mixed into the built class.
399      * @param {Object} px The set of prototype properties/methods to add to the built class.
400      * @param {Object} sx The set of static properties/methods to add to the built class.
401      * @return {Function} The newly created class.
402      */
403     Base.create = function(name, base, extensions, px, sx) {
404         return build(name, base, extensions, px, sx);
405     };
407     /**
408      * <p>Mixes in a list of extensions to an existing class.</p>
409      * @method mix
410      * @static
411      * @param {Function} main The existing class into which the extensions should be mixed.
412      * The class needs to be Base or a class derived from Base (e.g. Widget)
413      * @param {Function[]} extensions The set of extension classes which will mixed into the existing main class.
414      * @return {Function} The modified main class, with extensions mixed in.
415      */
416     Base.mix = function(main, extensions) {
418         if (main._CACHED_CLASS_DATA) {
419             main._CACHED_CLASS_DATA = null;
420         }
422         return build(null, main, extensions, null, null, {dynamic:false});
423     };
425     /**
426      * The build configuration for the Base class.
427      *
428      * Defines the static fields which need to be aggregated when the Base class
429      * is used as the main class passed to the
430      * <a href="#method_Base.build">Base.build</a> method.
431      *
432      * @property _buildCfg
433      * @type Object
434      * @static
435      * @final
436      * @private
437      */
438     BaseCore._buildCfg = {
439         aggregates: AGGREGATES.concat(),
441         custom: {
442             ATTRS         : attrsAggregator,
443             _ATTR_CFG     : attrCfgAggregator,
444             _NON_ATTRS_CFG: arrayAggregator
445         }
446     };
448     // Makes sure Base and BaseCore use separate `_buildCfg` objects.
449     Base._buildCfg = {
450         aggregates: AGGREGATES.concat(),
452         custom: {
453             ATTRS         : attrsAggregator,
454             _ATTR_CFG     : attrCfgAggregator,
455             _NON_ATTRS_CFG: arrayAggregator
456         }
457     };
460 }, '3.13.0', {"requires": ["base-base"]});