2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
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.
20 OP = Object.prototype,
21 CLONE_MARKER = "_~yuim~_";
23 // dispatch = function(o, f, c, proto, action) {
24 // if (o[action] && o.item) {
25 // return o[action].call(o, f, c);
27 // switch (A.test(o)) {
29 // return A[action](o, f, c);
31 // return A[action](Y.Array(o, 0, true), f, c);
33 // return Y.Object[action](o, f, c, proto);
39 * The following methods are added to the YUI instance
44 * Applies prototype properties from the supplier to the receiver.
45 * The receiver can be a constructor or an instance.
47 * @param {Function} r the object to receive the augmentation
48 * @param {Function} s the object that supplies the properties to augment
49 * @param ov {boolean} if true, properties already on the receiver
50 * will be overwritten if found on the supplier.
51 * @param wl {string[]} a whitelist. If supplied, only properties in
52 * this list will be applied to the receiver.
53 * @param args {Array | Any} arg or arguments to apply to the supplier
54 * constructor when initializing.
55 * @return {object} the augmented object
57 * @todo constructor optional?
58 * @todo understanding what an instance is augmented with
59 * @TODO best practices for overriding sequestered methods.
61 Y.augment = function(r, s, ov, wl, args) {
62 var sProto = s.prototype,
65 a = (args) ? Y.Array(args) : [],
68 applyConstructor = false,
69 sequestered, replacements, i;
71 // working on a class, so apply constructor infrastructure
72 if (rProto && construct) {
77 // sequester all of the functions in the supplier and replace with
78 // one that will restore all of them.
79 Y.each(sProto, function(v, k) {
80 replacements[k] = function() {
82 // overwrite the prototype with all of the sequestered functions,
83 // but only if it hasn't been overridden
84 for (i in sequestered) {
85 if (sequestered.hasOwnProperty(i) && (this[i] === replacements[i])) {
86 this[i] = sequestered[i];
90 // apply the constructor
91 construct.apply(this, a);
93 // apply the original sequestered function
94 return sequestered[k].apply(this, arguments);
97 if ((!wl || (k in wl)) && (ov || !(k in this))) {
98 if (L.isFunction(v)) {
99 // sequester the function
102 // replace the sequestered function with a function that will
103 // restore all sequestered functions and exectue the constructor.
104 this[k] = replacements[k];
113 // augmenting an instance, so apply the constructor immediately
115 applyConstructor = true;
118 Y.mix(target, newProto || sProto, ov, wl);
120 if (applyConstructor) {
128 * Applies object properties from the supplier to the receiver. If
129 * the target has the property, and the property is an object, the target
130 * object will be augmented with the supplier's value. If the property
131 * is an array, the suppliers value will be appended to the target.
133 * @param {Function} r the object to receive the augmentation
134 * @param {Function} s the object that supplies the properties to augment
135 * @param ov {boolean} if true, properties already on the receiver
136 * will be overwritten if found on the supplier.
137 * @param wl {string[]} a whitelist. If supplied, only properties in
138 * this list will be applied to the receiver.
139 * @return {object} the extended object
141 Y.aggregate = function(r, s, ov, wl) {
142 return Y.mix(r, s, ov, wl, 0, true);
146 * Utility to set up the prototype, constructor and superclass properties to
147 * support an inheritance strategy that can chain constructors and methods.
148 * Static members will not be inherited.
151 * @param {Function} r the object to modify
152 * @param {Function} s the object to inherit
153 * @param {Object} px prototype properties to add/override
154 * @param {Object} sx static properties to add/override
155 * @return {YUI} the YUI instance
157 Y.extend = function(r, s, px, sx) {
159 // @TODO error symbols
160 Y.error("extend failed, verify dependencies");
163 var sp = s.prototype, rp=Y.Object(sp);
169 // assign constructor property
170 if (s != Object && sp.constructor == OP.constructor) {
174 // add prototype overrides
179 // add object overrides
188 * Executes the supplied function for each item in
189 * a collection. Supports arrays, objects, and
192 * @param o the object to iterate
193 * @param f the function to execute. This function
194 * receives the value, key, and object as parameters
195 * @param proto if true, prototype properties are
196 * iterated on objects
197 * @return {YUI} the YUI instance
199 Y.each = function(o, f, c, proto) {
201 if (o.each && o.item) {
202 return o.each.call(o, f, c);
206 return A.each(o, f, c);
208 return A.each(Y.Array(o, 0, true), f, c);
210 return Y.Object.each(o, f, c, proto);
214 // return Y.Object.each(o, f, c);
217 // Y.each = function(o, f, c, proto) {
218 // return dispatch(o, f, c, proto, 'each');
222 * Executes the supplied function for each item in
223 * a collection. The operation stops if the function
224 * returns true. Supports arrays, objects, and
227 * @param o the object to iterate
228 * @param f the function to execute. This function
229 * receives the value, key, and object as parameters
230 * @param proto if true, prototype properties are
231 * iterated on objects
232 * @return {boolean} true if the function ever returns true, false otherwise
234 // Y.some = function(o, f, c, proto) {
235 // return dispatch(o, f, c, proto, 'some');
239 * Deep obj/array copy. Functions are cloned with Y.bind.
240 * Array-like objects are treated as arrays.
241 * Primitives are returned untouched. Optionally, a
242 * function can be provided to handle other data types,
243 * filter keys, validate values, etc.
246 * @param o what to clone
247 * @param safe {boolean} if true, objects will not have prototype
248 * items from the source. If false, they will. In this case, the
249 * original is initially protected, but the clone is not completely immune
250 * from changes to the source object prototype. Also, cloned prototype
251 * items that are deleted from the clone will result in the value
252 * of the source prototype being exposed. If operating on a non-safe
253 * clone, items should be nulled out rather than deleted.
255 * @param f optional function to apply to each item in a collection;
256 * it will be executed prior to applying the value to
257 * the new object. Return false to prevent the copy.
258 * @param c optional execution context for f
259 * @param owner Owner object passed when clone is iterating an
260 * object. Used to set up context for cloned functions.
261 * @return {Array|Object} the cloned object
263 Y.clone = function(o, safe, f, c, owner, cloned) {
265 if (!L.isObject(o)) {
269 var o2, marked = cloned || {}, stamp;
275 return new RegExp(o.source);
277 o2 = Y.bind(o, owner);
284 // #2528250 only one clone of a given object should be created.
285 if (o[CLONE_MARKER]) {
286 return marked[o[CLONE_MARKER]];
291 o2 = (safe) ? {} : Y.Object(o);
293 o[CLONE_MARKER] = stamp;
297 // #2528250 don't try to clone element properties
298 if (!o.addEventListener && !o.attachEvent) {
299 Y.each(o, function(v, k) {
300 if (!f || (f.call(c || this, v, k, this, o) !== false)) {
301 if (k !== CLONE_MARKER) {
302 this[k] = Y.clone(v, safe, f, c, owner || o, marked);
309 Y.each(marked, function(v, k) {
310 delete v[CLONE_MARKER];
320 * Returns a function that will execute the supplied function in the
321 * supplied object's context, optionally adding any additional
322 * supplied parameters to the beginning of the arguments collection the
323 * supplied to the function.
326 * @param f {Function|String} the function to bind, or a function name
327 * to execute on the context object
328 * @param c the execution context
329 * @param args* 0..n arguments to include before the arguments the
330 * function is executed with.
331 * @return {function} the wrapped function
333 Y.bind = function(f, c) {
334 var xargs = arguments.length > 2 ? Y.Array(arguments, 2, true) : null;
336 var fn = L.isString(f) ? c[f] : f,
337 args = (xargs) ? xargs.concat(Y.Array(arguments, 0, true)) : arguments;
338 return fn.apply(c || fn, args);
343 * Returns a function that will execute the supplied function in the
344 * supplied object's context, optionally adding any additional
345 * supplied parameters to the end of the arguments the function
349 * @param f {Function|String} the function to bind, or a function name
350 * to execute on the context object
351 * @param c the execution context
352 * @param args* 0..n arguments to append to the end of arguments collection
353 * supplied to the function
354 * @return {function} the wrapped function
356 Y.rbind = function(f, c) {
357 var xargs = arguments.length > 2 ? Y.Array(arguments, 2, true) : null;
359 var fn = L.isString(f) ? c[f] : f,
360 args = (xargs) ? Y.Array(arguments, 0, true).concat(xargs) : arguments;
361 return fn.apply(c || fn, args);