/* Copyright (c) 2010, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.com/yui/license.html version: 3.3.0 build: 3167 */ YUI.add('oop', function(Y) { /** * Supplies object inheritance and manipulation utilities. This adds * additional functionaity to what is provided in yui-base, and the * methods are applied directly to the YUI instance. This module * is required for most YUI components. * @module oop */ /** * The following methods are added to the YUI instance * @class YUI~oop */ var L = Y.Lang, A = Y.Array, OP = Object.prototype, CLONE_MARKER = '_~yuim~_', EACH = 'each', SOME = 'some', dispatch = function(o, f, c, proto, action) { if (o && o[action] && o !== Y) { return o[action].call(o, f, c); } else { switch (A.test(o)) { case 1: return A[action](o, f, c); case 2: return A[action](Y.Array(o, 0, true), f, c); default: return Y.Object[action](o, f, c, proto); } } }; /** * Applies prototype properties from the supplier to the receiver. * The receiver can be a constructor or an instance. * @method augment * @param {function} r the object to receive the augmentation. * @param {function} s the object that supplies the properties to augment. * @param {boolean} ov if true, properties already on the receiver * will be overwritten if found on the supplier. * @param {string[]} wl a whitelist. If supplied, only properties in * this list will be applied to the receiver. * @param {Array | Any} args arg or arguments to apply to the supplier * constructor when initializing. * @return {object} the augmented object. * * @todo constructor optional? * @todo understanding what an instance is augmented with * @todo best practices for overriding sequestered methods. */ Y.augment = function(r, s, ov, wl, args) { var sProto = s.prototype, newProto = null, construct = s, a = (args) ? Y.Array(args) : [], rProto = r.prototype, target = rProto || r, applyConstructor = false, sequestered, replacements; // working on a class, so apply constructor infrastructure if (rProto && construct) { sequestered = {}; replacements = {}; newProto = {}; // sequester all of the functions in the supplier and replace with // one that will restore all of them. Y.Object.each(sProto, function(v, k) { replacements[k] = function() { // overwrite the prototype with all of the sequestered functions, // but only if it hasn't been overridden for (var i in sequestered) { if (sequestered.hasOwnProperty(i) && (this[i] === replacements[i])) { this[i] = sequestered[i]; } } // apply the constructor construct.apply(this, a); // apply the original sequestered function return sequestered[k].apply(this, arguments); }; if ((!wl || (k in wl)) && (ov || !(k in this))) { if (L.isFunction(v)) { // sequester the function sequestered[k] = v; // replace the sequestered function with a function that will // restore all sequestered functions and exectue the constructor. this[k] = replacements[k]; } else { this[k] = v; } } }, newProto, true); // augmenting an instance, so apply the constructor immediately } else { applyConstructor = true; } Y.mix(target, newProto || sProto, ov, wl); if (applyConstructor) { s.apply(target, a); } return r; }; /** * Applies object properties from the supplier to the receiver. If * the target has the property, and the property is an object, the target * object will be augmented with the supplier's value. If the property * is an array, the suppliers value will be appended to the target. * @method aggregate * @param {function} r the object to receive the augmentation. * @param {function} s the object that supplies the properties to augment. * @param {boolean} ov if true, properties already on the receiver * will be overwritten if found on the supplier. * @param {string[]} wl a whitelist. If supplied, only properties in * this list will be applied to the receiver. * @return {object} the extended object. */ Y.aggregate = function(r, s, ov, wl) { return Y.mix(r, s, ov, wl, 0, true); }; /** * Utility to set up the prototype, constructor and superclass properties to * support an inheritance strategy that can chain constructors and methods. * Static members will not be inherited. * * @method extend * @param {function} r the object to modify. * @param {function} s the object to inherit. * @param {object} px prototype properties to add/override. * @param {object} sx static properties to add/override. * @return {object} the extended object. */ Y.extend = function(r, s, px, sx) { if (!s || !r) { Y.error('extend failed, verify dependencies'); } var sp = s.prototype, rp = Y.Object(sp); r.prototype = rp; rp.constructor = r; r.superclass = sp; // assign constructor property if (s != Object && sp.constructor == OP.constructor) { sp.constructor = s; } // add prototype overrides if (px) { Y.mix(rp, px, true); } // add object overrides if (sx) { Y.mix(r, sx, true); } return r; }; /** * Executes the supplied function for each item in * a collection. Supports arrays, objects, and * Y.NodeLists * @method each * @param {object} o the object to iterate. * @param {function} f the function to execute. This function * receives the value, key, and object as parameters. * @param {object} c the execution context for the function. * @param {boolean} proto if true, prototype properties are * iterated on objects. * @return {YUI} the YUI instance. */ Y.each = function(o, f, c, proto) { return dispatch(o, f, c, proto, EACH); }; /** * Executes the supplied function for each item in * a collection. The operation stops if the function * returns true. Supports arrays, objects, and * Y.NodeLists. * @method some * @param {object} o the object to iterate. * @param {function} f the function to execute. This function * receives the value, key, and object as parameters. * @param {object} c the execution context for the function. * @param {boolean} proto if true, prototype properties are * iterated on objects. * @return {boolean} true if the function ever returns true, * false otherwise. */ Y.some = function(o, f, c, proto) { return dispatch(o, f, c, proto, SOME); }; /** * Deep obj/array copy. Function clones are actually * wrappers around the original function. * Array-like objects are treated as arrays. * Primitives are returned untouched. Optionally, a * function can be provided to handle other data types, * filter keys, validate values, etc. * * @method clone * @param {object} o what to clone. * @param {boolean} safe if true, objects will not have prototype * items from the source. If false, they will. In this case, the * original is initially protected, but the clone is not completely * immune from changes to the source object prototype. Also, cloned * prototype items that are deleted from the clone will result * in the value of the source prototype being exposed. If operating * on a non-safe clone, items should be nulled out rather than deleted. * @param {function} f optional function to apply to each item in a * collection; it will be executed prior to applying the value to * the new object. Return false to prevent the copy. * @param {object} c optional execution context for f. * @param {object} owner Owner object passed when clone is iterating * an object. Used to set up context for cloned functions. * @param {object} cloned hash of previously cloned objects to avoid * multiple clones. * @return {Array|Object} the cloned object. */ Y.clone = function(o, safe, f, c, owner, cloned) { if (!L.isObject(o)) { return o; } // @todo cloning YUI instances doesn't currently work if (Y.instanceOf(o, YUI)) { return o; } var o2, marked = cloned || {}, stamp, yeach = Y.each; switch (L.type(o)) { case 'date': return new Date(o); case 'regexp': // if we do this we need to set the flags too // return new RegExp(o.source); return o; case 'function': // o2 = Y.bind(o, owner); // break; return o; case 'array': o2 = []; break; default: // #2528250 only one clone of a given object should be created. if (o[CLONE_MARKER]) { return marked[o[CLONE_MARKER]]; } stamp = Y.guid(); o2 = (safe) ? {} : Y.Object(o); o[CLONE_MARKER] = stamp; marked[stamp] = o; } // #2528250 don't try to clone element properties if (!o.addEventListener && !o.attachEvent) { yeach(o, function(v, k) { if ((k || k === 0) && (!f || (f.call(c || this, v, k, this, o) !== false))) { if (k !== CLONE_MARKER) { if (k == 'prototype') { // skip the prototype // } else if (o[k] === o) { // this[k] = this; } else { this[k] = Y.clone(v, safe, f, c, owner || o, marked); } } } }, o2); } if (!cloned) { Y.Object.each(marked, function(v, k) { if (v[CLONE_MARKER]) { try { delete v[CLONE_MARKER]; } catch (e) { v[CLONE_MARKER] = null; } } }, this); marked = null; } return o2; }; /** * Returns a function that will execute the supplied function in the * supplied object's context, optionally adding any additional * supplied parameters to the beginning of the arguments collection the * supplied to the function. * * @method bind * @param {Function|String} f the function to bind, or a function name * to execute on the context object. * @param {object} c the execution context. * @param {any} args* 0..n arguments to include before the arguments the * function is executed with. * @return {function} the wrapped function. */ Y.bind = function(f, c) { var xargs = arguments.length > 2 ? Y.Array(arguments, 2, true) : null; return function() { var fn = L.isString(f) ? c[f] : f, args = (xargs) ? xargs.concat(Y.Array(arguments, 0, true)) : arguments; return fn.apply(c || fn, args); }; }; /** * Returns a function that will execute the supplied function in the * supplied object's context, optionally adding any additional * supplied parameters to the end of the arguments the function * is executed with. * * @method rbind * @param {Function|String} f the function to bind, or a function name * to execute on the context object. * @param {object} c the execution context. * @param {any} args* 0..n arguments to append to the end of * arguments collection supplied to the function. * @return {function} the wrapped function. */ Y.rbind = function(f, c) { var xargs = arguments.length > 2 ? Y.Array(arguments, 2, true) : null; return function() { var fn = L.isString(f) ? c[f] : f, args = (xargs) ? Y.Array(arguments, 0, true).concat(xargs) : arguments; return fn.apply(c || fn, args); }; }; }, '3.3.0' );