@ckeditor/ckeditor5-engine 44.1.0-alpha.5 → 44.2.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/LICENSE.md +1 -1
- package/dist/index-content.css +2 -2
- package/dist/index-editor.css +2 -2
- package/dist/index.css +4 -4
- package/dist/index.css.map +1 -1
- package/dist/index.js +646 -77
- package/dist/index.js.map +1 -1
- package/package.json +23 -3
- package/src/controller/datacontroller.d.ts +1 -1
- package/src/controller/datacontroller.js +1 -1
- package/src/controller/editingcontroller.d.ts +1 -1
- package/src/controller/editingcontroller.js +1 -1
- package/src/conversion/conversion.d.ts +1 -1
- package/src/conversion/conversion.js +1 -1
- package/src/conversion/conversionhelpers.d.ts +1 -1
- package/src/conversion/conversionhelpers.js +1 -1
- package/src/conversion/downcastdispatcher.d.ts +1 -1
- package/src/conversion/downcastdispatcher.js +1 -1
- package/src/conversion/downcasthelpers.d.ts +1 -1
- package/src/conversion/downcasthelpers.js +1 -1
- package/src/conversion/mapper.d.ts +249 -20
- package/src/conversion/mapper.js +524 -38
- package/src/conversion/modelconsumable.d.ts +1 -1
- package/src/conversion/modelconsumable.js +1 -1
- package/src/conversion/upcastdispatcher.d.ts +1 -1
- package/src/conversion/upcastdispatcher.js +1 -1
- package/src/conversion/upcasthelpers.d.ts +1 -1
- package/src/conversion/upcasthelpers.js +1 -1
- package/src/conversion/viewconsumable.d.ts +1 -1
- package/src/conversion/viewconsumable.js +1 -1
- package/src/dataprocessor/basichtmlwriter.d.ts +1 -1
- package/src/dataprocessor/basichtmlwriter.js +1 -1
- package/src/dataprocessor/dataprocessor.d.ts +1 -1
- package/src/dataprocessor/dataprocessor.js +1 -1
- package/src/dataprocessor/htmldataprocessor.d.ts +1 -1
- package/src/dataprocessor/htmldataprocessor.js +1 -1
- package/src/dataprocessor/htmlwriter.d.ts +1 -1
- package/src/dataprocessor/htmlwriter.js +1 -1
- package/src/dataprocessor/xmldataprocessor.d.ts +1 -1
- package/src/dataprocessor/xmldataprocessor.js +1 -1
- package/src/dev-utils/model.d.ts +1 -1
- package/src/dev-utils/model.js +1 -1
- package/src/dev-utils/operationreplayer.d.ts +1 -1
- package/src/dev-utils/operationreplayer.js +1 -1
- package/src/dev-utils/utils.d.ts +1 -1
- package/src/dev-utils/utils.js +1 -1
- package/src/dev-utils/view.d.ts +1 -1
- package/src/dev-utils/view.js +1 -1
- package/src/index.d.ts +4 -2
- package/src/index.js +2 -1
- package/src/model/batch.d.ts +1 -1
- package/src/model/batch.js +7 -3
- package/src/model/differ.d.ts +1 -1
- package/src/model/differ.js +1 -1
- package/src/model/document.d.ts +1 -1
- package/src/model/document.js +1 -1
- package/src/model/documentfragment.d.ts +1 -1
- package/src/model/documentfragment.js +1 -1
- package/src/model/documentselection.d.ts +1 -1
- package/src/model/documentselection.js +1 -1
- package/src/model/element.d.ts +1 -1
- package/src/model/element.js +1 -1
- package/src/model/history.d.ts +1 -1
- package/src/model/history.js +1 -1
- package/src/model/item.d.ts +1 -1
- package/src/model/item.js +1 -1
- package/src/model/liveposition.d.ts +1 -1
- package/src/model/liveposition.js +1 -1
- package/src/model/liverange.d.ts +1 -1
- package/src/model/liverange.js +1 -1
- package/src/model/markercollection.d.ts +1 -1
- package/src/model/markercollection.js +1 -1
- package/src/model/model.d.ts +1 -1
- package/src/model/model.js +1 -1
- package/src/model/node.d.ts +1 -1
- package/src/model/node.js +1 -1
- package/src/model/nodelist.d.ts +1 -1
- package/src/model/nodelist.js +1 -1
- package/src/model/operation/attributeoperation.d.ts +1 -1
- package/src/model/operation/attributeoperation.js +1 -1
- package/src/model/operation/detachoperation.d.ts +1 -1
- package/src/model/operation/detachoperation.js +1 -1
- package/src/model/operation/insertoperation.d.ts +1 -1
- package/src/model/operation/insertoperation.js +1 -1
- package/src/model/operation/markeroperation.d.ts +1 -1
- package/src/model/operation/markeroperation.js +1 -1
- package/src/model/operation/mergeoperation.d.ts +1 -1
- package/src/model/operation/mergeoperation.js +1 -1
- package/src/model/operation/moveoperation.d.ts +1 -1
- package/src/model/operation/moveoperation.js +1 -1
- package/src/model/operation/nooperation.d.ts +1 -1
- package/src/model/operation/nooperation.js +1 -1
- package/src/model/operation/operation.d.ts +4 -1
- package/src/model/operation/operation.js +2 -2
- package/src/model/operation/operationfactory.d.ts +1 -1
- package/src/model/operation/operationfactory.js +1 -1
- package/src/model/operation/renameoperation.d.ts +1 -1
- package/src/model/operation/renameoperation.js +1 -1
- package/src/model/operation/rootattributeoperation.d.ts +1 -1
- package/src/model/operation/rootattributeoperation.js +1 -1
- package/src/model/operation/rootoperation.d.ts +1 -1
- package/src/model/operation/rootoperation.js +1 -1
- package/src/model/operation/splitoperation.d.ts +1 -1
- package/src/model/operation/splitoperation.js +1 -1
- package/src/model/operation/transform.d.ts +1 -1
- package/src/model/operation/transform.js +1 -1
- package/src/model/operation/utils.d.ts +1 -1
- package/src/model/operation/utils.js +1 -1
- package/src/model/position.d.ts +1 -1
- package/src/model/position.js +1 -1
- package/src/model/range.d.ts +1 -1
- package/src/model/range.js +1 -1
- package/src/model/rootelement.d.ts +1 -1
- package/src/model/rootelement.js +1 -1
- package/src/model/schema.d.ts +1 -1
- package/src/model/schema.js +1 -1
- package/src/model/selection.d.ts +1 -1
- package/src/model/selection.js +9 -2
- package/src/model/text.d.ts +1 -1
- package/src/model/text.js +1 -1
- package/src/model/textproxy.d.ts +1 -1
- package/src/model/textproxy.js +1 -1
- package/src/model/treewalker.d.ts +17 -1
- package/src/model/treewalker.js +26 -1
- package/src/model/typecheckable.d.ts +1 -1
- package/src/model/typecheckable.js +1 -1
- package/src/model/utils/autoparagraphing.d.ts +1 -1
- package/src/model/utils/autoparagraphing.js +1 -1
- package/src/model/utils/deletecontent.d.ts +1 -1
- package/src/model/utils/deletecontent.js +1 -1
- package/src/model/utils/getselectedcontent.d.ts +1 -1
- package/src/model/utils/getselectedcontent.js +1 -1
- package/src/model/utils/insertcontent.d.ts +1 -1
- package/src/model/utils/insertcontent.js +1 -1
- package/src/model/utils/insertobject.d.ts +1 -1
- package/src/model/utils/insertobject.js +1 -1
- package/src/model/utils/modifyselection.d.ts +1 -1
- package/src/model/utils/modifyselection.js +1 -1
- package/src/model/utils/selection-post-fixer.d.ts +1 -1
- package/src/model/utils/selection-post-fixer.js +1 -1
- package/src/model/writer.d.ts +1 -1
- package/src/model/writer.js +1 -1
- package/src/view/attributeelement.d.ts +1 -1
- package/src/view/attributeelement.js +1 -1
- package/src/view/containerelement.d.ts +1 -1
- package/src/view/containerelement.js +1 -1
- package/src/view/datatransfer.d.ts +1 -1
- package/src/view/datatransfer.js +1 -1
- package/src/view/document.d.ts +1 -1
- package/src/view/document.js +1 -1
- package/src/view/documentfragment.d.ts +6 -4
- package/src/view/documentfragment.js +7 -7
- package/src/view/documentselection.d.ts +1 -1
- package/src/view/documentselection.js +1 -1
- package/src/view/domconverter.d.ts +1 -1
- package/src/view/domconverter.js +1 -1
- package/src/view/downcastwriter.d.ts +1 -1
- package/src/view/downcastwriter.js +1 -1
- package/src/view/editableelement.d.ts +1 -1
- package/src/view/editableelement.js +1 -1
- package/src/view/element.d.ts +1 -1
- package/src/view/element.js +3 -3
- package/src/view/elementdefinition.d.ts +1 -1
- package/src/view/elementdefinition.js +1 -1
- package/src/view/emptyelement.d.ts +1 -1
- package/src/view/emptyelement.js +1 -1
- package/src/view/filler.d.ts +1 -1
- package/src/view/filler.js +1 -1
- package/src/view/item.d.ts +1 -1
- package/src/view/item.js +1 -1
- package/src/view/matcher.d.ts +1 -1
- package/src/view/matcher.js +1 -1
- package/src/view/node.d.ts +13 -4
- package/src/view/node.js +5 -4
- package/src/view/observer/arrowkeysobserver.d.ts +1 -1
- package/src/view/observer/arrowkeysobserver.js +1 -1
- package/src/view/observer/bubblingemittermixin.d.ts +1 -1
- package/src/view/observer/bubblingemittermixin.js +1 -1
- package/src/view/observer/bubblingeventinfo.d.ts +1 -1
- package/src/view/observer/bubblingeventinfo.js +1 -1
- package/src/view/observer/clickobserver.d.ts +1 -1
- package/src/view/observer/clickobserver.js +1 -1
- package/src/view/observer/compositionobserver.d.ts +1 -1
- package/src/view/observer/compositionobserver.js +1 -1
- package/src/view/observer/domeventdata.d.ts +1 -1
- package/src/view/observer/domeventdata.js +1 -1
- package/src/view/observer/domeventobserver.d.ts +1 -1
- package/src/view/observer/domeventobserver.js +1 -1
- package/src/view/observer/fakeselectionobserver.d.ts +1 -1
- package/src/view/observer/fakeselectionobserver.js +1 -1
- package/src/view/observer/focusobserver.d.ts +1 -1
- package/src/view/observer/focusobserver.js +1 -1
- package/src/view/observer/inputobserver.d.ts +1 -1
- package/src/view/observer/inputobserver.js +1 -1
- package/src/view/observer/keyobserver.d.ts +1 -1
- package/src/view/observer/keyobserver.js +1 -1
- package/src/view/observer/mouseobserver.d.ts +1 -1
- package/src/view/observer/mouseobserver.js +1 -1
- package/src/view/observer/mutationobserver.d.ts +1 -1
- package/src/view/observer/mutationobserver.js +1 -1
- package/src/view/observer/observer.d.ts +1 -1
- package/src/view/observer/observer.js +1 -1
- package/src/view/observer/selectionobserver.d.ts +1 -1
- package/src/view/observer/selectionobserver.js +1 -1
- package/src/view/observer/tabobserver.d.ts +1 -1
- package/src/view/observer/tabobserver.js +1 -1
- package/src/view/observer/touchobserver.d.ts +73 -0
- package/src/view/observer/touchobserver.js +29 -0
- package/src/view/placeholder.d.ts +1 -1
- package/src/view/placeholder.js +23 -17
- package/src/view/position.d.ts +1 -1
- package/src/view/position.js +1 -1
- package/src/view/range.d.ts +1 -1
- package/src/view/range.js +1 -1
- package/src/view/rawelement.d.ts +1 -1
- package/src/view/rawelement.js +1 -1
- package/src/view/renderer.d.ts +1 -1
- package/src/view/renderer.js +1 -1
- package/src/view/rooteditableelement.d.ts +1 -1
- package/src/view/rooteditableelement.js +1 -1
- package/src/view/selection.d.ts +1 -1
- package/src/view/selection.js +1 -1
- package/src/view/styles/background.d.ts +1 -1
- package/src/view/styles/background.js +1 -1
- package/src/view/styles/border.d.ts +1 -1
- package/src/view/styles/border.js +1 -1
- package/src/view/styles/margin.d.ts +1 -1
- package/src/view/styles/margin.js +1 -1
- package/src/view/styles/padding.d.ts +1 -1
- package/src/view/styles/padding.js +1 -1
- package/src/view/styles/utils.d.ts +1 -1
- package/src/view/styles/utils.js +1 -1
- package/src/view/stylesmap.d.ts +1 -1
- package/src/view/stylesmap.js +1 -1
- package/src/view/text.d.ts +1 -1
- package/src/view/text.js +1 -1
- package/src/view/textproxy.d.ts +1 -1
- package/src/view/textproxy.js +1 -1
- package/src/view/treewalker.d.ts +17 -1
- package/src/view/treewalker.js +25 -1
- package/src/view/typecheckable.d.ts +1 -1
- package/src/view/typecheckable.js +1 -1
- package/src/view/uielement.d.ts +1 -1
- package/src/view/uielement.js +1 -1
- package/src/view/upcastwriter.d.ts +1 -1
- package/src/view/upcastwriter.js +1 -1
- package/src/view/view.d.ts +1 -1
- package/src/view/view.js +1 -1
- package/theme/placeholder.css +1 -1
- package/theme/renderer.css +1 -1
- package/dist/controller/datacontroller.d.ts +0 -339
- package/dist/controller/editingcontroller.d.ts +0 -102
- package/dist/conversion/conversion.d.ts +0 -482
- package/dist/conversion/conversionhelpers.d.ts +0 -30
- package/dist/conversion/downcastdispatcher.d.ts +0 -566
- package/dist/conversion/downcasthelpers.d.ts +0 -1194
- package/dist/conversion/mapper.d.ts +0 -507
- package/dist/conversion/modelconsumable.d.ts +0 -205
- package/dist/conversion/upcastdispatcher.d.ts +0 -496
- package/dist/conversion/upcasthelpers.d.ts +0 -503
- package/dist/conversion/viewconsumable.d.ts +0 -373
- package/dist/dataprocessor/basichtmlwriter.d.ts +0 -22
- package/dist/dataprocessor/dataprocessor.d.ts +0 -65
- package/dist/dataprocessor/htmldataprocessor.d.ts +0 -80
- package/dist/dataprocessor/htmlwriter.d.ts +0 -20
- package/dist/dataprocessor/xmldataprocessor.d.ts +0 -94
- package/dist/dev-utils/model.d.ts +0 -130
- package/dist/dev-utils/operationreplayer.d.ts +0 -55
- package/dist/dev-utils/utils.d.ts +0 -41
- package/dist/dev-utils/view.d.ts +0 -324
- package/dist/index.d.ts +0 -122
- package/dist/model/batch.d.ts +0 -110
- package/dist/model/differ.d.ts +0 -511
- package/dist/model/document.d.ts +0 -278
- package/dist/model/documentfragment.d.ts +0 -223
- package/dist/model/documentselection.d.ts +0 -424
- package/dist/model/element.d.ts +0 -191
- package/dist/model/history.d.ts +0 -118
- package/dist/model/item.d.ts +0 -18
- package/dist/model/liveposition.d.ts +0 -81
- package/dist/model/liverange.d.ts +0 -106
- package/dist/model/markercollection.d.ts +0 -339
- package/dist/model/model.d.ts +0 -923
- package/dist/model/node.d.ts +0 -262
- package/dist/model/nodelist.d.ts +0 -119
- package/dist/model/operation/attributeoperation.d.ts +0 -107
- package/dist/model/operation/detachoperation.d.ts +0 -64
- package/dist/model/operation/insertoperation.d.ts +0 -94
- package/dist/model/operation/markeroperation.d.ts +0 -95
- package/dist/model/operation/mergeoperation.d.ts +0 -104
- package/dist/model/operation/moveoperation.d.ts +0 -100
- package/dist/model/operation/nooperation.d.ts +0 -42
- package/dist/model/operation/operation.d.ts +0 -100
- package/dist/model/operation/operationfactory.d.ts +0 -22
- package/dist/model/operation/renameoperation.d.ts +0 -87
- package/dist/model/operation/rootattributeoperation.d.ts +0 -102
- package/dist/model/operation/rootoperation.d.ts +0 -80
- package/dist/model/operation/splitoperation.d.ts +0 -113
- package/dist/model/operation/transform.d.ts +0 -104
- package/dist/model/operation/utils.d.ts +0 -75
- package/dist/model/position.d.ts +0 -549
- package/dist/model/range.d.ts +0 -462
- package/dist/model/rootelement.d.ts +0 -64
- package/dist/model/schema.d.ts +0 -1334
- package/dist/model/selection.d.ts +0 -486
- package/dist/model/text.d.ts +0 -70
- package/dist/model/textproxy.d.ts +0 -148
- package/dist/model/treewalker.d.ts +0 -190
- package/dist/model/typecheckable.d.ts +0 -289
- package/dist/model/utils/autoparagraphing.d.ts +0 -41
- package/dist/model/utils/deletecontent.d.ts +0 -62
- package/dist/model/utils/getselectedcontent.d.ts +0 -34
- package/dist/model/utils/insertcontent.d.ts +0 -50
- package/dist/model/utils/insertobject.d.ts +0 -51
- package/dist/model/utils/modifyselection.d.ts +0 -52
- package/dist/model/utils/selection-post-fixer.d.ts +0 -78
- package/dist/model/writer.d.ts +0 -855
- package/dist/view/attributeelement.d.ts +0 -112
- package/dist/view/containerelement.d.ts +0 -53
- package/dist/view/datatransfer.d.ts +0 -83
- package/dist/view/document.d.ts +0 -188
- package/dist/view/documentfragment.d.ts +0 -157
- package/dist/view/documentselection.d.ts +0 -310
- package/dist/view/domconverter.d.ts +0 -665
- package/dist/view/downcastwriter.d.ts +0 -1000
- package/dist/view/editableelement.d.ts +0 -66
- package/dist/view/element.d.ts +0 -472
- package/dist/view/elementdefinition.d.ts +0 -91
- package/dist/view/emptyelement.d.ts +0 -45
- package/dist/view/filler.d.ts +0 -115
- package/dist/view/item.d.ts +0 -18
- package/dist/view/matcher.d.ts +0 -490
- package/dist/view/node.d.ts +0 -166
- package/dist/view/observer/arrowkeysobserver.d.ts +0 -49
- package/dist/view/observer/bubblingemittermixin.d.ts +0 -170
- package/dist/view/observer/bubblingeventinfo.d.ts +0 -51
- package/dist/view/observer/clickobserver.d.ts +0 -47
- package/dist/view/observer/compositionobserver.d.ts +0 -86
- package/dist/view/observer/domeventdata.d.ts +0 -54
- package/dist/view/observer/domeventobserver.d.ts +0 -82
- package/dist/view/observer/fakeselectionobserver.d.ts +0 -51
- package/dist/view/observer/focusobserver.d.ts +0 -98
- package/dist/view/observer/inputobserver.d.ts +0 -90
- package/dist/view/observer/keyobserver.d.ts +0 -70
- package/dist/view/observer/mouseobserver.d.ts +0 -93
- package/dist/view/observer/mutationobserver.d.ts +0 -119
- package/dist/view/observer/observer.d.ts +0 -93
- package/dist/view/observer/selectionobserver.d.ts +0 -151
- package/dist/view/observer/tabobserver.d.ts +0 -50
- package/dist/view/placeholder.d.ts +0 -100
- package/dist/view/position.d.ts +0 -192
- package/dist/view/range.d.ts +0 -283
- package/dist/view/rawelement.d.ts +0 -77
- package/dist/view/renderer.d.ts +0 -281
- package/dist/view/rooteditableelement.d.ts +0 -45
- package/dist/view/selection.d.ts +0 -379
- package/dist/view/styles/background.d.ts +0 -37
- package/dist/view/styles/border.d.ts +0 -47
- package/dist/view/styles/margin.d.ts +0 -33
- package/dist/view/styles/padding.d.ts +0 -33
- package/dist/view/styles/utils.d.ts +0 -97
- package/dist/view/stylesmap.d.ts +0 -685
- package/dist/view/text.d.ts +0 -78
- package/dist/view/textproxy.d.ts +0 -101
- package/dist/view/treewalker.d.ts +0 -199
- package/dist/view/typecheckable.d.ts +0 -452
- package/dist/view/uielement.d.ts +0 -100
- package/dist/view/upcastwriter.d.ts +0 -421
- package/dist/view/view.d.ts +0 -488
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Copyright (c) 2003-
|
|
3
|
-
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-
|
|
2
|
+
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
4
4
|
*/
|
|
5
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, uid, spliceArray, priorities, isInsideSurrogatePair, isInsideCombinedSymbol, isInsideEmojiSequence } from '@ckeditor/ckeditor5-utils/dist/index.js';
|
|
6
6
|
import { clone, isObject, unset, get, merge, set, isPlainObject, extend, debounce, isEqualWith, cloneDeep, isEqual } from 'lodash-es';
|
|
@@ -28,23 +28,21 @@ let hasDisplayedPlaceholderDeprecationWarning = false;
|
|
|
28
28
|
* @param options.keepOnFocus If set `true`, the placeholder stay visible when the host element is focused.
|
|
29
29
|
*/ function enablePlaceholder({ view, element, text, isDirectHost = true, keepOnFocus = false }) {
|
|
30
30
|
const doc = view.document;
|
|
31
|
-
// Use a single
|
|
31
|
+
// Use a single post fixer per—document to update all placeholders.
|
|
32
32
|
if (!documentPlaceholders.has(doc)) {
|
|
33
33
|
documentPlaceholders.set(doc, new Map());
|
|
34
34
|
// If a post-fixer callback makes a change, it should return `true` so other post–fixers
|
|
35
35
|
// can re–evaluate the document again.
|
|
36
|
-
doc.registerPostFixer((writer)=>updateDocumentPlaceholders(doc, writer));
|
|
36
|
+
doc.registerPostFixer((writer)=>updateDocumentPlaceholders(documentPlaceholders.get(doc), writer));
|
|
37
37
|
// Update placeholders on isComposing state change since rendering is disabled while in composition mode.
|
|
38
38
|
doc.on('change:isComposing', ()=>{
|
|
39
|
-
view.change((writer)=>updateDocumentPlaceholders(doc, writer));
|
|
39
|
+
view.change((writer)=>updateDocumentPlaceholders(documentPlaceholders.get(doc), writer));
|
|
40
40
|
}, {
|
|
41
41
|
priority: 'high'
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
44
|
if (element.is('editableElement')) {
|
|
45
|
-
element.on('change:placeholder', (evtInfo, evt, text)=>
|
|
46
|
-
setPlaceholder(text);
|
|
47
|
-
});
|
|
45
|
+
element.on('change:placeholder', (evtInfo, evt, text)=>setPlaceholder(text));
|
|
48
46
|
}
|
|
49
47
|
if (element.placeholder) {
|
|
50
48
|
setPlaceholder(element.placeholder);
|
|
@@ -55,15 +53,21 @@ let hasDisplayedPlaceholderDeprecationWarning = false;
|
|
|
55
53
|
showPlaceholderTextDeprecationWarning();
|
|
56
54
|
}
|
|
57
55
|
function setPlaceholder(text) {
|
|
58
|
-
|
|
59
|
-
documentPlaceholders.get(doc).set(element, {
|
|
56
|
+
const config = {
|
|
60
57
|
text,
|
|
61
58
|
isDirectHost,
|
|
62
59
|
keepOnFocus,
|
|
63
60
|
hostElement: isDirectHost ? element : null
|
|
64
|
-
}
|
|
61
|
+
};
|
|
62
|
+
// Store information about the element placeholder under its document.
|
|
63
|
+
documentPlaceholders.get(doc).set(element, config);
|
|
65
64
|
// Update the placeholders right away.
|
|
66
|
-
view.change((writer)=>updateDocumentPlaceholders(
|
|
65
|
+
view.change((writer)=>updateDocumentPlaceholders([
|
|
66
|
+
[
|
|
67
|
+
element,
|
|
68
|
+
config
|
|
69
|
+
]
|
|
70
|
+
], writer));
|
|
67
71
|
}
|
|
68
72
|
}
|
|
69
73
|
/**
|
|
@@ -138,9 +142,7 @@ let hasDisplayedPlaceholderDeprecationWarning = false;
|
|
|
138
142
|
if (!element.isAttached()) {
|
|
139
143
|
return false;
|
|
140
144
|
}
|
|
141
|
-
|
|
142
|
-
const hasContent = Array.from(element.getChildren()).some((element)=>!element.is('uiElement'));
|
|
143
|
-
if (hasContent) {
|
|
145
|
+
if (hasContent(element)) {
|
|
144
146
|
return false;
|
|
145
147
|
}
|
|
146
148
|
const doc = element.document;
|
|
@@ -160,12 +162,21 @@ let hasDisplayedPlaceholderDeprecationWarning = false;
|
|
|
160
162
|
// If document is focused and the element is empty but the selection is not anchored inside it.
|
|
161
163
|
return !!selectionAnchor && selectionAnchor.parent !== element;
|
|
162
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Anything but uiElement(s) counts as content.
|
|
167
|
+
*/ function hasContent(element) {
|
|
168
|
+
for (const child of element.getChildren()){
|
|
169
|
+
if (!child.is('uiElement')) {
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
163
175
|
/**
|
|
164
176
|
* Updates all placeholders associated with a document in a post–fixer callback.
|
|
165
177
|
*
|
|
166
178
|
* @returns True if any changes were made to the view document.
|
|
167
|
-
*/ function updateDocumentPlaceholders(
|
|
168
|
-
const placeholders = documentPlaceholders.get(doc);
|
|
179
|
+
*/ function updateDocumentPlaceholders(placeholders, writer) {
|
|
169
180
|
const directHostElements = [];
|
|
170
181
|
let wasViewModified = false;
|
|
171
182
|
// First set placeholders on the direct hosts.
|
|
@@ -254,7 +265,7 @@ let hasDisplayedPlaceholderDeprecationWarning = false;
|
|
|
254
265
|
}
|
|
255
266
|
|
|
256
267
|
/**
|
|
257
|
-
* @license Copyright (c) 2003-
|
|
268
|
+
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
|
|
258
269
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
259
270
|
*/ /**
|
|
260
271
|
* @module engine/view/typecheckable
|
|
@@ -453,11 +464,12 @@ let hasDisplayedPlaceholderDeprecationWarning = false;
|
|
|
453
464
|
* @internal
|
|
454
465
|
* @param type Type of the change.
|
|
455
466
|
* @param node Changed node.
|
|
467
|
+
* @param data Additional data.
|
|
456
468
|
* @fires change
|
|
457
|
-
*/ _fireChange(type, node) {
|
|
458
|
-
this.fire(`change:${type}`, node);
|
|
469
|
+
*/ _fireChange(type, node, data) {
|
|
470
|
+
this.fire(`change:${type}`, node, data);
|
|
459
471
|
if (this.parent) {
|
|
460
|
-
this.parent._fireChange(type, node);
|
|
472
|
+
this.parent._fireChange(type, node, data);
|
|
461
473
|
}
|
|
462
474
|
}
|
|
463
475
|
/**
|
|
@@ -2391,7 +2403,9 @@ TextProxy$1.prototype.is = function(type) {
|
|
|
2391
2403
|
* @fires change
|
|
2392
2404
|
* @returns Number of inserted nodes.
|
|
2393
2405
|
*/ _insertChild(index, items) {
|
|
2394
|
-
this._fireChange('children', this
|
|
2406
|
+
this._fireChange('children', this, {
|
|
2407
|
+
index
|
|
2408
|
+
});
|
|
2395
2409
|
let count = 0;
|
|
2396
2410
|
const nodes = normalize$3(this.document, items);
|
|
2397
2411
|
for (const node of nodes){
|
|
@@ -2417,7 +2431,9 @@ TextProxy$1.prototype.is = function(type) {
|
|
|
2417
2431
|
* @fires change
|
|
2418
2432
|
* @returns The array of removed nodes.
|
|
2419
2433
|
*/ _removeChildren(index, howMany = 1) {
|
|
2420
|
-
this._fireChange('children', this
|
|
2434
|
+
this._fireChange('children', this, {
|
|
2435
|
+
index
|
|
2436
|
+
});
|
|
2421
2437
|
for(let i = index; i < index + howMany; i++){
|
|
2422
2438
|
this._children[i].parent = null;
|
|
2423
2439
|
}
|
|
@@ -2889,6 +2905,28 @@ RootEditableElement.prototype.is = function(type, name) {
|
|
|
2889
2905
|
this._position = prevPosition;
|
|
2890
2906
|
}
|
|
2891
2907
|
}
|
|
2908
|
+
/**
|
|
2909
|
+
* Moves tree walker {@link #position} to provided `position`. Tree walker will
|
|
2910
|
+
* continue traversing from that position.
|
|
2911
|
+
*
|
|
2912
|
+
* Note: in contrary to {@link ~TreeWalker#skip}, this method does not iterate over the nodes along the way.
|
|
2913
|
+
* It simply sets the current tree walker position to a new one.
|
|
2914
|
+
* From the performance standpoint, it is better to use {@link ~TreeWalker#jumpTo} rather than {@link ~TreeWalker#skip}.
|
|
2915
|
+
*
|
|
2916
|
+
* If the provided position is before the start boundary, the position will be
|
|
2917
|
+
* set to the start boundary. If the provided position is after the end boundary,
|
|
2918
|
+
* the position will be set to the end boundary.
|
|
2919
|
+
* This is done to prevent the treewalker from traversing outside the boundaries.
|
|
2920
|
+
*
|
|
2921
|
+
* @param position Position to jump to.
|
|
2922
|
+
*/ jumpTo(position) {
|
|
2923
|
+
if (this._boundaryStartParent && position.isBefore(this.boundaries.start)) {
|
|
2924
|
+
position = this.boundaries.start;
|
|
2925
|
+
} else if (this._boundaryEndParent && position.isAfter(this.boundaries.end)) {
|
|
2926
|
+
position = this.boundaries.end;
|
|
2927
|
+
}
|
|
2928
|
+
this._position = position.clone();
|
|
2929
|
+
}
|
|
2892
2930
|
/**
|
|
2893
2931
|
* Gets the next tree walker's value.
|
|
2894
2932
|
*
|
|
@@ -5493,7 +5531,9 @@ RawElement.prototype.is = function(type, name) {
|
|
|
5493
5531
|
* @param items Items to be inserted.
|
|
5494
5532
|
* @returns Number of inserted nodes.
|
|
5495
5533
|
*/ _insertChild(index, items) {
|
|
5496
|
-
this._fireChange('children', this
|
|
5534
|
+
this._fireChange('children', this, {
|
|
5535
|
+
index
|
|
5536
|
+
});
|
|
5497
5537
|
let count = 0;
|
|
5498
5538
|
const nodes = normalize$2(this.document, items);
|
|
5499
5539
|
for (const node of nodes){
|
|
@@ -5516,20 +5556,22 @@ RawElement.prototype.is = function(type, name) {
|
|
|
5516
5556
|
* @param howMany Number of nodes to remove.
|
|
5517
5557
|
* @returns The array of removed nodes.
|
|
5518
5558
|
*/ _removeChildren(index, howMany = 1) {
|
|
5519
|
-
this._fireChange('children', this
|
|
5559
|
+
this._fireChange('children', this, {
|
|
5560
|
+
index
|
|
5561
|
+
});
|
|
5520
5562
|
for(let i = index; i < index + howMany; i++){
|
|
5521
5563
|
this._children[i].parent = null;
|
|
5522
5564
|
}
|
|
5523
5565
|
return this._children.splice(index, howMany);
|
|
5524
5566
|
}
|
|
5525
5567
|
/**
|
|
5526
|
-
* Fires `change` event with given type of the change.
|
|
5527
|
-
*
|
|
5528
5568
|
* @internal
|
|
5529
5569
|
* @param type Type of the change.
|
|
5530
5570
|
* @param node Changed node.
|
|
5531
|
-
|
|
5532
|
-
|
|
5571
|
+
* @param data Additional data.
|
|
5572
|
+
* @fires module:engine/view/node~Node#event:change
|
|
5573
|
+
*/ _fireChange(type, node, data) {
|
|
5574
|
+
this.fire(`change:${type}`, node, data);
|
|
5533
5575
|
}
|
|
5534
5576
|
/**
|
|
5535
5577
|
* Sets a custom property. They can be used to add special data to elements.
|
|
@@ -10721,7 +10763,7 @@ function sameNodes(child1, child2) {
|
|
|
10721
10763
|
}
|
|
10722
10764
|
|
|
10723
10765
|
/**
|
|
10724
|
-
* @license Copyright (c) 2003-
|
|
10766
|
+
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
|
|
10725
10767
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
10726
10768
|
*/ /**
|
|
10727
10769
|
* @module engine/view/datatransfer
|
|
@@ -11538,7 +11580,7 @@ function getFiles(nativeDataTransfer) {
|
|
|
11538
11580
|
}
|
|
11539
11581
|
|
|
11540
11582
|
/**
|
|
11541
|
-
* @license Copyright (c) 2003-
|
|
11583
|
+
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
|
|
11542
11584
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
11543
11585
|
*/ /**
|
|
11544
11586
|
* @module engine/model/typecheckable
|
|
@@ -11848,6 +11890,29 @@ TextProxy.prototype.is = function(type) {
|
|
|
11848
11890
|
this._visitedParent = prevVisitedParent;
|
|
11849
11891
|
}
|
|
11850
11892
|
}
|
|
11893
|
+
/**
|
|
11894
|
+
* Moves tree walker {@link #position} to provided `position`. Tree walker will
|
|
11895
|
+
* continue traversing from that position.
|
|
11896
|
+
*
|
|
11897
|
+
* Note: in contrary to {@link ~TreeWalker#skip}, this method does not iterate over the nodes along the way.
|
|
11898
|
+
* It simply sets the current tree walker position to a new one.
|
|
11899
|
+
* From the performance standpoint, it is better to use {@link ~TreeWalker#jumpTo} rather than {@link ~TreeWalker#skip}.
|
|
11900
|
+
*
|
|
11901
|
+
* If the provided position is before the start boundary, the position will be
|
|
11902
|
+
* set to the start boundary. If the provided position is after the end boundary,
|
|
11903
|
+
* the position will be set to the end boundary.
|
|
11904
|
+
* This is done to prevent the treewalker from traversing outside the boundaries.
|
|
11905
|
+
*
|
|
11906
|
+
* @param position Position to jump to.
|
|
11907
|
+
*/ jumpTo(position) {
|
|
11908
|
+
if (this._boundaryStartParent && position.isBefore(this.boundaries.start)) {
|
|
11909
|
+
position = this.boundaries.start;
|
|
11910
|
+
} else if (this._boundaryEndParent && position.isAfter(this.boundaries.end)) {
|
|
11911
|
+
position = this.boundaries.end;
|
|
11912
|
+
}
|
|
11913
|
+
this._position = position.clone();
|
|
11914
|
+
this._visitedParent = position.parent;
|
|
11915
|
+
}
|
|
11851
11916
|
/**
|
|
11852
11917
|
* Gets the next tree walker's value.
|
|
11853
11918
|
*/ next() {
|
|
@@ -13861,6 +13926,9 @@ Range.prototype.is = function(type) {
|
|
|
13861
13926
|
* Stores marker names of markers which have changed due to unbinding a view element (so it is assumed that the view element
|
|
13862
13927
|
* has been removed, moved or renamed).
|
|
13863
13928
|
*/ _unboundMarkerNames = new Set();
|
|
13929
|
+
/**
|
|
13930
|
+
* Manages dynamic cache for the `Mapper` to improve the performance.
|
|
13931
|
+
*/ _cache = new MapperCache();
|
|
13864
13932
|
/**
|
|
13865
13933
|
* Creates an instance of the mapper.
|
|
13866
13934
|
*/ constructor(){
|
|
@@ -13935,7 +14003,11 @@ Range.prototype.is = function(type) {
|
|
|
13935
14003
|
if (options.defer) {
|
|
13936
14004
|
this._deferredBindingRemovals.set(viewElement, viewElement.root);
|
|
13937
14005
|
} else {
|
|
13938
|
-
this._viewToModelMapping.delete(viewElement);
|
|
14006
|
+
const wasFound = this._viewToModelMapping.delete(viewElement);
|
|
14007
|
+
if (wasFound) {
|
|
14008
|
+
// Stop tracking after the element is no longer mapped. We want to track all mapped elements and only mapped elements.
|
|
14009
|
+
this._cache.stopTracking(viewElement);
|
|
14010
|
+
}
|
|
13939
14011
|
if (this._modelToViewMapping.get(modelElement) == viewElement) {
|
|
13940
14012
|
this._modelToViewMapping.delete(modelElement);
|
|
13941
14013
|
}
|
|
@@ -13955,7 +14027,11 @@ Range.prototype.is = function(type) {
|
|
|
13955
14027
|
const viewElement = this.toViewElement(modelElement);
|
|
13956
14028
|
this._modelToViewMapping.delete(modelElement);
|
|
13957
14029
|
if (this._viewToModelMapping.get(viewElement) == modelElement) {
|
|
13958
|
-
this._viewToModelMapping.delete(viewElement);
|
|
14030
|
+
const wasFound = this._viewToModelMapping.delete(viewElement);
|
|
14031
|
+
if (wasFound) {
|
|
14032
|
+
// Stop tracking after the element is no longer mapped. We want to track all mapped elements and only mapped elements.
|
|
14033
|
+
this._cache.stopTracking(viewElement);
|
|
14034
|
+
}
|
|
13959
14035
|
}
|
|
13960
14036
|
}
|
|
13961
14037
|
/**
|
|
@@ -14102,6 +14178,10 @@ Range.prototype.is = function(type) {
|
|
|
14102
14178
|
return elements;
|
|
14103
14179
|
}
|
|
14104
14180
|
/**
|
|
14181
|
+
* **This method is deprecated and will be removed in one of the future CKEditor 5 releases.**
|
|
14182
|
+
*
|
|
14183
|
+
* **Using this method will turn off `Mapper` caching system and may degrade performance when operating on bigger documents.**
|
|
14184
|
+
*
|
|
14105
14185
|
* Registers a callback that evaluates the length in the model of a view element with the given name.
|
|
14106
14186
|
*
|
|
14107
14187
|
* The callback is fired with one argument, which is a view element instance. The callback is expected to return
|
|
@@ -14131,6 +14211,7 @@ Range.prototype.is = function(type) {
|
|
|
14131
14211
|
*
|
|
14132
14212
|
* @param viewElementName Name of view element for which callback is registered.
|
|
14133
14213
|
* @param lengthCallback Function return a length of view element instance in model.
|
|
14214
|
+
* @deprecated
|
|
14134
14215
|
*/ registerViewToModelLength(viewElementName, lengthCallback) {
|
|
14135
14216
|
this._viewToModelLengthCallbacks.set(viewElementName, lengthCallback);
|
|
14136
14217
|
}
|
|
@@ -14233,56 +14314,119 @@ Range.prototype.is = function(type) {
|
|
|
14233
14314
|
return len;
|
|
14234
14315
|
}
|
|
14235
14316
|
/**
|
|
14236
|
-
* Finds the position in
|
|
14317
|
+
* Finds the position in a view element or view document fragment node (or in its children) with the expected model offset.
|
|
14237
14318
|
*
|
|
14238
|
-
*
|
|
14319
|
+
* If the passed `viewContainer` is bound to model, `Mapper` will use caching mechanism to improve performance.
|
|
14239
14320
|
*
|
|
14240
|
-
*
|
|
14241
|
-
*
|
|
14321
|
+
* @param viewContainer Tree view element in which we are looking for the position.
|
|
14322
|
+
* @param modelOffset Expected offset.
|
|
14323
|
+
* @returns Found position.
|
|
14324
|
+
*/ findPositionIn(viewContainer, modelOffset) {
|
|
14325
|
+
if (modelOffset === 0) {
|
|
14326
|
+
// Quickly return if asked for a position at the beginning of the container. No need to fire complex mechanisms to find it.
|
|
14327
|
+
return this._moveViewPositionToTextNode(new Position$1(viewContainer, 0));
|
|
14328
|
+
}
|
|
14329
|
+
// Use cache only if there are no custom view-to-model length callbacks and only for bound elements.
|
|
14330
|
+
// View-to-model length callbacks are deprecated and should be removed in one of the following releases.
|
|
14331
|
+
// Then it will be possible to simplify some logic inside `Mapper`.
|
|
14332
|
+
// Note: we could consider requiring `viewContainer` to be a mapped item.
|
|
14333
|
+
const useCache = this._viewToModelLengthCallbacks.size == 0 && this._viewToModelMapping.has(viewContainer);
|
|
14334
|
+
if (useCache) {
|
|
14335
|
+
const cacheItem = this._cache.getClosest(viewContainer, modelOffset);
|
|
14336
|
+
return this._findPositionStartingFrom(cacheItem.viewPosition, cacheItem.modelOffset, modelOffset, viewContainer, true);
|
|
14337
|
+
} else {
|
|
14338
|
+
return this._findPositionStartingFrom(new Position$1(viewContainer, 0), 0, modelOffset, viewContainer, false);
|
|
14339
|
+
}
|
|
14340
|
+
}
|
|
14341
|
+
/**
|
|
14342
|
+
* Performs most of the logic for `Mapper#findPositionIn()`.
|
|
14242
14343
|
*
|
|
14243
|
-
*
|
|
14244
|
-
*
|
|
14245
|
-
* <p>fo|<b>bar</b>bom</p> -> expected offset: 4, actual offset: 2
|
|
14246
|
-
* <p>fo<b>bar</b>|bom</p> -> expected offset: 4, actual offset: 5 -> we are too far
|
|
14344
|
+
* It allows to start looking for the requested model offset from a given starting position, to enable caching. Using the cache,
|
|
14345
|
+
* you can set the starting point and skip all the calculations that were already previously done.
|
|
14247
14346
|
*
|
|
14248
|
-
*
|
|
14249
|
-
* <p>fo<b>|bar</b>bom</p> -> expected offset: 2, actual offset: 0
|
|
14250
|
-
* <p>fo<b>bar|</b>bom</p> -> expected offset: 2, actual offset: 3 -> we are too far
|
|
14347
|
+
* This method uses recursion to find positions inside deep structures. Example:
|
|
14251
14348
|
*
|
|
14252
|
-
* findPositionIn( bar, 2 - ( 3 - 3 ) ):
|
|
14253
|
-
* We are in the text node so we can simple find the offset.
|
|
14254
|
-
* <p>fo<b>ba|r</b>bom</p> -> expected offset: 2, actual offset: 2 -> position found
|
|
14255
14349
|
* ```
|
|
14350
|
+
* <p>fo<b>bar</b>bom</p> -> target offset: 4
|
|
14351
|
+
* <p>|fo<b>bar</b>bom</p> -> target offset: 4, traversed offset: 0
|
|
14352
|
+
* <p>fo|<b>bar</b>bom</p> -> target offset: 4, traversed offset: 2
|
|
14353
|
+
* <p>fo<b>bar</b>|bom</p> -> target offset: 4, traversed offset: 5 -> we are too far, look recursively in <b>.
|
|
14256
14354
|
*
|
|
14257
|
-
*
|
|
14258
|
-
*
|
|
14259
|
-
*
|
|
14260
|
-
|
|
14261
|
-
|
|
14262
|
-
|
|
14263
|
-
|
|
14264
|
-
|
|
14265
|
-
|
|
14266
|
-
|
|
14355
|
+
* <p>fo<b>|bar</b>bom</p> -> target offset: 4, traversed offset: 2
|
|
14356
|
+
* <p>fo<b>bar|</b>bom</p> -> target offset: 4, traversed offset: 5 -> we are too far, look inside "bar".
|
|
14357
|
+
*
|
|
14358
|
+
* <p>fo<b>ba|r</b>bom</p> -> target offset: 4, traversed offset: 2 -> position is inside text node at offset 4-2 = 2.
|
|
14359
|
+
* ```
|
|
14360
|
+
*
|
|
14361
|
+
* @param startViewPosition View position to start looking from.
|
|
14362
|
+
* @param startModelOffset Model offset related to `startViewPosition`.
|
|
14363
|
+
* @param targetModelOffset Target model offset to find.
|
|
14364
|
+
* @param viewContainer Mapped ancestor of `startViewPosition`. `startModelOffset` is the offset inside a model element or model
|
|
14365
|
+
* document fragment mapped to `viewContainer`.
|
|
14366
|
+
* @param useCache Whether `Mapper` should cache positions while traversing the view tree looking for `expectedModelOffset`.
|
|
14367
|
+
* @returns View position mapped to `targetModelOffset`.
|
|
14368
|
+
*/ _findPositionStartingFrom(startViewPosition, startModelOffset, targetModelOffset, viewContainer, useCache) {
|
|
14369
|
+
let viewParent = startViewPosition.parent;
|
|
14370
|
+
let viewOffset = startViewPosition.offset;
|
|
14267
14371
|
// In the text node it is simple: the offset in the model equals the offset in the text.
|
|
14268
14372
|
if (viewParent.is('$text')) {
|
|
14269
|
-
return new Position$1(viewParent,
|
|
14373
|
+
return new Position$1(viewParent, targetModelOffset - startModelOffset);
|
|
14270
14374
|
}
|
|
14271
|
-
//
|
|
14272
|
-
|
|
14273
|
-
|
|
14375
|
+
// Last scanned view node.
|
|
14376
|
+
let viewNode;
|
|
14377
|
+
// Total model offset of the view nodes that were visited so far.
|
|
14378
|
+
let traversedModelOffset = startModelOffset;
|
|
14379
|
+
// Model length of the last traversed view node.
|
|
14380
|
+
let lastLength = 0;
|
|
14381
|
+
while(traversedModelOffset < targetModelOffset){
|
|
14274
14382
|
viewNode = viewParent.getChild(viewOffset);
|
|
14383
|
+
if (!viewNode) {
|
|
14384
|
+
// If we still haven't reached the model offset, but we reached end of this `viewParent`, then we need to "leave" this
|
|
14385
|
+
// element and "go up", looking further for the target model offset. This can happen when cached model offset is "deeper"
|
|
14386
|
+
// but target model offset is "higher" in the view tree.
|
|
14387
|
+
//
|
|
14388
|
+
// Example: `<p>Foo<strong><em>Bar</em>^Baz</strong>Xyz</p>`
|
|
14389
|
+
//
|
|
14390
|
+
// Consider `^` is last cached position, when the `targetModelOffset` is `12`. In such case, we need to "go up" from
|
|
14391
|
+
// `<strong>` and continue traversing in `<p>`.
|
|
14392
|
+
//
|
|
14393
|
+
if (viewParent == viewContainer) {
|
|
14394
|
+
/**
|
|
14395
|
+
* A model position could not be mapped to the view because specified model offset was too big and could not be
|
|
14396
|
+
* found inside the mapped view element or view document fragment.
|
|
14397
|
+
*
|
|
14398
|
+
* @error mapping-model-offset-not-found
|
|
14399
|
+
*/ throw new CKEditorError('mapping-model-offset-not-found', this, {
|
|
14400
|
+
modelOffset: targetModelOffset,
|
|
14401
|
+
viewContainer
|
|
14402
|
+
});
|
|
14403
|
+
} else {
|
|
14404
|
+
viewOffset = viewParent.parent.getChildIndex(viewParent) + 1;
|
|
14405
|
+
viewParent = viewParent.parent;
|
|
14406
|
+
continue;
|
|
14407
|
+
}
|
|
14408
|
+
}
|
|
14275
14409
|
lastLength = this.getModelLength(viewNode);
|
|
14276
|
-
|
|
14410
|
+
traversedModelOffset += lastLength;
|
|
14277
14411
|
viewOffset++;
|
|
14412
|
+
if (useCache) {
|
|
14413
|
+
// Note, that we cache the view position before and after a visited element here, so before we (possibly) "enter" it
|
|
14414
|
+
// (see `else` below).
|
|
14415
|
+
//
|
|
14416
|
+
// Since `MapperCache#save` does not overwrite already cached model offsets, this way the cached position is set to
|
|
14417
|
+
// a correct location, that is the closest to the mapped `viewContainer`.
|
|
14418
|
+
//
|
|
14419
|
+
// However, in some cases, we still need to "hoist" the cached position (see `MapperCache#_hoistViewPosition()`).
|
|
14420
|
+
this._cache.save(viewParent, viewOffset, viewContainer, traversedModelOffset);
|
|
14421
|
+
}
|
|
14278
14422
|
}
|
|
14279
|
-
|
|
14280
|
-
|
|
14423
|
+
if (traversedModelOffset == targetModelOffset) {
|
|
14424
|
+
// If it equals we found the position.
|
|
14281
14425
|
return this._moveViewPositionToTextNode(new Position$1(viewParent, viewOffset));
|
|
14282
14426
|
} else {
|
|
14283
|
-
//
|
|
14284
|
-
//
|
|
14285
|
-
return this.
|
|
14427
|
+
// If it is higher we overstepped with the last traversed view node.
|
|
14428
|
+
// We need to "enter" it, and look for the view position / model offset inside the last visited view node.
|
|
14429
|
+
return this._findPositionStartingFrom(new Position$1(viewNode, 0), traversedModelOffset - lastLength, targetModelOffset, viewContainer, useCache);
|
|
14286
14430
|
}
|
|
14287
14431
|
}
|
|
14288
14432
|
/**
|
|
@@ -14311,6 +14455,401 @@ Range.prototype.is = function(type) {
|
|
|
14311
14455
|
return viewPosition;
|
|
14312
14456
|
}
|
|
14313
14457
|
}
|
|
14458
|
+
/**
|
|
14459
|
+
* Cache mechanism for {@link module:engine/conversion/mapper~Mapper Mapper}.
|
|
14460
|
+
*
|
|
14461
|
+
* `MapperCache` improves performance for model-to-view position mapping, which is the main `Mapper` task. Asking for a mapping is much
|
|
14462
|
+
* more frequent than actually performing changes, and even if the change happens, we can still partially keep the cache. This makes
|
|
14463
|
+
* caching a useful strategy for `Mapper`.
|
|
14464
|
+
*
|
|
14465
|
+
* `MapperCache` will store some data for view elements or view document fragments that are mapped by the `Mapper`. These view items
|
|
14466
|
+
* are "tracked" by the `MapperCache`. For such view items, we will keep entries of model offsets inside their mapped counterpart. For
|
|
14467
|
+
* the cached model offsets, we will keep a view position that is inside the tracked item. This allows us to either get the mapping
|
|
14468
|
+
* instantly, or at least in less steps than when calculating it from the beginning.
|
|
14469
|
+
*
|
|
14470
|
+
* Important problem related to caching is invalidating the cache. The cache must be invalidated each time the tracked view item changes.
|
|
14471
|
+
* Additionally, we should invalidate as small part of the cache as possible. Since all the logic is encapsulated inside `MapperCache`,
|
|
14472
|
+
* the `MapperCache` listens to view items {@link module:engine/view/node~ViewNodeChangeEvent `change` event} and reacts to it.
|
|
14473
|
+
* Then, it invalidates just the part of the cache that is "after" the changed part of the view.
|
|
14474
|
+
*
|
|
14475
|
+
* As mentioned, `MapperCache` currently is used only for model-to-view position mapping as it was much bigger problem than view-to-model
|
|
14476
|
+
* mapping. However, it should be possible to use it also for view-to-model.
|
|
14477
|
+
*
|
|
14478
|
+
* The main assumptions regarding `MapperCache` are:
|
|
14479
|
+
*
|
|
14480
|
+
* * it is an internal tool, used by `Mapper`, transparent to the outside (no additional effort when developing a plugin or a converter),
|
|
14481
|
+
* * it stores all the necessary data internally, which makes it easier to disable or debug,
|
|
14482
|
+
* * it is optimized for initial downcast process (long insertions), which is crucial for editor init and data save,
|
|
14483
|
+
* * it does not save all possible positions for memory considerations, although it is a possible improvement, which may have increase
|
|
14484
|
+
* performance, as well as simplify some parts of the `MapperCache` logic.
|
|
14485
|
+
*
|
|
14486
|
+
* @internal
|
|
14487
|
+
*/ class MapperCache extends /* #__PURE__ */ EmitterMixin() {
|
|
14488
|
+
/**
|
|
14489
|
+
* For every view element or document fragment tracked by `MapperCache`, it holds currently cached data, or more precisely,
|
|
14490
|
+
* model offset to view position mappings. See also `MappingCache` and `CacheItem`.
|
|
14491
|
+
*
|
|
14492
|
+
* If an item is tracked by `MapperCache` it has an entry in this structure, so this structure can be used to check which items
|
|
14493
|
+
* are tracked by `MapperCache`. When an item is no longer tracked, it is removed from this structure.
|
|
14494
|
+
*
|
|
14495
|
+
* Although `MappingCache` and `CacheItem` structures allows for caching any model offsets and view positions, we only cache
|
|
14496
|
+
* values for model offsets that are after a view node. So, in essence, positions inside text nodes are not cached. However, it takes
|
|
14497
|
+
* from one to at most a few steps, to get from a cached position to a position that is inside a view text node.
|
|
14498
|
+
*
|
|
14499
|
+
* Additionally, only one item per `modelOffset` is cached. There can be several view nodes that "end" at the same `modelOffset`.
|
|
14500
|
+
* In this case, we favour positions that are closer to the mapped item. For example
|
|
14501
|
+
*
|
|
14502
|
+
* * for model: `<paragraph>Some <$text bold=true italic=true>formatted</$text> text.</paragraph>`,
|
|
14503
|
+
* * and view: `<p>Some <em><strong>formatted</strong></em> text.</p>`,
|
|
14504
|
+
*
|
|
14505
|
+
* for model offset `14` (after "d"), we store view position after `<em>` element (i.e. view position: at `<p>`, offset `2`).
|
|
14506
|
+
*/ _cachedMapping = new WeakMap();
|
|
14507
|
+
/**
|
|
14508
|
+
* When `MapperCache` {@link ~MapperCache#save saves} view position -> model offset mapping, a `CacheItem` is inserted into certain
|
|
14509
|
+
* `MappingCache#cacheList` at some index. Additionally, we store that index with the view node that is before the cached view position.
|
|
14510
|
+
*
|
|
14511
|
+
* This allows to quickly get a cache list item related to certain view node, and hence, for fast cache invalidation.
|
|
14512
|
+
*
|
|
14513
|
+
* For example, consider view: `<p>Some <strong>bold</strong> text.</p>`, where `<p>` is a view element tracked by `MapperCache`.
|
|
14514
|
+
* If all `<p>` children were visited by `MapperCache`, then `<p>` cache list would have four items, related to following model offsets:
|
|
14515
|
+
* `0`, `5`, `9`, `15`. Then, view node `"Some "` would have index `1`, `<strong>` index `2`, and `" text." index `3`.
|
|
14516
|
+
*
|
|
14517
|
+
* Note that the index related with a node is always greater than `0`. The first item in cache list is always for model offset `0`
|
|
14518
|
+
* (and view offset `0`), and it is not related to any node.
|
|
14519
|
+
*/ _nodeToCacheListIndex = new WeakMap();
|
|
14520
|
+
/**
|
|
14521
|
+
* Callback fired whenever there is a direct or indirect children change in tracked view element or tracked view document fragment.
|
|
14522
|
+
*
|
|
14523
|
+
* This is specified as a property to make it easier to set as an event callback and to later turn off that event.
|
|
14524
|
+
*/ _invalidateOnChildrenChangeCallback = (evt, viewNode, data)=>{
|
|
14525
|
+
// View element or document fragment changed its children at `data.index`. Clear all cache starting from before that index.
|
|
14526
|
+
this._clearCacheInsideParent(viewNode, data.index);
|
|
14527
|
+
};
|
|
14528
|
+
/**
|
|
14529
|
+
* Callback fired whenever a view text node directly or indirectly inside a tracked view element or tracked view document fragment
|
|
14530
|
+
* changes its text data.
|
|
14531
|
+
*
|
|
14532
|
+
* This is specified as a property to make it easier to set as an event callback and to later turn off that event.
|
|
14533
|
+
*/ _invalidateOnTextChangeCallback = (evt, viewNode)=>{
|
|
14534
|
+
// Text node has changed. Clear all the cache starting from before this text node.
|
|
14535
|
+
this._clearCacheStartingBefore(viewNode);
|
|
14536
|
+
};
|
|
14537
|
+
/**
|
|
14538
|
+
* Saves cache for given view position mapping <-> model offset mapping. The view position should be after a node (i.e. it cannot
|
|
14539
|
+
* be the first position inside its parent, or in other words, `viewOffset` must be greater than `0`).
|
|
14540
|
+
*
|
|
14541
|
+
* @param viewParent View position parent.
|
|
14542
|
+
* @param viewOffset View position offset. Must be greater than `0`.
|
|
14543
|
+
* @param viewContainer Tracked view position ascendant (it may be the direct parent of the view position).
|
|
14544
|
+
* @param modelOffset Model offset in the model element or document fragment which is mapped to `viewContainer`.
|
|
14545
|
+
*/ save(viewParent, viewOffset, viewContainer, modelOffset) {
|
|
14546
|
+
// Get current cache for the tracked ancestor.
|
|
14547
|
+
const cache = this._cachedMapping.get(viewContainer);
|
|
14548
|
+
// See if there is already a cache defined for `modelOffset`.
|
|
14549
|
+
const cacheItem = cache.cacheMap.get(modelOffset);
|
|
14550
|
+
if (cacheItem) {
|
|
14551
|
+
// We already cached this offset. Don't overwrite the cache.
|
|
14552
|
+
//
|
|
14553
|
+
// This assumes that `Mapper` works in a way that we first cache the parent and only then cache children, as we prefer position
|
|
14554
|
+
// after the parent ("closer" to the tracked ancestor). It might be safer to check which position is preferred (newly saved or
|
|
14555
|
+
// the one currently in cache) but it would require additional processing. For now, `Mapper#_findPositionIn()` and
|
|
14556
|
+
// `Mapper#getModelLength()` are implemented so that parents are cached before their children.
|
|
14557
|
+
//
|
|
14558
|
+
// So, don't create new cache if one already exists. Instead, only save `_nodeToCacheListIndex` value for the related view node.
|
|
14559
|
+
const viewChild = viewParent.getChild(viewOffset - 1);
|
|
14560
|
+
// Figure out what index to save with `viewChild`.
|
|
14561
|
+
// We have a `cacheItem` for the `modelOffset`, so we can get a `viewPosition` from there. Before that view position, there
|
|
14562
|
+
// must be a node. That node must have an index set. This will be the index we will want to use.
|
|
14563
|
+
// Since we expect `viewOffset` to be greater than 0, then in almost all cases `modelOffset` will be greater than 0 as well.
|
|
14564
|
+
// As a result, we can expect `cacheItem.viewPosition.nodeBefore` to be set.
|
|
14565
|
+
//
|
|
14566
|
+
// However, in an edge case, were the tracked element contains a 0-model-length view element as the first child (UI element or
|
|
14567
|
+
// an empty attribute element), then `modelOffset` will be 0, and `cacheItem` will be the first cache item, which is before any
|
|
14568
|
+
// view node. In such edge case, `cacheItem.viewPosition.nodeBefore` is undefined, and we manually set to `0`.
|
|
14569
|
+
const index = cacheItem.viewPosition.nodeBefore ? this._nodeToCacheListIndex.get(cacheItem.viewPosition.nodeBefore) : 0;
|
|
14570
|
+
this._nodeToCacheListIndex.set(viewChild, index);
|
|
14571
|
+
return;
|
|
14572
|
+
}
|
|
14573
|
+
const viewPosition = new Position$1(viewParent, viewOffset);
|
|
14574
|
+
const newCacheItem = {
|
|
14575
|
+
viewPosition,
|
|
14576
|
+
modelOffset
|
|
14577
|
+
};
|
|
14578
|
+
// Extend the valid cache range.
|
|
14579
|
+
cache.maxModelOffset = modelOffset > cache.maxModelOffset ? modelOffset : cache.maxModelOffset;
|
|
14580
|
+
// Save the new cache item to the `cacheMap`.
|
|
14581
|
+
cache.cacheMap.set(modelOffset, newCacheItem);
|
|
14582
|
+
// Save the new cache item to the `cacheList`.
|
|
14583
|
+
let i = cache.cacheList.length - 1;
|
|
14584
|
+
// Mostly, we cache elements at the end of `cacheList` and the loop does not execute even once. But when we recursively visit nodes
|
|
14585
|
+
// in `Mapper#_findPositionIn()`, then we will first cache the parent, and then it's children, and they will not be added at the
|
|
14586
|
+
// end of `cacheList`. This is why we need to find correct index to insert them.
|
|
14587
|
+
while(i >= 0 && cache.cacheList[i].modelOffset > modelOffset){
|
|
14588
|
+
i--;
|
|
14589
|
+
}
|
|
14590
|
+
cache.cacheList.splice(i + 1, 0, newCacheItem);
|
|
14591
|
+
if (viewOffset > 0) {
|
|
14592
|
+
const viewChild = viewParent.getChild(viewOffset - 1);
|
|
14593
|
+
// There was an idea to also cache `viewContainer` here but, it could lead to wrong results. If we wanted to cache
|
|
14594
|
+
// `viewContainer`, we probably would need to clear `this._nodeToCacheListIndex` when cache is cleared.
|
|
14595
|
+
// Also, there was no gain from caching this value, the results were almost the same (statistical error).
|
|
14596
|
+
this._nodeToCacheListIndex.set(viewChild, i + 1);
|
|
14597
|
+
}
|
|
14598
|
+
}
|
|
14599
|
+
/**
|
|
14600
|
+
* For given `modelOffset` inside a model element mapped to given `viewContainer`, it returns the closest saved cache item
|
|
14601
|
+
* (view position and related model offset) to the requested one.
|
|
14602
|
+
*
|
|
14603
|
+
* It can be exactly the requested mapping, or it can be mapping that is the closest starting point to look for the requested mapping.
|
|
14604
|
+
*
|
|
14605
|
+
* `viewContainer` must be a view element or document fragment that is mapped by the {@link ~Mapper Mapper}.
|
|
14606
|
+
*
|
|
14607
|
+
* If `viewContainer` is not yet tracked by the `MapperCache`, it will be automatically tracked after calling this method.
|
|
14608
|
+
*
|
|
14609
|
+
* Note: this method will automatically "hoist" cached positions, i.e. it will return a position that is closest to the tracked element.
|
|
14610
|
+
*
|
|
14611
|
+
* For example, if `<p>` is tracked element, and `^` is cached position:
|
|
14612
|
+
*
|
|
14613
|
+
* ```
|
|
14614
|
+
* <p>This is <strong>some <em>heavily <u>formatted</u>^</em></strong> text.</p>
|
|
14615
|
+
* ```
|
|
14616
|
+
*
|
|
14617
|
+
* If this position would be returned, instead, a position directly in `<p>` would be returned:
|
|
14618
|
+
*
|
|
14619
|
+
* ```
|
|
14620
|
+
* <p>This is <strong>some <em>heavily <u>formatted</u></em></strong>^ text.</p>
|
|
14621
|
+
* ```
|
|
14622
|
+
*
|
|
14623
|
+
* Note, that `modelOffset` for both positions is the same.
|
|
14624
|
+
*
|
|
14625
|
+
* @param viewContainer Tracked view element or document fragment, which cache will be used.
|
|
14626
|
+
* @param modelOffset Model offset in a model element or document fragment, which is mapped to `viewContainer`.
|
|
14627
|
+
*/ getClosest(viewContainer, modelOffset) {
|
|
14628
|
+
const cache = this._cachedMapping.get(viewContainer);
|
|
14629
|
+
let result;
|
|
14630
|
+
if (cache) {
|
|
14631
|
+
if (modelOffset > cache.maxModelOffset) {
|
|
14632
|
+
result = cache.cacheList[cache.cacheList.length - 1];
|
|
14633
|
+
} else {
|
|
14634
|
+
const cacheItem = cache.cacheMap.get(modelOffset);
|
|
14635
|
+
if (cacheItem) {
|
|
14636
|
+
result = cacheItem;
|
|
14637
|
+
} else {
|
|
14638
|
+
result = this._findInCacheList(cache.cacheList, modelOffset);
|
|
14639
|
+
}
|
|
14640
|
+
}
|
|
14641
|
+
} else {
|
|
14642
|
+
result = this.startTracking(viewContainer);
|
|
14643
|
+
}
|
|
14644
|
+
const viewPosition = this._hoistViewPosition(result.viewPosition);
|
|
14645
|
+
return {
|
|
14646
|
+
modelOffset: result.modelOffset,
|
|
14647
|
+
viewPosition
|
|
14648
|
+
};
|
|
14649
|
+
}
|
|
14650
|
+
/**
|
|
14651
|
+
* Moves a view position to a preferred location.
|
|
14652
|
+
*
|
|
14653
|
+
* The view position is moved up from a non-tracked view element as long as it remains at the end of its current parent.
|
|
14654
|
+
*
|
|
14655
|
+
* See example below to understand when it is important:
|
|
14656
|
+
*
|
|
14657
|
+
* Starting state:
|
|
14658
|
+
*
|
|
14659
|
+
* ```
|
|
14660
|
+
* <p>This is <strong>some <em>heavily <u>formatted</u>^ piece of</em></strong> text.</p>
|
|
14661
|
+
* ```
|
|
14662
|
+
*
|
|
14663
|
+
* Then we remove " piece of " and invalidate some cache:
|
|
14664
|
+
*
|
|
14665
|
+
* ```
|
|
14666
|
+
* <p>This is <strong>some <em>heavily <u>formatted</u>^</em></strong> text.</p>
|
|
14667
|
+
* ```
|
|
14668
|
+
*
|
|
14669
|
+
* Now, if we ask for model offset after letter "d" in "formatted", we should get a position in " text", but we will get in `<em>`.
|
|
14670
|
+
* For this scenario, we need to hoist the position.
|
|
14671
|
+
*
|
|
14672
|
+
* ```
|
|
14673
|
+
* <p>This is <strong>some <em>heavily <u>formatted</u></em></strong>^ text.</p>
|
|
14674
|
+
* ```
|
|
14675
|
+
*/ _hoistViewPosition(viewPosition) {
|
|
14676
|
+
while(viewPosition.parent.parent && !this._cachedMapping.has(viewPosition.parent) && viewPosition.isAtEnd){
|
|
14677
|
+
const parent = viewPosition.parent.parent;
|
|
14678
|
+
const offset = parent.getChildIndex(viewPosition.parent) + 1;
|
|
14679
|
+
viewPosition = new Position$1(parent, offset);
|
|
14680
|
+
}
|
|
14681
|
+
return viewPosition;
|
|
14682
|
+
}
|
|
14683
|
+
/**
|
|
14684
|
+
* Starts tracking given `viewContainer`, which must be mapped to a model element or model document fragment.
|
|
14685
|
+
*
|
|
14686
|
+
* Note, that this method is automatically called by
|
|
14687
|
+
* {@link module:engine/conversion/mapper~MapperCache#getClosest `MapperCache#getClosest()`} and there is no need to call it manually.
|
|
14688
|
+
*
|
|
14689
|
+
* This method initializes the cache for `viewContainer` and adds callbacks for
|
|
14690
|
+
* {@link module:engine/view/node~ViewNodeChangeEvent `change` event} fired by `viewContainer`. `MapperCache` listens to `change` event
|
|
14691
|
+
* on the tracked elements to invalidate the stored cache.
|
|
14692
|
+
*/ startTracking(viewContainer) {
|
|
14693
|
+
const viewPosition = new Position$1(viewContainer, 0);
|
|
14694
|
+
const initialCacheItem = {
|
|
14695
|
+
viewPosition,
|
|
14696
|
+
modelOffset: 0
|
|
14697
|
+
};
|
|
14698
|
+
const initialCache = {
|
|
14699
|
+
maxModelOffset: 0,
|
|
14700
|
+
cacheList: [
|
|
14701
|
+
initialCacheItem
|
|
14702
|
+
],
|
|
14703
|
+
cacheMap: new Map([
|
|
14704
|
+
[
|
|
14705
|
+
0,
|
|
14706
|
+
initialCacheItem
|
|
14707
|
+
]
|
|
14708
|
+
])
|
|
14709
|
+
};
|
|
14710
|
+
this._cachedMapping.set(viewContainer, initialCache);
|
|
14711
|
+
// Listen to changes in tracked view containers in order to invalidate the cache.
|
|
14712
|
+
//
|
|
14713
|
+
// Possible performance improvement. This event bubbles, so if there are multiple tracked (mapped) elements that are ancestors
|
|
14714
|
+
// then this will be unnecessarily fired for each ancestor. This could be rewritten to listen only to roots and document fragments.
|
|
14715
|
+
viewContainer.on('change:children', this._invalidateOnChildrenChangeCallback);
|
|
14716
|
+
viewContainer.on('change:text', this._invalidateOnTextChangeCallback);
|
|
14717
|
+
return initialCacheItem;
|
|
14718
|
+
}
|
|
14719
|
+
/**
|
|
14720
|
+
* Stops tracking given `viewContainer`.
|
|
14721
|
+
*
|
|
14722
|
+
* It removes the cached data and stops listening to {@link module:engine/view/node~ViewNodeChangeEvent `change` event} on the
|
|
14723
|
+
* `viewContainer`.
|
|
14724
|
+
*/ stopTracking(viewContainer) {
|
|
14725
|
+
viewContainer.off('change:children', this._invalidateOnChildrenChangeCallback);
|
|
14726
|
+
viewContainer.off('change:text', this._invalidateOnTextChangeCallback);
|
|
14727
|
+
this._cachedMapping.delete(viewContainer);
|
|
14728
|
+
}
|
|
14729
|
+
/**
|
|
14730
|
+
* Invalidates cache inside `viewParent`, starting from given `index` in that parent.
|
|
14731
|
+
*/ _clearCacheInsideParent(viewParent, index) {
|
|
14732
|
+
if (index == 0) {
|
|
14733
|
+
// Change at the beginning of the parent.
|
|
14734
|
+
if (this._cachedMapping.has(viewParent)) {
|
|
14735
|
+
// If this is a tracked element, clear all cache.
|
|
14736
|
+
this._clearCacheAll(viewParent);
|
|
14737
|
+
} else {
|
|
14738
|
+
// If this is not a tracked element, remove cache starting from before this element.
|
|
14739
|
+
this._clearCacheStartingBefore(viewParent);
|
|
14740
|
+
}
|
|
14741
|
+
} else {
|
|
14742
|
+
// Change in the middle of the parent. Get a view node that's before the change.
|
|
14743
|
+
const lastValidNode = viewParent.getChild(index - 1);
|
|
14744
|
+
// Then, clear all cache starting from before this view node.
|
|
14745
|
+
//
|
|
14746
|
+
// Possible performance improvement. We could have had `_clearCacheAfter( lastValidNode )` instead.
|
|
14747
|
+
// If the `lastValidNode` is the last unchanged node, then we could clear everything AFTER it, not before.
|
|
14748
|
+
// However, with the current setup, it didn't work properly and the actual gain wasn't that big on the tested data.
|
|
14749
|
+
// The problem was with following example: <p>Foo<em><strong>Xyz</strong></em>Bar</p>.
|
|
14750
|
+
// In this example we cache position after <em>, i.e. view position `<p>` 2 is saved with model offset 6.
|
|
14751
|
+
// Now, if we add some text in `<em>`, we won't validate this cached item even though it gets outdated.
|
|
14752
|
+
// So, if there's a need to have `_clearCacheAfter()`, we need to solve the above case first.
|
|
14753
|
+
//
|
|
14754
|
+
this._clearCacheStartingBefore(lastValidNode);
|
|
14755
|
+
}
|
|
14756
|
+
}
|
|
14757
|
+
/**
|
|
14758
|
+
* Clears all the cache for given tracked `viewContainer`.
|
|
14759
|
+
*/ _clearCacheAll(viewContainer) {
|
|
14760
|
+
const cache = this._cachedMapping.get(viewContainer);
|
|
14761
|
+
if (cache.maxModelOffset > 0) {
|
|
14762
|
+
cache.maxModelOffset = 0;
|
|
14763
|
+
cache.cacheList.length = 1;
|
|
14764
|
+
cache.cacheMap.clear();
|
|
14765
|
+
cache.cacheMap.set(0, cache.cacheList[0]);
|
|
14766
|
+
}
|
|
14767
|
+
}
|
|
14768
|
+
/**
|
|
14769
|
+
* Clears all the stored cache starting before given `viewNode`. The `viewNode` can be any node that is inside a tracked view element
|
|
14770
|
+
* or view document fragment.
|
|
14771
|
+
*/ _clearCacheStartingBefore(viewNode) {
|
|
14772
|
+
// To quickly invalidate the cache, we base on the cache list index stored with the node. See docs for `this._nodeToCacheListIndex`.
|
|
14773
|
+
const cacheListIndex = this._nodeToCacheListIndex.get(viewNode);
|
|
14774
|
+
// If there is no index stored, it means that this `viewNode` has not been cached yet.
|
|
14775
|
+
if (cacheListIndex === undefined) {
|
|
14776
|
+
// If the node is not cached, maybe it's parent is. We will try to invalidate the cache starting from before the parent.
|
|
14777
|
+
// Note, that there always must be a parent if we got here.
|
|
14778
|
+
const viewParent = viewNode.parent;
|
|
14779
|
+
// If the parent is a non-tracked element, try clearing the cache starting before it.
|
|
14780
|
+
//
|
|
14781
|
+
// This situation may happen e.g. if structure like `<p><strong><em>Foo</em></strong>...` was stepped over in
|
|
14782
|
+
// `Mapper#_findPositionIn()` and the children are not cached yet, but the `<strong>` element is. If something changes
|
|
14783
|
+
// inside this structure, make sure to invalidate all the cache after `<strong>`.
|
|
14784
|
+
//
|
|
14785
|
+
// If the parent is a tracked element, then it means there's no cache to clear (nothing after the element is cached).
|
|
14786
|
+
// In this case, there's nothing to do.
|
|
14787
|
+
//
|
|
14788
|
+
if (!this._cachedMapping.has(viewParent)) {
|
|
14789
|
+
this._clearCacheStartingBefore(viewParent);
|
|
14790
|
+
}
|
|
14791
|
+
return;
|
|
14792
|
+
}
|
|
14793
|
+
// Note: there was a consideration to save the `viewContainer` value together with `cacheListIndex` value.
|
|
14794
|
+
// However, it is like it is on purpose. We want to find *current* mapped ancestor for the `viewNode`.
|
|
14795
|
+
// This is an essential step to verify if the cache is still up-to-date.
|
|
14796
|
+
// Actually, we could save `viewContainer` and compare it to current tracked ancestor to quickly invalidate.
|
|
14797
|
+
// But this kinda happens with our flow and other assumptions around caching list index anyway.
|
|
14798
|
+
let viewContainer = viewNode.parent;
|
|
14799
|
+
while(!this._cachedMapping.has(viewContainer)){
|
|
14800
|
+
viewContainer = viewContainer.parent;
|
|
14801
|
+
}
|
|
14802
|
+
this._clearCacheFromIndex(viewContainer, cacheListIndex);
|
|
14803
|
+
}
|
|
14804
|
+
/**
|
|
14805
|
+
* Clears all the cache in the cache list related to given `viewContainer`, starting from `index` (inclusive).
|
|
14806
|
+
*/ _clearCacheFromIndex(viewContainer, index) {
|
|
14807
|
+
if (index === 0) {
|
|
14808
|
+
// Don't remove the first entry in the cache (this entry is always a mapping between view offset 0 <-> model offset 0,
|
|
14809
|
+
// and it is a default value that is always expected to be in the cache list).
|
|
14810
|
+
//
|
|
14811
|
+
// The cache mechanism may ask to clear from index `0` in a case where a 0-model-length view element (UI element or empty
|
|
14812
|
+
// attribute element) was at the beginning of tracked element. In such scenario, the view element is mapped through
|
|
14813
|
+
// `nodeToCacheListIndex` to index `0`.
|
|
14814
|
+
index = 1;
|
|
14815
|
+
}
|
|
14816
|
+
// Cache is always available here because we initialize it just before adding a listener that fires `_clearCacheFromIndex()`.
|
|
14817
|
+
const cache = this._cachedMapping.get(viewContainer);
|
|
14818
|
+
const cacheItem = cache.cacheList[index - 1];
|
|
14819
|
+
if (!cacheItem) {
|
|
14820
|
+
return;
|
|
14821
|
+
}
|
|
14822
|
+
cache.maxModelOffset = cacheItem.modelOffset;
|
|
14823
|
+
// Remove from cache all `CacheItem`s that are "after" the index to clear from.
|
|
14824
|
+
const clearedItems = cache.cacheList.splice(index);
|
|
14825
|
+
// For each removed item, make sure to also remove it from `cacheMap` and clear related entry in `_nodeToCacheListIndex`.
|
|
14826
|
+
for (const item of clearedItems){
|
|
14827
|
+
cache.cacheMap.delete(item.modelOffset);
|
|
14828
|
+
const viewNode = item.viewPosition.nodeBefore;
|
|
14829
|
+
this._nodeToCacheListIndex.delete(viewNode);
|
|
14830
|
+
}
|
|
14831
|
+
}
|
|
14832
|
+
/**
|
|
14833
|
+
* Finds a cache item in the given cache list, which `modelOffset` is closest (but smaller or equal) to given `offset`.
|
|
14834
|
+
*
|
|
14835
|
+
* Since `cacheList` is a sorted array, this uses binary search to retrieve the item quickly.
|
|
14836
|
+
*/ _findInCacheList(cacheList, offset) {
|
|
14837
|
+
let start = 0;
|
|
14838
|
+
let end = cacheList.length - 1;
|
|
14839
|
+
let index = end - start >> 1;
|
|
14840
|
+
let item = cacheList[index];
|
|
14841
|
+
while(start < end){
|
|
14842
|
+
if (item.modelOffset < offset) {
|
|
14843
|
+
start = index + 1;
|
|
14844
|
+
} else {
|
|
14845
|
+
end = index - 1;
|
|
14846
|
+
}
|
|
14847
|
+
index = start + (end - start >> 1);
|
|
14848
|
+
item = cacheList[index];
|
|
14849
|
+
}
|
|
14850
|
+
return item.modelOffset <= offset ? item : cacheList[index - 1];
|
|
14851
|
+
}
|
|
14852
|
+
}
|
|
14314
14853
|
|
|
14315
14854
|
/**
|
|
14316
14855
|
* Manages a list of consumable values for the {@link module:engine/model/item~Item model items}.
|
|
@@ -16071,10 +16610,13 @@ Node$1.prototype.is = function(type) {
|
|
|
16071
16610
|
if (isStartBlockSelected(startBlock, range)) {
|
|
16072
16611
|
yield startBlock;
|
|
16073
16612
|
}
|
|
16074
|
-
|
|
16613
|
+
const treewalker = range.getWalker();
|
|
16614
|
+
for (const value of treewalker){
|
|
16075
16615
|
const block = value.item;
|
|
16076
16616
|
if (value.type == 'elementEnd' && isUnvisitedTopBlock(block, visited, range)) {
|
|
16077
16617
|
yield block;
|
|
16618
|
+
} else if (value.type == 'elementStart' && block.is('model:element') && block.root.document.model.schema.isBlock(block)) {
|
|
16619
|
+
treewalker.jumpTo(Position._createAt(block, 'end'));
|
|
16078
16620
|
}
|
|
16079
16621
|
}
|
|
16080
16622
|
const endBlock = getParentBlock$1(range.end, visited);
|
|
@@ -17910,7 +18452,7 @@ function cloneNodes(nodes) {
|
|
|
17910
18452
|
}
|
|
17911
18453
|
|
|
17912
18454
|
/**
|
|
17913
|
-
* @license Copyright (c) 2003-
|
|
18455
|
+
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
|
|
17914
18456
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
17915
18457
|
*/ /**
|
|
17916
18458
|
* @module engine/conversion/conversionhelpers
|
|
@@ -20064,7 +20606,7 @@ function getFromAttributeCreator(config) {
|
|
|
20064
20606
|
}
|
|
20065
20607
|
|
|
20066
20608
|
/**
|
|
20067
|
-
* @license Copyright (c) 2003-
|
|
20609
|
+
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
|
|
20068
20610
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
20069
20611
|
*/ /**
|
|
20070
20612
|
* @module engine/model/utils/autoparagraphing
|
|
@@ -25268,7 +25810,7 @@ function* _getUpcastDefinition(model, view, upcastAlso) {
|
|
|
25268
25810
|
}
|
|
25269
25811
|
|
|
25270
25812
|
/**
|
|
25271
|
-
* @license Copyright (c) 2003-
|
|
25813
|
+
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
|
|
25272
25814
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
25273
25815
|
*/ /* eslint-disable @typescript-eslint/no-unused-vars */ /**
|
|
25274
25816
|
* @module engine/model/operation/operation
|
|
@@ -25287,6 +25829,9 @@ function* _getUpcastDefinition(model, view, upcastAlso) {
|
|
|
25287
25829
|
/**
|
|
25288
25830
|
* {@link module:engine/model/batch~Batch Batch} to which the operation is added or `null` if the operation is not
|
|
25289
25831
|
* added to any batch yet.
|
|
25832
|
+
*
|
|
25833
|
+
* Note, that a {@link #isDocumentOperation non-document operation} has this property always set to `null`, and is never added
|
|
25834
|
+
* to any batch.
|
|
25290
25835
|
*/ batch;
|
|
25291
25836
|
/**
|
|
25292
25837
|
* Base operation constructor.
|
|
@@ -25315,7 +25860,7 @@ function* _getUpcastDefinition(model, view, upcastAlso) {
|
|
|
25315
25860
|
json.__className = this.constructor.className;
|
|
25316
25861
|
// Remove reference to the parent `Batch` to avoid circular dependencies.
|
|
25317
25862
|
delete json.batch;
|
|
25318
|
-
//
|
|
25863
|
+
// This can be derived from `baseVersion` so we can remove it.
|
|
25319
25864
|
delete json.isDocumentOperation;
|
|
25320
25865
|
return json;
|
|
25321
25866
|
}
|
|
@@ -29139,8 +29684,12 @@ LivePosition.prototype.is = function(type) {
|
|
|
29139
29684
|
* @param operation An operation to add.
|
|
29140
29685
|
* @returns The added operation.
|
|
29141
29686
|
*/ addOperation(operation) {
|
|
29142
|
-
operation.
|
|
29143
|
-
|
|
29687
|
+
if (operation.isDocumentOperation) {
|
|
29688
|
+
// Store only document operations in the batch.
|
|
29689
|
+
// Non-document operations are temporary and should be discarded after they are applied.
|
|
29690
|
+
operation.batch = this;
|
|
29691
|
+
this.operations.push(operation);
|
|
29692
|
+
}
|
|
29144
29693
|
return operation;
|
|
29145
29694
|
}
|
|
29146
29695
|
}
|
|
@@ -33446,7 +33995,7 @@ function replaceEntireContentWithParagraph(writer, selection) {
|
|
|
33446
33995
|
}
|
|
33447
33996
|
|
|
33448
33997
|
/**
|
|
33449
|
-
* @license Copyright (c) 2003-
|
|
33998
|
+
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
|
|
33450
33999
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
33451
34000
|
*/ /**
|
|
33452
34001
|
* @module engine/model/utils/getselectedcontent
|
|
@@ -35367,6 +35916,26 @@ function getSearchRange(start, isForward) {
|
|
|
35367
35916
|
}
|
|
35368
35917
|
}
|
|
35369
35918
|
|
|
35919
|
+
/**
|
|
35920
|
+
* Touch events observer.
|
|
35921
|
+
*
|
|
35922
|
+
* Note that this observer is not available by default. To make it available it needs to be added to
|
|
35923
|
+
* {@link module:engine/view/view~View} by {@link module:engine/view/view~View#addObserver} method.
|
|
35924
|
+
*/ class TouchObserver extends DomEventObserver {
|
|
35925
|
+
/**
|
|
35926
|
+
* @inheritDoc
|
|
35927
|
+
*/ domEventType = [
|
|
35928
|
+
'touchstart',
|
|
35929
|
+
'touchend',
|
|
35930
|
+
'touchmove'
|
|
35931
|
+
];
|
|
35932
|
+
/**
|
|
35933
|
+
* @inheritDoc
|
|
35934
|
+
*/ onDomEvent(domEvent) {
|
|
35935
|
+
this.fire(domEvent.type, domEvent);
|
|
35936
|
+
}
|
|
35937
|
+
}
|
|
35938
|
+
|
|
35370
35939
|
/**
|
|
35371
35940
|
* View upcast writer. It provides a set of methods used to manipulate non-semantic view trees.
|
|
35372
35941
|
*
|
|
@@ -35690,7 +36259,7 @@ function getSearchRange(start, isForward) {
|
|
|
35690
36259
|
}
|
|
35691
36260
|
|
|
35692
36261
|
/**
|
|
35693
|
-
* @license Copyright (c) 2003-
|
|
36262
|
+
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
|
|
35694
36263
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
35695
36264
|
*/ /**
|
|
35696
36265
|
* @module engine/view/styles/utils
|
|
@@ -37946,5 +38515,5 @@ function* convertAttributes(attributes, converter) {
|
|
|
37946
38515
|
}
|
|
37947
38516
|
}
|
|
37948
38517
|
|
|
37949
|
-
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 };
|
|
38518
|
+
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, TouchObserver, 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 };
|
|
37950
38519
|
//# sourceMappingURL=index.js.map
|