From 21db161c43998802af593eb914054f24fb9306cc Mon Sep 17 00:00:00 2001 From: cjblomqvist Date: Thu, 3 Dec 2015 20:53:26 +0100 Subject: [PATCH 01/17] Add new way of maintaining list of toPaths for greatly enhanced performance... Basically, this enables much fewer refs to be looped in onIndexChange for event, which can have dramatic effect when you have a combo of many refs and many index events. --- lib/Model/ref.js | 112 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 108 insertions(+), 4 deletions(-) diff --git a/lib/Model/ref.js b/lib/Model/ref.js index d0d5e7787..877dc0fbb 100644 --- a/lib/Model/ref.js +++ b/lib/Model/ref.js @@ -48,11 +48,13 @@ function addIndexListeners(model) { onIndexChange(segments, patchMove); }); function onIndexChange(segments, patch) { - var fromMap = model._refs.fromMap; - for (var from in fromMap) { - var ref = fromMap[from]; + var toPathMap = model._refs.toPathMap; + var refs = toPathMap.get(segments) || []; + + for(var i = 0, len = refs.length; i < len; i++) { + var ref = refs[i]; + var from = ref.from; if (!(ref.updateIndices && - util.contains(segments, ref.toSegments) && ref.toSegments.length > segments.length)) continue; var index = +ref.toSegments[segments.length]; var patched = patch(index); @@ -285,10 +287,12 @@ function Refs() { this.fromMap = new FromMap(); this.toMap = new ToMap(); this.parentToMap = new ToMap(); + this.toPathMap = new PathListMap(); } Refs.prototype.add = function(ref) { this.fromMap[ref.from] = ref; + this.toPathMap.add(ref.fromSegments, ref); // added listMapAdd(this.toMap, ref.to, ref); for (var i = 0, len = ref.parentTos.length; i < len; i++) { listMapAdd(this.parentToMap, ref.parentTos[i], ref); @@ -299,6 +303,7 @@ Refs.prototype.remove = function(from) { var ref = this.fromMap[from]; if (!ref) return; delete this.fromMap[from]; + this.toPathMap.delete(ref.toSegments, ref); // added listMapRemove(this.toMap, ref.to, ref); for (var i = 0, len = ref.parentTos.length; i < len; i++) { listMapRemove(this.parentToMap, ref.parentTos[i], ref); @@ -328,3 +333,102 @@ function listMapRemove(map, name, item) { items.splice(index, 1); if (!items.length) delete map[name]; } + +function PathListMap() { + this.map = {}; +} + +PathListMap.prototype.add = function (segments, item) { + var map = this.map; + + for(var i = 0, len = segments.length - 1; i < len; i++) { + map[segments[i]] = map[segments[i]] || {}; + map = map[segments[i]]; + } + + var segment = segments.pop(); + + map[segments] = map[segments] || []; + map[segments].push(item); +}; + +PathListMap.prototype.get = function (segments) { + var val = this.map; + + for(var i = 0, len = segments.length; i < len; i++) { + val = val[segments[i]]; + if(!val) return; + } + + val = flatten(val); + + return val; +}; + +function flatten(obj) { + if(Array.isArray(obj)) return obj; + + var arr = []; + var keys = Object.keys(obj); + + for(var i = 0, len = keys.length; i < len; i++) { + arr.concat(flatten(obj[i])); + } + + return arr; +} + +PathListMap.prototype.delete = function (segments, item) { + delList(this.map, segments, item, true); +}; + +function delList(map, segments, item, safe) { + var segment = segments.shift(); + + if(!segments.length) { + var arr = map[segment]; + + if(!arr) return true; + + if(arr.length < 2) { + if(safe) { + delete map[segment]; + return false; + } else { + return true; + } + } else { + var i = arr.indexOf(item); + + if(i > -1) arr.splice(i, 1); + + return false; + } + + if(safe) { + var arr = map[segment]; + if(arr.length > 1) { + + } else { + delete map[segment]; + } + } else { + return true; + } + } + + var nextMap = map[segment]; + if(!nextMap) return true; + + var nextSafe = (Object.keys(nextMap).length > 1); + var remove = del(nextMap, segments, item, nextSafe); + + if(remove) { + if(safe) { + delete map[segment]; + return false; + } else { + return true; + } + } +} From b7722cae7d2a9a8d40412e3e55fed8d999722735 Mon Sep 17 00:00:00 2001 From: cjblomqvist Date: Thu, 3 Dec 2015 21:29:06 +0100 Subject: [PATCH 02/17] A lot of fixes to previous rework using smarter PathMap --- lib/Model/ref.js | 46 ++++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/lib/Model/ref.js b/lib/Model/ref.js index 877dc0fbb..b62ce7a95 100644 --- a/lib/Model/ref.js +++ b/lib/Model/ref.js @@ -292,7 +292,7 @@ function Refs() { Refs.prototype.add = function(ref) { this.fromMap[ref.from] = ref; - this.toPathMap.add(ref.fromSegments, ref); // added + this.toPathMap.add(ref.toSegments, ref); listMapAdd(this.toMap, ref.to, ref); for (var i = 0, len = ref.parentTos.length; i < len; i++) { listMapAdd(this.parentToMap, ref.parentTos[i], ref); @@ -303,7 +303,7 @@ Refs.prototype.remove = function(from) { var ref = this.fromMap[from]; if (!ref) return; delete this.fromMap[from]; - this.toPathMap.delete(ref.toSegments, ref); // added + this.toPathMap.delete(ref.toSegments, ref); listMapRemove(this.toMap, ref.to, ref); for (var i = 0, len = ref.parentTos.length; i < len; i++) { listMapRemove(this.parentToMap, ref.parentTos[i], ref); @@ -342,14 +342,14 @@ PathListMap.prototype.add = function (segments, item) { var map = this.map; for(var i = 0, len = segments.length - 1; i < len; i++) { - map[segments[i]] = map[segments[i]] || {}; + map[segments[i]] = map[segments[i]] || {"$items": []}; map = map[segments[i]]; } - var segment = segments.pop(); + var segment = segments[segments.length - 1]; - map[segments] = map[segments] || []; - map[segments].push(item); + map[segment] = map[segment] || {"$items": []}; + map[segment]['$items'].push(item); }; PathListMap.prototype.get = function (segments) { @@ -366,12 +366,12 @@ PathListMap.prototype.get = function (segments) { }; function flatten(obj) { - if(Array.isArray(obj)) return obj; - - var arr = []; + var arr = obj.items; var keys = Object.keys(obj); for(var i = 0, len = keys.length; i < len; i++) { + if(keys[i] === '$items') continue; + arr.concat(flatten(obj[i])); } @@ -379,18 +379,19 @@ function flatten(obj) { } PathListMap.prototype.delete = function (segments, item) { - delList(this.map, segments, item, true); + delList(this.map, segments.slice(0), item, true); }; function delList(map, segments, item, safe) { var segment = segments.shift(); if(!segments.length) { - var arr = map[segment]; + if(!map[segment] || !map[segment]['$items']) return true; - if(!arr) return true; + var items = map[segment]['$items']; + var keys = Object.keys(map[segment]); - if(arr.length < 2) { + if(items.length < 2 && keys.length < 2) { if(safe) { delete map[segment]; return false; @@ -398,30 +399,19 @@ function delList(map, segments, item, safe) { return true; } } else { - var i = arr.indexOf(item); + var i = items.indexOf(item); - if(i > -1) arr.splice(i, 1); + if(i > -1) items.splice(i, 1); return false; } - - if(safe) { - var arr = map[segment]; - if(arr.length > 1) { - - } else { - delete map[segment]; - } - } else { - return true; - } } var nextMap = map[segment]; if(!nextMap) return true; - var nextSafe = (Object.keys(nextMap).length > 1); - var remove = del(nextMap, segments, item, nextSafe); + var nextSafe = (Object.keys(nextMap).length > 2 || nextMap['$items'].length); + var remove = delList(nextMap, segments, item, nextSafe); if(remove) { if(safe) { From 2e4c5e0e38cc22e44e1137e00c061f50ffd7700e Mon Sep 17 00:00:00 2001 From: cjblomqvist Date: Thu, 3 Dec 2015 21:31:36 +0100 Subject: [PATCH 03/17] Add PathMap for from as well, in preparation for removing to and fromMaps in other locations --- lib/Model/ref.js | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/lib/Model/ref.js b/lib/Model/ref.js index b62ce7a95..bd5f28999 100644 --- a/lib/Model/ref.js +++ b/lib/Model/ref.js @@ -288,10 +288,12 @@ function Refs() { this.toMap = new ToMap(); this.parentToMap = new ToMap(); this.toPathMap = new PathListMap(); + this.fromPathMap = new PathListMap(); } Refs.prototype.add = function(ref) { this.fromMap[ref.from] = ref; + this.fromPathMap.add(ref.fromSegments, ref); this.toPathMap.add(ref.toSegments, ref); listMapAdd(this.toMap, ref.to, ref); for (var i = 0, len = ref.parentTos.length; i < len; i++) { @@ -303,6 +305,7 @@ Refs.prototype.remove = function(from) { var ref = this.fromMap[from]; if (!ref) return; delete this.fromMap[from]; + this.fromPathMap.delete(ref.fromSegments); this.toPathMap.delete(ref.toSegments, ref); listMapRemove(this.toMap, ref.to, ref); for (var i = 0, len = ref.parentTos.length; i < len; i++) { @@ -334,6 +337,64 @@ function listMapRemove(map, name, item) { if (!items.length) delete map[name]; } +function PathMap() { + this.map = {}; +} + +PathMap.prototype.add = function (segments, item) { + var map = this.map; + + for(var i = 0, len = segments.length - 1; i < len; i++) { + map[segments[i]] = map[segments[i]] || {}; + map = map[segments[i]]; + } + + map[segments.pop()] = item; +}; + +PathMap.prototype.get = function (segments) { + var val = this.map; + + for(var i = 0, len = segments.length; i < len; i++) { + val = val[segments[i]]; + if(!val) return; + } + + return val; +}; + +PathMap.prototype.delete = function (segments) { + del(this.map, segments, true); +}; + +function del(map, segments, safe) { + var segment = segments.shift(); + + if(!segments.length) { + if(safe) { + delete map[segment]; + return false; + } else { + return true; + } + } + + var nextMap = map[segment]; + if(!nextMap) return true; + + var nextSafe = (Object.keys(nextMap).length > 1); + var remove = del(nextMap, segments, nextSafe); + + if(remove) { + if(safe) { + delete map[segment]; + return false; + } else { + return true; + } + } +} + function PathListMap() { this.map = {}; } From 6a58da9d928a0e1c2e8851c436a19b2b6fa70449 Mon Sep 17 00:00:00 2001 From: cjblomqvist Date: Thu, 3 Dec 2015 21:48:22 +0100 Subject: [PATCH 04/17] Additional fixes to rework where PathMap is used as FromMap for more efficient lookups --- lib/Model/ref.js | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/lib/Model/ref.js b/lib/Model/ref.js index bd5f28999..dd74c8bdb 100644 --- a/lib/Model/ref.js +++ b/lib/Model/ref.js @@ -210,9 +210,16 @@ Model.prototype.removeAllRefs = function(subpath) { this._removeAllRefs(segments); }; Model.prototype._removeAllRefs = function(segments) { - this._removeMapRefs(segments, this.root._refs.fromMap); + this._removePathMapRefs(segments, this.root._refs.fromPathMap); this._removeMapRefs(segments, this.root._refLists.fromMap); }; +Model.prototype._removePathMapRefs = function(segments, map) { + var refs = map.getList(segments); + for(var i = 0, len = refs.length; i < len; i++) { + var ref = refs[i]; + this._removeRef(ref.fromSegments, ref.from); + } +}; Model.prototype._removeMapRefs = function(segments, map) { for (var from in map) { var fromSegments = map[from].fromSegments; @@ -288,7 +295,7 @@ function Refs() { this.toMap = new ToMap(); this.parentToMap = new ToMap(); this.toPathMap = new PathListMap(); - this.fromPathMap = new PathListMap(); + this.fromPathMap = new PathMap(); } Refs.prototype.add = function(ref) { @@ -349,7 +356,7 @@ PathMap.prototype.add = function (segments, item) { map = map[segments[i]]; } - map[segments.pop()] = item; + map[segments[segments.length - 1]] = item; }; PathMap.prototype.get = function (segments) { @@ -363,8 +370,28 @@ PathMap.prototype.get = function (segments) { return val; }; +PathMap.prototype.getList = function (segments) { + var obj = this.get(segments); + + return flattenObj(obj); +}; + +function flattenObj(obj) { + if(!obj) return []; + if(obj instanceof Ref) return [obj]; + + var arr = []; + var keys = Object.keys(obj); + + for(var i = 0, len = keys.length; i < len; i++) { + arr.concat(flattenObj(obj[keys[i]])); + } + + return arr; +}; + PathMap.prototype.delete = function (segments) { - del(this.map, segments, true); + del(this.map, segments.slice(0), true); }; function del(map, segments, safe) { From a07843b433d5cacdc94deccf681d79911fa1a762 Mon Sep 17 00:00:00 2001 From: cjblomqvist Date: Thu, 3 Dec 2015 21:54:45 +0100 Subject: [PATCH 05/17] Additional rework to support nested levels of items and paths in PathMaps.... also removing dependency to Ref --- lib/Model/ref.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/Model/ref.js b/lib/Model/ref.js index dd74c8bdb..5e777e6e6 100644 --- a/lib/Model/ref.js +++ b/lib/Model/ref.js @@ -356,7 +356,7 @@ PathMap.prototype.add = function (segments, item) { map = map[segments[i]]; } - map[segments[segments.length - 1]] = item; + map[segments[segments.length - 1]] = {"$item": item}; }; PathMap.prototype.get = function (segments) { @@ -367,7 +367,7 @@ PathMap.prototype.get = function (segments) { if(!val) return; } - return val; + return (val && val['$item']) ? val['$item'] : void 0; }; PathMap.prototype.getList = function (segments) { @@ -378,12 +378,14 @@ PathMap.prototype.getList = function (segments) { function flattenObj(obj) { if(!obj) return []; - if(obj instanceof Ref) return [obj]; var arr = []; var keys = Object.keys(obj); + if(obj['$item']) arr.push(obj['$item']); for(var i = 0, len = keys.length; i < len; i++) { + if(keys[i] === '$item') continue; + arr.concat(flattenObj(obj[keys[i]])); } From 1b2f09c8c8f3ce1503fb9e0c4e399e9df2cb9556 Mon Sep 17 00:00:00 2001 From: cjblomqvist Date: Thu, 3 Dec 2015 22:06:03 +0100 Subject: [PATCH 06/17] Use new fromPathMap instead of fromMap when dereferencing --- lib/Model/ref.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Model/ref.js b/lib/Model/ref.js index 5e777e6e6..2be1e9f66 100644 --- a/lib/Model/ref.js +++ b/lib/Model/ref.js @@ -236,7 +236,7 @@ Model.prototype.dereference = function(subpath) { Model.prototype._dereference = function(segments, forArrayMutator, ignore) { if (segments.length === 0) return segments; - var refs = this.root._refs.fromMap; + var refs = this.root._refs.fromPathMap; var refLists = this.root._refLists.fromMap; var doAgain; do { @@ -245,7 +245,7 @@ Model.prototype._dereference = function(segments, forArrayMutator, ignore) { for (var i = 0, len = segments.length; i < len; i++) { subpath = (subpath) ? subpath + '.' + segments[i] : segments[i]; - var ref = refs[subpath]; + var ref = refs.get(subpath.split('.')); if (ref) { var remaining = segments.slice(i + 1); segments = ref.toSegments.concat(remaining); From e0d02ca7a00d65741368a2fba5c75fd50c10ac7b Mon Sep 17 00:00:00 2001 From: cjblomqvist Date: Thu, 3 Dec 2015 22:09:41 +0100 Subject: [PATCH 07/17] Use new fromPathMap instead of fromMap for refs when converting to JSON... enabling eventually removing the regular fromMap --- lib/Model/ref.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/Model/ref.js b/lib/Model/ref.js index 2be1e9f66..4f92d6a8f 100644 --- a/lib/Model/ref.js +++ b/lib/Model/ref.js @@ -323,8 +323,10 @@ Refs.prototype.remove = function(from) { Refs.prototype.toJSON = function() { var out = []; - for (var from in this.fromMap) { - var ref = this.fromMap[from]; + var refs = this.fromPathMap.getList([]); + + for(var i = 0, len = refs.length; i < len; i++) { + var ref = refs[i]; out.push([ref.from, ref.to]); } return out; From 59d801f23f1efa22fe38fedd4405c706860d6adb Mon Sep 17 00:00:00 2001 From: cjblomqvist Date: Thu, 3 Dec 2015 22:13:31 +0100 Subject: [PATCH 08/17] Remove fromMap completely from refs --- lib/Model/ref.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/Model/ref.js b/lib/Model/ref.js index 4f92d6a8f..6eb02a332 100644 --- a/lib/Model/ref.js +++ b/lib/Model/ref.js @@ -287,11 +287,9 @@ function Ref(model, from, to, options) { } this.updateIndices = options && options.updateIndices; } -function FromMap() {} function ToMap() {} function Refs() { - this.fromMap = new FromMap(); this.toMap = new ToMap(); this.parentToMap = new ToMap(); this.toPathMap = new PathListMap(); @@ -299,7 +297,6 @@ function Refs() { } Refs.prototype.add = function(ref) { - this.fromMap[ref.from] = ref; this.fromPathMap.add(ref.fromSegments, ref); this.toPathMap.add(ref.toSegments, ref); listMapAdd(this.toMap, ref.to, ref); @@ -309,9 +306,8 @@ Refs.prototype.add = function(ref) { }; Refs.prototype.remove = function(from) { - var ref = this.fromMap[from]; + var ref = this.fromPathMap.get((from || '').split('.')); if (!ref) return; - delete this.fromMap[from]; this.fromPathMap.delete(ref.fromSegments); this.toPathMap.delete(ref.toSegments, ref); listMapRemove(this.toMap, ref.to, ref); From eac9fdef97a2ed224d1b40be6b98b86527169e37 Mon Sep 17 00:00:00 2001 From: cjblomqvist Date: Thu, 3 Dec 2015 22:18:22 +0100 Subject: [PATCH 09/17] Fix syntax issue with new rework of Maps --- lib/Model/ref.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Model/ref.js b/lib/Model/ref.js index 6eb02a332..8c22494e3 100644 --- a/lib/Model/ref.js +++ b/lib/Model/ref.js @@ -454,7 +454,7 @@ PathListMap.prototype.get = function (segments) { }; function flatten(obj) { - var arr = obj.items; + var arr = obj['$items']; var keys = Object.keys(obj); for(var i = 0, len = keys.length; i < len; i++) { From eb7b6d4132aff5c85ad981bdad47565e5b762982 Mon Sep 17 00:00:00 2001 From: cjblomqvist Date: Thu, 3 Dec 2015 22:23:56 +0100 Subject: [PATCH 10/17] Use toPathMap instead of toMap with refs, part 1 --- lib/Model/ref.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/Model/ref.js b/lib/Model/ref.js index 8c22494e3..237eddad1 100644 --- a/lib/Model/ref.js +++ b/lib/Model/ref.js @@ -113,14 +113,14 @@ function addListener(model, type, fn) { // Find cases where an event is emitted on a path where a reference // is pointing. All original mutations happen on the fully dereferenced // location, so this detection only needs to happen in one direction - var toMap = model._refs.toMap; + var toPathMap = model._refs.toPathMap; var subpath; for (var i = 0, len = segments.length; i < len; i++) { subpath = (subpath) ? subpath + '.' + segments[i] : segments[i]; // If a ref is found pointing to a matching subpath, re-emit on the // place where the reference is coming from as if the mutation also - // occured at that path - var refs = toMap[subpath]; + // occured at that path + var refs = toPathMap.get(subpath.split('.'), true); if (!refs) continue; var remaining = segments.slice(i + 1); for (var refIndex = 0, numRefs = refs.length; refIndex < numRefs; refIndex++) { @@ -440,7 +440,7 @@ PathListMap.prototype.add = function (segments, item) { map[segment]['$items'].push(item); }; -PathListMap.prototype.get = function (segments) { +PathListMap.prototype.get = function (segments, onlyAtLevel) { var val = this.map; for(var i = 0, len = segments.length; i < len; i++) { @@ -448,9 +448,9 @@ PathListMap.prototype.get = function (segments) { if(!val) return; } - val = flatten(val); + if(onlyAtLevel) return val; - return val; + return flatten(val); }; function flatten(obj) { From 4b0b48d57e80eb5d78fd08542f7173fa3f8b3915 Mon Sep 17 00:00:00 2001 From: cjblomqvist Date: Thu, 3 Dec 2015 22:25:03 +0100 Subject: [PATCH 11/17] Remove toMap from refs as it's no longer needed --- lib/Model/ref.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/Model/ref.js b/lib/Model/ref.js index 237eddad1..2852af37f 100644 --- a/lib/Model/ref.js +++ b/lib/Model/ref.js @@ -290,7 +290,6 @@ function Ref(model, from, to, options) { function ToMap() {} function Refs() { - this.toMap = new ToMap(); this.parentToMap = new ToMap(); this.toPathMap = new PathListMap(); this.fromPathMap = new PathMap(); @@ -299,7 +298,6 @@ function Refs() { Refs.prototype.add = function(ref) { this.fromPathMap.add(ref.fromSegments, ref); this.toPathMap.add(ref.toSegments, ref); - listMapAdd(this.toMap, ref.to, ref); for (var i = 0, len = ref.parentTos.length; i < len; i++) { listMapAdd(this.parentToMap, ref.parentTos[i], ref); } @@ -310,7 +308,6 @@ Refs.prototype.remove = function(from) { if (!ref) return; this.fromPathMap.delete(ref.fromSegments); this.toPathMap.delete(ref.toSegments, ref); - listMapRemove(this.toMap, ref.to, ref); for (var i = 0, len = ref.parentTos.length; i < len; i++) { listMapRemove(this.parentToMap, ref.parentTos[i], ref); } From 6e7dc668db003d7a0c489d39f25c35520ab441be Mon Sep 17 00:00:00 2001 From: cjblomqvist Date: Thu, 3 Dec 2015 22:29:47 +0100 Subject: [PATCH 12/17] Remove parentToMap and all other ToMap related stuff from refs, and use PathListMap instead --- lib/Model/ref.js | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/lib/Model/ref.js b/lib/Model/ref.js index 2852af37f..ecbae00b3 100644 --- a/lib/Model/ref.js +++ b/lib/Model/ref.js @@ -140,8 +140,8 @@ function addListener(model, type, fn) { } // If a ref points to a child of a matching subpath, get the value in // case it has changed and set if different - var parentToMap = model._refs.parentToMap; - var refs = parentToMap[subpath]; + var parentToPathMap = model._refs.parentToPathMap; + var refs = parentToPathMap.get(subpath.split('.'), true); if (!refs) return; for (var refIndex = 0, numRefs = refs.length; refIndex < numRefs; refIndex++) { var ref = refs[refIndex]; @@ -287,10 +287,9 @@ function Ref(model, from, to, options) { } this.updateIndices = options && options.updateIndices; } -function ToMap() {} function Refs() { - this.parentToMap = new ToMap(); + this.parentToPathMap = new PathListMap(); this.toPathMap = new PathListMap(); this.fromPathMap = new PathMap(); } @@ -299,7 +298,7 @@ Refs.prototype.add = function(ref) { this.fromPathMap.add(ref.fromSegments, ref); this.toPathMap.add(ref.toSegments, ref); for (var i = 0, len = ref.parentTos.length; i < len; i++) { - listMapAdd(this.parentToMap, ref.parentTos[i], ref); + this.parentToPathMap.add(ref.parentTos[i].split('.'), ref); } }; @@ -309,7 +308,7 @@ Refs.prototype.remove = function(from) { this.fromPathMap.delete(ref.fromSegments); this.toPathMap.delete(ref.toSegments, ref); for (var i = 0, len = ref.parentTos.length; i < len; i++) { - listMapRemove(this.parentToMap, ref.parentTos[i], ref); + this.parentToPathMap.delete(ref.parentTos[i].split('.'), ref); } return ref; }; @@ -325,20 +324,6 @@ Refs.prototype.toJSON = function() { return out; }; -function listMapAdd(map, name, item) { - map[name] || (map[name] = []); - map[name].push(item); -} - -function listMapRemove(map, name, item) { - var items = map[name]; - if (!items) return; - var index = items.indexOf(item); - if (index === -1) return; - items.splice(index, 1); - if (!items.length) delete map[name]; -} - function PathMap() { this.map = {}; } From 3d92d5296984a4a66a4e2b4f26d7364cde875563 Mon Sep 17 00:00:00 2001 From: cjblomqvist Date: Thu, 3 Dec 2015 22:30:56 +0100 Subject: [PATCH 13/17] Remove accidental added spacing --- lib/Model/ref.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Model/ref.js b/lib/Model/ref.js index ecbae00b3..10a3aa35f 100644 --- a/lib/Model/ref.js +++ b/lib/Model/ref.js @@ -119,7 +119,7 @@ function addListener(model, type, fn) { subpath = (subpath) ? subpath + '.' + segments[i] : segments[i]; // If a ref is found pointing to a matching subpath, re-emit on the // place where the reference is coming from as if the mutation also - // occured at that path + // occured at that path var refs = toPathMap.get(subpath.split('.'), true); if (!refs) continue; var remaining = segments.slice(i + 1); From 5ec670726b1916c66a31226b9e9511a6ec559a80 Mon Sep 17 00:00:00 2001 From: cjblomqvist Date: Fri, 4 Dec 2015 15:25:08 +0100 Subject: [PATCH 14/17] Various minor fixes, including always returning arrays for PathListMaps --- lib/Model/ref.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/Model/ref.js b/lib/Model/ref.js index 10a3aa35f..626454b4a 100644 --- a/lib/Model/ref.js +++ b/lib/Model/ref.js @@ -121,7 +121,7 @@ function addListener(model, type, fn) { // place where the reference is coming from as if the mutation also // occured at that path var refs = toPathMap.get(subpath.split('.'), true); - if (!refs) continue; + if (!refs.length) continue; var remaining = segments.slice(i + 1); for (var refIndex = 0, numRefs = refs.length; refIndex < numRefs; refIndex++) { var ref = refs[refIndex]; @@ -142,7 +142,7 @@ function addListener(model, type, fn) { // case it has changed and set if different var parentToPathMap = model._refs.parentToPathMap; var refs = parentToPathMap.get(subpath.split('.'), true); - if (!refs) return; + if (!refs.length) return; for (var refIndex = 0, numRefs = refs.length; refIndex < numRefs; refIndex++) { var ref = refs[refIndex]; var value = model._get(ref.toSegments); @@ -427,16 +427,16 @@ PathListMap.prototype.get = function (segments, onlyAtLevel) { for(var i = 0, len = segments.length; i < len; i++) { val = val[segments[i]]; - if(!val) return; + if(!val) return []; } - if(onlyAtLevel) return val; + if(onlyAtLevel) return (val['$items'] || []); return flatten(val); }; function flatten(obj) { - var arr = obj['$items']; + var arr = obj['$items'] || []; var keys = Object.keys(obj); for(var i = 0, len = keys.length; i < len; i++) { From 88844985612c7709359ae2ac6fbb28df9ff6ab55 Mon Sep 17 00:00:00 2001 From: cjblomqvist Date: Fri, 5 Feb 2016 11:34:36 +0100 Subject: [PATCH 15/17] Fix issues with cleanup and bundling of refs --- lib/Model/ref.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/Model/ref.js b/lib/Model/ref.js index 626454b4a..f29e9c426 100644 --- a/lib/Model/ref.js +++ b/lib/Model/ref.js @@ -340,6 +340,12 @@ PathMap.prototype.add = function (segments, item) { }; PathMap.prototype.get = function (segments) { + var val = this._get(segments); + + return (val && val['$item']) ? val['$item'] : void 0; +}; + +PathMap.prototype._get = function (segments) { var val = this.map; for(var i = 0, len = segments.length; i < len; i++) { @@ -347,7 +353,7 @@ PathMap.prototype.get = function (segments) { if(!val) return; } - return (val && val['$item']) ? val['$item'] : void 0; + return val; }; PathMap.prototype.getList = function (segments) { @@ -366,7 +372,7 @@ function flattenObj(obj) { for(var i = 0, len = keys.length; i < len; i++) { if(keys[i] === '$item') continue; - arr.concat(flattenObj(obj[keys[i]])); + arr = arr.concat(flattenObj(obj[keys[i]], log)); } return arr; From 416ab166b66673be166f8528afe493e17390fccd Mon Sep 17 00:00:00 2001 From: cjblomqvist Date: Fri, 5 Feb 2016 11:56:22 +0100 Subject: [PATCH 16/17] Additional fix to refs cleanup/bundle issues --- lib/Model/ref.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Model/ref.js b/lib/Model/ref.js index f29e9c426..192ef267e 100644 --- a/lib/Model/ref.js +++ b/lib/Model/ref.js @@ -357,7 +357,7 @@ PathMap.prototype._get = function (segments) { }; PathMap.prototype.getList = function (segments) { - var obj = this.get(segments); + var obj = this._get(segments); return flattenObj(obj); }; From bff89cf8f19bc37cd966db1b12fa0f022bc89144 Mon Sep 17 00:00:00 2001 From: cjblomqvist Date: Fri, 5 Feb 2016 12:10:27 +0100 Subject: [PATCH 17/17] Remove debugging param no longer applicable --- lib/Model/ref.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Model/ref.js b/lib/Model/ref.js index 192ef267e..8502d1558 100644 --- a/lib/Model/ref.js +++ b/lib/Model/ref.js @@ -372,7 +372,7 @@ function flattenObj(obj) { for(var i = 0, len = keys.length; i < len; i++) { if(keys[i] === '$item') continue; - arr = arr.concat(flattenObj(obj[keys[i]], log)); + arr = arr.concat(flattenObj(obj[keys[i]])); } return arr;