|
1 /* |
|
2 YUI 3.10.3 (build 2fb5187) |
|
3 Copyright 2013 Yahoo! Inc. All rights reserved. |
|
4 Licensed under the BSD License. |
|
5 http://yuilibrary.com/license/ |
|
6 */ |
|
7 |
|
8 YUI.add('oop', function (Y, NAME) { |
|
9 |
|
10 /** |
|
11 Adds object inheritance and manipulation utilities to the YUI instance. This |
|
12 module is required by most YUI components. |
|
13 |
|
14 @module oop |
|
15 **/ |
|
16 |
|
17 var L = Y.Lang, |
|
18 A = Y.Array, |
|
19 OP = Object.prototype, |
|
20 CLONE_MARKER = '_~yuim~_', |
|
21 |
|
22 hasOwn = OP.hasOwnProperty, |
|
23 toString = OP.toString; |
|
24 |
|
25 function dispatch(o, f, c, proto, action) { |
|
26 if (o && o[action] && o !== Y) { |
|
27 return o[action].call(o, f, c); |
|
28 } else { |
|
29 switch (A.test(o)) { |
|
30 case 1: |
|
31 return A[action](o, f, c); |
|
32 case 2: |
|
33 return A[action](Y.Array(o, 0, true), f, c); |
|
34 default: |
|
35 return Y.Object[action](o, f, c, proto); |
|
36 } |
|
37 } |
|
38 } |
|
39 |
|
40 /** |
|
41 Augments the _receiver_ with prototype properties from the _supplier_. The |
|
42 receiver may be a constructor function or an object. The supplier must be a |
|
43 constructor function. |
|
44 |
|
45 If the _receiver_ is an object, then the _supplier_ constructor will be called |
|
46 immediately after _receiver_ is augmented, with _receiver_ as the `this` object. |
|
47 |
|
48 If the _receiver_ is a constructor function, then all prototype methods of |
|
49 _supplier_ that are copied to _receiver_ will be sequestered, and the |
|
50 _supplier_ constructor will not be called immediately. The first time any |
|
51 sequestered method is called on the _receiver_'s prototype, all sequestered |
|
52 methods will be immediately copied to the _receiver_'s prototype, the |
|
53 _supplier_'s constructor will be executed, and finally the newly unsequestered |
|
54 method that was called will be executed. |
|
55 |
|
56 This sequestering logic sounds like a bunch of complicated voodoo, but it makes |
|
57 it cheap to perform frequent augmentation by ensuring that suppliers' |
|
58 constructors are only called if a supplied method is actually used. If none of |
|
59 the supplied methods is ever used, then there's no need to take the performance |
|
60 hit of calling the _supplier_'s constructor. |
|
61 |
|
62 @method augment |
|
63 @param {Function|Object} receiver Object or function to be augmented. |
|
64 @param {Function} supplier Function that supplies the prototype properties with |
|
65 which to augment the _receiver_. |
|
66 @param {Boolean} [overwrite=false] If `true`, properties already on the receiver |
|
67 will be overwritten if found on the supplier's prototype. |
|
68 @param {String[]} [whitelist] An array of property names. If specified, |
|
69 only the whitelisted prototype properties will be applied to the receiver, and |
|
70 all others will be ignored. |
|
71 @param {Array|any} [args] Argument or array of arguments to pass to the |
|
72 supplier's constructor when initializing. |
|
73 @return {Function} Augmented object. |
|
74 @for YUI |
|
75 **/ |
|
76 Y.augment = function (receiver, supplier, overwrite, whitelist, args) { |
|
77 var rProto = receiver.prototype, |
|
78 sequester = rProto && supplier, |
|
79 sProto = supplier.prototype, |
|
80 to = rProto || receiver, |
|
81 |
|
82 copy, |
|
83 newPrototype, |
|
84 replacements, |
|
85 sequestered, |
|
86 unsequester; |
|
87 |
|
88 args = args ? Y.Array(args) : []; |
|
89 |
|
90 if (sequester) { |
|
91 newPrototype = {}; |
|
92 replacements = {}; |
|
93 sequestered = {}; |
|
94 |
|
95 copy = function (value, key) { |
|
96 if (overwrite || !(key in rProto)) { |
|
97 if (toString.call(value) === '[object Function]') { |
|
98 sequestered[key] = value; |
|
99 |
|
100 newPrototype[key] = replacements[key] = function () { |
|
101 return unsequester(this, value, arguments); |
|
102 }; |
|
103 } else { |
|
104 newPrototype[key] = value; |
|
105 } |
|
106 } |
|
107 }; |
|
108 |
|
109 unsequester = function (instance, fn, fnArgs) { |
|
110 // Unsequester all sequestered functions. |
|
111 for (var key in sequestered) { |
|
112 if (hasOwn.call(sequestered, key) |
|
113 && instance[key] === replacements[key]) { |
|
114 |
|
115 instance[key] = sequestered[key]; |
|
116 } |
|
117 } |
|
118 |
|
119 // Execute the supplier constructor. |
|
120 supplier.apply(instance, args); |
|
121 |
|
122 // Finally, execute the original sequestered function. |
|
123 return fn.apply(instance, fnArgs); |
|
124 }; |
|
125 |
|
126 if (whitelist) { |
|
127 Y.Array.each(whitelist, function (name) { |
|
128 if (name in sProto) { |
|
129 copy(sProto[name], name); |
|
130 } |
|
131 }); |
|
132 } else { |
|
133 Y.Object.each(sProto, copy, null, true); |
|
134 } |
|
135 } |
|
136 |
|
137 Y.mix(to, newPrototype || sProto, overwrite, whitelist); |
|
138 |
|
139 if (!sequester) { |
|
140 supplier.apply(to, args); |
|
141 } |
|
142 |
|
143 return receiver; |
|
144 }; |
|
145 |
|
146 /** |
|
147 * Copies object properties from the supplier to the receiver. If the target has |
|
148 * the property, and the property is an object, the target object will be |
|
149 * augmented with the supplier's value. |
|
150 * |
|
151 * @method aggregate |
|
152 * @param {Object} receiver Object to receive the augmentation. |
|
153 * @param {Object} supplier Object that supplies the properties with which to |
|
154 * augment the receiver. |
|
155 * @param {Boolean} [overwrite=false] If `true`, properties already on the receiver |
|
156 * will be overwritten if found on the supplier. |
|
157 * @param {String[]} [whitelist] Whitelist. If supplied, only properties in this |
|
158 * list will be applied to the receiver. |
|
159 * @return {Object} Augmented object. |
|
160 */ |
|
161 Y.aggregate = function(r, s, ov, wl) { |
|
162 return Y.mix(r, s, ov, wl, 0, true); |
|
163 }; |
|
164 |
|
165 /** |
|
166 * Utility to set up the prototype, constructor and superclass properties to |
|
167 * support an inheritance strategy that can chain constructors and methods. |
|
168 * Static members will not be inherited. |
|
169 * |
|
170 * @method extend |
|
171 * @param {function} r the object to modify. |
|
172 * @param {function} s the object to inherit. |
|
173 * @param {object} px prototype properties to add/override. |
|
174 * @param {object} sx static properties to add/override. |
|
175 * @return {object} the extended object. |
|
176 */ |
|
177 Y.extend = function(r, s, px, sx) { |
|
178 if (!s || !r) { |
|
179 Y.error('extend failed, verify dependencies'); |
|
180 } |
|
181 |
|
182 var sp = s.prototype, rp = Y.Object(sp); |
|
183 r.prototype = rp; |
|
184 |
|
185 rp.constructor = r; |
|
186 r.superclass = sp; |
|
187 |
|
188 // assign constructor property |
|
189 if (s != Object && sp.constructor == OP.constructor) { |
|
190 sp.constructor = s; |
|
191 } |
|
192 |
|
193 // add prototype overrides |
|
194 if (px) { |
|
195 Y.mix(rp, px, true); |
|
196 } |
|
197 |
|
198 // add object overrides |
|
199 if (sx) { |
|
200 Y.mix(r, sx, true); |
|
201 } |
|
202 |
|
203 return r; |
|
204 }; |
|
205 |
|
206 /** |
|
207 * Executes the supplied function for each item in |
|
208 * a collection. Supports arrays, objects, and |
|
209 * NodeLists |
|
210 * @method each |
|
211 * @param {object} o the object to iterate. |
|
212 * @param {function} f the function to execute. This function |
|
213 * receives the value, key, and object as parameters. |
|
214 * @param {object} c the execution context for the function. |
|
215 * @param {boolean} proto if true, prototype properties are |
|
216 * iterated on objects. |
|
217 * @return {YUI} the YUI instance. |
|
218 */ |
|
219 Y.each = function(o, f, c, proto) { |
|
220 return dispatch(o, f, c, proto, 'each'); |
|
221 }; |
|
222 |
|
223 /** |
|
224 * Executes the supplied function for each item in |
|
225 * a collection. The operation stops if the function |
|
226 * returns true. Supports arrays, objects, and |
|
227 * NodeLists. |
|
228 * @method some |
|
229 * @param {object} o the object to iterate. |
|
230 * @param {function} f the function to execute. This function |
|
231 * receives the value, key, and object as parameters. |
|
232 * @param {object} c the execution context for the function. |
|
233 * @param {boolean} proto if true, prototype properties are |
|
234 * iterated on objects. |
|
235 * @return {boolean} true if the function ever returns true, |
|
236 * false otherwise. |
|
237 */ |
|
238 Y.some = function(o, f, c, proto) { |
|
239 return dispatch(o, f, c, proto, 'some'); |
|
240 }; |
|
241 |
|
242 /** |
|
243 Deep object/array copy. Function clones are actually wrappers around the |
|
244 original function. Array-like objects are treated as arrays. Primitives are |
|
245 returned untouched. Optionally, a function can be provided to handle other data |
|
246 types, filter keys, validate values, etc. |
|
247 |
|
248 **Note:** Cloning a non-trivial object is a reasonably heavy operation, due to |
|
249 the need to recursively iterate down non-primitive properties. Clone should be |
|
250 used only when a deep clone down to leaf level properties is explicitly |
|
251 required. This method will also |
|
252 |
|
253 In many cases (for example, when trying to isolate objects used as hashes for |
|
254 configuration properties), a shallow copy, using `Y.merge()` is normally |
|
255 sufficient. If more than one level of isolation is required, `Y.merge()` can be |
|
256 used selectively at each level which needs to be isolated from the original |
|
257 without going all the way to leaf properties. |
|
258 |
|
259 @method clone |
|
260 @param {object} o what to clone. |
|
261 @param {boolean} safe if true, objects will not have prototype items from the |
|
262 source. If false, they will. In this case, the original is initially |
|
263 protected, but the clone is not completely immune from changes to the source |
|
264 object prototype. Also, cloned prototype items that are deleted from the |
|
265 clone will result in the value of the source prototype being exposed. If |
|
266 operating on a non-safe clone, items should be nulled out rather than |
|
267 deleted. |
|
268 @param {function} f optional function to apply to each item in a collection; it |
|
269 will be executed prior to applying the value to the new object. |
|
270 Return false to prevent the copy. |
|
271 @param {object} c optional execution context for f. |
|
272 @param {object} owner Owner object passed when clone is iterating an object. |
|
273 Used to set up context for cloned functions. |
|
274 @param {object} cloned hash of previously cloned objects to avoid multiple |
|
275 clones. |
|
276 @return {Array|Object} the cloned object. |
|
277 **/ |
|
278 Y.clone = function(o, safe, f, c, owner, cloned) { |
|
279 var o2, marked, stamp; |
|
280 |
|
281 // Does not attempt to clone: |
|
282 // |
|
283 // * Non-typeof-object values, "primitive" values don't need cloning. |
|
284 // |
|
285 // * YUI instances, cloning complex object like YUI instances is not |
|
286 // advised, this is like cloning the world. |
|
287 // |
|
288 // * DOM nodes (#2528250), common host objects like DOM nodes cannot be |
|
289 // "subclassed" in Firefox and old versions of IE. Trying to use |
|
290 // `Object.create()` or `Y.extend()` on a DOM node will throw an error in |
|
291 // these browsers. |
|
292 // |
|
293 // Instad, the passed-in `o` will be return as-is when it matches one of the |
|
294 // above criteria. |
|
295 if (!L.isObject(o) || |
|
296 Y.instanceOf(o, YUI) || |
|
297 (o.addEventListener || o.attachEvent)) { |
|
298 |
|
299 return o; |
|
300 } |
|
301 |
|
302 marked = cloned || {}; |
|
303 |
|
304 switch (L.type(o)) { |
|
305 case 'date': |
|
306 return new Date(o); |
|
307 case 'regexp': |
|
308 // if we do this we need to set the flags too |
|
309 // return new RegExp(o.source); |
|
310 return o; |
|
311 case 'function': |
|
312 // o2 = Y.bind(o, owner); |
|
313 // break; |
|
314 return o; |
|
315 case 'array': |
|
316 o2 = []; |
|
317 break; |
|
318 default: |
|
319 |
|
320 // #2528250 only one clone of a given object should be created. |
|
321 if (o[CLONE_MARKER]) { |
|
322 return marked[o[CLONE_MARKER]]; |
|
323 } |
|
324 |
|
325 stamp = Y.guid(); |
|
326 |
|
327 o2 = (safe) ? {} : Y.Object(o); |
|
328 |
|
329 o[CLONE_MARKER] = stamp; |
|
330 marked[stamp] = o; |
|
331 } |
|
332 |
|
333 Y.each(o, function(v, k) { |
|
334 if ((k || k === 0) && (!f || (f.call(c || this, v, k, this, o) !== false))) { |
|
335 if (k !== CLONE_MARKER) { |
|
336 if (k == 'prototype') { |
|
337 // skip the prototype |
|
338 // } else if (o[k] === o) { |
|
339 // this[k] = this; |
|
340 } else { |
|
341 this[k] = |
|
342 Y.clone(v, safe, f, c, owner || o, marked); |
|
343 } |
|
344 } |
|
345 } |
|
346 }, o2); |
|
347 |
|
348 if (!cloned) { |
|
349 Y.Object.each(marked, function(v, k) { |
|
350 if (v[CLONE_MARKER]) { |
|
351 try { |
|
352 delete v[CLONE_MARKER]; |
|
353 } catch (e) { |
|
354 v[CLONE_MARKER] = null; |
|
355 } |
|
356 } |
|
357 }, this); |
|
358 marked = null; |
|
359 } |
|
360 |
|
361 return o2; |
|
362 }; |
|
363 |
|
364 /** |
|
365 * Returns a function that will execute the supplied function in the |
|
366 * supplied object's context, optionally adding any additional |
|
367 * supplied parameters to the beginning of the arguments collection the |
|
368 * supplied to the function. |
|
369 * |
|
370 * @method bind |
|
371 * @param {Function|String} f the function to bind, or a function name |
|
372 * to execute on the context object. |
|
373 * @param {object} c the execution context. |
|
374 * @param {any} args* 0..n arguments to include before the arguments the |
|
375 * function is executed with. |
|
376 * @return {function} the wrapped function. |
|
377 */ |
|
378 Y.bind = function(f, c) { |
|
379 var xargs = arguments.length > 2 ? |
|
380 Y.Array(arguments, 2, true) : null; |
|
381 return function() { |
|
382 var fn = L.isString(f) ? c[f] : f, |
|
383 args = (xargs) ? |
|
384 xargs.concat(Y.Array(arguments, 0, true)) : arguments; |
|
385 return fn.apply(c || fn, args); |
|
386 }; |
|
387 }; |
|
388 |
|
389 /** |
|
390 * Returns a function that will execute the supplied function in the |
|
391 * supplied object's context, optionally adding any additional |
|
392 * supplied parameters to the end of the arguments the function |
|
393 * is executed with. |
|
394 * |
|
395 * @method rbind |
|
396 * @param {Function|String} f the function to bind, or a function name |
|
397 * to execute on the context object. |
|
398 * @param {object} c the execution context. |
|
399 * @param {any} args* 0..n arguments to append to the end of |
|
400 * arguments collection supplied to the function. |
|
401 * @return {function} the wrapped function. |
|
402 */ |
|
403 Y.rbind = function(f, c) { |
|
404 var xargs = arguments.length > 2 ? Y.Array(arguments, 2, true) : null; |
|
405 return function() { |
|
406 var fn = L.isString(f) ? c[f] : f, |
|
407 args = (xargs) ? |
|
408 Y.Array(arguments, 0, true).concat(xargs) : arguments; |
|
409 return fn.apply(c || fn, args); |
|
410 }; |
|
411 }; |
|
412 |
|
413 |
|
414 }, '3.10.3', {"requires": ["yui-base"]}); |