yui-core.js revision ace5e2555c33a0ceddef42649f6f38fa006707cc
// requires lang
YUI.add("core", function(Y) {
var L = Y.Lang,
A = Y.Array,
OP = Object.prototype,
IEF = ["toString", "valueOf"],
PROTO = 'prototype',
/**
* IE will not enumerate native functions in a derived object even if the
* function was overridden. This is a workaround for specific functions
* we care about on the Object prototype.
* @property _iefix
* @param {Function} r the object to receive the augmentation
* @param {Function} s the object that supplies the properties to augment
* @param w a whitelist object (the keys are the valid items to reference)
* @static
* @private
* @for YUI
*/
_iefix = (Y.UA && Y.UA.ie) ?
function(r, s, w) {
for (var i=0, a=IEF; i<a.length; i=i+1) {
var n = a[i], f = s[n];
if (L.isFunction(f) && f != OP[n]) {
if (!w || (n in w)) {
r[n]=f;
}
}
}
} : function() {};
/**
* Returns a new object containing all of the properties of
* all the supplied objects. The properties from later objects
* will overwrite those in earlier objects. Passing in a
* single object will create a shallow copy of it. For a deep
* copy, use clone.
* @method merge
* @param arguments {Object*} the objects to merge
* @return the new merged object
*/
Y.merge = function() {
// var o={}, a=arguments;
// for (var i=0, l=a.length; i<l; i=i+1) {
//var a=arguments, o=Y.Object(a[0]);
var a=arguments, o={};
for (var i=0, l=a.length; i<l; i=i+1) {
Y.mix(o, a[i], true);
}
return o;
};
/**
* Applies the supplier's properties to the receiver. By default
* all prototype and static propertes on the supplier are applied
* to the corresponding spot on the receiver. By default all
* properties are applied, and a property that is already on the
* reciever will not be overwritten. The default behavior can
* be modified by supplying the appropriate parameters.
*
* @TODO add constants for the modes
*
* @method mix
* @static
* @param {Function} r the object to receive the augmentation
* @param {Function} s the object that supplies the properties to augment
* @param ov {boolean} if true, properties already on the receiver
* will be overwritten if found on the supplier.
* @param wl {string[]} a whitelist. If supplied, only properties in
* this list will be applied to the receiver.
* @param {int} mode what should be copies, and to where
* default(0): object to object
* 1: prototype to prototype (old augment)
* 2: prototype to prototype and object props (new augment)
* 3: prototype to object
* 4: object to prototype
* @param merge {boolean} merge objects instead of overwriting/ignoring
* Used by Y.aggregate
* @return the augmented object
* @TODO review for PR2
*/
Y.mix = function(r, s, ov, wl, mode, merge) {
if (!s||!r) {
return Y;
}
var w = (wl && wl.length) ? A.hash(wl) : null, m = merge,
f = function(fr, fs, proto, iwl) {
var arr = m && L.isArray(fr);
for (var i in fs) {
// We never want to overwrite the prototype
// if (PROTO === i) {
if (PROTO === i || '_yuid' === i) {
continue;
}
// Y.log('i: ' + i + ", " + fs[i]);
// @TODO deal with the hasownprop issue
// check white list if it was supplied
if (!w || iwl || (i in w)) {
// if the receiver has this property, it is an object,
// and merge is specified, merge the two objects.
if (m && L.isObject(fr[i], true)) {
// console.log('aggregate RECURSE: ' + i);
// @TODO recursive or no?
// Y.mix(fr[i], fs[i]); // not recursive
f(fr[i], fs[i], proto, true); // recursive
// otherwise apply the property only if overwrite
// is specified or the receiver doesn't have one.
// @TODO make sure the 'arr' check isn't desructive
} else if (!arr && (ov || !(i in fr))) {
// console.log('hash: ' + i);
fr[i] = fs[i];
// if merge is specified and the receiver is an array,
// append the array item
} else if (arr) {
// console.log('array: ' + i);
// @TODO probably will need to remove dups
fr.push(fs[i]);
}
}
}
_iefix(fr, fs, w);
};
var rp = r.prototype, sp = s.prototype;
switch (mode) {
case 1: // proto to proto
f(rp, sp, true);
break;
case 2: // object to object and proto to proto
f(r, s);
f(rp, sp, true);
break;
case 3: // proto to static
f(r, sp, true);
break;
case 4: // static to proto
f(rp, s);
break;
default: // object to object
f(r, s);
}
return r;
};
/**
* Applies prototype properties from the supplier to the receiver.
* The receiver can be a constructor or an instance.
* @param {Function} r the object to receive the augmentation
* @param {Function} s the object that supplies the properties to augment
* @param ov {boolean} if true, properties already on the receiver
* will be overwritten if found on the supplier.
* @param wl {string[]} a whitelist. If supplied, only properties in
* this list will be applied to the receiver.
* @param args {Array | Any} arg or arguments to apply to the supplier
* constructor when initializing.
* @return the augmented object
*
* @todo constructor optional?
* @todo understanding what an instance is augmented with
* @TODO evaluate if we can do this in a way that doesn't interfere
* with normal inheritance
*/
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;
// working on a class, so apply constructor infrastructure
if (rProto && construct) {
// Y.log('augment will call constructor:');
// Y.Do.before(r, construct);
var sequestered = {}, replacements = {};
newProto = {};
// sequester all of the functions in the supplier and replace with
// one that will restore all of them.
Y.each(sProto, function(v, k) {
// var initialized = false;
replacements[k] = function() {
var me = this;
// Y.log('sequestered function "' + k + '" executed. Initializing Event.Target');
// overwrite the prototype with all of the sequestered functions,
// but only if it hasn't been overridden
for (var i in sequestered) {
if (Y.Object.owns(sequestered, i) && (me[i] === replacements[i])) {
// Y.log('... restoring ' + k);
me[i] = sequestered[i];
}
}
// apply the constructor
construct.apply(me, a);
// apply the original sequestered function
return sequestered[k].apply(me, arguments);
};
if ((!wl || (k in wl)) && (ov || !(k in this))) {
// Y.log('augment: ' + k);
if (Y.Lang.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 {
// Y.log('augment() applying non-function: ' + k);
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.
*
* @param {Function} r the object to receive the augmentation
* @param {Function} s the object that supplies the properties to augment
* @param ov {boolean} if true, properties already on the receiver
* will be overwritten if found on the supplier.
* @param wl {string[]} a whitelist. If supplied, only properties in
* this list will be applied to the receiver.
* @return 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
* @static
* @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 {YUI} the YUI instance
*/
Y.extend = function(r, s, px, sx) {
if (!s||!r) {
Y.fail("extend failed, verify dependencies");
}
var sp = s.prototype, rp=Y.Object(sp), i;
r.prototype=rp;
rp.constructor=r;
r.superclass=sp;
// If the superclass doesn't have a standard constructor,
// define one so that Super() works
if (s != Object && sp.constructor == OP.constructor) {
sp.constructor=s;
}
// Add object properties too
// @TODO removed for now because it isn't that useful and
// has caused a few issues overwriting things that should
// not be. You can do this manually if needed. Revisit
// if this is something that really is needed for some
// reason.
// Y.mix(r, s);
// Add superclass convienience functions
// @TODO revisit when we have something that works
// Y.augment(r, Ext);
// Add prototype overrides
if (px) {
Y.mix(rp, px, true);
}
// Add object overrides
if (sx) {
Y.mix(r, sx, true);
}
return r;
};
Y.each = function(o, f, c, proto) {
if (o.each && o.item) {
return o.each.call(o, f, c);
} else {
switch (A.test(o)) {
case 1:
return A.each(o, f, c);
case 2:
return A.each(Y.Array(o, 0, true), f, c);
default:
return Y.Object.each(o, f, c, proto);
}
}
// return Y.Object.each(o, f, c);
};
/**
* Deep obj/array copy. Functions will are cloned with Y.bind.
* 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 o what to clone
* @param safe {boolean} if true, objects will not have prototype
* items from the source. If false, it does. In this case, the
* original is initally 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 to be exposed. If operating on a non-safe
* clone, items should be nulled out rather than deleted.
* @TODO review
* @param 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 c optional execution context for f
* @param owner Owner object passed when clone is iterating an
* object. Used to set up context for cloned functions.
* @return {Array|Object} the cloned object
*/
Y.clone = function(o, safe, f, c, owner) {
if (!L.isObject(o)) {
return o;
}
if (L.isDate(o)) {
return new Date(o);
}
var func = L.isFunction(o), o2;
if (func) {
if (o instanceof RegExp) {
return new RegExp(o.source);
}
o2 = Y.bind(o, owner);
} else {
o2 = (safe) ? {} : Y.Object(o);
}
Y.each(o, function(v, k) {
if (!f || (f.call(c || this, v, k, this, o) !== false)) {
this[k] = Y.clone(v, safe, f, c, this);
}
}, o2);
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 end of the arguments the function
* is executed with.
* @TODO review param order for PR2
*
* @param f {Function} the function to bind
* @param c the execution context
* @param args* 0..n arguments to append to the arguments collection for the function
* @return the wrapped function
*/
Y.bind = function(f, c) {
// if (!f) {
// Y.log('no f');
// }
var a = Y.Array(arguments, 2, true);
return function () {
// @todo bind args first, or function args first?
// return f.apply(c || f, a.concat(Y.Array(arguments, 0, true)));
return f.apply(c || f, Y.Array(arguments, 0, true).concat(a));
};
};
Y.ready = function(f, c) {
var a = arguments, m = (a.length > 1) ? Y.bind.apply(Y, a) : f;
Y.on("yui:load", m);
return this;
};
// Overload specs: element/selector?/widget?
Y.get = function() {
return Y.Node.get.apply(Y.Node, arguments);
};
// DOM events and custom events
Y.on = function(type, f, o) {
if (type.indexOf(':') > -1) {
var cat = type.split(':');
switch (cat[0]) {
default:
return Y.subscribe.apply(Y, arguments);
}
} else {
return Y.Event.attach.apply(Y.Event, arguments);
}
};
Y.detach = function(type, f, o) {
if (Y.Lang.isObject(type) && type.detach) {
return type.detach();
} else if (type.indexOf(':') > -1) {
var cat = type.split(':');
switch (cat[0]) {
default:
return Y.unsubscribe.apply(Y, arguments);
}
} else {
return Y.Event.detach.apply(Y.Event, arguments);
}
};
/**
* Executes the callback before a DOM event, custom event
* or method. If the first argument is a function, it
* is assumed the target is a method.
*
* For DOM and custom events:
* type, callback, context, 1-n arguments
*
* For methods:
* callback, object (method host), methodName, context, 1-n arguments
*
* @method before
* @return unsubscribe handle
*/
Y.before = function(type, f, o) {
// method override
// callback, object, sMethod
if (Y.Lang.isFunction(type)) {
return Y.Do.before.apply(Y.Do, arguments);
}
return Y;
};
/**
* Executes the callback after a DOM event, custom event
* or method. If the first argument is a function, it
* is assumed the target is a method.
*
* @TODO add event
*
* For DOM and custom events:
* type, callback, context, 1-n arguments
*
* For methods:
* callback, object (method host), methodName, context, 1-n arguments
*
* @method after
* @return unsubscribe handle
*/
Y.after = function(type, f, o) {
if (Y.Lang.isFunction(type)) {
return Y.Do.after.apply(Y.Do, arguments);
}
return Y;
};
}, "@VERSION@");