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

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