@ckeditor/ckeditor5-engine 43.2.0 → 43.3.0-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.css.map +1 -1
- package/dist/index.js +149 -72
- package/dist/index.js.map +1 -1
- package/dist/model/documentfragment.d.ts +8 -1
- package/dist/model/element.d.ts +11 -1
- package/dist/model/node.d.ts +20 -17
- package/dist/model/nodelist.d.ts +23 -8
- package/dist/model/position.d.ts +9 -2
- package/package.json +2 -2
- package/src/model/document.js +2 -1
- package/src/model/documentfragment.d.ts +8 -1
- package/src/model/documentfragment.js +12 -3
- package/src/model/element.d.ts +11 -1
- package/src/model/element.js +15 -3
- package/src/model/node.d.ts +20 -17
- package/src/model/node.js +24 -30
- package/src/model/nodelist.d.ts +23 -8
- package/src/model/nodelist.js +78 -30
- package/src/model/position.d.ts +9 -2
- package/src/model/position.js +24 -5
- package/src/view/domconverter.js +3 -1
package/src/model/nodelist.js
CHANGED
|
@@ -14,7 +14,7 @@ import { CKEditorError, spliceArray } from '@ckeditor/ckeditor5-utils';
|
|
|
14
14
|
*/
|
|
15
15
|
export default class NodeList {
|
|
16
16
|
/**
|
|
17
|
-
* Creates
|
|
17
|
+
* Creates a node list.
|
|
18
18
|
*
|
|
19
19
|
* @internal
|
|
20
20
|
* @param nodes Nodes contained in this node list.
|
|
@@ -24,6 +24,14 @@ export default class NodeList {
|
|
|
24
24
|
* Nodes contained in this node list.
|
|
25
25
|
*/
|
|
26
26
|
this._nodes = [];
|
|
27
|
+
/**
|
|
28
|
+
* This array maps numbers (offsets) to node that is placed at that offset.
|
|
29
|
+
*
|
|
30
|
+
* This array is similar to `_nodes` with the difference that one node may occupy multiple consecutive items in the array.
|
|
31
|
+
*
|
|
32
|
+
* This array is needed to quickly retrieve a node that is placed at given offset.
|
|
33
|
+
*/
|
|
34
|
+
this._offsetToNode = [];
|
|
27
35
|
if (nodes) {
|
|
28
36
|
this._insertNodes(0, nodes);
|
|
29
37
|
}
|
|
@@ -46,7 +54,7 @@ export default class NodeList {
|
|
|
46
54
|
* Sum of {@link module:engine/model/node~Node#offsetSize offset sizes} of all nodes contained inside this node list.
|
|
47
55
|
*/
|
|
48
56
|
get maxOffset() {
|
|
49
|
-
return this.
|
|
57
|
+
return this._offsetToNode.length;
|
|
50
58
|
}
|
|
51
59
|
/**
|
|
52
60
|
* Gets the node at the given index. Returns `null` if incorrect index was passed.
|
|
@@ -55,32 +63,32 @@ export default class NodeList {
|
|
|
55
63
|
return this._nodes[index] || null;
|
|
56
64
|
}
|
|
57
65
|
/**
|
|
58
|
-
*
|
|
66
|
+
* Gets the node at the given offset. Returns `null` if incorrect offset was passed.
|
|
67
|
+
*/
|
|
68
|
+
getNodeAtOffset(offset) {
|
|
69
|
+
return this._offsetToNode[offset] || null;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Returns an index of the given node or `null` if given node does not have a parent.
|
|
73
|
+
*
|
|
74
|
+
* This is an alias to {@link module:engine/model/node~Node#index}.
|
|
59
75
|
*/
|
|
60
76
|
getNodeIndex(node) {
|
|
61
|
-
|
|
62
|
-
return index == -1 ? null : index;
|
|
77
|
+
return node.index;
|
|
63
78
|
}
|
|
64
79
|
/**
|
|
65
|
-
* Returns the
|
|
66
|
-
*
|
|
80
|
+
* Returns the offset at which given node is placed in its parent or `null` if given node does not have a parent.
|
|
81
|
+
*
|
|
82
|
+
* This is an alias to {@link module:engine/model/node~Node#startOffset}.
|
|
67
83
|
*/
|
|
68
84
|
getNodeStartOffset(node) {
|
|
69
|
-
|
|
70
|
-
if (index === null) {
|
|
71
|
-
return null;
|
|
72
|
-
}
|
|
73
|
-
let sum = 0;
|
|
74
|
-
for (let i = 0; i < index; i++) {
|
|
75
|
-
sum += this._nodes[i].offsetSize;
|
|
76
|
-
}
|
|
77
|
-
return sum;
|
|
85
|
+
return node.startOffset;
|
|
78
86
|
}
|
|
79
87
|
/**
|
|
80
88
|
* Converts index to offset in node list.
|
|
81
89
|
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
90
|
+
* Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `model-nodelist-index-out-of-bounds` if given index is less
|
|
91
|
+
* than `0` or more than {@link #length}.
|
|
84
92
|
*/
|
|
85
93
|
indexToOffset(index) {
|
|
86
94
|
if (index == this._nodes.length) {
|
|
@@ -100,18 +108,15 @@ export default class NodeList {
|
|
|
100
108
|
/**
|
|
101
109
|
* Converts offset in node list to index.
|
|
102
110
|
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
111
|
+
* Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `model-nodelist-offset-out-of-bounds` if given offset is less
|
|
112
|
+
* than `0` or more than {@link #maxOffset}.
|
|
105
113
|
*/
|
|
106
114
|
offsetToIndex(offset) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (offset >= totalOffset && offset < totalOffset + node.offsetSize) {
|
|
110
|
-
return this.getNodeIndex(node);
|
|
111
|
-
}
|
|
112
|
-
totalOffset += node.offsetSize;
|
|
115
|
+
if (offset == this._offsetToNode.length) {
|
|
116
|
+
return this._nodes.length;
|
|
113
117
|
}
|
|
114
|
-
|
|
118
|
+
const node = this._offsetToNode[offset];
|
|
119
|
+
if (!node) {
|
|
115
120
|
/**
|
|
116
121
|
* Given offset cannot be found in the node list.
|
|
117
122
|
*
|
|
@@ -124,7 +129,7 @@ export default class NodeList {
|
|
|
124
129
|
nodeList: this
|
|
125
130
|
});
|
|
126
131
|
}
|
|
127
|
-
return this.
|
|
132
|
+
return this.getNodeIndex(node);
|
|
128
133
|
}
|
|
129
134
|
/**
|
|
130
135
|
* Inserts given nodes at given index.
|
|
@@ -145,7 +150,18 @@ export default class NodeList {
|
|
|
145
150
|
throw new CKEditorError('model-nodelist-insertnodes-not-node', this);
|
|
146
151
|
}
|
|
147
152
|
}
|
|
148
|
-
|
|
153
|
+
const nodesArray = Array.from(nodes);
|
|
154
|
+
const offsetsArray = makeOffsetsArray(nodesArray);
|
|
155
|
+
let offset = this.indexToOffset(index);
|
|
156
|
+
// Splice nodes array and offsets array into the nodelist.
|
|
157
|
+
this._nodes = spliceArray(this._nodes, nodesArray, index, 0);
|
|
158
|
+
this._offsetToNode = spliceArray(this._offsetToNode, offsetsArray, offset, 0);
|
|
159
|
+
// Refresh indexes and offsets for nodes inside this node list. We need to do this for all inserted nodes and all nodes after them.
|
|
160
|
+
for (let i = index; i < this._nodes.length; i++) {
|
|
161
|
+
this._nodes[i]._index = i;
|
|
162
|
+
this._nodes[i]._startOffset = offset;
|
|
163
|
+
offset += this._nodes[i].offsetSize;
|
|
164
|
+
}
|
|
149
165
|
}
|
|
150
166
|
/**
|
|
151
167
|
* Removes one or more nodes starting at the given index.
|
|
@@ -156,7 +172,26 @@ export default class NodeList {
|
|
|
156
172
|
* @returns Array containing removed nodes.
|
|
157
173
|
*/
|
|
158
174
|
_removeNodes(indexStart, howMany = 1) {
|
|
159
|
-
|
|
175
|
+
if (howMany == 0) {
|
|
176
|
+
return [];
|
|
177
|
+
}
|
|
178
|
+
// Remove nodes from this nodelist.
|
|
179
|
+
let offset = this.indexToOffset(indexStart);
|
|
180
|
+
const nodes = this._nodes.splice(indexStart, howMany);
|
|
181
|
+
const lastNode = nodes[nodes.length - 1];
|
|
182
|
+
const removedOffsetSum = lastNode.startOffset + lastNode.offsetSize - offset;
|
|
183
|
+
this._offsetToNode.splice(offset, removedOffsetSum);
|
|
184
|
+
// Reset index and start offset properties for the removed nodes -- they do not have a parent anymore.
|
|
185
|
+
for (const node of nodes) {
|
|
186
|
+
node._index = null;
|
|
187
|
+
node._startOffset = null;
|
|
188
|
+
}
|
|
189
|
+
for (let i = indexStart; i < this._nodes.length; i++) {
|
|
190
|
+
this._nodes[i]._index = i;
|
|
191
|
+
this._nodes[i]._startOffset = offset;
|
|
192
|
+
offset += this._nodes[i].offsetSize;
|
|
193
|
+
}
|
|
194
|
+
return nodes;
|
|
160
195
|
}
|
|
161
196
|
/**
|
|
162
197
|
* Converts `NodeList` instance to an array containing nodes that were inserted in the node list. Nodes
|
|
@@ -168,3 +203,16 @@ export default class NodeList {
|
|
|
168
203
|
return this._nodes.map(node => node.toJSON());
|
|
169
204
|
}
|
|
170
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* Creates an array of nodes in the format as in {@link module:engine/model/nodelist~NodeList#_offsetToNode}, i.e. one node will
|
|
208
|
+
* occupy multiple items if its offset size is greater than one.
|
|
209
|
+
*/
|
|
210
|
+
function makeOffsetsArray(nodes) {
|
|
211
|
+
const offsets = [];
|
|
212
|
+
for (const node of nodes) {
|
|
213
|
+
const start = offsets.length;
|
|
214
|
+
offsets.length += node.offsetSize;
|
|
215
|
+
offsets.fill(node, start);
|
|
216
|
+
}
|
|
217
|
+
return offsets;
|
|
218
|
+
}
|
package/src/model/position.d.ts
CHANGED
|
@@ -124,11 +124,11 @@ export default class Position extends TypeCheckable {
|
|
|
124
124
|
*/
|
|
125
125
|
get textNode(): Text | null;
|
|
126
126
|
/**
|
|
127
|
-
* Node directly after this position
|
|
127
|
+
* Node directly after this position. Returns `null` if this position is at the end of its parent, or if it is in a text node.
|
|
128
128
|
*/
|
|
129
129
|
get nodeAfter(): Node | null;
|
|
130
130
|
/**
|
|
131
|
-
* Node directly before this position
|
|
131
|
+
* Node directly before this position. Returns `null` if this position is at the start of its parent, or if it is in a text node.
|
|
132
132
|
*/
|
|
133
133
|
get nodeBefore(): Node | null;
|
|
134
134
|
/**
|
|
@@ -139,6 +139,10 @@ export default class Position extends TypeCheckable {
|
|
|
139
139
|
* Is `true` if position is at the end of its {@link module:engine/model/position~Position#parent parent}, `false` otherwise.
|
|
140
140
|
*/
|
|
141
141
|
get isAtEnd(): boolean;
|
|
142
|
+
/**
|
|
143
|
+
* Checks whether the position is valid in current model tree, that is whether it points to an existing place in the model.
|
|
144
|
+
*/
|
|
145
|
+
isValid(): boolean;
|
|
142
146
|
/**
|
|
143
147
|
* Checks whether this position is before or after given position.
|
|
144
148
|
*
|
|
@@ -496,6 +500,7 @@ export type PositionStickiness = 'toNone' | 'toNext' | 'toPrevious';
|
|
|
496
500
|
* * {@link module:engine/model/position~getNodeAfterPosition}
|
|
497
501
|
* * {@link module:engine/model/position~getNodeBeforePosition}
|
|
498
502
|
*
|
|
503
|
+
* @param position
|
|
499
504
|
* @param positionParent The parent of the given position.
|
|
500
505
|
*/
|
|
501
506
|
export declare function getTextNodeAtPosition(position: Position, positionParent: Element | DocumentFragment): Text | null;
|
|
@@ -518,6 +523,7 @@ export declare function getTextNodeAtPosition(position: Position, positionParent
|
|
|
518
523
|
* * {@link module:engine/model/position~getTextNodeAtPosition}
|
|
519
524
|
* * {@link module:engine/model/position~getNodeBeforePosition}
|
|
520
525
|
*
|
|
526
|
+
* @param position Position to check.
|
|
521
527
|
* @param positionParent The parent of the given position.
|
|
522
528
|
* @param textNode Text node at the given position.
|
|
523
529
|
*/
|
|
@@ -532,6 +538,7 @@ export declare function getNodeAfterPosition(position: Position, positionParent:
|
|
|
532
538
|
* * {@link module:engine/model/position~getTextNodeAtPosition}
|
|
533
539
|
* * {@link module:engine/model/position~getNodeAfterPosition}
|
|
534
540
|
*
|
|
541
|
+
* @param position Position to check.
|
|
535
542
|
* @param positionParent The parent of the given position.
|
|
536
543
|
* @param textNode Text node at the given position.
|
|
537
544
|
*/
|
package/src/model/position.js
CHANGED
|
@@ -100,7 +100,7 @@ export default class Position extends TypeCheckable {
|
|
|
100
100
|
get parent() {
|
|
101
101
|
let parent = this.root;
|
|
102
102
|
for (let i = 0; i < this.path.length - 1; i++) {
|
|
103
|
-
parent = parent.
|
|
103
|
+
parent = parent.getChildAtOffset(this.path[i]);
|
|
104
104
|
if (!parent) {
|
|
105
105
|
/**
|
|
106
106
|
* The position's path is incorrect. This means that a position does not point to
|
|
@@ -141,7 +141,7 @@ export default class Position extends TypeCheckable {
|
|
|
141
141
|
return getTextNodeAtPosition(this, this.parent);
|
|
142
142
|
}
|
|
143
143
|
/**
|
|
144
|
-
* Node directly after this position
|
|
144
|
+
* Node directly after this position. Returns `null` if this position is at the end of its parent, or if it is in a text node.
|
|
145
145
|
*/
|
|
146
146
|
get nodeAfter() {
|
|
147
147
|
// Cache the parent and reuse for performance reasons. See #6579 and #6582.
|
|
@@ -149,7 +149,7 @@ export default class Position extends TypeCheckable {
|
|
|
149
149
|
return getNodeAfterPosition(this, parent, getTextNodeAtPosition(this, parent));
|
|
150
150
|
}
|
|
151
151
|
/**
|
|
152
|
-
* Node directly before this position
|
|
152
|
+
* Node directly before this position. Returns `null` if this position is at the start of its parent, or if it is in a text node.
|
|
153
153
|
*/
|
|
154
154
|
get nodeBefore() {
|
|
155
155
|
// Cache the parent and reuse for performance reasons. See #6579 and #6582.
|
|
@@ -168,6 +168,22 @@ export default class Position extends TypeCheckable {
|
|
|
168
168
|
get isAtEnd() {
|
|
169
169
|
return this.offset == this.parent.maxOffset;
|
|
170
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Checks whether the position is valid in current model tree, that is whether it points to an existing place in the model.
|
|
173
|
+
*/
|
|
174
|
+
isValid() {
|
|
175
|
+
if (this.offset < 0) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
let parent = this.root;
|
|
179
|
+
for (let i = 0; i < this.path.length - 1; i++) {
|
|
180
|
+
parent = parent.getChildAtOffset(this.path[i]);
|
|
181
|
+
if (!parent) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return this.offset <= parent.maxOffset;
|
|
186
|
+
}
|
|
171
187
|
/**
|
|
172
188
|
* Checks whether this position is before or after given position.
|
|
173
189
|
*
|
|
@@ -841,10 +857,11 @@ Position.prototype.is = function (type) {
|
|
|
841
857
|
* * {@link module:engine/model/position~getNodeAfterPosition}
|
|
842
858
|
* * {@link module:engine/model/position~getNodeBeforePosition}
|
|
843
859
|
*
|
|
860
|
+
* @param position
|
|
844
861
|
* @param positionParent The parent of the given position.
|
|
845
862
|
*/
|
|
846
863
|
export function getTextNodeAtPosition(position, positionParent) {
|
|
847
|
-
const node = positionParent.
|
|
864
|
+
const node = positionParent.getChildAtOffset(position.offset);
|
|
848
865
|
if (node && node.is('$text') && node.startOffset < position.offset) {
|
|
849
866
|
return node;
|
|
850
867
|
}
|
|
@@ -869,6 +886,7 @@ export function getTextNodeAtPosition(position, positionParent) {
|
|
|
869
886
|
* * {@link module:engine/model/position~getTextNodeAtPosition}
|
|
870
887
|
* * {@link module:engine/model/position~getNodeBeforePosition}
|
|
871
888
|
*
|
|
889
|
+
* @param position Position to check.
|
|
872
890
|
* @param positionParent The parent of the given position.
|
|
873
891
|
* @param textNode Text node at the given position.
|
|
874
892
|
*/
|
|
@@ -876,7 +894,7 @@ export function getNodeAfterPosition(position, positionParent, textNode) {
|
|
|
876
894
|
if (textNode !== null) {
|
|
877
895
|
return null;
|
|
878
896
|
}
|
|
879
|
-
return positionParent.
|
|
897
|
+
return positionParent.getChildAtOffset(position.offset);
|
|
880
898
|
}
|
|
881
899
|
/**
|
|
882
900
|
* Returns the node before the given position.
|
|
@@ -888,6 +906,7 @@ export function getNodeAfterPosition(position, positionParent, textNode) {
|
|
|
888
906
|
* * {@link module:engine/model/position~getTextNodeAtPosition}
|
|
889
907
|
* * {@link module:engine/model/position~getNodeAfterPosition}
|
|
890
908
|
*
|
|
909
|
+
* @param position Position to check.
|
|
891
910
|
* @param positionParent The parent of the given position.
|
|
892
911
|
* @param textNode Text node at the given position.
|
|
893
912
|
*/
|
package/src/view/domconverter.js
CHANGED
|
@@ -77,7 +77,7 @@ export default class DomConverter {
|
|
|
77
77
|
this.document = document;
|
|
78
78
|
this.renderingMode = renderingMode;
|
|
79
79
|
this.blockFillerMode = blockFillerMode || (renderingMode === 'editing' ? 'br' : 'nbsp');
|
|
80
|
-
this.preElements = ['pre'];
|
|
80
|
+
this.preElements = ['pre', 'textarea'];
|
|
81
81
|
this.blockElements = [
|
|
82
82
|
'address', 'article', 'aside', 'blockquote', 'caption', 'center', 'dd', 'details', 'dir', 'div',
|
|
83
83
|
'dl', 'dt', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header',
|
|
@@ -1097,6 +1097,8 @@ export default class DomConverter {
|
|
|
1097
1097
|
// for inline objects can verify if the element is empty.
|
|
1098
1098
|
if (this._isInlineObjectElement(viewElement)) {
|
|
1099
1099
|
inlineNodes.push(viewElement);
|
|
1100
|
+
// Inline object content should be handled as a flow-root.
|
|
1101
|
+
this._processDomInlineNodes(null, nestedInlineNodes, options);
|
|
1100
1102
|
}
|
|
1101
1103
|
else {
|
|
1102
1104
|
// It's an inline element that is not an object (like <b>, <i>) or a block element.
|