You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

409 lines
12 KiB

var domdiff = (function () {
'use strict';
/*! (c) Andrea Giammarchi - ISC */
var self = null || /* istanbul ignore next */{};
try {
self.Map = Map;
} catch (Map) {
self.Map = function Map() {
var i = 0;
var k = [];
var v = [];
return {
delete: function _delete(key) {
var had = contains(key);
if (had) {
k.splice(i, 1);
v.splice(i, 1);
}
return had;
},
forEach: function forEach(callback, context) {
k.forEach(function (key, i) {
callback.call(context, v[i], key, this);
}, this);
},
get: function get(key) {
return contains(key) ? v[i] : void 0;
},
has: function has(key) {
return contains(key);
},
set: function set(key, value) {
v[contains(key) ? i : k.push(key) - 1] = value;
return this;
}
};
function contains(v) {
i = k.indexOf(v);
return -1 < i;
}
};
}
var Map$1 = self.Map;
var iOF = [].indexOf;
var append = function append(get, parent, children, start, end, before) {
var isSelect = 'selectedIndex' in parent;
var noSelection = isSelect;
while (start < end) {
var child = get(children[start], 1);
parent.insertBefore(child, before);
if (isSelect && noSelection && child.selected) {
noSelection = !noSelection;
var selectedIndex = parent.selectedIndex;
parent.selectedIndex = selectedIndex < 0 ? start : iOF.call(parent.querySelectorAll('option'), child);
}
start++;
}
};
var eqeq = function eqeq(a, b) {
return a == b;
};
var identity = function identity(O) {
return O;
};
var indexOf = function indexOf(moreNodes, moreStart, moreEnd, lessNodes, lessStart, lessEnd, compare) {
var length = lessEnd - lessStart;
/* istanbul ignore if */
if (length < 1) return -1;
while (moreEnd - moreStart >= length) {
var m = moreStart;
var l = lessStart;
while (m < moreEnd && l < lessEnd && compare(moreNodes[m], lessNodes[l])) {
m++;
l++;
}
if (l === lessEnd) return moreStart;
moreStart = m + 1;
}
return -1;
};
var isReversed = function isReversed(futureNodes, futureEnd, currentNodes, currentStart, currentEnd, compare) {
while (currentStart < currentEnd && compare(currentNodes[currentStart], futureNodes[futureEnd - 1])) {
currentStart++;
futureEnd--;
}
return futureEnd === 0;
};
var next = function next(get, list, i, length, before) {
return i < length ? get(list[i], 0) : 0 < i ? get(list[i - 1], -0).nextSibling : before;
};
var remove = function remove(get, parent, children, start, end) {
if (end - start < 2) parent.removeChild(get(children[start], -1));else {
var range = parent.ownerDocument.createRange();
range.setStartBefore(get(children[start], -1));
range.setEndAfter(get(children[end - 1], -1));
range.deleteContents();
}
};
// - - - - - - - - - - - - - - - - - - -
// diff related constants and utilities
// - - - - - - - - - - - - - - - - - - -
var DELETION = -1;
var INSERTION = 1;
var SKIP = 0;
var SKIP_OND = 50;
var HS = function HS(futureNodes, futureStart, futureEnd, futureChanges, currentNodes, currentStart, currentEnd, currentChanges) {
var k = 0;
/* istanbul ignore next */
var minLen = futureChanges < currentChanges ? futureChanges : currentChanges;
var link = Array(minLen++);
var tresh = Array(minLen);
tresh[0] = -1;
for (var i = 1; i < minLen; i++) {
tresh[i] = currentEnd;
}var keymap = new Map$1();
for (var _i = currentStart; _i < currentEnd; _i++) {
keymap.set(currentNodes[_i], _i);
}for (var _i2 = futureStart; _i2 < futureEnd; _i2++) {
var idxInOld = keymap.get(futureNodes[_i2]);
if (idxInOld != null) {
k = findK(tresh, minLen, idxInOld);
/* istanbul ignore else */
if (-1 < k) {
tresh[k] = idxInOld;
link[k] = {
newi: _i2,
oldi: idxInOld,
prev: link[k - 1]
};
}
}
}
k = --minLen;
--currentEnd;
while (tresh[k] > currentEnd) {
--k;
}minLen = currentChanges + futureChanges - k;
var diff = Array(minLen);
var ptr = link[k];
--futureEnd;
while (ptr) {
var _ptr = ptr,
newi = _ptr.newi,
oldi = _ptr.oldi;
while (futureEnd > newi) {
diff[--minLen] = INSERTION;
--futureEnd;
}
while (currentEnd > oldi) {
diff[--minLen] = DELETION;
--currentEnd;
}
diff[--minLen] = SKIP;
--futureEnd;
--currentEnd;
ptr = ptr.prev;
}
while (futureEnd >= futureStart) {
diff[--minLen] = INSERTION;
--futureEnd;
}
while (currentEnd >= currentStart) {
diff[--minLen] = DELETION;
--currentEnd;
}
return diff;
};
// this is pretty much the same petit-dom code without the delete map part
// https://github.com/yelouafi/petit-dom/blob/bd6f5c919b5ae5297be01612c524c40be45f14a7/src/vdom.js#L556-L561
var OND = function OND(futureNodes, futureStart, rows, currentNodes, currentStart, cols, compare) {
var length = rows + cols;
var v = [];
var d = void 0,
k = void 0,
r = void 0,
c = void 0,
pv = void 0,
cv = void 0,
pd = void 0;
outer: for (d = 0; d <= length; d++) {
/* istanbul ignore if */
if (d > SKIP_OND) return null;
pd = d - 1;
/* istanbul ignore next */
pv = d ? v[d - 1] : [0, 0];
cv = v[d] = [];
for (k = -d; k <= d; k += 2) {
if (k === -d || k !== d && pv[pd + k - 1] < pv[pd + k + 1]) {
c = pv[pd + k + 1];
} else {
c = pv[pd + k - 1] + 1;
}
r = c - k;
while (c < cols && r < rows && compare(currentNodes[currentStart + c], futureNodes[futureStart + r])) {
c++;
r++;
}
if (c === cols && r === rows) {
break outer;
}
cv[d + k] = c;
}
}
var diff = Array(d / 2 + length / 2);
var diffIdx = diff.length - 1;
for (d = v.length - 1; d >= 0; d--) {
while (c > 0 && r > 0 && compare(currentNodes[currentStart + c - 1], futureNodes[futureStart + r - 1])) {
// diagonal edge = equality
diff[diffIdx--] = SKIP;
c--;
r--;
}
if (!d) break;
pd = d - 1;
/* istanbul ignore next */
pv = d ? v[d - 1] : [0, 0];
k = c - r;
if (k === -d || k !== d && pv[pd + k - 1] < pv[pd + k + 1]) {
// vertical edge = insertion
r--;
diff[diffIdx--] = INSERTION;
} else {
// horizontal edge = deletion
c--;
diff[diffIdx--] = DELETION;
}
}
return diff;
};
var applyDiff = function applyDiff(diff, get, parentNode, futureNodes, futureStart, currentNodes, currentStart, currentLength, before) {
var live = new Map$1();
var length = diff.length;
var currentIndex = currentStart;
var i = 0;
while (i < length) {
switch (diff[i++]) {
case SKIP:
futureStart++;
currentIndex++;
break;
case INSERTION:
// TODO: bulk appends for sequential nodes
live.set(futureNodes[futureStart], 1);
append(get, parentNode, futureNodes, futureStart++, futureStart, currentIndex < currentLength ? get(currentNodes[currentIndex], 0) : before);
break;
case DELETION:
currentIndex++;
break;
}
}
i = 0;
while (i < length) {
switch (diff[i++]) {
case SKIP:
currentStart++;
break;
case DELETION:
// TODO: bulk removes for sequential nodes
if (live.has(currentNodes[currentStart])) currentStart++;else remove(get, parentNode, currentNodes, currentStart++, currentStart);
break;
}
}
};
var findK = function findK(ktr, length, j) {
var lo = 1;
var hi = length;
while (lo < hi) {
var mid = (lo + hi) / 2 >>> 0;
if (j < ktr[mid]) hi = mid;else lo = mid + 1;
}
return lo;
};
var smartDiff = function smartDiff(get, parentNode, futureNodes, futureStart, futureEnd, futureChanges, currentNodes, currentStart, currentEnd, currentChanges, currentLength, compare, before) {
applyDiff(OND(futureNodes, futureStart, futureChanges, currentNodes, currentStart, currentChanges, compare) || HS(futureNodes, futureStart, futureEnd, futureChanges, currentNodes, currentStart, currentEnd, currentChanges), get, parentNode, futureNodes, futureStart, currentNodes, currentStart, currentLength, before);
};
/*! (c) 2018 Andrea Giammarchi (ISC) */
var domdiff = function domdiff(parentNode, // where changes happen
currentNodes, // Array of current items/nodes
futureNodes, // Array of future items/nodes
options // optional object with one of the following properties
// before: domNode
// compare(generic, generic) => true if same generic
// node(generic) => Node
) {
if (!options) options = {};
var compare = options.compare || eqeq;
var get = options.node || identity;
var before = options.before == null ? null : get(options.before, 0);
var currentLength = currentNodes.length;
var currentEnd = currentLength;
var currentStart = 0;
var futureEnd = futureNodes.length;
var futureStart = 0;
// common prefix
while (currentStart < currentEnd && futureStart < futureEnd && compare(currentNodes[currentStart], futureNodes[futureStart])) {
currentStart++;
futureStart++;
}
// common suffix
while (currentStart < currentEnd && futureStart < futureEnd && compare(currentNodes[currentEnd - 1], futureNodes[futureEnd - 1])) {
currentEnd--;
futureEnd--;
}
var currentSame = currentStart === currentEnd;
var futureSame = futureStart === futureEnd;
// same list
if (currentSame && futureSame) return futureNodes;
// only stuff to add
if (currentSame && futureStart < futureEnd) {
append(get, parentNode, futureNodes, futureStart, futureEnd, next(get, currentNodes, currentStart, currentLength, before));
return futureNodes;
}
// only stuff to remove
if (futureSame && currentStart < currentEnd) {
remove(get, parentNode, currentNodes, currentStart, currentEnd);
return futureNodes;
}
var currentChanges = currentEnd - currentStart;
var futureChanges = futureEnd - futureStart;
var i = -1;
// 2 simple indels: the shortest sequence is a subsequence of the longest
if (currentChanges < futureChanges) {
i = indexOf(futureNodes, futureStart, futureEnd, currentNodes, currentStart, currentEnd, compare);
// inner diff
if (-1 < i) {
append(get, parentNode, futureNodes, futureStart, i, get(currentNodes[currentStart], 0));
append(get, parentNode, futureNodes, i + currentChanges, futureEnd, next(get, currentNodes, currentEnd, currentLength, before));
return futureNodes;
}
}
/* istanbul ignore else */
else if (futureChanges < currentChanges) {
i = indexOf(currentNodes, currentStart, currentEnd, futureNodes, futureStart, futureEnd, compare);
// outer diff
if (-1 < i) {
remove(get, parentNode, currentNodes, currentStart, i);
remove(get, parentNode, currentNodes, i + futureChanges, currentEnd);
return futureNodes;
}
}
// common case with one replacement for many nodes
// or many nodes replaced for a single one
/* istanbul ignore else */
if (currentChanges < 2 || futureChanges < 2) {
append(get, parentNode, futureNodes, futureStart, futureEnd, get(currentNodes[currentStart], 0));
remove(get, parentNode, currentNodes, currentStart, currentEnd);
return futureNodes;
}
// the half match diff part has been skipped in petit-dom
// https://github.com/yelouafi/petit-dom/blob/bd6f5c919b5ae5297be01612c524c40be45f14a7/src/vdom.js#L391-L397
// accordingly, I think it's safe to skip in here too
// if one day it'll come out like the speediest thing ever to do
// then I might add it in here too
// Extra: before going too fancy, what about reversed lists ?
// This should bail out pretty quickly if that's not the case.
if (currentChanges === futureChanges && isReversed(futureNodes, futureEnd, currentNodes, currentStart, currentEnd, compare)) {
append(get, parentNode, futureNodes, futureStart, futureEnd, next(get, currentNodes, currentEnd, currentLength, before));
return futureNodes;
}
// last resort through a smart diff
smartDiff(get, parentNode, futureNodes, futureStart, futureEnd, futureChanges, currentNodes, currentStart, currentEnd, currentChanges, currentLength, compare, before);
return futureNodes;
};
return domdiff;
}());