DowncastHelpers
Downcast conversion helper functions.
Learn more about downcast helpers.
Methods
-
constructor( dispatchers )inheritedmodule:engine/conversion/downcasthelpers~DowncastHelpers#constructor -
add( conversionHelper ) → thisinheritedmodule:engine/conversion/downcasthelpers~DowncastHelpers#addRegisters a conversion helper.
Note: See full usage example in the
conversion.for()method description.Parameters
conversionHelper : ( dispatcher: DowncastDispatcher ) => voidThe function to be called on event.
Returns
this
-
attributeToAttribute( config = { [config.converterPriority], config.model, config.view, [config.converterPriority], config.model, config.view } ) → thismodule:engine/conversion/downcasthelpers~DowncastHelpers#attributeToAttributeModel attribute to view attribute conversion helper.
This conversion results in adding an attribute to a view node, basing on an attribute from a model node. For example,
<imageInline src='foo.jpg'></imageInline>is converted to<img src='foo.jpg'></img>.editor.conversion.for( 'downcast' ).attributeToAttribute( { model: 'source', view: 'src' } ); editor.conversion.for( 'downcast' ).attributeToAttribute( { model: 'source', view: 'href', converterPriority: 'high' } ); editor.conversion.for( 'downcast' ).attributeToAttribute( { model: { name: 'imageInline', key: 'source' }, view: 'src' } ); editor.conversion.for( 'downcast' ).attributeToAttribute( { model: { name: 'styled', values: [ 'dark', 'light' ] }, view: { dark: { key: 'class', value: [ 'styled', 'styled-dark' ] }, light: { key: 'class', value: [ 'styled', 'styled-light' ] } } } ); editor.conversion.for( 'downcast' ).attributeToAttribute( { model: 'styled', view: modelAttributeValue => ( { key: 'class', value: 'styled-' + modelAttributeValue } ) } );Copy codeNote: Downcasting to a style property requires providing
valueas an object:editor.conversion.for( 'downcast' ).attributeToAttribute( { model: 'lineHeight', view: modelAttributeValue => ( { key: 'style', value: { 'line-height': modelAttributeValue, 'border-bottom': '1px dotted #ba2' } } ) } );Copy codeSee
conversion.for()to learn how to add a converter to the conversion process.Type parameters
TValues : extends string
Parameters
config : objectConversion configuration.
Properties[ config.converterPriority ] : PriorityStringConverter priority.
config.model : string | objectThe key of the attribute to convert from or a
{ key, values, [ name ] }object describing the attribute key, possible values and, optionally, an element name to convert from.config.view : string | DowncastAttributeDescriptor | DowncastAttributeCreatorFunctionA view attribute key, or a
{ key, value }object or a function that takes the model attribute value and downcast conversion API as parameters and returns a{ key, value }object. If thekeyis'class', thevaluecan be aStringor an array ofStrings. If thekeyis'style', thevalueis an object with key-value pairs. In other cases,valueis aString. Ifconfig.model.valuesis set,config.viewshould be an object assigning values fromconfig.model.valuesto{ key, value }objects or a functions.[ config.converterPriority ] : PriorityStringConverter priority.
config.model : objectThe key of the attribute to convert from or a
{ key, values, [ name ] }object describing the attribute key, possible values and, optionally, an element name to convert from.config.view : Record<TValues, DowncastAttributeDescriptor | DowncastAttributeCreatorFunction>A view attribute key, or a
{ key, value }object or a function that takes the model attribute value and downcast conversion API as parameters and returns a{ key, value }object. If thekeyis'class', thevaluecan be aStringor an array ofStrings. If thekeyis'style', thevalueis an object with key-value pairs. In other cases,valueis aString. Ifconfig.model.valuesis set,config.viewshould be an object assigning values fromconfig.model.valuesto{ key, value }objects or a functions.
Returns
this
-
attributeToElement( config = { [config.converterPriority], config.model, config.view, [config.converterPriority], config.model, config.view } ) → thismodule:engine/conversion/downcasthelpers~DowncastHelpers#attributeToElementModel attribute to view element conversion helper.
This conversion results in wrapping view nodes with a view attribute element. For example, a model text node with
"Foo"as data and theboldattribute becomes<strong>Foo</strong>in the view.editor.conversion.for( 'downcast' ).attributeToElement( { model: 'bold', view: 'strong' } ); editor.conversion.for( 'downcast' ).attributeToElement( { model: 'bold', view: 'b', converterPriority: 'high' } ); editor.conversion.for( 'downcast' ).attributeToElement( { model: 'invert', view: { name: 'span', classes: [ 'font-light', 'bg-dark' ] } } ); editor.conversion.for( 'downcast' ).attributeToElement( { model: { key: 'fontSize', values: [ 'big', 'small' ] }, view: { big: { name: 'span', styles: { 'font-size': '1.2em' } }, small: { name: 'span', styles: { 'font-size': '0.8em' } } } } ); editor.conversion.for( 'downcast' ).attributeToElement( { model: 'bold', view: ( modelAttributeValue, conversionApi ) => { const { writer } = conversionApi; return writer.createAttributeElement( 'span', { style: 'font-weight:' + modelAttributeValue } ); } } ); editor.conversion.for( 'downcast' ).attributeToElement( { model: { key: 'color', name: '$text' }, view: ( modelAttributeValue, conversionApi ) => { const { writer } = conversionApi; return writer.createAttributeElement( 'span', { style: 'color:' + modelAttributeValue } ); } } );Copy codeSee
conversion.for()to learn how to add a converter to the conversion process.Type parameters
TValues : extends string
Parameters
config : objectConversion configuration.
Properties[ config.converterPriority ] : PriorityStringConverter priority.
config.model : string | objectThe key of the attribute to convert from or a
{ key, values }object.valuesis an array ofStrings with possible values if the model attribute is an enumerable.config.view : ViewElementDefinition | DowncastAttributeElementCreatorFunctionA view element definition or a function that takes the model attribute value and downcast conversion API as parameters and returns a view attribute element. If
config.model.valuesis given,config.viewshould be an object assigning values fromconfig.model.valuesto view element definitions or functions.[ config.converterPriority ] : PriorityStringConverter priority.
config.model : objectThe key of the attribute to convert from or a
{ key, values }object.valuesis an array ofStrings with possible values if the model attribute is an enumerable.config.view : Record<TValues, ViewElementDefinition | DowncastAttributeElementCreatorFunction>A view element definition or a function that takes the model attribute value and downcast conversion API as parameters and returns a view attribute element. If
config.model.valuesis given,config.viewshould be an object assigning values fromconfig.model.valuesto view element definitions or functions.
Returns
this
-
elementToElement( config = { [config.converterPriority], config.model, config.view } ) → thismodule:engine/conversion/downcasthelpers~DowncastHelpers#elementToElementModel element to view element conversion helper.
This conversion results in creating a view element. For example, model
<paragraph>Foo</paragraph>becomes<p>Foo</p>in the view.editor.conversion.for( 'downcast' ).elementToElement( { model: 'paragraph', view: 'p' } ); editor.conversion.for( 'downcast' ).elementToElement( { model: 'paragraph', view: 'div', converterPriority: 'high' } ); editor.conversion.for( 'downcast' ).elementToElement( { model: 'fancyParagraph', view: { name: 'p', classes: 'fancy' } } ); editor.conversion.for( 'downcast' ).elementToElement( { model: 'heading', view: ( modelElement, conversionApi ) => { const { writer } = conversionApi; return writer.createContainerElement( 'h' + modelElement.getAttribute( 'level' ) ); } } );Copy codeThe element-to-element conversion supports the reconversion mechanism. It can be enabled by using either the
attributesor thechildrenprops on a model description. You will find a couple examples below.In order to reconvert an element if any of its direct children have been added or removed, use the
childrenproperty on amodeldescription. For example, this model:<box> <paragraph>Some text.</paragraph> </box>Copy codewill be converted into this structure in the view:
<div class="box" data-type="single"> <p>Some text.</p> </div>Copy codeBut if more items were inserted in the model:
<box> <paragraph>Some text.</paragraph> <paragraph>Other item.</paragraph> </box>Copy codeit will be converted into this structure in the view (note the element
data-typechange):<div class="box" data-type="multiple"> <p>Some text.</p> <p>Other item.</p> </div>Copy codeSuch a converter would look like this (note that the
paragraphelements are converted separately):editor.conversion.for( 'downcast' ).elementToElement( { model: { name: 'box', children: true }, view: ( modelElement, conversionApi ) => { const { writer } = conversionApi; return writer.createContainerElement( 'div', { class: 'box', 'data-type': modelElement.childCount == 1 ? 'single' : 'multiple' } ); } } );Copy codeIn order to reconvert element if any of its attributes have been updated, use the
attributesproperty on amodeldescription. For example, this model:<heading level="2">Some text.</heading>Copy codewill be converted into this structure in the view:
<h2>Some text.</h2>Copy codeBut if the
headingelement'slevelattribute has been updated to3for example, then it will be converted into this structure in the view:<h3>Some text.</h3>Copy codeSuch a converter would look as follows:
editor.conversion.for( 'downcast' ).elementToElement( { model: { name: 'heading', attributes: 'level' }, view: ( modelElement, conversionApi ) => { const { writer } = conversionApi; return writer.createContainerElement( 'h' + modelElement.getAttribute( 'level' ) ); } } );Copy codeSee
conversion.for()to learn how to add a converter to the conversion process.You can read more about the element-to-element conversion in the downcast conversion guide.
Parameters
config : objectConversion configuration.
Properties[ config.converterPriority ] : PriorityStringConverter priority.
config.model : string | objectThe description or a name of the model element to convert.
config.view : ViewElementDefinition | DowncastElementCreatorFunctionA view element definition or a function that takes the model element and downcast conversion API as parameters and returns a view container element.
Returns
this
-
elementToStructure( config = { [config.converterPriority], config.model, config.view } ) → thismodule:engine/conversion/downcasthelpers~DowncastHelpers#elementToStructureThe model element to view structure (several elements) conversion helper.
This conversion results in creating a view structure with one or more slots defined for the child nodes. For example, a model
<table>may become this structure in the view:<figure class="table"> <table> <tbody>${ slot for table rows }</tbody> </table> </figure>Copy codeThe children of the model's
<table>element will be inserted into the<tbody>element. If theelementToElement()helper was used, the children would be inserted into the<figure>.Imagine a table feature where for this model structure:
<table headingRows="1"> <tableRow> ... table cells 1 ... </tableRow> <tableRow> ... table cells 2 ... </tableRow> <tableRow> ... table cells 3 ... </tableRow> <caption>Caption text</caption> </table>Copy codewe want to generate this view structure:
<figure class="table"> <table> <thead> <tr> ... table cells 1 ... </tr> </thead> <tbody> <tr> ... table cells 2 ... </tr> <tr> ... table cells 3 ... </tr> </tbody> </table> <figcaption>Caption text</figcaption> </figure>Copy codeThe converter has to take the
headingRowsattribute into consideration when allocating the<tableRow>elements into the<tbody>and<thead>elements. Hence, we need two slots and need to define proper filter callbacks for them.Additionally, all elements other than
<tableRow>should be placed outside the<table>tag. In the example above, this will handle the table caption.Such a converter would look like this:
editor.conversion.for( 'downcast' ).elementToStructure( { model: { name: 'table', attributes: [ 'headingRows' ] }, view: ( modelElement, conversionApi ) => { const { writer } = conversionApi; const figureElement = writer.createContainerElement( 'figure', { class: 'table' } ); const tableElement = writer.createContainerElement( 'table' ); writer.insert( writer.createPositionAt( figureElement, 0 ), tableElement ); const headingRows = modelElement.getAttribute( 'headingRows' ) || 0; if ( headingRows > 0 ) { const tableHead = writer.createContainerElement( 'thead' ); const headSlot = writer.createSlot( node => node.is( 'element', 'tableRow' ) && node.index < headingRows ); writer.insert( writer.createPositionAt( tableElement, 'end' ), tableHead ); writer.insert( writer.createPositionAt( tableHead, 0 ), headSlot ); } if ( headingRows < tableUtils.getRows( table ) ) { const tableBody = writer.createContainerElement( 'tbody' ); const bodySlot = writer.createSlot( node => node.is( 'element', 'tableRow' ) && node.index >= headingRows ); writer.insert( writer.createPositionAt( tableElement, 'end' ), tableBody ); writer.insert( writer.createPositionAt( tableBody, 0 ), bodySlot ); } const restSlot = writer.createSlot( node => !node.is( 'element', 'tableRow' ) ); writer.insert( writer.createPositionAt( figureElement, 'end' ), restSlot ); return figureElement; } } );Copy codeNote: The children of a model element that's being converted must be allocated in the same order in the view in which they are placed in the model.
See
conversion.for()to learn how to add a converter to the conversion process.Parameters
config : objectConversion configuration. *
Properties[ config.converterPriority ] : PriorityStringConverter priority.
config.model : string | objectThe description or a name of the model element to convert.
config.view : DowncastElementCreatorFunctionA function that takes the model element and downcast conversion API as parameters and returns a view container element with slots for model child nodes to be converted into.
Returns
this
-
markerToData( config = { [config.converterPriority], config.model, [config.view] } ) → thismodule:engine/conversion/downcasthelpers~DowncastHelpers#markerToDataModel marker converter for data downcast.
This conversion creates a representation for model marker boundaries in the view:
- If the marker boundary is before or after a model element, a view attribute is set on a corresponding view element.
- In other cases, a view element with the specified tag name is inserted at the corresponding view position.
Typically, the marker names use the
group:uniqueId:otherDataconvention. For example:comment:e34zfk9k2n459df53sjl34:zx32c. The default configuration for this conversion is that the first part is thegrouppart and the rest of the marker name becomes thenamepart.Tag and attribute names and values are generated from the marker name:
- The templates for attributes are
data-[group]-start-before="[name]",data-[group]-start-after="[name]",data-[group]-end-before="[name]"anddata-[group]-end-after="[name]". - The templates for view elements are
<[group]-start name="[name]">and<[group]-end name="[name]">.
Attributes mark whether the given marker's start or end boundary is before or after the given element. The
data-[group]-start-beforeanddata-[group]-end-afterattributes are favored. The other two are used when the former two cannot be used.The conversion configuration can take a function that will generate different group and name parts. If such a function is set as the
config.viewparameter, it is passed a marker name and it is expected to return an object with two properties:groupandname. If the function returns a falsy value, the conversion will not take place.Basic usage:
// Using the default conversion. // In this case, all markers with names starting with 'comment:' will be converted. // The `group` parameter will be set to `comment`. // The `name` parameter will be the rest of the marker name (without the `:`). editor.conversion.for( 'dataDowncast' ).markerToData( { model: 'comment' } );Copy codeAn example of a view that may be generated by this conversion (assuming a marker with the name
comment:commentId:uidmarked by[]):// Model: <paragraph>Foo[bar</paragraph> <imageBlock src="abc.jpg"></imageBlock>] // View: <p>Foo<comment-start name="commentId:uid"></comment-start>bar</p> <figure data-comment-end-after="commentId:uid" class="image"><img src="abc.jpg" /></figure>Copy codeIn the example above, the comment starts before "bar" and ends after the image.
If the
namepart is empty, the following view may be generated:<p>Foo <myMarker-start></myMarker-start>bar</p> <figure data-myMarker-end-after="" class="image"><img src="abc.jpg" /></figure>Copy codeNote: A situation where some markers have the
namepart and some do not, is incorrect and should be avoided.Examples where
data-group-start-afteranddata-group-end-beforeare used:// Model: <blockQuote>[]<paragraph>Foo</paragraph></blockQuote> // View: <blockquote><p data-group-end-before="name" data-group-start-before="name">Foo</p></blockquote>Copy codeSimilarly, when a marker is collapsed after the last element:
// Model: <blockQuote><paragraph>Foo</paragraph>[]</blockQuote> // View: <blockquote><p data-group-end-after="name" data-group-start-after="name">Foo</p></blockquote>Copy codeWhen there are multiple markers from the same group stored in the same attribute of the same element, their name parts are put together in the attribute value, for example:
data-group-start-before="name1,name2,name3".Other examples of usage:
// Using a custom function which is the same as the default conversion: editor.conversion.for( 'dataDowncast' ).markerToData( { model: 'comment', view: markerName => ( { group: 'comment', name: markerName.substr( 8 ) // Removes 'comment:' part. } ) } ); // Using the converter priority: editor.conversion.for( 'dataDowncast' ).markerToData( { model: 'comment', view: markerName => ( { group: 'comment', name: markerName.substr( 8 ) // Removes 'comment:' part. } ), converterPriority: 'high' } );Copy codeThis kind of conversion is useful for saving data into the database, so it should be used in the data conversion pipeline.
See the
conversion.for()API guide to learn how to add a converter to the conversion process.Parameters
config : objectConversion configuration.
Properties[ config.converterPriority ] : PriorityStringConverter priority.
config.model : stringThe name of the model marker (or the model marker group) to convert.
[ config.view ] : DowncastMarkerDataCreatorFunctionA function that takes the model marker name and downcast conversion API as the parameters and returns an object with the
groupandnameproperties.
Returns
this
-
markerToElement( config = { [config.converterPriority], config.model, config.view } ) → thismodule:engine/conversion/downcasthelpers~DowncastHelpers#markerToElementModel marker to view element conversion helper.
Note: This method should be used mainly for editing the downcast and it is recommended to use the
#markerToData()helper instead.This helper may produce invalid HTML code (e.g. a span between table cells). It should only be used when you are sure that the produced HTML will be semantically correct.
This conversion results in creating a view element on the boundaries of the converted marker. If the converted marker is collapsed, only one element is created. For example, a model marker set like this:
<paragraph>F[oo b]ar</paragraph>becomes<p>F<span data-marker="search"></span>oo b<span data-marker="search"></span>ar</p>in the view.editor.conversion.for( 'editingDowncast' ).markerToElement( { model: 'search', view: 'marker-search' } ); editor.conversion.for( 'editingDowncast' ).markerToElement( { model: 'search', view: 'search-result', converterPriority: 'high' } ); editor.conversion.for( 'editingDowncast' ).markerToElement( { model: 'search', view: { name: 'span', attributes: { 'data-marker': 'search' } } } ); editor.conversion.for( 'editingDowncast' ).markerToElement( { model: 'search', view: ( markerData, conversionApi ) => { const { writer } = conversionApi; return writer.createUIElement( 'span', { 'data-marker': 'search', 'data-start': markerData.isOpening } ); } } );Copy codeIf a function is passed as the
config.viewparameter, it will be used to generate both boundary elements. The function receives thedataobject and downcast conversion API as a parameters and should return an instance of the view UI element. Thedataobject andconversionApiare passed from event-addMarker. Additionally, thedata.isOpeningparameter is passed, which is set totruefor the marker start boundary element, andfalsefor the marker end boundary element.See
conversion.for()to learn how to add a converter to the conversion process.Parameters
config : objectConversion configuration.
Properties[ config.converterPriority ] : PriorityStringConverter priority.
config.model : stringThe name of the model marker (or model marker group) to convert.
config.view : ViewElementDefinition | DowncastMarkerElementCreatorFunctionA view element definition or a function that takes the model marker data and downcast conversion API as a parameters and returns a view UI element.
Returns
this
-
markerToHighlight( config = { [config.converterPriority], config.model, config.view } ) → thismodule:engine/conversion/downcasthelpers~DowncastHelpers#markerToHighlightModel marker to highlight conversion helper.
This conversion results in creating a highlight on view nodes. For this kind of conversion, the
DowncastHighlightDescriptorshould be provided.For text nodes, a
<span>ViewAttributeElementis created and it wraps all text nodes in the converted marker range. For example, a model marker set like this:<paragraph>F[oo b]ar</paragraph>becomes<p>F<span class="comment">oo b</span>ar</p>in the view.ViewContainerElementmay provide a custom way of handling highlight. Most often, the element itself is given classes and attributes described in the highlight descriptor (instead of being wrapped in<span>). For example, a model marker set like this:[<imageInline src="foo.jpg"></imageInline>]becomes<img src="foo.jpg" class="comment"></img>in the view.For container elements, the conversion is two-step. While the converter processes the highlight descriptor and passes it to a container element, it is the container element instance itself that applies values from the highlight descriptor. So, in a sense, the converter takes care of stating what should be applied on what, while the element decides how to apply that.
editor.conversion.for( 'downcast' ).markerToHighlight( { model: 'comment', view: { classes: 'comment' } } ); editor.conversion.for( 'downcast' ).markerToHighlight( { model: 'comment', view: { classes: 'comment' }, converterPriority: 'high' } ); editor.conversion.for( 'downcast' ).markerToHighlight( { model: 'comment', view: ( data, conversionApi ) => { // Assuming that the marker name is in a form of comment:commentType:commentId. const [ , commentType, commentId ] = data.markerName.split( ':' ); return { classes: [ 'comment', 'comment-' + commentType ], attributes: { 'data-comment-id': commentId } }; } } );Copy codeIf a function is passed as the
config.viewparameter, it will be used to generate the highlight descriptor. The function receives thedataobject and downcast conversion API as the parameters and should return a highlight descriptor. Thedataobject properties are passed from event-addMarker.See
conversion.for()to learn how to add a converter to the conversion process.Parameters
config : objectConversion configuration.
Properties[ config.converterPriority ] : PriorityStringConverter priority.
config.model : stringThe name of the model marker (or model marker group) to convert.
config.view : DowncastHighlightDescriptor | DowncastHighlightDescriptorCreatorFunctionA highlight descriptor that will be used for highlighting or a function that takes the model marker data and downcast conversion API as a parameters and returns a highlight descriptor.
Returns
this