@ckeditor/ckeditor5-engine 35.0.1 → 35.2.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 +176 -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 +980 -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 +757 -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 +199 -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,13 +2,10 @@
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/findoptimalinsertionrange
8
7
  */
9
-
10
8
  import first from '@ckeditor/ckeditor5-utils/src/first';
11
-
12
9
  // Returns a model range which is optimal (in terms of UX) for inserting a widget block.
13
10
  //
14
11
  // For instance, if a selection is in the middle of a paragraph, the collapsed range before this paragraph
@@ -32,37 +29,29 @@ import first from '@ckeditor/ckeditor5-utils/src/first';
32
29
  // The `before` value will try to find a position before selection.
33
30
  // The `after` value will try to find a position after selection.
34
31
  // @returns {module:engine/model/range~Range} The optimal range.
35
- export function findOptimalInsertionRange( selection, model, place = 'auto' ) {
36
- const selectedElement = selection.getSelectedElement();
37
-
38
- if ( selectedElement && model.schema.isObject( selectedElement ) && !model.schema.isInline( selectedElement ) ) {
39
- if ( [ 'before', 'after' ].includes( place ) ) {
40
- return model.createRange( model.createPositionAt( selectedElement, place ) );
41
- }
42
-
43
- return model.createRangeOn( selectedElement );
44
- }
45
-
46
- const firstBlock = first( selection.getSelectedBlocks() );
47
-
48
- // There are no block elements within ancestors (in the current limit element).
49
- if ( !firstBlock ) {
50
- return model.createRange( selection.focus );
51
- }
52
-
53
- // If inserting into an empty block – return position in that block. It will get
54
- // replaced with the image by insertContent(). #42.
55
- if ( firstBlock.isEmpty ) {
56
- return model.createRange( model.createPositionAt( firstBlock, 0 ) );
57
- }
58
-
59
- const positionAfter = model.createPositionAfter( firstBlock );
60
-
61
- // If selection is at the end of the block - return position after the block.
62
- if ( selection.focus.isTouching( positionAfter ) ) {
63
- return model.createRange( positionAfter );
64
- }
65
-
66
- // Otherwise, return position before the block.
67
- return model.createRange( model.createPositionBefore( firstBlock ) );
32
+ export function findOptimalInsertionRange(selection, model, place = 'auto') {
33
+ const selectedElement = selection.getSelectedElement();
34
+ if (selectedElement && model.schema.isObject(selectedElement) && !model.schema.isInline(selectedElement)) {
35
+ if (place == 'before' || place == 'after') {
36
+ return model.createRange(model.createPositionAt(selectedElement, place));
37
+ }
38
+ return model.createRangeOn(selectedElement);
39
+ }
40
+ const firstBlock = first(selection.getSelectedBlocks());
41
+ // There are no block elements within ancestors (in the current limit element).
42
+ if (!firstBlock) {
43
+ return model.createRange(selection.focus);
44
+ }
45
+ // If inserting into an empty block return position in that block. It will get
46
+ // replaced with the image by insertContent(). #42.
47
+ if (firstBlock.isEmpty) {
48
+ return model.createRange(model.createPositionAt(firstBlock, 0));
49
+ }
50
+ const positionAfter = model.createPositionAfter(firstBlock);
51
+ // If selection is at the end of the block - return position after the block.
52
+ if (selection.focus.isTouching(positionAfter)) {
53
+ return model.createRange(positionAfter);
54
+ }
55
+ // Otherwise, return position before the block.
56
+ return model.createRange(model.createPositionBefore(firstBlock));
68
57
  }
@@ -2,11 +2,9 @@
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/getselectedcontent
8
7
  */
9
-
10
8
  /**
11
9
  * Gets a clone of the selected content.
12
10
  *
@@ -28,123 +26,103 @@
28
26
  * The selection of which content will be returned.
29
27
  * @returns {module:engine/model/documentfragment~DocumentFragment}
30
28
  */
31
- export default function getSelectedContent( model, selection ) {
32
- return model.change( writer => {
33
- const frag = writer.createDocumentFragment();
34
- const range = selection.getFirstRange();
35
-
36
- if ( !range || range.isCollapsed ) {
37
- return frag;
38
- }
39
-
40
- const root = range.start.root;
41
- const commonPath = range.start.getCommonPath( range.end );
42
- const commonParent = root.getNodeByPath( commonPath );
43
-
44
- // ## 1st step
45
- //
46
- // First, we'll clone a fragment represented by a minimal flat range
47
- // containing the original range to be cloned.
48
- // E.g. let's consider such a range:
49
- //
50
- // <p>x</p><quote><p>y</p><h>fir[st</h></quote><p>se]cond</p><p>z</p>
51
- //
52
- // A minimal flat range containing this one is:
53
- //
54
- // <p>x</p>[<quote><p>y</p><h>first</h></quote><p>second</p>]<p>z</p>
55
- //
56
- // We can easily clone this structure, preserving e.g. the <quote> element.
57
- let flatSubtreeRange;
58
-
59
- if ( range.start.parent == range.end.parent ) {
60
- // The original range is flat, so take it.
61
- flatSubtreeRange = range;
62
- } else {
63
- flatSubtreeRange = writer.createRange(
64
- writer.createPositionAt( commonParent, range.start.path[ commonPath.length ] ),
65
- writer.createPositionAt( commonParent, range.end.path[ commonPath.length ] + 1 )
66
- );
67
- }
68
-
69
- const howMany = flatSubtreeRange.end.offset - flatSubtreeRange.start.offset;
70
-
71
- // Clone the whole contents.
72
- for ( const item of flatSubtreeRange.getItems( { shallow: true } ) ) {
73
- if ( item.is( '$textProxy' ) ) {
74
- writer.appendText( item.data, item.getAttributes(), frag );
75
- } else {
76
- writer.append( writer.cloneElement( item, true ), frag );
77
- }
78
- }
79
-
80
- // ## 2nd step
81
- //
82
- // If the original range wasn't flat, then we need to remove the excess nodes from the both ends of the cloned fragment.
83
- //
84
- // For example, for the range shown in the 1st step comment, we need to remove these pieces:
85
- //
86
- // <quote>[<p>y</p>]<h>[fir]st</h></quote><p>se[cond]</p>
87
- //
88
- // So this will be the final copied content:
89
- //
90
- // <quote><h>st</h></quote><p>se</p>
91
- //
92
- // In order to do that, we remove content from these two ranges:
93
- //
94
- // [<quote><p>y</p><h>fir]st</h></quote><p>se[cond</p>]
95
- if ( flatSubtreeRange != range ) {
96
- // Find the position of the original range in the cloned fragment.
97
- const newRange = range._getTransformedByMove( flatSubtreeRange.start, writer.createPositionAt( frag, 0 ), howMany )[ 0 ];
98
-
99
- const leftExcessRange = writer.createRange( writer.createPositionAt( frag, 0 ), newRange.start );
100
- const rightExcessRange = writer.createRange( newRange.end, writer.createPositionAt( frag, 'end' ) );
101
-
102
- removeRangeContent( rightExcessRange, writer );
103
- removeRangeContent( leftExcessRange, writer );
104
- }
105
-
106
- return frag;
107
- } );
29
+ export default function getSelectedContent(model, selection) {
30
+ return model.change(writer => {
31
+ const frag = writer.createDocumentFragment();
32
+ const range = selection.getFirstRange();
33
+ if (!range || range.isCollapsed) {
34
+ return frag;
35
+ }
36
+ const root = range.start.root;
37
+ const commonPath = range.start.getCommonPath(range.end);
38
+ const commonParent = root.getNodeByPath(commonPath);
39
+ // ## 1st step
40
+ //
41
+ // First, we'll clone a fragment represented by a minimal flat range
42
+ // containing the original range to be cloned.
43
+ // E.g. let's consider such a range:
44
+ //
45
+ // <p>x</p><quote><p>y</p><h>fir[st</h></quote><p>se]cond</p><p>z</p>
46
+ //
47
+ // A minimal flat range containing this one is:
48
+ //
49
+ // <p>x</p>[<quote><p>y</p><h>first</h></quote><p>second</p>]<p>z</p>
50
+ //
51
+ // We can easily clone this structure, preserving e.g. the <quote> element.
52
+ let flatSubtreeRange;
53
+ if (range.start.parent == range.end.parent) {
54
+ // The original range is flat, so take it.
55
+ flatSubtreeRange = range;
56
+ }
57
+ else {
58
+ flatSubtreeRange = writer.createRange(writer.createPositionAt(commonParent, range.start.path[commonPath.length]), writer.createPositionAt(commonParent, range.end.path[commonPath.length] + 1));
59
+ }
60
+ const howMany = flatSubtreeRange.end.offset - flatSubtreeRange.start.offset;
61
+ // Clone the whole contents.
62
+ for (const item of flatSubtreeRange.getItems({ shallow: true })) {
63
+ if (item.is('$textProxy')) {
64
+ writer.appendText(item.data, item.getAttributes(), frag);
65
+ }
66
+ else {
67
+ writer.append(writer.cloneElement(item, true), frag);
68
+ }
69
+ }
70
+ // ## 2nd step
71
+ //
72
+ // If the original range wasn't flat, then we need to remove the excess nodes from the both ends of the cloned fragment.
73
+ //
74
+ // For example, for the range shown in the 1st step comment, we need to remove these pieces:
75
+ //
76
+ // <quote>[<p>y</p>]<h>[fir]st</h></quote><p>se[cond]</p>
77
+ //
78
+ // So this will be the final copied content:
79
+ //
80
+ // <quote><h>st</h></quote><p>se</p>
81
+ //
82
+ // In order to do that, we remove content from these two ranges:
83
+ //
84
+ // [<quote><p>y</p><h>fir]st</h></quote><p>se[cond</p>]
85
+ if (flatSubtreeRange != range) {
86
+ // Find the position of the original range in the cloned fragment.
87
+ const newRange = range._getTransformedByMove(flatSubtreeRange.start, writer.createPositionAt(frag, 0), howMany)[0];
88
+ const leftExcessRange = writer.createRange(writer.createPositionAt(frag, 0), newRange.start);
89
+ const rightExcessRange = writer.createRange(newRange.end, writer.createPositionAt(frag, 'end'));
90
+ removeRangeContent(rightExcessRange, writer);
91
+ removeRangeContent(leftExcessRange, writer);
92
+ }
93
+ return frag;
94
+ });
108
95
  }
109
-
110
96
  // After https://github.com/ckeditor/ckeditor5-engine/issues/690 is fixed,
111
97
  // this function will, most likely, be able to rewritten using getMinimalFlatRanges().
112
- function removeRangeContent( range, writer ) {
113
- const parentsToCheck = [];
114
-
115
- Array.from( range.getItems( { direction: 'backward' } ) )
116
- // We should better store ranges because text proxies will lose integrity
117
- // with the text nodes when we'll start removing content.
118
- .map( item => writer.createRangeOn( item ) )
119
- // Filter only these items which are fully contained in the passed range.
120
- //
121
- // E.g. for the following range: [<quote><p>y</p><h>fir]st</h>
122
- // the walker will return the entire <h> element, when only the "fir" item inside it is fully contained.
123
- .filter( itemRange => {
124
- // We should be able to use Range.containsRange, but https://github.com/ckeditor/ckeditor5-engine/issues/691.
125
- const contained =
126
- ( itemRange.start.isAfter( range.start ) || itemRange.start.isEqual( range.start ) ) &&
127
- ( itemRange.end.isBefore( range.end ) || itemRange.end.isEqual( range.end ) );
128
-
129
- return contained;
130
- } )
131
- .forEach( itemRange => {
132
- parentsToCheck.push( itemRange.start.parent );
133
-
134
- writer.remove( itemRange );
135
- } );
136
-
137
- // Remove ancestors of the removed items if they turned to be empty now
138
- // (their whole content was contained in the range).
139
- parentsToCheck.forEach( parentToCheck => {
140
- let parent = parentToCheck;
141
-
142
- while ( parent.parent && parent.isEmpty ) {
143
- const removeRange = writer.createRangeOn( parent );
144
-
145
- parent = parent.parent;
146
-
147
- writer.remove( removeRange );
148
- }
149
- } );
98
+ function removeRangeContent(range, writer) {
99
+ const parentsToCheck = [];
100
+ Array.from(range.getItems({ direction: 'backward' }))
101
+ // We should better store ranges because text proxies will lose integrity
102
+ // with the text nodes when we'll start removing content.
103
+ .map(item => writer.createRangeOn(item))
104
+ // Filter only these items which are fully contained in the passed range.
105
+ //
106
+ // E.g. for the following range: [<quote><p>y</p><h>fir]st</h>
107
+ // the walker will return the entire <h> element, when only the "fir" item inside it is fully contained.
108
+ .filter(itemRange => {
109
+ // We should be able to use Range.containsRange, but https://github.com/ckeditor/ckeditor5-engine/issues/691.
110
+ const contained = (itemRange.start.isAfter(range.start) || itemRange.start.isEqual(range.start)) &&
111
+ (itemRange.end.isBefore(range.end) || itemRange.end.isEqual(range.end));
112
+ return contained;
113
+ })
114
+ .forEach(itemRange => {
115
+ parentsToCheck.push(itemRange.start.parent);
116
+ writer.remove(itemRange);
117
+ });
118
+ // Remove ancestors of the removed items if they turned to be empty now
119
+ // (their whole content was contained in the range).
120
+ parentsToCheck.forEach(parentToCheck => {
121
+ let parent = parentToCheck;
122
+ while (parent.parent && parent.isEmpty) {
123
+ const removeRange = writer.createRangeOn(parent);
124
+ parent = parent.parent;
125
+ writer.remove(removeRange);
126
+ }
127
+ });
150
128
  }