Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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