@ckeditor/ckeditor5-engine 41.4.1 → 42.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +6 -0
  2. package/dist/index.js +15151 -13052
  3. package/dist/index.js.map +1 -1
  4. package/dist/types/controller/datacontroller.d.ts +1 -1
  5. package/dist/types/controller/editingcontroller.d.ts +1 -1
  6. package/dist/types/conversion/downcastdispatcher.d.ts +1 -1
  7. package/dist/types/conversion/mapper.d.ts +1 -1
  8. package/dist/types/conversion/upcastdispatcher.d.ts +1 -1
  9. package/dist/types/index.d.ts +2 -1
  10. package/dist/types/model/differ.d.ts +134 -42
  11. package/dist/types/model/document.d.ts +1 -1
  12. package/dist/types/model/documentselection.d.ts +1 -1
  13. package/dist/types/model/liveposition.d.ts +1 -1
  14. package/dist/types/model/liverange.d.ts +1 -1
  15. package/dist/types/model/markercollection.d.ts +2 -2
  16. package/dist/types/model/model.d.ts +1 -1
  17. package/dist/types/model/schema.d.ts +32 -6
  18. package/dist/types/model/selection.d.ts +1 -1
  19. package/dist/types/view/document.d.ts +1 -1
  20. package/dist/types/view/documentfragment.d.ts +1 -1
  21. package/dist/types/view/documentselection.d.ts +1 -1
  22. package/dist/types/view/domconverter.d.ts +9 -0
  23. package/dist/types/view/editableelement.d.ts +1 -1
  24. package/dist/types/view/node.d.ts +1 -1
  25. package/dist/types/view/observer/observer.d.ts +1 -1
  26. package/dist/types/view/renderer.d.ts +1 -1
  27. package/dist/types/view/selection.d.ts +1 -1
  28. package/dist/types/view/view.d.ts +1 -1
  29. package/package.json +2 -2
  30. package/src/controller/datacontroller.d.ts +1 -1
  31. package/src/controller/datacontroller.js +1 -1
  32. package/src/controller/editingcontroller.d.ts +1 -1
  33. package/src/controller/editingcontroller.js +1 -1
  34. package/src/conversion/downcastdispatcher.d.ts +1 -1
  35. package/src/conversion/downcastdispatcher.js +1 -1
  36. package/src/conversion/mapper.d.ts +1 -1
  37. package/src/conversion/mapper.js +1 -1
  38. package/src/conversion/upcastdispatcher.d.ts +1 -1
  39. package/src/conversion/upcastdispatcher.js +1 -1
  40. package/src/index.d.ts +2 -1
  41. package/src/index.js +1 -0
  42. package/src/model/differ.d.ts +134 -42
  43. package/src/model/differ.js +247 -125
  44. package/src/model/document.d.ts +1 -1
  45. package/src/model/document.js +1 -1
  46. package/src/model/documentselection.d.ts +1 -1
  47. package/src/model/documentselection.js +1 -1
  48. package/src/model/liveposition.d.ts +1 -1
  49. package/src/model/liveposition.js +1 -1
  50. package/src/model/liverange.d.ts +1 -1
  51. package/src/model/liverange.js +1 -1
  52. package/src/model/markercollection.d.ts +2 -2
  53. package/src/model/markercollection.js +2 -2
  54. package/src/model/model.d.ts +1 -1
  55. package/src/model/model.js +1 -1
  56. package/src/model/schema.d.ts +32 -6
  57. package/src/model/schema.js +208 -101
  58. package/src/model/selection.d.ts +1 -1
  59. package/src/model/selection.js +1 -1
  60. package/src/view/document.d.ts +1 -1
  61. package/src/view/document.js +1 -1
  62. package/src/view/documentfragment.d.ts +1 -1
  63. package/src/view/documentfragment.js +1 -1
  64. package/src/view/documentselection.d.ts +1 -1
  65. package/src/view/documentselection.js +1 -1
  66. package/src/view/domconverter.d.ts +9 -0
  67. package/src/view/domconverter.js +27 -5
  68. package/src/view/editableelement.d.ts +1 -1
  69. package/src/view/editableelement.js +1 -1
  70. package/src/view/node.d.ts +1 -1
  71. package/src/view/node.js +1 -1
  72. package/src/view/observer/observer.d.ts +1 -1
  73. package/src/view/observer/observer.js +1 -1
  74. package/src/view/renderer.d.ts +1 -1
  75. package/src/view/renderer.js +1 -1
  76. package/src/view/selection.d.ts +1 -1
  77. package/src/view/selection.js +1 -1
  78. package/src/view/view.d.ts +1 -1
  79. package/src/view/view.js +1 -1
@@ -15,7 +15,7 @@ import Range from './range.js';
15
15
  * changed elements, after all changes are applied on the model document. Calculates the diff between saved
16
16
  * elements and new ones and returns a change set.
17
17
  */
18
- export default class Differ {
18
+ class Differ {
19
19
  /**
20
20
  * Creates a `Differ` instance.
21
21
  *
@@ -30,18 +30,48 @@ export default class Differ {
30
30
  */
31
31
  this._changesInElement = new Map();
32
32
  /**
33
- * A map that stores "element's children snapshots". A snapshot is representing children of a given element before
34
- * the first change was applied on that element. Snapshot items are objects with two properties: `name`,
35
- * containing the element name (or `'$text'` for a text node) and `attributes` which is a map of the node's attributes.
33
+ * Stores a snapshot for these model nodes that might have changed.
34
+ *
35
+ * This complements {@link ~Differ#_elementChildrenSnapshots `_elementChildrenSnapshots`}.
36
+ *
37
+ * See also {@link ~DifferSnapshot}.
38
+ */
39
+ this._elementsSnapshots = new Map();
40
+ /**
41
+ * For each element or document fragment inside which there was a change, it stores a snapshot of the child nodes list (an array
42
+ * of children snapshots that represent the state in the element / fragment before any change has happened).
43
+ *
44
+ * This complements {@link ~Differ#_elementsSnapshots `_elementsSnapshots`}.
45
+ *
46
+ * See also {@link ~DifferSnapshot}.
47
+ */
48
+ this._elementChildrenSnapshots = new Map();
49
+ /**
50
+ * Keeps the state for a given element, describing how the element was changed so far. It is used to evaluate the `action` property
51
+ * of diff items returned by {@link ~Differ#getChanges}.
52
+ *
53
+ * Possible values, in the order from the lowest priority to the highest priority:
54
+ *
55
+ * * `'refresh'` - element was refreshed,
56
+ * * `'rename'` - element was renamed,
57
+ * * `'move'` - element was moved (or, usually, removed, that is moved to the graveyard).
58
+ *
59
+ * Element that was refreshed, may change its state to `'rename'` if it was later renamed, or to `'move'` if it was removed.
60
+ * But the element cannot change its state from `'move'` to `'rename'`, or from `'rename'` to `'refresh'`.
61
+ *
62
+ * Only already existing elements are registered in `_elementState`. If a new element was inserted as a result of a buffered operation,
63
+ * it is not be registered in `_elementState`.
36
64
  */
37
- this._elementSnapshots = new Map();
65
+ this._elementState = new Map();
38
66
  /**
39
67
  * A map that stores all changed markers.
40
68
  *
41
69
  * The keys of the map are marker names.
70
+ *
42
71
  * The values of the map are objects with the following properties:
43
- * - `oldMarkerData`,
44
- * - `newMarkerData`.
72
+ *
73
+ * * `oldMarkerData`,
74
+ * * `newMarkerData`.
45
75
  */
46
76
  this._changedMarkers = new Map();
47
77
  /**
@@ -84,7 +114,7 @@ export default class Differ {
84
114
  return this._changesInElement.size == 0 && this._changedMarkers.size == 0 && this._changedRoots.size == 0;
85
115
  }
86
116
  /**
87
- * Buffers the given operation. An operation has to be buffered before it is executed.
117
+ * Buffers the given operation. **An operation has to be buffered before it is executed.**
88
118
  *
89
119
  * @param operationToBuffer An operation to buffer.
90
120
  */
@@ -133,6 +163,11 @@ export default class Differ {
133
163
  if (!targetParentInserted) {
134
164
  this._markInsert(operation.targetPosition.parent, operation.getMovedRangeStart().offset, operation.howMany);
135
165
  }
166
+ // Remember -- operation is buffered before it is executed. So, it was not executed yet.
167
+ const range = Range._createFromPositionAndShift(operation.sourcePosition, operation.howMany);
168
+ for (const node of range.getItems({ shallow: true })) {
169
+ this._setElementState(node, 'move');
170
+ }
136
171
  break;
137
172
  }
138
173
  case 'rename': {
@@ -146,6 +181,7 @@ export default class Differ {
146
181
  const markerData = marker.getData();
147
182
  this.bufferMarkerChange(marker.name, markerData, markerData);
148
183
  }
184
+ this._setElementState(operation.position.nodeAfter, 'rename');
149
185
  break;
150
186
  }
151
187
  case 'split': {
@@ -153,6 +189,11 @@ export default class Differ {
153
189
  // Mark that children of the split element were removed.
154
190
  if (!this._isInInsertedElement(splitElement)) {
155
191
  this._markRemove(splitElement, operation.splitPosition.offset, operation.howMany);
192
+ // Remember -- operation is buffered before it is executed. So, it was not executed yet.
193
+ const range = Range._createFromPositionAndShift(operation.splitPosition, operation.howMany);
194
+ for (const node of range.getItems({ shallow: true })) {
195
+ this._setElementState(node, 'move');
196
+ }
156
197
  }
157
198
  // Mark that the new element (split copy) was inserted.
158
199
  if (!this._isInInsertedElement(operation.insertionPosition.parent)) {
@@ -161,6 +202,7 @@ export default class Differ {
161
202
  // If the split took the element from the graveyard, mark that the element from the graveyard was removed.
162
203
  if (operation.graveyardPosition) {
163
204
  this._markRemove(operation.graveyardPosition.parent, operation.graveyardPosition.offset, 1);
205
+ this._setElementState(operation.graveyardPosition.nodeAfter, 'move');
164
206
  }
165
207
  break;
166
208
  }
@@ -173,10 +215,16 @@ export default class Differ {
173
215
  // Mark that the merged element was inserted into graveyard.
174
216
  const graveyardParent = operation.graveyardPosition.parent;
175
217
  this._markInsert(graveyardParent, operation.graveyardPosition.offset, 1);
218
+ this._setElementState(mergedElement, 'move');
176
219
  // Mark that children of merged element were inserted at new parent.
177
220
  const mergedIntoElement = operation.targetPosition.parent;
178
221
  if (!this._isInInsertedElement(mergedIntoElement)) {
179
222
  this._markInsert(mergedIntoElement, operation.targetPosition.offset, mergedElement.maxOffset);
223
+ // Remember -- operation is buffered before it is executed. So, it was not executed yet.
224
+ const range = Range._createFromPositionAndShift(operation.sourcePosition, operation.howMany);
225
+ for (const node of range.getItems({ shallow: true })) {
226
+ this._setElementState(node, 'move');
227
+ }
180
228
  }
181
229
  break;
182
230
  }
@@ -338,9 +386,9 @@ export default class Differ {
338
386
  }
339
387
  // Will contain returned results.
340
388
  let diffSet = [];
341
- // Check all changed elements.
389
+ // Check all changed elements/roots.
342
390
  for (const element of this._changesInElement.keys()) {
343
- // Get changes for this element and sort them.
391
+ // Get changes inside this element/root and sort them.
344
392
  const changes = this._changesInElement.get(element).sort((a, b) => {
345
393
  if (a.offset === b.offset) {
346
394
  if (a.type != b.type) {
@@ -354,31 +402,34 @@ export default class Differ {
354
402
  return a.offset < b.offset ? -1 : 1;
355
403
  });
356
404
  // Get children of this element before any change was applied on it.
357
- const snapshotChildren = this._elementSnapshots.get(element);
405
+ const childrenBefore = this._elementChildrenSnapshots.get(element);
358
406
  // Get snapshot of current element's children.
359
- const elementChildren = _getChildrenSnapshot(element.getChildren());
360
- // Generate actions basing on changes done on element.
361
- const actions = _generateActionsFromChanges(snapshotChildren.length, changes);
362
- let i = 0; // Iterator in `elementChildren` array -- iterates through current children of element.
363
- let j = 0; // Iterator in `snapshotChildren` array -- iterates through old children of element.
407
+ const childrenAfter = _getChildrenSnapshots(element.getChildren());
408
+ // Generate diff instructions based on changes done in the element/root.
409
+ const diffInstructions = _generateDiffInstructionsFromChanges(childrenBefore.length, changes);
410
+ let i = 0; // Iterator in `childrenAfter` array -- iterates through current children of element.
411
+ let j = 0; // Iterator in `childrenBefore` array -- iterates through old children of element.
364
412
  // Process every action.
365
- for (const action of actions) {
366
- if (action === 'i') {
367
- // Generate diff item for this element and insert it into the diff set.
368
- diffSet.push(this._getInsertDiff(element, i, elementChildren[i]));
413
+ for (const instruction of diffInstructions) {
414
+ if (instruction === 'i') {
415
+ const action = this._getDiffActionForNode(childrenAfter[i].node, 'insert');
416
+ const childSnapshotBefore = this._elementsSnapshots.get(childrenAfter[i].node);
417
+ const diffItem = this._getInsertDiff(element, i, action, childrenAfter[i], childSnapshotBefore);
418
+ diffSet.push(diffItem);
369
419
  i++;
370
420
  }
371
- else if (action === 'r') {
372
- // Generate diff item for this element and insert it into the diff set.
373
- diffSet.push(this._getRemoveDiff(element, i, snapshotChildren[j]));
421
+ else if (instruction === 'r') {
422
+ const action = this._getDiffActionForNode(childrenBefore[j].node, 'remove');
423
+ const diffItem = this._getRemoveDiff(element, i, action, childrenBefore[j]);
424
+ diffSet.push(diffItem);
374
425
  j++;
375
426
  }
376
- else if (action === 'a') {
427
+ else if (instruction === 'a') {
377
428
  // Take attributes from saved and current children.
378
- const elementAttributes = elementChildren[i].attributes;
379
- const snapshotAttributes = snapshotChildren[j].attributes;
429
+ const beforeAttributes = childrenBefore[j].attributes;
430
+ const afterAttributes = childrenAfter[i].attributes;
380
431
  let range;
381
- if (elementChildren[i].name == '$text') {
432
+ if (childrenAfter[i].name == '$text') {
382
433
  range = new Range(Position._createAt(element, i), Position._createAt(element, i + 1));
383
434
  }
384
435
  else {
@@ -387,7 +438,8 @@ export default class Differ {
387
438
  }
388
439
  // Generate diff items for this change (there might be multiple attributes changed and
389
440
  // there is a single diff for each of them) and insert them into the diff set.
390
- diffSet.push(...this._getAttributesDiff(range, snapshotAttributes, elementAttributes));
441
+ const diffItems = this._getAttributesDiff(range, beforeAttributes, afterAttributes);
442
+ diffSet.push(...diffItems);
391
443
  i++;
392
444
  j++;
393
445
  }
@@ -498,12 +550,69 @@ export default class Differ {
498
550
  */
499
551
  reset() {
500
552
  this._changesInElement.clear();
501
- this._elementSnapshots.clear();
553
+ this._elementChildrenSnapshots.clear();
554
+ this._elementsSnapshots.clear();
555
+ this._elementState.clear();
502
556
  this._changedMarkers.clear();
503
557
  this._changedRoots.clear();
504
- this._refreshedItems = new Set();
558
+ this._refreshedItems.clear();
559
+ this._cachedChanges = null;
560
+ }
561
+ /**
562
+ * Marks the given `item` in differ to be "refreshed". It means that the item will be marked as removed and inserted
563
+ * in the differ changes set, so it will be effectively re-converted when the differ changes are handled by a dispatcher.
564
+ *
565
+ * @internal
566
+ * @param item Item to refresh.
567
+ */
568
+ _refreshItem(item) {
569
+ if (this._isInInsertedElement(item.parent)) {
570
+ return;
571
+ }
572
+ this._markRemove(item.parent, item.startOffset, item.offsetSize);
573
+ this._markInsert(item.parent, item.startOffset, item.offsetSize);
574
+ this._refreshedItems.add(item);
575
+ this._setElementState(item, 'refresh');
576
+ const range = Range._createOn(item);
577
+ for (const marker of this._markerCollection.getMarkersIntersectingRange(range)) {
578
+ const markerData = marker.getData();
579
+ this.bufferMarkerChange(marker.name, markerData, markerData);
580
+ }
581
+ // Clear cache after each buffered operation as it is no longer valid.
505
582
  this._cachedChanges = null;
506
583
  }
584
+ /**
585
+ * Buffers all the data related to given root like it was all just added to the editor.
586
+ *
587
+ * Following changes are buffered:
588
+ *
589
+ * * root is attached,
590
+ * * all root content is inserted,
591
+ * * all root attributes are added,
592
+ * * all markers inside the root are added.
593
+ *
594
+ * @internal
595
+ */
596
+ _bufferRootLoad(root) {
597
+ if (!root.isAttached()) {
598
+ return;
599
+ }
600
+ this._bufferRootStateChange(root.rootName, true);
601
+ this._markInsert(root, 0, root.maxOffset);
602
+ // Buffering root attribute changes makes sense and is actually needed, even though we buffer root state change above.
603
+ // Because the root state change is buffered, the root attributes changes are not returned by the differ.
604
+ // But, if the root attribute is removed in the same change block, or the root is detached, then the differ results would be wrong.
605
+ //
606
+ for (const key of root.getAttributeKeys()) {
607
+ this._bufferRootAttributeChange(root.rootName, key, null, root.getAttribute(key));
608
+ }
609
+ for (const marker of this._markerCollection) {
610
+ if (marker.getRange().root == root) {
611
+ const markerData = marker.getData();
612
+ this.bufferMarkerChange(marker.name, { ...markerData, range: null }, markerData);
613
+ }
614
+ }
615
+ }
507
616
  /**
508
617
  * Buffers the root state change after the root was attached or detached
509
618
  */
@@ -563,60 +672,6 @@ export default class Differ {
563
672
  this._changedRoots.set(rootName, diffItem);
564
673
  }
565
674
  }
566
- /**
567
- * Marks the given `item` in differ to be "refreshed". It means that the item will be marked as removed and inserted
568
- * in the differ changes set, so it will be effectively re-converted when the differ changes are handled by a dispatcher.
569
- *
570
- * @internal
571
- * @param item Item to refresh.
572
- */
573
- _refreshItem(item) {
574
- if (this._isInInsertedElement(item.parent)) {
575
- return;
576
- }
577
- this._markRemove(item.parent, item.startOffset, item.offsetSize);
578
- this._markInsert(item.parent, item.startOffset, item.offsetSize);
579
- this._refreshedItems.add(item);
580
- const range = Range._createOn(item);
581
- for (const marker of this._markerCollection.getMarkersIntersectingRange(range)) {
582
- const markerData = marker.getData();
583
- this.bufferMarkerChange(marker.name, markerData, markerData);
584
- }
585
- // Clear cache after each buffered operation as it is no longer valid.
586
- this._cachedChanges = null;
587
- }
588
- /**
589
- * Buffers all the data related to given root like it was all just added to the editor.
590
- *
591
- * Following changes are buffered:
592
- *
593
- * * root is attached,
594
- * * all root content is inserted,
595
- * * all root attributes are added,
596
- * * all markers inside the root are added.
597
- *
598
- * @internal
599
- */
600
- _bufferRootLoad(root) {
601
- if (!root.isAttached()) {
602
- return;
603
- }
604
- this._bufferRootStateChange(root.rootName, true);
605
- this._markInsert(root, 0, root.maxOffset);
606
- // Buffering root attribute changes makes sense and is actually needed, even though we buffer root state change above.
607
- // Because the root state change is buffered, the root attributes changes are not returned by the differ.
608
- // But, if the root attribute is removed in the same change block, or the root is detached, then the differ results would be wrong.
609
- //
610
- for (const key of root.getAttributeKeys()) {
611
- this._bufferRootAttributeChange(root.rootName, key, null, root.getAttribute(key));
612
- }
613
- for (const marker of this._markerCollection) {
614
- if (marker.getRange().root == root) {
615
- const markerData = marker.getData();
616
- this.bufferMarkerChange(marker.name, { ...markerData, range: null }, markerData);
617
- }
618
- }
619
- }
620
675
  /**
621
676
  * Saves and handles an insert change.
622
677
  */
@@ -652,8 +707,8 @@ export default class Differ {
652
707
  * Saves and handles a model change.
653
708
  */
654
709
  _markChange(parent, changeItem) {
655
- // First, make a snapshot of this parent's children (it will be made only if it was not made before).
656
- this._makeSnapshot(parent);
710
+ // First, make a snapshot of the parent and its children (it will be made only if it was not made before).
711
+ this._makeSnapshots(parent);
657
712
  // Then, get all changes that already were done on the element (empty array if this is the first change).
658
713
  const changes = this._getChangesForElement(parent);
659
714
  // Then, look through all the changes, and transform them or the new change.
@@ -669,6 +724,49 @@ export default class Differ {
669
724
  }
670
725
  }
671
726
  }
727
+ /**
728
+ * Tries to set given state for given item.
729
+ *
730
+ * This method does simple validation (it sets the state only for model elements, not for text proxy nodes). It also follows state
731
+ * setting rules, that is, `'refresh'` cannot overwrite `'rename'`, and `'rename'` cannot overwrite `'move'`.
732
+ */
733
+ _setElementState(node, state) {
734
+ if (!node.is('element')) {
735
+ return;
736
+ }
737
+ const currentStatePriority = Differ._statesPriority.indexOf(this._elementState.get(node));
738
+ const newStatePriority = Differ._statesPriority.indexOf(state);
739
+ if (newStatePriority > currentStatePriority) {
740
+ this._elementState.set(node, state);
741
+ }
742
+ }
743
+ /**
744
+ * Returns a value for {@link ~DifferItemAction `action`} property for diff items returned by {@link ~Differ#getChanges}.
745
+ * This method aims to return `'rename'` or `'refresh'` when it should, and `diffItemType` ("default action") in all other cases.
746
+ *
747
+ * It bases on a few factors:
748
+ *
749
+ * * for text nodes, the method always returns `diffItemType`,
750
+ * * for newly inserted element, the method returns `diffItemType`,
751
+ * * if {@link ~Differ#_elementState element state} was not recorded, the method returns `diffItemType`,
752
+ * * if state was recorded, and it was `'move'` (default action), the method returns `diffItemType`,
753
+ * * finally, if state was `'refresh'` or `'rename'`, the method returns the state value.
754
+ */
755
+ _getDiffActionForNode(node, diffItemType) {
756
+ if (!node.is('element')) {
757
+ // Text node.
758
+ return diffItemType;
759
+ }
760
+ if (!this._elementsSnapshots.has(node)) {
761
+ // Newly inserted element.
762
+ return diffItemType;
763
+ }
764
+ const state = this._elementState.get(node);
765
+ if (!state || state == 'move') {
766
+ return diffItemType;
767
+ }
768
+ return state;
769
+ }
672
770
  /**
673
771
  * Gets an array of changes that have already been saved for a given element.
674
772
  */
@@ -684,11 +782,16 @@ export default class Differ {
684
782
  return changes;
685
783
  }
686
784
  /**
687
- * Saves a children snapshot for a given element.
785
+ * Creates and saves a snapshot for all children of the given element.
688
786
  */
689
- _makeSnapshot(element) {
690
- if (!this._elementSnapshots.has(element)) {
691
- this._elementSnapshots.set(element, _getChildrenSnapshot(element.getChildren()));
787
+ _makeSnapshots(element) {
788
+ if (this._elementChildrenSnapshots.has(element)) {
789
+ return;
790
+ }
791
+ const childrenSnapshots = _getChildrenSnapshots(element.getChildren());
792
+ this._elementChildrenSnapshots.set(element, childrenSnapshots);
793
+ for (const snapshot of childrenSnapshots) {
794
+ this._elementsSnapshots.set(snapshot.node, snapshot);
692
795
  }
693
796
  }
694
797
  /**
@@ -906,37 +1009,47 @@ export default class Differ {
906
1009
  *
907
1010
  * @param parent The element in which the change happened.
908
1011
  * @param offset The offset at which change happened.
909
- * @param elementSnapshot The snapshot of the removed element a character.
1012
+ * @param action Further specifies what kind of action led to generating this change.
1013
+ * @param elementSnapshot Snapshot of the inserted node after changes.
1014
+ * @param elementSnapshotBefore Snapshot of the inserted node before changes.
910
1015
  * @returns The diff item.
911
1016
  */
912
- _getInsertDiff(parent, offset, elementSnapshot) {
913
- return {
1017
+ _getInsertDiff(parent, offset, action, elementSnapshot, elementSnapshotBefore) {
1018
+ const diffItem = {
914
1019
  type: 'insert',
915
1020
  position: Position._createAt(parent, offset),
916
1021
  name: elementSnapshot.name,
917
1022
  attributes: new Map(elementSnapshot.attributes),
918
1023
  length: 1,
919
1024
  changeCount: this._changeCount++,
920
- _element: elementSnapshot.element
1025
+ action
921
1026
  };
1027
+ if (action != 'insert' && elementSnapshotBefore) {
1028
+ diffItem.before = {
1029
+ name: elementSnapshotBefore.name,
1030
+ attributes: new Map(elementSnapshotBefore.attributes)
1031
+ };
1032
+ }
1033
+ return diffItem;
922
1034
  }
923
1035
  /**
924
1036
  * Returns an object with a single remove change description.
925
1037
  *
926
1038
  * @param parent The element in which change happened.
927
1039
  * @param offset The offset at which change happened.
928
- * @param elementSnapshot The snapshot of the removed element a character.
1040
+ * @param action Further specifies what kind of action led to generating this change.
1041
+ * @param elementSnapshot The snapshot of the removed node before changes.
929
1042
  * @returns The diff item.
930
1043
  */
931
- _getRemoveDiff(parent, offset, elementSnapshot) {
1044
+ _getRemoveDiff(parent, offset, action, elementSnapshot) {
932
1045
  return {
933
1046
  type: 'remove',
1047
+ action,
934
1048
  position: Position._createAt(parent, offset),
935
1049
  name: elementSnapshot.name,
936
1050
  attributes: new Map(elementSnapshot.attributes),
937
1051
  length: 1,
938
- changeCount: this._changeCount++,
939
- _element: elementSnapshot.element
1052
+ changeCount: this._changeCount++
940
1053
  };
941
1054
  }
942
1055
  /**
@@ -1016,42 +1129,51 @@ export default class Differ {
1016
1129
  const range = new Range(Position._createAt(parent, offset), Position._createAt(parent, offset + howMany));
1017
1130
  for (const item of range.getItems({ shallow: true })) {
1018
1131
  if (item.is('element')) {
1019
- this._elementSnapshots.delete(item);
1020
1132
  this._changesInElement.delete(item);
1021
1133
  this._removeAllNestedChanges(item, 0, item.maxOffset);
1022
1134
  }
1023
1135
  }
1024
1136
  }
1025
1137
  }
1138
+ /**
1139
+ * Priority of the {@link ~Differ#_elementState element states}. States on higher indexes of the array can overwrite states on the lower
1140
+ * indexes.
1141
+ */
1142
+ Differ._statesPriority = [undefined, 'refresh', 'rename', 'move'];
1143
+ export default Differ;
1144
+ /**
1145
+ * Returns a snapshot for the specified child node. Text node snapshots have the `name` property set to `$text`.
1146
+ */
1147
+ function _getSingleNodeSnapshot(node) {
1148
+ return {
1149
+ node,
1150
+ name: node.is('$text') ? '$text' : node.name,
1151
+ attributes: new Map(node.getAttributes())
1152
+ };
1153
+ }
1026
1154
  /**
1027
1155
  * Returns an array that is a copy of passed child list with the exception that text nodes are split to one or more
1028
1156
  * objects, each representing one character and attributes set on that character.
1029
1157
  */
1030
- function _getChildrenSnapshot(children) {
1031
- const snapshot = [];
1158
+ function _getChildrenSnapshots(children) {
1159
+ const snapshots = [];
1032
1160
  for (const child of children) {
1033
1161
  if (child.is('$text')) {
1034
- for (let i = 0; i < child.data.length; i++) {
1035
- snapshot.push({
1036
- name: '$text',
1037
- attributes: new Map(child.getAttributes())
1038
- });
1162
+ for (let i = 0; i < child.data.length; ++i) {
1163
+ snapshots.push(_getSingleNodeSnapshot(child));
1039
1164
  }
1040
1165
  }
1041
1166
  else {
1042
- snapshot.push({
1043
- name: child.name,
1044
- attributes: new Map(child.getAttributes()),
1045
- element: child
1046
- });
1167
+ snapshots.push(_getSingleNodeSnapshot(child));
1047
1168
  }
1048
1169
  }
1049
- return snapshot;
1170
+ return snapshots;
1050
1171
  }
1051
1172
  /**
1052
- * Generates array of actions for given changes set.
1053
- * It simulates what `diff` function does.
1173
+ * Generates array of diff instructions for given changes set.
1174
+ *
1054
1175
  * Generated actions are:
1176
+ *
1055
1177
  * - 'e' for 'equal' - when item at that position did not change,
1056
1178
  * - 'i' for 'insert' - when item at that position was inserted,
1057
1179
  * - 'r' for 'remove' - when item at that position was removed,
@@ -1066,9 +1188,9 @@ function _getChildrenSnapshot(children) {
1066
1188
  * type: insert, offset: 2, howMany: 2
1067
1189
  * type: attribute, offset: 4, howMany: 1
1068
1190
  *
1069
- * expected actions: equal (f), remove (o), equal (o), insert (x), insert (y), attribute (b), equal (A), equal (R)
1191
+ * Expected actions: equal (f), remove (o), equal (o), insert (x), insert (y), attribute (b), equal (A), equal (R)
1070
1192
  *
1071
- * steps taken by th script:
1193
+ * Steps taken by the script:
1072
1194
  *
1073
1195
  * 1. change = "type: remove, offset: 1, howMany: 1"; offset = 0; oldChildrenHandled = 0
1074
1196
  * 1.1 between this change and the beginning is one not-changed node, fill with one equal action, one old child has been handled
@@ -1095,8 +1217,8 @@ function _getChildrenSnapshot(children) {
1095
1217
  *
1096
1218
  * The result actions are: equal, remove, equal, insert, insert, attribute, equal, equal.
1097
1219
  */
1098
- function _generateActionsFromChanges(oldChildrenLength, changes) {
1099
- const actions = [];
1220
+ function _generateDiffInstructionsFromChanges(oldChildrenLength, changes) {
1221
+ const diff = [];
1100
1222
  let offset = 0;
1101
1223
  let oldChildrenHandled = 0;
1102
1224
  // Go through all buffered changes.
@@ -1104,21 +1226,21 @@ function _generateActionsFromChanges(oldChildrenLength, changes) {
1104
1226
  // First, fill "holes" between changes with "equal" actions.
1105
1227
  if (change.offset > offset) {
1106
1228
  for (let i = 0; i < change.offset - offset; i++) {
1107
- actions.push('e');
1229
+ diff.push('e');
1108
1230
  }
1109
1231
  oldChildrenHandled += change.offset - offset;
1110
1232
  }
1111
1233
  // Then, fill up actions accordingly to change type.
1112
1234
  if (change.type == 'insert') {
1113
1235
  for (let i = 0; i < change.howMany; i++) {
1114
- actions.push('i');
1236
+ diff.push('i');
1115
1237
  }
1116
1238
  // The last handled offset is after inserted range.
1117
1239
  offset = change.offset + change.howMany;
1118
1240
  }
1119
1241
  else if (change.type == 'remove') {
1120
1242
  for (let i = 0; i < change.howMany; i++) {
1121
- actions.push('r');
1243
+ diff.push('r');
1122
1244
  }
1123
1245
  // The last handled offset is at the position where the nodes were removed.
1124
1246
  offset = change.offset;
@@ -1126,7 +1248,7 @@ function _generateActionsFromChanges(oldChildrenLength, changes) {
1126
1248
  oldChildrenHandled += change.howMany;
1127
1249
  }
1128
1250
  else {
1129
- actions.push(...'a'.repeat(change.howMany).split(''));
1251
+ diff.push(...'a'.repeat(change.howMany).split(''));
1130
1252
  // The last handled offset is at the position after the changed range.
1131
1253
  offset = change.offset + change.howMany;
1132
1254
  // We changed `howMany` old nodes, update `oldChildrenHandled`.
@@ -1137,13 +1259,13 @@ function _generateActionsFromChanges(oldChildrenLength, changes) {
1137
1259
  // has not been changed / removed at the end of their parent.
1138
1260
  if (oldChildrenHandled < oldChildrenLength) {
1139
1261
  for (let i = 0; i < oldChildrenLength - oldChildrenHandled - offset; i++) {
1140
- actions.push('e');
1262
+ diff.push('e');
1141
1263
  }
1142
1264
  }
1143
- return actions;
1265
+ return diff;
1144
1266
  }
1145
1267
  /**
1146
- * Filter callback for Array.filter that filters out change entries that are in graveyard.
1268
+ * Filter callback for `Array.filter` that filters out change entries that are in graveyard.
1147
1269
  */
1148
1270
  function _changesInGraveyardFilter(entry) {
1149
1271
  const posInGy = 'position' in entry && entry.position.root.rootName == '$graveyard';
@@ -36,7 +36,7 @@ declare const Document_base: {
36
36
  * However, the document may contain multiple roots – e.g. when the editor has multiple editable areas
37
37
  * (e.g. a title and a body of a message).
38
38
  */
39
- export default class Document extends Document_base {
39
+ export default class Document extends /* #__PURE__ */ Document_base {
40
40
  /**
41
41
  * The {@link module:engine/model/model~Model model} that the document is a part of.
42
42
  */
@@ -29,7 +29,7 @@ const graveyardName = '$graveyard';
29
29
  * However, the document may contain multiple roots – e.g. when the editor has multiple editable areas
30
30
  * (e.g. a title and a body of a message).
31
31
  */
32
- export default class Document extends EmitterMixin() {
32
+ export default class Document extends /* #__PURE__ */ EmitterMixin() {
33
33
  /**
34
34
  * Creates an empty document instance with no {@link #roots} (other than
35
35
  * the {@link #graveyard graveyard root}).
@@ -39,7 +39,7 @@ declare const DocumentSelection_base: import("@ckeditor/ckeditor5-utils").Mixed<
39
39
  * If you need to represent a selection in document fragment,
40
40
  * use {@link module:engine/model/selection~Selection Selection class} instead.
41
41
  */
42
- export default class DocumentSelection extends DocumentSelection_base {
42
+ export default class DocumentSelection extends /* #__PURE__ */ DocumentSelection_base {
43
43
  /**
44
44
  * Selection used internally by that class (`DocumentSelection` is a proxy to that selection).
45
45
  */
@@ -36,7 +36,7 @@ const storePrefix = 'selection:';
36
36
  * If you need to represent a selection in document fragment,
37
37
  * use {@link module:engine/model/selection~Selection Selection class} instead.
38
38
  */
39
- export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
39
+ export default class DocumentSelection extends /* #__PURE__ */ EmitterMixin(TypeCheckable) {
40
40
  /**
41
41
  * Creates an empty live selection for given {@link module:engine/model/document~Document}.
42
42
  *
@@ -23,7 +23,7 @@ declare const LivePosition_base: import("@ckeditor/ckeditor5-utils").Mixed<typeo
23
23
  * have to be unbound.
24
24
  * Use {@link module:engine/model/liveposition~LivePosition#detach} whenever you don't need `LivePosition` anymore.
25
25
  */
26
- export default class LivePosition extends LivePosition_base {
26
+ export default class LivePosition extends /* #__PURE__ */ LivePosition_base {
27
27
  /**
28
28
  * Root of the position path.
29
29
  */