@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,18 +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/view/observer/mutationobserver
8
7
  */
9
-
10
8
  /* globals window */
11
-
12
9
  import Observer from './observer';
13
10
  import ViewSelection from '../selection';
14
11
  import { startsWithFiller, getDataWithoutFiller } from '../filler';
15
12
  import { isEqualWith } from 'lodash-es';
16
-
17
13
  /**
18
14
  * Mutation observer class observes changes in the DOM, fires {@link module:engine/view/document~Document#event:mutations} event, mark view
19
15
  * elements as changed and call {@link module:engine/view/renderer~Renderer#render}.
@@ -31,315 +27,224 @@ import { isEqualWith } from 'lodash-es';
31
27
  * @extends module:engine/view/observer/observer~Observer
32
28
  */
33
29
  export default class MutationObserver extends Observer {
34
- constructor( view ) {
35
- super( view );
36
-
37
- /**
38
- * Native mutation observer config.
39
- *
40
- * @private
41
- * @member {Object}
42
- */
43
- this._config = {
44
- childList: true,
45
- characterData: true,
46
- characterDataOldValue: true,
47
- subtree: true
48
- };
49
-
50
- /**
51
- * Reference to the {@link module:engine/view/view~View#domConverter}.
52
- *
53
- * @member {module:engine/view/domconverter~DomConverter}
54
- */
55
- this.domConverter = view.domConverter;
56
-
57
- /**
58
- * Reference to the {@link module:engine/view/view~View#_renderer}.
59
- *
60
- * @member {module:engine/view/renderer~Renderer}
61
- */
62
- this.renderer = view._renderer;
63
-
64
- /**
65
- * Observed DOM elements.
66
- *
67
- * @private
68
- * @member {Array.<HTMLElement>}
69
- */
70
- this._domElements = [];
71
-
72
- /**
73
- * Native mutation observer.
74
- *
75
- * @private
76
- * @member {MutationObserver}
77
- */
78
- this._mutationObserver = new window.MutationObserver( this._onMutations.bind( this ) );
79
- }
80
-
81
- /**
82
- * Synchronously fires {@link module:engine/view/document~Document#event:mutations} event with all mutations in record queue.
83
- * At the same time empties the queue so mutations will not be fired twice.
84
- */
85
- flush() {
86
- this._onMutations( this._mutationObserver.takeRecords() );
87
- }
88
-
89
- /**
90
- * @inheritDoc
91
- */
92
- observe( domElement ) {
93
- this._domElements.push( domElement );
94
-
95
- if ( this.isEnabled ) {
96
- this._mutationObserver.observe( domElement, this._config );
97
- }
98
- }
99
-
100
- /**
101
- * @inheritDoc
102
- */
103
- enable() {
104
- super.enable();
105
-
106
- for ( const domElement of this._domElements ) {
107
- this._mutationObserver.observe( domElement, this._config );
108
- }
109
- }
110
-
111
- /**
112
- * @inheritDoc
113
- */
114
- disable() {
115
- super.disable();
116
-
117
- this._mutationObserver.disconnect();
118
- }
119
-
120
- /**
121
- * @inheritDoc
122
- */
123
- destroy() {
124
- super.destroy();
125
-
126
- this._mutationObserver.disconnect();
127
- }
128
-
129
- /**
130
- * Handles mutations. Deduplicates, mark view elements to sync, fire event and call render.
131
- *
132
- * @private
133
- * @param {Array.<Object>} domMutations Array of native mutations.
134
- */
135
- _onMutations( domMutations ) {
136
- // As a result of this.flush() we can have an empty collection.
137
- if ( domMutations.length === 0 ) {
138
- return;
139
- }
140
-
141
- const domConverter = this.domConverter;
142
-
143
- // Use map and set for deduplication.
144
- const mutatedTexts = new Map();
145
- const mutatedElements = new Set();
146
-
147
- // Handle `childList` mutations first, so we will be able to check if the `characterData` mutation is in the
148
- // element with changed structure anyway.
149
- for ( const mutation of domMutations ) {
150
- if ( mutation.type === 'childList' ) {
151
- const element = domConverter.mapDomToView( mutation.target );
152
-
153
- // Do not collect mutations from UIElements and RawElements.
154
- if ( element && ( element.is( 'uiElement' ) || element.is( 'rawElement' ) ) ) {
155
- continue;
156
- }
157
-
158
- if ( element && !this._isBogusBrMutation( mutation ) ) {
159
- mutatedElements.add( element );
160
- }
161
- }
162
- }
163
-
164
- // Handle `characterData` mutations later, when we have the full list of nodes which changed structure.
165
- for ( const mutation of domMutations ) {
166
- const element = domConverter.mapDomToView( mutation.target );
167
-
168
- // Do not collect mutations from UIElements and RawElements.
169
- if ( element && ( element.is( 'uiElement' ) || element.is( 'rawElement' ) ) ) {
170
- continue;
171
- }
172
-
173
- if ( mutation.type === 'characterData' ) {
174
- const text = domConverter.findCorrespondingViewText( mutation.target );
175
-
176
- if ( text && !mutatedElements.has( text.parent ) ) {
177
- // Use text as a key, for deduplication. If there will be another mutation on the same text element
178
- // we will have only one in the map.
179
- mutatedTexts.set( text, {
180
- type: 'text',
181
- oldText: text.data,
182
- newText: getDataWithoutFiller( mutation.target ),
183
- node: text
184
- } );
185
- }
186
- // When we added first letter to the text node which had only inline filler, for the DOM it is mutation
187
- // on text, but for the view, where filler text node did not existed, new text node was created, so we
188
- // need to fire 'children' mutation instead of 'text'.
189
- else if ( !text && startsWithFiller( mutation.target ) ) {
190
- mutatedElements.add( domConverter.mapDomToView( mutation.target.parentNode ) );
191
- }
192
- }
193
- }
194
-
195
- // Now we build the list of mutations to fire and mark elements. We did not do it earlier to avoid marking the
196
- // same node multiple times in case of duplication.
197
-
198
- // List of mutations we will fire.
199
- const viewMutations = [];
200
-
201
- for ( const mutatedText of mutatedTexts.values() ) {
202
- this.renderer.markToSync( 'text', mutatedText.node );
203
- viewMutations.push( mutatedText );
204
- }
205
-
206
- for ( const viewElement of mutatedElements ) {
207
- const domElement = domConverter.mapViewToDom( viewElement );
208
- const viewChildren = Array.from( viewElement.getChildren() );
209
- const newViewChildren = Array.from( domConverter.domChildrenToView( domElement, { withChildren: false } ) );
210
-
211
- // It may happen that as a result of many changes (sth was inserted and then removed),
212
- // both elements haven't really changed. #1031
213
- if ( !isEqualWith( viewChildren, newViewChildren, sameNodes ) ) {
214
- this.renderer.markToSync( 'children', viewElement );
215
-
216
- viewMutations.push( {
217
- type: 'children',
218
- oldChildren: viewChildren,
219
- newChildren: newViewChildren,
220
- node: viewElement
221
- } );
222
- }
223
- }
224
-
225
- // Retrieve `domSelection` using `ownerDocument` of one of mutated nodes.
226
- // There should not be simultaneous mutation in multiple documents, so it's fine.
227
- const domSelection = domMutations[ 0 ].target.ownerDocument.getSelection();
228
-
229
- let viewSelection = null;
230
-
231
- if ( domSelection && domSelection.anchorNode ) {
232
- // If `domSelection` is inside a dom node that is already bound to a view node from view tree, get
233
- // corresponding selection in the view and pass it together with `viewMutations`. The `viewSelection` may
234
- // be used by features handling mutations.
235
- // Only one range is supported.
236
-
237
- const viewSelectionAnchor = domConverter.domPositionToView( domSelection.anchorNode, domSelection.anchorOffset );
238
- const viewSelectionFocus = domConverter.domPositionToView( domSelection.focusNode, domSelection.focusOffset );
239
-
240
- // Anchor and focus has to be properly mapped to view.
241
- if ( viewSelectionAnchor && viewSelectionFocus ) {
242
- viewSelection = new ViewSelection( viewSelectionAnchor );
243
- viewSelection.setFocus( viewSelectionFocus );
244
- }
245
- }
246
-
247
- // In case only non-relevant mutations were recorded it skips the event and force render (#5600).
248
- if ( viewMutations.length ) {
249
- this.document.fire( 'mutations', viewMutations, viewSelection );
250
-
251
- // If nothing changes on `mutations` event, at this point we have "dirty DOM" (changed) and de-synched
252
- // view (which has not been changed). In order to "reset DOM" we render the view again.
253
- this.view.forceRender();
254
- }
255
-
256
- function sameNodes( child1, child2 ) {
257
- // First level of comparison (array of children vs array of children) – use the Lodash's default behavior.
258
- if ( Array.isArray( child1 ) ) {
259
- return;
260
- }
261
-
262
- // Elements.
263
- if ( child1 === child2 ) {
264
- return true;
265
- }
266
- // Texts.
267
- else if ( child1.is( '$text' ) && child2.is( '$text' ) ) {
268
- return child1.data === child2.data;
269
- }
270
-
271
- // Not matching types.
272
- return false;
273
- }
274
- }
275
-
276
- /**
277
- * Checks if mutation was generated by the browser inserting bogus br on the end of the block element.
278
- * Such mutations are generated while pressing space or performing native spellchecker correction
279
- * on the end of the block element in Firefox browser.
280
- *
281
- * @private
282
- * @param {Object} mutation Native mutation object.
283
- * @returns {Boolean}
284
- */
285
- _isBogusBrMutation( mutation ) {
286
- let addedNode = null;
287
-
288
- // Check if mutation added only one node on the end of its parent.
289
- if ( mutation.nextSibling === null && mutation.removedNodes.length === 0 && mutation.addedNodes.length == 1 ) {
290
- addedNode = this.domConverter.domToView( mutation.addedNodes[ 0 ], {
291
- withChildren: false
292
- } );
293
- }
294
-
295
- return addedNode && addedNode.is( 'element', 'br' );
296
- }
30
+ constructor(view) {
31
+ super(view);
32
+ /**
33
+ * Native mutation observer config.
34
+ *
35
+ * @private
36
+ * @member {Object}
37
+ */
38
+ this._config = {
39
+ childList: true,
40
+ characterData: true,
41
+ characterDataOldValue: true,
42
+ subtree: true
43
+ };
44
+ /**
45
+ * Reference to the {@link module:engine/view/view~View#domConverter}.
46
+ *
47
+ * @member {module:engine/view/domconverter~DomConverter}
48
+ */
49
+ this.domConverter = view.domConverter;
50
+ /**
51
+ * Reference to the {@link module:engine/view/view~View#_renderer}.
52
+ *
53
+ * @member {module:engine/view/renderer~Renderer}
54
+ */
55
+ this.renderer = view._renderer;
56
+ /**
57
+ * Observed DOM elements.
58
+ *
59
+ * @private
60
+ * @member {Array.<HTMLElement>}
61
+ */
62
+ this._domElements = [];
63
+ /**
64
+ * Native mutation observer.
65
+ *
66
+ * @private
67
+ * @member {MutationObserver}
68
+ */
69
+ this._mutationObserver = new window.MutationObserver(this._onMutations.bind(this));
70
+ }
71
+ /**
72
+ * Synchronously fires {@link module:engine/view/document~Document#event:mutations} event with all mutations in record queue.
73
+ * At the same time empties the queue so mutations will not be fired twice.
74
+ */
75
+ flush() {
76
+ this._onMutations(this._mutationObserver.takeRecords());
77
+ }
78
+ /**
79
+ * @inheritDoc
80
+ */
81
+ observe(domElement) {
82
+ this._domElements.push(domElement);
83
+ if (this.isEnabled) {
84
+ this._mutationObserver.observe(domElement, this._config);
85
+ }
86
+ }
87
+ /**
88
+ * @inheritDoc
89
+ */
90
+ enable() {
91
+ super.enable();
92
+ for (const domElement of this._domElements) {
93
+ this._mutationObserver.observe(domElement, this._config);
94
+ }
95
+ }
96
+ /**
97
+ * @inheritDoc
98
+ */
99
+ disable() {
100
+ super.disable();
101
+ this._mutationObserver.disconnect();
102
+ }
103
+ /**
104
+ * @inheritDoc
105
+ */
106
+ destroy() {
107
+ super.destroy();
108
+ this._mutationObserver.disconnect();
109
+ }
110
+ /**
111
+ * Handles mutations. Deduplicates, mark view elements to sync, fire event and call render.
112
+ *
113
+ * @private
114
+ * @param {Array.<Object>} domMutations Array of native mutations.
115
+ */
116
+ _onMutations(domMutations) {
117
+ // As a result of this.flush() we can have an empty collection.
118
+ if (domMutations.length === 0) {
119
+ return;
120
+ }
121
+ const domConverter = this.domConverter;
122
+ // Use map and set for deduplication.
123
+ const mutatedTexts = new Map();
124
+ const mutatedElements = new Set();
125
+ // Handle `childList` mutations first, so we will be able to check if the `characterData` mutation is in the
126
+ // element with changed structure anyway.
127
+ for (const mutation of domMutations) {
128
+ if (mutation.type === 'childList') {
129
+ const element = domConverter.mapDomToView(mutation.target);
130
+ // Do not collect mutations from UIElements and RawElements.
131
+ if (element && (element.is('uiElement') || element.is('rawElement'))) {
132
+ continue;
133
+ }
134
+ if (element && !this._isBogusBrMutation(mutation)) {
135
+ mutatedElements.add(element);
136
+ }
137
+ }
138
+ }
139
+ // Handle `characterData` mutations later, when we have the full list of nodes which changed structure.
140
+ for (const mutation of domMutations) {
141
+ const element = domConverter.mapDomToView(mutation.target);
142
+ // Do not collect mutations from UIElements and RawElements.
143
+ if (element && (element.is('uiElement') || element.is('rawElement'))) {
144
+ continue;
145
+ }
146
+ if (mutation.type === 'characterData') {
147
+ const text = domConverter.findCorrespondingViewText(mutation.target);
148
+ if (text && !mutatedElements.has(text.parent)) {
149
+ // Use text as a key, for deduplication. If there will be another mutation on the same text element
150
+ // we will have only one in the map.
151
+ mutatedTexts.set(text, {
152
+ type: 'text',
153
+ oldText: text.data,
154
+ newText: getDataWithoutFiller(mutation.target),
155
+ node: text
156
+ });
157
+ }
158
+ // When we added first letter to the text node which had only inline filler, for the DOM it is mutation
159
+ // on text, but for the view, where filler text node did not existed, new text node was created, so we
160
+ // need to fire 'children' mutation instead of 'text'.
161
+ else if (!text && startsWithFiller(mutation.target)) {
162
+ mutatedElements.add(domConverter.mapDomToView(mutation.target.parentNode));
163
+ }
164
+ }
165
+ }
166
+ // Now we build the list of mutations to fire and mark elements. We did not do it earlier to avoid marking the
167
+ // same node multiple times in case of duplication.
168
+ // List of mutations we will fire.
169
+ const viewMutations = [];
170
+ for (const mutatedText of mutatedTexts.values()) {
171
+ this.renderer.markToSync('text', mutatedText.node);
172
+ viewMutations.push(mutatedText);
173
+ }
174
+ for (const viewElement of mutatedElements) {
175
+ const domElement = domConverter.mapViewToDom(viewElement);
176
+ const viewChildren = Array.from(viewElement.getChildren());
177
+ const newViewChildren = Array.from(domConverter.domChildrenToView(domElement, { withChildren: false }));
178
+ // It may happen that as a result of many changes (sth was inserted and then removed),
179
+ // both elements haven't really changed. #1031
180
+ if (!isEqualWith(viewChildren, newViewChildren, sameNodes)) {
181
+ this.renderer.markToSync('children', viewElement);
182
+ viewMutations.push({
183
+ type: 'children',
184
+ oldChildren: viewChildren,
185
+ newChildren: newViewChildren,
186
+ node: viewElement
187
+ });
188
+ }
189
+ }
190
+ // Retrieve `domSelection` using `ownerDocument` of one of mutated nodes.
191
+ // There should not be simultaneous mutation in multiple documents, so it's fine.
192
+ const domSelection = domMutations[0].target.ownerDocument.getSelection();
193
+ let viewSelection = null;
194
+ if (domSelection && domSelection.anchorNode) {
195
+ // If `domSelection` is inside a dom node that is already bound to a view node from view tree, get
196
+ // corresponding selection in the view and pass it together with `viewMutations`. The `viewSelection` may
197
+ // be used by features handling mutations.
198
+ // Only one range is supported.
199
+ const viewSelectionAnchor = domConverter.domPositionToView(domSelection.anchorNode, domSelection.anchorOffset);
200
+ const viewSelectionFocus = domConverter.domPositionToView(domSelection.focusNode, domSelection.focusOffset);
201
+ // Anchor and focus has to be properly mapped to view.
202
+ if (viewSelectionAnchor && viewSelectionFocus) {
203
+ viewSelection = new ViewSelection(viewSelectionAnchor);
204
+ viewSelection.setFocus(viewSelectionFocus);
205
+ }
206
+ }
207
+ // In case only non-relevant mutations were recorded it skips the event and force render (#5600).
208
+ if (viewMutations.length) {
209
+ this.document.fire('mutations', viewMutations, viewSelection);
210
+ // If nothing changes on `mutations` event, at this point we have "dirty DOM" (changed) and de-synched
211
+ // view (which has not been changed). In order to "reset DOM" we render the view again.
212
+ this.view.forceRender();
213
+ }
214
+ function sameNodes(child1, child2) {
215
+ // First level of comparison (array of children vs array of children) – use the Lodash's default behavior.
216
+ if (Array.isArray(child1)) {
217
+ return;
218
+ }
219
+ // Elements.
220
+ if (child1 === child2) {
221
+ return true;
222
+ }
223
+ // Texts.
224
+ else if (child1.is('$text') && child2.is('$text')) {
225
+ return child1.data === child2.data;
226
+ }
227
+ // Not matching types.
228
+ return false;
229
+ }
230
+ }
231
+ /**
232
+ * Checks if mutation was generated by the browser inserting bogus br on the end of the block element.
233
+ * Such mutations are generated while pressing space or performing native spellchecker correction
234
+ * on the end of the block element in Firefox browser.
235
+ *
236
+ * @private
237
+ * @param {Object} mutation Native mutation object.
238
+ * @returns {Boolean}
239
+ */
240
+ _isBogusBrMutation(mutation) {
241
+ let addedNode = null;
242
+ // Check if mutation added only one node on the end of its parent.
243
+ if (mutation.nextSibling === null && mutation.removedNodes.length === 0 && mutation.addedNodes.length == 1) {
244
+ addedNode = this.domConverter.domToView(mutation.addedNodes[0], {
245
+ withChildren: false
246
+ });
247
+ }
248
+ return addedNode && addedNode.is('element', 'br');
249
+ }
297
250
  }
298
-
299
- /**
300
- * Fired when mutation occurred. If tree view is not changed on this event, DOM will be reverted to the state before
301
- * mutation, so all changes which should be applied, should be handled on this event.
302
- *
303
- * Introduced by {@link module:engine/view/observer/mutationobserver~MutationObserver}.
304
- *
305
- * Note that because {@link module:engine/view/observer/mutationobserver~MutationObserver} is attached by the
306
- * {@link module:engine/view/view~View} this event is available by default.
307
- *
308
- * @see module:engine/view/observer/mutationobserver~MutationObserver
309
- * @event module:engine/view/document~Document#event:mutations
310
- * @param {Array.<module:engine/view/observer/mutationobserver~MutatedText|module:engine/view/observer/mutationobserver~MutatedChildren>}
311
- * viewMutations Array of mutations.
312
- * For mutated texts it will be {@link module:engine/view/observer/mutationobserver~MutatedText} and for mutated elements it will be
313
- * {@link module:engine/view/observer/mutationobserver~MutatedChildren}. You can recognize the type based on the `type` property.
314
- * @param {module:engine/view/selection~Selection|null} viewSelection View selection that is a result of converting DOM selection to view.
315
- * Keep in
316
- * mind that the DOM selection is already "updated", meaning that it already acknowledges changes done in mutation.
317
- */
318
-
319
- /**
320
- * Mutation item for text.
321
- *
322
- * @see module:engine/view/document~Document#event:mutations
323
- * @see module:engine/view/observer/mutationobserver~MutatedChildren
324
- *
325
- * @typedef {Object} module:engine/view/observer/mutationobserver~MutatedText
326
- *
327
- * @property {String} type For text mutations it is always 'text'.
328
- * @property {module:engine/view/text~Text} node Mutated text node.
329
- * @property {String} oldText Old text.
330
- * @property {String} newText New text.
331
- */
332
-
333
- /**
334
- * Mutation item for child nodes.
335
- *
336
- * @see module:engine/view/document~Document#event:mutations
337
- * @see module:engine/view/observer/mutationobserver~MutatedText
338
- *
339
- * @typedef {Object} module:engine/view/observer/mutationobserver~MutatedChildren
340
- *
341
- * @property {String} type For child nodes mutations it is always 'children'.
342
- * @property {module:engine/view/element~Element} node Parent of the mutated children.
343
- * @property {Array.<module:engine/view/node~Node>} oldChildren Old child nodes.
344
- * @property {Array.<module:engine/view/node~Node>} newChildren New child nodes.
345
- */