Skip to content
Browse files

Adjust change listener interface

-   Content changes are now range changes.
-   Property changes are now a mixin, included in all collections.
-   All mixins now have a constructor and prototype.
-   Lists no longer implement range change notifications since they cannot
    produce a proper index, suitable for mirroring those changes on another
    collection.
-   Replace add each content change with mapped change.
-   Redocumented change listeners.
  • Loading branch information...
1 parent e3634f6 commit 975f8a324e49e4e42362d1f67cc4cdbf62f52743 @kriskowal committed
View
208 README.md
@@ -14,6 +14,10 @@ Object.
except that they use and return nodes instead of integer indicies in
analogous functions.
+ Lists have a `head` `Node`. The node type is available as `Node` on
+ the list prototype and can be overridden by inheritors. Each node
+ has `prev` and `next` properties.
+
- **Set(values, equals, hash, content)**
A collection of unique values. The set can be iterated in the order
@@ -803,57 +807,142 @@ SortedArrayMap, FastSet, FastMap, Dict)
Repeats the given value either finite times or indefinitely.
-### Observables
-
-`List`, `Set`, and `SortedSet` can be observed for content changes.
-
-A content change handler can have various forms. The simplest form is a
-function that accepts `plus`, `minus`, and `index` as arguments where
-`plus` is an array of added values, `minus` is an array of deleted
-values, and `index` is the position of the change or undefined. In that
-case, `this` will be the collection that dispatches the event.
-
-Alternately, you can dispatch events to a handler object. If the
-handler has a `handleContentChange` function (for noticing a change
-after it has occurred) or a `handleContentWillChange` function (for
-noticing a change before it has occurred), the event will be dispatched
-to one of those. The function has the same `(plus, minus, index)`
-signature.
-
-You can also dispatch change events to a DOM-compatible
-`handleEvent(event)` method, in which case the handler will receive an
-event with `phase`, `currentTarget`, `target`, `plus`, `minus`, and
-`index` properties. `phase` is either `"before"` or `"after"`. The
-targets are both the collection in flux.
-
-- `(plus, minus, index)`
-- `handleContentChange(plus, minus, index)`
-- `handleContentWillChange(plus, minus, index)`
-- `handleEvent({phase, currentTarget, target, plus, minus, index})`
-
-The methods of the collection for managing content changes are generic,
-in the `observable` module, and have the following forms:
-
-- `addContentChangeListener(listener, beforeChange)`
-- `removeContentChangeListener(listener, beforeChange)`
-- `dispatchContentChange(plus, minus, index)`
-- `addBeforeContentChangeListener(listener)`
-- `removeBeforeContentChangeListener(listener)`
-- `dispatchBeforeContentChange(plus, minus, index)`
-- `getContentChangeDescriptor()`
-
-
-## List
-
-Lists are backed by a cyclic doubly-linked list with a head node. The
-nodes are returned by "find" methods and accepted by "slice" and
-"splice" as representatives of positions within the list. Their
-properties and methods are part of the interface of the structure.
-
-- `prev`: the previous node, or the `head` of the list if this is the
- first node
-- `next`: the next node, or the `head` of the list if this is the last
- node
+### Change Listeners
+
+All collections support change listeners. There are three types of
+changes.
+
+- **property changes**
+
+ `PropertyChanges` from the `listen/property-changes` module can
+ configure listeners for property changes to specific keys of any
+ object.
+
+ With the `listen/array-changes` module required, `PropertyChanges`
+ can also listen to changes to the length and indexed properties of
+ an array. The only caveat is that watched arrays can only modify
+ their contents with method calls like `array.push`. All methods of
+ a watched array support change dispatch. In addition, arrays have a
+ `set` method to make setting the value at a particular index
+ observable.
+
+ - **PropertyChanges.addPropertyChangeListener(object, key,
+ listener, before)**
+ - **PropertyChanges.removePropertyChangeListener(object, key,
+ listener, before)**
+ - **PropertyChanges.dispatchPropertyChange(object, key, value,
+ before)**
+ - **PropertyChanges.addBeforePropertyChangeListener(object, key,
+ listener)**
+ - **PropertyChanges.removeBeforePropertyChangeListener(object,
+ key, listener)**
+ - **PropertyChanges.dispatchBeforePropertyChange(object, key,
+ value)**
+ - **PropertyChanges.getPropertyChangeDescriptor(object, key)**
+
+ All of these functions delegate to methods of the same name if one
+ exists on the object.
+
+ - **object.addPropertyChangeListener(key, listener, before)**
+ - **object.removePropertyChangeListener(key, listener, before)**
+ - **object.dispatchPropertyChange(key, value)**
+ - **object.addBeforePropertyChangeListener(key, listener)**
+ - **object.removeBeforePropertyChangeListener(key, listener)**
+ - **object.dispatchBeforePropertyChange(key, value)**
+ - **object.getPropertyChangeDescriptor(key)**
+
+ Additionally, `PropertyChanges.prototype` can be **mixed into**
+ other types of objects to support the property change dispatch
+ interface. All collections support this interface.
+
+ The **listener** for a property change receives the arguments
+ `value`, `key`, and `object`, just as a `forEach` or `map` callback.
+ The listener may alternately be a delegate object that implements
+ one of these methods:
+
+- **map changes**
+
+ A map change listener receives notifications for the creation,
+ removal, or updates for any item in a map data structure.
+
+ With the `listen/array-changes` module required, `Array` can also
+ dispatch map changes for the values at each index.
+
+ - **collection.addMapChangeListener(listener)**
+ - **collection.removeMapChangeListener(listener)**
+ - **collection.dispatchMapChange(key, value)**
+ - **collection.addBeforeMapChangeListener(listener)**
+ - **collection.removeBeforeMapChangeListener(listener)**
+ - **collection.dispatchBeforeMapChange(key, value)**
+ - **collection.getMapChangeDescriptor()**
+
+ The **listener** for a map change receives the `value`, `key`, and
+ collection `object` as arguments, the same pattern as a `forEach` or
+ `map` callback. In the after change phase, a value of `undefined`
+ may indicate that the value was deleted or set to `undefined`. In
+ the before change phase, a value of `undefined` may indicate the the
+ value was added or was previously `undefined`.
+
+ The `listen/map-changes` module exports a map changes **mixin**.
+ The methods of `MaxChanges.prototype` can be copied to any
+ collection that needs this interface. Its mutation methods will
+ then need to dispatch map changes.
+
+- **range changes**
+
+ A range change listener receives notifications when a range of
+ values at a particular position is added, removed, or replaced
+ within an ordered collection.
+
+ - **collection.addRangeChangeListener(listener)**
+ - **collection.removeRangeChangeListener(listener)**
+ - **collection.dispatchRangeChange(plus, minus, index)**
+ - **collection.addBeforeRangeChangeListener(listener)**
+ - **collection.removeBeforeRangeChangeListener(listener)**
+ - **collection.dispatchBeforeRangeChange(plus, minus, index)**
+ - **collection.getRangeChangeDescriptor()**
+
+ The **listener** for a range change is a function that accepts
+ `plus`, `minus`, and `index` arguments. `plus` and `minus` are the
+ values that were added or removed at the `index`. Whatever
+ operation caused these changes is equivalent to:
+
+ ```javascript
+ var minus = collection.splice(index, minus.length, ...plus)
+ ```
+
+ The listener can alternately be an object that has either a
+ `handleRangeChange` or `handleRangeWillChange` method, depending on
+ whether the notification is dispatched after or before the change
+ takes effect. The arguments are the same either way, but `this`
+ will be the handler object.
+
+ If the listener is neither of the above, it can be a delegate that
+ implements a W3C-alike `handleEvent(event)` method. The event has
+ these properties:
+
+ - `phase` of `"before"` or `"after"`
+ - `currentTarget` is the object
+ - `target` is the object
+ - `plus`
+ - `minus`
+ - `index`
+
+ The following support range change dispatch:
+
+ - `Array`
+ - `SortedSet`
+ - `SortedArray`
+ - `SortedArraySet`
+
+ The `listen/range-changes` module exports a range changes **mixin**.
+ The methods of `RangeChanges.prototype` can be copied to any
+ collection that needs this interface. Its mutation methods will
+ need to dispatch the range changes.
+
+All **descriptors** are objects with the properties `changeListeners`
+and `willChangeListeners`. Both are arrays of listener functions or
+objects, in the order in which they were added.
## Set and Map
@@ -924,7 +1013,7 @@ tree.
## Object and Function Shims
The collection methods on the `Object` constructor all polymorphically
-defer to the corresponding method of any object that implements the
+delegate to the corresponding method of any object that implements the
method of the same name. So, `Object.has` can be used to check whether
a key exists on an object, or in any collection that implements `has`.
This permits the `Object` interface to be agnostic of the input type.
@@ -973,6 +1062,10 @@ a method, to aid in distinguishing "static" functions.
Goals
+- handle event for other change dispatch systems, in addition to range,
+ and the rest of the W3C event interface
+- automate the generation of the method support tables in readme and
+ normalize declaration order
- comprehensive specs and spec coverage tests
- map change dispatch and listeners
- fast list splicing
@@ -983,12 +1076,13 @@ More possible collections
- arc-map
- sorted-order (sorted, can contain duplicates, perhaps backed by splay
tree with relaxation on the uniqueness invariant)
-- sorted-multi-map (sorted, can contain duplicate entries, perhaps
- backed by sorted-list)
-- string-set (set of strings, backed by a trie)
+- sorted-multi-map (sorted, can contain duplicate entries, backed by
+ sorted-map)
+- trie-set
+- trie-map
- immutable-* (mutation functions return new objects that largely share
the previous version's internal state, some perhaps backed by a hash
trie)
-- array heap implementation
-- binary heap implementation
+- array-heap implementation
+- binary-heap implementation
View
229 collections.cat.js
132 additions, 97 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
211 collections.min.js
113 additions, 98 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
8 demo/observable-array-demo.js
@@ -1,17 +1,17 @@
-require("../observable-array");
+require("../listen/array-changes");
var array = [];
-Object.addOwnPropertyChangeListener(array, "length", function (length) {
+array.addPropertyChangeListener("length", function (length) {
console.log("changed", length);
});
-Object.addOwnPropertyChangeListener(array, 0, function (value) {
+array.addPropertyChangeListener(0, function (value) {
console.log("array[0] changed to", value);
});
-array.addEachContentChangeListener(function (value, key) {
+array.addMapChangeListener(function (value, key) {
console.log(key, value);
});
View
6 dict.js
@@ -3,6 +3,7 @@
var Shim = require("./shim");
var GenericCollection = require("./generic-collection");
var GenericMap = require("./generic-map");
+var PropertyChanges = require("./listen/property-changes");
// Burgled from https://github.com/domenic/dict
@@ -26,8 +27,9 @@ function unmangle(mangled) {
return mangled.slice(1);
}
-Object.addEach(Dict.prototype, GenericCollection);
-Object.addEach(Dict.prototype, GenericMap);
+Object.addEach(Dict.prototype, GenericCollection.prototype);
+Object.addEach(Dict.prototype, GenericMap.prototype);
+Object.addEach(Dict.prototype, PropertyChanges.prototype);
Dict.prototype.constructClone = function (values) {
return new this.constructor(values, this.mangle, this.content);
View
6 fast-map.js
@@ -4,6 +4,7 @@ var Shim = require("./shim");
var Set = require("./fast-set");
var GenericCollection = require("./generic-collection");
var GenericMap = require("./generic-map");
+var PropertyChanges = require("./listen/property-changes");
module.exports = FastMap;
@@ -30,8 +31,9 @@ function FastMap(values, equals, hash, content) {
this.addEach(values);
}
-Object.addEach(FastMap.prototype, GenericCollection);
-Object.addEach(FastMap.prototype, GenericMap);
+Object.addEach(FastMap.prototype, GenericCollection.prototype);
+Object.addEach(FastMap.prototype, GenericMap.prototype);
+Object.addEach(FastMap.prototype, PropertyChanges.prototype);
FastMap.prototype.constructClone = function (values) {
return new this.constructor(
View
6 fast-set.js
@@ -6,6 +6,7 @@ var List = require("./list");
var GenericCollection = require("./generic-collection");
var GenericSet = require("./generic-set");
var TreeLog = require("./tree-log");
+var PropertyChanges = require("./listen/property-changes");
var object_has = Object.prototype.hasOwnProperty;
@@ -26,8 +27,9 @@ function FastSet(values, equals, hash, content) {
this.addEach(values);
}
-Object.addEach(FastSet.prototype, GenericCollection);
-Object.addEach(FastSet.prototype, GenericSet);
+Object.addEach(FastSet.prototype, GenericCollection.prototype);
+Object.addEach(FastSet.prototype, GenericSet.prototype);
+Object.addEach(FastSet.prototype, PropertyChanges.prototype);
FastSet.prototype.Buckets = Dict;
FastSet.prototype.Bucket = List;
View
65 generic-collection.js
@@ -1,8 +1,11 @@
"use strict";
-var GenericCollection = exports;
+module.exports = GenericCollection;
+function GenericCollection() {
+ throw new Error("Can't construct. GenericCollection is a mixin.");
+}
-GenericCollection.addEach = function (values) {
+GenericCollection.prototype.addEach = function (values) {
if (values && Object(values) === values) {
if (typeof values.forEach === "function") {
values.forEach(this.add, this);
@@ -20,7 +23,7 @@ GenericCollection.addEach = function (values) {
}
};
-GenericCollection.deleteEach = function (values) {
+GenericCollection.prototype.deleteEach = function (values) {
values.forEach(function (value) {
this["delete"](value);
}, this);
@@ -29,14 +32,14 @@ GenericCollection.deleteEach = function (values) {
// all of the following functions are implemented in terms of "reduce".
// some need "constructClone".
-GenericCollection.forEach = function (callback /*, thisp*/) {
+GenericCollection.prototype.forEach = function (callback /*, thisp*/) {
var thisp = arguments[1];
return this.reduce(function (undefined, value, key, object, depth) {
callback.call(thisp, value, key, object, depth);
}, undefined);
};
-GenericCollection.map = function (callback /*, thisp*/) {
+GenericCollection.prototype.map = function (callback /*, thisp*/) {
var thisp = arguments[1];
var result = [];
this.reduce(function (undefined, value, key, object, depth) {
@@ -45,15 +48,15 @@ GenericCollection.map = function (callback /*, thisp*/) {
return result;
};
-GenericCollection.toArray = function () {
- return this.map(identity);
+GenericCollection.prototype.toArray = function () {
+ return this.map(Function.identity);
};
// this depends on stringable keys, which apply to Array and Iterator
// because they have numeric keys and all Maps since they may use
// strings as keys. List, Set, and SortedSet have nodes for keys, so
// toObject would not be meaningful.
-GenericCollection.toObject = function () {
+GenericCollection.prototype.toObject = function () {
var object = {};
this.reduce(function (undefined, value, key) {
object[key] = value;
@@ -61,7 +64,7 @@ GenericCollection.toObject = function () {
return object;
};
-GenericCollection.filter = function (callback /*, thisp*/) {
+GenericCollection.prototype.filter = function (callback /*, thisp*/) {
var thisp = arguments[1];
var result = this.constructClone();
this.reduce(function (undefined, value, key, object, depth) {
@@ -72,52 +75,50 @@ GenericCollection.filter = function (callback /*, thisp*/) {
return result;
};
-GenericCollection.every = function (callback /*, thisp*/) {
+GenericCollection.prototype.every = function (callback /*, thisp*/) {
var thisp = arguments[1];
return this.reduce(function (result, value, key, object, depth) {
return result && callback.call(thisp, value, key, object, depth);
}, true);
};
-GenericCollection.some = function (callback /*, thisp*/) {
+GenericCollection.prototype.some = function (callback /*, thisp*/) {
var thisp = arguments[1];
return this.reduce(function (result, value, key, object, depth) {
return result || callback.call(thisp, value, key, object, depth);
}, false);
};
-GenericCollection.all = function () {
+GenericCollection.prototype.all = function () {
return this.every(Boolean);
};
-GenericCollection.any = function () {
+GenericCollection.prototype.any = function () {
return this.some(Boolean);
};
-GenericCollection.min = function (compare) {
+GenericCollection.prototype.min = function (compare) {
compare = this.contentCompare || Object.compare;
return this.reduce(function (result, value) {
return compare(value, result) < 0 ? value : result;
}, Infinity);
};
-GenericCollection.max = function (compare) {
+GenericCollection.prototype.max = function (compare) {
compare = this.contentCompare || Object.compare;
return this.reduce(function (result, value) {
return compare(value, result) > 0 ? value : result;
}, -Infinity);
};
-GenericCollection.sum = function (zero) {
+GenericCollection.prototype.sum = function (zero) {
zero = zero === undefined ? 0 : zero;
- return this.reduce(add, zero);
+ return this.reduce(function (a, b) {
+ return a + b;
+ }, zero);
};
-function add(a, b) {
- return a + b;
-}
-
-GenericCollection.average = function (zero) {
+GenericCollection.prototype.average = function (zero) {
var sum = zero === undefined ? 0 : zero;
var count = zero === undefined ? 0 : zero;
this.reduce(function (undefined, value) {
@@ -127,7 +128,7 @@ GenericCollection.average = function (zero) {
return sum / count;
};
-GenericCollection.concat = function () {
+GenericCollection.prototype.concat = function () {
var result = this.constructClone(this);
for (var i = 0; i < arguments.length; i++) {
result.addEach(arguments[i]);
@@ -135,7 +136,7 @@ GenericCollection.concat = function () {
return result;
};
-GenericCollection.flatten = function () {
+GenericCollection.prototype.flatten = function () {
var self = this;
return this.reduce(function (result, array) {
array.forEach(function (value) {
@@ -145,7 +146,7 @@ GenericCollection.flatten = function () {
}, []);
};
-GenericCollection.zip = function () {
+GenericCollection.prototype.zip = function () {
var table = Array.prototype.slice.call(arguments);
table.unshift(this);
return transpose(table);
@@ -174,14 +175,14 @@ function transpose(table) {
return transpose;
}
-GenericCollection.sorted = function (compare, by, order) {
+GenericCollection.prototype.sorted = function (compare, by, order) {
compare = compare || this.contentCompare || Object.compare;
// account for comparators generated by Function.by
if (compare.by) {
by = compare.by;
compare = compare.compare || this.contentCompare || Object.compare;
} else {
- by = by || identity;
+ by = by || Function.identity;
}
if (order === undefined)
order = 1;
@@ -199,11 +200,11 @@ GenericCollection.sorted = function (compare, by, order) {
});
};
-GenericCollection.reversed = function () {
+GenericCollection.prototype.reversed = function () {
return this.constructClone(this).reverse();
};
-GenericCollection.clone = function (depth, memo) {
+GenericCollection.prototype.clone = function (depth, memo) {
if (depth === undefined) {
depth = Infinity;
} else if (depth === 0) {
@@ -216,11 +217,7 @@ GenericCollection.clone = function (depth, memo) {
return clone;
};
-function identity(value) {
- return value;
-}
-
-GenericCollection.only = function () {
+GenericCollection.prototype.only = function () {
if (this.length === 0) {
throw new Error("Can't get only value in empty collection.");
}
View
63 generic-map.js
@@ -1,15 +1,20 @@
"use strict";
-var GenericMap = exports;
-
var Object = require("./shim-object");
-var MapChanges = require("./dispatch/map-changes");
+var MapChanges = require("./listen/map-changes");
+var PropertyChanges = require("./listen/property-changes");
+
+module.exports = GenericMap;
+function GenericMap() {
+ throw new Error("Can't construct. GenericMap is a mixin.");
+}
-Object.addEach(GenericMap, MapChanges);
+Object.addEach(GenericMap.prototype, MapChanges.prototype);
+Object.addEach(GenericMap.prototype, PropertyChanges.prototype);
// all of these methods depend on the constructor providing a `store` set
-GenericMap.addEach = function (values) {
+GenericMap.prototype.addEach = function (values) {
if (values && Object(values) === values) {
if (typeof values.forEach === "function") {
// copy map-alikes
@@ -32,7 +37,7 @@ GenericMap.addEach = function (values) {
}
}
-GenericMap.get = function (key, defaultValue) {
+GenericMap.prototype.get = function (key, defaultValue) {
var item = this.store.get(new this.Item(key));
if (item) {
return item.value;
@@ -43,7 +48,7 @@ GenericMap.get = function (key, defaultValue) {
}
};
-GenericMap.set = function (key, value) {
+GenericMap.prototype.set = function (key, value) {
var item = new this.Item(key, value);
var found = this.store.get(item);
var grew = false;
@@ -70,15 +75,15 @@ GenericMap.set = function (key, value) {
return grew;
};
-GenericMap.add = function (value, key) {
+GenericMap.prototype.add = function (value, key) {
return this.set(key, value);
};
-GenericMap.has = function (key) {
+GenericMap.prototype.has = function (key) {
return this.store.has(new this.Item(key));
};
-GenericMap['delete'] = function (key) {
+GenericMap.prototype['delete'] = function (key) {
var item = new this.Item(key);
if (this.store.has(item)) {
var from = this.store.get(item).value;
@@ -95,48 +100,40 @@ GenericMap['delete'] = function (key) {
return false;
};
-GenericMap.clear = function () {
+GenericMap.prototype.clear = function () {
this.store.clear();
this.length = 0;
};
-GenericMap.reduce = function (callback, basis, thisp) {
+GenericMap.prototype.reduce = function (callback, basis, thisp) {
return this.store.reduce(function (basis, item) {
return callback.call(thisp, basis, item.value, item.key, this);
}, basis, this);
};
-GenericMap.reduceRight = function (callback, basis, thisp) {
+GenericMap.prototype.reduceRight = function (callback, basis, thisp) {
return this.store.reduceRight(function (basis, item) {
return callback.call(thisp, basis, item.value, item.key, this);
}, basis, this);
};
-GenericMap.keys = function () {
- return this.map(getKey);
+GenericMap.prototype.keys = function () {
+ return this.map(function (value, key) {
+ return key;
+ });
};
-function getKey(value, key) {
- return key;
-}
-
-GenericMap.values = function () {
- return this.map(getValue);
+GenericMap.prototype.values = function () {
+ return this.map(Function.identity);
};
-function getValue(value) {
- return value;
-}
-
-GenericMap.items = function () {
- return this.map(getItem);
+GenericMap.prototype.items = function () {
+ return this.map(function (value, key) {
+ return [key, value];
+ });
};
-function getItem(value, key) {
- return [key, value];
-}
-
-GenericMap.equals = function (that, equals) {
+GenericMap.prototype.equals = function (that, equals) {
equals = equals || Object.equals;
if (this === that) {
return true;
@@ -152,7 +149,7 @@ GenericMap.equals = function (that, equals) {
}
};
-GenericMap.Item = Item;
+GenericMap.prototype.Item = Item;
function Item(key, value) {
this.key = key;
View
9 generic-order.js
@@ -1,9 +1,12 @@
var Object = require("./shim-object");
-var GenericOrder = exports;
+module.exports = GenericOrder;
+function GenericOrder() {
+ throw new Error("Can't construct. GenericOrder is a mixin.");
+}
-GenericOrder.equals = function (that, equals) {
+GenericOrder.prototype.equals = function (that, equals) {
equals = equals || this.contentEquals || Object.equals;
if (this === that) {
@@ -22,7 +25,7 @@ GenericOrder.equals = function (that, equals) {
);
};
-GenericOrder.compare = function (that, compare) {
+GenericOrder.prototype.compare = function (that, compare) {
compare = compare || this.contentCompare || Object.compare;
if (this === that) {
View
15 generic-set.js
@@ -1,31 +1,34 @@
-var GenericSet = exports;
+module.exports = GenericSet;
+function GenericSet() {
+ throw new Error("Can't construct. GenericSet is a mixin.");
+}
-GenericSet.union = function (that) {
+GenericSet.prototype.union = function (that) {
var union = this.constructClone(this);
union.addEach(that);
return union;
};
-GenericSet.intersection = function (that) {
+GenericSet.prototype.intersection = function (that) {
return this.constructClone(this.filter(function (value) {
return that.has(value);
}));
};
-GenericSet.difference = function (that) {
+GenericSet.prototype.difference = function (that) {
var union = this.constructClone(this);
union.deleteEach(that);
return union;
};
-GenericSet.symmetricDifference = function (that) {
+GenericSet.prototype.symmetricDifference = function (that) {
var union = this.union(that);
var intersection = this.intersection(that);
return union.difference(intersection);
};
-GenericSet.equals = function (that, equals) {
+GenericSet.prototype.equals = function (that, equals) {
var self = this;
return (
Object.can(that, "reduce") &&
View
2 iterator.js
@@ -36,7 +36,7 @@ function Iterator(iterable) {
}
-Object.addEach(Iterator.prototype, GenericCollection);
+Object.addEach(Iterator.prototype, GenericCollection.prototype);
// this is a bit of a cheat so flatten and such work with the generic
// reducible
View
48 list.js
@@ -5,7 +5,7 @@ module.exports = List;
var Shim = require("./shim");
var GenericCollection = require("./generic-collection");
var GenericOrder = require("./generic-order");
-var ContentChanges = require("./dispatch/content-changes");
+var PropertyChanges = require("./listen/property-changes");
function List(values, equals, content) {
if (!(this instanceof List)) {
@@ -20,9 +20,9 @@ function List(values, equals, content) {
this.addEach(values);
}
-Object.addEach(List.prototype, GenericCollection);
-Object.addEach(List.prototype, GenericOrder);
-Object.addEach(List.prototype, ContentChanges);
+Object.addEach(List.prototype, GenericCollection.prototype);
+Object.addEach(List.prototype, GenericOrder.prototype);
+Object.addEach(List.prototype, PropertyChanges.prototype);
List.prototype.constructClone = function (values) {
return new this.constructor(values, this.contentEquals, this.content);
@@ -68,14 +68,8 @@ List.prototype.get = function (value, equals) {
List.prototype['delete'] = function (value, equals) {
var found = this.findLast(value, equals);
if (found) {
- if (this.dispatchesContentChanges) {
- this.dispatchBeforeContentChange([], [value]);
- }
found['delete']();
this.length--;
- if (this.dispatchesContentChanges) {
- this.dispatchContentChange([], [value]);
- }
return true;
}
return false;
@@ -83,26 +77,13 @@ List.prototype['delete'] = function (value, equals) {
List.prototype.clear = function () {
var minus;
- if (this.dispatchesContentChanges) {
- minus = this.toArray();
- this.dispatchBeforeContentChange([], minus);
- }
this.head.next = this.head.prev = this.head;
this.length = 0;
- if (this.dispatchesContentChanges) {
- this.dispatchContentChange([], minus);
- }
};
List.prototype.add = function (value) {
- if (this.dispatchesContentChanges) {
- this.dispatchBeforeContentChange([value], []);
- }
this.head.addBefore(new this.Node(value));
this.length++;
- if (this.dispatchesContentChanges) {
- this.dispatchContentChange([value], []);
- }
return true;
};
@@ -110,15 +91,9 @@ List.prototype.push = function () {
var head = this.head;
for (var i = 0; i < arguments.length; i++) {
var value = arguments[i];
- if (this.dispatchesContentChanges) {
- this.dispatchBeforeContentChange([value], []);
- }
var node = new this.Node(value);
head.addBefore(node);
this.length++;
- if (this.dispatchesContentChanges) {
- this.dispatchContentChange([value], []);
- }
}
};
@@ -129,9 +104,6 @@ List.prototype.unshift = function () {
var node = new this.Node(value);
at.addAfter(node);
this.length++;
- if (this.dispatchesContentChanges) {
- this.dispatchContentChange([value], []);
- }
at = node;
}
};
@@ -141,14 +113,8 @@ List.prototype.pop = function () {
var head = this.head;
if (head.prev !== head) {
value = head.prev.value;
- if (this.dispatchesContentChanges) {
- this.dispatchBeforeContentChange([], [value]);
- }
head.prev['delete']();
this.length--;
- if (this.dispatchesContentChanges) {
- this.dispatchContentChange([], [value]);
- }
}
return value;
};
@@ -158,14 +124,8 @@ List.prototype.shift = function () {
var head = this.head;
if (head.prev !== head) {
value = head.next.value;
- if (this.dispatchesContentChanges) {
- this.dispatchBeforeContentChange([], [value]);
- }
head.next['delete']();
this.length--;
- if (this.dispatchesContentChanges) {
- this.dispatchContentChange([], [value]);
- }
}
return value;
};
View
26 dispatch/array-changes.js → listen/array-changes.js
@@ -16,7 +16,7 @@ require("../shim");
var List = require("../list");
var WeakMap = require("../weak-map");
var PropertyChanges = require("./property-changes");
-var ContentChanges = require("./content-changes");
+var RangeChanges = require("./range-changes");
var MapChanges = require("./map-changes");
var array_splice = Array.prototype.splice;
@@ -39,8 +39,9 @@ if (protoIsSupported) {
}
Array.prototype.makeObservable = array_makeObservable;
-Object.addEach(Array.prototype, ContentChanges);
-Object.addEach(Array.prototype, MapChanges);
+Object.addEach(Array.prototype, PropertyChanges.prototype);
+Object.addEach(Array.prototype, RangeChanges.prototype);
+Object.addEach(Array.prototype, MapChanges.prototype);
var observableArrayProperties = {
@@ -60,7 +61,7 @@ var observableArrayProperties = {
value: function reverse() {
// dispatch before change events
- this.dispatchBeforeContentChange(this, this, 0);
+ this.dispatchBeforeRangeChange(this, this, 0);
for (var i = 0; i < this.length; i++) {
PropertyChanges.dispatchBeforePropertyChange(this, i, this[i]);
this.dispatchBeforeMapChange(i, this[i]);
@@ -74,7 +75,7 @@ var observableArrayProperties = {
PropertyChanges.dispatchPropertyChange(this, i, this[i]);
this.dispatchMapChange(i, this[i]);
}
- this.dispatchContentChange(this, this, 0);
+ this.dispatchRangeChange(this, this, 0);
return this;
},
@@ -86,7 +87,7 @@ var observableArrayProperties = {
value: function sort() {
// dispatch before change events
- this.dispatchBeforeContentChange(this, this, 0);
+ this.dispatchBeforeRangeChange(this, this, 0);
for (var i = 0; i < this.length; i++) {
PropertyChanges.dispatchBeforePropertyChange(this, i, this[i]);
this.dispatchBeforeMapChange(i, this[i]);
@@ -100,7 +101,7 @@ var observableArrayProperties = {
PropertyChanges.dispatchPropertyChange(this, i, this[i]);
this.dispatchMapChange(i, this[i]);
}
- this.dispatchContentChange(this, this, 0);
+ this.dispatchRangeChange(this, this, 0);
return this;
},
@@ -121,7 +122,7 @@ var observableArrayProperties = {
if (diff) {
PropertyChanges.dispatchBeforePropertyChange(this, "length", this.length);
}
- this.dispatchBeforeContentChange(plus, minus, start);
+ this.dispatchBeforeRangeChange(plus, minus, start);
if (diff === 0) { // substring replacement
for (var i = start; i < start + plus.length; i++) {
PropertyChanges.dispatchBeforePropertyChange(this, i, this[i]);
@@ -155,10 +156,7 @@ var observableArrayProperties = {
this.dispatchMapChange(i, this[i]);
}
}
- // in addEachContentChange, the content change event may remove
- // some of the above dispatched listeners, so contentChange must
- // occur after ownPropertyChanges
- this.dispatchContentChange(plus, minus, start);
+ this.dispatchRangeChange(plus, minus, start);
if (diff) {
PropertyChanges.dispatchPropertyChange(this, "length", this.length);
}
@@ -237,8 +235,8 @@ var observableArrayProperties = {
configurable: true
},
- wipe: {
- value: function wipe() {
+ clear: {
+ value: function clear() {
return this.splice(0, this.length);
},
writable: true,
View
19 dispatch/map-changes.js → listen/map-changes.js
@@ -3,7 +3,10 @@
var WeakMap = require("../weak-map");
var List = require("../list");
-var DispatchMapChange = exports;
+module.exports = MapChanges;
+function MapChanges() {
+ throw new Error("Can't construct. MapChanges is a mixin.");
+}
var object_owns = Object.prototype.hasOwnProperty;
@@ -19,7 +22,7 @@ var object_owns = Object.prototype.hasOwnProperty;
}
*/
-DispatchMapChange.getMapChangeDescriptor = function () {
+MapChanges.prototype.getMapChangeDescriptor = function () {
if (!this.mapChangeDescriptor) {
this.mapChangeDescriptor = {
willChangeListeners: new List(),
@@ -29,7 +32,7 @@ DispatchMapChange.getMapChangeDescriptor = function () {
return this.mapChangeDescriptor;
};
-DispatchMapChange.addMapChangeListener = function (listener, beforeChange) {
+MapChanges.prototype.addMapChangeListener = function (listener, beforeChange) {
if (this.makeObservable && !this.dispatchMapChanges) {
// for Array
this.makeObservable();
@@ -45,7 +48,7 @@ DispatchMapChange.addMapChangeListener = function (listener, beforeChange) {
this.dispatchesMapChanges = true;
};
-DispatchMapChange.removeMapChangeListener = function (listener, beforeChange) {
+MapChanges.prototype.removeMapChangeListener = function (listener, beforeChange) {
var descriptor = this.getMapChangeDescriptor();
var listeners;
@@ -62,7 +65,7 @@ DispatchMapChange.removeMapChangeListener = function (listener, beforeChange) {
node["delete"]();
};
-DispatchMapChange.dispatchMapChange = function (key, value, beforeChange) {
+MapChanges.prototype.dispatchMapChange = function (key, value, beforeChange) {
var descriptor = this.getMapChangeDescriptor();
var listeners;
@@ -85,15 +88,15 @@ DispatchMapChange.dispatchMapChange = function (key, value, beforeChange) {
}, this);
};
-DispatchMapChange.addBeforeMapChangeListener = function (listener) {
+MapChanges.prototype.addBeforeMapChangeListener = function (listener) {
return this.addMapChangeListener(listener, true);
};
-DispatchMapChange.removeBeforeMapChangeListener = function (listener) {
+MapChanges.prototype.removeBeforeMapChangeListener = function (listener) {
return this.removeMapChangeListener(listener, true);
};
-DispatchMapChange.dispatchBeforeMapChange = function (key, value) {
+MapChanges.prototype.dispatchBeforeMapChange = function (key, value) {
return this.dispatchMapChange(key, value, true);
};
View
188 dispatch/property-changes.js → listen/property-changes.js
@@ -13,7 +13,6 @@
*/
var WeakMap = require("../weak-map");
-var List = require("../list");
var object_owns = Object.prototype.hasOwnProperty;
@@ -55,55 +54,62 @@ var propertyChangeDescriptors = new WeakMap();
*/
var overriddenObjectDescriptors = new WeakMap();
-exports.getPropertyChangeDescriptor = function (object, key) {
- if (!propertyChangeDescriptors.has(object)) {
- propertyChangeDescriptors.set(object, {});
+module.exports = PropertyChanges;
+
+function PropertyChanges() {
+ throw new Error("This is an abstract interface. Mix it. Don't construct it");
+}
+
+PropertyChanges.prototype.getPropertyChangeDescriptor = function (key) {
+ if (!propertyChangeDescriptors.has(this)) {
+ propertyChangeDescriptors.set(this, {});
}
- var objectPropertyChangeDescriptors = propertyChangeDescriptors.get(object);
+ var objectPropertyChangeDescriptors = propertyChangeDescriptors.get(this);
if (!object_owns.call(objectPropertyChangeDescriptors, key)) {
objectPropertyChangeDescriptors[key] = {
- willChangeListeners: new List(),
- changeListeners: new List()
+ willChangeListeners: [],
+ changeListeners: []
};
}
return objectPropertyChangeDescriptors[key];
};
-exports.hasPropertyChangeDescriptor = function (object, key) {
- if (!propertyChangeDescriptors.has(object)) {
+PropertyChanges.prototype.hasPropertyChangeDescriptor = function (key) {
+ if (!propertyChangeDescriptors.has(this)) {
return false;
}
if (!key) {
return true;
}
- var objectPropertyChangeDescriptors = propertyChangeDescriptors.get(object);
+ var objectPropertyChangeDescriptors = propertyChangeDescriptors.get(this);
if (!object_owns.call(objectPropertyChangeDescriptors, key)) {
return false;
}
return true;
};
-exports.addPropertyChangeListener = function (object, key, listener, beforeChange) {
- if (object.addPropertyChangeListener) {
- return object.addPropertyChangeListener(key, listener, beforeChange);
- }
- if (object.makeObservable && !object.isObservable) {
- object.makeObservable(); // particularly for observable arrays, for
+PropertyChanges.prototype.addPropertyChangeListener = function (key, listener, beforeChange) {
+ if (this.makeObservable && !this.isObservable) {
+ this.makeObservable(); // particularly for observable arrays, for
// their length property
}
- var descriptor = exports.getPropertyChangeDescriptor(object, key);
+ var descriptor = PropertyChanges.getPropertyChangeDescriptor(this, key);
var listeners;
if (beforeChange) {
listeners = descriptor.willChangeListeners;
} else {
listeners = descriptor.changeListeners;
}
- exports.makePropertyObservable(object, key);
+ PropertyChanges.makePropertyObservable(this, key);
listeners.push(listener);
};
-exports.removePropertyChangeListener = function (object, key, listener, beforeChange) {
- var descriptor = exports.getPropertyChangeDescriptor(object, key);
+PropertyChanges.prototype.addBeforePropertyChangeListener = function (key, listener) {
+ return PropertyChanges.addPropertyChangeListener(this, key, listener, true);
+};
+
+PropertyChanges.prototype.removePropertyChangeListener = function (key, listener, beforeChange) {
+ var descriptor = PropertyChanges.getPropertyChangeDescriptor(this, key);
var listeners;
if (beforeChange) {
@@ -112,19 +118,23 @@ exports.removePropertyChangeListener = function (object, key, listener, beforeCh
listeners = descriptor.changeListeners;
}
- var node = listeners.findLast(listener);
- if (!node) {
+ var index = listeners.lastIndexOf(listener);
+ if (index === -1) {
throw new Error("Can't remove listener: does not exist.");
}
- node["delete"]();
+ listeners.splice(index, 1);
if (listeners.length === 0) {
- exports.makePropertyUnobservable(object, key);
+ PropertyChanges.makePropertyUnobservable(this, key);
}
};
-exports.dispatchPropertyChange = function (object, key, value, beforeChange) {
- var descriptor = exports.getPropertyChangeDescriptor(object, key);
+PropertyChanges.prototype.removeBeforePropertyChangeListener = function (key, listener) {
+ return PropertyChanges.removePropertyChangeListener(this, key, listener, true);
+};
+
+PropertyChanges.prototype.dispatchPropertyChange = function (key, value, beforeChange) {
+ var descriptor = PropertyChanges.getPropertyChangeDescriptor(this, key);
var listeners;
if (beforeChange) {
@@ -149,42 +159,30 @@ exports.dispatchPropertyChange = function (object, key, value, beforeChange) {
listener
);
if (listener.call) {
- listener.call(thisp, value, key, object);
+ listener.call(thisp, value, key, this);
}
- });
-};
-
-exports.addBeforePropertyChangeListener = function (object, key, listener) {
- return exports.addPropertyChangeListener(object, key, listener, true);
-};
-
-exports.removeBeforePropertyChangeListener = function (object, key, listener) {
- return exports.removePropertyChangeListener(object, key, listener, true);
+ }, this);
};
-exports.dispatchBeforePropertyChange = function (object, key, value) {
- return exports.dispatchPropertyChange(object, key, value, true);
+PropertyChanges.prototype.dispatchBeforePropertyChange = function (key, listener) {
+ return PropertyChanges.dispatchPropertyChange(this, key, listener, true);
};
-exports.makePropertyObservable = function (object, key) {
+PropertyChanges.prototype.makePropertyObservable = function (key) {
// arrays are special. we do not support direct setting of properties
// on an array. instead, call .set(index, value). this is observable.
// 'length' property is observable for all mutating methods because
// our overrides explicitly dispatch that change.
- if (object instanceof Array) {
+ if (Array.isArray(this)) {
return;
}
- if (object.makePropertyObservable) {
- return object.makePropertyObservable(key);
- }
-
// memoize overridden property descriptor table
- if (!overriddenObjectDescriptors.has(object)) {
+ if (!overriddenObjectDescriptors.has(this)) {
overriddenPropertyDescriptors = {};
- overriddenObjectDescriptors.set(object, overriddenPropertyDescriptors);
+ overriddenObjectDescriptors.set(this, overriddenPropertyDescriptors);
}
- var overriddenPropertyDescriptors = overriddenObjectDescriptors.get(object);
+ var overriddenPropertyDescriptors = overriddenObjectDescriptors.get(this);
if (object_owns.call(overriddenPropertyDescriptors, key)) {
// if we have already recorded an overridden property descriptor,
@@ -195,7 +193,7 @@ exports.makePropertyObservable = function (object, key) {
// walk up the prototype chain to find a property descriptor for
// the property name
var overriddenDescriptor;
- var attached = object;
+ var attached = this;
var formerDescriptor = Object.getOwnPropertyDescriptor(attached, key);
do {
overriddenDescriptor = Object.getOwnPropertyDescriptor(attached, key);
@@ -247,9 +245,9 @@ exports.makePropertyObservable = function (object, key) {
if (value === overriddenDescriptor.value) {
return value;
}
- exports.dispatchBeforePropertyChange(this, key, overriddenDescriptor.value);
+ PropertyChanges.dispatchBeforePropertyChange(this, key, overriddenDescriptor.value);
overriddenDescriptor.value = value;
- exports.dispatchPropertyChange(this, key, value);
+ PropertyChanges.dispatchPropertyChange(this, key, value);
return value;
},
enumerable: overriddenDescriptor.enumerable,
@@ -272,7 +270,7 @@ exports.makePropertyObservable = function (object, key) {
if (value === formerValue) {
return value;
}
- exports.dispatchBeforePropertyChange(this, key, formerValue);
+ PropertyChanges.dispatchBeforePropertyChange(this, key, formerValue);
// call through to actual setter
if (overriddenDescriptor.set) {
overriddenDescriptor.set.apply(this, arguments)
@@ -284,7 +282,7 @@ exports.makePropertyObservable = function (object, key) {
}
// dispatch the new value: the given value if there is
// no getter, or the actual value if there is one
- exports.dispatchPropertyChange(this, key, value);
+ PropertyChanges.dispatchPropertyChange(this, key, value);
return value;
},
enumerable: overriddenDescriptor.enumerable,
@@ -292,26 +290,22 @@ exports.makePropertyObservable = function (object, key) {
};
}
- Object.defineProperty(object, key, newDescriptor);
+ Object.defineProperty(this, key, newDescriptor);
};
-exports.makePropertyUnobservable = function (object, key) {
+PropertyChanges.prototype.makePropertyUnobservable = function (key) {
// arrays are special. we do not support direct setting of properties
// on an array. instead, call .set(index, value). this is observable.
// 'length' property is observable for all mutating methods because
// our overrides explicitly dispatch that change.
- if (object instanceof Array) {
+ if (Array.isArray(this)) {
return;
}
- if (object.makePropertyUnobservable) {
- return object.makePropertyUnobservable(key);
- }
-
- if (!overriddenObjectDescriptors.has(object)) {
+ if (!overriddenObjectDescriptors.has(this)) {
throw new Error("Can't uninstall observer on property");
}
- var overriddenPropertyDescriptors = overriddenObjectDescriptors.get(object);
+ var overriddenPropertyDescriptors = overriddenObjectDescriptors.get(this);
if (!overriddenPropertyDescriptors[key]) {
throw new Error("Can't uninstall observer on property");
@@ -319,6 +313,76 @@ exports.makePropertyUnobservable = function (object, key) {
var overriddenDescriptor = overriddenPropertyDescriptors[key];
- Object.defineProperty(object, key, overriddenDescriptor);
+ Object.defineProperty(this, key, overriddenDescriptor);
+};
+
+// constructor functions
+
+PropertyChanges.getPropertyChangeDescriptor = function (object, key) {
+ if (object.getPropertyChangeDescriptor) {
+ return object.getPropertyChangeDescriptor(key);
+ } else {
+ return PropertyChanges.prototype.getPropertyChangeDescriptor.call(object, key);
+ }
+};
+
+PropertyChanges.hasPropertyChangeDescriptor = function (object, key) {
+ if (object.hasPropertyChangeDescriptor) {
+ return object.hasPropertyChangeDescriptor(key);
+ } else {
+ return PropertyChanges.prototype.hasPropertyChangeDescriptor.call(object, key);
+ }
+};
+
+PropertyChanges.addPropertyChangeListener = function (object, key, listener, beforeChange) {
+ if (object.addPropertyChangeListener) {
+ return object.addPropertyChangeListener(key, listener, beforeChange);
+ } else {
+ return PropertyChanges.prototype.addPropertyChangeListener.call(object, key, listener, beforeChange);
+ }
+};
+
+PropertyChanges.removePropertyChangeListener = function (object, key, listener, beforeChange) {
+ if (object.removePropertyChangeListener) {
+ return object.removePropertyChangeListener(key, listener, beforeChange);
+ } else {
+ return PropertyChanges.prototype.removePropertyChangeListener.call(object, key, listener, beforeChange);
+ }
+};
+
+PropertyChanges.dispatchPropertyChange = function (object, key, value, beforeChange) {
+ if (object.dispatchPropertyChange) {
+ return object.dispatchPropertyChange(key, value, beforeChange);
+ } else {
+ return PropertyChanges.prototype.dispatchPropertyChange.call(object, key, value, beforeChange);
+ }
+};
+
+PropertyChanges.addBeforePropertyChangeListener = function (object, key, listener) {
+ return PropertyChanges.addPropertyChangeListener(object, key, listener, true);
+};
+
+PropertyChanges.removeBeforePropertyChangeListener = function (object, key, listener) {
+ return PropertyChanges.removePropertyChangeListener(object, key, listener, true);
+};
+
+PropertyChanges.dispatchBeforePropertyChange = function (object, key, value) {
+ return PropertyChanges.dispatchPropertyChange(object, key, value, true);
+};
+
+PropertyChanges.makePropertyObservable = function (object, key) {
+ if (object.makePropertyObservable) {
+ return object.makePropertyObservable(key);
+ } else {
+ return PropertyChanges.prototype.makePropertyObservable.call(object, key);
+ }
+};
+
+PropertyChanges.makePropertyUnobservable = function (object, key) {
+ if (object.makePropertyUnobservable) {
+ return object.makePropertyUnobservable(key);
+ } else {
+ return PropertyChanges.prototype.makePropertyUnobservable.call(object, key);
+ }
};
View
43 dispatch/content-changes.js → listen/range-changes.js
@@ -4,9 +4,12 @@ var WeakMap = require("../weak-map");
var contentChangeDescriptors = new WeakMap(); // {isActive, willChangeListeners, changeListeners}
-var DispatchContentChange = exports;
+module.exports = RangeChanges;
+function RangeChanges() {
+ throw new Error("Can't construct. RangeChanges is a mixin.");
+}
-DispatchContentChange.getContentChangeDescriptor = function () {
+RangeChanges.prototype.getRangeChangeDescriptor = function () {
if (!contentChangeDescriptors.has(this)) {
contentChangeDescriptors.set(this, {
isActive: false,
@@ -17,13 +20,13 @@ DispatchContentChange.getContentChangeDescriptor = function () {
return contentChangeDescriptors.get(this);
};
-DispatchContentChange.addContentChangeListener = function (listener, beforeChange) {
+RangeChanges.prototype.addRangeChangeListener = function (listener, beforeChange) {
// a concession for objects like Array that are not inherently observable
if (!this.isObservable && this.makeObservable) {
this.makeObservable();
}
- var descriptor = this.getContentChangeDescriptor();
+ var descriptor = this.getRangeChangeDescriptor();
var listeners;
if (beforeChange) {
@@ -34,11 +37,11 @@ DispatchContentChange.addContentChangeListener = function (listener, beforeChang
// even if already registered
listeners.push(listener);
- this.dispatchesContentChanges = !!listeners.length;
+ this.dispatchesRangeChanges = !!listeners.length;
};
-DispatchContentChange.removeContentChangeListener = function (listener, beforeChange) {
- var descriptor = this.getContentChangeDescriptor();
+RangeChanges.prototype.removeRangeChangeListener = function (listener, beforeChange) {
+ var descriptor = this.getRangeChangeDescriptor();
var listeners;
if (beforeChange) {
@@ -52,11 +55,11 @@ DispatchContentChange.removeContentChangeListener = function (listener, beforeCh
throw new Error("Can't remove listener: does not exist.");
}
listeners.splice(index, 1);
- this.dispatchesContentChanges = !!listeners.length;
+ this.dispatchesRangeChanges = !!listeners.length;
};
-DispatchContentChange.dispatchContentChange = function (plus, minus, index, beforeChange) {
- var descriptor = this.getContentChangeDescriptor();
+RangeChanges.prototype.dispatchRangeChange = function (plus, minus, index, beforeChange) {
+ var descriptor = this.getRangeChangeDescriptor();
if (descriptor.isActive) {
return;
@@ -86,12 +89,12 @@ DispatchContentChange.dispatchContentChange = function (plus, minus, index, befo
index: index
});
} else {
- // support listener() listener.handleContentChange() and
- // listener.handleContentChange() forms
+ // support listener() listener.handleRangeChange() and
+ // listener.handleRangeChange() forms
if (beforeChange) {
- listener = listener.handleContentWillChange || listener;
+ listener = listener.handleRangeWillChange || listener;
} else {
- listener = listener.handleContentChange || listener;
+ listener = listener.handleRangeChange || listener;
}
if (listener.call) {
listener.call(this, plus, minus, index, beforeChange);
@@ -103,15 +106,15 @@ DispatchContentChange.dispatchContentChange = function (plus, minus, index, befo
}
};
-DispatchContentChange.addBeforeContentChangeListener = function (listener) {
- return this.addContentChangeListener(listener, true);
+RangeChanges.prototype.addBeforeRangeChangeListener = function (listener) {
+ return this.addRangeChangeListener(listener, true);
};
-DispatchContentChange.removeBeforeContentChangeListener = function (listener) {
- return this.removeContentChangeListener(listener, true);
+RangeChanges.prototype.removeBeforeRangeChangeListener = function (listener) {
+ return this.removeRangeChangeListener(listener, true);
};
-DispatchContentChange.dispatchBeforeContentChange = function (plus, minus, index) {
- return this.dispatchContentChange(plus, minus, index, true);
+RangeChanges.prototype.dispatchBeforeRangeChange = function (plus, minus, index) {
+ return this.dispatchRangeChange(plus, minus, index, true);
};
View
6 lru-map.js
@@ -4,6 +4,7 @@ var Shim = require("./shim");
var LruSet = require("./lru-set");
var GenericCollection = require("./generic-collection");
var GenericMap = require("./generic-map");
+var PropertyChanges = require("./listen/property-changes");
module.exports = LruMap;
@@ -31,8 +32,9 @@ function LruMap(values, maxLength, equals, hash, content) {
this.addEach(values);
}
-Object.addEach(LruMap.prototype, GenericCollection);
-Object.addEach(LruMap.prototype, GenericMap);
+Object.addEach(LruMap.prototype, GenericCollection.prototype);
+Object.addEach(LruMap.prototype, GenericMap.prototype);
+Object.addEach(LruMap.prototype, PropertyChanges.prototype);
LruMap.prototype.constructClone = function (values) {
return new this.constructor(
View
19 lru-set.js
@@ -4,7 +4,7 @@ var Shim = require("./shim");
var Set = require("./set");
var GenericCollection = require("./generic-collection");
var GenericSet = require("./generic-set");
-var ContentChanges = require("./dispatch/content-changes");
+var PropertyChanges = require("./listen/property-changes");
module.exports = LruSet;
@@ -25,9 +25,9 @@ function LruSet(values, maxLength, equals, hash, content) {
this.addEach(values);
}
-Object.addEach(LruSet.prototype, GenericCollection);
-Object.addEach(LruSet.prototype, GenericSet);
-Object.addEach(LruSet.prototype, ContentChanges);
+Object.addEach(LruSet.prototype, GenericCollection.prototype);
+Object.addEach(LruSet.prototype, GenericSet.prototype);
+Object.addEach(LruSet.prototype, PropertyChanges.prototype);
LruSet.prototype.constructClone = function (values) {
return new this.constructor(
@@ -106,17 +106,6 @@ LruSet.prototype.reduceRight = function () {
}, basis, this);
};
-LruSet.prototype.makeObservable = function () {
- var self = this;
- this.store.addBeforeContentChangeListener(function () {
- self.dispatchBeforeContentChange.apply(self, arguments);
- });
- this.store.addContentChangeListener(function () {
- self.dispatchContentChange.apply(self, arguments);
- });
- this.isObservable = true;
-};
-
LruSet.prototype.iterate = function () {
return this.store.iterate();
};
View
6 map.js
@@ -4,6 +4,7 @@ var Shim = require("./shim");
var Set = require("./set");
var GenericCollection = require("./generic-collection");
var GenericMap = require("./generic-map");
+var PropertyChanges = require("./listen/property-changes");
module.exports = Map;
@@ -30,8 +31,9 @@ function Map(values, equals, hash, content) {
this.addEach(values);
}
-Object.addEach(Map.prototype, GenericCollection);
-Object.addEach(Map.prototype, GenericMap); // overrides GenericCollection
+Object.addEach(Map.prototype, GenericCollection.prototype);
+Object.addEach(Map.prototype, GenericMap.prototype); // overrides GenericCollection
+Object.addEach(Map.prototype, PropertyChanges.prototype);
Map.prototype.constructClone = function (values) {
return new this.constructor(
View
5 minify
@@ -14,11 +14,14 @@
generic-set.js \
iterator.js \
list.js \
+ listen/array-changes.js \
+ listen/map-changes.js \
+ listen/property-changes.js \
+ listen/range-changes.js \
lru-map.js \
lru-set.js \
map.js \
multi-map.js \
- observable.js \
set.js \
shim-array-es5.js \
shim-array.js \
View
19 set.js
@@ -5,7 +5,7 @@ var List = require("./list");
var FastSet = require("./fast-set");
var GenericCollection = require("./generic-collection");
var GenericSet = require("./generic-set");
-var ContentChanges = require("./dispatch/content-changes");
+var PropertyChanges = require("./listen/property-changes");
module.exports = Set;
@@ -37,9 +37,9 @@ function Set(values, equals, hash, content) {
this.addEach(values);
}
-Object.addEach(Set.prototype, GenericCollection);
-Object.addEach(Set.prototype, GenericSet);
-Object.addEach(Set.prototype, ContentChanges);
+Object.addEach(Set.prototype, GenericCollection.prototype);
+Object.addEach(Set.prototype, GenericSet.prototype);
+Object.addEach(Set.prototype, PropertyChanges.prototype);
Set.prototype.Order = List;
Set.prototype.Store = FastSet;
@@ -116,17 +116,6 @@ Set.prototype.reduceRight = function (callback, basis /*, thisp*/) {
}, basis, this);
};
-Set.prototype.makeObservable = function () {
- var self = this;
- this.store.addBeforeContentChangeListener(function () {
- self.dispatchBeforeContentChange.apply(self, arguments);
- });
- this.store.addContentChangeListener(function () {
- self.dispatchContentChange.apply(self, arguments);
- });
- this.isObservable = true;
-};
-
Set.prototype.iterate = function () {
return this.order.iterate();
};
View
34 shim-array.js
@@ -25,21 +25,21 @@ Array.from = function (values) {
return array;
};
-Array.prototype.addEach = GenericCollection.addEach;
-Array.prototype.deleteEach = GenericCollection.deleteEach;
-Array.prototype.toArray = GenericCollection.toArray;
-Array.prototype.toObject = GenericCollection.toObject;
-Array.prototype.all = GenericCollection.all;
-Array.prototype.any = GenericCollection.any;
-Array.prototype.min = GenericCollection.min;
-Array.prototype.max = GenericCollection.max;
-Array.prototype.sum = GenericCollection.sum;
-Array.prototype.average = GenericCollection.average;
-Array.prototype.only = GenericCollection.only;
-Array.prototype.flatten = GenericCollection.flatten;
-Array.prototype.zip = GenericCollection.zip;
-Array.prototype.sorted = GenericCollection.sorted;
-Array.prototype.reversed = GenericCollection.reversed;
+Array.prototype.addEach = GenericCollection.prototype.addEach;
+Array.prototype.deleteEach = GenericCollection.prototype.deleteEach;
+Array.prototype.toArray = GenericCollection.prototype.toArray;
+Array.prototype.toObject = GenericCollection.prototype.toObject;
+Array.prototype.all = GenericCollection.prototype.all;
+Array.prototype.any = GenericCollection.prototype.any;
+Array.prototype.min = GenericCollection.prototype.min;
+Array.prototype.max = GenericCollection.prototype.max;
+Array.prototype.sum = GenericCollection.prototype.sum;
+Array.prototype.average = GenericCollection.prototype.average;
+Array.prototype.only = GenericCollection.prototype.only;
+Array.prototype.flatten = GenericCollection.prototype.flatten;
+Array.prototype.zip = GenericCollection.prototype.zip;
+Array.prototype.sorted = GenericCollection.prototype.sorted;
+Array.prototype.reversed = GenericCollection.prototype.reversed;
Array.prototype.constructClone = function (values) {
var clone = new this.constructor();
@@ -135,7 +135,7 @@ Array.prototype.compare = function (that, compare) {
}
if (!that || !Array.isArray(that)) {
- return GenericOrder.compare.call(this, that, compare);
+ return GenericOrder.prototype.compare.call(this, that, compare);
}
length = Math.min(this.length, that.length);
@@ -171,7 +171,7 @@ Array.prototype.equals = function (that) {
return true;
}
if (!that || !Array.isArray(that)) {
- return GenericOrder.equals.call(this, that);
+ return GenericOrder.prototype.equals.call(this, that);
}
if (length !== that.length) {
View
6 sorted-array-map.js
@@ -4,6 +4,7 @@ var Shim = require("./shim");
var SortedArraySet = require("./sorted-array-set");
var GenericCollection = require("./generic-collection");
var GenericMap = require("./generic-map");
+var PropertyChanges = require("./listen/property-changes");
module.exports = SortedArrayMap;
@@ -30,8 +31,9 @@ function SortedArrayMap(values, equals, compare, content) {
this.addEach(values);
}
-Object.addEach(SortedArrayMap.prototype, GenericCollection);
-Object.addEach(SortedArrayMap.prototype, GenericMap);
+Object.addEach(SortedArrayMap.prototype, GenericCollection.prototype);
+Object.addEach(SortedArrayMap.prototype, GenericMap.prototype);
+Object.addEach(SortedArrayMap.prototype, PropertyChanges.prototype);
SortedArrayMap.prototype.constructClone = function (values) {
return new this.constructor(
View
4 sorted-array-set.js
@@ -5,6 +5,7 @@ module.exports = SortedArraySet;
var Shim = require("./shim");
var SortedArray = require("./sorted-array");
var GenericSet = require("./generic-set");
+var PropertyChanges = require("./listen/property-changes");
function SortedArraySet(values, equals, compare, content) {
if (!(this instanceof SortedArraySet)) {
@@ -17,7 +18,8 @@ SortedArraySet.prototype = Object.create(SortedArray.prototype);
SortedArraySet.prototype.constructor = SortedArraySet;
-Object.addEach(SortedArraySet.prototype, GenericSet);
+Object.addEach(SortedArraySet.prototype, GenericSet.prototype);
+Object.addEach(SortedArraySet.prototype, PropertyChanges.prototype);
SortedArraySet.prototype.add = function (value) {
if (!this.has(value)) {
View
40 sorted-array.js
@@ -4,7 +4,8 @@ module.exports = SortedArray;
var Shim = require("./shim");
var GenericCollection = require("./generic-collection");
-var ContentChanges = require("./dispatch/content-changes");
+var PropertyChanges = require("./listen/property-changes");
+var RangeChanges = require("./listen/range-changes");
function SortedArray(values, equals, compare, content) {
if (!(this instanceof SortedArray)) {
@@ -24,8 +25,9 @@ function SortedArray(values, equals, compare, content) {
this.addEach(values);
}
-Object.addEach(SortedArray.prototype, GenericCollection);
-Object.addEach(SortedArray.prototype, ContentChanges);
+Object.addEach(SortedArray.prototype, GenericCollection.prototype);
+Object.addEach(SortedArray.prototype, PropertyChanges.prototype);
+Object.addEach(SortedArray.prototype, RangeChanges.prototype);
function search(array, value, compare) {
var first = 0;
@@ -114,13 +116,13 @@ SortedArray.prototype.get = function (value) {
SortedArray.prototype.add = function (value) {
var index = searchForInsertionIndex(this.array, value, this.contentCompare);
- if (this.dispatchesContentChanges) {
- this.dispatchBeforeContentChange([value], [], index);
+ if (this.dispatchesRangeChanges) {
+ this.dispatchBeforeRangeChange([value], [], index);
}
this.array.splice(index, 0, value);
this.length++;
- if (this.dispatchesContentChanges) {
- this.dispatchContentChange([value], [], index);
+ if (this.dispatchesRangeChanges) {
+ this.dispatchRangeChange([value], [], index);
}
return true;
};
@@ -128,13 +130,13 @@ SortedArray.prototype.add = function (value) {
SortedArray.prototype["delete"] = function (value) {
var index = searchFirst(this.array, value, this.contentCompare, this.contentEquals);
if (index !== -1) {
- if (this.dispatchesContentChanges) {
- this.dispatchBeforeContentChange([], [value], index);
+ if (this.dispatchesRangeChanges) {
+ this.dispatchBeforeRangeChange([], [value], index);
}
this.array.splice(index, 1);
this.length--;
- if (this.dispatchesContentChanges) {
- this.dispatchContentChange([], [value], index);
+ if (this.dispatchesRangeChanges) {
+ this.dispatchRangeChange([], [value], index);
}
return true;
} else {
@@ -194,13 +196,13 @@ SortedArray.prototype.swap = function (index, length, plus) {
length = Infinity;
}
var minus = this.slice(index, index + length);
- if (this.dispatchesContentChanges) {
- this.dispatchBeforeContentChange(plus, minus, index);
+ if (this.dispatchesRangeChanges) {
+ this.dispatchBeforeRangeChange(plus, minus, index);
}
this.array.splice(index, length);
this.addEach(plus);
- if (this.dispatchesContentChanges) {
- this.dispatchContentChange(plus, minus, index);
+ if (this.dispatchesRangeChanges) {
+ this.dispatchRangeChange(plus, minus, index);
}
return minus;
};
@@ -237,14 +239,14 @@ SortedArray.prototype.one = function () {
SortedArray.prototype.clear = function () {
var minus;
- if (this.dispatchesContentChanges) {
+ if (this.dispatchesRangeChanges) {
minus = this.array.slice();
- this.dispatchBeforeContentChange([], minus, 0);
+ this.dispatchBeforeRangeChange([], minus, 0);
}
this.length = 0;
this.array.clear();
- if (this.dispatchesContentChanges) {
- this.dispatchContentChange([], minus, 0);
+ if (this.dispatchesRangeChanges) {
+ this.dispatchRangeChange([], minus, 0);
}
};
View
6 sorted-map.js
@@ -4,6 +4,7 @@ var Shim = require("./shim");
var SortedSet = require("./sorted-set");
var GenericCollection = require("./generic-collection");
var GenericMap = require("./generic-map");
+var PropertyChanges = require("./listen/property-changes");
module.exports = SortedMap;
@@ -30,9 +31,10 @@ function SortedMap(values, equals, compare, content) {
this.addEach(values);
}
-Object.addEach(SortedMap.prototype, GenericCollection);
-Object.addEach(SortedMap.prototype, GenericMap);
+Object.addEach(SortedMap.prototype, GenericCollection.prototype);
+Object.addEach(SortedMap.prototype, GenericMap.prototype);
// GenericMap overrides GenericCollection, particularly addEach
+Object.addEach(SortedMap.prototype, PropertyChanges.prototype);
SortedMap.prototype.constructClone = function (values) {
return new this.constructor(
View
42 sorted-set.js
@@ -5,7 +5,8 @@ module.exports = SortedSet;
var Shim = require("./shim");
var GenericCollection = require("./generic-collection");
var GenericSet = require("./generic-set");
-var ContentChanges = require("./dispatch/content-changes");
+var PropertyChanges = require("./listen/property-changes");
+var RangeChanges = require("./listen/range-changes");
var TreeLog = require("./tree-log");
function SortedSet(values, equals, compare, content) {