@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,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
- */