|
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('base-base', function(Y) { |
|
9 |
|
10 /** |
|
11 * The base module provides the Base class, which objects requiring attribute and custom event support can extend. |
|
12 * The module also provides two ways to reuse code - An augmentable Plugin.Host interface which provides plugin support |
|
13 * (which is augmented to the Base class) and Base.build which provides a way to |
|
14 * build custom classes using extensions. |
|
15 * |
|
16 * @module base |
|
17 */ |
|
18 |
|
19 /** |
|
20 * The base-base submodule provides the Base class without the Plugin support, provided by Plugin.Host, |
|
21 * and without the extension support provided by Base.build. |
|
22 * |
|
23 * @module base |
|
24 * @submodule base-base |
|
25 */ |
|
26 var O = Y.Object, |
|
27 L = Y.Lang, |
|
28 DOT = ".", |
|
29 DESTROY = "destroy", |
|
30 INIT = "init", |
|
31 INITIALIZED = "initialized", |
|
32 DESTROYED = "destroyed", |
|
33 INITIALIZER = "initializer", |
|
34 OBJECT_CONSTRUCTOR = Object.prototype.constructor, |
|
35 DEEP = "deep", |
|
36 SHALLOW = "shallow", |
|
37 DESTRUCTOR = "destructor", |
|
38 |
|
39 Attribute = Y.Attribute; |
|
40 |
|
41 /** |
|
42 * <p> |
|
43 * A base class which objects requiring attributes and custom event support can |
|
44 * extend. Base also handles the chaining of initializer and destructor methods across |
|
45 * the hierarchy as part of object construction and destruction. Additionally, attributes configured |
|
46 * through the static <a href="#property_Base.ATTRS">ATTRS</a> property for each class |
|
47 * in the hierarchy will be initialized by Base. |
|
48 * </p> |
|
49 * |
|
50 * <p> |
|
51 * The static <a href="#property_Base.NAME">NAME</a> property of each class extending |
|
52 * from Base will be used as the identifier for the class, and is used by Base to prefix |
|
53 * all events fired by instances of that class. |
|
54 * </p> |
|
55 * @class Base |
|
56 * @constructor |
|
57 * @uses Attribute |
|
58 * @uses Plugin.Host |
|
59 * |
|
60 * @param {Object} config Object with configuration property name/value pairs |
|
61 */ |
|
62 function Base() { |
|
63 |
|
64 Attribute.call(this); |
|
65 |
|
66 // If Plugin.Host has been augmented [ through base-pluginhost ], setup it's |
|
67 // initial state, but don't initialize Plugins yet. That's done after initialization. |
|
68 var PluginHost = Y.Plugin && Y.Plugin.Host; |
|
69 if (this._initPlugins && PluginHost) { |
|
70 PluginHost.call(this); |
|
71 } |
|
72 |
|
73 if (this._lazyAddAttrs !== false) { this._lazyAddAttrs = true; } |
|
74 |
|
75 this.init.apply(this, arguments); |
|
76 } |
|
77 |
|
78 /** |
|
79 * The list of properties which can be configured for |
|
80 * each attribute (e.g. setter, getter, writeOnce, readOnly etc.) |
|
81 * |
|
82 * @property Base._ATTR_CFG |
|
83 * @type Array |
|
84 * @static |
|
85 * @private |
|
86 */ |
|
87 Base._ATTR_CFG = Attribute._ATTR_CFG.concat("cloneDefaultValue"); |
|
88 |
|
89 /** |
|
90 * <p> |
|
91 * The string to be used to identify instances of |
|
92 * this class, for example in prefixing events. |
|
93 * </p> |
|
94 * <p> |
|
95 * Classes extending Base, should define their own |
|
96 * static NAME property, which should be camelCase by |
|
97 * convention (e.g. MyClass.NAME = "myClass";). |
|
98 * </p> |
|
99 * @property Base.NAME |
|
100 * @type String |
|
101 * @static |
|
102 */ |
|
103 Base.NAME = "base"; |
|
104 |
|
105 /** |
|
106 * The default set of attributes which will be available for instances of this class, and |
|
107 * their configuration. In addition to the configuration properties listed by |
|
108 * Attribute's <a href="Attribute.html#method_addAttr">addAttr</a> method, the attribute |
|
109 * can also be configured with a "cloneDefaultValue" property, which defines how the statically |
|
110 * defined value field should be protected ("shallow", "deep" and false are supported values). |
|
111 * |
|
112 * By default if the value is an object literal or an array it will be "shallow" cloned, to |
|
113 * protect the default value. |
|
114 * |
|
115 * @property Base.ATTRS |
|
116 * @type Object |
|
117 * @static |
|
118 */ |
|
119 Base.ATTRS = { |
|
120 /** |
|
121 * Flag indicating whether or not this object |
|
122 * has been through the init lifecycle phase. |
|
123 * |
|
124 * @attribute initialized |
|
125 * @readonly |
|
126 * @default false |
|
127 * @type boolean |
|
128 */ |
|
129 initialized: { |
|
130 readOnly:true, |
|
131 value:false |
|
132 }, |
|
133 |
|
134 /** |
|
135 * Flag indicating whether or not this object |
|
136 * has been through the destroy lifecycle phase. |
|
137 * |
|
138 * @attribute destroyed |
|
139 * @readonly |
|
140 * @default false |
|
141 * @type boolean |
|
142 */ |
|
143 destroyed: { |
|
144 readOnly:true, |
|
145 value:false |
|
146 } |
|
147 }; |
|
148 |
|
149 Base.prototype = { |
|
150 |
|
151 /** |
|
152 * Init lifecycle method, invoked during construction. |
|
153 * Fires the init event prior to setting up attributes and |
|
154 * invoking initializers for the class hierarchy. |
|
155 * |
|
156 * @method init |
|
157 * @final |
|
158 * @chainable |
|
159 * @param {Object} config Object with configuration property name/value pairs |
|
160 * @return {Base} A reference to this object |
|
161 */ |
|
162 init: function(config) { |
|
163 |
|
164 /** |
|
165 * The string used to identify the class of this object. |
|
166 * |
|
167 * @deprecated Use this.constructor.NAME |
|
168 * @property name |
|
169 * @type String |
|
170 */ |
|
171 this._yuievt.config.prefix = this.name = this.constructor.NAME; |
|
172 |
|
173 /** |
|
174 * <p> |
|
175 * Lifecycle event for the init phase, fired prior to initialization. |
|
176 * Invoking the preventDefault() method on the event object provided |
|
177 * to subscribers will prevent initialization from occuring. |
|
178 * </p> |
|
179 * <p> |
|
180 * Subscribers to the "after" momemt of this event, will be notified |
|
181 * after initialization of the object is complete (and therefore |
|
182 * cannot prevent initialization). |
|
183 * </p> |
|
184 * |
|
185 * @event init |
|
186 * @preventable _defInitFn |
|
187 * @param {EventFacade} e Event object, with a cfg property which |
|
188 * refers to the configuration object passed to the constructor. |
|
189 */ |
|
190 this.publish(INIT, { |
|
191 queuable:false, |
|
192 defaultFn:this._defInitFn |
|
193 }); |
|
194 |
|
195 if (config) { |
|
196 if (config.on) { |
|
197 this.on(config.on); |
|
198 } |
|
199 if (config.after) { |
|
200 this.after(config.after); |
|
201 } |
|
202 } |
|
203 |
|
204 this.fire(INIT, {cfg: config}); |
|
205 |
|
206 return this; |
|
207 }, |
|
208 |
|
209 /** |
|
210 * <p> |
|
211 * Destroy lifecycle method. Fires the destroy |
|
212 * event, prior to invoking destructors for the |
|
213 * class hierarchy. |
|
214 * </p> |
|
215 * <p> |
|
216 * Subscribers to the destroy |
|
217 * event can invoke preventDefault on the event object, to prevent destruction |
|
218 * from proceeding. |
|
219 * </p> |
|
220 * @method destroy |
|
221 * @return {Base} A reference to this object |
|
222 * @final |
|
223 * @chainable |
|
224 */ |
|
225 destroy: function() { |
|
226 |
|
227 /** |
|
228 * <p> |
|
229 * Lifecycle event for the destroy phase, |
|
230 * fired prior to destruction. Invoking the preventDefault |
|
231 * method on the event object provided to subscribers will |
|
232 * prevent destruction from proceeding. |
|
233 * </p> |
|
234 * <p> |
|
235 * Subscribers to the "after" moment of this event, will be notified |
|
236 * after destruction is complete (and as a result cannot prevent |
|
237 * destruction). |
|
238 * </p> |
|
239 * @event destroy |
|
240 * @preventable _defDestroyFn |
|
241 * @param {EventFacade} e Event object |
|
242 */ |
|
243 this.publish(DESTROY, { |
|
244 queuable:false, |
|
245 defaultFn: this._defDestroyFn |
|
246 }); |
|
247 this.fire(DESTROY); |
|
248 return this; |
|
249 }, |
|
250 |
|
251 /** |
|
252 * Default init event handler |
|
253 * |
|
254 * @method _defInitFn |
|
255 * @param {EventFacade} e Event object, with a cfg property which |
|
256 * refers to the configuration object passed to the constructor. |
|
257 * @protected |
|
258 */ |
|
259 _defInitFn : function(e) { |
|
260 this._initHierarchy(e.cfg); |
|
261 if (this._initPlugins) { |
|
262 // Need to initPlugins manually, to handle constructor parsing, static Plug parsing |
|
263 this._initPlugins(e.cfg); |
|
264 } |
|
265 this._set(INITIALIZED, true); |
|
266 }, |
|
267 |
|
268 /** |
|
269 * Default destroy event handler |
|
270 * |
|
271 * @method _defDestroyFn |
|
272 * @param {EventFacade} e Event object |
|
273 * @protected |
|
274 */ |
|
275 _defDestroyFn : function(e) { |
|
276 this._destroyHierarchy(); |
|
277 if (this._destroyPlugins) { |
|
278 this._destroyPlugins(); |
|
279 } |
|
280 this._set(DESTROYED, true); |
|
281 }, |
|
282 |
|
283 /** |
|
284 * Returns the class hierarchy for this object, with Base being the last class in the array. |
|
285 * |
|
286 * @method _getClasses |
|
287 * @protected |
|
288 * @return {Function[]} An array of classes (constructor functions), making up the class hierarchy for this object. |
|
289 * This value is cached the first time the method, or _getAttrCfgs, is invoked. Subsequent invocations return the |
|
290 * cached value. |
|
291 */ |
|
292 _getClasses : function() { |
|
293 if (!this._classes) { |
|
294 this._initHierarchyData(); |
|
295 } |
|
296 return this._classes; |
|
297 }, |
|
298 |
|
299 /** |
|
300 * Returns an aggregated set of attribute configurations, by traversing the class hierarchy. |
|
301 * |
|
302 * @method _getAttrCfgs |
|
303 * @protected |
|
304 * @return {Object} The hash of attribute configurations, aggregated across classes in the hierarchy |
|
305 * This value is cached the first time the method, or _getClasses, is invoked. Subsequent invocations return |
|
306 * the cached value. |
|
307 */ |
|
308 _getAttrCfgs : function() { |
|
309 if (!this._attrs) { |
|
310 this._initHierarchyData(); |
|
311 } |
|
312 return this._attrs; |
|
313 }, |
|
314 |
|
315 /** |
|
316 * A helper method used when processing ATTRS across the class hierarchy during |
|
317 * initialization. Returns a disposable object with the attributes defined for |
|
318 * the provided class, extracted from the set of all attributes passed in . |
|
319 * |
|
320 * @method _filterAttrCfs |
|
321 * @private |
|
322 * |
|
323 * @param {Function} clazz The class for which the desired attributes are required. |
|
324 * @param {Object} allCfgs The set of all attribute configurations for this instance. |
|
325 * Attributes will be removed from this set, if they belong to the filtered class, so |
|
326 * that by the time all classes are processed, allCfgs will be empty. |
|
327 * |
|
328 * @return {Object} The set of attributes belonging to the class passed in, in the form |
|
329 * of an object with attribute name/configuration pairs. |
|
330 */ |
|
331 _filterAttrCfgs : function(clazz, allCfgs) { |
|
332 var cfgs = null, attr, attrs = clazz.ATTRS; |
|
333 |
|
334 if (attrs) { |
|
335 for (attr in attrs) { |
|
336 if (attrs.hasOwnProperty(attr) && allCfgs[attr]) { |
|
337 cfgs = cfgs || {}; |
|
338 cfgs[attr] = allCfgs[attr]; |
|
339 delete allCfgs[attr]; |
|
340 } |
|
341 } |
|
342 } |
|
343 |
|
344 return cfgs; |
|
345 }, |
|
346 |
|
347 /** |
|
348 * A helper method used by _getClasses and _getAttrCfgs, which determines both |
|
349 * the array of classes and aggregate set of attribute configurations |
|
350 * across the class hierarchy for the instance. |
|
351 * |
|
352 * @method _initHierarchyData |
|
353 * @private |
|
354 */ |
|
355 _initHierarchyData : function() { |
|
356 var c = this.constructor, |
|
357 classes = [], |
|
358 attrs = []; |
|
359 |
|
360 while (c) { |
|
361 // Add to classes |
|
362 classes[classes.length] = c; |
|
363 |
|
364 // Add to attributes |
|
365 if (c.ATTRS) { |
|
366 attrs[attrs.length] = c.ATTRS; |
|
367 } |
|
368 c = c.superclass ? c.superclass.constructor : null; |
|
369 } |
|
370 |
|
371 this._classes = classes; |
|
372 this._attrs = this._aggregateAttrs(attrs); |
|
373 }, |
|
374 |
|
375 /** |
|
376 * A helper method, used by _initHierarchyData to aggregate |
|
377 * attribute configuration across the instances class hierarchy. |
|
378 * |
|
379 * The method will potect the attribute configuration value to protect the statically defined |
|
380 * default value in ATTRS if required (if the value is an object literal, array or the |
|
381 * attribute configuration has cloneDefaultValue set to shallow or deep). |
|
382 * |
|
383 * @method _aggregateAttrs |
|
384 * @private |
|
385 * @param {Array} allAttrs An array of ATTRS definitions across classes in the hierarchy |
|
386 * (subclass first, Base last) |
|
387 * @return {Object} The aggregate set of ATTRS definitions for the instance |
|
388 */ |
|
389 _aggregateAttrs : function(allAttrs) { |
|
390 var attr, |
|
391 attrs, |
|
392 cfg, |
|
393 val, |
|
394 path, |
|
395 i, |
|
396 clone, |
|
397 cfgProps = Base._ATTR_CFG, |
|
398 aggAttrs = {}; |
|
399 |
|
400 if (allAttrs) { |
|
401 for (i = allAttrs.length-1; i >= 0; --i) { |
|
402 attrs = allAttrs[i]; |
|
403 |
|
404 for (attr in attrs) { |
|
405 if (attrs.hasOwnProperty(attr)) { |
|
406 |
|
407 // Protect config passed in |
|
408 cfg = Y.mix({}, attrs[attr], true, cfgProps); |
|
409 |
|
410 val = cfg.value; |
|
411 clone = cfg.cloneDefaultValue; |
|
412 |
|
413 if (val) { |
|
414 if ( (clone === undefined && (OBJECT_CONSTRUCTOR === val.constructor || L.isArray(val))) || clone === DEEP || clone === true) { |
|
415 cfg.value = Y.clone(val); |
|
416 } else if (clone === SHALLOW) { |
|
417 cfg.value = Y.merge(val); |
|
418 } |
|
419 // else if (clone === false), don't clone the static default value. |
|
420 // It's intended to be used by reference. |
|
421 } |
|
422 |
|
423 path = null; |
|
424 if (attr.indexOf(DOT) !== -1) { |
|
425 path = attr.split(DOT); |
|
426 attr = path.shift(); |
|
427 } |
|
428 |
|
429 if (path && aggAttrs[attr] && aggAttrs[attr].value) { |
|
430 O.setValue(aggAttrs[attr].value, path, val); |
|
431 } else if (!path){ |
|
432 if (!aggAttrs[attr]) { |
|
433 aggAttrs[attr] = cfg; |
|
434 } else { |
|
435 Y.mix(aggAttrs[attr], cfg, true, cfgProps); |
|
436 } |
|
437 } |
|
438 } |
|
439 } |
|
440 } |
|
441 } |
|
442 |
|
443 return aggAttrs; |
|
444 }, |
|
445 |
|
446 /** |
|
447 * Initializes the class hierarchy for the instance, which includes |
|
448 * initializing attributes for each class defined in the class's |
|
449 * static <a href="#property_Base.ATTRS">ATTRS</a> property and |
|
450 * invoking the initializer method on the prototype of each class in the hierarchy. |
|
451 * |
|
452 * @method _initHierarchy |
|
453 * @param {Object} userVals Object with configuration property name/value pairs |
|
454 * @private |
|
455 */ |
|
456 _initHierarchy : function(userVals) { |
|
457 var lazy = this._lazyAddAttrs, |
|
458 constr, |
|
459 constrProto, |
|
460 ci, |
|
461 ei, |
|
462 el, |
|
463 classes = this._getClasses(), |
|
464 attrCfgs = this._getAttrCfgs(); |
|
465 |
|
466 for (ci = classes.length-1; ci >= 0; ci--) { |
|
467 |
|
468 constr = classes[ci]; |
|
469 constrProto = constr.prototype; |
|
470 |
|
471 if (constr._yuibuild && constr._yuibuild.exts && !constr._yuibuild.dynamic) { |
|
472 for (ei = 0, el = constr._yuibuild.exts.length; ei < el; ei++) { |
|
473 constr._yuibuild.exts[ei].apply(this, arguments); |
|
474 } |
|
475 } |
|
476 |
|
477 this.addAttrs(this._filterAttrCfgs(constr, attrCfgs), userVals, lazy); |
|
478 |
|
479 if (constrProto.hasOwnProperty(INITIALIZER)) { |
|
480 constrProto.initializer.apply(this, arguments); |
|
481 } |
|
482 } |
|
483 }, |
|
484 |
|
485 /** |
|
486 * Destroys the class hierarchy for this instance by invoking |
|
487 * the descructor method on the prototype of each class in the hierarchy. |
|
488 * |
|
489 * @method _destroyHierarchy |
|
490 * @private |
|
491 */ |
|
492 _destroyHierarchy : function() { |
|
493 var constr, |
|
494 constrProto, |
|
495 ci, cl, |
|
496 classes = this._getClasses(); |
|
497 |
|
498 for (ci = 0, cl = classes.length; ci < cl; ci++) { |
|
499 constr = classes[ci]; |
|
500 constrProto = constr.prototype; |
|
501 if (constrProto.hasOwnProperty(DESTRUCTOR)) { |
|
502 constrProto.destructor.apply(this, arguments); |
|
503 } |
|
504 } |
|
505 }, |
|
506 |
|
507 /** |
|
508 * Default toString implementation. Provides the constructor NAME |
|
509 * and the instance ID. |
|
510 * |
|
511 * @method toString |
|
512 * @return {String} String representation for this object |
|
513 */ |
|
514 toString: function() { |
|
515 return this.constructor.NAME + "[" + Y.stamp(this) + "]"; |
|
516 } |
|
517 }; |
|
518 |
|
519 // Straightup augment, no wrapper functions |
|
520 Y.mix(Base, Attribute, false, null, 1); |
|
521 |
|
522 // Fix constructor |
|
523 Base.prototype.constructor = Base; |
|
524 |
|
525 Y.Base = Base; |
|
526 |
|
527 // Fix constructor |
|
528 Base.prototype.constructor = Base; |
|
529 |
|
530 |
|
531 }, '3.0.0' ,{requires:['attribute-base']}); |