@ckeditor/ckeditor5-engine
Version:
The editing engine of CKEditor 5 – the best browser-based rich text editor.
720 lines (719 loc) • 35 kB
TypeScript
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
/**
* @module engine/conversion/mapper
*/
import { ModelPosition } from '../model/position.js';
import { ModelRange } from '../model/range.js';
import { ViewPosition } from '../view/position.js';
import { ViewRange } from '../view/range.js';
import { type ViewDocumentFragment } from '../view/documentfragment.js';
import { type ViewElement } from '../view/element.js';
import { type ModelElement } from '../model/element.js';
import { type ModelDocumentFragment } from '../model/documentfragment.js';
import type { ViewNode } from '../view/node.js';
declare const Mapper_base: {
new (): import("@ckeditor/ckeditor5-utils").Emitter;
prototype: import("@ckeditor/ckeditor5-utils").Emitter;
};
/**
* Maps elements, positions and markers between the {@link module:engine/view/document~ViewDocument view} and
* the {@link module:engine/model/model model}.
*
* The instance of the Mapper used for the editing pipeline is available in
* {@link module:engine/controller/editingcontroller~EditingController#mapper `editor.editing.mapper`}.
*
* Mapper uses bound elements to find corresponding elements and positions, so, to get proper results,
* all model elements should be {@link module:engine/conversion/mapper~Mapper#bindElements bound}.
*
* To map the complex model to/from view relations, you may provide custom callbacks for the
* {@link module:engine/conversion/mapper~Mapper#event:modelToViewPosition modelToViewPosition event} and
* {@link module:engine/conversion/mapper~Mapper#event:viewToModelPosition viewToModelPosition event} that are fired whenever
* a position mapping request occurs.
* Those events are fired by the {@link module:engine/conversion/mapper~Mapper#toViewPosition toViewPosition}
* and {@link module:engine/conversion/mapper~Mapper#toModelPosition toModelPosition} methods. `Mapper` adds its own default callbacks
* with `'lowest'` priority. To override default `Mapper` mapping, add custom callback with higher priority and
* stop the event.
*/
export declare class Mapper extends /* #__PURE__ */ Mapper_base {
/**
* Model element to view element mapping.
*/
private _modelToViewMapping;
/**
* View element to model element mapping.
*/
private _viewToModelMapping;
/**
* A map containing callbacks between view element names and functions evaluating length of view elements
* in model.
*/
private _viewToModelLengthCallbacks;
/**
* Model marker name to view elements mapping.
*
* Keys are `String`s while values are `Set`s with {@link module:engine/view/element~ViewElement view elements}.
* One marker (name) can be mapped to multiple elements.
*/
private _markerNameToElements;
/**
* View element to model marker names mapping.
*
* This is reverse to {@link ~Mapper#_markerNameToElements} map.
*/
private _elementToMarkerNames;
/**
* The map of removed view elements with their current root (used for deferred unbinding).
*/
private _deferredBindingRemovals;
/**
* Stores marker names of markers which have changed due to unbinding a view element (so it is assumed that the view element
* has been removed, moved or renamed).
*/
private _unboundMarkerNames;
/**
* Manages dynamic cache for the `Mapper` to improve the performance.
*/
private _cache;
/**
* Creates an instance of the mapper.
*/
constructor();
/**
* Marks model and view elements as corresponding. Corresponding elements can be retrieved by using
* the {@link module:engine/conversion/mapper~Mapper#toModelElement toModelElement} and
* {@link module:engine/conversion/mapper~Mapper#toViewElement toViewElement} methods.
* The information that elements are bound is also used to translate positions.
*
* @param modelElement Model element.
* @param viewElement View element.
*/
bindElements(modelElement: ModelElement | ModelDocumentFragment, viewElement: ViewElement | ViewDocumentFragment): void;
/**
* Unbinds the given {@link module:engine/view/element~ViewElement view element} from the map.
*
* **Note:** view-to-model binding will be removed, if it existed. However, corresponding model-to-view binding
* will be removed only if model element is still bound to the passed `viewElement`.
*
* This behavior allows for re-binding model element to another view element without fear of losing the new binding
* when the previously bound view element is unbound.
*
* @param viewElement View element to unbind.
* @param options The options object.
* @param options.defer Controls whether the binding should be removed immediately or deferred until a
* {@link #flushDeferredBindings `flushDeferredBindings()`} call.
*/
unbindViewElement(viewElement: ViewElement, options?: {
defer?: boolean;
}): void;
/**
* Unbinds the given {@link module:engine/model/element~ModelElement model element} from the map.
*
* **Note:** the model-to-view binding will be removed, if it existed. However, the corresponding view-to-model binding
* will be removed only if the view element is still bound to the passed `modelElement`.
*
* This behavior lets for re-binding view element to another model element without fear of losing the new binding
* when the previously bound model element is unbound.
*
* @param modelElement Model element to unbind.
*/
unbindModelElement(modelElement: ModelElement): void;
/**
* Binds the given marker name with the given {@link module:engine/view/element~ViewElement view element}. The element
* will be added to the current set of elements bound with the given marker name.
*
* @param element Element to bind.
* @param name Marker name.
*/
bindElementToMarker(element: ViewElement, name: string): void;
/**
* Unbinds an element from given marker name.
*
* @param element Element to unbind.
* @param name Marker name.
*/
unbindElementFromMarkerName(element: ViewElement, name: string): void;
/**
* Returns all marker names of markers which have changed due to unbinding a view element (so it is assumed that the view element
* has been removed, moved or renamed) since the last flush. After returning, the marker names list is cleared.
*/
flushUnboundMarkerNames(): Array<string>;
/**
* Unbinds all deferred binding removals of view elements that in the meantime were not re-attached to some root or document fragment.
*
* See: {@link #unbindViewElement `unbindViewElement()`}.
*/
flushDeferredBindings(): void;
/**
* Removes all model to view and view to model bindings.
*/
clearBindings(): void;
/**
* Gets the corresponding model element.
*
* **Note:** {@link module:engine/view/uielement~ViewUIElement} does not have corresponding element in model.
*
* @label ELEMENT
* @param viewElement View element.
* @returns Corresponding model element or `undefined` if not found.
*/
toModelElement(viewElement: ViewElement): ModelElement | undefined;
/**
* Gets the corresponding model document fragment.
*
* @label DOCUMENT_FRAGMENT
* @param viewDocumentFragment View document fragment.
* @returns Corresponding model document fragment or `undefined` if not found.
*/
toModelElement(viewDocumentFragment: ViewDocumentFragment): ModelDocumentFragment | undefined;
/**
* Gets the corresponding view element.
*
* @label ELEMENT
* @param modelElement Model element.
* @returns Corresponding view element or `undefined` if not found.
*/
toViewElement(modelElement: ModelElement): ViewElement | undefined;
/**
* Gets the corresponding view document fragment.
*
* @label DOCUMENT_FRAGMENT
* @param modelDocumentFragment Model document fragment.
* @returns Corresponding view document fragment or `undefined` if not found.
*/
toViewElement(modelDocumentFragment: ModelDocumentFragment): ViewDocumentFragment | undefined;
/**
* Gets the corresponding model range.
*
* @param viewRange View range.
* @returns Corresponding model range.
*/
toModelRange(viewRange: ViewRange): ModelRange;
/**
* Gets the corresponding view range.
*
* @param modelRange Model range.
* @returns Corresponding view range.
*/
toViewRange(modelRange: ModelRange): ViewRange;
/**
* Gets the corresponding model position.
*
* @fires viewToModelPosition
* @param viewPosition View position.
* @returns Corresponding model position.
*/
toModelPosition(viewPosition: ViewPosition): ModelPosition;
/**
* Gets the corresponding view position.
*
* @fires modelToViewPosition
* @param modelPosition Model position.
* @param options Additional options for position mapping process.
* @param options.isPhantom Should be set to `true` if the model position to map is pointing to a place
* in model tree which no longer exists. For example, it could be an end of a removed model range.
* @returns Corresponding view position.
*/
toViewPosition(modelPosition: ModelPosition, options?: {
isPhantom?: boolean;
}): ViewPosition;
/**
* Gets all view elements bound to the given marker name.
*
* @param name Marker name.
* @returns View elements bound with the given marker name or `null`
* if no elements are bound to the given marker name.
*/
markerNameToElements(name: string): Set<ViewElement> | null;
/**
* **This method is deprecated and will be removed in one of the future CKEditor 5 releases.**
*
* **Using this method will turn off `Mapper` caching system and may degrade performance when operating on bigger documents.**
*
* Registers a callback that evaluates the length in the model of a view element with the given name.
*
* The callback is fired with one argument, which is a view element instance. The callback is expected to return
* a number representing the length of the view element in the model.
*
* ```ts
* // List item in view may contain nested list, which have other list items. In model though,
* // the lists are represented by flat structure. Because of those differences, length of list view element
* // may be greater than one. In the callback it's checked how many nested list items are in evaluated list item.
*
* function getViewListItemLength( element ) {
* let length = 1;
*
* for ( let child of element.getChildren() ) {
* if ( child.name == 'ul' || child.name == 'ol' ) {
* for ( let item of child.getChildren() ) {
* length += getViewListItemLength( item );
* }
* }
* }
*
* return length;
* }
*
* mapper.registerViewToModelLength( 'li', getViewListItemLength );
* ```
*
* @param viewElementName Name of view element for which callback is registered.
* @param lengthCallback Function return a length of view element instance in model.
* @deprecated
*/
registerViewToModelLength(viewElementName: string, lengthCallback: (element: ViewElement) => number): void;
/**
* For the given `viewPosition`, finds and returns the closest ancestor of this position that has a mapping to
* the model.
*
* @param viewPosition Position for which a mapped ancestor should be found.
*/
findMappedViewAncestor(viewPosition: ViewPosition): ViewElement;
/**
* Calculates model offset based on the view position and the block element.
*
* Example:
*
* ```html
* <p>foo<b>ba|r</b></p> // _toModelOffset( b, 2, p ) -> 5
* ```
*
* Is a sum of:
*
* ```html
* <p>foo|<b>bar</b></p> // _toModelOffset( p, 3, p ) -> 3
* <p>foo<b>ba|r</b></p> // _toModelOffset( b, 2, b ) -> 2
* ```
*
* @param viewParent Position parent.
* @param viewOffset Position offset.
* @param viewBlock Block used as a base to calculate offset.
* @returns Offset in the model.
*/
private _toModelOffset;
/**
* Gets the length of the view element in the model.
*
* The length is calculated as follows:
* * if a {@link #registerViewToModelLength length mapping callback} is provided for the given `viewNode`, it is used to
* evaluate the model length (`viewNode` is used as first and only parameter passed to the callback),
* * length of a {@link module:engine/view/text~ViewText text node} is equal to the length of its
* {@link module:engine/view/text~ViewText#data data},
* * length of a {@link module:engine/view/uielement~ViewUIElement ui element} is equal to 0,
* * length of a mapped {@link module:engine/view/element~ViewElement element} is equal to 1,
* * length of a non-mapped {@link module:engine/view/element~ViewElement element} is equal to the length of its children.
*
* Examples:
*
* ```
* foo -> 3 // Text length is equal to its data length.
* <p>foo</p> -> 1 // Length of an element which is mapped is by default equal to 1.
* <b>foo</b> -> 3 // Length of an element which is not mapped is a length of its children.
* <div><p>x</p><p>y</p></div> -> 2 // Assuming that <div> is not mapped and <p> are mapped.
* ```
*
* @param viewNode View node.
* @returns Length of the node in the tree model.
*/
getModelLength(viewNode: ViewNode | ViewDocumentFragment): number;
/**
* Finds the position in a view element or view document fragment node (or in its children) with the expected model offset.
*
* If the passed `viewContainer` is bound to model, `Mapper` will use caching mechanism to improve performance.
*
* @param viewContainer Tree view element in which we are looking for the position.
* @param modelOffset Expected offset.
* @returns Found position.
*/
findPositionIn(viewContainer: ViewElement | ViewDocumentFragment, modelOffset: number): ViewPosition;
/**
* Performs most of the logic for `Mapper#findPositionIn()`.
*
* It allows to start looking for the requested model offset from a given starting position, to enable caching. Using the cache,
* you can set the starting point and skip all the calculations that were already previously done.
*
* This method uses recursion to find positions inside deep structures. Example:
*
* ```
* <p>fo<b>bar</b>bom</p> -> target offset: 4
* <p>|fo<b>bar</b>bom</p> -> target offset: 4, traversed offset: 0
* <p>fo|<b>bar</b>bom</p> -> target offset: 4, traversed offset: 2
* <p>fo<b>bar</b>|bom</p> -> target offset: 4, traversed offset: 5 -> we are too far, look recursively in <b>.
*
* <p>fo<b>|bar</b>bom</p> -> target offset: 4, traversed offset: 2
* <p>fo<b>bar|</b>bom</p> -> target offset: 4, traversed offset: 5 -> we are too far, look inside "bar".
*
* <p>fo<b>ba|r</b>bom</p> -> target offset: 4, traversed offset: 2 -> position is inside text node at offset 4-2 = 2.
* ```
*
* @param startViewPosition View position to start looking from.
* @param startModelOffset Model offset related to `startViewPosition`.
* @param targetModelOffset Target model offset to find.
* @param viewContainer Mapped ancestor of `startViewPosition`. `startModelOffset` is the offset inside a model element or model
* document fragment mapped to `viewContainer`.
* @param useCache Whether `Mapper` should cache positions while traversing the view tree looking for `expectedModelOffset`.
* @returns View position mapped to `targetModelOffset`.
*/
private _findPositionStartingFrom;
/**
* Gets the length of the view element in the model and updates cache values after each view item it visits.
*
* See also {@link #getModelLength}.
*
* @param viewNode View node.
* @param viewContainer Ancestor of `viewNode` that is a mapped view element.
* @param modelOffset Model offset at which the `viewNode` starts.
* @returns Length of the node in the tree model.
*/
private _getModelLengthAndCache;
/**
* Because we prefer positions in the text nodes over positions next to text nodes, if the view position was next to a text node,
* it moves it into the text node instead.
*
* ```
* <p>[]<b>foo</b></p> -> <p>[]<b>foo</b></p> // do not touch if position is not directly next to text
* <p>foo[]<b>foo</b></p> -> <p>foo{}<b>foo</b></p> // move to text node
* <p><b>[]foo</b></p> -> <p><b>{}foo</b></p> // move to text node
* ```
*
* @param viewPosition Position potentially next to the text node.
* @returns Position in the text node if possible.
*/
private _moveViewPositionToTextNode;
}
declare const MapperCache_base: {
new (): import("@ckeditor/ckeditor5-utils").Emitter;
prototype: import("@ckeditor/ckeditor5-utils").Emitter;
};
/**
* Cache mechanism for {@link module:engine/conversion/mapper~Mapper Mapper}.
*
* `MapperCache` improves performance for model-to-view position mapping, which is the main `Mapper` task. Asking for a mapping is much
* more frequent than actually performing changes, and even if the change happens, we can still partially keep the cache. This makes
* caching a useful strategy for `Mapper`.
*
* `MapperCache` will store some data for view elements or view document fragments that are mapped by the `Mapper`. These view items
* are "tracked" by the `MapperCache`. For such view items, we will keep entries of model offsets inside their mapped counterpart. For
* the cached model offsets, we will keep a view position that is inside the tracked item. This allows us to either get the mapping
* instantly, or at least in less steps than when calculating it from the beginning.
*
* Important problem related to caching is invalidating the cache. The cache must be invalidated each time the tracked view item changes.
* Additionally, we should invalidate as small part of the cache as possible. Since all the logic is encapsulated inside `MapperCache`,
* the `MapperCache` listens to view items {@link module:engine/view/node~ViewNodeChangeEvent `change` event} and reacts to it.
* Then, it invalidates just the part of the cache that is "after" the changed part of the view.
*
* As mentioned, `MapperCache` currently is used only for model-to-view position mapping as it was much bigger problem than view-to-model
* mapping. However, it should be possible to use it also for view-to-model.
*
* The main assumptions regarding `MapperCache` are:
*
* * it is an internal tool, used by `Mapper`, transparent to the outside (no additional effort when developing a plugin or a converter),
* * it stores all the necessary data internally, which makes it easier to disable or debug,
* * it is optimized for initial downcast process (long insertions), which is crucial for editor init and data save,
* * it does not save all possible positions mapping for memory considerations, although it is a possible improvement, which may increase
* performance, as well as simplify some parts of the `MapperCache` logic.
*
* @internal
*/
export declare class MapperCache extends /* #__PURE__ */ MapperCache_base {
/**
* For every view element or document fragment tracked by `MapperCache`, it holds currently cached data, or more precisely,
* model offset to view position mappings. See also `MappingCache` and `CacheItem`.
*
* If an item is tracked by `MapperCache` it has an entry in this structure, so this structure can be used to check which items
* are tracked by `MapperCache`. When an item is no longer tracked, it is removed from this structure.
*
* Although `MappingCache` and `CacheItem` structures allows for caching any model offsets and view positions, we only cache
* values for model offsets that are after a view node. So, in essence, positions inside text nodes are not cached. However, it takes
* from one to at most a few steps, to get from a cached position to a position that is inside a view text node.
*
* Additionally, only one item per `modelOffset` is cached. There can be several view positions that map to the same `modelOffset`.
* Only the first save for `modelOffset` is stored.
*/
private _cachedMapping;
/**
* When `MapperCache` {@link ~MapperCache#save saves} view position -> model offset mapping, a `CacheItem` is inserted into certain
* `MappingCache#cacheList` at some index. Additionally, we store that index with the view node that is before the cached view position.
*
* This allows to quickly get a cache list item related to certain view node, and hence, for fast cache invalidation.
*
* For example, consider view: `<p>Some <strong>bold</strong> text.</p>`, where `<p>` is a view element tracked by `MapperCache`.
* If all `<p>` children were visited by `MapperCache`, then `<p>` cache list would have four items, related to following model offsets:
* `0`, `5`, `9`, `15`. Then, view node `"Some "` would have index `1`, `<strong>` index `2`, and `" text." index `3`.
*
* 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`
* (and view offset `0`), and it is not related to any node.
*/
private _nodeToCacheListIndex;
/**
* Callback fired whenever there is a direct or indirect children change in tracked view element or tracked view document fragment.
*
* This is specified as a property to make it easier to set as an event callback and to later turn off that event.
*/
private _invalidateOnChildrenChangeCallback;
/**
* Callback fired whenever a view text node directly or indirectly inside a tracked view element or tracked view document fragment
* changes its text data.
*
* This is specified as a property to make it easier to set as an event callback and to later turn off that event.
*/
private _invalidateOnTextChangeCallback;
/**
* Saves cache for given view position mapping <-> model offset mapping. The view position should be after a node (i.e. it cannot
* be the first position inside its parent, or in other words, `viewOffset` must be greater than `0`).
*
* Note, that if `modelOffset` for given `viewContainer` was already saved, the stored view position (i.e. parent+offset) will not
* be overwritten. However, it is important to still save it, as we still store additional data related to cached view positions.
*
* @param viewParent View position parent.
* @param viewOffset View position offset. Must be greater than `0`.
* @param viewContainer Tracked view position ascendant (it may be the direct parent of the view position).
* @param modelOffset Model offset in the model element or document fragment which is mapped to `viewContainer`.
*/
save(viewParent: ViewElement | ViewDocumentFragment, viewOffset: number, viewContainer: ViewElement | ViewDocumentFragment, modelOffset: number): void;
/**
* For given `modelOffset` inside a model element mapped to given `viewContainer`, it returns the closest saved cache item
* (view position and related model offset) to the requested one.
*
* It can be exactly the requested mapping, or it can be mapping that is the closest starting point to look for the requested mapping.
*
* `viewContainer` must be a view element or document fragment that is mapped by the {@link ~Mapper Mapper}.
*
* If `viewContainer` is not yet tracked by the `MapperCache`, it will be automatically tracked after calling this method.
*
* Note: this method will automatically "hoist" cached positions, i.e. it will return a position that is closest to the tracked element.
*
* For example, if `<p>` is tracked element, and `^` is cached position:
*
* ```
* <p>This is <strong>some <em>heavily <u>formatted</u>^</em></strong> text.</p>
* ```
*
* If this position would be returned, instead, a position directly in `<p>` would be returned:
*
* ```
* <p>This is <strong>some <em>heavily <u>formatted</u></em></strong>^ text.</p>
* ```
*
* Note, that `modelOffset` for both positions is the same.
*
* @param viewContainer Tracked view element or document fragment, which cache will be used.
* @param modelOffset Model offset in a model element or document fragment, which is mapped to `viewContainer`.
*/
getClosest(viewContainer: ViewElement | ViewDocumentFragment, modelOffset: number): CacheItem;
/**
* Starts tracking given `viewContainer`, which must be mapped to a model element or model document fragment.
*
* Note, that this method is automatically called by
* {@link module:engine/conversion/mapper~MapperCache#getClosest `MapperCache#getClosest()`} and there is no need to call it manually.
*
* This method initializes the cache for `viewContainer` and adds callbacks for
* {@link module:engine/view/node~ViewNodeChangeEvent `change` event} fired by `viewContainer`. `MapperCache` listens to `change` event
* on the tracked elements to invalidate the stored cache.
*/
startTracking(viewContainer: ViewElement | ViewDocumentFragment): CacheItem;
/**
* Stops tracking given `viewContainer`.
*
* It removes the cached data and stops listening to {@link module:engine/view/node~ViewNodeChangeEvent `change` event} on the
* `viewContainer`.
*/
stopTracking(viewContainer: ViewElement | ViewDocumentFragment): void;
/**
* Invalidates cache inside `viewParent`, starting from given `index` in that parent.
*
* This method may clear a bit more cache than just what was saved after given `index`, but it is guaranteed that at least it
* will invalidate everything after `index`.
*/
private _clearCacheInsideParent;
/**
* Clears all the cache for given tracked `viewContainer`.
*/
private _clearCacheAll;
/**
* Clears all the stored cache that is after given `viewNode`. The `viewNode` can be any node that is inside a tracked view element
* or view document fragment.
*
* In reality, this function may clear a bit more cache than just "starting after" `viewNode`, but it is guaranteed that at least
* all cache after `viewNode` is invalidated.
*/
private _clearCacheAfter;
/**
* Clears all the cache in the cache list related to given `viewContainer`, starting from `index` (inclusive).
*/
private _clearCacheFromCacheIndex;
/**
* Finds a cache item in the given cache list, which `modelOffset` is closest (but smaller or equal) to given `offset`.
*
* Since `cacheList` is a sorted array, this uses binary search to retrieve the item quickly.
*/
private _findInCacheList;
}
/**
* Fired for each model-to-view position mapping request. The purpose of this event is to enable custom model-to-view position
* mapping. Callbacks added to this event take {@link module:engine/model/position~ModelPosition model position} and are expected to
* calculate the {@link module:engine/view/position~ViewPosition view position}. The calculated view position should be added as
* a `viewPosition` value in the `data` object that is passed as one of parameters to the event callback.
*
* ```ts
* // Assume that "captionedImage" model element is converted to <img> and following <span> elements in view,
* // and the model element is bound to <img> element. Force mapping model positions inside "captionedImage" to that
* // <span> element.
* mapper.on( 'modelToViewPosition', ( evt, data ) => {
* const positionParent = modelPosition.parent;
*
* if ( positionParent.name == 'captionedImage' ) {
* const viewImg = data.mapper.toViewElement( positionParent );
* const viewCaption = viewImg.nextSibling; // The <span> element.
*
* data.viewPosition = new ViewPosition( viewCaption, modelPosition.offset );
*
* // Stop the event if other callbacks should not modify calculated value.
* evt.stop();
* }
* } );
* ```
*
* **Note:** keep in mind that sometimes a "phantom" model position is being converted. A "phantom" model position is
* a position that points to a nonexistent place in model. Such a position might still be valid for conversion, though
* (it would point to a correct place in the view when converted). One example of such a situation is when a range is
* removed from the model, there may be a need to map the range's end (which is no longer a valid model position). To
* handle such situations, check the `data.isPhantom` flag:
*
* ```ts
* // Assume that there is a "customElement" model element and whenever the position is before it,
* // we want to move it to the inside of the view element bound to "customElement".
* mapper.on( 'modelToViewPosition', ( evt, data ) => {
* if ( data.isPhantom ) {
* return;
* }
*
* // Below line might crash for phantom position that does not exist in model.
* const sibling = data.modelPosition.nodeBefore;
*
* // Check if this is the element we are interested in.
* if ( !sibling.is( 'element', 'customElement' ) ) {
* return;
* }
*
* const viewElement = data.mapper.toViewElement( sibling );
*
* data.viewPosition = new ViewPosition( sibling, 0 );
*
* evt.stop();
* } );
* ```
*
* **Note:** the default mapping callback is provided with a `low` priority setting and does not cancel the event, so it is possible to
* attach a custom callback after a default callback and also use `data.viewPosition` calculated by the default callback
* (for example to fix it).
*
* **Note:** the default mapping callback will not fire if `data.viewPosition` is already set.
*
* **Note:** these callbacks are called **very often**. For efficiency reasons, it is advised to use them only when position
* mapping between the given model and view elements is unsolvable by using just elements mapping and default algorithm.
* Also, the condition that checks if a special case scenario happened should be as simple as possible.
*
* @eventName ~Mapper#modelToViewPosition
*/
export type MapperModelToViewPositionEvent = {
name: 'modelToViewPosition';
args: [MapperModelToViewPositionEventData];
};
/**
* Data pipeline object that can store and pass data between callbacks. The callback should add
* the `viewPosition` value to that object with calculated the {@link module:engine/view/position~ViewPosition view position}.
*/
export type MapperModelToViewPositionEventData = {
/**
* Mapper instance that fired the event.
*/
mapper: Mapper;
/**
* The model position.
*/
modelPosition: ModelPosition;
/**
* The callback should add the `viewPosition` value to that object with calculated the
* {@link module:engine/view/position~ViewPosition view position}.
*/
viewPosition?: ViewPosition;
/**
* Should be set to `true` if the model position to map is pointing to a place
* in model tree which no longer exists. For example, it could be an end of a removed model range.
*/
isPhantom?: boolean;
};
/**
* Fired for each view-to-model position mapping request. See {@link module:engine/conversion/mapper~Mapper#event:modelToViewPosition}.
*
* ```ts
* // See example in `modelToViewPosition` event description.
* // This custom mapping will map positions from <span> element next to <img> to the "captionedImage" element.
* mapper.on( 'viewToModelPosition', ( evt, data ) => {
* const positionParent = viewPosition.parent;
*
* if ( positionParent.hasClass( 'image-caption' ) ) {
* const viewImg = positionParent.previousSibling;
* const modelImg = data.mapper.toModelElement( viewImg );
*
* data.modelPosition = new ModelPosition( modelImg, viewPosition.offset );
* evt.stop();
* }
* } );
* ```
*
* **Note:** the default mapping callback is provided with a `low` priority setting and does not cancel the event, so it is possible to
* attach a custom callback after the default callback and also use `data.modelPosition` calculated by the default callback
* (for example to fix it).
*
* **Note:** the default mapping callback will not fire if `data.modelPosition` is already set.
*
* **Note:** these callbacks are called **very often**. For efficiency reasons, it is advised to use them only when position
* mapping between the given model and view elements is unsolvable by using just elements mapping and default algorithm.
* Also, the condition that checks if special case scenario happened should be as simple as possible.
*
* @eventName ~Mapper#viewToModelPosition
*/
export type MapperViewToModelPositionEvent = {
name: 'viewToModelPosition';
args: [MapperViewToModelPositionEventData];
};
/**
* Data pipeline object that can store and pass data between callbacks. The callback should add
* `modelPosition` value to that object with calculated {@link module:engine/model/position~ModelPosition model position}.
*/
export type MapperViewToModelPositionEventData = {
/**
* Mapper instance that fired the event.
*/
mapper: Mapper;
/**
* The callback should add `modelPosition` value to that object with calculated
* {@link module:engine/model/position~ModelPosition model position}.
*/
modelPosition?: ModelPosition;
/**
* The view position.
*/
viewPosition: ViewPosition;
};
/**
* Informs that given `viewPosition` corresponds to given `modelOffset` (with the assumption that the model offset is in a model element
* mapped to the `viewPosition` parent or its ancestor).
*
* For example, for model `<paragraph>Some <$text bold=true>bold</$text> text</paragraph>` and view `<p>Some <strong>bold</strong> text</p>`
* and assuming `<paragraph>` and `<p>` are mapped, following example `CacheItem`s are possible:
*
* * `viewPosition` = `<p>`, 1; `modelOffset` = 5
* * `viewPosition` = `<strong>, 1; `modelOffset` = 9
* * `viewPosition` = `<p>`, 2; `modelOffset` = 9
* * `viewPosition` = `<p>`, 3; `modelOffset` = 14
*/
type CacheItem = {
viewPosition: ViewPosition;
modelOffset: number;
};
export {};