@ckeditor/ckeditor5-engine 34.2.0 → 35.1.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/CHANGELOG.md +823 -0
- package/LICENSE.md +4 -0
- package/package.json +32 -25
- package/src/controller/datacontroller.js +467 -561
- package/src/controller/editingcontroller.js +168 -204
- package/src/conversion/conversion.js +541 -565
- package/src/conversion/conversionhelpers.js +24 -28
- package/src/conversion/downcastdispatcher.js +457 -686
- package/src/conversion/downcasthelpers.js +1583 -1965
- package/src/conversion/mapper.js +518 -707
- package/src/conversion/modelconsumable.js +240 -283
- package/src/conversion/upcastdispatcher.js +372 -718
- package/src/conversion/upcasthelpers.js +707 -818
- package/src/conversion/viewconsumable.js +524 -581
- package/src/dataprocessor/basichtmlwriter.js +12 -16
- package/src/dataprocessor/dataprocessor.js +5 -0
- package/src/dataprocessor/htmldataprocessor.js +101 -117
- package/src/dataprocessor/htmlwriter.js +1 -18
- package/src/dataprocessor/xmldataprocessor.js +117 -138
- package/src/dev-utils/model.js +260 -352
- package/src/dev-utils/operationreplayer.js +106 -126
- package/src/dev-utils/utils.js +34 -51
- package/src/dev-utils/view.js +632 -753
- package/src/index.js +0 -11
- package/src/model/batch.js +111 -127
- package/src/model/differ.js +988 -1233
- package/src/model/document.js +340 -449
- package/src/model/documentfragment.js +327 -364
- package/src/model/documentselection.js +996 -1189
- package/src/model/element.js +306 -410
- package/src/model/history.js +224 -262
- package/src/model/item.js +5 -0
- package/src/model/liveposition.js +84 -145
- package/src/model/liverange.js +108 -185
- package/src/model/markercollection.js +379 -480
- package/src/model/model.js +883 -1034
- package/src/model/node.js +419 -463
- package/src/model/nodelist.js +175 -201
- package/src/model/operation/attributeoperation.js +153 -182
- package/src/model/operation/detachoperation.js +64 -83
- package/src/model/operation/insertoperation.js +135 -166
- package/src/model/operation/markeroperation.js +114 -140
- package/src/model/operation/mergeoperation.js +163 -191
- package/src/model/operation/moveoperation.js +157 -187
- package/src/model/operation/nooperation.js +28 -38
- package/src/model/operation/operation.js +106 -125
- package/src/model/operation/operationfactory.js +30 -34
- package/src/model/operation/renameoperation.js +109 -135
- package/src/model/operation/rootattributeoperation.js +155 -188
- package/src/model/operation/splitoperation.js +196 -232
- package/src/model/operation/transform.js +1833 -2204
- package/src/model/operation/utils.js +140 -204
- package/src/model/position.js +899 -1053
- package/src/model/range.js +910 -1028
- package/src/model/rootelement.js +77 -97
- package/src/model/schema.js +1189 -1835
- package/src/model/selection.js +745 -862
- package/src/model/text.js +90 -114
- package/src/model/textproxy.js +204 -240
- package/src/model/treewalker.js +316 -397
- package/src/model/typecheckable.js +16 -0
- package/src/model/utils/autoparagraphing.js +32 -44
- package/src/model/utils/deletecontent.js +334 -418
- package/src/model/utils/findoptimalinsertionrange.js +25 -36
- package/src/model/utils/getselectedcontent.js +96 -118
- package/src/model/utils/insertcontent.js +654 -773
- package/src/model/utils/insertobject.js +96 -119
- package/src/model/utils/modifyselection.js +120 -158
- package/src/model/utils/selection-post-fixer.js +153 -201
- package/src/model/writer.js +1305 -1474
- package/src/view/attributeelement.js +189 -225
- package/src/view/containerelement.js +75 -85
- package/src/view/document.js +172 -215
- package/src/view/documentfragment.js +200 -249
- package/src/view/documentselection.js +338 -367
- package/src/view/domconverter.js +1371 -1613
- package/src/view/downcastwriter.js +1747 -2076
- package/src/view/editableelement.js +81 -97
- package/src/view/element.js +739 -890
- package/src/view/elementdefinition.js +5 -0
- package/src/view/emptyelement.js +82 -92
- package/src/view/filler.js +35 -50
- package/src/view/item.js +5 -0
- package/src/view/matcher.js +260 -559
- package/src/view/node.js +274 -360
- package/src/view/observer/arrowkeysobserver.js +19 -28
- package/src/view/observer/bubblingemittermixin.js +120 -263
- package/src/view/observer/bubblingeventinfo.js +47 -55
- package/src/view/observer/clickobserver.js +7 -13
- package/src/view/observer/compositionobserver.js +14 -24
- package/src/view/observer/domeventdata.js +57 -67
- package/src/view/observer/domeventobserver.js +40 -64
- package/src/view/observer/fakeselectionobserver.js +81 -96
- package/src/view/observer/focusobserver.js +45 -61
- package/src/view/observer/inputobserver.js +7 -13
- package/src/view/observer/keyobserver.js +17 -27
- package/src/view/observer/mouseobserver.js +7 -14
- package/src/view/observer/mutationobserver.js +220 -315
- package/src/view/observer/observer.js +81 -102
- package/src/view/observer/selectionobserver.js +191 -246
- package/src/view/observer/tabobserver.js +23 -36
- package/src/view/placeholder.js +128 -173
- package/src/view/position.js +350 -401
- package/src/view/range.js +453 -513
- package/src/view/rawelement.js +85 -112
- package/src/view/renderer.js +874 -1014
- package/src/view/rooteditableelement.js +80 -90
- package/src/view/selection.js +608 -689
- package/src/view/styles/background.js +43 -44
- package/src/view/styles/border.js +220 -276
- package/src/view/styles/margin.js +8 -17
- package/src/view/styles/padding.js +8 -16
- package/src/view/styles/utils.js +127 -160
- package/src/view/stylesmap.js +728 -905
- package/src/view/text.js +102 -126
- package/src/view/textproxy.js +144 -170
- package/src/view/treewalker.js +383 -479
- package/src/view/typecheckable.js +19 -0
- package/src/view/uielement.js +166 -187
- package/src/view/upcastwriter.js +395 -449
- package/src/view/view.js +569 -664
- package/src/dataprocessor/dataprocessor.jsdoc +0 -64
- package/src/model/item.jsdoc +0 -14
- package/src/view/elementdefinition.jsdoc +0 -59
- package/src/view/item.jsdoc +0 -14
|
@@ -2,16 +2,14 @@
|
|
|
2
2
|
* @license Copyright (c) 2003-2022, 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
|
-
|
|
6
5
|
/**
|
|
7
6
|
* @module engine/model/utils/insertobject
|
|
8
7
|
*/
|
|
9
|
-
|
|
8
|
+
import { findOptimalInsertionRange } from './findoptimalinsertionrange';
|
|
9
|
+
import DocumentSelection from '../documentselection';
|
|
10
|
+
import Selection from '../selection';
|
|
10
11
|
import first from '@ckeditor/ckeditor5-utils/src/first';
|
|
11
12
|
import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
|
|
12
|
-
|
|
13
|
-
import { findOptimalInsertionRange } from './findoptimalinsertionrange';
|
|
14
|
-
|
|
15
13
|
/**
|
|
16
14
|
* Inserts an {@glink framework/guides/deep-dive/schema#object-elements object element} at a specific position in the editor content.
|
|
17
15
|
*
|
|
@@ -46,85 +44,69 @@ import { findOptimalInsertionRange } from './findoptimalinsertionrange';
|
|
|
46
44
|
* would return the model to the state before the insertion. If no changes were preformed by `insertObject()`, returns a range collapsed
|
|
47
45
|
* at the insertion position.
|
|
48
46
|
*/
|
|
49
|
-
export default function insertObject(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
// Insert the prepared content at the optionally adjusted selection.
|
|
113
|
-
const affectedRange = model.insertContent( elementToInsert, insertionSelection );
|
|
114
|
-
|
|
115
|
-
// Nothing got inserted.
|
|
116
|
-
if ( affectedRange.isCollapsed ) {
|
|
117
|
-
return affectedRange;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if ( options.setSelection ) {
|
|
121
|
-
updateSelection( writer, object, options.setSelection, attributesToCopy );
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return affectedRange;
|
|
125
|
-
} );
|
|
47
|
+
export default function insertObject(model, object, selectable, placeOrOffset, options = {}) {
|
|
48
|
+
if (!model.schema.isObject(object)) {
|
|
49
|
+
/**
|
|
50
|
+
* Tried to insert an element with {@link module:engine/model/utils/insertobject insertObject()} function
|
|
51
|
+
* that is not defined as an object in schema.
|
|
52
|
+
* See {@link module:engine/model/schema~SchemaItemDefinition#isObject `SchemaItemDefinition`}.
|
|
53
|
+
* If you want to insert content that is not an object you might want to use
|
|
54
|
+
* {@link module:engine/model/utils/insertcontent insertContent()} function.
|
|
55
|
+
* @error insertobject-element-not-an-object
|
|
56
|
+
*/
|
|
57
|
+
throw new CKEditorError('insertobject-element-not-an-object', model, { object });
|
|
58
|
+
}
|
|
59
|
+
// Normalize selectable to a selection instance.
|
|
60
|
+
let originalSelection;
|
|
61
|
+
if (!selectable) {
|
|
62
|
+
originalSelection = model.document.selection;
|
|
63
|
+
}
|
|
64
|
+
else if (selectable instanceof Selection || selectable instanceof DocumentSelection) {
|
|
65
|
+
originalSelection = selectable;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
originalSelection = model.createSelection(selectable, placeOrOffset);
|
|
69
|
+
}
|
|
70
|
+
// Adjust the insertion selection.
|
|
71
|
+
let insertionSelection = originalSelection;
|
|
72
|
+
if (options.findOptimalPosition && model.schema.isBlock(object)) {
|
|
73
|
+
insertionSelection = model.createSelection(findOptimalInsertionRange(originalSelection, model, options.findOptimalPosition));
|
|
74
|
+
}
|
|
75
|
+
// Collect attributes to be copied on the inserted object.
|
|
76
|
+
const firstSelectedBlock = first(originalSelection.getSelectedBlocks());
|
|
77
|
+
const attributesToCopy = {};
|
|
78
|
+
if (firstSelectedBlock) {
|
|
79
|
+
Object.assign(attributesToCopy, model.schema.getAttributesWithProperty(firstSelectedBlock, 'copyOnReplace', true));
|
|
80
|
+
}
|
|
81
|
+
return model.change(writer => {
|
|
82
|
+
// Remove the selected content to find out what the parent of the inserted object would be.
|
|
83
|
+
// It would be removed inside model.insertContent() anyway.
|
|
84
|
+
if (!insertionSelection.isCollapsed) {
|
|
85
|
+
model.deleteContent(insertionSelection, { doNotAutoparagraph: true });
|
|
86
|
+
}
|
|
87
|
+
let elementToInsert = object;
|
|
88
|
+
const insertionPositionParent = insertionSelection.anchor.parent;
|
|
89
|
+
// Autoparagraphing of an inline objects.
|
|
90
|
+
if (!model.schema.checkChild(insertionPositionParent, object) &&
|
|
91
|
+
model.schema.checkChild(insertionPositionParent, 'paragraph') &&
|
|
92
|
+
model.schema.checkChild('paragraph', object)) {
|
|
93
|
+
elementToInsert = writer.createElement('paragraph');
|
|
94
|
+
writer.insert(object, elementToInsert);
|
|
95
|
+
}
|
|
96
|
+
// Apply attributes that are allowed on the inserted object (or paragraph if autoparagraphed).
|
|
97
|
+
model.schema.setAllowedAttributes(elementToInsert, attributesToCopy, writer);
|
|
98
|
+
// Insert the prepared content at the optionally adjusted selection.
|
|
99
|
+
const affectedRange = model.insertContent(elementToInsert, insertionSelection);
|
|
100
|
+
// Nothing got inserted.
|
|
101
|
+
if (affectedRange.isCollapsed) {
|
|
102
|
+
return affectedRange;
|
|
103
|
+
}
|
|
104
|
+
if (options.setSelection) {
|
|
105
|
+
updateSelection(writer, object, options.setSelection, attributesToCopy);
|
|
106
|
+
}
|
|
107
|
+
return affectedRange;
|
|
108
|
+
});
|
|
126
109
|
}
|
|
127
|
-
|
|
128
110
|
// Updates document selection based on given `place` parameter in relation to `contextElement` element.
|
|
129
111
|
//
|
|
130
112
|
// @private
|
|
@@ -134,40 +116,35 @@ export default function insertObject( model, object, selectable, placeOrOffset,
|
|
|
134
116
|
// Value `on` will set selection on the passed `contextElement`. Value `after` will set selection after `contextElement`.
|
|
135
117
|
// @param {Object} attributes Attributes keys and values to set on a paragraph that this function can create when
|
|
136
118
|
// `place` parameter is equal to `after` but there is no element with `$text` node to set selection in.
|
|
137
|
-
function updateSelection(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
*
|
|
169
|
-
* @error insertobject-invalid-place-parameter-value
|
|
170
|
-
*/
|
|
171
|
-
throw new CKEditorError( 'insertobject-invalid-place-parameter-value', model );
|
|
172
|
-
}
|
|
119
|
+
function updateSelection(writer, contextElement, place, paragraphAttributes) {
|
|
120
|
+
const model = writer.model;
|
|
121
|
+
if (place == 'after') {
|
|
122
|
+
let nextElement = contextElement.nextSibling;
|
|
123
|
+
// Check whether an element next to the inserted element is defined and can contain a text.
|
|
124
|
+
const canSetSelection = nextElement && model.schema.checkChild(nextElement, '$text');
|
|
125
|
+
// If the element is missing, but a paragraph could be inserted next to the element, let's add it.
|
|
126
|
+
if (!canSetSelection && model.schema.checkChild(contextElement.parent, 'paragraph')) {
|
|
127
|
+
nextElement = writer.createElement('paragraph');
|
|
128
|
+
model.schema.setAllowedAttributes(nextElement, paragraphAttributes, writer);
|
|
129
|
+
model.insertContent(nextElement, writer.createPositionAfter(contextElement));
|
|
130
|
+
}
|
|
131
|
+
// Put the selection inside the element, at the beginning.
|
|
132
|
+
if (nextElement) {
|
|
133
|
+
writer.setSelection(nextElement, 0);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else if (place == 'on') {
|
|
137
|
+
writer.setSelection(contextElement, 'on');
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
/**
|
|
141
|
+
* The unsupported `options.setSelection` parameter was passed
|
|
142
|
+
* to the {@link module:engine/model/utils/insertobject insertObject()} function.
|
|
143
|
+
* Check the {@link module:engine/model/utils/insertobject insertObject()} API documentation for allowed
|
|
144
|
+
* `options.setSelection` parameter values.
|
|
145
|
+
*
|
|
146
|
+
* @error insertobject-invalid-place-parameter-value
|
|
147
|
+
*/
|
|
148
|
+
throw new CKEditorError('insertobject-invalid-place-parameter-value', model);
|
|
149
|
+
}
|
|
173
150
|
}
|
|
@@ -2,19 +2,15 @@
|
|
|
2
2
|
* @license Copyright (c) 2003-2022, 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
|
-
|
|
6
5
|
/**
|
|
7
6
|
* @module engine/model/utils/modifyselection
|
|
8
7
|
*/
|
|
9
|
-
|
|
8
|
+
import DocumentSelection from '../documentselection';
|
|
10
9
|
import Position from '../position';
|
|
11
|
-
import TreeWalker from '../treewalker';
|
|
12
10
|
import Range from '../range';
|
|
11
|
+
import TreeWalker from '../treewalker';
|
|
13
12
|
import { isInsideSurrogatePair, isInsideCombinedSymbol, isInsideEmojiSequence } from '@ckeditor/ckeditor5-utils/src/unicode';
|
|
14
|
-
import DocumentSelection from '../documentselection';
|
|
15
|
-
|
|
16
13
|
const wordBoundaryCharacters = ' ,.?!:;"-()';
|
|
17
|
-
|
|
18
14
|
/**
|
|
19
15
|
* Modifies the selection. Currently, the supported modifications are:
|
|
20
16
|
*
|
|
@@ -51,186 +47,152 @@ const wordBoundaryCharacters = ' ,.?!:;"-()';
|
|
|
51
47
|
* @param {'character'|'codePoint'|'word'} [options.unit='character'] The unit by which selection should be modified.
|
|
52
48
|
* @param {Boolean} [options.treatEmojiAsSingleUnit=false] Whether multi-characer emoji sequences should be handled as single unit.
|
|
53
49
|
*/
|
|
54
|
-
export default function modifySelection(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
} else {
|
|
85
|
-
selection.setFocus( position );
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
50
|
+
export default function modifySelection(model, selection, options = {}) {
|
|
51
|
+
const schema = model.schema;
|
|
52
|
+
const isForward = options.direction != 'backward';
|
|
53
|
+
const unit = options.unit ? options.unit : 'character';
|
|
54
|
+
const treatEmojiAsSingleUnit = !!options.treatEmojiAsSingleUnit;
|
|
55
|
+
const focus = selection.focus;
|
|
56
|
+
const walker = new TreeWalker({
|
|
57
|
+
boundaries: getSearchRange(focus, isForward),
|
|
58
|
+
singleCharacters: true,
|
|
59
|
+
direction: isForward ? 'forward' : 'backward'
|
|
60
|
+
});
|
|
61
|
+
const data = { walker, schema, isForward, unit, treatEmojiAsSingleUnit };
|
|
62
|
+
let next;
|
|
63
|
+
while ((next = walker.next())) {
|
|
64
|
+
if (next.done) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const position = tryExtendingTo(data, next.value);
|
|
68
|
+
if (position) {
|
|
69
|
+
if (selection instanceof DocumentSelection) {
|
|
70
|
+
model.change(writer => {
|
|
71
|
+
writer.setSelectionFocus(position);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
selection.setFocus(position);
|
|
76
|
+
}
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
91
80
|
}
|
|
92
|
-
|
|
93
81
|
// Checks whether the selection can be extended to the the walker's next value (next position).
|
|
94
82
|
// @param {{ walker, unit, isForward, schema, treatEmojiAsSingleUnit }} data
|
|
95
83
|
// @param {module:engine/view/treewalker~TreeWalkerValue} value
|
|
96
|
-
function tryExtendingTo(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
// If text allowed on this position, extend to this place.
|
|
133
|
-
if ( schema.checkChild( nextPosition, '$text' ) ) {
|
|
134
|
-
return nextPosition;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
84
|
+
function tryExtendingTo(data, value) {
|
|
85
|
+
const { isForward, walker, unit, schema, treatEmojiAsSingleUnit } = data;
|
|
86
|
+
const { type, item, nextPosition } = value;
|
|
87
|
+
// If found text, we can certainly put the focus in it. Let's just find a correct position
|
|
88
|
+
// based on the unit.
|
|
89
|
+
if (type == 'text') {
|
|
90
|
+
if (data.unit === 'word') {
|
|
91
|
+
return getCorrectWordBreakPosition(walker, isForward);
|
|
92
|
+
}
|
|
93
|
+
return getCorrectPosition(walker, unit, treatEmojiAsSingleUnit);
|
|
94
|
+
}
|
|
95
|
+
// Entering an element.
|
|
96
|
+
if (type == (isForward ? 'elementStart' : 'elementEnd')) {
|
|
97
|
+
// If it's a selectable, we can select it now.
|
|
98
|
+
if (schema.isSelectable(item)) {
|
|
99
|
+
return Position._createAt(item, isForward ? 'after' : 'before');
|
|
100
|
+
}
|
|
101
|
+
// If text allowed on this position, extend to this place.
|
|
102
|
+
if (schema.checkChild(nextPosition, '$text')) {
|
|
103
|
+
return nextPosition;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Leaving an element.
|
|
107
|
+
else {
|
|
108
|
+
// If leaving a limit element, stop.
|
|
109
|
+
if (schema.isLimit(item)) {
|
|
110
|
+
// NOTE: Fast-forward the walker until the end.
|
|
111
|
+
walker.skip(() => true);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
// If text allowed on this position, extend to this place.
|
|
115
|
+
if (schema.checkChild(nextPosition, '$text')) {
|
|
116
|
+
return nextPosition;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
137
119
|
}
|
|
138
|
-
|
|
139
120
|
// Finds a correct position by walking in a text node and checking whether selection can be extended to given position
|
|
140
121
|
// or should be extended further.
|
|
141
122
|
//
|
|
142
123
|
// @param {module:engine/model/treewalker~TreeWalker} walker
|
|
143
124
|
// @param {String} unit The unit by which selection should be modified.
|
|
144
125
|
// @param {Boolean} treatEmojiAsSingleUnit
|
|
145
|
-
function getCorrectPosition(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
offset = walker.position.offset - textNode.startOffset;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return walker.position;
|
|
126
|
+
function getCorrectPosition(walker, unit, treatEmojiAsSingleUnit) {
|
|
127
|
+
const textNode = walker.position.textNode;
|
|
128
|
+
if (textNode) {
|
|
129
|
+
const data = textNode.data;
|
|
130
|
+
let offset = walker.position.offset - textNode.startOffset;
|
|
131
|
+
while (isInsideSurrogatePair(data, offset) ||
|
|
132
|
+
(unit == 'character' && isInsideCombinedSymbol(data, offset)) ||
|
|
133
|
+
(treatEmojiAsSingleUnit && isInsideEmojiSequence(data, offset))) {
|
|
134
|
+
walker.next();
|
|
135
|
+
offset = walker.position.offset - textNode.startOffset;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return walker.position;
|
|
164
139
|
}
|
|
165
|
-
|
|
166
140
|
// Finds a correct position of a word break by walking in a text node and checking whether selection can be extended to given position
|
|
167
141
|
// or should be extended further.
|
|
168
142
|
//
|
|
169
143
|
// @param {module:engine/model/treewalker~TreeWalker} walker
|
|
170
144
|
// @param {Boolean} isForward Is the direction in which the selection should be modified is forward.
|
|
171
|
-
function getCorrectWordBreakPosition(
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
offset = walker.position.offset - textNode.startOffset;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return walker.position;
|
|
145
|
+
function getCorrectWordBreakPosition(walker, isForward) {
|
|
146
|
+
let textNode = walker.position.textNode;
|
|
147
|
+
if (textNode) {
|
|
148
|
+
let offset = walker.position.offset - textNode.startOffset;
|
|
149
|
+
while (!isAtWordBoundary(textNode.data, offset, isForward) && !isAtNodeBoundary(textNode, offset, isForward)) {
|
|
150
|
+
walker.next();
|
|
151
|
+
// Check of adjacent text nodes with different attributes (like BOLD).
|
|
152
|
+
// Example : 'foofoo []bar<$text bold="true">bar</$text> bazbaz'
|
|
153
|
+
// should expand to : 'foofoo [bar<$text bold="true">bar</$text>] bazbaz'.
|
|
154
|
+
const nextNode = isForward ? walker.position.nodeAfter : walker.position.nodeBefore;
|
|
155
|
+
// Scan only text nodes. Ignore inline elements (like `<softBreak>`).
|
|
156
|
+
if (nextNode && nextNode.is('$text')) {
|
|
157
|
+
// Check boundary char of an adjacent text node.
|
|
158
|
+
const boundaryChar = nextNode.data.charAt(isForward ? 0 : nextNode.data.length - 1);
|
|
159
|
+
// Go to the next node if the character at the boundary of that node belongs to the same word.
|
|
160
|
+
if (!wordBoundaryCharacters.includes(boundaryChar)) {
|
|
161
|
+
// If adjacent text node belongs to the same word go to it & reset values.
|
|
162
|
+
walker.next();
|
|
163
|
+
textNode = walker.position.textNode;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
offset = walker.position.offset - textNode.startOffset;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return walker.position;
|
|
204
170
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}
|
|
171
|
+
function getSearchRange(start, isForward) {
|
|
172
|
+
const root = start.root;
|
|
173
|
+
const searchEnd = Position._createAt(root, isForward ? 'end' : 0);
|
|
174
|
+
if (isForward) {
|
|
175
|
+
return new Range(start, searchEnd);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
return new Range(searchEnd, start);
|
|
179
|
+
}
|
|
215
180
|
}
|
|
216
|
-
|
|
217
181
|
// Checks if selection is on word boundary.
|
|
218
182
|
//
|
|
219
183
|
// @param {String} data The text node value to investigate.
|
|
220
184
|
// @param {Number} offset Position offset.
|
|
221
185
|
// @param {Boolean} isForward Is the direction in which the selection should be modified is forward.
|
|
222
|
-
function isAtWordBoundary(
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
return wordBoundaryCharacters.includes( data.charAt( offsetToCheck ) );
|
|
186
|
+
function isAtWordBoundary(data, offset, isForward) {
|
|
187
|
+
// The offset to check depends on direction.
|
|
188
|
+
const offsetToCheck = offset + (isForward ? 0 : -1);
|
|
189
|
+
return wordBoundaryCharacters.includes(data.charAt(offsetToCheck));
|
|
227
190
|
}
|
|
228
|
-
|
|
229
191
|
// Checks if selection is on node boundary.
|
|
230
192
|
//
|
|
231
193
|
// @param {module:engine/model/text~Text} textNode The text node to investigate.
|
|
232
194
|
// @param {Number} offset Position offset.
|
|
233
195
|
// @param {Boolean} isForward Is the direction in which the selection should be modified is forward.
|
|
234
|
-
function isAtNodeBoundary(
|
|
235
|
-
|
|
196
|
+
function isAtNodeBoundary(textNode, offset, isForward) {
|
|
197
|
+
return offset === (isForward ? textNode.endOffset : 0);
|
|
236
198
|
}
|