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 the 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.
// 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:
// 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.