3 Copyright 2012 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
7 YUI.add('oop', function(Y) {
10 Adds object inheritance and manipulation utilities to the YUI instance. This
11 module is required by most YUI components.
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);
30 return A[action](o, f, c);
32 return A[action](Y.Array(o, 0, true), f, c);
34 return Y.Object[action](o, f, c, proto);
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
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.
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.
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,
87 args = args ? Y.Array(args) : [];
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);
103 newPrototype[key] = value;
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];
118 // Execute the supplier constructor.
119 supplier.apply(instance, args);
121 // Finally, execute the original sequestered function.
122 return fn.apply(instance, fnArgs);
126 Y.Array.each(whitelist, function (name) {
127 if (name in sProto) {
128 copy(sProto[name], name);
132 Y.Object.each(sProto, copy, null, true);
136 Y.mix(to, newPrototype || sProto, overwrite, whitelist);
139 supplier.apply(to, args);
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.
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.
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.
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.
176 Y.extend = function(r, s, px, sx) {
178 Y.error('extend failed, verify dependencies');
181 var sp = s.prototype, rp = Y.Object(sp);
187 // assign constructor property
188 if (s != Object && sp.constructor == OP.constructor) {
192 // add prototype overrides
197 // add object overrides
206 * Executes the supplied function for each item in
207 * a collection. Supports arrays, objects, and
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.
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
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,
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.
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
266 * @return {Array|Object} the cloned object.
268 Y.clone = function(o, safe, f, c, owner, cloned) {
270 if (!L.isObject(o)) {
274 // @todo cloning YUI instances doesn't currently work
275 if (Y.instanceOf(o, YUI)) {
279 var o2, marked = cloned || {}, stamp,
286 // if we do this we need to set the flags too
287 // return new RegExp(o.source);
290 // o2 = Y.bind(o, owner);
298 // #2528250 only one clone of a given object should be created.
299 if (o[CLONE_MARKER]) {
300 return marked[o[CLONE_MARKER]];
305 o2 = (safe) ? {} : Y.Object(o);
307 o[CLONE_MARKER] = stamp;
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) {
322 Y.clone(v, safe, f, c, owner || o, marked);
330 Y.Object.each(marked, function(v, k) {
331 if (v[CLONE_MARKER]) {
333 delete v[CLONE_MARKER];
335 v[CLONE_MARKER] = null;
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.
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.
360 Y.bind = function(f, c) {
361 var xargs = arguments.length > 2 ?
362 Y.Array(arguments, 2, true) : null;
364 var fn = L.isString(f) ? c[f] : f,
366 xargs.concat(Y.Array(arguments, 0, true)) : arguments;
367 return fn.apply(c || fn, args);
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
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.
385 Y.rbind = function(f, c) {
386 var xargs = arguments.length > 2 ? Y.Array(arguments, 2, true) : null;
388 var fn = L.isString(f) ? c[f] : f,
390 Y.Array(arguments, 0, true).concat(xargs) : arguments;
391 return fn.apply(c || fn, args);
396 }, '3.5.1' ,{requires:['yui-base']});