|
|
- import Map from '@ungap/essential-map';
-
- const {indexOf: iOF} = [];
- export const append = (get, parent, children, start, end, before) => {
- const isSelect = 'selectedIndex' in parent;
- let noSelection = isSelect;
- while (start < end) {
- const child = get(children[start], 1);
- parent.insertBefore(child, before);
- if (isSelect && noSelection && child.selected) {
- noSelection = !noSelection;
- let {selectedIndex} = parent;
- parent.selectedIndex = selectedIndex < 0 ?
- start :
- iOF.call(parent.querySelectorAll('option'), child);
- }
- start++;
- }
- };
-
- export const eqeq = (a, b) => a == b;
-
- export const identity = O => O;
-
- export const indexOf = (
- moreNodes,
- moreStart,
- moreEnd,
- lessNodes,
- lessStart,
- lessEnd,
- compare
- ) => {
- const length = lessEnd - lessStart;
- /* istanbul ignore if */
- if (length < 1)
- return -1;
- while ((moreEnd - moreStart) >= length) {
- let m = moreStart;
- let l = lessStart;
- while (
- m < moreEnd &&
- l < lessEnd &&
- compare(moreNodes[m], lessNodes[l])
- ) {
- m++;
- l++;
- }
- if (l === lessEnd)
- return moreStart;
- moreStart = m + 1;
- }
- return -1;
- };
-
- export const isReversed = (
- futureNodes,
- futureEnd,
- currentNodes,
- currentStart,
- currentEnd,
- compare
- ) => {
- while (
- currentStart < currentEnd &&
- compare(
- currentNodes[currentStart],
- futureNodes[futureEnd - 1]
- )) {
- currentStart++;
- futureEnd--;
- };
- return futureEnd === 0;
- };
-
- export const next = (get, list, i, length, before) => i < length ?
- get(list[i], 0) :
- (0 < i ?
- get(list[i - 1], -0).nextSibling :
- before);
-
- export const remove = (get, parent, children, start, end) => {
- if ((end - start) < 2)
- parent.removeChild(get(children[start], -1));
- else {
- const range = parent.ownerDocument.createRange();
- range.setStartBefore(get(children[start], -1));
- range.setEndAfter(get(children[end - 1], -1));
- range.deleteContents();
- }
- };
-
- // - - - - - - - - - - - - - - - - - - -
- // diff related constants and utilities
- // - - - - - - - - - - - - - - - - - - -
-
- const DELETION = -1;
- const INSERTION = 1;
- const SKIP = 0;
- const SKIP_OND = 50;
-
- const HS = (
- futureNodes,
- futureStart,
- futureEnd,
- futureChanges,
- currentNodes,
- currentStart,
- currentEnd,
- currentChanges
- ) => {
-
- let k = 0;
- /* istanbul ignore next */
- let minLen = futureChanges < currentChanges ? futureChanges : currentChanges;
- const link = Array(minLen++);
- const tresh = Array(minLen);
- tresh[0] = -1;
-
- for (let i = 1; i < minLen; i++)
- tresh[i] = currentEnd;
-
- const keymap = new Map;
- for (let i = currentStart; i < currentEnd; i++)
- keymap.set(currentNodes[i], i);
-
- for (let i = futureStart; i < futureEnd; i++) {
- const idxInOld = keymap.get(futureNodes[i]);
- if (idxInOld != null) {
- 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;
- const diff = Array(minLen);
- let ptr = link[k];
- --futureEnd;
- while (ptr) {
- const {newi, oldi} = ptr;
- 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
- const OND = (
- futureNodes,
- futureStart,
- rows,
- currentNodes,
- currentStart,
- cols,
- compare
- ) => {
- const length = rows + cols;
- const v = [];
- let 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;
- }
- }
-
- const diff = Array(d / 2 + length / 2);
- let 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;
- };
-
- const applyDiff = (
- diff,
- get,
- parentNode,
- futureNodes,
- futureStart,
- currentNodes,
- currentStart,
- currentLength,
- before
- ) => {
- const live = new Map;
- const length = diff.length;
- let currentIndex = currentStart;
- let 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;
- }
- }
- };
-
- const findK = (ktr, length, j) => {
- let lo = 1;
- let hi = length;
- while (lo < hi) {
- const mid = ((lo + hi) / 2) >>> 0;
- if (j < ktr[mid])
- hi = mid;
- else
- lo = mid + 1;
- }
- return lo;
- }
-
- export const 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
- );
- };
|