|
1 /* |
|
2 Copyright (c) 2009, Yahoo! Inc. All rights reserved. |
|
3 Code licensed under the BSD License: |
|
4 http://developer.yahoo.net/yui/license.txt |
|
5 version: 3.0.0b1 |
|
6 build: 1163 |
|
7 */ |
|
8 YUI.add('oop', function(Y) { |
|
9 |
|
10 /** |
|
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. |
|
15 * @module oop |
|
16 */ |
|
17 |
|
18 var L = Y.Lang, |
|
19 A = Y.Array, |
|
20 OP = Object.prototype; |
|
21 |
|
22 /** |
|
23 * The following methods are added to the YUI instance |
|
24 * @class YUI~oop |
|
25 */ |
|
26 |
|
27 /** |
|
28 * Applies prototype properties from the supplier to the receiver. |
|
29 * The receiver can be a constructor or an instance. |
|
30 * @method augment |
|
31 * @param {Function} r the object to receive the augmentation |
|
32 * @param {Function} s the object that supplies the properties to augment |
|
33 * @param ov {boolean} if true, properties already on the receiver |
|
34 * will be overwritten if found on the supplier. |
|
35 * @param wl {string[]} a whitelist. If supplied, only properties in |
|
36 * this list will be applied to the receiver. |
|
37 * @param args {Array | Any} arg or arguments to apply to the supplier |
|
38 * constructor when initializing. |
|
39 * @return {object} the augmented object |
|
40 * |
|
41 * @todo constructor optional? |
|
42 * @todo understanding what an instance is augmented with |
|
43 * @TODO best practices for overriding sequestered methods. |
|
44 */ |
|
45 Y.augment = function(r, s, ov, wl, args) { |
|
46 var sProto = s.prototype, |
|
47 newProto = null, |
|
48 construct = s, |
|
49 a = (args) ? Y.Array(args) : [], |
|
50 rProto = r.prototype, |
|
51 target = rProto || r, |
|
52 applyConstructor = false, |
|
53 sequestered, replacements, i; |
|
54 |
|
55 // working on a class, so apply constructor infrastructure |
|
56 if (rProto && construct) { |
|
57 sequestered = {}; |
|
58 replacements = {}; |
|
59 newProto = {}; |
|
60 |
|
61 // sequester all of the functions in the supplier and replace with |
|
62 // one that will restore all of them. |
|
63 Y.each(sProto, function(v, k) { |
|
64 replacements[k] = function() { |
|
65 |
|
66 // overwrite the prototype with all of the sequestered functions, |
|
67 // but only if it hasn't been overridden |
|
68 for (i in sequestered) { |
|
69 if (sequestered.hasOwnProperty(i) && (this[i] === replacements[i])) { |
|
70 this[i] = sequestered[i]; |
|
71 } |
|
72 } |
|
73 |
|
74 // apply the constructor |
|
75 construct.apply(this, a); |
|
76 |
|
77 // apply the original sequestered function |
|
78 return sequestered[k].apply(this, arguments); |
|
79 }; |
|
80 |
|
81 if ((!wl || (k in wl)) && (ov || !(k in this))) { |
|
82 if (L.isFunction(v)) { |
|
83 // sequester the function |
|
84 sequestered[k] = v; |
|
85 |
|
86 // replace the sequestered function with a function that will |
|
87 // restore all sequestered functions and exectue the constructor. |
|
88 this[k] = replacements[k]; |
|
89 } else { |
|
90 this[k] = v; |
|
91 } |
|
92 |
|
93 } |
|
94 |
|
95 }, newProto, true); |
|
96 |
|
97 // augmenting an instance, so apply the constructor immediately |
|
98 } else { |
|
99 applyConstructor = true; |
|
100 } |
|
101 |
|
102 Y.mix(target, newProto || sProto, ov, wl); |
|
103 |
|
104 if (applyConstructor) { |
|
105 s.apply(target, a); |
|
106 } |
|
107 |
|
108 return r; |
|
109 }; |
|
110 |
|
111 /** |
|
112 * Applies object properties from the supplier to the receiver. If |
|
113 * the target has the property, and the property is an object, the target |
|
114 * object will be augmented with the supplier's value. If the property |
|
115 * is an array, the suppliers value will be appended to the target. |
|
116 * @method aggregate |
|
117 * @param {Function} r the object to receive the augmentation |
|
118 * @param {Function} s the object that supplies the properties to augment |
|
119 * @param ov {boolean} if true, properties already on the receiver |
|
120 * will be overwritten if found on the supplier. |
|
121 * @param wl {string[]} a whitelist. If supplied, only properties in |
|
122 * this list will be applied to the receiver. |
|
123 * @return {object} the extended object |
|
124 */ |
|
125 Y.aggregate = function(r, s, ov, wl) { |
|
126 return Y.mix(r, s, ov, wl, 0, true); |
|
127 }; |
|
128 |
|
129 /** |
|
130 * Utility to set up the prototype, constructor and superclass properties to |
|
131 * support an inheritance strategy that can chain constructors and methods. |
|
132 * Static members will not be inherited. |
|
133 * |
|
134 * @method extend |
|
135 * @param {Function} r the object to modify |
|
136 * @param {Function} s the object to inherit |
|
137 * @param {Object} px prototype properties to add/override |
|
138 * @param {Object} sx static properties to add/override |
|
139 * @return {YUI} the YUI instance |
|
140 */ |
|
141 Y.extend = function(r, s, px, sx) { |
|
142 if (!s||!r) { |
|
143 // @TODO error symbols |
|
144 Y.error("extend failed, verify dependencies"); |
|
145 } |
|
146 |
|
147 var sp = s.prototype, rp=Y.Object(sp); |
|
148 r.prototype=rp; |
|
149 |
|
150 rp.constructor=r; |
|
151 r.superclass=sp; |
|
152 |
|
153 // assign constructor property |
|
154 if (s != Object && sp.constructor == OP.constructor) { |
|
155 sp.constructor=s; |
|
156 } |
|
157 |
|
158 // add prototype overrides |
|
159 if (px) { |
|
160 Y.mix(rp, px, true); |
|
161 } |
|
162 |
|
163 // add object overrides |
|
164 if (sx) { |
|
165 Y.mix(r, sx, true); |
|
166 } |
|
167 |
|
168 return r; |
|
169 }; |
|
170 |
|
171 /** |
|
172 * Executes the supplied function for each item in |
|
173 * a collection. Supports arrays, objects, and |
|
174 * Y.NodeLists |
|
175 * @method each |
|
176 * @param o the object to iterate |
|
177 * @param f the function to execute. This function |
|
178 * receives the value, key, and object as parameters |
|
179 * @param proto if true, prototype properties are |
|
180 * iterated on objects |
|
181 * @return {YUI} the YUI instance |
|
182 */ |
|
183 Y.each = function(o, f, c, proto) { |
|
184 |
|
185 if (o.each && o.item) { |
|
186 return o.each.call(o, f, c); |
|
187 } else { |
|
188 switch (A.test(o)) { |
|
189 case 1: |
|
190 return A.each(o, f, c); |
|
191 case 2: |
|
192 return A.each(Y.Array(o, 0, true), f, c); |
|
193 default: |
|
194 return Y.Object.each(o, f, c, proto); |
|
195 } |
|
196 } |
|
197 |
|
198 // return Y.Object.each(o, f, c); |
|
199 }; |
|
200 |
|
201 /** |
|
202 * Deep obj/array copy. Functions will are cloned with Y.bind. |
|
203 * Array-like objects are treated as arrays. |
|
204 * primitives are returned untouched. Optionally a |
|
205 * function can be provided to handle other data types, |
|
206 * filter keys, validate values, etc. |
|
207 * |
|
208 * @method clone |
|
209 * @param o what to clone |
|
210 * @param safe {boolean} if true, objects will not have prototype |
|
211 * items from the source. If false, it does. In this case, the |
|
212 * original is initally protected, but the clone is not completely immune |
|
213 * from changes to the source object prototype. Also, cloned prototype |
|
214 * items that are deleted from the clone will result in the value |
|
215 * of the source prototype to be exposed. If operating on a non-safe |
|
216 * clone, items should be nulled out rather than deleted. |
|
217 * @TODO review |
|
218 * @param f optional function to apply to each item in a collection |
|
219 * it will be executed prior to applying the value to |
|
220 * the new object. Return false to prevent the copy. |
|
221 * @param c optional execution context for f |
|
222 * @param owner Owner object passed when clone is iterating an |
|
223 * object. Used to set up context for cloned functions. |
|
224 * @return {Array|Object} the cloned object |
|
225 */ |
|
226 Y.clone = function(o, safe, f, c, owner) { |
|
227 |
|
228 if (!L.isObject(o)) { |
|
229 return o; |
|
230 } |
|
231 |
|
232 var o2; |
|
233 |
|
234 switch (L.type(o)) { |
|
235 case 'date': |
|
236 return new Date(o); |
|
237 case 'regexp': |
|
238 return new RegExp(o.source); |
|
239 case 'function': |
|
240 o2 = Y.bind(o, owner); |
|
241 break; |
|
242 case 'array': |
|
243 o2 = []; |
|
244 break; |
|
245 default: |
|
246 o2 = (safe) ? {} : Y.Object(o); |
|
247 } |
|
248 |
|
249 Y.each(o, function(v, k) { |
|
250 if (!f || (f.call(c || this, v, k, this, o) !== false)) { |
|
251 this[k] = Y.clone(v, safe, f, c, this); |
|
252 } |
|
253 }, o2); |
|
254 |
|
255 return o2; |
|
256 }; |
|
257 |
|
258 |
|
259 /** |
|
260 * Returns a function that will execute the supplied function in the |
|
261 * supplied object's context, optionally adding any additional |
|
262 * supplied parameters to the beginning of the arguments collection the |
|
263 * supplied to the function. |
|
264 * |
|
265 * @method bind |
|
266 * @param f {Function|String} the function to bind, or a function name |
|
267 * to execute on the context object |
|
268 * @param c the execution context |
|
269 * @param args* 0..n arguments to include before the arguments the |
|
270 * function is executed with. |
|
271 * @return {function} the wrapped function |
|
272 */ |
|
273 Y.bind = function(f, c) { |
|
274 var xargs = arguments.length > 2 ? Y.Array(arguments, 2, true) : null; |
|
275 return function () { |
|
276 var fn = L.isString(f) ? c[f] : f, |
|
277 args = (xargs) ? xargs.concat(Y.Array(arguments, 0, true)) : arguments; |
|
278 return fn.apply(c || fn, args); |
|
279 }; |
|
280 }; |
|
281 |
|
282 /** |
|
283 * Returns a function that will execute the supplied function in the |
|
284 * supplied object's context, optionally adding any additional |
|
285 * supplied parameters to the end of the arguments the function |
|
286 * is executed with. |
|
287 * |
|
288 * @method rbind |
|
289 * @param f {Function|String} the function to bind, or a function name |
|
290 * to execute on the context object |
|
291 * @param c the execution context |
|
292 * @param args* 0..n arguments to append to the end of arguments collection |
|
293 * supplied to the function |
|
294 * @return {function} the wrapped function |
|
295 */ |
|
296 Y.rbind = function(f, c) { |
|
297 var xargs = arguments.length > 2 ? Y.Array(arguments, 2, true) : null; |
|
298 return function () { |
|
299 var fn = L.isString(f) ? c[f] : f, |
|
300 args = (xargs) ? Y.Array(arguments, 0, true).concat(xargs) : arguments; |
|
301 return fn.apply(c || fn, args); |
|
302 }; |
|
303 }; |
|
304 |
|
305 |
|
306 |
|
307 }, '3.0.0b1' ); |