/**
* @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/
/**
* @module engine/conversion/definition-based-converters
*/
import AttributeElement from '../view/attributeelement';
import ViewContainerElement from '../view/containerelement';
import buildModelConverter from './buildmodelconverter';
import buildViewConverter from './buildviewconverter';
/**
* Helper for creating model to view converter from model's element
* to {@link module:engine/view/containerelement~ContainerElement}.
*
* By defining conversion as simple model element to view element conversion using simplified definition:
*
* modelElementToViewContainerElement( {
* model: 'heading1',
* view: 'h1',
* }, [ editor.editing.modelToView, editor.data.modelToView ] );
*
* Or defining full-flavored view object:
*
* modelElementToViewContainerElement( {
* model: 'heading1',
* view: {
* name: 'h1',
* class: [ 'header', 'article-header' ],
* attribute: {
* data-header: 'level-1',
* }
* },
* }, [ editor.editing.modelToView, editor.data.modelToView ] );
*
* Above will generate the following view element:
*
*
...
*
* @param {module:engine/conversion/definition-based-converters~ConverterDefinition} definition A conversion configuration.
* @param {Array.} dispatchers
*/
export function modelElementToViewContainerElement( definition, dispatchers ) {
const { model: modelElement, viewDefinition } = parseConverterDefinition( definition );
buildModelConverter()
.for( ...dispatchers )
.fromElement( modelElement )
.toElement( () => ViewContainerElement.createFromDefinition( viewDefinition ) );
}
/**
* Helper for creating view to model element converter. It will convert also all matched view elements defined in
* `acceptAlso` property. The `model` property is used as model element name.
*
* Conversion from model to view might be defined as simple one to one conversion:
*
* viewToModelElement( { model: 'heading1', view: 'h1' }, [ dispatcher ] );
*
* As a full-flavored definition:
*
* viewToModelElement( {
* model: 'heading1',
* view: {
* name: 'p',
* attribute: {
* 'data-heading': 'true'
* },
* // You may need to use a high-priority listener to catch elements
* // which are handled by other (usually – more generic) converters too.
* priority: 'high'
* }
* }, [ editor.data.viewToModel ] );
*
* or with `acceptAlso` property to match many elements:
*
* viewToModelElement( {
* model: 'heading1',
* view: 'h1',
* acceptAlso: [
* { name: 'p', attribute: { 'data-heading': 'level1' }, priority: 'high' },
* { name: 'h2', class: 'heading-main' },
* { name: 'div', style: { 'font-weight': 'bold', font-size: '24px' } }
* ]
* }, [ editor.data.viewToModel ] );
*
* The above example will convert an existing view elements:
*
* A heading
* Another heading
* Paragraph-like heading
* Another non-semantic header
*
* into `heading1` model elements so in model it will be represented as:
*
* A heading
* Another heading
* Paragraph-like heading
* Another non-semantic header
*
* @param {module:engine/conversion/definition-based-converters~ConverterDefinition} definition A conversion configuration.
* @param {Array.} dispatchers
*/
export function viewToModelElement( definition, dispatchers ) {
const { model: modelElement, viewDefinitions } = parseConverterDefinition( definition );
const converter = prepareViewConverter( dispatchers, viewDefinitions );
converter.toElement( modelElement );
}
/**
* Helper for creating model to view converter from model's attribute
* to {@link module:engine/view/attributeelement~AttributeElement}.
*
* By defining conversion as simple model element to view element conversion using simplified definition:
*
* modelAttributeToViewAttributeElement( 'bold', {
* model: 'true',
* view: 'strong',
* }, [ editor.editing.modelToView, editor.data.modelToView ] );
*
* Or defining full-flavored view object:
*
* modelAttributeToViewAttributeElement( 'fontSize', {
* model: 'big',
* view: {
* name: 'span',
* style: {
* 'font-size': '1.2em'
* }
* },
* }, [ editor.editing.modelToView, editor.data.modelToView ] );
*
* Above will generate the following view element for model's attribute `fontSize` with a `big` value set:
*
* ...
*
* @param {String} attributeName The name of the model attribute which should be converted.
* @param {module:engine/conversion/definition-based-converters~ConverterDefinition} definition A conversion configuration.
* @param {Array.} dispatchers
*/
export function modelAttributeToViewAttributeElement( attributeName, definition, dispatchers ) {
const { model: attributeValue, viewDefinition } = parseConverterDefinition( definition );
buildModelConverter()
.for( ...dispatchers )
.fromAttribute( attributeName )
.toElement( value => {
if ( value != attributeValue ) {
return;
}
return AttributeElement.createFromDefinition( viewDefinition );
} );
}
/**
* Helper for creating view to model converter from view to model attribute. It will convert also all matched view elements defined in
* `acceptAlso` property. The `model` property is used as model's attribute value to match.
*
* Conversion from model to view might be defined as simple one to one conversion:
*
* viewToModelAttribute( 'bold', { model: true, view: 'strong' }, [ dispatcher ] );
*
* As a full-flavored definition:
*
* viewToModelAttribute( 'fontSize', {
* model: 'big',
* view: {
* name: 'span',
* style: {
* 'font-size': '1.2em'
* }
* }
* }, [ editor.data.viewToModel ] );
*
* or with `acceptAlso` property to match many elements:
*
* viewToModelAttribute( 'fontSize', {
* model: 'big',
* view: {
* name: 'span',
* class: 'text-big'
* },
* acceptAlso: [
* { name: 'span', attribute: { 'data-size': 'big' } },
* { name: 'span', class: [ 'font', 'font-huge' ] },
* { name: 'span', style: { font-size: '18px' } }
* ]
* }, [ editor.data.viewToModel ] );
*
* The above example will convert an existing view elements:
*
*
* An example text with some big elements:
* two,
* four
*
*
* into `fontSize` model attribute with 'big' value set so it will be represented:
*
*
* An example text with some big elements:
* <$text fontSize="big>one$text>,
* <$text fontSize="big>two$text>,
* <$text fontSize="big>three$text>,
* <$text fontSize="big>four$text>
*
*
* @param {String} attributeName Attribute name to which convert.
* @param {module:engine/conversion/definition-based-converters~ConverterDefinition} definition A conversion configuration.
* @param {Array.} dispatchers
*/
export function viewToModelAttribute( attributeName, definition, dispatchers ) {
const { model: attributeValue, viewDefinitions } = parseConverterDefinition( definition );
const converter = prepareViewConverter( dispatchers, viewDefinitions );
converter.toAttribute( () => ( {
key: attributeName,
value: attributeValue
} ) );
}
// Prepares a {@link module:engine/conversion/definition-based-converters~ConverterDefinition definition object} for building converters.
//
// @param {module:engine/conversion/definition-based-converters~ConverterDefinition} definition An object that defines view to model
// and model to view conversion.
// @returns {Object}
function parseConverterDefinition( definition ) {
const model = definition.model;
const view = definition.view;
const viewDefinition = typeof view == 'string' ? { name: view } : view;
const viewDefinitions = Array.from( definition.acceptsAlso ? definition.acceptsAlso : [] );
viewDefinitions.push( viewDefinition );
return { model, viewDefinition, viewDefinitions };
}
// Helper method for preparing a view converter from passed view definitions.
//
// @param {Array.} dispatchers
// @param {Array.} viewDefinitions
// @returns {module:engine/conversion/buildviewconverter~ViewConverterBuilder}
function prepareViewConverter( dispatchers, viewDefinitions ) {
const converter = buildViewConverter().for( ...dispatchers );
for ( const viewDefinition of viewDefinitions ) {
converter.from( Object.assign( {}, viewDefinition ) );
if ( viewDefinition.priority ) {
converter.withPriority( viewDefinition.priority );
}
}
return converter;
}
/**
* Defines conversion details. It is used in configuration based converters:
* - {@link module:engine/conversion/definition-based-converters~modelAttributeToViewAttributeElement}
* - {@link module:engine/conversion/definition-based-converters~modelElementToViewContainerElement}
* - {@link module:engine/conversion/definition-based-converters~viewToModelAttribute}
* - {@link module:engine/conversion/definition-based-converters~viewToModelElement}
*
* @typedef {Object} ConverterDefinition
* @property {String} model Defines to model conversion. When using to element conversion
* ({@link module:engine/conversion/definition-based-converters~viewToModelElement}
* and {@link module:engine/conversion/definition-based-converters~modelElementToViewContainerElement})
* it defines element name. When using to attribute conversion
* ({@link module:engine/conversion/definition-based-converters~viewToModelAttribute}
* and {@link module:engine/conversion/definition-based-converters~modelAttributeToViewAttributeElement})
* it defines attribute value to which it is converted.
* @property {module:engine/view/viewelementdefinition~ViewElementDefinition} view Defines model to view conversion and is also used
* in view to model conversion pipeline.
* @property {Array.} acceptAlso An array with all matched elements that
* view to model conversion should also accepts.
*/