@ckeditor/ckeditor5-engine 35.0.1 → 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 (124) hide show
  1. package/CHANGELOG.md +4 -4
  2. package/package.json +30 -24
  3. package/src/controller/datacontroller.js +467 -561
  4. package/src/controller/editingcontroller.js +168 -204
  5. package/src/conversion/conversion.js +541 -565
  6. package/src/conversion/conversionhelpers.js +24 -28
  7. package/src/conversion/downcastdispatcher.js +457 -686
  8. package/src/conversion/downcasthelpers.js +1583 -1965
  9. package/src/conversion/mapper.js +518 -707
  10. package/src/conversion/modelconsumable.js +240 -283
  11. package/src/conversion/upcastdispatcher.js +372 -718
  12. package/src/conversion/upcasthelpers.js +707 -818
  13. package/src/conversion/viewconsumable.js +524 -581
  14. package/src/dataprocessor/basichtmlwriter.js +12 -16
  15. package/src/dataprocessor/dataprocessor.js +5 -0
  16. package/src/dataprocessor/htmldataprocessor.js +100 -116
  17. package/src/dataprocessor/htmlwriter.js +1 -18
  18. package/src/dataprocessor/xmldataprocessor.js +116 -137
  19. package/src/dev-utils/model.js +260 -352
  20. package/src/dev-utils/operationreplayer.js +106 -126
  21. package/src/dev-utils/utils.js +34 -51
  22. package/src/dev-utils/view.js +632 -753
  23. package/src/index.js +0 -11
  24. package/src/model/batch.js +111 -127
  25. package/src/model/differ.js +988 -1233
  26. package/src/model/document.js +340 -449
  27. package/src/model/documentfragment.js +327 -364
  28. package/src/model/documentselection.js +996 -1189
  29. package/src/model/element.js +306 -410
  30. package/src/model/history.js +224 -262
  31. package/src/model/item.js +5 -0
  32. package/src/model/liveposition.js +84 -145
  33. package/src/model/liverange.js +108 -185
  34. package/src/model/markercollection.js +379 -480
  35. package/src/model/model.js +883 -1034
  36. package/src/model/node.js +419 -463
  37. package/src/model/nodelist.js +175 -201
  38. package/src/model/operation/attributeoperation.js +153 -182
  39. package/src/model/operation/detachoperation.js +64 -83
  40. package/src/model/operation/insertoperation.js +135 -166
  41. package/src/model/operation/markeroperation.js +114 -140
  42. package/src/model/operation/mergeoperation.js +163 -191
  43. package/src/model/operation/moveoperation.js +157 -187
  44. package/src/model/operation/nooperation.js +28 -38
  45. package/src/model/operation/operation.js +106 -125
  46. package/src/model/operation/operationfactory.js +30 -34
  47. package/src/model/operation/renameoperation.js +109 -135
  48. package/src/model/operation/rootattributeoperation.js +155 -188
  49. package/src/model/operation/splitoperation.js +196 -232
  50. package/src/model/operation/transform.js +1833 -2204
  51. package/src/model/operation/utils.js +140 -204
  52. package/src/model/position.js +899 -1053
  53. package/src/model/range.js +910 -1028
  54. package/src/model/rootelement.js +77 -97
  55. package/src/model/schema.js +1189 -1835
  56. package/src/model/selection.js +745 -862
  57. package/src/model/text.js +90 -114
  58. package/src/model/textproxy.js +204 -240
  59. package/src/model/treewalker.js +316 -397
  60. package/src/model/typecheckable.js +16 -0
  61. package/src/model/utils/autoparagraphing.js +32 -44
  62. package/src/model/utils/deletecontent.js +334 -418
  63. package/src/model/utils/findoptimalinsertionrange.js +25 -36
  64. package/src/model/utils/getselectedcontent.js +96 -118
  65. package/src/model/utils/insertcontent.js +654 -773
  66. package/src/model/utils/insertobject.js +96 -119
  67. package/src/model/utils/modifyselection.js +120 -158
  68. package/src/model/utils/selection-post-fixer.js +153 -201
  69. package/src/model/writer.js +1305 -1474
  70. package/src/view/attributeelement.js +189 -225
  71. package/src/view/containerelement.js +75 -85
  72. package/src/view/document.js +172 -215
  73. package/src/view/documentfragment.js +200 -249
  74. package/src/view/documentselection.js +338 -367
  75. package/src/view/domconverter.js +1370 -1617
  76. package/src/view/downcastwriter.js +1747 -2076
  77. package/src/view/editableelement.js +81 -97
  78. package/src/view/element.js +739 -890
  79. package/src/view/elementdefinition.js +5 -0
  80. package/src/view/emptyelement.js +82 -92
  81. package/src/view/filler.js +35 -50
  82. package/src/view/item.js +5 -0
  83. package/src/view/matcher.js +260 -559
  84. package/src/view/node.js +274 -360
  85. package/src/view/observer/arrowkeysobserver.js +19 -28
  86. package/src/view/observer/bubblingemittermixin.js +120 -263
  87. package/src/view/observer/bubblingeventinfo.js +47 -55
  88. package/src/view/observer/clickobserver.js +7 -13
  89. package/src/view/observer/compositionobserver.js +14 -24
  90. package/src/view/observer/domeventdata.js +57 -67
  91. package/src/view/observer/domeventobserver.js +40 -64
  92. package/src/view/observer/fakeselectionobserver.js +81 -96
  93. package/src/view/observer/focusobserver.js +45 -61
  94. package/src/view/observer/inputobserver.js +7 -13
  95. package/src/view/observer/keyobserver.js +17 -27
  96. package/src/view/observer/mouseobserver.js +7 -14
  97. package/src/view/observer/mutationobserver.js +220 -315
  98. package/src/view/observer/observer.js +81 -102
  99. package/src/view/observer/selectionobserver.js +191 -246
  100. package/src/view/observer/tabobserver.js +23 -36
  101. package/src/view/placeholder.js +128 -173
  102. package/src/view/position.js +350 -401
  103. package/src/view/range.js +453 -513
  104. package/src/view/rawelement.js +85 -112
  105. package/src/view/renderer.js +874 -1018
  106. package/src/view/rooteditableelement.js +80 -90
  107. package/src/view/selection.js +608 -689
  108. package/src/view/styles/background.js +43 -44
  109. package/src/view/styles/border.js +220 -276
  110. package/src/view/styles/margin.js +8 -17
  111. package/src/view/styles/padding.js +8 -16
  112. package/src/view/styles/utils.js +127 -160
  113. package/src/view/stylesmap.js +728 -905
  114. package/src/view/text.js +102 -126
  115. package/src/view/textproxy.js +144 -170
  116. package/src/view/treewalker.js +383 -479
  117. package/src/view/typecheckable.js +19 -0
  118. package/src/view/uielement.js +166 -187
  119. package/src/view/upcastwriter.js +395 -449
  120. package/src/view/view.js +569 -664
  121. package/src/dataprocessor/dataprocessor.jsdoc +0 -64
  122. package/src/model/item.jsdoc +0 -14
  123. package/src/view/elementdefinition.jsdoc +0 -59
  124. 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
  }