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.

392 lines
12 KiB

4 years ago
  1. var domdiff = (function () {
  2. 'use strict';
  3. var iOF = [].indexOf;
  4. var append = function append(get, parent, children, start, end, before) {
  5. var isSelect = 'selectedIndex' in parent;
  6. var noSelection = isSelect;
  7. while (start < end) {
  8. var child = get(children[start], 1);
  9. parent.insertBefore(child, before);
  10. if (isSelect && noSelection && child.selected) {
  11. noSelection = !noSelection;
  12. var selectedIndex = parent.selectedIndex;
  13. parent.selectedIndex = selectedIndex < 0 ? start : iOF.call(parent.querySelectorAll('option'), child);
  14. }
  15. start++;
  16. }
  17. };
  18. var eqeq = function eqeq(a, b) {
  19. return a == b;
  20. };
  21. var identity = function identity(O) {
  22. return O;
  23. };
  24. var indexOf = function indexOf(moreNodes, moreStart, moreEnd, lessNodes, lessStart, lessEnd, compare) {
  25. var length = lessEnd - lessStart;
  26. /* istanbul ignore if */
  27. if (length < 1) return -1;
  28. while (moreEnd - moreStart >= length) {
  29. var m = moreStart;
  30. var l = lessStart;
  31. while (m < moreEnd && l < lessEnd && compare(moreNodes[m], lessNodes[l])) {
  32. m++;
  33. l++;
  34. }
  35. if (l === lessEnd) return moreStart;
  36. moreStart = m + 1;
  37. }
  38. return -1;
  39. };
  40. var isReversed = function isReversed(futureNodes, futureEnd, currentNodes, currentStart, currentEnd, compare) {
  41. while (currentStart < currentEnd && compare(currentNodes[currentStart], futureNodes[futureEnd - 1])) {
  42. currentStart++;
  43. futureEnd--;
  44. }
  45. return futureEnd === 0;
  46. };
  47. var next = function next(get, list, i, length, before) {
  48. return i < length ? get(list[i], 0) : 0 < i ? get(list[i - 1], -0).nextSibling : before;
  49. };
  50. var remove = function remove(get, children, start, end) {
  51. while (start < end) {
  52. drop(get(children[start++], -1));
  53. }
  54. }; // - - - - - - - - - - - - - - - - - - -
  55. // diff related constants and utilities
  56. // - - - - - - - - - - - - - - - - - - -
  57. var DELETION = -1;
  58. var INSERTION = 1;
  59. var SKIP = 0;
  60. var SKIP_OND = 50;
  61. var HS = function HS(futureNodes, futureStart, futureEnd, futureChanges, currentNodes, currentStart, currentEnd, currentChanges) {
  62. var k = 0;
  63. /* istanbul ignore next */
  64. var minLen = futureChanges < currentChanges ? futureChanges : currentChanges;
  65. var link = Array(minLen++);
  66. var tresh = Array(minLen);
  67. tresh[0] = -1;
  68. for (var i = 1; i < minLen; i++) {
  69. tresh[i] = currentEnd;
  70. }
  71. var nodes = currentNodes.slice(currentStart, currentEnd);
  72. for (var _i = futureStart; _i < futureEnd; _i++) {
  73. var index = nodes.indexOf(futureNodes[_i]);
  74. if (-1 < index) {
  75. var idxInOld = index + currentStart;
  76. k = findK(tresh, minLen, idxInOld);
  77. /* istanbul ignore else */
  78. if (-1 < k) {
  79. tresh[k] = idxInOld;
  80. link[k] = {
  81. newi: _i,
  82. oldi: idxInOld,
  83. prev: link[k - 1]
  84. };
  85. }
  86. }
  87. }
  88. k = --minLen;
  89. --currentEnd;
  90. while (tresh[k] > currentEnd) {
  91. --k;
  92. }
  93. minLen = currentChanges + futureChanges - k;
  94. var diff = Array(minLen);
  95. var ptr = link[k];
  96. --futureEnd;
  97. while (ptr) {
  98. var _ptr = ptr,
  99. newi = _ptr.newi,
  100. oldi = _ptr.oldi;
  101. while (futureEnd > newi) {
  102. diff[--minLen] = INSERTION;
  103. --futureEnd;
  104. }
  105. while (currentEnd > oldi) {
  106. diff[--minLen] = DELETION;
  107. --currentEnd;
  108. }
  109. diff[--minLen] = SKIP;
  110. --futureEnd;
  111. --currentEnd;
  112. ptr = ptr.prev;
  113. }
  114. while (futureEnd >= futureStart) {
  115. diff[--minLen] = INSERTION;
  116. --futureEnd;
  117. }
  118. while (currentEnd >= currentStart) {
  119. diff[--minLen] = DELETION;
  120. --currentEnd;
  121. }
  122. return diff;
  123. }; // this is pretty much the same petit-dom code without the delete map part
  124. // https://github.com/yelouafi/petit-dom/blob/bd6f5c919b5ae5297be01612c524c40be45f14a7/src/vdom.js#L556-L561
  125. var OND = function OND(futureNodes, futureStart, rows, currentNodes, currentStart, cols, compare) {
  126. var length = rows + cols;
  127. var v = [];
  128. var d, k, r, c, pv, cv, pd;
  129. outer: for (d = 0; d <= length; d++) {
  130. /* istanbul ignore if */
  131. if (d > SKIP_OND) return null;
  132. pd = d - 1;
  133. /* istanbul ignore next */
  134. pv = d ? v[d - 1] : [0, 0];
  135. cv = v[d] = [];
  136. for (k = -d; k <= d; k += 2) {
  137. if (k === -d || k !== d && pv[pd + k - 1] < pv[pd + k + 1]) {
  138. c = pv[pd + k + 1];
  139. } else {
  140. c = pv[pd + k - 1] + 1;
  141. }
  142. r = c - k;
  143. while (c < cols && r < rows && compare(currentNodes[currentStart + c], futureNodes[futureStart + r])) {
  144. c++;
  145. r++;
  146. }
  147. if (c === cols && r === rows) {
  148. break outer;
  149. }
  150. cv[d + k] = c;
  151. }
  152. }
  153. var diff = Array(d / 2 + length / 2);
  154. var diffIdx = diff.length - 1;
  155. for (d = v.length - 1; d >= 0; d--) {
  156. while (c > 0 && r > 0 && compare(currentNodes[currentStart + c - 1], futureNodes[futureStart + r - 1])) {
  157. // diagonal edge = equality
  158. diff[diffIdx--] = SKIP;
  159. c--;
  160. r--;
  161. }
  162. if (!d) break;
  163. pd = d - 1;
  164. /* istanbul ignore next */
  165. pv = d ? v[d - 1] : [0, 0];
  166. k = c - r;
  167. if (k === -d || k !== d && pv[pd + k - 1] < pv[pd + k + 1]) {
  168. // vertical edge = insertion
  169. r--;
  170. diff[diffIdx--] = INSERTION;
  171. } else {
  172. // horizontal edge = deletion
  173. c--;
  174. diff[diffIdx--] = DELETION;
  175. }
  176. }
  177. return diff;
  178. };
  179. var applyDiff = function applyDiff(diff, get, parentNode, futureNodes, futureStart, currentNodes, currentStart, currentLength, before) {
  180. var live = [];
  181. var length = diff.length;
  182. var currentIndex = currentStart;
  183. var i = 0;
  184. while (i < length) {
  185. switch (diff[i++]) {
  186. case SKIP:
  187. futureStart++;
  188. currentIndex++;
  189. break;
  190. case INSERTION:
  191. // TODO: bulk appends for sequential nodes
  192. live.push(futureNodes[futureStart]);
  193. append(get, parentNode, futureNodes, futureStart++, futureStart, currentIndex < currentLength ? get(currentNodes[currentIndex], 0) : before);
  194. break;
  195. case DELETION:
  196. currentIndex++;
  197. break;
  198. }
  199. }
  200. i = 0;
  201. while (i < length) {
  202. switch (diff[i++]) {
  203. case SKIP:
  204. currentStart++;
  205. break;
  206. case DELETION:
  207. // TODO: bulk removes for sequential nodes
  208. if (-1 < live.indexOf(currentNodes[currentStart])) currentStart++;else remove(get, currentNodes, currentStart++, currentStart);
  209. break;
  210. }
  211. }
  212. };
  213. var findK = function findK(ktr, length, j) {
  214. var lo = 1;
  215. var hi = length;
  216. while (lo < hi) {
  217. var mid = (lo + hi) / 2 >>> 0;
  218. if (j < ktr[mid]) hi = mid;else lo = mid + 1;
  219. }
  220. return lo;
  221. };
  222. var smartDiff = function smartDiff(get, parentNode, futureNodes, futureStart, futureEnd, futureChanges, currentNodes, currentStart, currentEnd, currentChanges, currentLength, compare, before) {
  223. 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);
  224. };
  225. var drop = function drop(node) {
  226. return (node.remove || dropChild).call(node);
  227. };
  228. function dropChild() {
  229. var parentNode = this.parentNode;
  230. /* istanbul ignore else */
  231. if (parentNode) parentNode.removeChild(this);
  232. }
  233. /*! (c) 2018 Andrea Giammarchi (ISC) */
  234. var domdiff = function domdiff(parentNode, // where changes happen
  235. currentNodes, // Array of current items/nodes
  236. futureNodes, // Array of future items/nodes
  237. options // optional object with one of the following properties
  238. // before: domNode
  239. // compare(generic, generic) => true if same generic
  240. // node(generic) => Node
  241. ) {
  242. if (!options) options = {};
  243. var compare = options.compare || eqeq;
  244. var get = options.node || identity;
  245. var before = options.before == null ? null : get(options.before, 0);
  246. var currentLength = currentNodes.length;
  247. var currentEnd = currentLength;
  248. var currentStart = 0;
  249. var futureEnd = futureNodes.length;
  250. var futureStart = 0; // common prefix
  251. while (currentStart < currentEnd && futureStart < futureEnd && compare(currentNodes[currentStart], futureNodes[futureStart])) {
  252. currentStart++;
  253. futureStart++;
  254. } // common suffix
  255. while (currentStart < currentEnd && futureStart < futureEnd && compare(currentNodes[currentEnd - 1], futureNodes[futureEnd - 1])) {
  256. currentEnd--;
  257. futureEnd--;
  258. }
  259. var currentSame = currentStart === currentEnd;
  260. var futureSame = futureStart === futureEnd; // same list
  261. if (currentSame && futureSame) return futureNodes; // only stuff to add
  262. if (currentSame && futureStart < futureEnd) {
  263. append(get, parentNode, futureNodes, futureStart, futureEnd, next(get, currentNodes, currentStart, currentLength, before));
  264. return futureNodes;
  265. } // only stuff to remove
  266. if (futureSame && currentStart < currentEnd) {
  267. remove(get, currentNodes, currentStart, currentEnd);
  268. return futureNodes;
  269. }
  270. var currentChanges = currentEnd - currentStart;
  271. var futureChanges = futureEnd - futureStart;
  272. var i = -1; // 2 simple indels: the shortest sequence is a subsequence of the longest
  273. if (currentChanges < futureChanges) {
  274. i = indexOf(futureNodes, futureStart, futureEnd, currentNodes, currentStart, currentEnd, compare); // inner diff
  275. if (-1 < i) {
  276. append(get, parentNode, futureNodes, futureStart, i, get(currentNodes[currentStart], 0));
  277. append(get, parentNode, futureNodes, i + currentChanges, futureEnd, next(get, currentNodes, currentEnd, currentLength, before));
  278. return futureNodes;
  279. }
  280. }
  281. /* istanbul ignore else */
  282. else if (futureChanges < currentChanges) {
  283. i = indexOf(currentNodes, currentStart, currentEnd, futureNodes, futureStart, futureEnd, compare); // outer diff
  284. if (-1 < i) {
  285. remove(get, currentNodes, currentStart, i);
  286. remove(get, currentNodes, i + futureChanges, currentEnd);
  287. return futureNodes;
  288. }
  289. } // common case with one replacement for many nodes
  290. // or many nodes replaced for a single one
  291. /* istanbul ignore else */
  292. if (currentChanges < 2 || futureChanges < 2) {
  293. append(get, parentNode, futureNodes, futureStart, futureEnd, get(currentNodes[currentStart], 0));
  294. remove(get, currentNodes, currentStart, currentEnd);
  295. return futureNodes;
  296. } // the half match diff part has been skipped in petit-dom
  297. // https://github.com/yelouafi/petit-dom/blob/bd6f5c919b5ae5297be01612c524c40be45f14a7/src/vdom.js#L391-L397
  298. // accordingly, I think it's safe to skip in here too
  299. // if one day it'll come out like the speediest thing ever to do
  300. // then I might add it in here too
  301. // Extra: before going too fancy, what about reversed lists ?
  302. // This should bail out pretty quickly if that's not the case.
  303. if (currentChanges === futureChanges && isReversed(futureNodes, futureEnd, currentNodes, currentStart, currentEnd, compare)) {
  304. append(get, parentNode, futureNodes, futureStart, futureEnd, next(get, currentNodes, currentEnd, currentLength, before));
  305. return futureNodes;
  306. } // last resort through a smart diff
  307. smartDiff(get, parentNode, futureNodes, futureStart, futureEnd, futureChanges, currentNodes, currentStart, currentEnd, currentChanges, currentLength, compare, before);
  308. return futureNodes;
  309. };
  310. return domdiff;
  311. }());