@ckeditor/ckeditor5-engine 45.2.0 → 45.2.1-alpha.1
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.
- package/dist/index.js +79 -46
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/conversion/mapper.d.ts +11 -27
- package/src/conversion/mapper.js +76 -47
- package/src/view/domconverter.js +7 -0
package/dist/index.js
CHANGED
|
@@ -9826,6 +9826,13 @@ const UNSAFE_ELEMENT_REPLACEMENT_ATTRIBUTE = 'data-ck-unsafe-element';
|
|
|
9826
9826
|
if (startsWithFiller(domParent)) {
|
|
9827
9827
|
offset += INLINE_FILLER_LENGTH;
|
|
9828
9828
|
}
|
|
9829
|
+
// In case someone uses outdated view position, but DOM text node was already changed while typing.
|
|
9830
|
+
// See: https://github.com/ckeditor/ckeditor5/issues/18648.
|
|
9831
|
+
// Note that when checking Renderer#_isSelectionInInlineFiller() this might be other element
|
|
9832
|
+
// than a text node as it is triggered before applying view changes to the DOM.
|
|
9833
|
+
if (domParent.data && offset > domParent.data.length) {
|
|
9834
|
+
offset = domParent.data.length;
|
|
9835
|
+
}
|
|
9829
9836
|
return {
|
|
9830
9837
|
parent: domParent,
|
|
9831
9838
|
offset
|
|
@@ -15626,32 +15633,92 @@ Range.prototype.is = function(type) {
|
|
|
15626
15633
|
} else {
|
|
15627
15634
|
viewOffset = viewParent.parent.getChildIndex(viewParent) + 1;
|
|
15628
15635
|
viewParent = viewParent.parent;
|
|
15636
|
+
// Cache view position after stepping out of the view element to make sure that all visited view positions are cached.
|
|
15637
|
+
// Otherwise, cache invalidation may work incorrectly.
|
|
15638
|
+
if (useCache) {
|
|
15639
|
+
this._cache.save(viewParent, viewOffset, viewContainer, traversedModelOffset);
|
|
15640
|
+
}
|
|
15629
15641
|
continue;
|
|
15630
15642
|
}
|
|
15631
15643
|
}
|
|
15632
|
-
|
|
15644
|
+
if (useCache) {
|
|
15645
|
+
lastLength = this._getModelLengthAndCache(viewNode, viewContainer, traversedModelOffset);
|
|
15646
|
+
} else {
|
|
15647
|
+
lastLength = this.getModelLength(viewNode);
|
|
15648
|
+
}
|
|
15633
15649
|
traversedModelOffset += lastLength;
|
|
15634
15650
|
viewOffset++;
|
|
15635
|
-
|
|
15636
|
-
|
|
15637
|
-
|
|
15638
|
-
|
|
15639
|
-
|
|
15640
|
-
|
|
15641
|
-
|
|
15642
|
-
|
|
15643
|
-
|
|
15651
|
+
}
|
|
15652
|
+
let viewPosition = new Position$1(viewParent, viewOffset);
|
|
15653
|
+
if (useCache) {
|
|
15654
|
+
// Make sure to hoist view position and save cache for all view positions along the way that have the same `modelOffset`.
|
|
15655
|
+
//
|
|
15656
|
+
// Consider example view:
|
|
15657
|
+
//
|
|
15658
|
+
// <p>Foo<strong><i>bar<em>xyz</em></i></strong>abc</p>
|
|
15659
|
+
//
|
|
15660
|
+
// Lets assume that we looked for model offset `9`, starting from `6` (as it was previously cached value).
|
|
15661
|
+
// In this case we will only traverse over `<em>xyz</em>` and cache view positions after "xyz" and after `</em>`.
|
|
15662
|
+
// After stepping over `<em>xyz</em>`, we will stop processing this view, as we will reach target model offset `9`.
|
|
15663
|
+
//
|
|
15664
|
+
// However, position before `</strong>` and before `abc` are also valid positions for model offset `9`.
|
|
15665
|
+
// Additionally, `Mapper` is supposed to return "hoisted" view positions, that is, we prefer positions that are closer to
|
|
15666
|
+
// the mapped `viewContainer`. If a position is nested inside attribute elements, it should be "moved up" if possible.
|
|
15667
|
+
//
|
|
15668
|
+
// As we hoist the view position, we need to make sure that all view positions valid for model offset `9` are cached.
|
|
15669
|
+
// This is necessary for cache invalidation to work correctly.
|
|
15670
|
+
//
|
|
15671
|
+
// To hoist a view position, we "go up" as long as the position is at the end of a non-mapped view element. We also cache
|
|
15672
|
+
// all necessary values. See example:
|
|
15673
|
+
//
|
|
15674
|
+
// <p>Foo<strong><i>bar<em>xyz</em>^</i></strong>abc</p>
|
|
15675
|
+
// <p>Foo<strong><i>bar<em>xyz</em></i>^</strong>abc</p>
|
|
15676
|
+
// <p>Foo<strong><i>bar<em>xyz</em></i></strong>^abc</p>
|
|
15677
|
+
//
|
|
15678
|
+
while(viewPosition.isAtEnd && viewPosition.parent !== viewContainer && viewPosition.parent.parent){
|
|
15679
|
+
const cacheViewParent = viewPosition.parent.parent;
|
|
15680
|
+
const cacheViewOffset = cacheViewParent.getChildIndex(viewPosition.parent) + 1;
|
|
15681
|
+
this._cache.save(cacheViewParent, cacheViewOffset, viewContainer, traversedModelOffset);
|
|
15682
|
+
viewPosition = new Position$1(cacheViewParent, cacheViewOffset);
|
|
15644
15683
|
}
|
|
15645
15684
|
}
|
|
15646
15685
|
if (traversedModelOffset == targetModelOffset) {
|
|
15647
15686
|
// If it equals we found the position.
|
|
15648
|
-
|
|
15687
|
+
//
|
|
15688
|
+
// Try moving view position into a text node if possible, as the editor engine prefers positions inside view text nodes.
|
|
15689
|
+
//
|
|
15690
|
+
// <p>Foo<strong><i>bar<em>xyz</em></i></strong>[]abc</p> --> <p>Foo<strong><i>bar<em>xyz</em></i></strong>{}abc</p>
|
|
15691
|
+
//
|
|
15692
|
+
return this._moveViewPositionToTextNode(viewPosition);
|
|
15649
15693
|
} else {
|
|
15650
15694
|
// If it is higher we overstepped with the last traversed view node.
|
|
15651
15695
|
// We need to "enter" it, and look for the view position / model offset inside the last visited view node.
|
|
15652
15696
|
return this._findPositionStartingFrom(new Position$1(viewNode, 0), traversedModelOffset - lastLength, targetModelOffset, viewContainer, useCache);
|
|
15653
15697
|
}
|
|
15654
15698
|
}
|
|
15699
|
+
/**
|
|
15700
|
+
* Gets the length of the view element in the model and updates cache values after each view item it visits.
|
|
15701
|
+
*
|
|
15702
|
+
* See also {@link #getModelLength}.
|
|
15703
|
+
*
|
|
15704
|
+
* @param viewNode View node.
|
|
15705
|
+
* @param viewContainer Ancestor of `viewNode` that is a mapped view element.
|
|
15706
|
+
* @param modelOffset Model offset at which the `viewNode` starts.
|
|
15707
|
+
* @returns Length of the node in the tree model.
|
|
15708
|
+
*/ _getModelLengthAndCache(viewNode, viewContainer, modelOffset) {
|
|
15709
|
+
let len = 0;
|
|
15710
|
+
if (this._viewToModelMapping.has(viewNode)) {
|
|
15711
|
+
len = 1;
|
|
15712
|
+
} else if (viewNode.is('$text')) {
|
|
15713
|
+
len = viewNode.data.length;
|
|
15714
|
+
} else if (!viewNode.is('uiElement')) {
|
|
15715
|
+
for (const child of viewNode.getChildren()){
|
|
15716
|
+
len += this._getModelLengthAndCache(child, viewContainer, modelOffset + len);
|
|
15717
|
+
}
|
|
15718
|
+
}
|
|
15719
|
+
this._cache.save(viewNode.parent, viewNode.index + 1, viewContainer, modelOffset + len);
|
|
15720
|
+
return len;
|
|
15721
|
+
}
|
|
15655
15722
|
/**
|
|
15656
15723
|
* Because we prefer positions in the text nodes over positions next to text nodes, if the view position was next to a text node,
|
|
15657
15724
|
* it moves it into the text node instead.
|
|
@@ -15864,45 +15931,11 @@ Range.prototype.is = function(type) {
|
|
|
15864
15931
|
} else {
|
|
15865
15932
|
result = this.startTracking(viewContainer);
|
|
15866
15933
|
}
|
|
15867
|
-
const viewPosition = this._hoistViewPosition(result.viewPosition);
|
|
15868
15934
|
return {
|
|
15869
15935
|
modelOffset: result.modelOffset,
|
|
15870
|
-
viewPosition
|
|
15936
|
+
viewPosition: result.viewPosition.clone()
|
|
15871
15937
|
};
|
|
15872
15938
|
}
|
|
15873
|
-
/**
|
|
15874
|
-
* Moves a view position to a preferred location.
|
|
15875
|
-
*
|
|
15876
|
-
* The view position is moved up from a non-tracked view element as long as it remains at the end of its current parent.
|
|
15877
|
-
*
|
|
15878
|
-
* See example below to understand when it is important:
|
|
15879
|
-
*
|
|
15880
|
-
* Starting state:
|
|
15881
|
-
*
|
|
15882
|
-
* ```
|
|
15883
|
-
* <p>This is <strong>some <em>heavily <u>formatted</u>^ piece of</em></strong> text.</p>
|
|
15884
|
-
* ```
|
|
15885
|
-
*
|
|
15886
|
-
* Then we remove " piece of " and invalidate some cache:
|
|
15887
|
-
*
|
|
15888
|
-
* ```
|
|
15889
|
-
* <p>This is <strong>some <em>heavily <u>formatted</u>^</em></strong> text.</p>
|
|
15890
|
-
* ```
|
|
15891
|
-
*
|
|
15892
|
-
* Now, if we ask for model offset after letter "d" in "formatted", we should get a position in " text", but we will get in `<em>`.
|
|
15893
|
-
* For this scenario, we need to hoist the position.
|
|
15894
|
-
*
|
|
15895
|
-
* ```
|
|
15896
|
-
* <p>This is <strong>some <em>heavily <u>formatted</u></em></strong>^ text.</p>
|
|
15897
|
-
* ```
|
|
15898
|
-
*/ _hoistViewPosition(viewPosition) {
|
|
15899
|
-
while(viewPosition.parent.parent && !this._cachedMapping.has(viewPosition.parent) && viewPosition.isAtEnd){
|
|
15900
|
-
const parent = viewPosition.parent.parent;
|
|
15901
|
-
const offset = parent.getChildIndex(viewPosition.parent) + 1;
|
|
15902
|
-
viewPosition = new Position$1(parent, offset);
|
|
15903
|
-
}
|
|
15904
|
-
return viewPosition;
|
|
15905
|
-
}
|
|
15906
15939
|
/**
|
|
15907
15940
|
* Starts tracking given `viewContainer`, which must be mapped to a model element or model document fragment.
|
|
15908
15941
|
*
|