@ckeditor/ckeditor5-engine 42.0.2-alpha.2 → 43.0.0-alpha.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.
- package/CHANGELOG.md +1 -820
- package/dist/dev-utils/model.d.ts +2 -0
- package/dist/dev-utils/view.d.ts +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +466 -271
- package/dist/index.js.map +1 -1
- package/dist/model/schema.d.ts +149 -51
- package/dist/view/observer/focusobserver.d.ts +12 -0
- package/dist/view/observer/mutationobserver.d.ts +34 -5
- package/dist/view/observer/selectionobserver.d.ts +1 -2
- package/dist/view/renderer.d.ts +12 -0
- package/dist/view/view.d.ts +1 -4
- package/package.json +2 -2
- package/src/conversion/upcasthelpers.js +0 -7
- package/src/dev-utils/model.d.ts +2 -0
- package/src/dev-utils/model.js +4 -2
- package/src/dev-utils/utils.js +7 -0
- package/src/dev-utils/view.d.ts +1 -0
- package/src/dev-utils/view.js +3 -0
- package/src/index.d.ts +3 -1
- package/src/index.js +2 -0
- package/src/model/model.js +1 -5
- package/src/model/schema.d.ts +149 -51
- package/src/model/schema.js +200 -70
- package/src/model/utils/insertcontent.js +21 -65
- package/src/view/domconverter.js +13 -9
- package/src/view/observer/compositionobserver.js +2 -0
- package/src/view/observer/focusobserver.d.ts +12 -0
- package/src/view/observer/focusobserver.js +55 -25
- package/src/view/observer/inputobserver.js +7 -5
- package/src/view/observer/mutationobserver.d.ts +34 -5
- package/src/view/observer/mutationobserver.js +8 -11
- package/src/view/observer/selectionobserver.d.ts +1 -2
- package/src/view/observer/selectionobserver.js +27 -9
- package/src/view/renderer.d.ts +12 -0
- package/src/view/renderer.js +111 -63
- package/src/view/view.d.ts +1 -4
- package/src/view/view.js +9 -0
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @license Copyright (c) 2003-2024, 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
|
-
import { logWarning, EmitterMixin, CKEditorError, compareArrays, toArray, toMap, isIterable, ObservableMixin, count, EventInfo, Collection, keyCodes, isText, env, remove as remove$1, insertAt, diff, isNode, isComment, indexOf,
|
|
5
|
+
import { logWarning, EmitterMixin, CKEditorError, compareArrays, toArray, toMap, isIterable, ObservableMixin, count, EventInfo, Collection, keyCodes, isText, env, remove as remove$1, insertAt, diff, fastDiff, isNode, isComment, indexOf, global, isValidAttributeName, first, getAncestors, DomEmitterMixin, getCode, isArrowKeyCode, scrollViewportToShowTarget, spliceArray, uid, priorities, isInsideSurrogatePair, isInsideCombinedSymbol, isInsideEmojiSequence } from '@ckeditor/ckeditor5-utils/dist/index.js';
|
|
6
6
|
import { clone, isPlainObject, isObject, unset, get, merge, set, extend, debounce, isEqualWith, cloneDeep, isEqual } from 'lodash-es';
|
|
7
7
|
|
|
8
8
|
// Each document stores information about its placeholder elements and check functions.
|
|
@@ -7332,6 +7332,7 @@ const validNodesToInsert = [
|
|
|
7332
7332
|
this.selection = selection;
|
|
7333
7333
|
this.set('isFocused', false);
|
|
7334
7334
|
this.set('isSelecting', false);
|
|
7335
|
+
this.set('isComposing', false);
|
|
7335
7336
|
// Rendering the selection and inline filler manipulation should be postponed in (non-Android) Blink until the user finishes
|
|
7336
7337
|
// creating the selection in DOM to avoid accidental selection collapsing
|
|
7337
7338
|
// (https://github.com/ckeditor/ckeditor5/issues/10562, https://github.com/ckeditor/ckeditor5/issues/10723).
|
|
@@ -7343,12 +7344,6 @@ const validNodesToInsert = [
|
|
|
7343
7344
|
}
|
|
7344
7345
|
});
|
|
7345
7346
|
}
|
|
7346
|
-
this.set('isComposing', false);
|
|
7347
|
-
this.on('change:isComposing', ()=>{
|
|
7348
|
-
if (!this.isComposing) {
|
|
7349
|
-
this.render();
|
|
7350
|
-
}
|
|
7351
|
-
});
|
|
7352
7347
|
}
|
|
7353
7348
|
/**
|
|
7354
7349
|
* Marks a view node to be updated in the DOM by {@link #render `render()`}.
|
|
@@ -7402,15 +7397,15 @@ const validNodesToInsert = [
|
|
|
7402
7397
|
// and we should not do it because the difference between view and DOM could lead to position mapping problems.
|
|
7403
7398
|
if (this.isComposing && !env.isAndroid) {
|
|
7404
7399
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
7405
|
-
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Rendering aborted while isComposing',
|
|
7406
|
-
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', ''
|
|
7400
|
+
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Rendering aborted while isComposing.',
|
|
7401
|
+
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-style: italic'
|
|
7407
7402
|
// @if CK_DEBUG_TYPING // );
|
|
7408
7403
|
// @if CK_DEBUG_TYPING // }
|
|
7409
7404
|
return;
|
|
7410
7405
|
}
|
|
7411
7406
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
7412
7407
|
// @if CK_DEBUG_TYPING // console.group( '%c[Renderer]%c Rendering',
|
|
7413
|
-
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', ''
|
|
7408
|
+
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-weight: bold'
|
|
7414
7409
|
// @if CK_DEBUG_TYPING // );
|
|
7415
7410
|
// @if CK_DEBUG_TYPING // }
|
|
7416
7411
|
let inlineFillerPosition = null;
|
|
@@ -7689,10 +7684,10 @@ const validNodesToInsert = [
|
|
|
7689
7684
|
}
|
|
7690
7685
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
7691
7686
|
// @if CK_DEBUG_TYPING // console.group( '%c[Renderer]%c Update text',
|
|
7692
|
-
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', ''
|
|
7687
|
+
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-weight: normal'
|
|
7693
7688
|
// @if CK_DEBUG_TYPING // );
|
|
7694
7689
|
// @if CK_DEBUG_TYPING // }
|
|
7695
|
-
|
|
7690
|
+
this._updateTextNode(domText, expectedText);
|
|
7696
7691
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
7697
7692
|
// @if CK_DEBUG_TYPING // console.groupEnd();
|
|
7698
7693
|
// @if CK_DEBUG_TYPING // }
|
|
@@ -7741,7 +7736,7 @@ const validNodesToInsert = [
|
|
|
7741
7736
|
}
|
|
7742
7737
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
7743
7738
|
// @if CK_DEBUG_TYPING // console.group( '%c[Renderer]%c Update children',
|
|
7744
|
-
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', ''
|
|
7739
|
+
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-weight: normal'
|
|
7745
7740
|
// @if CK_DEBUG_TYPING // );
|
|
7746
7741
|
// @if CK_DEBUG_TYPING // }
|
|
7747
7742
|
// IME on Android inserts a new text node while typing after a link
|
|
@@ -7773,6 +7768,11 @@ const validNodesToInsert = [
|
|
|
7773
7768
|
// We need to make sure that we update the existing text node and not replace it with another one.
|
|
7774
7769
|
// The composition and different "language" browser extensions are fragile to text node being completely replaced.
|
|
7775
7770
|
const actions = this._findUpdateActions(diff, actualDomChildren, expectedDomChildren, areTextNodes);
|
|
7771
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping && actions.every( a => a == 'equal' ) ) {
|
|
7772
|
+
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Nothing to update.',
|
|
7773
|
+
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-style: italic'
|
|
7774
|
+
// @if CK_DEBUG_TYPING // );
|
|
7775
|
+
// @if CK_DEBUG_TYPING // }
|
|
7776
7776
|
let i = 0;
|
|
7777
7777
|
const nodesToUnbind = new Set();
|
|
7778
7778
|
// Handle deletions first.
|
|
@@ -7784,9 +7784,22 @@ const validNodesToInsert = [
|
|
|
7784
7784
|
for (const action of actions){
|
|
7785
7785
|
if (action === 'delete') {
|
|
7786
7786
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
7787
|
-
// @if CK_DEBUG_TYPING //
|
|
7788
|
-
// @if CK_DEBUG_TYPING //
|
|
7789
|
-
// @if CK_DEBUG_TYPING //
|
|
7787
|
+
// @if CK_DEBUG_TYPING // const node = actualDomChildren[ i ];
|
|
7788
|
+
// @if CK_DEBUG_TYPING // if ( isText( node ) ) {
|
|
7789
|
+
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Remove text node' +
|
|
7790
|
+
// @if CK_DEBUG_TYPING // `${ this.isComposing ? ' while composing (may break composition)' : '' }: ` +
|
|
7791
|
+
// @if CK_DEBUG_TYPING // `%c${ _escapeTextNodeData( node.data ) }%c (${ node.data.length })`,
|
|
7792
|
+
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold',
|
|
7793
|
+
// @if CK_DEBUG_TYPING // this.isComposing ? 'color: red; font-weight: bold' : '', 'color: blue', ''
|
|
7794
|
+
// @if CK_DEBUG_TYPING // );
|
|
7795
|
+
// @if CK_DEBUG_TYPING // } else {
|
|
7796
|
+
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Remove element' +
|
|
7797
|
+
// @if CK_DEBUG_TYPING // `${ this.isComposing ? ' while composing (may break composition)' : '' }: `,
|
|
7798
|
+
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold',
|
|
7799
|
+
// @if CK_DEBUG_TYPING // this.isComposing ? 'color: red; font-weight: bold' : '',
|
|
7800
|
+
// @if CK_DEBUG_TYPING // node
|
|
7801
|
+
// @if CK_DEBUG_TYPING // );
|
|
7802
|
+
// @if CK_DEBUG_TYPING // }
|
|
7790
7803
|
// @if CK_DEBUG_TYPING // }
|
|
7791
7804
|
nodesToUnbind.add(actualDomChildren[i]);
|
|
7792
7805
|
remove$1(actualDomChildren[i]);
|
|
@@ -7798,23 +7811,27 @@ const validNodesToInsert = [
|
|
|
7798
7811
|
for (const action of actions){
|
|
7799
7812
|
if (action === 'insert') {
|
|
7800
7813
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
7801
|
-
// @if CK_DEBUG_TYPING //
|
|
7802
|
-
// @if CK_DEBUG_TYPING //
|
|
7803
|
-
// @if CK_DEBUG_TYPING //
|
|
7814
|
+
// @if CK_DEBUG_TYPING // const node = expectedDomChildren[ i ];
|
|
7815
|
+
// @if CK_DEBUG_TYPING // if ( isText( node ) ) {
|
|
7816
|
+
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Insert text node' +
|
|
7817
|
+
// @if CK_DEBUG_TYPING // `${ this.isComposing ? ' while composing (may break composition)' : '' }: ` +
|
|
7818
|
+
// @if CK_DEBUG_TYPING // `%c${ _escapeTextNodeData( node.data ) }%c (${ node.data.length })`,
|
|
7819
|
+
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold',
|
|
7820
|
+
// @if CK_DEBUG_TYPING // this.isComposing ? 'color: red; font-weight: bold' : '',
|
|
7821
|
+
// @if CK_DEBUG_TYPING // 'color: blue', ''
|
|
7822
|
+
// @if CK_DEBUG_TYPING // );
|
|
7823
|
+
// @if CK_DEBUG_TYPING // } else {
|
|
7824
|
+
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Insert element:',
|
|
7825
|
+
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-weight: normal',
|
|
7826
|
+
// @if CK_DEBUG_TYPING // node
|
|
7827
|
+
// @if CK_DEBUG_TYPING // );
|
|
7828
|
+
// @if CK_DEBUG_TYPING // }
|
|
7804
7829
|
// @if CK_DEBUG_TYPING // }
|
|
7805
7830
|
insertAt(domElement, i, expectedDomChildren[i]);
|
|
7806
7831
|
i++;
|
|
7807
7832
|
} else if (action === 'update') {
|
|
7808
|
-
|
|
7809
|
-
// @if CK_DEBUG_TYPING // console.group( '%c[Renderer]%c Update text node',
|
|
7810
|
-
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', ''
|
|
7811
|
-
// @if CK_DEBUG_TYPING // );
|
|
7812
|
-
// @if CK_DEBUG_TYPING // }
|
|
7813
|
-
updateTextNode(actualDomChildren[i], expectedDomChildren[i].data);
|
|
7833
|
+
this._updateTextNode(actualDomChildren[i], expectedDomChildren[i].data);
|
|
7814
7834
|
i++;
|
|
7815
|
-
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
7816
|
-
// @if CK_DEBUG_TYPING // console.groupEnd();
|
|
7817
|
-
// @if CK_DEBUG_TYPING // }
|
|
7818
7835
|
} else if (action === 'equal') {
|
|
7819
7836
|
// Force updating text nodes inside elements which did not change and do not need to be re-rendered (#1125).
|
|
7820
7837
|
// Do it here (not in the loop above) because only after insertions the `i` index is correct.
|
|
@@ -7890,6 +7907,60 @@ const validNodesToInsert = [
|
|
|
7890
7907
|
}
|
|
7891
7908
|
return newActions.concat(diff(actualSlice, expectedSlice, comparator).map((action)=>action === 'equal' ? 'update' : action));
|
|
7892
7909
|
}
|
|
7910
|
+
/**
|
|
7911
|
+
* Checks if text needs to be updated and possibly updates it by removing and inserting only parts
|
|
7912
|
+
* of the data from the existing text node to reduce impact on the IME composition.
|
|
7913
|
+
*
|
|
7914
|
+
* @param domText DOM text node to update.
|
|
7915
|
+
* @param expectedText The expected data of a text node.
|
|
7916
|
+
*/ _updateTextNode(domText, expectedText) {
|
|
7917
|
+
const actualText = domText.data;
|
|
7918
|
+
if (actualText == expectedText) {
|
|
7919
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
7920
|
+
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Text node does not need update:%c ' +
|
|
7921
|
+
// @if CK_DEBUG_TYPING // `${ _escapeTextNodeData( actualText ) }%c (${ actualText.length })`,
|
|
7922
|
+
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-style: italic', 'color: blue', ''
|
|
7923
|
+
// @if CK_DEBUG_TYPING // );
|
|
7924
|
+
// @if CK_DEBUG_TYPING // }
|
|
7925
|
+
return;
|
|
7926
|
+
}
|
|
7927
|
+
// Our approach to interleaving space character with NBSP might differ with the one implemented by the browser.
|
|
7928
|
+
// Avoid modifying the text node in the DOM if only NBSPs and spaces are interchanged.
|
|
7929
|
+
// We should avoid DOM modifications while composing to avoid breakage of composition.
|
|
7930
|
+
// See: https://github.com/ckeditor/ckeditor5/issues/13994.
|
|
7931
|
+
if (env.isAndroid && this.isComposing && actualText.replace(/\u00A0/g, ' ') == expectedText.replace(/\u00A0/g, ' ')) {
|
|
7932
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
7933
|
+
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Text node ignore NBSP changes while composing: ' +
|
|
7934
|
+
// @if CK_DEBUG_TYPING // `%c${ _escapeTextNodeData( actualText ) }%c (${ actualText.length }) ->` +
|
|
7935
|
+
// @if CK_DEBUG_TYPING // ` %c${ _escapeTextNodeData( expectedText ) }%c (${ expectedText.length })`,
|
|
7936
|
+
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-style: italic', 'color: blue', '', 'color: blue', ''
|
|
7937
|
+
// @if CK_DEBUG_TYPING // );
|
|
7938
|
+
// @if CK_DEBUG_TYPING // }
|
|
7939
|
+
return;
|
|
7940
|
+
}
|
|
7941
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
7942
|
+
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Update text node' +
|
|
7943
|
+
// @if CK_DEBUG_TYPING // `${ this.isComposing ? ' while composing (may break composition)' : '' }: ` +
|
|
7944
|
+
// @if CK_DEBUG_TYPING // `%c${ _escapeTextNodeData( actualText ) }%c (${ actualText.length }) ->` +
|
|
7945
|
+
// @if CK_DEBUG_TYPING // ` %c${ _escapeTextNodeData( expectedText ) }%c (${ expectedText.length })`,
|
|
7946
|
+
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', this.isComposing ? 'color: red; font-weight: bold' : '',
|
|
7947
|
+
// @if CK_DEBUG_TYPING // 'color: blue', '', 'color: blue', ''
|
|
7948
|
+
// @if CK_DEBUG_TYPING // );
|
|
7949
|
+
// @if CK_DEBUG_TYPING // }
|
|
7950
|
+
this._updateTextNodeInternal(domText, expectedText);
|
|
7951
|
+
}
|
|
7952
|
+
/**
|
|
7953
|
+
* Part of the `_updateTextNode` method extracted for easier testing.
|
|
7954
|
+
*/ _updateTextNodeInternal(domText, expectedText) {
|
|
7955
|
+
const actions = fastDiff(domText.data, expectedText);
|
|
7956
|
+
for (const action of actions){
|
|
7957
|
+
if (action.type === 'insert') {
|
|
7958
|
+
domText.insertData(action.index, action.values.join(''));
|
|
7959
|
+
} else {
|
|
7960
|
+
domText.deleteData(action.index, action.howMany);
|
|
7961
|
+
}
|
|
7962
|
+
}
|
|
7963
|
+
}
|
|
7893
7964
|
/**
|
|
7894
7965
|
* Marks text nodes to be synchronized.
|
|
7895
7966
|
*
|
|
@@ -7983,7 +8054,7 @@ const validNodesToInsert = [
|
|
|
7983
8054
|
const focus = this.domConverter.viewPositionToDom(this.selection.focus);
|
|
7984
8055
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
7985
8056
|
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Update DOM selection:',
|
|
7986
|
-
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', '', anchor, focus
|
|
8057
|
+
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', '', anchor, focus
|
|
7987
8058
|
// @if CK_DEBUG_TYPING // );
|
|
7988
8059
|
// @if CK_DEBUG_TYPING // }
|
|
7989
8060
|
domSelection.setBaseAndExtent(anchor.parent, anchor.offset, focus.parent, focus.offset);
|
|
@@ -8183,39 +8254,14 @@ function filterOutFakeSelectionContainer(domChildList, fakeSelectionContainer) {
|
|
|
8183
8254
|
// Fill it with a text node so we can update it later.
|
|
8184
8255
|
container.textContent = '\u00A0';
|
|
8185
8256
|
return container;
|
|
8186
|
-
}
|
|
8187
|
-
|
|
8188
|
-
|
|
8189
|
-
|
|
8190
|
-
|
|
8191
|
-
|
|
8192
|
-
|
|
8193
|
-
|
|
8194
|
-
const actualText = domText.data;
|
|
8195
|
-
if (actualText == expectedText) {
|
|
8196
|
-
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
8197
|
-
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Text node does not need update:',
|
|
8198
|
-
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', '',
|
|
8199
|
-
// @if CK_DEBUG_TYPING // `"${ domText.data }" (${ domText.data.length })`
|
|
8200
|
-
// @if CK_DEBUG_TYPING // );
|
|
8201
|
-
// @if CK_DEBUG_TYPING // }
|
|
8202
|
-
return;
|
|
8203
|
-
}
|
|
8204
|
-
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
8205
|
-
// @if CK_DEBUG_TYPING // console.info( '%c[Renderer]%c Update text node:',
|
|
8206
|
-
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', '',
|
|
8207
|
-
// @if CK_DEBUG_TYPING // `"${ domText.data }" (${ domText.data.length }) -> "${ expectedText }" (${ expectedText.length })`
|
|
8208
|
-
// @if CK_DEBUG_TYPING // );
|
|
8209
|
-
// @if CK_DEBUG_TYPING // }
|
|
8210
|
-
const actions = fastDiff(actualText, expectedText);
|
|
8211
|
-
for (const action of actions){
|
|
8212
|
-
if (action.type === 'insert') {
|
|
8213
|
-
domText.insertData(action.index, action.values.join(''));
|
|
8214
|
-
} else {
|
|
8215
|
-
domText.deleteData(action.index, action.howMany);
|
|
8216
|
-
}
|
|
8217
|
-
}
|
|
8218
|
-
}
|
|
8257
|
+
} // @if CK_DEBUG_TYPING // function _escapeTextNodeData( text ) {
|
|
8258
|
+
// @if CK_DEBUG_TYPING // const escapedText = text
|
|
8259
|
+
// @if CK_DEBUG_TYPING // .replace( /&/g, '&' )
|
|
8260
|
+
// @if CK_DEBUG_TYPING // .replace( /\u00A0/g, ' ' )
|
|
8261
|
+
// @if CK_DEBUG_TYPING // .replace( /\u2060/g, '⁠' );
|
|
8262
|
+
// @if CK_DEBUG_TYPING //
|
|
8263
|
+
// @if CK_DEBUG_TYPING // return `"${ escapedText }"`;
|
|
8264
|
+
// @if CK_DEBUG_TYPING // }
|
|
8219
8265
|
|
|
8220
8266
|
const BR_FILLER_REF = BR_FILLER(global.document); // eslint-disable-line new-cap
|
|
8221
8267
|
const NBSP_FILLER_REF = NBSP_FILLER(global.document); // eslint-disable-line new-cap
|
|
@@ -9522,16 +9568,18 @@ const UNSAFE_ELEMENT_REPLACEMENT_ATTRIBUTE = 'data-ck-unsafe-element';
|
|
|
9522
9568
|
startPosition: getNext ? Position$1._createAfter(node) : Position$1._createBefore(node),
|
|
9523
9569
|
direction: getNext ? 'forward' : 'backward'
|
|
9524
9570
|
});
|
|
9525
|
-
for (const
|
|
9526
|
-
//
|
|
9527
|
-
if (
|
|
9571
|
+
for (const { item } of treeWalker){
|
|
9572
|
+
// Found a text node in the same container element.
|
|
9573
|
+
if (item.is('$textProxy')) {
|
|
9574
|
+
return item;
|
|
9575
|
+
} else if (item.is('element') && item.getCustomProperty('dataPipeline:transparentRendering')) {
|
|
9576
|
+
continue;
|
|
9577
|
+
} else if (item.is('element', 'br')) {
|
|
9528
9578
|
return null;
|
|
9529
|
-
} else if (this._isInlineObjectElement(
|
|
9530
|
-
return
|
|
9531
|
-
} else if (
|
|
9579
|
+
} else if (this._isInlineObjectElement(item)) {
|
|
9580
|
+
return item;
|
|
9581
|
+
} else if (item.is('containerElement')) {
|
|
9532
9582
|
return null;
|
|
9533
|
-
} else if (value.item.is('$textProxy')) {
|
|
9534
|
-
return value.item;
|
|
9535
9583
|
}
|
|
9536
9584
|
}
|
|
9537
9585
|
return null;
|
|
@@ -10018,6 +10066,7 @@ const UNSAFE_ELEMENT_REPLACEMENT_ATTRIBUTE = 'data-ck-unsafe-element';
|
|
|
10018
10066
|
}
|
|
10019
10067
|
}
|
|
10020
10068
|
|
|
10069
|
+
// @if CK_DEBUG_TYPING // const { _debouncedLine } = require( '../../dev-utils/utils.js' );
|
|
10021
10070
|
/**
|
|
10022
10071
|
* Mutation observer's role is to watch for any DOM changes inside the editor that weren't
|
|
10023
10072
|
* done by the editor's {@link module:engine/view/renderer~Renderer} itself and reverting these changes.
|
|
@@ -10032,9 +10081,6 @@ const UNSAFE_ELEMENT_REPLACEMENT_ATTRIBUTE = 'data-ck-unsafe-element';
|
|
|
10032
10081
|
/**
|
|
10033
10082
|
* Reference to the {@link module:engine/view/view~View#domConverter}.
|
|
10034
10083
|
*/ domConverter;
|
|
10035
|
-
/**
|
|
10036
|
-
* Reference to the {@link module:engine/view/view~View#_renderer}.
|
|
10037
|
-
*/ renderer;
|
|
10038
10084
|
/**
|
|
10039
10085
|
* Native mutation observer config.
|
|
10040
10086
|
*/ _config;
|
|
@@ -10054,7 +10100,6 @@ const UNSAFE_ELEMENT_REPLACEMENT_ATTRIBUTE = 'data-ck-unsafe-element';
|
|
|
10054
10100
|
subtree: true
|
|
10055
10101
|
};
|
|
10056
10102
|
this.domConverter = view.domConverter;
|
|
10057
|
-
this.renderer = view._renderer;
|
|
10058
10103
|
this._domElements = new Set();
|
|
10059
10104
|
this._mutationObserver = new window.MutationObserver(this._onMutations.bind(this));
|
|
10060
10105
|
}
|
|
@@ -10150,10 +10195,12 @@ const UNSAFE_ELEMENT_REPLACEMENT_ATTRIBUTE = 'data-ck-unsafe-element';
|
|
|
10150
10195
|
}
|
|
10151
10196
|
// Now we build the list of mutations to mark elements. We did not do it earlier to avoid marking the
|
|
10152
10197
|
// same node multiple times in case of duplication.
|
|
10153
|
-
|
|
10198
|
+
const mutations = [];
|
|
10154
10199
|
for (const textNode of mutatedTextNodes){
|
|
10155
|
-
|
|
10156
|
-
|
|
10200
|
+
mutations.push({
|
|
10201
|
+
type: 'text',
|
|
10202
|
+
node: textNode
|
|
10203
|
+
});
|
|
10157
10204
|
}
|
|
10158
10205
|
for (const viewElement of elementsWithMutatedChildren){
|
|
10159
10206
|
const domElement = domConverter.mapViewToDom(viewElement);
|
|
@@ -10164,20 +10211,23 @@ const UNSAFE_ELEMENT_REPLACEMENT_ATTRIBUTE = 'data-ck-unsafe-element';
|
|
|
10164
10211
|
// It may happen that as a result of many changes (sth was inserted and then removed),
|
|
10165
10212
|
// both elements haven't really changed. #1031
|
|
10166
10213
|
if (!isEqualWith(viewChildren, newViewChildren, sameNodes)) {
|
|
10167
|
-
|
|
10168
|
-
|
|
10214
|
+
mutations.push({
|
|
10215
|
+
type: 'children',
|
|
10216
|
+
node: viewElement
|
|
10217
|
+
});
|
|
10169
10218
|
}
|
|
10170
10219
|
}
|
|
10171
10220
|
// In case only non-relevant mutations were recorded it skips the event and force render (#5600).
|
|
10172
|
-
if (
|
|
10221
|
+
if (mutations.length) {
|
|
10173
10222
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
10223
|
+
// @if CK_DEBUG_TYPING // _debouncedLine();
|
|
10174
10224
|
// @if CK_DEBUG_TYPING // console.group( '%c[MutationObserver]%c Mutations detected',
|
|
10175
|
-
// @if CK_DEBUG_TYPING // 'font-weight:bold;color:green', ''
|
|
10225
|
+
// @if CK_DEBUG_TYPING // 'font-weight: bold; color: green', 'font-weight: bold'
|
|
10176
10226
|
// @if CK_DEBUG_TYPING // );
|
|
10177
10227
|
// @if CK_DEBUG_TYPING // }
|
|
10178
|
-
|
|
10179
|
-
|
|
10180
|
-
|
|
10228
|
+
this.document.fire('mutations', {
|
|
10229
|
+
mutations
|
|
10230
|
+
});
|
|
10181
10231
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
10182
10232
|
// @if CK_DEBUG_TYPING // console.groupEnd();
|
|
10183
10233
|
// @if CK_DEBUG_TYPING // }
|
|
@@ -10225,7 +10275,7 @@ function sameNodes(child1, child2) {
|
|
|
10225
10275
|
*/ class FocusObserver extends DomEventObserver {
|
|
10226
10276
|
/**
|
|
10227
10277
|
* Identifier of the timeout currently used by focus listener to delay rendering execution.
|
|
10228
|
-
*/ _renderTimeoutId;
|
|
10278
|
+
*/ _renderTimeoutId = null;
|
|
10229
10279
|
/**
|
|
10230
10280
|
* Set to `true` if the document is in the process of setting the focus.
|
|
10231
10281
|
*
|
|
@@ -10243,30 +10293,18 @@ function sameNodes(child1, child2) {
|
|
|
10243
10293
|
super(view);
|
|
10244
10294
|
this.useCapture = true;
|
|
10245
10295
|
const document = this.document;
|
|
10246
|
-
document.on('focus', ()=>
|
|
10247
|
-
|
|
10248
|
-
|
|
10249
|
-
|
|
10250
|
-
|
|
10251
|
-
|
|
10252
|
-
|
|
10253
|
-
|
|
10254
|
-
|
|
10255
|
-
// in a situation where `selectionchange` already caused selection change.
|
|
10256
|
-
this._renderTimeoutId = setTimeout(()=>{
|
|
10257
|
-
this.flush();
|
|
10258
|
-
view.change(()=>{});
|
|
10259
|
-
}, 50);
|
|
10260
|
-
});
|
|
10261
|
-
document.on('blur', (evt, data)=>{
|
|
10262
|
-
const selectedEditable = document.selection.editableElement;
|
|
10263
|
-
if (selectedEditable === null || selectedEditable === data.target) {
|
|
10264
|
-
document.isFocused = false;
|
|
10265
|
-
this._isFocusChanging = false;
|
|
10266
|
-
// Re-render the document to update view elements
|
|
10267
|
-
// (changing document.isFocused already marked view as changed since last rendering).
|
|
10268
|
-
view.change(()=>{});
|
|
10296
|
+
document.on('focus', ()=>this._handleFocus());
|
|
10297
|
+
document.on('blur', (evt, data)=>this._handleBlur(data));
|
|
10298
|
+
// Focus the editor in cases where browser dispatches `beforeinput` event to a not-focused editable element.
|
|
10299
|
+
// This is flushed by the beforeinput listener in the `InsertTextObserver`.
|
|
10300
|
+
// Note that focus is set only if the document is not focused yet.
|
|
10301
|
+
// See https://github.com/ckeditor/ckeditor5/issues/14702.
|
|
10302
|
+
document.on('beforeinput', ()=>{
|
|
10303
|
+
if (!document.isFocused) {
|
|
10304
|
+
this._handleFocus();
|
|
10269
10305
|
}
|
|
10306
|
+
}, {
|
|
10307
|
+
priority: 'highest'
|
|
10270
10308
|
});
|
|
10271
10309
|
}
|
|
10272
10310
|
/**
|
|
@@ -10285,10 +10323,47 @@ function sameNodes(child1, child2) {
|
|
|
10285
10323
|
/**
|
|
10286
10324
|
* @inheritDoc
|
|
10287
10325
|
*/ destroy() {
|
|
10326
|
+
this._clearTimeout();
|
|
10327
|
+
super.destroy();
|
|
10328
|
+
}
|
|
10329
|
+
/**
|
|
10330
|
+
* The `focus` event handler.
|
|
10331
|
+
*/ _handleFocus() {
|
|
10332
|
+
this._clearTimeout();
|
|
10333
|
+
this._isFocusChanging = true;
|
|
10334
|
+
// Unfortunately native `selectionchange` event is fired asynchronously.
|
|
10335
|
+
// We need to wait until `SelectionObserver` handle the event and then render. Otherwise rendering will
|
|
10336
|
+
// overwrite new DOM selection with selection from the view.
|
|
10337
|
+
// See https://github.com/ckeditor/ckeditor5-engine/issues/795 for more details.
|
|
10338
|
+
// Long timeout is needed to solve #676 and https://github.com/ckeditor/ckeditor5-engine/issues/1157 issues.
|
|
10339
|
+
//
|
|
10340
|
+
// Using `view.change()` instead of `view.forceRender()` to prevent double rendering
|
|
10341
|
+
// in a situation where `selectionchange` already caused selection change.
|
|
10342
|
+
this._renderTimeoutId = setTimeout(()=>{
|
|
10343
|
+
this._renderTimeoutId = null;
|
|
10344
|
+
this.flush();
|
|
10345
|
+
this.view.change(()=>{});
|
|
10346
|
+
}, 50);
|
|
10347
|
+
}
|
|
10348
|
+
/**
|
|
10349
|
+
* The `blur` event handler.
|
|
10350
|
+
*/ _handleBlur(data) {
|
|
10351
|
+
const selectedEditable = this.document.selection.editableElement;
|
|
10352
|
+
if (selectedEditable === null || selectedEditable === data.target) {
|
|
10353
|
+
this.document.isFocused = false;
|
|
10354
|
+
this._isFocusChanging = false;
|
|
10355
|
+
// Re-render the document to update view elements
|
|
10356
|
+
// (changing document.isFocused already marked view as changed since last rendering).
|
|
10357
|
+
this.view.change(()=>{});
|
|
10358
|
+
}
|
|
10359
|
+
}
|
|
10360
|
+
/**
|
|
10361
|
+
* Clears timeout.
|
|
10362
|
+
*/ _clearTimeout() {
|
|
10288
10363
|
if (this._renderTimeoutId) {
|
|
10289
10364
|
clearTimeout(this._renderTimeoutId);
|
|
10365
|
+
this._renderTimeoutId = null;
|
|
10290
10366
|
}
|
|
10291
|
-
super.destroy();
|
|
10292
10367
|
}
|
|
10293
10368
|
}
|
|
10294
10369
|
|
|
@@ -10367,7 +10442,7 @@ function sameNodes(child1, child2) {
|
|
|
10367
10442
|
}
|
|
10368
10443
|
// Make sure that model selection is up-to-date at the end of selecting process.
|
|
10369
10444
|
// Sometimes `selectionchange` events could arrive after the `mouseup` event and that selection could be already outdated.
|
|
10370
|
-
this._handleSelectionChange(
|
|
10445
|
+
this._handleSelectionChange(domDocument);
|
|
10371
10446
|
this.document.isSelecting = false;
|
|
10372
10447
|
// The safety timeout can be canceled when the document leaves the "is selecting" state.
|
|
10373
10448
|
this._documentIsSelectingInactivityTimeoutDebounced.cancel();
|
|
@@ -10398,10 +10473,11 @@ function sameNodes(child1, child2) {
|
|
|
10398
10473
|
});
|
|
10399
10474
|
this.listenTo(domDocument, 'selectionchange', (evt, domEvent)=>{
|
|
10400
10475
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
10476
|
+
// @if CK_DEBUG_TYPING // _debouncedLine();
|
|
10401
10477
|
// @if CK_DEBUG_TYPING // const domSelection = domDocument.defaultView!.getSelection();
|
|
10402
|
-
// @if CK_DEBUG_TYPING // console.group( '%c[SelectionObserver]%c selectionchange', 'color:green', ''
|
|
10478
|
+
// @if CK_DEBUG_TYPING // console.group( '%c[SelectionObserver]%c selectionchange', 'color: green', ''
|
|
10403
10479
|
// @if CK_DEBUG_TYPING // );
|
|
10404
|
-
// @if CK_DEBUG_TYPING // console.info( '%c[SelectionObserver]%c DOM Selection:', 'font-weight:bold;color:green', '',
|
|
10480
|
+
// @if CK_DEBUG_TYPING // console.info( '%c[SelectionObserver]%c DOM Selection:', 'font-weight: bold; color: green', '',
|
|
10405
10481
|
// @if CK_DEBUG_TYPING // { node: domSelection!.anchorNode, offset: domSelection!.anchorOffset },
|
|
10406
10482
|
// @if CK_DEBUG_TYPING // { node: domSelection!.focusNode, offset: domSelection!.focusOffset }
|
|
10407
10483
|
// @if CK_DEBUG_TYPING // );
|
|
@@ -10411,13 +10487,13 @@ function sameNodes(child1, child2) {
|
|
|
10411
10487
|
if (this.document.isComposing && !env.isAndroid) {
|
|
10412
10488
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
10413
10489
|
// @if CK_DEBUG_TYPING // console.info( '%c[SelectionObserver]%c Selection change ignored (isComposing)',
|
|
10414
|
-
// @if CK_DEBUG_TYPING // 'font-weight:bold;color:green', ''
|
|
10490
|
+
// @if CK_DEBUG_TYPING // 'font-weight: bold; color: green', ''
|
|
10415
10491
|
// @if CK_DEBUG_TYPING // );
|
|
10416
10492
|
// @if CK_DEBUG_TYPING // console.groupEnd();
|
|
10417
10493
|
// @if CK_DEBUG_TYPING // }
|
|
10418
10494
|
return;
|
|
10419
10495
|
}
|
|
10420
|
-
this._handleSelectionChange(
|
|
10496
|
+
this._handleSelectionChange(domDocument);
|
|
10421
10497
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
10422
10498
|
// @if CK_DEBUG_TYPING // console.groupEnd();
|
|
10423
10499
|
// @if CK_DEBUG_TYPING // }
|
|
@@ -10425,6 +10501,26 @@ function sameNodes(child1, child2) {
|
|
|
10425
10501
|
// using their mouse).
|
|
10426
10502
|
this._documentIsSelectingInactivityTimeoutDebounced();
|
|
10427
10503
|
});
|
|
10504
|
+
// Update the model DocumentSelection just after the Renderer and the SelectionObserver are locked.
|
|
10505
|
+
// We do this synchronously (without waiting for the `selectionchange` DOM event) as browser updates
|
|
10506
|
+
// the DOM selection (but not visually) to span the text that is under composition and could be replaced.
|
|
10507
|
+
this.listenTo(this.view.document, 'compositionstart', ()=>{
|
|
10508
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
10509
|
+
// @if CK_DEBUG_TYPING // const domSelection = domDocument.defaultView!.getSelection();
|
|
10510
|
+
// @if CK_DEBUG_TYPING // console.group( '%c[SelectionObserver]%c update selection on compositionstart', 'color: green', ''
|
|
10511
|
+
// @if CK_DEBUG_TYPING // );
|
|
10512
|
+
// @if CK_DEBUG_TYPING // console.info( '%c[SelectionObserver]%c DOM Selection:', 'font-weight: bold; color: green', '',
|
|
10513
|
+
// @if CK_DEBUG_TYPING // { node: domSelection!.anchorNode, offset: domSelection!.anchorOffset },
|
|
10514
|
+
// @if CK_DEBUG_TYPING // { node: domSelection!.focusNode, offset: domSelection!.focusOffset }
|
|
10515
|
+
// @if CK_DEBUG_TYPING // );
|
|
10516
|
+
// @if CK_DEBUG_TYPING // }
|
|
10517
|
+
this._handleSelectionChange(domDocument);
|
|
10518
|
+
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
10519
|
+
// @if CK_DEBUG_TYPING // console.groupEnd();
|
|
10520
|
+
// @if CK_DEBUG_TYPING // }
|
|
10521
|
+
}, {
|
|
10522
|
+
priority: 'lowest'
|
|
10523
|
+
});
|
|
10428
10524
|
this._documents.add(domDocument);
|
|
10429
10525
|
}
|
|
10430
10526
|
/**
|
|
@@ -10451,9 +10547,8 @@ function sameNodes(child1, child2) {
|
|
|
10451
10547
|
* a selection changes and fires {@link module:engine/view/document~Document#event:selectionChange} event on every change
|
|
10452
10548
|
* and {@link module:engine/view/document~Document#event:selectionChangeDone} when a selection stop changing.
|
|
10453
10549
|
*
|
|
10454
|
-
* @param domEvent DOM event.
|
|
10455
10550
|
* @param domDocument DOM document.
|
|
10456
|
-
*/ _handleSelectionChange(
|
|
10551
|
+
*/ _handleSelectionChange(domDocument) {
|
|
10457
10552
|
if (!this.isEnabled) {
|
|
10458
10553
|
return;
|
|
10459
10554
|
}
|
|
@@ -10501,7 +10596,7 @@ function sameNodes(child1, child2) {
|
|
|
10501
10596
|
};
|
|
10502
10597
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
10503
10598
|
// @if CK_DEBUG_TYPING // console.info( '%c[SelectionObserver]%c Fire selection change:',
|
|
10504
|
-
// @if CK_DEBUG_TYPING // 'font-weight:bold;color:green', '',
|
|
10599
|
+
// @if CK_DEBUG_TYPING // 'font-weight: bold; color: green', '',
|
|
10505
10600
|
// @if CK_DEBUG_TYPING // newViewSelection.getFirstRange()
|
|
10506
10601
|
// @if CK_DEBUG_TYPING // );
|
|
10507
10602
|
// @if CK_DEBUG_TYPING // }
|
|
@@ -10521,6 +10616,7 @@ function sameNodes(child1, child2) {
|
|
|
10521
10616
|
}
|
|
10522
10617
|
}
|
|
10523
10618
|
|
|
10619
|
+
// @if CK_DEBUG_TYPING // const { _debouncedLine } = require( '../../dev-utils/utils.js' );
|
|
10524
10620
|
/**
|
|
10525
10621
|
* {@link module:engine/view/document~Document#event:compositionstart Compositionstart},
|
|
10526
10622
|
* {@link module:engine/view/document~Document#event:compositionupdate compositionupdate} and
|
|
@@ -10567,6 +10663,7 @@ function sameNodes(child1, child2) {
|
|
|
10567
10663
|
* @inheritDoc
|
|
10568
10664
|
*/ onDomEvent(domEvent) {
|
|
10569
10665
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
10666
|
+
// @if CK_DEBUG_TYPING // _debouncedLine();
|
|
10570
10667
|
// @if CK_DEBUG_TYPING // console.group( `%c[CompositionObserver]%c ${ domEvent.type }`, 'color: green', '' );
|
|
10571
10668
|
// @if CK_DEBUG_TYPING // }
|
|
10572
10669
|
this.fire(domEvent.type, domEvent, {
|
|
@@ -10672,6 +10769,7 @@ function getFiles(nativeDataTransfer) {
|
|
|
10672
10769
|
return items.filter((item)=>item.kind === 'file').map((item)=>item.getAsFile());
|
|
10673
10770
|
}
|
|
10674
10771
|
|
|
10772
|
+
// @if CK_DEBUG_TYPING // const { _debouncedLine } = require( '../../dev-utils/utils.js' );
|
|
10675
10773
|
/**
|
|
10676
10774
|
* Observer for events connected with data input.
|
|
10677
10775
|
*
|
|
@@ -10685,6 +10783,7 @@ function getFiles(nativeDataTransfer) {
|
|
|
10685
10783
|
* @inheritDoc
|
|
10686
10784
|
*/ onDomEvent(domEvent) {
|
|
10687
10785
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
10786
|
+
// @if CK_DEBUG_TYPING // _debouncedLine();
|
|
10688
10787
|
// @if CK_DEBUG_TYPING // console.group( `%c[InputObserver]%c ${ domEvent.type }: ${ domEvent.inputType }`,
|
|
10689
10788
|
// @if CK_DEBUG_TYPING // 'color: green', 'color: default'
|
|
10690
10789
|
// @if CK_DEBUG_TYPING // );
|
|
@@ -10702,14 +10801,14 @@ function getFiles(nativeDataTransfer) {
|
|
|
10702
10801
|
data = domEvent.data;
|
|
10703
10802
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
10704
10803
|
// @if CK_DEBUG_TYPING // console.info( `%c[InputObserver]%c event data: %c${ JSON.stringify( data ) }`,
|
|
10705
|
-
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', 'font-weight:bold', 'color: blue;'
|
|
10804
|
+
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-weight: bold', 'color: blue;'
|
|
10706
10805
|
// @if CK_DEBUG_TYPING // );
|
|
10707
10806
|
// @if CK_DEBUG_TYPING // }
|
|
10708
10807
|
} else if (dataTransfer) {
|
|
10709
10808
|
data = dataTransfer.getData('text/plain');
|
|
10710
10809
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
10711
10810
|
// @if CK_DEBUG_TYPING // console.info( `%c[InputObserver]%c event data transfer: %c${ JSON.stringify( data ) }`,
|
|
10712
|
-
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', 'font-weight:bold', 'color: blue;'
|
|
10811
|
+
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-weight: bold', 'color: blue;'
|
|
10713
10812
|
// @if CK_DEBUG_TYPING // );
|
|
10714
10813
|
// @if CK_DEBUG_TYPING // }
|
|
10715
10814
|
}
|
|
@@ -10720,7 +10819,7 @@ function getFiles(nativeDataTransfer) {
|
|
|
10720
10819
|
targetRanges = Array.from(viewDocument.selection.getRanges());
|
|
10721
10820
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
10722
10821
|
// @if CK_DEBUG_TYPING // console.info( '%c[InputObserver]%c using fake selection:',
|
|
10723
|
-
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', 'font-weight:bold', targetRanges,
|
|
10822
|
+
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-weight: bold', targetRanges,
|
|
10724
10823
|
// @if CK_DEBUG_TYPING // viewDocument.selection.isFake ? 'fake view selection' : 'fake DOM parent'
|
|
10725
10824
|
// @if CK_DEBUG_TYPING // );
|
|
10726
10825
|
// @if CK_DEBUG_TYPING // }
|
|
@@ -10740,7 +10839,7 @@ function getFiles(nativeDataTransfer) {
|
|
|
10740
10839
|
}).filter((range)=>!!range);
|
|
10741
10840
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
10742
10841
|
// @if CK_DEBUG_TYPING // console.info( '%c[InputObserver]%c using target ranges:',
|
|
10743
|
-
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', 'font-weight:bold', targetRanges
|
|
10842
|
+
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-weight: bold', targetRanges
|
|
10744
10843
|
// @if CK_DEBUG_TYPING // );
|
|
10745
10844
|
// @if CK_DEBUG_TYPING // }
|
|
10746
10845
|
} else if (env.isAndroid) {
|
|
@@ -10748,7 +10847,7 @@ function getFiles(nativeDataTransfer) {
|
|
|
10748
10847
|
targetRanges = Array.from(view.domConverter.domSelectionToView(domSelection).getRanges());
|
|
10749
10848
|
// @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
|
|
10750
10849
|
// @if CK_DEBUG_TYPING // console.info( '%c[InputObserver]%c using selection ranges:',
|
|
10751
|
-
// @if CK_DEBUG_TYPING // 'color: green;font-weight: bold', 'font-weight:bold', targetRanges
|
|
10850
|
+
// @if CK_DEBUG_TYPING // 'color: green; font-weight: bold', 'font-weight: bold', targetRanges
|
|
10752
10851
|
// @if CK_DEBUG_TYPING // );
|
|
10753
10852
|
// @if CK_DEBUG_TYPING // }
|
|
10754
10853
|
}
|
|
@@ -10923,8 +11022,6 @@ function getFiles(nativeDataTransfer) {
|
|
|
10923
11022
|
*/ domRoots = new Map();
|
|
10924
11023
|
/**
|
|
10925
11024
|
* Instance of the {@link module:engine/view/renderer~Renderer renderer}.
|
|
10926
|
-
*
|
|
10927
|
-
* @internal
|
|
10928
11025
|
*/ _renderer;
|
|
10929
11026
|
/**
|
|
10930
11027
|
* A DOM root attributes cache. It saves the initial values of DOM root attributes before the DOM element
|
|
@@ -11003,6 +11100,19 @@ function getFiles(nativeDataTransfer) {
|
|
|
11003
11100
|
}
|
|
11004
11101
|
});
|
|
11005
11102
|
}
|
|
11103
|
+
// Listen to external content mutations (directly in the DOM) and mark them to get verified by the renderer.
|
|
11104
|
+
this.listenTo(this.document, 'mutations', (evt, { mutations })=>{
|
|
11105
|
+
mutations.forEach((mutation)=>this._renderer.markToSync(mutation.type, mutation.node));
|
|
11106
|
+
}, {
|
|
11107
|
+
priority: 'low'
|
|
11108
|
+
});
|
|
11109
|
+
// After all mutated nodes were marked to sync we can trigger view to DOM synchronization
|
|
11110
|
+
// to make sure the DOM structure matches the view.
|
|
11111
|
+
this.listenTo(this.document, 'mutations', ()=>{
|
|
11112
|
+
this.forceRender();
|
|
11113
|
+
}, {
|
|
11114
|
+
priority: 'lowest'
|
|
11115
|
+
});
|
|
11006
11116
|
}
|
|
11007
11117
|
/**
|
|
11008
11118
|
* Attaches a DOM root element to the view element and enable all observers on that element.
|
|
@@ -20291,14 +20401,7 @@ function getFromAttributeCreator(config) {
|
|
|
20291
20401
|
if (data.viewItem.data.trim().length == 0) {
|
|
20292
20402
|
return;
|
|
20293
20403
|
}
|
|
20294
|
-
// Wrap `$text` in paragraph and include any marker that is directly before `$text`. See #13053.
|
|
20295
|
-
const nodeBefore = position.nodeBefore;
|
|
20296
20404
|
position = wrapInParagraph(position, writer);
|
|
20297
|
-
if (nodeBefore && nodeBefore.is('element', '$marker')) {
|
|
20298
|
-
// Move `$marker` to the paragraph.
|
|
20299
|
-
writer.move(writer.createRangeOn(nodeBefore), position);
|
|
20300
|
-
position = writer.createPositionAfter(nodeBefore);
|
|
20301
|
-
}
|
|
20302
20405
|
}
|
|
20303
20406
|
consumable.consume(data.viewItem);
|
|
20304
20407
|
const text = writer.createText(data.viewItem.data);
|
|
@@ -21782,6 +21885,22 @@ const CONSUMABLE_TYPES = [
|
|
|
21782
21885
|
/**
|
|
21783
21886
|
* A dictionary containing attribute properties.
|
|
21784
21887
|
*/ _attributeProperties = {};
|
|
21888
|
+
/**
|
|
21889
|
+
* Stores additional callbacks registered for schema items, which are evaluated when {@link ~Schema#checkChild} is called.
|
|
21890
|
+
*
|
|
21891
|
+
* Keys are schema item names for which the callbacks are registered. Values are arrays with the callbacks.
|
|
21892
|
+
*
|
|
21893
|
+
* Some checks are added under {@link ~Schema#_genericCheckSymbol} key, these are evaluated for every {@link ~Schema#checkChild} call.
|
|
21894
|
+
*/ _customChildChecks = new Map();
|
|
21895
|
+
/**
|
|
21896
|
+
* Stores additional callbacks registered for attribute names, which are evaluated when {@link ~Schema#checkAttribute} is called.
|
|
21897
|
+
*
|
|
21898
|
+
* Keys are schema attribute names for which the callbacks are registered. Values are arrays with the callbacks.
|
|
21899
|
+
*
|
|
21900
|
+
* Some checks are added under {@link ~Schema#_genericCheckSymbol} key, these are evaluated for every
|
|
21901
|
+
* {@link ~Schema#checkAttribute} call.
|
|
21902
|
+
*/ _customAttributeChecks = new Map();
|
|
21903
|
+
_genericCheckSymbol = Symbol('$generic');
|
|
21785
21904
|
_compiledDefinitions;
|
|
21786
21905
|
/**
|
|
21787
21906
|
* Creates a schema instance.
|
|
@@ -22054,7 +22173,7 @@ const CONSUMABLE_TYPES = [
|
|
|
22054
22173
|
return !!(def.isContent || def.isObject);
|
|
22055
22174
|
}
|
|
22056
22175
|
/**
|
|
22057
|
-
* Checks whether the given node
|
|
22176
|
+
* Checks whether the given node can be a child of the given context.
|
|
22058
22177
|
*
|
|
22059
22178
|
* ```ts
|
|
22060
22179
|
* schema.checkChild( model.document.getRoot(), paragraph ); // -> false
|
|
@@ -22062,27 +22181,33 @@ const CONSUMABLE_TYPES = [
|
|
|
22062
22181
|
* schema.register( 'paragraph', {
|
|
22063
22182
|
* allowIn: '$root'
|
|
22064
22183
|
* } );
|
|
22184
|
+
*
|
|
22065
22185
|
* schema.checkChild( model.document.getRoot(), paragraph ); // -> true
|
|
22066
22186
|
* ```
|
|
22067
22187
|
*
|
|
22068
|
-
*
|
|
22069
|
-
* schema
|
|
22070
|
-
*
|
|
22071
|
-
*
|
|
22188
|
+
* Both {@link module:engine/model/schema~Schema#addChildCheck callback checks} and declarative rules (added when
|
|
22189
|
+
* {@link module:engine/model/schema~Schema#register registering} and {@link module:engine/model/schema~Schema#extend extending} items)
|
|
22190
|
+
* are evaluated when this method is called.
|
|
22191
|
+
*
|
|
22192
|
+
* Note that callback checks have bigger priority than declarative rules checks and may overwrite them.
|
|
22193
|
+
*
|
|
22194
|
+
* Note that when verifying whether the given node can be a child of the given context, the schema also verifies the entire
|
|
22195
|
+
* context – from its root to its last element. Therefore, it is possible for `checkChild()` to return `false` even though
|
|
22196
|
+
* the `context` last element can contain the checked child. It happens if one of the `context` elements does not allow its child.
|
|
22197
|
+
* When `context` is verified, {@link module:engine/model/schema~Schema#addChildCheck custom checks} are considered as well.
|
|
22072
22198
|
*
|
|
22073
22199
|
* @fires checkChild
|
|
22074
22200
|
* @param context The context in which the child will be checked.
|
|
22075
22201
|
* @param def The child to check.
|
|
22076
22202
|
*/ checkChild(context, def) {
|
|
22077
|
-
// Note: context and
|
|
22203
|
+
// Note: `context` and `def` are already normalized here to `SchemaContext` and `SchemaCompiledItemDefinition`.
|
|
22078
22204
|
if (!def) {
|
|
22079
22205
|
return false;
|
|
22080
22206
|
}
|
|
22081
|
-
return this._checkContextMatch(
|
|
22207
|
+
return this._checkContextMatch(context, def);
|
|
22082
22208
|
}
|
|
22083
22209
|
/**
|
|
22084
|
-
* Checks whether the given attribute can be applied in the given context (on the last
|
|
22085
|
-
* item of the context).
|
|
22210
|
+
* Checks whether the given attribute can be applied in the given context (on the last item of the context).
|
|
22086
22211
|
*
|
|
22087
22212
|
* ```ts
|
|
22088
22213
|
* schema.checkAttribute( textNode, 'bold' ); // -> false
|
|
@@ -22090,22 +22215,36 @@ const CONSUMABLE_TYPES = [
|
|
|
22090
22215
|
* schema.extend( '$text', {
|
|
22091
22216
|
* allowAttributes: 'bold'
|
|
22092
22217
|
* } );
|
|
22218
|
+
*
|
|
22093
22219
|
* schema.checkAttribute( textNode, 'bold' ); // -> true
|
|
22094
22220
|
* ```
|
|
22095
22221
|
*
|
|
22222
|
+
* Both {@link module:engine/model/schema~Schema#addAttributeCheck callback checks} and declarative rules (added when
|
|
22223
|
+
* {@link module:engine/model/schema~Schema#register registering} and {@link module:engine/model/schema~Schema#extend extending} items)
|
|
22224
|
+
* are evaluated when this method is called.
|
|
22225
|
+
*
|
|
22226
|
+
* Note that callback checks have bigger priority than declarative rules checks and may overwrite them.
|
|
22227
|
+
*
|
|
22096
22228
|
* @fires checkAttribute
|
|
22097
22229
|
* @param context The context in which the attribute will be checked.
|
|
22230
|
+
* @param attributeName Name of attribute to check in the given context.
|
|
22098
22231
|
*/ checkAttribute(context, attributeName) {
|
|
22232
|
+
// Note: `context` is already normalized here to `SchemaContext`.
|
|
22099
22233
|
const def = this.getDefinition(context.last);
|
|
22100
22234
|
if (!def) {
|
|
22101
22235
|
return false;
|
|
22102
22236
|
}
|
|
22103
|
-
|
|
22237
|
+
// First, check all attribute checks declared as callbacks.
|
|
22238
|
+
// Note that `_evaluateAttributeChecks()` will return `undefined` if neither child check was applicable (no decision was made).
|
|
22239
|
+
const isAllowed = this._evaluateAttributeChecks(context, attributeName);
|
|
22240
|
+
// If the decision was not made inside attribute check callbacks, then use declarative rules.
|
|
22241
|
+
return isAllowed !== undefined ? isAllowed : def.allowAttributes.includes(attributeName);
|
|
22104
22242
|
}
|
|
22105
22243
|
/**
|
|
22106
22244
|
* Checks whether the given element (`elementToMerge`) can be merged with the specified base element (`positionOrBaseElement`).
|
|
22107
22245
|
*
|
|
22108
|
-
* In other words –
|
|
22246
|
+
* In other words – both elements are not a limit elements and whether `elementToMerge`'s children
|
|
22247
|
+
* {@link #checkChild are allowed} in the `positionOrBaseElement`.
|
|
22109
22248
|
*
|
|
22110
22249
|
* This check ensures that elements merged with {@link module:engine/model/writer~Writer#merge `Writer#merge()`}
|
|
22111
22250
|
* will be valid.
|
|
@@ -22135,6 +22274,9 @@ const CONSUMABLE_TYPES = [
|
|
|
22135
22274
|
}
|
|
22136
22275
|
return this.checkMerge(nodeBefore, nodeAfter);
|
|
22137
22276
|
}
|
|
22277
|
+
if (this.isLimit(positionOrBaseElement) || this.isLimit(elementToMerge)) {
|
|
22278
|
+
return false;
|
|
22279
|
+
}
|
|
22138
22280
|
for (const child of elementToMerge.getChildren()){
|
|
22139
22281
|
if (!this.checkChild(positionOrBaseElement, child)) {
|
|
22140
22282
|
return false;
|
|
@@ -22147,112 +22289,137 @@ const CONSUMABLE_TYPES = [
|
|
|
22147
22289
|
*
|
|
22148
22290
|
* Callbacks allow you to implement rules which are not otherwise possible to achieve
|
|
22149
22291
|
* by using the declarative API of {@link module:engine/model/schema~SchemaItemDefinition}.
|
|
22150
|
-
* For example, by using this method you can disallow elements in specific contexts.
|
|
22151
22292
|
*
|
|
22152
|
-
*
|
|
22153
|
-
* you can use that event instead.
|
|
22293
|
+
* Note that callback checks have bigger priority than declarative rules checks and may overwrite them.
|
|
22154
22294
|
*
|
|
22155
|
-
*
|
|
22295
|
+
* For example, by using this method you can disallow elements in specific contexts:
|
|
22156
22296
|
*
|
|
22157
22297
|
* ```ts
|
|
22158
|
-
* // Disallow heading1
|
|
22298
|
+
* // Disallow `heading1` inside a `blockQuote` that is inside a table.
|
|
22159
22299
|
* schema.addChildCheck( ( context, childDefinition ) => {
|
|
22160
|
-
* if ( context.endsWith( 'blockQuote' )
|
|
22300
|
+
* if ( context.endsWith( 'tableCell blockQuote' ) ) {
|
|
22161
22301
|
* return false;
|
|
22162
22302
|
* }
|
|
22303
|
+
* }, 'heading1' );
|
|
22304
|
+
* ```
|
|
22305
|
+
*
|
|
22306
|
+
* You can skip the optional `itemName` parameter to evaluate the callback for every `checkChild()` call.
|
|
22307
|
+
*
|
|
22308
|
+
* ```ts
|
|
22309
|
+
* // Inside specific custom element, allow only children, which allows for a specific attribute.
|
|
22310
|
+
* schema.addChildCheck( ( context, childDefinition ) => {
|
|
22311
|
+
* if ( context.endsWith( 'myElement' ) ) {
|
|
22312
|
+
* return childDefinition.allowAttributes.includes( 'myAttribute' );
|
|
22313
|
+
* }
|
|
22163
22314
|
* } );
|
|
22164
22315
|
* ```
|
|
22165
22316
|
*
|
|
22166
|
-
*
|
|
22317
|
+
* Please note that the generic callbacks may affect the editor performance and should be avoided if possible.
|
|
22318
|
+
*
|
|
22319
|
+
* When one of the callbacks makes a decision (returns `true` or `false`) the processing is finished and other callbacks are not fired.
|
|
22320
|
+
* Callbacks are fired in the order they were added, however generic callbacks are fired before callbacks added for a specified item.
|
|
22321
|
+
*
|
|
22322
|
+
* You can also use `checkChild` event, if you need even better control. The result from the example above could also be
|
|
22323
|
+
* achieved with following event callback:
|
|
22167
22324
|
*
|
|
22168
22325
|
* ```ts
|
|
22169
22326
|
* schema.on( 'checkChild', ( evt, args ) => {
|
|
22170
22327
|
* const context = args[ 0 ];
|
|
22171
22328
|
* const childDefinition = args[ 1 ];
|
|
22172
22329
|
*
|
|
22173
|
-
* if ( context.endsWith( '
|
|
22330
|
+
* if ( context.endsWith( 'myElement' ) ) {
|
|
22174
22331
|
* // Prevent next listeners from being called.
|
|
22175
22332
|
* evt.stop();
|
|
22176
|
-
* // Set the checkChild()
|
|
22177
|
-
* evt.return =
|
|
22333
|
+
* // Set the `checkChild()` return value.
|
|
22334
|
+
* evt.return = childDefinition.allowAttributes.includes( 'myAttribute' );
|
|
22178
22335
|
* }
|
|
22179
22336
|
* }, { priority: 'high' } );
|
|
22180
22337
|
* ```
|
|
22181
22338
|
*
|
|
22339
|
+
* Note that the callback checks and declarative rules checks are processed on `normal` priority.
|
|
22340
|
+
*
|
|
22341
|
+
* Adding callbacks this way can also negatively impact editor performance.
|
|
22342
|
+
*
|
|
22182
22343
|
* @param callback The callback to be called. It is called with two parameters:
|
|
22183
22344
|
* {@link module:engine/model/schema~SchemaContext} (context) instance and
|
|
22184
|
-
* {@link module:engine/model/schema~SchemaCompiledItemDefinition} (
|
|
22185
|
-
*
|
|
22186
|
-
*
|
|
22187
|
-
|
|
22188
|
-
|
|
22189
|
-
|
|
22190
|
-
|
|
22191
|
-
|
|
22192
|
-
|
|
22193
|
-
|
|
22194
|
-
}
|
|
22195
|
-
const retValue = callback(ctx, childDef);
|
|
22196
|
-
if (typeof retValue == 'boolean') {
|
|
22197
|
-
evt.stop();
|
|
22198
|
-
evt.return = retValue;
|
|
22199
|
-
}
|
|
22200
|
-
}, {
|
|
22201
|
-
priority: 'high'
|
|
22202
|
-
});
|
|
22345
|
+
* {@link module:engine/model/schema~SchemaCompiledItemDefinition} (definition). The callback may return `true/false` to override
|
|
22346
|
+
* `checkChild()`'s return value. If it does not return a boolean value, the default algorithm (or other callbacks) will define
|
|
22347
|
+
* `checkChild()`'s return value.
|
|
22348
|
+
* @param itemName Name of the schema item for which the callback is registered. If specified, the callback will be run only for
|
|
22349
|
+
* `checkChild()` calls which `def` parameter matches the `itemName`. Otherwise, the callback will run for every `checkChild` call.
|
|
22350
|
+
*/ addChildCheck(callback, itemName) {
|
|
22351
|
+
const key = itemName !== undefined ? itemName : this._genericCheckSymbol;
|
|
22352
|
+
const checks = this._customChildChecks.get(key) || [];
|
|
22353
|
+
checks.push(callback);
|
|
22354
|
+
this._customChildChecks.set(key, checks);
|
|
22203
22355
|
}
|
|
22204
22356
|
/**
|
|
22205
22357
|
* Allows registering a callback to the {@link #checkAttribute} method calls.
|
|
22206
22358
|
*
|
|
22207
22359
|
* Callbacks allow you to implement rules which are not otherwise possible to achieve
|
|
22208
22360
|
* by using the declarative API of {@link module:engine/model/schema~SchemaItemDefinition}.
|
|
22209
|
-
* For example, by using this method you can disallow attribute if node to which it is applied
|
|
22210
|
-
* is contained within some other element (e.g. you want to disallow `bold` on `$text` within `heading1`).
|
|
22211
22361
|
*
|
|
22212
|
-
*
|
|
22213
|
-
* you can use that event instead.
|
|
22362
|
+
* Note that callback checks have bigger priority than declarative rules checks and may overwrite them.
|
|
22214
22363
|
*
|
|
22215
|
-
*
|
|
22364
|
+
* For example, by using this method you can disallow setting attributes on nodes in specific contexts:
|
|
22216
22365
|
*
|
|
22217
22366
|
* ```ts
|
|
22218
|
-
* // Disallow bold on
|
|
22367
|
+
* // Disallow setting `bold` on text inside `heading1` element:
|
|
22368
|
+
* schema.addAttributeCheck( context => {
|
|
22369
|
+
* if ( context.endsWith( 'heading1 $text' ) ) {
|
|
22370
|
+
* return false;
|
|
22371
|
+
* }
|
|
22372
|
+
* }, 'bold' );
|
|
22373
|
+
* ```
|
|
22374
|
+
*
|
|
22375
|
+
* You can skip the optional `attributeName` parameter to evaluate the callback for every `checkAttribute()` call.
|
|
22376
|
+
*
|
|
22377
|
+
* ```ts
|
|
22378
|
+
* // Disallow formatting attributes on text inside custom `myTitle` element:
|
|
22219
22379
|
* schema.addAttributeCheck( ( context, attributeName ) => {
|
|
22220
|
-
* if ( context.endsWith( '
|
|
22380
|
+
* if ( context.endsWith( 'myTitle $text' ) && schema.getAttributeProperties( attributeName ).isFormatting ) {
|
|
22221
22381
|
* return false;
|
|
22222
22382
|
* }
|
|
22223
22383
|
* } );
|
|
22224
22384
|
* ```
|
|
22225
22385
|
*
|
|
22226
|
-
*
|
|
22386
|
+
* Please note that the generic callbacks may affect the editor performance and should be avoided if possible.
|
|
22387
|
+
*
|
|
22388
|
+
* When one of the callbacks makes a decision (returns `true` or `false`) the processing is finished and other callbacks are not fired.
|
|
22389
|
+
* Callbacks are fired in the order they were added, however generic callbacks are fired before callbacks added for a specified item.
|
|
22390
|
+
*
|
|
22391
|
+
* You can also use {@link #event:checkAttribute} event, if you need even better control. The result from the example above could also
|
|
22392
|
+
* be achieved with following event callback:
|
|
22227
22393
|
*
|
|
22228
22394
|
* ```ts
|
|
22229
22395
|
* schema.on( 'checkAttribute', ( evt, args ) => {
|
|
22230
22396
|
* const context = args[ 0 ];
|
|
22231
22397
|
* const attributeName = args[ 1 ];
|
|
22232
22398
|
*
|
|
22233
|
-
* if ( context.endsWith( '
|
|
22399
|
+
* if ( context.endsWith( 'myTitle $text' ) && schema.getAttributeProperties( attributeName ).isFormatting ) {
|
|
22234
22400
|
* // Prevent next listeners from being called.
|
|
22235
22401
|
* evt.stop();
|
|
22236
|
-
* // Set the checkAttribute()
|
|
22402
|
+
* // Set the `checkAttribute()` return value.
|
|
22237
22403
|
* evt.return = false;
|
|
22238
22404
|
* }
|
|
22239
22405
|
* }, { priority: 'high' } );
|
|
22240
22406
|
* ```
|
|
22241
22407
|
*
|
|
22408
|
+
* Note that the callback checks and declarative rules checks are processed on `normal` priority.
|
|
22409
|
+
*
|
|
22410
|
+
* Adding callbacks this way can also negatively impact editor performance.
|
|
22411
|
+
*
|
|
22242
22412
|
* @param callback The callback to be called. It is called with two parameters:
|
|
22243
|
-
* {@link module:engine/model/schema~SchemaContext
|
|
22244
|
-
*
|
|
22245
|
-
*
|
|
22246
|
-
|
|
22247
|
-
|
|
22248
|
-
|
|
22249
|
-
|
|
22250
|
-
|
|
22251
|
-
|
|
22252
|
-
|
|
22253
|
-
}, {
|
|
22254
|
-
priority: 'high'
|
|
22255
|
-
});
|
|
22413
|
+
* {@link module:engine/model/schema~SchemaContext `context`} and attribute name. The callback may return `true` or `false`, to
|
|
22414
|
+
* override `checkAttribute()`'s return value. If it does not return a boolean value, the default algorithm (or other callbacks)
|
|
22415
|
+
* will define `checkAttribute()`'s return value.
|
|
22416
|
+
* @param attributeName Name of the attribute for which the callback is registered. If specified, the callback will be run only for
|
|
22417
|
+
* `checkAttribute()` calls with matching `attributeName`. Otherwise, the callback will run for every `checkAttribute()` call.
|
|
22418
|
+
*/ addAttributeCheck(callback, attributeName) {
|
|
22419
|
+
const key = attributeName !== undefined ? attributeName : this._genericCheckSymbol;
|
|
22420
|
+
const checks = this._customAttributeChecks.get(key) || [];
|
|
22421
|
+
checks.push(callback);
|
|
22422
|
+
this._customAttributeChecks.set(key, checks);
|
|
22256
22423
|
}
|
|
22257
22424
|
/**
|
|
22258
22425
|
* This method allows assigning additional metadata to the model attributes. For example,
|
|
@@ -22579,18 +22746,68 @@ const CONSUMABLE_TYPES = [
|
|
|
22579
22746
|
// Compile final definitions. Unnecessary properties are removed and some additional cleaning is applied.
|
|
22580
22747
|
this._compiledDefinitions = compileDefinitions(definitions);
|
|
22581
22748
|
}
|
|
22582
|
-
_checkContextMatch(
|
|
22583
|
-
const
|
|
22584
|
-
|
|
22585
|
-
|
|
22586
|
-
|
|
22587
|
-
|
|
22588
|
-
|
|
22589
|
-
|
|
22590
|
-
|
|
22591
|
-
} else {
|
|
22749
|
+
_checkContextMatch(context, def) {
|
|
22750
|
+
const parentItem = context.last;
|
|
22751
|
+
// First, check all child checks declared as callbacks.
|
|
22752
|
+
// Note that `_evaluateChildChecks()` will return `undefined` if neither child check was applicable (no decision was made).
|
|
22753
|
+
let isAllowed = this._evaluateChildChecks(context, def);
|
|
22754
|
+
// If the decision was not made inside child check callbacks, then use declarative rules.
|
|
22755
|
+
isAllowed = isAllowed !== undefined ? isAllowed : def.allowIn.includes(parentItem.name);
|
|
22756
|
+
// If the item is not allowed in the `context`, return `false`.
|
|
22757
|
+
if (!isAllowed) {
|
|
22592
22758
|
return false;
|
|
22593
22759
|
}
|
|
22760
|
+
// If the item is allowed, recursively verify the rest of the `context`.
|
|
22761
|
+
const parentItemDefinition = this.getDefinition(parentItem);
|
|
22762
|
+
const parentContext = context.trimLast();
|
|
22763
|
+
// One of the items in the original `context` did not have a definition specified. In this case, the whole context is disallowed.
|
|
22764
|
+
if (!parentItemDefinition) {
|
|
22765
|
+
return false;
|
|
22766
|
+
}
|
|
22767
|
+
// Whole `context` was verified and passed checks.
|
|
22768
|
+
if (parentContext.length == 0) {
|
|
22769
|
+
return true;
|
|
22770
|
+
}
|
|
22771
|
+
// Verify "truncated" parent context. The last item of the original context is now the definition to check.
|
|
22772
|
+
return this._checkContextMatch(parentContext, parentItemDefinition);
|
|
22773
|
+
}
|
|
22774
|
+
/**
|
|
22775
|
+
* Calls child check callbacks to decide whether `def` is allowed in `context`. It uses both generic and specific (defined for `def`
|
|
22776
|
+
* item) callbacks. If neither callback makes a decision, `undefined` is returned.
|
|
22777
|
+
*
|
|
22778
|
+
* Note that the first callback that makes a decision "wins", i.e., if any callback returns `true` or `false`, then the processing
|
|
22779
|
+
* is over and that result is returned.
|
|
22780
|
+
*/ _evaluateChildChecks(context, def) {
|
|
22781
|
+
const genericChecks = this._customChildChecks.get(this._genericCheckSymbol) || [];
|
|
22782
|
+
const childChecks = this._customChildChecks.get(def.name) || [];
|
|
22783
|
+
for (const check of [
|
|
22784
|
+
...genericChecks,
|
|
22785
|
+
...childChecks
|
|
22786
|
+
]){
|
|
22787
|
+
const result = check(context, def);
|
|
22788
|
+
if (result !== undefined) {
|
|
22789
|
+
return result;
|
|
22790
|
+
}
|
|
22791
|
+
}
|
|
22792
|
+
}
|
|
22793
|
+
/**
|
|
22794
|
+
* Calls attribute check callbacks to decide whether `attributeName` can be set on the last element of `context`. It uses both
|
|
22795
|
+
* generic and specific (defined for `attributeName`) callbacks. If neither callback makes a decision, `undefined` is returned.
|
|
22796
|
+
*
|
|
22797
|
+
* Note that the first callback that makes a decision "wins", i.e., if any callback returns `true` or `false`, then the processing
|
|
22798
|
+
* is over and that result is returned.
|
|
22799
|
+
*/ _evaluateAttributeChecks(context, attributeName) {
|
|
22800
|
+
const genericChecks = this._customAttributeChecks.get(this._genericCheckSymbol) || [];
|
|
22801
|
+
const childChecks = this._customAttributeChecks.get(attributeName) || [];
|
|
22802
|
+
for (const check of [
|
|
22803
|
+
...genericChecks,
|
|
22804
|
+
...childChecks
|
|
22805
|
+
]){
|
|
22806
|
+
const result = check(context, attributeName);
|
|
22807
|
+
if (result !== undefined) {
|
|
22808
|
+
return result;
|
|
22809
|
+
}
|
|
22810
|
+
}
|
|
22594
22811
|
}
|
|
22595
22812
|
/**
|
|
22596
22813
|
* Takes a flat range and an attribute name. Traverses the range recursively and deeply to find and return all ranges
|
|
@@ -22768,6 +22985,21 @@ const CONSUMABLE_TYPES = [
|
|
|
22768
22985
|
];
|
|
22769
22986
|
return ctx;
|
|
22770
22987
|
}
|
|
22988
|
+
/**
|
|
22989
|
+
* Returns a new schema context that is based on this context but has the last item removed.
|
|
22990
|
+
*
|
|
22991
|
+
* ```ts
|
|
22992
|
+
* const ctxParagraph = new SchemaContext( [ '$root', 'blockQuote', 'paragraph' ] );
|
|
22993
|
+
* const ctxBlockQuote = ctxParagraph.trimLast(); // Items in `ctxBlockQuote` are: `$root` an `blockQuote`.
|
|
22994
|
+
* const ctxRoot = ctxBlockQuote.trimLast(); // Items in `ctxRoot` are: `$root`.
|
|
22995
|
+
* ```
|
|
22996
|
+
*
|
|
22997
|
+
* @returns A new reduced schema context instance.
|
|
22998
|
+
*/ trimLast() {
|
|
22999
|
+
const ctx = new SchemaContext([]);
|
|
23000
|
+
ctx._items = this._items.slice(0, -1);
|
|
23001
|
+
return ctx;
|
|
23002
|
+
}
|
|
22771
23003
|
/**
|
|
22772
23004
|
* Gets an item on the given index.
|
|
22773
23005
|
*/ getItem(index) {
|
|
@@ -33350,25 +33582,14 @@ function removeRangeContent(range, writer) {
|
|
|
33350
33582
|
/**
|
|
33351
33583
|
* Handles insertion of a single node.
|
|
33352
33584
|
*/ _handleNode(node) {
|
|
33353
|
-
//
|
|
33354
|
-
//
|
|
33355
|
-
|
|
33356
|
-
|
|
33357
|
-
this.
|
|
33358
|
-
return;
|
|
33359
|
-
}
|
|
33360
|
-
// Try to find a place for the given node.
|
|
33361
|
-
// Check if a node can be inserted in the given position or it would be accepted if a paragraph would be inserted.
|
|
33362
|
-
// Inserts the auto paragraph if it would allow for insertion.
|
|
33363
|
-
let isAllowed = this._checkAndAutoParagraphToAllowedPosition(node);
|
|
33364
|
-
if (!isAllowed) {
|
|
33365
|
-
// Split the position.parent's branch up to a point where the node can be inserted.
|
|
33366
|
-
// If it isn't allowed in the whole branch, then of course don't split anything.
|
|
33367
|
-
isAllowed = this._checkAndSplitToAllowedPosition(node);
|
|
33368
|
-
if (!isAllowed) {
|
|
33585
|
+
// Split the position.parent's branch up to a point where the node can be inserted.
|
|
33586
|
+
// If it isn't allowed in the whole branch, then of course don't split anything.
|
|
33587
|
+
if (!this._checkAndSplitToAllowedPosition(node)) {
|
|
33588
|
+
// Handle element children if it's not an object (strip container).
|
|
33589
|
+
if (!this.schema.isObject(node)) {
|
|
33369
33590
|
this._handleDisallowedNode(node);
|
|
33370
|
-
return;
|
|
33371
33591
|
}
|
|
33592
|
+
return;
|
|
33372
33593
|
}
|
|
33373
33594
|
// Add node to the current temporary DocumentFragment.
|
|
33374
33595
|
this._appendToFragment(node);
|
|
@@ -33404,24 +33625,12 @@ function removeRangeContent(range, writer) {
|
|
|
33404
33625
|
this.position = livePosition.toPosition();
|
|
33405
33626
|
livePosition.detach();
|
|
33406
33627
|
}
|
|
33407
|
-
/**
|
|
33408
|
-
* @param node The object element.
|
|
33409
|
-
*/ _handleObject(node) {
|
|
33410
|
-
// Try finding it a place in the tree.
|
|
33411
|
-
if (this._checkAndSplitToAllowedPosition(node)) {
|
|
33412
|
-
this._appendToFragment(node);
|
|
33413
|
-
} else {
|
|
33414
|
-
this._tryAutoparagraphing(node);
|
|
33415
|
-
}
|
|
33416
|
-
}
|
|
33417
33628
|
/**
|
|
33418
33629
|
* @param node The disallowed node which needs to be handled.
|
|
33419
33630
|
*/ _handleDisallowedNode(node) {
|
|
33420
33631
|
// If the node is an element, try inserting its children (strip the parent).
|
|
33421
33632
|
if (node.is('element')) {
|
|
33422
33633
|
this.handleNodes(node.getChildren());
|
|
33423
|
-
} else {
|
|
33424
|
-
this._tryAutoparagraphing(node);
|
|
33425
33634
|
}
|
|
33426
33635
|
}
|
|
33427
33636
|
/**
|
|
@@ -33626,35 +33835,8 @@ function removeRangeContent(range, writer) {
|
|
|
33626
33835
|
return nextSibling instanceof Element && this.canMergeWith.has(nextSibling) && this.model.schema.checkMerge(node, nextSibling);
|
|
33627
33836
|
}
|
|
33628
33837
|
/**
|
|
33629
|
-
*
|
|
33630
|
-
|
|
33631
|
-
* @param node The node which needs to be autoparagraphed.
|
|
33632
|
-
*/ _tryAutoparagraphing(node) {
|
|
33633
|
-
const paragraph = this.writer.createElement('paragraph');
|
|
33634
|
-
// Do not autoparagraph if the paragraph won't be allowed there,
|
|
33635
|
-
// cause that would lead to an infinite loop. The paragraph would be rejected in
|
|
33636
|
-
// the next _handleNode() call and we'd be here again.
|
|
33637
|
-
if (this._getAllowedIn(this.position.parent, paragraph) && this.schema.checkChild(paragraph, node)) {
|
|
33638
|
-
paragraph._appendChild(node);
|
|
33639
|
-
this._handleNode(paragraph);
|
|
33640
|
-
}
|
|
33641
|
-
}
|
|
33642
|
-
/**
|
|
33643
|
-
* Checks if a node can be inserted in the given position or it would be accepted if a paragraph would be inserted.
|
|
33644
|
-
* It also handles inserting the paragraph.
|
|
33645
|
-
*
|
|
33646
|
-
* @returns Whether an allowed position was found.
|
|
33647
|
-
* `false` is returned if the node isn't allowed at the current position or in auto paragraph, `true` if was.
|
|
33648
|
-
*/ _checkAndAutoParagraphToAllowedPosition(node) {
|
|
33649
|
-
if (this.schema.checkChild(this.position.parent, node)) {
|
|
33650
|
-
return true;
|
|
33651
|
-
}
|
|
33652
|
-
// Do not auto paragraph if the paragraph won't be allowed there,
|
|
33653
|
-
// cause that would lead to an infinite loop. The paragraph would be rejected in
|
|
33654
|
-
// the next _handleNode() call and we'd be here again.
|
|
33655
|
-
if (!this.schema.checkChild(this.position.parent, 'paragraph') || !this.schema.checkChild('paragraph', node)) {
|
|
33656
|
-
return false;
|
|
33657
|
-
}
|
|
33838
|
+
* Inserts a paragraph and moves the insertion position into it.
|
|
33839
|
+
*/ _insertAutoParagraph() {
|
|
33658
33840
|
// Insert nodes collected in temporary DocumentFragment if the position parent needs change to process further nodes.
|
|
33659
33841
|
this._insertPartialFragment();
|
|
33660
33842
|
// Insert a paragraph and move insertion position to it.
|
|
@@ -33663,7 +33845,6 @@ function removeRangeContent(range, writer) {
|
|
|
33663
33845
|
this._setAffectedBoundaries(this.position);
|
|
33664
33846
|
this._lastAutoParagraph = paragraph;
|
|
33665
33847
|
this.position = this.writer.createPositionAt(paragraph, 0);
|
|
33666
|
-
return true;
|
|
33667
33848
|
}
|
|
33668
33849
|
/**
|
|
33669
33850
|
* @returns Whether an allowed position was found.
|
|
@@ -33707,17 +33888,30 @@ function removeRangeContent(range, writer) {
|
|
|
33707
33888
|
this.canMergeWith.add(this.position.nodeAfter);
|
|
33708
33889
|
}
|
|
33709
33890
|
}
|
|
33891
|
+
// At this point, we split elements up to the parent in which `node` is allowed.
|
|
33892
|
+
// Note that `_getAllowedIn()` checks if the `node` is allowed either directly, or when auto-paragraphed.
|
|
33893
|
+
// So, let's check if the `node` is allowed directly. If not, we need to auto-paragraph it.
|
|
33894
|
+
if (!this.schema.checkChild(this.position.parent, node)) {
|
|
33895
|
+
this._insertAutoParagraph();
|
|
33896
|
+
}
|
|
33710
33897
|
return true;
|
|
33711
33898
|
}
|
|
33712
33899
|
/**
|
|
33713
33900
|
* Gets the element in which the given node is allowed. It checks the passed element and all its ancestors.
|
|
33714
33901
|
*
|
|
33902
|
+
* It also verifies if auto-paragraphing could help.
|
|
33903
|
+
*
|
|
33715
33904
|
* @param contextElement The element in which context the node should be checked.
|
|
33716
33905
|
* @param childNode The node to check.
|
|
33717
33906
|
*/ _getAllowedIn(contextElement, childNode) {
|
|
33907
|
+
// Check if a node can be inserted in the given context...
|
|
33718
33908
|
if (this.schema.checkChild(contextElement, childNode)) {
|
|
33719
33909
|
return contextElement;
|
|
33720
33910
|
}
|
|
33911
|
+
// ...or it would be accepted if a paragraph would be inserted.
|
|
33912
|
+
if (this.schema.checkChild(contextElement, 'paragraph') && this.schema.checkChild('paragraph', childNode)) {
|
|
33913
|
+
return contextElement;
|
|
33914
|
+
}
|
|
33721
33915
|
// If the child wasn't allowed in the context element and the element is a limit there's no point in
|
|
33722
33916
|
// checking any further towards the root. This is it: the limit is unsplittable and there's nothing
|
|
33723
33917
|
// we can do about it. Without this check, the algorithm will analyze parent of the limit and may create
|
|
@@ -34117,11 +34311,7 @@ function getSearchRange(start, isForward) {
|
|
|
34117
34311
|
// at the end of the conversion. `UpcastDispatcher` or at least `Conversion` class looks like a
|
|
34118
34312
|
// better place for this registration but both know nothing about `Schema`.
|
|
34119
34313
|
this.schema.register('$marker');
|
|
34120
|
-
this.schema.addChildCheck((
|
|
34121
|
-
if (childDefinition.name === '$marker') {
|
|
34122
|
-
return true;
|
|
34123
|
-
}
|
|
34124
|
-
});
|
|
34314
|
+
this.schema.addChildCheck(()=>true, '$marker'); // Allow everywhere.
|
|
34125
34315
|
injectSelectionPostFixer(this);
|
|
34126
34316
|
// Post-fixer which takes care of adding empty paragraph elements to the empty roots.
|
|
34127
34317
|
this.document.registerPostFixer(autoParagraphEmptyRoots);
|
|
@@ -36494,6 +36684,9 @@ setData$1._parse = parse$1;
|
|
|
36494
36684
|
const processor = new XmlDataProcessor(viewDocument, {
|
|
36495
36685
|
namespaces: Object.keys(allowedTypes)
|
|
36496
36686
|
});
|
|
36687
|
+
if (options.inlineObjectElements) {
|
|
36688
|
+
processor.domConverter.inlineObjectElements.push(...options.inlineObjectElements);
|
|
36689
|
+
}
|
|
36497
36690
|
// Convert data to view.
|
|
36498
36691
|
let view = processor.toView(data);
|
|
36499
36692
|
// At this point we have a view tree with Elements that could have names like `attribute:b:1`. In the next step
|
|
@@ -37159,7 +37352,8 @@ getData._stringify = stringify;
|
|
|
37159
37352
|
selectionAttributes: options.selectionAttributes,
|
|
37160
37353
|
context: [
|
|
37161
37354
|
modelRoot.name
|
|
37162
|
-
]
|
|
37355
|
+
],
|
|
37356
|
+
inlineObjectElements: options.inlineObjectElements
|
|
37163
37357
|
});
|
|
37164
37358
|
// Retrieve DocumentFragment and Selection from parsed model.
|
|
37165
37359
|
if ('model' in parsedResult) {
|
|
@@ -37330,7 +37524,8 @@ setData._parse = parse;
|
|
|
37330
37524
|
// Parse data to view using view utils.
|
|
37331
37525
|
const parsedResult = parse$1(data, {
|
|
37332
37526
|
sameSelectionCharacters: true,
|
|
37333
|
-
lastRangeBackward: !!options.lastRangeBackward
|
|
37527
|
+
lastRangeBackward: !!options.lastRangeBackward,
|
|
37528
|
+
inlineObjectElements: options.inlineObjectElements
|
|
37334
37529
|
});
|
|
37335
37530
|
// Retrieve DocumentFragment and Selection from parsed view.
|
|
37336
37531
|
let viewDocumentFragment;
|
|
@@ -37465,5 +37660,5 @@ function* convertAttributes(attributes, converter) {
|
|
|
37465
37660
|
}
|
|
37466
37661
|
}
|
|
37467
37662
|
|
|
37468
|
-
export { AttributeElement, AttributeOperation, BubblingEventInfo, ClickObserver, Conversion, DataController, DataTransfer, DocumentFragment, DocumentSelection, DomConverter, DomEventData, DomEventObserver, DowncastWriter, EditingController, View as EditingView, Element, FocusObserver, History, HtmlDataProcessor, InsertOperation, LivePosition, LiveRange, MarkerOperation, Matcher, MergeOperation, Model, MouseObserver, MoveOperation, NoOperation, Observer, OperationFactory, Position, Range, RenameOperation, Renderer, RootAttributeOperation, RootOperation, SplitOperation, StylesMap, StylesProcessor, TabObserver, Text, TextProxy, TreeWalker, UpcastWriter, AttributeElement as ViewAttributeElement, ContainerElement as ViewContainerElement, Document$1 as ViewDocument, DocumentFragment$1 as ViewDocumentFragment, EditableElement as ViewEditableElement, Element$1 as ViewElement, EmptyElement as ViewEmptyElement, RawElement as ViewRawElement, RootEditableElement as ViewRootEditableElement, Text$1 as ViewText, TreeWalker$1 as ViewTreeWalker, UIElement as ViewUIElement, XmlDataProcessor, getData as _getModelData, getData$1 as _getViewData, parse as _parseModel, parse$1 as _parseView, setData as _setModelData, setData$1 as _setViewData, stringify as _stringifyModel, stringify$1 as _stringifyView, addBackgroundRules, addBorderRules, addMarginRules, addPaddingRules, disablePlaceholder, enablePlaceholder, getBoxSidesShorthandValue, getBoxSidesValueReducer, getBoxSidesValues, getFillerOffset$4 as getFillerOffset, getPositionShorthandNormalizer, getShorthandValues, hidePlaceholder, isAttachment, isColor, isLength, isLineStyle, isPercentage, isPosition, isRepeat, isURL, needsPlaceholder, showPlaceholder, transformSets };
|
|
37663
|
+
export { AttributeElement, AttributeOperation, BubblingEventInfo, ClickObserver, Conversion, DataController, DataTransfer, DocumentFragment, DocumentSelection, DomConverter, DomEventData, DomEventObserver, DowncastWriter, EditingController, View as EditingView, Element, FocusObserver, History, HtmlDataProcessor, InsertOperation, LivePosition, LiveRange, MarkerOperation, Matcher, MergeOperation, Model, MouseObserver, MoveOperation, NoOperation, Observer, OperationFactory, Position, Range, RenameOperation, Renderer, RootAttributeOperation, RootOperation, SplitOperation, StylesMap, StylesProcessor, TabObserver, Text, TextProxy, TreeWalker, UpcastWriter, AttributeElement as ViewAttributeElement, ContainerElement as ViewContainerElement, Document$1 as ViewDocument, DocumentFragment$1 as ViewDocumentFragment, EditableElement as ViewEditableElement, Element$1 as ViewElement, EmptyElement as ViewEmptyElement, RawElement as ViewRawElement, RootEditableElement as ViewRootEditableElement, Text$1 as ViewText, TreeWalker$1 as ViewTreeWalker, UIElement as ViewUIElement, XmlDataProcessor, getData as _getModelData, getData$1 as _getViewData, parse as _parseModel, parse$1 as _parseView, setData as _setModelData, setData$1 as _setViewData, stringify as _stringifyModel, stringify$1 as _stringifyView, addBackgroundRules, addBorderRules, addMarginRules, addPaddingRules, autoParagraphEmptyRoots, disablePlaceholder, enablePlaceholder, getBoxSidesShorthandValue, getBoxSidesValueReducer, getBoxSidesValues, getFillerOffset$4 as getFillerOffset, getPositionShorthandNormalizer, getShorthandValues, hidePlaceholder, isAttachment, isColor, isLength, isLineStyle, isParagraphable, isPercentage, isPosition, isRepeat, isURL, needsPlaceholder, showPlaceholder, transformSets, wrapInParagraph };
|
|
37469
37664
|
//# sourceMappingURL=index.js.map
|