@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
package/src/model/selection.js
CHANGED
|
@@ -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/selection
|
|
8
7
|
*/
|
|
@@ -10,93 +9,79 @@ import TypeCheckable from './typecheckable';
|
|
|
10
9
|
import Node from './node';
|
|
11
10
|
import Position from './position';
|
|
12
11
|
import Range from './range';
|
|
13
|
-
import CKEditorError from '@ckeditor/ckeditor5-utils
|
|
14
|
-
import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
|
|
15
|
-
import isIterable from '@ckeditor/ckeditor5-utils/src/isiterable';
|
|
12
|
+
import { CKEditorError, EmitterMixin, isIterable } from '@ckeditor/ckeditor5-utils';
|
|
16
13
|
/**
|
|
17
14
|
* Selection is a set of {@link module:engine/model/range~Range ranges}. It has a direction specified by its
|
|
18
15
|
* {@link module:engine/model/selection~Selection#anchor anchor} and {@link module:engine/model/selection~Selection#focus focus}
|
|
19
16
|
* (it can be {@link module:engine/model/selection~Selection#isBackward forward or backward}).
|
|
20
17
|
* Additionally, selection may have its own attributes (think – whether text typed in in this selection
|
|
21
18
|
* should have those attributes – e.g. whether you type a bolded text).
|
|
22
|
-
*
|
|
23
|
-
* @mixes module:utils/emittermixin~EmitterMixin
|
|
24
19
|
*/
|
|
25
20
|
export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
26
21
|
/**
|
|
27
22
|
* Creates a new selection instance based on the given {@link module:engine/model/selection~Selectable selectable}
|
|
28
23
|
* or creates an empty selection if no arguments were passed.
|
|
29
24
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
25
|
+
* ```ts
|
|
26
|
+
* // Creates empty selection without ranges.
|
|
27
|
+
* const selection = writer.createSelection();
|
|
32
28
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
29
|
+
* // Creates selection at the given range.
|
|
30
|
+
* const range = writer.createRange( start, end );
|
|
31
|
+
* const selection = writer.createSelection( range );
|
|
36
32
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
33
|
+
* // Creates selection at the given ranges
|
|
34
|
+
* const ranges = [ writer.createRange( start1, end2 ), writer.createRange( star2, end2 ) ];
|
|
35
|
+
* const selection = writer.createSelection( ranges );
|
|
40
36
|
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
37
|
+
* // Creates selection from the other selection.
|
|
38
|
+
* // Note: It doesn't copy selection attributes.
|
|
39
|
+
* const otherSelection = writer.createSelection();
|
|
40
|
+
* const selection = writer.createSelection( otherSelection );
|
|
45
41
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
42
|
+
* // Creates selection from the given document selection.
|
|
43
|
+
* // Note: It doesn't copy selection attributes.
|
|
44
|
+
* const documentSelection = model.document.selection;
|
|
45
|
+
* const selection = writer.createSelection( documentSelection );
|
|
50
46
|
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
47
|
+
* // Creates selection at the given position.
|
|
48
|
+
* const position = writer.createPositionFromPath( root, path );
|
|
49
|
+
* const selection = writer.createSelection( position );
|
|
54
50
|
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
51
|
+
* // Creates selection at the given offset in the given element.
|
|
52
|
+
* const paragraph = writer.createElement( 'paragraph' );
|
|
53
|
+
* const selection = writer.createSelection( paragraph, offset );
|
|
58
54
|
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
55
|
+
* // Creates a range inside an {@link module:engine/model/element~Element element} which starts before the
|
|
56
|
+
* // first child of that element and ends after the last child of that element.
|
|
57
|
+
* const selection = writer.createSelection( paragraph, 'in' );
|
|
62
58
|
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
59
|
+
* // Creates a range on an {@link module:engine/model/item~Item item} which starts before the item and ends
|
|
60
|
+
* // just after the item.
|
|
61
|
+
* const selection = writer.createSelection( paragraph, 'on' );
|
|
62
|
+
* ```
|
|
66
63
|
*
|
|
67
64
|
* Selection's constructor allow passing additional options (`'backward'`) as the last argument.
|
|
68
65
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
66
|
+
* ```ts
|
|
67
|
+
* // Creates backward selection.
|
|
68
|
+
* const selection = writer.createSelection( range, { backward: true } );
|
|
69
|
+
* ```
|
|
71
70
|
*
|
|
72
|
-
* @
|
|
73
|
-
* @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Sets place or offset of the selection.
|
|
74
|
-
* @param {Object} [options]
|
|
75
|
-
* @param {Boolean} [options.backward] Sets this selection instance to be backward.
|
|
71
|
+
* @internal
|
|
76
72
|
*/
|
|
77
73
|
constructor(...args) {
|
|
78
74
|
super();
|
|
79
75
|
/**
|
|
80
76
|
* Specifies whether the last added range was added as a backward or forward range.
|
|
81
|
-
*
|
|
82
|
-
* @private
|
|
83
|
-
* @type {Boolean}
|
|
84
77
|
*/
|
|
85
78
|
this._lastRangeBackward = false;
|
|
86
|
-
/**
|
|
87
|
-
* Stores selection ranges.
|
|
88
|
-
*
|
|
89
|
-
* @protected
|
|
90
|
-
* @type {Array.<module:engine/model/range~Range>}
|
|
91
|
-
*/
|
|
92
|
-
this._ranges = [];
|
|
93
79
|
/**
|
|
94
80
|
* List of attributes set on current selection.
|
|
95
|
-
*
|
|
96
|
-
* @protected
|
|
97
|
-
* @type {Map.<String,*>}
|
|
98
81
|
*/
|
|
99
82
|
this._attrs = new Map();
|
|
83
|
+
/** @internal */
|
|
84
|
+
this._ranges = [];
|
|
100
85
|
if (args.length) {
|
|
101
86
|
this.setTo(...args);
|
|
102
87
|
}
|
|
@@ -115,8 +100,6 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
115
100
|
* May be set to `null` if there are no ranges in the selection.
|
|
116
101
|
*
|
|
117
102
|
* @see #focus
|
|
118
|
-
* @readonly
|
|
119
|
-
* @type {module:engine/model/position~Position|null}
|
|
120
103
|
*/
|
|
121
104
|
get anchor() {
|
|
122
105
|
if (this._ranges.length > 0) {
|
|
@@ -132,8 +115,6 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
132
115
|
* May be set to `null` if there are no ranges in the selection.
|
|
133
116
|
*
|
|
134
117
|
* @see #anchor
|
|
135
|
-
* @readonly
|
|
136
|
-
* @type {module:engine/model/position~Position|null}
|
|
137
118
|
*/
|
|
138
119
|
get focus() {
|
|
139
120
|
if (this._ranges.length > 0) {
|
|
@@ -145,9 +126,6 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
145
126
|
/**
|
|
146
127
|
* Whether the selection is collapsed. Selection is collapsed when there is exactly one range in it
|
|
147
128
|
* and it is collapsed.
|
|
148
|
-
*
|
|
149
|
-
* @readonly
|
|
150
|
-
* @type {Boolean}
|
|
151
129
|
*/
|
|
152
130
|
get isCollapsed() {
|
|
153
131
|
const length = this._ranges.length;
|
|
@@ -160,18 +138,12 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
160
138
|
}
|
|
161
139
|
/**
|
|
162
140
|
* Returns the number of ranges in the selection.
|
|
163
|
-
*
|
|
164
|
-
* @readonly
|
|
165
|
-
* @type {Number}
|
|
166
141
|
*/
|
|
167
142
|
get rangeCount() {
|
|
168
143
|
return this._ranges.length;
|
|
169
144
|
}
|
|
170
145
|
/**
|
|
171
146
|
* Specifies whether the selection's {@link #focus} precedes the selection's {@link #anchor}.
|
|
172
|
-
*
|
|
173
|
-
* @readonly
|
|
174
|
-
* @type {Boolean}
|
|
175
147
|
*/
|
|
176
148
|
get isBackward() {
|
|
177
149
|
return !this.isCollapsed && this._lastRangeBackward;
|
|
@@ -180,9 +152,8 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
180
152
|
* Checks whether this selection is equal to the given selection. Selections are equal if they have the same directions,
|
|
181
153
|
* the same number of ranges and all ranges from one selection equal to ranges from the another selection.
|
|
182
154
|
*
|
|
183
|
-
* @param
|
|
184
|
-
*
|
|
185
|
-
* @returns {Boolean} `true` if selections are equal, `false` otherwise.
|
|
155
|
+
* @param otherSelection Selection to compare with.
|
|
156
|
+
* @returns `true` if selections are equal, `false` otherwise.
|
|
186
157
|
*/
|
|
187
158
|
isEqual(otherSelection) {
|
|
188
159
|
if (this.rangeCount != otherSelection.rangeCount) {
|
|
@@ -210,8 +181,6 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
210
181
|
}
|
|
211
182
|
/**
|
|
212
183
|
* Returns an iterable object that iterates over copies of selection ranges.
|
|
213
|
-
*
|
|
214
|
-
* @returns {Iterable.<module:engine/model/range~Range>}
|
|
215
184
|
*/
|
|
216
185
|
*getRanges() {
|
|
217
186
|
for (const range of this._ranges) {
|
|
@@ -225,8 +194,6 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
225
194
|
* (not to confuse with the first range added to the selection).
|
|
226
195
|
*
|
|
227
196
|
* Returns `null` if there are no ranges in selection.
|
|
228
|
-
*
|
|
229
|
-
* @returns {module:engine/model/range~Range|null}
|
|
230
197
|
*/
|
|
231
198
|
getFirstRange() {
|
|
232
199
|
let first = null;
|
|
@@ -244,8 +211,6 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
244
211
|
* recently added to the selection).
|
|
245
212
|
*
|
|
246
213
|
* Returns `null` if there are no ranges in selection.
|
|
247
|
-
*
|
|
248
|
-
* @returns {module:engine/model/range~Range|null}
|
|
249
214
|
*/
|
|
250
215
|
getLastRange() {
|
|
251
216
|
let last = null;
|
|
@@ -262,8 +227,6 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
262
227
|
* any other position in the selection.
|
|
263
228
|
*
|
|
264
229
|
* Returns `null` if there are no ranges in selection.
|
|
265
|
-
*
|
|
266
|
-
* @returns {module:engine/model/position~Position|null}
|
|
267
230
|
*/
|
|
268
231
|
getFirstPosition() {
|
|
269
232
|
const first = this.getFirstRange();
|
|
@@ -275,8 +238,6 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
275
238
|
* any other position in the selection.
|
|
276
239
|
*
|
|
277
240
|
* Returns `null` if there are no ranges in selection.
|
|
278
|
-
*
|
|
279
|
-
* @returns {module:engine/model/position~Position|null}
|
|
280
241
|
*/
|
|
281
242
|
getLastPosition() {
|
|
282
243
|
const lastRange = this.getLastRange();
|
|
@@ -286,52 +247,55 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
286
247
|
* Sets this selection's ranges and direction to the specified location based on the given
|
|
287
248
|
* {@link module:engine/model/selection~Selectable selectable}.
|
|
288
249
|
*
|
|
289
|
-
*
|
|
290
|
-
*
|
|
250
|
+
* ```ts
|
|
251
|
+
* // Removes all selection's ranges.
|
|
252
|
+
* selection.setTo( null );
|
|
291
253
|
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
254
|
+
* // Sets selection to the given range.
|
|
255
|
+
* const range = writer.createRange( start, end );
|
|
256
|
+
* selection.setTo( range );
|
|
295
257
|
*
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
258
|
+
* // Sets selection to given ranges.
|
|
259
|
+
* const ranges = [ writer.createRange( start1, end2 ), writer.createRange( star2, end2 ) ];
|
|
260
|
+
* selection.setTo( ranges );
|
|
299
261
|
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
262
|
+
* // Sets selection to other selection.
|
|
263
|
+
* // Note: It doesn't copy selection attributes.
|
|
264
|
+
* const otherSelection = writer.createSelection();
|
|
265
|
+
* selection.setTo( otherSelection );
|
|
304
266
|
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
267
|
+
* // Sets selection to the given document selection.
|
|
268
|
+
* // Note: It doesn't copy selection attributes.
|
|
269
|
+
* const documentSelection = new DocumentSelection( doc );
|
|
270
|
+
* selection.setTo( documentSelection );
|
|
309
271
|
*
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
272
|
+
* // Sets collapsed selection at the given position.
|
|
273
|
+
* const position = writer.createPositionFromPath( root, path );
|
|
274
|
+
* selection.setTo( position );
|
|
313
275
|
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
276
|
+
* // Sets collapsed selection at the position of the given node and an offset.
|
|
277
|
+
* selection.setTo( paragraph, offset );
|
|
278
|
+
* ```
|
|
316
279
|
*
|
|
317
280
|
* Creates a range inside an {@link module:engine/model/element~Element element} which starts before the first child of
|
|
318
281
|
* that element and ends after the last child of that element.
|
|
319
282
|
*
|
|
320
|
-
*
|
|
283
|
+
* ```ts
|
|
284
|
+
* selection.setTo( paragraph, 'in' );
|
|
285
|
+
* ```
|
|
321
286
|
*
|
|
322
287
|
* Creates a range on an {@link module:engine/model/item~Item item} which starts before the item and ends just after the item.
|
|
323
288
|
*
|
|
324
|
-
*
|
|
289
|
+
* ```ts
|
|
290
|
+
* selection.setTo( paragraph, 'on' );
|
|
291
|
+
* ```
|
|
325
292
|
*
|
|
326
293
|
* `Selection#setTo()`' method allow passing additional options (`backward`) as the last argument.
|
|
327
294
|
*
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
*
|
|
332
|
-
* @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Sets place or offset of the selection.
|
|
333
|
-
* @param {Object} [options]
|
|
334
|
-
* @param {Boolean} [options.backward] Sets this selection instance to be backward.
|
|
295
|
+
* ```ts
|
|
296
|
+
* // Sets backward selection.
|
|
297
|
+
* const selection = writer.createSelection( range, { backward: true } );
|
|
298
|
+
* ```
|
|
335
299
|
*/
|
|
336
300
|
setTo(...args) {
|
|
337
301
|
let [selectable, placeOrOffset, options] = args;
|
|
@@ -403,10 +367,9 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
403
367
|
* is treated like the last added range and is used to set {@link module:engine/model/selection~Selection#anchor} and
|
|
404
368
|
* {@link module:engine/model/selection~Selection#focus}. Accepts a flag describing in which direction the selection is made.
|
|
405
369
|
*
|
|
406
|
-
* @protected
|
|
407
370
|
* @fires change:range
|
|
408
|
-
* @param
|
|
409
|
-
* @param
|
|
371
|
+
* @param newRanges Ranges to set.
|
|
372
|
+
* @param isLastBackward Flag describing if last added range was selected forward - from start to end (`false`)
|
|
410
373
|
* or backward - from end to start (`true`).
|
|
411
374
|
*/
|
|
412
375
|
_setRanges(newRanges, isLastBackward = false) {
|
|
@@ -446,9 +409,7 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
446
409
|
* {@link module:engine/model/writer~Writer#createPositionAt writer.createPositionAt()} parameters.
|
|
447
410
|
*
|
|
448
411
|
* @fires change:range
|
|
449
|
-
* @param {module:engine/model/item~Item
|
|
450
|
-
* @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when
|
|
451
|
-
* first parameter is a {@link module:engine/model/item~Item model item}.
|
|
412
|
+
* @param offset Offset or one of the flags. Used only when first parameter is a {@link module:engine/model/item~Item model item}.
|
|
452
413
|
*/
|
|
453
414
|
setFocus(itemOrPosition, offset) {
|
|
454
415
|
if (this.anchor === null) {
|
|
@@ -480,8 +441,8 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
480
441
|
/**
|
|
481
442
|
* Gets an attribute value for given key or `undefined` if that attribute is not set on the selection.
|
|
482
443
|
*
|
|
483
|
-
* @param
|
|
484
|
-
* @returns
|
|
444
|
+
* @param key Key of attribute to look for.
|
|
445
|
+
* @returns Attribute value or `undefined`.
|
|
485
446
|
*/
|
|
486
447
|
getAttribute(key) {
|
|
487
448
|
return this._attrs.get(key);
|
|
@@ -491,16 +452,12 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
491
452
|
*
|
|
492
453
|
* Attributes are returned as arrays containing two items. First one is attribute key and second is attribute value.
|
|
493
454
|
* This format is accepted by native `Map` object and also can be passed in `Node` constructor.
|
|
494
|
-
*
|
|
495
|
-
* @returns {Iterable.<*>}
|
|
496
455
|
*/
|
|
497
456
|
getAttributes() {
|
|
498
457
|
return this._attrs.entries();
|
|
499
458
|
}
|
|
500
459
|
/**
|
|
501
460
|
* Returns iterable that iterates over this selection's attribute keys.
|
|
502
|
-
*
|
|
503
|
-
* @returns {Iterable.<String>}
|
|
504
461
|
*/
|
|
505
462
|
getAttributeKeys() {
|
|
506
463
|
return this._attrs.keys();
|
|
@@ -508,8 +465,8 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
508
465
|
/**
|
|
509
466
|
* Checks if the selection has an attribute for given key.
|
|
510
467
|
*
|
|
511
|
-
* @param
|
|
512
|
-
* @returns
|
|
468
|
+
* @param key Key of attribute to check.
|
|
469
|
+
* @returns `true` if attribute with given key is set on selection, `false` otherwise.
|
|
513
470
|
*/
|
|
514
471
|
hasAttribute(key) {
|
|
515
472
|
return this._attrs.has(key);
|
|
@@ -521,7 +478,7 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
521
478
|
* removed attribute key.
|
|
522
479
|
*
|
|
523
480
|
* @fires change:attribute
|
|
524
|
-
* @param
|
|
481
|
+
* @param key Key of attribute to remove.
|
|
525
482
|
*/
|
|
526
483
|
removeAttribute(key) {
|
|
527
484
|
if (this.hasAttribute(key)) {
|
|
@@ -536,8 +493,8 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
536
493
|
* the attribute key.
|
|
537
494
|
*
|
|
538
495
|
* @fires change:attribute
|
|
539
|
-
* @param
|
|
540
|
-
* @param
|
|
496
|
+
* @param key Key of attribute to set.
|
|
497
|
+
* @param value Attribute value.
|
|
541
498
|
*/
|
|
542
499
|
setAttribute(key, value) {
|
|
543
500
|
if (this.getAttribute(key) !== value) {
|
|
@@ -549,8 +506,6 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
549
506
|
* Returns the selected element. {@link module:engine/model/element~Element Element} is considered as selected if there is only
|
|
550
507
|
* one range in the selection, and that range contains exactly one element.
|
|
551
508
|
* Returns `null` if there is no selected element.
|
|
552
|
-
*
|
|
553
|
-
* @returns {module:engine/model/element~Element|null}
|
|
554
509
|
*/
|
|
555
510
|
getSelectedElement() {
|
|
556
511
|
if (this.rangeCount !== 1) {
|
|
@@ -568,40 +523,48 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
568
523
|
*
|
|
569
524
|
* In this case the function will return exactly all 3 paragraphs (note: `<blockQuote>` is not a block itself):
|
|
570
525
|
*
|
|
571
|
-
*
|
|
572
|
-
*
|
|
573
|
-
*
|
|
574
|
-
*
|
|
575
|
-
*
|
|
526
|
+
* ```xml
|
|
527
|
+
* <paragraph>[a</paragraph>
|
|
528
|
+
* <blockQuote>
|
|
529
|
+
* <paragraph>b</paragraph>
|
|
530
|
+
* </blockQuote>
|
|
531
|
+
* <paragraph>c]d</paragraph>
|
|
532
|
+
* ```
|
|
576
533
|
*
|
|
577
534
|
* In this case the paragraph will also be returned, despite the collapsed selection:
|
|
578
535
|
*
|
|
579
|
-
*
|
|
536
|
+
* ```xml
|
|
537
|
+
* <paragraph>[]a</paragraph>
|
|
538
|
+
* ```
|
|
580
539
|
*
|
|
581
540
|
* In such a scenario, however, only blocks A, B & E will be returned as blocks C & D are nested in block B:
|
|
582
541
|
*
|
|
583
|
-
*
|
|
584
|
-
*
|
|
585
|
-
*
|
|
586
|
-
*
|
|
587
|
-
*
|
|
588
|
-
*
|
|
542
|
+
* ```xml
|
|
543
|
+
* [<blockA></blockA>
|
|
544
|
+
* <blockB>
|
|
545
|
+
* <blockC></blockC>
|
|
546
|
+
* <blockD></blockD>
|
|
547
|
+
* </blockB>
|
|
548
|
+
* <blockE></blockE>]
|
|
549
|
+
* ```
|
|
589
550
|
*
|
|
590
551
|
* If the selection is inside a block all the inner blocks (A & B) are returned:
|
|
591
552
|
*
|
|
592
|
-
*
|
|
593
|
-
*
|
|
594
|
-
*
|
|
595
|
-
*
|
|
553
|
+
* ```xml
|
|
554
|
+
* <block>
|
|
555
|
+
* <blockA>[a</blockA>
|
|
556
|
+
* <blockB>b]</blockB>
|
|
557
|
+
* </block>
|
|
558
|
+
* ```
|
|
596
559
|
*
|
|
597
560
|
* **Special case**: If a selection ends at the beginning of a block, that block is not returned as from user perspective
|
|
598
561
|
* this block wasn't selected. See [#984](https://github.com/ckeditor/ckeditor5-engine/issues/984) for more details.
|
|
599
562
|
*
|
|
600
|
-
*
|
|
601
|
-
*
|
|
602
|
-
*
|
|
603
|
-
*
|
|
604
|
-
*
|
|
563
|
+
* ```xml
|
|
564
|
+
* <paragraph>[a</paragraph>
|
|
565
|
+
* <paragraph>b</paragraph>
|
|
566
|
+
* <paragraph>]c</paragraph> // this block will not be returned
|
|
567
|
+
* ```
|
|
605
568
|
*/
|
|
606
569
|
*getSelectedBlocks() {
|
|
607
570
|
const visited = new WeakSet();
|
|
@@ -631,9 +594,6 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
631
594
|
*
|
|
632
595
|
* By default, this method will check whether the entire content of the selection's current root is selected.
|
|
633
596
|
* Useful to check if e.g. the user has just pressed <kbd>Ctrl</kbd> + <kbd>A</kbd>.
|
|
634
|
-
*
|
|
635
|
-
* @param {module:engine/model/element~Element} [element=this.anchor.root]
|
|
636
|
-
* @returns {Boolean}
|
|
637
597
|
*/
|
|
638
598
|
containsEntireContent(element = this.anchor.root) {
|
|
639
599
|
const limitStartPosition = Position._createAt(element, 0);
|
|
@@ -644,9 +604,6 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
644
604
|
/**
|
|
645
605
|
* Adds given range to internal {@link #_ranges ranges array}. Throws an error
|
|
646
606
|
* if given range is intersecting with any range that is already stored in this selection.
|
|
647
|
-
*
|
|
648
|
-
* @protected
|
|
649
|
-
* @param {module:engine/model/range~Range} range Range to add.
|
|
650
607
|
*/
|
|
651
608
|
_pushRange(range) {
|
|
652
609
|
this._checkRange(range);
|
|
@@ -654,9 +611,6 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
654
611
|
}
|
|
655
612
|
/**
|
|
656
613
|
* Checks if given range intersects with ranges that are already in the selection. Throws an error if it does.
|
|
657
|
-
*
|
|
658
|
-
* @protected
|
|
659
|
-
* @param {module:engine/model/range~Range} range Range to check.
|
|
660
614
|
*/
|
|
661
615
|
_checkRange(range) {
|
|
662
616
|
for (let i = 0; i < this._ranges.length; i++) {
|
|
@@ -665,8 +619,8 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
665
619
|
* Trying to add a range that intersects with another range in the selection.
|
|
666
620
|
*
|
|
667
621
|
* @error model-selection-range-intersects
|
|
668
|
-
* @param
|
|
669
|
-
* @param
|
|
622
|
+
* @param addedRange Range that was added to the selection.
|
|
623
|
+
* @param intersectingRange Range in the selection that intersects with `addedRange`.
|
|
670
624
|
*/
|
|
671
625
|
throw new CKEditorError('model-selection-range-intersects', [this, range], { addedRange: range, intersectingRange: this._ranges[i] });
|
|
672
626
|
}
|
|
@@ -675,9 +629,6 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
675
629
|
/**
|
|
676
630
|
* Replaces all the ranges by the given ones.
|
|
677
631
|
* Uses {@link #_popRange _popRange} and {@link #_pushRange _pushRange} to ensure proper ranges removal and addition.
|
|
678
|
-
*
|
|
679
|
-
* @param {Array.<module:engine/model/range~Range>} ranges
|
|
680
|
-
* @protected
|
|
681
632
|
*/
|
|
682
633
|
_replaceAllRanges(ranges) {
|
|
683
634
|
this._removeAllRanges();
|
|
@@ -688,8 +639,6 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
688
639
|
/**
|
|
689
640
|
* Deletes ranges from internal range array. Uses {@link #_popRange _popRange} to
|
|
690
641
|
* ensure proper ranges removal.
|
|
691
|
-
*
|
|
692
|
-
* @protected
|
|
693
642
|
*/
|
|
694
643
|
_removeAllRanges() {
|
|
695
644
|
while (this._ranges.length > 0) {
|
|
@@ -698,32 +647,20 @@ export default class Selection extends EmitterMixin(TypeCheckable) {
|
|
|
698
647
|
}
|
|
699
648
|
/**
|
|
700
649
|
* Removes most recently added range from the selection.
|
|
701
|
-
*
|
|
702
|
-
* @protected
|
|
703
650
|
*/
|
|
704
651
|
_popRange() {
|
|
705
652
|
this._ranges.pop();
|
|
706
653
|
}
|
|
707
654
|
}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
*
|
|
711
|
-
* selection.is( 'selection' ); // -> true
|
|
712
|
-
* selection.is( 'model:selection' ); // -> true
|
|
713
|
-
*
|
|
714
|
-
* selection.is( 'view:selection' ); // -> false
|
|
715
|
-
* selection.is( 'range' ); // -> false
|
|
716
|
-
*
|
|
717
|
-
* {@link module:engine/model/node~Node#is Check the entire list of model objects} which implement the `is()` method.
|
|
718
|
-
*
|
|
719
|
-
* @param {String} type
|
|
720
|
-
* @returns {Boolean}
|
|
721
|
-
*/
|
|
655
|
+
// The magic of type inference using `is` method is centralized in `TypeCheckable` class.
|
|
656
|
+
// Proper overload would interfere with that.
|
|
722
657
|
Selection.prototype.is = function (type) {
|
|
723
658
|
return type === 'selection' || type === 'model:selection';
|
|
724
659
|
};
|
|
725
|
-
|
|
726
|
-
|
|
660
|
+
/**
|
|
661
|
+
* Checks whether the given element extends $block in the schema and has a parent (is not a root).
|
|
662
|
+
* Marks it as already visited.
|
|
663
|
+
*/
|
|
727
664
|
function isUnvisitedBlock(element, visited) {
|
|
728
665
|
if (visited.has(element)) {
|
|
729
666
|
return false;
|
|
@@ -731,13 +668,17 @@ function isUnvisitedBlock(element, visited) {
|
|
|
731
668
|
visited.add(element);
|
|
732
669
|
return element.root.document.model.schema.isBlock(element) && !!element.parent;
|
|
733
670
|
}
|
|
734
|
-
|
|
671
|
+
/**
|
|
672
|
+
* Checks if the given element is a $block was not previously visited and is a top block in a range.
|
|
673
|
+
*/
|
|
735
674
|
function isUnvisitedTopBlock(element, visited, range) {
|
|
736
675
|
return isUnvisitedBlock(element, visited) && isTopBlockInRange(element, range);
|
|
737
676
|
}
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
677
|
+
/**
|
|
678
|
+
* Finds the lowest element in position's ancestors which is a block.
|
|
679
|
+
* It will search until first ancestor that is a limit element.
|
|
680
|
+
* Marks all ancestors as already visited to not include any of them later on.
|
|
681
|
+
*/
|
|
741
682
|
function getParentBlock(position, visited) {
|
|
742
683
|
const element = position.parent;
|
|
743
684
|
const schema = element.root.document.model.schema;
|
|
@@ -756,10 +697,9 @@ function getParentBlock(position, visited) {
|
|
|
756
697
|
ancestors.forEach(element => visited.add(element));
|
|
757
698
|
return block;
|
|
758
699
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
// @param {module:engine/model/range~Range} range Range to check.
|
|
700
|
+
/**
|
|
701
|
+
* Checks if the blocks is not nested in other block inside a range.
|
|
702
|
+
*/
|
|
763
703
|
function isTopBlockInRange(block, range) {
|
|
764
704
|
const parentBlock = findAncestorBlock(block);
|
|
765
705
|
if (!parentBlock) {
|
|
@@ -769,10 +709,9 @@ function isTopBlockInRange(block, range) {
|
|
|
769
709
|
const isParentInRange = range.containsRange(Range._createOn(parentBlock), true);
|
|
770
710
|
return !isParentInRange;
|
|
771
711
|
}
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
// @returns {module:engine/model/node~Node|undefined}
|
|
712
|
+
/**
|
|
713
|
+
* Returns first ancestor block of a node.
|
|
714
|
+
*/
|
|
776
715
|
function findAncestorBlock(node) {
|
|
777
716
|
const schema = node.root.document.model.schema;
|
|
778
717
|
let parent = node.parent;
|