@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,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 );