|
|
- 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;
-
- }());
|