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/
8
YUI.add('base-core', function (Y, NAME) {
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 - It augments Base with the Plugin.Host interface which provides
13
* plugin support and also provides the BaseCore.build method which provides a way to build custom classes using extensions.
19
* <p>The base-core module provides the BaseCore class, the lightest version of Base,
20
* which provides Base's basic lifecycle management and ATTRS construction support,
21
* but doesn't fire init/destroy or attribute change events.</p>
23
* <p>It mixes in AttributeCore, which is the lightest version of Attribute</p>
26
* @submodule base-core
31
INITIALIZED = "initialized",
32
DESTROYED = "destroyed",
33
INITIALIZER = "initializer",
35
OBJECT_CONSTRUCTOR = Object.prototype.constructor,
38
DESTRUCTOR = "destructor",
40
AttributeCore = Y.AttributeCore,
42
_wlmix = function(r, s, wlhash) {
53
* The BaseCore class, is the lightest version of Base, and provides Base's
54
* basic lifecycle management and ATTRS construction support, but doesn't
55
* fire init/destroy or attribute change events.
57
* BaseCore also handles the chaining of initializer and destructor methods across
58
* the hierarchy as part of object construction and destruction. Additionally, attributes
59
* configured through the static <a href="#property_BaseCore.ATTRS">ATTRS</a>
60
* property for each class in the hierarchy will be initialized by BaseCore.
62
* Classes which require attribute support, but don't intend to use/expose attribute
63
* change events can extend BaseCore instead of Base for optimal kweight and
64
* runtime performance.
69
* @param {Object} cfg Object with configuration property name/value pairs.
70
* The object can be used to provide initial values for the objects published
73
function BaseCore(cfg) {
74
if (!this._BaseInvoked) {
75
this._BaseInvoked = true;
82
* The list of properties which can be configured for each attribute
83
* (e.g. setter, getter, writeOnce, readOnly etc.)
90
BaseCore._ATTR_CFG = AttributeCore._ATTR_CFG.concat("cloneDefaultValue");
93
* The array of non-attribute configuration properties supported by this class.
95
* For example `BaseCore` defines a "plugins" configuration property which
96
* should not be set up as an attribute. This property is primarily required so
97
* that when <a href="#property__allowAdHocAttrs">`_allowAdHocAttrs`</a> is enabled by a class,
98
* non-attribute configuration properties don't get added as ad-hoc attributes.
100
* @property _NON_ATTRS_CFG
105
BaseCore._NON_ATTRS_CFG = ["plugins"];
108
* This property controls whether or not instances of this class should
109
* allow users to add ad-hoc attributes through the constructor configuration
112
* AdHoc attributes are attributes which are not defined by the class, and are
113
* not handled by the MyClass._NON_ATTRS_CFG
115
* @property _allowAdHocAttrs
117
* @default undefined (false)
122
* The string to be used to identify instances of this class.
124
* Classes extending BaseCore, should define their own
125
* static NAME property, which should be camelCase by
126
* convention (e.g. MyClass.NAME = "myClass";).
132
BaseCore.NAME = "baseCore";
135
* The default set of attributes which will be available for instances of this class, and
136
* their configuration. In addition to the configuration properties listed by
137
* AttributeCore's <a href="AttributeCore.html#method_addAttr">addAttr</a> method,
138
* the attribute can also be configured with a "cloneDefaultValue" property, which
139
* defines how the statically defined value field should be protected
140
* ("shallow", "deep" and false are supported values).
142
* By default if the value is an object literal or an array it will be "shallow"
143
* cloned, to protect the default value.
151
* Flag indicating whether or not this object
152
* has been through the init lifecycle phase.
154
* @attribute initialized
165
* Flag indicating whether or not this object
166
* has been through the destroy lifecycle phase.
168
* @attribute destroyed
180
Provides a way to safely modify a `Y.BaseCore` subclass' static `ATTRS`
181
after the class has been defined or created.
183
BaseCore-based classes cache information about the class hierarchy in order
184
to efficiently create instances. This cache includes includes the aggregated
185
`ATTRS` configs. If the static `ATTRS` configs need to be modified after the
186
class has been defined or create, then use this method which will make sure
187
to clear any cached data before making any modifications.
190
@param {Function} [ctor] The constructor function whose `ATTRS` should be
191
modified. If a `ctor` function is not specified, then `this` is assumed
192
to be the constructor which hosts the `ATTRS`.
193
@param {Object} configs The collection of `ATTRS` configs to mix with the
194
existing attribute configurations.
198
BaseCore.modifyAttrs = function (ctor, configs) {
199
// When called without a constructor, assume `this` is the constructor.
200
if (typeof ctor !== 'function') {
205
var attrs, attr, name;
207
// Eagerly create the `ATTRS` object if it doesn't already exist.
208
attrs = ctor.ATTRS || (ctor.ATTRS = {});
211
// Clear cache because it has ATTRS aggregation data which is about
213
ctor._CACHED_CLASS_DATA = null;
215
for (name in configs) {
216
if (configs.hasOwnProperty(name)) {
217
attr = attrs[name] || (attrs[name] = {});
218
Y.mix(attr, configs[name], true);
224
BaseCore.prototype = {
227
* Internal construction logic for BaseCore.
230
* @param {Object} config The constructor configuration object
233
_initBase : function(config) {
237
this._initAttribute(config);
239
// If Plugin.Host has been augmented [ through base-pluginhost ], setup it's
240
// initial state, but don't initialize Plugins yet. That's done after initialization.
241
var PluginHost = Y.Plugin && Y.Plugin.Host;
242
if (this._initPlugins && PluginHost) {
243
PluginHost.call(this);
246
if (this._lazyAddAttrs !== false) { this._lazyAddAttrs = true; }
249
* The string used to identify the class of this object.
251
* @deprecated Use this.constructor.NAME
255
this.name = this.constructor.NAME;
257
this.init.apply(this, arguments);
261
* Initializes AttributeCore
263
* @method _initAttribute
266
_initAttribute: function() {
267
AttributeCore.call(this);
271
* Init lifecycle method, invoked during construction. Sets up attributes
272
* and invokes initializers for the class hierarchy.
276
* @param {Object} cfg Object with configuration property name/value pairs
277
* @return {BaseCore} A reference to this object
279
init: function(cfg) {
287
* Internal initialization implementation for BaseCore
292
_baseInit: function(cfg) {
293
this._initHierarchy(cfg);
295
if (this._initPlugins) {
296
// Need to initPlugins manually, to handle constructor parsing, static Plug parsing
297
this._initPlugins(cfg);
299
this._set(INITIALIZED, true);
303
* Destroy lifecycle method. Invokes destructors for the class hierarchy.
306
* @return {BaseCore} A reference to this object
309
destroy: function() {
315
* Internal destroy implementation for BaseCore
317
* @method _baseDestroy
320
_baseDestroy : function() {
321
if (this._destroyPlugins) {
322
this._destroyPlugins();
324
this._destroyHierarchy();
325
this._set(DESTROYED, true);
329
* Returns the class hierarchy for this object, with BaseCore being the last class in the array.
331
* @method _getClasses
333
* @return {Function[]} An array of classes (constructor functions), making up the class hierarchy for this object.
334
* This value is cached the first time the method, or _getAttrCfgs, is invoked. Subsequent invocations return the
337
_getClasses : function() {
338
if (!this._classes) {
339
this._initHierarchyData();
341
return this._classes;
345
* Returns an aggregated set of attribute configurations, by traversing
346
* the class hierarchy.
348
* @method _getAttrCfgs
350
* @return {Object} The hash of attribute configurations, aggregated across classes in the hierarchy
351
* This value is cached the first time the method, or _getClasses, is invoked. Subsequent invocations return
354
_getAttrCfgs : function() {
356
this._initHierarchyData();
362
* A helper method used when processing ATTRS across the class hierarchy during
363
* initialization. Returns a disposable object with the attributes defined for
364
* the provided class, extracted from the set of all attributes passed in.
366
* @method _filterAttrCfgs
369
* @param {Function} clazz The class for which the desired attributes are required.
370
* @param {Object} allCfgs The set of all attribute configurations for this instance.
371
* Attributes will be removed from this set, if they belong to the filtered class, so
372
* that by the time all classes are processed, allCfgs will be empty.
374
* @return {Object} The set of attributes belonging to the class passed in, in the form
375
* of an object with attribute name/configuration pairs.
377
_filterAttrCfgs : function(clazz, allCfgs) {
387
filtered = this._filteredAttrs,
391
for (attr in attrs) {
392
attrCfg = allCfgs[attr];
394
// Using hasOwnProperty, since it's faster (for the 80% case where filtered doesn't have attr) for the majority
395
// of browsers, FF being the major outlier. http://jsperf.com/in-vs-hasownproperty/6. May revisit.
396
if (attrCfg && !filtered.hasOwnProperty(attr)) {
403
// Revisit once all unit tests pass for further optimizations. See if we really need to isolate this.
404
cfg = cfgs[attr] = _wlmix({}, attrCfg, this._attrCfgHash());
406
filtered[attr] = true;
410
if (val && (typeof val === "object")) {
411
this._cloneDefaultValue(attr, cfg);
414
if (allCfgs._subAttrs && allCfgs._subAttrs.hasOwnProperty(attr)) {
415
subAttrs = allCfgs._subAttrs[attr];
417
for (subAttrPath in subAttrs) {
418
subAttr = subAttrs[subAttrPath];
421
O.setValue(cfg.value, subAttr.path, subAttr.value);
433
* @method _filterAdHocAttrs
436
* @param {Object} allAttrs The set of all attribute configurations for this instance.
437
* Attributes will be removed from this set, if they belong to the filtered class, so
438
* that by the time all classes are processed, allCfgs will be empty.
439
* @param {Object} userVals The config object passed in by the user, from which adhoc attrs are to be filtered.
440
* @return {Object} The set of adhoc attributes passed in, in the form
441
* of an object with attribute name/configuration pairs.
443
_filterAdHocAttrs : function(allAttrs, userVals) {
445
nonAttrs = this._nonAttrs,
450
for (attr in userVals) {
451
if (!allAttrs[attr] && !nonAttrs[attr] && userVals.hasOwnProperty(attr)) {
463
* A helper method used by _getClasses and _getAttrCfgs, which determines both
464
* the array of classes and aggregate set of attribute configurations
465
* across the class hierarchy for the instance.
467
* @method _initHierarchyData
470
_initHierarchyData : function() {
472
var ctor = this.constructor,
473
cachedClassData = ctor._CACHED_CLASS_DATA,
479
needsAttrCfgHash = !ctor._ATTR_CFG_HASH,
485
// Start with `this` instance's constructor.
488
if (!cachedClassData) {
492
classes[classes.length] = c;
496
attrs[attrs.length] = c.ATTRS;
499
// Aggregate ATTR cfg whitelist.
500
if (needsAttrCfgHash) {
501
attrCfg = c._ATTR_CFG;
502
attrCfgHash = attrCfgHash || {};
505
for (i = 0, l = attrCfg.length; i < l; i += 1) {
506
attrCfgHash[attrCfg[i]] = true;
511
// Commenting out the if. We always aggregate, since we don't
512
// know if we'll be needing this on the instance or not.
513
// if (this._allowAdHocAttrs) {
514
nonAttrsCfg = c._NON_ATTRS_CFG;
516
for (i = 0, l = nonAttrsCfg.length; i < l; i++) {
517
nonAttrs[nonAttrsCfg[i]] = true;
522
c = c.superclass ? c.superclass.constructor : null;
525
// Cache computed `_ATTR_CFG_HASH` on the constructor.
526
if (needsAttrCfgHash) {
527
ctor._ATTR_CFG_HASH = attrCfgHash;
530
cachedClassData = ctor._CACHED_CLASS_DATA = {
533
attrs : this._aggregateAttrs(attrs)
538
this._classes = cachedClassData.classes;
539
this._attrs = cachedClassData.attrs;
540
this._nonAttrs = cachedClassData.nonAttrs;
544
* Utility method to define the attribute hash used to filter/whitelist property mixes for
545
* this class for iteration performance reasons.
547
* @method _attrCfgHash
550
_attrCfgHash: function() {
551
return this.constructor._ATTR_CFG_HASH;
555
* This method assumes that the value has already been checked to be an object.
556
* Since it's on a critical path, we don't want to re-do the check.
558
* @method _cloneDefaultValue
559
* @param {Object} cfg
562
_cloneDefaultValue : function(attr, cfg) {
565
clone = cfg.cloneDefaultValue;
567
if (clone === DEEP || clone === true) {
568
cfg.value = Y.clone(val);
569
} else if (clone === SHALLOW) {
570
cfg.value = Y.merge(val);
571
} else if ((clone === undefined && (OBJECT_CONSTRUCTOR === val.constructor || L.isArray(val)))) {
572
cfg.value = Y.clone(val);
574
// else if (clone === false), don't clone the static default value.
575
// It's intended to be used by reference.
579
* A helper method, used by _initHierarchyData to aggregate
580
* attribute configuration across the instances class hierarchy.
582
* The method will protect the attribute configuration value to protect the statically defined
583
* default value in ATTRS if required (if the value is an object literal, array or the
584
* attribute configuration has cloneDefaultValue set to shallow or deep).
586
* @method _aggregateAttrs
588
* @param {Array} allAttrs An array of ATTRS definitions across classes in the hierarchy
589
* (subclass first, Base last)
590
* @return {Object} The aggregate set of ATTRS definitions for the instance
592
_aggregateAttrs : function(allAttrs) {
600
cfgPropsHash = this._attrCfgHash(),
605
for (i = allAttrs.length-1; i >= 0; --i) {
609
for (attr in attrs) {
610
if (attrs.hasOwnProperty(attr)) {
612
// PERF TODO: Do we need to merge here, since we're merging later in filterAttrCfg
613
// Should we move this down to only merge if we hit the path or valueFn ifs below?
614
cfg = _wlmix({}, attrs[attr], cfgPropsHash);
617
if (attr.indexOf(DOT) !== -1) {
618
path = attr.split(DOT);
622
aggAttr = aggAttrs[attr];
624
if (path && aggAttr && aggAttr.value) {
626
subAttrsHash = aggAttrs._subAttrs;
629
subAttrsHash = aggAttrs._subAttrs = {};
632
if (!subAttrsHash[attr]) {
633
subAttrsHash[attr] = {};
636
subAttrsHash[attr][path.join(DOT)] = {
644
aggAttrs[attr] = cfg;
646
if (aggAttr.valueFn && VALUE in cfg) {
647
aggAttr.valueFn = null;
650
// Mix into existing config.
651
_wlmix(aggAttr, cfg, cfgPropsHash);
663
* Initializes the class hierarchy for the instance, which includes
664
* initializing attributes for each class defined in the class's
665
* static <a href="#property_BaseCore.ATTRS">ATTRS</a> property and
666
* invoking the initializer method on the prototype of each class in the hierarchy.
668
* @method _initHierarchy
669
* @param {Object} userVals Object with configuration property name/value pairs
672
_initHierarchy : function(userVals) {
673
var lazy = this._lazyAddAttrs,
681
classes = this._getClasses(),
682
attrCfgs = this._getAttrCfgs(),
683
cl = classes.length - 1;
685
this._filteredAttrs = {};
687
for (ci = cl; ci >= 0; ci--) {
689
constr = classes[ci];
690
constrProto = constr.prototype;
691
exts = constr._yuibuild && constr._yuibuild.exts;
694
for (ei = 0, el = exts.length; ei < el; ei++) {
695
exts[ei].apply(this, arguments);
699
this.addAttrs(this._filterAttrCfgs(constr, attrCfgs), userVals, lazy);
701
if (this._allowAdHocAttrs && ci === cl) {
702
this.addAttrs(this._filterAdHocAttrs(attrCfgs, userVals), userVals, lazy);
705
// Using INITIALIZER in hasOwnProperty check, for performance reasons (helps IE6 avoid GC thresholds when
706
// referencing string literals). Not using it in apply, again, for performance "." is faster.
707
if (constrProto.hasOwnProperty(INITIALIZER)) {
708
constrProto.initializer.apply(this, arguments);
712
for (ei = 0; ei < el; ei++) {
713
extProto = exts[ei].prototype;
714
if (extProto.hasOwnProperty(INITIALIZER)) {
715
extProto.initializer.apply(this, arguments);
721
this._filteredAttrs = null;
725
* Destroys the class hierarchy for this instance by invoking
726
* the destructor method on the prototype of each class in the hierarchy.
728
* @method _destroyHierarchy
731
_destroyHierarchy : function() {
734
ci, cl, ei, el, exts, extProto,
735
classes = this._getClasses();
737
for (ci = 0, cl = classes.length; ci < cl; ci++) {
738
constr = classes[ci];
739
constrProto = constr.prototype;
740
exts = constr._yuibuild && constr._yuibuild.exts;
743
for (ei = 0, el = exts.length; ei < el; ei++) {
744
extProto = exts[ei].prototype;
745
if (extProto.hasOwnProperty(DESTRUCTOR)) {
746
extProto.destructor.apply(this, arguments);
751
if (constrProto.hasOwnProperty(DESTRUCTOR)) {
752
constrProto.destructor.apply(this, arguments);
758
* Default toString implementation. Provides the constructor NAME
759
* and the instance guid, if set.
762
* @return {String} String representation for this object
764
toString: function() {
765
return this.name + "[" + Y.stamp(this, true) + "]";
769
// Straightup augment, no wrapper functions
770
Y.mix(BaseCore, AttributeCore, false, null, 1);
773
BaseCore.prototype.constructor = BaseCore;
775
Y.BaseCore = BaseCore;
778
}, '3.10.3', {"requires": ["attribute-core"]});