MDL-32843 import YUI 3.5.1
[moodle.git] / lib / yui / 3.5.1 / build / oop / oop.js
blob55ab9ed7291a82638138e64dc1058f1d76a17f48
1 /*
2 YUI 3.5.1 (build 22)
3 Copyright 2012 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
7 YUI.add('oop', function(Y) {
9 /**
10 Adds object inheritance and manipulation utilities to the YUI instance. This
11 module is required by most YUI components.
13 @module oop
14 **/
16 var L            = Y.Lang,
17     A            = Y.Array,
18     OP           = Object.prototype,
19     CLONE_MARKER = '_~yuim~_',
21     hasOwn   = OP.hasOwnProperty,
22     toString = OP.toString;
24 function dispatch(o, f, c, proto, action) {
25     if (o && o[action] && o !== Y) {
26         return o[action].call(o, f, c);
27     } else {
28         switch (A.test(o)) {
29             case 1:
30                 return A[action](o, f, c);
31             case 2:
32                 return A[action](Y.Array(o, 0, true), f, c);
33             default:
34                 return Y.Object[action](o, f, c, proto);
35         }
36     }
39 /**
40 Augments the _receiver_ with prototype properties from the _supplier_. The
41 receiver may be a constructor function or an object. The supplier must be a
42 constructor function.
44 If the _receiver_ is an object, then the _supplier_ constructor will be called
45 immediately after _receiver_ is augmented, with _receiver_ as the `this` object.
47 If the _receiver_ is a constructor function, then all prototype methods of
48 _supplier_ that are copied to _receiver_ will be sequestered, and the
49 _supplier_ constructor will not be called immediately. The first time any
50 sequestered method is called on the _receiver_'s prototype, all sequestered
51 methods will be immediately copied to the _receiver_'s prototype, the
52 _supplier_'s constructor will be executed, and finally the newly unsequestered
53 method that was called will be executed.
55 This sequestering logic sounds like a bunch of complicated voodoo, but it makes
56 it cheap to perform frequent augmentation by ensuring that suppliers'
57 constructors are only called if a supplied method is actually used. If none of
58 the supplied methods is ever used, then there's no need to take the performance
59 hit of calling the _supplier_'s constructor.
61 @method augment
62 @param {Function|Object} receiver Object or function to be augmented.
63 @param {Function} supplier Function that supplies the prototype properties with
64   which to augment the _receiver_.
65 @param {Boolean} [overwrite=false] If `true`, properties already on the receiver
66   will be overwritten if found on the supplier's prototype.
67 @param {String[]} [whitelist] An array of property names. If specified,
68   only the whitelisted prototype properties will be applied to the receiver, and
69   all others will be ignored.
70 @param {Array|any} [args] Argument or array of arguments to pass to the
71   supplier's constructor when initializing.
72 @return {Function} Augmented object.
73 @for YUI
74 **/
75 Y.augment = function (receiver, supplier, overwrite, whitelist, args) {
76     var rProto    = receiver.prototype,
77         sequester = rProto && supplier,
78         sProto    = supplier.prototype,
79         to        = rProto || receiver,
81         copy,
82         newPrototype,
83         replacements,
84         sequestered,
85         unsequester;
87     args = args ? Y.Array(args) : [];
89     if (sequester) {
90         newPrototype = {};
91         replacements = {};
92         sequestered  = {};
94         copy = function (value, key) {
95             if (overwrite || !(key in rProto)) {
96                 if (toString.call(value) === '[object Function]') {
97                     sequestered[key] = value;
99                     newPrototype[key] = replacements[key] = function () {
100                         return unsequester(this, value, arguments);
101                     };
102                 } else {
103                     newPrototype[key] = value;
104                 }
105             }
106         };
108         unsequester = function (instance, fn, fnArgs) {
109             // Unsequester all sequestered functions.
110             for (var key in sequestered) {
111                 if (hasOwn.call(sequestered, key)
112                         && instance[key] === replacements[key]) {
114                     instance[key] = sequestered[key];
115                 }
116             }
118             // Execute the supplier constructor.
119             supplier.apply(instance, args);
121             // Finally, execute the original sequestered function.
122             return fn.apply(instance, fnArgs);
123         };
125         if (whitelist) {
126             Y.Array.each(whitelist, function (name) {
127                 if (name in sProto) {
128                     copy(sProto[name], name);
129                 }
130             });
131         } else {
132             Y.Object.each(sProto, copy, null, true);
133         }
134     }
136     Y.mix(to, newPrototype || sProto, overwrite, whitelist);
138     if (!sequester) {
139         supplier.apply(to, args);
140     }
142     return receiver;
146  * Copies object properties from the supplier to the receiver. If the target has
147  * the property, and the property is an object, the target object will be
148  * augmented with the supplier's value.
150  * @method aggregate
151  * @param {Object} receiver Object to receive the augmentation.
152  * @param {Object} supplier Object that supplies the properties with which to
153  *     augment the receiver.
154  * @param {Boolean} [overwrite=false] If `true`, properties already on the receiver
155  *     will be overwritten if found on the supplier.
156  * @param {String[]} [whitelist] Whitelist. If supplied, only properties in this
157  *     list will be applied to the receiver.
158  * @return {Object} Augmented object.
159  */
160 Y.aggregate = function(r, s, ov, wl) {
161     return Y.mix(r, s, ov, wl, 0, true);
165  * Utility to set up the prototype, constructor and superclass properties to
166  * support an inheritance strategy that can chain constructors and methods.
167  * Static members will not be inherited.
169  * @method extend
170  * @param {function} r   the object to modify.
171  * @param {function} s the object to inherit.
172  * @param {object} px prototype properties to add/override.
173  * @param {object} sx static properties to add/override.
174  * @return {object} the extended object.
175  */
176 Y.extend = function(r, s, px, sx) {
177     if (!s || !r) {
178         Y.error('extend failed, verify dependencies');
179     }
181     var sp = s.prototype, rp = Y.Object(sp);
182     r.prototype = rp;
184     rp.constructor = r;
185     r.superclass = sp;
187     // assign constructor property
188     if (s != Object && sp.constructor == OP.constructor) {
189         sp.constructor = s;
190     }
192     // add prototype overrides
193     if (px) {
194         Y.mix(rp, px, true);
195     }
197     // add object overrides
198     if (sx) {
199         Y.mix(r, sx, true);
200     }
202     return r;
206  * Executes the supplied function for each item in
207  * a collection.  Supports arrays, objects, and
208  * NodeLists
209  * @method each
210  * @param {object} o the object to iterate.
211  * @param {function} f the function to execute.  This function
212  * receives the value, key, and object as parameters.
213  * @param {object} c the execution context for the function.
214  * @param {boolean} proto if true, prototype properties are
215  * iterated on objects.
216  * @return {YUI} the YUI instance.
217  */
218 Y.each = function(o, f, c, proto) {
219     return dispatch(o, f, c, proto, 'each');
223  * Executes the supplied function for each item in
224  * a collection.  The operation stops if the function
225  * returns true. Supports arrays, objects, and
226  * NodeLists.
227  * @method some
228  * @param {object} o the object to iterate.
229  * @param {function} f the function to execute.  This function
230  * receives the value, key, and object as parameters.
231  * @param {object} c the execution context for the function.
232  * @param {boolean} proto if true, prototype properties are
233  * iterated on objects.
234  * @return {boolean} true if the function ever returns true,
235  * false otherwise.
236  */
237 Y.some = function(o, f, c, proto) {
238     return dispatch(o, f, c, proto, 'some');
242  * Deep object/array copy.  Function clones are actually
243  * wrappers around the original function.
244  * Array-like objects are treated as arrays.
245  * Primitives are returned untouched.  Optionally, a
246  * function can be provided to handle other data types,
247  * filter keys, validate values, etc.
249  * @method clone
250  * @param {object} o what to clone.
251  * @param {boolean} safe if true, objects will not have prototype
252  * items from the source.  If false, they will.  In this case, the
253  * original is initially protected, but the clone is not completely
254  * immune from changes to the source object prototype.  Also, cloned
255  * prototype items that are deleted from the clone will result
256  * in the value of the source prototype being exposed.  If operating
257  * on a non-safe clone, items should be nulled out rather than deleted.
258  * @param {function} f optional function to apply to each item in a
259  * collection; it will be executed prior to applying the value to
260  * the new object.  Return false to prevent the copy.
261  * @param {object} c optional execution context for f.
262  * @param {object} owner Owner object passed when clone is iterating
263  * an object.  Used to set up context for cloned functions.
264  * @param {object} cloned hash of previously cloned objects to avoid
265  * multiple clones.
266  * @return {Array|Object} the cloned object.
267  */
268 Y.clone = function(o, safe, f, c, owner, cloned) {
270     if (!L.isObject(o)) {
271         return o;
272     }
274     // @todo cloning YUI instances doesn't currently work
275     if (Y.instanceOf(o, YUI)) {
276         return o;
277     }
279     var o2, marked = cloned || {}, stamp,
280         yeach = Y.each;
282     switch (L.type(o)) {
283         case 'date':
284             return new Date(o);
285         case 'regexp':
286             // if we do this we need to set the flags too
287             // return new RegExp(o.source);
288             return o;
289         case 'function':
290             // o2 = Y.bind(o, owner);
291             // break;
292             return o;
293         case 'array':
294             o2 = [];
295             break;
296         default:
298             // #2528250 only one clone of a given object should be created.
299             if (o[CLONE_MARKER]) {
300                 return marked[o[CLONE_MARKER]];
301             }
303             stamp = Y.guid();
305             o2 = (safe) ? {} : Y.Object(o);
307             o[CLONE_MARKER] = stamp;
308             marked[stamp] = o;
309     }
311     // #2528250 don't try to clone element properties
312     if (!o.addEventListener && !o.attachEvent) {
313         yeach(o, function(v, k) {
314 if ((k || k === 0) && (!f || (f.call(c || this, v, k, this, o) !== false))) {
315                 if (k !== CLONE_MARKER) {
316                     if (k == 'prototype') {
317                         // skip the prototype
318                     // } else if (o[k] === o) {
319                     //     this[k] = this;
320                     } else {
321                         this[k] =
322                             Y.clone(v, safe, f, c, owner || o, marked);
323                     }
324                 }
325             }
326         }, o2);
327     }
329     if (!cloned) {
330         Y.Object.each(marked, function(v, k) {
331             if (v[CLONE_MARKER]) {
332                 try {
333                     delete v[CLONE_MARKER];
334                 } catch (e) {
335                     v[CLONE_MARKER] = null;
336                 }
337             }
338         }, this);
339         marked = null;
340     }
342     return o2;
347  * Returns a function that will execute the supplied function in the
348  * supplied object's context, optionally adding any additional
349  * supplied parameters to the beginning of the arguments collection the
350  * supplied to the function.
352  * @method bind
353  * @param {Function|String} f the function to bind, or a function name
354  * to execute on the context object.
355  * @param {object} c the execution context.
356  * @param {any} args* 0..n arguments to include before the arguments the
357  * function is executed with.
358  * @return {function} the wrapped function.
359  */
360 Y.bind = function(f, c) {
361     var xargs = arguments.length > 2 ?
362             Y.Array(arguments, 2, true) : null;
363     return function() {
364         var fn = L.isString(f) ? c[f] : f,
365             args = (xargs) ?
366                 xargs.concat(Y.Array(arguments, 0, true)) : arguments;
367         return fn.apply(c || fn, args);
368     };
372  * Returns a function that will execute the supplied function in the
373  * supplied object's context, optionally adding any additional
374  * supplied parameters to the end of the arguments the function
375  * is executed with.
377  * @method rbind
378  * @param {Function|String} f the function to bind, or a function name
379  * to execute on the context object.
380  * @param {object} c the execution context.
381  * @param {any} args* 0..n arguments to append to the end of
382  * arguments collection supplied to the function.
383  * @return {function} the wrapped function.
384  */
385 Y.rbind = function(f, c) {
386     var xargs = arguments.length > 2 ? Y.Array(arguments, 2, true) : null;
387     return function() {
388         var fn = L.isString(f) ? c[f] : f,
389             args = (xargs) ?
390                 Y.Array(arguments, 0, true).concat(xargs) : arguments;
391         return fn.apply(c || fn, args);
392     };
396 }, '3.5.1' ,{requires:['yui-base']});