Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

BaseObservable — Extracting lifecycle and attribute change observability from `Y.Base` #168

Closed
wants to merge 20 commits into from

5 participants

@ericf
Owner

This extracts Y.BaseObservable, an extension for Y.BaseCore, from Y.Base.

By extracting the lifecycle and attribute change observability features, BaseCore subclasses have the potential to mix these features back in at runtime. These changes (and probably more) are needed to create the Y.Model.Base class and Y.Model.Observable extension.

I ran the base unit test and they all pass; additionally, I ran app's and a few other higher-level components to make sure I didn't introduce any gaps or regressions.

Running Locally

I explicitly did not commit the build files or the Loader metadata update, when trying out these changes locally, do the following:

$ cd src/attribute/ && shifter
$ cd ../base/ && shifter
$ cd ../yui/ && shifter

Todos

There are several things that still have to be done before this is merged in. Many of these came up in a code review of this Pull Request.

  • Determine if `Y.BaseObservable` can be mixed into a `Y.BaseCore` subclass at runtime.

  • Try implementing `Y.Model.Base` and `Y.Model.Observable` on this structure.

  • Move Attribute's `_protecteAttrs()` prototype method to a static utility method on the `Y.Attribute` constructor function.

  • Have `Y.BaseObservable` not mix `Y.Attribute` as a whole, instead:

    • Y.Base will itself mix-in Y.AttributeExtras before Y.BaseObservable.

    • Investigate whether Y.Model.Base should itself mix-in Y.AttributeExtras.

  • Remove `Base._buildCfg = ` from `base-build`:

    • Create a separate object used for Base's config that's a copy of BaseCore's.

  • Move the `_ATTR_CFG_HASH` computation into `base-build`.

  • Naming: I kind of like "observable" better, e.g. `Y.BaseCore` and `Y.BaseObservable`. This should trickle down to `Y.AttributeObservable` too.

  • Figure out correct yuidoc tags to use to make the API docs right (possibly a bug in yuidocjs).

ericf added some commits
@ericf ericf Move Base.js to BaseEvents.js 2d9f814
@ericf ericf Extract BaseEvents from Base.
Move the observablility of `Y.Base`'s lifecycle and attribute changes
into `Y.BaseEvents`, an extension for `Y.BaseCore`.
f1544a1
@ericf ericf Update `base` module and submodule metadata to include `base-events`. b1e13ff
src/base/js/Base.js
((59 lines not shown))
* @method _attrCfgHash
* @private
*/
_attrCfgHash: function() {
return Base._ATTR_CFG_HASH;
- },
@ericf Owner
ericf added a note

Having this method on Y.Base may affect the ability to mix in Y.BaseEvents into a Y.BaseCore subclass at runtime.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
src/base/js/BaseEvents.js
((9 lines not shown))
+
+ DESTROY = "destroy",
+ INIT = "init",
+
+ BUBBLETARGETS = "bubbleTargets",
+ _BUBBLETARGETS = "_bubbleTargets",
+
+ Attribute = Y.Attribute,
+ AttributeEvents = Y.AttributeEvents;
+
+ /**
+ Provides an augmentable implementation of lifecycle and attribute events for
+ `BaseCore`.
+
+ @class BaseEvents
+ @extensionfor BaseCore
@ericf Owner
ericf added a note

This is currently causing yuidoc issues. Adding @for Base clears up Y.Base's docs, but leaves Y.BaseEvents without any "own" methods, events, properties.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@ericf
Owner

@sdesai This is the initial work I've done to extract Y.BaseEvents from Y.Base. There is still more I want to do to make sure this services the needs of Y.Model.Base and Y.Model.Events. Let me know if anything stands out to you…

@lsmith

I wonder if this is similar to the DT class extensions, in that I included @for DataTable.Base in the docs so the methods would show up where they were most likely to be searched for. The module description for the class extensions include a note that the API docs are in DataTable, since otherwise they appear empty. (e.g. http://yuilibrary.com/yui/docs/api/classes/DataTable.Mutable.html)

@ericf
Owner

@lsmith Yup, this is the same thing I'm seeing. I guess we can sort that out when @davglass get's back and determine if there's something we want yuidoc to do differently or not.

src/base/js/Base.js
@@ -97,29 +72,29 @@
* @static
* @private
*/
- Base._ATTR_CFG = Attribute._ATTR_CFG.concat("cloneDefaultValue");
+ Base._ATTR_CFG = Y.BaseCore._ATTR_CFG.concat(Y.BaseEvents._ATTR_CFG);
Base._ATTR_CFG_HASH = Y.Array.hash(Base._ATTR_CFG);
@ericf Owner
ericf added a note

I'm pretty sure _ATTR_CFG and _ATTR_CFG_HASH should be aggregated by Y.Base.create() and Y.Base.mix(). Looking at Y.Base._buildCfg, I see the following:

Base._buildCfg = {
    custom: {
        ATTRS: function (prop, r, s) { /* ... */ },
        _NON_ATTRS_CFG: arrayAggregator
    }
};

It seems natural to add _ATTR_CFG and possibly _ATTR_CFG_HASH to this set. This way, you could mix Y.Base.Events (which wants to add things to _ATTR_CFG) to a Y.Base.Core subclass at runtime via Y.Base.mix().

Since _ATTR_CFG_HASH depends on _ATTR_CFG being fully aggregated, it would be brittle to rely on object property order for its function to be called after _ATTR_CFG's :-/

@ericf Owner
ericf added a note

The implementation I ended up with is in d92409c

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@ericf ericf Update `Y.Base.create/mix` to work with `Y.BaseCore`.
This allows BaseCore to be the root Base-based superclass enabling the
ability to mix in BaseEvents at runtime. The following is now possible:

    Y.Foo = Y.Base.create('foo', Y.BaseCore, []);

    // And...

    Y.Bar = Y.Base.create('bar', Y.BaseCore, [Y.BaseEvents]);

    // Or...

    Y.Baz = Y.Base.create('baz', Y.BaseCore, []);
    Y.Base.mix(Y.Baz, [Y.BaseEvents]);
d92409c
@ericf
Owner

@sdesai I'd like to get this in 3.x, but would like you to review it first. This is a precursor to #2532429, and @caridy might want to use the new capabilities this provides.

@caridy
Owner

@sdesai the idea is to try to use this feature on the new mojito prototype we are building during the current cycle :)

@sdesai

Will take a look this week.

@sdesai

Sorry about the delay looking into this. I'm just going to put it in the next sprint, so it gets done.

ericf added some commits
@ericf ericf Merge branch 'master' into base-events
Conflicts:
	src/base/base-base.properties
	src/base/build.xml
	src/base/tests/base-tests.js
547702f
@ericf ericf Update Base's build.json, and remove old base-events Ant build files. 0546dd9
@ericf
Owner

Updated the Todos list in the Pull Request description based on feedback from @sdesai when we had a code review.

ericf added some commits
@ericf ericf Move Attribute's `_protectAttrs()` method to a static utility method.
This moves the protected `_protectAttrs()` method to a static utility
method on `Y.AttributeCore` and `Y.Attribute`.
19ba4d1
@ericf ericf Move hack to fix Attribute's IE Node clone issue down to AttributeCore. 8ced7fd
@ericf ericf Deprecate AttributeComplex and move its overrides into AttributeCore. b346ca0
@ericf ericf Change BaseEvents to mix AttributeEvents and Base to mix AttributeExtras
The reduces dependencies of BaseEvents, but does introduce the fact that
mixing BaseEvents into BaseCore does not equal the functionality of
Base. This is because Base now explicitly mixes in AttributeExtras.
bfcf08c
@ericf ericf Give BaseCore and Base their own `_buildCfg` objects. a4cc1a7
@ericf ericf Cache computation of Base/BaseCore's `_ATTR_CFG_HASH`.
A Base/BaseCore-based class will now compute its `_ATTR_CFG_HASH` on
demand, when it is needed. If an extension is mixed in via
`Y.Base.mix()`, then the cached hash will be nulled-out.
755e200
@ericf ericf Move AttributeEvents.js -> AttributeObservable.js. 36156ce
@ericf ericf Rename AttributeEvents -> AttributeObservable and module names.
This renames:

* Y.AttributeEvents -> Y.AttributeObservable
* attribute-events -> attribute-observable
1ba3973
@ericf ericf Move BaseEvents.js to BaseObservable.js 578a93f
@ericf ericf Rename BaseEvents -> BaseObservable.
This renames:

* Y.BaseEvents -> Y.BaseObservable
* base-events -> base-observable
a862773
@ericf
Owner

Note: Travis is lying, this does actually pass all tests, I just decided against checking in the build files for the sake of the diff.

@davglass
Owner
@ericf
Owner

It has been determined that the API documentation anomalies are issues with YUIDoc that will be reported and fixed there.

@ericf
Owner

I am going to merge this into the 3.x branch today. /cc @sdesai

@ericf
Owner

This has been merged in 3.x.

@ericf ericf closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jun 15, 2012
  1. @ericf

    Move Base.js to BaseEvents.js

    ericf authored
Commits on Jun 22, 2012
  1. @ericf

    Extract BaseEvents from Base.

    ericf authored
    Move the observablility of `Y.Base`'s lifecycle and attribute changes
    into `Y.BaseEvents`, an extension for `Y.BaseCore`.
  2. @ericf
Commits on Jun 25, 2012
  1. @ericf

    Update `Y.Base.create/mix` to work with `Y.BaseCore`.

    ericf authored
    This allows BaseCore to be the root Base-based superclass enabling the
    ability to mix in BaseEvents at runtime. The following is now possible:
    
        Y.Foo = Y.Base.create('foo', Y.BaseCore, []);
    
        // And...
    
        Y.Bar = Y.Base.create('bar', Y.BaseCore, [Y.BaseEvents]);
    
        // Or...
    
        Y.Baz = Y.Base.create('baz', Y.BaseCore, []);
        Y.Base.mix(Y.Baz, [Y.BaseEvents]);
Commits on Sep 26, 2012
  1. @ericf

    Merge branch 'master' into base-events

    ericf authored
    Conflicts:
    	src/base/base-base.properties
    	src/base/build.xml
    	src/base/tests/base-tests.js
  2. @ericf
Commits on Oct 9, 2012
  1. @ericf

    Move Attribute's `_protectAttrs()` method to a static utility method.

    ericf authored
    This moves the protected `_protectAttrs()` method to a static utility
    method on `Y.AttributeCore` and `Y.Attribute`.
Commits on Oct 10, 2012
  1. @ericf
  2. @ericf
  3. @ericf

    Change BaseEvents to mix AttributeEvents and Base to mix AttributeExtras

    ericf authored
    The reduces dependencies of BaseEvents, but does introduce the fact that
    mixing BaseEvents into BaseCore does not equal the functionality of
    Base. This is because Base now explicitly mixes in AttributeExtras.
  4. @ericf
Commits on Oct 12, 2012
  1. @ericf

    Cache computation of Base/BaseCore's `_ATTR_CFG_HASH`.

    ericf authored
    A Base/BaseCore-based class will now compute its `_ATTR_CFG_HASH` on
    demand, when it is needed. If an extension is mixed in via
    `Y.Base.mix()`, then the cached hash will be nulled-out.
  2. @ericf
  3. @ericf

    Rename AttributeEvents -> AttributeObservable and module names.

    ericf authored
    This renames:
    
    * Y.AttributeEvents -> Y.AttributeObservable
    * attribute-events -> attribute-observable
  4. @ericf
  5. @ericf

    Rename BaseEvents -> BaseObservable.

    ericf authored
    This renames:
    
    * Y.BaseEvents -> Y.BaseObservable
    * base-events -> base-observable
Commits on Oct 22, 2012
  1. @ericf
  2. @ericf
  3. @ericf
  4. @ericf

    Update Base's change history.

    ericf authored
This page is out of date. Refresh to see the latest.
Showing with 644 additions and 1,115 deletions.
  1. +0 −269 build/attribute-events/attribute-events-coverage.js
  2. +0 −1  build/attribute-events/attribute-events-min.js
  3. +0 −194 build/attribute-events/attribute-events.js
  4. +17 −0 src/attribute/HISTORY.md
  5. +2 −2 src/attribute/build.json
  6. +18 −15 src/attribute/js/Attribute.js
  7. +110 −35 src/attribute/js/AttributeCore.js
  8. +0 −190 src/attribute/js/AttributeEvents.js
  9. +1 −0  src/attribute/js/AttributeExtras.js
  10. +21 −12 build/attribute-events/attribute-events-debug.js → src/attribute/js/AttributeObservable.js
  11. +15 −92 src/attribute/js/ComplexAttribute.js
  12. +5 −2 src/attribute/meta/attribute.json
  13. +1 −1  src/attribute/tests/unit/assets/attribute-core-tests.js
  14. +1 −1  src/attribute/tests/unit/assets/attribute-tests.js
  15. +12 −0 src/base/HISTORY.md
  16. +9 −4 src/base/build.json
  17. +40 −243 src/base/js/Base.js
  18. +67 −32 src/base/js/BaseBuild.js
  19. +32 −8 src/base/js/BaseCore.js
  20. +198 −0 src/base/js/BaseObservable.js
  21. +12 −9 src/base/meta/base.json
  22. +1 −1  src/base/tests/unit/assets/base-core-tests.js
  23. +79 −1 src/base/tests/unit/assets/base-tests.js
  24. +1 −1  src/get/tests/coverage/get.html
  25. +1 −1  src/get/tests/unit/coverage.html
  26. +1 −1  src/get/tests/unit/get.html
View
269 build/attribute-events/attribute-events-coverage.js
@@ -1,269 +0,0 @@
-if (typeof _yuitest_coverage == "undefined"){
- _yuitest_coverage = {};
- _yuitest_coverline = function(src, line){
- var coverage = _yuitest_coverage[src];
- if (!coverage.lines[line]){
- coverage.calledLines++;
- }
- coverage.lines[line]++;
- };
- _yuitest_coverfunc = function(src, name, line){
- var coverage = _yuitest_coverage[src],
- funcId = name + ":" + line;
- if (!coverage.functions[funcId]){
- coverage.calledFunctions++;
- }
- coverage.functions[funcId]++;
- };
-}
-_yuitest_coverage["build/attribute-events/attribute-events.js"] = {
- lines: {},
- functions: {},
- coveredLines: 0,
- calledLines: 0,
- coveredFunctions: 0,
- calledFunctions: 0,
- path: "build/attribute-events/attribute-events.js",
- code: []
-};
-_yuitest_coverage["build/attribute-events/attribute-events.js"].code=["YUI.add('attribute-events', function (Y, NAME) {",""," /**"," * The attribute module provides an augmentable Attribute implementation, which "," * adds configurable attributes and attribute change events to the class being "," * augmented. It also provides a State class, which is used internally by Attribute,"," * but can also be used independently to provide a name/property/value data structure to"," * store state."," *"," * @module attribute"," */",""," /**"," * The attribute-events submodule provides augmentable attribute change event support "," * for AttributeCore based implementations."," *"," * @module attribute"," * @submodule attribute-events"," */"," var EventTarget = Y.EventTarget,",""," CHANGE = \"Change\","," BROADCAST = \"broadcast\","," PUBLISHED = \"published\";",""," /**"," * Provides an augmentable implementation of attribute change events for "," * AttributeCore. "," *"," * @class AttributeEvents"," * @uses EventTarget"," */"," function AttributeEvents() {"," // Perf tweak - avoid creating event literals if not required."," this._ATTR_E_FACADE = {};"," EventTarget.call(this, {emitFacade:true});"," }",""," AttributeEvents._ATTR_CFG = [BROADCAST];",""," AttributeEvents.prototype = {",""," /**"," * Sets the value of an attribute."," *"," * @method set"," * @chainable"," *"," * @param {String} name The name of the attribute. If the "," * current value of the attribute is an Object, dot notation can be used"," * to set the value of a property within the object (e.g. <code>set(\"x.y.z\", 5)</code>)."," *"," * @param {Any} value The value to set the attribute to."," *"," * @param {Object} opts (Optional) Optional event data to be mixed into"," * the event facade passed to subscribers of the attribute's change event. This "," * can be used as a flexible way to identify the source of a call to set, allowing "," * the developer to distinguish between set called internally by the host, vs. "," * set called externally by the application developer."," *"," * @return {Object} A reference to the host object."," */"," set : function(name, val, opts) {"," return this._setAttr(name, val, opts);"," },",""," /**"," * Allows setting of readOnly/writeOnce attributes. See <a href=\"#method_set\">set</a> for argument details."," *"," * @method _set"," * @protected"," * @chainable"," * "," * @param {String} name The name of the attribute."," * @param {Any} val The value to set the attribute to."," * @param {Object} opts (Optional) Optional event data to be mixed into"," * the event facade passed to subscribers of the attribute's change event."," * @return {Object} A reference to the host object."," */"," _set : function(name, val, opts) {"," return this._setAttr(name, val, opts, true);"," },",""," /**"," * Sets multiple attribute values."," *"," * @method setAttrs"," * @param {Object} attrs An object with attributes name/value pairs."," * @param {Object} opts Properties to mix into the event payload. These are shared and mixed into each set "," * @return {Object} A reference to the host object."," * @chainable"," */"," setAttrs : function(attrs, opts) {"," return this._setAttrs(attrs, opts);"," },",""," /**"," * Implementation behind the public setAttrs method, to set multiple attribute values."," *"," * @method _setAttrs"," * @protected"," * @param {Object} attrs An object with attributes name/value pairs."," * @param {Object} opts Properties to mix into the event payload. These are shared and mixed into each set "," * @return {Object} A reference to the host object."," * @chainable"," */"," _setAttrs : function(attrs, opts) {"," var attr;"," for (attr in attrs) {"," if ( attrs.hasOwnProperty(attr) ) {"," this.set(attr, attrs[attr], opts);"," }"," }"," return this;"," },",""," /**"," * Utility method to help setup the event payload and fire the attribute change event."," * "," * @method _fireAttrChange"," * @private"," * @param {String} attrName The name of the attribute"," * @param {String} subAttrName The full path of the property being changed, "," * if this is a sub-attribute value being change. Otherwise null."," * @param {Any} currVal The current value of the attribute"," * @param {Any} newVal The new value of the attribute"," * @param {Object} opts Any additional event data to mix into the attribute change event's event facade."," */"," _fireAttrChange : function(attrName, subAttrName, currVal, newVal, opts) {"," var host = this,"," eventName = attrName + CHANGE,"," state = host._state,"," facade,"," broadcast,"," evtCfg;",""," if (!state.get(attrName, PUBLISHED)) {"," "," evtCfg = {"," queuable:false,"," defaultTargetOnly: true, "," defaultFn:host._defAttrChangeFn, "," silent:true"," };",""," broadcast = state.get(attrName, BROADCAST);"," if (broadcast !== undefined) {"," evtCfg.broadcast = broadcast;"," }",""," host.publish(eventName, evtCfg);"," "," state.add(attrName, PUBLISHED, true);"," }",""," facade = (opts) ? Y.merge(opts) : host._ATTR_E_FACADE;",""," // Not using the single object signature for fire({type:..., newVal:...}), since "," // we don't want to override type. Changed to the fire(type, {newVal:...}) signature.",""," // facade.type = eventName;"," facade.attrName = attrName;"," facade.subAttrName = subAttrName;"," facade.prevVal = currVal;"," facade.newVal = newVal;",""," // host.fire(facade);"," host.fire(eventName, facade);"," },",""," /**"," * Default function for attribute change events."," *"," * @private"," * @method _defAttrChangeFn"," * @param {EventFacade} e The event object for attribute change events."," */"," _defAttrChangeFn : function(e) {"," if (!this._setAttrVal(e.attrName, e.subAttrName, e.prevVal, e.newVal)) {"," // Prevent \"after\" listeners from being invoked since nothing changed."," e.stopImmediatePropagation();"," } else {"," e.newVal = this.get(e.attrName);"," }"," }"," };",""," // Basic prototype augment - no lazy constructor invocation."," Y.mix(AttributeEvents, EventTarget, false, null, 1);",""," Y.AttributeEvents = AttributeEvents;","","","}, '@VERSION@', {\"requires\": [\"event-custom\"]});"];
-_yuitest_coverage["build/attribute-events/attribute-events.js"].lines = {"1":0,"20":0,"33":0,"35":0,"36":0,"39":0,"41":0,"64":0,"81":0,"94":0,"108":0,"109":0,"110":0,"111":0,"114":0,"130":0,"137":0,"139":0,"146":0,"147":0,"148":0,"151":0,"153":0,"156":0,"162":0,"163":0,"164":0,"165":0,"168":0,"179":0,"181":0,"183":0,"189":0,"191":0};
-_yuitest_coverage["build/attribute-events/attribute-events.js"].functions = {"AttributeEvents:33":0,"set:63":0,"_set:80":0,"setAttrs:93":0,"_setAttrs:107":0,"_fireAttrChange:129":0,"_defAttrChangeFn:178":0,"(anonymous 1):1":0};
-_yuitest_coverage["build/attribute-events/attribute-events.js"].coveredLines = 34;
-_yuitest_coverage["build/attribute-events/attribute-events.js"].coveredFunctions = 8;
-_yuitest_coverline("build/attribute-events/attribute-events.js", 1);
-YUI.add('attribute-events', function (Y, NAME) {
-
- /**
- * The attribute module provides an augmentable Attribute implementation, which
- * adds configurable attributes and attribute change events to the class being
- * augmented. It also provides a State class, which is used internally by Attribute,
- * but can also be used independently to provide a name/property/value data structure to
- * store state.
- *
- * @module attribute
- */
-
- /**
- * The attribute-events submodule provides augmentable attribute change event support
- * for AttributeCore based implementations.
- *
- * @module attribute
- * @submodule attribute-events
- */
- _yuitest_coverfunc("build/attribute-events/attribute-events.js", "(anonymous 1)", 1);
-_yuitest_coverline("build/attribute-events/attribute-events.js", 20);
-var EventTarget = Y.EventTarget,
-
- CHANGE = "Change",
- BROADCAST = "broadcast",
- PUBLISHED = "published";
-
- /**
- * Provides an augmentable implementation of attribute change events for
- * AttributeCore.
- *
- * @class AttributeEvents
- * @uses EventTarget
- */
- _yuitest_coverline("build/attribute-events/attribute-events.js", 33);
-function AttributeEvents() {
- // Perf tweak - avoid creating event literals if not required.
- _yuitest_coverfunc("build/attribute-events/attribute-events.js", "AttributeEvents", 33);
-_yuitest_coverline("build/attribute-events/attribute-events.js", 35);
-this._ATTR_E_FACADE = {};
- _yuitest_coverline("build/attribute-events/attribute-events.js", 36);
-EventTarget.call(this, {emitFacade:true});
- }
-
- _yuitest_coverline("build/attribute-events/attribute-events.js", 39);
-AttributeEvents._ATTR_CFG = [BROADCAST];
-
- _yuitest_coverline("build/attribute-events/attribute-events.js", 41);
-AttributeEvents.prototype = {
-
- /**
- * Sets the value of an attribute.
- *
- * @method set
- * @chainable
- *
- * @param {String} name The name of the attribute. If the
- * current value of the attribute is an Object, dot notation can be used
- * to set the value of a property within the object (e.g. <code>set("x.y.z", 5)</code>).
- *
- * @param {Any} value The value to set the attribute to.
- *
- * @param {Object} opts (Optional) Optional event data to be mixed into
- * the event facade passed to subscribers of the attribute's change event. This
- * can be used as a flexible way to identify the source of a call to set, allowing
- * the developer to distinguish between set called internally by the host, vs.
- * set called externally by the application developer.
- *
- * @return {Object} A reference to the host object.
- */
- set : function(name, val, opts) {
- _yuitest_coverfunc("build/attribute-events/attribute-events.js", "set", 63);
-_yuitest_coverline("build/attribute-events/attribute-events.js", 64);
-return this._setAttr(name, val, opts);
- },
-
- /**
- * Allows setting of readOnly/writeOnce attributes. See <a href="#method_set">set</a> for argument details.
- *
- * @method _set
- * @protected
- * @chainable
- *
- * @param {String} name The name of the attribute.
- * @param {Any} val The value to set the attribute to.
- * @param {Object} opts (Optional) Optional event data to be mixed into
- * the event facade passed to subscribers of the attribute's change event.
- * @return {Object} A reference to the host object.
- */
- _set : function(name, val, opts) {
- _yuitest_coverfunc("build/attribute-events/attribute-events.js", "_set", 80);
-_yuitest_coverline("build/attribute-events/attribute-events.js", 81);
-return this._setAttr(name, val, opts, true);
- },
-
- /**
- * Sets multiple attribute values.
- *
- * @method setAttrs
- * @param {Object} attrs An object with attributes name/value pairs.
- * @param {Object} opts Properties to mix into the event payload. These are shared and mixed into each set
- * @return {Object} A reference to the host object.
- * @chainable
- */
- setAttrs : function(attrs, opts) {
- _yuitest_coverfunc("build/attribute-events/attribute-events.js", "setAttrs", 93);
-_yuitest_coverline("build/attribute-events/attribute-events.js", 94);
-return this._setAttrs(attrs, opts);
- },
-
- /**
- * Implementation behind the public setAttrs method, to set multiple attribute values.
- *
- * @method _setAttrs
- * @protected
- * @param {Object} attrs An object with attributes name/value pairs.
- * @param {Object} opts Properties to mix into the event payload. These are shared and mixed into each set
- * @return {Object} A reference to the host object.
- * @chainable
- */
- _setAttrs : function(attrs, opts) {
- _yuitest_coverfunc("build/attribute-events/attribute-events.js", "_setAttrs", 107);
-_yuitest_coverline("build/attribute-events/attribute-events.js", 108);
-var attr;
- _yuitest_coverline("build/attribute-events/attribute-events.js", 109);
-for (attr in attrs) {
- _yuitest_coverline("build/attribute-events/attribute-events.js", 110);
-if ( attrs.hasOwnProperty(attr) ) {
- _yuitest_coverline("build/attribute-events/attribute-events.js", 111);
-this.set(attr, attrs[attr], opts);
- }
- }
- _yuitest_coverline("build/attribute-events/attribute-events.js", 114);
-return this;
- },
-
- /**
- * Utility method to help setup the event payload and fire the attribute change event.
- *
- * @method _fireAttrChange
- * @private
- * @param {String} attrName The name of the attribute
- * @param {String} subAttrName The full path of the property being changed,
- * if this is a sub-attribute value being change. Otherwise null.
- * @param {Any} currVal The current value of the attribute
- * @param {Any} newVal The new value of the attribute
- * @param {Object} opts Any additional event data to mix into the attribute change event's event facade.
- */
- _fireAttrChange : function(attrName, subAttrName, currVal, newVal, opts) {
- _yuitest_coverfunc("build/attribute-events/attribute-events.js", "_fireAttrChange", 129);
-_yuitest_coverline("build/attribute-events/attribute-events.js", 130);
-var host = this,
- eventName = attrName + CHANGE,
- state = host._state,
- facade,
- broadcast,
- evtCfg;
-
- _yuitest_coverline("build/attribute-events/attribute-events.js", 137);
-if (!state.get(attrName, PUBLISHED)) {
-
- _yuitest_coverline("build/attribute-events/attribute-events.js", 139);
-evtCfg = {
- queuable:false,
- defaultTargetOnly: true,
- defaultFn:host._defAttrChangeFn,
- silent:true
- };
-
- _yuitest_coverline("build/attribute-events/attribute-events.js", 146);
-broadcast = state.get(attrName, BROADCAST);
- _yuitest_coverline("build/attribute-events/attribute-events.js", 147);
-if (broadcast !== undefined) {
- _yuitest_coverline("build/attribute-events/attribute-events.js", 148);
-evtCfg.broadcast = broadcast;
- }
-
- _yuitest_coverline("build/attribute-events/attribute-events.js", 151);
-host.publish(eventName, evtCfg);
-
- _yuitest_coverline("build/attribute-events/attribute-events.js", 153);
-state.add(attrName, PUBLISHED, true);
- }
-
- _yuitest_coverline("build/attribute-events/attribute-events.js", 156);
-facade = (opts) ? Y.merge(opts) : host._ATTR_E_FACADE;
-
- // Not using the single object signature for fire({type:..., newVal:...}), since
- // we don't want to override type. Changed to the fire(type, {newVal:...}) signature.
-
- // facade.type = eventName;
- _yuitest_coverline("build/attribute-events/attribute-events.js", 162);
-facade.attrName = attrName;
- _yuitest_coverline("build/attribute-events/attribute-events.js", 163);
-facade.subAttrName = subAttrName;
- _yuitest_coverline("build/attribute-events/attribute-events.js", 164);
-facade.prevVal = currVal;
- _yuitest_coverline("build/attribute-events/attribute-events.js", 165);
-facade.newVal = newVal;
-
- // host.fire(facade);
- _yuitest_coverline("build/attribute-events/attribute-events.js", 168);
-host.fire(eventName, facade);
- },
-
- /**
- * Default function for attribute change events.
- *
- * @private
- * @method _defAttrChangeFn
- * @param {EventFacade} e The event object for attribute change events.
- */
- _defAttrChangeFn : function(e) {
- _yuitest_coverfunc("build/attribute-events/attribute-events.js", "_defAttrChangeFn", 178);
-_yuitest_coverline("build/attribute-events/attribute-events.js", 179);
-if (!this._setAttrVal(e.attrName, e.subAttrName, e.prevVal, e.newVal)) {
- // Prevent "after" listeners from being invoked since nothing changed.
- _yuitest_coverline("build/attribute-events/attribute-events.js", 181);
-e.stopImmediatePropagation();
- } else {
- _yuitest_coverline("build/attribute-events/attribute-events.js", 183);
-e.newVal = this.get(e.attrName);
- }
- }
- };
-
- // Basic prototype augment - no lazy constructor invocation.
- _yuitest_coverline("build/attribute-events/attribute-events.js", 189);
-Y.mix(AttributeEvents, EventTarget, false, null, 1);
-
- _yuitest_coverline("build/attribute-events/attribute-events.js", 191);
-Y.AttributeEvents = AttributeEvents;
-
-
-}, '@VERSION@', {"requires": ["event-custom"]});
View
1  build/attribute-events/attribute-events-min.js
@@ -1 +0,0 @@
-YUI.add("attribute-events",function(e,t){function o(){this._ATTR_E_FACADE={},n.call(this,{emitFacade:!0})}var n=e.EventTarget,r="Change",i="broadcast",s="published";o._ATTR_CFG=[i],o.prototype={set:function(e,t,n){return this._setAttr(e,t,n)},_set:function(e,t,n){return this._setAttr(e,t,n,!0)},setAttrs:function(e,t){return this._setAttrs(e,t)},_setAttrs:function(e,t){var n;for(n in e)e.hasOwnProperty(n)&&this.set(n,e[n],t);return this},_fireAttrChange:function(t,n,o,u,a){var f=this,l=t+r,c=f._state,h,p,d;c.get(t,s)||(d={queuable:!1,defaultTargetOnly:!0,defaultFn:f._defAttrChangeFn,silent:!0},p=c.get(t,i),p!==undefined&&(d.broadcast=p),f.publish(l,d),c.add(t,s,!0)),h=a?e.merge(a):f._ATTR_E_FACADE,h.attrName=t,h.subAttrName=n,h.prevVal=o,h.newVal=u,f.fire(l,h)},_defAttrChangeFn:function(e){this._setAttrVal(e.attrName,e.subAttrName,e.prevVal,e.newVal)?e.newVal=this.get(e.attrName):e.stopImmediatePropagation()}},e.mix(o,n,!1,null,1),e.AttributeEvents=o},"@VERSION@",{requires:["event-custom"]});
View
194 build/attribute-events/attribute-events.js
@@ -1,194 +0,0 @@
-YUI.add('attribute-events', function (Y, NAME) {
-
- /**
- * The attribute module provides an augmentable Attribute implementation, which
- * adds configurable attributes and attribute change events to the class being
- * augmented. It also provides a State class, which is used internally by Attribute,
- * but can also be used independently to provide a name/property/value data structure to
- * store state.
- *
- * @module attribute
- */
-
- /**
- * The attribute-events submodule provides augmentable attribute change event support
- * for AttributeCore based implementations.
- *
- * @module attribute
- * @submodule attribute-events
- */
- var EventTarget = Y.EventTarget,
-
- CHANGE = "Change",
- BROADCAST = "broadcast",
- PUBLISHED = "published";
-
- /**
- * Provides an augmentable implementation of attribute change events for
- * AttributeCore.
- *
- * @class AttributeEvents
- * @uses EventTarget
- */
- function AttributeEvents() {
- // Perf tweak - avoid creating event literals if not required.
- this._ATTR_E_FACADE = {};
- EventTarget.call(this, {emitFacade:true});
- }
-
- AttributeEvents._ATTR_CFG = [BROADCAST];
-
- AttributeEvents.prototype = {
-
- /**
- * Sets the value of an attribute.
- *
- * @method set
- * @chainable
- *
- * @param {String} name The name of the attribute. If the
- * current value of the attribute is an Object, dot notation can be used
- * to set the value of a property within the object (e.g. <code>set("x.y.z", 5)</code>).
- *
- * @param {Any} value The value to set the attribute to.
- *
- * @param {Object} opts (Optional) Optional event data to be mixed into
- * the event facade passed to subscribers of the attribute's change event. This
- * can be used as a flexible way to identify the source of a call to set, allowing
- * the developer to distinguish between set called internally by the host, vs.
- * set called externally by the application developer.
- *
- * @return {Object} A reference to the host object.
- */
- set : function(name, val, opts) {
- return this._setAttr(name, val, opts);
- },
-
- /**
- * Allows setting of readOnly/writeOnce attributes. See <a href="#method_set">set</a> for argument details.
- *
- * @method _set
- * @protected
- * @chainable
- *
- * @param {String} name The name of the attribute.
- * @param {Any} val The value to set the attribute to.
- * @param {Object} opts (Optional) Optional event data to be mixed into
- * the event facade passed to subscribers of the attribute's change event.
- * @return {Object} A reference to the host object.
- */
- _set : function(name, val, opts) {
- return this._setAttr(name, val, opts, true);
- },
-
- /**
- * Sets multiple attribute values.
- *
- * @method setAttrs
- * @param {Object} attrs An object with attributes name/value pairs.
- * @param {Object} opts Properties to mix into the event payload. These are shared and mixed into each set
- * @return {Object} A reference to the host object.
- * @chainable
- */
- setAttrs : function(attrs, opts) {
- return this._setAttrs(attrs, opts);
- },
-
- /**
- * Implementation behind the public setAttrs method, to set multiple attribute values.
- *
- * @method _setAttrs
- * @protected
- * @param {Object} attrs An object with attributes name/value pairs.
- * @param {Object} opts Properties to mix into the event payload. These are shared and mixed into each set
- * @return {Object} A reference to the host object.
- * @chainable
- */
- _setAttrs : function(attrs, opts) {
- var attr;
- for (attr in attrs) {
- if ( attrs.hasOwnProperty(attr) ) {
- this.set(attr, attrs[attr], opts);
- }
- }
- return this;
- },
-
- /**
- * Utility method to help setup the event payload and fire the attribute change event.
- *
- * @method _fireAttrChange
- * @private
- * @param {String} attrName The name of the attribute
- * @param {String} subAttrName The full path of the property being changed,
- * if this is a sub-attribute value being change. Otherwise null.
- * @param {Any} currVal The current value of the attribute
- * @param {Any} newVal The new value of the attribute
- * @param {Object} opts Any additional event data to mix into the attribute change event's event facade.
- */
- _fireAttrChange : function(attrName, subAttrName, currVal, newVal, opts) {
- var host = this,
- eventName = attrName + CHANGE,
- state = host._state,
- facade,
- broadcast,
- evtCfg;
-
- if (!state.get(attrName, PUBLISHED)) {
-
- evtCfg = {
- queuable:false,
- defaultTargetOnly: true,
- defaultFn:host._defAttrChangeFn,
- silent:true
- };
-
- broadcast = state.get(attrName, BROADCAST);
- if (broadcast !== undefined) {
- evtCfg.broadcast = broadcast;
- }
-
- host.publish(eventName, evtCfg);
-
- state.add(attrName, PUBLISHED, true);
- }
-
- facade = (opts) ? Y.merge(opts) : host._ATTR_E_FACADE;
-
- // Not using the single object signature for fire({type:..., newVal:...}), since
- // we don't want to override type. Changed to the fire(type, {newVal:...}) signature.
-
- // facade.type = eventName;
- facade.attrName = attrName;
- facade.subAttrName = subAttrName;
- facade.prevVal = currVal;
- facade.newVal = newVal;
-
- // host.fire(facade);
- host.fire(eventName, facade);
- },
-
- /**
- * Default function for attribute change events.
- *
- * @private
- * @method _defAttrChangeFn
- * @param {EventFacade} e The event object for attribute change events.
- */
- _defAttrChangeFn : function(e) {
- if (!this._setAttrVal(e.attrName, e.subAttrName, e.prevVal, e.newVal)) {
- // Prevent "after" listeners from being invoked since nothing changed.
- e.stopImmediatePropagation();
- } else {
- e.newVal = this.get(e.attrName);
- }
- }
- };
-
- // Basic prototype augment - no lazy constructor invocation.
- Y.mix(AttributeEvents, EventTarget, false, null, 1);
-
- Y.AttributeEvents = AttributeEvents;
-
-
-}, '@VERSION@', {"requires": ["event-custom"]});
View
17 src/attribute/HISTORY.md
@@ -1,6 +1,23 @@
Attribute Change History
========================
+3.8.0
+-----
+
+ * [!] The `AttributeEvents` class extension and the `attribute-events` module
+ have been renamed to `AttributeObservable` and `attribute-observable`
+ respectively. The old names are deprecated, but have been retained as
+ aliases for backwards compatibility. They will be removed in a future
+ version of YUI.
+
+ * [!] The `AttributeComplex` class extension and the `attribute-complex`
+ module have been deprecated. This functionality is now part of
+ `AttributeCore`, and this extension and module are no longer needed.
+
+ * Moved AttributeCore's protected `_protectAttrs()` utility method to a public
+ static method, `protectAttrs()`, which is available on both `Y.Attribute`
+ and `Y.AttributeCore` namespaces.
+
3.7.0
-----
View
4 src/attribute/build.json
@@ -22,9 +22,9 @@
"ComplexAttribute.js"
]
},
- "attribute-events": {
+ "attribute-observable": {
"jsfiles": [
- "AttributeEvents.js"
+ "AttributeObservable.js"
]
}
}
View
33 src/attribute/js/Attribute.js
@@ -45,29 +45,21 @@
* @param values {Object} The initial attribute values to apply (passed through to <a href="#method_addAttrs">addAttrs</a>). These are not merged/cloned. The caller is responsible for isolating user provided values if required.
* @param lazy {boolean} Whether or not to add attributes lazily (passed through to <a href="#method_addAttrs">addAttrs</a>).
* @uses AttributeCore
- * @uses AttributeEvents
+ * @uses AttributeObservable
* @uses EventTarget
* @uses AttributeExtras
*/
- var Attribute = function() {
-
- // Fix #2531929
- // Complete hack, to make sure the first clone of a node value in IE doesn't doesn't hurt state - maintains 3.4.1 behavior.
- // Too late in the release cycle to do anything about the core problem.
- // The root issue is that cloning a Y.Node instance results in an object which barfs in IE, when you access it's properties (since 3.3.0).
- this._ATTR_E_FACADE = null;
- this._yuievt = null;
-
+ function Attribute() {
Y.AttributeCore.apply(this, arguments);
- Y.AttributeEvents.apply(this, arguments);
+ Y.AttributeObservable.apply(this, arguments);
Y.AttributeExtras.apply(this, arguments);
- };
+ }
Y.mix(Attribute, Y.AttributeCore, false, null, 1);
Y.mix(Attribute, Y.AttributeExtras, false, null, 1);
// Needs to be "true", to overwrite methods from AttributeCore
- Y.mix(Attribute, Y.AttributeEvents, true, null, 1);
+ Y.mix(Attribute, Y.AttributeObservable, true, null, 1);
/**
* <p>The value to return from an attribute setter in order to prevent the set from going through.</p>
@@ -95,6 +87,17 @@
* @static
* @protected
*/
- Attribute._ATTR_CFG = Y.AttributeCore._ATTR_CFG.concat(Y.AttributeEvents._ATTR_CFG);
+ Attribute._ATTR_CFG = Y.AttributeCore._ATTR_CFG.concat(Y.AttributeObservable._ATTR_CFG);
+
+ /**
+ * Utility method to protect an attribute configuration hash, by merging the
+ * entire object and the individual attr config objects.
+ *
+ * @method protectAttrs
+ * @static
+ * @param {Object} attrs A hash of attribute to configuration object pairs.
+ * @return {Object} A protected version of the `attrs` argument.
+ */
+ Attribute.protectAttrs = Y.AttributeCore.protectAttrs;
- Y.Attribute = Attribute;
View
145 src/attribute/js/AttributeCore.js
@@ -62,7 +62,7 @@
* <p>See the <a href="#method_addAttr">addAttr</a> method, for the complete set of configuration
* options available for attributes.</p>
*
- * <p>Object/Classes based on AttributeCore can augment <a href="AttributeEvents.html">AttributeEvents</a>
+ * <p>Object/Classes based on AttributeCore can augment <a href="AttributeObservable.html">AttributeObservable</a>
* (with true for overwrite) and <a href="AttributeExtras.html">AttributeExtras</a> to add attribute event and
* additional, less commonly used attribute methods, such as `modifyAttr`, `removeAttr` and `reset`.</p>
*
@@ -72,7 +72,13 @@
* @param lazy {boolean} Whether or not to add attributes lazily (passed through to <a href="#method_addAttrs">addAttrs</a>).
*/
function AttributeCore(attrs, values, lazy) {
- this._initAttrHost(attrs, values, lazy);
+ // HACK: Fix #2531929
+ // Complete hack, to make sure the first clone of a node value in IE doesn't doesn't hurt state - maintains 3.4.1 behavior.
+ // Too late in the release cycle to do anything about the core problem.
+ // The root issue is that cloning a Y.Node instance results in an object which barfs in IE, when you access it's properties (since 3.3.0).
+ this._yuievt = null;
+
+ this._initAttrHost(attrs, values, lazy);
}
/**
@@ -103,6 +109,28 @@
* @protected
*/
AttributeCore._ATTR_CFG = [SETTER, GETTER, VALIDATOR, VALUE, VALUE_FN, WRITE_ONCE, READ_ONLY, LAZY_ADD, BYPASS_PROXY];
+
+ /**
+ * Utility method to protect an attribute configuration hash, by merging the
+ * entire object and the individual attr config objects.
+ *
+ * @method protectAttrs
+ * @static
+ * @param {Object} attrs A hash of attribute to configuration object pairs.
+ * @return {Object} A protected version of the `attrs` argument.
+ */
+ AttributeCore.protectAttrs = function (attrs) {
+ if (attrs) {
+ attrs = Y.merge(attrs);
+ for (var attr in attrs) {
+ if (attrs.hasOwnProperty(attr)) {
+ attrs[attr] = Y.merge(attrs[attr]);
+ }
+ }
+ }
+
+ return attrs;
+ };
AttributeCore.prototype = {
@@ -372,7 +400,7 @@
* @param {Object} opts (Optional) Optional event data to be mixed into
* the event facade passed to subscribers of the attribute's change event.
* This is currently a hack. There's no real need for the AttributeCore implementation
- * to support this parameter, but breaking it out into AttributeEvents, results in
+ * to support this parameter, but breaking it out into AttributeObservable, results in
* additional function hops for the critical path.
* @param {boolean} force If true, allows the caller to set values for
* readOnly or writeOnce attributes which have already been set.
@@ -793,32 +821,52 @@
* @protected
* @param {Object} attrs A hash of attribute to configuration object pairs.
* @return {Object} A protected version of the attrs argument.
+ * @deprecated Use `AttributeCore.protectAttrs()` or
+ * `Attribute.protectAttrs()` which are the same static utility method.
*/
- _protectAttrs : function(attrs) {
- if (attrs) {
- attrs = Y.merge(attrs);
- for (var attr in attrs) {
- if (attrs.hasOwnProperty(attr)) {
- attrs[attr] = Y.merge(attrs[attr]);
- }
- }
- }
- return attrs;
- },
+ _protectAttrs : AttributeCore.protectAttrs,
/**
- * Utility method to normalize attribute values. The base implementation
- * simply merges the hash to protect the original.
+ * Utility method to split out simple attribute name/value pairs ("x")
+ * from complex attribute name/value pairs ("x.y.z"), so that complex
+ * attributes can be keyed by the top level attribute name.
*
* @method _normAttrVals
* @param {Object} valueHash An object with attribute name/value pairs
*
- * @return {Object}
+ * @return {Object} An object literal with 2 properties - "simple" and "complex",
+ * containing simple and complex attribute values respectively keyed
+ * by the top level attribute name, or null, if valueHash is falsey.
*
* @private
*/
_normAttrVals : function(valueHash) {
- return (valueHash) ? Y.merge(valueHash) : null;
+ var vals = {},
+ subvals = {},
+ path,
+ attr,
+ v, k;
+
+ if (valueHash) {
+ for (k in valueHash) {
+ if (valueHash.hasOwnProperty(k)) {
+ if (k.indexOf(DOT) !== -1) {
+ path = k.split(DOT);
+ attr = path.shift();
+ v = subvals[attr] = subvals[attr] || [];
+ v[v.length] = {
+ path : path,
+ value: valueHash[k]
+ };
+ } else {
+ vals[k] = valueHash[k];
+ }
+ }
+ }
+ return { simple:vals, complex:subvals };
+ } else {
+ return null;
+ }
},
/**
@@ -837,25 +885,52 @@
* @private
*/
_getAttrInitVal : function(attr, cfg, initValues) {
- var val, valFn;
- // init value is provided by the user if it exists, else, provided by the config
- if (!cfg.readOnly && initValues && initValues.hasOwnProperty(attr)) {
- val = initValues[attr];
- } else {
- val = cfg.value;
- valFn = cfg.valueFn;
-
+
+ var val = cfg.value,
+ valFn = cfg.valueFn,
+ tmpVal,
+ initValSet = false,
+ simple,
+ complex,
+ i,
+ l,
+ path,
+ subval,
+ subvals;
+
+ if (!cfg.readOnly && initValues) {
+ // Simple Attributes
+ simple = initValues.simple;
+ if (simple && simple.hasOwnProperty(attr)) {
+ val = simple[attr];
+ initValSet = true;
+ }
+ }
+
+ if (valFn && !initValSet) {
+ if (!valFn.call) {
+ valFn = this[valFn];
+ }
if (valFn) {
- if (!valFn.call) {
- valFn = this[valFn];
- }
- if (valFn) {
- val = valFn.call(this, attr);
- }
+ tmpVal = valFn.call(this, attr);
+ val = tmpVal;
}
}
- Y.log('initValue for ' + attr + ':' + val, 'info', 'attribute');
+ if (!cfg.readOnly && initValues) {
+
+ // Complex Attributes (complex values applied, after simple, in case both are set)
+ complex = initValues.complex;
+
+ if (complex && complex.hasOwnProperty(attr) && (val !== undefined) && (val !== null)) {
+ subvals = complex[attr];
+ for (i = 0, l = subvals.length; i < l; ++i) {
+ path = subvals[i].path;
+ subval = subvals[i].value;
+ O.setValue(val, path, subval);
+ }
+ }
+ }
return val;
},
@@ -878,8 +953,8 @@
baseInst = (Base && Y.instanceOf(this, Base)),
baseCoreInst = (!baseInst && BaseCore && Y.instanceOf(this, BaseCore));
- if ( attrs && !baseInst && !baseCoreInst) {
- this.addAttrs(this._protectAttrs(attrs), values, lazy);
+ if (attrs && !baseInst && !baseCoreInst) {
+ this.addAttrs(Y.AttributeCore.protectAttrs(attrs), values, lazy);
}
}
};
View
190 src/attribute/js/AttributeEvents.js
@@ -1,190 +0,0 @@
- /**
- * The attribute module provides an augmentable Attribute implementation, which
- * adds configurable attributes and attribute change events to the class being
- * augmented. It also provides a State class, which is used internally by Attribute,
- * but can also be used independently to provide a name/property/value data structure to
- * store state.
- *
- * @module attribute
- */
-
- /**
- * The attribute-events submodule provides augmentable attribute change event support
- * for AttributeCore based implementations.
- *
- * @module attribute
- * @submodule attribute-events
- */
- var EventTarget = Y.EventTarget,
-
- CHANGE = "Change",
- BROADCAST = "broadcast",
- PUBLISHED = "published";
-
- /**
- * Provides an augmentable implementation of attribute change events for
- * AttributeCore.
- *
- * @class AttributeEvents
- * @uses EventTarget
- */
- function AttributeEvents() {
- // Perf tweak - avoid creating event literals if not required.
- this._ATTR_E_FACADE = {};
- EventTarget.call(this, {emitFacade:true});
- }
-
- AttributeEvents._ATTR_CFG = [BROADCAST];
-
- AttributeEvents.prototype = {
-
- /**
- * Sets the value of an attribute.
- *
- * @method set
- * @chainable
- *
- * @param {String} name The name of the attribute. If the
- * current value of the attribute is an Object, dot notation can be used
- * to set the value of a property within the object (e.g. <code>set("x.y.z", 5)</code>).
- *
- * @param {Any} value The value to set the attribute to.
- *
- * @param {Object} opts (Optional) Optional event data to be mixed into
- * the event facade passed to subscribers of the attribute's change event. This
- * can be used as a flexible way to identify the source of a call to set, allowing
- * the developer to distinguish between set called internally by the host, vs.
- * set called externally by the application developer.
- *
- * @return {Object} A reference to the host object.
- */
- set : function(name, val, opts) {
- return this._setAttr(name, val, opts);
- },
-
- /**
- * Allows setting of readOnly/writeOnce attributes. See <a href="#method_set">set</a> for argument details.
- *
- * @method _set
- * @protected
- * @chainable
- *
- * @param {String} name The name of the attribute.
- * @param {Any} val The value to set the attribute to.
- * @param {Object} opts (Optional) Optional event data to be mixed into
- * the event facade passed to subscribers of the attribute's change event.
- * @return {Object} A reference to the host object.
- */
- _set : function(name, val, opts) {
- return this._setAttr(name, val, opts, true);
- },
-
- /**
- * Sets multiple attribute values.
- *
- * @method setAttrs
- * @param {Object} attrs An object with attributes name/value pairs.
- * @param {Object} opts Properties to mix into the event payload. These are shared and mixed into each set
- * @return {Object} A reference to the host object.
- * @chainable
- */
- setAttrs : function(attrs, opts) {
- return this._setAttrs(attrs, opts);
- },
-
- /**
- * Implementation behind the public setAttrs method, to set multiple attribute values.
- *
- * @method _setAttrs
- * @protected
- * @param {Object} attrs An object with attributes name/value pairs.
- * @param {Object} opts Properties to mix into the event payload. These are shared and mixed into each set
- * @return {Object} A reference to the host object.
- * @chainable
- */
- _setAttrs : function(attrs, opts) {
- var attr;
- for (attr in attrs) {
- if ( attrs.hasOwnProperty(attr) ) {
- this.set(attr, attrs[attr], opts);
- }
- }
- return this;
- },
-
- /**
- * Utility method to help setup the event payload and fire the attribute change event.
- *
- * @method _fireAttrChange
- * @private
- * @param {String} attrName The name of the attribute
- * @param {String} subAttrName The full path of the property being changed,
- * if this is a sub-attribute value being change. Otherwise null.
- * @param {Any} currVal The current value of the attribute
- * @param {Any} newVal The new value of the attribute
- * @param {Object} opts Any additional event data to mix into the attribute change event's event facade.
- */
- _fireAttrChange : function(attrName, subAttrName, currVal, newVal, opts) {
- var host = this,
- eventName = attrName + CHANGE,
- state = host._state,
- facade,
- broadcast,
- evtCfg;
-
- if (!state.get(attrName, PUBLISHED)) {
-
- evtCfg = {
- queuable:false,
- defaultTargetOnly: true,
- defaultFn:host._defAttrChangeFn,
- silent:true
- };
-
- broadcast = state.get(attrName, BROADCAST);
- if (broadcast !== undefined) {
- evtCfg.broadcast = broadcast;
- }
-
- host.publish(eventName, evtCfg);
-
- state.add(attrName, PUBLISHED, true);
- }
-
- facade = (opts) ? Y.merge(opts) : host._ATTR_E_FACADE;
-
- // Not using the single object signature for fire({type:..., newVal:...}), since
- // we don't want to override type. Changed to the fire(type, {newVal:...}) signature.
-
- // facade.type = eventName;
- facade.attrName = attrName;
- facade.subAttrName = subAttrName;
- facade.prevVal = currVal;
- facade.newVal = newVal;
-
- // host.fire(facade);
- host.fire(eventName, facade);
- },
-
- /**
- * Default function for attribute change events.
- *
- * @private
- * @method _defAttrChangeFn
- * @param {EventFacade} e The event object for attribute change events.
- */
- _defAttrChangeFn : function(e) {
- if (!this._setAttrVal(e.attrName, e.subAttrName, e.prevVal, e.newVal)) {
- Y.log('State not updated and stopImmediatePropagation called for attribute: ' + e.attrName + ' , value:' + e.newVal, 'warn', 'attribute');
- // Prevent "after" listeners from being invoked since nothing changed.
- e.stopImmediatePropagation();
- } else {
- e.newVal = this.get(e.attrName);
- }
- }
- };
-
- // Basic prototype augment - no lazy constructor invocation.
- Y.mix(AttributeEvents, EventTarget, false, null, 1);
-
- Y.AttributeEvents = AttributeEvents;
View
1  src/attribute/js/AttributeExtras.js
@@ -31,6 +31,7 @@
* methods for Attribute management such as modifyAttrs(), removeAttr and reset()
*
* @class AttributeExtras
+ * @extensionfor AttributeCore
*/
function AttributeExtras() {}
View
33 build/attribute-events/attribute-events-debug.js → src/attribute/js/AttributeObservable.js
@@ -1,5 +1,3 @@
-YUI.add('attribute-events', function (Y, NAME) {
-
/**
* The attribute module provides an augmentable Attribute implementation, which
* adds configurable attributes and attribute change events to the class being
@@ -11,11 +9,11 @@ YUI.add('attribute-events', function (Y, NAME) {
*/
/**
- * The attribute-events submodule provides augmentable attribute change event support
+ * The `attribute-observable` submodule provides augmentable attribute change event support
* for AttributeCore based implementations.
*
* @module attribute
- * @submodule attribute-events
+ * @submodule attribute-observable
*/
var EventTarget = Y.EventTarget,
@@ -27,18 +25,20 @@ YUI.add('attribute-events', function (Y, NAME) {
* Provides an augmentable implementation of attribute change events for
* AttributeCore.
*
- * @class AttributeEvents
+ * @class AttributeObservable
+ * @extensionfor AttributeCore
* @uses EventTarget
*/
- function AttributeEvents() {
+ function AttributeObservable() {
// Perf tweak - avoid creating event literals if not required.
this._ATTR_E_FACADE = {};
+
EventTarget.call(this, {emitFacade:true});
}
- AttributeEvents._ATTR_CFG = [BROADCAST];
+ AttributeObservable._ATTR_CFG = [BROADCAST];
- AttributeEvents.prototype = {
+ AttributeObservable.prototype = {
/**
* Sets the value of an attribute.
@@ -187,9 +187,18 @@ YUI.add('attribute-events', function (Y, NAME) {
};
// Basic prototype augment - no lazy constructor invocation.
- Y.mix(AttributeEvents, EventTarget, false, null, 1);
+ Y.mix(AttributeObservable, EventTarget, false, null, 1);
- Y.AttributeEvents = AttributeEvents;
+ Y.AttributeObservable = AttributeObservable;
-
-}, '@VERSION@', {"requires": ["event-custom"]});
+ /**
+ The `AttributeEvents` class extension was deprecated in YUI 3.8.0 and is now
+ an alias for the `AttributeObservable` class extension. Use that class
+ extnesion instead. This alias will be removed in a future version of YUI.
+
+ @class AttributeEvents
+ @uses EventTarget
+ @deprecated Use `AttributeObservable` instead.
+ @see AttributeObservable
+ **/
+ Y.AttributeEvents = AttributeObservable;
View
107 src/attribute/js/ComplexAttribute.js
@@ -4,16 +4,16 @@
* @module attribute
* @submodule attribute-complex
* @for Attribute
+ * @deprecated AttributeComplex's overrides are now part of AttributeCore.
*/
- var O = Y.Object,
- DOT = ".";
-
- Y.Attribute.Complex = function() {};
- Y.Attribute.Complex.prototype = {
+ var Attribute = Y.Attribute;
+ Attribute.Complex = function() {};
+ Attribute.Complex.prototype = {
+
/**
- * Utility method to split out simple attribute name/value pairs ("x")
+ * Utility method to split out simple attribute name/value pairs ("x")
* from complex attribute name/value pairs ("x.y.z"), so that complex
* attributes can be keyed by the top level attribute name.
*
@@ -21,44 +21,17 @@
* @param {Object} valueHash An object with attribute name/value pairs
*
* @return {Object} An object literal with 2 properties - "simple" and "complex",
- * containing simple and complex attribute values respectively keyed
+ * containing simple and complex attribute values respectively keyed
* by the top level attribute name, or null, if valueHash is falsey.
*
* @private
*/
- _normAttrVals : function(valueHash) {
- var vals = {},
- subvals = {},
- path,
- attr,
- v, k;
-
- if (valueHash) {
- for (k in valueHash) {
- if (valueHash.hasOwnProperty(k)) {
- if (k.indexOf(DOT) !== -1) {
- path = k.split(DOT);
- attr = path.shift();
- v = subvals[attr] = subvals[attr] || [];
- v[v.length] = {
- path : path,
- value: valueHash[k]
- };
- } else {
- vals[k] = valueHash[k];
- }
- }
- }
- return { simple:vals, complex:subvals };
- } else {
- return null;
- }
- },
-
+ _normAttrVals : Attribute.prototype._normAttrVals,
+
/**
* Returns the initial value of the given attribute from
- * either the default configuration provided, or the
- * over-ridden value if it exists in the set of initValues
+ * either the default configuration provided, or the
+ * over-ridden value if it exists in the set of initValues
* provided and the attribute is not read-only.
*
* @param {String} attr The name of the attribute
@@ -70,59 +43,9 @@
* @method _getAttrInitVal
* @private
*/
- _getAttrInitVal : function(attr, cfg, initValues) {
-
- var val = cfg.value,
- valFn = cfg.valueFn,
- tmpVal,
- initValSet = false,
- simple,
- complex,
- i,
- l,
- path,
- subval,
- subvals;
-
- if (!cfg.readOnly && initValues) {
- // Simple Attributes
- simple = initValues.simple;
- if (simple && simple.hasOwnProperty(attr)) {
- val = simple[attr];
- initValSet = true;
- }
- }
-
- if (valFn && !initValSet) {
- if (!valFn.call) {
- valFn = this[valFn];
- }
- if (valFn) {
- tmpVal = valFn.call(this, attr);
- val = tmpVal;
- }
- }
-
- if (!cfg.readOnly && initValues) {
-
- // Complex Attributes (complex values applied, after simple, in case both are set)
- complex = initValues.complex;
-
- if (complex && complex.hasOwnProperty(attr) && (val !== undefined) && (val !== null)) {
- subvals = complex[attr];
- for (i = 0, l = subvals.length; i < l; ++i) {
- path = subvals[i].path;
- subval = subvals[i].value;
- O.setValue(val, path, subval);
- }
- }
- }
-
- return val;
- }
+ _getAttrInitVal : Attribute.prototype._getAttrInitVal
+
};
- Y.mix(Y.Attribute, Y.Attribute.Complex, true, null, 1);
-
- // Consistency with the rest of the Attribute addons for now.
- Y.AttributeComplex = Y.Attribute.Complex;
+ // Consistency with the rest of the Attribute addons for now.
+ Y.AttributeComplex = Attribute.Complex;
View
7 src/attribute/meta/attribute.json
@@ -12,7 +12,7 @@
"oop"
]
},
- "attribute-events": {
+ "attribute-observable": {
"requires" : [
"event-custom"
]
@@ -20,7 +20,7 @@
"attribute-base": {
"requires": [
"attribute-core",
- "attribute-events",
+ "attribute-observable",
"attribute-extras"
]
},
@@ -28,6 +28,9 @@
"requires": [
"attribute-base"
]
+ },
+ "attribute-events": {
+ "use": ["attribute-observable"]
}
}
}
View
2  src/attribute/tests/unit/assets/attribute-core-tests.js
@@ -376,7 +376,7 @@ YUI.add('attribute-core-tests', function(Y) {
testProtect : function() {
var h = this.createHost();
- var q = h._protectAttrs(AttrHost.ATTRS);
+ var q = Y.Attribute.protectAttrs(AttrHost.ATTRS);
Y.Assert.areNotSame(AttrHost.ATTRS, q);
Y.Assert.areEqual(Y.dump(AttrHost.ATTRS), Y.dump(q));
View
2  src/attribute/tests/unit/assets/attribute-tests.js
@@ -1297,7 +1297,7 @@ YUI.add('attribute-tests', function(Y) {
testProtect : function() {
var h = this.createHost();
- var q = h._protectAttrs(AttrHost.ATTRS);
+ var q = Y.Attribute.protectAttrs(AttrHost.ATTRS);
Y.Assert.areNotSame(AttrHost.ATTRS, q);
Y.Assert.areEqual(Y.dump(AttrHost.ATTRS), Y.dump(q));
View
12 src/base/HISTORY.md
@@ -1,6 +1,18 @@
Base Change History
===================
+3.8.0
+-----
+
+ * `Y.Base.create()` can now be used with `Y.BaseCore`, in addition to its
+ existing usage with `Y.Base`. This allows non-observable, BaseCore
+ hierarchies to use class extensions.
+
+ * Extracted the `Y.BaseObservable` class extension from `Y.Base`. This is a
+ new class extension for `Y.BaseCore` which adds support for observability.
+ This allows BaseCore subclasses to mix-in BaseObservable at runtime,
+ bringing their functionality up to that of `Y.Base`.
+
3.6.0
-----
View
13 src/base/build.json
@@ -1,14 +1,14 @@
{
"name": "base",
"builds": {
- "base-pluginhost": {
+ "base-core": {
"jsfiles": [
- "BasePluginHost.js"
+ "BaseCore.js"
]
},
- "base-core": {
+ "base-observable": {
"jsfiles": [
- "BaseCore.js"
+ "BaseObservable.js"
]
},
"base-base": {
@@ -20,6 +20,11 @@
"jsfiles": [
"BaseBuild.js"
]
+ },
+ "base-pluginhost": {
+ "jsfiles": [
+ "BasePluginHost.js"
+ ]
}
}
}
View
283 src/base/js/Base.js
@@ -1,75 +1,53 @@
/**
- * The base module provides the Base class, which objects requiring attribute and custom event support can extend.
- * The module also provides two ways to reuse code - It augments Base with the Plugin.Host interface which provides
+ * The base module provides the Base class, which objects requiring attribute and custom event support can extend.
+ * The module also provides two ways to reuse code - It augments Base with the Plugin.Host interface which provides
* plugin support and also provides the BaseCore.build method which provides a way to build custom classes using extensions.
*
* @module base
*/
/**
- * The base-base submodule provides the Base class without the Plugin support, provided by Plugin.Host,
+ * The base-base submodule provides the Base class without the Plugin support, provided by Plugin.Host,
* and without the extension support provided by BaseCore.build.
*
* @module base
* @submodule base-base
*/
- /**
- * The base module provides the Base class, which objects requiring attribute and custom event support can extend.
- * The module also provides two ways to reuse code - It augments Base with the Plugin.Host interface which provides
- * plugin support and also provides the Base.build method which provides a way to build custom classes using extensions.
- *
- * @module base
- */
-
- /**
- * The base-base submodule provides the Base class without the Plugin support, provided by Plugin.Host,
- * and without the extension support provided by Base.build.
- *
- * @module base
- * @submodule base-base
- */
- var L = Y.Lang,
-
- DESTROY = "destroy",
- INIT = "init",
-
- BUBBLETARGETS = "bubbleTargets",
- _BUBBLETARGETS = "_bubbleTargets",
-
- BaseCore = Y.BaseCore,
- AttributeCore = Y.AttributeCore,
- Attribute = Y.Attribute;
+ var AttributeCore = Y.AttributeCore,
+ AttributeExtras = Y.AttributeExtras,
+ BaseCore = Y.BaseCore,
+ BaseObservable = Y.BaseObservable;
/**
* <p>
- * A base class which objects requiring attributes and custom event support can
- * extend. Base also handles the chaining of initializer and destructor methods across
- * the hierarchy as part of object construction and destruction. Additionally, attributes configured
- * through the static <a href="#property_ATTRS">ATTRS</a> property for each class
+ * A base class which objects requiring attributes and custom event support can
+ * extend. Base also handles the chaining of initializer and destructor methods across
+ * the hierarchy as part of object construction and destruction. Additionally, attributes configured
+ * through the static <a href="#property_ATTRS">ATTRS</a> property for each class
* in the hierarchy will be initialized by Base.
* </p>
*
* <p>
- * The static <a href="#property_NAME">NAME</a> property of each class extending
- * from Base will be used as the identifier for the class, and is used by Base to prefix
+ * The static <a href="#property_NAME">NAME</a> property of each class extending
+ * from Base will be used as the identifier for the class, and is used by Base to prefix
* all events fired by instances of that class.
* </p>
*
* @class Base
* @constructor
* @uses BaseCore
- * @uses Attribute
+ * @uses BaseObservable
* @uses AttributeCore
- * @uses AttributeEvents
+ * @uses AttributeObservable
* @uses AttributeExtras
* @uses EventTarget
*
- * @param {Object} config Object with configuration property name/value pairs. The object can be
+ * @param {Object} config Object with configuration property name/value pairs. The object can be
* used to provide default values for the objects published attributes.
*
* <p>
- * The config object can also contain the following non-attribute properties, providing a convenient
+ * The config object can also contain the following non-attribute properties, providing a convenient
* way to configure events listeners and plugins for the instance, as part of the constructor call:
* </p>
*
@@ -86,10 +64,12 @@
*/
function Base() {
BaseCore.apply(this, arguments);
+ BaseObservable.apply(this, arguments);
+ AttributeExtras.apply(this, arguments);
}
/**
- * The list of properties which can be configured for
+ * The list of properties which can be configured for
* each attribute (e.g. setter, getter, writeOnce, readOnly etc.)
*
* @property _ATTR_CFG
@@ -97,29 +77,28 @@
* @static
* @private
*/
- Base._ATTR_CFG = Attribute._ATTR_CFG.concat("cloneDefaultValue");
- Base._ATTR_CFG_HASH = Y.Array.hash(Base._ATTR_CFG);
+ Base._ATTR_CFG = BaseCore._ATTR_CFG.concat(BaseObservable._ATTR_CFG);
/**
- * The array of non-attribute configuration properties supported by this class.
- *
- * `Base` supports "on", "after", "plugins" and "bubbleTargets" properties,
- * which are not set up as attributes.
+ * The array of non-attribute configuration properties supported by this class.
*
- * This property is primarily required so that when
+ * `Base` supports "on", "after", "plugins" and "bubbleTargets" properties,
+ * which are not set up as attributes.
+ *
+ * This property is primarily required so that when
* <a href="#property__allowAdHocAttrs">`_allowAdHocAttrs`</a> is enabled by
- * a class, non-attribute configurations don't get added as ad-hoc attributes.
+ * a class, non-attribute configurations don't get added as ad-hoc attributes.
*
* @property _NON_ATTRS_CFG
* @type Array
* @static
* @private
*/
- Base._NON_ATTRS_CFG = BaseCore._NON_ATTRS_CFG.concat(["on", "after", "bubbleTargets"]);
+ Base._NON_ATTRS_CFG = BaseCore._NON_ATTRS_CFG.concat(BaseObservable._NON_ATTRS_CFG);
/**
* <p>
- * The string to be used to identify instances of
+ * The string to be used to identify instances of
* this class, for example in prefixing events.
* </p>
* <p>
@@ -131,211 +110,29 @@
* @type String
* @static
*/
- Base.NAME = "base";
+ Base.NAME = 'base';
/**
- * The default set of attributes which will be available for instances of this class, and
- * their configuration. In addition to the configuration properties listed by
- * Attribute's <a href="Attribute.html#method_addAttr">addAttr</a> method, the attribute
+ * The default set of attributes which will be available for instances of this class, and
+ * their configuration. In addition to the configuration properties listed by
+ * Attribute's <a href="Attribute.html#method_addAttr">addAttr</a> method, the attribute
* can also be configured with a "cloneDefaultValue" property, which defines how the statically
- * defined value field should be protected ("shallow", "deep" and false are supported values).
+ * defined value field should be protected ("shallow", "deep" and false are supported values).
*
- * By default if the value is an object literal or an array it will be "shallow" cloned, to
+ * By default if the value is an object literal or an array it will be "shallow" cloned, to
* protect the default value.
*
* @property ATTRS
* @type Object
* @static
*/
- Base.ATTRS = AttributeCore.prototype._protectAttrs(BaseCore.ATTRS);
-
- Base.prototype = {
-
- /**
- * Internal construction logic for Base.
- *
- * @method _initBase
- * @param {Object} config The constructor configuration object
- * @private
- */
- _initBase: function(cfg) {
- Y.log('init called', 'life', 'base');
-
- this._eventPrefix = this.constructor.EVENT_PREFIX || this.constructor.NAME;
+ Base.ATTRS = AttributeCore.protectAttrs(BaseCore.ATTRS);
- Y.BaseCore.prototype._initBase.call(this, cfg);
- },
-
- /**
- * Initializes Attribute
- *
- * @method _initAttribute
- * @private
- */
- _initAttribute: function(cfg) {
- Attribute.call(this);
- this._yuievt.config.prefix = this._eventPrefix;
- },
-
- /**
- * Utility method to define the attribute hash used to filter/whitelist property mixes for
- * this class.
- *
- * @method _attrCfgHash
- * @private
- */
- _attrCfgHash: function() {
- return Base._ATTR_CFG_HASH;
- },
-
- /**
- * Init lifecycle method, invoked during construction.
- * Fires the init event prior to setting up attributes and
- * invoking initializers for the class hierarchy.
- *
- * @method init
- * @chainable
- * @param {Object} config Object with configuration property name/value pairs
- * @return {Base} A reference to this object
- */
- init: function(config) {
- /**
- * <p>
- * Lifecycle event for the init phase, fired prior to initialization.
- * Invoking the preventDefault() method on the event object provided
- * to subscribers will prevent initialization from occuring.
- * </p>
- * <p>
- * Subscribers to the "after" momemt of this event, will be notified
- * after initialization of the object is complete (and therefore
- * cannot prevent initialization).
- * </p>
- *
- * @event init
- * @preventable _defInitFn
- * @param {EventFacade} e Event object, with a cfg property which
- * refers to the configuration object passed to the constructor.
- */
- this.publish(INIT, {
- queuable:false,
- fireOnce:true,
- defaultTargetOnly:true,
- defaultFn:this._defInitFn
- });
-
- this._preInitEventCfg(config);
-
- this.fire(INIT, {cfg: config});
-
- return this;
- },
-
- /**
- * Handles the special on, after and target properties which allow the user to
- * easily configure on and after listeners as well as bubble targets during
- * construction, prior to init.
- *
- * @private
- * @method _preInitEventCfg
- * @param {Object} config The user configuration object
- */
- _preInitEventCfg : function(config) {
- if (config) {
- if (config.on) {
- this.on(config.on);
- }
- if (config.after) {
- this.after(config.after);
- }
- }
-
- var i, l, target,
- userTargets = (config && BUBBLETARGETS in config);
-
- if (userTargets || _BUBBLETARGETS in this) {
- target = userTargets ? (config && config.bubbleTargets) : this._bubbleTargets;
- if (L.isArray(target)) {
- for (i = 0, l = target.length; i < l; i++) {
- this.addTarget(target[i]);
- }
- } else if (target) {
- this.addTarget(target);
- }
- }
- },
-
- /**
- * <p>
- * Destroy lifecycle method. Fires the destroy
- * event, prior to invoking destructors for the
- * class hierarchy.
- * </p>
- * <p>
- * Subscribers to the destroy
- * event can invoke preventDefault on the event object, to prevent destruction
- * from proceeding.
- * </p>
- * @method destroy
- * @return {Base} A reference to this object
- * @chainable
- */
- destroy: function() {
- Y.log('destroy called', 'life', 'base');
-
- /**
- * <p>
- * Lifecycle event for the destroy phase,
- * fired prior to destruction. Invoking the preventDefault
- * method on the event object provided to subscribers will
- * prevent destruction from proceeding.
- * </p>
- * <p>
- * Subscribers to the "after" moment of this event, will be notified
- * after destruction is complete (and as a result cannot prevent
- * destruction).
- * </p>
- * @event destroy
- * @preventable _defDestroyFn
- * @param {EventFacade} e Event object
- */
- this.publish(DESTROY, {
- queuable:false,
- fireOnce:true,
- defaultTargetOnly:true,
- defaultFn: this._defDestroyFn
- });
- this.fire(DESTROY);
-
- this.detachAll();
- return this;
- },
-
- /**
- * Default init event handler
- *
- * @method _defInitFn
- * @param {EventFacade} e Event object, with a cfg property which
- * refers to the configuration object passed to the constructor.
- * @protected
- */
- _defInitFn : function(e) {
- this._baseInit(e.cfg);
- },
-
- /**
- * Default destroy event handler
- *
- * @method _defDestroyFn
- * @param {EventFacade} e Event object
- * @protected
- */
- _defDestroyFn : function(e) {
- this._baseDestroy(e.cfg);
- }
- };
-
- Y.mix(Base, Attribute, false, null, 1);
Y.mix(Base, BaseCore, false, null, 1);
+ Y.mix(Base, AttributeExtras, false, null, 1);
+
+ // Needs to be `true`, to overwrite methods from `BaseCore`.
+ Y.mix(Base, BaseObservable, true, null, 1);
// Fix constructor
Base.prototype.constructor = Base;
View
99 src/base/js/BaseBuild.js
@@ -7,16 +7,55 @@
* @submodule base-build
* @for Base
*/
- var Base = Y.Base,
- L = Y.Lang,
+ var BaseCore = Y.BaseCore,
+ Base = Y.Base,
+ L = Y.Lang,
+
INITIALIZER = "initializer",
- DESTRUCTOR = "destructor",
- build,
- arrayAggregator = function (prop, r, s) {
- if (s[prop]) {
- r[prop] = (r[prop] || []).concat(s[prop]);
- }
- };
+ DESTRUCTOR = "destructor",
+ AGGREGATES = ["_PLUG", "_UNPLUG"],
+
+ build;
+
+ // Utility function used in `_buildCfg` to aggregate array values into a new
+ // array from the sender constructor to the reciver constructor.
+ function arrayAggregator(prop, r, s) {
+ if (s[prop]) {
+ r[prop] = (r[prop] || []).concat(s[prop]);
+ }
+ }
+
+ // Utility function used in `_buildCfg` to aggregate `_ATTR_CFG` array
+ // values from the sender constructor into a new array on reciver's
+ // constructor, and clear the cached hash.
+ function attrCfgAggregator(prop, r, s) {
+ if (s._ATTR_CFG) {
+ arrayAggregator.apply(null, arguments);
+
+ // Clear cached hash.
+ r._ATTR_CFG_HASH = null;
+ }
+ }
+
+ // Utility function used in `_buildCfg` to aggregate ATTRS configs from one
+ // the sender constructor to the reciver constructor.
+ function attrsAggregator(prop, r, s) {
+ var sAttrs, rAttrs, a;
+
+ r.ATTRS = r.ATTRS || {};
+
+ if (s.ATTRS) {
+ sAttrs = s.ATTRS;
+ rAttrs = r.ATTRS;
+
+ for (a in sAttrs) {
+ if (sAttrs.hasOwnProperty(a)) {
+ rAttrs[a] = rAttrs[a] || {};
+ Y.mix(rAttrs[a], sAttrs[a], true);
+ }
+ }
+ }
+ }
Base._build = function(name, main, extensions, px, sx, cfg) {
@@ -381,9 +420,9 @@
/**
* The build configuration for the Base class.
*
- * Defines the static fields which need to be aggregated
- * when the Base class is used as the main class passed to
- * the <a href="#method_Base.build">Base.build</a> method.
+ * Defines the static fields which need to be aggregated when the Base class
+ * is used as the main class passed to the
+ * <a href="#method_Base.build">Base.build</a> method.
*
* @property _buildCfg
* @type Object
@@ -391,27 +430,23 @@
* @final
* @private
*/
- Base._buildCfg = {
- custom : {
- ATTRS : function(prop, r, s) {
-
- r.ATTRS = r.ATTRS || {};
-
- if (s.ATTRS) {
+ BaseCore._buildCfg = {
+ custom: {
+ ATTRS : attrsAggregator,
+ _ATTR_CFG : attrCfgAggregator,
+ _NON_ATTRS_CFG: arrayAggregator
+ },
- var sAttrs = s.ATTRS,
- rAttrs = r.ATTRS,
- a;
+ aggregates: AGGREGATES.concat()
+ };
- for (a in sAttrs) {
- if (sAttrs.hasOwnProperty(a)) {
- rAttrs[a] = rAttrs[a] || {};
- Y.mix(rAttrs[a], sAttrs[a], true);
- }
- }
- }
- },
- _NON_ATTRS_CFG : arrayAggregator
+ // Makes sure Base and BaseCore use separate `_buildCfg` objects.
+ Base._buildCfg = {
+ custom: {
+ ATTRS : attrsAggregator,
+ _ATTR_CFG : attrCfgAggregator,
+ _NON_ATTRS_CFG: arrayAggregator
},
- aggregates : ["_PLUG", "_UNPLUG"]
+
+ aggregates: AGGREGATES.concat()
};
View
40 src/base/js/BaseCore.js
@@ -81,7 +81,6 @@
* @private
*/
BaseCore._ATTR_CFG = AttributeCore._ATTR_CFG.concat("cloneDefaultValue");
- BaseCore._ATTR_CFG_HASH = Y.Array.hash(BaseCore._ATTR_CFG);
/**
* The array of non-attribute configuration properties supported by this class.
@@ -180,6 +179,7 @@
* @private
*/
_initBase : function(config) {
+ Y.log('init called', 'life', 'base');
Y.stamp(this);
@@ -213,7 +213,7 @@
* @private
*/
_initAttribute: function() {
- AttributeCore.apply(this);
+ AttributeCore.call(this);
},
/**
@@ -379,14 +379,21 @@
* @private
*/
_initHierarchyData : function() {
- var c = this.constructor,
+ var ctor = this.constructor,
+ c,
i,
l,
+ attrCfg,
+ attrCfgHash,
+ needsAttrCfgHash = !ctor._ATTR_CFG_HASH,
nonAttrsCfg,
nonAttrs = (this._allowAdHocAttrs) ? {} : null,
classes = [],
attrs = [];
+ // Start with `this` instance's constructor.
+ c = ctor;
+
while (c) {
// Add to classes
classes[classes.length] = c;
@@ -396,8 +403,20 @@
attrs[attrs.length] = c.ATTRS;
}
+ // Aggregate ATTR cfg whitelist.
+ if (needsAttrCfgHash) {
+ attrCfg = c._ATTR_CFG;
+ attrCfgHash = attrCfgHash || {};
+
+ if (attrCfg) {
+ for (i = 0, l = attrCfg.length; i < l; i += 1) {
+ attrCfgHash[attrCfg[i]] = true;
+ }
+ }
+ }
+
if (this._allowAdHocAttrs) {
- nonAttrsCfg = c._NON_ATTRS_CFG;
+ nonAttrsCfg = c._NON_ATTRS_CFG;
if (nonAttrsCfg) {
for (i = 0, l = nonAttrsCfg.length; i < l; i++) {
nonAttrs[nonAttrsCfg[i]] = true;
@@ -408,20 +427,25 @@
c = c.superclass ? c.superclass.constructor : null;
}
+ // Cache computed `_ATTR_CFG_HASH` on the constructor.
+ if (needsAttrCfgHash) {
+ ctor._ATTR_CFG_HASH = attrCfgHash;
+ }
+
this._classes = classes;
this._nonAttrs = nonAttrs;
this._attrs = this._aggregateAttrs(attrs);
},
/**
- * Utility method to define the attribute hash used to filter/whitelist property mixes for
- * this class.
- *
+ * Utility method to define the attribute hash used to filter/whitelist property mixes for
+ * this class.
+ *
* @method _attrCfgHash
* @private
*/
_attrCfgHash: function() {
- return BaseCore._ATTR_CFG_HASH;
+ return this.constructor._ATTR_CFG_HASH;
},
/**
View
198 src/base/js/BaseObservable.js
@@ -0,0 +1,198 @@
+ /**
+ The `base-observable` submodule adds observability to Base's lifecycle and
+ attributes, and also make it an `EventTarget`.
+
+ @module base
+ @submodule base-observable
+ **/
+ var L = Y.Lang,
+
+ DESTROY = "destroy",
+ INIT = "init",
+
+ BUBBLETARGETS = "bubbleTargets",
+ _BUBBLETARGETS = "_bubbleTargets",
+
+ AttributeObservable = Y.AttributeObservable,
+ BaseCore = Y.BaseCore;
+
+ /**
+ Provides an augmentable implementation of lifecycle and attribute events for
+ `BaseCore`.
+
+ @class BaseObservable
+ @extensionfor BaseCore
+ @uses AttributeObservable
+ @uses EventTarget
+ @since 3.8.0
+ **/
+ function BaseObservable() {}
+
+ BaseObservable._ATTR_CFG = AttributeObservable._ATTR_CFG.concat();
+ BaseObservable._NON_ATTRS_CFG = ["on", "after", "bubbleTargets"];
+
+ BaseObservable.prototype = {
+
+ /**
+ * Initializes Attribute
+ *
+ * @method _initAttribute
+ * @private
+ */
+ _initAttribute: function(cfg) {
+ BaseCore.prototype._initAttribute.apply(this, arguments);
+ AttributeObservable.call(this);
+
+ this._eventPrefix = this.constructor.EVENT_PREFIX || this.constructor.NAME;