Class

Mapper (engine/conversion)

@ckeditor/ckeditor5-engine/src/conversion/mapper

class

Maps elements, positions and markers between the view and the model.

The instance of the Mapper used for the editing pipeline is available in editor.editing.mapper.

Mapper uses bound elements to find corresponding elements and positions, so, to get proper results, all model elements should be bound.

To map complex model to/from view relations, you may provide custom callbacks for modelToViewPosition event and viewToModelPosition event that are fired whenever a position mapping request occurs. Those events are fired by toViewPosition and toModelPosition methods. Mapper adds it's own default callbacks with 'lowest' priority. To override default Mapper mapping, add custom callback with higher priority and stop the event.

Filtering

Properties

  • _elementToMarkerNames : Map

    private

    View element to model marker names mapping.

    This is reverse to _markerNameToElements map.

  • _markerNameToElements : Map

    private

    Model marker name to view elements mapping.

    Keys are Strings while values are Sets with view elements. One marker (name) can be mapped to multiple elements.

  • _modelToViewMapping : WeakMap

    private

    Model element to view element mapping.

  • _unboundMarkerNames : Set.<Marker>

    private

    Stores marker names of markers which has changed due to unbinding a view element (so it is assumed that the view element has been removed, moved or renamed).

  • _viewToModelLengthCallbacks : Map

    private

    A map containing callbacks between view element names and functions evaluating length of view elements in model.

  • _viewToModelMapping : WeakMap

    private

    View element to model element mapping.

Methods

  • constructor()

    Creates an instance of the mapper.

  • bindElementToMarker( element, name )

    Binds given marker name with given view element. The element will be added to the current set of elements bound with given marker name.

    Parameters

    element : Element

    Element to bind.

    name : String

    Marker name.

  • bindElements( modelElement, viewElement )

    Marks model and view elements as corresponding. Corresponding elements can be retrieved by using the toModelElement and toViewElement methods. The information that elements are bound is also used to translate positions.

    Parameters

    modelElement : Element

    Model element.

    viewElement : Element

    View element.

  • clearBindings()

    Removes all model to view and view to model bindings.

  • delegate( events ) → EmitterMixinDelegateChain

    mixed

    Delegates selected events to another Emitter. For instance:

    emitterA.delegate( 'eventX' ).to( emitterB );
    emitterA.delegate( 'eventX', 'eventY' ).to( emitterC );
    

    then eventX is delegated (fired by) emitterB and emitterC along with data:

    emitterA.fire( 'eventX', data );
    

    and eventY is delegated (fired by) emitterC along with data:

    emitterA.fire( 'eventY', data );

    Parameters

    events : String

    Event names that will be delegated to another emitter.

    Returns

    EmitterMixinDelegateChain
  • findMappedViewAncestor( viewPosition ) → Element

    For given viewPosition, finds and returns the closest ancestor of this position that has a mapping to the model.

    Parameters

    viewPosition : Position

    Position for which mapped ancestor should be found.

    Returns

    Element
  • findPositionIn( viewParent, expectedOffset ) → Position

    Finds the position in the view node (or its children) with the expected model offset.

    Example:

    <p>fo<b>bar</b>bom</p> -> expected offset: 4
    
    findPositionIn( p, 4 ):
    <p>|fo<b>bar</b>bom</p> -> expected offset: 4, actual offset: 0
    <p>fo|<b>bar</b>bom</p> -> expected offset: 4, actual offset: 2
    <p>fo<b>bar</b>|bom</p> -> expected offset: 4, actual offset: 5 -> we are too far
    
    findPositionIn( b, 4 - ( 5 - 3 ) ):
    <p>fo<b>|bar</b>bom</p> -> expected offset: 2, actual offset: 0
    <p>fo<b>bar|</b>bom</p> -> expected offset: 2, actual offset: 3 -> we are too far
    
    findPositionIn( bar, 2 - ( 3 - 3 ) ):
    We are in the text node so we can simple find the offset.
    <p>fo<b>ba|r</b>bom</p> -> expected offset: 2, actual offset: 2 -> position found

    Parameters

    viewParent : Element

    Tree view element in which we are looking for the position.

    expectedOffset : Number

    Expected offset.

    Returns

    Position

    Found position.

  • fire( eventOrInfo, [ args ] ) → *

    mixed

    Fires an event, executing all callbacks registered for it.

    The first parameter passed to callbacks is an EventInfo object, followed by the optional args provided in the fire() method call.

    Parameters

    eventOrInfo : String | EventInfo

    The name of the event or EventInfo object if event is delegated.

    [ args ] : *

    Additional arguments to be passed to the callbacks.

    Returns

    *

    By default the method returns undefined. However, the return value can be changed by listeners through modification of the evt.return's property (the event info is the first param of every callback).

  • flushUnboundMarkerNames() → Array.<String>

    Returns all marker names of markers which has 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.

    Returns

    Array.<String>
  • getModelLength( viewNode ) → Number

    Gets the length of the view element in the model.

    The length is calculated as follows:

    • if length mapping callback is provided for given viewNode it is used to evaluate model length (viewNode is used as first and only parameter passed to the callback),
    • length of a text node is equal to the length of it's data,
    • length of a ui element is equal to 0,
    • length of a mapped element is equal to 1,
    • length of a not-mapped element is equal to the length of it's children.

    Examples:

    foo                          -> 3 // Text length is equal to it's 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.

    Parameters

    viewNode : Element

    View node.

    Returns

    Number

    Length of the node in the tree model.

  • listenTo( emitter, event, callback, [ options ] = { [options.priority] } )

    mixed

    Registers a callback function to be executed when an event is fired in a specific (emitter) object.

    Events can be grouped in namespaces using :. When namespaced event is fired, it additionally fires all callbacks for that namespace.

    // myEmitter.on( ... ) is a shorthand for myEmitter.listenTo( myEmitter, ... ).
    myEmitter.on( 'myGroup', genericCallback );
    myEmitter.on( 'myGroup:myEvent', specificCallback );
    
    // genericCallback is fired.
    myEmitter.fire( 'myGroup' );
    // both genericCallback and specificCallback are fired.
    myEmitter.fire( 'myGroup:myEvent' );
    // genericCallback is fired even though there are no callbacks for "foo".
    myEmitter.fire( 'myGroup:foo' );
    

    An event callback can stop the event and set the return value of the fire method.

    Parameters

    emitter : Emitter

    The object that fires the event.

    event : String

    The name of the event.

    callback : function

    The function to be called on event.

    [ options ] : Object

    Additional options.

    Properties
    [ options.priority ] : PriorityString | Number

    The priority of this event callback. The higher the priority value the sooner the callback will be fired. Events having the same priority are called in the order they were added.

    Defaults to 'normal'

    Defaults to {}

  • markerNameToElements( name ) → Set.<Element> | null

    Gets all view elements bound to the given marker name.

    Parameters

    name : String

    Marker name.

    Returns

    Set.<Element> | null

    View elements bound with given marker name or null if no elements are bound to given marker name.

  • off( event, callback )

    mixed

    Stops executing the callback on the given event. Shorthand for this.stopListening( this, event, callback ).

    Parameters

    event : String

    The name of the event.

    callback : function

    The function to stop being called.

  • on( event, callback, [ options ] = { [options.priority] } )

    mixed

    Registers a callback function to be executed when an event is fired.

    Shorthand for this.listenTo( this, event, callback, options ) (it makes the emitter listen on itself).

    Parameters

    event : String

    The name of the event.

    callback : function

    The function to be called on event.

    [ options ] : Object

    Additional options.

    Properties
    [ options.priority ] : PriorityString | Number

    The priority of this event callback. The higher the priority value the sooner the callback will be fired. Events having the same priority are called in the order they were added.

    Defaults to 'normal'

    Defaults to {}

  • once( event, callback, [ options ] = { [options.priority] } )

    mixed

    Registers a callback function to be executed on the next time the event is fired only. This is similar to calling on followed by off in the callback.

    Parameters

    event : String

    The name of the event.

    callback : function

    The function to be called on event.

    [ options ] : Object

    Additional options.

    Properties
    [ options.priority ] : PriorityString | Number

    The priority of this event callback. The higher the priority value the sooner the callback will be fired. Events having the same priority are called in the order they were added.

    Defaults to 'normal'

    Defaults to {}

  • registerViewToModelLength( viewElementName, lengthCallback )

    Registers a callback that evaluates the length in the model of a view element with 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 view element in model.

    // 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 );

    Parameters

    viewElementName : String

    Name of view element for which callback is registered.

    lengthCallback : function

    Function return a length of view element instance in model.

  • stopDelegating( [ event ], [ emitter ] )

    mixed

    Stops delegating events. It can be used at different levels:

    • To stop delegating all events.
    • To stop delegating a specific event to all emitters.
    • To stop delegating a specific event to a specific emitter.

    Parameters

    [ event ] : String

    The name of the event to stop delegating. If omitted, stops it all delegations.

    [ emitter ] : Emitter

    (requires event) The object to stop delegating a particular event to. If omitted, stops delegation of event to all emitters.

  • stopListening( [ emitter ], [ event ], [ callback ] )

    mixed

    Stops listening for events. It can be used at different levels:

    • To stop listening to a specific callback.
    • To stop listening to a specific event.
    • To stop listening to all events fired by a specific object.
    • To stop listening to all events fired by all objects.

    Parameters

    [ emitter ] : Emitter

    The object to stop listening to. If omitted, stops it for all objects.

    [ event ] : String

    (Requires the emitter) The name of the event to stop listening to. If omitted, stops it for all events from emitter.

    [ callback ] : function

    (Requires the event) The function to be removed from the call list for the given event.

  • toModelElement( viewElement ) → Element | undefined

    Gets the corresponding model element.

    Note: UIElement does not have corresponding element in model.

    Parameters

    viewElement : Element

    View element.

    Returns

    Element | undefined

    Corresponding model element or undefined if not found.

  • toModelPosition( viewPosition ) → Position

    Gets the corresponding model position.

    Parameters

    viewPosition : Position

    View position.

    Returns

    Position

    Corresponding model position.

    Fires

  • toModelRange( viewRange ) → Range

    Gets the corresponding model range.

    Parameters

    viewRange : Range

    View range.

    Returns

    Range

    Corresponding model range.

  • toViewElement( modelElement ) → Element | undefined

    Gets the corresponding view element.

    Parameters

    modelElement : Element

    Model element.

    Returns

    Element | undefined

    Corresponding view element or undefined if not found.

  • toViewPosition( modelPosition, [ options ] = { [options.isPhantom] } ) → Position

    Gets the corresponding view position.

    Parameters

    modelPosition : Position

    Model position.

    [ options ] : Object

    Additional options for position mapping process.

    Properties
    [ options.isPhantom ] : Boolean

    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.

    Defaults to false

    Returns

    Position

    Corresponding view position.

    Fires

  • toViewRange( modelRange ) → Range

    Gets the corresponding view range.

    Parameters

    modelRange : Range

    Model range.

    Returns

    Range

    Corresponding view range.

  • unbindElementFromMarkerName( element, name )

    Unbinds an element from given marker name.

    Parameters

    element : Element

    Element to unbind.

    name : String

    Marker name.

  • unbindModelElement( modelElement )

    Unbinds given model element from the map.

    Note: model-to-view binding will be removed, if it existed. However, corresponding view-to-model binding will be removed only if view element is still bound to 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.

    Parameters

    modelElement : Element

    Model element to unbind.

  • unbindViewElement( viewElement )

    Unbinds given 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 passed viewElement.

    This behavior lets for re-binding model element to another view element without fear of losing the new binding when the previously bound view element is unbound.

    Parameters

    viewElement : Element

    View element to unbind.

  • _addEventListener( event, callback, [ options ] = { [options.priority] } )

    protected mixed

    Adds callback to emitter for given event.

    Parameters

    event : String

    The name of the event.

    callback : function

    The function to be called on event.

    [ options ] : Object

    Additional options.

    Properties
    [ options.priority ] : PriorityString | Number

    The priority of this event callback. The higher the priority value the sooner the callback will be fired. Events having the same priority are called in the order they were added.

    Defaults to 'normal'

    Defaults to {}

  • _removeEventListener( event, callback )

    protected mixed

    Removes callback from emitter for given event.

    Parameters

    event : String

    The name of the event.

    callback : function

    The function to stop being called.

  • _moveViewPositionToTextNode( viewPosition ) → Position

    private

    Because we prefer positions in text nodes over positions next to text node moves view position to the text node if it was next to it.

    <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

    Parameters

    viewPosition : Position

    Position potentially next to text node.

    Returns

    Position

    Position in text node if possible.

  • _toModelOffset( viewParent, viewOffset, viewBlock ) → Number

    private

    Calculates model offset based on the view position and the block element.

    Example:

    <p>foo<b>ba|r</b></p> // _toModelOffset( b, 2, p ) -> 5
    

    Is a sum of:

    <p>foo|<b>bar</b></p> // _toModelOffset( p, 3, p ) -> 3
    <p>foo<b>ba|r</b></p> // _toModelOffset( b, 2, b ) -> 2

    Parameters

    viewParent : Element

    Position parent.

    viewOffset : Number

    Position offset.

    viewBlock : Element

    Block used as a base to calculate offset.

    Returns

    Number

    Offset in the model.

Events

  • modelToViewPosition( eventInfo, data = { data.mapper } )

    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 model position and are expected to calculate view position. Calculated view position should be added as viewPosition value in data object that is passed as one of parameters to the event callback.

    // 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. "Phantom" model position is a position that points to a non-existing place in model. Such position might still be valid for conversion, though (it would point to a correct place in view when converted). One example of such situation is when a range is removed from model, there may be a need to map the range's end (which is no longer valid model position). To handle such situation, check data.isPhantom flag:

    // Assume that there is "customElement" model element and whenever 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: default mapping callback is provided with low priority setting and does not cancel the event, so it is possible to attach a custom callback after default callback and also use data.viewPosition calculated by default callback (for example to fix it).

    Note: 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 given model and view elements is unsolvable using just elements mapping and default algorithm. Also, the condition that checks if special case scenario happened should be as simple as possible.

    Parameters

    eventInfo : EventInfo

    An object containing information about the fired event.

    data : Object

    Data pipeline object that can store and pass data between callbacks. The callback should add viewPosition value to that object with calculated view position.

    Properties
    data.mapper : Mapper

    Mapper instance that fired the event.

  • viewToModelPosition( eventInfo, data = { data.mapper } )

    Fired for each view-to-model position mapping request. See event-modelToViewPosition.

    // 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: default mapping callback is provided with low priority setting and does not cancel the event, so it is possible to attach a custom callback after default callback and also use data.modelPosition calculated by default callback (for example to fix it).

    Note: 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 given model and view elements is unsolvable using just elements mapping and default algorithm. Also, the condition that checks if special case scenario happened should be as simple as possible.

    Parameters

    eventInfo : EventInfo

    An object containing information about the fired event.

    data : Object

    Data pipeline object that can store and pass data between callbacks. The callback should add modelPosition value to that object with calculated model position.

    Properties
    data.mapper : Mapper

    Mapper instance that fired the event.