|
|
- var domdiff = (function () {
- 'use strict';
-
- 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, children, start, end) {
- while (start < end) {
- drop(get(children[start++], -1));
- }
- }; // - - - - - - - - - - - - - - - - - - -
- // 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 nodes = currentNodes.slice(currentStart, currentEnd);
-
- for (var _i = futureStart; _i < futureEnd; _i++) {
- var index = nodes.indexOf(futureNodes[_i]);
-
- if (-1 < index) {
- var idxInOld = index + currentStart;
- k = findK(tresh, minLen, idxInOld);
- /* istanbul ignore else */
-
- if (-1 < k) {
- tresh[k] = idxInOld;
- link[k] = {
- newi: _i,
- 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, k, r, c, pv, cv, pd;
-
- 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 = [];
- 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.push(futureNodes[futureStart]);
- 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 (-1 < live.indexOf(currentNodes[currentStart])) currentStart++;else remove(get, 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);
- };
-
- var drop = function drop(node) {
- return (node.remove || dropChild).call(node);
- };
-
- function dropChild() {
- var parentNode = this.parentNode;
- /* istanbul ignore else */
-
- if (parentNode) parentNode.removeChild(this);
- }
-
- /*! (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, 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, currentNodes, currentStart, i);
- remove(get, 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, 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;
-
- }());
|