@ckeditor/ckeditor5-engine 45.1.0 → 45.2.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/dist/index.js +72 -26
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/dataprocessor/htmldataprocessor.js +0 -1
- package/src/dataprocessor/xmldataprocessor.js +0 -1
- package/src/dev-utils/model.js +1 -1
- package/src/dev-utils/operationreplayer.js +0 -1
- package/src/dev-utils/utils.js +0 -1
- package/src/dev-utils/view.js +0 -1
- package/src/view/domconverter.js +2 -3
- package/src/view/observer/focusobserver.js +0 -1
- package/src/view/observer/inputobserver.js +39 -2
- package/src/view/observer/mutationobserver.js +0 -1
- package/src/view/observer/selectionobserver.js +3 -3
- package/src/view/renderer.d.ts +3 -3
- package/src/view/renderer.js +30 -20
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ckeditor/ckeditor5-engine",
|
|
3
|
-
"version": "45.
|
|
3
|
+
"version": "45.2.0-alpha.1",
|
|
4
4
|
"description": "The editing engine of CKEditor 5 – the best browser-based rich text editor.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"wysiwyg",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"type": "module",
|
|
25
25
|
"main": "src/index.js",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@ckeditor/ckeditor5-utils": "45.
|
|
27
|
+
"@ckeditor/ckeditor5-utils": "45.2.0-alpha.1",
|
|
28
28
|
"es-toolkit": "1.32.0"
|
|
29
29
|
},
|
|
30
30
|
"author": "CKSource (http://cksource.com/)",
|
package/src/dev-utils/model.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
/**
|
|
6
6
|
* @module engine/dev-utils/operationreplayer
|
|
7
7
|
*/
|
|
8
|
-
/* global setTimeout */
|
|
9
8
|
import OperationFactory from '../model/operation/operationfactory.js';
|
|
10
9
|
/**
|
|
11
10
|
* Operation replayer is a development tool created for easy replaying of operations on the document from stringified operations.
|
package/src/dev-utils/utils.js
CHANGED
package/src/dev-utils/view.js
CHANGED
package/src/view/domconverter.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
/**
|
|
6
6
|
* @module engine/view/domconverter
|
|
7
7
|
*/
|
|
8
|
-
/* globals Node, NodeFilter, DOMParser */
|
|
9
8
|
import ViewText from './text.js';
|
|
10
9
|
import ViewElement from './element.js';
|
|
11
10
|
import ViewUIElement from './uielement.js';
|
|
@@ -955,7 +954,7 @@ export default class DomConverter {
|
|
|
955
954
|
range.setStart(selection.anchorNode, selection.anchorOffset);
|
|
956
955
|
range.setEnd(selection.focusNode, selection.focusOffset);
|
|
957
956
|
}
|
|
958
|
-
catch
|
|
957
|
+
catch {
|
|
959
958
|
// Safari sometimes gives us a selection that makes Range.set{Start,End} throw.
|
|
960
959
|
// See https://github.com/ckeditor/ckeditor5/issues/12375.
|
|
961
960
|
return false;
|
|
@@ -1598,7 +1597,7 @@ function isGeckoRestrictedDomSelection(domSelection) {
|
|
|
1598
1597
|
try {
|
|
1599
1598
|
Object.prototype.toString.call(container);
|
|
1600
1599
|
}
|
|
1601
|
-
catch
|
|
1600
|
+
catch {
|
|
1602
1601
|
return true;
|
|
1603
1602
|
}
|
|
1604
1603
|
return false;
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
/**
|
|
6
6
|
* @module engine/view/observer/focusobserver
|
|
7
7
|
*/
|
|
8
|
-
/* globals setTimeout, clearTimeout */
|
|
9
8
|
import DomEventObserver from './domeventobserver.js';
|
|
10
9
|
// @if CK_DEBUG_TYPING // const { _debouncedLine, _buildLogMessage } = require( '../../dev-utils/utils.js' );
|
|
11
10
|
/**
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import DomEventObserver from './domeventobserver.js';
|
|
9
9
|
import DataTransfer from '../datatransfer.js';
|
|
10
|
-
import { env } from '@ckeditor/ckeditor5-utils';
|
|
10
|
+
import { env, isText, indexOf } from '@ckeditor/ckeditor5-utils';
|
|
11
11
|
import { INLINE_FILLER_LENGTH, startsWithFiller } from '../filler.js';
|
|
12
12
|
// @if CK_DEBUG_TYPING // const { _debouncedLine, _buildLogMessage } = require( '../../dev-utils/utils.js' );
|
|
13
13
|
/**
|
|
@@ -90,7 +90,8 @@ export default class InputObserver extends DomEventObserver {
|
|
|
90
90
|
if (viewStart && startsWithFiller(domRange.startContainer) && domRange.startOffset < INLINE_FILLER_LENGTH) {
|
|
91
91
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
92
92
|
// @if CK_DEBUG_TYPING // console.info( ..._buildLogMessage( this, 'InputObserver',
|
|
93
|
-
// @if CK_DEBUG_TYPING // '
|
|
93
|
+
// @if CK_DEBUG_TYPING // '%cTarget range starts in an inline filler - adjusting it',
|
|
94
|
+
// @if CK_DEBUG_TYPING // 'font-style: italic'
|
|
94
95
|
// @if CK_DEBUG_TYPING // ) );
|
|
95
96
|
// @if CK_DEBUG_TYPING // }
|
|
96
97
|
domEvent.preventDefault();
|
|
@@ -107,6 +108,16 @@ export default class InputObserver extends DomEventObserver {
|
|
|
107
108
|
return false;
|
|
108
109
|
}, { direction: 'backward', singleCharacters: true });
|
|
109
110
|
}
|
|
111
|
+
// Check if there is no an inline filler just after the target range.
|
|
112
|
+
if (isFollowedByInlineFiller(domRange.endContainer, domRange.endOffset)) {
|
|
113
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
114
|
+
// @if CK_DEBUG_TYPING // console.info( ..._buildLogMessage( this, 'InputObserver',
|
|
115
|
+
// @if CK_DEBUG_TYPING // '%cTarget range ends just before an inline filler - prevent default behavior',
|
|
116
|
+
// @if CK_DEBUG_TYPING // 'font-style: italic'
|
|
117
|
+
// @if CK_DEBUG_TYPING // ) );
|
|
118
|
+
// @if CK_DEBUG_TYPING // }
|
|
119
|
+
domEvent.preventDefault();
|
|
120
|
+
}
|
|
110
121
|
if (viewStart) {
|
|
111
122
|
return view.createRange(viewStart, viewEnd);
|
|
112
123
|
}
|
|
@@ -157,6 +168,8 @@ export default class InputObserver extends DomEventObserver {
|
|
|
157
168
|
// it to paragraphs as it is our default action for enter handling.
|
|
158
169
|
const parts = data.split(/\n{1,2}/g);
|
|
159
170
|
let partTargetRanges = targetRanges;
|
|
171
|
+
// Handle all parts on our side as we rely on paragraph inserting and synchronously updated view selection.
|
|
172
|
+
domEvent.preventDefault();
|
|
160
173
|
for (let i = 0; i < parts.length; i++) {
|
|
161
174
|
const dataPart = parts[i];
|
|
162
175
|
if (dataPart != '') {
|
|
@@ -197,3 +210,27 @@ export default class InputObserver extends DomEventObserver {
|
|
|
197
210
|
// @if CK_DEBUG_TYPING // }
|
|
198
211
|
}
|
|
199
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Returns `true` if there is an inline filler just after the position in DOM.
|
|
215
|
+
* It walks up the DOM tree if the offset is at the end of the node.
|
|
216
|
+
*/
|
|
217
|
+
function isFollowedByInlineFiller(node, offset) {
|
|
218
|
+
while (node.parentNode) {
|
|
219
|
+
if (isText(node)) {
|
|
220
|
+
if (offset != node.data.length) {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
if (offset != node.childNodes.length) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
offset = indexOf(node) + 1;
|
|
230
|
+
node = node.parentNode;
|
|
231
|
+
if (offset < node.childNodes.length && startsWithFiller(node.childNodes[offset])) {
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
/**
|
|
6
6
|
* @module engine/view/observer/selectionobserver
|
|
7
7
|
*/
|
|
8
|
-
/* global setInterval, clearInterval */
|
|
9
8
|
import Observer from './observer.js';
|
|
10
9
|
import MutationObserver from './mutationobserver.js';
|
|
11
10
|
import FocusObserver from './focusobserver.js';
|
|
@@ -237,8 +236,9 @@ export default class SelectionObserver extends Observer {
|
|
|
237
236
|
this.view.hasDomSelection = true;
|
|
238
237
|
// Mark the latest focus change as complete (we got new selection after the focus so the selection is in the focused element).
|
|
239
238
|
this.focusObserver.flush();
|
|
240
|
-
// Ignore selection change as the editable is not focused.
|
|
241
|
-
|
|
239
|
+
// Ignore selection change as the editable is not focused. Note that in read-only mode, we have to update
|
|
240
|
+
// the model selection as there won't be any focus change to flush the pending selection changes.
|
|
241
|
+
if (!this.view.document.isFocused && !this.view.document.isReadOnly) {
|
|
242
242
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
243
243
|
// @if CK_DEBUG_TYPING // console.info( ..._buildLogMessage( this, 'SelectionObserver',
|
|
244
244
|
// @if CK_DEBUG_TYPING // 'Ignore selection change while editable is not focused'
|
package/src/view/renderer.d.ts
CHANGED
|
@@ -240,13 +240,13 @@ export default class Renderer extends /* #__PURE__ */ Renderer_base {
|
|
|
240
240
|
/**
|
|
241
241
|
* Updates the fake selection.
|
|
242
242
|
*
|
|
243
|
-
* @param
|
|
243
|
+
* @param domEditable A valid DOM editable where the fake selection container should be added.
|
|
244
244
|
*/
|
|
245
245
|
private _updateFakeSelection;
|
|
246
246
|
/**
|
|
247
247
|
* Updates the DOM selection.
|
|
248
248
|
*
|
|
249
|
-
* @param
|
|
249
|
+
* @param domEditable A valid DOM editable where the DOM selection should be rendered.
|
|
250
250
|
*/
|
|
251
251
|
private _updateDomSelection;
|
|
252
252
|
/**
|
|
@@ -258,7 +258,7 @@ export default class Renderer extends /* #__PURE__ */ Renderer_base {
|
|
|
258
258
|
/**
|
|
259
259
|
* Checks whether the fake selection needs to be updated.
|
|
260
260
|
*
|
|
261
|
-
* @param
|
|
261
|
+
* @param domEditable A valid DOM editable where a new fake selection container should be added.
|
|
262
262
|
*/
|
|
263
263
|
private _fakeSelectionNeedsUpdate;
|
|
264
264
|
/**
|
package/src/view/renderer.js
CHANGED
|
@@ -797,15 +797,25 @@ export default class Renderer extends /* #__PURE__ */ ObservableMixin() {
|
|
|
797
797
|
this._removeFakeSelection();
|
|
798
798
|
return;
|
|
799
799
|
}
|
|
800
|
-
const
|
|
801
|
-
// Do
|
|
802
|
-
if (!this.isFocused || !
|
|
800
|
+
const domEditable = this.domConverter.mapViewToDom(this.selection.editableElement);
|
|
801
|
+
// Do not update DOM selection if there is no focus, or there is no DOM element corresponding to selection's editable element.
|
|
802
|
+
if (!this.isFocused || !domEditable) {
|
|
803
803
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
804
804
|
// @if CK_DEBUG_TYPING // console.info( ..._buildLogMessage( this, 'Renderer',
|
|
805
805
|
// @if CK_DEBUG_TYPING // 'Skip updating DOM selection:',
|
|
806
|
-
// @if CK_DEBUG_TYPING // `isFocused: ${ this.isFocused },
|
|
806
|
+
// @if CK_DEBUG_TYPING // `isFocused: ${ this.isFocused }, hasDomEditable: ${ !!domEditable }`
|
|
807
807
|
// @if CK_DEBUG_TYPING // ) );
|
|
808
808
|
// @if CK_DEBUG_TYPING // }
|
|
809
|
+
// But if there was a fake selection, and it is not fake anymore - remove it as it can map to no longer existing widget.
|
|
810
|
+
// See https://github.com/ckeditor/ckeditor5/issues/18123.
|
|
811
|
+
if (!this.selection.isFake && this._fakeSelectionContainer && this._fakeSelectionContainer.isConnected) {
|
|
812
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
813
|
+
// @if CK_DEBUG_TYPING // console.info( ..._buildLogMessage( this, 'Renderer',
|
|
814
|
+
// @if CK_DEBUG_TYPING // 'Remove fake selection (not focused editable)'
|
|
815
|
+
// @if CK_DEBUG_TYPING // ) );
|
|
816
|
+
// @if CK_DEBUG_TYPING // }
|
|
817
|
+
this._removeFakeSelection();
|
|
818
|
+
}
|
|
809
819
|
return;
|
|
810
820
|
}
|
|
811
821
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
@@ -815,40 +825,40 @@ export default class Renderer extends /* #__PURE__ */ ObservableMixin() {
|
|
|
815
825
|
// @if CK_DEBUG_TYPING // }
|
|
816
826
|
// Render fake selection - create the fake selection container (if needed) and move DOM selection to it.
|
|
817
827
|
if (this.selection.isFake) {
|
|
818
|
-
this._updateFakeSelection(
|
|
828
|
+
this._updateFakeSelection(domEditable);
|
|
819
829
|
}
|
|
820
830
|
// There was a fake selection so remove it and update the DOM selection.
|
|
821
831
|
// This is especially important on Android because otherwise IME will try to compose over the fake selection container.
|
|
822
832
|
else if (this._fakeSelectionContainer && this._fakeSelectionContainer.isConnected) {
|
|
823
833
|
this._removeFakeSelection();
|
|
824
|
-
this._updateDomSelection(
|
|
834
|
+
this._updateDomSelection(domEditable);
|
|
825
835
|
}
|
|
826
836
|
// Update the DOM selection in case of a plain selection change (no fake selection is involved).
|
|
827
837
|
// On non-Android the whole rendering is disabled in composition mode (including DOM selection update),
|
|
828
838
|
// but updating DOM selection should be also disabled on Android if in the middle of the composition
|
|
829
839
|
// (to not interrupt it).
|
|
830
840
|
else if (!(this.isComposing && env.isAndroid)) {
|
|
831
|
-
this._updateDomSelection(
|
|
841
|
+
this._updateDomSelection(domEditable);
|
|
832
842
|
}
|
|
833
843
|
}
|
|
834
844
|
/**
|
|
835
845
|
* Updates the fake selection.
|
|
836
846
|
*
|
|
837
|
-
* @param
|
|
847
|
+
* @param domEditable A valid DOM editable where the fake selection container should be added.
|
|
838
848
|
*/
|
|
839
|
-
_updateFakeSelection(
|
|
840
|
-
const domDocument =
|
|
849
|
+
_updateFakeSelection(domEditable) {
|
|
850
|
+
const domDocument = domEditable.ownerDocument;
|
|
841
851
|
if (!this._fakeSelectionContainer) {
|
|
842
852
|
this._fakeSelectionContainer = createFakeSelectionContainer(domDocument);
|
|
843
853
|
}
|
|
844
854
|
const container = this._fakeSelectionContainer;
|
|
845
855
|
// Bind fake selection container with the current selection *position*.
|
|
846
856
|
this.domConverter.bindFakeSelection(container, this.selection);
|
|
847
|
-
if (!this._fakeSelectionNeedsUpdate(
|
|
857
|
+
if (!this._fakeSelectionNeedsUpdate(domEditable)) {
|
|
848
858
|
return;
|
|
849
859
|
}
|
|
850
|
-
if (!container.parentElement || container.parentElement !=
|
|
851
|
-
|
|
860
|
+
if (!container.parentElement || container.parentElement != domEditable) {
|
|
861
|
+
domEditable.appendChild(container);
|
|
852
862
|
}
|
|
853
863
|
container.textContent = this.selection.fakeSelectionLabel || '\u00A0';
|
|
854
864
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
@@ -865,10 +875,10 @@ export default class Renderer extends /* #__PURE__ */ ObservableMixin() {
|
|
|
865
875
|
/**
|
|
866
876
|
* Updates the DOM selection.
|
|
867
877
|
*
|
|
868
|
-
* @param
|
|
878
|
+
* @param domEditable A valid DOM editable where the DOM selection should be rendered.
|
|
869
879
|
*/
|
|
870
|
-
_updateDomSelection(
|
|
871
|
-
const domSelection =
|
|
880
|
+
_updateDomSelection(domEditable) {
|
|
881
|
+
const domSelection = domEditable.ownerDocument.defaultView.getSelection();
|
|
872
882
|
// Let's check whether DOM selection needs updating at all.
|
|
873
883
|
if (!this._domSelectionNeedsUpdate(domSelection)) {
|
|
874
884
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
@@ -924,14 +934,14 @@ export default class Renderer extends /* #__PURE__ */ ObservableMixin() {
|
|
|
924
934
|
/**
|
|
925
935
|
* Checks whether the fake selection needs to be updated.
|
|
926
936
|
*
|
|
927
|
-
* @param
|
|
937
|
+
* @param domEditable A valid DOM editable where a new fake selection container should be added.
|
|
928
938
|
*/
|
|
929
|
-
_fakeSelectionNeedsUpdate(
|
|
939
|
+
_fakeSelectionNeedsUpdate(domEditable) {
|
|
930
940
|
const container = this._fakeSelectionContainer;
|
|
931
|
-
const domSelection =
|
|
941
|
+
const domSelection = domEditable.ownerDocument.getSelection();
|
|
932
942
|
// Fake selection needs to be updated if there's no fake selection container, or the container currently sits
|
|
933
943
|
// in a different root.
|
|
934
|
-
if (!container || container.parentElement !==
|
|
944
|
+
if (!container || container.parentElement !== domEditable) {
|
|
935
945
|
return true;
|
|
936
946
|
}
|
|
937
947
|
// Make sure that the selection actually is within the fake selection.
|