diff --git a/listen/map-changes.js b/listen/map-changes.js index e2afe9d..5213d7e 100644 --- a/listen/map-changes.js +++ b/listen/map-changes.js @@ -17,8 +17,10 @@ var object_owns = Object.prototype.hasOwnProperty; here for shallow map changes. { + isActive:boolean willChangeListeners:Array(Function) changeListeners:Array(Function) + allowsNestedDispatch:boolean } */ @@ -32,24 +34,27 @@ MapChanges.prototype.getAllMapChangeDescriptors = function () { return mapChangeDescriptors.get(this); }; -MapChanges.prototype.getMapChangeDescriptor = function (token) { +MapChanges.prototype.getMapChangeDescriptor = function (token, allowsNestedDispatch) { var tokenChangeDescriptors = this.getAllMapChangeDescriptors(); token = token || ""; + allowsNestedDispatch = allowsNestedDispatch || false; if (!tokenChangeDescriptors.has(token)) { tokenChangeDescriptors.set(token, { + isActive: false, willChangeListeners: new List(), - changeListeners: new List() + changeListeners: new List(), + allowsNestedDispatch: allowsNestedDispatch }); } return tokenChangeDescriptors.get(token); }; -MapChanges.prototype.addMapChangeListener = function (listener, token, beforeChange) { +MapChanges.prototype.addMapChangeListener = function (listener, token, beforeChange, allowsNestedDispatch) { if (!this.isObservable && this.makeObservable) { // for Array this.makeObservable(); } - var descriptor = this.getMapChangeDescriptor(token); + var descriptor = this.getMapChangeDescriptor(token, allowsNestedDispatch); var listeners; if (beforeChange) { listeners = descriptor.willChangeListeners; @@ -97,7 +102,7 @@ MapChanges.prototype.dispatchMapChange = function (key, value, beforeChange) { var changeName = "Map" + (beforeChange ? "WillChange" : "Change"); descriptors.forEach(function (descriptor, token) { - if (descriptor.isActive) { + if (descriptor.isActive && ! descriptor.allowsNestedDispatch) { return; } else { descriptor.isActive = true; @@ -133,8 +138,8 @@ MapChanges.prototype.dispatchMapChange = function (key, value, beforeChange) { }, this); }; -MapChanges.prototype.addBeforeMapChangeListener = function (listener, token) { - return this.addMapChangeListener(listener, token, true); +MapChanges.prototype.addBeforeMapChangeListener = function (listener, token, allowsNestedDispatch) { + return this.addMapChangeListener(listener, token, true, allowsNestedDispatch); }; MapChanges.prototype.removeBeforeMapChangeListener = function (listener, token) { diff --git a/listen/property-changes.js b/listen/property-changes.js index e796935..47b3773 100644 --- a/listen/property-changes.js +++ b/listen/property-changes.js @@ -26,8 +26,10 @@ var object_owns = Object.prototype.hasOwnProperty; // corresponding handler method names. // // { +// isActive:boolean // willChangeListeners:{current, active:Array, ...method names} // changeListeners:{current, active:Array, ...method names} +// allowsNestedDispatch:boolean // } // Maybe remove entries from this table if the corresponding object no longer @@ -60,7 +62,7 @@ function PropertyChanges() { PropertyChanges.debug = true; -PropertyChanges.prototype.getOwnPropertyChangeDescriptor = function (key) { +PropertyChanges.prototype.getOwnPropertyChangeDescriptor = function (key, allowsNestedDispatch) { if (!this.__propertyChangeListeners__) { Object.defineProperty(this, "__propertyChangeListeners__", { value: {}, @@ -73,7 +75,9 @@ PropertyChanges.prototype.getOwnPropertyChangeDescriptor = function (key) { if (!object_owns.call(objectPropertyChangeDescriptors, key)) { var propertyName = String(key); propertyName = propertyName && propertyName[0].toUpperCase() + propertyName.slice(1); + allowsNestedDispatch = allowsNestedDispatch || false; objectPropertyChangeDescriptors[key] = { + isActive: false, willChangeListeners: { current: [], active: [], @@ -85,7 +89,8 @@ PropertyChanges.prototype.getOwnPropertyChangeDescriptor = function (key) { active: [], specificHandlerMethodName: "handle" + propertyName + "Change", genericHandlerMethodName: "handlePropertyChange" - } + }, + allowsNestedDispatch: allowsNestedDispatch }; } return objectPropertyChangeDescriptors[key]; @@ -105,12 +110,12 @@ PropertyChanges.prototype.hasOwnPropertyChangeDescriptor = function (key) { return true; }; -PropertyChanges.prototype.addOwnPropertyChangeListener = function (key, listener, beforeChange) { +PropertyChanges.prototype.addOwnPropertyChangeListener = function (key, listener, beforeChange, allowsNestedDispatch) { if (this.makeObservable && !this.isObservable) { this.makeObservable(); // particularly for observable arrays, for // their length property } - var descriptor = PropertyChanges.getOwnPropertyChangeDescriptor(this, key); + var descriptor = PropertyChanges.getOwnPropertyChangeDescriptor(this, key, allowsNestedDispatch); var listeners; if (beforeChange) { listeners = descriptor.willChangeListeners; @@ -127,8 +132,8 @@ PropertyChanges.prototype.addOwnPropertyChangeListener = function (key, listener }; }; -PropertyChanges.prototype.addBeforeOwnPropertyChangeListener = function (key, listener) { - return PropertyChanges.addOwnPropertyChangeListener(this, key, listener, true); +PropertyChanges.prototype.addBeforeOwnPropertyChangeListener = function (key, listener, allowsNestedDispatch) { + return PropertyChanges.addOwnPropertyChangeListener(this, key, listener, true, allowsNestedDispatch); }; PropertyChanges.prototype.removeOwnPropertyChangeListener = function (key, listener, beforeChange) { @@ -155,7 +160,7 @@ PropertyChanges.prototype.removeBeforeOwnPropertyChangeListener = function (key, PropertyChanges.prototype.dispatchOwnPropertyChange = function (key, value, beforeChange) { var descriptor = PropertyChanges.getOwnPropertyChangeDescriptor(this, key); - if (descriptor.isActive) { + if (descriptor.isActive && ! descriptor.allowsNestedDispatch) { return; } descriptor.isActive = true; diff --git a/listen/range-changes.js b/listen/range-changes.js index a249f5d..9d80a2e 100644 --- a/listen/range-changes.js +++ b/listen/range-changes.js @@ -3,7 +3,20 @@ var WeakMap = require("weak-map"); var Dict = require("../dict"); -var rangeChangeDescriptors = new WeakMap(); // {isActive, willChangeListeners, changeListeners} +/* + Object range change descriptors carry information necessary for adding, removing, + dispatching, and shorting events to listeners for range changes when values + are removed or added at an index or multiple indexes of a flat collection. + + { + isActive:boolean + willChangeListeners:Array(Function) + changeListeners:Array(Function) + allowsNestedDispatch:boolean + } +*/ + +var rangeChangeDescriptors = new WeakMap(); module.exports = RangeChanges; function RangeChanges() { @@ -17,26 +30,28 @@ RangeChanges.prototype.getAllRangeChangeDescriptors = function () { return rangeChangeDescriptors.get(this); }; -RangeChanges.prototype.getRangeChangeDescriptor = function (token) { +RangeChanges.prototype.getRangeChangeDescriptor = function (token, allowsNestedDispatch) { var tokenChangeDescriptors = this.getAllRangeChangeDescriptors(); token = token || ""; + allowsNestedDispatch = allowsNestedDispatch || false; if (!tokenChangeDescriptors.has(token)) { tokenChangeDescriptors.set(token, { isActive: false, changeListeners: [], - willChangeListeners: [] + willChangeListeners: [], + allowsNestedDispatch: allowsNestedDispatch }); } return tokenChangeDescriptors.get(token); }; -RangeChanges.prototype.addRangeChangeListener = function (listener, token, beforeChange) { +RangeChanges.prototype.addRangeChangeListener = function (listener, token, beforeChange, allowsNestedDispatch) { // a concession for objects like Array that are not inherently observable if (!this.isObservable && this.makeObservable) { this.makeObservable(); } - var descriptor = this.getRangeChangeDescriptor(token); + var descriptor = this.getRangeChangeDescriptor(token, allowsNestedDispatch); var listeners; if (beforeChange) { @@ -87,7 +102,7 @@ RangeChanges.prototype.dispatchRangeChange = function (plus, minus, index, befor var changeName = "Range" + (beforeChange ? "WillChange" : "Change"); descriptors.forEach(function (descriptor, token) { - if (descriptor.isActive) { + if (descriptor.isActive && ! descriptor.allowsNestedDispatch) { return; } else { descriptor.isActive = true; @@ -128,8 +143,8 @@ RangeChanges.prototype.dispatchRangeChange = function (plus, minus, index, befor }, this); }; -RangeChanges.prototype.addBeforeRangeChangeListener = function (listener, token) { - return this.addRangeChangeListener(listener, token, true); +RangeChanges.prototype.addBeforeRangeChangeListener = function (listener, token, allowsNestedDispatch) { + return this.addRangeChangeListener(listener, token, true, allowsNestedDispatch); }; RangeChanges.prototype.removeBeforeRangeChangeListener = function (listener, token) {