|
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.0 |
|
6 build: 1549 |
|
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 CLONE_MARKER = "_~yuim~_"; |
|
22 |
|
23 // dispatch = function(o, f, c, proto, action) { |
|
24 // if (o[action] && o.item) { |
|
25 // return o[action].call(o, f, c); |
|
26 // } else { |
|
27 // switch (A.test(o)) { |
|
28 // case 1: |
|
29 // return A[action](o, f, c); |
|
30 // case 2: |
|
31 // return A[action](Y.Array(o, 0, true), f, c); |
|
32 // default: |
|
33 // return Y.Object[action](o, f, c, proto); |
|
34 // } |
|
35 // } |
|
36 // }; |
|
37 |
|
38 /** |
|
39 * The following methods are added to the YUI instance |
|
40 * @class YUI~oop |
|
41 */ |
|
42 |
|
43 /** |
|
44 * Applies prototype properties from the supplier to the receiver. |
|
45 * The receiver can be a constructor or an instance. |
|
46 * @method augment |
|
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 |
|
56 * |
|
57 * @todo constructor optional? |
|
58 * @todo understanding what an instance is augmented with |
|
59 * @TODO best practices for overriding sequestered methods. |
|
60 */ |
|
61 Y.augment = function(r, s, ov, wl, args) { |
|
62 var sProto = s.prototype, |
|
63 newProto = null, |
|
64 construct = s, |
|
65 a = (args) ? Y.Array(args) : [], |
|
66 rProto = r.prototype, |
|
67 target = rProto || r, |
|
68 applyConstructor = false, |
|
69 sequestered, replacements, i; |
|
70 |
|
71 // working on a class, so apply constructor infrastructure |
|
72 if (rProto && construct) { |
|
73 sequestered = {}; |
|
74 replacements = {}; |
|
75 newProto = {}; |
|
76 |
|
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() { |
|
81 |
|
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]; |
|
87 } |
|
88 } |
|
89 |
|
90 // apply the constructor |
|
91 construct.apply(this, a); |
|
92 |
|
93 // apply the original sequestered function |
|
94 return sequestered[k].apply(this, arguments); |
|
95 }; |
|
96 |
|
97 if ((!wl || (k in wl)) && (ov || !(k in this))) { |
|
98 if (L.isFunction(v)) { |
|
99 // sequester the function |
|
100 sequestered[k] = v; |
|
101 |
|
102 // replace the sequestered function with a function that will |
|
103 // restore all sequestered functions and exectue the constructor. |
|
104 this[k] = replacements[k]; |
|
105 } else { |
|
106 this[k] = v; |
|
107 } |
|
108 |
|
109 } |
|
110 |
|
111 }, newProto, true); |
|
112 |
|
113 // augmenting an instance, so apply the constructor immediately |
|
114 } else { |
|
115 applyConstructor = true; |
|
116 } |
|
117 |
|
118 Y.mix(target, newProto || sProto, ov, wl); |
|
119 |
|
120 if (applyConstructor) { |
|
121 s.apply(target, a); |
|
122 } |
|
123 |
|
124 return r; |
|
125 }; |
|
126 |
|
127 /** |
|
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. |
|
132 * @method aggregate |
|
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 |
|
140 */ |
|
141 Y.aggregate = function(r, s, ov, wl) { |
|
142 return Y.mix(r, s, ov, wl, 0, true); |
|
143 }; |
|
144 |
|
145 /** |
|
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. |
|
149 * |
|
150 * @method extend |
|
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 |
|
156 */ |
|
157 Y.extend = function(r, s, px, sx) { |
|
158 if (!s||!r) { |
|
159 // @TODO error symbols |
|
160 Y.error("extend failed, verify dependencies"); |
|
161 } |
|
162 |
|
163 var sp = s.prototype, rp=Y.Object(sp); |
|
164 r.prototype=rp; |
|
165 |
|
166 rp.constructor=r; |
|
167 r.superclass=sp; |
|
168 |
|
169 // assign constructor property |
|
170 if (s != Object && sp.constructor == OP.constructor) { |
|
171 sp.constructor=s; |
|
172 } |
|
173 |
|
174 // add prototype overrides |
|
175 if (px) { |
|
176 Y.mix(rp, px, true); |
|
177 } |
|
178 |
|
179 // add object overrides |
|
180 if (sx) { |
|
181 Y.mix(r, sx, true); |
|
182 } |
|
183 |
|
184 return r; |
|
185 }; |
|
186 |
|
187 /** |
|
188 * Executes the supplied function for each item in |
|
189 * a collection. Supports arrays, objects, and |
|
190 * Y.NodeLists |
|
191 * @method each |
|
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 |
|
198 */ |
|
199 Y.each = function(o, f, c, proto) { |
|
200 |
|
201 if (o.each && o.item) { |
|
202 return o.each.call(o, f, c); |
|
203 } else { |
|
204 switch (A.test(o)) { |
|
205 case 1: |
|
206 return A.each(o, f, c); |
|
207 case 2: |
|
208 return A.each(Y.Array(o, 0, true), f, c); |
|
209 default: |
|
210 return Y.Object.each(o, f, c, proto); |
|
211 } |
|
212 } |
|
213 |
|
214 // return Y.Object.each(o, f, c); |
|
215 }; |
|
216 |
|
217 // Y.each = function(o, f, c, proto) { |
|
218 // return dispatch(o, f, c, proto, 'each'); |
|
219 // }; |
|
220 |
|
221 /* |
|
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 |
|
225 * Y.NodeLists. |
|
226 * @method some |
|
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 |
|
233 */ |
|
234 // Y.some = function(o, f, c, proto) { |
|
235 // return dispatch(o, f, c, proto, 'some'); |
|
236 // }; |
|
237 |
|
238 /** |
|
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. |
|
244 * |
|
245 * @method clone |
|
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. |
|
254 * @TODO review |
|
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 |
|
262 */ |
|
263 Y.clone = function(o, safe, f, c, owner, cloned) { |
|
264 |
|
265 if (!L.isObject(o)) { |
|
266 return o; |
|
267 } |
|
268 |
|
269 var o2, marked = cloned || {}, stamp; |
|
270 |
|
271 switch (L.type(o)) { |
|
272 case 'date': |
|
273 return new Date(o); |
|
274 case 'regexp': |
|
275 return new RegExp(o.source); |
|
276 case 'function': |
|
277 o2 = Y.bind(o, owner); |
|
278 break; |
|
279 case 'array': |
|
280 o2 = []; |
|
281 break; |
|
282 default: |
|
283 |
|
284 // #2528250 only one clone of a given object should be created. |
|
285 if (o[CLONE_MARKER]) { |
|
286 return marked[o[CLONE_MARKER]]; |
|
287 } |
|
288 |
|
289 stamp = Y.guid(); |
|
290 |
|
291 o2 = (safe) ? {} : Y.Object(o); |
|
292 |
|
293 o[CLONE_MARKER] = stamp; |
|
294 marked[stamp] = o; |
|
295 } |
|
296 |
|
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); |
|
303 } |
|
304 } |
|
305 }, o2); |
|
306 } |
|
307 |
|
308 if (!cloned) { |
|
309 Y.each(marked, function(v, k) { |
|
310 delete v[CLONE_MARKER]; |
|
311 }); |
|
312 marked = null; |
|
313 } |
|
314 |
|
315 return o2; |
|
316 }; |
|
317 |
|
318 |
|
319 /** |
|
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. |
|
324 * |
|
325 * @method bind |
|
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 |
|
332 */ |
|
333 Y.bind = function(f, c) { |
|
334 var xargs = arguments.length > 2 ? Y.Array(arguments, 2, true) : null; |
|
335 return function () { |
|
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); |
|
339 }; |
|
340 }; |
|
341 |
|
342 /** |
|
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 |
|
346 * is executed with. |
|
347 * |
|
348 * @method rbind |
|
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 |
|
355 */ |
|
356 Y.rbind = function(f, c) { |
|
357 var xargs = arguments.length > 2 ? Y.Array(arguments, 2, true) : null; |
|
358 return function () { |
|
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); |
|
362 }; |
|
363 }; |
|
364 |
|
365 |
|
366 |
|
367 }, '3.0.0' ); |