@ckeditor/ckeditor5-engine 35.3.2 → 36.0.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.
- package/LICENSE.md +1 -1
- package/package.json +22 -22
- package/src/controller/datacontroller.js +10 -8
- package/src/controller/editingcontroller.js +3 -4
- package/src/conversion/conversion.js +2 -3
- package/src/conversion/conversionhelpers.js +1 -1
- package/src/conversion/downcastdispatcher.js +3 -3
- package/src/conversion/downcasthelpers.js +2 -3
- package/src/conversion/mapper.js +3 -4
- package/src/conversion/modelconsumable.js +2 -2
- package/src/conversion/upcastdispatcher.js +3 -4
- package/src/conversion/upcasthelpers.js +10 -3
- package/src/conversion/viewconsumable.js +2 -2
- package/src/dataprocessor/basichtmlwriter.js +1 -1
- package/src/dataprocessor/dataprocessor.js +1 -1
- package/src/dataprocessor/htmldataprocessor.js +3 -3
- package/src/dataprocessor/htmlwriter.js +1 -1
- package/src/dataprocessor/xmldataprocessor.js +6 -2
- package/src/dev-utils/model.js +3 -3
- package/src/dev-utils/operationreplayer.js +1 -1
- package/src/dev-utils/utils.js +1 -1
- package/src/dev-utils/view.js +1 -1
- package/src/index.js +13 -3
- package/src/model/batch.js +10 -47
- package/src/model/differ.js +81 -174
- package/src/model/document.js +38 -97
- package/src/model/documentfragment.js +44 -97
- package/src/model/documentselection.js +152 -251
- package/src/model/element.js +48 -101
- package/src/model/history.js +15 -46
- package/src/model/item.js +1 -1
- package/src/model/liveposition.js +11 -39
- package/src/model/liverange.js +14 -38
- package/src/model/markercollection.js +42 -115
- package/src/model/model.js +214 -292
- package/src/model/node.js +36 -128
- package/src/model/nodelist.js +12 -41
- package/src/model/operation/attributeoperation.js +14 -45
- package/src/model/operation/detachoperation.js +4 -17
- package/src/model/operation/insertoperation.js +7 -35
- package/src/model/operation/markeroperation.js +9 -48
- package/src/model/operation/mergeoperation.js +9 -42
- package/src/model/operation/moveoperation.js +15 -39
- package/src/model/operation/nooperation.js +1 -7
- package/src/model/operation/operation.js +5 -63
- package/src/model/operation/operationfactory.js +3 -6
- package/src/model/operation/renameoperation.js +9 -29
- package/src/model/operation/rootattributeoperation.js +19 -48
- package/src/model/operation/splitoperation.js +10 -48
- package/src/model/operation/transform.js +110 -151
- package/src/model/operation/utils.js +37 -52
- package/src/model/position.js +118 -230
- package/src/model/range.js +146 -202
- package/src/model/rootelement.js +8 -47
- package/src/model/schema.js +245 -282
- package/src/model/selection.js +135 -196
- package/src/model/text.js +10 -37
- package/src/model/textproxy.js +16 -70
- package/src/model/treewalker.js +11 -102
- package/src/model/typecheckable.js +1 -1
- package/src/model/utils/autoparagraphing.js +11 -12
- package/src/model/utils/deletecontent.js +93 -62
- package/src/model/utils/findoptimalinsertionrange.js +25 -25
- package/src/model/utils/getselectedcontent.js +3 -6
- package/src/model/utils/insertcontent.js +37 -130
- package/src/model/utils/insertobject.js +40 -40
- package/src/model/utils/modifyselection.js +24 -34
- package/src/model/utils/selection-post-fixer.js +53 -59
- package/src/model/writer.js +209 -316
- package/src/view/attributeelement.js +2 -2
- package/src/view/containerelement.js +1 -1
- package/src/view/datatransfer.js +1 -1
- package/src/view/document.js +3 -5
- package/src/view/documentfragment.js +50 -4
- package/src/view/documentselection.js +2 -3
- package/src/view/domconverter.js +4 -8
- package/src/view/downcastwriter.js +2 -3
- package/src/view/editableelement.js +2 -3
- package/src/view/element.js +6 -8
- package/src/view/elementdefinition.js +1 -1
- package/src/view/emptyelement.js +2 -2
- package/src/view/filler.js +2 -3
- package/src/view/item.js +1 -1
- package/src/view/matcher.js +2 -2
- package/src/view/node.js +2 -5
- package/src/view/observer/arrowkeysobserver.js +1 -1
- package/src/view/observer/bubblingemittermixin.js +3 -6
- package/src/view/observer/bubblingeventinfo.js +2 -2
- package/src/view/observer/clickobserver.js +1 -1
- package/src/view/observer/compositionobserver.js +1 -1
- package/src/view/observer/domeventdata.js +1 -1
- package/src/view/observer/domeventobserver.js +1 -1
- package/src/view/observer/fakeselectionobserver.js +2 -2
- package/src/view/observer/focusobserver.js +25 -3
- package/src/view/observer/inputobserver.js +2 -2
- package/src/view/observer/keyobserver.js +2 -2
- package/src/view/observer/mouseobserver.js +1 -1
- package/src/view/observer/mutationobserver.js +1 -1
- package/src/view/observer/observer.js +3 -3
- package/src/view/observer/selectionobserver.js +21 -3
- package/src/view/observer/tabobserver.js +2 -2
- package/src/view/placeholder.js +1 -1
- package/src/view/position.js +2 -3
- package/src/view/range.js +1 -1
- package/src/view/rawelement.js +2 -2
- package/src/view/renderer.js +3 -12
- package/src/view/rooteditableelement.js +1 -1
- package/src/view/selection.js +2 -6
- package/src/view/styles/background.js +1 -1
- package/src/view/styles/border.js +1 -1
- package/src/view/styles/margin.js +1 -1
- package/src/view/styles/padding.js +1 -1
- package/src/view/styles/utils.js +1 -1
- package/src/view/stylesmap.js +1 -1
- package/src/view/text.js +1 -1
- package/src/view/textproxy.js +2 -2
- package/src/view/treewalker.js +2 -2
- package/src/view/typecheckable.js +1 -1
- package/src/view/uielement.js +2 -3
- package/src/view/upcastwriter.js +1 -1
- package/src/view/view.js +7 -7
- package/theme/placeholder.css +1 -1
- package/theme/renderer.css +1 -1
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Copyright (c) 2003-
|
|
2
|
+
* @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
|
-
/* eslint-disable new-cap */
|
|
6
5
|
/**
|
|
7
6
|
* @module engine/model/documentselection
|
|
8
7
|
*/
|
|
@@ -11,11 +10,7 @@ import LiveRange from './liverange';
|
|
|
11
10
|
import Selection from './selection';
|
|
12
11
|
import Text from './text';
|
|
13
12
|
import TextProxy from './textproxy';
|
|
14
|
-
import CKEditorError from '@ckeditor/ckeditor5-utils
|
|
15
|
-
import Collection from '@ckeditor/ckeditor5-utils/src/collection';
|
|
16
|
-
import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
|
|
17
|
-
import toMap from '@ckeditor/ckeditor5-utils/src/tomap';
|
|
18
|
-
import uid from '@ckeditor/ckeditor5-utils/src/uid';
|
|
13
|
+
import { CKEditorError, Collection, EmitterMixin, toMap, uid } from '@ckeditor/ckeditor5-utils';
|
|
19
14
|
const storePrefix = 'selection:';
|
|
20
15
|
/**
|
|
21
16
|
* `DocumentSelection` is a special selection which is used as the
|
|
@@ -40,33 +35,23 @@ const storePrefix = 'selection:';
|
|
|
40
35
|
* that are inside {@link module:engine/model/documentfragment~DocumentFragment document fragment}.
|
|
41
36
|
* If you need to represent a selection in document fragment,
|
|
42
37
|
* use {@link module:engine/model/selection~Selection Selection class} instead.
|
|
43
|
-
*
|
|
44
|
-
* @mixes module:utils/emittermixin~EmitterMixin
|
|
45
38
|
*/
|
|
46
39
|
export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
47
40
|
/**
|
|
48
41
|
* Creates an empty live selection for given {@link module:engine/model/document~Document}.
|
|
49
42
|
*
|
|
50
|
-
* @param
|
|
43
|
+
* @param doc Document which owns this selection.
|
|
51
44
|
*/
|
|
52
45
|
constructor(doc) {
|
|
53
46
|
super();
|
|
54
|
-
/**
|
|
55
|
-
* Selection used internally by that class (`DocumentSelection` is a proxy to that selection).
|
|
56
|
-
*
|
|
57
|
-
* @protected
|
|
58
|
-
*/
|
|
59
47
|
this._selection = new LiveSelection(doc);
|
|
60
48
|
this._selection.delegate('change:range').to(this);
|
|
61
49
|
this._selection.delegate('change:attribute').to(this);
|
|
62
50
|
this._selection.delegate('change:marker').to(this);
|
|
63
51
|
}
|
|
64
52
|
/**
|
|
65
|
-
*
|
|
53
|
+
* Describes whether the selection is collapsed. Selection is collapsed when there is exactly one range which is
|
|
66
54
|
* collapsed.
|
|
67
|
-
*
|
|
68
|
-
* @readonly
|
|
69
|
-
* @type {Boolean}
|
|
70
55
|
*/
|
|
71
56
|
get isCollapsed() {
|
|
72
57
|
return this._selection.isCollapsed;
|
|
@@ -80,8 +65,6 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
80
65
|
* Is set to `null` if there are no ranges in selection.
|
|
81
66
|
*
|
|
82
67
|
* @see #focus
|
|
83
|
-
* @readonly
|
|
84
|
-
* @type {module:engine/model/position~Position|null}
|
|
85
68
|
*/
|
|
86
69
|
get anchor() {
|
|
87
70
|
return this._selection.anchor;
|
|
@@ -92,17 +75,12 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
92
75
|
* Is set to `null` if there are no ranges in selection.
|
|
93
76
|
*
|
|
94
77
|
* @see #anchor
|
|
95
|
-
* @readonly
|
|
96
|
-
* @type {module:engine/model/position~Position|null}
|
|
97
78
|
*/
|
|
98
79
|
get focus() {
|
|
99
80
|
return this._selection.focus;
|
|
100
81
|
}
|
|
101
82
|
/**
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
* @readonly
|
|
105
|
-
* @type {Number}
|
|
83
|
+
* Number of ranges in selection.
|
|
106
84
|
*/
|
|
107
85
|
get rangeCount() {
|
|
108
86
|
return this._selection.rangeCount;
|
|
@@ -110,9 +88,6 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
110
88
|
/**
|
|
111
89
|
* Describes whether `Documentselection` has own range(s) set, or if it is defaulted to
|
|
112
90
|
* {@link module:engine/model/document~Document#_getDefaultRange document's default range}.
|
|
113
|
-
*
|
|
114
|
-
* @readonly
|
|
115
|
-
* @type {Boolean}
|
|
116
91
|
*/
|
|
117
92
|
get hasOwnRange() {
|
|
118
93
|
return this._selection.hasOwnRange;
|
|
@@ -131,9 +106,6 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
131
106
|
* Describes whether the gravity is overridden (using {@link module:engine/model/writer~Writer#overrideSelectionGravity}) or not.
|
|
132
107
|
*
|
|
133
108
|
* Note that the gravity remains overridden as long as will not be restored the same number of times as it was overridden.
|
|
134
|
-
*
|
|
135
|
-
* @readonly
|
|
136
|
-
* @returns {Boolean}
|
|
137
109
|
*/
|
|
138
110
|
get isGravityOverridden() {
|
|
139
111
|
return this._selection.isGravityOverridden;
|
|
@@ -143,9 +115,6 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
143
115
|
* Marker is a selection marker when selection range is inside the marker range.
|
|
144
116
|
*
|
|
145
117
|
* **Note**: Only markers from {@link ~DocumentSelection#observeMarkers observed markers groups} are collected.
|
|
146
|
-
*
|
|
147
|
-
* @readonly
|
|
148
|
-
* @type {module:utils/collection~Collection}
|
|
149
118
|
*/
|
|
150
119
|
get markers() {
|
|
151
120
|
return this._selection.markers;
|
|
@@ -154,15 +123,12 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
154
123
|
* Used for the compatibility with the {@link module:engine/model/selection~Selection#isEqual} method.
|
|
155
124
|
*
|
|
156
125
|
* @internal
|
|
157
|
-
* @protected
|
|
158
126
|
*/
|
|
159
127
|
get _ranges() {
|
|
160
128
|
return this._selection._ranges;
|
|
161
129
|
}
|
|
162
130
|
/**
|
|
163
131
|
* Returns an iterable that iterates over copies of selection ranges.
|
|
164
|
-
*
|
|
165
|
-
* @returns {Iterable.<module:engine/model/range~Range>}
|
|
166
132
|
*/
|
|
167
133
|
getRanges() {
|
|
168
134
|
return this._selection.getRanges();
|
|
@@ -173,8 +139,6 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
173
139
|
* any other position in the selection.
|
|
174
140
|
*
|
|
175
141
|
* Returns `null` if there are no ranges in selection.
|
|
176
|
-
*
|
|
177
|
-
* @returns {module:engine/model/position~Position|null}
|
|
178
142
|
*/
|
|
179
143
|
getFirstPosition() {
|
|
180
144
|
return this._selection.getFirstPosition();
|
|
@@ -185,8 +149,6 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
185
149
|
* any other position in the selection.
|
|
186
150
|
*
|
|
187
151
|
* Returns `null` if there are no ranges in selection.
|
|
188
|
-
*
|
|
189
|
-
* @returns {module:engine/model/position~Position|null}
|
|
190
152
|
*/
|
|
191
153
|
getLastPosition() {
|
|
192
154
|
return this._selection.getLastPosition();
|
|
@@ -198,8 +160,6 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
198
160
|
* (not to confuse with the first range added to the selection).
|
|
199
161
|
*
|
|
200
162
|
* Returns `null` if there are no ranges in selection.
|
|
201
|
-
*
|
|
202
|
-
* @returns {module:engine/model/range~Range|null}
|
|
203
163
|
*/
|
|
204
164
|
getFirstRange() {
|
|
205
165
|
return this._selection.getFirstRange();
|
|
@@ -211,8 +171,6 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
211
171
|
* recently added to the selection).
|
|
212
172
|
*
|
|
213
173
|
* Returns `null` if there are no ranges in selection.
|
|
214
|
-
*
|
|
215
|
-
* @returns {module:engine/model/range~Range|null}
|
|
216
174
|
*/
|
|
217
175
|
getLastRange() {
|
|
218
176
|
return this._selection.getLastRange();
|
|
@@ -227,40 +185,48 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
227
185
|
*
|
|
228
186
|
* In this case the function will return exactly all 3 paragraphs (note: `<blockQuote>` is not a block itself):
|
|
229
187
|
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
188
|
+
* ```
|
|
189
|
+
* <paragraph>[a</paragraph>
|
|
190
|
+
* <blockQuote>
|
|
191
|
+
* <paragraph>b</paragraph>
|
|
192
|
+
* </blockQuote>
|
|
193
|
+
* <paragraph>c]d</paragraph>
|
|
194
|
+
* ```
|
|
235
195
|
*
|
|
236
196
|
* In this case the paragraph will also be returned, despite the collapsed selection:
|
|
237
197
|
*
|
|
238
|
-
*
|
|
198
|
+
* ```
|
|
199
|
+
* <paragraph>[]a</paragraph>
|
|
200
|
+
* ```
|
|
239
201
|
*
|
|
240
202
|
* In such a scenario, however, only blocks A, B & E will be returned as blocks C & D are nested in block B:
|
|
241
203
|
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
204
|
+
* ```
|
|
205
|
+
* [<blockA></blockA>
|
|
206
|
+
* <blockB>
|
|
207
|
+
* <blockC></blockC>
|
|
208
|
+
* <blockD></blockD>
|
|
209
|
+
* </blockB>
|
|
210
|
+
* <blockE></blockE>]
|
|
211
|
+
* ```
|
|
248
212
|
*
|
|
249
213
|
* If the selection is inside a block all the inner blocks (A & B) are returned:
|
|
250
214
|
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
215
|
+
* ```
|
|
216
|
+
* <block>
|
|
217
|
+
* <blockA>[a</blockA>
|
|
218
|
+
* <blockB>b]</blockB>
|
|
219
|
+
* </block>
|
|
220
|
+
* ```
|
|
255
221
|
*
|
|
256
222
|
* **Special case**: If a selection ends at the beginning of a block, that block is not returned as from user perspective
|
|
257
223
|
* this block wasn't selected. See [#984](https://github.com/ckeditor/ckeditor5-engine/issues/984) for more details.
|
|
258
224
|
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
225
|
+
* ```
|
|
226
|
+
* <paragraph>[a</paragraph>
|
|
227
|
+
* <paragraph>b</paragraph>
|
|
228
|
+
* <paragraph>]c</paragraph> // this block will not be returned
|
|
229
|
+
* ```
|
|
264
230
|
*/
|
|
265
231
|
getSelectedBlocks() {
|
|
266
232
|
return this._selection.getSelectedBlocks();
|
|
@@ -269,8 +235,6 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
269
235
|
* Returns the selected element. {@link module:engine/model/element~Element Element} is considered as selected if there is only
|
|
270
236
|
* one range in the selection, and that range contains exactly one element.
|
|
271
237
|
* Returns `null` if there is no selected element.
|
|
272
|
-
*
|
|
273
|
-
* @returns {module:engine/model/element~Element|null}
|
|
274
238
|
*/
|
|
275
239
|
getSelectedElement() {
|
|
276
240
|
return this._selection.getSelectedElement();
|
|
@@ -282,9 +246,6 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
282
246
|
*
|
|
283
247
|
* By default, this method will check whether the entire content of the selection's current root is selected.
|
|
284
248
|
* Useful to check if e.g. the user has just pressed <kbd>Ctrl</kbd> + <kbd>A</kbd>.
|
|
285
|
-
*
|
|
286
|
-
* @param {module:engine/model/element~Element} [element=this.anchor.root]
|
|
287
|
-
* @returns {Boolean}
|
|
288
249
|
*/
|
|
289
250
|
containsEntireContent(element) {
|
|
290
251
|
return this._selection.containsEntireContent(element);
|
|
@@ -297,8 +258,6 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
297
258
|
}
|
|
298
259
|
/**
|
|
299
260
|
* Returns iterable that iterates over this selection's attribute keys.
|
|
300
|
-
*
|
|
301
|
-
* @returns {Iterable.<String>}
|
|
302
261
|
*/
|
|
303
262
|
getAttributeKeys() {
|
|
304
263
|
return this._selection.getAttributeKeys();
|
|
@@ -308,8 +267,6 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
308
267
|
*
|
|
309
268
|
* Attributes are returned as arrays containing two items. First one is attribute key and second is attribute value.
|
|
310
269
|
* This format is accepted by native `Map` object and also can be passed in `Node` constructor.
|
|
311
|
-
*
|
|
312
|
-
* @returns {Iterable.<*>}
|
|
313
270
|
*/
|
|
314
271
|
getAttributes() {
|
|
315
272
|
return this._selection.getAttributes();
|
|
@@ -317,8 +274,8 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
317
274
|
/**
|
|
318
275
|
* Gets an attribute value for given key or `undefined` if that attribute is not set on the selection.
|
|
319
276
|
*
|
|
320
|
-
* @param
|
|
321
|
-
* @returns
|
|
277
|
+
* @param key Key of attribute to look for.
|
|
278
|
+
* @returns Attribute value or `undefined`.
|
|
322
279
|
*/
|
|
323
280
|
getAttribute(key) {
|
|
324
281
|
return this._selection.getAttribute(key);
|
|
@@ -326,8 +283,8 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
326
283
|
/**
|
|
327
284
|
* Checks if the selection has an attribute for given key.
|
|
328
285
|
*
|
|
329
|
-
* @param
|
|
330
|
-
* @returns
|
|
286
|
+
* @param key Key of attribute to check.
|
|
287
|
+
* @returns `true` if attribute with given key is set on selection, `false` otherwise.
|
|
331
288
|
*/
|
|
332
289
|
hasAttribute(key) {
|
|
333
290
|
return this._selection.hasAttribute(key);
|
|
@@ -345,7 +302,7 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
345
302
|
*
|
|
346
303
|
* See also {@link module:engine/model/markercollection~MarkerCollection#getMarkersGroup `MarkerCollection#getMarkersGroup()`}.
|
|
347
304
|
*
|
|
348
|
-
* @param
|
|
305
|
+
* @param prefixOrName The marker group prefix or marker name.
|
|
349
306
|
*/
|
|
350
307
|
observeMarkers(prefixOrName) {
|
|
351
308
|
this._selection.observeMarkers(prefixOrName);
|
|
@@ -359,9 +316,7 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
359
316
|
*
|
|
360
317
|
* @see module:engine/model/writer~Writer#setSelectionFocus
|
|
361
318
|
* @internal
|
|
362
|
-
* @
|
|
363
|
-
* @param {module:engine/model/item~Item|module:engine/model/position~Position} itemOrPosition
|
|
364
|
-
* @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when
|
|
319
|
+
* @param offset Offset or one of the flags. Used only when
|
|
365
320
|
* first parameter is a {@link module:engine/model/item~Item model item}.
|
|
366
321
|
*/
|
|
367
322
|
_setFocus(itemOrPosition, offset) {
|
|
@@ -374,11 +329,6 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
374
329
|
*
|
|
375
330
|
* @see module:engine/model/writer~Writer#setSelection
|
|
376
331
|
* @internal
|
|
377
|
-
* @protected
|
|
378
|
-
* @param {module:engine/model/selection~Selectable} selectable
|
|
379
|
-
* @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Sets place or offset of the selection.
|
|
380
|
-
* @param {Object} [options]
|
|
381
|
-
* @param {Boolean} [options.backward] Sets this selection instance to be backward.
|
|
382
332
|
*/
|
|
383
333
|
_setTo(...args) {
|
|
384
334
|
this._selection.setTo(...args);
|
|
@@ -389,9 +339,8 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
389
339
|
*
|
|
390
340
|
* @see module:engine/model/writer~Writer#setSelectionAttribute
|
|
391
341
|
* @internal
|
|
392
|
-
* @
|
|
393
|
-
* @param
|
|
394
|
-
* @param {*} value Attribute value.
|
|
342
|
+
* @param key Key of the attribute to set.
|
|
343
|
+
* @param value Attribute value.
|
|
395
344
|
*/
|
|
396
345
|
_setAttribute(key, value) {
|
|
397
346
|
this._selection.setAttribute(key, value);
|
|
@@ -404,8 +353,7 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
404
353
|
*
|
|
405
354
|
* @see module:engine/model/writer~Writer#removeSelectionAttribute
|
|
406
355
|
* @internal
|
|
407
|
-
* @
|
|
408
|
-
* @param {String} key Key of the attribute to remove.
|
|
356
|
+
* @param key Key of the attribute to remove.
|
|
409
357
|
*/
|
|
410
358
|
_removeAttribute(key) {
|
|
411
359
|
this._selection.removeAttribute(key);
|
|
@@ -413,8 +361,7 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
413
361
|
/**
|
|
414
362
|
* Returns an iterable that iterates through all selection attributes stored in current selection's parent.
|
|
415
363
|
*
|
|
416
|
-
* @
|
|
417
|
-
* @returns {Iterable.<*>}
|
|
364
|
+
* @internal
|
|
418
365
|
*/
|
|
419
366
|
_getStoredAttributes() {
|
|
420
367
|
return this._selection.getStoredAttributes();
|
|
@@ -431,8 +378,7 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
431
378
|
*
|
|
432
379
|
* @see module:engine/model/writer~Writer#overrideSelectionGravity
|
|
433
380
|
* @internal
|
|
434
|
-
* @
|
|
435
|
-
* @returns {String} The unique id which allows restoring the gravity.
|
|
381
|
+
* @returns The unique id which allows restoring the gravity.
|
|
436
382
|
*/
|
|
437
383
|
_overrideGravity() {
|
|
438
384
|
return this._selection.overrideGravity();
|
|
@@ -446,8 +392,7 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
446
392
|
*
|
|
447
393
|
* @see module:engine/model/writer~Writer#restoreSelectionGravity
|
|
448
394
|
* @internal
|
|
449
|
-
* @
|
|
450
|
-
* @param {String} uid The unique id returned by {@link #_overrideGravity}.
|
|
395
|
+
* @param uid The unique id returned by {@link #_overrideGravity}.
|
|
451
396
|
*/
|
|
452
397
|
_restoreGravity(uid) {
|
|
453
398
|
this._selection.restoreGravity(uid);
|
|
@@ -456,9 +401,8 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
456
401
|
* Generates and returns an attribute key for selection attributes store, basing on original attribute key.
|
|
457
402
|
*
|
|
458
403
|
* @internal
|
|
459
|
-
* @
|
|
460
|
-
* @
|
|
461
|
-
* @returns {String} Converted attribute key, applicable for selection store.
|
|
404
|
+
* @param key Attribute key to convert.
|
|
405
|
+
* @returns Converted attribute key, applicable for selection store.
|
|
462
406
|
*/
|
|
463
407
|
static _getStoreAttributeKey(key) {
|
|
464
408
|
return storePrefix + key;
|
|
@@ -466,98 +410,74 @@ export default class DocumentSelection extends EmitterMixin(TypeCheckable) {
|
|
|
466
410
|
/**
|
|
467
411
|
* Checks whether the given attribute key is an attribute stored on an element.
|
|
468
412
|
*
|
|
469
|
-
* @
|
|
470
|
-
* @param {String} key
|
|
471
|
-
* @returns {Boolean}
|
|
413
|
+
* @internal
|
|
472
414
|
*/
|
|
473
415
|
static _isStoreAttributeKey(key) {
|
|
474
416
|
return key.startsWith(storePrefix);
|
|
475
417
|
}
|
|
476
418
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
*
|
|
480
|
-
* selection.is( 'selection' ); // -> true
|
|
481
|
-
* selection.is( 'documentSelection' ); // -> true
|
|
482
|
-
* selection.is( 'model:selection' ); // -> true
|
|
483
|
-
* selection.is( 'model:documentSelection' ); // -> true
|
|
484
|
-
*
|
|
485
|
-
* selection.is( 'view:selection' ); // -> false
|
|
486
|
-
* selection.is( 'element' ); // -> false
|
|
487
|
-
* selection.is( 'node' ); // -> false
|
|
488
|
-
*
|
|
489
|
-
* {@link module:engine/model/node~Node#is Check the entire list of model objects} which implement the `is()` method.
|
|
490
|
-
*
|
|
491
|
-
* @param {String} type
|
|
492
|
-
* @returns {Boolean}
|
|
493
|
-
*/
|
|
419
|
+
// The magic of type inference using `is` method is centralized in `TypeCheckable` class.
|
|
420
|
+
// Proper overload would interfere with that.
|
|
494
421
|
DocumentSelection.prototype.is = function (type) {
|
|
495
422
|
return type === 'selection' ||
|
|
496
423
|
type == 'model:selection' ||
|
|
497
424
|
type == 'documentSelection' ||
|
|
498
425
|
type == 'model:documentSelection';
|
|
499
426
|
};
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
//
|
|
427
|
+
/**
|
|
428
|
+
* `LiveSelection` is used internally by {@link module:engine/model/documentselection~DocumentSelection} and shouldn't be used directly.
|
|
429
|
+
*
|
|
430
|
+
* LiveSelection` is automatically updated upon changes in the {@link module:engine/model/document~Document document}
|
|
431
|
+
* to always contain valid ranges. Its attributes are inherited from the text unless set explicitly.
|
|
432
|
+
*
|
|
433
|
+
* Differences between {@link module:engine/model/selection~Selection} and `LiveSelection` are:
|
|
434
|
+
* * there is always a range in `LiveSelection` - even if no ranges were added there is a "default range"
|
|
435
|
+
* present in the selection,
|
|
436
|
+
* * ranges added to this selection updates automatically when the document changes,
|
|
437
|
+
* * attributes of `LiveSelection` are updated automatically according to selection ranges.
|
|
438
|
+
*/
|
|
513
439
|
class LiveSelection extends Selection {
|
|
514
|
-
|
|
515
|
-
|
|
440
|
+
/**
|
|
441
|
+
* Creates an empty live selection for given {@link module:engine/model/document~Document}.
|
|
442
|
+
*
|
|
443
|
+
* @param doc Document which owns this selection.
|
|
444
|
+
*/
|
|
516
445
|
constructor(doc) {
|
|
517
446
|
super();
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
447
|
+
/**
|
|
448
|
+
* List of selection markers.
|
|
449
|
+
* Marker is a selection marker when selection range is inside the marker range.
|
|
450
|
+
*/
|
|
522
451
|
this.markers = new Collection({ idProperty: 'name' });
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
// @protected
|
|
531
|
-
// @member {module:engine/model/document~Document}
|
|
532
|
-
this._document = doc;
|
|
533
|
-
// Keeps mapping of attribute name to priority with which the attribute got modified (added/changed/removed)
|
|
534
|
-
// last time. Possible values of priority are: `'low'` and `'normal'`.
|
|
535
|
-
//
|
|
536
|
-
// Priorities are used by internal `LiveSelection` mechanisms. All attributes set using `LiveSelection`
|
|
537
|
-
// attributes API are set with `'normal'` priority.
|
|
538
|
-
//
|
|
539
|
-
// @private
|
|
540
|
-
// @member {Map} module:engine/model/liveselection~LiveSelection#_attributePriority
|
|
452
|
+
/**
|
|
453
|
+
* Keeps mapping of attribute name to priority with which the attribute got modified (added/changed/removed)
|
|
454
|
+
* last time. Possible values of priority are: `'low'` and `'normal'`.
|
|
455
|
+
*
|
|
456
|
+
* Priorities are used by internal `LiveSelection` mechanisms. All attributes set using `LiveSelection`
|
|
457
|
+
* attributes API are set with `'normal'` priority.
|
|
458
|
+
*/
|
|
541
459
|
this._attributePriority = new Map();
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
460
|
+
/**
|
|
461
|
+
* Position to which the selection should be set if the last selection range was moved to the graveyard.
|
|
462
|
+
*/
|
|
545
463
|
this._selectionRestorePosition = null;
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
464
|
+
/**
|
|
465
|
+
* Flag that informs whether the selection ranges have changed. It is changed on true when `LiveRange#change:range` event is fired.
|
|
466
|
+
*/
|
|
549
467
|
this._hasChangedRange = false;
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
468
|
+
/**
|
|
469
|
+
* Each overriding gravity adds an UID to the set and each removal removes it.
|
|
470
|
+
* Gravity is overridden when there's at least one UID in the set.
|
|
471
|
+
* Gravity is restored when the set is empty.
|
|
472
|
+
* This is to prevent conflicts when gravity is overridden by more than one feature at the same time.
|
|
473
|
+
*/
|
|
556
474
|
this._overriddenGravityRegister = new Set();
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
475
|
+
/**
|
|
476
|
+
* Prefixes of marker names that should affect `LiveSelection#markers` collection.
|
|
477
|
+
*/
|
|
560
478
|
this._observedMarkers = new Set();
|
|
479
|
+
this._model = doc.model;
|
|
480
|
+
this._document = doc;
|
|
561
481
|
// Ensure selection is correct after each operation.
|
|
562
482
|
this.listenTo(this._model, 'applyOperation', (evt, args) => {
|
|
563
483
|
const operation = args[0];
|
|
@@ -602,23 +522,23 @@ class LiveSelection extends Selection {
|
|
|
602
522
|
get rangeCount() {
|
|
603
523
|
return this._ranges.length ? this._ranges.length : 1;
|
|
604
524
|
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
// @type {Boolean}
|
|
525
|
+
/**
|
|
526
|
+
* Describes whether `LiveSelection` has own range(s) set, or if it is defaulted to
|
|
527
|
+
* {@link module:engine/model/document~Document#_getDefaultRange document's default range}.
|
|
528
|
+
*/
|
|
610
529
|
get hasOwnRange() {
|
|
611
530
|
return this._ranges.length > 0;
|
|
612
531
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
// @type {Boolean}
|
|
532
|
+
/**
|
|
533
|
+
* When set to `true` then selection attributes on node before the caret won't be taken
|
|
534
|
+
* into consideration while updating selection attributes.
|
|
535
|
+
*/
|
|
618
536
|
get isGravityOverridden() {
|
|
619
537
|
return !!this._overriddenGravityRegister.size;
|
|
620
538
|
}
|
|
621
|
-
|
|
539
|
+
/**
|
|
540
|
+
* Unbinds all events previously bound by live selection.
|
|
541
|
+
*/
|
|
622
542
|
destroy() {
|
|
623
543
|
for (let i = 0; i < this._ranges.length; i++) {
|
|
624
544
|
this._ranges[i].detach();
|
|
@@ -680,7 +600,7 @@ class LiveSelection extends Selection {
|
|
|
680
600
|
* UID obtained from the {@link module:engine/model/writer~Writer#overrideSelectionGravity} to restore.
|
|
681
601
|
*
|
|
682
602
|
* @error document-selection-gravity-wrong-restore
|
|
683
|
-
* @param
|
|
603
|
+
* @param uid The unique identifier returned by
|
|
684
604
|
* {@link module:engine/model/documentselection~DocumentSelection#_overrideGravity}.
|
|
685
605
|
*/
|
|
686
606
|
throw new CKEditorError('document-selection-gravity-wrong-restore', this, { uid });
|
|
@@ -717,18 +637,17 @@ class LiveSelection extends Selection {
|
|
|
717
637
|
* starts or ends at incorrect position.
|
|
718
638
|
*
|
|
719
639
|
* @error document-selection-wrong-position
|
|
720
|
-
* @param
|
|
640
|
+
* @param range
|
|
721
641
|
*/
|
|
722
642
|
throw new CKEditorError('document-selection-wrong-position', this, { range });
|
|
723
643
|
}
|
|
724
644
|
}
|
|
725
645
|
}
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
// @param {module:engine/model/range~Range} range
|
|
646
|
+
/**
|
|
647
|
+
* Prepares given range to be added to selection. Checks if it is correct,
|
|
648
|
+
* converts it to {@link module:engine/model/liverange~LiveRange LiveRange}
|
|
649
|
+
* and sets listeners listening to the range's change event.
|
|
650
|
+
*/
|
|
732
651
|
_prepareRange(range) {
|
|
733
652
|
this._checkRange(range);
|
|
734
653
|
if (range.root == this._document.graveyard) {
|
|
@@ -819,11 +738,9 @@ class LiveSelection extends Selection {
|
|
|
819
738
|
this.fire('change:marker', { oldMarkers, directChange: false });
|
|
820
739
|
}
|
|
821
740
|
}
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
// @param {Boolean} clearAll
|
|
826
|
-
// @fires change:attribute
|
|
741
|
+
/**
|
|
742
|
+
* Updates this selection attributes according to its ranges and the {@link module:engine/model/document~Document model document}.
|
|
743
|
+
*/
|
|
827
744
|
_updateAttributes(clearAll) {
|
|
828
745
|
const newAttributes = toMap(this._getSurroundingAttributes());
|
|
829
746
|
const oldAttributes = toMap(this.getAttributes());
|
|
@@ -862,15 +779,10 @@ class LiveSelection extends Selection {
|
|
|
862
779
|
this.fire('change:attribute', { attributeKeys: changed, directChange: false });
|
|
863
780
|
}
|
|
864
781
|
}
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
// @param {String} key Attribute key.
|
|
870
|
-
// @param {*} value Attribute value.
|
|
871
|
-
// @param {Boolean} [directChange=true] `true` if the change is caused by `Selection` API, `false` if change
|
|
872
|
-
// is caused by `Batch` API.
|
|
873
|
-
// @returns {Boolean} Whether value has changed.
|
|
782
|
+
/**
|
|
783
|
+
* Internal method for setting `LiveSelection` attribute. Supports attribute priorities (through `directChange`
|
|
784
|
+
* parameter).
|
|
785
|
+
*/
|
|
874
786
|
_setAttribute(key, value, directChange = true) {
|
|
875
787
|
const priority = directChange ? 'normal' : 'low';
|
|
876
788
|
if (priority == 'low' && this._attributePriority.get(key) == 'normal') {
|
|
@@ -887,18 +799,13 @@ class LiveSelection extends Selection {
|
|
|
887
799
|
this._attributePriority.set(key, priority);
|
|
888
800
|
return true;
|
|
889
801
|
}
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
// @param {String} key Attribute key.
|
|
898
|
-
// @param {Boolean} [directChange=true] `true` if the change is caused by `Selection` API, `false` if change
|
|
899
|
-
// is caused by `Batch` API.
|
|
900
|
-
// @returns {Boolean} Whether attribute was removed. May not be true if such attributes didn't exist or the
|
|
901
|
-
// existing attribute had higher priority.
|
|
802
|
+
/**
|
|
803
|
+
* Internal method for removing `LiveSelection` attribute. Supports attribute priorities (through `directChange`
|
|
804
|
+
* parameter).
|
|
805
|
+
*
|
|
806
|
+
* NOTE: Even if attribute is not present in the selection but is provided to this method, it's priority will
|
|
807
|
+
* be changed according to `directChange` parameter.
|
|
808
|
+
*/
|
|
902
809
|
_removeAttribute(key, directChange = true) {
|
|
903
810
|
const priority = directChange ? 'normal' : 'low';
|
|
904
811
|
if (priority == 'low' && this._attributePriority.get(key) == 'normal') {
|
|
@@ -914,12 +821,10 @@ class LiveSelection extends Selection {
|
|
|
914
821
|
this._attrs.delete(key);
|
|
915
822
|
return true;
|
|
916
823
|
}
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
// @param {Map.<String,*>} attrs Iterable object containing attributes to be set.
|
|
922
|
-
// @returns {Set.<String>} Changed attribute keys.
|
|
824
|
+
/**
|
|
825
|
+
* Internal method for setting multiple `LiveSelection` attributes. Supports attribute priorities (through
|
|
826
|
+
* `directChange` parameter).
|
|
827
|
+
*/
|
|
923
828
|
_setAttributesTo(attrs) {
|
|
924
829
|
const changed = new Set();
|
|
925
830
|
for (const [oldKey, oldValue] of this.getAttributes()) {
|
|
@@ -939,10 +844,9 @@ class LiveSelection extends Selection {
|
|
|
939
844
|
}
|
|
940
845
|
return changed;
|
|
941
846
|
}
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
// @returns {Iterable.<*>}
|
|
847
|
+
/**
|
|
848
|
+
* Returns an iterable that iterates through all selection attributes stored in current selection's parent.
|
|
849
|
+
*/
|
|
946
850
|
*getStoredAttributes() {
|
|
947
851
|
const selectionParent = this.getFirstPosition().parent;
|
|
948
852
|
if (this.isCollapsed && selectionParent.isEmpty) {
|
|
@@ -954,12 +858,11 @@ class LiveSelection extends Selection {
|
|
|
954
858
|
}
|
|
955
859
|
}
|
|
956
860
|
}
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
// @returns {Iterable.<*>} Collection of attributes.
|
|
861
|
+
/**
|
|
862
|
+
* Checks model text nodes that are closest to the selection's first position and returns attributes of first
|
|
863
|
+
* found element. If there are no text nodes in selection's first position parent, it returns selection
|
|
864
|
+
* attributes stored in that parent.
|
|
865
|
+
*/
|
|
963
866
|
_getSurroundingAttributes() {
|
|
964
867
|
const position = this.getFirstPosition();
|
|
965
868
|
const schema = this._model.schema;
|
|
@@ -1016,10 +919,10 @@ class LiveSelection extends Selection {
|
|
|
1016
919
|
}
|
|
1017
920
|
return attrs;
|
|
1018
921
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
922
|
+
/**
|
|
923
|
+
* Fixes the selection after all its ranges got removed.
|
|
924
|
+
* @param deletionPosition Position where the deletion happened.
|
|
925
|
+
*/
|
|
1023
926
|
_fixGraveyardSelection(deletionPosition) {
|
|
1024
927
|
// Find a range that is a correct selection range and is closest to the position where the deletion happened.
|
|
1025
928
|
const selectionRange = this._model.schema.getNearestSelectionRange(deletionPosition);
|
|
@@ -1031,22 +934,20 @@ class LiveSelection extends Selection {
|
|
|
1031
934
|
// If nearest valid selection range cannot be found don't add any range. Selection will be set to the default range.
|
|
1032
935
|
}
|
|
1033
936
|
}
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
// @returns {Boolean}
|
|
937
|
+
/**
|
|
938
|
+
* Helper function for {@link module:engine/model/liveselection~LiveSelection#_updateAttributes}.
|
|
939
|
+
*
|
|
940
|
+
* It takes model item, checks whether it is a text node (or text proxy) and, if so, returns it's attributes. If not, returns `null`.
|
|
941
|
+
*/
|
|
1040
942
|
function getAttrsIfCharacter(node) {
|
|
1041
943
|
if (node instanceof TextProxy || node instanceof Text) {
|
|
1042
944
|
return node.getAttributes();
|
|
1043
945
|
}
|
|
1044
946
|
return null;
|
|
1045
947
|
}
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
// @param {module:engine/model/batch~Batch} batch
|
|
948
|
+
/**
|
|
949
|
+
* Removes selection attributes from element which is not empty anymore.
|
|
950
|
+
*/
|
|
1050
951
|
function clearAttributesStoredInElement(model, batch) {
|
|
1051
952
|
const differ = model.document.differ;
|
|
1052
953
|
for (const entry of differ.getChanges()) {
|