|
|
@@ -7,9 +7,6 @@ import ModelRange from '../model/range';
|
|
|
import ModelSelection from '../model/selection';
|
|
|
import ModelElement from '../model/element';
|
|
|
|
|
|
-import ViewContainerElement from '../view/containerelement';
|
|
|
-import ViewUIElement from '../view/uielement';
|
|
|
-import ViewElement from '../view/element';
|
|
|
import ViewAttributeElement from '../view/attributeelement';
|
|
|
import ViewRange from '../view/range';
|
|
|
import DocumentSelection from '../model/documentselection';
|
|
|
@@ -29,7 +26,7 @@ import cloneDeep from '@ckeditor/ckeditor5-utils/src/lib/lodash/cloneDeep';
|
|
|
*
|
|
|
* downcastElementToElement( { model: 'paragraph', view: 'p' } );
|
|
|
*
|
|
|
- * downcastElementToElement( { model: 'paragraph', view: 'p' }, 'high' );
|
|
|
+ * downcastElementToElement( { model: 'paragraph', view: 'div', priority: 'high' } );
|
|
|
*
|
|
|
* downcastElementToElement( {
|
|
|
* model: 'fancyParagraph',
|
|
|
@@ -41,33 +38,24 @@ import cloneDeep from '@ckeditor/ckeditor5-utils/src/lib/lodash/cloneDeep';
|
|
|
*
|
|
|
* downcastElementToElement( {
|
|
|
* model: 'heading',
|
|
|
- * view: ( modelItem, consumable, conversionApi ) => {
|
|
|
- * const viewWriter = conversionApi.writer;
|
|
|
- *
|
|
|
- * return viewWriter.createContainerElement( 'h' + modelItem.getAttribute( 'level' ) );
|
|
|
- * }
|
|
|
+ * view: ( modelElement, viewWriter ) => viewWriter.createContainerElement( 'h' + modelElement.getAttribute( 'level' ) )
|
|
|
* } );
|
|
|
*
|
|
|
* See {@link module:engine/conversion/conversion~Conversion#for} to learn how to add converter to conversion process.
|
|
|
*
|
|
|
* @param {Object} config Conversion configuration.
|
|
|
* @param {String} config.model Name of the model element to convert.
|
|
|
- * @param {String|module:engine/view/elementdefinition~ElementDefinition|Function} config.view View element name, or a
|
|
|
- * view element definition, or a function that will be provided with all the parameters of the dispatcher's
|
|
|
- * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:insert insert event}.
|
|
|
- * It's expected that the function returns a {@link module:engine/view/containerelement~ContainerElement}.
|
|
|
- * The view element will be used then in conversion.
|
|
|
- *
|
|
|
- * @param {module:utils/priorities~PriorityString} [priority='normal'] Converter priority.
|
|
|
+ * @param {module:engine/view/elementdefinition~ElementDefinition|Function} config.view View element definition or a function
|
|
|
+ * that takes model element and view writer as a parameters and returns a view container element.
|
|
|
* @returns {Function} Conversion helper.
|
|
|
*/
|
|
|
-export function downcastElementToElement( config, priority = 'normal' ) {
|
|
|
+export function downcastElementToElement( config ) {
|
|
|
config = cloneDeep( config );
|
|
|
|
|
|
- _normalizeToElementConfig( config, ViewContainerElement );
|
|
|
+ config.view = _normalizeToElementConfig( config.view, 'container' );
|
|
|
|
|
|
return dispatcher => {
|
|
|
- dispatcher.on( 'insert:' + config.model, insertElement( config.view ), { priority } );
|
|
|
+ dispatcher.on( 'insert:' + config.model, insertElement( config.view ), { priority: config.priority || 'normal' } );
|
|
|
};
|
|
|
}
|
|
|
|
|
|
@@ -77,75 +65,74 @@ export function downcastElementToElement( config, priority = 'normal' ) {
|
|
|
* This conversion results in wrapping view nodes in a view attribute element. For example, model text node with data
|
|
|
* `"Foo"` and `bold` attribute becomes `<strong>Foo</strong>` in the view.
|
|
|
*
|
|
|
- * downcastAttributeToElement( 'bold', { view: 'strong' } );
|
|
|
+ * downcastAttributeToElement( { model: 'bold', view: 'strong' } );
|
|
|
*
|
|
|
- * downcastAttributeToElement( 'bold', { view: 'strong' }, 'high' );
|
|
|
+ * downcastAttributeToElement( { model: 'bold', view: 'b', priority: 'high' } );
|
|
|
*
|
|
|
- * downcastAttributeToElement( 'bold', {
|
|
|
+ * downcastAttributeToElement( {
|
|
|
+ * model: 'invert',
|
|
|
* view: {
|
|
|
* name: 'span',
|
|
|
- * class: 'bold'
|
|
|
+ * class: [ 'font-light', 'bg-dark' ]
|
|
|
* }
|
|
|
* } );
|
|
|
*
|
|
|
- * downcastAttributeToElement( 'styled', {
|
|
|
- * model: 'dark',
|
|
|
+ * downcastAttributeToElement( {
|
|
|
+ * model: {
|
|
|
+ * key: 'fontSize',
|
|
|
+ * values: [ 'big', 'small' ]
|
|
|
+ * },
|
|
|
* view: {
|
|
|
- * name: 'span',
|
|
|
- * class: [ 'styled', 'styled-dark' ]
|
|
|
- * }
|
|
|
- * } );
|
|
|
- *
|
|
|
- * downcastAttributeToElement( 'fontSize', [
|
|
|
- * {
|
|
|
- * model: 'big',
|
|
|
- * view: {
|
|
|
+ * big: {
|
|
|
* name: 'span',
|
|
|
* style: {
|
|
|
* 'font-size': '1.2em'
|
|
|
* }
|
|
|
- * }
|
|
|
- * },
|
|
|
- * {
|
|
|
- * model: 'small',
|
|
|
- * view: {
|
|
|
+ * },
|
|
|
+ * small: {
|
|
|
* name: 'span',
|
|
|
* style: {
|
|
|
* 'font-size': '0.8em'
|
|
|
* }
|
|
|
* }
|
|
|
* }
|
|
|
- * ] );
|
|
|
- *
|
|
|
- * downcastAttributeToElement( 'bold', {
|
|
|
- * view: ( modelAttributeValue, data, consumable, conversionApi ) => {
|
|
|
- * const viewWriter = conversionApi.writer;
|
|
|
+ * } );
|
|
|
*
|
|
|
- * return viewWriter( 'span', { style: 'font-weight:' + modelAttributeValue } );
|
|
|
+ * downcastAttributeToElement( {
|
|
|
+ * model: 'bold',
|
|
|
+ * view: ( modelAttributeValue, viewWriter ) => {
|
|
|
+ * return viewWriter.createAttributeElement( 'span', { style: 'font-weight:' + modelAttributeValue } );
|
|
|
* }
|
|
|
* } );
|
|
|
*
|
|
|
* See {@link module:engine/conversion/conversion~Conversion#for} to learn how to add converter to conversion process.
|
|
|
*
|
|
|
- * @param {String} modelAttributeKey The key of the attribute to convert.
|
|
|
- * @param {Object|Array.<Object>} config Conversion configuration. It is possible to provide multiple configurations in an array.
|
|
|
- * @param {*} [config.model] The value of the converted model attribute for which the `view` property is defined.
|
|
|
- * If omitted, the configuration item will be used as a "default" configuration when no other item matches the attribute value.
|
|
|
- * @param {String|module:engine/view/elementdefinition~ElementDefinition|Function} config.view View element name,
|
|
|
- * or a view element definition, or a function that takes model element as a parameter and returns a view attribute element.
|
|
|
- * The view element will be used then in conversion.
|
|
|
- * @param {module:utils/priorities~PriorityString} [priority='normal'] Converter priority.
|
|
|
+ * @param {Object} config Conversion configuration.
|
|
|
+ * @param {String|Object} config.model Key of the attribute to convert from or a `{ key, values }` object. `values` is an array
|
|
|
+ * of `String`s with possible values if the model attribute is enumerable.
|
|
|
+ * @param {module:engine/view/elementdefinition~ElementDefinition|Function|Object} config.view View element definition or a function
|
|
|
+ * that takes model attribute value and view writer as parameters and returns a view attribute element. If `config.model.values` is
|
|
|
+ * given, `config.view` should be an object assigning values from `config.model.values` to view element definitions or functions.
|
|
|
+ * @param {module:utils/priorities~PriorityString} [config.priority='normal'] Converter priority.
|
|
|
* @returns {Function} Conversion helper.
|
|
|
*/
|
|
|
-export function downcastAttributeToElement( modelAttributeKey, config, priority = 'normal' ) {
|
|
|
+export function downcastAttributeToElement( config ) {
|
|
|
config = cloneDeep( config );
|
|
|
|
|
|
- _normalizeToElementConfig( config, ViewAttributeElement );
|
|
|
+ const modelKey = config.model.key ? config.model.key : config.model;
|
|
|
+
|
|
|
+ if ( config.model.values ) {
|
|
|
+ for ( const modelValue of config.model.values ) {
|
|
|
+ config.view[ modelValue ] = _normalizeToElementConfig( config.view[ modelValue ], 'attribute' );
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ config.view = _normalizeToElementConfig( config.view, 'attribute' );
|
|
|
+ }
|
|
|
|
|
|
- const elementCreator = _getCreatorForArrayConfig( config );
|
|
|
+ const elementCreator = _getFromAttributeCreator( config );
|
|
|
|
|
|
return dispatcher => {
|
|
|
- dispatcher.on( 'attribute:' + modelAttributeKey, wrap( elementCreator ), { priority } );
|
|
|
+ dispatcher.on( 'attribute:' + modelKey, wrap( elementCreator ), { priority: config.priority || 'normal' } );
|
|
|
};
|
|
|
}
|
|
|
|
|
|
@@ -155,74 +142,75 @@ export function downcastAttributeToElement( modelAttributeKey, config, priority
|
|
|
* This conversion results in adding an attribute on a view node, basing on an attribute from a model node. For example,
|
|
|
* `<image src='foo.jpg'></image>` is converted to `<img src='foo.jpg'></img>`.
|
|
|
*
|
|
|
- * downcastAttributeToAttribute( 'src' );
|
|
|
- *
|
|
|
- * downcastAttributeToAttribute( 'source', { view: 'src' } );
|
|
|
+ * downcastAttributeToAttribute( { model: 'source', view: 'src' } );
|
|
|
*
|
|
|
- * downcastAttributeToAttribute( 'source', { view: 'src' }, 'high' );
|
|
|
+ * downcastAttributeToAttribute( { model: 'source', view: 'href', priority: 'high' } );
|
|
|
*
|
|
|
- * downcastAttributeToAttribute( 'stylish', {
|
|
|
- * view: {
|
|
|
- * key: 'class',
|
|
|
- * value: 'styled'
|
|
|
- * }
|
|
|
+ * downcastAttributeToAttribute( {
|
|
|
+ * model: {
|
|
|
+ * name: 'image',
|
|
|
+ * key: 'source'
|
|
|
+ * },
|
|
|
+ * view: 'src'
|
|
|
* } );
|
|
|
*
|
|
|
- * downcastAttributeToAttribute( 'styled', {
|
|
|
- * model: 'dark',
|
|
|
+ * downcastAttributeToAttribute( {
|
|
|
+ * model: {
|
|
|
+ * name: 'styled',
|
|
|
+ * values: [ 'dark', 'light' ]
|
|
|
+ * },
|
|
|
* view: {
|
|
|
- * key: 'class',
|
|
|
- * value: 'styled styled-dark'
|
|
|
- * }
|
|
|
- * } );
|
|
|
- *
|
|
|
- * downcastAttributeToAttribute( 'style', [
|
|
|
- * {
|
|
|
- * model: 'dark',
|
|
|
- * view: {
|
|
|
+ * dark: {
|
|
|
* key: 'class',
|
|
|
- * value: 'styled-dark'
|
|
|
- * }
|
|
|
- * },
|
|
|
- * {
|
|
|
- * model: 'light',
|
|
|
- * view: {
|
|
|
+ * value: [ 'styled', 'styled-dark' ]
|
|
|
+ * },
|
|
|
+ * light: {
|
|
|
* key: 'class',
|
|
|
- * value: 'styled-light'
|
|
|
+ * value: [ 'styled', 'styled-light' ]
|
|
|
* }
|
|
|
* }
|
|
|
- * ] );
|
|
|
+ * } );
|
|
|
*
|
|
|
- * downcastAttributeToAttribute( 'style', {
|
|
|
- * view: attributeValue => ( { key: 'class', value: 'style-' + attributeValue } )
|
|
|
+ * downcastAttributeToAttribute( {
|
|
|
+ * model: 'styled',
|
|
|
+ * view: modelAttributeValue => ( { key: 'class', value: 'styled-' + modelAttributeValue } )
|
|
|
* } );
|
|
|
*
|
|
|
* See {@link module:engine/conversion/conversion~Conversion#for} to learn how to add converter to conversion process.
|
|
|
*
|
|
|
- * @param {String} modelAttributeKey The key of the attribute to convert.
|
|
|
- * @param {Object|Array.<Object>} [config] Conversion configuration. It is possible to provide multiple configurations in an array.
|
|
|
- * If not set, the conversion helper will assume 1-to-1 conversion, that is the view attribute key and view attribute value
|
|
|
- * will be same as model attribute key and model attribute value.
|
|
|
- * @param {*} [config.model] The value of the converted model attribute for which the `view` property is defined.
|
|
|
- * If `true` is provided, the configuration item will be used as a "default" configuration when no other item matches
|
|
|
- * the attribute value.
|
|
|
- * @param {String|Object|Function} [config.view] View attribute key, or an object with `key` and `value` properties (both `String`),
|
|
|
- * or a function that takes model attribute value and returns an object with `key` and `value` properties (both `String`).
|
|
|
- * If nothing is passed, the view attribute key and value will be equal to the model attribute key and value.
|
|
|
- * If a `String` is passed, it will be used as view attribute key and view attribute value will be equal to model attribute value.
|
|
|
- * If an object or a function returning an object is passed, its properties will be used to set view attribute key and value.
|
|
|
- * @param {module:utils/priorities~PriorityString} [priority='normal'] Converter priority.
|
|
|
+ * @param {Object} config Conversion configuration.
|
|
|
+ * @param {String|Object} config.model 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.
|
|
|
+ * @param {String|Object|Function} config.view View attribute key, or a `{ key, value }` object or a function that takes
|
|
|
+ * model attribute value and returns a `{ key, value }` object. If `key` is `'class'`, `value` can be a `String` or an
|
|
|
+ * array of `String`s. If `key` is `'style'`, `value` is an object with key-value pairs. In other cases, `value` is a `String`.
|
|
|
+ * If `config.model.values` is set, `config.view` should be an object assigning values from `config.model.values` to
|
|
|
+ * `{ key, value }` objects or a functions.
|
|
|
+ * @param {module:utils/priorities~PriorityString} [config.priority='normal'] Converter priority.
|
|
|
* @returns {Function} Conversion helper.
|
|
|
*/
|
|
|
-export function downcastAttributeToAttribute( modelAttributeKey, config = {}, priority = 'normal' ) {
|
|
|
+export function downcastAttributeToAttribute( config ) {
|
|
|
config = cloneDeep( config );
|
|
|
|
|
|
- _normalizeToAttributeConfig( modelAttributeKey, config );
|
|
|
+ const modelKey = config.model.key ? config.model.key : config.model;
|
|
|
+ let eventName = 'attribute:' + modelKey;
|
|
|
|
|
|
- const elementCreator = _getCreatorForArrayConfig( config );
|
|
|
+ if ( config.model.name ) {
|
|
|
+ eventName += ':' + config.model.name;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( config.model.values ) {
|
|
|
+ for ( const modelValue of config.model.values ) {
|
|
|
+ config.view[ modelValue ] = _normalizeToAttributeConfig( config.view[ modelValue ] );
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ config.view = _normalizeToAttributeConfig( config.view );
|
|
|
+ }
|
|
|
+
|
|
|
+ const elementCreator = _getFromAttributeCreator( config );
|
|
|
|
|
|
return dispatcher => {
|
|
|
- dispatcher.on( 'attribute:' + modelAttributeKey, changeAttribute( elementCreator ), { priority } );
|
|
|
+ dispatcher.on( eventName, changeAttribute( elementCreator ), { priority: config.priority || 'normal' } );
|
|
|
};
|
|
|
}
|
|
|
|
|
|
@@ -235,9 +223,7 @@ export function downcastAttributeToAttribute( modelAttributeKey, config = {}, pr
|
|
|
*
|
|
|
* downcastMarkerToElement( { model: 'search', view: 'marker-search' } );
|
|
|
*
|
|
|
- * downcastMarkerToElement( { model: 'search', view: 'marker-search' }, 'high' );
|
|
|
- *
|
|
|
- * downcastMarkerToElement( { model: 'search', view: new ViewUIElement( 'span', { data-marker: 'search' } ) } );
|
|
|
+ * downcastMarkerToElement( { model: 'search', view: 'search-result', priority: 'high' } );
|
|
|
*
|
|
|
* downcastMarkerToElement( {
|
|
|
* model: 'search',
|
|
|
@@ -251,8 +237,8 @@ export function downcastAttributeToAttribute( modelAttributeKey, config = {}, pr
|
|
|
*
|
|
|
* downcastMarkerToElement( {
|
|
|
* model: 'search',
|
|
|
- * view: ( data, conversionApi ) => {
|
|
|
- * return conversionApi.writer.createUIElement( 'span', { 'data-marker': 'search', 'data-start': data.isOpening } );
|
|
|
+ * view: ( markerData, viewWriter ) => {
|
|
|
+ * return viewWriter.createUIElement( 'span', { 'data-marker': 'search', 'data-start': markerData.isOpening } );
|
|
|
* }
|
|
|
* } );
|
|
|
*
|
|
|
@@ -269,20 +255,19 @@ export function downcastAttributeToAttribute( modelAttributeKey, config = {}, pr
|
|
|
*
|
|
|
* @param {Object} config Conversion configuration.
|
|
|
* @param {String} config.model Name of the model marker (or model marker group) to convert.
|
|
|
- * @param {module:engine/view/elementdefinition~ElementDefinition|Function} config.view View element definition
|
|
|
- * which will be used to build a view element for conversion or a function that takes model marker data as a parameter and
|
|
|
- * returns view element to use in conversion.
|
|
|
- * @param {module:utils/priorities~PriorityString} [priority='normal'] Converter priority.
|
|
|
+ * @param {module:engine/view/elementdefinition~ElementDefinition|Function} config.view View element definition or a function
|
|
|
+ * that takes model marker data as a parameter and returns view ui element.
|
|
|
+ * @param {module:utils/priorities~PriorityString} [config.priority='normal'] Converter priority.
|
|
|
* @returns {Function} Conversion helper.
|
|
|
*/
|
|
|
-export function downcastMarkerToElement( config, priority = 'normal' ) {
|
|
|
+export function downcastMarkerToElement( config ) {
|
|
|
config = cloneDeep( config );
|
|
|
|
|
|
- _normalizeToElementConfig( config, ViewUIElement );
|
|
|
+ config.view = _normalizeToElementConfig( config.view, 'ui' );
|
|
|
|
|
|
return dispatcher => {
|
|
|
- dispatcher.on( 'addMarker:' + config.model, insertUIElement( config.view ), { priority } );
|
|
|
- dispatcher.on( 'removeMarker:' + config.model, removeUIElement( config.view ), { priority } );
|
|
|
+ dispatcher.on( 'addMarker:' + config.model, insertUIElement( config.view ), { priority: config.priority || 'normal' } );
|
|
|
+ dispatcher.on( 'removeMarker:' + config.model, removeUIElement( config.view ), { priority: config.priority || 'normal' } );
|
|
|
};
|
|
|
}
|
|
|
|
|
|
@@ -307,7 +292,7 @@ export function downcastMarkerToElement( config, priority = 'normal' ) {
|
|
|
*
|
|
|
* downcastMarkerToHighlight( { model: 'comment', view: { class: 'comment' } } );
|
|
|
*
|
|
|
- * downcastMarkerToHighlight( { model: 'comment', view: { class: 'new-comment' } }, 'high' );
|
|
|
+ * downcastMarkerToHighlight( { model: 'comment', view: { class: 'new-comment' }, priority: 'high' } );
|
|
|
*
|
|
|
* downcastMarkerToHighlight( {
|
|
|
* model: 'comment',
|
|
|
@@ -322,9 +307,8 @@ export function downcastMarkerToElement( config, priority = 'normal' ) {
|
|
|
* } );
|
|
|
*
|
|
|
* If function is passed as `config.view` parameter, it will be used to generate highlight descriptor. The function
|
|
|
- * receives `data` and `conversionApi` objects as parameters and should return
|
|
|
- * {@link module:engine/conversion/downcast-converters~HighlightDescriptor}. The `data` and `conversionApi` objects are passed from
|
|
|
- * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:addMarker}.
|
|
|
+ * receives `data` object as parameter and should return a {@link module:engine/conversion/downcast-converters~HighlightDescriptor}.
|
|
|
+ * The `data` object properties are passed from {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:addMarker}.
|
|
|
*
|
|
|
* See {@link module:engine/conversion/conversion~Conversion#for} to learn how to add converter to conversion process.
|
|
|
*
|
|
|
@@ -332,139 +316,107 @@ export function downcastMarkerToElement( config, priority = 'normal' ) {
|
|
|
* @param {String} config.model Name of the model marker (or model marker group) to convert.
|
|
|
* @param {module:engine/conversion/downcast-converters~HighlightDescriptor|Function} config.view Highlight descriptor
|
|
|
* which will be used for highlighting or a function that takes model marker data as a parameter and returns a highlight descriptor.
|
|
|
- * @param {module:utils/priorities~PriorityString} [priority='normal'] Converter priority.
|
|
|
+ * @param {module:utils/priorities~PriorityString} [config.priority='normal'] Converter priority.
|
|
|
* @returns {Function} Conversion helper.
|
|
|
*/
|
|
|
-export function downcastMarkerToHighlight( config, priority = 'normal' ) {
|
|
|
+export function downcastMarkerToHighlight( config ) {
|
|
|
return dispatcher => {
|
|
|
- dispatcher.on( 'addMarker:' + config.model, highlightText( config.view ), { priority } );
|
|
|
- dispatcher.on( 'addMarker:' + config.model, highlightElement( config.view ), { priority } );
|
|
|
- dispatcher.on( 'removeMarker:' + config.model, removeHighlight( config.view ), { priority } );
|
|
|
+ dispatcher.on( 'addMarker:' + config.model, highlightText( config.view ), { priority: config.priority || 'normal' } );
|
|
|
+ dispatcher.on( 'addMarker:' + config.model, highlightElement( config.view ), { priority: config.priority || 'normal' } );
|
|
|
+ dispatcher.on( 'removeMarker:' + config.model, removeHighlight( config.view ), { priority: config.priority || 'normal' } );
|
|
|
};
|
|
|
}
|
|
|
|
|
|
-// Takes config and adds default parameters if they don't exist and normalizes other parameters to be used in downcast converters
|
|
|
-// for generating a view element.
|
|
|
+// Takes `config.view`, and if it is a {@link module:engine/view/elementdefinition~ElementDefinition}, converts it
|
|
|
+// to a function (because lower level converters accepts only element creator functions).
|
|
|
//
|
|
|
-// @param {Object} config Object with conversion helper configuration.
|
|
|
-// @param {Function} ViewElementClass View element class to use when generating view element from config.
|
|
|
-function _normalizeToElementConfig( config, ViewElementClass ) {
|
|
|
- // If config is given as an array, normalize each entry separately.
|
|
|
- if ( Array.isArray( config ) ) {
|
|
|
- for ( const configEntry of config ) {
|
|
|
- _normalizeToElementConfig( configEntry, ViewElementClass );
|
|
|
- }
|
|
|
-
|
|
|
- return;
|
|
|
+// @param {module:engine/view/elementdefinition~ElementDefinition|Function} view View configuration.
|
|
|
+// @param {'container'|'attribute'|'ui'} viewElementType View element type to create.
|
|
|
+// @returns {Function} Element creator function to use in lower level converters.
|
|
|
+function _normalizeToElementConfig( view, viewElementType ) {
|
|
|
+ if ( typeof view == 'function' ) {
|
|
|
+ // If `view` is already a function, don't do anything.
|
|
|
+ return view;
|
|
|
}
|
|
|
|
|
|
- const view = config.view;
|
|
|
-
|
|
|
- // Build `.view` property.
|
|
|
- // It is expected to be either string, element definition or creator function.
|
|
|
- if ( typeof view == 'string' ) {
|
|
|
- // If `.view` is a string, create a function that returns view element instance out of given `ViewElementClass`.
|
|
|
- config.view = () => new ViewElementClass( view );
|
|
|
- } else if ( typeof view == 'object' ) {
|
|
|
- // If `.view` is an object, use it to build view element instance.
|
|
|
- const element = _createViewElementFromDefinition( view, ViewElementClass );
|
|
|
- config.view = () => element.clone();
|
|
|
- }
|
|
|
- // `.view` can be also a function that is already valid type which don't have to be normalized.
|
|
|
+ return ( modelData, viewWriter ) => _createViewElementFromDefinition( view, viewWriter, viewElementType );
|
|
|
}
|
|
|
|
|
|
// Creates view element instance from provided viewElementDefinition and class.
|
|
|
//
|
|
|
// @param {module:engine/view/elementdefinition~ElementDefinition} viewElementDefinition
|
|
|
-// @param {Function} ViewElementClass
|
|
|
+// @param {module:engine/view/writer~Writer} viewWriter
|
|
|
+// @param {'container'|'attribute'|'ui'} viewElementType
|
|
|
// @returns {module:engine/view/element~Element}
|
|
|
-function _createViewElementFromDefinition( viewElementDefinition, ViewElementClass ) {
|
|
|
- const element = new ViewElementClass( viewElementDefinition.name, Object.assign( {}, viewElementDefinition.attribute ) );
|
|
|
+function _createViewElementFromDefinition( viewElementDefinition, viewWriter, viewElementType ) {
|
|
|
+ if ( typeof viewElementDefinition == 'string' ) {
|
|
|
+ // If `viewElementDefinition` is given as a `String`, normalize it to an object with `name` property.
|
|
|
+ viewElementDefinition = { name: viewElementDefinition };
|
|
|
+ }
|
|
|
+
|
|
|
+ let element;
|
|
|
+
|
|
|
+ if ( viewElementType == 'container' ) {
|
|
|
+ element = viewWriter.createContainerElement( viewElementDefinition.name, Object.assign( {}, viewElementDefinition.attribute ) );
|
|
|
+ } else if ( viewElementType == 'attribute' ) {
|
|
|
+ element = viewWriter.createAttributeElement( viewElementDefinition.name, Object.assign( {}, viewElementDefinition.attribute ) );
|
|
|
+ } else {
|
|
|
+ // 'ui'.
|
|
|
+ element = viewWriter.createUIElement( viewElementDefinition.name, Object.assign( {}, viewElementDefinition.attribute ) );
|
|
|
+ }
|
|
|
|
|
|
if ( viewElementDefinition.style ) {
|
|
|
- element._setStyle( viewElementDefinition.style );
|
|
|
+ const keys = Object.keys( viewElementDefinition.style );
|
|
|
+
|
|
|
+ for ( const key of keys ) {
|
|
|
+ viewWriter.setStyle( key, viewElementDefinition.style[ key ], element );
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if ( viewElementDefinition.class ) {
|
|
|
- element._addClass( viewElementDefinition.class );
|
|
|
+ const classes = viewElementDefinition.class;
|
|
|
+
|
|
|
+ if ( typeof classes == 'string' ) {
|
|
|
+ viewWriter.addClass( classes, element );
|
|
|
+ } else {
|
|
|
+ for ( const className of classes ) {
|
|
|
+ viewWriter.addClass( className, element );
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
return element;
|
|
|
}
|
|
|
|
|
|
-// Takes config and adds default parameters if they don't exist and normalizes other parameters to be used in downcast converters
|
|
|
-// for generating view attribute.
|
|
|
-//
|
|
|
-// @param {String} modelAttributeKey Model attribute key for which config is defined.
|
|
|
-// @param {Object} [config] Config with conversion helper configuration.
|
|
|
-function _normalizeToAttributeConfig( modelAttributeKey, config ) {
|
|
|
- // If config is given as an array, normalize each entry separately.
|
|
|
- if ( Array.isArray( config ) ) {
|
|
|
- for ( const configEntry of config ) {
|
|
|
- _normalizeToAttributeConfig( modelAttributeKey, configEntry );
|
|
|
- }
|
|
|
+function _getFromAttributeCreator( config ) {
|
|
|
+ if ( config.model.values ) {
|
|
|
+ return ( modelAttributeValue, viewWriter ) => {
|
|
|
+ const view = config.view[ modelAttributeValue ];
|
|
|
|
|
|
- return;
|
|
|
- }
|
|
|
+ if ( view ) {
|
|
|
+ return view( modelAttributeValue, viewWriter );
|
|
|
+ }
|
|
|
|
|
|
- // Build `.view` property.
|
|
|
- // It is expected to be a creator function, that takes attribute value and model item and returns an object
|
|
|
- // with `key` property and `value` property which are view attribute key and view attribute value.
|
|
|
- if ( !config.view ) {
|
|
|
- // If `.view` is not set, take both attribute name and attribute value from model.
|
|
|
- const viewAttributeKey = modelAttributeKey;
|
|
|
- config.view = modelAttributeValue => ( { key: viewAttributeKey, value: modelAttributeValue } );
|
|
|
- } else if ( typeof config.view == 'string' ) {
|
|
|
- // If `.view` is set as a string, use it as a view attribute name. Value will be taken from model attribute value.
|
|
|
- const viewAttributeKey = config.view;
|
|
|
- config.view = modelAttributeValue => ( { key: viewAttributeKey, value: modelAttributeValue } );
|
|
|
- } else if ( typeof config.view == 'object' ) {
|
|
|
- // If `.view` is set as an object, use set key and value.
|
|
|
- const viewAttributeKey = config.view.key;
|
|
|
- const viewAttributeValue = config.view.value;
|
|
|
- config.view = () => ( { key: viewAttributeKey, value: viewAttributeValue } );
|
|
|
+ return null;
|
|
|
+ };
|
|
|
+ } else {
|
|
|
+ return config.view;
|
|
|
}
|
|
|
- // `.view` can be also already a function.
|
|
|
}
|
|
|
|
|
|
-// Takes config and creates a view element creator function that chooses an appropriate entry from the config depending on
|
|
|
-// the value of model attribute.
|
|
|
-//
|
|
|
-// Supports specifying config as a single object or an array of objects.
|
|
|
-// Supports `.view` defined as an object and as a function.
|
|
|
+// Takes config and adds default parameters if they don't exist and normalizes other parameters to be used in downcast converters
|
|
|
+// for generating view attribute.
|
|
|
//
|
|
|
-// @param {Object|Array.<Object>} config Config with conversion helper configuration.
|
|
|
-function _getCreatorForArrayConfig( config ) {
|
|
|
- if ( !Array.isArray( config ) ) {
|
|
|
- config = [ config ];
|
|
|
+// @param {Object} view View configuration.
|
|
|
+function _normalizeToAttributeConfig( view ) {
|
|
|
+ if ( typeof view == 'string' ) {
|
|
|
+ return modelAttributeValue => ( { key: view, value: modelAttributeValue } );
|
|
|
+ } else if ( typeof view == 'object' ) {
|
|
|
+ return () => view;
|
|
|
+ } else {
|
|
|
+ // function.
|
|
|
+ return view;
|
|
|
}
|
|
|
-
|
|
|
- // Get "default config" entry. It is the entry with `.model` property set to `true`.
|
|
|
- // "Default" entry should be used if no other entry matched model attribute value.
|
|
|
- const defaultConfig = config.find( configEntry => configEntry.model === undefined );
|
|
|
-
|
|
|
- // Return a creator function.
|
|
|
- return ( modelAttributeValue, data, consumable, conversionApi ) => {
|
|
|
- // Set default config at first. It will be used if no other entry matches model attribute value.
|
|
|
- let matchedConfigEntry = defaultConfig;
|
|
|
-
|
|
|
- // Creator should check all entries from the config...
|
|
|
- for ( const configEntry of config ) {
|
|
|
- if ( configEntry.model === modelAttributeValue ) {
|
|
|
- // If `.model` specified in entry matches converted attribute's value, choose it.
|
|
|
- matchedConfigEntry = configEntry;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // If there was default config or matched config...
|
|
|
- if ( matchedConfigEntry ) {
|
|
|
- // The entry `.view` is a function after it got normalized earlier, execute it and return the value.
|
|
|
- return matchedConfigEntry.view( modelAttributeValue, data, consumable, conversionApi );
|
|
|
- }
|
|
|
-
|
|
|
- return null;
|
|
|
- };
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -495,7 +447,7 @@ function _getCreatorForArrayConfig( config ) {
|
|
|
*/
|
|
|
export function insertElement( elementCreator ) {
|
|
|
return ( evt, data, consumable, conversionApi ) => {
|
|
|
- const viewElement = elementCreator( data.item, consumable, conversionApi );
|
|
|
+ const viewElement = elementCreator( data.item, conversionApi.writer );
|
|
|
|
|
|
if ( !viewElement ) {
|
|
|
return;
|
|
|
@@ -579,20 +531,13 @@ export function remove() {
|
|
|
*/
|
|
|
export function insertUIElement( elementCreator ) {
|
|
|
return ( evt, data, consumable, conversionApi ) => {
|
|
|
- let viewStartElement, viewEndElement;
|
|
|
-
|
|
|
// Create two view elements. One will be inserted at the beginning of marker, one at the end.
|
|
|
// If marker is collapsed, only "opening" element will be inserted.
|
|
|
- if ( elementCreator instanceof ViewElement ) {
|
|
|
- viewStartElement = elementCreator.clone( true );
|
|
|
- viewEndElement = elementCreator.clone( true );
|
|
|
- } else {
|
|
|
- data.isOpening = true;
|
|
|
- viewStartElement = elementCreator( data, conversionApi );
|
|
|
+ data.isOpening = true;
|
|
|
+ const viewStartElement = elementCreator( data, conversionApi.writer );
|
|
|
|
|
|
- data.isOpening = false;
|
|
|
- viewEndElement = elementCreator( data, conversionApi );
|
|
|
- }
|
|
|
+ data.isOpening = false;
|
|
|
+ const viewEndElement = elementCreator( data, conversionApi.writer );
|
|
|
|
|
|
if ( !viewStartElement || !viewEndElement ) {
|
|
|
return;
|
|
|
@@ -639,20 +584,13 @@ export function insertUIElement( elementCreator ) {
|
|
|
*/
|
|
|
export function removeUIElement( elementCreator ) {
|
|
|
return ( evt, data, conversionApi ) => {
|
|
|
- let viewStartElement, viewEndElement;
|
|
|
-
|
|
|
// Create two view elements. One will be used to remove "opening element", the other for "closing element".
|
|
|
// If marker was collapsed, only "opening" element will be removed.
|
|
|
- if ( elementCreator instanceof ViewElement ) {
|
|
|
- viewStartElement = elementCreator.clone( true );
|
|
|
- viewEndElement = elementCreator.clone( true );
|
|
|
- } else {
|
|
|
- data.isOpening = true;
|
|
|
- viewStartElement = elementCreator( data, conversionApi );
|
|
|
+ data.isOpening = true;
|
|
|
+ const viewStartElement = elementCreator( data, conversionApi.writer );
|
|
|
|
|
|
- data.isOpening = false;
|
|
|
- viewEndElement = elementCreator( data, conversionApi );
|
|
|
- }
|
|
|
+ data.isOpening = false;
|
|
|
+ const viewEndElement = elementCreator( data, conversionApi.writer );
|
|
|
|
|
|
if ( !viewStartElement || !viewEndElement ) {
|
|
|
return;
|
|
|
@@ -702,8 +640,7 @@ export function removeUIElement( elementCreator ) {
|
|
|
*
|
|
|
* @param {Function} [attributeCreator] Function returning an object with two properties: `key` and `value`, which
|
|
|
* represents attribute key and attribute value to be set on a {@link module:engine/view/element~Element view element}.
|
|
|
- * The function is passed all the parameters of the
|
|
|
- * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:attribute} event.
|
|
|
+ * The function is passed model attribute value as first parameter and additional data about the change as a second parameter.
|
|
|
* @returns {Function} Set/change attribute converter.
|
|
|
*/
|
|
|
export function changeAttribute( attributeCreator ) {
|
|
|
@@ -714,22 +651,49 @@ export function changeAttribute( attributeCreator ) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // First remove the old attribute if there was one.
|
|
|
- const oldAttribute = attributeCreator( data.attributeOldValue, data, consumable, conversionApi );
|
|
|
- const mapper = conversionApi.mapper;
|
|
|
+ const viewElement = conversionApi.mapper.toViewElement( data.item );
|
|
|
const viewWriter = conversionApi.writer;
|
|
|
|
|
|
+ // First remove the old attribute if there was one.
|
|
|
+ const oldAttribute = attributeCreator( data.attributeOldValue, data );
|
|
|
+
|
|
|
if ( data.attributeOldValue !== null && oldAttribute ) {
|
|
|
- const viewElement = mapper.toViewElement( data.item );
|
|
|
- viewWriter.removeAttribute( oldAttribute.key, viewElement );
|
|
|
+ if ( oldAttribute.key == 'class' ) {
|
|
|
+ const classes = Array.isArray( oldAttribute.value ) ? oldAttribute.value : [ oldAttribute.value ];
|
|
|
+
|
|
|
+ for ( const className of classes ) {
|
|
|
+ viewWriter.removeClass( className, viewElement );
|
|
|
+ }
|
|
|
+ } else if ( oldAttribute.key == 'style' ) {
|
|
|
+ const keys = Object.keys( oldAttribute.value );
|
|
|
+
|
|
|
+ for ( const key of keys ) {
|
|
|
+ viewWriter.removeStyle( key, viewElement );
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ viewWriter.removeAttribute( oldAttribute.key, viewElement );
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// Then, if conversion was successful, set the new attribute.
|
|
|
const newAttribute = attributeCreator( data.attributeNewValue, data, consumable, conversionApi );
|
|
|
|
|
|
if ( data.attributeNewValue !== null && newAttribute ) {
|
|
|
- const viewElement = mapper.toViewElement( data.item );
|
|
|
- viewWriter.setAttribute( newAttribute.key, newAttribute.value, viewElement );
|
|
|
+ if ( newAttribute.key == 'class' ) {
|
|
|
+ const classes = Array.isArray( newAttribute.value ) ? newAttribute.value : [ newAttribute.value ];
|
|
|
+
|
|
|
+ for ( const className of classes ) {
|
|
|
+ viewWriter.addClass( className, viewElement );
|
|
|
+ }
|
|
|
+ } else if ( newAttribute.key == 'style' ) {
|
|
|
+ const keys = Object.keys( newAttribute.value );
|
|
|
+
|
|
|
+ for ( const key of keys ) {
|
|
|
+ viewWriter.setStyle( key, newAttribute.value[ key ], viewElement );
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ viewWriter.setAttribute( newAttribute.key, newAttribute.value, viewElement );
|
|
|
+ }
|
|
|
}
|
|
|
};
|
|
|
}
|
|
|
@@ -768,10 +732,10 @@ export function wrap( elementCreator ) {
|
|
|
return ( evt, data, consumable, conversionApi ) => {
|
|
|
// Recreate current wrapping node. It will be used to unwrap view range if the attribute value has changed
|
|
|
// or the attribute was removed.
|
|
|
- const oldViewElement = elementCreator( data.attributeOldValue, data, consumable, conversionApi );
|
|
|
+ const oldViewElement = elementCreator( data.attributeOldValue, conversionApi.writer );
|
|
|
|
|
|
// Create node to wrap with.
|
|
|
- const newViewElement = elementCreator( data.attributeNewValue, data, consumable, conversionApi );
|
|
|
+ const newViewElement = elementCreator( data.attributeNewValue, conversionApi.writer );
|
|
|
|
|
|
if ( !oldViewElement && !newViewElement ) {
|
|
|
return;
|