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