@ckeditor/ckeditor5-engine
Advanced tools
Comparing version 10.0.0 to 10.1.0
Changelog | ||
========= | ||
## [10.1.0](https://github.com/ckeditor/ckeditor5-engine/compare/v10.0.0...v10.1.0) (2018-06-21) | ||
### Features | ||
* Introduce `ElementDefinition#priority` property which allows specifying the priority of created element during the downcast conversion. Closes [#1408](https://github.com/ckeditor/ckeditor5-engine/issues/1408). ([e20e133](https://github.com/ckeditor/ckeditor5-engine/commit/e20e133)) | ||
* Introduced `ModelDocument#change:data` event. Closes [#1418](https://github.com/ckeditor/ckeditor5-engine/issues/1418). ([872f4ff](https://github.com/ckeditor/ckeditor5-engine/commit/872f4ff)) | ||
* Introduced a selection post-fixer. Its role is to ensure that after all changes are applied the selection is placed in a correct position. Closes [#1156](https://github.com/ckeditor/ckeditor5-engine/issues/1156). Closes [#1176](https://github.com/ckeditor/ckeditor5-engine/issues/1176). Closes [#1182](https://github.com/ckeditor/ckeditor5-engine/issues/1182). Closes [ckeditor/ckeditor5-table#11](https://github.com/ckeditor/ckeditor5-table/issues/11). Closes [ckeditor/ckeditor5-table#12](https://github.com/ckeditor/ckeditor5-table/issues/12). Closes [ckeditor/ckeditor5-table#15](https://github.com/ckeditor/ckeditor5-table/issues/15). Closes [ckeditor/ckeditor5#562](https://github.com/ckeditor/ckeditor5/issues/562). Closes [ckeditor/ckeditor5#611](https://github.com/ckeditor/ckeditor5/issues/611). ([6cf91a1](https://github.com/ckeditor/ckeditor5-engine/commit/6cf91a1)) | ||
### Bug fixes | ||
* Block filler will be inserted into the container if its last child is a `<br>` element. Closes [#1422](https://github.com/ckeditor/ckeditor5-engine/issues/1422). ([ba3d641](https://github.com/ckeditor/ckeditor5-engine/commit/ba3d641)) | ||
* Fixed view <-> DOM conversion of whitespaces around `<br>` elements. Closes [ckeditor/ckeditor5#1024](https://github.com/ckeditor/ckeditor5/issues/1024). ([3e74554](https://github.com/ckeditor/ckeditor5-engine/commit/3e74554)) | ||
* Renderer should avoid doing unnecessary DOM structure changes. Ensuring that the DOM gets updated less frequently fixes many issues with text composition. Closes [#1417](https://github.com/ckeditor/ckeditor5-engine/issues/1417). Closes [#1409](https://github.com/ckeditor/ckeditor5-engine/issues/1409). Closes [#1349](https://github.com/ckeditor/ckeditor5-engine/issues/1349). Closes [#1334](https://github.com/ckeditor/ckeditor5-engine/issues/1334). Closes [#898](https://github.com/ckeditor/ckeditor5-engine/issues/898). Closes [ckeditor/ckeditor5-typing#129](https://github.com/ckeditor/ckeditor5-typing/issues/129). Closes [ckeditor/ckeditor5-typing#89](https://github.com/ckeditor/ckeditor5-typing/issues/89). Closes [#1427](https://github.com/ckeditor/ckeditor5-engine/issues/1427). ([457afde](https://github.com/ckeditor/ckeditor5-engine/commit/457afde)) | ||
### Other changes | ||
* Renderer now uses partial text replacing when updating text nodes instead of replacing entire nodes. Closes [#403](https://github.com/ckeditor/ckeditor5-engine/issues/403). ([797cd97](https://github.com/ckeditor/ckeditor5-engine/commit/797cd97)) | ||
## [10.0.0](https://github.com/ckeditor/ckeditor5-engine/compare/v1.0.0-beta.4...v10.0.0) (2018-04-25) | ||
@@ -103,3 +122,3 @@ | ||
* Conversion utilities refactor. Closes [#1236](https://github.com/ckeditor/ckeditor5-engine/issues/1236). ([fd128a1](https://github.com/ckeditor/ckeditor5-engine/commit/fd128a1)) | ||
* Fix `render()` and `change()` flow. Introduce postfixers in view. Closes [#1312](https://github.com/ckeditor/ckeditor5-engine/issues/1312). ([63b9d14](https://github.com/ckeditor/ckeditor5-engine/commit/63b9d14)) | ||
* Fixed `render()` and `change()` methods flow. Introduced post-fixers in the view. Closes [#1312](https://github.com/ckeditor/ckeditor5-engine/issues/1312). ([63b9d14](https://github.com/ckeditor/ckeditor5-engine/commit/63b9d14)) | ||
* Introduced several improvements to conversion helpers. Closes [#1295](https://github.com/ckeditor/ckeditor5-engine/issues/1295). Closes [#1293](https://github.com/ckeditor/ckeditor5-engine/issues/1293). Closes [#1292](https://github.com/ckeditor/ckeditor5-engine/issues/1292). Closes [#1291](https://github.com/ckeditor/ckeditor5-engine/issues/1291). Closes [#1290](https://github.com/ckeditor/ckeditor5-engine/issues/1290). Closes [#1305](https://github.com/ckeditor/ckeditor5-engine/issues/1305). ([809ea24](https://github.com/ckeditor/ckeditor5-engine/commit/809ea24)) | ||
@@ -106,0 +125,0 @@ * Keep the same marker instance when marker is updated. ([8eba5e9](https://github.com/ckeditor/ckeditor5-engine/commit/8eba5e9)) |
{ | ||
"name": "@ckeditor/ckeditor5-engine", | ||
"version": "10.0.0", | ||
"version": "10.1.0", | ||
"description": "CKEditor 5 editing engine.", | ||
@@ -28,17 +28,17 @@ "keywords": [ | ||
"dependencies": { | ||
"@ckeditor/ckeditor5-utils": "^10.0.0" | ||
"@ckeditor/ckeditor5-utils": "^10.1.0" | ||
}, | ||
"devDependencies": { | ||
"@ckeditor/ckeditor5-basic-styles": "^10.0.0", | ||
"@ckeditor/ckeditor5-core": "^10.0.0", | ||
"@ckeditor/ckeditor5-editor-classic": "^10.0.0", | ||
"@ckeditor/ckeditor5-enter": "^10.0.0", | ||
"@ckeditor/ckeditor5-essentials": "^10.0.0", | ||
"@ckeditor/ckeditor5-heading": "^10.0.0", | ||
"@ckeditor/ckeditor5-list": "^10.0.0", | ||
"@ckeditor/ckeditor5-paragraph": "^10.0.0", | ||
"@ckeditor/ckeditor5-typing": "^10.0.0", | ||
"@ckeditor/ckeditor5-undo": "^10.0.0", | ||
"@ckeditor/ckeditor5-widget": "^10.0.0", | ||
"@ckeditor/ckeditor5-link": "^10.0.0", | ||
"@ckeditor/ckeditor5-basic-styles": "^10.0.1", | ||
"@ckeditor/ckeditor5-core": "^10.1.0", | ||
"@ckeditor/ckeditor5-editor-classic": "^10.0.1", | ||
"@ckeditor/ckeditor5-enter": "^10.1.0", | ||
"@ckeditor/ckeditor5-essentials": "^10.1.0", | ||
"@ckeditor/ckeditor5-heading": "^10.0.1", | ||
"@ckeditor/ckeditor5-list": "^11.0.0", | ||
"@ckeditor/ckeditor5-paragraph": "^10.0.1", | ||
"@ckeditor/ckeditor5-typing": "^10.0.1", | ||
"@ckeditor/ckeditor5-undo": "^10.0.1", | ||
"@ckeditor/ckeditor5-widget": "^10.1.0", | ||
"@ckeditor/ckeditor5-link": "^10.0.2", | ||
"eslint": "^4.15.0", | ||
@@ -45,0 +45,0 @@ "eslint-config-ckeditor5": "^1.0.7", |
@@ -7,3 +7,3 @@ CKEditor 5 editing engine | ||
[![Build Status](https://travis-ci.org/ckeditor/ckeditor5-engine.svg?branch=master)](https://travis-ci.org/ckeditor/ckeditor5-engine) | ||
[![BrowserStack Status](https://www.browserstack.com/automate/badge.svg?badge_key=d3hvenZqQVZERFQ5d09FWXdyT0ozVXhLaVltRFRjTTUyZGpvQWNmWVhUUT0tLUZqNlJ1YWRUd0RvdEVOaEptM1B2Q0E9PQ==--c9d3dee40b9b4471ff3fb516d9ecf8d09292c7e0)](https://www.browserstack.com/automate/public-build/d3hvenZqQVZERFQ5d09FWXdyT0ozVXhLaVltRFRjTTUyZGpvQWNmWVhUUT0tLUZqNlJ1YWRUd0RvdEVOaEptM1B2Q0E9PQ==--c9d3dee40b9b4471ff3fb516d9ecf8d09292c7e0) | ||
[![BrowserStack Status](https://automate.browserstack.com/automate/badge.svg?badge_key=d3hvenZqQVZERFQ5d09FWXdyT0ozVXhLaVltRFRjTTUyZGpvQWNmWVhUUT0tLUZqNlJ1YWRUd0RvdEVOaEptM1B2Q0E9PQ==--c9d3dee40b9b4471ff3fb516d9ecf8d09292c7e0)](https://automate.browserstack.com/public-build/d3hvenZqQVZERFQ5d09FWXdyT0ozVXhLaVltRFRjTTUyZGpvQWNmWVhUUT0tLUZqNlJ1YWRUd0RvdEVOaEptM1B2Q0E9PQ==--c9d3dee40b9b4471ff3fb516d9ecf8d09292c7e0) | ||
[![Coverage Status](https://coveralls.io/repos/github/ckeditor/ckeditor5-engine/badge.svg?branch=master)](https://coveralls.io/github/ckeditor/ckeditor5-engine?branch=master) | ||
@@ -10,0 +10,0 @@ <br> |
@@ -16,7 +16,3 @@ /** | ||
import { convertSelectionChange } from '../conversion/upcast-selection-converters'; | ||
import { | ||
convertRangeSelection, | ||
convertCollapsedSelection, | ||
clearAttributes | ||
} from '../conversion/downcast-selection-converters'; | ||
import { clearAttributes, convertCollapsedSelection, convertRangeSelection } from '../conversion/downcast-selection-converters'; | ||
@@ -23,0 +19,0 @@ import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin'; |
@@ -25,6 +25,6 @@ /** | ||
/** | ||
* An utility class that helps adding converters to upcast and downcast dispatchers. | ||
* A utility class that helps add converters to upcast and downcast dispatchers. | ||
* | ||
* We recommend reading first the {@glink framework/guides/architecture/editing-engine} guide to understand the | ||
* core concepts of the conversion mechanisms. | ||
* We recommend reading the {@glink framework/guides/architecture/editing-engine Editing engine architecture} guide first to | ||
* understand the core concepts of the conversion mechanisms. | ||
* | ||
@@ -40,3 +40,3 @@ * The instance of the conversion manager is available in the | ||
* | ||
* To add a converter to a specific group use the {@link module:engine/conversion/conversion~Conversion#for `for()`} | ||
* To add a converter to a specific group, use the {@link module:engine/conversion/conversion~Conversion#for `for()`} | ||
* method: | ||
@@ -53,5 +53,5 @@ * | ||
* The functions used in `add()` calls are one-way converters (i.e. you need to remember yourself to add | ||
* a converter in the other direction, if you feature requires that). They are also called "conversion helpers". | ||
* a converter in the other direction, if your feature requires that). They are also called "conversion helpers". | ||
* You can find a set of them in the {@link module:engine/conversion/downcast-converters} and | ||
* {@link module:engine/conversion/upcast-converters} modules | ||
* {@link module:engine/conversion/upcast-converters} modules. | ||
* | ||
@@ -61,12 +61,12 @@ * Besides allowing to register converters to specific dispatchers, you can also use methods available in this | ||
* | ||
* * {@link module:engine/conversion/conversion~Conversion#elementToElement `elementToElement()`} – | ||
* model element to view element and vice versa | ||
* * {@link module:engine/conversion/conversion~Conversion#attributeToElement `attributeToElement()`} – | ||
* model attribute to view element and vice versa | ||
* * {@link module:engine/conversion/conversion~Conversion#attributeToElement `attributeToElement()`} – | ||
* model attribute to view element and vice versa | ||
* * {@link module:engine/conversion/conversion~Conversion#elementToElement `elementToElement()`} – | ||
* Model element to view element and vice versa. | ||
* * {@link module:engine/conversion/conversion~Conversion#attributeToElement `attributeToElement()`} – | ||
* Model attribute to view element and vice versa. | ||
* * {@link module:engine/conversion/conversion~Conversion#attributeToElement `attributeToElement()`} – | ||
* Model attribute to view element and vice versa. | ||
*/ | ||
export default class Conversion { | ||
/** | ||
* Creates new Conversion instance. | ||
* Creates a new conversion instance. | ||
*/ | ||
@@ -82,12 +82,12 @@ constructor() { | ||
/** | ||
* Registers one or more converters under given group name. Then, group name can be used to assign a converter | ||
* Registers one or more converters under a given group name. The group name can then be used to assign a converter | ||
* to multiple dispatchers at once. | ||
* | ||
* If given group name is used for a second time, | ||
* {@link module:utils/ckeditorerror~CKEditorError conversion-register-group-exists} error is thrown. | ||
* If a given group name is used for the second time, the | ||
* {@link module:utils/ckeditorerror~CKEditorError `conversion-register-group-exists` error} is thrown. | ||
* | ||
* @param {String} groupName A name for dispatchers group. | ||
* @param {String} groupName The name for dispatchers group. | ||
* @param {Array.<module:engine/conversion/downcastdispatcher~DowncastDispatcher| | ||
* module:engine/conversion/upcastdispatcher~UpcastDispatcher>} dispatchers Dispatchers to register | ||
* under given name. | ||
* under the given name. | ||
*/ | ||
@@ -108,4 +108,4 @@ register( groupName, dispatchers ) { | ||
/** | ||
* Provides chainable API to assign converters to dispatchers registered under given group name. Converters are added | ||
* by calling `.add()` method of an object returned by this function. | ||
* Provides chainable API to assign converters to dispatchers registered under a given group name. Converters are added | ||
* by calling the `.add()` method of an object returned by this function. | ||
* | ||
@@ -116,23 +116,23 @@ * conversion.for( 'downcast' ) | ||
* | ||
* In above example, `conversionHelperA` and `conversionHelperB` will be called for all dispatchers from `'model'` group. | ||
* In this example `conversionHelperA` and `conversionHelperB` will be called for all dispatchers from the `'model'` group. | ||
* | ||
* `.add()` takes exactly one parameter, which is a function. That function should accept one parameter, which | ||
* is a dispatcher instance. The function should add an actual converter to passed dispatcher instance. | ||
* The `.add()` method takes exactly one parameter, which is a function. This function should accept one parameter that | ||
* is a dispatcher instance. The function should add an actual converter to the passed dispatcher instance. | ||
* | ||
* Conversion helpers for most common cases are already provided. They are flexible enough to cover most use cases. | ||
* See documentation to learn how they can be configured. | ||
* See the documentation to learn how they can be configured. | ||
* | ||
* For downcast (model to view conversion), these are: | ||
* For downcast (model-to-view conversion), these are: | ||
* | ||
* * {@link module:engine/conversion/downcast-converters~downcastElementToElement downcast element to element converter}, | ||
* * {@link module:engine/conversion/downcast-converters~downcastAttributeToElement downcast attribute to element converter}, | ||
* * {@link module:engine/conversion/downcast-converters~downcastAttributeToAttribute downcast attribute to attribute converter}. | ||
* * {@link module:engine/conversion/downcast-converters~downcastElementToElement Downcast element-to-element converter}, | ||
* * {@link module:engine/conversion/downcast-converters~downcastAttributeToElement Downcast attribute-to-element converter}, | ||
* * {@link module:engine/conversion/downcast-converters~downcastAttributeToAttribute Downcast attribute-to-attribute converter}. | ||
* | ||
* For upcast (view to model conversion), these are: | ||
* For upcast (view-to-model conversion), these are: | ||
* | ||
* * {@link module:engine/conversion/upcast-converters~upcastElementToElement upcast element to element converter}, | ||
* * {@link module:engine/conversion/upcast-converters~upcastElementToAttribute upcast attribute to element converter}, | ||
* * {@link module:engine/conversion/upcast-converters~upcastAttributeToAttribute upcast attribute to attribute converter}. | ||
* * {@link module:engine/conversion/upcast-converters~upcastElementToElement Upcast element-to-element converter}, | ||
* * {@link module:engine/conversion/upcast-converters~upcastElementToAttribute Upcast attribute-to-element converter}, | ||
* * {@link module:engine/conversion/upcast-converters~upcastAttributeToAttribute Upcast attribute-to-attribute converter}. | ||
* | ||
* An example of using conversion helpers to convert `paragraph` model element to `p` view element (and back): | ||
* An example of using conversion helpers to convert the `paragraph` model element to the `p` view element (and back): | ||
* | ||
@@ -146,9 +146,9 @@ * // Define conversion configuration - model element 'paragraph' should be converted to view element 'p'. | ||
* | ||
* An example of providing custom conversion helper that uses custom converter function: | ||
* An example of providing a custom conversion helper that uses a custom converter function: | ||
* | ||
* // Adding custom `myConverter` converter for 'paragraph' element insertion, with default priority ('normal'). | ||
* // Adding a custom `myConverter` converter for 'paragraph' element insertion, with the default priority ('normal'). | ||
* conversion.for( 'downcast' ).add( conversion.customConverter( 'insert:paragraph', myConverter ) ); | ||
* | ||
* @param {String} groupName Name of dispatchers group to add converters to. | ||
* @returns {Object} Object with `.add()` method, providing a way to add converters. | ||
* @param {String} groupName The name of dispatchers group to add the converters to. | ||
* @returns {Object} An object with the `.add()` method, providing a way to add converters. | ||
*/ | ||
@@ -168,10 +168,10 @@ for( groupName ) { | ||
/** | ||
* Sets up converters between the model and the view which convert a model element to a view element (and vice versa). | ||
* For example, model `<paragraph>Foo</paragraph>` is `<p>Foo</p>` in the view. | ||
* Sets up converters between the model and the view that convert a model element to a view element (and vice versa). | ||
* For example, the model `<paragraph>Foo</paragraph>` is `<p>Foo</p>` in the view. | ||
* | ||
* // Simple conversion from `paragraph` model element to `<p>` view element (and vice versa). | ||
* // A simple conversion from the `paragraph` model element to the `<p>` view element (and vice versa). | ||
* conversion.elementToElement( { model: 'paragraph', view: 'p' } ); | ||
* | ||
* // Override other converters by specifying converter definition with higher priority. | ||
* conversion.elementToElement( { model: 'paragraph', view: 'div', priority: 'high' } ); | ||
* // Override other converters by specifying a converter definition with a higher priority. | ||
* conversion.elementToElement( { model: 'paragraph', view: 'div', converterPriority: 'high' } ); | ||
* | ||
@@ -187,3 +187,3 @@ * // View specified as an object instead of a string. | ||
* | ||
* // Use `upcastAlso` to define other view elements that should be also converted to `paragraph` element. | ||
* // Use `upcastAlso` to define other view elements that should also be converted to a `paragraph` element. | ||
* conversion.elementToElement( { | ||
@@ -195,3 +195,3 @@ * model: 'paragraph', | ||
* { | ||
* // Any element with `display: block` style. | ||
* // Any element with the `display: block` style. | ||
* styles: { | ||
@@ -225,5 +225,5 @@ * display: 'block' | ||
* if ( size > 26 ) { | ||
* // Returned value be an object with the matched properties. | ||
* // Those properties will be "consumed" during conversion. | ||
* // See `engine.view.Matcher~MatcherPattern` and `engine.view.Matcher#match` for more. | ||
* // Returned value can be an object with the matched properties. | ||
* // These properties will be "consumed" during the conversion. | ||
* // See `engine.view.Matcher~MatcherPattern` and `engine.view.Matcher#match` for more details. | ||
* | ||
@@ -237,6 +237,6 @@ * return { name: true, styles: [ 'font-size' ] }; | ||
* | ||
* `definition.model` is a `String` with a model element name to converter from/to. | ||
* `definition.model` is a `String` with a model element name to convert from or to. | ||
* See {@link module:engine/conversion/conversion~ConverterDefinition} to learn about other parameters. | ||
* | ||
* @param {module:engine/conversion/conversion~ConverterDefinition} definition Converter definition. | ||
* @param {module:engine/conversion/conversion~ConverterDefinition} definition The converter definition. | ||
*/ | ||
@@ -253,3 +253,3 @@ elementToElement( definition ) { | ||
view, | ||
priority: definition.priority | ||
converterPriority: definition.converterPriority | ||
} ) | ||
@@ -261,10 +261,10 @@ ); | ||
/** | ||
* Sets up converters between the model and the view which convert a model attribute to a view element (and vice versa). | ||
* For example, model text node with data `"Foo"` and `bold` attribute is `<strong>Foo</strong>` in the view. | ||
* Sets up converters between the model and the view that convert a model attribute to a view element (and vice versa). | ||
* For example, a model text node with `"Foo"` as data and the `bold` attribute is `<strong>Foo</strong>` in the view. | ||
* | ||
* // Simple conversion from `bold=true` attribute to `<strong>` view element (and vice versa). | ||
* // A simple conversion from the `bold=true` attribute to the `<strong>` view element (and vice versa). | ||
* conversion.attributeToElement( { model: 'bold', view: 'strong' } ); | ||
* | ||
* // Override other converters by specifying converter definition with higher priority. | ||
* conversion.attributeToElement( { model: 'bold', view: 'b', priority: 'high' } ); | ||
* // Override other converters by specifying a converter definition with a higher priority. | ||
* conversion.attributeToElement( { model: 'bold', view: 'b', converterPriority: 'high' } ); | ||
* | ||
@@ -280,4 +280,4 @@ * // View specified as an object instead of a string. | ||
* | ||
* // Use `config.model.name` to define conversion only from given node type, `$text` in this case. | ||
* // The same attribute on different elements may be then handled by a different converter. | ||
* // Use `config.model.name` to define the conversion only from a given node type, `$text` in this case. | ||
* // The same attribute on different elements may then be handled by a different converter. | ||
* conversion.attributeToElement( { | ||
@@ -305,3 +305,3 @@ * model: { | ||
* | ||
* // Use `upcastAlso` to define other view elements that should be also converted to `bold` attribute. | ||
* // Use `upcastAlso` to define other view elements that should also be converted to the `bold` attribute. | ||
* conversion.attributeToElement( { | ||
@@ -326,5 +326,5 @@ * model: 'bold', | ||
* if ( viewElement.is( 'span' ) && fontWeight && /\d+/.test() && Number( fontWeight ) > 500 ) { | ||
* // Returned value be an object with the matched properties. | ||
* // Those properties will be "consumed" during conversion. | ||
* // See `engine.view.Matcher~MatcherPattern` and `engine.view.Matcher#match` for more. | ||
* // Returned value can be an object with the matched properties. | ||
* // These properties will be "consumed" during the conversion. | ||
* // See `engine.view.Matcher~MatcherPattern` and `engine.view.Matcher#match` for more details. | ||
* | ||
@@ -340,3 +340,3 @@ * return { | ||
* | ||
* // Conversion from/to a model attribute key which value is an enum (`fontSize=big|small`). | ||
* // Conversion from and to a model attribute key whose value is an enum (`fontSize=big|small`). | ||
* // `upcastAlso` set as callback enables a conversion of a wide range of different view elements. | ||
@@ -379,5 +379,5 @@ * conversion.attributeToElement( { | ||
* if ( viewElement.is( 'span' ) && size > 10 ) { | ||
* // Returned value be an object with the matched properties. | ||
* // Those properties will be "consumed" during conversion. | ||
* // See `engine.view.Matcher~MatcherPattern` and `engine.view.Matcher#match` for more. | ||
* // Returned value can be an object with the matched properties. | ||
* // These properties will be "consumed" during the conversion. | ||
* // See `engine.view.Matcher~MatcherPattern` and `engine.view.Matcher#match` for more details. | ||
* | ||
@@ -405,5 +405,5 @@ * return { name: true, styles: [ 'font-size' ] }; | ||
* if ( viewElement.is( 'span' ) && size < 10 ) { | ||
* // Returned value be an object with the matched properties. | ||
* // Those properties will be "consumed" during conversion. | ||
* // See `engine.view.Matcher~MatcherPattern` and `engine.view.Matcher#match` for more. | ||
* // Returned value can be an object with the matched properties. | ||
* // These properties will be "consumed" during the conversion. | ||
* // See `engine.view.Matcher~MatcherPattern` and `engine.view.Matcher#match` for more details. | ||
* | ||
@@ -418,7 +418,7 @@ * return { name: true, styles: [ 'font-size' ] }; | ||
* | ||
* `definition.model` parameter specifies what model attribute should be converted from/to. It can be a `{ key, value }` object | ||
* describing attribute key and value to convert or a `String` specifying just attribute key (then `value` is set to `true`). | ||
* The `definition.model` parameter specifies which model attribute should be converted from or to. It can be a `{ key, value }` object | ||
* describing the attribute key and value to convert or a `String` specifying just the attribute key (then `value` is set to `true`). | ||
* See {@link module:engine/conversion/conversion~ConverterDefinition} to learn about other parameters. | ||
* | ||
* @param {module:engine/conversion/conversion~ConverterDefinition} definition Converter definition. | ||
* @param {module:engine/conversion/conversion~ConverterDefinition} definition The converter definition. | ||
*/ | ||
@@ -442,9 +442,9 @@ attributeToElement( definition ) { | ||
/** | ||
* Sets up converters between the model and the view which convert a model attribute to a view attribute (and vice versa). | ||
* For example, `<image src='foo.jpg'></image>` is converted to `<img src='foo.jpg'></img>` (same attribute key and value). | ||
* Sets up converters between the model and the view that convert a model attribute to a view attribute (and vice versa). | ||
* For example, `<image src='foo.jpg'></image>` is converted to `<img src='foo.jpg'></img>` (the same attribute key and value). | ||
* | ||
* // Simple conversion from `source` model attribute to `src` view attribute (and vice versa). | ||
* // A simple conversion from the `source` model attribute to the `src` view attribute (and vice versa). | ||
* conversion.attributeToAttribute( { model: 'source', view: 'src' } ); | ||
* | ||
* // Attributes values are strictly specified. | ||
* // Attribute values are strictly specified. | ||
* conversion.attributeToAttribute( { | ||
@@ -465,3 +465,3 @@ * model: { | ||
* | ||
* // Set style attribute. | ||
* // Set the style attribute. | ||
* conversion.attributeToAttribute( { | ||
@@ -486,4 +486,4 @@ * model: { | ||
* | ||
* // Conversion from/to a model attribute key which value is an enum (`align=right|center`). | ||
* // Use `upcastAlso` to define other view elements that should be also converted to `align=right` attribute. | ||
* // Conversion from and to a model attribute key whose value is an enum (`align=right|center`). | ||
* // Use `upcastAlso` to define other view elements that should also be converted to the `align=right` attribute. | ||
* conversion.attributeToAttribute( { | ||
@@ -518,13 +518,13 @@ * model: { | ||
* | ||
* `definition.model` parameter specifies what model attribute should be converted from/to. | ||
* The `definition.model` parameter specifies which model attribute should be converted from and to. | ||
* It can be a `{ key, [ values ], [ name ] }` object or a `String`, which will be treated like `{ key: definition.model }`. | ||
* `key` property is the model attribute key to convert from/to. | ||
* `values` are the possible model attribute values. If `values` is not set, model attribute value will be the same as the | ||
* The `key` property is the model attribute key to convert from and to. | ||
* The `values` are the possible model attribute values. If `values` is not set, the model attribute value will be the same as the | ||
* view attribute value. | ||
* If `name` is set, conversion will be set up only for model elements with the given name. | ||
* If `name` is set, the conversion will be set up only for model elements with the given name. | ||
* | ||
* `definition.view` parameter specifies what view attribute should be converted from/to. | ||
* The `definition.view` parameter specifies which view attribute should be converted from and to. | ||
* It can be a `{ key, value, [ name ] }` object or a `String`, which will be treated like `{ key: definition.view }`. | ||
* `key` property is the view attribute key to convert from/to. | ||
* `value` is the view attribute value to convert from/to. If `definition.value` is not set, view attribute value will be | ||
* The `key` property is the view attribute key to convert from and to. | ||
* The `value` is the view attribute value to convert from and to. If `definition.value` is not set, the view attribute value will be | ||
* the same as the model attribute value. | ||
@@ -534,16 +534,16 @@ * If `key` is `'class'`, `value` can be a `String` or an array of `String`s. | ||
* In other cases, `value` is a `String`. | ||
* If `name` is set, conversion will be set up only for model elements with the given name. | ||
* If `definition.model.values` is set, `definition.view` is an object which assigns values from `definition.model.values` | ||
* If `name` is set, the conversion will be set up only for model elements with the given name. | ||
* If `definition.model.values` is set, `definition.view` is an object that assigns values from `definition.model.values` | ||
* to `{ key, value, [ name ] }` objects. | ||
* | ||
* `definition.upcastAlso` specifies which other matching view elements should be also upcast to given model configuration. | ||
* `definition.upcastAlso` specifies which other matching view elements should also be upcast to the given model configuration. | ||
* If `definition.model.values` is set, `definition.upcastAlso` should be an object assigning values from `definition.model.values` | ||
* to {@link module:engine/view/matcher~MatcherPattern}s or arrays of {@link module:engine/view/matcher~MatcherPattern}s. | ||
* | ||
* **Note:** `definition.model` and `definition.view` form should be mirrored, that is the same type of parameters should | ||
* **Note:** `definition.model` and `definition.view` form should be mirrored, so the same types of parameters should | ||
* be given in both parameters. | ||
* | ||
* @param {Object} definition Converter definition. | ||
* @param {String|Object} definition.model Model attribute to convert from/to. | ||
* @param {String|Object} definition.view View attribute to convert from/to. | ||
* @param {Object} definition The converter definition. | ||
* @param {String|Object} definition.model The model attribute to convert from and to. | ||
* @param {String|Object} definition.view The view attribute to convert from and to. | ||
* @param {module:engine/view/matcher~MatcherPattern|Array.<module:engine/view/matcher~MatcherPattern>} [definition.upcastAlso] | ||
@@ -569,6 +569,6 @@ * Any view element matching `definition.upcastAlso` will also be converted to the given model attribute. `definition.upcastAlso` | ||
/** | ||
* Returns dispatchers registered under given group name. | ||
* Returns dispatchers registered under a given group name. | ||
* | ||
* If given group name has not been registered, | ||
* {@link module:utils/ckeditorerror~CKEditorError conversion-for-unknown-group} error is thrown. | ||
* If the given group name has not been registered, the | ||
* {@link module:utils/ckeditorerror~CKEditorError `conversion-for-unknown-group` error} is thrown. | ||
* | ||
@@ -597,21 +597,21 @@ * @private | ||
/** | ||
* Defines how the model should be converted from/to the view. | ||
* Defines how the model should be converted from and to the view. | ||
* | ||
* @typedef {Object} module:engine/conversion/conversion~ConverterDefinition | ||
* | ||
* @property {*} [model] Model conversion definition. Describes model element or model attribute to convert. This parameter differs | ||
* for different functions that accepts `ConverterDefinition`. See the description of a function to learn how to set it. | ||
* @property {module:engine/view/elementdefinition~ElementDefinition|Object} view Definition of a view element to convert from/to. | ||
* If `model` describes multiple values, `view` is an object that assigns those values (`view` object keys) to view element definitions | ||
* @property {*} [model] The model conversion definition. Describes the model element or model attribute to convert. This parameter differs | ||
* for different functions that accept `ConverterDefinition`. See the description of the function to learn how to set it. | ||
* @property {module:engine/view/elementdefinition~ElementDefinition|Object} view The definition of the view element to convert from and | ||
* to. If `model` describes multiple values, `view` is an object that assigns these values (`view` object keys) to view element definitions | ||
* (`view` object values). | ||
* @property {module:engine/view/matcher~MatcherPattern|Array.<module:engine/view/matcher~MatcherPattern>} [upcastAlso] | ||
* Any view element matching `upcastAlso` will also be converted to model. If `model` describes multiple values, `upcastAlso` | ||
* is an object that assigns those values (`upcastAlso` object keys) to {@link module:engine/view/matcher~MatcherPattern}s | ||
* Any view element matching `upcastAlso` will also be converted to the model. If `model` describes multiple values, `upcastAlso` | ||
* is an object that assigns these values (`upcastAlso` object keys) to {@link module:engine/view/matcher~MatcherPattern}s | ||
* (`upcastAlso` object values). | ||
* @property {module:utils/priorities~PriorityString} [priority] Conversion priority. | ||
* @property {module:utils/priorities~PriorityString} [converterPriority] The converter priority. | ||
*/ | ||
// Helper function for `Conversion` `.add()` method. | ||
// Helper function for the `Conversion` `.add()` method. | ||
// | ||
// Calls `conversionHelper` on each dispatcher from the group specified earlier in `.for()` call, effectively | ||
// Calls `conversionHelper` on each dispatcher from the group specified earlier in the `.for()` call, effectively | ||
// adding converters to all specified dispatchers. | ||
@@ -618,0 +618,0 @@ // |
@@ -17,3 +17,3 @@ /** | ||
/** | ||
* Contains downcast (model to view) converters for {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher}. | ||
* Contains downcast (model-to-view) converters for {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher}. | ||
* | ||
@@ -30,3 +30,3 @@ * @module engine/conversion/downcast-converters | ||
* | ||
* downcastElementToElement( { model: 'paragraph', view: 'div', priority: 'high' } ); | ||
* downcastElementToElement( { model: 'paragraph', view: 'div', converterPriority: 'high' } ); | ||
* | ||
@@ -46,8 +46,9 @@ * downcastElementToElement( { | ||
* | ||
* See {@link module:engine/conversion/conversion~Conversion#for} to learn how to add converter to conversion process. | ||
* See {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} to learn how to add a converter | ||
* to the conversion process. | ||
* | ||
* @param {Object} config Conversion configuration. | ||
* @param {String} config.model Name of the model element to convert. | ||
* @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. | ||
* @param {String} config.model The name of the model element to convert. | ||
* @param {module:engine/view/elementdefinition~ElementDefinition|Function} config.view A view element definition or a function | ||
* that takes the model element and view writer as parameters and returns a view container element. | ||
* @returns {Function} Conversion helper. | ||
@@ -61,3 +62,3 @@ */ | ||
return dispatcher => { | ||
dispatcher.on( 'insert:' + config.model, insertElement( config.view ), { priority: config.priority || 'normal' } ); | ||
dispatcher.on( 'insert:' + config.model, insertElement( config.view ), { priority: config.converterPriority || 'normal' } ); | ||
}; | ||
@@ -69,8 +70,8 @@ } | ||
* | ||
* 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. | ||
* This conversion results in wrapping view nodes with a view attribute element. For example, a model text node with | ||
* `"Foo"` as data and the `bold` attribute becomes `<strong>Foo</strong>` in the view. | ||
* | ||
* downcastAttributeToElement( { model: 'bold', view: 'strong' } ); | ||
* | ||
* downcastAttributeToElement( { model: 'bold', view: 'b', priority: 'high' } ); | ||
* downcastAttributeToElement( { model: 'bold', view: 'b', converterPriority: 'high' } ); | ||
* | ||
@@ -123,11 +124,12 @@ * downcastAttributeToElement( { | ||
* | ||
* See {@link module:engine/conversion/conversion~Conversion#for} to learn how to add converter to conversion process. | ||
* See {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} to learn how to add a converter | ||
* to the conversion process. | ||
* | ||
* @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 | ||
* @param {String|Object} config.model The 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 an enumerable. | ||
* @param {module:engine/view/elementdefinition~ElementDefinition|Function|Object} config.view A view element definition or a function | ||
* that takes the 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. | ||
* @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority. | ||
* @returns {Function} Conversion helper. | ||
@@ -156,3 +158,3 @@ */ | ||
return dispatcher => { | ||
dispatcher.on( eventName, wrap( elementCreator ), { priority: config.priority || 'normal' } ); | ||
dispatcher.on( eventName, wrap( elementCreator ), { priority: config.converterPriority || 'normal' } ); | ||
}; | ||
@@ -164,3 +166,3 @@ } | ||
* | ||
* This conversion results in adding an attribute on a view node, basing on an attribute from a model node. For example, | ||
* This conversion results in adding an attribute to 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>`. | ||
@@ -170,3 +172,3 @@ * | ||
* | ||
* downcastAttributeToAttribute( { model: 'source', view: 'href', priority: 'high' } ); | ||
* downcastAttributeToAttribute( { model: 'source', view: 'href', converterPriority: 'high' } ); | ||
* | ||
@@ -203,13 +205,14 @@ * downcastAttributeToAttribute( { | ||
* | ||
* See {@link module:engine/conversion/conversion~Conversion#for} to learn how to add converter to conversion process. | ||
* See {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} to learn how to add a converter | ||
* to the conversion process. | ||
* | ||
* @param {Object} config Conversion configuration. | ||
* @param {String|Object} config.model Key of the attribute to convert from or a `{ key, values, [ name ] }` object describing | ||
* @param {String|Object} config.model The 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 | ||
* @param {String|Object|Function} config.view A view attribute key, or a `{ key, value }` object or a function that takes | ||
* the 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. | ||
* @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority. | ||
* @returns {Function} Conversion helper. | ||
@@ -238,3 +241,3 @@ */ | ||
return dispatcher => { | ||
dispatcher.on( eventName, changeAttribute( elementCreator ), { priority: config.priority || 'normal' } ); | ||
dispatcher.on( eventName, changeAttribute( elementCreator ), { priority: config.converterPriority || 'normal' } ); | ||
}; | ||
@@ -246,4 +249,4 @@ } | ||
* | ||
* This conversion results in creating a view element on the boundaries of the converted marker. If converted marker | ||
* is collapsed, only one element is created. For example, model marker set like this `<paragraph>F[oo b]ar</paragraph>` | ||
* 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, 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. | ||
@@ -253,3 +256,3 @@ * | ||
* | ||
* downcastMarkerToElement( { model: 'search', view: 'search-result', priority: 'high' } ); | ||
* downcastMarkerToElement( { model: 'search', view: 'search-result', converterPriority: 'high' } ); | ||
* | ||
@@ -273,18 +276,18 @@ * downcastMarkerToElement( { | ||
* | ||
* If function is passed as `config.view` parameter, it will be used to generate both boundary elements. The function | ||
* receives `data` object as parameter and should return an instance of {@link module:engine/view/uielement~UIElement view.UIElement}. | ||
* The `data` and `conversionApi` objects are passed from | ||
* If a function is passed as the `config.view` parameter, it will be used to generate both boundary elements. The function | ||
* receives the `data` object as a parameter and should return an instance of the | ||
* {@link module:engine/view/uielement~UIElement view UI element}. The `data` and `conversionApi` objects are passed from | ||
* {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:addMarker}. Additionally, | ||
* `data.isOpening` parameter is passed, which is set to `true` for marker start boundary element, and `false` to | ||
* marker end boundary element. | ||
* the `data.isOpening` parameter is passed, which is set to `true` for the marker start boundary element, and `false` to | ||
* the marker end boundary element. | ||
* | ||
* This kind of conversion is useful for saving data into data base, so it should be used in data conversion pipeline. | ||
* This kind of conversion is useful for saving data into the database, so it should be used in the data conversion pipeline. | ||
* | ||
* See {@link module:engine/conversion/conversion~Conversion#for} to learn how to add converter to conversion process. | ||
* See {@link module:engine/conversion/conversion~Conversion#for} to learn how to add a converter to the conversion process. | ||
* | ||
* @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 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. | ||
* @param {String} config.model The name of the model marker (or model marker group) to convert. | ||
* @param {module:engine/view/elementdefinition~ElementDefinition|Function} config.view A view element definition or a function | ||
* that takes the model marker data as a parameter and returns a view UI element. | ||
* @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority. | ||
* @returns {Function} Conversion helper. | ||
@@ -298,4 +301,4 @@ */ | ||
return dispatcher => { | ||
dispatcher.on( 'addMarker:' + config.model, insertUIElement( config.view ), { priority: config.priority || 'normal' } ); | ||
dispatcher.on( 'removeMarker:' + config.model, removeUIElement( config.view ), { priority: config.priority || 'normal' } ); | ||
dispatcher.on( 'addMarker:' + config.model, insertUIElement( config.view ), { priority: config.converterPriority || 'normal' } ); | ||
dispatcher.on( 'removeMarker:' + config.model, removeUIElement( config.view ), { priority: config.converterPriority || 'normal' } ); | ||
}; | ||
@@ -310,18 +313,18 @@ } | ||
* | ||
* For text nodes, a `span` {@link module:engine/view/attributeelement~AttributeElement} is created and it wraps all text nodes | ||
* in the converted marker range. For example, model marker set like this `<paragraph>F[oo b]ar</paragraph>` becomes | ||
* For text nodes, a `<span>` {@link module:engine/view/attributeelement~AttributeElement} is 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. | ||
* | ||
* {@link module:engine/view/containerelement~ContainerElement} may provide 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, model marker set like this `[<image src="foo.jpg"></image>]` becomes `<img src="foo.jpg" class="comment"></img>` | ||
* {@link module:engine/view/containerelement~ContainerElement} may 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: `[<image src="foo.jpg"></image>]` becomes `<img src="foo.jpg" class="comment"></img>` | ||
* in the view. | ||
* | ||
* For container elements, the conversion is two-step. While the converter processes highlight descriptor and passes it | ||
* to a container element, it is the container element instance itself which applies values from highlight descriptor. | ||
* So, in a sense, converter takes care of stating what should be applied on what, while element decides how to apply that. | ||
* 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. | ||
* | ||
* downcastMarkerToHighlight( { model: 'comment', view: { classes: 'comment' } } ); | ||
* | ||
* downcastMarkerToHighlight( { model: 'comment', view: { classes: 'new-comment' }, priority: 'high' } ); | ||
* downcastMarkerToHighlight( { model: 'comment', view: { classes: 'new-comment' }, converterPriority: 'high' } ); | ||
* | ||
@@ -331,3 +334,3 @@ * downcastMarkerToHighlight( { | ||
* view: data => { | ||
* // Assuming that marker name is in a form of comment:commentType. | ||
* // Assuming that the marker name is in a form of comment:commentType. | ||
* const commentType = data.markerName.split( ':' )[ 1 ]; | ||
@@ -341,13 +344,14 @@ * | ||
* | ||
* If function is passed as `config.view` parameter, it will be used to generate highlight descriptor. The function | ||
* receives `data` object as parameter and should return a {@link module:engine/conversion/downcast-converters~HighlightDescriptor}. | ||
* If a function is passed as the `config.view` parameter, it will be used to generate the highlight descriptor. The function | ||
* receives the `data` object as a parameter and should return a | ||
* {@link module:engine/conversion/downcast-converters~HighlightDescriptor highlight descriptor}. | ||
* 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. | ||
* See {@link module:engine/conversion/conversion~Conversion#for} to learn how to add a converter to the conversion process. | ||
* | ||
* @param {Object} config Conversion configuration. | ||
* @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} [config.priority='normal'] Converter priority. | ||
* @param {String} config.model The name of the model marker (or model marker group) to convert. | ||
* @param {module:engine/conversion/downcast-converters~HighlightDescriptor|Function} config.view A highlight descriptor | ||
* that will be used for highlighting or a function that takes the model marker data as a parameter and returns a highlight descriptor. | ||
* @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority. | ||
* @returns {Function} Conversion helper. | ||
@@ -357,10 +361,10 @@ */ | ||
return dispatcher => { | ||
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' } ); | ||
dispatcher.on( 'addMarker:' + config.model, highlightText( config.view ), { priority: config.converterPriority || 'normal' } ); | ||
dispatcher.on( 'addMarker:' + config.model, highlightElement( config.view ), { priority: config.converterPriority || 'normal' } ); | ||
dispatcher.on( 'removeMarker:' + config.model, removeHighlight( config.view ), { priority: config.converterPriority || 'normal' } ); | ||
}; | ||
} | ||
// 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). | ||
// Takes `config.view`, and if it is an {@link module:engine/view/elementdefinition~ElementDefinition}, converts it | ||
// to a function (because lower level converters accept only element creator functions). | ||
// | ||
@@ -379,3 +383,3 @@ // @param {module:engine/view/elementdefinition~ElementDefinition|Function} view View configuration. | ||
// Creates view element instance from provided viewElementDefinition and class. | ||
// Creates a view element instance from the provided {@link module:engine/view/elementdefinition~ElementDefinition} and class. | ||
// | ||
@@ -393,10 +397,15 @@ // @param {module:engine/view/elementdefinition~ElementDefinition} viewElementDefinition | ||
let element; | ||
const attributes = Object.assign( {}, viewElementDefinition.attributes ); | ||
if ( viewElementType == 'container' ) { | ||
element = viewWriter.createContainerElement( viewElementDefinition.name, Object.assign( {}, viewElementDefinition.attributes ) ); | ||
element = viewWriter.createContainerElement( viewElementDefinition.name, attributes ); | ||
} else if ( viewElementType == 'attribute' ) { | ||
element = viewWriter.createAttributeElement( viewElementDefinition.name, Object.assign( {}, viewElementDefinition.attributes ) ); | ||
const options = { | ||
priority: viewElementDefinition.priority || ViewAttributeElement.DEFAULT_PRIORITY | ||
}; | ||
element = viewWriter.createAttributeElement( viewElementDefinition.name, attributes, options ); | ||
} else { | ||
// 'ui'. | ||
element = viewWriter.createUIElement( viewElementDefinition.name, Object.assign( {}, viewElementDefinition.attributes ) ); | ||
element = viewWriter.createUIElement( viewElementDefinition.name, attributes ); | ||
} | ||
@@ -443,4 +452,4 @@ | ||
// 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. | ||
// Takes the configuration, adds default parameters if they do not exist and normalizes other parameters to be used in downcast converters | ||
// for generating a view attribute. | ||
// | ||
@@ -467,10 +476,10 @@ // @param {Object} view View configuration. | ||
/** | ||
* Function factory, creates a converter that converts node insertion changes from the model to the view. | ||
* Passed function 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/element~Element}. | ||
* The result of the function will be inserted to the view. | ||
* Function factory that creates a converter which converts node insertion changes from the model to the view. | ||
* The function passed will be provided with all the parameters of the dispatcher's | ||
* {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:insert `insert` event}. | ||
* It is expected that the function returns an {@link module:engine/view/element~Element}. | ||
* The result of the function will be inserted into the view. | ||
* | ||
* The converter automatically consumes corresponding value from consumables list, stops the event (see | ||
* {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher}) and bind model and view elements. | ||
* The converter automatically consumes the corresponding value from the consumables list, stops the event (see | ||
* {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher}) and binds the model and view elements. | ||
* | ||
@@ -483,3 +492,3 @@ * downcastDispatcher.on( | ||
* | ||
* // Do something fancy with myElem using `modelItem` or other parameters. | ||
* // Do something fancy with `myElem` using `modelItem` or other parameters. | ||
* | ||
@@ -513,5 +522,5 @@ * return myElem; | ||
/** | ||
* Function factory, creates a default downcast converter for text insertion changes. | ||
* Function factory that creates a default downcast converter for text insertion changes. | ||
* | ||
* The converter automatically consumes corresponding value from consumables list and stops the event (see | ||
* The converter automatically consumes the corresponding value from the consumables list and stops the event (see | ||
* {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher}). | ||
@@ -538,3 +547,3 @@ * | ||
/** | ||
* Function factory, creates a default downcast converter for node remove changes. | ||
* Function factory that creates a default downcast converter for node remove changes. | ||
* | ||
@@ -567,13 +576,13 @@ * modelDispatcher.on( 'remove', remove() ); | ||
/** | ||
* Function factory, creates a converter that converts marker adding change to the view ui element. | ||
* Function factory that creates a converter which converts marker adding change to the | ||
* {@link module:engine/view/uielement~UIElement view UI element}. | ||
* | ||
* The view ui element, that will be added to the view, depends on passed parameter. See {@link ~insertElement}. | ||
* In a case of a non-collapsed range, the ui element will not wrap nodes but separate elements will be placed at the beginning | ||
* The view UI element that will be added to the view depends on the passed parameter. See {@link ~insertElement}. | ||
* In case of a non-collapsed range, the UI element will not wrap nodes but separate elements will be placed at the beginning | ||
* and at the end of the range. | ||
* | ||
* This converter binds created {@link module:engine/view/uielement~UIElement}s with marker name using | ||
* the {@link module:engine/conversion/mapper~Mapper#bindElementToMarker}. | ||
* This converter binds created UI elements with the marker name using {@link module:engine/conversion/mapper~Mapper#bindElementToMarker}. | ||
* | ||
* @param {module:engine/view/uielement~UIElement|Function} elementCreator View ui element or a function returning a view element | ||
* which will be inserted. | ||
* @param {module:engine/view/uielement~UIElement|Function} elementCreator A view UI element or a function returning the view element | ||
* that will be inserted. | ||
* @returns {Function} Insert element event converter. | ||
@@ -629,8 +638,8 @@ */ | ||
/** | ||
* Function factory, returns a default downcast converter for removing {@link module:engine/view/uielement~UIElement ui element} | ||
* Function factory that returns a default downcast converter for removing a {@link module:engine/view/uielement~UIElement UI element} | ||
* basing on marker remove change. | ||
* | ||
* This converter unbinds elements from marker name. | ||
* This converter unbinds elements from the marker name. | ||
* | ||
* @returns {Function} Remove ui element converter. | ||
* @returns {Function} Removed UI element converter. | ||
*/ | ||
@@ -658,15 +667,15 @@ export function removeUIElement() { | ||
/** | ||
* Function factory, creates a converter that converts set/change/remove attribute changes from the model to the view. | ||
* Function factory that creates a converter which converts set/change/remove attribute changes from the model to the view. | ||
* | ||
* Attributes from model are converted to the view element attributes in the view. You may provide a custom function to generate | ||
* a key-value attribute pair to add/change/remove. If not provided, model attributes will be converted to view elements | ||
* attributes on 1-to-1 basis. | ||
* Attributes from the model are converted to the view element attributes in the view. You may provide a custom function to generate | ||
* a key-value attribute pair to add/change/remove. If not provided, model attributes will be converted to view element | ||
* attributes on a one-to-one basis. | ||
* | ||
* **Note:** Provided attribute creator should always return the same `key` for given attribute from the model. | ||
* **Note:** The provided attribute creator should always return the same `key` for a given attribute from the model. | ||
* | ||
* The converter automatically consumes corresponding value from consumables list and stops the event (see | ||
* The converter automatically consumes the corresponding value from the consumables list and stops the event (see | ||
* {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher}). | ||
* | ||
* modelDispatcher.on( 'attribute:customAttr:myElem', changeAttribute( ( value, data ) => { | ||
* // Change attribute key from `customAttr` to `class` in view. | ||
* // Change attribute key from `customAttr` to `class` in the view. | ||
* const key = 'class'; | ||
@@ -680,3 +689,3 @@ * let value = data.attributeNewValue; | ||
* | ||
* // Return key-value pair. | ||
* // Return the key-value pair. | ||
* return { key, value }; | ||
@@ -686,4 +695,4 @@ * } ) ); | ||
* @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 model attribute value as first parameter and additional data about the change as a second parameter. | ||
* represent the attribute key and attribute value to be set on a {@link module:engine/view/element~Element view element}. | ||
* The function is passed the model attribute value as the first parameter and additional data about the change as the second parameter. | ||
* @returns {Function} Set/change attribute converter. | ||
@@ -750,8 +759,8 @@ */ | ||
/** | ||
* Function factory, creates a converter that converts set/change/remove attribute changes from the model to the view. | ||
* Also can be used to convert selection attributes. In that case, an empty attribute element will be created and the | ||
* Function factory that creates a converter which converts set/change/remove attribute changes from the model to the view. | ||
* It can also be used to convert selection attributes. In that case, an empty attribute element will be created and the | ||
* selection will be put inside it. | ||
* | ||
* Attributes from model are converted to a view element that will be wrapping those view nodes that are bound to | ||
* model elements having given attribute. This is useful for attributes like `bold`, which may be set on text nodes in model | ||
* Attributes from the model are converted to a view element that will be wrapping these view nodes that are bound to | ||
* model elements having the given attribute. This is useful for attributes like `bold` that may be set on text nodes in the model | ||
* but are represented as an element in the view: | ||
@@ -764,9 +773,9 @@ * | ||
* | ||
* Passed `Function` will be provided with attribute value and then all the parameters of the | ||
* {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:attribute attribute event}. | ||
* It's expected that the function returns a {@link module:engine/view/element~Element}. | ||
* Passed `Function` will be provided with the attribute value and then all the parameters of the | ||
* {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:attribute `attribute` event}. | ||
* It is expected that the function returns an {@link module:engine/view/element~Element}. | ||
* The result of the function will be the wrapping element. | ||
* When provided `Function` does not return element, then will be no conversion. | ||
* When the provided `Function` does not return any element, no conversion will take place. | ||
* | ||
* The converter automatically consumes corresponding value from consumables list, stops the event (see | ||
* The converter automatically consumes the corresponding value from the consumables list and stops the event (see | ||
* {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher}). | ||
@@ -778,3 +787,3 @@ * | ||
* | ||
* @param {Function} elementCreator Function returning a view element, which will be used for wrapping. | ||
* @param {Function} elementCreator Function returning a view element that will be used for wrapping. | ||
* @returns {Function} Set/change attribute converter. | ||
@@ -822,18 +831,18 @@ */ | ||
/** | ||
* Function factory, creates converter that converts text inside marker's range. Converter wraps the text with | ||
* {@link module:engine/view/attributeelement~AttributeElement} created from provided descriptor. | ||
* Function factory that creates a converter which converts the text inside marker's range. The converter wraps the text with | ||
* {@link module:engine/view/attributeelement~AttributeElement} created from the provided descriptor. | ||
* See {link module:engine/conversion/downcast-converters~createViewElementFromHighlightDescriptor}. | ||
* | ||
* Also can be used to convert selection that is inside a marker. In that case, an empty attribute element will be | ||
* It can also be used to convert the selection that is inside a marker. In that case, an empty attribute element will be | ||
* created and the selection will be put inside it. | ||
* | ||
* If the highlight descriptor will not provide `priority` property, `10` will be used. | ||
* If the highlight descriptor does not provide the `priority` property, `10` will be used. | ||
* | ||
* If the highlight descriptor will not provide `id` property, name of the marker will be used. | ||
* If the highlight descriptor does not provide the `id` property, the name of the marker will be used. | ||
* | ||
* This converter binds created {@link module:engine/view/attributeelement~AttributeElement}s with marker name using | ||
* the {@link module:engine/conversion/mapper~Mapper#bindElementToMarker}. | ||
* This converter binds the created {@link module:engine/view/attributeelement~AttributeElement attribute elemens} with the marker name | ||
* using the {@link module:engine/conversion/mapper~Mapper#bindElementToMarker} method. | ||
* | ||
* @param {module:engine/conversion/downcast-converters~HighlightDescriptor|Function} highlightDescriptor | ||
* @return {Function} | ||
* @returns {Function} | ||
*/ | ||
@@ -884,20 +893,20 @@ export function highlightText( highlightDescriptor ) { | ||
/** | ||
* Converter function factory. Creates a function which applies the marker's highlight to an element inside the marker's range. | ||
* Converter function factory. It creates a function which applies the marker's highlight to an element inside the marker's range. | ||
* | ||
* The converter checks if an element has `addHighlight` function stored as | ||
* The converter checks if an element has the `addHighlight` function stored as a | ||
* {@link module:engine/view/element~Element#_setCustomProperty custom property} and, if so, uses it to apply the highlight. | ||
* In such case converter will consume all element's children, assuming that they were handled by element itself. | ||
* In such case the converter will consume all element's children, assuming that they were handled by the element itself. | ||
* | ||
* When `addHighlight` custom property is not present, element is not converted in any special way. | ||
* This means that converters will proceed to convert element's child nodes. | ||
* When the `addHighlight` custom property is not present, the element is not converted in any special way. | ||
* This means that converters will proceed to convert the element's child nodes. | ||
* | ||
* If the highlight descriptor will not provide `priority` property, `10` will be used. | ||
* If the highlight descriptor does not provide the `priority` property, `10` will be used. | ||
* | ||
* If the highlight descriptor will not provide `id` property, name of the marker will be used. | ||
* If the highlight descriptor does not provide the `id` property, the name of the marker will be used. | ||
* | ||
* This converter binds altered {@link module:engine/view/containerelement~ContainerElement}s with marker name using | ||
* the {@link module:engine/conversion/mapper~Mapper#bindElementToMarker}. | ||
* This converter binds altered {@link module:engine/view/containerelement~ContainerElement container elements} with the marker name using | ||
* the {@link module:engine/conversion/mapper~Mapper#bindElementToMarker} method. | ||
* | ||
* @param {module:engine/conversion/downcast-converters~HighlightDescriptor|Function} highlightDescriptor | ||
* @return {Function} | ||
* @returns {Function} | ||
*/ | ||
@@ -943,24 +952,24 @@ export function highlightElement( highlightDescriptor ) { | ||
/** | ||
* Function factory, creates a converter that converts removing model marker to the view. | ||
* Function factory that creates a converter which converts the removing model marker to the view. | ||
* | ||
* Both text nodes and elements are handled by this converter but they are handled a bit differently. | ||
* | ||
* Text nodes are unwrapped using {@link module:engine/view/attributeelement~AttributeElement} created from provided | ||
* highlight descriptor. See {link module:engine/conversion/downcast-converters~highlightDescriptorToAttributeElement}. | ||
* Text nodes are unwrapped using the {@link module:engine/view/attributeelement~AttributeElement attribute element} created from the | ||
* provided highlight descriptor. See {link module:engine/conversion/downcast-converters~HighlightDescriptor}. | ||
* | ||
* For elements, the converter checks if an element has `removeHighlight` function stored as | ||
* For elements, the converter checks if an element has the `removeHighlight` function stored as a | ||
* {@link module:engine/view/element~Element#_setCustomProperty custom property}. If so, it uses it to remove the highlight. | ||
* In such case, children of that element will not be converted. | ||
* In such case, the children of that element will not be converted. | ||
* | ||
* When `removeHighlight` is not present, element is not converted in any special way. | ||
* Instead converter will proceed to convert element's child nodes. | ||
* When `removeHighlight` is not present, the element is not converted in any special way. | ||
* The converter will proceed to convert the element's child nodes instead. | ||
* | ||
* If the highlight descriptor will not provide `priority` property, `10` will be used. | ||
* If the highlight descriptor does not provide the `priority` property, `10` will be used. | ||
* | ||
* If the highlight descriptor will not provide `id` property, name of the marker will be used. | ||
* If the highlight descriptor does not provide the `id` property, the name of the marker will be used. | ||
* | ||
* This converter unbinds elements from marker name. | ||
* This converter unbinds elements from the marker name. | ||
* | ||
* @param {module:engine/conversion/downcast-converters~HighlightDescriptor|Function} highlightDescriptor | ||
* @return {Function} | ||
* @returns {Function} | ||
*/ | ||
@@ -1030,5 +1039,5 @@ export function removeHighlight( highlightDescriptor ) { | ||
/** | ||
* Creates `span` {@link module:engine/view/attributeelement~AttributeElement view attribute element} from information | ||
* provided by {@link module:engine/conversion/downcast-converters~HighlightDescriptor} object. If priority | ||
* is not provided in descriptor - default priority will be used. | ||
* Creates a `<span>` {@link module:engine/view/attributeelement~AttributeElement view attribute element} from the information | ||
* provided by the {@link module:engine/conversion/downcast-converters~HighlightDescriptor highlight descriptor} object. If a priority | ||
* is not provided in the descriptor, the default priority will be used. | ||
* | ||
@@ -1055,33 +1064,34 @@ * @param {module:engine/conversion/downcast-converters~HighlightDescriptor} descriptor | ||
/** | ||
* Object describing how the marker highlight should be represented in the view. | ||
* An object describing how the marker highlight should be represented in the view. | ||
* | ||
* Each text node contained in a highlighted range will be wrapped in a `span` {@link module:engine/view/attributeelement~AttributeElement} | ||
* with CSS class(es), attributes and priority described by this object. | ||
* Each text node contained in a highlighted range will be wrapped in a `<span>` | ||
* {@link module:engine/view/attributeelement~AttributeElement view attribute element} with CSS class(es), attributes and a priority | ||
* described by this object. | ||
* | ||
* Additionally, each {@link module:engine/view/containerelement~ContainerElement} can handle displaying the highlight separately | ||
* by providing `addHighlight` and `removeHighlight` custom properties. In this case: | ||
* Additionally, each {@link module:engine/view/containerelement~ContainerElement container element} can handle displaying the highlight | ||
* separately by providing the `addHighlight` and `removeHighlight` custom properties. In this case: | ||
* | ||
* * `HighlightDescriptor` object is passed to the `addHighlight` function upon conversion and should be used to apply the highlight to | ||
* the element, | ||
* * descriptor `id` is passed to the `removeHighlight` function upon conversion and should be used to remove the highlight of given | ||
* id from the element. | ||
* * The `HighlightDescriptor` object is passed to the `addHighlight` function upon conversion and should be used to apply the highlight to | ||
* the element. | ||
* * The descriptor `id` is passed to the `removeHighlight` function upon conversion and should be used to remove the highlight with the | ||
* given ID from the element. | ||
* | ||
* @typedef {Object} module:engine/conversion/downcast-converters~HighlightDescriptor | ||
* | ||
* @property {String|Array.<String>} classes CSS class or array of classes to set. If descriptor is used to | ||
* create {@link module:engine/view/attributeelement~AttributeElement} over text nodes, those classes will be set | ||
* on that {@link module:engine/view/attributeelement~AttributeElement}. If descriptor is applied to an element, | ||
* usually those class will be set on that element, however this depends on how the element converts the descriptor. | ||
* @property {String|Array.<String>} classes A CSS class or an array of classes to set. If the descriptor is used to | ||
* create an {@link module:engine/view/attributeelement~AttributeElement attribute element} over text nodes, these classes will be set | ||
* on that attribute element. If the descriptor is applied to an element, usually these classes will be set on that element, however, | ||
* this depends on how the element converts the descriptor. | ||
* | ||
* @property {String} [id] Descriptor identifier. If not provided, defaults to converted marker's name. | ||
* @property {String} [id] Descriptor identifier. If not provided, it defaults to the converted marker's name. | ||
* | ||
* @property {Number} [priority] Descriptor priority. If not provided, defaults to `10`. If descriptor is used to create | ||
* {@link module:engine/view/attributeelement~AttributeElement}, it will be that element's | ||
* {@link module:engine/view/attributeelement~AttributeElement#priority}. If descriptor is applied to an element, | ||
* @property {Number} [priority] Descriptor priority. If not provided, it defaults to `10`. If the descriptor is used to create | ||
* an {@link module:engine/view/attributeelement~AttributeElement attribute element}, it will be that element's | ||
* {@link module:engine/view/attributeelement~AttributeElement#priority priority}. If the descriptor is applied to an element, | ||
* the priority will be used to determine which descriptor is more important. | ||
* | ||
* @property {Object} [attributes] Attributes to set. If descriptor is used to create | ||
* {@link module:engine/view/attributeelement~AttributeElement} over text nodes, those attributes will be set on that | ||
* {@link module:engine/view/attributeelement~AttributeElement}. If descriptor is applied to an element, usually those | ||
* attributes will be set on that element, however this depends on how the element converts the descriptor. | ||
* @property {Object} [attributes] Attributes to set. If the descriptor is used to create | ||
* an {@link module:engine/view/attributeelement~AttributeElement attribute element} over text nodes, these attributes will be set on that | ||
* attribute element. If the descriptor is applied to an element, usually these attributes will be set on that element, however, | ||
* this depends on how the element converts the descriptor. | ||
*/ |
@@ -9,3 +9,3 @@ /** | ||
* {@link module:engine/view/documentselection~DocumentSelection view selection} converters for | ||
* {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher}. | ||
* {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher downcast dispatcher}. | ||
* | ||
@@ -16,5 +16,5 @@ * @module engine/conversion/downcast-selection-converters | ||
/** | ||
* Function factory, creates a converter that converts non-collapsed {@link module:engine/model/selection~Selection model selection} to | ||
* {@link module:engine/view/documentselection~DocumentSelection view selection}. The converter consumes appropriate | ||
* value from `consumable` object and maps model positions from selection to view positions. | ||
* Function factory that creates a converter which converts a non-collapsed {@link module:engine/model/selection~Selection model selection} | ||
* to a {@link module:engine/view/documentselection~DocumentSelection view selection}. The converter consumes appropriate | ||
* value from the `consumable` object and maps model positions from the selection to view positions. | ||
* | ||
@@ -49,5 +49,5 @@ * modelDispatcher.on( 'selection', convertRangeSelection() ); | ||
/** | ||
* Function factory, creates a converter that converts collapsed {@link module:engine/model/selection~Selection model selection} to | ||
* {@link module:engine/view/documentselection~DocumentSelection view selection}. The converter consumes appropriate | ||
* value from `consumable` object, maps model selection position to view position and breaks | ||
* Function factory that creates a converter which converts a collapsed {@link module:engine/model/selection~Selection model selection} to | ||
* a {@link module:engine/view/documentselection~DocumentSelection view selection}. The converter consumes appropriate | ||
* value from the `consumable` object, maps the model selection position to the view position and breaks | ||
* {@link module:engine/view/attributeelement~AttributeElement attribute elements} at the selection position. | ||
@@ -57,3 +57,3 @@ * | ||
* | ||
* Example of view state before and after converting collapsed selection: | ||
* An example of the view state before and after converting the collapsed selection: | ||
* | ||
@@ -63,5 +63,5 @@ * <p><strong>f^oo<strong>bar</p> | ||
* | ||
* By breaking attribute elements like `<strong>`, selection is in correct element. Then, when selection attribute is | ||
* converted, the broken attributes might be merged again, or the position where the selection is may be wrapped | ||
* in different, appropriate attribute elements. | ||
* By breaking attribute elements like `<strong>`, the selection is in a correct element. Then, when the selection attribute is | ||
* converted, broken attributes might be merged again, or the position where the selection is may be wrapped | ||
* with different, appropriate attribute elements. | ||
* | ||
@@ -95,5 +95,5 @@ * See also {@link module:engine/conversion/downcast-selection-converters~clearAttributes} which does a clean-up | ||
/** | ||
* Function factory, creates a converter that clears artifacts after the previous | ||
* Function factory that creates a converter which clears artifacts after the previous | ||
* {@link module:engine/model/selection~Selection model selection} conversion. It removes all empty | ||
* {@link module:engine/view/attributeelement~AttributeElement view attribute elements} and merge sibling attributes at all start and end | ||
* {@link module:engine/view/attributeelement~AttributeElement view attribute elements} and merges sibling attributes at all start and end | ||
* positions of all ranges. | ||
@@ -115,3 +115,3 @@ * | ||
* See {@link module:engine/conversion/downcast-selection-converters~convertCollapsedSelection} | ||
* which do the opposite by breaking attributes in the selection position. | ||
* which does the opposite by breaking attributes in the selection position. | ||
* | ||
@@ -118,0 +118,0 @@ * @returns {Function} Selection converter. |
@@ -29,3 +29,3 @@ /** | ||
* | ||
* upcastElementToElement( { view: 'p', model: 'paragraph', priority: 'high' } ); | ||
* upcastElementToElement( { view: 'p', model: 'paragraph', converterPriority: 'high' } ); | ||
* | ||
@@ -56,3 +56,3 @@ * upcastElementToElement( { | ||
* instance or a function that takes a view element and returns a model element. The model element will be inserted in the model. | ||
* @param {module:utils/priorities~PriorityString} [config.priority='normal'] Converter priority. | ||
* @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority. | ||
* @returns {Function} Conversion helper. | ||
@@ -69,3 +69,3 @@ */ | ||
return dispatcher => { | ||
dispatcher.on( eventName, converter, { priority: config.priority || 'normal' } ); | ||
dispatcher.on( eventName, converter, { priority: config.converterPriority || 'normal' } ); | ||
}; | ||
@@ -84,3 +84,3 @@ } | ||
* | ||
* upcastElementToAttribute( { view: 'strong', model: 'bold', priority: 'high' } ); | ||
* upcastElementToAttribute( { view: 'strong', model: 'bold', converterPriority: 'high' } ); | ||
* | ||
@@ -137,3 +137,3 @@ * upcastElementToAttribute( { | ||
* If `String` is given, the model attribute value will be set to `true`. | ||
* @param {module:utils/priorities~PriorityString} [config.priority='normal'] Converter priority. | ||
* @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority. | ||
* @returns {Function} Conversion helper. | ||
@@ -152,3 +152,3 @@ */ | ||
return dispatcher => { | ||
dispatcher.on( eventName, converter, { priority: config.priority || 'normal' } ); | ||
dispatcher.on( eventName, converter, { priority: config.converterPriority || 'normal' } ); | ||
}; | ||
@@ -169,3 +169,3 @@ } | ||
* | ||
* upcastAttributeToAttribute( { view: { key: 'src' }, model: 'source', priority: 'normal' } ); | ||
* upcastAttributeToAttribute( { view: { key: 'src' }, model: 'source', converterPriority: 'normal' } ); | ||
* | ||
@@ -219,3 +219,3 @@ * upcastAttributeToAttribute( { | ||
* If `String` is given, the model attribute value will be same as view attribute value. | ||
* @param {module:utils/priorities~PriorityString} [config.priority='low'] Converter priority. | ||
* @param {module:utils/priorities~PriorityString} [config.converterPriority='low'] Converter priority. | ||
* @returns {Function} Conversion helper. | ||
@@ -237,3 +237,3 @@ */ | ||
return dispatcher => { | ||
dispatcher.on( 'element', converter, { priority: config.priority || 'low' } ); | ||
dispatcher.on( 'element', converter, { priority: config.converterPriority || 'low' } ); | ||
}; | ||
@@ -252,3 +252,3 @@ } | ||
* | ||
* upcastElementToMarker( { view: 'marker-search', model: 'search', priority: 'high' } ); | ||
* upcastElementToMarker( { view: 'marker-search', model: 'search', converterPriority: 'high' } ); | ||
* | ||
@@ -276,3 +276,3 @@ * upcastElementToMarker( { | ||
* a model marker name. | ||
* @param {module:utils/priorities~PriorityString} [config.priority='normal'] Converter priority. | ||
* @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority. | ||
* @returns {Function} Conversion helper. | ||
@@ -279,0 +279,0 @@ */ |
@@ -16,3 +16,2 @@ /** | ||
import Model from '../model/model'; | ||
import Batch from '../model/batch'; | ||
import ModelRange from '../model/range'; | ||
@@ -98,3 +97,2 @@ import ModelPosition from '../model/position'; | ||
const modelRoot = model.document.getRoot( options.rootName || 'main' ); | ||
const batch = new Batch( options.batchType || 'transparent' ); | ||
@@ -116,3 +114,3 @@ // Parse data string to model. | ||
model.enqueueChange( batch, writer => { | ||
model.change( writer => { | ||
// Replace existing model in document by new one. | ||
@@ -119,0 +117,0 @@ writer.remove( ModelRange.createIn( modelRoot ) ); |
@@ -75,3 +75,3 @@ /** | ||
* @param {module:engine/model/delta/delta~Delta} delta A delta to add. | ||
* @return {module:engine/model/delta/delta~Delta} An added delta. | ||
* @returns {module:engine/model/delta/delta~Delta} An added delta. | ||
*/ | ||
@@ -78,0 +78,0 @@ addDelta( delta ) { |
@@ -114,3 +114,3 @@ /** | ||
/** | ||
* Buffers a given operation. An operation has to be buffered before it is executed. | ||
* Buffers the given operation. An operation has to be buffered before it is executed. | ||
* | ||
@@ -175,3 +175,3 @@ * Operation type is checked and it is checked which nodes it will affect. These nodes are then stored in `Differ` | ||
this.bufferMarkerChange( marker.name, markerRange, markerRange ); | ||
this.bufferMarkerChange( marker.name, markerRange, markerRange, marker.affectsData ); | ||
} | ||
@@ -188,3 +188,3 @@ | ||
/** | ||
* Buffers marker change. | ||
* Buffers a marker change. | ||
* | ||
@@ -195,4 +195,5 @@ * @param {String} markerName The name of the marker that changed. | ||
* @param {module:engine/model/range~Range|null} newRange Marker range after the change or `null` if the marker was removed. | ||
* @param {Boolean} affectsData Flag indicating whether marker affects the editor data. | ||
*/ | ||
bufferMarkerChange( markerName, oldRange, newRange ) { | ||
bufferMarkerChange( markerName, oldRange, newRange, affectsData ) { | ||
const buffered = this._changedMarkers.get( markerName ); | ||
@@ -203,6 +204,8 @@ | ||
oldRange, | ||
newRange | ||
newRange, | ||
affectsData | ||
} ); | ||
} else { | ||
buffered.newRange = newRange; | ||
buffered.affectsData = affectsData; | ||
@@ -252,2 +255,24 @@ if ( buffered.oldRange == null && buffered.newRange == null ) { | ||
/** | ||
* Checks whether some of the buffered changes affect the editor data. | ||
* | ||
* Types of changes which affect the editor data: | ||
* | ||
* * model structure changes, | ||
* * attribute changes, | ||
* * changes of markers which were defined as `affectingData`. | ||
* | ||
* @returns {Boolean} | ||
*/ | ||
hasDataChanges() { | ||
for ( const [ , change ] of this._changedMarkers ) { | ||
if ( change.affectsData ) { | ||
return true; | ||
} | ||
} | ||
// If markers do not affect the data, check whether there are some changes in elements. | ||
return this._changesInElement.size > 0; | ||
} | ||
/** | ||
* Calculates the diff between the old model tree state (the state before the first buffered operations since the last {@link #reset} | ||
@@ -254,0 +279,0 @@ * call) and the new model tree state (actual one). It should be called after all buffered operations are executed. |
@@ -26,6 +26,15 @@ /** | ||
/** | ||
* Document tree model describes all editable data in the editor. It may contain multiple | ||
* {@link module:engine/model/document~Document#roots root elements}. For example, if the editor has multiple editable areas, | ||
* each area will be represented by a separate root. | ||
* Data model's document. It contains the model's structure, its selection and the history of changes. | ||
* | ||
* Read more about working with the model in | ||
* {@glink framework/guides/architecture/editing-engine#model introduction to the the editing engine's architecture}. | ||
* | ||
* Usually, the document contains just one {@link module:engine/model/document~Document#roots root element}, so | ||
* you can retrieve it by just calling {@link module:engine/model/document~Document#getRoot} without specifying its name: | ||
* | ||
* model.document.getRoot(); // -> returns the main root | ||
* | ||
* However, the document may contain multiple roots – e.g. when the editor has multiple editable areas | ||
* (e.g. a title and a body of a message). | ||
* | ||
* @mixes module:utils/emittermixin~EmitterMixin | ||
@@ -50,2 +59,3 @@ */ | ||
* operations are applied on a proper document version. | ||
* | ||
* If the {@link module:engine/model/operation/operation~Operation#baseVersion base version} does not match the document version, | ||
@@ -70,3 +80,3 @@ * a {@link module:utils/ckeditorerror~CKEditorError model-document-applyOperation-wrong-version} error is thrown. | ||
/** | ||
* The selection done on this document. | ||
* The selection in this document. | ||
* | ||
@@ -151,4 +161,5 @@ * @readonly | ||
// Wait for `_change` event from model, which signalizes that outermost change block has finished. | ||
// When this happens, check if there were any changes done on document, and if so, call post fixers, | ||
// When this happens, check if there were any changes done on document, and if so, call post-fixers, | ||
// fire `change` event for features and conversion and then reset the differ. | ||
// Fire `change:data` event when at least one operation or buffered marker changes the data. | ||
this.listenTo( model, '_change', ( evt, writer ) => { | ||
@@ -158,3 +169,7 @@ if ( !this.differ.isEmpty || hasSelectionChanged ) { | ||
this.fire( 'change', writer.batch ); | ||
if ( this.differ.hasDataChanges() ) { | ||
this.fire( 'change:data', writer.batch ); | ||
} else { | ||
this.fire( 'change', writer.batch ); | ||
} | ||
@@ -171,3 +186,3 @@ this.differ.reset(); | ||
// Whenever marker is updated, buffer that change. | ||
this.differ.bufferMarkerChange( marker.name, oldRange, newRange ); | ||
this.differ.bufferMarkerChange( marker.name, oldRange, newRange, marker.affectsData ); | ||
@@ -177,3 +192,3 @@ if ( oldRange === null ) { | ||
marker.on( 'change', ( evt, oldRange ) => { | ||
this.differ.bufferMarkerChange( marker.name, oldRange, marker.getRange() ); | ||
this.differ.bufferMarkerChange( marker.name, oldRange, marker.getRange(), marker.affectsData ); | ||
} ); | ||
@@ -386,12 +401,11 @@ } | ||
* model.document.on( 'change', () => { | ||
* console.log( 'The Document has changed!' ); | ||
* console.log( 'The document has changed!' ); | ||
* } ); | ||
* | ||
* If, however, you only want to be notified about structure changes, then check whether the | ||
* {@link module:engine/model/differ~Differ differ} contains any changes: | ||
* If, however, you only want to be notified about the data changes, then use the | ||
* {@link module:engine/model/document~Document#event:change:data change:data} event, | ||
* which is fired for document structure changes and marker changes (which affects the data). | ||
* | ||
* model.document.on( 'change', () => { | ||
* if ( model.document.differ.getChanges().length > 0 ) { | ||
* console.log( 'The Document has changed!' ); | ||
* } | ||
* model.document.on( 'change:data', () => { | ||
* console.log( 'The data has changed!' ); | ||
* } ); | ||
@@ -402,2 +416,22 @@ * | ||
*/ | ||
/** | ||
* It is a narrower version of the {@link #event:change} event. It is fired for changes which | ||
* affect the editor data. This is: | ||
* | ||
* * document structure changes, | ||
* * marker changes (which affects the data). | ||
* | ||
* If you want to be notified about the data changes, then listen to this event: | ||
* | ||
* model.document.on( 'change:data', () => { | ||
* console.log( 'The data has changed!' ); | ||
* } ); | ||
* | ||
* If you would like to listen to all document changes, then check out the | ||
* {@link module:engine/model/document~Document#event:change change} event. | ||
* | ||
* @event change:data | ||
* @param {module:engine/model/batch~Batch} batch The batch that was used in the executed changes block. | ||
*/ | ||
} | ||
@@ -404,0 +438,0 @@ |
@@ -319,3 +319,3 @@ /** | ||
// @param {String|module:engine/model/item~Item|Iterable.<module:engine/model/item~Item>} | ||
// @return {Iterable.<module:engine/model/node~Node>} | ||
// @returns {Iterable.<module:engine/model/node~Node>} | ||
function normalize( nodes ) { | ||
@@ -322,0 +322,0 @@ // Separate condition because string is iterable. |
@@ -145,3 +145,3 @@ /** | ||
* @readonly | ||
* @return {Boolean} | ||
* @returns {Boolean} | ||
*/ | ||
@@ -148,0 +148,0 @@ get isGravityOverridden() { |
@@ -322,3 +322,3 @@ /** | ||
// @param {String|module:engine/model/item~Item|Iterable.<String|module:engine/model/item~Item>} | ||
// @return {Iterable.<module:engine/model/node~Node>} | ||
// @returns {Iterable.<module:engine/model/node~Node>} | ||
function normalize( nodes ) { | ||
@@ -325,0 +325,0 @@ // Separate condition because string is iterable. |
@@ -91,5 +91,7 @@ /** | ||
* @param {Boolean} [managedUsingOperations=false] Specifies whether the marker is managed using operations. | ||
* @param {Boolean} [affectsData=false] Specifies whether the marker affects the data produced by the data pipeline | ||
* (is persisted in the editor's data). | ||
* @returns {module:engine/model/markercollection~Marker} `Marker` instance which was added or updated. | ||
*/ | ||
_set( markerOrName, range, managedUsingOperations = false ) { | ||
_set( markerOrName, range, managedUsingOperations = false, affectsData = false ) { | ||
const markerName = markerOrName instanceof Marker ? markerOrName.name : markerOrName; | ||
@@ -112,2 +114,7 @@ const oldMarker = this._markers.get( markerName ); | ||
if ( typeof affectsData === 'boolean' && affectsData != oldMarker.affectsData ) { | ||
oldMarker._affectsData = affectsData; | ||
hasChanged = true; | ||
} | ||
if ( hasChanged ) { | ||
@@ -121,3 +128,3 @@ this.fire( 'update:' + markerName, oldMarker, oldRange, range ); | ||
const liveRange = LiveRange.createFromRange( range ); | ||
const marker = new Marker( markerName, liveRange, managedUsingOperations ); | ||
const marker = new Marker( markerName, liveRange, managedUsingOperations, affectsData ); | ||
@@ -319,4 +326,6 @@ this._markers.set( markerName, marker ); | ||
* @param {Boolean} managedUsingOperations Specifies whether the marker is managed using operations. | ||
* @param {Boolean} affectsData Specifies whether the marker affects the data produced by the data pipeline | ||
* (is persisted in the editor's data). | ||
*/ | ||
constructor( name, liveRange, managedUsingOperations ) { | ||
constructor( name, liveRange, managedUsingOperations, affectsData ) { | ||
/** | ||
@@ -331,5 +340,13 @@ * Marker's name. | ||
/** | ||
* Range marked by the marker. | ||
* | ||
* @protected | ||
* @member {module:engine/model/liverange~LiveRange} | ||
*/ | ||
this._liveRange = this._attachLiveRange( liveRange ); | ||
/** | ||
* Flag indicates if the marker is managed using operations or not. | ||
* | ||
* @protected | ||
* @private | ||
* @member {Boolean} | ||
@@ -340,12 +357,13 @@ */ | ||
/** | ||
* Range marked by the marker. | ||
* Specifies whether the marker affects the data produced by the data pipeline | ||
* (is persisted in the editor's data). | ||
* | ||
* @private | ||
* @member {module:engine/model/liverange~LiveRange} #_liveRange | ||
* @member {Boolean} | ||
*/ | ||
this._liveRange = this._attachLiveRange( liveRange ); | ||
this._affectsData = affectsData; | ||
} | ||
/** | ||
* Returns value of flag indicates if the marker is managed using operations or not. | ||
* A value indicating if the marker is managed using operations. | ||
* See {@link ~Marker marker class description} to learn more about marker types. | ||
@@ -365,2 +383,15 @@ * See {@link module:engine/model/writer~Writer#addMarker}. | ||
/** | ||
* A value indicating if the marker changes the data. | ||
* | ||
* @returns {Boolean} | ||
*/ | ||
get affectsData() { | ||
if ( !this._liveRange ) { | ||
throw new CKEditorError( 'marker-destroyed: Cannot use a destroyed marker instance.' ); | ||
} | ||
return this._affectsData; | ||
} | ||
/** | ||
* Returns current marker start position. | ||
@@ -392,3 +423,3 @@ * | ||
/** | ||
* Returns a range that represents current state of marker. | ||
* Returns a range that represents the current state of the marker. | ||
* | ||
@@ -413,7 +444,7 @@ * Keep in mind that returned value is a {@link module:engine/model/range~Range Range}, not a | ||
/** | ||
* Binds new live range to marker and detach the old one if is attached. | ||
* Binds new live range to the marker and detach the old one if is attached. | ||
* | ||
* @protected | ||
* @param {module:engine/model/liverange~LiveRange} liveRange Live range to attach | ||
* @return {module:engine/model/liverange~LiveRange} Attached live range. | ||
* @returns {module:engine/model/liverange~LiveRange} Attached live range. | ||
*/ | ||
@@ -420,0 +451,0 @@ _attachLiveRange( liveRange ) { |
@@ -29,2 +29,3 @@ /** | ||
import getSelectedContent from './utils/getselectedcontent'; | ||
import { injectSelectionPostFixer } from './utils/selection-post-fixer'; | ||
@@ -115,2 +116,4 @@ /** | ||
} ); | ||
injectSelectionPostFixer( this ); | ||
} | ||
@@ -117,0 +120,0 @@ |
@@ -157,5 +157,10 @@ /** | ||
* | ||
* @error nodelist-offset-out-of-bounds | ||
* @error model-nodelist-offset-out-of-bounds | ||
* @param {Number} offset | ||
* @param {module:engine/model/nodelist~NodeList} nodeList Stringified node list. | ||
*/ | ||
throw new CKEditorError( 'model-nodelist-offset-out-of-bounds: Given offset cannot be found in the node list.' ); | ||
throw new CKEditorError( 'model-nodelist-offset-out-of-bounds: Given offset cannot be found in the node list.', { | ||
offset, | ||
nodeList: this | ||
} ); | ||
} | ||
@@ -162,0 +167,0 @@ |
@@ -23,5 +23,7 @@ /** | ||
* @param {Number|null} baseVersion Document {@link module:engine/model/document~Document#version} on which operation | ||
* @param {Boolean} affectsData Specifies whether the marker operation affects the data produced by the data pipeline | ||
* (is persisted in the editor's data). | ||
* can be applied or `null` if the operation operates on detached (non-document) tree. | ||
*/ | ||
constructor( name, oldRange, newRange, markers, baseVersion ) { | ||
constructor( name, oldRange, newRange, markers, baseVersion, affectsData ) { | ||
super( baseVersion ); | ||
@@ -54,2 +56,11 @@ | ||
/** | ||
* Specifies whether the marker operation affects the data produced by the data pipeline | ||
* (is persisted in the editor's data). | ||
* | ||
* @readonly | ||
* @member {Boolean} | ||
*/ | ||
this.affectsData = affectsData; | ||
/** | ||
* Marker collection on which change should be executed. | ||
@@ -76,3 +87,3 @@ * | ||
clone() { | ||
return new MarkerOperation( this.name, this.oldRange, this.newRange, this._markers, this.baseVersion ); | ||
return new MarkerOperation( this.name, this.oldRange, this.newRange, this._markers, this.baseVersion, this.affectsData ); | ||
} | ||
@@ -86,3 +97,3 @@ | ||
getReversed() { | ||
return new MarkerOperation( this.name, this.newRange, this.oldRange, this._markers, this.baseVersion + 1 ); | ||
return new MarkerOperation( this.name, this.newRange, this.oldRange, this._markers, this.baseVersion + 1, this.affectsData ); | ||
} | ||
@@ -96,3 +107,3 @@ | ||
this._markers[ type ]( this.name, this.newRange, true ); | ||
this._markers[ type ]( this.name, this.newRange, true, this.affectsData ); | ||
} | ||
@@ -119,3 +130,3 @@ | ||
/** | ||
* Creates `MarkerOperation` object from deserilized object, i.e. from parsed JSON string. | ||
* Creates `MarkerOperation` object from deserialized object, i.e. from parsed JSON string. | ||
* | ||
@@ -132,5 +143,6 @@ * @param {Object} json Deserialized JSON object. | ||
document.model.markers, | ||
json.baseVersion | ||
json.baseVersion, | ||
json.affectsData | ||
); | ||
} | ||
} |
@@ -40,3 +40,3 @@ /** | ||
/** | ||
* Creates concrete `Operation` object from deserilized object, i.e. from parsed JSON string. | ||
* Creates concrete `Operation` object from deserialized object, i.e. from parsed JSON string. | ||
* | ||
@@ -43,0 +43,0 @@ * @param {Object} json Deserialized JSON object. |
@@ -336,3 +336,4 @@ /** | ||
* Returns `true` if the given item is defined to be | ||
* a limit element by {@link module:engine/model/schema~SchemaItemDefinition}'s `isLimit` property. | ||
* a limit element by {@link module:engine/model/schema~SchemaItemDefinition}'s `isLimit` or `isObject` property | ||
* (all objects are also limits). | ||
* | ||
@@ -342,2 +343,3 @@ * schema.isLimit( 'paragraph' ); // -> false | ||
* schema.isLimit( editor.model.document.getRoot() ); // -> true | ||
* schema.isLimit( 'image' ); // -> true | ||
* | ||
@@ -349,3 +351,7 @@ * @param {module:engine/model/item~Item|module:engine/model/schema~SchemaContextItem|String} item | ||
return !!( def && def.isLimit ); | ||
if ( !def ) { | ||
return false; | ||
} | ||
return !!( def.isLimit || def.isObject ); | ||
} | ||
@@ -381,4 +387,9 @@ | ||
* | ||
* Note: When verifying whether the given node can be a child of the given context, the | ||
* schema also verifies the entire context — from its root to its last element. Therefore, it is possible | ||
* for `checkChild()` to return `false` even though the context's last element can contain the checked child. | ||
* It happens if one of the context's elements does not allow its child. | ||
* | ||
* @fires checkChild | ||
* @param {module:engine/model/schema~SchemaContextDefinition} context Context in which the child will be checked. | ||
* @param {module:engine/model/schema~SchemaContextDefinition} context The context in which the child will be checked. | ||
* @param {module:engine/model/node~Node|String} def The child to check. | ||
@@ -407,3 +418,3 @@ */ | ||
* @fires checkAttribute | ||
* @param {module:engine/model/schema~SchemaContextDefinition} context Context in which the attribute will be checked. | ||
* @param {module:engine/model/schema~SchemaContextDefinition} context The context in which the attribute will be checked. | ||
* @param {String} attributeName | ||
@@ -424,3 +435,3 @@ */ | ||
* | ||
* In other words – whether `elementToMerge`'s children {@link #checkChild are allowed} in the `positionOrBaseElement`. | ||
* In other words — whether `elementToMerge`'s children {@link #checkChild are allowed} in the `positionOrBaseElement`. | ||
* | ||
@@ -430,8 +441,8 @@ * This check ensures that elements merged with {@link module:engine/model/writer~Writer#merge `Writer#merge()`} | ||
* | ||
* Instead of elements, you can pass the instance of {@link module:engine/model/position~Position} class as the `positionOrBaseElement`. | ||
* It means that the elements before and after the position will be checked whether they can be merged. | ||
* Instead of elements, you can pass the instance of the {@link module:engine/model/position~Position} class as the | ||
* `positionOrBaseElement`. It means that the elements before and after the position will be checked whether they can be merged. | ||
* | ||
* @param {module:engine/model/position~Position|module:engine/model/element~Element} positionOrBaseElement The position or base | ||
* element to which the `elementToMerge` will be merged. | ||
* @param {module:engine/model/element~Element} elementToMerge The element to merge. Required if `positionOrBaseElement` is a element. | ||
* @param {module:engine/model/element~Element} elementToMerge The element to merge. Required if `positionOrBaseElement` is an element. | ||
* @returns {Boolean} | ||
@@ -752,4 +763,4 @@ */ | ||
// Do not split limit elements and objects. | ||
if ( this.isLimit( parent ) || this.isObject( parent ) ) { | ||
// Do not split limit elements. | ||
if ( this.isLimit( parent ) ) { | ||
return null; | ||
@@ -973,10 +984,10 @@ } | ||
* | ||
* * `allowIn` – a string or an array of strings. Defines in which other items this item will be allowed. | ||
* * `allowAttributes` – a string or an array of strings. Defines allowed attributes of the given item. | ||
* * `allowContentOf` – a string or an array of strings. Inherit "allowed children" from other items. | ||
* * `allowWhere` – a string or an array of strings. Inherit "allowed in" from other items. | ||
* * `allowAttributesOf` – a string or an array of strings. Inherit attributes from other items. | ||
* * `inheritTypesFrom` – a string or an array of strings. Inherit `is*` properties of other items. | ||
* * `inheritAllFrom` – a string. A shorthand for `allowContentOf`, `allowWhere`, `allowAttributesOf`, `inheritTypesFrom`. | ||
* * additionall, you can define the following `is*` properties: `isBlock`, `isLimit`, `isObject`. Read about them below. | ||
* * `allowIn` – A string or an array of strings. Defines in which other items this item will be allowed. | ||
* * `allowAttributes` – A string or an array of strings. Defines allowed attributes of the given item. | ||
* * `allowContentOf` – A string or an array of strings. Inherits "allowed children" from other items. | ||
* * `allowWhere` – A string or an array of strings. Inherits "allowed in" from other items. | ||
* * `allowAttributesOf` – A string or an array of strings. Inherits attributes from other items. | ||
* * `inheritTypesFrom` – A string or an array of strings. Inherits `is*` properties of other items. | ||
* * `inheritAllFrom` – A string. A shorthand for `allowContentOf`, `allowWhere`, `allowAttributesOf`, `inheritTypesFrom`. | ||
* * Additionally, you can define the following `is*` properties: `isBlock`, `isLimit`, `isObject`. Read about them below. | ||
* | ||
@@ -988,11 +999,13 @@ * # The is* properties | ||
* | ||
* * `isBlock` – whether this item is paragraph-like. Generally speaking, a content is usually made out of blocks | ||
* * `isBlock` – Whether this item is paragraph-like. Generally speaking, content is usually made out of blocks | ||
* like paragraphs, list items, images, headings, etc. All these elements are marked as blocks. A block | ||
* should not allow another block inside. Note: there's also the `$block` generic item which has `isBlock` set to `true`. | ||
* should not allow another block inside. Note: There is also the `$block` generic item which has `isBlock` set to `true`. | ||
* Most block type items will inherit from `$block` (through `inheritAllFrom`). | ||
* * `isLimit` – can be understood as whether this element should not be split by <kbd>Enter</kbd>. | ||
* Examples of limit elements – `$root`, table cell, image caption, etc. In other words, all actions which happen inside | ||
* a limit element are limitted to its content. | ||
* * `isObject` – whether item is "self-contained" and should be treated as a whole. Examples of object elements – | ||
* `image`, `table`, `video`, etc. | ||
* * `isLimit` – It can be understood as whether this element should not be split by <kbd>Enter</kbd>. | ||
* Examples of limit elements: `$root`, table cell, image caption, etc. In other words, all actions that happen inside | ||
* a limit element are limited to its content. **Note:** All objects (`isObject`) are treated as limit elements, too. | ||
* * `isObject` – Whether an item is "self-contained" and should be treated as a whole. Examples of object elements: | ||
* `image`, `table`, `video`, etc. **Note:** An object is also a limit, so | ||
* {@link module:engine/model/schema~Schema#isLimit `isLimit()`} | ||
* returns `true` for object elements automatically. | ||
* | ||
@@ -1015,3 +1028,3 @@ * # Generic items | ||
* | ||
* They reflect a typical editor content which is contained within one root, consists of several blocks | ||
* They reflect typical editor content that is contained within one root, consists of several blocks | ||
* (paragraphs, lists items, headings, images) which, in turn, may contain text inside. | ||
@@ -1039,3 +1052,3 @@ * | ||
* Make `image` a block object, which is allowed everywhere where `$block` is. | ||
* Also, allow `src` and `alt` attributes on it: | ||
* Also, allow `src` and `alt` attributes in it: | ||
* | ||
@@ -1050,3 +1063,3 @@ * schema.register( 'image', { | ||
* Make `caption` allowed in `image` and make it allow all the content of `$block`s (usually, `$text`). | ||
* Also, mark it as a limit element so it can't be split: | ||
* Also, mark it as a limit element so it cannot be split: | ||
* | ||
@@ -1063,3 +1076,3 @@ * schema.register( 'caption', { | ||
* inheritAllFrom: '$block', | ||
* allowAttributes: [ 'type', 'indent' ] | ||
* allowAttributes: [ 'listType', 'listIndent' ] | ||
* } ); | ||
@@ -1074,3 +1087,3 @@ * | ||
* inheritTypesFrom: '$block', | ||
* allowAttributes: [ 'type', 'indent' ] | ||
* allowAttributes: [ 'listType', 'listIndent' ] | ||
* } ); | ||
@@ -1083,5 +1096,5 @@ * | ||
* generic items as much as possible. | ||
* * Keep your model clean – limit it to the actual data and store information in an normalized way. | ||
* * Remember about definining the `is*` properties. They don't affect the allowed structures, but they can | ||
* affect how editor features treat your elements. | ||
* * Keep your model clean. Limit it to the actual data and store information in a normalized way. | ||
* * Remember about definining the `is*` properties. They do not affect the allowed structures, but they can | ||
* affect how the editor features treat your elements. | ||
* | ||
@@ -1094,4 +1107,4 @@ * @typedef {Object} module:engine/model/schema~SchemaItemDefinition | ||
* compilation by the {@link module:engine/model/schema~Schema schema}. | ||
* Rules feed to the schema by {@link module:engine/model/schema~Schema#register} | ||
* and {@link module:engine/model/schema~Schema#extend} are defined in the | ||
* Rules fed to the schema by {@link module:engine/model/schema~Schema#register} | ||
* and {@link module:engine/model/schema~Schema#extend} methods are defined in the | ||
* {@link module:engine/model/schema~SchemaItemDefinition} format. | ||
@@ -1103,6 +1116,6 @@ * Later on, they are compiled to `SchemaCompiledItemDefition` so when you use e.g. | ||
* | ||
* * `name` property, | ||
* * `is*` properties, | ||
* * `allowIn` array, | ||
* * `allowAttributes` array. | ||
* * The `name` property, | ||
* * The `is*` properties, | ||
* * The `allowIn` array, | ||
* * The `allowAttributes` array. | ||
* | ||
@@ -1113,5 +1126,5 @@ * @typedef {Object} module:engine/model/schema~SchemaCompiledItemDefinition | ||
/** | ||
* A schema context – a list of ancestors of a given position in the document. | ||
* A schema context — a list of ancestors of a given position in the document. | ||
* | ||
* Considering such a position: | ||
* Considering such position: | ||
* | ||
@@ -1164,3 +1177,3 @@ * <$root> | ||
/** | ||
* Number of items. | ||
* The number of items. | ||
* | ||
@@ -1194,3 +1207,3 @@ * @type {Number} | ||
/** | ||
* Returns new SchemaContext instance with additional item | ||
* Returns a new schema context instance with an additional item. | ||
* | ||
@@ -1214,5 +1227,5 @@ * Item can be added as: | ||
* | ||
* @param {String|module:engine/model/node~Node|Array<String|module:engine/model/node~Node>} item Item that will be added | ||
* to current context. | ||
* @returns {module:engine/model/schema~SchemaContext} New SchemaContext instance with additional item. | ||
* @param {String|module:engine/model/node~Node|Array<String|module:engine/model/node~Node>} item An item that will be added | ||
* to the current context. | ||
* @returns {module:engine/model/schema~SchemaContext} A new schema context instance with an additional item. | ||
*/ | ||
@@ -1219,0 +1232,0 @@ push( item ) { |
@@ -188,3 +188,3 @@ /** | ||
for ( const value of rangeToCheck.getWalker() ) { | ||
if ( schema.isObject( value.item ) || schema.isLimit( value.item ) ) { | ||
if ( schema.isLimit( value.item ) ) { | ||
return false; | ||
@@ -191,0 +191,0 @@ } |
@@ -148,3 +148,3 @@ /** | ||
// TMP this will become a postfixer. | ||
// TMP this will become a post-fixer. | ||
this.schema.removeDisallowedAttributes( this._filterAttributesOf, this.writer ); | ||
@@ -151,0 +151,0 @@ this._filterAttributesOf = []; |
@@ -55,3 +55,3 @@ /** | ||
* | ||
* Note that the should never be stored and used outside of the `change()` or | ||
* Note that the writer should never be stored and used outside of the `change()` and | ||
* `enqueueChange()` blocks. | ||
@@ -66,4 +66,4 @@ * | ||
* | ||
* **Note:** It is not recommended to use it directly. Use {@link module:engine/model/model~Model#change} or | ||
* {@link module:engine/model/model~Model#enqueueChange} instead. | ||
* **Note:** It is not recommended to use it directly. Use {@link module:engine/model/model~Model#change `Model#change()`} or | ||
* {@link module:engine/model/model~Model#enqueueChange `Model#enqueueChange()`} instead. | ||
* | ||
@@ -76,2 +76,4 @@ * @protected | ||
/** | ||
* Instance of the model on which this writer operates. | ||
* | ||
* @readonly | ||
@@ -83,2 +85,4 @@ * @type {module:engine/model/model~Model} | ||
/** | ||
* The batch to which this writer will add changes. | ||
* | ||
* @readonly | ||
@@ -94,3 +98,3 @@ * @type {module:engine/model/batch~Batch} | ||
* writer.createText( 'foo' ); | ||
* writer.createText( 'foo', { 'bold': true } ); | ||
* writer.createText( 'foo', { bold: true } ); | ||
* | ||
@@ -109,3 +113,3 @@ * @param {String} data Text data. | ||
* writer.createElement( 'paragraph' ); | ||
* writer.createElement( 'paragraph', { 'alignment': 'center' } ); | ||
* writer.createElement( 'paragraph', { alignment: 'center' } ); | ||
* | ||
@@ -137,3 +141,3 @@ * @param {String} name Name of the element. | ||
* | ||
* const text = writer.createText( 'foo' ); | ||
* const text = writer.createText( 'foo' ); | ||
* writer.insert( text, paragraph, 5 ); | ||
@@ -143,3 +147,3 @@ * | ||
* | ||
* const text = writer.createText( 'foo' ); | ||
* const text = writer.createText( 'foo' ); | ||
* writer.insert( text, paragraph, 'end' ); | ||
@@ -149,14 +153,14 @@ * | ||
* | ||
* const paragraph = writer.createElement( 'paragraph' ); | ||
* const paragraph = writer.createElement( 'paragraph' ); | ||
* writer.insert( paragraph, anotherParagraph, 'after' ); | ||
* | ||
* These parameters works the same way as {@link module:engine/model/position~Position.createAt}. | ||
* These parameters works the same way as {@link module:engine/model/position~Position.createAt `Position.createAt()`}. | ||
* | ||
* Note that if the item already has parent it will be removed from the previous parent. | ||
* | ||
* Note that you cannot re-insert a node from a document to a different document or document fragment. In this case, | ||
* Note that you cannot re-insert a node from a document to a different document or a document fragment. In this case, | ||
* `model-writer-insert-forbidden-move` is thrown. | ||
* | ||
* If you want to move {@link module:engine/model/range~Range range} instead of an | ||
* {@link module:engine/model/item~Item item} use {@link module:engine/model/writer~Writer#move move}. | ||
* {@link module:engine/model/item~Item item} use {@link module:engine/model/writer~Writer#move `Writer#move()`}. | ||
* | ||
@@ -226,3 +230,3 @@ * @param {module:engine/model/item~Item|module:engine/model/documentfragment~DocumentFragment} item Item or document | ||
* writer.insertText( 'foo', position ); | ||
* writer.insertText( 'foo', { 'bold': true }, position ); | ||
* writer.insertText( 'foo', { bold: true }, position ); | ||
* | ||
@@ -232,7 +236,10 @@ * Instead of using position you can use parent and offset or define that text should be inserted at the end | ||
* | ||
* writer.insertText( 'foo', paragraph, 5 ); // inserts in paragraph, at offset 5 | ||
* writer.insertText( 'foo', paragraph, 'end' ); // inserts at the end of the paragraph | ||
* writer.insertText( 'foo', image, 'after' ); // inserts after image | ||
* // Inserts 'foo' in paragraph, at offset 5: | ||
* writer.insertText( 'foo', paragraph, 5 ); | ||
* // Inserts 'foo' at the end of a paragraph: | ||
* writer.insertText( 'foo', paragraph, 'end' ); | ||
* // Inserts 'foo' after an image: | ||
* writer.insertText( 'foo', image, 'after' ); | ||
* | ||
* These parameters works the same way as {@link module:engine/model/position~Position.createAt}. | ||
* These parameters work in the same way as {@link module:engine/model/position~Position.createAt `Position.createAt()`}. | ||
* | ||
@@ -257,3 +264,3 @@ * @param {String} data Text data. | ||
* writer.insertElement( 'paragraph', position ); | ||
* writer.insertElement( 'paragraph', { 'alignment': 'center' }, position ); | ||
* writer.insertElement( 'paragraph', { alignment: 'center' }, position ); | ||
* | ||
@@ -263,7 +270,10 @@ * Instead of using position you can use parent and offset or define that text should be inserted at the end | ||
* | ||
* writer.insertElement( 'paragraph', paragraph, 5 ); // inserts in paragraph, at offset 5 | ||
* writer.insertElement( 'paragraph', blockquote, 'end' ); // insets at the end of the blockquote | ||
* writer.insertElement( 'paragraph', image, 'after' ); // inserts after image | ||
* // Inserts paragraph in the root at offset 5: | ||
* writer.insertElement( 'paragraph', root, 5 ); | ||
* // Inserts paragraph at the end of a blockquote: | ||
* writer.insertElement( 'paragraph', blockquote, 'end' ); | ||
* // Inserts after an image: | ||
* writer.insertElement( 'paragraph', image, 'after' ); | ||
* | ||
* These parameters works the same way as {@link module:engine/model/position~Position.createAt}. | ||
* These parameters works the same way as {@link module:engine/model/position~Position.createAt `Position.createAt()`}. | ||
* | ||
@@ -293,3 +303,3 @@ * @param {String} name Name of the element. | ||
* If you want to move {@link module:engine/model/range~Range range} instead of an | ||
* {@link module:engine/model/item~Item item} use {@link module:engine/model/writer~Writer#move move}. | ||
* {@link module:engine/model/item~Item item} use {@link module:engine/model/writer~Writer#move `Writer#move()`}. | ||
* | ||
@@ -308,3 +318,3 @@ * @param {module:engine/model/item~Item|module:engine/model/documentfragment~DocumentFragment} | ||
* writer.appendText( 'foo', paragraph ); | ||
* writer.appendText( 'foo', { 'bold': true }, paragraph ); | ||
* writer.appendText( 'foo', { bold: true }, paragraph ); | ||
* | ||
@@ -327,3 +337,3 @@ * @param {String} text Text data. | ||
* writer.appendElement( 'paragraph', root ); | ||
* writer.appendElement( 'paragraph', { 'alignment': 'center' }, root ); | ||
* writer.appendElement( 'paragraph', { alignment: 'center' }, root ); | ||
* | ||
@@ -366,4 +376,4 @@ * @param {String} name Name of the element. | ||
* writer.setAttributes( { | ||
* 'bold': true, | ||
* 'italic': true | ||
* bold: true, | ||
* italic: true | ||
* }, range ); | ||
@@ -431,7 +441,10 @@ * | ||
* | ||
* writer.move( sourceRange, paragraph, 5 ); // moves all items in the range to the paragraph at offset 5 | ||
* writer.move( sourceRange, blockquote, 'end' ); // moves all items in the range at the end of the blockquote | ||
* writer.move( sourceRange, image, 'after' ); // moves all items in the range after the image | ||
* // Moves all items in the range to the paragraph at offset 5: | ||
* writer.move( sourceRange, paragraph, 5 ); | ||
* // Moves all items in the range to the end of a blockquote: | ||
* writer.move( sourceRange, blockquote, 'end' ); | ||
* // Moves all items in the range to a position after an image: | ||
* writer.move( sourceRange, image, 'after' ); | ||
* | ||
* These parameters works the same way as {@link module:engine/model/position~Position.createAt}. | ||
* These parameters works the same way as {@link module:engine/model/position~Position.createAt `Position.createAt()`}. | ||
* | ||
@@ -575,3 +588,3 @@ * Note that items can be moved only within the same tree. It means that you can move items within the same root | ||
/** | ||
* Renames given element. | ||
* Renames the given element. | ||
* | ||
@@ -606,6 +619,6 @@ * @param {module:engine/model/element~Element} element The element to rename. | ||
/** | ||
* Splits elements start from the given position and goes to the top of the model tree as long as given | ||
* `limitElement` won't be reached. When limitElement is not defined then only a parent of given position will be split. | ||
* Splits elements starting from the given position and going to the top of the model tree as long as given | ||
* `limitElement` is reached. When `limitElement` is not defined then only the parent of the given position will be split. | ||
* | ||
* The element needs to have a parent. It cannot be a root element nor document fragment. | ||
* The element needs to have a parent. It cannot be a root element nor a document fragment. | ||
* The `writer-split-element-no-parent` error will be thrown if you try to split an element with no parent. | ||
@@ -694,5 +707,6 @@ * | ||
/** | ||
* Wraps given range with given element or with a new element with specified name, if string has been passed. | ||
* Wraps the given range with the given element or with a new element (if a string was passed). | ||
* | ||
* **Note:** range to wrap should be a "flat range" (see {@link module:engine/model/range~Range#isFlat}). If not, error will be thrown. | ||
* **Note:** range to wrap should be a "flat range" (see {@link module:engine/model/range~Range#isFlat `Range#isFlat`}). | ||
* If not, an error will be thrown. | ||
* | ||
@@ -804,10 +818,20 @@ * @param {module:engine/model/range~Range} range Range to wrap. | ||
* | ||
* The `options.affectsData` parameter, which defaults to `false`, allows you to define if a marker affects the data. It should be | ||
* `true` when the marker change changes the data returned by the | ||
* {@link module:core/editor/utils/dataapimixin~DataApi#getData `editor.getData()`} method. | ||
* When set to `true` it fires the {@link module:engine/model/document~Document#event:change:data `change:data`} event. | ||
* When set to `false` it fires the {@link module:engine/model/document~Document#event:change `change`} event. | ||
* | ||
* Create marker directly base on marker's name: | ||
* | ||
* addMarker( markerName, { range, usingOperation: false } ); | ||
* addMarker( markerName, { range, usingOperation: false } ); | ||
* | ||
* Create marker using operation: | ||
* | ||
* addMarker( markerName, { range, usingOperation: true } ); | ||
* addMarker( markerName, { range, usingOperation: true } ); | ||
* | ||
* Create marker that affects the editor data: | ||
* | ||
* addMarker( markerName, { range, usingOperation: false, affectsData: true } ); | ||
* | ||
* Note: For efficiency reasons, it's best to create and keep as little markers as possible. | ||
@@ -818,5 +842,6 @@ * | ||
* @param {Object} options | ||
* @param {Boolean} options.usingOperation Flag indicated whether the marker should be added by MarkerOperation. | ||
* @param {Boolean} options.usingOperation Flag indicating that the marker should be added by MarkerOperation. | ||
* See {@link module:engine/model/markercollection~Marker#managedUsingOperations}. | ||
* @param {module:engine/model/range~Range} options.range Marker range. | ||
* @param {Boolean} [options.affectsData=false] Flag indicating that the marker changes the editor data. | ||
* @returns {module:engine/model/markercollection~Marker} Marker that was set. | ||
@@ -829,3 +854,3 @@ */ | ||
/** | ||
* The options.usingOperations parameter is required when adding a new marker. | ||
* The `options.usingOperations` parameter is required when adding a new marker. | ||
* | ||
@@ -841,2 +866,3 @@ * @error writer-addMarker-no-usingOperations | ||
const range = options.range; | ||
const affectsData = options.affectsData === undefined ? false : options.affectsData; | ||
@@ -862,6 +888,6 @@ if ( this.model.markers.has( name ) ) { | ||
if ( !usingOperation ) { | ||
return this.model.markers._set( name, range, usingOperation ); | ||
return this.model.markers._set( name, range, usingOperation, affectsData ); | ||
} | ||
applyMarkerOperation( this, name, null, range ); | ||
applyMarkerOperation( this, name, null, range, affectsData ); | ||
@@ -882,17 +908,26 @@ return this.model.markers.get( name ); | ||
* markers managed by operations and not-managed by operations. It is possible to change this option for an existing marker. | ||
* This is useful when a marker have been created earlier and then later, it needs to be added to the document history. | ||
* | ||
* The `options.affectsData` parameter, which defaults to `false`, allows you to define if a marker affects the data. It should be | ||
* `true` when the marker change changes the data returned by | ||
* the {@link module:core/editor/utils/dataapimixin~DataApi#getData `editor.getData()`} method. | ||
* When set to `true` it fires the {@link module:engine/model/document~Document#event:change:data `change:data`} event. | ||
* When set to `false` it fires the {@link module:engine/model/document~Document#event:change `change`} event. | ||
* | ||
* Update marker directly base on marker's name: | ||
* | ||
* updateMarker( markerName, { range } ); | ||
* updateMarker( markerName, { range } ); | ||
* | ||
* Update marker using operation: | ||
* | ||
* updateMarker( marker, { range, usingOperation: true } ); | ||
* updateMarker( markerName, { range, usingOperation: true } ); | ||
* updateMarker( marker, { range, usingOperation: true } ); | ||
* updateMarker( markerName, { range, usingOperation: true } ); | ||
* | ||
* Change marker's option (start using operations to manage it): | ||
* | ||
* updateMarker( marker, { usingOperation: true } ); | ||
* updateMarker( marker, { usingOperation: true } ); | ||
* | ||
* Change marker's option (inform the engine, that the marker does not affect the data anymore): | ||
* | ||
* updateMarker( markerName, { affectsData: false } ); | ||
* | ||
* @see module:engine/model/markercollection~Marker | ||
@@ -904,8 +939,8 @@ * @param {String} markerOrName Name of a marker to update, or a marker instance. | ||
* See {@link module:engine/model/markercollection~Marker#managedUsingOperations}. | ||
* @param {Boolean} [options.affectsData] Flag indicating that the marker changes the editor data. | ||
*/ | ||
updateMarker( markerOrName, options ) { | ||
updateMarker( markerOrName, options = {} ) { | ||
this._assertWriterUsedCorrectly(); | ||
const markerName = typeof markerOrName == 'string' ? markerOrName : markerOrName.name; | ||
const currentMarker = this.model.markers.get( markerName ); | ||
@@ -922,14 +957,22 @@ | ||
const newRange = options && options.range; | ||
const hasUsingOperationDefined = !!options && typeof options.usingOperation == 'boolean'; | ||
const hasUsingOperationDefined = typeof options.usingOperation == 'boolean'; | ||
const affectsDataDefined = typeof options.affectsData == 'boolean'; | ||
if ( !hasUsingOperationDefined && !newRange ) { | ||
// Use previously defined marker's affectsData if the property is not provided. | ||
const affectsData = affectsDataDefined ? options.affectsData : currentMarker.affectsData; | ||
if ( !hasUsingOperationDefined && !options.range && !affectsDataDefined ) { | ||
/** | ||
* One of options is required - provide range or usingOperations. | ||
* One of the options is required - provide range, usingOperations or affectsData. | ||
* | ||
* @error writer-updateMarker-wrong-options | ||
*/ | ||
throw new CKEditorError( 'writer-updateMarker-wrong-options: One of options is required - provide range or usingOperations.' ); | ||
throw new CKEditorError( | ||
'writer-updateMarker-wrong-options: One of the options is required - provide range, usingOperations or affectsData.' | ||
); | ||
} | ||
const currentRange = currentMarker.getRange(); | ||
const updatedRange = options.range ? options.range : currentRange; | ||
if ( hasUsingOperationDefined && options.usingOperation !== currentMarker.managedUsingOperations ) { | ||
@@ -939,13 +982,11 @@ // The marker type is changed so it's necessary to create proper operations. | ||
// If marker changes to a managed one treat this as synchronizing existing marker. | ||
// If marker changes to a managed one treat this as synchronizing existing marker. | ||
// Create `MarkerOperation` with `oldRange` set to `null`, so reverse operation will remove the marker. | ||
applyMarkerOperation( this, markerName, null, newRange ? newRange : currentMarker.getRange() ); | ||
applyMarkerOperation( this, markerName, null, updatedRange, affectsData ); | ||
} else { | ||
// If marker changes to a marker that do not use operations then we need to create additional operation | ||
// that removes that marker first. | ||
const currentRange = currentMarker.getRange(); | ||
applyMarkerOperation( this, markerName, currentRange, null ); | ||
applyMarkerOperation( this, markerName, currentRange, null, affectsData ); | ||
// Although not managed the marker itself should stay in model and its range should be preserver or changed to passed range. | ||
this.model.markers._set( markerName, newRange ? newRange : currentRange ); | ||
this.model.markers._set( markerName, updatedRange, undefined, affectsData ); | ||
} | ||
@@ -958,5 +999,5 @@ | ||
if ( currentMarker.managedUsingOperations ) { | ||
applyMarkerOperation( this, markerName, currentMarker.getRange(), newRange ); | ||
applyMarkerOperation( this, markerName, currentRange, updatedRange, affectsData ); | ||
} else { | ||
this.model.markers._set( markerName, newRange ); | ||
this.model.markers._set( markerName, updatedRange, undefined, affectsData ); | ||
} | ||
@@ -996,3 +1037,3 @@ } | ||
applyMarkerOperation( this, name, oldRange, null ); | ||
applyMarkerOperation( this, name, oldRange, null, marker.affectsData ); | ||
} | ||
@@ -1011,3 +1052,3 @@ | ||
* // Sets selection to given ranges. | ||
* const ranges = [ new Range( start1, end2 ), new Range( star2, end2 ) ]; | ||
* const ranges = [ new Range( start1, end2 ), new Range( star2, end2 ) ]; | ||
* writer.setSelection( range ); | ||
@@ -1019,11 +1060,11 @@ * | ||
* | ||
* // Sets selection to the given document selection. | ||
* // Sets selection to the given document selection. | ||
* const documentSelection = new DocumentSelection( doc ); | ||
* writer.setSelection( documentSelection ); | ||
* | ||
* // Sets collapsed selection at the given position. | ||
* // Sets collapsed selection at the given position. | ||
* const position = new Position( root, path ); | ||
* writer.setSelection( position ); | ||
* | ||
* // Sets collapsed selection at the position of the given node and an offset. | ||
* // Sets collapsed selection at the position of the given node and an offset. | ||
* writer.setSelection( paragraph, offset ); | ||
@@ -1040,3 +1081,3 @@ * | ||
* | ||
* // Removes all selection's ranges. | ||
* // Removes all selection's ranges. | ||
* writer.setSelection( null ); | ||
@@ -1046,3 +1087,3 @@ * | ||
* | ||
* // Sets selection as backward. | ||
* // Sets selection as backward. | ||
* writer.setSelection( range, { backward: true } ); | ||
@@ -1068,3 +1109,4 @@ * | ||
* | ||
* The location can be specified in the same form as {@link module:engine/model/position~Position.createAt} parameters. | ||
* The location can be specified in the same form as | ||
* {@link module:engine/model/position~Position.createAt `Position.createAt()`} parameters. | ||
* | ||
@@ -1097,3 +1139,3 @@ * @param {module:engine/model/item~Item|module:engine/model/position~Position} itemOrPosition | ||
* @param {String|Object|Iterable.<*>} keyOrObjectOrIterable Key of the attribute to set | ||
* or object / iterable of key - value attribute pairs. | ||
* or object / iterable of key => value attribute pairs. | ||
* @param {*} [value] Attribute value. | ||
@@ -1116,9 +1158,9 @@ */ | ||
* | ||
* Using key | ||
* Remove one attribute: | ||
* | ||
* writer.removeSelectionAttribute( 'italic' ); | ||
* writer.removeSelectionAttribute( 'italic' ); | ||
* | ||
* Using iterable of keys | ||
* Remove multiple attributes: | ||
* | ||
* writer.removeSelectionAttribute( [ 'italic', 'bold' ] ); | ||
* writer.removeSelectionAttribute( [ 'italic', 'bold' ] ); | ||
* | ||
@@ -1352,3 +1394,4 @@ * @param {String|Iterable.<String>} keyOrIterableOfKeys Key of the attribute to remove or an iterable of attribute keys to remove. | ||
// @param {module:engine/model/range~Range} newRange Marker range after the change. | ||
function applyMarkerOperation( writer, name, oldRange, newRange ) { | ||
// @param {Boolean} affectsData | ||
function applyMarkerOperation( writer, name, oldRange, newRange, affectsData ) { | ||
const model = writer.model; | ||
@@ -1358,3 +1401,3 @@ const doc = model.document; | ||
const operation = new MarkerOperation( name, oldRange, newRange, model.markers, doc.version ); | ||
const operation = new MarkerOperation( name, oldRange, newRange, model.markers, doc.version, affectsData ); | ||
@@ -1361,0 +1404,0 @@ writer.batch.addDelta( delta ); |
@@ -223,5 +223,5 @@ /** | ||
// @param {module:engine/view/element~Element} element | ||
// @return {Number} | ||
// @returns {Number} | ||
function nonUiChildrenCount( element ) { | ||
return Array.from( element.getChildren() ).filter( element => !element.is( 'uiElement' ) ).length; | ||
} |
@@ -83,3 +83,11 @@ /** | ||
function getFillerOffset() { | ||
for ( const child of this.getChildren() ) { | ||
const children = [ ...this.getChildren() ]; | ||
const lastChild = children[ this.childCount - 1 ]; | ||
// Block filler is required after a `<br>` if it's the last element in its container. See #1422. | ||
if ( lastChild && lastChild.is( 'element', 'br' ) ) { | ||
return this.childCount; | ||
} | ||
for ( const child of children ) { | ||
// If there's any non-UI element – don't render the bogus. | ||
@@ -86,0 +94,0 @@ if ( !child.is( 'uiElement' ) ) { |
@@ -209,3 +209,3 @@ /** | ||
// @param {String|module:engine/view/item~Item|Iterable.<String|module:engine/view/item~Item>} | ||
// @return {Iterable.<module:engine/view/node~Node>} | ||
// @returns {Iterable.<module:engine/view/node~Node>} | ||
function normalize( nodes ) { | ||
@@ -212,0 +212,0 @@ // Separate condition because string is iterable. |
@@ -26,2 +26,3 @@ /** | ||
import isText from '@ckeditor/ckeditor5-utils/src/dom/istext'; | ||
import isElement from '@ckeditor/ckeditor5-utils/src/lib/lodash/isElement'; | ||
@@ -816,3 +817,3 @@ /** | ||
* @param {Node} domNode | ||
* @return {module:engine/view/uielement~UIElement|null} | ||
* @returns {module:engine/view/uielement~UIElement|null} | ||
*/ | ||
@@ -953,6 +954,7 @@ getParentUIElement( domNode ) { | ||
* Following changes are done: | ||
* | ||
* * multiple whitespaces are replaced to a single space, | ||
* * space at the beginning of the text node is removed, if it is a first text node in it's container | ||
* element or if previous text node ends by space character, | ||
* * space at the end of the text node is removed, if it is a last text node in it's container. | ||
* * space at the beginning of a text node is removed if it is the first text node in its container | ||
* element or if the previous text node ends with a space character, | ||
* * space at the end of the text node is removed, if it is the last text node in its container. | ||
* | ||
@@ -976,13 +978,16 @@ * @param {Node} node DOM text node to process. | ||
const prevNode = this._getTouchingDomTextNode( node, false ); | ||
const nextNode = this._getTouchingDomTextNode( node, true ); | ||
const prevNode = this._getTouchingInlineDomNode( node, false ); | ||
const nextNode = this._getTouchingInlineDomNode( node, true ); | ||
// If previous dom text node does not exist or it ends by whitespace character, remove space character from the beginning | ||
const shouldLeftTrim = this._checkShouldLeftTrimDomText( prevNode ); | ||
const shouldRightTrim = this._checkShouldRightTrimDomText( node, nextNode ); | ||
// If the previous dom text node does not exist or it ends by whitespace character, remove space character from the beginning | ||
// of this text node. Such space character is treated as a whitespace. | ||
if ( !prevNode || /[^\S\u00A0]/.test( prevNode.data.charAt( prevNode.data.length - 1 ) ) ) { | ||
if ( shouldLeftTrim ) { | ||
data = data.replace( /^ /, '' ); | ||
} | ||
// If next text node does not exist remove space character from the end of this text node. | ||
if ( !nextNode && !startsWithFiller( node ) ) { | ||
// If the next text node does not exist remove space character from the end of this text node. | ||
if ( shouldRightTrim ) { | ||
data = data.replace( / $/, '' ); | ||
@@ -1008,3 +1013,3 @@ } | ||
// We do that replacement only if this is the first node or the previous node ends on whitespace character. | ||
if ( !prevNode || /[^\S\u00A0]/.test( prevNode.data.charAt( prevNode.data.length - 1 ) ) ) { | ||
if ( shouldLeftTrim ) { | ||
data = data.replace( /^\u00A0/, ' ' ); | ||
@@ -1016,4 +1021,4 @@ } | ||
// `x_ _ _` -> `x_ `. Find at the end of string (can be followed only by spaces). | ||
// We do that replacement only if this is the last node or the next node starts by . | ||
if ( !nextNode || nextNode.data.charAt( 0 ) == '\u00A0' ) { | ||
// We do that replacement only if this is the last node or the next node starts with or is a <br>. | ||
if ( isText( nextNode ) ? nextNode.data.charAt( 0 ) == '\u00A0' : true ) { | ||
data = data.replace( /\u00A0( *)$/, ' $1' ); | ||
@@ -1028,2 +1033,35 @@ } | ||
/** | ||
* Helper function which checks if a DOM text node, preceded by the given `prevNode` should | ||
* be trimmed from the left side. | ||
* | ||
* @param {Node} prevNode | ||
*/ | ||
_checkShouldLeftTrimDomText( prevNode ) { | ||
if ( !prevNode ) { | ||
return true; | ||
} | ||
if ( isElement( prevNode ) ) { | ||
return true; | ||
} | ||
return /[^\S\u00A0]/.test( prevNode.data.charAt( prevNode.data.length - 1 ) ); | ||
} | ||
/** | ||
* Helper function which checks if a DOM text node, succeeded by the given `nextNode` should | ||
* be trimmed from the right side. | ||
* | ||
* @param {Node} node | ||
* @param {Node} prevNode | ||
*/ | ||
_checkShouldRightTrimDomText( node, nextNode ) { | ||
if ( nextNode ) { | ||
return false; | ||
} | ||
return !startsWithFiller( node ); | ||
} | ||
/** | ||
* Helper function. For given {@link module:engine/view/text~Text view text node}, it finds previous or next sibling | ||
@@ -1043,8 +1081,13 @@ * that is contained in the same container element. If there is no such sibling, `null` is returned. | ||
for ( const value of treeWalker ) { | ||
// ViewContainerElement is found on a way to next ViewText node, so given `node` was first/last | ||
// text node in its container element. | ||
if ( value.item.is( 'containerElement' ) ) { | ||
// ViewContainerElement is found on a way to next ViewText node, so given `node` was first/last | ||
// text node in its container element. | ||
return null; | ||
} else if ( value.item.is( 'textProxy' ) ) { | ||
// Found a text node in the same container element. | ||
} | ||
// <br> found – it works like a block boundary, so do not scan further. | ||
else if ( value.item.is( 'br' ) ) { | ||
return null; | ||
} | ||
// Found a text node in the same container element. | ||
else if ( value.item.is( 'textProxy' ) ) { | ||
return value.item; | ||
@@ -1058,11 +1101,23 @@ } | ||
/** | ||
* Helper function. For given `Text` node, it finds previous or next sibling that is contained in the same block element. | ||
* If there is no such sibling, `null` is returned. | ||
* Helper function. For the given text node, it finds the closest touching node which is either | ||
* a text node or a `<br>`. The search is terminated at block element boundaries and if a matching node | ||
* wasn't found so far, `null` is returned. | ||
* | ||
* In the following DOM structure: | ||
* | ||
* <p>foo<b>bar</b><br>bom</p> | ||
* | ||
* * `foo` doesn't have its previous touching inline node (`null` is returned), | ||
* * `foo`'s next touching inline node is `bar` | ||
* * `bar`'s next touching inline node is `<br>` | ||
* | ||
* This method returns text nodes and `<br>` elements because these types of nodes affect how | ||
* spaces in the given text node need to be converted. | ||
* | ||
* @private | ||
* @param {Text} node | ||
* @param {Boolean} getNext | ||
* @returns {Text|null} | ||
* @returns {Text|Element|null} | ||
*/ | ||
_getTouchingDomTextNode( node, getNext ) { | ||
_getTouchingInlineDomNode( node, getNext ) { | ||
if ( !node.parentNode ) { | ||
@@ -1076,4 +1131,16 @@ return null; | ||
const treeWalker = document.createTreeWalker( topmostParent, NodeFilter.SHOW_TEXT ); | ||
const treeWalker = document.createTreeWalker( topmostParent, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, { | ||
acceptNode( node ) { | ||
if ( isText( node ) ) { | ||
return NodeFilter.FILTER_ACCEPT; | ||
} | ||
if ( node.tagName == 'BR' ) { | ||
return NodeFilter.FILTER_ACCEPT; | ||
} | ||
return NodeFilter.FILTER_SKIP; | ||
} | ||
} ); | ||
treeWalker.currentNode = node; | ||
@@ -1080,0 +1147,0 @@ |
@@ -70,3 +70,3 @@ /** | ||
* @readonly | ||
* @return {module:engine/view/document~Document} | ||
* @returns {module:engine/view/document~Document} | ||
*/ | ||
@@ -73,0 +73,0 @@ get document() { |
@@ -882,3 +882,3 @@ /** | ||
// @param {String|module:engine/view/item~Item|Iterable.<String|module:engine/view/item~Item>} | ||
// @return {Iterable.<module:engine/view/node~Node>} | ||
// @returns {Iterable.<module:engine/view/node~Node>} | ||
function normalize( nodes ) { | ||
@@ -885,0 +885,0 @@ // Separate condition because string is iterable. |
@@ -32,3 +32,3 @@ /** | ||
// Create view post fixer that will add placeholder where needed. | ||
// Create view post-fixer that will add placeholder where needed. | ||
document.registerPostFixer( writer => updateAllPlaceholders( document, writer ) ); | ||
@@ -35,0 +35,0 @@ } |
@@ -21,15 +21,17 @@ /** | ||
import isText from '@ckeditor/ckeditor5-utils/src/dom/istext'; | ||
import isNode from '@ckeditor/ckeditor5-utils/src/dom/isnode'; | ||
import fastDiff from '@ckeditor/ckeditor5-utils/src/fastdiff'; | ||
/** | ||
* Renderer updates DOM structure and selection, to make them a reflection of the view structure and selection. | ||
* Renderer is responsible for updating the DOM structure and the DOM selection based on | ||
* the {@link module:engine/view/renderer~Renderer#markToSync information about updated view nodes}. | ||
* In other words, it renders the view to the DOM. | ||
* | ||
* View nodes which may need to be rendered needs to be {@link module:engine/view/renderer~Renderer#markToSync marked}. | ||
* Then, on {@link module:engine/view/renderer~Renderer#render render}, renderer compares view nodes with DOM nodes | ||
* in order to check which ones really need to be refreshed. Finally, it creates DOM nodes from these view nodes, | ||
* {@link module:engine/view/domconverter~DomConverter#bindElements binds} them and inserts into the DOM tree. | ||
* Its main responsibility is to make only the necessary, minimal changes to the DOM. However, unlike in many | ||
* virtual DOM implementations, the primary reason for doing minimal changes is not the performance but ensuring | ||
* that native editing features such as text composition, autocompletion, spell checking, selection's x-index are | ||
* affected as little as possible. | ||
* | ||
* Every time {@link module:engine/view/renderer~Renderer#render render} is called, renderer additionally checks if | ||
* {@link module:engine/view/renderer~Renderer#selection selection} needs update and updates it if so. | ||
* | ||
* Renderer uses {@link module:engine/view/domconverter~DomConverter} to transform and bind nodes. | ||
* Renderer uses {@link module:engine/view/domconverter~DomConverter} to transform view nodes and positions | ||
* to and from the DOM. | ||
*/ | ||
@@ -93,2 +95,10 @@ export default class Renderer { | ||
/** | ||
* Indicates if the view document is focused and selection can be rendered. Selection will not be rendered if | ||
* this is set to `false`. | ||
* | ||
* @member {Boolean} | ||
*/ | ||
this.isFocused = false; | ||
/** | ||
* The text node in which the inline filler was rendered. | ||
@@ -102,10 +112,2 @@ * | ||
/** | ||
* Indicates if the view document is focused and selection can be rendered. Selection will not be rendered if | ||
* this is set to `false`. | ||
* | ||
* @member {Boolean} | ||
*/ | ||
this.isFocused = false; | ||
/** | ||
* DOM element containing fake selection. | ||
@@ -120,5 +122,5 @@ * | ||
/** | ||
* Mark node to be synchronized. | ||
* Marks a view node to be updated in the DOM by {@link #render `render()`}. | ||
* | ||
* Note that only view nodes which parents have corresponding DOM elements need to be marked to be synchronized. | ||
* Note that only view nodes whose parents have corresponding DOM elements need to be marked to be synchronized. | ||
* | ||
@@ -160,24 +162,11 @@ * @see #markedAttributes | ||
/** | ||
* Render method checks {@link #markedAttributes}, | ||
* {@link #markedChildren} and {@link #markedTexts} and updates all | ||
* nodes which need to be updated. Then it clears all three sets. Also, every time render is called it compares and | ||
* if needed updates the selection. | ||
* Renders all buffered changes ({@link #markedAttributes}, {@link #markedChildren} and {@link #markedTexts}) and | ||
* the current view selection (if needed) to the DOM by applying a minimal set of changes to it. | ||
* | ||
* Renderer tries not to break text composition (e.g. IME) and x-index of the selection, | ||
* Renderer tries not to break the text composition (e.g. IME) and x-index of the selection, | ||
* so it does as little as it is needed to update the DOM. | ||
* | ||
* For attributes it adds new attributes to DOM elements, updates values and removes | ||
* attributes which do not exist in the view element. | ||
* | ||
* For text nodes it updates the text string if it is different. Note that if parent element is marked as an element | ||
* which changed child list, text node update will not be done, because it may not be possible to | ||
* {@link module:engine/view/domconverter~DomConverter#findCorrespondingDomText find a corresponding DOM text}. | ||
* The change will be handled in the parent element. | ||
* | ||
* For elements, which child lists have changed, it calculates a {@link module:utils/diff~diff} and adds or removes children which have | ||
* changed. | ||
* | ||
* Rendering also handles {@link module:engine/view/filler fillers}. Especially, it checks if the inline filler is needed | ||
* at selection position and adds or removes it. To prevent breaking text composition inline filler will not be | ||
* removed as long selection is in the text node which needed it at first. | ||
* Renderer also handles {@link module:engine/view/filler fillers}. Especially, it checks if the inline filler is needed | ||
* at the selection position and adds or removes it. To prevent breaking text composition inline filler will not be | ||
* removed as long as the selection is in the text node which needed it at first. | ||
*/ | ||
@@ -187,2 +176,7 @@ render() { | ||
// Refresh mappings. | ||
for ( const element of this.markedChildren ) { | ||
this._updateChildrenMappings( element ); | ||
} | ||
// There was inline filler rendered in the DOM but it's not | ||
@@ -252,6 +246,84 @@ // at the selection position any more, so we can remove it | ||
/** | ||
* Adds inline filler at given position. | ||
* Updates mappings of view element's children. | ||
* | ||
* Children that were replaced in the view structure by similar elements (same tag name) are treated as 'replaced'. | ||
* This means that their mappings can be updated so the new view elements are mapped to the existing DOM elements. | ||
* Thanks to that these elements do not need to be re-rendered completely. | ||
* | ||
* @private | ||
* @param {module:engine/view/node~Node} viewElement The view element whose children mappings will be updated. | ||
*/ | ||
_updateChildrenMappings( viewElement ) { | ||
const domElement = this.domConverter.mapViewToDom( viewElement ); | ||
if ( !domElement ) { | ||
// If there is no `domElement` it means that it was already removed from DOM and there is no need to process it. | ||
return; | ||
} | ||
const diff = this._diffChildren( viewElement ); | ||
const actions = this._findReplaceActions( diff.actions, diff.actualDomChildren, diff.expectedDomChildren ); | ||
if ( actions.indexOf( 'replace' ) !== -1 ) { | ||
const counter = { equal: 0, insert: 0, delete: 0 }; | ||
for ( const action of actions ) { | ||
if ( action === 'replace' ) { | ||
const insertIndex = counter.equal + counter.insert; | ||
const deleteIndex = counter.equal + counter.delete; | ||
const viewChild = viewElement.getChild( insertIndex ); | ||
// The 'uiElement' is a special one and its children are not stored in a view (#799), | ||
// so we cannot use it with replacing flow (since it uses view children during rendering | ||
// which will always result in rendering empty element). | ||
if ( viewChild && !viewChild.is( 'uiElement' ) ) { | ||
this._updateElementMappings( viewChild, diff.actualDomChildren[ deleteIndex ] ); | ||
} | ||
remove( diff.expectedDomChildren[ insertIndex ] ); | ||
counter.equal++; | ||
} else { | ||
counter[ action ]++; | ||
} | ||
} | ||
} | ||
} | ||
/** | ||
* Updates mappings of a given view element. | ||
* | ||
* @private | ||
* @param {module:engine/view/node~Node} viewElement The view element whose mappings will be updated. | ||
* @param {Node} domElement The DOM element representing the given view element. | ||
*/ | ||
_updateElementMappings( viewElement, domElement ) { | ||
// Because we replace new view element mapping with the existing one, the corresponding DOM element | ||
// will not be rerendered. The new view element may have different attributes than the previous one. | ||
// Since its corresponding DOM element will not be rerendered, new attributes will not be added | ||
// to the DOM, so we need to mark it here to make sure its attributes gets updated. | ||
// Such situations may happen if only new view element was added to `this.markedAttributes` | ||
// or none of the elements were added (relying on 'this._updateChildren()' which by rerendering the element | ||
// also rerenders its attributes). See #1427 for more detailed case study. | ||
const newViewChild = this.domConverter.mapDomToView( domElement ); | ||
// It may also happen that 'newViewChild' mapping is not present since its parent mapping | ||
// was already removed (the 'domConverter.unbindDomElement()' method also unbinds children | ||
// mappings) so we also check for '!newViewChild'. | ||
if ( !newViewChild || newViewChild && !newViewChild.isSimilar( viewElement ) ) { | ||
this.markedAttributes.add( viewElement ); | ||
} | ||
// Remap 'DomConverter' bindings. | ||
this.domConverter.unbindDomElement( domElement ); | ||
this.domConverter.bindElements( domElement, viewElement ); | ||
// View element may have children which needs to be updated, but are not marked, mark them to update. | ||
this.markedChildren.add( viewElement ); | ||
} | ||
/** | ||
* Adds inline filler at a given position. | ||
* | ||
* The position can be given as an array of DOM nodes and an offset in that array, | ||
* or a DOM parent element and offset in that element. | ||
* or a DOM parent element and an offset in that element. | ||
* | ||
@@ -262,3 +334,3 @@ * @private | ||
* @param {Number} offset | ||
* @returns {Text} The DOM text node that contains inline filler. | ||
* @returns {Text} The DOM text node that contains an inline filler. | ||
*/ | ||
@@ -289,7 +361,7 @@ _addInlineFiller( domDocument, domParentOrArray, offset ) { | ||
* Here, we assume that we know that the filler is needed and | ||
* {@link #_isSelectionInInlineFiller is at the selection position}, and, since it's needed, | ||
* it's somewhere at the selection postion. | ||
* {@link #_isSelectionInInlineFiller is at the selection position}, and, since it is needed, | ||
* it is somewhere at the selection position. | ||
* | ||
* Note: we cannot restore the filler position based on the filler's DOM text node, because | ||
* when this method is called (before rendering) the bindings will often be broken. View to DOM | ||
* Note: The filler position cannot be restored based on the filler's DOM text node, because | ||
* when this method is called (before rendering), the bindings will often be broken. View-to-DOM | ||
* bindings are only dependable after rendering. | ||
@@ -311,8 +383,8 @@ * | ||
/** | ||
* Returns `true` if the selection hasn't left the inline filler's text node. | ||
* If it is `true` it means that the filler had been added for a reason and the selection does not | ||
* left the filler's text node. E.g. the user can be in the middle of a composition so it should not be touched. | ||
* Returns `true` if the selection has not left the inline filler's text node. | ||
* If it is `true`, it means that the filler had been added for a reason and the selection did not | ||
* leave the filler's text node. For example, the user can be in the middle of a composition so it should not be touched. | ||
* | ||
* @private | ||
* @returns {Boolean} True if the inline filler and selection are in the same place. | ||
* @returns {Boolean} `true` if the inline filler and selection are in the same place. | ||
*/ | ||
@@ -375,3 +447,3 @@ _isSelectionInInlineFiller() { | ||
* @private | ||
* @returns {Boolean} True if the inline fillers should be added. | ||
* @returns {Boolean} `true` if the inline filler should be added. | ||
*/ | ||
@@ -423,3 +495,3 @@ _needsInlineFillerAtSelection() { | ||
* @param {Object} options | ||
* @param {module:engine/view/position~Position} options.inlineFillerPosition The position on which the inline | ||
* @param {module:engine/view/position~Position} options.inlineFillerPosition The position where the inline | ||
* filler should be rendered. | ||
@@ -441,3 +513,11 @@ */ | ||
if ( actualText != expectedText ) { | ||
domText.data = expectedText; | ||
const actions = fastDiff( actualText, expectedText ); | ||
for ( const action of actions ) { | ||
if ( action.type === 'insert' ) { | ||
domText.insertData( action.index, action.values.join( '' ) ); | ||
} else { // 'delete' | ||
domText.deleteData( action.index, action.howMany ); | ||
} | ||
} | ||
} | ||
@@ -447,9 +527,18 @@ } | ||
/** | ||
* Checks if attributes list needs to be updated and possibly updates it. | ||
* Checks if attribute list needs to be updated and possibly updates it. | ||
* | ||
* @private | ||
* @param {module:engine/view/element~Element} viewElement View element to update. | ||
* @param {module:engine/view/element~Element} viewElement The view element to update. | ||
*/ | ||
_updateAttrs( viewElement ) { | ||
const domElement = this.domConverter.mapViewToDom( viewElement ); | ||
if ( !domElement ) { | ||
// If there is no `domElement` it means that 'viewElement' is outdated as its mapping was updated | ||
// in 'this._updateChildrenMappings()'. There is no need to process it as new view element which | ||
// replaced old 'viewElement' mapping was also added to 'this.markedAttributes' | ||
// in 'this._updateChildrenMappings()' so it will be processed separately. | ||
return; | ||
} | ||
const domAttrKeys = Array.from( domElement.attributes ).map( attr => attr.name ); | ||
@@ -477,33 +566,24 @@ const viewAttrKeys = viewElement.getAttributeKeys(); | ||
* @param {Object} options | ||
* @param {module:engine/view/position~Position} options.inlineFillerPosition The position on which the inline | ||
* @param {module:engine/view/position~Position} options.inlineFillerPosition The position where the inline | ||
* filler should be rendered. | ||
*/ | ||
_updateChildren( viewElement, options ) { | ||
const domConverter = this.domConverter; | ||
const domElement = domConverter.mapViewToDom( viewElement ); | ||
const domElement = this.domConverter.mapViewToDom( viewElement ); | ||
if ( !domElement ) { | ||
// If there is no `domElement` it means that it was already removed from DOM. | ||
// There is no need to update it. It will be updated when re-inserted. | ||
// There is no need to process it. It will be processed when re-inserted. | ||
return; | ||
} | ||
const domDocument = domElement.ownerDocument; | ||
const filler = options.inlineFillerPosition; | ||
const actualDomChildren = domElement.childNodes; | ||
const expectedDomChildren = Array.from( domConverter.viewChildrenToDom( viewElement, domDocument, { bind: true } ) ); | ||
const inlineFillerPosition = options.inlineFillerPosition; | ||
// As binding may change actual DOM children we need to do this before diffing. | ||
const expectedDomChildren = this._getElementExpectedChildren( viewElement, domElement, { bind: true, inlineFillerPosition } ); | ||
const diff = this._diffChildren( viewElement, inlineFillerPosition ); | ||
const actualDomChildren = diff.actualDomChildren; | ||
// Inline filler element has to be created during children update because we need it to diff actual dom | ||
// elements with expected dom elements. We need inline filler in expected dom elements so we won't re-render | ||
// text node if it is not necessary. | ||
if ( filler && filler.parent == viewElement ) { | ||
this._addInlineFiller( domDocument, expectedDomChildren, filler.offset ); | ||
} | ||
const actions = diff( actualDomChildren, expectedDomChildren, sameNodes ); | ||
let i = 0; | ||
const nodesToUnbind = new Set(); | ||
for ( const action of actions ) { | ||
for ( const action of diff.actions ) { | ||
if ( action === 'insert' ) { | ||
@@ -517,3 +597,3 @@ insertAt( domElement, i, expectedDomChildren[ i ] ); | ||
// Force updating text nodes inside elements which did not change and do not need to be re-rendered (#1125). | ||
this._markDescendantTextToSync( domConverter.domToView( expectedDomChildren[ i ] ) ); | ||
this._markDescendantTextToSync( this.domConverter.domToView( expectedDomChildren[ i ] ) ); | ||
i++; | ||
@@ -531,26 +611,103 @@ } | ||
} | ||
} | ||
function sameNodes( actualDomChild, expectedDomChild ) { | ||
// Elements. | ||
if ( actualDomChild === expectedDomChild ) { | ||
return true; | ||
} | ||
// Texts. | ||
else if ( isText( actualDomChild ) && isText( expectedDomChild ) ) { | ||
return actualDomChild.data === expectedDomChild.data; | ||
} | ||
// Block fillers. | ||
else if ( isBlockFiller( actualDomChild, domConverter.blockFiller ) && | ||
isBlockFiller( expectedDomChild, domConverter.blockFiller ) ) { | ||
return true; | ||
} | ||
/** | ||
* Compares view element's actual and expected children and returns an action sequence which can be used to transform | ||
* actual children into expected ones. | ||
* | ||
* @private | ||
* @param {module:engine/view/node~Node} viewElement The view element whose children will be compared. | ||
* @param {module:engine/view/position~Position} [inlineFillerPosition=null] The position where the inline | ||
* filler should be rendered. | ||
* @returns {Object|null} result | ||
* @returns {Array.<String>} result.actions List of actions based on {@link module:utils/diff~diff} function. | ||
* @returns {Array.<Node>} result.actualDomChildren Current view element's DOM children. | ||
* @returns {Array.<Node>} result.expectedDomChildren Expected view element's DOM children. | ||
*/ | ||
_diffChildren( viewElement, inlineFillerPosition = null ) { | ||
const domElement = this.domConverter.mapViewToDom( viewElement ); | ||
const actualDomChildren = domElement.childNodes; | ||
const expectedDomChildren = this._getElementExpectedChildren( viewElement, domElement, | ||
{ withChildren: false, inlineFillerPosition } ); | ||
// Not matching types. | ||
return false; | ||
return { | ||
actions: diff( actualDomChildren, expectedDomChildren, sameNodes.bind( null, this.domConverter.blockFiller ) ), | ||
actualDomChildren, | ||
expectedDomChildren | ||
}; | ||
} | ||
/** | ||
* Returns expected DOM children for a given view element. | ||
* | ||
* @private | ||
* @param {module:engine/view/node~Node} viewElement View element whose children will be returned. | ||
* @param {Node} domElement The DOM representation of a given view element. | ||
* @param {Object} options See the {@link module:engine/view/domconverter~DomConverter#viewToDom} options parameter. | ||
* @param {module:engine/view/position~Position} [options.inlineFillerPosition=null] The position where | ||
* the inline filler should be rendered. | ||
* @returns {Array.<Node>} The view element's expected children. | ||
*/ | ||
_getElementExpectedChildren( viewElement, domElement, options ) { | ||
const expectedDomChildren = Array.from( this.domConverter.viewChildrenToDom( viewElement, domElement.ownerDocument, options ) ); | ||
const filler = options.inlineFillerPosition; | ||
// Inline filler element has to be created as it is present in a DOM, but not in a view. It is required | ||
// during diffing so text nodes could be compared correctly and also during rendering to maintain | ||
// proper order and indexes while updating the DOM. | ||
if ( filler && filler.parent === viewElement ) { | ||
this._addInlineFiller( domElement.ownerDocument, expectedDomChildren, filler.offset ); | ||
} | ||
return expectedDomChildren; | ||
} | ||
/** | ||
* Marks text nodes to be synced. | ||
* Finds DOM nodes that were replaced with the similar nodes (same tag name) in the view. All nodes are compared | ||
* within one `insert`/`delete` action group, for example: | ||
* | ||
* Actual DOM: <p><b>Foo</b>Bar<i>Baz</i><b>Bax</b></p> | ||
* Expected DOM: <p>Bar<b>123</b><i>Baz</i><b>456</b></p> | ||
* Input actions: [ insert, insert, delete, delete, equal, insert, delete ] | ||
* Output actions: [ insert, replace, delete, equal, replace ] | ||
* | ||
* @private | ||
* @param {Array.<String>} actions Actions array which is a result of the {@link module:utils/diff~diff} function. | ||
* @param {Array.<Node>} actualDom Actual DOM children | ||
* @param {Array.<Node>} expectedDom Expected DOM children. | ||
* @returns {Array.<String>} Actions array modified with the `replace` actions. | ||
*/ | ||
_findReplaceActions( actions, actualDom, expectedDom ) { | ||
// If there is no both 'insert' and 'delete' actions, no need to check for replaced elements. | ||
if ( actions.indexOf( 'insert' ) === -1 || actions.indexOf( 'delete' ) === -1 ) { | ||
return actions; | ||
} | ||
let newActions = []; | ||
let actualSlice = []; | ||
let expectedSlice = []; | ||
const counter = { equal: 0, insert: 0, delete: 0 }; | ||
for ( const action of actions ) { | ||
if ( action === 'insert' ) { | ||
expectedSlice.push( expectedDom[ counter.equal + counter.insert ] ); | ||
} else if ( action === 'delete' ) { | ||
actualSlice.push( actualDom[ counter.equal + counter.delete ] ); | ||
} else { // equal | ||
newActions = newActions.concat( diff( actualSlice, expectedSlice, areSimilar ).map( x => x === 'equal' ? 'replace' : x ) ); | ||
newActions.push( 'equal' ); | ||
// Reset stored elements on 'equal'. | ||
actualSlice = []; | ||
expectedSlice = []; | ||
} | ||
counter[ action ]++; | ||
} | ||
return newActions.concat( diff( actualSlice, expectedSlice, areSimilar ).map( x => x === 'equal' ? 'replace' : x ) ); | ||
} | ||
/** | ||
* Marks text nodes to be synchronized. | ||
* | ||
* If a text node is passed, it will be marked. If an element is passed, all descendant text nodes inside it will be marked. | ||
@@ -576,3 +733,3 @@ * | ||
/** | ||
* Checks if selection needs to be updated and possibly updates it. | ||
* Checks if the selection needs to be updated and possibly updates it. | ||
* | ||
@@ -607,6 +764,6 @@ * @private | ||
/** | ||
* Updates fake selection. | ||
* Updates the fake selection. | ||
* | ||
* @private | ||
* @param {HTMLElement} domRoot Valid DOM root where fake selection container should be added. | ||
* @param {HTMLElement} domRoot A valid DOM root where the fake selection container should be added. | ||
*/ | ||
@@ -654,6 +811,6 @@ _updateFakeSelection( domRoot ) { | ||
/** | ||
* Updates DOM selection. | ||
* Updates the DOM selection. | ||
* | ||
* @private | ||
* @param {HTMLElement} domRoot Valid DOM root where DOM selection should be rendered. | ||
* @param {HTMLElement} domRoot A valid DOM root where the DOM selection should be rendered. | ||
*/ | ||
@@ -685,6 +842,6 @@ _updateDomSelection( domRoot ) { | ||
/** | ||
* Checks whether given DOM selection needs to be updated. | ||
* Checks whether a given DOM selection needs to be updated. | ||
* | ||
* @private | ||
* @param {Selection} domSelection DOM selection to check. | ||
* @param {Selection} domSelection The DOM selection to check. | ||
* @returns {Boolean} | ||
@@ -715,3 +872,3 @@ */ | ||
/** | ||
* Removes DOM selection. | ||
* Removes the DOM selection. | ||
* | ||
@@ -736,3 +893,3 @@ * @private | ||
/** | ||
* Removes fake selection. | ||
* Removes the fake selection. | ||
* | ||
@@ -781,1 +938,45 @@ * @private | ||
} | ||
// Whether two DOM nodes should be considered as similar. | ||
// Nodes are considered similar if they have the same tag name. | ||
// | ||
// @private | ||
// @param {Node} node1 | ||
// @param {Node} node2 | ||
// @returns {Boolean} | ||
function areSimilar( node1, node2 ) { | ||
return isNode( node1 ) && isNode( node2 ) && | ||
!isText( node1 ) && !isText( node2 ) && | ||
node1.tagName.toLowerCase() === node2.tagName.toLowerCase(); | ||
} | ||
// Whether two dom nodes should be considered as the same. | ||
// Two nodes which are considered the same are: | ||
// | ||
// * Text nodes with the same text. | ||
// * Element nodes represented by the same object. | ||
// * Two block filler elements. | ||
// | ||
// @private | ||
// @param {Function} blockFiller Block filler creator function, see {@link module:engine/view/domconverter~DomConverter#blockFiller}. | ||
// @param {Node} node1 | ||
// @param {Node} node2 | ||
// @returns {Boolean} | ||
function sameNodes( blockFiller, actualDomChild, expectedDomChild ) { | ||
// Elements. | ||
if ( actualDomChild === expectedDomChild ) { | ||
return true; | ||
} | ||
// Texts. | ||
else if ( isText( actualDomChild ) && isText( expectedDomChild ) ) { | ||
return actualDomChild.data === expectedDomChild.data; | ||
} | ||
// Block fillers. | ||
else if ( isBlockFiller( actualDomChild, blockFiller ) && | ||
isBlockFiller( expectedDomChild, blockFiller ) ) { | ||
return true; | ||
} | ||
// Not matching types. | ||
return false; | ||
} |
@@ -84,3 +84,3 @@ /** | ||
* @param {Document} domDocument | ||
* @return {HTMLElement} | ||
* @returns {HTMLElement} | ||
*/ | ||
@@ -87,0 +87,0 @@ render( domDocument ) { |
@@ -328,9 +328,10 @@ /** | ||
* This may be caused by: | ||
* * calling {@link #change} or {@link #render} during rendering process, | ||
* * calling {@link #change} or {@link #render} inside of | ||
* {@link module:engine/view/document~Document#registerPostFixer post fixer function}. | ||
* | ||
* * calling {@link #change} or {@link #render} during rendering process, | ||
* * calling {@link #change} or {@link #render} inside of | ||
* {@link module:engine/view/document~Document#registerPostFixer post-fixer function}. | ||
*/ | ||
throw new CKEditorError( | ||
'cannot-change-view-tree: ' + | ||
'Attempting to make changes to the view when it is in incorrect state: rendering or post fixers are in progress. ' + | ||
'Attempting to make changes to the view when it is in incorrect state: rendering or post-fixers are in progress. ' + | ||
'This may cause some unexpected behaviour and inconsistency between the DOM and the view.' | ||
@@ -353,3 +354,3 @@ ); | ||
// Execute all document post fixers after the change. | ||
// Execute all document post-fixers after the change. | ||
this._postFixersInProgress = true; | ||
@@ -400,3 +401,3 @@ this.document._callPostFixers( this._writer ); | ||
* Fired after a topmost {@link module:engine/view/view~View#change change block} and all | ||
* {@link module:engine/view/document~Document#registerPostFixer post fixers} are executed. | ||
* {@link module:engine/view/document~Document#registerPostFixer post-fixers} are executed. | ||
* | ||
@@ -411,3 +412,3 @@ * Actual rendering is performed as a first listener on 'normal' priority. | ||
* balloon panel. If you wants to change view structure use | ||
* {@link module:engine/view/document~Document#registerPostFixer post fixers}. | ||
* {@link module:engine/view/document~Document#registerPostFixer post-fixers}. | ||
* | ||
@@ -414,0 +415,0 @@ * @event module:engine/view/view~View#event:render |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1432594
123
33396