2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
8 YUI.add('oop', function(Y) {
11 * Supplies object inheritance and manipulation utilities. This adds
12 * additional functionaity to what is provided in yui-base, and the
13 * methods are applied directly to the YUI instance. This module
14 * is required for most YUI components.
19 * The following methods are added to the YUI instance
25 OP = Object.prototype,
26 CLONE_MARKER = '_~yuim~_',
30 dispatch = function(o, f, c, proto, action) {
31 if (o && o[action] && o !== Y) {
32 return o[action].call(o, f, c);
36 return A[action](o, f, c);
38 return A[action](Y.Array(o, 0, true), f, c);
40 return Y.Object[action](o, f, c, proto);
47 * Applies prototype properties from the supplier to the receiver.
48 * The receiver can be a constructor or an instance.
50 * @param {function} r the object to receive the augmentation.
51 * @param {function} s the object that supplies the properties to augment.
52 * @param {boolean} ov if true, properties already on the receiver
53 * will be overwritten if found on the supplier.
54 * @param {string[]} wl a whitelist. If supplied, only properties in
55 * this list will be applied to the receiver.
56 * @param {Array | Any} args arg or arguments to apply to the supplier
57 * constructor when initializing.
58 * @return {object} the augmented object.
60 * @todo constructor optional?
61 * @todo understanding what an instance is augmented with
62 * @todo best practices for overriding sequestered methods.
64 Y.augment = function(r, s, ov, wl, args) {
65 var sProto = s.prototype,
68 a = (args) ? Y.Array(args) : [],
71 applyConstructor = false,
72 sequestered, replacements;
74 // working on a class, so apply constructor infrastructure
75 if (rProto && construct) {
80 // sequester all of the functions in the supplier and replace with
81 // one that will restore all of them.
82 Y.Object.each(sProto, function(v, k) {
83 replacements[k] = function() {
85 // overwrite the prototype with all of the sequestered functions,
86 // but only if it hasn't been overridden
87 for (var i in sequestered) {
88 if (sequestered.hasOwnProperty(i) &&
89 (this[i] === replacements[i])) {
90 this[i] = sequestered[i];
94 // apply the constructor
95 construct.apply(this, a);
97 // apply the original sequestered function
98 return sequestered[k].apply(this, arguments);
101 if ((!wl || (k in wl)) && (ov || !(k in this))) {
102 if (L.isFunction(v)) {
103 // sequester the function
106 // replace the sequestered function with a function that will
107 // restore all sequestered functions and exectue the constructor.
108 this[k] = replacements[k];
116 // augmenting an instance, so apply the constructor immediately
118 applyConstructor = true;
121 Y.mix(target, newProto || sProto, ov, wl);
123 if (applyConstructor) {
131 * Applies object properties from the supplier to the receiver. If
132 * the target has the property, and the property is an object, the target
133 * object will be augmented with the supplier's value. If the property
134 * is an array, the suppliers value will be appended to the target.
136 * @param {function} r the object to receive the augmentation.
137 * @param {function} s the object that supplies the properties to augment.
138 * @param {boolean} ov if true, properties already on the receiver
139 * will be overwritten if found on the supplier.
140 * @param {string[]} wl a whitelist. If supplied, only properties in
141 * this list will be applied to the receiver.
142 * @return {object} the extended object.
144 Y.aggregate = function(r, s, ov, wl) {
145 return Y.mix(r, s, ov, wl, 0, true);
149 * Utility to set up the prototype, constructor and superclass properties to
150 * support an inheritance strategy that can chain constructors and methods.
151 * Static members will not be inherited.
154 * @param {function} r the object to modify.
155 * @param {function} s the object to inherit.
156 * @param {object} px prototype properties to add/override.
157 * @param {object} sx static properties to add/override.
158 * @return {object} the extended object.
160 Y.extend = function(r, s, px, sx) {
162 Y.error('extend failed, verify dependencies');
165 var sp = s.prototype, rp = Y.Object(sp);
171 // assign constructor property
172 if (s != Object && sp.constructor == OP.constructor) {
176 // add prototype overrides
181 // add object overrides
190 * Executes the supplied function for each item in
191 * a collection. Supports arrays, objects, and
194 * @param {object} o the object to iterate.
195 * @param {function} f the function to execute. This function
196 * receives the value, key, and object as parameters.
197 * @param {object} c the execution context for the function.
198 * @param {boolean} proto if true, prototype properties are
199 * iterated on objects.
200 * @return {YUI} the YUI instance.
202 Y.each = function(o, f, c, proto) {
203 return dispatch(o, f, c, proto, EACH);
207 * Executes the supplied function for each item in
208 * a collection. The operation stops if the function
209 * returns true. Supports arrays, objects, and
212 * @param {object} o the object to iterate.
213 * @param {function} f the function to execute. This function
214 * receives the value, key, and object as parameters.
215 * @param {object} c the execution context for the function.
216 * @param {boolean} proto if true, prototype properties are
217 * iterated on objects.
218 * @return {boolean} true if the function ever returns true,
221 Y.some = function(o, f, c, proto) {
222 return dispatch(o, f, c, proto, SOME);
226 * Deep obj/array copy. Function clones are actually
227 * wrappers around the original function.
228 * Array-like objects are treated as arrays.
229 * Primitives are returned untouched. Optionally, a
230 * function can be provided to handle other data types,
231 * filter keys, validate values, etc.
234 * @param {object} o what to clone.
235 * @param {boolean} safe if true, objects will not have prototype
236 * items from the source. If false, they will. In this case, the
237 * original is initially protected, but the clone is not completely
238 * immune from changes to the source object prototype. Also, cloned
239 * prototype items that are deleted from the clone will result
240 * in the value of the source prototype being exposed. If operating
241 * on a non-safe clone, items should be nulled out rather than deleted.
242 * @param {function} f optional function to apply to each item in a
243 * collection; it will be executed prior to applying the value to
244 * the new object. Return false to prevent the copy.
245 * @param {object} c optional execution context for f.
246 * @param {object} owner Owner object passed when clone is iterating
247 * an object. Used to set up context for cloned functions.
248 * @param {object} cloned hash of previously cloned objects to avoid
250 * @return {Array|Object} the cloned object.
252 Y.clone = function(o, safe, f, c, owner, cloned) {
254 if (!L.isObject(o)) {
258 // @todo cloning YUI instances doesn't currently work
259 if (Y.instanceOf(o, YUI)) {
263 var o2, marked = cloned || {}, stamp,
270 // if we do this we need to set the flags too
271 // return new RegExp(o.source);
274 // o2 = Y.bind(o, owner);
282 // #2528250 only one clone of a given object should be created.
283 if (o[CLONE_MARKER]) {
284 return marked[o[CLONE_MARKER]];
289 o2 = (safe) ? {} : Y.Object(o);
291 o[CLONE_MARKER] = stamp;
295 // #2528250 don't try to clone element properties
296 if (!o.addEventListener && !o.attachEvent) {
297 yeach(o, function(v, k) {
298 if ((k || k === 0) && (!f || (f.call(c || this, v, k, this, o) !== false))) {
299 if (k !== CLONE_MARKER) {
300 if (k == 'prototype') {
301 // skip the prototype
302 // } else if (o[k] === o) {
306 Y.clone(v, safe, f, c, owner || o, marked);
314 Y.Object.each(marked, function(v, k) {
315 if (v[CLONE_MARKER]) {
317 delete v[CLONE_MARKER];
319 v[CLONE_MARKER] = null;
331 * Returns a function that will execute the supplied function in the
332 * supplied object's context, optionally adding any additional
333 * supplied parameters to the beginning of the arguments collection the
334 * supplied to the function.
337 * @param {Function|String} f the function to bind, or a function name
338 * to execute on the context object.
339 * @param {object} c the execution context.
340 * @param {any} args* 0..n arguments to include before the arguments the
341 * function is executed with.
342 * @return {function} the wrapped function.
344 Y.bind = function(f, c) {
345 var xargs = arguments.length > 2 ?
346 Y.Array(arguments, 2, true) : null;
348 var fn = L.isString(f) ? c[f] : f,
350 xargs.concat(Y.Array(arguments, 0, true)) : arguments;
351 return fn.apply(c || fn, args);
356 * Returns a function that will execute the supplied function in the
357 * supplied object's context, optionally adding any additional
358 * supplied parameters to the end of the arguments the function
362 * @param {Function|String} f the function to bind, or a function name
363 * to execute on the context object.
364 * @param {object} c the execution context.
365 * @param {any} args* 0..n arguments to append to the end of
366 * arguments collection supplied to the function.
367 * @return {function} the wrapped function.
369 Y.rbind = function(f, c) {
370 var xargs = arguments.length > 2 ? Y.Array(arguments, 2, true) : null;
372 var fn = L.isString(f) ? c[f] : f,
374 Y.Array(arguments, 0, true).concat(xargs) : arguments;
375 return fn.apply(c || fn, args);