@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,31 +2,18 @@
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/controller/editingcontroller
8
7
  */
9
-
10
8
  import RootEditableElement from '../view/rooteditableelement';
11
9
  import View from '../view/view';
12
10
  import Mapper from '../conversion/mapper';
13
11
  import DowncastDispatcher from '../conversion/downcastdispatcher';
14
- import {
15
- clearAttributes,
16
- convertCollapsedSelection,
17
- convertRangeSelection,
18
- insertAttributesAndChildren,
19
- insertText,
20
- remove
21
- } from '../conversion/downcasthelpers';
22
-
23
- import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
24
- import mix from '@ckeditor/ckeditor5-utils/src/mix';
12
+ import { clearAttributes, convertCollapsedSelection, convertRangeSelection, insertAttributesAndChildren, insertText, remove } from '../conversion/downcasthelpers';
13
+ import { Observable } from '@ckeditor/ckeditor5-utils/src/observablemixin';
25
14
  import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
26
15
  import { convertSelectionChange } from '../conversion/upcasthelpers';
27
-
28
16
  // @if CK_DEBUG_ENGINE // const { dumpTrees, initDocumentDumping } = require( '../dev-utils/utils' );
29
-
30
17
  /**
31
18
  * A controller for the editing pipeline. The editing pipeline controls the {@link ~EditingController#model model} rendering,
32
19
  * including selection handling. It also creates the {@link ~EditingController#view view} which builds a
@@ -34,193 +21,170 @@ import { convertSelectionChange } from '../conversion/upcasthelpers';
34
21
  *
35
22
  * @mixes module:utils/observablemixin~ObservableMixin
36
23
  */
37
- export default class EditingController {
38
- /**
39
- * Creates an editing controller instance.
40
- *
41
- * @param {module:engine/model/model~Model} model Editing model.
42
- * @param {module:engine/view/stylesmap~StylesProcessor} stylesProcessor The styles processor instance.
43
- */
44
- constructor( model, stylesProcessor ) {
45
- /**
46
- * Editor model.
47
- *
48
- * @readonly
49
- * @member {module:engine/model/model~Model}
50
- */
51
- this.model = model;
52
-
53
- /**
54
- * Editing view controller.
55
- *
56
- * @readonly
57
- * @member {module:engine/view/view~View}
58
- */
59
- this.view = new View( stylesProcessor );
60
-
61
- /**
62
- * A mapper that describes the model-view binding.
63
- *
64
- * @readonly
65
- * @member {module:engine/conversion/mapper~Mapper}
66
- */
67
- this.mapper = new Mapper();
68
-
69
- /**
70
- * Downcast dispatcher that converts changes from the model to the {@link #view editing view}.
71
- *
72
- * @readonly
73
- * @member {module:engine/conversion/downcastdispatcher~DowncastDispatcher} #downcastDispatcher
74
- */
75
- this.downcastDispatcher = new DowncastDispatcher( {
76
- mapper: this.mapper,
77
- schema: model.schema
78
- } );
79
-
80
- const doc = this.model.document;
81
- const selection = doc.selection;
82
- const markers = this.model.markers;
83
-
84
- // When plugins listen on model changes (on selection change, post fixers, etc.) and change the view as a result of
85
- // the model's change, they might trigger view rendering before the conversion is completed (e.g. before the selection
86
- // is converted). We disable rendering for the length of the outermost model change() block to prevent that.
87
- //
88
- // See https://github.com/ckeditor/ckeditor5-engine/issues/1528
89
- this.listenTo( this.model, '_beforeChanges', () => {
90
- this.view._disableRendering( true );
91
- }, { priority: 'highest' } );
92
-
93
- this.listenTo( this.model, '_afterChanges', () => {
94
- this.view._disableRendering( false );
95
- }, { priority: 'lowest' } );
96
-
97
- // Whenever model document is changed, convert those changes to the view (using model.Document#differ).
98
- // Do it on 'low' priority, so changes are converted after other listeners did their job.
99
- // Also convert model selection.
100
- this.listenTo( doc, 'change', () => {
101
- this.view.change( writer => {
102
- this.downcastDispatcher.convertChanges( doc.differ, markers, writer );
103
- this.downcastDispatcher.convertSelection( selection, markers, writer );
104
- } );
105
- }, { priority: 'low' } );
106
-
107
- // Convert selection from the view to the model when it changes in the view.
108
- this.listenTo( this.view.document, 'selectionChange', convertSelectionChange( this.model, this.mapper ) );
109
-
110
- // Attach default model converters.
111
- this.downcastDispatcher.on( 'insert:$text', insertText(), { priority: 'lowest' } );
112
- this.downcastDispatcher.on( 'insert', insertAttributesAndChildren(), { priority: 'lowest' } );
113
- this.downcastDispatcher.on( 'remove', remove(), { priority: 'low' } );
114
-
115
- // Attach default model selection converters.
116
- this.downcastDispatcher.on( 'selection', clearAttributes(), { priority: 'high' } );
117
- this.downcastDispatcher.on( 'selection', convertRangeSelection(), { priority: 'low' } );
118
- this.downcastDispatcher.on( 'selection', convertCollapsedSelection(), { priority: 'low' } );
119
-
120
- // Binds {@link module:engine/view/document~Document#roots view roots collection} to
121
- // {@link module:engine/model/document~Document#roots model roots collection} so creating
122
- // model root automatically creates corresponding view root.
123
- this.view.document.roots.bindTo( this.model.document.roots ).using( root => {
124
- // $graveyard is a special root that has no reflection in the view.
125
- if ( root.rootName == '$graveyard' ) {
126
- return null;
127
- }
128
-
129
- const viewRoot = new RootEditableElement( this.view.document, root.name );
130
-
131
- viewRoot.rootName = root.rootName;
132
- this.mapper.bindElements( root, viewRoot );
133
-
134
- return viewRoot;
135
- } );
136
-
137
- // @if CK_DEBUG_ENGINE // initDocumentDumping( this.model.document );
138
- // @if CK_DEBUG_ENGINE // initDocumentDumping( this.view.document );
139
-
140
- // @if CK_DEBUG_ENGINE // dumpTrees( this.model.document, this.model.document.version );
141
- // @if CK_DEBUG_ENGINE // dumpTrees( this.view.document, this.model.document.version );
142
-
143
- // @if CK_DEBUG_ENGINE // this.model.document.on( 'change', () => {
144
- // @if CK_DEBUG_ENGINE // dumpTrees( this.view.document, this.model.document.version );
145
- // @if CK_DEBUG_ENGINE // }, { priority: 'lowest' } );
146
- }
147
-
148
- /**
149
- * Removes all event listeners attached to the `EditingController`. Destroys all objects created
150
- * by `EditingController` that need to be destroyed.
151
- */
152
- destroy() {
153
- this.view.destroy();
154
- this.stopListening();
155
- }
156
-
157
- /**
158
- * Calling this method will refresh the marker by triggering the downcast conversion for it.
159
- *
160
- * Reconverting the marker is useful when you want to change its {@link module:engine/view/element~Element view element}
161
- * without changing any marker data. For instance:
162
- *
163
- * let isCommentActive = false;
164
- *
165
- * model.conversion.markerToHighlight( {
166
- * model: 'comment',
167
- * view: data => {
168
- * const classes = [ 'comment-marker' ];
169
- *
170
- * if ( isCommentActive ) {
171
- * classes.push( 'comment-marker--active' );
172
- * }
173
- *
174
- * return { classes };
175
- * }
176
- * } );
177
- *
178
- * // ...
179
- *
180
- * // Change the property that indicates if marker is displayed as active or not.
181
- * isCommentActive = true;
182
- *
183
- * // Reconverting will downcast and synchronize the marker with the new isCommentActive state value.
184
- * editor.editing.reconvertMarker( 'comment' );
185
- *
186
- * **Note**: If you want to reconvert a model item, use {@link #reconvertItem} instead.
187
- *
188
- * @param {String|module:engine/model/markercollection~Marker} markerOrName Name of a marker to update, or a marker instance.
189
- */
190
- reconvertMarker( markerOrName ) {
191
- const markerName = typeof markerOrName == 'string' ? markerOrName : markerOrName.name;
192
- const currentMarker = this.model.markers.get( markerName );
193
-
194
- if ( !currentMarker ) {
195
- /**
196
- * The marker with the provided name does not exist and cannot be reconverted.
197
- *
198
- * @error editingcontroller-reconvertmarker-marker-not-exist
199
- * @param {String} markerName The name of the reconverted marker.
200
- */
201
- throw new CKEditorError( 'editingcontroller-reconvertmarker-marker-not-exist', this, { markerName } );
202
- }
203
-
204
- this.model.change( () => {
205
- this.model.markers._refresh( currentMarker );
206
- } );
207
- }
208
-
209
- /**
210
- * Calling this method will downcast a model item on demand (by requesting a refresh in the {@link module:engine/model/differ~Differ}).
211
- *
212
- * You can use it if you want the view representation of a specific item updated as a response to external modifications. For instance,
213
- * when the view structure depends not only on the associated model data but also on some external state.
214
- *
215
- * **Note**: If you want to reconvert a model marker, use {@link #reconvertMarker} instead.
216
- *
217
- * @param {module:engine/model/item~Item} item Item to refresh.
218
- */
219
- reconvertItem( item ) {
220
- this.model.change( () => {
221
- this.model.document.differ._refreshItem( item );
222
- } );
223
- }
24
+ export default class EditingController extends Observable {
25
+ /**
26
+ * Creates an editing controller instance.
27
+ *
28
+ * @param {module:engine/model/model~Model} model Editing model.
29
+ * @param {module:engine/view/stylesmap~StylesProcessor} stylesProcessor The styles processor instance.
30
+ */
31
+ constructor(model, stylesProcessor) {
32
+ super();
33
+ /**
34
+ * Editor model.
35
+ *
36
+ * @readonly
37
+ * @member {module:engine/model/model~Model}
38
+ */
39
+ this.model = model;
40
+ /**
41
+ * Editing view controller.
42
+ *
43
+ * @readonly
44
+ * @member {module:engine/view/view~View}
45
+ */
46
+ this.view = new View(stylesProcessor);
47
+ /**
48
+ * A mapper that describes the model-view binding.
49
+ *
50
+ * @readonly
51
+ * @member {module:engine/conversion/mapper~Mapper}
52
+ */
53
+ this.mapper = new Mapper();
54
+ /**
55
+ * Downcast dispatcher that converts changes from the model to the {@link #view editing view}.
56
+ *
57
+ * @readonly
58
+ * @member {module:engine/conversion/downcastdispatcher~DowncastDispatcher} #downcastDispatcher
59
+ */
60
+ this.downcastDispatcher = new DowncastDispatcher({
61
+ mapper: this.mapper,
62
+ schema: model.schema
63
+ });
64
+ const doc = this.model.document;
65
+ const selection = doc.selection;
66
+ const markers = this.model.markers;
67
+ // When plugins listen on model changes (on selection change, post fixers, etc.) and change the view as a result of
68
+ // the model's change, they might trigger view rendering before the conversion is completed (e.g. before the selection
69
+ // is converted). We disable rendering for the length of the outermost model change() block to prevent that.
70
+ //
71
+ // See https://github.com/ckeditor/ckeditor5-engine/issues/1528
72
+ this.listenTo(this.model, '_beforeChanges', () => {
73
+ this.view._disableRendering(true);
74
+ }, { priority: 'highest' });
75
+ this.listenTo(this.model, '_afterChanges', () => {
76
+ this.view._disableRendering(false);
77
+ }, { priority: 'lowest' });
78
+ // Whenever model document is changed, convert those changes to the view (using model.Document#differ).
79
+ // Do it on 'low' priority, so changes are converted after other listeners did their job.
80
+ // Also convert model selection.
81
+ this.listenTo(doc, 'change', () => {
82
+ this.view.change(writer => {
83
+ this.downcastDispatcher.convertChanges(doc.differ, markers, writer);
84
+ this.downcastDispatcher.convertSelection(selection, markers, writer);
85
+ });
86
+ }, { priority: 'low' });
87
+ // Convert selection from the view to the model when it changes in the view.
88
+ this.listenTo(this.view.document, 'selectionChange', convertSelectionChange(this.model, this.mapper));
89
+ // Attach default model converters.
90
+ this.downcastDispatcher.on('insert:$text', insertText(), { priority: 'lowest' });
91
+ this.downcastDispatcher.on('insert', insertAttributesAndChildren(), { priority: 'lowest' });
92
+ this.downcastDispatcher.on('remove', remove(), { priority: 'low' });
93
+ // Attach default model selection converters.
94
+ this.downcastDispatcher.on('selection', clearAttributes(), { priority: 'high' });
95
+ this.downcastDispatcher.on('selection', convertRangeSelection(), { priority: 'low' });
96
+ this.downcastDispatcher.on('selection', convertCollapsedSelection(), { priority: 'low' });
97
+ // Binds {@link module:engine/view/document~Document#roots view roots collection} to
98
+ // {@link module:engine/model/document~Document#roots model roots collection} so creating
99
+ // model root automatically creates corresponding view root.
100
+ this.view.document.roots.bindTo(this.model.document.roots).using(root => {
101
+ // $graveyard is a special root that has no reflection in the view.
102
+ if (root.rootName == '$graveyard') {
103
+ return null;
104
+ }
105
+ const viewRoot = new RootEditableElement(this.view.document, root.name);
106
+ viewRoot.rootName = root.rootName;
107
+ this.mapper.bindElements(root, viewRoot);
108
+ return viewRoot;
109
+ });
110
+ // @if CK_DEBUG_ENGINE // initDocumentDumping( this.model.document );
111
+ // @if CK_DEBUG_ENGINE // initDocumentDumping( this.view.document );
112
+ // @if CK_DEBUG_ENGINE // dumpTrees( this.model.document, this.model.document.version );
113
+ // @if CK_DEBUG_ENGINE // dumpTrees( this.view.document, this.model.document.version );
114
+ // @if CK_DEBUG_ENGINE // this.model.document.on( 'change', () => {
115
+ // @if CK_DEBUG_ENGINE // dumpTrees( this.view.document, this.model.document.version );
116
+ // @if CK_DEBUG_ENGINE // }, { priority: 'lowest' } );
117
+ }
118
+ /**
119
+ * Removes all event listeners attached to the `EditingController`. Destroys all objects created
120
+ * by `EditingController` that need to be destroyed.
121
+ */
122
+ destroy() {
123
+ this.view.destroy();
124
+ this.stopListening();
125
+ }
126
+ /**
127
+ * Calling this method will refresh the marker by triggering the downcast conversion for it.
128
+ *
129
+ * Reconverting the marker is useful when you want to change its {@link module:engine/view/element~Element view element}
130
+ * without changing any marker data. For instance:
131
+ *
132
+ * let isCommentActive = false;
133
+ *
134
+ * model.conversion.markerToHighlight( {
135
+ * model: 'comment',
136
+ * view: data => {
137
+ * const classes = [ 'comment-marker' ];
138
+ *
139
+ * if ( isCommentActive ) {
140
+ * classes.push( 'comment-marker--active' );
141
+ * }
142
+ *
143
+ * return { classes };
144
+ * }
145
+ * } );
146
+ *
147
+ * // ...
148
+ *
149
+ * // Change the property that indicates if marker is displayed as active or not.
150
+ * isCommentActive = true;
151
+ *
152
+ * // Reconverting will downcast and synchronize the marker with the new isCommentActive state value.
153
+ * editor.editing.reconvertMarker( 'comment' );
154
+ *
155
+ * **Note**: If you want to reconvert a model item, use {@link #reconvertItem} instead.
156
+ *
157
+ * @param {String|module:engine/model/markercollection~Marker} markerOrName Name of a marker to update, or a marker instance.
158
+ */
159
+ reconvertMarker(markerOrName) {
160
+ const markerName = typeof markerOrName == 'string' ? markerOrName : markerOrName.name;
161
+ const currentMarker = this.model.markers.get(markerName);
162
+ if (!currentMarker) {
163
+ /**
164
+ * The marker with the provided name does not exist and cannot be reconverted.
165
+ *
166
+ * @error editingcontroller-reconvertmarker-marker-not-exist
167
+ * @param {String} markerName The name of the reconverted marker.
168
+ */
169
+ throw new CKEditorError('editingcontroller-reconvertmarker-marker-not-exist', this, { markerName });
170
+ }
171
+ this.model.change(() => {
172
+ this.model.markers._refresh(currentMarker);
173
+ });
174
+ }
175
+ /**
176
+ * Calling this method will downcast a model item on demand (by requesting a refresh in the {@link module:engine/model/differ~Differ}).
177
+ *
178
+ * You can use it if you want the view representation of a specific item updated as a response to external modifications. For instance,
179
+ * when the view structure depends not only on the associated model data but also on some external state.
180
+ *
181
+ * **Note**: If you want to reconvert a model marker, use {@link #reconvertMarker} instead.
182
+ *
183
+ * @param {module:engine/model/item~Item} item Item to refresh.
184
+ */
185
+ reconvertItem(item) {
186
+ this.model.change(() => {
187
+ this.model.document.differ._refreshItem(item);
188
+ });
189
+ }
224
190
  }
225
-
226
- mix( EditingController, ObservableMixin );