Skip to content

Commit 9e6c5fb

Browse files
authored
Merge pull request #68 from fiduswriter/cache-subtree-maps
Cache subtree maps
2 parents 5c2a937 + 686e257 commit 9e6c5fb

File tree

2 files changed

+139
-31
lines changed

2 files changed

+139
-31
lines changed

diffDOM.js

Lines changed: 136 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -40,22 +40,7 @@
4040
}
4141
};
4242

43-
var SubsetMapping = function SubsetMapping(a, b) {
44-
this.oldValue = a;
45-
this.newValue = b;
46-
};
4743

48-
SubsetMapping.prototype = {
49-
contains: function contains(subset) {
50-
if (subset.length < this.length) {
51-
return subset.newValue >= this.newValue && subset.newValue < this.newValue + this.length;
52-
}
53-
return false;
54-
},
55-
toString: function toString() {
56-
return this.length + " element subset, first mapping: old " + this.oldValue + " → new " + this.newValue;
57-
}
58-
};
5944

6045
var elementDescriptors = function(el) {
6146
var output = [];
@@ -318,11 +303,12 @@
318303
if (lcsSize === 0) {
319304
return false;
320305
}
321-
origin = [index[0] - lcsSize, index[1] - lcsSize];
322-
ret = new SubsetMapping(origin[0], origin[1]);
323-
ret.length = lcsSize;
324306

325-
return ret;
307+
return {
308+
oldValue: index[0] - lcsSize,
309+
newValue: index[1] - lcsSize,
310+
length: lcsSize
311+
};
326312
};
327313

328314
/**
@@ -413,6 +399,9 @@
413399
}
414400
}
415401
}
402+
403+
oldTree.subsets = subsets;
404+
oldTree.subsetsAge = 100;
416405
return subsets;
417406
};
418407

@@ -448,8 +437,8 @@
448437
debug: false,
449438
diffcap: 10, // Limit for how many diffs are accepting when debugging. Inactive when debug is false.
450439
maxDepth: false, // False or a numeral. If set to a numeral, limits the level of depth that the the diff mechanism looks for differences. If false, goes through the entire tree.
451-
maxChildCount: false, // False or a numeral. If set to a numeral, does not try to diff the contents of nodes with more children if there are more than maxChildCountDiffCount differences among child nodes.
452-
maxChildCountDiffCount: 3, // Numeral. See maxChildCount.
440+
maxChildCount: 50, // False or a numeral. If set to a numeral, does not try to diff the contents of nodes with more children if there are more than maxChildDiffCount differences among child nodes.
441+
maxChildDiffCount: 3, // Numeral. See maxChildCount.
453442
valueDiffing: true, // Whether to take into consideration the values of forms that differ from auto assigned values (when a user fills out a form).
454443
// syntax: textDiff: function (node, currentValue, expectedValue, newValue)
455444
textDiff: function() {
@@ -640,15 +629,15 @@
640629
];
641630
}
642631

643-
if (this.maxChildCount && t1.childNodes && t2.childNodes && t1.childNodes.length > this.maxChildCount && t2.childNodes.length > this.maxChildCount) {
632+
if (route.length && this.maxChildCount && t1.childNodes && t2.childNodes && t1.childNodes.length > this.maxChildCount && t2.childNodes.length > this.maxChildCount) {
644633
var childNodesLength = t1.childNodes.length < t2.childNodes.length ? t1.childNodes.length : t2.childNodes.length, childDiffCount = 0, j = 0;
645-
while (childDiffCount < this.maxChildCountDiffCount && j < childNodesLength) {
634+
while (childDiffCount < this.maxChildDiffCount && j < childNodesLength) {
646635
if (!isEqual(t1.childNodes[j], t2.childNodes[j])) {
647636
childDiffCount++;
648637
}
649638
j++;
650639
}
651-
if (childDiffCount === this.maxChildCountDiffCount) {
640+
if (childDiffCount === this.maxChildDiffCount) {
652641
return [new Diff()
653642
.setValue(t._const.action, t._const.replaceElement)
654643
.setValue(t._const.oldValue, cloneObj(t1))
@@ -809,12 +798,13 @@
809798
return node;
810799
},
811800
findInnerDiff: function(t1, t2, route) {
812-
var t = this;
813-
var subtrees = (t1.childNodes && t2.childNodes) ? markSubTrees(t1, t2) : [],
801+
var oldSubsets = t1.subsets;
802+
//var subtrees = (t1.childNodes && t2.childNodes) ? markSubTrees(t1, t2) : [],
803+
var subtrees = t1.subsets && t1.subsetsAge-- ? t1.subsets : (t1.childNodes && t2.childNodes) ? markSubTrees(t1, t2) : [],
814804
t1ChildNodes = t1.childNodes ? t1.childNodes : [],
815805
t2ChildNodes = t2.childNodes ? t2.childNodes : [],
816806
childNodesLengthDifference, diffs = [],
817-
index = 0,
807+
index = 0, t = this,
818808
last, e1, e2, i;
819809

820810
if (subtrees.length > 0) {
@@ -1087,6 +1077,7 @@
10871077
node = routeInfo.node,
10881078
parentNode = routeInfo.parentNode,
10891079
nodeIndex = routeInfo.nodeIndex,
1080+
newSubsets = [], splitLength,
10901081
newNode, movedNode, nodeArray, route, length, c, i;
10911082

10921083
var t = this;
@@ -1169,9 +1160,71 @@
11691160
movedNode = nodeArray[i];
11701161
node.childNodes.splice(diff[t._const.to], 0, movedNode);
11711162
}
1163+
if (node.subsets) {
1164+
1165+
node.subsets.forEach(function(map) {
1166+
if (diff[t._const.from] < diff[t._const.to] && map.oldValue <= diff[t._const.to] && map.oldValue > diff[t._const.from]) {
1167+
map.oldValue -= diff.groupLength;
1168+
splitLength = map.oldValue + map.length - diff[t._const.to];
1169+
if (splitLength > 0) {
1170+
// new insertion splits map.
1171+
newSubsets.push({
1172+
oldValue: diff[t._const.to] + diff.groupLength,
1173+
newValue: map.newValue + map.length - splitLength,
1174+
length: splitLength
1175+
})
1176+
map.length -= splitLength;
1177+
}
1178+
} else if (diff[t._const.from] > diff[t._const.to] && map.oldValue > diff[t._const.to] && map.oldValue < diff[t._const.from]) {
1179+
map.oldValue += diff.groupLength;
1180+
splitLength = map.oldValue + map.length - diff[t._const.to];
1181+
if (splitLength > 0) {
1182+
// new insertion splits map.
1183+
newSubsets.push({
1184+
oldValue: diff[t._const.to] + diff.groupLength,
1185+
newValue: map.newValue + map.length - splitLength,
1186+
length: splitLength
1187+
})
1188+
map.length -= splitLength;
1189+
}
1190+
} else if (map.oldValue === diff[t._const.from]) {
1191+
map.oldValue = diff[t._const.to];
1192+
}
1193+
});
1194+
if (node.subsets && newSubsets.length) {
1195+
node.subsets = node.subsets.concat(newSubsets);
1196+
}
1197+
}
1198+
11721199
break;
11731200
case this._const.removeElement:
11741201
parentNode.childNodes.splice(nodeIndex, 1);
1202+
if (parentNode.subsets) {
1203+
parentNode.subsets.forEach(function(map) {
1204+
if (map.oldValue > nodeIndex) {
1205+
map.oldValue -= 1;
1206+
} else if (map.oldValue === nodeIndex) {
1207+
map.delete = true;
1208+
} else if (map.oldValue < nodeIndex && (map.oldValue + map.length) > nodeIndex) {
1209+
if (map.oldValue + map.length - 1 === nodeIndex) {
1210+
map.length--;
1211+
} else {
1212+
newSubsets.push({
1213+
newValue: map.newValue + nodeIndex - map.oldValue,
1214+
oldValue: nodeIndex,
1215+
length: map.length - nodeIndex + map.oldValue - 1
1216+
})
1217+
map.length = nodeIndex - map.oldValue
1218+
}
1219+
}
1220+
});
1221+
parentNode.subsets = parentNode.subsets.filter(function(map) {
1222+
return !map.delete;
1223+
});
1224+
if (newSubsets.length) {
1225+
parentNode.subsets = parentNode.subsets.concat(newSubsets);
1226+
}
1227+
}
11751228
break;
11761229
case this._const.addElement:
11771230
route = diff[this._const.route].slice();
@@ -1191,12 +1244,53 @@
11911244
} else {
11921245
node.childNodes.splice(c, 0, newNode);
11931246
}
1247+
if (node.subsets) {
1248+
node.subsets.forEach(function(map) {
1249+
if (map.oldValue >= c) {
1250+
map.oldValue += 1;
1251+
} if (map.oldValue < c && (map.oldValue + map.length) > c) {
1252+
splitLength = map.oldValue + map.length - c
1253+
newSubsets.push({
1254+
newValue: map.newValue + map.length - splitLength,
1255+
oldValue: c + 1,
1256+
length: splitLength
1257+
})
1258+
map.length -= splitLength
1259+
}
1260+
});
1261+
}
11941262
break;
11951263
case this._const.removeTextElement:
11961264
parentNode.childNodes.splice(nodeIndex, 1);
11971265
if (parentNode.nodeName === 'TEXTAREA') {
11981266
delete parentNode.value;
11991267
}
1268+
if (parentNode.subsets) {
1269+
parentNode.subsets.forEach(function(map) {
1270+
if (map.oldValue > nodeIndex) {
1271+
map.oldValue -= 1;
1272+
} else if (map.oldValue === nodeIndex) {
1273+
map.delete = true;
1274+
} else if (map.oldValue < nodeIndex && (map.oldValue + map.length) > nodeIndex) {
1275+
if (map.oldValue + map.length - 1 === nodeIndex) {
1276+
map.length--;
1277+
} else {
1278+
newSubsets.push({
1279+
newValue: map.newValue + nodeIndex - map.oldValue,
1280+
oldValue: nodeIndex,
1281+
length: map.length - nodeIndex + map.oldValue - 1
1282+
})
1283+
map.length = nodeIndex - map.oldValue
1284+
}
1285+
}
1286+
});
1287+
parentNode.subsets = parentNode.subsets.filter(function(map) {
1288+
return !map.delete;
1289+
});
1290+
if (newSubsets.length) {
1291+
parentNode.subsets = parentNode.subsets.concat(newSubsets);
1292+
}
1293+
}
12001294
break;
12011295
case this._const.addTextElement:
12021296
route = diff[this._const.route].slice();
@@ -1217,6 +1311,21 @@
12171311
if (node.nodeName === 'TEXTAREA') {
12181312
node.value = diff[this._const.newValue];
12191313
}
1314+
if (node.subsets) {
1315+
node.subsets.forEach(function(map) {
1316+
if (map.oldValue >= c) {
1317+
map.oldValue += 1;
1318+
} if (map.oldValue < c && (map.oldValue + map.length) > c) {
1319+
splitLength = map.oldValue + map.length - c
1320+
newSubsets.push({
1321+
newValue: map.newValue + map.length - splitLength,
1322+
oldValue: c + 1,
1323+
length: splitLength
1324+
})
1325+
map.length -= splitLength
1326+
}
1327+
});
1328+
}
12201329
break;
12211330
default:
12221331
console.log('unknown action');

tests/long-list.html

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,17 @@
3333
<script>
3434
var dd = new diffDOM({
3535
debug: true,
36-
diffcap: 100,
37-
valueDiffing: false,
38-
maxChildCount: 50
36+
diffcap: 1000,
37+
valueDiffing: false
3938
}),
4039
tl = new TraceLogger(dd), elements, first, second, third, fourth, theDiff;
4140

4241
let divs = [].slice.call(document.querySelectorAll('div'))
4342

4443

4544
let diffs = dd.diff(divs[0], divs[1])
46-
console.log(diffs)
4745
dd.apply(divs[0], diffs)
46+
console.log(diffs)
4847
</script>
4948

5049
</body>

0 commit comments

Comments
 (0)