@ckeditor/ckeditor5-engine 36.0.1 → 37.0.0-alpha.1
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.
- package/README.md +1 -1
- package/package.json +25 -24
- package/src/controller/datacontroller.d.ts +331 -0
- package/src/controller/datacontroller.js +72 -116
- package/src/controller/editingcontroller.d.ts +98 -0
- package/src/controller/editingcontroller.js +22 -46
- package/src/conversion/conversion.d.ts +476 -0
- package/src/conversion/conversion.js +328 -347
- package/src/conversion/conversionhelpers.d.ts +26 -0
- package/src/conversion/conversionhelpers.js +1 -5
- package/src/conversion/downcastdispatcher.d.ts +547 -0
- package/src/conversion/downcastdispatcher.js +74 -152
- package/src/conversion/downcasthelpers.d.ts +1226 -0
- package/src/conversion/downcasthelpers.js +843 -762
- package/src/conversion/mapper.d.ts +503 -0
- package/src/conversion/mapper.js +84 -99
- package/src/conversion/modelconsumable.d.ts +201 -0
- package/src/conversion/modelconsumable.js +96 -99
- package/src/conversion/upcastdispatcher.d.ts +492 -0
- package/src/conversion/upcastdispatcher.js +73 -100
- package/src/conversion/upcasthelpers.d.ts +499 -0
- package/src/conversion/upcasthelpers.js +406 -373
- package/src/conversion/viewconsumable.d.ts +369 -0
- package/src/conversion/viewconsumable.js +139 -173
- package/src/dataprocessor/basichtmlwriter.d.ts +18 -0
- package/src/dataprocessor/basichtmlwriter.js +0 -9
- package/src/dataprocessor/dataprocessor.d.ts +61 -0
- package/src/dataprocessor/htmldataprocessor.d.ts +76 -0
- package/src/dataprocessor/htmldataprocessor.js +6 -28
- package/src/dataprocessor/htmlwriter.d.ts +16 -0
- package/src/dataprocessor/xmldataprocessor.d.ts +90 -0
- package/src/dataprocessor/xmldataprocessor.js +8 -40
- package/src/dev-utils/model.d.ts +124 -0
- package/src/dev-utils/model.js +41 -38
- package/src/dev-utils/operationreplayer.d.ts +51 -0
- package/src/dev-utils/operationreplayer.js +6 -14
- package/src/dev-utils/utils.d.ts +37 -0
- package/src/dev-utils/utils.js +5 -18
- package/src/dev-utils/view.d.ts +319 -0
- package/src/dev-utils/view.js +205 -226
- package/src/index.d.ts +108 -0
- package/src/index.js +4 -0
- package/src/model/batch.d.ts +106 -0
- package/src/model/differ.d.ts +329 -0
- package/src/model/document.d.ts +246 -0
- package/src/model/document.js +1 -1
- package/src/model/documentfragment.d.ts +196 -0
- package/src/model/documentfragment.js +2 -2
- package/src/model/documentselection.d.ts +420 -0
- package/src/model/element.d.ts +165 -0
- package/src/model/history.d.ts +114 -0
- package/src/model/item.d.ts +14 -0
- package/src/model/liveposition.d.ts +77 -0
- package/src/model/liverange.d.ts +102 -0
- package/src/model/markercollection.d.ts +335 -0
- package/src/model/model.d.ts +885 -0
- package/src/model/model.js +59 -81
- package/src/model/node.d.ts +256 -0
- package/src/model/nodelist.d.ts +91 -0
- package/src/model/operation/attributeoperation.d.ts +98 -0
- package/src/model/operation/detachoperation.d.ts +55 -0
- package/src/model/operation/insertoperation.d.ts +85 -0
- package/src/model/operation/markeroperation.d.ts +86 -0
- package/src/model/operation/mergeoperation.d.ts +95 -0
- package/src/model/operation/mergeoperation.js +1 -1
- package/src/model/operation/moveoperation.d.ts +91 -0
- package/src/model/operation/nooperation.d.ts +33 -0
- package/src/model/operation/operation.d.ts +89 -0
- package/src/model/operation/operationfactory.d.ts +18 -0
- package/src/model/operation/renameoperation.d.ts +78 -0
- package/src/model/operation/rootattributeoperation.d.ts +97 -0
- package/src/model/operation/rootattributeoperation.js +1 -1
- package/src/model/operation/splitoperation.d.ts +104 -0
- package/src/model/operation/splitoperation.js +1 -1
- package/src/model/operation/transform.d.ts +100 -0
- package/src/model/operation/utils.d.ts +71 -0
- package/src/model/operation/utils.js +1 -1
- package/src/model/position.d.ts +539 -0
- package/src/model/position.js +1 -1
- package/src/model/range.d.ts +458 -0
- package/src/model/range.js +1 -1
- package/src/model/rootelement.d.ts +40 -0
- package/src/model/schema.d.ts +1176 -0
- package/src/model/schema.js +19 -19
- package/src/model/selection.d.ts +472 -0
- package/src/model/text.d.ts +66 -0
- package/src/model/text.js +0 -2
- package/src/model/textproxy.d.ts +144 -0
- package/src/model/treewalker.d.ts +186 -0
- package/src/model/treewalker.js +19 -10
- package/src/model/typecheckable.d.ts +285 -0
- package/src/model/utils/autoparagraphing.d.ts +37 -0
- package/src/model/utils/deletecontent.d.ts +58 -0
- package/src/model/utils/findoptimalinsertionrange.d.ts +32 -0
- package/src/model/utils/getselectedcontent.d.ts +30 -0
- package/src/model/utils/insertcontent.d.ts +46 -0
- package/src/model/utils/insertcontent.js +2 -12
- package/src/model/utils/insertobject.d.ts +44 -0
- package/src/model/utils/insertobject.js +3 -14
- package/src/model/utils/modifyselection.d.ts +48 -0
- package/src/model/utils/selection-post-fixer.d.ts +65 -0
- package/src/model/writer.d.ts +823 -0
- package/src/model/writer.js +6 -61
- package/src/view/attributeelement.d.ts +108 -0
- package/src/view/attributeelement.js +25 -69
- package/src/view/containerelement.d.ts +49 -0
- package/src/view/containerelement.js +10 -43
- package/src/view/datatransfer.d.ts +75 -0
- package/src/view/document.d.ts +184 -0
- package/src/view/document.js +15 -84
- package/src/view/documentfragment.d.ts +149 -0
- package/src/view/documentfragment.js +39 -81
- package/src/view/documentselection.d.ts +306 -0
- package/src/view/documentselection.js +42 -143
- package/src/view/domconverter.d.ts +650 -0
- package/src/view/domconverter.js +157 -283
- package/src/view/downcastwriter.d.ts +996 -0
- package/src/view/downcastwriter.js +259 -426
- package/src/view/editableelement.d.ts +52 -0
- package/src/view/editableelement.js +9 -49
- package/src/view/element.d.ts +468 -0
- package/src/view/element.js +150 -222
- package/src/view/elementdefinition.d.ts +87 -0
- package/src/view/emptyelement.d.ts +41 -0
- package/src/view/emptyelement.js +11 -44
- package/src/view/filler.d.ts +111 -0
- package/src/view/filler.js +24 -21
- package/src/view/item.d.ts +14 -0
- package/src/view/matcher.d.ts +486 -0
- package/src/view/matcher.js +247 -218
- package/src/view/node.d.ts +163 -0
- package/src/view/node.js +26 -100
- package/src/view/observer/arrowkeysobserver.d.ts +41 -0
- package/src/view/observer/arrowkeysobserver.js +0 -13
- package/src/view/observer/bubblingemittermixin.d.ts +166 -0
- package/src/view/observer/bubblingemittermixin.js +36 -25
- package/src/view/observer/bubblingeventinfo.d.ts +47 -0
- package/src/view/observer/bubblingeventinfo.js +3 -29
- package/src/view/observer/clickobserver.d.ts +43 -0
- package/src/view/observer/clickobserver.js +9 -19
- package/src/view/observer/compositionobserver.d.ts +82 -0
- package/src/view/observer/compositionobserver.js +13 -42
- package/src/view/observer/domeventdata.d.ts +50 -0
- package/src/view/observer/domeventdata.js +5 -30
- package/src/view/observer/domeventobserver.d.ts +69 -0
- package/src/view/observer/domeventobserver.js +19 -21
- package/src/view/observer/fakeselectionobserver.d.ts +43 -0
- package/src/view/observer/fakeselectionobserver.js +0 -16
- package/src/view/observer/focusobserver.d.ts +82 -0
- package/src/view/observer/focusobserver.js +14 -40
- package/src/view/observer/inputobserver.d.ts +86 -0
- package/src/view/observer/inputobserver.js +18 -64
- package/src/view/observer/keyobserver.d.ts +66 -0
- package/src/view/observer/keyobserver.js +8 -42
- package/src/view/observer/mouseobserver.d.ts +89 -0
- package/src/view/observer/mouseobserver.js +8 -28
- package/src/view/observer/mutationobserver.d.ts +82 -0
- package/src/view/observer/mutationobserver.js +7 -37
- package/src/view/observer/observer.d.ts +84 -0
- package/src/view/observer/observer.js +12 -25
- package/src/view/observer/selectionobserver.d.ts +144 -0
- package/src/view/observer/selectionobserver.js +17 -107
- package/src/view/observer/tabobserver.d.ts +42 -0
- package/src/view/observer/tabobserver.js +0 -14
- package/src/view/placeholder.d.ts +85 -0
- package/src/view/placeholder.js +26 -43
- package/src/view/position.d.ts +189 -0
- package/src/view/position.js +36 -83
- package/src/view/range.d.ts +279 -0
- package/src/view/range.js +79 -122
- package/src/view/rawelement.d.ts +73 -0
- package/src/view/rawelement.js +34 -48
- package/src/view/renderer.d.ts +265 -0
- package/src/view/renderer.js +105 -193
- package/src/view/rooteditableelement.d.ts +41 -0
- package/src/view/rooteditableelement.js +12 -40
- package/src/view/selection.d.ts +375 -0
- package/src/view/selection.js +79 -153
- package/src/view/styles/background.d.ts +33 -0
- package/src/view/styles/background.js +14 -12
- package/src/view/styles/border.d.ts +43 -0
- package/src/view/styles/border.js +58 -48
- package/src/view/styles/margin.d.ts +29 -0
- package/src/view/styles/margin.js +13 -11
- package/src/view/styles/padding.d.ts +29 -0
- package/src/view/styles/padding.js +13 -11
- package/src/view/styles/utils.d.ts +93 -0
- package/src/view/styles/utils.js +22 -48
- package/src/view/stylesmap.d.ts +675 -0
- package/src/view/stylesmap.js +249 -244
- package/src/view/text.d.ts +74 -0
- package/src/view/text.js +16 -46
- package/src/view/textproxy.d.ts +97 -0
- package/src/view/textproxy.js +10 -59
- package/src/view/treewalker.d.ts +195 -0
- package/src/view/treewalker.js +43 -106
- package/src/view/typecheckable.d.ts +448 -0
- package/src/view/uielement.d.ts +96 -0
- package/src/view/uielement.js +29 -63
- package/src/view/upcastwriter.d.ts +417 -0
- package/src/view/upcastwriter.js +86 -157
- package/src/view/view.d.ts +417 -0
- package/src/view/view.js +47 -175
package/src/view/renderer.js
CHANGED
|
@@ -27,72 +27,38 @@ export default class Renderer extends ObservableMixin() {
|
|
|
27
27
|
/**
|
|
28
28
|
* Creates a renderer instance.
|
|
29
29
|
*
|
|
30
|
-
* @param
|
|
31
|
-
* @param
|
|
30
|
+
* @param domConverter Converter instance.
|
|
31
|
+
* @param selection View selection.
|
|
32
32
|
*/
|
|
33
33
|
constructor(domConverter, selection) {
|
|
34
34
|
super();
|
|
35
35
|
/**
|
|
36
36
|
* Set of DOM Documents instances.
|
|
37
|
-
*
|
|
38
|
-
* @readonly
|
|
39
|
-
* @member {Set.<Document>}
|
|
40
37
|
*/
|
|
41
38
|
this.domDocuments = new Set();
|
|
42
|
-
/**
|
|
43
|
-
* Converter instance.
|
|
44
|
-
*
|
|
45
|
-
* @readonly
|
|
46
|
-
* @member {module:engine/view/domconverter~DomConverter}
|
|
47
|
-
*/
|
|
48
|
-
this.domConverter = domConverter;
|
|
49
39
|
/**
|
|
50
40
|
* Set of nodes which attributes changed and may need to be rendered.
|
|
51
|
-
*
|
|
52
|
-
* @readonly
|
|
53
|
-
* @member {Set.<module:engine/view/node~ViewNode>}
|
|
54
41
|
*/
|
|
55
42
|
this.markedAttributes = new Set();
|
|
56
43
|
/**
|
|
57
44
|
* Set of elements which child lists changed and may need to be rendered.
|
|
58
|
-
*
|
|
59
|
-
* @readonly
|
|
60
|
-
* @member {Set.<module:engine/view/node~ViewNode>}
|
|
61
45
|
*/
|
|
62
46
|
this.markedChildren = new Set();
|
|
63
47
|
/**
|
|
64
48
|
* Set of text nodes which text data changed and may need to be rendered.
|
|
65
|
-
*
|
|
66
|
-
* @readonly
|
|
67
|
-
* @member {Set.<module:engine/view/node~ViewNode>}
|
|
68
49
|
*/
|
|
69
50
|
this.markedTexts = new Set();
|
|
70
51
|
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* @readonly
|
|
74
|
-
* @member {module:engine/view/documentselection~DocumentSelection}
|
|
52
|
+
* The text node in which the inline filler was rendered.
|
|
75
53
|
*/
|
|
76
|
-
this.
|
|
54
|
+
this._inlineFiller = null;
|
|
77
55
|
/**
|
|
78
|
-
*
|
|
79
|
-
* this is set to `false`.
|
|
80
|
-
*
|
|
81
|
-
* @member {Boolean}
|
|
82
|
-
* @observable
|
|
56
|
+
* DOM element containing fake selection.
|
|
83
57
|
*/
|
|
58
|
+
this._fakeSelectionContainer = null;
|
|
59
|
+
this.domConverter = domConverter;
|
|
60
|
+
this.selection = selection;
|
|
84
61
|
this.set('isFocused', false);
|
|
85
|
-
/**
|
|
86
|
-
* Indicates whether the user is making a selection in the document (e.g. holding the mouse button and moving the cursor).
|
|
87
|
-
* When they stop selecting, the property goes back to `false`.
|
|
88
|
-
*
|
|
89
|
-
* Note: In some browsers, the renderer will stop rendering the selection and inline fillers while the user is making
|
|
90
|
-
* a selection to avoid glitches in DOM selection
|
|
91
|
-
* (https://github.com/ckeditor/ckeditor5/issues/10562, https://github.com/ckeditor/ckeditor5/issues/10723).
|
|
92
|
-
*
|
|
93
|
-
* @member {Boolean}
|
|
94
|
-
* @observable
|
|
95
|
-
*/
|
|
96
62
|
this.set('isSelecting', false);
|
|
97
63
|
// Rendering the selection and inline filler manipulation should be postponed in (non-Android) Blink until the user finishes
|
|
98
64
|
// creating the selection in DOM to avoid accidental selection collapsing
|
|
@@ -105,34 +71,12 @@ export default class Renderer extends ObservableMixin() {
|
|
|
105
71
|
}
|
|
106
72
|
});
|
|
107
73
|
}
|
|
108
|
-
/**
|
|
109
|
-
* True if composition is in progress inside the document.
|
|
110
|
-
*
|
|
111
|
-
* This property is bound to the {@link module:engine/view/document~Document#isComposing `Document#isComposing`} property.
|
|
112
|
-
*
|
|
113
|
-
* @member {Boolean}
|
|
114
|
-
* @observable
|
|
115
|
-
*/
|
|
116
74
|
this.set('isComposing', false);
|
|
117
75
|
this.on('change:isComposing', () => {
|
|
118
76
|
if (!this.isComposing) {
|
|
119
77
|
this.render();
|
|
120
78
|
}
|
|
121
79
|
});
|
|
122
|
-
/**
|
|
123
|
-
* The text node in which the inline filler was rendered.
|
|
124
|
-
*
|
|
125
|
-
* @private
|
|
126
|
-
* @member {Text}
|
|
127
|
-
*/
|
|
128
|
-
this._inlineFiller = null;
|
|
129
|
-
/**
|
|
130
|
-
* DOM element containing fake selection.
|
|
131
|
-
*
|
|
132
|
-
* @private
|
|
133
|
-
* @type {null|HTMLElement}
|
|
134
|
-
*/
|
|
135
|
-
this._fakeSelectionContainer = null;
|
|
136
80
|
}
|
|
137
81
|
/**
|
|
138
82
|
* Marks a view node to be updated in the DOM by {@link #render `render()`}.
|
|
@@ -143,8 +87,8 @@ export default class Renderer extends ObservableMixin() {
|
|
|
143
87
|
* @see #markedChildren
|
|
144
88
|
* @see #markedTexts
|
|
145
89
|
*
|
|
146
|
-
* @param
|
|
147
|
-
* @param
|
|
90
|
+
* @param type Type of the change.
|
|
91
|
+
* @param node ViewNode to be marked.
|
|
148
92
|
*/
|
|
149
93
|
markToSync(type, node) {
|
|
150
94
|
if (type === 'text') {
|
|
@@ -165,6 +109,8 @@ export default class Renderer extends ObservableMixin() {
|
|
|
165
109
|
this.markedChildren.add(node);
|
|
166
110
|
}
|
|
167
111
|
else {
|
|
112
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
113
|
+
const unreachable = type;
|
|
168
114
|
/**
|
|
169
115
|
* Unknown type passed to Renderer.markToSync.
|
|
170
116
|
*
|
|
@@ -191,14 +137,14 @@ export default class Renderer extends ObservableMixin() {
|
|
|
191
137
|
// On Android composition events are immediately applied to the model, so we don't need to skip rendering,
|
|
192
138
|
// and we should not do it because the difference between view and DOM could lead to position mapping problems.
|
|
193
139
|
if (this.isComposing && !env.isAndroid) {
|
|
194
|
-
// @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
|
|
140
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
195
141
|
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Rendering aborted while isComposing',
|
|
196
142
|
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', ''
|
|
197
143
|
// @if CK_DEBUG_TYPING // );
|
|
198
144
|
// @if CK_DEBUG_TYPING // }
|
|
199
145
|
return;
|
|
200
146
|
}
|
|
201
|
-
// @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
|
|
147
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
202
148
|
// @if CK_DEBUG_TYPING // console.group( '%c[Renderer]%c Rendering',
|
|
203
149
|
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', ''
|
|
204
150
|
// @if CK_DEBUG_TYPING // );
|
|
@@ -287,7 +233,7 @@ export default class Renderer extends ObservableMixin() {
|
|
|
287
233
|
this.markedTexts.clear();
|
|
288
234
|
this.markedAttributes.clear();
|
|
289
235
|
this.markedChildren.clear();
|
|
290
|
-
// @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
|
|
236
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
291
237
|
// @if CK_DEBUG_TYPING // console.groupEnd();
|
|
292
238
|
// @if CK_DEBUG_TYPING // }
|
|
293
239
|
}
|
|
@@ -298,8 +244,7 @@ export default class Renderer extends ObservableMixin() {
|
|
|
298
244
|
* This means that their mappings can be updated so the new view elements are mapped to the existing DOM elements.
|
|
299
245
|
* Thanks to that these elements do not need to be re-rendered completely.
|
|
300
246
|
*
|
|
301
|
-
* @
|
|
302
|
-
* @param {module:engine/view/node~ViewNode} viewElement The view element whose children mappings will be updated.
|
|
247
|
+
* @param viewElement The view element whose children mappings will be updated.
|
|
303
248
|
*/
|
|
304
249
|
_updateChildrenMappings(viewElement) {
|
|
305
250
|
const domElement = this.domConverter.mapViewToDom(viewElement);
|
|
@@ -342,9 +287,8 @@ export default class Renderer extends ObservableMixin() {
|
|
|
342
287
|
/**
|
|
343
288
|
* Updates mappings of a given view element.
|
|
344
289
|
*
|
|
345
|
-
* @
|
|
346
|
-
* @param
|
|
347
|
-
* @param {ViewNode} domElement The DOM element representing the given view element.
|
|
290
|
+
* @param viewElement The view element whose mappings will be updated.
|
|
291
|
+
* @param domElement The DOM element representing the given view element.
|
|
348
292
|
*/
|
|
349
293
|
_updateElementMappings(viewElement, domElement) {
|
|
350
294
|
// Remap 'DomConverter' bindings.
|
|
@@ -372,9 +316,6 @@ export default class Renderer extends ObservableMixin() {
|
|
|
372
316
|
* Note: The filler position cannot be restored based on the filler's DOM text node, because
|
|
373
317
|
* when this method is called (before rendering), the bindings will often be broken. View-to-DOM
|
|
374
318
|
* bindings are only dependable after rendering.
|
|
375
|
-
*
|
|
376
|
-
* @private
|
|
377
|
-
* @returns {module:engine/view/position~Position}
|
|
378
319
|
*/
|
|
379
320
|
_getInlineFillerPosition() {
|
|
380
321
|
const firstPos = this.selection.getFirstPosition();
|
|
@@ -390,8 +331,7 @@ export default class Renderer extends ObservableMixin() {
|
|
|
390
331
|
* If it is `true`, it means that the filler had been added for a reason and the selection did not
|
|
391
332
|
* leave the filler's text node. For example, the user can be in the middle of a composition so it should not be touched.
|
|
392
333
|
*
|
|
393
|
-
* @
|
|
394
|
-
* @returns {Boolean} `true` if the inline filler and selection are in the same place.
|
|
334
|
+
* @returns `true` if the inline filler and selection are in the same place.
|
|
395
335
|
*/
|
|
396
336
|
_isSelectionInInlineFiller() {
|
|
397
337
|
if (this.selection.rangeCount != 1 || !this.selection.isCollapsed) {
|
|
@@ -414,8 +354,6 @@ export default class Renderer extends ObservableMixin() {
|
|
|
414
354
|
}
|
|
415
355
|
/**
|
|
416
356
|
* Removes the inline filler.
|
|
417
|
-
*
|
|
418
|
-
* @private
|
|
419
357
|
*/
|
|
420
358
|
_removeInlineFiller() {
|
|
421
359
|
const domFillerNode = this._inlineFiller;
|
|
@@ -440,8 +378,7 @@ export default class Renderer extends ObservableMixin() {
|
|
|
440
378
|
/**
|
|
441
379
|
* Checks if the inline {@link module:engine/view/filler filler} should be added.
|
|
442
380
|
*
|
|
443
|
-
* @
|
|
444
|
-
* @returns {Boolean} `true` if the inline filler should be added.
|
|
381
|
+
* @returns `true` if the inline filler should be added.
|
|
445
382
|
*/
|
|
446
383
|
_needsInlineFillerAtSelection() {
|
|
447
384
|
if (this.selection.rangeCount != 1 || !this.selection.isCollapsed) {
|
|
@@ -481,11 +418,8 @@ export default class Renderer extends ObservableMixin() {
|
|
|
481
418
|
/**
|
|
482
419
|
* Checks if text needs to be updated and possibly updates it.
|
|
483
420
|
*
|
|
484
|
-
* @
|
|
485
|
-
* @param
|
|
486
|
-
* @param {Object} options
|
|
487
|
-
* @param {module:engine/view/position~Position} options.inlineFillerPosition The position where the inline
|
|
488
|
-
* filler should be rendered.
|
|
421
|
+
* @param viewText View text to update.
|
|
422
|
+
* @param options.inlineFillerPosition The position where the inline filler should be rendered.
|
|
489
423
|
*/
|
|
490
424
|
_updateText(viewText, options) {
|
|
491
425
|
const domText = this.domConverter.findCorrespondingDomText(viewText);
|
|
@@ -495,21 +429,20 @@ export default class Renderer extends ObservableMixin() {
|
|
|
495
429
|
if (filler && filler.parent == viewText.parent && filler.offset == viewText.index) {
|
|
496
430
|
expectedText = INLINE_FILLER + expectedText;
|
|
497
431
|
}
|
|
498
|
-
// @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
|
|
432
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
499
433
|
// @if CK_DEBUG_TYPING // console.group( '%c[Renderer]%c Update text',
|
|
500
434
|
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', ''
|
|
501
435
|
// @if CK_DEBUG_TYPING // );
|
|
502
436
|
// @if CK_DEBUG_TYPING // }
|
|
503
437
|
updateTextNode(domText, expectedText);
|
|
504
|
-
// @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
|
|
438
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
505
439
|
// @if CK_DEBUG_TYPING // console.groupEnd();
|
|
506
440
|
// @if CK_DEBUG_TYPING // }
|
|
507
441
|
}
|
|
508
442
|
/**
|
|
509
443
|
* Checks if attribute list needs to be updated and possibly updates it.
|
|
510
444
|
*
|
|
511
|
-
* @
|
|
512
|
-
* @param {module:engine/view/element~Element} viewElement The view element to update.
|
|
445
|
+
* @param viewElement The view element to update.
|
|
513
446
|
*/
|
|
514
447
|
_updateAttrs(viewElement) {
|
|
515
448
|
const domElement = this.domConverter.mapViewToDom(viewElement);
|
|
@@ -540,11 +473,8 @@ export default class Renderer extends ObservableMixin() {
|
|
|
540
473
|
* Note that on Android, to reduce the risk of composition breaks, it tries to update data of an existing
|
|
541
474
|
* child text nodes instead of replacing them completely.
|
|
542
475
|
*
|
|
543
|
-
* @
|
|
544
|
-
* @param
|
|
545
|
-
* @param {Object} options
|
|
546
|
-
* @param {module:engine/view/position~Position} options.inlineFillerPosition The position where the inline
|
|
547
|
-
* filler should be rendered.
|
|
476
|
+
* @param viewElement View element to update.
|
|
477
|
+
* @param options.inlineFillerPosition The position where the inline filler should be rendered.
|
|
548
478
|
*/
|
|
549
479
|
_updateChildren(viewElement, options) {
|
|
550
480
|
const domElement = this.domConverter.mapViewToDom(viewElement);
|
|
@@ -553,7 +483,7 @@ export default class Renderer extends ObservableMixin() {
|
|
|
553
483
|
// There is no need to process it. It will be processed when re-inserted.
|
|
554
484
|
return;
|
|
555
485
|
}
|
|
556
|
-
// @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
|
|
486
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
557
487
|
// @if CK_DEBUG_TYPING // console.group( '%c[Renderer]%c Update children',
|
|
558
488
|
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', ''
|
|
559
489
|
// @if CK_DEBUG_TYPING // );
|
|
@@ -603,7 +533,7 @@ export default class Renderer extends ObservableMixin() {
|
|
|
603
533
|
// It doesn't matter in what order we remove or add nodes, as long as we remove and add correct nodes at correct indexes.
|
|
604
534
|
for (const action of actions) {
|
|
605
535
|
if (action === 'delete') {
|
|
606
|
-
// @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
|
|
536
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
607
537
|
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Remove node',
|
|
608
538
|
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', '', actualDomChildren[ i ]
|
|
609
539
|
// @if CK_DEBUG_TYPING // );
|
|
@@ -618,7 +548,7 @@ export default class Renderer extends ObservableMixin() {
|
|
|
618
548
|
i = 0;
|
|
619
549
|
for (const action of actions) {
|
|
620
550
|
if (action === 'insert') {
|
|
621
|
-
// @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
|
|
551
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
622
552
|
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Insert node',
|
|
623
553
|
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', '', expectedDomChildren[ i ]
|
|
624
554
|
// @if CK_DEBUG_TYPING // );
|
|
@@ -628,14 +558,14 @@ export default class Renderer extends ObservableMixin() {
|
|
|
628
558
|
}
|
|
629
559
|
// Update the existing text node data. Note that replace action is generated only for Android for now.
|
|
630
560
|
else if (action === 'replace') {
|
|
631
|
-
// @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
|
|
561
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
632
562
|
// @if CK_DEBUG_TYPING // console.group( '%c[Renderer]%c Update text node',
|
|
633
563
|
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', ''
|
|
634
564
|
// @if CK_DEBUG_TYPING // );
|
|
635
565
|
// @if CK_DEBUG_TYPING // }
|
|
636
566
|
updateTextNode(actualDomChildren[i], expectedDomChildren[i].data);
|
|
637
567
|
i++;
|
|
638
|
-
// @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
|
|
568
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
639
569
|
// @if CK_DEBUG_TYPING // console.groupEnd();
|
|
640
570
|
// @if CK_DEBUG_TYPING // }
|
|
641
571
|
}
|
|
@@ -654,17 +584,16 @@ export default class Renderer extends ObservableMixin() {
|
|
|
654
584
|
this.domConverter.unbindDomElement(node);
|
|
655
585
|
}
|
|
656
586
|
}
|
|
657
|
-
// @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
|
|
587
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
658
588
|
// @if CK_DEBUG_TYPING // console.groupEnd();
|
|
659
589
|
// @if CK_DEBUG_TYPING // }
|
|
660
590
|
}
|
|
661
591
|
/**
|
|
662
592
|
* Shorthand for diffing two arrays or node lists of DOM nodes.
|
|
663
593
|
*
|
|
664
|
-
* @
|
|
665
|
-
* @param
|
|
666
|
-
* @
|
|
667
|
-
* @returns {Array.<String>} The list of actions based on the {@link module:utils/diff~diff} function.
|
|
594
|
+
* @param actualDomChildren Actual DOM children
|
|
595
|
+
* @param expectedDomChildren Expected DOM children.
|
|
596
|
+
* @returns The list of actions based on the {@link module:utils/diff~diff} function.
|
|
668
597
|
*/
|
|
669
598
|
_diffNodeLists(actualDomChildren, expectedDomChildren) {
|
|
670
599
|
actualDomChildren = filterOutFakeSelectionContainer(actualDomChildren, this._fakeSelectionContainer);
|
|
@@ -674,18 +603,19 @@ export default class Renderer extends ObservableMixin() {
|
|
|
674
603
|
* Finds DOM nodes that were replaced with the similar nodes (same tag name) in the view. All nodes are compared
|
|
675
604
|
* within one `insert`/`delete` action group, for example:
|
|
676
605
|
*
|
|
677
|
-
*
|
|
678
|
-
*
|
|
679
|
-
*
|
|
680
|
-
*
|
|
606
|
+
* ```
|
|
607
|
+
* Actual DOM: <p><b>Foo</b>Bar<i>Baz</i><b>Bax</b></p>
|
|
608
|
+
* Expected DOM: <p>Bar<b>123</b><i>Baz</i><b>456</b></p>
|
|
609
|
+
* Input actions: [ insert, insert, delete, delete, equal, insert, delete ]
|
|
610
|
+
* Output actions: [ insert, replace, delete, equal, replace ]
|
|
611
|
+
* ```
|
|
681
612
|
*
|
|
682
|
-
* @
|
|
683
|
-
* @param
|
|
684
|
-
* @param
|
|
685
|
-
* @param
|
|
686
|
-
* @param
|
|
687
|
-
* @
|
|
688
|
-
* @returns {Array.<String>} Actions array modified with the `replace` actions.
|
|
613
|
+
* @param actions Actions array which is a result of the {@link module:utils/diff~diff} function.
|
|
614
|
+
* @param actualDom Actual DOM children
|
|
615
|
+
* @param expectedDom Expected DOM children.
|
|
616
|
+
* @param options Options
|
|
617
|
+
* @param options.replaceText Mark text nodes replacement.
|
|
618
|
+
* @returns Actions array modified with the `replace` actions.
|
|
689
619
|
*/
|
|
690
620
|
_findReplaceActions(actions, actualDom, expectedDom, options = {}) {
|
|
691
621
|
// If there is no both 'insert' and 'delete' actions, no need to check for replaced elements.
|
|
@@ -721,8 +651,7 @@ export default class Renderer extends ObservableMixin() {
|
|
|
721
651
|
*
|
|
722
652
|
* If a text node is passed, it will be marked. If an element is passed, all descendant text nodes inside it will be marked.
|
|
723
653
|
*
|
|
724
|
-
* @
|
|
725
|
-
* @param {module:engine/view/node~ViewNode} viewNode View node to sync.
|
|
654
|
+
* @param viewNode View node to sync.
|
|
726
655
|
*/
|
|
727
656
|
_markDescendantTextToSync(viewNode) {
|
|
728
657
|
if (!viewNode) {
|
|
@@ -739,8 +668,6 @@ export default class Renderer extends ObservableMixin() {
|
|
|
739
668
|
}
|
|
740
669
|
/**
|
|
741
670
|
* Checks if the selection needs to be updated and possibly updates it.
|
|
742
|
-
*
|
|
743
|
-
* @private
|
|
744
671
|
*/
|
|
745
672
|
_updateSelection() {
|
|
746
673
|
// Block updating DOM selection in (non-Android) Blink while the user is selecting to prevent accidental selection collapsing.
|
|
@@ -782,8 +709,7 @@ export default class Renderer extends ObservableMixin() {
|
|
|
782
709
|
/**
|
|
783
710
|
* Updates the fake selection.
|
|
784
711
|
*
|
|
785
|
-
* @
|
|
786
|
-
* @param {HTMLElement} domRoot A valid DOM root where the fake selection container should be added.
|
|
712
|
+
* @param domRoot A valid DOM root where the fake selection container should be added.
|
|
787
713
|
*/
|
|
788
714
|
_updateFakeSelection(domRoot) {
|
|
789
715
|
const domDocument = domRoot.ownerDocument;
|
|
@@ -809,8 +735,7 @@ export default class Renderer extends ObservableMixin() {
|
|
|
809
735
|
/**
|
|
810
736
|
* Updates the DOM selection.
|
|
811
737
|
*
|
|
812
|
-
* @
|
|
813
|
-
* @param {HTMLElement} domRoot A valid DOM root where the DOM selection should be rendered.
|
|
738
|
+
* @param domRoot A valid DOM root where the DOM selection should be rendered.
|
|
814
739
|
*/
|
|
815
740
|
_updateDomSelection(domRoot) {
|
|
816
741
|
const domSelection = domRoot.ownerDocument.defaultView.getSelection();
|
|
@@ -825,7 +750,7 @@ export default class Renderer extends ObservableMixin() {
|
|
|
825
750
|
// selected. If there is any editable selected, it is okay (editable is taken from selection anchor).
|
|
826
751
|
const anchor = this.domConverter.viewPositionToDom(this.selection.anchor);
|
|
827
752
|
const focus = this.domConverter.viewPositionToDom(this.selection.focus);
|
|
828
|
-
// @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
|
|
753
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
829
754
|
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Update DOM selection:',
|
|
830
755
|
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', '', anchor, focus
|
|
831
756
|
// @if CK_DEBUG_TYPING // );
|
|
@@ -840,9 +765,7 @@ export default class Renderer extends ObservableMixin() {
|
|
|
840
765
|
/**
|
|
841
766
|
* Checks whether a given DOM selection needs to be updated.
|
|
842
767
|
*
|
|
843
|
-
* @
|
|
844
|
-
* @param {Selection} domSelection The DOM selection to check.
|
|
845
|
-
* @returns {Boolean}
|
|
768
|
+
* @param domSelection The DOM selection to check.
|
|
846
769
|
*/
|
|
847
770
|
_domSelectionNeedsUpdate(domSelection) {
|
|
848
771
|
if (!this.domConverter.isDomSelectionCorrect(domSelection)) {
|
|
@@ -864,9 +787,7 @@ export default class Renderer extends ObservableMixin() {
|
|
|
864
787
|
/**
|
|
865
788
|
* Checks whether the fake selection needs to be updated.
|
|
866
789
|
*
|
|
867
|
-
* @
|
|
868
|
-
* @param {HTMLElement} domRoot A valid DOM root where a new fake selection container should be added.
|
|
869
|
-
* @returns {Boolean}
|
|
790
|
+
* @param domRoot A valid DOM root where a new fake selection container should be added.
|
|
870
791
|
*/
|
|
871
792
|
_fakeSelectionNeedsUpdate(domRoot) {
|
|
872
793
|
const container = this._fakeSelectionContainer;
|
|
@@ -884,8 +805,6 @@ export default class Renderer extends ObservableMixin() {
|
|
|
884
805
|
}
|
|
885
806
|
/**
|
|
886
807
|
* Removes the DOM selection.
|
|
887
|
-
*
|
|
888
|
-
* @private
|
|
889
808
|
*/
|
|
890
809
|
_removeDomSelection() {
|
|
891
810
|
for (const doc of this.domDocuments) {
|
|
@@ -901,8 +820,6 @@ export default class Renderer extends ObservableMixin() {
|
|
|
901
820
|
}
|
|
902
821
|
/**
|
|
903
822
|
* Removes the fake selection.
|
|
904
|
-
*
|
|
905
|
-
* @private
|
|
906
823
|
*/
|
|
907
824
|
_removeFakeSelection() {
|
|
908
825
|
const container = this._fakeSelectionContainer;
|
|
@@ -912,8 +829,6 @@ export default class Renderer extends ObservableMixin() {
|
|
|
912
829
|
}
|
|
913
830
|
/**
|
|
914
831
|
* Checks if focus needs to be updated and possibly updates it.
|
|
915
|
-
*
|
|
916
|
-
* @private
|
|
917
832
|
*/
|
|
918
833
|
_updateFocus() {
|
|
919
834
|
if (this.isFocused) {
|
|
@@ -924,11 +839,9 @@ export default class Renderer extends ObservableMixin() {
|
|
|
924
839
|
}
|
|
925
840
|
}
|
|
926
841
|
}
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
// @param {module:engine/view/element~Element} element
|
|
931
|
-
// @returns {Boolean}
|
|
842
|
+
/**
|
|
843
|
+
* Checks if provided element is editable.
|
|
844
|
+
*/
|
|
932
845
|
function isEditable(element) {
|
|
933
846
|
if (element.getAttribute('contenteditable') == 'false') {
|
|
934
847
|
return false;
|
|
@@ -936,16 +849,14 @@ function isEditable(element) {
|
|
|
936
849
|
const parent = element.findAncestor(element => element.hasAttribute('contenteditable'));
|
|
937
850
|
return !parent || parent.getAttribute('contenteditable') == 'true';
|
|
938
851
|
}
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
// @param {Number} offset
|
|
948
|
-
// @returns {Text} The DOM text node that contains an inline filler.
|
|
852
|
+
/**
|
|
853
|
+
* Adds inline filler at a given position.
|
|
854
|
+
*
|
|
855
|
+
* The position can be given as an array of DOM nodes and an offset in that array,
|
|
856
|
+
* or a DOM parent element and an offset in that element.
|
|
857
|
+
*
|
|
858
|
+
* @returns The DOM text node that contains an inline filler.
|
|
859
|
+
*/
|
|
949
860
|
function addInlineFiller(domDocument, domParentOrArray, offset) {
|
|
950
861
|
const childNodes = domParentOrArray instanceof Array ? domParentOrArray : domParentOrArray.childNodes;
|
|
951
862
|
const nodeAfterFiller = childNodes[offset];
|
|
@@ -964,36 +875,33 @@ function addInlineFiller(domDocument, domParentOrArray, offset) {
|
|
|
964
875
|
return fillerNode;
|
|
965
876
|
}
|
|
966
877
|
}
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
// @param {ViewNode} node1
|
|
972
|
-
// @param {ViewNode} node2
|
|
973
|
-
// @returns {Boolean}
|
|
878
|
+
/**
|
|
879
|
+
* Whether two DOM nodes should be considered as similar.
|
|
880
|
+
* Nodes are considered similar if they have the same tag name.
|
|
881
|
+
*/
|
|
974
882
|
function areSimilar(node1, node2) {
|
|
975
883
|
return isNode(node1) && isNode(node2) &&
|
|
976
884
|
!isText(node1) && !isText(node2) &&
|
|
977
885
|
!isComment(node1) && !isComment(node2) &&
|
|
978
886
|
node1.tagName.toLowerCase() === node2.tagName.toLowerCase();
|
|
979
887
|
}
|
|
980
|
-
|
|
888
|
+
/**
|
|
889
|
+
* Whether two DOM nodes are text nodes.
|
|
890
|
+
*/
|
|
981
891
|
function areTextNodes(node1, node2) {
|
|
982
892
|
return isNode(node1) && isNode(node2) &&
|
|
983
893
|
isText(node1) && isText(node2);
|
|
984
894
|
}
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
// @param {ViewNode} node2
|
|
996
|
-
// @returns {Boolean}
|
|
895
|
+
/**
|
|
896
|
+
* Whether two dom nodes should be considered as the same.
|
|
897
|
+
* Two nodes which are considered the same are:
|
|
898
|
+
*
|
|
899
|
+
* * Text nodes with the same text.
|
|
900
|
+
* * Element nodes represented by the same object.
|
|
901
|
+
* * Two block filler elements.
|
|
902
|
+
*
|
|
903
|
+
* @param blockFillerMode Block filler mode, see {@link module:engine/view/domconverter~DomConverter#blockFillerMode}.
|
|
904
|
+
*/
|
|
997
905
|
function sameNodes(domConverter, actualDomChild, expectedDomChild) {
|
|
998
906
|
// Elements.
|
|
999
907
|
if (actualDomChild === expectedDomChild) {
|
|
@@ -1011,13 +919,17 @@ function sameNodes(domConverter, actualDomChild, expectedDomChild) {
|
|
|
1011
919
|
// Not matching types.
|
|
1012
920
|
return false;
|
|
1013
921
|
}
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
922
|
+
/**
|
|
923
|
+
* The following is a Firefox–specific hack (https://github.com/ckeditor/ckeditor5-engine/issues/1439).
|
|
924
|
+
* When the native DOM selection is at the end of the block and preceded by <br /> e.g.
|
|
925
|
+
*
|
|
926
|
+
* ```html
|
|
927
|
+
* <p>foo<br/>[]</p>
|
|
928
|
+
* ```
|
|
929
|
+
*
|
|
930
|
+
* which happens a lot when using the soft line break, the browser fails to (visually) move the
|
|
931
|
+
* caret to the new line. A quick fix is as simple as force–refreshing the selection with the same range.
|
|
932
|
+
*/
|
|
1021
933
|
function fixGeckoSelectionAfterBr(focus, domSelection) {
|
|
1022
934
|
const parent = focus.parent;
|
|
1023
935
|
// This fix works only when the focus point is at the very end of an element.
|
|
@@ -1043,11 +955,9 @@ function filterOutFakeSelectionContainer(domChildList, fakeSelectionContainer) {
|
|
|
1043
955
|
}
|
|
1044
956
|
return childList;
|
|
1045
957
|
}
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
// @param {Document} domDocument
|
|
1050
|
-
// @returns {HTMLElement}
|
|
958
|
+
/**
|
|
959
|
+
* Creates a fake selection container for a given document.
|
|
960
|
+
*/
|
|
1051
961
|
function createFakeSelectionContainer(domDocument) {
|
|
1052
962
|
const container = domDocument.createElement('div');
|
|
1053
963
|
container.className = 'ck-fake-selection-container';
|
|
@@ -1062,15 +972,17 @@ function createFakeSelectionContainer(domDocument) {
|
|
|
1062
972
|
container.textContent = '\u00A0';
|
|
1063
973
|
return container;
|
|
1064
974
|
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
975
|
+
/**
|
|
976
|
+
* Checks if text needs to be updated and possibly updates it by removing and inserting only parts
|
|
977
|
+
* of the data from the existing text node to reduce impact on the IME composition.
|
|
978
|
+
*
|
|
979
|
+
* @param domText DOM text node to update.
|
|
980
|
+
* @param expectedText The expected data of a text node.
|
|
981
|
+
*/
|
|
1070
982
|
function updateTextNode(domText, expectedText) {
|
|
1071
983
|
const actualText = domText.data;
|
|
1072
984
|
if (actualText == expectedText) {
|
|
1073
|
-
// @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
|
|
985
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
1074
986
|
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Text node does not need update:',
|
|
1075
987
|
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', '',
|
|
1076
988
|
// @if CK_DEBUG_TYPING // `"${ domText.data }" (${ domText.data.length })`
|
|
@@ -1078,7 +990,7 @@ function updateTextNode(domText, expectedText) {
|
|
|
1078
990
|
// @if CK_DEBUG_TYPING // }
|
|
1079
991
|
return;
|
|
1080
992
|
}
|
|
1081
|
-
// @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
|
|
993
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
1082
994
|
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Update text node:',
|
|
1083
995
|
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', '',
|
|
1084
996
|
// @if CK_DEBUG_TYPING // `"${ domText.data }" (${ domText.data.length }) -> "${ expectedText }" (${ expectedText.length })`
|