collections
Advanced tools
Comparing version 3.0.0 to 5.0.0
@@ -0,1 +1,12 @@ | ||
## v5.0.0 | ||
- Some backward compatibility breaking changes: | ||
- Native Maps, WeakMaps and Sets are now used when available | ||
- Aligns Map with latest standard with PR #137 | ||
- Use of a second argument for default value in get() is deprecated | ||
- keys(), values() and entries() now return an iterator per standards, | ||
methods returning an array are now keysArray(), valuesArray(), entriesArray() | ||
- It's not possible to create a Map by passing an anonymous object to the constructor, that feature is now available as Map.from(); | ||
- Introduces .from() method on constructors. | ||
## v3.0.0 | ||
@@ -2,0 +13,0 @@ |
@@ -31,2 +31,4 @@ "use strict"; | ||
Deque.from = GenericCollection.from; | ||
Deque.prototype.maxCapacity = (1 << 30) | 0; | ||
@@ -443,2 +445,1 @@ Deque.prototype.minCapacity = 16; | ||
} | ||
198
dict.js
"use strict"; | ||
var Shim = require("./shim"); | ||
var GenericCollection = require("./generic-collection"); | ||
var GenericMap = require("./generic-map"); | ||
var Dict = require("./_dict"); | ||
var PropertyChanges = require("./listen/property-changes"); | ||
var MapChanges = require("./listen/map-changes"); | ||
@@ -11,194 +10,3 @@ // Burgled from https://github.com/domenic/dict | ||
module.exports = Dict; | ||
function Dict(values, getDefault) { | ||
if (!(this instanceof Dict)) { | ||
return new Dict(values, getDefault); | ||
} | ||
getDefault = getDefault || Function.noop; | ||
this.getDefault = getDefault; | ||
this.store = Object.create(null); | ||
this.length = 0; | ||
this.addEach(values); | ||
} | ||
Dict.Dict = Dict; // hack so require("dict").Dict will work in MontageJS. | ||
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.getDefault); | ||
}; | ||
Dict.prototype.assertString = function (key) { | ||
if (typeof key !== "string") { | ||
throw new TypeError("key must be a string but Got " + key); | ||
} | ||
} | ||
Object.defineProperty(Dict.prototype,"$__proto__",{writable:true}); | ||
Object.defineProperty(Dict.prototype,"_hasProto",{ | ||
get:function() { | ||
return this.hasOwnProperty("$__proto__") && typeof this._protoValue !== "undefined"; | ||
} | ||
}); | ||
Object.defineProperty(Dict.prototype,"_protoValue",{ | ||
get:function() { | ||
return this["$__proto__"]; | ||
}, | ||
set: function(value) { | ||
this["$__proto__"] = value; | ||
} | ||
}); | ||
Dict.prototype.get = function (key, defaultValue) { | ||
this.assertString(key); | ||
if (key === "__proto__") { | ||
if (this._hasProto) { | ||
return this._protoValue; | ||
} else if (arguments.length > 1) { | ||
return defaultValue; | ||
} else { | ||
return this.getDefault(key); | ||
} | ||
} | ||
else { | ||
if (key in this.store) { | ||
return this.store[key]; | ||
} else if (arguments.length > 1) { | ||
return defaultValue; | ||
} else { | ||
return this.getDefault(key); | ||
} | ||
} | ||
}; | ||
Dict.prototype.set = function (key, value) { | ||
this.assertString(key); | ||
var isProtoKey = (key === "__proto__"); | ||
if (isProtoKey ? this._hasProto : key in this.store) { // update | ||
if (this.dispatchesMapChanges) { | ||
this.dispatchBeforeMapChange(key, isProtoKey ? this._protoValue : this.store[key]); | ||
} | ||
isProtoKey | ||
? this._protoValue = value | ||
: this.store[key] = value; | ||
if (this.dispatchesMapChanges) { | ||
this.dispatchMapChange(key, value); | ||
} | ||
return false; | ||
} else { // create | ||
if (this.dispatchesMapChanges) { | ||
this.dispatchBeforeMapChange(key, undefined); | ||
} | ||
this.length++; | ||
isProtoKey | ||
? this._protoValue = value | ||
: this.store[key] = value; | ||
if (this.dispatchesMapChanges) { | ||
this.dispatchMapChange(key, value); | ||
} | ||
return true; | ||
} | ||
}; | ||
Dict.prototype.has = function (key) { | ||
this.assertString(key); | ||
return key === "__proto__" ? this._hasProto : key in this.store; | ||
}; | ||
Dict.prototype["delete"] = function (key) { | ||
this.assertString(key); | ||
if (key === "__proto__") { | ||
if (this._hasProto) { | ||
if (this.dispatchesMapChanges) { | ||
this.dispatchBeforeMapChange(key, this._protoValue); | ||
} | ||
this._protoValue = undefined; | ||
this.length--; | ||
if (this.dispatchesMapChanges) { | ||
this.dispatchMapChange(key, undefined); | ||
} | ||
return true; | ||
} | ||
return false; | ||
} | ||
else { | ||
if (key in this.store) { | ||
if (this.dispatchesMapChanges) { | ||
this.dispatchBeforeMapChange(key, this.store[key]); | ||
} | ||
delete this.store[key]; | ||
this.length--; | ||
if (this.dispatchesMapChanges) { | ||
this.dispatchMapChange(key, undefined); | ||
} | ||
return true; | ||
} | ||
return false; | ||
} | ||
}; | ||
Dict.prototype.clear = function () { | ||
var key; | ||
if (this._hasProto) { | ||
if (this.dispatchesMapChanges) { | ||
this.dispatchBeforeMapChange("__proto__", this._protoValue); | ||
} | ||
this._protoValue = undefined; | ||
if (this.dispatchesMapChanges) { | ||
this.dispatchMapChange("__proto__", undefined); | ||
} | ||
} | ||
for (key in this.store) { | ||
if (this.dispatchesMapChanges) { | ||
this.dispatchBeforeMapChange(key, this.store[key]); | ||
} | ||
delete this.store[key]; | ||
if (this.dispatchesMapChanges) { | ||
this.dispatchMapChange(key, undefined); | ||
} | ||
} | ||
this.length = 0; | ||
}; | ||
Dict.prototype.reduce = function (callback, basis, thisp) { | ||
if(this._hasProto) { | ||
basis = callback.call(thisp, basis, "$__proto__", "__proto__", this); | ||
} | ||
var store = this.store; | ||
for (var key in this.store) { | ||
basis = callback.call(thisp, basis, store[key], key, this); | ||
} | ||
return basis; | ||
}; | ||
Dict.prototype.reduceRight = function (callback, basis, thisp) { | ||
var self = this; | ||
var store = this.store; | ||
basis = Object.keys(this.store).reduceRight(function (basis, key) { | ||
return callback.call(thisp, basis, store[key], key, self); | ||
}, basis); | ||
if(this._hasProto) { | ||
return callback.call(thisp, basis, this._protoValue, "__proto__", self); | ||
} | ||
return basis; | ||
}; | ||
Dict.prototype.one = function () { | ||
var key; | ||
for (key in this.store) { | ||
return this.store[key]; | ||
} | ||
return this._protoValue; | ||
}; | ||
Dict.prototype.toJSON = function () { | ||
return this.toObject(); | ||
}; | ||
Object.addEach(Dict.prototype, MapChanges.prototype); |
@@ -8,2 +8,3 @@ "use strict"; | ||
var PropertyChanges = require("./listen/property-changes"); | ||
var MapChanges = require("./listen/map-changes"); | ||
@@ -40,3 +41,5 @@ module.exports = FastMap; | ||
Object.addEach(FastMap.prototype, PropertyChanges.prototype); | ||
Object.addEach(FastMap.prototype, MapChanges.prototype); | ||
FastMap.from = GenericCollection.from; | ||
FastMap.prototype.constructClone = function (values) { | ||
@@ -59,2 +62,1 @@ return new this.constructor( | ||
} | ||
185
fast-set.js
"use strict"; | ||
var Shim = require("./shim"); | ||
var Dict = require("./dict"); | ||
var List = require("./list"); | ||
var GenericCollection = require("./generic-collection"); | ||
var GenericSet = require("./generic-set"); | ||
var TreeLog = require("./tree-log"); | ||
var FastSet = require("./_fast-set"); | ||
var PropertyChanges = require("./listen/property-changes"); | ||
var object_has = Object.prototype.hasOwnProperty; | ||
module.exports = FastSet; | ||
function FastSet(values, equals, hash, getDefault) { | ||
if (!(this instanceof FastSet)) { | ||
return new FastSet(values, equals, hash, getDefault); | ||
} | ||
equals = equals || Object.equals; | ||
hash = hash || Object.hash; | ||
getDefault = getDefault || Function.noop; | ||
this.contentEquals = equals; | ||
this.contentHash = hash; | ||
this.getDefault = getDefault; | ||
var self = this; | ||
this.buckets = new this.Buckets(null, function getDefaultBucket() { | ||
return new self.Bucket(); | ||
}); | ||
this.length = 0; | ||
this.addEach(values); | ||
} | ||
FastSet.FastSet = FastSet; // hack so require("fast-set").FastSet will work in MontageJS | ||
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; | ||
FastSet.prototype.constructClone = function (values) { | ||
return new this.constructor( | ||
values, | ||
this.contentEquals, | ||
this.contentHash, | ||
this.getDefault | ||
); | ||
}; | ||
FastSet.prototype.has = function (value) { | ||
var hash = this.contentHash(value); | ||
return this.buckets.get(hash).has(value); | ||
}; | ||
FastSet.prototype.get = function (value, equals) { | ||
if (equals) { | ||
throw new Error("FastSet#get does not support second argument: equals"); | ||
} | ||
var hash = this.contentHash(value); | ||
var buckets = this.buckets; | ||
if (buckets.has(hash)) { | ||
return buckets.get(hash).get(value); | ||
} else { | ||
return this.getDefault(value); | ||
} | ||
}; | ||
FastSet.prototype["delete"] = function (value, equals) { | ||
if (equals) { | ||
throw new Error("FastSet#delete does not support second argument: equals"); | ||
} | ||
var hash = this.contentHash(value); | ||
var buckets = this.buckets; | ||
if (buckets.has(hash)) { | ||
var bucket = buckets.get(hash); | ||
if (bucket["delete"](value)) { | ||
this.length--; | ||
if (bucket.length === 0) { | ||
buckets["delete"](hash); | ||
} | ||
return true; | ||
} | ||
} | ||
return false; | ||
}; | ||
FastSet.prototype.clear = function () { | ||
this.buckets.clear(); | ||
this.length = 0; | ||
}; | ||
FastSet.prototype.add = function (value) { | ||
var hash = this.contentHash(value); | ||
var buckets = this.buckets; | ||
if (!buckets.has(hash)) { | ||
buckets.set(hash, new this.Bucket(null, this.contentEquals)); | ||
} | ||
if (!buckets.get(hash).has(value)) { | ||
buckets.get(hash).add(value); | ||
this.length++; | ||
return true; | ||
} | ||
return false; | ||
}; | ||
FastSet.prototype.reduce = function (callback, basis /*, thisp*/) { | ||
var thisp = arguments[2]; | ||
var buckets = this.buckets; | ||
var index = 0; | ||
return buckets.reduce(function (basis, bucket) { | ||
return bucket.reduce(function (basis, value) { | ||
return callback.call(thisp, basis, value, index++, this); | ||
}, basis, this); | ||
}, basis, this); | ||
}; | ||
FastSet.prototype.one = function () { | ||
if (this.length > 0) { | ||
return this.buckets.one().one(); | ||
} | ||
}; | ||
FastSet.prototype.iterate = function () { | ||
return this.buckets.values().flatten().iterate(); | ||
}; | ||
FastSet.prototype.log = function (charmap, logNode, callback, thisp) { | ||
charmap = charmap || TreeLog.unicodeSharp; | ||
logNode = logNode || this.logNode; | ||
if (!callback) { | ||
callback = console.log; | ||
thisp = console; | ||
} | ||
callback = callback.bind(thisp); | ||
var buckets = this.buckets; | ||
var hashes = buckets.keys(); | ||
hashes.forEach(function (hash, index) { | ||
var branch; | ||
var leader; | ||
if (index === hashes.length - 1) { | ||
branch = charmap.fromAbove; | ||
leader = ' '; | ||
} else if (index === 0) { | ||
branch = charmap.branchDown; | ||
leader = charmap.strafe; | ||
} else { | ||
branch = charmap.fromBoth; | ||
leader = charmap.strafe; | ||
} | ||
var bucket = buckets.get(hash); | ||
callback.call(thisp, branch + charmap.through + charmap.branchDown + ' ' + hash); | ||
bucket.forEach(function (value, node) { | ||
var branch, below; | ||
if (node === bucket.head.prev) { | ||
branch = charmap.fromAbove; | ||
below = ' '; | ||
} else { | ||
branch = charmap.fromBoth; | ||
below = charmap.strafe; | ||
} | ||
var written; | ||
logNode( | ||
node, | ||
function (line) { | ||
if (!written) { | ||
callback.call(thisp, leader + ' ' + branch + charmap.through + charmap.through + line); | ||
written = true; | ||
} else { | ||
callback.call(thisp, leader + ' ' + below + ' ' + line); | ||
} | ||
}, | ||
function (line) { | ||
callback.call(thisp, leader + ' ' + charmap.strafe + ' ' + line); | ||
} | ||
); | ||
}); | ||
}); | ||
}; | ||
FastSet.prototype.logNode = function (node, write) { | ||
var value = node.value; | ||
if (Object(value) === value) { | ||
JSON.stringify(value, null, 4).split("\n").forEach(function (line) { | ||
write(" " + line); | ||
}); | ||
} else { | ||
write(" " + value); | ||
} | ||
}; | ||
@@ -8,6 +8,15 @@ "use strict"; | ||
var DOMTokenList = global.DOMTokenList || function(){}; | ||
GenericCollection.EmptyArray = Object.freeze([]); | ||
/* TODO: optimize for DOMTokenList and Array to use for() instead of forEach */ | ||
GenericCollection.prototype.addEach = function (values) { | ||
if (values && Object(values) === values) { | ||
//We want to eliminate everything but array like: Strings, Arrays, DOMTokenList | ||
if(values && (values instanceof Array || (values instanceof DOMTokenList) || values instanceof String)) { | ||
for (var i = 0; i < values.length; i++) { | ||
this.add(values[i], i); | ||
} | ||
} | ||
else if (values && Object(values) === values) { | ||
if (typeof values.forEach === "function") { | ||
@@ -26,7 +35,2 @@ values.forEach(this.add, this); | ||
} | ||
} else if (values && typeof values.length === "number") { | ||
// Strings | ||
for (var i = 0; i < values.length; i++) { | ||
this.add(values[i], i); | ||
} | ||
} | ||
@@ -111,2 +115,6 @@ return this; | ||
GenericCollection.from = function () { | ||
return this.apply(this,arguments); | ||
}; | ||
GenericCollection.prototype.filter = function (callback /*, thisp*/) { | ||
@@ -113,0 +121,0 @@ var thisp = arguments[1]; |
"use strict"; | ||
var Object = require("./shim-object"); | ||
var MapChanges = require("./listen/map-changes"); | ||
var PropertyChanges = require("./listen/property-changes"); | ||
var Iterator = require("./iterator"); | ||
@@ -12,5 +11,2 @@ module.exports = GenericMap; | ||
Object.addEach(GenericMap.prototype, MapChanges.prototype); | ||
Object.addEach(GenericMap.prototype, PropertyChanges.prototype); | ||
// all of these methods depend on the constructor providing a `store` set | ||
@@ -21,2 +17,3 @@ | ||
GenericMap.prototype.addEach = function (values) { | ||
var i; | ||
if (values && Object(values) === values) { | ||
@@ -38,3 +35,3 @@ if (typeof values.forEach === "function") { | ||
// Arguments | ||
for (var i = 0; i < values.length; i++) { | ||
for (i = 0; i < values.length; i++) { | ||
this.add(values[i], i); | ||
@@ -50,3 +47,3 @@ } | ||
// String | ||
for (var i = 0; i < values.length; i++) { | ||
for (i = 0; i < values.length; i++) { | ||
this.add(values[i], i); | ||
@@ -56,3 +53,3 @@ } | ||
return this; | ||
} | ||
}; | ||
@@ -64,2 +61,3 @@ GenericMap.prototype.get = function (key, defaultValue) { | ||
} else if (arguments.length > 1) { | ||
console.log("Use of a second argument as default value is deprecated to match standards"); | ||
return defaultValue; | ||
@@ -124,3 +122,3 @@ } else { | ||
GenericMap.prototype.clear = function () { | ||
var keys; | ||
var keys, key; | ||
if (this.dispatchesMapChanges) { | ||
@@ -130,3 +128,3 @@ this.forEach(function (value, key) { | ||
}, this); | ||
keys = this.keys(); | ||
keys = this.keysArray(); | ||
} | ||
@@ -136,5 +134,8 @@ this.store.clear(); | ||
if (this.dispatchesMapChanges) { | ||
keys.forEach(function (key) { | ||
for(var i=0;(key = keys[i]);i++) { | ||
this.dispatchMapChange(key); | ||
}, this); | ||
} | ||
// keys.forEach(function (key) { | ||
// this.dispatchMapChange(key); | ||
// }, this); | ||
} | ||
@@ -155,3 +156,3 @@ }; | ||
GenericMap.prototype.keys = function () { | ||
GenericMap.prototype.keysArray = function () { | ||
return this.map(function (value, key) { | ||
@@ -161,8 +162,14 @@ return key; | ||
}; | ||
GenericMap.prototype.keys = function () { | ||
return new Iterator(this.keysArray()); | ||
}; | ||
GenericMap.prototype.values = function () { | ||
GenericMap.prototype.valuesArray = function () { | ||
return this.map(Function.identity); | ||
}; | ||
GenericMap.prototype.values = function () { | ||
return new Iterator(this.valuesArray()); | ||
}; | ||
GenericMap.prototype.entries = function () { | ||
GenericMap.prototype.entriesArray = function () { | ||
return this.map(function (value, key) { | ||
@@ -172,6 +179,9 @@ return [key, value]; | ||
}; | ||
GenericMap.prototype.entries = function () { | ||
return new Iterator(this.entriesArray()); | ||
}; | ||
// XXX deprecated | ||
GenericMap.prototype.items = function () { | ||
return this.entries(); | ||
return this.entriesArray(); | ||
}; | ||
@@ -196,5 +206,6 @@ | ||
GenericMap.prototype.toJSON = function () { | ||
return this.entries(); | ||
return this.entriesArray(); | ||
}; | ||
GenericMap.prototype.Item = Item; | ||
@@ -214,2 +225,1 @@ | ||
}; | ||
@@ -51,2 +51,11 @@ | ||
GenericSet.prototype.forEach = function (callback /*, thisp*/) { | ||
var thisp = arguments[1]; | ||
return this.reduce(function (undefined, value, key, object, depth) { | ||
//ECMASCRIPT Sets send value twice in callback to forEach | ||
callback.call(thisp, value, value, object, depth); | ||
}, undefined); | ||
}; | ||
GenericSet.prototype.toJSON = function () { | ||
@@ -74,1 +83,9 @@ return this.toArray(); | ||
var _valuesArrayFunction = function(value,key) {return value;}; | ||
GenericSet.prototype.valuesArray = function() { | ||
return this.map(_valuesArrayFunction); | ||
} | ||
var _entriesArrayFunction = function(value,key) {return [key,value];}; | ||
GenericSet.prototype.entriesArray = function() { | ||
return this.map(_entriesArrayFunction); | ||
} |
@@ -34,2 +34,4 @@ | ||
Heap.from = GenericCollection.from; | ||
Heap.prototype.constructClone = function (values) { | ||
@@ -245,2 +247,1 @@ return new this.constructor( | ||
}; | ||
@@ -11,2 +11,7 @@ "use strict"; | ||
var values = iterable && iterable.values && iterable.values(); | ||
if(values && typeof values.next === "function" ) { | ||
return values; | ||
} | ||
if (!(this instanceof Iterator)) { | ||
@@ -13,0 +18,0 @@ return new Iterator(iterable); |
@@ -8,2 +8,3 @@ "use strict"; | ||
var PropertyChanges = require("./listen/property-changes"); | ||
var MapChanges = require("./listen/map-changes"); | ||
@@ -41,3 +42,7 @@ module.exports = LfuMap; | ||
Object.addEach(LfuMap.prototype, PropertyChanges.prototype); | ||
Object.addEach(LfuMap.prototype, MapChanges.prototype); | ||
Object.defineProperty(LfuMap.prototype,"size",GenericCollection._sizePropertyDescriptor); | ||
LfuMap.from = GenericCollection.from; | ||
LfuMap.prototype.constructClone = function (values) { | ||
@@ -79,4 +84,3 @@ return new this.constructor( | ||
} | ||
GenericMap.prototype.addMapChangeListener.apply(this, arguments); | ||
MapChanges.prototype.addMapChangeListener.apply(this, arguments); | ||
}; | ||
@@ -6,3 +6,3 @@ "use strict"; | ||
var Shim = require("./shim"); | ||
var Set = require("./set"); | ||
var Set = require("./set").CollectionsSet; | ||
var GenericCollection = require("./generic-collection"); | ||
@@ -50,2 +50,4 @@ var GenericSet = require("./generic-set"); | ||
Object.addEach(LfuSet.prototype, RangeChanges.prototype); | ||
Object.defineProperty(LfuSet.prototype,"size",GenericCollection._sizePropertyDescriptor); | ||
LfuSet.from = GenericCollection.from; | ||
@@ -248,2 +250,1 @@ LfuSet.prototype.constructClone = function (values) { | ||
} | ||
498
list.js
"use strict"; | ||
module.exports = List; | ||
var Shim = require("./shim"); | ||
var GenericCollection = require("./generic-collection"); | ||
var GenericOrder = require("./generic-order"); | ||
var _List = require("./_list"); | ||
var PropertyChanges = require("./listen/property-changes"); | ||
var RangeChanges = require("./listen/range-changes"); | ||
module.exports = List; | ||
function List(values, equals, getDefault) { | ||
if (!(this instanceof List)) { | ||
return new List(values, equals, getDefault); | ||
} | ||
var head = this.head = new this.Node(); | ||
head.next = head; | ||
head.prev = head; | ||
this.contentEquals = equals || Object.equals; | ||
this.getDefault = getDefault || Function.noop; | ||
this.length = 0; | ||
this.addEach(values); | ||
return _List._init(List, this, values, equals, getDefault); | ||
} | ||
List.prototype = new _List(); | ||
List.prototype.constructor = List; | ||
List.List = List; // hack so require("list").List will work in MontageJS | ||
List.from = _List.from; | ||
Object.addEach(List.prototype, GenericCollection.prototype); | ||
Object.addEach(List.prototype, GenericOrder.prototype); | ||
Object.addEach(List.prototype, PropertyChanges.prototype); | ||
Object.addEach(List.prototype, RangeChanges.prototype); | ||
List.prototype.constructClone = function (values) { | ||
return new this.constructor(values, this.contentEquals, this.getDefault); | ||
List.prototype.makeObservable = function () { | ||
this.head.index = -1; | ||
this.updateIndexes(this.head.next, 0); | ||
this.dispatchesRangeChanges = true; | ||
}; | ||
List.prototype.find = function (value, equals, index) { | ||
equals = equals || this.contentEquals; | ||
var head = this.head; | ||
var at = this.scan(index, head.next); | ||
while (at !== head) { | ||
if (equals(at.value, value)) { | ||
return at; | ||
} | ||
at = at.next; | ||
Object.defineProperties(List.prototype, { | ||
"_dispatchEmptyArray": { | ||
value: [] | ||
} | ||
}; | ||
}); | ||
List.prototype.findLast = function (value, equals, index) { | ||
equals = equals || this.contentEquals; | ||
var head = this.head; | ||
var at = this.scan(index, head.prev); | ||
while (at !== head) { | ||
if (equals(at.value, value)) { | ||
return at; | ||
} | ||
at = at.prev; | ||
} | ||
}; | ||
/* | ||
var list_clear = _List.prototype.clear, | ||
set_add = GlobalSet.prototype.add, | ||
set_delete = GlobalSet.prototype.delete; | ||
*/ | ||
List.prototype.has = function (value, equals) { | ||
return !!this.find(value, equals); | ||
}; | ||
List.prototype.get = function (value, equals) { | ||
var found = this.find(value, equals); | ||
if (found) { | ||
return found.value; | ||
} | ||
return this.getDefault(value); | ||
}; | ||
// LIFO (delete removes the most recently added equivalent value) | ||
@@ -91,18 +59,8 @@ List.prototype["delete"] = function (value, equals) { | ||
List.prototype.deleteAll = function (value, equals) { | ||
equals = equals || this.contentEquals; | ||
var head = this.head; | ||
var at = head.next; | ||
var count = 0; | ||
while (at !== head) { | ||
if (equals(value, at.value)) { | ||
at["delete"](); | ||
count++; | ||
} | ||
at = at.next; | ||
} | ||
this.length -= count; | ||
return count; | ||
}; | ||
Object.defineProperty(List.prototype, "superClear", { | ||
value: _List.prototype.clear, | ||
enumerable: false, | ||
configurable: true, | ||
writable:true | ||
}); | ||
List.prototype.clear = function () { | ||
@@ -115,4 +73,3 @@ var plus, minus; | ||
} | ||
this.head.next = this.head.prev = this.head; | ||
this.length = 0; | ||
this.superClear(); | ||
if (this.dispatchesRangeChanges) { | ||
@@ -129,4 +86,5 @@ this.dispatchRangeChange(plus, minus, 0); | ||
} | ||
this.head.addBefore(node); | ||
this.length++; | ||
this._addNode(node); | ||
if (this.dispatchesRangeChanges) { | ||
@@ -138,4 +96,10 @@ this.dispatchRangeChange([value], [], node.index); | ||
Object.defineProperty(List.prototype, "superPush", { | ||
value: _List.prototype.push, | ||
enumerable: false, | ||
configurable: true, | ||
writable:true | ||
}); | ||
List.prototype.push = function () { | ||
var head = this.head; | ||
if (this.dispatchesRangeChanges) { | ||
@@ -148,8 +112,9 @@ var plus = Array.prototype.slice.call(arguments); | ||
} | ||
for (var i = 0; i < arguments.length; i++) { | ||
var value = arguments[i]; | ||
var node = new this.Node(value); | ||
head.addBefore(node); | ||
} | ||
this.length += arguments.length; | ||
arguments.length === 1 | ||
? this.superPush.call(this, arguments[0]) | ||
: (arguments.length === 2) | ||
? this.superPush.call(this, arguments[0], arguments[1]) | ||
: this.superPush.apply(this, arguments); | ||
if (this.dispatchesRangeChanges) { | ||
@@ -161,2 +126,9 @@ this.updateIndexes(start.next, start.index === undefined ? 0 : start.index + 1); | ||
Object.defineProperty(List.prototype, "superUnshift", { | ||
value: _List.prototype.unshift, | ||
enumerable: false, | ||
configurable: true, | ||
writable:true | ||
}); | ||
List.prototype.unshift = function () { | ||
@@ -168,10 +140,9 @@ if (this.dispatchesRangeChanges) { | ||
} | ||
var at = this.head; | ||
for (var i = 0; i < arguments.length; i++) { | ||
var value = arguments[i]; | ||
var node = new this.Node(value); | ||
at.addAfter(node); | ||
at = node; | ||
} | ||
this.length += arguments.length; | ||
arguments.length === 1 | ||
? this.superUnshift.call(this, arguments[0]) | ||
: (arguments.length === 2) | ||
? this.superUnshift.call(this, arguments[0], arguments[1]) | ||
: this.superUnshift.apply(this, arguments); | ||
if (this.dispatchesRangeChanges) { | ||
@@ -183,179 +154,114 @@ this.updateIndexes(this.head.next, 0); | ||
List.prototype.pop = function () { | ||
var value; | ||
var head = this.head; | ||
if (head.prev !== head) { | ||
value = head.prev.value; | ||
Object.defineProperty(List.prototype, "_beforePop", { | ||
value: function(value, index) { | ||
var popDispatchValueArray; | ||
if (this.dispatchesRangeChanges) { | ||
var plus = []; | ||
var minus = [value]; | ||
var index = this.length - 1; | ||
this.dispatchBeforeRangeChange(plus, minus, index); | ||
popDispatchValueArray = [value]; | ||
this.dispatchBeforeRangeChange(/*plus*/this._dispatchEmptyArray, /*minus*/popDispatchValueArray, index); | ||
} | ||
head.prev['delete'](); | ||
this.length--; | ||
return popDispatchValueArray; | ||
}, | ||
enumerable: false, | ||
configurable: true, | ||
writable:true | ||
}); | ||
Object.defineProperty(List.prototype, "_afterPop", { | ||
value: function(value, index, popDispatchValueArray) { | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchRangeChange(plus, minus, index); | ||
this.dispatchRangeChange(/*plus*/this._dispatchEmptyArray, /*minus*/popDispatchValueArray, index); | ||
} | ||
} | ||
return value; | ||
}, | ||
enumerable: false, | ||
configurable: true, | ||
writable:true | ||
}); | ||
Object.defineProperty(List.prototype, "superPop", { | ||
value: _List.prototype.pop, | ||
enumerable: false, | ||
configurable: true, | ||
writable:true | ||
}); | ||
List.prototype.pop = function () { | ||
return this.superPop(this._beforePop,this._afterPop); | ||
}; | ||
List.prototype.shift = function () { | ||
var value; | ||
var head = this.head; | ||
if (head.prev !== head) { | ||
value = head.next.value; | ||
Object.defineProperty(List.prototype, "_beforeShift", { | ||
value: function(value, index) { | ||
var dispatchValueArray; | ||
if (this.dispatchesRangeChanges) { | ||
var plus = []; | ||
var minus = [value]; | ||
this.dispatchBeforeRangeChange(plus, minus, 0); | ||
dispatchValueArray = [value]; | ||
this.dispatchBeforeRangeChange(/*plus*/this._dispatchEmptyArray, /*minus*/dispatchValueArray, index); | ||
} | ||
head.next['delete'](); | ||
this.length--; | ||
return dispatchValueArray; | ||
}, | ||
enumerable: false, | ||
configurable: true, | ||
writable:true | ||
}); | ||
Object.defineProperty(List.prototype, "_afterShift", { | ||
value: function(value, index, dispatchValueArray) { | ||
if (this.dispatchesRangeChanges) { | ||
this.updateIndexes(this.head.next, 0); | ||
this.dispatchRangeChange(plus, minus, 0); | ||
this.updateIndexes(this.head.next, index); | ||
this.dispatchRangeChange(/*plus*/this._dispatchEmptyArray, /*minus*/dispatchValueArray, index); | ||
} | ||
} | ||
return value; | ||
}, | ||
enumerable: false, | ||
configurable: true, | ||
writable:true | ||
}); | ||
Object.defineProperty(List.prototype, "superShift", { | ||
value: _List.prototype.shift, | ||
enumerable: false, | ||
configurable: true, | ||
writable:true | ||
}); | ||
List.prototype.shift = function () { | ||
return this.superShift(this._beforeShift,this._afterShift); | ||
}; | ||
List.prototype.peek = function () { | ||
if (this.head !== this.head.next) { | ||
return this.head.next.value; | ||
} | ||
}; | ||
List.prototype.poke = function (value) { | ||
if (this.head !== this.head.next) { | ||
this.head.next.value = value; | ||
} else { | ||
this.push(value); | ||
} | ||
}; | ||
List.prototype.one = function () { | ||
return this.peek(); | ||
}; | ||
// TODO | ||
// List.prototype.indexOf = function (value) { | ||
// }; | ||
// TODO | ||
// List.prototype.lastIndexOf = function (value) { | ||
// }; | ||
// an internal utility for coercing index offsets to nodes | ||
List.prototype.scan = function (at, fallback) { | ||
var head = this.head; | ||
if (typeof at === "number") { | ||
var count = at; | ||
if (count >= 0) { | ||
at = head.next; | ||
while (count) { | ||
count--; | ||
at = at.next; | ||
if (at == head) { | ||
break; | ||
} | ||
} | ||
} else { | ||
at = head; | ||
while (count < 0) { | ||
count++; | ||
at = at.prev; | ||
if (at == head) { | ||
break; | ||
} | ||
} | ||
} | ||
return at; | ||
} else { | ||
return at || fallback; | ||
} | ||
}; | ||
// at and end may both be positive or negative numbers (in which cases they | ||
// correspond to numeric indicies, or nodes) | ||
List.prototype.slice = function (at, end) { | ||
var sliced = []; | ||
var head = this.head; | ||
at = this.scan(at, head.next); | ||
end = this.scan(end, head); | ||
while (at !== end && at !== head) { | ||
sliced.push(at.value); | ||
at = at.next; | ||
} | ||
return sliced; | ||
}; | ||
List.prototype.splice = function (at, length /*...plus*/) { | ||
return this.swap(at, length, Array.prototype.slice.call(arguments, 2)); | ||
}; | ||
Object.defineProperty(List.prototype, "superSwap", { | ||
value: _List.prototype.swap, | ||
enumerable: false, | ||
configurable: true, | ||
writable:true | ||
}); | ||
List.prototype.swap = function (start, length, plus) { | ||
var initial = start; | ||
// start will be head if start is null or -1 (meaning from the end), but | ||
// will be head.next if start is 0 (meaning from the beginning) | ||
start = this.scan(start, this.head); | ||
if (length == null) { | ||
length = Infinity; | ||
} | ||
plus = Array.from(plus); | ||
// collect the minus array | ||
var minus = []; | ||
var at = start; | ||
while (length-- && length >= 0 && at !== this.head) { | ||
minus.push(at.value); | ||
at = at.next; | ||
} | ||
// before range change | ||
var index, startNode; | ||
if (this.dispatchesRangeChanges) { | ||
if (start === this.head) { | ||
index = this.length; | ||
} else if (start.prev === this.head) { | ||
index = 0; | ||
} else { | ||
index = start.index; | ||
var _beforeSwap = function(start, plus, minus) { | ||
if (this.dispatchesRangeChanges) { | ||
if (start === this.head) { | ||
index = this.length; | ||
} else if (start.prev === this.head) { | ||
index = 0; | ||
} else { | ||
index = start.index; | ||
} | ||
startNode = start.prev; | ||
this.dispatchBeforeRangeChange(plus, minus, index); | ||
} | ||
startNode = start.prev; | ||
this.dispatchBeforeRangeChange(plus, minus, index); | ||
} | ||
// delete minus | ||
var at = start; | ||
for (var i = 0, at = start; i < minus.length; i++, at = at.next) { | ||
at["delete"](); | ||
} | ||
// add plus | ||
if (initial == null && at === this.head) { | ||
at = this.head.next; | ||
} | ||
for (var i = 0; i < plus.length; i++) { | ||
var node = new this.Node(plus[i]); | ||
at.addBefore(node); | ||
} | ||
// adjust length | ||
this.length += plus.length - minus.length; | ||
// after range change | ||
if (this.dispatchesRangeChanges) { | ||
if (start === this.head) { | ||
this.updateIndexes(this.head.next, 0); | ||
} else { | ||
this.updateIndexes(startNode.next, startNode.index + 1); | ||
}; | ||
var _afterSwap = function(start, plus, minus) { | ||
// after range change | ||
if (this.dispatchesRangeChanges) { | ||
if (start === this.head) { | ||
this.updateIndexes(this.head.next, 0); | ||
} else { | ||
this.updateIndexes(startNode.next, startNode.index + 1); | ||
} | ||
this.dispatchRangeChange(plus, minus, index); | ||
} | ||
this.dispatchRangeChange(plus, minus, index); | ||
} | ||
}; | ||
return minus; | ||
return this.superSwap(start, length, plus, _beforeSwap, _afterSwap); | ||
}; | ||
Object.defineProperty(List.prototype, "superReverse", { | ||
value: _List.prototype.reverse, | ||
enumerable: false, | ||
configurable: true, | ||
writable:true | ||
}); | ||
List.prototype.reverse = function () { | ||
@@ -367,9 +273,3 @@ if (this.dispatchesRangeChanges) { | ||
} | ||
var at = this.head; | ||
do { | ||
var temp = at.next; | ||
at.next = at.prev; | ||
at.prev = temp; | ||
at = at.next; | ||
} while (at !== this.head); | ||
this.superReverse(); | ||
if (this.dispatchesRangeChanges) { | ||
@@ -380,99 +280,1 @@ this.dispatchRangeChange(plus, minus, 0); | ||
}; | ||
List.prototype.sort = function () { | ||
this.swap(0, this.length, this.sorted()); | ||
}; | ||
// TODO account for missing basis argument | ||
List.prototype.reduce = function (callback, basis /*, thisp*/) { | ||
var thisp = arguments[2]; | ||
var head = this.head; | ||
var at = head.next; | ||
while (at !== head) { | ||
basis = callback.call(thisp, basis, at.value, at, this); | ||
at = at.next; | ||
} | ||
return basis; | ||
}; | ||
List.prototype.reduceRight = function (callback, basis /*, thisp*/) { | ||
var thisp = arguments[2]; | ||
var head = this.head; | ||
var at = head.prev; | ||
while (at !== head) { | ||
basis = callback.call(thisp, basis, at.value, at, this); | ||
at = at.prev; | ||
} | ||
return basis; | ||
}; | ||
List.prototype.updateIndexes = function (node, index) { | ||
while (node !== this.head) { | ||
node.index = index++; | ||
node = node.next; | ||
} | ||
}; | ||
List.prototype.makeObservable = function () { | ||
this.head.index = -1; | ||
this.updateIndexes(this.head.next, 0); | ||
this.dispatchesRangeChanges = true; | ||
}; | ||
List.prototype.iterate = function () { | ||
return new ListIterator(this.head); | ||
}; | ||
function ListIterator(head) { | ||
this.head = head; | ||
this.at = head.next; | ||
}; | ||
ListIterator.prototype.__iterationObject = null; | ||
Object.defineProperty(ListIterator.prototype,"_iterationObject", { | ||
get: function() { | ||
return this.__iterationObject || (this.__iterationObject = { done: false, value:null}); | ||
} | ||
}); | ||
ListIterator.prototype.next = function () { | ||
if (this.at === this.head) { | ||
this._iterationObject.done = true; | ||
this._iterationObject.value = void 0; | ||
} else { | ||
var value = this.at.value; | ||
this.at = this.at.next; | ||
this._iterationObject.value = value; | ||
} | ||
return this._iterationObject; | ||
}; | ||
List.prototype.Node = Node; | ||
function Node(value) { | ||
this.value = value; | ||
this.prev = null; | ||
this.next = null; | ||
}; | ||
Node.prototype["delete"] = function () { | ||
this.prev.next = this.next; | ||
this.next.prev = this.prev; | ||
}; | ||
Node.prototype.addBefore = function (node) { | ||
var prev = this.prev; | ||
this.prev = node; | ||
node.prev = prev; | ||
prev.next = node; | ||
node.next = this; | ||
}; | ||
Node.prototype.addAfter = function (node) { | ||
var next = this.next; | ||
this.next = node; | ||
node.next = next; | ||
next.prev = node; | ||
node.prev = this; | ||
}; |
@@ -16,7 +16,4 @@ /* | ||
require("../shim"); | ||
var List = require("../list"); | ||
var PropertyChanges = require("./property-changes"); | ||
var RangeChanges = require("./range-changes"); | ||
var MapChanges = require("./map-changes"); | ||
var array_splice = Array.prototype.splice; | ||
var array_spliceOne = Array.prototype.spliceOne; | ||
var array_slice = Array.prototype.slice; | ||
@@ -49,15 +46,2 @@ var array_reverse = Array.prototype.reverse; | ||
function defineEach(prototype) { | ||
for (var name in prototype) { | ||
Object.defineProperty(Array.prototype, name, { | ||
value: prototype[name], | ||
writable: true, | ||
configurable: true, | ||
enumerable: false | ||
}); | ||
} | ||
} | ||
defineEach(PropertyChanges.prototype); | ||
//This is a no-op test in property-changes.js - PropertyChanges.prototype.makePropertyObservable, so might as well not pay the price every time.... | ||
@@ -71,5 +55,2 @@ Object.defineProperty(Array.prototype, "makePropertyObservable", { | ||
defineEach(RangeChanges.prototype); | ||
defineEach(MapChanges.prototype); | ||
var observableArrayProperties = { | ||
@@ -148,3 +129,3 @@ | ||
value: function swap(start, length, plus) { | ||
var hasOwnPropertyChangeDescriptor, i, j; | ||
var hasOwnPropertyChangeDescriptor, i, j, plusLength; | ||
if (plus) { | ||
@@ -157,2 +138,3 @@ if (!Array.isArray(plus)) { | ||
} | ||
plusLength = plus.length; | ||
@@ -163,4 +145,4 @@ if (start < 0) { | ||
var holes = start - this.length; | ||
var newPlus = Array(holes + plus.length); | ||
for (i = 0, j = holes; i < plus.length; i++, j++) { | ||
var newPlus = Array(holes + plusLength); | ||
for (i = 0, j = holes; i < plusLength; i++, j++) { | ||
if (i in plus) { | ||
@@ -171,2 +153,3 @@ newPlus[j] = plus[i]; | ||
plus = newPlus; | ||
plusLength = plus.length; | ||
start = this.length; | ||
@@ -178,3 +161,3 @@ } | ||
// minus will be empty | ||
if (plus.length === 0) { | ||
if (plusLength === 0) { | ||
// at this point if plus is empty there is nothing to do. | ||
@@ -187,7 +170,6 @@ return []; // [], but spare us an instantiation | ||
} | ||
var diff = plus.length - minus.length; | ||
var diff = plusLength - minus.length; | ||
var oldLength = this.length; | ||
var newLength = Math.max(this.length + diff, start + plus.length); | ||
var longest = Math.max(oldLength, newLength); | ||
var newLength = Math.max(this.length + diff, start + plusLength); | ||
var longest = (oldLength > newLength) ? oldLength : newLength; | ||
// dispatch before change events | ||
@@ -199,3 +181,3 @@ if (diff) { | ||
if (diff === 0) { // substring replacement | ||
this._dispatchBeforeOwnPropertyChange(start, plus.length); | ||
this._dispatchBeforeOwnPropertyChange(start, plusLength); | ||
} else if ((hasOwnPropertyChangeDescriptor = PropertyChanges.hasOwnPropertyChangeDescriptor(this))) { | ||
@@ -216,3 +198,3 @@ // all subsequent values changed or shifted. | ||
if (diff === 0) { // substring replacement | ||
this._dispatchOwnPropertyChange(start,plus.length); | ||
this._dispatchOwnPropertyChange(start,plusLength); | ||
} else if (hasOwnPropertyChangeDescriptor) { | ||
@@ -252,5 +234,70 @@ // all subsequent values changed or shifted. | ||
spliceOne: { | ||
value: function splice(start,itemToAdd) { | ||
//Nothhing to add so length will go down by one. | ||
var plus, minus, oldLength = this.length, newLength, longest, argumentsLength = arguments.length, hasOwnPropertyChangeDescriptor; | ||
if(argumentsLength === 1) { | ||
PropertyChanges.dispatchBeforeOwnPropertyChange(this, "length", this.length); | ||
newLength = this.length - 1; | ||
plus = Array.empty; | ||
} | ||
//Care about 2 only | ||
else { | ||
plus = [itemToAdd]; | ||
newLength = this.length; | ||
} | ||
minus = [this[start]]; | ||
longest = (oldLength > newLength) ? oldLength : newLength; | ||
this.dispatchBeforeRangeChange(plus, minus, start); | ||
if (argumentsLength === 2) { // substring replacement | ||
this._dispatchBeforeOwnPropertyChange(start, 1); | ||
} else if ((hasOwnPropertyChangeDescriptor = PropertyChanges.hasOwnPropertyChangeDescriptor(this))) { | ||
// all subsequent values changed or shifted. | ||
// avoid (longest - start) long walks if there are no | ||
// registered descriptors. | ||
this._dispatchBeforeOwnPropertyChange(start, longest-start); | ||
} | ||
if (argumentsLength === 1) { // substring replacement | ||
array_spliceOne.call(this,start); | ||
} | ||
else { | ||
array_spliceOne.call(this,start,itemToAdd); | ||
} | ||
// dispatch after change events | ||
if (argumentsLength === 2) { // substring replacement | ||
this._dispatchOwnPropertyChange(start,1); | ||
} else if (hasOwnPropertyChangeDescriptor) { | ||
// all subsequent values changed or shifted. | ||
// avoid (longest - start) long walks if there are no | ||
// registered descriptors. | ||
this._dispatchOwnPropertyChange(start,longest-start); | ||
} | ||
this.dispatchRangeChange(plus, minus, start); | ||
if(argumentsLength === 1) { | ||
this.dispatchOwnPropertyChange("length", this.length); | ||
} | ||
}, | ||
writable: true, | ||
configurable: true | ||
}, | ||
_setSwapBuffer: { | ||
get: function() { | ||
return this.__setSwapBuffer || (Object.defineProperty(this,"__setSwapBuffer",{ | ||
value: [], | ||
writable: true, | ||
configurable: true, | ||
enumerable: false | ||
})).__setSwapBuffer; | ||
}, | ||
enumerable: false | ||
}, | ||
set: { | ||
value: function set(index, value) { | ||
this.swap(index, index >= this.length ? 0 : 1, [value]); | ||
this._setSwapBuffer[0] = value | ||
this.swap(index, index >= this.length ? 0 : 1, this._setSwapBuffer); | ||
return true; | ||
@@ -339,1 +386,8 @@ }, | ||
var PropertyChanges = require("./property-changes"); | ||
var RangeChanges = require("./range-changes"); | ||
var MapChanges = require("./map-changes"); | ||
Object.defineEach(Array.prototype, PropertyChanges.prototype, false, /*configurable*/true, /*enumerable*/ false, /*writable*/true); | ||
Object.defineEach(Array.prototype, RangeChanges.prototype, false, /*configurable*/true, /*enumerable*/ false, /*writable*/true); | ||
Object.defineEach(Array.prototype, MapChanges.prototype, false, /*configurable*/true, /*enumerable*/ false, /*writable*/true); |
"use strict"; | ||
var WeakMap = require("weak-map"); | ||
var List = require("../list"); | ||
var WeakMap = require("weak-map"), | ||
Map = require("../_map"), | ||
ChangeDescriptor = require("./change-descriptor"), | ||
ObjectChangeDescriptor = ChangeDescriptor.ObjectChangeDescriptor, | ||
ChangeListenersRecord = ChangeDescriptor.ChangeListenersRecord, | ||
ListenerGhost = ChangeDescriptor.ListenerGhost; | ||
@@ -20,3 +24,3 @@ module.exports = MapChanges; | ||
{ | ||
willChangeListeners:Array(Function) | ||
willChangeListeners:Array(Fgunction) | ||
changeListeners:Array(Function) | ||
@@ -27,10 +31,56 @@ } | ||
var mapChangeDescriptors = new WeakMap(); | ||
var Dict = null; | ||
function MapChangeDescriptor(name) { | ||
this.name = name; | ||
this.isActive = false; | ||
this._willChangeListeners = null; | ||
this._changeListeners = null; | ||
}; | ||
MapChangeDescriptor.prototype = new ObjectChangeDescriptor(); | ||
MapChangeDescriptor.prototype.constructor = MapChangeDescriptor; | ||
MapChangeDescriptor.prototype.changeListenersRecordConstructor = MapChangeListenersRecord; | ||
MapChangeDescriptor.prototype.willChangeListenersRecordConstructor = MapWillChangeListenersRecord; | ||
var MapChangeListenersSpecificHandlerMethodName = new Map(); | ||
function MapChangeListenersRecord(name) { | ||
var specificHandlerMethodName = MapChangeListenersSpecificHandlerMethodName.get(name); | ||
if(!specificHandlerMethodName) { | ||
specificHandlerMethodName = "handle"; | ||
specificHandlerMethodName += name.slice(0, 1).toUpperCase(); | ||
specificHandlerMethodName += name.slice(1); | ||
specificHandlerMethodName += "MapChange"; | ||
MapChangeListenersSpecificHandlerMethodName.set(name,specificHandlerMethodName); | ||
} | ||
this.specificHandlerMethodName = specificHandlerMethodName; | ||
return this; | ||
} | ||
MapChangeListenersRecord.prototype = new ChangeListenersRecord(); | ||
MapChangeListenersRecord.prototype.constructor = MapChangeListenersRecord; | ||
MapChangeListenersRecord.prototype.genericHandlerMethodName = "handleMapChange"; | ||
var MapWillChangeListenersSpecificHandlerMethodName = new Map(); | ||
function MapWillChangeListenersRecord(name) { | ||
var specificHandlerMethodName = MapWillChangeListenersSpecificHandlerMethodName.get(name); | ||
if(!specificHandlerMethodName) { | ||
specificHandlerMethodName = "handle"; | ||
specificHandlerMethodName += name.slice(0, 1).toUpperCase(); | ||
specificHandlerMethodName += name.slice(1); | ||
specificHandlerMethodName += "MapWillChange"; | ||
MapWillChangeListenersSpecificHandlerMethodName.set(name,specificHandlerMethodName); | ||
} | ||
this.specificHandlerMethodName = specificHandlerMethodName; | ||
return this; | ||
} | ||
MapWillChangeListenersRecord.prototype = new ChangeListenersRecord(); | ||
MapWillChangeListenersRecord.prototype.constructor = MapWillChangeListenersRecord; | ||
MapWillChangeListenersRecord.prototype.genericHandlerMethodName = "handleMapWillChange"; | ||
MapChanges.prototype.getAllMapChangeDescriptors = function () { | ||
if (!mapChangeDescriptors.has(this)) { | ||
if (!Dict) { | ||
Dict = require("../dict"); | ||
} | ||
mapChangeDescriptors.set(this, Dict()); | ||
mapChangeDescriptors.set(this, new Map()); | ||
} | ||
@@ -44,6 +94,3 @@ return mapChangeDescriptors.get(this); | ||
if (!tokenChangeDescriptors.has(token)) { | ||
tokenChangeDescriptors.set(token, { | ||
willChangeListeners: new List(), | ||
changeListeners: new List() | ||
}); | ||
tokenChangeDescriptors.set(token, new MapChangeDescriptor(token)); | ||
} | ||
@@ -53,3 +100,20 @@ return tokenChangeDescriptors.get(token); | ||
MapChanges.prototype.addMapChangeListener = function (listener, token, beforeChange) { | ||
var ObjectsDispatchesMapChanges = new WeakMap(), | ||
dispatchesMapChangesGetter = function() { | ||
return ObjectsDispatchesMapChanges.get(this); | ||
}, | ||
dispatchesMapChangesSetter = function(value) { | ||
return ObjectsDispatchesMapChanges.set(this,value); | ||
}, | ||
dispatchesChangesMethodName = "dispatchesMapChanges", | ||
dispatchesChangesPropertyDescriptor = { | ||
get: dispatchesMapChangesGetter, | ||
set: dispatchesMapChangesSetter, | ||
configurable: true, | ||
enumerable: false | ||
}; | ||
MapChanges.prototype.addMapChangeListener = function addMapChangeListener(listener, token, beforeChange) { | ||
//console.log("this:",this," addMapChangeListener(",listener,",",token,",",beforeChange); | ||
if (!this.isObservable && this.makeObservable) { | ||
@@ -66,10 +130,21 @@ // for Array | ||
} | ||
listeners.push(listener); | ||
Object.defineProperty(this, "dispatchesMapChanges", { | ||
value: true, | ||
writable: true, | ||
configurable: true, | ||
enumerable: false | ||
}); | ||
// console.log("addMapChangeListener()",listener, token); | ||
//console.log("this:",this," addMapChangeListener() listeners._current is ",listeners._current); | ||
if(!listeners._current) { | ||
listeners._current = listener; | ||
} | ||
else if(!Array.isArray(listeners._current)) { | ||
listeners._current = [listeners._current,listener] | ||
} | ||
else { | ||
listeners._current.push(listener); | ||
} | ||
if(Object.getOwnPropertyDescriptor((this.__proto__||Object.getPrototypeOf(this)),dispatchesChangesMethodName) === void 0) { | ||
Object.defineProperty((this.__proto__||Object.getPrototypeOf(this)), dispatchesChangesMethodName, dispatchesChangesPropertyDescriptor); | ||
} | ||
this.dispatchesMapChanges = true; | ||
var self = this; | ||
@@ -96,12 +171,30 @@ return function cancelMapChangeListener() { | ||
var node = listeners.findLast(listener); | ||
if (!node) { | ||
throw new Error("Can't remove map change listener: does not exist: token " + JSON.stringify(token)); | ||
if(listeners._current) { | ||
if(listeners._current === listener) { | ||
listeners._current = null; | ||
} | ||
else { | ||
var index = listeners._current.lastIndexOf(listener); | ||
if (index === -1) { | ||
throw new Error("Can't remove map change listener: does not exist: token " + JSON.stringify(token)); | ||
} | ||
else { | ||
if(descriptor.isActive) { | ||
listeners.ghostCount = listeners.ghostCount+1 | ||
listeners._current[index]=ListenerGhost | ||
} | ||
else { | ||
listeners._current.spliceOne(index); | ||
} | ||
} | ||
} | ||
} | ||
node["delete"](); | ||
}; | ||
MapChanges.prototype.dispatchMapChange = function (key, value, beforeChange) { | ||
var descriptors = this.getAllMapChangeDescriptors(); | ||
var changeName = "Map" + (beforeChange ? "WillChange" : "Change"); | ||
var descriptors = this.getAllMapChangeDescriptors(), | ||
Ghost = ListenerGhost; | ||
descriptors.forEach(function (descriptor, token) { | ||
@@ -111,31 +204,50 @@ | ||
return; | ||
} else { | ||
descriptor.isActive = true; | ||
} | ||
var listeners; | ||
if (beforeChange) { | ||
listeners = descriptor.willChangeListeners; | ||
} else { | ||
listeners = descriptor.changeListeners; | ||
} | ||
var listeners = beforeChange ? descriptor.willChangeListeners : descriptor.changeListeners; | ||
if(listeners && listeners._current) { | ||
var tokenName = "handle" + ( | ||
token.slice(0, 1).toUpperCase() + | ||
token.slice(1) | ||
) + changeName; | ||
var tokenName = listeners.specificHandlerMethodName; | ||
if(Array.isArray(listeners._current) && listeners._current.length) { | ||
try { | ||
// dispatch to each listener | ||
listeners.forEach(function (listener) { | ||
if (listener[tokenName]) { | ||
listener[tokenName](value, key, this); | ||
} else if (listener.call) { | ||
listener.call(listener, value, key, this); | ||
} else { | ||
throw new Error("Handler " + listener + " has no method " + tokenName + " and is not callable"); | ||
//removeGostListenersIfNeeded returns listeners.current or a new filtered one when conditions are met | ||
var currentListeners = listeners.removeCurrentGostListenersIfNeeded(), | ||
i, countI, listener; | ||
descriptor.isActive = true; | ||
try { | ||
for(i=0, countI = currentListeners.length;i<countI;i++) { | ||
// dispatch to each listener | ||
if ((listener = currentListeners[i]) !== Ghost) { | ||
if (listener[tokenName]) { | ||
listener[tokenName](value, key, this); | ||
} else if (listener.call) { | ||
listener.call(listener, value, key, this); | ||
} else { | ||
throw new Error("Handler " + listener + " has no method " + tokenName + " and is not callable"); | ||
} | ||
} | ||
} | ||
} finally { | ||
descriptor.isActive = false; | ||
} | ||
}, this); | ||
} finally { | ||
descriptor.isActive = false; | ||
} | ||
else { | ||
descriptor.isActive = true; | ||
// dispatch each listener | ||
try { | ||
listener = listeners._current; | ||
if (listener[tokenName]) { | ||
listener[tokenName](value, key, this); | ||
} else if (listener.call) { | ||
listener.call(listener, value, key, this); | ||
} else { | ||
throw new Error("Handler " + listener + " has no method " + tokenName + " and is not callable"); | ||
} | ||
} finally { | ||
descriptor.isActive = false; | ||
} | ||
} | ||
} | ||
@@ -157,2 +269,1 @@ | ||
}; | ||
@@ -15,4 +15,4 @@ /* | ||
require("../shim"); | ||
// objectHasOwnProperty.call(myObject, key) will be used instead of | ||
@@ -66,38 +66,40 @@ // myObject.hasOwnProperty(key) to allow myObject have defined | ||
require("../shim"); | ||
var Map = require("../_map"); | ||
var WeakMap = require("../weak-map"); | ||
var ChangeDescriptor = require("./change-descriptor"), | ||
ObjectChangeDescriptor = ChangeDescriptor.ObjectChangeDescriptor, | ||
ListenerGhost = ChangeDescriptor.ListenerGhost; | ||
PropertyChanges.debug = true; | ||
var ObjectsPropertyChangeListeners = new WeakMap(); | ||
var ObjectChangeDescriptorName = new Map(); | ||
PropertyChanges.ObjectChangeDescriptor = function() { | ||
} | ||
PropertyChanges.prototype.getOwnPropertyChangeDescriptor = function (key) { | ||
if (!this.__propertyChangeListeners__) { | ||
Object.defineProperty(this, "__propertyChangeListeners__", { | ||
value: {}, | ||
enumerable: false, | ||
configurable: true, | ||
writable: true | ||
}); | ||
var objectPropertyChangeDescriptors = ObjectsPropertyChangeListeners.get(this), keyChangeDescriptor; | ||
if (!objectPropertyChangeDescriptors) { | ||
objectPropertyChangeDescriptors = Object.create(null); | ||
ObjectsPropertyChangeListeners.set(this,objectPropertyChangeDescriptors); | ||
} | ||
var objectPropertyChangeDescriptors = this.__propertyChangeListeners__; | ||
if (!objectHasOwnProperty.call(objectPropertyChangeDescriptors, key)) { | ||
var propertyName = String(key); | ||
propertyName = propertyName && propertyName[0].toUpperCase() + propertyName.slice(1); | ||
objectPropertyChangeDescriptors[key] = { | ||
willChangeListeners: { | ||
current: [], | ||
active: [], | ||
specificHandlerMethodName: "handle" + propertyName + "WillChange", | ||
genericHandlerMethodName: "handlePropertyWillChange" | ||
}, | ||
changeListeners: { | ||
current: [], | ||
active: [], | ||
specificHandlerMethodName: "handle" + propertyName + "Change", | ||
genericHandlerMethodName: "handlePropertyChange" | ||
} | ||
}; | ||
if ( (keyChangeDescriptor = objectPropertyChangeDescriptors[key]) === void 0) { | ||
var propertyName = ObjectChangeDescriptorName.get(key); | ||
if(!propertyName) { | ||
propertyName = String(key); | ||
propertyName = propertyName && propertyName[0].toUpperCase() + propertyName.slice(1); | ||
ObjectChangeDescriptorName.set(key,propertyName); | ||
} | ||
return objectPropertyChangeDescriptors[key] = new ObjectChangeDescriptor(propertyName); | ||
} | ||
return objectPropertyChangeDescriptors[key]; | ||
return keyChangeDescriptor; | ||
}; | ||
PropertyChanges.prototype.hasOwnPropertyChangeDescriptor = function (key) { | ||
if (!this.__propertyChangeListeners__) { | ||
var objectPropertyChangeDescriptors = ObjectsPropertyChangeListeners.get(this); | ||
if (!objectPropertyChangeDescriptors) { | ||
return false; | ||
@@ -108,4 +110,3 @@ } | ||
} | ||
var objectPropertyChangeDescriptors = this.__propertyChangeListeners__; | ||
if (!objectHasOwnProperty.call(objectPropertyChangeDescriptors, key)) { | ||
if (objectPropertyChangeDescriptors[key] === void 0) { | ||
return false; | ||
@@ -121,12 +122,17 @@ } | ||
} | ||
var descriptor = PropertyChanges.getOwnPropertyChangeDescriptor(this, key); | ||
var listeners; | ||
if (beforeChange) { | ||
listeners = descriptor.willChangeListeners; | ||
} else { | ||
listeners = descriptor.changeListeners; | ||
} | ||
var descriptor = PropertyChanges.getOwnPropertyChangeDescriptor(this, key), | ||
listeners = beforeChange ? descriptor.willChangeListeners : descriptor.changeListeners; | ||
PropertyChanges.makePropertyObservable(this, key); | ||
listeners.current.push(listener); | ||
if(!listeners._current) { | ||
listeners._current = listener; | ||
} | ||
else if(!Array.isArray(listeners._current)) { | ||
listeners._current = [listeners._current,listener] | ||
} | ||
else { | ||
listeners._current.push(listener); | ||
} | ||
var self = this; | ||
@@ -143,3 +149,3 @@ return function cancelOwnPropertyChangeListener() { | ||
PropertyChanges.prototype.removeOwnPropertyChangeListener = function (key, listener, beforeChange) { | ||
PropertyChanges.prototype.removeOwnPropertyChangeListener = function removeOwnPropertyChangeListener(key, listener, beforeChange) { | ||
var descriptor = PropertyChanges.getOwnPropertyChangeDescriptor(this, key); | ||
@@ -149,13 +155,30 @@ | ||
if (beforeChange) { | ||
listeners = descriptor.willChangeListeners; | ||
listeners = descriptor._willChangeListeners; | ||
} else { | ||
listeners = descriptor.changeListeners; | ||
listeners = descriptor._changeListeners; | ||
} | ||
var index = listeners.current.lastIndexOf(listener); | ||
if (index === -1) { | ||
throw new Error("Can't remove property change listener: does not exist: property name" + JSON.stringify(key)); | ||
if(listeners) { | ||
if(listeners._current) { | ||
if(listeners._current === listener) { | ||
listeners._current = null; | ||
} | ||
else { | ||
var index = listeners._current.lastIndexOf(listener); | ||
if (index === -1) { | ||
throw new Error("Can't remove property change listener: does not exist: property name" + JSON.stringify(key)); | ||
} | ||
if(descriptor.isActive) { | ||
listeners.ghostCount = listeners.ghostCount+1; | ||
listeners._current[index]=removeOwnPropertyChangeListener.ListenerGhost; | ||
} | ||
else { | ||
listeners._current.spliceOne(index); | ||
} | ||
} | ||
} | ||
} | ||
listeners.current.splice(index, 1); | ||
}; | ||
PropertyChanges.prototype.removeOwnPropertyChangeListener.ListenerGhost = ListenerGhost; | ||
@@ -166,3 +189,3 @@ PropertyChanges.prototype.removeBeforeOwnPropertyChangeListener = function (key, listener) { | ||
PropertyChanges.prototype.dispatchOwnPropertyChange = function (key, value, beforeChange) { | ||
PropertyChanges.prototype.dispatchOwnPropertyChange = function dispatchOwnPropertyChange(key, value, beforeChange) { | ||
var descriptor = PropertyChanges.getOwnPropertyChangeDescriptor(this, key), | ||
@@ -173,9 +196,5 @@ listeners; | ||
descriptor.isActive = true; | ||
if (beforeChange) { | ||
listeners = descriptor.willChangeListeners; | ||
} else { | ||
listeners = descriptor.changeListeners; | ||
} | ||
listeners = beforeChange ? descriptor._willChangeListeners: descriptor._changeListeners; | ||
try { | ||
dispatchEach(listeners, key, value, this); | ||
dispatchOwnPropertyChange.dispatchEach(listeners, key, value, this); | ||
} finally { | ||
@@ -186,23 +205,40 @@ descriptor.isActive = false; | ||
}; | ||
PropertyChanges.prototype.dispatchOwnPropertyChange.dispatchEach = dispatchEach; | ||
function dispatchEach(listeners, key, value, object) { | ||
// copy snapshot of current listeners to active listeners | ||
var active = listeners.active; | ||
var current = listeners.current; | ||
var index = current.length; | ||
var listener, length = index, i, thisp; | ||
if(listeners && listeners._current) { | ||
// copy snapshot of current listeners to active listeners | ||
var current, | ||
listener, | ||
i, | ||
countI, | ||
thisp, | ||
specificHandlerMethodName = listeners.specificHandlerMethodName, | ||
genericHandlerMethodName = listeners.genericHandlerMethodName, | ||
Ghost = ListenerGhost; | ||
if (active.length > index) { | ||
active.length = index; | ||
} | ||
while (index--) { | ||
active[index] = current[index]; | ||
} | ||
for (i = 0; i < length; i++) { | ||
thisp = active[i]; | ||
//This is fixing the issue causing a regression in Montage's repetition | ||
if (!i || current.indexOf(thisp) >= 0) { | ||
if(Array.isArray(listeners._current)) { | ||
//removeGostListenersIfNeeded returns listeners.current or a new filtered one when conditions are met | ||
current = listeners.removeCurrentGostListenersIfNeeded(); | ||
//We use a for to guarantee we won't dispatch to listeners that would be added after we started | ||
for(i=0, countI = current.length;i<countI;i++) { | ||
if ((thisp = current[i]) !== Ghost) { | ||
//This is fixing the issue causing a regression in Montage's repetition | ||
listener = ( | ||
thisp[specificHandlerMethodName] || | ||
thisp[genericHandlerMethodName] || | ||
thisp | ||
); | ||
if (!listener.call) { | ||
throw new Error("No event listener for " + listeners.specificHandlerName + " or " + listeners.genericHandlerName + " or call on " + listener); | ||
} | ||
listener.call(thisp, value, key, object); | ||
} | ||
} | ||
} | ||
else { | ||
thisp = listeners._current; | ||
listener = ( | ||
thisp[listeners.specificHandlerMethodName] || | ||
thisp[listeners.genericHandlerMethodName] || | ||
thisp[specificHandlerMethodName] || | ||
thisp[genericHandlerMethodName] || | ||
thisp | ||
@@ -219,2 +255,5 @@ ); | ||
dispatchEach.ListenerGhost = ListenerGhost; | ||
PropertyChanges.prototype.dispatchBeforeOwnPropertyChange = function (key, listener) { | ||
@@ -224,2 +263,11 @@ return PropertyChanges.dispatchOwnPropertyChange(this, key, listener, true); | ||
var ObjectsOverriddenPropertyDescriptors = new WeakMap(), | ||
Objects__state__ = new WeakMap(), | ||
propertyListener = { | ||
get: void 0, | ||
set: void 0, | ||
configurable: true, | ||
enumerable: false | ||
}; | ||
PropertyChanges.prototype.makePropertyObservable = function (key) { | ||
@@ -232,3 +280,8 @@ // arrays are special. we do not support direct setting of properties | ||
var overriddenPropertyDescriptors = this.__overriddenPropertyDescriptors__; | ||
var overriddenPropertyDescriptors = ObjectsOverriddenPropertyDescriptors.get(this); | ||
if (overriddenPropertyDescriptors && overriddenPropertyDescriptors.get(key) !== void 0) { | ||
// if we have already recorded an overridden property descriptor, | ||
// we have already installed the observer, so short-here | ||
return; | ||
} | ||
@@ -243,29 +296,11 @@ // memoize overridden property descriptor table | ||
} | ||
overriddenPropertyDescriptors = {}; | ||
Object.defineProperty(this, "__overriddenPropertyDescriptors__", { | ||
value: overriddenPropertyDescriptors, | ||
enumerable: false, | ||
writable: true, | ||
configurable: true | ||
}); | ||
} else { | ||
if (objectHasOwnProperty.call(overriddenPropertyDescriptors, key)) { | ||
// if we have already recorded an overridden property descriptor, | ||
// we have already installed the observer, so short-here | ||
return; | ||
} | ||
overriddenPropertyDescriptors = new Map(); | ||
ObjectsOverriddenPropertyDescriptors.set(this,overriddenPropertyDescriptors); | ||
} | ||
var state; | ||
if (typeof this.__state__ === "object") { | ||
state = this.__state__; | ||
} else { | ||
state = {}; | ||
Object.defineProperty(this, "__state__", { | ||
value: state, | ||
writable: true, | ||
enumerable: false | ||
}); | ||
} | ||
state[key] = this[key]; | ||
// var state = Objects__state__.get(this); | ||
// if (typeof state !== "object") { | ||
// Objects__state__.set(this,(state = {})); | ||
// } | ||
// state[key] = this[key]; | ||
@@ -288,3 +323,3 @@ | ||
overriddenDescriptor = { | ||
value: undefined, | ||
value: void 0, | ||
enumerable: true, | ||
@@ -305,3 +340,3 @@ writable: true, | ||
// and so we can reuse the overridden descriptor when uninstalling | ||
overriddenPropertyDescriptors[key] = overriddenDescriptor; | ||
overriddenPropertyDescriptors.set(key,overriddenDescriptor); | ||
@@ -311,3 +346,2 @@ | ||
var propertyListener; | ||
// in both of these new descriptor variants, we reuse the overridden | ||
@@ -320,56 +354,66 @@ // descriptor to either store the current value or apply getters | ||
if ('value' in overriddenDescriptor) { | ||
propertyListener = { | ||
get: function () { | ||
return overriddenDescriptor.value; | ||
}, | ||
set: function (value) { | ||
var descriptor, | ||
isActive; | ||
propertyListener.get = function () { | ||
return overriddenDescriptor.value; | ||
}; | ||
propertyListener.set = function dispatchingSetter(value) { | ||
var descriptor, | ||
isActive, | ||
overriddenDescriptor = dispatchingSetter.overriddenDescriptor; | ||
if (value !== overriddenDescriptor.value) { | ||
descriptor = this.__propertyChangeListeners__[key]; | ||
isActive = descriptor.isActive; | ||
if (!isActive) { | ||
descriptor.isActive = true; | ||
try { | ||
dispatchEach(descriptor.willChangeListeners, key, overriddenDescriptor.value, this); | ||
} finally {} | ||
if (value !== overriddenDescriptor.value) { | ||
descriptor = dispatchingSetter.descriptor; | ||
if (!(isActive = descriptor.isActive)) { | ||
descriptor.isActive = true; | ||
try { | ||
dispatchingSetter.dispatchEach(descriptor._willChangeListeners, key, overriddenDescriptor.value, this); | ||
} finally {} | ||
} | ||
overriddenDescriptor.value = value; | ||
if (!isActive) { | ||
try { | ||
dispatchingSetter.dispatchEach(descriptor._changeListeners, key, value, this); | ||
} finally { | ||
descriptor.isActive = false; | ||
} | ||
overriddenDescriptor.value = value; | ||
state[key] = value; | ||
if (!isActive) { | ||
try { | ||
dispatchEach(descriptor.changeListeners, key, value, this); | ||
} finally { | ||
descriptor.isActive = false; | ||
} | ||
} | ||
} | ||
}, | ||
enumerable: overriddenDescriptor.enumerable, | ||
configurable: true | ||
} | ||
}; | ||
propertyListener.set.dispatchEach = dispatchEach; | ||
propertyListener.set.overriddenDescriptor = overriddenDescriptor; | ||
propertyListener.set.descriptor = ObjectsPropertyChangeListeners.get(this)[key]; | ||
propertyListener.enumerable = overriddenDescriptor.enumerable; | ||
propertyListener.configurable = true | ||
} else { // 'get' or 'set', but not necessarily both | ||
propertyListener = { | ||
get: overriddenDescriptor.get, | ||
set: function (value) { | ||
var formerValue = this[key], | ||
propertyListener.get = overriddenDescriptor.get; | ||
propertyListener.set = function dispatchingSetter() { | ||
var formerValue = dispatchingSetter.overriddenGetter.call(this), | ||
descriptor, | ||
isActive; | ||
isActive, | ||
newValue; | ||
overriddenDescriptor.set.call(this, value); | ||
value = this[key]; | ||
if (value !== formerValue) { | ||
descriptor = this.__propertyChangeListeners__[key]; | ||
isActive = descriptor.isActive; | ||
if (!isActive) { | ||
if(arguments.length === 1) { | ||
dispatchingSetter.overriddenSetter.call(this,arguments[0]); | ||
} | ||
else if(arguments.length === 2) { | ||
dispatchingSetter.overriddenSetter.call(this,arguments[0],arguments[1]); | ||
} | ||
else { | ||
dispatchingSetter.overriddenSetter.apply(this, arguments); | ||
} | ||
if ((newValue = dispatchingSetter.overriddenGetter.call(this)) !== formerValue) { | ||
descriptor = dispatchingSetter.descriptor; | ||
if (!(isActive = descriptor.isActive)) { | ||
descriptor.isActive = true; | ||
try { | ||
dispatchEach(descriptor.willChangeListeners, key, formerValue, this); | ||
dispatchingSetter.dispatchEach(descriptor._willChangeListeners, key, formerValue, this); | ||
} finally {} | ||
} | ||
state[key] = value; | ||
if (!isActive) { | ||
try { | ||
dispatchEach(descriptor.changeListeners, key, value, this); | ||
dispatchingSetter.dispatchEach(descriptor._changeListeners, key, newValue, this); | ||
} finally { | ||
@@ -380,6 +424,9 @@ descriptor.isActive = false; | ||
} | ||
}, | ||
enumerable: overriddenDescriptor.enumerable, | ||
configurable: true | ||
}; | ||
}; | ||
propertyListener.enumerable = overriddenDescriptor.enumerable; | ||
propertyListener.configurable = true; | ||
propertyListener.set.dispatchEach = dispatchEach; | ||
propertyListener.set.overriddenSetter = overriddenDescriptor.set; | ||
propertyListener.set.overriddenGetter = overriddenDescriptor.get; | ||
propertyListener.set.descriptor = ObjectsPropertyChangeListeners.get(this)[key]; | ||
} | ||
@@ -409,7 +456,6 @@ | ||
PropertyChanges.addOwnPropertyChangeListener = function (object, key, listener, beforeChange) { | ||
if (!Object.isObject(object)) { | ||
} else if (object.addOwnPropertyChangeListener) { | ||
return object.addOwnPropertyChangeListener(key, listener, beforeChange); | ||
} else { | ||
return PropertyChanges.prototype.addOwnPropertyChangeListener.call(object, key, listener, beforeChange); | ||
if (Object.isObject(object)) { | ||
return object.addOwnPropertyChangeListener | ||
? object.addOwnPropertyChangeListener(key, listener, beforeChange) | ||
: this.prototype.addOwnPropertyChangeListener.call(object, key, listener, beforeChange); | ||
} | ||
@@ -416,0 +462,0 @@ }; |
"use strict"; | ||
var WeakMap = require("weak-map"); | ||
var Dict = require("../dict"); | ||
//TODO: | ||
// Remove Dict and use native Map as much as possible here | ||
//Use ObjectChangeDescriptor to avoid creating useless arrays and benefit from similar gains made in property-changes | ||
var WeakMap = require("weak-map"), | ||
Map = require("../_map"), | ||
ChangeDescriptor = require("./change-descriptor"), | ||
ObjectChangeDescriptor = ChangeDescriptor.ObjectChangeDescriptor, | ||
ChangeListenersRecord = ChangeDescriptor.ChangeListenersRecord, | ||
ListenerGhost = ChangeDescriptor.ListenerGhost; | ||
var rangeChangeDescriptors = new WeakMap(); // {isActive, willChangeListeners, changeListeners} | ||
// | ||
function RangeChangeDescriptor(name) { | ||
this.name = name; | ||
this.isActive = false; | ||
this._willChangeListeners = null; | ||
this._changeListeners = null; | ||
}; | ||
RangeChangeDescriptor.prototype = new ObjectChangeDescriptor(); | ||
RangeChangeDescriptor.prototype.constructor = RangeChangeDescriptor; | ||
RangeChangeDescriptor.prototype.changeListenersRecordConstructor = RangeChangeListenersRecord; | ||
RangeChangeDescriptor.prototype.willChangeListenersRecordConstructor = RangeWillChangeListenersRecord; | ||
Object.defineProperty(RangeChangeDescriptor.prototype,"active",{ | ||
get: function() { | ||
return this._active || (this._active = this._current ? this._current.slice():[]); | ||
} | ||
}); | ||
var RangeChangeListenersSpecificHandlerMethodName = new Map(); | ||
function RangeChangeListenersRecord(name) { | ||
var specificHandlerMethodName = RangeChangeListenersSpecificHandlerMethodName.get(name); | ||
if(!specificHandlerMethodName) { | ||
specificHandlerMethodName = "handle"; | ||
specificHandlerMethodName += name.slice(0, 1).toUpperCase(); | ||
specificHandlerMethodName += name.slice(1); | ||
specificHandlerMethodName += "RangeChange"; | ||
RangeChangeListenersSpecificHandlerMethodName.set(name,specificHandlerMethodName); | ||
} | ||
this.specificHandlerMethodName = specificHandlerMethodName; | ||
return this; | ||
} | ||
RangeChangeListenersRecord.prototype = new ChangeListenersRecord(); | ||
RangeChangeListenersRecord.prototype.constructor = RangeChangeListenersRecord; | ||
var RangeWillChangeListenersSpecificHandlerMethodName = new Map(); | ||
function RangeWillChangeListenersRecord(name) { | ||
var specificHandlerMethodName = RangeWillChangeListenersSpecificHandlerMethodName.get(name); | ||
if(!specificHandlerMethodName) { | ||
specificHandlerMethodName = "handle"; | ||
specificHandlerMethodName += name.slice(0, 1).toUpperCase(); | ||
specificHandlerMethodName += name.slice(1); | ||
specificHandlerMethodName += "RangeWillChange"; | ||
RangeWillChangeListenersSpecificHandlerMethodName.set(name,specificHandlerMethodName); | ||
} | ||
this.specificHandlerMethodName = specificHandlerMethodName; | ||
return this; | ||
} | ||
RangeWillChangeListenersRecord.prototype = new ChangeListenersRecord(); | ||
RangeWillChangeListenersRecord.prototype.constructor = RangeWillChangeListenersRecord; | ||
module.exports = RangeChanges; | ||
@@ -15,3 +79,3 @@ function RangeChanges() { | ||
if (!rangeChangeDescriptors.has(this)) { | ||
rangeChangeDescriptors.set(this, Dict()); | ||
rangeChangeDescriptors.set(this, new Map()); | ||
} | ||
@@ -25,7 +89,3 @@ return rangeChangeDescriptors.get(this); | ||
if (!tokenChangeDescriptors.has(token)) { | ||
tokenChangeDescriptors.set(token, { | ||
isActive: false, | ||
changeListeners: [], | ||
willChangeListeners: [] | ||
}); | ||
tokenChangeDescriptors.set(token, new RangeChangeDescriptor(token)); | ||
} | ||
@@ -35,3 +95,18 @@ return tokenChangeDescriptors.get(token); | ||
RangeChanges.prototype.addRangeChangeListener = function (listener, token, beforeChange) { | ||
var ObjectsDispatchesRangeChanges = new WeakMap(), | ||
dispatchesRangeChangesGetter = function() { | ||
return ObjectsDispatchesRangeChanges.get(this); | ||
}, | ||
dispatchesRangeChangesSetter = function(value) { | ||
return ObjectsDispatchesRangeChanges.set(this,value); | ||
}, | ||
dispatchesChangesMethodName = "dispatchesRangeChanges", | ||
dispatchesChangesPropertyDescriptor = { | ||
get: dispatchesRangeChangesGetter, | ||
set: dispatchesRangeChangesSetter, | ||
configurable: true, | ||
enumerable: false | ||
}; | ||
RangeChanges.prototype.addRangeChangeListener = function addRangeChangeListener(listener, token, beforeChange) { | ||
// a concession for objects like Array that are not inherently observable | ||
@@ -52,10 +127,17 @@ if (!this.isObservable && this.makeObservable) { | ||
// even if already registered | ||
listeners.push(listener); | ||
Object.defineProperty(this, "dispatchesRangeChanges", { | ||
value: true, | ||
writable: true, | ||
configurable: true, | ||
enumerable: false | ||
}); | ||
if(!listeners._current) { | ||
listeners._current = listener; | ||
} | ||
else if(!Array.isArray(listeners._current)) { | ||
listeners._current = [listeners._current,listener] | ||
} | ||
else { | ||
listeners._current.push(listener); | ||
} | ||
if(Object.getOwnPropertyDescriptor((this.__proto__||Object.getPrototypeOf(this)),dispatchesChangesMethodName) === void 0) { | ||
Object.defineProperty((this.__proto__||Object.getPrototypeOf(this)), dispatchesChangesMethodName, dispatchesChangesPropertyDescriptor); | ||
} | ||
this.dispatchesRangeChanges = true; | ||
var self = this; | ||
@@ -72,2 +154,3 @@ return function cancelRangeChangeListener() { | ||
RangeChanges.prototype.removeRangeChangeListener = function (listener, token, beforeChange) { | ||
@@ -78,12 +161,28 @@ var descriptor = this.getRangeChangeDescriptor(token); | ||
if (beforeChange) { | ||
listeners = descriptor.willChangeListeners; | ||
listeners = descriptor._willChangeListeners; | ||
} else { | ||
listeners = descriptor.changeListeners; | ||
listeners = descriptor._changeListeners; | ||
} | ||
var index = listeners.lastIndexOf(listener); | ||
if (index === -1) { | ||
throw new Error("Can't remove range change listener: does not exist: token " + JSON.stringify(token)); | ||
if(listeners._current) { | ||
if(listeners._current === listener) { | ||
listeners._current = null; | ||
} | ||
else { | ||
var index = listeners._current.lastIndexOf(listener); | ||
if (index === -1) { | ||
throw new Error("Can't remove range change listener: does not exist: token " + JSON.stringify(token)); | ||
} | ||
else { | ||
if(descriptor.isActive) { | ||
listeners.ghostCount = listeners.ghostCount+1 | ||
listeners._current[index]=ListenerGhost | ||
} | ||
else { | ||
listeners._current.spliceOne(index); | ||
} | ||
} | ||
} | ||
} | ||
listeners.splice(index, 1); | ||
}; | ||
@@ -93,43 +192,60 @@ | ||
var descriptors = this.getAllRangeChangeDescriptors(); | ||
var changeName = "Range" + (beforeChange ? "WillChange" : "Change"); | ||
descriptors.forEach(function (descriptor, token) { | ||
descriptors.dispatchBeforeChange = beforeChange; | ||
descriptors.forEach(function (descriptor, token, descriptors) { | ||
if (descriptor.isActive) { | ||
return; | ||
} else { | ||
descriptor.isActive = true; | ||
} | ||
// before or after | ||
var listeners; | ||
if (beforeChange) { | ||
listeners = descriptor.willChangeListeners; | ||
} else { | ||
listeners = descriptor.changeListeners; | ||
} | ||
var listeners = beforeChange ? descriptor._willChangeListeners : descriptor._changeListeners; | ||
if(listeners && listeners._current) { | ||
var tokenName = listeners.specificHandlerMethodName; | ||
if(Array.isArray(listeners._current) && listeners._current.length) { | ||
// notably, defaults to "handleRangeChange" or "handleRangeWillChange" | ||
// if token is "" (the default) | ||
var tokenName = "handle" + ( | ||
token.slice(0, 1).toUpperCase() + | ||
token.slice(1) | ||
) + changeName; | ||
// notably, defaults to "handleRangeChange" or "handleRangeWillChange" | ||
// if token is "" (the default) | ||
// dispatch each listener | ||
try { | ||
listeners.slice().forEach(function (listener) { | ||
if (listeners.indexOf(listener) < 0) { | ||
return; | ||
descriptor.isActive = true; | ||
// dispatch each listener | ||
try { | ||
var i, | ||
countI, | ||
listener, | ||
//removeGostListenersIfNeeded returns listeners.current or a new filtered one when conditions are met | ||
currentListeners = listeners.removeCurrentGostListenersIfNeeded(), | ||
Ghost = ListenerGhost; | ||
for(i=0, countI = currentListeners.length;i<countI;i++) { | ||
if ((listener = currentListeners[i]) !== Ghost) { | ||
if (listener[tokenName]) { | ||
listener[tokenName](plus, minus, index, this, beforeChange); | ||
} else if (listener.call) { | ||
listener.call(this, plus, minus, index, this, beforeChange); | ||
} else { | ||
throw new Error("Handler " + listener + " has no method " + tokenName + " and is not callable"); | ||
} | ||
} | ||
} | ||
} finally { | ||
descriptor.isActive = false; | ||
} | ||
if (listener[tokenName]) { | ||
listener[tokenName](plus, minus, index, this, beforeChange); | ||
} else if (listener.call) { | ||
listener.call(this, plus, minus, index, this, beforeChange); | ||
} else { | ||
throw new Error("Handler " + listener + " has no method " + tokenName + " and is not callable"); | ||
} | ||
else { | ||
descriptor.isActive = true; | ||
// dispatch each listener | ||
try { | ||
listener = listeners._current; | ||
if (listener[tokenName]) { | ||
listener[tokenName](plus, minus, index, this, beforeChange); | ||
} else if (listener.call) { | ||
listener.call(this, plus, minus, index, this, beforeChange); | ||
} else { | ||
throw new Error("Handler " + listener + " has no method " + tokenName + " and is not callable"); | ||
} | ||
} finally { | ||
descriptor.isActive = false; | ||
} | ||
}, this); | ||
} finally { | ||
descriptor.isActive = false; | ||
} | ||
} | ||
}, this); | ||
@@ -149,2 +265,1 @@ }; | ||
}; | ||
@@ -8,2 +8,3 @@ "use strict"; | ||
var PropertyChanges = require("./listen/property-changes"); | ||
var MapChanges = require("./listen/map-changes"); | ||
@@ -41,3 +42,7 @@ module.exports = LruMap; | ||
Object.addEach(LruMap.prototype, PropertyChanges.prototype); | ||
Object.addEach(LruMap.prototype, MapChanges.prototype); | ||
Object.defineProperty(LruMap.prototype,"size",GenericCollection._sizePropertyDescriptor); | ||
LruMap.from = GenericCollection.from; | ||
LruMap.prototype.constructClone = function (values) { | ||
@@ -79,4 +84,3 @@ return new this.constructor( | ||
} | ||
GenericMap.prototype.addMapChangeListener.apply(this, arguments); | ||
MapChanges.prototype.addMapChangeListener.apply(this, arguments); | ||
}; | ||
"use strict"; | ||
var Shim = require("./shim"); | ||
var Set = require("./set"); | ||
var Set = require("./set").CollectionsSet; | ||
var GenericCollection = require("./generic-collection"); | ||
@@ -35,2 +35,4 @@ var GenericSet = require("./generic-set"); | ||
Object.addEach(LruSet.prototype, RangeChanges.prototype); | ||
Object.defineProperty(LruSet.prototype,"size",GenericCollection._sizePropertyDescriptor); | ||
LruSet.from = GenericCollection.from; | ||
@@ -149,2 +151,1 @@ LruSet.prototype.constructClone = function (values) { | ||
}; | ||
61
map.js
"use strict"; | ||
var Shim = require("./shim"); | ||
var Set = require("./set"); | ||
var GenericCollection = require("./generic-collection"); | ||
var GenericMap = require("./generic-map"); | ||
var Map = require("./_map"); | ||
var PropertyChanges = require("./listen/property-changes"); | ||
var MapChanges = require("./listen/map-changes"); | ||
module.exports = Map; | ||
function Map(values, equals, hash, getDefault) { | ||
if (!(this instanceof Map)) { | ||
return new Map(values, equals, hash, getDefault); | ||
} | ||
equals = equals || Object.equals; | ||
hash = hash || Object.hash; | ||
getDefault = getDefault || Function.noop; | ||
this.contentEquals = equals; | ||
this.contentHash = hash; | ||
this.getDefault = getDefault; | ||
this.store = new Set( | ||
undefined, | ||
function keysEqual(a, b) { | ||
return equals(a.key, b.key); | ||
}, | ||
function keyHash(item) { | ||
return hash(item.key); | ||
} | ||
); | ||
this.length = 0; | ||
this.addEach(values); | ||
if(global.Map === void 0) { | ||
Object.addEach(Map.prototype, PropertyChanges.prototype); | ||
Object.addEach(Map.prototype, MapChanges.prototype); | ||
} | ||
else { | ||
Map.Map = Map; // hack so require("map").Map will work in MontageJS | ||
Object.addEach(Map.prototype, GenericCollection.prototype); | ||
Object.addEach(Map.prototype, GenericMap.prototype); // overrides GenericCollection | ||
Object.addEach(Map.prototype, PropertyChanges.prototype); | ||
Object.defineProperty(Map.prototype,"size",GenericCollection._sizePropertyDescriptor); | ||
Map.prototype.constructClone = function (values) { | ||
return new this.constructor( | ||
values, | ||
this.contentEquals, | ||
this.contentHash, | ||
this.getDefault | ||
); | ||
}; | ||
Map.prototype.log = function (charmap, logNode, callback, thisp) { | ||
logNode = logNode || this.logNode; | ||
this.store.log(charmap, function (node, log, logBefore) { | ||
logNode(node.value.value, log, logBefore); | ||
}, callback, thisp); | ||
}; | ||
Map.prototype.logNode = function (node, log) { | ||
log(' key: ' + node.key); | ||
log(' value: ' + node.value); | ||
}; | ||
Object.defineEach(Map.prototype, PropertyChanges.prototype, false, /*configurable*/true, /*enumerable*/ false, /*writable*/true); | ||
Object.defineEach(Map.prototype, MapChanges.prototype, false, /*configurable*/true, /*enumerable*/ false, /*writable*/true); | ||
} |
"use strict"; | ||
var Map = require("./map"); | ||
var Map = require("./map").CollectionsMap; | ||
@@ -41,2 +41,1 @@ module.exports = MultiMap; | ||
}; | ||
{ | ||
"name": "collections", | ||
"version": "3.0.0", | ||
"version": "5.0.0", | ||
"description": "data structures with idiomatic JavaScript collection interfaces", | ||
@@ -34,3 +34,3 @@ "homepage": "http://www.collectionsjs.com", | ||
"devDependencies": { | ||
"jasmine-node": "~1.14.x", | ||
"jasmine-node": "montagestudio/jasmine-node#master", | ||
"istanbul": "*", | ||
@@ -37,0 +37,0 @@ "opener": "*" |
334
set.js
"use strict"; | ||
var Shim = require("./shim"); | ||
var List = require("./list"); | ||
var FastSet = require("./fast-set"); | ||
var GenericCollection = require("./generic-collection"); | ||
var GenericSet = require("./generic-set"); | ||
var Set = require("./_set"); | ||
var PropertyChanges = require("./listen/property-changes"); | ||
var RangeChanges = require("./listen/range-changes"); | ||
var Iterator = require("./iterator"); | ||
var MapChanges = require("./listen/map-changes"); | ||
var GlobalSet; | ||
module.exports = Set; | ||
function Set(values, equals, hash, getDefault) { | ||
if (!(this instanceof Set)) { | ||
return new Set(values, equals, hash, getDefault); | ||
if(global.Set !== void 0) { | ||
GlobalSet = global.Set; | ||
module.exports = Set | ||
// use different strategies for making sets observable between Internet | ||
// Explorer and other browsers. | ||
var protoIsSupported = {}.__proto__ === Object.prototype, | ||
set_makeObservable; | ||
if (protoIsSupported) { | ||
set_makeObservable = function () { | ||
this.__proto__ = ChangeDispatchSet; | ||
}; | ||
} else { | ||
set_makeObservable = function () { | ||
Object.defineProperties(this, observableSetProperties); | ||
}; | ||
} | ||
equals = equals || Object.equals; | ||
hash = hash || Object.hash; | ||
getDefault = getDefault || Function.noop; | ||
this.contentEquals = equals; | ||
this.contentHash = hash; | ||
this.getDefault = getDefault; | ||
// a list of values in insertion order, used for all operations that depend | ||
// on iterating in insertion order | ||
this.order = new this.Order(undefined, equals); | ||
// a set of nodes from the order list, indexed by the corresponding value, | ||
// used for all operations that need to quickly seek value in the list | ||
this.store = new this.Store( | ||
undefined, | ||
function (a, b) { | ||
return equals(a.value, b.value); | ||
Object.defineProperty(GlobalSet.prototype, "makeObservable", { | ||
value: set_makeObservable, | ||
writable: true, | ||
configurable: true, | ||
enumerable: false | ||
}); | ||
var set_clear = GlobalSet.prototype.clear, | ||
set_add = GlobalSet.prototype.add, | ||
set_delete = GlobalSet.prototype.delete; | ||
var observableSetProperties = { | ||
"_dispatchEmptyArray": { | ||
value: [] | ||
}, | ||
function (node) { | ||
return hash(node.value); | ||
} | ||
); | ||
this.length = 0; | ||
this.addEach(values); | ||
} | ||
clear : { | ||
value: function () { | ||
var clearing; | ||
if (this.dispatchesRangeChanges) { | ||
clearing = this.toArray(); | ||
this.dispatchBeforeRangeChange(this._dispatchEmptyArray, clearing, 0); | ||
} | ||
Set.Set = Set; // hack so require("set").Set will work in MontageJS | ||
set_clear.call(this); | ||
Object.addEach(Set.prototype, GenericCollection.prototype); | ||
Object.addEach(Set.prototype, GenericSet.prototype); | ||
Object.addEach(Set.prototype, PropertyChanges.prototype); | ||
Object.addEach(Set.prototype, RangeChanges.prototype); | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchRangeChange(this._dispatchEmptyArray, clearing, 0); | ||
} | ||
}, | ||
writable: true, | ||
configurable: true | ||
Object.defineProperty(Set.prototype,"size",GenericCollection._sizePropertyDescriptor); | ||
}, | ||
add : { | ||
value: function (value) { | ||
if (!this.has(value)) { | ||
var index = this.size; | ||
var dispatchValueArray = [value]; | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchBeforeRangeChange(dispatchValueArray, this._dispatchEmptyArray, index); | ||
} | ||
Set.prototype.Order = List; | ||
Set.prototype.Store = FastSet; | ||
set_add.call(this,value); | ||
Set.prototype.constructClone = function (values) { | ||
return new this.constructor(values, this.contentEquals, this.contentHash, this.getDefault); | ||
}; | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchRangeChange(dispatchValueArray, this._dispatchEmptyArray, index); | ||
} | ||
return true; | ||
} | ||
return false; | ||
}, | ||
writable: true, | ||
configurable: true | ||
}, | ||
Set.prototype.has = function (value) { | ||
var node = new this.order.Node(value); | ||
return this.store.has(node); | ||
}; | ||
"delete": { | ||
value: function (value,index) { | ||
if (this.has(value)) { | ||
if(index === undefined) { | ||
var setIterator = this.values(); | ||
index = 0 | ||
while(setIterator.next().value !== value) { | ||
index++; | ||
} | ||
} | ||
var dispatchValueArray = [value]; | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchBeforeRangeChange(this._dispatchEmptyArray, dispatchValueArray, index); | ||
} | ||
Set.prototype.get = function (value, equals) { | ||
if (equals) { | ||
throw new Error("Set#get does not support second argument: equals"); | ||
} | ||
var node = new this.order.Node(value); | ||
node = this.store.get(node); | ||
if (node) { | ||
return node.value; | ||
} else { | ||
return this.getDefault(value); | ||
} | ||
}; | ||
set_delete.call(this,value); | ||
Set.prototype.add = function (value) { | ||
var node = new this.order.Node(value); | ||
if (!this.store.has(node)) { | ||
var index = this.length; | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchBeforeRangeChange([value], [], index); | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchRangeChange(this._dispatchEmptyArray, dispatchValueArray, index); | ||
} | ||
return true; | ||
} | ||
return false; | ||
} | ||
} | ||
this.order.add(value); | ||
node = this.order.head.prev; | ||
this.store.add(node); | ||
this.length++; | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchRangeChange([value], [], index); | ||
} | ||
return true; | ||
} | ||
return false; | ||
}; | ||
}; | ||
Set.prototype["delete"] = function (value, equals) { | ||
if (equals) { | ||
throw new Error("Set#delete does not support second argument: equals"); | ||
} | ||
var node = new this.order.Node(value); | ||
if (this.store.has(node)) { | ||
node = this.store.get(node); | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchBeforeRangeChange([], [value], node.index); | ||
} | ||
this.store["delete"](node); // removes from the set | ||
this.order.splice(node, 1); // removes the node from the list | ||
this.length--; | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchRangeChange([], [value], node.index); | ||
} | ||
return true; | ||
} | ||
return false; | ||
}; | ||
var ChangeDispatchSet = Object.create(GlobalSet.prototype, observableSetProperties); | ||
Set.prototype.pop = function () { | ||
if (this.length) { | ||
var result = this.order.head.prev.value; | ||
this["delete"](result); | ||
return result; | ||
} | ||
}; | ||
Set.prototype.shift = function () { | ||
if (this.length) { | ||
var result = this.order.head.next.value; | ||
this["delete"](result); | ||
return result; | ||
} | ||
}; | ||
Object.defineEach(Set.prototype, PropertyChanges.prototype, false, /*configurable*/true, /*enumerable*/ false, /*writable*/true); | ||
//This is a no-op test in property-changes.js - PropertyChanges.prototype.makePropertyObservable, so might as well not pay the price every time.... | ||
Object.defineProperty(Set.prototype, "makePropertyObservable", { | ||
value: function(){}, | ||
writable: true, | ||
configurable: true, | ||
enumerable: false | ||
}); | ||
Set.prototype.one = function () { | ||
if (this.length > 0) { | ||
return this.store.one().value; | ||
} | ||
}; | ||
Object.defineEach(Set.prototype, RangeChanges.prototype, false, /*configurable*/true, /*enumerable*/ false, /*writable*/true); | ||
Object.defineEach(Set.prototype, MapChanges.prototype, false, /*configurable*/true, /*enumerable*/ false, /*writable*/true); | ||
Set.prototype.clear = function () { | ||
var clearing; | ||
if (this.dispatchesRangeChanges) { | ||
clearing = this.toArray(); | ||
this.dispatchBeforeRangeChange([], clearing, 0); | ||
} | ||
var _CollectionsSet = Set.CollectionsSet; | ||
function CollectionsSet(values, equals, hash, getDefault) { | ||
return _CollectionsSet._init(CollectionsSet, this, values, equals, hash, getDefault); | ||
} | ||
this.store.clear(); | ||
this.order.clear(); | ||
this.length = 0; | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchRangeChange([], clearing, 0); | ||
} | ||
}; | ||
Set.prototype.reduce = function (callback, basis /*, thisp*/) { | ||
var thisp = arguments[2]; | ||
var list = this.order; | ||
var index = 0; | ||
return list.reduce(function (basis, value) { | ||
return callback.call(thisp, basis, value, index++, this); | ||
}, basis, this); | ||
}; | ||
// hack so require("set").Set will work in MontageJS | ||
CollectionsSet.Set = CollectionsSet; | ||
CollectionsSet.from = _CollectionsSet.from; | ||
Set.CollectionsSet = CollectionsSet; | ||
Set.prototype.reduceRight = function (callback, basis /*, thisp*/) { | ||
var thisp = arguments[2]; | ||
var list = this.order; | ||
var index = this.length - 1; | ||
return list.reduceRight(function (basis, value) { | ||
return callback.call(thisp, basis, value, index--, this); | ||
}, basis, this); | ||
}; | ||
CollectionsSet.prototype = new _CollectionsSet(); | ||
CollectionsSet.prototype.constructor = CollectionsSet; | ||
Set.prototype.iterate = function () { | ||
return this.order.iterate(); | ||
}; | ||
var List = require("./list"); | ||
var FastSet = require("./fast-set"); | ||
CollectionsSet.prototype.Order = List; | ||
CollectionsSet.prototype.Store = FastSet; | ||
Set.prototype.values = function () { | ||
return new Iterator(this); | ||
}; | ||
Object.defineProperty(CollectionsSet.prototype,"_dispatchEmptyArray", { | ||
value: [] | ||
}); | ||
Set.prototype.log = function () { | ||
var set = this.store; | ||
return set.log.apply(set, arguments); | ||
}; | ||
CollectionsSet.prototype.add = function (value) { | ||
var node = new this.order.Node(value); | ||
if (!this.store.has(node)) { | ||
var index = this.length; | ||
var dispatchValueArray = [value]; | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchBeforeRangeChange(dispatchValueArray, this._dispatchEmptyArray, index); | ||
} | ||
this.order.add(value); | ||
node = this.order.head.prev; | ||
this.store.add(node); | ||
this.length++; | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchRangeChange(dispatchValueArray, this._dispatchEmptyArray, index); | ||
} | ||
return true; | ||
} | ||
return false; | ||
}; | ||
CollectionsSet.prototype["delete"] = function (value, equals) { | ||
if (equals) { | ||
throw new Error("Set#delete does not support second argument: equals"); | ||
} | ||
var node = new this.order.Node(value); | ||
if (this.store.has(node)) { | ||
node = this.store.get(node); | ||
var dispatchValueArray = [value]; | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchBeforeRangeChange(this._dispatchEmptyArray, dispatchValueArray, node.index); | ||
} | ||
this.store["delete"](node); // removes from the set | ||
this.order.splice(node, 1); // removes the node from the list | ||
this.length--; | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchRangeChange(this._dispatchEmptyArray, dispatchValueArray, node.index); | ||
} | ||
return true; | ||
} | ||
return false; | ||
}; | ||
CollectionsSet.prototype.clear = function () { | ||
var clearing; | ||
if (this.dispatchesRangeChanges) { | ||
clearing = this.toArray(); | ||
this.dispatchBeforeRangeChange(this._dispatchEmptyArray, clearing, 0); | ||
} | ||
this._clear(); | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchRangeChange(this._dispatchEmptyArray, clearing, 0); | ||
} | ||
}; | ||
Set.prototype.makeObservable = function () { | ||
if(global.Set === void 0) { | ||
module.exports = CollectionsSet | ||
} | ||
Object.addEach(Set.CollectionsSet.prototype, PropertyChanges.prototype); | ||
Object.addEach(Set.CollectionsSet.prototype, RangeChanges.prototype); | ||
Set.CollectionsSet.prototype.makeObservable = function () { | ||
this.order.makeObservable(); | ||
}; |
@@ -115,3 +115,3 @@ "use strict"; | ||
if (index !== -1) { | ||
this.splice(index, 1); | ||
this.spliceOne(index); | ||
return true; | ||
@@ -258,3 +258,3 @@ } | ||
length = Math.min(this.length, that.length); | ||
length = (this.length < that.length) ? this.length : that.length; | ||
@@ -340,2 +340,19 @@ for (i = 0; i < length; i++) { | ||
if(Array.prototype.spliceOne === void 0) { | ||
define("spliceOne", function (index,itemToAdd) { | ||
var len=this.length; | ||
if (!len) { return } | ||
if(arguments.length === 1) { | ||
while (index<len) { | ||
this[index] = this[index+1]; | ||
index++ | ||
} | ||
this.length--; | ||
} | ||
else { | ||
this[index] = itemToAdd; | ||
} | ||
}); | ||
} | ||
define("Iterator", ArrayIterator); | ||
@@ -342,0 +359,0 @@ |
@@ -194,3 +194,4 @@ "use strict"; | ||
Object.addEach = function (target, source) { | ||
Object.addEach = function (target, source, overrides) { | ||
var overridesExistingProperty = arguments.length === 3 ? overrides : true; | ||
if (!source) { | ||
@@ -216,5 +217,7 @@ } else if (typeof source.forEach === "function" && !source.hasOwnProperty("forEach")) { | ||
// copy other objects as map-alikes | ||
Object.keys(source).forEach(function (key) { | ||
target[key] = source[key]; | ||
}); | ||
for(var keys = Object.keys(source), i = 0, key;(key = keys[i]); i++) { | ||
if(overridesExistingProperty || !Object.owns(target,key)) { | ||
target[key] = source[key]; | ||
} | ||
} | ||
} | ||
@@ -224,2 +227,73 @@ return target; | ||
/* | ||
var defineEach = function defineEach(target, prototype) { | ||
// console.log("Map defineEach: ",Object.keys(prototype)); | ||
var proto = Map.prototype; | ||
for (var name in prototype) { | ||
if(!proto.hasOwnProperty(name)) { | ||
Object.defineProperty(proto, name, { | ||
value: prototype[name], | ||
writable: writable, | ||
configurable: configurable, | ||
enumerable: enumerable | ||
}); | ||
} | ||
} | ||
} | ||
*/ | ||
Object.defineEach = function (target, source, overrides, configurable, enumerable, writable) { | ||
var overridesExistingProperty = arguments.length === 3 ? overrides : true; | ||
if (!source) { | ||
} else if (typeof source.forEach === "function" && !source.hasOwnProperty("forEach")) { | ||
// copy map-alikes | ||
if (source.isMap === true) { | ||
source.forEach(function (value, key) { | ||
Object.defineProperty(target, key, { | ||
value: value, | ||
writable: writable, | ||
configurable: configurable, | ||
enumerable: enumerable | ||
}); | ||
}); | ||
// iterate key value pairs of other iterables | ||
} else { | ||
source.forEach(function (pair) { | ||
Object.defineProperty(target, pair[0], { | ||
value: pair[1], | ||
writable: writable, | ||
configurable: configurable, | ||
enumerable: enumerable | ||
}); | ||
}); | ||
} | ||
} else if (typeof source.length === "number") { | ||
// arguments, strings | ||
for (var index = 0; index < source.length; index++) { | ||
Object.defineProperty(target, index, { | ||
value: source[index], | ||
writable: writable, | ||
configurable: configurable, | ||
enumerable: enumerable | ||
}); | ||
} | ||
} else { | ||
// copy other objects as map-alikes | ||
for(var keys = Object.keys(source), i = 0, key;(key = keys[i]); i++) { | ||
if(overridesExistingProperty || !Object.owns(target,key)) { | ||
Object.defineProperty(target, key, { | ||
value: source[key], | ||
writable: writable, | ||
configurable: configurable, | ||
enumerable: enumerable | ||
}); | ||
} | ||
} | ||
} | ||
return target; | ||
}; | ||
/** | ||
@@ -355,2 +429,3 @@ Iterates over the owned properties of an object. | ||
equals = equals || Object.equals; | ||
//console.log("Object.equals: a:",a, "b:",b, "equals:",equals); | ||
// unbox objects, but do not confuse object literals | ||
@@ -357,0 +432,0 @@ a = Object.getValueOf(a); |
@@ -8,2 +8,3 @@ "use strict"; | ||
var PropertyChanges = require("./listen/property-changes"); | ||
var MapChanges = require("./listen/map-changes"); | ||
@@ -41,3 +42,6 @@ module.exports = SortedArrayMap; | ||
Object.addEach(SortedArrayMap.prototype, PropertyChanges.prototype); | ||
Object.addEach(SortedArrayMap.prototype, MapChanges.prototype); | ||
SortedArrayMap.from = GenericCollection.from; | ||
SortedArrayMap.prototype.isSorted = true; | ||
@@ -53,2 +57,1 @@ | ||
}; | ||
@@ -27,2 +27,4 @@ "use strict"; | ||
SortedArraySet.from = SortedArray.from; | ||
SortedArraySet.prototype.isSorted = true; | ||
@@ -54,2 +56,1 @@ | ||
}; | ||
@@ -31,2 +31,4 @@ "use strict"; | ||
SortedArray.from = GenericCollection.from; | ||
Object.addEach(SortedArray.prototype, GenericCollection.prototype); | ||
@@ -132,3 +134,3 @@ Object.addEach(SortedArray.prototype, PropertyChanges.prototype); | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchBeforeRangeChange([value], [], index); | ||
this.dispatchBeforeRangeChange([value], Array.empty, index); | ||
} | ||
@@ -138,3 +140,3 @@ this.array.splice(index, 0, value); | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchRangeChange([value], [], index); | ||
this.dispatchRangeChange([value], Array.empty, index); | ||
} | ||
@@ -151,8 +153,8 @@ return true; | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchBeforeRangeChange([], [value], index); | ||
this.dispatchBeforeRangeChange(Array.empty, [value], index); | ||
} | ||
this.array.splice(index, 1); | ||
this.array.spliceOne(index); | ||
this.length--; | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchRangeChange([], [value], index); | ||
this.dispatchRangeChange(Array.empty, [value], index); | ||
} | ||
@@ -179,3 +181,3 @@ return true; | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchBeforeRangeChange([], minus, start); | ||
this.dispatchBeforeRangeChange(Array.empty, minus, start); | ||
} | ||
@@ -185,3 +187,3 @@ this.array.splice(start, minus.length); | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchRangeChange([], minus, start); | ||
this.dispatchRangeChange(Array.empty, minus, start); | ||
} | ||
@@ -258,3 +260,3 @@ return minus.length; | ||
if (index === undefined && length === undefined) { | ||
return []; | ||
return Array.empty; | ||
} | ||
@@ -275,3 +277,3 @@ index = index || 0; | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchRangeChange([], minus, index); | ||
this.dispatchRangeChange(Array.empty, minus, index); | ||
} | ||
@@ -316,3 +318,3 @@ this.addEach(plus); | ||
minus = this.array.slice(); | ||
this.dispatchBeforeRangeChange([], minus, 0); | ||
this.dispatchBeforeRangeChange(Array.empty, minus, 0); | ||
} | ||
@@ -322,3 +324,3 @@ this.length = 0; | ||
if (this.dispatchesRangeChanges) { | ||
this.dispatchRangeChange([], minus, 0); | ||
this.dispatchRangeChange(Array.empty, minus, 0); | ||
} | ||
@@ -325,0 +327,0 @@ }; |
@@ -8,2 +8,3 @@ "use strict"; | ||
var PropertyChanges = require("./listen/property-changes"); | ||
var MapChanges = require("./listen/map-changes"); | ||
@@ -38,5 +39,9 @@ module.exports = SortedMap; | ||
SortedMap.from = GenericCollection.from; | ||
Object.addEach(SortedMap.prototype, GenericCollection.prototype); | ||
Object.addEach(SortedMap.prototype, GenericMap.prototype); | ||
Object.addEach(SortedMap.prototype, PropertyChanges.prototype); | ||
Object.addEach(SortedMap.prototype, MapChanges.prototype); | ||
Object.defineProperty(SortedMap.prototype,"size",GenericCollection._sizePropertyDescriptor); | ||
@@ -51,2 +56,5 @@ SortedMap.prototype.constructClone = function (values) { | ||
}; | ||
SortedMap.prototype.iterate = function () { | ||
return this.store.iterate(); | ||
}; | ||
@@ -64,2 +72,1 @@ SortedMap.prototype.log = function (charmap, logNode, callback, thisp) { | ||
}; | ||
@@ -31,2 +31,4 @@ "use strict"; | ||
Object.addEach(SortedSet.prototype, RangeChanges.prototype); | ||
Object.defineProperty(SortedSet.prototype,"size",GenericCollection._sizePropertyDescriptor); | ||
SortedSet.from = GenericCollection.from; | ||
@@ -33,0 +35,0 @@ SortedSet.prototype.isSorted = true; |
@@ -1,1 +0,1 @@ | ||
module.exports = require("weak-map"); | ||
module.exports = (global.WeakMap !== void 0) ? global.WeakMap : require("weak-map"); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
277636
48
7387