@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,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
  }