\n\t\t//\n\t\t// Without this special condition, the default algorithm leaves an \"artifact\" range from one of `differenceSet` parts:\n\t\t//\n\t\t//
ab
c[d
e]f
-->
ab
{
}
c[d
e]f
\n\t\t//\n\t\t// This special case is applied only if the range is to be kept together (not spread).\n\t\tconst moveRange = Range._createFromPositionAndShift( sourcePosition, howMany );\n\t\tconst insertPosition = targetPosition._getTransformedByDeletion( sourcePosition, howMany );\n\n\t\tif ( this.containsPosition( targetPosition ) && !spread ) {\n\t\t\tif ( moveRange.containsPosition( this.start ) || moveRange.containsPosition( this.end ) ) {\n\t\t\t\tconst start = this.start._getTransformedByMove( sourcePosition, targetPosition, howMany );\n\t\t\t\tconst end = this.end._getTransformedByMove( sourcePosition, targetPosition, howMany );\n\n\t\t\t\treturn [ new Range( start, end ) ];\n\t\t\t}\n\t\t}\n\n\t\t// Default algorithm.\n\t\tlet result;\n\n\t\tconst differenceSet = this.getDifference( moveRange );\n\t\tlet difference = null;\n\n\t\tconst common = this.getIntersection( moveRange );\n\n\t\tif ( differenceSet.length == 1 ) {\n\t\t\t// `moveRange` and this range may intersect but may be separate.\n\t\t\tdifference = new Range(\n\t\t\t\tdifferenceSet[ 0 ].start._getTransformedByDeletion( sourcePosition, howMany ),\n\t\t\t\tdifferenceSet[ 0 ].end._getTransformedByDeletion( sourcePosition, howMany )\n\t\t\t);\n\t\t} else if ( differenceSet.length == 2 ) {\n\t\t\t// `moveRange` is inside this range.\n\t\t\tdifference = new Range(\n\t\t\t\tthis.start,\n\t\t\t\tthis.end._getTransformedByDeletion( sourcePosition, howMany )\n\t\t\t);\n\t\t} // else, `moveRange` contains this range.\n\n\t\tif ( difference ) {\n\t\t\tresult = difference._getTransformedByInsertion( insertPosition, howMany, common !== null || spread );\n\t\t} else {\n\t\t\tresult = [];\n\t\t}\n\n\t\tif ( common ) {\n\t\t\tconst transformedCommon = new Range(\n\t\t\t\tcommon.start._getCombined( moveRange.start, insertPosition ),\n\t\t\t\tcommon.end._getCombined( moveRange.start, insertPosition )\n\t\t\t);\n\n\t\t\tif ( result.length == 2 ) {\n\t\t\t\tresult.splice( 1, 0, transformedCommon );\n\t\t\t} else {\n\t\t\t\tresult.push( transformedCommon );\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Returns a copy of this range that is transformed by deletion of `howMany` nodes from `deletePosition`.\n\t *\n\t * If the deleted range is intersecting with the transformed range, the transformed range will be shrank.\n\t *\n\t * If the deleted range contains transformed range, `null` will be returned.\n\t *\n\t * @protected\n\t * @param {module:engine/model/position~Position} deletionPosition Position from which nodes are removed.\n\t * @param {Number} howMany How many nodes are removed.\n\t * @returns {module:engine/model/range~Range|null} Result of the transformation.\n\t */\n\t_getTransformedByDeletion( deletePosition, howMany ) {\n\t\tlet newStart = this.start._getTransformedByDeletion( deletePosition, howMany );\n\t\tlet newEnd = this.end._getTransformedByDeletion( deletePosition, howMany );\n\n\t\tif ( newStart == null && newEnd == null ) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif ( newStart == null ) {\n\t\t\tnewStart = deletePosition;\n\t\t}\n\n\t\tif ( newEnd == null ) {\n\t\t\tnewEnd = deletePosition;\n\t\t}\n\n\t\treturn new Range( newStart, newEnd );\n\t}\n\n\t/**\n\t * Creates a new range, spreading from specified {@link module:engine/model/position~Position position} to a position moved by\n\t * given `shift`. If `shift` is a negative value, shifted position is treated as the beginning of the range.\n\t *\n\t * @protected\n\t * @param {module:engine/model/position~Position} position Beginning of the range.\n\t * @param {Number} shift How long the range should be.\n\t * @returns {module:engine/model/range~Range}\n\t */\n\tstatic _createFromPositionAndShift( position, shift ) {\n\t\tconst start = position;\n\t\tconst end = position.getShiftedBy( shift );\n\n\t\treturn shift > 0 ? new this( start, end ) : new this( end, start );\n\t}\n\n\t/**\n\t * Creates a range inside an {@link module:engine/model/element~Element element} which starts before the first child of\n\t * that element and ends after the last child of that element.\n\t *\n\t * @protected\n\t * @param {module:engine/model/element~Element} element Element which is a parent for the range.\n\t * @returns {module:engine/model/range~Range}\n\t */\n\tstatic _createIn( element ) {\n\t\treturn new this( Position._createAt( element, 0 ), Position._createAt( element, element.maxOffset ) );\n\t}\n\n\t/**\n\t * Creates a range that starts before given {@link module:engine/model/item~Item model item} and ends after it.\n\t *\n\t * @protected\n\t * @param {module:engine/model/item~Item} item\n\t * @returns {module:engine/model/range~Range}\n\t */\n\tstatic _createOn( item ) {\n\t\treturn this._createFromPositionAndShift( Position._createBefore( item ), item.offsetSize );\n\t}\n\n\t/**\n\t * Combines all ranges from the passed array into a one range. At least one range has to be passed.\n\t * Passed ranges must not have common parts.\n\t *\n\t * The first range from the array is a reference range. If other ranges start or end on the exactly same position where\n\t * the reference range, they get combined into one range.\n\t *\n\t *\t\t[ ][] [ ][ ][ ][ ][] [ ] // Passed ranges, shown sorted\n\t *\t\t[ ] // The result of the function if the first range was a reference range.\n\t *\t [ ] // The result of the function if the third-to-seventh range was a reference range.\n\t *\t [ ] // The result of the function if the last range was a reference range.\n\t *\n\t * @param {Array.} ranges Ranges to combine.\n\t * @returns {module:engine/model/range~Range} Combined range.\n\t */\n\tstatic _createFromRanges( ranges ) {\n\t\tif ( ranges.length === 0 ) {\n\t\t\t/**\n\t\t\t * At least one range has to be passed to\n\t\t\t * {@link module:engine/model/range~Range._createFromRanges `Range._createFromRanges()`}.\n\t\t\t *\n\t\t\t * @error range-create-from-ranges-empty-array\n\t\t\t */\n\t\t\tthrow new CKEditorError( 'range-create-from-ranges-empty-array: At least one range has to be passed.' );\n\t\t} else if ( ranges.length == 1 ) {\n\t\t\treturn ranges[ 0 ].clone();\n\t\t}\n\n\t\t// 1. Set the first range in `ranges` array as a reference range.\n\t\t// If we are going to return just a one range, one of the ranges need to be the reference one.\n\t\t// Other ranges will be stuck to that range, if possible.\n\t\tconst ref = ranges[ 0 ];\n\n\t\t// 2. Sort all the ranges so it's easier to process them.\n\t\tranges.sort( ( a, b ) => {\n\t\t\treturn a.start.isAfter( b.start ) ? 1 : -1;\n\t\t} );\n\n\t\t// 3. Check at which index the reference range is now.\n\t\tconst refIndex = ranges.indexOf( ref );\n\n\t\t// 4. At this moment we don't need the original range.\n\t\t// We are going to modify the result and we need to return a new instance of Range.\n\t\t// We have to create a copy of the reference range.\n\t\tconst result = new this( ref.start, ref.end );\n\n\t\t// 5. Ranges should be checked and glued starting from the range that is closest to the reference range.\n\t\t// Since ranges are sorted, start with the range with index that is closest to reference range index.\n\t\tif ( refIndex > 0 ) {\n\t\t\tfor ( let i = refIndex - 1; true; i++ ) {\n\t\t\t\tif ( ranges[ i ].end.isEqual( result.start ) ) {\n\t\t\t\t\tresult.start = Position._createAt( ranges[ i ].start );\n\t\t\t\t} else {\n\t\t\t\t\t// If ranges are not starting/ending at the same position there is no point in looking further.\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// 6. Ranges should be checked and glued starting from the range that is closest to the reference range.\n\t\t// Since ranges are sorted, start with the range with index that is closest to reference range index.\n\t\tfor ( let i = refIndex + 1; i < ranges.length; i++ ) {\n\t\t\tif ( ranges[ i ].start.isEqual( result.end ) ) {\n\t\t\t\tresult.end = Position._createAt( ranges[ i ].end );\n\t\t\t} else {\n\t\t\t\t// If ranges are not starting/ending at the same position there is no point in looking further.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Creates a `Range` instance from given plain object (i.e. parsed JSON string).\n\t *\n\t * @param {Object} json Plain object to be converted to `Range`.\n\t * @param {module:engine/model/document~Document} doc Document object that will be range owner.\n\t * @returns {module:engine/model/element~Element} `Range` instance created using given plain object.\n\t */\n\tstatic fromJSON( json, doc ) {\n\t\treturn new this( Position.fromJSON( json.start, doc ), Position.fromJSON( json.end, doc ) );\n\t}\n}\n","/**\n * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\n/**\n * @module engine/conversion/mapper\n */\n\nimport ModelPosition from '../model/position';\nimport ModelRange from '../model/range';\n\nimport ViewPosition from '../view/position';\nimport ViewRange from '../view/range';\nimport ViewText from '../view/text';\n\nimport EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';\nimport mix from '@ckeditor/ckeditor5-utils/src/mix';\n\n/**\n * Maps elements, positions and markers between {@link module:engine/view/document~Document the view} and\n * {@link module:engine/model/model the model}.\n *\n * Mapper use bound elements to find corresponding elements and positions, so, to get proper results,\n * all model elements should be {@link module:engine/conversion/mapper~Mapper#bindElements bound}.\n *\n * To map complex model to/from view relations, you may provide custom callbacks for\n * {@link module:engine/conversion/mapper~Mapper#event:modelToViewPosition modelToViewPosition event} and\n * {@link module:engine/conversion/mapper~Mapper#event:viewToModelPosition viewToModelPosition event} that are fired whenever\n * a position mapping request occurs.\n * Those events are fired by {@link module:engine/conversion/mapper~Mapper#toViewPosition toViewPosition}\n * and {@link module:engine/conversion/mapper~Mapper#toModelPosition toModelPosition} methods. `Mapper` adds it's own default callbacks\n * with `'lowest'` priority. To override default `Mapper` mapping, add custom callback with higher priority and\n * stop the event.\n */\nexport default class Mapper {\n\t/**\n\t * Creates an instance of the mapper.\n\t */\n\tconstructor() {\n\t\t/**\n\t\t * Model element to view element mapping.\n\t\t *\n\t\t * @private\n\t\t * @member {WeakMap}\n\t\t */\n\t\tthis._modelToViewMapping = new WeakMap();\n\n\t\t/**\n\t\t * View element to model element mapping.\n\t\t *\n\t\t * @private\n\t\t * @member {WeakMap}\n\t\t */\n\t\tthis._viewToModelMapping = new WeakMap();\n\n\t\t/**\n\t\t * A map containing callbacks between view element names and functions evaluating length of view elements\n\t\t * in model.\n\t\t *\n\t\t * @private\n\t\t * @member {Map}\n\t\t */\n\t\tthis._viewToModelLengthCallbacks = new Map();\n\n\t\t/**\n\t\t * Model marker name to view elements mapping.\n\t\t *\n\t\t * Keys are `String`s while values are `Set`s with {@link module:engine/view/element~Element view elements}.\n\t\t * One marker (name) can be mapped to multiple elements.\n\t\t *\n\t\t * @private\n\t\t * @member {Map}\n\t\t */\n\t\tthis._markerNameToElements = new Map();\n\n\t\t// Default mapper algorithm for mapping model position to view position.\n\t\tthis.on( 'modelToViewPosition', ( evt, data ) => {\n\t\t\tif ( data.viewPosition ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst viewContainer = this._modelToViewMapping.get( data.modelPosition.parent );\n\n\t\t\tdata.viewPosition = this._findPositionIn( viewContainer, data.modelPosition.offset );\n\t\t}, { priority: 'low' } );\n\n\t\t// Default mapper algorithm for mapping view position to model position.\n\t\tthis.on( 'viewToModelPosition', ( evt, data ) => {\n\t\t\tif ( data.modelPosition ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet viewBlock = data.viewPosition.parent;\n\t\t\tlet modelParent = this._viewToModelMapping.get( viewBlock );\n\n\t\t\twhile ( !modelParent ) {\n\t\t\t\tviewBlock = viewBlock.parent;\n\t\t\t\tmodelParent = this._viewToModelMapping.get( viewBlock );\n\t\t\t}\n\n\t\t\tconst modelOffset = this._toModelOffset( data.viewPosition.parent, data.viewPosition.offset, viewBlock );\n\n\t\t\tdata.modelPosition = ModelPosition._createAt( modelParent, modelOffset );\n\t\t}, { priority: 'low' } );\n\t}\n\n\t/**\n\t * Marks model and view elements as corresponding. Corresponding elements can be retrieved by using\n\t * the {@link module:engine/conversion/mapper~Mapper#toModelElement toModelElement} and\n\t * {@link module:engine/conversion/mapper~Mapper#toViewElement toViewElement} methods.\n\t * The information that elements are bound is also used to translate positions.\n\t *\n\t * @param {module:engine/model/element~Element} modelElement Model element.\n\t * @param {module:engine/view/element~Element} viewElement View element.\n\t */\n\tbindElements( modelElement, viewElement ) {\n\t\tthis._modelToViewMapping.set( modelElement, viewElement );\n\t\tthis._viewToModelMapping.set( viewElement, modelElement );\n\t}\n\n\t/**\n\t * Unbinds given {@link module:engine/view/element~Element view element} from the map.\n\t *\n\t * **Note:** view-to-model binding will be removed, if it existed. However, corresponding model-to-view binding\n\t * will be removed only if model element is still bound to passed `viewElement`.\n\t *\n\t * This behavior lets for re-binding model element to another view element without fear of losing the new binding\n\t * when the previously bound view element is unbound.\n\t *\n\t * @param {module:engine/view/element~Element} viewElement View element to unbind.\n\t */\n\tunbindViewElement( viewElement ) {\n\t\tconst modelElement = this.toModelElement( viewElement );\n\n\t\tthis._viewToModelMapping.delete( viewElement );\n\n\t\tif ( this._modelToViewMapping.get( modelElement ) == viewElement ) {\n\t\t\tthis._modelToViewMapping.delete( modelElement );\n\t\t}\n\t}\n\n\t/**\n\t * Unbinds given {@link module:engine/model/element~Element model element} from the map.\n\t *\n\t * **Note:** model-to-view binding will be removed, if it existed. However, corresponding view-to-model binding\n\t * will be removed only if view element is still bound to passed `modelElement`.\n\t *\n\t * This behavior lets for re-binding view element to another model element without fear of losing the new binding\n\t * when the previously bound model element is unbound.\n\t *\n\t * @param {module:engine/model/element~Element} modelElement Model element to unbind.\n\t */\n\tunbindModelElement( modelElement ) {\n\t\tconst viewElement = this.toViewElement( modelElement );\n\n\t\tthis._modelToViewMapping.delete( modelElement );\n\n\t\tif ( this._viewToModelMapping.get( viewElement ) == modelElement ) {\n\t\t\tthis._viewToModelMapping.delete( viewElement );\n\t\t}\n\t}\n\n\t/**\n\t * Binds given marker name with given {@link module:engine/view/element~Element view element}. The element\n\t * will be added to the current set of elements bound with given marker name.\n\t *\n\t * @param {module:engine/view/element~Element} element Element to bind.\n\t * @param {String} name Marker name.\n\t */\n\tbindElementToMarker( element, name ) {\n\t\tconst elements = this._markerNameToElements.get( name ) || new Set();\n\n\t\telements.add( element );\n\n\t\tthis._markerNameToElements.set( name, elements );\n\t}\n\n\t/**\n\t * Unbinds all elements from given marker name.\n\t *\n\t * @param {String} name Marker name.\n\t */\n\tunbindElementsFromMarkerName( name ) {\n\t\tthis._markerNameToElements.delete( name );\n\t}\n\n\t/**\n\t * Removes all model to view and view to model bindings.\n\t */\n\tclearBindings() {\n\t\tthis._modelToViewMapping = new WeakMap();\n\t\tthis._viewToModelMapping = new WeakMap();\n\t\tthis._markerNameToElements = new Map();\n\t}\n\n\t/**\n\t * Gets the corresponding model element.\n\t *\n\t * **Note:** {@link module:engine/view/uielement~UIElement} does not have corresponding element in model.\n\t *\n\t * @param {module:engine/view/element~Element} viewElement View element.\n\t * @returns {module:engine/model/element~Element|undefined} Corresponding model element or `undefined` if not found.\n\t */\n\ttoModelElement( viewElement ) {\n\t\treturn this._viewToModelMapping.get( viewElement );\n\t}\n\n\t/**\n\t * Gets the corresponding view element.\n\t *\n\t * @param {module:engine/model/element~Element} modelElement Model element.\n\t * @returns {module:engine/view/element~Element|undefined} Corresponding view element or `undefined` if not found.\n\t */\n\ttoViewElement( modelElement ) {\n\t\treturn this._modelToViewMapping.get( modelElement );\n\t}\n\n\t/**\n\t * Gets the corresponding model range.\n\t *\n\t * @param {module:engine/view/range~Range} viewRange View range.\n\t * @returns {module:engine/model/range~Range} Corresponding model range.\n\t */\n\ttoModelRange( viewRange ) {\n\t\treturn new ModelRange( this.toModelPosition( viewRange.start ), this.toModelPosition( viewRange.end ) );\n\t}\n\n\t/**\n\t * Gets the corresponding view range.\n\t *\n\t * @param {module:engine/model/range~Range} modelRange Model range.\n\t * @returns {module:engine/view/range~Range} Corresponding view range.\n\t */\n\ttoViewRange( modelRange ) {\n\t\treturn new ViewRange( this.toViewPosition( modelRange.start ), this.toViewPosition( modelRange.end ) );\n\t}\n\n\t/**\n\t * Gets the corresponding model position.\n\t *\n\t * @fires viewToModelPosition\n\t * @param {module:engine/view/position~Position} viewPosition View position.\n\t * @returns {module:engine/model/position~Position} Corresponding model position.\n\t */\n\ttoModelPosition( viewPosition ) {\n\t\tconst data = {\n\t\t\tviewPosition,\n\t\t\tmapper: this\n\t\t};\n\n\t\tthis.fire( 'viewToModelPosition', data );\n\n\t\treturn data.modelPosition;\n\t}\n\n\t/**\n\t * Gets the corresponding view position.\n\t *\n\t * @fires modelToViewPosition\n\t * @param {module:engine/model/position~Position} modelPosition Model position.\n\t * @param {Object} [options] Additional options for position mapping process.\n\t * @param {Boolean} [options.isPhantom=false] Should be set to `true` if the model position to map is pointing to a place\n\t * in model tree which no longer exists. For example, it could be an end of a removed model range.\n\t * @returns {module:engine/view/position~Position} Corresponding view position.\n\t */\n\ttoViewPosition( modelPosition, options = { isPhantom: false } ) {\n\t\tconst data = {\n\t\t\tmodelPosition,\n\t\t\tmapper: this,\n\t\t\tisPhantom: options.isPhantom\n\t\t};\n\n\t\tthis.fire( 'modelToViewPosition', data );\n\n\t\treturn data.viewPosition;\n\t}\n\n\t/**\n\t * Gets all view elements bound to the given marker name.\n\t *\n\t * @param {String} name Marker name.\n\t * @returns {Set.|null} View elements bound with given marker name or `null`\n\t * if no elements are bound to given marker name.\n\t */\n\tmarkerNameToElements( name ) {\n\t\tconst boundElements = this._markerNameToElements.get( name );\n\n\t\tif ( !boundElements ) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst elements = new Set();\n\n\t\tfor ( const element of boundElements ) {\n\t\t\tif ( element.is( 'attributeElement' ) ) {\n\t\t\t\tfor ( const clone of element.getElementsWithSameId() ) {\n\t\t\t\t\telements.add( clone );\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\telements.add( element );\n\t\t\t}\n\t\t}\n\n\t\treturn elements;\n\t}\n\n\t/**\n\t * Registers a callback that evaluates the length in the model of a view element with given name.\n\t *\n\t * The callback is fired with one argument, which is a view element instance. The callback is expected to return\n\t * a number representing the length of view element in model.\n\t *\n\t *\t\t// List item in view may contain nested list, which have other list items. In model though,\n\t *\t\t// the lists are represented by flat structure. Because of those differences, length of list view element\n\t *\t\t// may be greater than one. In the callback it's checked how many nested list items are in evaluated list item.\n\t *\n\t *\t\tfunction getViewListItemLength( element ) {\n\t *\t\t\tlet length = 1;\n\t *\n\t *\t\t\tfor ( let child of element.getChildren() ) {\n\t *\t\t\t\tif ( child.name == 'ul' || child.name == 'ol' ) {\n\t *\t\t\t\t\tfor ( let item of child.getChildren() ) {\n\t *\t\t\t\t\t\tlength += getViewListItemLength( item );\n\t *\t\t\t\t\t}\n\t *\t\t\t\t}\n\t *\t\t\t}\n\t *\n\t *\t\t\treturn length;\n\t *\t\t}\n\t *\n\t *\t\tmapper.registerViewToModelLength( 'li', getViewListItemLength );\n\t *\n\t * @param {String} viewElementName Name of view element for which callback is registered.\n\t * @param {Function} lengthCallback Function return a length of view element instance in model.\n\t */\n\tregisterViewToModelLength( viewElementName, lengthCallback ) {\n\t\tthis._viewToModelLengthCallbacks.set( viewElementName, lengthCallback );\n\t}\n\n\t/**\n\t * Calculates model offset based on the view position and the block element.\n\t *\n\t * Example:\n\t *\n\t *\t\t
fooba|r
// _toModelOffset( b, 2, p ) -> 5\n\t *\n\t * Is a sum of:\n\t *\n\t *\t\t
foo|bar
// _toModelOffset( p, 3, p ) -> 3\n\t *\t\t
fooba|r
// _toModelOffset( b, 2, b ) -> 2\n\t *\n\t * @private\n\t * @param {module:engine/view/element~Element} viewParent Position parent.\n\t * @param {Number} viewOffset Position offset.\n\t * @param {module:engine/view/element~Element} viewBlock Block used as a base to calculate offset.\n\t * @returns {Number} Offset in the model.\n\t */\n\t_toModelOffset( viewParent, viewOffset, viewBlock ) {\n\t\tif ( viewBlock != viewParent ) {\n\t\t\t// See example.\n\t\t\tconst offsetToParentStart = this._toModelOffset( viewParent.parent, viewParent.index, viewBlock );\n\t\t\tconst offsetInParent = this._toModelOffset( viewParent, viewOffset, viewParent );\n\n\t\t\treturn offsetToParentStart + offsetInParent;\n\t\t}\n\n\t\t// viewBlock == viewParent, so we need to calculate the offset in the parent element.\n\n\t\t// If the position is a text it is simple (\"ba|r\" -> 2).\n\t\tif ( viewParent.is( 'text' ) ) {\n\t\t\treturn viewOffset;\n\t\t}\n\n\t\t// If the position is in an element we need to sum lengths of siblings ( bar foo | -> 3 + 3 = 6 ).\n\t\tlet modelOffset = 0;\n\n\t\tfor ( let i = 0; i < viewOffset; i++ ) {\n\t\t\tmodelOffset += this.getModelLength( viewParent.getChild( i ) );\n\t\t}\n\n\t\treturn modelOffset;\n\t}\n\n\t/**\n\t * Gets the length of the view element in the model.\n\t *\n\t * The length is calculated as follows:\n\t * * if {@link #registerViewToModelLength length mapping callback} is provided for given `viewNode` it is used to\n\t * evaluate model length (`viewNode` is used as first and only parameter passed to the callback),\n\t * * length of a {@link module:engine/view/text~Text text node} is equal to the length of it's\n\t * {@link module:engine/view/text~Text#data data},\n\t * * length of a {@link module:engine/view/uielement~UIElement ui element} is equal to 0,\n\t * * length of a mapped {@link module:engine/view/element~Element element} is equal to 1,\n\t * * length of a not-mapped {@link module:engine/view/element~Element element} is equal to the length of it's children.\n\t *\n\t * Examples:\n\t *\n\t *\t\tfoo -> 3 // Text length is equal to it's data length.\n\t *\t\t
foo
-> 1 // Length of an element which is mapped is by default equal to 1.\n\t *\t\tfoo -> 3 // Length of an element which is not mapped is a length of its children.\n\t *\t\t
x
y
-> 2 // Assuming that
is not mapped and
are mapped.\n\t *\n\t * @param {module:engine/view/element~Element} viewNode View node.\n\t * @returns {Number} Length of the node in the tree model.\n\t */\n\tgetModelLength( viewNode ) {\n\t\tif ( this._viewToModelLengthCallbacks.get( viewNode.name ) ) {\n\t\t\tconst callback = this._viewToModelLengthCallbacks.get( viewNode.name );\n\n\t\t\treturn callback( viewNode );\n\t\t} else if ( this._viewToModelMapping.has( viewNode ) ) {\n\t\t\treturn 1;\n\t\t} else if ( viewNode.is( 'text' ) ) {\n\t\t\treturn viewNode.data.length;\n\t\t} else if ( viewNode.is( 'uiElement' ) ) {\n\t\t\treturn 0;\n\t\t} else {\n\t\t\tlet len = 0;\n\n\t\t\tfor ( const child of viewNode.getChildren() ) {\n\t\t\t\tlen += this.getModelLength( child );\n\t\t\t}\n\n\t\t\treturn len;\n\t\t}\n\t}\n\n\t/**\n\t * Finds the position in the view node (or its children) with the expected model offset.\n\t *\n\t * Example:\n\t *\n\t *\t\t
-> expected offset: 4, actual offset: 5 -> we are too far\n\t *\n\t *\t\t_findPositionIn( b, 4 - ( 5 - 3 ) ):\n\t *\t\t
fo|barbom
-> expected offset: 2, actual offset: 0\n\t *\t\t
fobar|bom
-> expected offset: 2, actual offset: 3 -> we are too far\n\t *\n\t *\t\t_findPositionIn( bar, 2 - ( 3 - 3 ) ):\n\t *\t\tWe are in the text node so we can simple find the offset.\n\t *\t\t
foba|rbom
-> expected offset: 2, actual offset: 2 -> position found\n\t *\n\t * @private\n\t * @param {module:engine/view/element~Element} viewParent Tree view element in which we are looking for the position.\n\t * @param {Number} expectedOffset Expected offset.\n\t * @returns {module:engine/view/position~Position} Found position.\n\t */\n\t_findPositionIn( viewParent, expectedOffset ) {\n\t\t// Last scanned view node.\n\t\tlet viewNode;\n\t\t// Length of the last scanned view node.\n\t\tlet lastLength = 0;\n\n\t\tlet modelOffset = 0;\n\t\tlet viewOffset = 0;\n\n\t\t// In the text node it is simple: offset in the model equals offset in the text.\n\t\tif ( viewParent.is( 'text' ) ) {\n\t\t\treturn new ViewPosition( viewParent, expectedOffset );\n\t\t}\n\n\t\t// In other cases we add lengths of child nodes to find the proper offset.\n\n\t\t// If it is smaller we add the length.\n\t\twhile ( modelOffset < expectedOffset ) {\n\t\t\tviewNode = viewParent.getChild( viewOffset );\n\t\t\tlastLength = this.getModelLength( viewNode );\n\t\t\tmodelOffset += lastLength;\n\t\t\tviewOffset++;\n\t\t}\n\n\t\t// If it equals we found the position.\n\t\tif ( modelOffset == expectedOffset ) {\n\t\t\treturn this._moveViewPositionToTextNode( new ViewPosition( viewParent, viewOffset ) );\n\t\t}\n\t\t// If it is higher we need to enter last child.\n\t\telse {\n\t\t\t// ( modelOffset - lastLength ) is the offset to the child we enter,\n\t\t\t// so we subtract it from the expected offset to fine the offset in the child.\n\t\t\treturn this._findPositionIn( viewNode, expectedOffset - ( modelOffset - lastLength ) );\n\t\t}\n\t}\n\n\t/**\n\t * Because we prefer positions in text nodes over positions next to text node moves view position to the text node\n\t * if it was next to it.\n\t *\n\t *\t\t
[]foo
->
[]foo
// do not touch if position is not directly next to text\n\t *\t\t
foo[]foo
->
foo{}foo
// move to text node\n\t *\t\t
[]foo
->
{}foo
// move to text node\n\t *\n\t * @private\n\t * @param {module:engine/view/position~Position} viewPosition Position potentially next to text node.\n\t * @returns {module:engine/view/position~Position} Position in text node if possible.\n\t */\n\t_moveViewPositionToTextNode( viewPosition ) {\n\t\t// If the position is just after text node, put it at the end of that text node.\n\t\t// If the position is just before text node, put it at the beginning of that text node.\n\t\tconst nodeBefore = viewPosition.nodeBefore;\n\t\tconst nodeAfter = viewPosition.nodeAfter;\n\n\t\tif ( nodeBefore instanceof ViewText ) {\n\t\t\treturn new ViewPosition( nodeBefore, nodeBefore.data.length );\n\t\t} else if ( nodeAfter instanceof ViewText ) {\n\t\t\treturn new ViewPosition( nodeAfter, 0 );\n\t\t}\n\n\t\t// Otherwise, just return the given position.\n\t\treturn viewPosition;\n\t}\n\n\t/**\n\t * Fired for each model-to-view position mapping request. The purpose of this event is to enable custom model-to-view position\n\t * mapping. Callbacks added to this event take {@link module:engine/model/position~Position model position} and are expected to\n\t * calculate {@link module:engine/view/position~Position view position}. Calculated view position should be added as `viewPosition`\n\t * value in `data` object that is passed as one of parameters to the event callback.\n\t *\n\t * \t\t// Assume that \"captionedImage\" model element is converted to and following elements in view,\n\t * \t\t// and the model element is bound to element. Force mapping model positions inside \"captionedImage\" to that\n\t * \t\t// element.\n\t *\t\tmapper.on( 'modelToViewPosition', ( evt, data ) => {\n\t *\t\t\tconst positionParent = modelPosition.parent;\n\t *\n\t *\t\t\tif ( positionParent.name == 'captionedImage' ) {\n\t *\t\t\t\tconst viewImg = data.mapper.toViewElement( positionParent );\n\t *\t\t\t\tconst viewCaption = viewImg.nextSibling; // The element.\n\t *\n\t *\t\t\t\tdata.viewPosition = new ViewPosition( viewCaption, modelPosition.offset );\n\t *\n\t *\t\t\t\t// Stop the event if other callbacks should not modify calculated value.\n\t *\t\t\t\tevt.stop();\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t * **Note:** keep in mind that sometimes a \"phantom\" model position is being converted. \"Phantom\" model position is\n\t * a position that points to a non-existing place in model. Such position might still be valid for conversion, though\n\t * (it would point to a correct place in view when converted). One example of such situation is when a range is\n\t * removed from model, there may be a need to map the range's end (which is no longer valid model position). To\n\t * handle such situation, check `data.isPhantom` flag:\n\t *\n\t * \t\t// Assume that there is \"customElement\" model element and whenever position is before it, we want to move it\n\t * \t\t// to the inside of the view element bound to \"customElement\".\n\t *\t\tmapper.on( 'modelToViewPosition', ( evt, data ) => {\n\t *\t\t\tif ( data.isPhantom ) {\n\t *\t\t\t\treturn;\n\t *\t\t\t}\n\t *\n\t *\t\t\t// Below line might crash for phantom position that does not exist in model.\n\t *\t\t\tconst sibling = data.modelPosition.nodeBefore;\n\t *\n\t *\t\t\t// Check if this is the element we are interested in.\n\t *\t\t\tif ( !sibling.is( 'customElement' ) ) {\n\t *\t\t\t\treturn;\n\t *\t\t\t}\n\t *\n\t *\t\t\tconst viewElement = data.mapper.toViewElement( sibling );\n\t *\n\t *\t\t\tdata.viewPosition = new ViewPosition( sibling, 0 );\n\t *\n\t *\t\t\tevt.stop();\n\t *\t\t} );\n\t *\n\t * **Note:** default mapping callback is provided with `low` priority setting and does not cancel the event, so it is possible to\n\t * attach a custom callback after default callback and also use `data.viewPosition` calculated by default callback\n\t * (for example to fix it).\n\t *\n\t * **Note:** default mapping callback will not fire if `data.viewPosition` is already set.\n\t *\n\t * **Note:** these callbacks are called **very often**. For efficiency reasons, it is advised to use them only when position\n\t * mapping between given model and view elements is unsolvable using just elements mapping and default algorithm. Also,\n\t * the condition that checks if special case scenario happened should be as simple as possible.\n\t *\n\t * @event modelToViewPosition\n\t * @param {Object} data Data pipeline object that can store and pass data between callbacks. The callback should add\n\t * `viewPosition` value to that object with calculated {@link module:engine/view/position~Position view position}.\n\t * @param {module:engine/conversion/mapper~Mapper} data.mapper Mapper instance that fired the event.\n\t */\n\n\t/**\n\t * Fired for each view-to-model position mapping request. See {@link module:engine/conversion/mapper~Mapper#event:modelToViewPosition}.\n\t *\n\t * \t\t// See example in `modelToViewPosition` event description.\n\t * \t\t// This custom mapping will map positions from element next to to the \"captionedImage\" element.\n\t *\t\tmapper.on( 'viewToModelPosition', ( evt, data ) => {\n\t *\t\t\tconst positionParent = viewPosition.parent;\n\t *\n\t *\t\t\tif ( positionParent.hasClass( 'image-caption' ) ) {\n\t *\t\t\t\tconst viewImg = positionParent.previousSibling;\n\t *\t\t\t\tconst modelImg = data.mapper.toModelElement( viewImg );\n\t *\n\t *\t\t\t\tdata.modelPosition = new ModelPosition( modelImg, viewPosition.offset );\n\t *\t\t\t\tevt.stop();\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t * **Note:** default mapping callback is provided with `low` priority setting and does not cancel the event, so it is possible to\n\t * attach a custom callback after default callback and also use `data.modelPosition` calculated by default callback\n\t * (for example to fix it).\n\t *\n\t * **Note:** default mapping callback will not fire if `data.modelPosition` is already set.\n\t *\n\t * **Note:** these callbacks are called **very often**. For efficiency reasons, it is advised to use them only when position\n\t * mapping between given model and view elements is unsolvable using just elements mapping and default algorithm. Also,\n\t * the condition that checks if special case scenario happened should be as simple as possible.\n\t *\n\t * @event viewToModelPosition\n\t * @param {Object} data Data pipeline object that can store and pass data between callbacks. The callback should add\n\t * `modelPosition` value to that object with calculated {@link module:engine/model/position~Position model position}.\n\t * @param {module:engine/conversion/mapper~Mapper} data.mapper Mapper instance that fired the event.\n\t */\n}\n\nmix( Mapper, EmitterMixin );\n","/**\n * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\n/**\n * @module engine/conversion/modelconsumable\n */\n\nimport TextProxy from '../model/textproxy';\n\n/**\n * Manages a list of consumable values for {@link module:engine/model/item~Item model items}.\n *\n * Consumables are various aspects of the model. A model item can be broken down into singular properties that might be\n * taken into consideration when converting that item.\n *\n * `ModelConsumable` is used by {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher} while analyzing changed\n * parts of {@link module:engine/model/document~Document the document}. The added / changed / removed model items are broken down\n * into singular properties (the item itself and it's attributes). All those parts are saved in `ModelConsumable`. Then,\n * during conversion, when given part of model item is converted (i.e. the view element has been inserted into the view,\n * but without attributes), consumable value is removed from `ModelConsumable`.\n *\n * For model items, `ModelConsumable` stores consumable values of one of following types: `insert`, `addattribute:`,\n * `changeattributes:`, `removeattributes:`.\n *\n * In most cases, it is enough to let {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher}\n * gather consumable values, so there is no need to use\n * {@link module:engine/conversion/modelconsumable~ModelConsumable#add add method} directly.\n * However, it is important to understand how consumable values can be\n * {@link module:engine/conversion/modelconsumable~ModelConsumable#consume consumed}.\n * See {@link module:engine/conversion/downcast-selection-converters default downcast converters} for more information.\n *\n * Keep in mind, that one conversion event may have multiple callbacks (converters) attached to it. Each of those is\n * able to convert one or more parts of the model. However, when one of those callbacks actually converts\n * something, other should not, because they would duplicate the results. Using `ModelConsumable` helps avoiding\n * this situation, because callbacks should only convert those values, which were not yet consumed from `ModelConsumable`.\n *\n * Consuming multiple values in a single callback:\n *\n *\t\t// Converter for custom `image` element that might have a `caption` element inside which changes\n *\t\t// how the image is displayed in the view:\n *\t\t//\n *\t\t// Model:\n *\t\t//\n *\t\t// [image]\n *\t\t// └─ [caption]\n *\t\t// └─ foo\n *\t\t//\n *\t\t// View:\n *\t\t//\n *\t\t// \n *\t\t// ├─ \n *\t\t// └─
\n *\t\t// └─ foo\n *\t\tmodelConversionDispatcher.on( 'insert:image', ( evt, data, conversionApi ) => {\n *\t\t\t// First, consume the `image` element.\n *\t\t\tconversionApi.consumable.consume( data.item, 'insert' );\n *\n *\t\t\t// Just create normal image element for the view.\n *\t\t\t// Maybe it will be \"decorated\" later.\n *\t\t\tconst viewImage = new ViewElement( 'img' );\n *\t\t\tconst insertPosition = conversionApi.mapper.toViewPosition( data.range.start );\n *\t\t\tconst viewWriter = conversionApi.writer;\n *\n *\t\t\t// Check if the `image` element has children.\n *\t\t\tif ( data.item.childCount > 0 ) {\n *\t\t\t\tconst modelCaption = data.item.getChild( 0 );\n *\n *\t\t\t\t// `modelCaption` insertion change is consumed from consumable values.\n *\t\t\t\t// It will not be converted by other converters, but it's children (probably some text) will be.\n *\t\t\t\t// Through mapping, converters for text will know where to insert contents of `modelCaption`.\n *\t\t\t\tif ( conversionApi.consumable.consume( modelCaption, 'insert' ) ) {\n *\t\t\t\t\tconst viewCaption = new ViewElement( 'figcaption' );\n *\n *\t\t\t\t\tconst viewImageHolder = new ViewElement( 'figure', null, [ viewImage, viewCaption ] );\n *\n *\t\t\t\t\tconversionApi.mapper.bindElements( modelCaption, viewCaption );\n *\t\t\t\t\tconversionApi.mapper.bindElements( data.item, viewImageHolder );\n *\t\t\t\t\tviewWriter.insert( insertPosition, viewImageHolder );\n *\t\t\t\t}\n *\t\t\t} else {\n *\t\t\t\tconversionApi.mapper.bindElements( data.item, viewImage );\n *\t\t\t\tviewWriter.insert( insertPosition, viewImage );\n *\t\t\t}\n *\n *\t\t\tevt.stop();\n *\t\t} );\n */\nexport default class ModelConsumable {\n\t/**\n\t * Creates an empty consumables list.\n\t */\n\tconstructor() {\n\t\t/**\n\t\t * Contains list of consumable values.\n\t\t *\n\t\t * @private\n\t\t * @member {Map} module:engine/conversion/modelconsumable~ModelConsumable#_consumable\n\t\t */\n\t\tthis._consumable = new Map();\n\n\t\t/**\n\t\t * For each {@link module:engine/model/textproxy~TextProxy} added to `ModelConsumable`, this registry holds parent\n\t\t * of that `TextProxy` and start and end indices of that `TextProxy`. This allows identification of `TextProxy`\n\t\t * instances that points to the same part of the model but are different instances. Each distinct `TextProxy`\n\t\t * is given unique `Symbol` which is then registered as consumable. This process is transparent for `ModelConsumable`\n\t\t * API user because whenever `TextProxy` is added, tested, consumed or reverted, internal mechanisms of\n\t\t * `ModelConsumable` translates `TextProxy` to that unique `Symbol`.\n\t\t *\n\t\t * @private\n\t\t * @member {Map} module:engine/conversion/modelconsumable~ModelConsumable#_textProxyRegistry\n\t\t */\n\t\tthis._textProxyRegistry = new Map();\n\t}\n\n\t/**\n\t * Adds a consumable value to the consumables list and links it with given model item.\n\t *\n\t *\t\tmodelConsumable.add( modelElement, 'insert' ); // Add `modelElement` insertion change to consumable values.\n\t *\t\tmodelConsumable.add( modelElement, 'addAttribute:bold' ); // Add `bold` attribute insertion on `modelElement` change.\n\t *\t\tmodelConsumable.add( modelElement, 'removeAttribute:bold' ); // Add `bold` attribute removal on `modelElement` change.\n\t *\t\tmodelConsumable.add( modelSelection, 'selection' ); // Add `modelSelection` to consumable values.\n\t *\t\tmodelConsumable.add( modelRange, 'range' ); // Add `modelRange` to consumable values.\n\t *\n\t * @param {module:engine/model/item~Item|module:engine/model/selection~Selection|module:engine/model/range~Range} item\n\t * Model item, range or selection that has the consumable.\n\t * @param {String} type Consumable type. Will be normalized to a proper form, that is either `` or `:`.\n\t * Second colon and everything after will be cut. Passing event name is a safe and good practice.\n\t */\n\tadd( item, type ) {\n\t\ttype = _normalizeConsumableType( type );\n\n\t\tif ( item instanceof TextProxy ) {\n\t\t\titem = this._getSymbolForTextProxy( item );\n\t\t}\n\n\t\tif ( !this._consumable.has( item ) ) {\n\t\t\tthis._consumable.set( item, new Map() );\n\t\t}\n\n\t\tthis._consumable.get( item ).set( type, true );\n\t}\n\n\t/**\n\t * Removes given consumable value from given model item.\n\t *\n\t *\t\tmodelConsumable.consume( modelElement, 'insert' ); // Remove `modelElement` insertion change from consumable values.\n\t *\t\tmodelConsumable.consume( modelElement, 'addAttribute:bold' ); // Remove `bold` attribute insertion on `modelElement` change.\n\t *\t\tmodelConsumable.consume( modelElement, 'removeAttribute:bold' ); // Remove `bold` attribute removal on `modelElement` change.\n\t *\t\tmodelConsumable.consume( modelSelection, 'selection' ); // Remove `modelSelection` from consumable values.\n\t *\t\tmodelConsumable.consume( modelRange, 'range' ); // Remove 'modelRange' from consumable values.\n\t *\n\t * @param {module:engine/model/item~Item|module:engine/model/selection~Selection|module:engine/model/range~Range} item\n\t * Model item, range or selection from which consumable will be consumed.\n\t * @param {String} type Consumable type. Will be normalized to a proper form, that is either `` or `:`.\n\t * Second colon and everything after will be cut. Passing event name is a safe and good practice.\n\t * @returns {Boolean} `true` if consumable value was available and was consumed, `false` otherwise.\n\t */\n\tconsume( item, type ) {\n\t\ttype = _normalizeConsumableType( type );\n\n\t\tif ( item instanceof TextProxy ) {\n\t\t\titem = this._getSymbolForTextProxy( item );\n\t\t}\n\n\t\tif ( this.test( item, type ) ) {\n\t\t\tthis._consumable.get( item ).set( type, false );\n\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Tests whether there is a consumable value of given type connected with given model item.\n\t *\n\t *\t\tmodelConsumable.test( modelElement, 'insert' ); // Check for `modelElement` insertion change.\n\t *\t\tmodelConsumable.test( modelElement, 'addAttribute:bold' ); // Check for `bold` attribute insertion on `modelElement` change.\n\t *\t\tmodelConsumable.test( modelElement, 'removeAttribute:bold' ); // Check for `bold` attribute removal on `modelElement` change.\n\t *\t\tmodelConsumable.test( modelSelection, 'selection' ); // Check if `modelSelection` is consumable.\n\t *\t\tmodelConsumable.test( modelRange, 'range' ); // Check if `modelRange` is consumable.\n\t *\n\t * @param {module:engine/model/item~Item|module:engine/model/selection~Selection|module:engine/model/range~Range} item\n\t * Model item, range or selection to be tested.\n\t * @param {String} type Consumable type. Will be normalized to a proper form, that is either `` or `:`.\n\t * Second colon and everything after will be cut. Passing event name is a safe and good practice.\n\t * @returns {null|Boolean} `null` if such consumable was never added, `false` if the consumable values was\n\t * already consumed or `true` if it was added and not consumed yet.\n\t */\n\ttest( item, type ) {\n\t\ttype = _normalizeConsumableType( type );\n\n\t\tif ( item instanceof TextProxy ) {\n\t\t\titem = this._getSymbolForTextProxy( item );\n\t\t}\n\n\t\tconst itemConsumables = this._consumable.get( item );\n\n\t\tif ( itemConsumables === undefined ) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst value = itemConsumables.get( type );\n\n\t\tif ( value === undefined ) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn value;\n\t}\n\n\t/**\n\t * Reverts consuming of consumable value.\n\t *\n\t *\t\tmodelConsumable.revert( modelElement, 'insert' ); // Revert consuming `modelElement` insertion change.\n\t *\t\tmodelConsumable.revert( modelElement, 'addAttribute:bold' ); // Revert consuming `bold` attribute insert from `modelElement`.\n\t *\t\tmodelConsumable.revert( modelElement, 'removeAttribute:bold' ); // Revert consuming `bold` attribute remove from `modelElement`.\n\t *\t\tmodelConsumable.revert( modelSelection, 'selection' ); // Revert consuming `modelSelection`.\n\t *\t\tmodelConsumable.revert( modelRange, 'range' ); // Revert consuming `modelRange`.\n\t *\n\t * @param {module:engine/model/item~Item|module:engine/model/selection~Selection|module:engine/model/range~Range} item\n\t * Model item, range or selection to be reverted.\n\t * @param {String} type Consumable type.\n\t * @returns {null|Boolean} `true` if consumable has been reversed, `false` otherwise. `null` if the consumable has\n\t * never been added.\n\t */\n\trevert( item, type ) {\n\t\ttype = _normalizeConsumableType( type );\n\n\t\tif ( item instanceof TextProxy ) {\n\t\t\titem = this._getSymbolForTextProxy( item );\n\t\t}\n\n\t\tconst test = this.test( item, type );\n\n\t\tif ( test === false ) {\n\t\t\tthis._consumable.get( item ).set( type, true );\n\n\t\t\treturn true;\n\t\t} else if ( test === true ) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Gets a unique symbol for passed {@link module:engine/model/textproxy~TextProxy} instance. All `TextProxy` instances that\n\t * have same parent, same start index and same end index will get the same symbol.\n\t *\n\t * Used internally to correctly consume `TextProxy` instances.\n\t *\n\t * @private\n\t * @param {module:engine/model/textproxy~TextProxy} textProxy `TextProxy` instance to get a symbol for.\n\t * @returns {Symbol} Symbol representing all equal instances of `TextProxy`.\n\t */\n\t_getSymbolForTextProxy( textProxy ) {\n\t\tlet symbol = null;\n\n\t\tconst startMap = this._textProxyRegistry.get( textProxy.startOffset );\n\n\t\tif ( startMap ) {\n\t\t\tconst endMap = startMap.get( textProxy.endOffset );\n\n\t\t\tif ( endMap ) {\n\t\t\t\tsymbol = endMap.get( textProxy.parent );\n\t\t\t}\n\t\t}\n\n\t\tif ( !symbol ) {\n\t\t\tsymbol = this._addSymbolForTextProxy( textProxy.startOffset, textProxy.endOffset, textProxy.parent );\n\t\t}\n\n\t\treturn symbol;\n\t}\n\n\t/**\n\t * Adds a symbol for given properties that characterizes a {@link module:engine/model/textproxy~TextProxy} instance.\n\t *\n\t * Used internally to correctly consume `TextProxy` instances.\n\t *\n\t * @private\n\t * @param {Number} startIndex Text proxy start index in it's parent.\n\t * @param {Number} endIndex Text proxy end index in it's parent.\n\t * @param {module:engine/model/element~Element} parent Text proxy parent.\n\t * @returns {Symbol} Symbol generated for given properties.\n\t */\n\t_addSymbolForTextProxy( start, end, parent ) {\n\t\tconst symbol = Symbol( 'textProxySymbol' );\n\t\tlet startMap, endMap;\n\n\t\tstartMap = this._textProxyRegistry.get( start );\n\n\t\tif ( !startMap ) {\n\t\t\tstartMap = new Map();\n\t\t\tthis._textProxyRegistry.set( start, startMap );\n\t\t}\n\n\t\tendMap = startMap.get( end );\n\n\t\tif ( !endMap ) {\n\t\t\tendMap = new Map();\n\t\t\tstartMap.set( end, endMap );\n\t\t}\n\n\t\tendMap.set( parent, symbol );\n\n\t\treturn symbol;\n\t}\n}\n\n// Returns a normalized consumable type name from given string. A normalized consumable type name is a string that has\n// at most one colon, for example: `insert` or `addMarker:highlight`. If string to normalize has more \"parts\" (more colons),\n// the other parts are dropped, for example: `addattribute:bold:$text` -> `addattributes:bold`.\n//\n// @param {String} type Consumable type.\n// @returns {String} Normalized consumable type.\nfunction _normalizeConsumableType( type ) {\n\tconst parts = type.split( ':' );\n\n\treturn parts.length > 1 ? parts[ 0 ] + ':' + parts[ 1 ] : parts[ 0 ];\n}\n","/**\n * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\n/**\n * @module engine/conversion/downcastdispatcher\n */\n\nimport Consumable from './modelconsumable';\nimport Range from '../model/range';\nimport EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';\nimport mix from '@ckeditor/ckeditor5-utils/src/mix';\nimport { extend } from 'lodash-es';\n\n/**\n * `DowncastDispatcher` is a central point of downcasting (conversion from model to view), which is a process of reacting to changes\n * in the model and firing a set of events. Callbacks listening to those events are called converters. Those\n * converters role is to convert the model changes to changes in view (for example, adding view nodes or\n * changing attributes on view elements).\n *\n * During conversion process, `DowncastDispatcher` fires events, basing on state of the model and prepares\n * data for those events. It is important to understand that those events are connected with changes done on model,\n * for example: \"node has been inserted\" or \"attribute has changed\". This is in a contrary to upcasting (view to model conversion),\n * where we convert view state (view nodes) to a model tree.\n *\n * The events are prepared basing on a diff created by {@link module:engine/model/differ~Differ Differ}, which buffers them\n * and then passes to `DowncastDispatcher` as a diff between old model state and new model state.\n *\n * Note, that because changes are converted there is a need to have a mapping between model structure and view structure.\n * To map positions and elements during downcast (model to view conversion) use {@link module:engine/conversion/mapper~Mapper}.\n *\n * `DowncastDispatcher` fires following events for model tree changes:\n *\n * * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:insert insert}\n * if a range of nodes has been inserted to the model tree,\n * * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:remove remove}\n * if a range of nodes has been removed from the model tree,\n * * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:attribute attribute}\n * if attribute has been added, changed or removed from a model node.\n *\n * For {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:insert insert}\n * and {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:attribute attribute},\n * `DowncastDispatcher` generates {@link module:engine/conversion/modelconsumable~ModelConsumable consumables}.\n * These are used to have a control over which changes has been already consumed. It is useful when some converters\n * overwrite other or converts multiple changes (for example converts insertion of an element and also converts that\n * element's attributes during insertion).\n *\n * Additionally, `DowncastDispatcher` fires events for {@link module:engine/model/markercollection~Marker marker} changes:\n *\n * * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:addMarker} if a marker has been added,\n * * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:removeMarker} if a marker has been removed.\n *\n * Note, that changing a marker is done through removing the marker from the old range, and adding on the new range,\n * so both those events are fired.\n *\n * Finally, `DowncastDispatcher` also handles firing events for {@link module:engine/model/selection model selection}\n * conversion:\n *\n * * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:selection}\n * which converts selection from model to view,\n * * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:attribute}\n * which is fired for every selection attribute,\n * * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:addMarker}\n * which is fired for every marker which contains selection.\n *\n * Unlike model tree and markers, events for selection are not fired for changes but for selection state.\n *\n * When providing custom listeners for `DowncastDispatcher` remember to check whether given change has not been\n * {@link module:engine/conversion/modelconsumable~ModelConsumable#consume consumed} yet.\n *\n * When providing custom listeners for `DowncastDispatcher` keep in mind that any callback that had\n * {@link module:engine/conversion/modelconsumable~ModelConsumable#consume consumed} a value from a consumable and\n * converted the change should also stop the event (for efficiency purposes).\n *\n * When providing custom listeners for `DowncastDispatcher` remember to use provided\n * {@link module:engine/view/downcastwriter~DowncastWriter view downcast writer} to apply changes to the view document.\n *\n * Example of a custom converter for `DowncastDispatcher`:\n *\n *\t\t// We will convert inserting \"paragraph\" model element into the model.\n *\t\tdowncastDispatcher.on( 'insert:paragraph', ( evt, data, conversionApi ) => {\n *\t\t\t// Remember to check whether the change has not been consumed yet and consume it.\n *\t\t\tif ( conversionApi.consumable.consume( data.item, 'insert' ) ) {\n *\t\t\t\treturn;\n *\t\t\t}\n *\n *\t\t\t// Translate position in model to position in view.\n *\t\t\tconst viewPosition = conversionApi.mapper.toViewPosition( data.range.start );\n *\n *\t\t\t// Create
element that will be inserted in view at `viewPosition`.\n *\t\t\tconst viewElement = conversionApi.writer.createContainerElement( 'p' );\n *\n *\t\t\t// Bind the newly created view element to model element so positions will map accordingly in future.\n *\t\t\tconversionApi.mapper.bindElements( data.item, viewElement );\n *\n *\t\t\t// Add the newly created view element to the view.\n *\t\t\tconversionApi.writer.insert( viewPosition, viewElement );\n *\n *\t\t\t// Remember to stop the event propagation.\n *\t\t\tevt.stop();\n *\t\t} );\n */\nexport default class DowncastDispatcher {\n\t/**\n\t * Creates a `DowncastDispatcher` instance.\n\t *\n\t * @param {Object} [conversionApi] Interface passed by dispatcher to the events calls.\n\t */\n\tconstructor( conversionApi = {} ) {\n\t\t/**\n\t\t * Interface passed by dispatcher to the events callbacks.\n\t\t *\n\t\t * @member {Object}\n\t\t */\n\t\tthis.conversionApi = extend( { dispatcher: this }, conversionApi );\n\t}\n\n\t/**\n\t * Takes {@link module:engine/model/differ~Differ model differ} object with buffered changes and fires conversion basing on it.\n\t *\n\t * @param {module:engine/model/differ~Differ} differ Differ object with buffered changes.\n\t * @param {module:engine/view/downcastwriter~DowncastWriter} writer View writer that should be used to modify view document.\n\t */\n\tconvertChanges( differ, writer ) {\n\t\t// Before the view is updated, remove markers which have changed.\n\t\tfor ( const change of differ.getMarkersToRemove() ) {\n\t\t\tthis.convertMarkerRemove( change.name, change.range, writer );\n\t\t}\n\n\t\t// Convert changes that happened on model tree.\n\t\tfor ( const entry of differ.getChanges() ) {\n\t\t\tif ( entry.type == 'insert' ) {\n\t\t\t\tthis.convertInsert( Range._createFromPositionAndShift( entry.position, entry.length ), writer );\n\t\t\t} else if ( entry.type == 'remove' ) {\n\t\t\t\tthis.convertRemove( entry.position, entry.length, entry.name, writer );\n\t\t\t} else {\n\t\t\t\t// entry.type == 'attribute'.\n\t\t\t\tthis.convertAttribute( entry.range, entry.attributeKey, entry.attributeOldValue, entry.attributeNewValue, writer );\n\t\t\t}\n\t\t}\n\n\t\t// After the view is updated, convert markers which have changed.\n\t\tfor ( const change of differ.getMarkersToAdd() ) {\n\t\t\tthis.convertMarkerAdd( change.name, change.range, writer );\n\t\t}\n\t}\n\n\t/**\n\t * Starts conversion of a range insertion.\n\t *\n\t * For each node in the range, {@link #event:insert insert event is fired}. For each attribute on each node,\n\t * {@link #event:attribute attribute event is fired}.\n\t *\n\t * @fires insert\n\t * @fires attribute\n\t * @param {module:engine/model/range~Range} range Inserted range.\n\t * @param {module:engine/view/downcastwriter~DowncastWriter} writer View writer that should be used to modify view document.\n\t */\n\tconvertInsert( range, writer ) {\n\t\tthis.conversionApi.writer = writer;\n\n\t\t// Create a list of things that can be consumed, consisting of nodes and their attributes.\n\t\tthis.conversionApi.consumable = this._createInsertConsumable( range );\n\n\t\t// Fire a separate insert event for each node and text fragment contained in the range.\n\t\tfor ( const value of range ) {\n\t\t\tconst item = value.item;\n\t\t\tconst itemRange = Range._createFromPositionAndShift( value.previousPosition, value.length );\n\t\t\tconst data = {\n\t\t\t\titem,\n\t\t\t\trange: itemRange\n\t\t\t};\n\n\t\t\tthis._testAndFire( 'insert', data );\n\n\t\t\t// Fire a separate addAttribute event for each attribute that was set on inserted items.\n\t\t\t// This is important because most attributes converters will listen only to add/change/removeAttribute events.\n\t\t\t// If we would not add this part, attributes on inserted nodes would not be converted.\n\t\t\tfor ( const key of item.getAttributeKeys() ) {\n\t\t\t\tdata.attributeKey = key;\n\t\t\t\tdata.attributeOldValue = null;\n\t\t\t\tdata.attributeNewValue = item.getAttribute( key );\n\n\t\t\t\tthis._testAndFire( `attribute:${ key }`, data );\n\t\t\t}\n\t\t}\n\n\t\tthis._clearConversionApi();\n\t}\n\n\t/**\n\t * Fires conversion of a single node removal. Fires {@link #event:remove remove event} with provided data.\n\t *\n\t * @param {module:engine/model/position~Position} position Position from which node was removed.\n\t * @param {Number} length Offset size of removed node.\n\t * @param {String} name Name of removed node.\n\t * @param {module:engine/view/downcastwriter~DowncastWriter} writer View writer that should be used to modify view document.\n\t */\n\tconvertRemove( position, length, name, writer ) {\n\t\tthis.conversionApi.writer = writer;\n\n\t\tthis.fire( 'remove:' + name, { position, length }, this.conversionApi );\n\n\t\tthis._clearConversionApi();\n\t}\n\n\t/**\n\t * Starts conversion of attribute change on given `range`.\n\t *\n\t * For each node in the given `range`, {@link #event:attribute attribute event} is fired with the passed data.\n\t *\n\t * @fires attribute\n\t * @param {module:engine/model/range~Range} range Changed range.\n\t * @param {String} key Key of the attribute that has changed.\n\t * @param {*} oldValue Attribute value before the change or `null` if the attribute has not been set before.\n\t * @param {*} newValue New attribute value or `null` if the attribute has been removed.\n\t * @param {module:engine/view/downcastwriter~DowncastWriter} writer View writer that should be used to modify view document.\n\t */\n\tconvertAttribute( range, key, oldValue, newValue, writer ) {\n\t\tthis.conversionApi.writer = writer;\n\n\t\t// Create a list with attributes to consume.\n\t\tthis.conversionApi.consumable = this._createConsumableForRange( range, `attribute:${ key }` );\n\n\t\t// Create a separate attribute event for each node in the range.\n\t\tfor ( const value of range ) {\n\t\t\tconst item = value.item;\n\t\t\tconst itemRange = Range._createFromPositionAndShift( value.previousPosition, value.length );\n\t\t\tconst data = {\n\t\t\t\titem,\n\t\t\t\trange: itemRange,\n\t\t\t\tattributeKey: key,\n\t\t\t\tattributeOldValue: oldValue,\n\t\t\t\tattributeNewValue: newValue\n\t\t\t};\n\n\t\t\tthis._testAndFire( `attribute:${ key }`, data );\n\t\t}\n\n\t\tthis._clearConversionApi();\n\t}\n\n\t/**\n\t * Starts model selection conversion.\n\t *\n\t * Fires events for given {@link module:engine/model/selection~Selection selection} to start selection conversion.\n\t *\n\t * @fires selection\n\t * @fires addMarker\n\t * @fires attribute\n\t * @param {module:engine/model/selection~Selection} selection Selection to convert.\n\t * @param {Array.} markers Array of markers containing model markers.\n\t * @param {module:engine/view/downcastwriter~DowncastWriter} writer View writer that should be used to modify view document.\n\t */\n\tconvertSelection( selection, markers, writer ) {\n\t\tconst markersAtSelection = Array.from( markers.getMarkersAtPosition( selection.getFirstPosition() ) );\n\n\t\tthis.conversionApi.writer = writer;\n\t\tthis.conversionApi.consumable = this._createSelectionConsumable( selection, markersAtSelection );\n\n\t\tthis.fire( 'selection', { selection }, this.conversionApi );\n\n\t\tif ( !selection.isCollapsed ) {\n\t\t\treturn;\n\t\t}\n\n\t\tfor ( const marker of markersAtSelection ) {\n\t\t\tconst markerRange = marker.getRange();\n\n\t\t\tif ( !shouldMarkerChangeBeConverted( selection.getFirstPosition(), marker, this.conversionApi.mapper ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst data = {\n\t\t\t\titem: selection,\n\t\t\t\tmarkerName: marker.name,\n\t\t\t\tmarkerRange\n\t\t\t};\n\n\t\t\tif ( this.conversionApi.consumable.test( selection, 'addMarker:' + marker.name ) ) {\n\t\t\t\tthis.fire( 'addMarker:' + marker.name, data, this.conversionApi );\n\t\t\t}\n\t\t}\n\n\t\tfor ( const key of selection.getAttributeKeys() ) {\n\t\t\tconst data = {\n\t\t\t\titem: selection,\n\t\t\t\trange: selection.getFirstRange(),\n\t\t\t\tattributeKey: key,\n\t\t\t\tattributeOldValue: null,\n\t\t\t\tattributeNewValue: selection.getAttribute( key )\n\t\t\t};\n\n\t\t\t// Do not fire event if the attribute has been consumed.\n\t\t\tif ( this.conversionApi.consumable.test( selection, 'attribute:' + data.attributeKey ) ) {\n\t\t\t\tthis.fire( 'attribute:' + data.attributeKey + ':$text', data, this.conversionApi );\n\t\t\t}\n\t\t}\n\n\t\tthis._clearConversionApi();\n\t}\n\n\t/**\n\t * Converts added marker. Fires {@link #event:addMarker addMarker} event for each item\n\t * in marker's range. If range is collapsed single event is dispatched. See event description for more details.\n\t *\n\t * @fires addMarker\n\t * @param {String} markerName Marker name.\n\t * @param {module:engine/model/range~Range} markerRange Marker range.\n\t * @param {module:engine/view/downcastwriter~DowncastWriter} writer View writer that should be used to modify view document.\n\t */\n\tconvertMarkerAdd( markerName, markerRange, writer ) {\n\t\t// Do not convert if range is in graveyard or not in the document (e.g. in DocumentFragment).\n\t\tif ( !markerRange.root.document || markerRange.root.rootName == '$graveyard' ) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.conversionApi.writer = writer;\n\n\t\t// In markers' case, event name == consumable name.\n\t\tconst eventName = 'addMarker:' + markerName;\n\n\t\t//\n\t\t// First, fire an event for the whole marker.\n\t\t//\n\t\tconst consumable = new Consumable();\n\t\tconsumable.add( markerRange, eventName );\n\n\t\tthis.conversionApi.consumable = consumable;\n\n\t\tthis.fire( eventName, { markerName, markerRange }, this.conversionApi );\n\n\t\t//\n\t\t// Do not fire events for each item inside the range if the range got consumed.\n\t\t//\n\t\tif ( !consumable.test( markerRange, eventName ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t//\n\t\t// Then, fire an event for each item inside the marker range.\n\t\t//\n\t\tthis.conversionApi.consumable = this._createConsumableForRange( markerRange, eventName );\n\n\t\tfor ( const item of markerRange.getItems() ) {\n\t\t\t// Do not fire event for already consumed items.\n\t\t\tif ( !this.conversionApi.consumable.test( item, eventName ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst data = { item, range: Range._createOn( item ), markerName, markerRange };\n\n\t\t\tthis.fire( eventName, data, this.conversionApi );\n\t\t}\n\n\t\tthis._clearConversionApi();\n\t}\n\n\t/**\n\t * Fires conversion of marker removal. Fires {@link #event:removeMarker removeMarker} event with provided data.\n\t *\n\t * @fires removeMarker\n\t * @param {String} markerName Marker name.\n\t * @param {module:engine/model/range~Range} markerRange Marker range.\n\t * @param {module:engine/view/downcastwriter~DowncastWriter} writer View writer that should be used to modify view document.\n\t */\n\tconvertMarkerRemove( markerName, markerRange, writer ) {\n\t\t// Do not convert if range is in graveyard or not in the document (e.g. in DocumentFragment).\n\t\tif ( !markerRange.root.document || markerRange.root.rootName == '$graveyard' ) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.conversionApi.writer = writer;\n\n\t\tthis.fire( 'removeMarker:' + markerName, { markerName, markerRange }, this.conversionApi );\n\n\t\tthis._clearConversionApi();\n\t}\n\n\t/**\n\t * Creates {@link module:engine/conversion/modelconsumable~ModelConsumable} with values to consume from given range,\n\t * assuming that the range has just been inserted to the model.\n\t *\n\t * @private\n\t * @param {module:engine/model/range~Range} range Inserted range.\n\t * @returns {module:engine/conversion/modelconsumable~ModelConsumable} Values to consume.\n\t */\n\t_createInsertConsumable( range ) {\n\t\tconst consumable = new Consumable();\n\n\t\tfor ( const value of range ) {\n\t\t\tconst item = value.item;\n\n\t\t\tconsumable.add( item, 'insert' );\n\n\t\t\tfor ( const key of item.getAttributeKeys() ) {\n\t\t\t\tconsumable.add( item, 'attribute:' + key );\n\t\t\t}\n\t\t}\n\n\t\treturn consumable;\n\t}\n\n\t/**\n\t * Creates {@link module:engine/conversion/modelconsumable~ModelConsumable} with values to consume for given range.\n\t *\n\t * @private\n\t * @param {module:engine/model/range~Range} range Affected range.\n\t * @param {String} type Consumable type.\n\t * @returns {module:engine/conversion/modelconsumable~ModelConsumable} Values to consume.\n\t */\n\t_createConsumableForRange( range, type ) {\n\t\tconst consumable = new Consumable();\n\n\t\tfor ( const item of range.getItems() ) {\n\t\t\tconsumable.add( item, type );\n\t\t}\n\n\t\treturn consumable;\n\t}\n\n\t/**\n\t * Creates {@link module:engine/conversion/modelconsumable~ModelConsumable} with selection consumable values.\n\t *\n\t * @private\n\t * @param {module:engine/model/selection~Selection} selection Selection to create consumable from.\n\t * @param {Iterable.} markers Markers which contains selection.\n\t * @returns {module:engine/conversion/modelconsumable~ModelConsumable} Values to consume.\n\t */\n\t_createSelectionConsumable( selection, markers ) {\n\t\tconst consumable = new Consumable();\n\n\t\tconsumable.add( selection, 'selection' );\n\n\t\tfor ( const marker of markers ) {\n\t\t\tconsumable.add( selection, 'addMarker:' + marker.name );\n\t\t}\n\n\t\tfor ( const key of selection.getAttributeKeys() ) {\n\t\t\tconsumable.add( selection, 'attribute:' + key );\n\t\t}\n\n\t\treturn consumable;\n\t}\n\n\t/**\n\t * Tests passed `consumable` to check whether given event can be fired and if so, fires it.\n\t *\n\t * @private\n\t * @fires insert\n\t * @fires attribute\n\t * @param {String} type Event type.\n\t * @param {Object} data Event data.\n\t */\n\t_testAndFire( type, data ) {\n\t\tif ( !this.conversionApi.consumable.test( data.item, type ) ) {\n\t\t\t// Do not fire event if the item was consumed.\n\t\t\treturn;\n\t\t}\n\n\t\tconst name = data.item.name || '$text';\n\n\t\tthis.fire( type + ':' + name, data, this.conversionApi );\n\t}\n\n\t/**\n\t * Clears conversion API object.\n\t *\n\t * @private\n\t */\n\t_clearConversionApi() {\n\t\tdelete this.conversionApi.writer;\n\t\tdelete this.conversionApi.consumable;\n\t}\n\n\t/**\n\t * Fired for inserted nodes.\n\t *\n\t * `insert` is a namespace for a class of events. Names of actually called events follow this pattern:\n\t * `insert:name`. `name` is either `'$text'`, when {@link module:engine/model/text~Text a text node} has been inserted,\n\t * or {@link module:engine/model/element~Element#name name} of inserted element.\n\t *\n\t * This way listeners can either listen to a general `insert` event or specific event (for example `insert:paragraph`).\n\t *\n\t * @event insert\n\t * @param {Object} data Additional information about the change.\n\t * @param {module:engine/model/item~Item} data.item Inserted item.\n\t * @param {module:engine/model/range~Range} data.range Range spanning over inserted item.\n\t * @param {Object} conversionApi Conversion interface to be used by callback, passed in `DowncastDispatcher` constructor.\n\t */\n\n\t/**\n\t * Fired for removed nodes.\n\t *\n\t * `remove` is a namespace for a class of events. Names of actually called events follow this pattern:\n\t * `remove:name`. `name` is either `'$text'`, when {@link module:engine/model/text~Text a text node} has been removed,\n\t * or the {@link module:engine/model/element~Element#name name} of removed element.\n\t *\n\t * This way listeners can either listen to a general `remove` event or specific event (for example `remove:paragraph`).\n\t *\n\t * @event remove\n\t * @param {Object} data Additional information about the change.\n\t * @param {module:engine/model/position~Position} data.position Position from which the node has been removed.\n\t * @param {Number} data.length Offset size of the removed node.\n\t * @param {Object} conversionApi Conversion interface to be used by callback, passed in `DowncastDispatcher` constructor.\n\t */\n\n\t/**\n\t * Fired in the following cases:\n\t *\n\t * * when an attribute has been added, changed, or removed from a node,\n\t * * when a node with an attribute is inserted,\n\t * * when collapsed model selection attribute is converted.\n\t *\n\t * `attribute` is a namespace for a class of events. Names of actually called events follow this pattern:\n\t * `attribute:attributeKey:name`. `attributeKey` is the key of added/changed/removed attribute.\n\t * `name` is either `'$text'` if change was on {@link module:engine/model/text~Text a text node},\n\t * or the {@link module:engine/model/element~Element#name name} of element which attribute has changed.\n\t *\n\t * This way listeners can either listen to a general `attribute:bold` event or specific event (for example `attribute:src:image`).\n\t *\n\t * @event attribute\n\t * @param {Object} data Additional information about the change.\n\t * @param {module:engine/model/item~Item|module:engine/model/documentselection~DocumentSelection} data.item Changed item\n\t * or converted selection.\n\t * @param {module:engine/model/range~Range} data.range Range spanning over changed item or selection range.\n\t * @param {String} data.attributeKey Attribute key.\n\t * @param {*} data.attributeOldValue Attribute value before the change. This is `null` when selection attribute is converted.\n\t * @param {*} data.attributeNewValue New attribute value.\n\t * @param {module:engine/conversion/modelconsumable~ModelConsumable} consumable Values to consume.\n\t * @param {Object} conversionApi Conversion interface to be used by callback, passed in `DowncastDispatcher` constructor.\n\t */\n\n\t/**\n\t * Fired for {@link module:engine/model/selection~Selection selection} changes.\n\t *\n\t * @event selection\n\t * @param {module:engine/model/selection~Selection} selection Selection that is converted.\n\t * @param {module:engine/conversion/modelconsumable~ModelConsumable} consumable Values to consume.\n\t * @param {Object} conversionApi Conversion interface to be used by callback, passed in `DowncastDispatcher` constructor.\n\t */\n\n\t/**\n\t * Fired when a new marker is added to the model. Also fired when collapsed model selection that is inside marker is converted.\n\t *\n\t * `addMarker` is a namespace for a class of events. Names of actually called events follow this pattern:\n\t * `addMarker:markerName`. By specifying certain marker names, you can make the events even more gradual. For example,\n\t * if markers are named `foo:abc`, `foo:bar`, then it is possible to listen to `addMarker:foo` or `addMarker:foo:abc` and\n\t * `addMarker:foo:bar` events.\n\t *\n\t * If the marker range is not collapsed:\n\t *\n\t * * the event is fired for each item in the marker range one by one,\n\t * * consumables object includes each item of the marker range and the consumable value is same as event name.\n\t *\n\t * If the marker range is collapsed:\n\t *\n\t * * there is only one event,\n\t * * consumables object includes marker range with event name.\n\t *\n\t * If selection inside a marker is converted:\n\t *\n\t * * there is only one event,\n\t * * consumables object includes selection instance with event name.\n\t *\n\t * @event addMarker\n\t * @param {Object} data Additional information about the change.\n\t * @param {module:engine/model/item~Item|module:engine/model/selection~Selection} data.item Item inside the new marker or\n\t * the selection that is being converted.\n\t * @param {module:engine/model/range~Range} [data.range] Range spanning over converted item. Available only in marker conversion, if\n\t * the marker range was not collapsed.\n\t * @param {module:engine/model/range~Range} data.markerRange Marker range.\n\t * @param {String} data.markerName Marker name.\n\t * @param {module:engine/conversion/modelconsumable~ModelConsumable} consumable Values to consume.\n\t * @param {Object} conversionApi Conversion interface to be used by callback, passed in `DowncastDispatcher` constructor.\n\t */\n\n\t/**\n\t * Fired when marker is removed from the model.\n\t *\n\t * `removeMarker` is a namespace for a class of events. Names of actually called events follow this pattern:\n\t * `removeMarker:markerName`. By specifying certain marker names, you can make the events even more gradual. For example,\n\t * if markers are named `foo:abc`, `foo:bar`, then it is possible to listen to `removeMarker:foo` or `removeMarker:foo:abc` and\n\t * `removeMarker:foo:bar` events.\n\t *\n\t * @event removeMarker\n\t * @param {Object} data Additional information about the change.\n\t * @param {module:engine/model/range~Range} data.markerRange Marker range.\n\t * @param {String} data.markerName Marker name.\n\t * @param {Object} conversionApi Conversion interface to be used by callback, passed in `DowncastDispatcher` constructor.\n\t */\n}\n\nmix( DowncastDispatcher, EmitterMixin );\n\n// Helper function, checks whether change of `marker` at `modelPosition` should be converted. Marker changes are not\n// converted if they happen inside an element with custom conversion method.\n//\n// @param {module:engine/model/position~Position} modelPosition\n// @param {module:engine/model/markercollection~Marker} marker\n// @param {module:engine/conversion/mapper~Mapper} mapper\n// @returns {Boolean}\nfunction shouldMarkerChangeBeConverted( modelPosition, marker, mapper ) {\n\tconst range = marker.getRange();\n\tconst ancestors = Array.from( modelPosition.getAncestors() );\n\tancestors.shift(); // Remove root element. It cannot be passed to `model.Range#containsItem`.\n\tancestors.reverse();\n\n\tconst hasCustomHandling = ancestors.some( element => {\n\t\tif ( range.containsItem( element ) ) {\n\t\t\tconst viewElement = mapper.toViewElement( element );\n\n\t\t\treturn !!viewElement.getCustomProperty( 'addHighlight' );\n\t\t}\n\t} );\n\n\treturn !hasCustomHandling;\n}\n","/**\n * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\n/**\n * @module engine/model/selection\n */\n\nimport Position from './position';\nimport Element from './element';\nimport Node from './node';\nimport Range from './range';\nimport EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';\nimport CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';\nimport mix from '@ckeditor/ckeditor5-utils/src/mix';\nimport isIterable from '@ckeditor/ckeditor5-utils/src/isiterable';\n\n/**\n * Selection is a set of {@link module:engine/model/range~Range ranges}. It has a direction specified by its\n * {@link module:engine/model/selection~Selection#anchor anchor} and {@link module:engine/model/selection~Selection#focus focus}\n * (it can be {@link module:engine/model/selection~Selection#isBackward forward or backward}).\n * Additionally, selection may have its own attributes (think – whether text typed in in this selection\n * should have those attributes – e.g. whether you type a bolded text).\n *\n * @mixes module:utils/emittermixin~EmitterMixin\n */\nexport default class Selection {\n\t/**\n\t * Creates a new selection instance\n\t * based on the given {@link module:engine/model/selection~Selection selection},\n\t * or based on the given {@link module:engine/model/range~Range range},\n\t * or based on an iterable collection of {@link module:engine/model/range~Range ranges}\n\t * or at the given {@link module:engine/model/position~Position position},\n\t * or on the given {@link module:engine/model/element~Element element},\n\t * or creates an empty selection if no arguments were passed.\n\t *\n\t *\t\t// Creates empty selection without ranges.\n\t *\t\tconst selection = writer.createSelection();\n\t *\n\t *\t\t// Creates selection at the given range.\n\t *\t\tconst range = writer.createRange( start, end );\n\t *\t\tconst selection = writer.createSelection( range );\n\t *\n\t *\t\t// Creates selection at the given ranges\n\t *\t\tconst ranges = [ writer.createRange( start1, end2 ), writer.createRange( star2, end2 ) ];\n\t *\t\tconst selection = writer.createSelection( ranges );\n\t *\n\t *\t\t// Creates selection from the other selection.\n\t *\t\t// Note: It doesn't copies selection attributes.\n\t *\t\tconst otherSelection = writer.createSelection();\n\t *\t\tconst selection = writer.createSelection( otherSelection );\n\t *\n\t *\t\t// Creates selection from the given document selection.\n\t *\t\t// Note: It doesn't copies selection attributes.\n\t *\t\tconst documentSelection = model.document.selection;\n\t *\t\tconst selection = writer.createSelection( documentSelection );\n\t *\n\t *\t\t// Creates selection at the given position.\n\t *\t\tconst position = writer.createPositionFromPath( root, path );\n\t *\t\tconst selection = writer.createSelection( position );\n\t *\n\t *\t\t// Creates selection at the given offset in the given element.\n\t *\t\tconst paragraph = writer.createElement( 'paragraph' );\n\t *\t\tconst selection = writer.createSelection( paragraph, offset );\n\t *\n\t *\t\t// Creates a range inside an {@link module:engine/model/element~Element element} which starts before the\n\t *\t\t// first child of that element and ends after the last child of that element.\n\t *\t\tconst selection = writer.createSelection( paragraph, 'in' );\n\t *\n\t *\t\t// Creates a range on an {@link module:engine/model/item~Item item} which starts before the item and ends\n\t *\t\t// just after the item.\n\t *\t\tconst selection = writer.createSelection( paragraph, 'on' );\n\t *\n\t * Selection's constructor allow passing additional options (`'backward'`) as the last argument.\n\t *\n\t *\t\t// Creates backward selection.\n\t *\t\tconst selection = writer.createSelection( range, { backward: true } );\n\t *\n\t * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection|\n\t * module:engine/model/position~Position|module:engine/model/element~Element|\n\t * Iterable.|module:engine/model/range~Range|null} selectable\n\t * @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Sets place or offset of the selection.\n\t * @param {Object} [options]\n\t * @param {Boolean} [options.backward] Sets this selection instance to be backward.\n\t */\n\tconstructor( selectable, placeOrOffset, options ) {\n\t\t/**\n\t\t * Specifies whether the last added range was added as a backward or forward range.\n\t\t *\n\t\t * @private\n\t\t * @type {Boolean}\n\t\t */\n\t\tthis._lastRangeBackward = false;\n\n\t\t/**\n\t\t * Stores selection ranges.\n\t\t *\n\t\t * @protected\n\t\t * @type {Array.}\n\t\t */\n\t\tthis._ranges = [];\n\n\t\t/**\n\t\t * List of attributes set on current selection.\n\t\t *\n\t\t * @protected\n\t\t * @type {Map.}\n\t\t */\n\t\tthis._attrs = new Map();\n\n\t\tif ( selectable ) {\n\t\t\tthis.setTo( selectable, placeOrOffset, options );\n\t\t}\n\t}\n\n\t/**\n\t * Selection anchor. Anchor is the position from which the selection was started. If a user is making a selection\n\t * by dragging the mouse, the anchor is where the user pressed the mouse button (the beggining of the selection).\n\t *\n\t * Anchor and {@link #focus} define the direction of the selection, which is important\n\t * when expanding/shrinking selection. The focus moves, while the anchor should remain in the same place.\n\t *\n\t * Anchor is always set to the {@link module:engine/model/range~Range#start start} or\n\t * {@link module:engine/model/range~Range#end end} position of the last of selection's ranges. Whether it is\n\t * the `start` or `end` depends on the specified `options.backward`. See the {@link #setTo `setTo()`} method.\n\t *\n\t * May be set to `null` if there are no ranges in the selection.\n\t *\n\t * @see #focus\n\t * @readonly\n\t * @type {module:engine/model/position~Position|null}\n\t */\n\tget anchor() {\n\t\tif ( this._ranges.length > 0 ) {\n\t\t\tconst range = this._ranges[ this._ranges.length - 1 ];\n\n\t\t\treturn this._lastRangeBackward ? range.end : range.start;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Selection focus. Focus is the position where the selection ends. If a user is making a selection\n\t * by dragging the mouse, the focus is where the mouse cursor is.\n\t *\n\t * May be set to `null` if there are no ranges in the selection.\n\t *\n\t * @see #anchor\n\t * @readonly\n\t * @type {module:engine/model/position~Position|null}\n\t */\n\tget focus() {\n\t\tif ( this._ranges.length > 0 ) {\n\t\t\tconst range = this._ranges[ this._ranges.length - 1 ];\n\n\t\t\treturn this._lastRangeBackward ? range.start : range.end;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Whether the selection is collapsed. Selection is collapsed when there is exactly one range in it\n\t * and it is collapsed.\n\t *\n\t * @readonly\n\t * @type {Boolean}\n\t */\n\tget isCollapsed() {\n\t\tconst length = this._ranges.length;\n\n\t\tif ( length === 1 ) {\n\t\t\treturn this._ranges[ 0 ].isCollapsed;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of ranges in the selection.\n\t *\n\t * @readonly\n\t * @type {Number}\n\t */\n\tget rangeCount() {\n\t\treturn this._ranges.length;\n\t}\n\n\t/**\n\t * Specifies whether the selection's {@link #focus} precedes the selection's {@link #anchor}.\n\t *\n\t * @readonly\n\t * @type {Boolean}\n\t */\n\tget isBackward() {\n\t\treturn !this.isCollapsed && this._lastRangeBackward;\n\t}\n\n\t/**\n\t * Checks whether this selection is equal to the given selection. Selections are equal if they have the same directions,\n\t * the same number of ranges and all ranges from one selection equal to ranges from the another selection.\n\t *\n\t * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} otherSelection\n\t * Selection to compare with.\n\t * @returns {Boolean} `true` if selections are equal, `false` otherwise.\n\t */\n\tisEqual( otherSelection ) {\n\t\tif ( this.rangeCount != otherSelection.rangeCount ) {\n\t\t\treturn false;\n\t\t} else if ( this.rangeCount === 0 ) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif ( !this.anchor.isEqual( otherSelection.anchor ) || !this.focus.isEqual( otherSelection.focus ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfor ( const thisRange of this._ranges ) {\n\t\t\tlet found = false;\n\n\t\t\tfor ( const otherRange of otherSelection._ranges ) {\n\t\t\t\tif ( thisRange.isEqual( otherRange ) ) {\n\t\t\t\t\tfound = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( !found ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Returns an iterable object that iterates over copies of selection ranges.\n\t *\n\t * @returns {Iterable.}\n\t */\n\t* getRanges() {\n\t\tfor ( const range of this._ranges ) {\n\t\t\tyield new Range( range.start, range.end );\n\t\t}\n\t}\n\n\t/**\n\t * Returns a copy of the first range in the selection.\n\t * First range is the one which {@link module:engine/model/range~Range#start start} position\n\t * {@link module:engine/model/position~Position#isBefore is before} start position of all other ranges\n\t * (not to confuse with the first range added to the selection).\n\t *\n\t * Returns `null` if there are no ranges in selection.\n\t *\n\t * @returns {module:engine/model/range~Range|null}\n\t */\n\tgetFirstRange() {\n\t\tlet first = null;\n\n\t\tfor ( const range of this._ranges ) {\n\t\t\tif ( !first || range.start.isBefore( first.start ) ) {\n\t\t\t\tfirst = range;\n\t\t\t}\n\t\t}\n\n\t\treturn first ? new Range( first.start, first.end ) : null;\n\t}\n\n\t/**\n\t * Returns a copy of the last range in the selection.\n\t * Last range is the one which {@link module:engine/model/range~Range#end end} position\n\t * {@link module:engine/model/position~Position#isAfter is after} end position of all other ranges (not to confuse with the range most\n\t * recently added to the selection).\n\t *\n\t * Returns `null` if there are no ranges in selection.\n\t *\n\t * @returns {module:engine/model/range~Range|null}\n\t */\n\tgetLastRange() {\n\t\tlet last = null;\n\n\t\tfor ( const range of this._ranges ) {\n\t\t\tif ( !last || range.end.isAfter( last.end ) ) {\n\t\t\t\tlast = range;\n\t\t\t}\n\t\t}\n\n\t\treturn last ? new Range( last.start, last.end ) : null;\n\t}\n\n\t/**\n\t * Returns the first position in the selection.\n\t * First position is the position that {@link module:engine/model/position~Position#isBefore is before}\n\t * any other position in the selection.\n\t *\n\t * Returns `null` if there are no ranges in selection.\n\t *\n\t * @returns {module:engine/model/position~Position|null}\n\t */\n\tgetFirstPosition() {\n\t\tconst first = this.getFirstRange();\n\n\t\treturn first ? first.start.clone() : null;\n\t}\n\n\t/**\n\t * Returns the last position in the selection.\n\t * Last position is the position that {@link module:engine/model/position~Position#isAfter is after}\n\t * any other position in the selection.\n\t *\n\t * Returns `null` if there are no ranges in selection.\n\t *\n\t * @returns {module:engine/model/position~Position|null}\n\t */\n\tgetLastPosition() {\n\t\tconst lastRange = this.getLastRange();\n\n\t\treturn lastRange ? lastRange.end.clone() : null;\n\t}\n\n\t/**\n\t * Sets this selection's ranges and direction to the specified location based on the given\n\t * {@link module:engine/model/selection~Selection selection}, {@link module:engine/model/position~Position position},\n\t * {@link module:engine/model/element~Element element}, {@link module:engine/model/position~Position position},\n\t * {@link module:engine/model/range~Range range}, an iterable of {@link module:engine/model/range~Range ranges} or null.\n\t *\n\t *\t\t// Removes all selection's ranges.\n\t *\t\tselection.setTo( null );\n\t *\n\t *\t\t// Sets selection to the given range.\n\t *\t\tconst range = writer.createRange( start, end );\n\t *\t\tselection.setTo( range );\n\t *\n\t *\t\t// Sets selection to given ranges.\n\t *\t\tconst ranges = [ writer.createRange( start1, end2 ), writer.createRange( star2, end2 ) ];\n\t *\t\tselection.setTo( ranges );\n\t *\n\t *\t\t// Sets selection to other selection.\n\t *\t\t// Note: It doesn't copies selection attributes.\n\t *\t\tconst otherSelection = writer.createSelection();\n\t *\t\tselection.setTo( otherSelection );\n\t *\n\t *\t\t// Sets selection to the given document selection.\n\t *\t\t// Note: It doesn't copies selection attributes.\n\t *\t\tconst documentSelection = new DocumentSelection( doc );\n\t *\t\tselection.setTo( documentSelection );\n\t *\n\t *\t\t// Sets collapsed selection at the given position.\n\t *\t\tconst position = writer.createPositionFromPath( root, path );\n\t *\t\tselection.setTo( position );\n\t *\n\t *\t\t// Sets collapsed selection at the position of the given node and an offset.\n\t *\t\tselection.setTo( paragraph, offset );\n\t *\n\t * Creates a range inside an {@link module:engine/model/element~Element element} which starts before the first child of\n \t * that element and ends after the last child of that element.\n\t *\n\t *\t\tselection.setTo( paragraph, 'in' );\n\t *\n\t * Creates a range on an {@link module:engine/model/item~Item item} which starts before the item and ends just after the item.\n\t *\n\t *\t\tselection.setTo( paragraph, 'on' );\n\t *\n\t * `Selection#setTo()`' method allow passing additional options (`backward`) as the last argument.\n\t *\n\t *\t\t// Sets backward selection.\n\t *\t\tconst selection = writer.createSelection( range, { backward: true } );\n\t *\n\t * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection|\n\t * module:engine/model/position~Position|module:engine/model/node~Node|\n\t * Iterable.|module:engine/model/range~Range|null} selectable\n\t * @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Sets place or offset of the selection.\n\t * @param {Object} [options]\n\t * @param {Boolean} [options.backward] Sets this selection instance to be backward.\n\t */\n\tsetTo( selectable, placeOrOffset, options ) {\n\t\tif ( selectable === null ) {\n\t\t\tthis._setRanges( [] );\n\t\t} else if ( selectable instanceof Selection ) {\n\t\t\tthis._setRanges( selectable.getRanges(), selectable.isBackward );\n\t\t} else if ( selectable && typeof selectable.getRanges == 'function' ) {\n\t\t\t// We assume that the selectable is a DocumentSelection.\n\t\t\t// It can't be imported here, because it would lead to circular imports.\n\t\t\tthis._setRanges( selectable.getRanges(), selectable.isBackward );\n\t\t} else if ( selectable instanceof Range ) {\n\t\t\tthis._setRanges( [ selectable ], !!placeOrOffset && !!placeOrOffset.backward );\n\t\t} else if ( selectable instanceof Position ) {\n\t\t\tthis._setRanges( [ new Range( selectable ) ] );\n\t\t} else if ( selectable instanceof Node ) {\n\t\t\tconst backward = !!options && !!options.backward;\n\t\t\tlet range;\n\n\t\t\tif ( placeOrOffset == 'in' ) {\n\t\t\t\trange = Range._createIn( selectable );\n\t\t\t} else if ( placeOrOffset == 'on' ) {\n\t\t\t\trange = Range._createOn( selectable );\n\t\t\t} else if ( placeOrOffset !== undefined ) {\n\t\t\t\trange = new Range( Position._createAt( selectable, placeOrOffset ) );\n\t\t\t} else {\n\t\t\t\t/**\n\t\t\t\t * selection.setTo requires the second parameter when the first parameter is a node.\n\t\t\t\t *\n\t\t\t\t * @error model-selection-setTo-required-second-parameter\n\t\t\t\t */\n\t\t\t\tthrow new CKEditorError(\n\t\t\t\t\t'model-selection-setTo-required-second-parameter: ' +\n\t\t\t\t\t'selection.setTo requires the second parameter when the first parameter is a node.' );\n\t\t\t}\n\n\t\t\tthis._setRanges( [ range ], backward );\n\t\t} else if ( isIterable( selectable ) ) {\n\t\t\t// We assume that the selectable is an iterable of ranges.\n\t\t\tthis._setRanges( selectable, placeOrOffset && !!placeOrOffset.backward );\n\t\t} else {\n\t\t\t/**\n\t\t\t * Cannot set selection to given place.\n\t\t\t *\n\t\t\t * @error model-selection-setTo-not-selectable\n\t\t\t */\n\t\t\tthrow new CKEditorError( 'model-selection-setTo-not-selectable: Cannot set selection to given place.' );\n\t\t}\n\t}\n\n\t/**\n\t * Replaces all ranges that were added to the selection with given array of ranges. Last range of the array\n\t * is treated like the last added range and is used to set {@link module:engine/model/selection~Selection#anchor} and\n\t * {@link module:engine/model/selection~Selection#focus}. Accepts a flag describing in which direction the selection is made.\n\t *\n\t * @protected\n\t * @fires change:range\n\t * @param {Iterable.} newRanges Ranges to set.\n\t * @param {Boolean} [isLastBackward=false] Flag describing if last added range was selected forward - from start to end (`false`)\n\t * or backward - from end to start (`true`).\n\t */\n\t_setRanges( newRanges, isLastBackward = false ) {\n\t\tnewRanges = Array.from( newRanges );\n\n\t\t// Check whether there is any range in new ranges set that is different than all already added ranges.\n\t\tconst anyNewRange = newRanges.some( newRange => {\n\t\t\tif ( !( newRange instanceof Range ) ) {\n\t\t\t\t/**\n\t\t\t\t * Selection range set to an object that is not an instance of {@link module:engine/model/range~Range}.\n\t\t\t\t *\n\t\t\t\t * Only {@link module:engine/model/range~Range} instances can be used to set a selection.\n\t\t\t\t * Common mistakes leading to this error are:\n\t\t\t\t *\n\t\t\t\t * * using DOM `Range` object,\n\t\t\t\t * * incorrect CKEditor 5 installation with multiple `ckeditor5-engine` packages having different versions.\n\t\t\t\t *\n\t\t\t\t * @error model-selection-set-ranges-not-range\n\t\t\t\t */\n\t\t\t\tthrow new CKEditorError(\n\t\t\t\t\t'model-selection-set-ranges-not-range: ' +\n\t\t\t\t\t'Selection range set to an object that is not an instance of model.Range.'\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn this._ranges.every( oldRange => {\n\t\t\t\treturn !oldRange.isEqual( newRange );\n\t\t\t} );\n\t\t} );\n\n\t\t// Don't do anything if nothing changed.\n\t\tif ( newRanges.length === this._ranges.length && !anyNewRange ) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._removeAllRanges();\n\n\t\tfor ( const range of newRanges ) {\n\t\t\tthis._pushRange( range );\n\t\t}\n\n\t\tthis._lastRangeBackward = !!isLastBackward;\n\n\t\tthis.fire( 'change:range', { directChange: true } );\n\t}\n\n\t/**\n\t * Moves {@link module:engine/model/selection~Selection#focus} to the specified location.\n\t *\n\t * The location can be specified in the same form as\n\t * {@link module:engine/model/writer~Writer#createPositionAt writer.createPositionAt()} parameters.\n\t *\n\t * @fires change:range\n\t * @param {module:engine/model/item~Item|module:engine/model/position~Position} itemOrPosition\n\t * @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when\n\t * first parameter is a {@link module:engine/model/item~Item model item}.\n\t */\n\tsetFocus( itemOrPosition, offset ) {\n\t\tif ( this.anchor === null ) {\n\t\t\t/**\n\t\t\t * Cannot set selection focus if there are no ranges in selection.\n\t\t\t *\n\t\t\t * @error model-selection-setFocus-no-ranges\n\t\t\t */\n\t\t\tthrow new CKEditorError(\n\t\t\t\t'model-selection-setFocus-no-ranges: Cannot set selection focus if there are no ranges in selection.'\n\t\t\t);\n\t\t}\n\n\t\tconst newFocus = Position._createAt( itemOrPosition, offset );\n\n\t\tif ( newFocus.compareWith( this.focus ) == 'same' ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst anchor = this.anchor;\n\n\t\tif ( this._ranges.length ) {\n\t\t\tthis._popRange();\n\t\t}\n\n\t\tif ( newFocus.compareWith( anchor ) == 'before' ) {\n\t\t\tthis._pushRange( new Range( newFocus, anchor ) );\n\t\t\tthis._lastRangeBackward = true;\n\t\t} else {\n\t\t\tthis._pushRange( new Range( anchor, newFocus ) );\n\t\t\tthis._lastRangeBackward = false;\n\t\t}\n\n\t\tthis.fire( 'change:range', { directChange: true } );\n\t}\n\n\t/**\n\t * Gets an attribute value for given key or `undefined` if that attribute is not set on the selection.\n\t *\n\t * @param {String} key Key of attribute to look for.\n\t * @returns {*} Attribute value or `undefined`.\n\t */\n\tgetAttribute( key ) {\n\t\treturn this._attrs.get( key );\n\t}\n\n\t/**\n\t * Returns iterable that iterates over this selection's attributes.\n\t *\n\t * Attributes are returned as arrays containing two items. First one is attribute key and second is attribute value.\n\t * This format is accepted by native `Map` object and also can be passed in `Node` constructor.\n\t *\n\t * @returns {Iterable.<*>}\n\t */\n\tgetAttributes() {\n\t\treturn this._attrs.entries();\n\t}\n\n\t/**\n\t * Returns iterable that iterates over this selection's attribute keys.\n\t *\n\t * @returns {Iterable.}\n\t */\n\tgetAttributeKeys() {\n\t\treturn this._attrs.keys();\n\t}\n\n\t/**\n\t * Checks if the selection has an attribute for given key.\n\t *\n\t * @param {String} key Key of attribute to check.\n\t * @returns {Boolean} `true` if attribute with given key is set on selection, `false` otherwise.\n\t */\n\thasAttribute( key ) {\n\t\treturn this._attrs.has( key );\n\t}\n\n\t/**\n\t * Removes an attribute with given key from the selection.\n\t *\n\t * If given attribute was set on the selection, fires the {@link #event:change:range} event with\n\t * removed attribute key.\n\t *\n\t * @fires change:attribute\n\t * @param {String} key Key of attribute to remove.\n\t */\n\tremoveAttribute( key ) {\n\t\tif ( this.hasAttribute( key ) ) {\n\t\t\tthis._attrs.delete( key );\n\n\t\t\tthis.fire( 'change:attribute', { attributeKeys: [ key ], directChange: true } );\n\t\t}\n\t}\n\n\t/**\n\t * Sets attribute on the selection. If attribute with the same key already is set, it's value is overwritten.\n\t *\n\t * If the attribute value has changed, fires the {@link #event:change:range} event with\n\t * the attribute key.\n\t *\n\t * @fires change:attribute\n\t * @param {String} key Key of attribute to set.\n\t * @param {*} value Attribute value.\n\t */\n\tsetAttribute( key, value ) {\n\t\tif ( this.getAttribute( key ) !== value ) {\n\t\t\tthis._attrs.set( key, value );\n\n\t\t\tthis.fire( 'change:attribute', { attributeKeys: [ key ], directChange: true } );\n\t\t}\n\t}\n\n\t/**\n\t * Returns the selected element. {@link module:engine/model/element~Element Element} is considered as selected if there is only\n\t * one range in the selection, and that range contains exactly one element.\n\t * Returns `null` if there is no selected element.\n\t *\n\t * @returns {module:engine/model/element~Element|null}\n\t */\n\tgetSelectedElement() {\n\t\tif ( this.rangeCount !== 1 ) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst range = this.getFirstRange();\n\t\tconst nodeAfterStart = range.start.nodeAfter;\n\t\tconst nodeBeforeEnd = range.end.nodeBefore;\n\n\t\treturn ( nodeAfterStart instanceof Element && nodeAfterStart == nodeBeforeEnd ) ? nodeAfterStart : null;\n\t}\n\n\t/**\n\t * Gets elements of type \"block\" touched by the selection.\n\t *\n\t * This method's result can be used for example to apply block styling to all blocks covered by this selection.\n\t *\n\t * **Note:** `getSelectedBlocks()` always returns the deepest block.\n\t *\n\t * In this case the function will return exactly all 3 paragraphs:\n\t *\n\t *\t\t[a\n\t *\t\t\n\t *\t\t\tb\n\t *\t\t\n\t *\t\tc]d\n\t *\n\t * In this case the paragraph will also be returned, despite the collapsed selection:\n\t *\n\t *\t\t[]a\n\t *\n\t * **Special case**: If a selection ends at the beginning of a block, that block is not returned as from user perspective\n\t * this block wasn't selected. See [#984](https://github.com/ckeditor/ckeditor5-engine/issues/984) for more details.\n\t *\n\t *\t\t[a\n\t *\t\tb\n\t *\t\t]c // this block will not be returned\n\t *\n\t * @returns {Iterable.}\n\t */\n\t* getSelectedBlocks() {\n\t\tconst visited = new WeakSet();\n\n\t\tfor ( const range of this.getRanges() ) {\n\t\t\tconst startBlock = getParentBlock( range.start, visited );\n\n\t\t\tif ( startBlock ) {\n\t\t\t\tyield startBlock;\n\t\t\t}\n\n\t\t\tfor ( const value of range.getWalker() ) {\n\t\t\t\tif ( value.type == 'elementEnd' && isUnvisitedBlockContainer( value.item, visited ) ) {\n\t\t\t\t\tyield value.item;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst endBlock = getParentBlock( range.end, visited );\n\n\t\t\t// #984. Don't return the end block if the range ends right at its beginning.\n\t\t\tif ( endBlock && !range.end.isTouching( Position._createAt( endBlock, 0 ) ) ) {\n\t\t\t\tyield endBlock;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Checks whether the selection contains the entire content of the given element. This means that selection must start\n\t * at a position {@link module:engine/model/position~Position#isTouching touching} the element's start and ends at position\n\t * touching the element's end.\n\t *\n\t * By default, this method will check whether the entire content of the selection's current root is selected.\n\t * Useful to check if e.g. the user has just pressed Ctrl + A.\n\t *\n\t * @param {module:engine/model/element~Element} [element=this.anchor.root]\n\t * @returns {Boolean}\n\t */\n\tcontainsEntireContent( element = this.anchor.root ) {\n\t\tconst limitStartPosition = Position._createAt( element, 0 );\n\t\tconst limitEndPosition = Position._createAt( element, 'end' );\n\n\t\treturn limitStartPosition.isTouching( this.getFirstPosition() ) &&\n\t\t\tlimitEndPosition.isTouching( this.getLastPosition() );\n\t}\n\n\t/**\n\t * Adds given range to internal {@link #_ranges ranges array}. Throws an error\n\t * if given range is intersecting with any range that is already stored in this selection.\n\t *\n\t * @protected\n\t * @param {module:engine/model/range~Range} range Range to add.\n\t */\n\t_pushRange( range ) {\n\t\tthis._checkRange( range );\n\t\tthis._ranges.push( new Range( range.start, range.end ) );\n\t}\n\n\t/**\n\t * Checks if given range intersects with ranges that are already in the selection. Throws an error if it does.\n\t *\n\t * @protected\n\t * @param {module:engine/model/range~Range} range Range to check.\n\t */\n\t_checkRange( range ) {\n\t\tfor ( let i = 0; i < this._ranges.length; i++ ) {\n\t\t\tif ( range.isIntersecting( this._ranges[ i ] ) ) {\n\t\t\t\t/**\n\t\t\t\t * Trying to add a range that intersects with another range in the selection.\n\t\t\t\t *\n\t\t\t\t * @error model-selection-range-intersects\n\t\t\t\t * @param {module:engine/model/range~Range} addedRange Range that was added to the selection.\n\t\t\t\t * @param {module:engine/model/range~Range} intersectingRange Range in the selection that intersects with `addedRange`.\n\t\t\t\t */\n\t\t\t\tthrow new CKEditorError(\n\t\t\t\t\t'model-selection-range-intersects: Trying to add a range that intersects with another range in the selection.',\n\t\t\t\t\t{ addedRange: range, intersectingRange: this._ranges[ i ] }\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Deletes ranges from internal range array. Uses {@link #_popRange _popRange} to\n\t * ensure proper ranges removal.\n\t *\n\t * @protected\n\t */\n\t_removeAllRanges() {\n\t\twhile ( this._ranges.length > 0 ) {\n\t\t\tthis._popRange();\n\t\t}\n\t}\n\n\t/**\n\t * Removes most recently added range from the selection.\n\t *\n\t * @protected\n\t */\n\t_popRange() {\n\t\tthis._ranges.pop();\n\t}\n\n\t/**\n\t * Fired when selection range(s) changed.\n\t *\n\t * @event change:range\n\t * @param {Boolean} directChange In case of {@link module:engine/model/selection~Selection} class it is always set\n\t * to `true` which indicates that the selection change was caused by a direct use of selection's API.\n\t * The {@link module:engine/model/documentselection~DocumentSelection}, however, may change because its position\n\t * was directly changed through the {@link module:engine/model/writer~Writer writer} or because its position was\n\t * changed because the structure of the model has been changed (which means an indirect change).\n\t * The indirect change does not occur in case of normal (detached) selections because they are \"static\" (as \"not live\")\n\t * which mean that they are not updated once the document changes.\n\t */\n\n\t/**\n\t * Fired when selection attribute changed.\n\t *\n\t * @event change:attribute\n\t * @param {Boolean} directChange In case of {@link module:engine/model/selection~Selection} class it is always set\n\t * to `true` which indicates that the selection change was caused by a direct use of selection's API.\n\t * The {@link module:engine/model/documentselection~DocumentSelection}, however, may change because its attributes\n\t * were directly changed through the {@link module:engine/model/writer~Writer writer} or because its position was\n\t * changed in the model and its attributes were refreshed (which means an indirect change).\n\t * The indirect change does not occur in case of normal (detached) selections because they are \"static\" (as \"not live\")\n\t * which mean that they are not updated once the document changes.\n\t * @param {Array.} attributeKeys Array containing keys of attributes that changed.\n\t */\n}\n\nmix( Selection, EmitterMixin );\n\n// Checks whether the given element extends $block in the schema and has a parent (is not a root).\n// Marks it as already visited.\nfunction isUnvisitedBlockContainer( element, visited ) {\n\tif ( visited.has( element ) ) {\n\t\treturn false;\n\t}\n\n\tvisited.add( element );\n\n\treturn element.document.model.schema.isBlock( element ) && element.parent;\n}\n\n// Finds the lowest element in position's ancestors which is a block.\n// Marks all ancestors as already visited to not include any of them later on.\nfunction getParentBlock( position, visited ) {\n\tconst ancestors = position.parent.getAncestors( { parentFirst: true, includeSelf: true } );\n\tconst block = ancestors.find( element => isUnvisitedBlockContainer( element, visited ) );\n\n\t// Mark all ancestors of this position's parent, because find() might've stopped early and\n\t// the found block may be a child of another block.\n\tancestors.forEach( element => visited.add( element ) );\n\n\treturn block;\n}\n","/**\n * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\n/**\n * @module engine/model/liverange\n */\n\nimport Range from './range';\nimport EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';\nimport mix from '@ckeditor/ckeditor5-utils/src/mix';\n\n/**\n * `LiveRange` is a type of {@link module:engine/model/range~Range Range}\n * that updates itself as {@link module:engine/model/document~Document document}\n * is changed through operations. It may be used as a bookmark.\n *\n * **Note:** Be very careful when dealing with `LiveRange`. Each `LiveRange` instance bind events that might\n * have to be unbound. Use {@link module:engine/model/liverange~LiveRange#detach detach} whenever you don't need `LiveRange` anymore.\n */\nexport default class LiveRange extends Range {\n\t/**\n\t * Creates a live range.\n\t *\n\t * @see module:engine/model/range~Range\n\t */\n\tconstructor( start, end ) {\n\t\tsuper( start, end );\n\n\t\tbindWithDocument.call( this );\n\t}\n\n\t/**\n\t * Unbinds all events previously bound by `LiveRange`. Use it whenever you don't need `LiveRange` instance\n\t * anymore (i.e. when leaving scope in which it was declared or before re-assigning variable that was\n\t * referring to it).\n\t */\n\tdetach() {\n\t\tthis.stopListening();\n\t}\n\n\t/**\n\t * Creates a {@link module:engine/model/range~Range range instance} that is equal to this live range.\n\t *\n\t * @returns {module:engine/model/range~Range}\n\t */\n\ttoRange() {\n\t\treturn new Range( this.start, this.end );\n\t}\n\n\t/**\n\t * Creates a `LiveRange` instance that is equal to the given range.\n\t *\n\t * @param {module:engine/model/range~Range} range\n\t * @returns {module:engine/model/liverange~LiveRange}\n\t */\n\tstatic fromRange( range ) {\n\t\treturn new LiveRange( range.start, range.end );\n\t}\n\n\t/**\n\t * @see module:engine/model/range~Range._createIn\n\t * @static\n\t * @protected\n\t * @method module:engine/model/liverange~LiveRange._createIn\n\t * @param {module:engine/model/element~Element} element\n\t * @returns {module:engine/model/liverange~LiveRange}\n\t */\n\n\t/**\n\t * @see module:engine/model/range~Range._createOn\n\t * @static\n\t * @protected\n\t * @method module:engine/model/liverange~LiveRange._createOn\n\t * @param {module:engine/model/element~Element} element\n\t * @returns {module:engine/model/liverange~LiveRange}\n\t */\n\n\t/**\n\t * @see module:engine/model/range~Range._createFromPositionAndShift\n\t * @static\n\t * @protected\n\t * @method module:engine/model/liverange~LiveRange._createFromPositionAndShift\n\t * @param {module:engine/model/position~Position} position\n\t * @param {Number} shift\n\t * @returns {module:engine/model/liverange~LiveRange}\n\t */\n\n\t/**\n\t * Fired when `LiveRange` instance boundaries have changed due to changes in the\n\t * {@link module:engine/model/document~Document document}.\n\t *\n\t * @event change:range\n\t * @param {module:engine/model/range~Range} oldRange Range with start and end position equal to start and end position of this live\n\t * range before it got changed.\n\t * @param {Object} data Object with additional information about the change.\n\t * @param {module:engine/model/position~Position|null} data.deletionPosition Source position for remove and merge changes.\n\t * Available if the range was moved to the graveyard root, `null` otherwise.\n\t */\n\n\t/**\n\t * Fired when `LiveRange` instance boundaries have not changed after a change in {@link module:engine/model/document~Document document}\n\t * but the change took place inside the range, effectively changing its content.\n\t *\n\t * @event change:content\n\t * @param {module:engine/model/range~Range} range Range with start and end position equal to start and end position of\n\t * change range.\n\t * @param {Object} data Object with additional information about the change.\n\t * @param {null} data.deletionPosition Due to the nature of this event, this property is always set to `null`. It is passed\n\t * for compatibility with the {@link module:engine/model/liverange~LiveRange#event:change:range} event.\n\t */\n}\n\n// Binds this `LiveRange` to the {@link module:engine/model/document~Document document}\n// that owns this range's {@link module:engine/model/range~Range#root root}.\n//\n// @private\nfunction bindWithDocument() {\n\tthis.listenTo(\n\t\tthis.root.document.model,\n\t\t'applyOperation',\n\t\t( event, args ) => {\n\t\t\tconst operation = args[ 0 ];\n\n\t\t\tif ( !operation.isDocumentOperation ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttransform.call( this, operation );\n\t\t},\n\t\t{ priority: 'low' }\n\t);\n}\n\n// Updates this range accordingly to the updates applied to the model. Bases on change events.\n//\n// @private\n// @param {module:engine/model/operation/operation~Operation} operation Executed operation.\nfunction transform( operation ) {\n\t// Transform the range by the operation. Join the result ranges if needed.\n\tconst ranges = this.getTransformedByOperation( operation );\n\tconst result = Range._createFromRanges( ranges );\n\n\tconst boundariesChanged = !result.isEqual( this );\n\tconst contentChanged = doesOperationChangeRangeContent( this, operation );\n\n\tlet deletionPosition = null;\n\n\tif ( boundariesChanged ) {\n\t\t// If range boundaries have changed, fire `change:range` event.\n\t\t//\n\t\tif ( result.root.rootName == '$graveyard' ) {\n\t\t\t// If the range was moved to the graveyard root, set `deletionPosition`.\n\t\t\tif ( operation.type == 'remove' ) {\n\t\t\t\tdeletionPosition = operation.sourcePosition;\n\t\t\t} else {\n\t\t\t\t// Merge operation.\n\t\t\t\tdeletionPosition = operation.deletionPosition;\n\t\t\t}\n\t\t}\n\n\t\tconst oldRange = this.toRange();\n\n\t\tthis.start = result.start;\n\t\tthis.end = result.end;\n\n\t\tthis.fire( 'change:range', oldRange, { deletionPosition } );\n\t} else if ( contentChanged ) {\n\t\t// If range boundaries have not changed, but there was change inside the range, fire `change:content` event.\n\t\tthis.fire( 'change:content', this.toRange(), { deletionPosition } );\n\t}\n}\n\n// Checks whether given operation changes something inside the range (even if it does not change boundaries).\n//\n// @private\n// @param {module:engine/model/range~Range} range Range to check.\n// @param {module:engine/model/operation/operation~Operation} operation Executed operation.\n// @returns {Boolean}\nfunction doesOperationChangeRangeContent( range, operation ) {\n\tswitch ( operation.type ) {\n\t\tcase 'insert':\n\t\t\treturn range.containsPosition( operation.position );\n\t\tcase 'move':\n\t\tcase 'remove':\n\t\tcase 'reinsert':\n\t\tcase 'merge':\n\t\t\treturn range.containsPosition( operation.sourcePosition ) ||\n\t\t\t\trange.start.isEqual( operation.sourcePosition ) ||\n\t\t\t\trange.containsPosition( operation.targetPosition );\n\t\tcase 'split':\n\t\t\treturn range.containsPosition( operation.splitPosition ) || range.containsPosition( operation.insertionPosition );\n\t}\n\n\treturn false;\n}\n\nmix( LiveRange, EmitterMixin );\n","/**\n * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\n/**\n * @module engine/model/documentselection\n */\n\nimport mix from '@ckeditor/ckeditor5-utils/src/mix';\nimport EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';\n\nimport Selection from './selection';\nimport LiveRange from './liverange';\nimport Text from './text';\nimport TextProxy from './textproxy';\nimport toMap from '@ckeditor/ckeditor5-utils/src/tomap';\nimport CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';\nimport log from '@ckeditor/ckeditor5-utils/src/log';\nimport uid from '@ckeditor/ckeditor5-utils/src/uid';\n\nconst storePrefix = 'selection:';\n\n/**\n * `DocumentSelection` is a special selection which is used as the\n * {@link module:engine/model/document~Document#selection document's selection}.\n * There can be only one instance of `DocumentSelection` per document.\n *\n * Document selection can only be changed by using the {@link module:engine/model/writer~Writer} instance\n * inside the {@link module:engine/model/model~Model#change `change()`} block, as it provides a secure way to modify model.\n *\n * `DocumentSelection` is automatically updated upon changes in the {@link module:engine/model/document~Document document}\n * to always contain valid ranges. Its attributes are inherited from the text unless set explicitly.\n *\n * Differences between {@link module:engine/model/selection~Selection} and `DocumentSelection` are:\n * * there is always a range in `DocumentSelection` - even if no ranges were added there is a \"default range\"\n * present in the selection,\n * * ranges added to this selection updates automatically when the document changes,\n * * attributes of `DocumentSelection` are updated automatically according to selection ranges.\n *\n * Since `DocumentSelection` uses {@link module:engine/model/liverange~LiveRange live ranges}\n * and is updated when {@link module:engine/model/document~Document document}\n * changes, it cannot be set on {@link module:engine/model/node~Node nodes}\n * that are inside {@link module:engine/model/documentfragment~DocumentFragment document fragment}.\n * If you need to represent a selection in document fragment,\n * use {@link module:engine/model/selection~Selection Selection class} instead.\n *\n * @mixes module:utils/emittermixin~EmitterMixin\n */\nexport default class DocumentSelection {\n\t/**\n\t * Creates an empty live selection for given {@link module:engine/model/document~Document}.\n\t *\n\t * @param {module:engine/model/document~Document} doc Document which owns this selection.\n\t */\n\tconstructor( doc ) {\n\t\t/**\n\t\t * Selection used internally by that class (`DocumentSelection` is a proxy to that selection).\n\t\t *\n\t\t * @protected\n\t\t */\n\t\tthis._selection = new LiveSelection( doc );\n\n\t\tthis._selection.delegate( 'change:range' ).to( this );\n\t\tthis._selection.delegate( 'change:attribute' ).to( this );\n\t}\n\n\t/**\n\t * Returns whether the selection is collapsed. Selection is collapsed when there is exactly one range which is\n\t * collapsed.\n\t *\n\t * @readonly\n\t * @type {Boolean}\n\t */\n\tget isCollapsed() {\n\t\treturn this._selection.isCollapsed;\n\t}\n\n\t/**\n\t * Selection anchor. Anchor may be described as a position where the most recent part of the selection starts.\n\t * Together with {@link #focus} they define the direction of selection, which is important\n\t * when expanding/shrinking selection. Anchor is always {@link module:engine/model/range~Range#start start} or\n\t * {@link module:engine/model/range~Range#end end} position of the most recently added range.\n\t *\n\t * Is set to `null` if there are no ranges in selection.\n\t *\n\t * @see #focus\n\t * @readonly\n\t * @type {module:engine/model/position~Position|null}\n\t */\n\tget anchor() {\n\t\treturn this._selection.anchor;\n\t}\n\n\t/**\n\t * Selection focus. Focus is a position where the selection ends.\n\t *\n\t * Is set to `null` if there are no ranges in selection.\n\t *\n\t * @see #anchor\n\t * @readonly\n\t * @type {module:engine/model/position~Position|null}\n\t */\n\tget focus() {\n\t\treturn this._selection.focus;\n\t}\n\n\t/**\n\t * Returns number of ranges in selection.\n\t *\n\t * @readonly\n\t * @type {Number}\n\t */\n\tget rangeCount() {\n\t\treturn this._selection.rangeCount;\n\t}\n\n\t/**\n\t * Describes whether `Documentselection` has own range(s) set, or if it is defaulted to\n\t * {@link module:engine/model/document~Document#_getDefaultRange document's default range}.\n\t *\n\t * @readonly\n\t * @type {Boolean}\n\t */\n\tget hasOwnRange() {\n\t\treturn this._selection.hasOwnRange;\n\t}\n\n\t/**\n\t * Specifies whether the {@link #focus}\n\t * precedes {@link #anchor}.\n\t *\n\t * @readonly\n\t * @type {Boolean}\n\t */\n\tget isBackward() {\n\t\treturn this._selection.isBackward;\n\t}\n\n\t/**\n\t * Describes whether the gravity is overridden (using {@link module:engine/model/writer~Writer#overrideSelectionGravity}) or not.\n\t *\n\t * Note that the gravity remains overridden as long as will not be restored the same number of times as it was overridden.\n\t *\n\t * @readonly\n\t * @returns {Boolean}\n\t */\n\tget isGravityOverridden() {\n\t\treturn this._selection.isGravityOverridden;\n\t}\n\n\t/**\n\t * Used for the compatibility with the {@link module:engine/model/selection~Selection#isEqual} method.\n\t *\n\t * @protected\n\t */\n\tget _ranges() {\n\t\treturn this._selection._ranges;\n\t}\n\n\t/**\n\t * Returns an iterable that iterates over copies of selection ranges.\n\t *\n\t * @returns {Iterable.}\n\t */\n\tgetRanges() {\n\t\treturn this._selection.getRanges();\n\t}\n\n\t/**\n\t * Returns the first position in the selection.\n\t * First position is the position that {@link module:engine/model/position~Position#isBefore is before}\n\t * any other position in the selection.\n\t *\n\t * Returns `null` if there are no ranges in selection.\n\t *\n\t * @returns {module:engine/model/position~Position|null}\n\t */\n\tgetFirstPosition() {\n\t\treturn this._selection.getFirstPosition();\n\t}\n\n\t/**\n\t * Returns the last position in the selection.\n\t * Last position is the position that {@link module:engine/model/position~Position#isAfter is after}\n\t * any other position in the selection.\n\t *\n\t * Returns `null` if there are no ranges in selection.\n\t *\n\t * @returns {module:engine/model/position~Position|null}\n\t */\n\tgetLastPosition() {\n\t\treturn this._selection.getLastPosition();\n\t}\n\n\t/**\n\t * Returns a copy of the first range in the selection.\n\t * First range is the one which {@link module:engine/model/range~Range#start start} position\n\t * {@link module:engine/model/position~Position#isBefore is before} start position of all other ranges\n\t * (not to confuse with the first range added to the selection).\n\t *\n\t * Returns `null` if there are no ranges in selection.\n\t *\n\t * @returns {module:engine/model/range~Range|null}\n\t */\n\tgetFirstRange() {\n\t\treturn this._selection.getFirstRange();\n\t}\n\n\t/**\n\t * Returns a copy of the last range in the selection.\n\t * Last range is the one which {@link module:engine/model/range~Range#end end} position\n\t * {@link module:engine/model/position~Position#isAfter is after} end position of all other ranges (not to confuse with the range most\n\t * recently added to the selection).\n\t *\n\t * Returns `null` if there are no ranges in selection.\n\t *\n\t * @returns {module:engine/model/range~Range|null}\n\t */\n\tgetLastRange() {\n\t\treturn this._selection.getLastRange();\n\t}\n\n\t/**\n\t * Gets elements of type \"block\" touched by the selection.\n\t *\n\t * This method's result can be used for example to apply block styling to all blocks covered by this selection.\n\t *\n\t * **Note:** `getSelectedBlocks()` always returns the deepest block.\n\t *\n\t * In this case the function will return exactly all 3 paragraphs:\n\t *\n\t *\t\t[a\n\t *\t\t\n\t *\t\t\tb\n\t *\t\t\n\t *\t\tc]d\n\t *\n\t * In this case the paragraph will also be returned, despite the collapsed selection:\n\t *\n\t *\t\t[]a\n\t *\n\t * **Special case**: If a selection ends at the beginning of a block, that block is not returned as from user perspective\n\t * this block wasn't selected. See [#984](https://github.com/ckeditor/ckeditor5-engine/issues/984) for more details.\n\t *\n\t *\t\t[a\n\t *\t\tb\n\t *\t\t]c // this block will not be returned\n\t *\n\t * @returns {Iterator.}\n\t */\n\tgetSelectedBlocks() {\n\t\treturn this._selection.getSelectedBlocks();\n\t}\n\n\t/**\n\t * Returns the selected element. {@link module:engine/model/element~Element Element} is considered as selected if there is only\n\t * one range in the selection, and that range contains exactly one element.\n\t * Returns `null` if there is no selected element.\n\t *\n\t * @returns {module:engine/model/element~Element|null}\n\t */\n\tgetSelectedElement() {\n\t\treturn this._selection.getSelectedElement();\n\t}\n\n\t/**\n\t * Checks whether the selection contains the entire content of the given element. This means that selection must start\n\t * at a position {@link module:engine/model/position~Position#isTouching touching} the element's start and ends at position\n\t * touching the element's end.\n\t *\n\t * By default, this method will check whether the entire content of the selection's current root is selected.\n\t * Useful to check if e.g. the user has just pressed Ctrl + A.\n\t *\n\t * @param {module:engine/model/element~Element} [element=this.anchor.root]\n\t * @returns {Boolean}\n\t */\n\tcontainsEntireContent( element ) {\n\t\treturn this._selection.containsEntireContent( element );\n\t}\n\n\t/**\n\t * Unbinds all events previously bound by document selection.\n\t */\n\tdestroy() {\n\t\tthis._selection.destroy();\n\t}\n\n\t/**\n\t * Returns iterable that iterates over this selection's attribute keys.\n\t *\n\t * @returns {Iterable.}\n\t */\n\tgetAttributeKeys() {\n\t\treturn this._selection.getAttributeKeys();\n\t}\n\n\t/**\n\t * Returns iterable that iterates over this selection's attributes.\n\t *\n\t * Attributes are returned as arrays containing two items. First one is attribute key and second is attribute value.\n\t * This format is accepted by native `Map` object and also can be passed in `Node` constructor.\n\t *\n\t * @returns {Iterable.<*>}\n\t */\n\tgetAttributes() {\n\t\treturn this._selection.getAttributes();\n\t}\n\n\t/**\n\t * Gets an attribute value for given key or `undefined` if that attribute is not set on the selection.\n\t *\n\t * @param {String} key Key of attribute to look for.\n\t * @returns {*} Attribute value or `undefined`.\n\t */\n\tgetAttribute( key ) {\n\t\treturn this._selection.getAttribute( key );\n\t}\n\n\t/**\n\t * Checks if the selection has an attribute for given key.\n\t *\n\t * @param {String} key Key of attribute to check.\n\t * @returns {Boolean} `true` if attribute with given key is set on selection, `false` otherwise.\n\t */\n\thasAttribute( key ) {\n\t\treturn this._selection.hasAttribute( key );\n\t}\n\n\t/**\n\t * Moves {@link module:engine/model/documentselection~DocumentSelection#focus} to the specified location.\n\t * Should be used only within the {@link module:engine/model/writer~Writer#setSelectionFocus} method.\n\t *\n\t * The location can be specified in the same form as\n\t * {@link module:engine/model/writer~Writer#createPositionAt writer.createPositionAt()} parameters.\n\t *\n\t * @see module:engine/model/writer~Writer#setSelectionFocus\n\t * @protected\n\t * @param {module:engine/model/item~Item|module:engine/model/position~Position} itemOrPosition\n\t * @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when\n\t * first parameter is a {@link module:engine/model/item~Item model item}.\n\t */\n\t_setFocus( itemOrPosition, offset ) {\n\t\tthis._selection.setFocus( itemOrPosition, offset );\n\t}\n\n\t/**\n\t * Sets this selection's ranges and direction to the specified location based on the given\n\t * {@link module:engine/model/selection~Selection selection}, {@link module:engine/model/position~Position position},\n\t * {@link module:engine/model/node~Node node}, {@link module:engine/model/position~Position position},\n\t * {@link module:engine/model/range~Range range}, an iterable of {@link module:engine/model/range~Range ranges} or null.\n\t * Should be used only within the {@link module:engine/model/writer~Writer#setSelection} method.\n\t *\n\t * @see module:engine/model/writer~Writer#setSelection\n\t * @protected\n\t * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection|\n\t * module:engine/model/position~Position|module:engine/model/node~Node|\n\t * Iterable.|module:engine/model/range~Range|null} selectable\n\t * @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Sets place or offset of the selection.\n\t * @param {Object} [options]\n\t * @param {Boolean} [options.backward] Sets this selection instance to be backward.\n\t */\n\t_setTo( selectable, placeOrOffset, options ) {\n\t\tthis._selection.setTo( selectable, placeOrOffset, options );\n\t}\n\n\t/**\n\t * Sets attribute on the selection. If attribute with the same key already is set, it's value is overwritten.\n\t * Should be used only within the {@link module:engine/model/writer~Writer#setSelectionAttribute} method.\n\t *\n\t * @see module:engine/model/writer~Writer#setSelectionAttribute\n\t * @protected\n\t * @param {String} key Key of the attribute to set.\n\t * @param {*} value Attribute value.\n\t */\n\t_setAttribute( key, value ) {\n\t\tthis._selection.setAttribute( key, value );\n\t}\n\n\t/**\n\t * Removes an attribute with given key from the selection.\n\t * If the given attribute was set on the selection, fires the {@link module:engine/model/selection~Selection#event:change:range}\n\t * event with removed attribute key.\n\t * Should be used only within the {@link module:engine/model/writer~Writer#removeSelectionAttribute} method.\n\t *\n\t * @see module:engine/model/writer~Writer#removeSelectionAttribute\n\t * @protected\n\t * @param {String} key Key of the attribute to remove.\n\t */\n\t_removeAttribute( key ) {\n\t\tthis._selection.removeAttribute( key );\n\t}\n\n\t/**\n\t * Returns an iterable that iterates through all selection attributes stored in current selection's parent.\n\t *\n\t * @protected\n\t * @returns {Iterable.<*>}\n\t */\n\t_getStoredAttributes() {\n\t\treturn this._selection._getStoredAttributes();\n\t}\n\n\t/**\n\t * Temporarily changes the gravity of the selection from the left to the right.\n\t *\n\t * The gravity defines from which direction the selection inherits its attributes. If it's the default left\n\t * gravity, the selection (after being moved by the the user) inherits attributes from its left hand side.\n\t * This method allows to temporarily override this behavior by forcing the gravity to the right.\n\t *\n\t * It returns an unique identifier which is required to restore the gravity. It guarantees the symmetry\n\t * of the process.\n\t *\n\t * @see module:engine/model/writer~Writer#overrideSelectionGravity\n\t * @protected\n\t * @returns {String} The unique id which allows restoring the gravity.\n\t */\n\t_overrideGravity() {\n\t\treturn this._selection.overrideGravity();\n\t}\n\n\t/**\n\t * Restores the {@link ~DocumentSelection#_overrideGravity overridden gravity}.\n\t *\n\t * Restoring the gravity is only possible using the unique identifier returned by\n\t * {@link ~DocumentSelection#_overrideGravity}. Note that the gravity remains overridden as long as won't be restored\n\t * the same number of times it was overridden.\n\t *\n\t * @see module:engine/model/writer~Writer#restoreSelectionGravity\n\t * @protected\n\t * @param {String} uid The unique id returned by {@link #_overrideGravity}.\n\t */\n\t_restoreGravity( uid ) {\n\t\tthis._selection.restoreGravity( uid );\n\t}\n\n\t/**\n\t * Generates and returns an attribute key for selection attributes store, basing on original attribute key.\n\t *\n\t * @protected\n\t * @param {String} key Attribute key to convert.\n\t * @returns {String} Converted attribute key, applicable for selection store.\n\t */\n\tstatic _getStoreAttributeKey( key ) {\n\t\treturn storePrefix + key;\n\t}\n\n\t/**\n\t * Checks whether the given attribute key is an attribute stored on an element.\n\t *\n\t * @protected\n\t * @param {String} key\n\t * @returns {Boolean}\n\t */\n\tstatic _isStoreAttributeKey( key ) {\n\t\treturn key.startsWith( storePrefix );\n\t}\n}\n\nmix( DocumentSelection, EmitterMixin );\n\n/**\n * Fired when selection range(s) changed.\n *\n * @event change:range\n * @param {Boolean} directChange In case of {@link module:engine/model/selection~Selection} class it is always set\n * to `true` which indicates that the selection change was caused by a direct use of selection's API.\n * The {@link module:engine/model/documentselection~DocumentSelection}, however, may change because its position\n * was directly changed through the {@link module:engine/model/writer~Writer writer} or because its position was\n * changed because the structure of the model has been changed (which means an indirect change).\n * The indirect change does not occur in case of normal (detached) selections because they are \"static\" (as \"not live\")\n * which mean that they are not updated once the document changes.\n */\n\n/**\n * Fired when selection attribute changed.\n *\n * @event change:attribute\n * @param {Boolean} directChange In case of {@link module:engine/model/selection~Selection} class it is always set\n * to `true` which indicates that the selection change was caused by a direct use of selection's API.\n * The {@link module:engine/model/documentselection~DocumentSelection}, however, may change because its attributes\n * were directly changed through the {@link module:engine/model/writer~Writer writer} or because its position was\n * changed in the model and its attributes were refreshed (which means an indirect change).\n * The indirect change does not occur in case of normal (detached) selections because they are \"static\" (as \"not live\")\n * which mean that they are not updated once the document changes.\n * @param {Array.} attributeKeys Array containing keys of attributes that changed.\n */\n\n// `LiveSelection` is used internally by {@link module:engine/model/documentselection~DocumentSelection} and shouldn't be used directly.\n//\n// LiveSelection` is automatically updated upon changes in the {@link module:engine/model/document~Document document}\n// to always contain valid ranges. Its attributes are inherited from the text unless set explicitly.\n//\n// Differences between {@link module:engine/model/selection~Selection} and `LiveSelection` are:\n// * there is always a range in `LiveSelection` - even if no ranges were added there is a \"default range\"\n// present in the selection,\n// * ranges added to this selection updates automatically when the document changes,\n// * attributes of `LiveSelection` are updated automatically according to selection ranges.\n//\n// @extends module:engine/model/selection~Selection\n//\n\nclass LiveSelection extends Selection {\n\t// Creates an empty live selection for given {@link module:engine/model/document~Document}.\n\t// @param {module:engine/model/document~Document} doc Document which owns this selection.\n\tconstructor( doc ) {\n\t\tsuper();\n\n\t\t// Document which owns this selection.\n\t\t//\n\t\t// @protected\n\t\t// @member {module:engine/model/model~Model}\n\t\tthis._model = doc.model;\n\n\t\t// Document which owns this selection.\n\t\t//\n\t\t// @protected\n\t\t// @member {module:engine/model/document~Document}\n\t\tthis._document = doc;\n\n\t\t// Keeps mapping of attribute name to priority with which the attribute got modified (added/changed/removed)\n\t\t// last time. Possible values of priority are: `'low'` and `'normal'`.\n\t\t//\n\t\t// Priorities are used by internal `LiveSelection` mechanisms. All attributes set using `LiveSelection`\n\t\t// attributes API are set with `'normal'` priority.\n\t\t//\n\t\t// @private\n\t\t// @member {Map} module:engine/model/liveselection~LiveSelection#_attributePriority\n\t\tthis._attributePriority = new Map();\n\n\t\t// Contains data required to fix ranges which have been moved to the graveyard.\n\t\t// @private\n\t\t// @member {Array} module:engine/model/liveselection~LiveSelection#_fixGraveyardRangesData\n\t\tthis._fixGraveyardRangesData = [];\n\n\t\t// Flag that informs whether the selection ranges have changed. It is changed on true when `LiveRange#change:range` event is fired.\n\t\t// @private\n\t\t// @member {Array} module:engine/model/liveselection~LiveSelection#_hasChangedRange\n\t\tthis._hasChangedRange = false;\n\n\t\t// Each overriding gravity adds an UID to the set and each removal removes it.\n\t\t// Gravity is overridden when there's at least one UID in the set.\n\t\t// Gravity is restored when the set is empty.\n\t\t// This is to prevent conflicts when gravity is overridden by more than one feature at the same time.\n\t\t// @private\n\t\t// @type {Set}\n\t\tthis._overriddenGravityRegister = new Set();\n\n\t\t// Add events that will ensure selection correctness.\n\t\tthis.on( 'change:range', () => {\n\t\t\tfor ( const range of this.getRanges() ) {\n\t\t\t\tif ( !this._document._validateSelectionRange( range ) ) {\n\t\t\t\t\t/**\n\t\t\t\t\t * Range from {@link module:engine/model/documentselection~DocumentSelection document selection}\n\t\t\t\t\t * starts or ends at incorrect position.\n\t\t\t\t\t *\n\t\t\t\t\t * @error document-selection-wrong-position\n\t\t\t\t\t * @param {module:engine/model/range~Range} range\n\t\t\t\t\t */\n\t\t\t\t\tthrow new CKEditorError(\n\t\t\t\t\t\t'document-selection-wrong-position: Range from document selection starts or ends at incorrect position.',\n\t\t\t\t\t\t{ range }\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\n\t\tthis.listenTo( this._document, 'change', ( evt, batch ) => {\n\t\t\t// Update selection's attributes.\n\t\t\tthis._updateAttributes( false );\n\n\t\t\t// Clear selection attributes from element if no longer empty.\n\t\t\tclearAttributesStoredInElement( this._model, batch );\n\t\t} );\n\n\t\tthis.listenTo( this._model, 'applyOperation', () => {\n\t\t\twhile ( this._fixGraveyardRangesData.length ) {\n\t\t\t\tconst { liveRange, sourcePosition } = this._fixGraveyardRangesData.shift();\n\n\t\t\t\tthis._fixGraveyardSelection( liveRange, sourcePosition );\n\t\t\t}\n\n\t\t\tif ( this._hasChangedRange ) {\n\t\t\t\tthis._hasChangedRange = false;\n\n\t\t\t\tthis.fire( 'change:range', { directChange: false } );\n\t\t\t}\n\t\t}, { priority: 'lowest' } );\n\t}\n\n\tget isCollapsed() {\n\t\tconst length = this._ranges.length;\n\n\t\treturn length === 0 ? this._document._getDefaultRange().isCollapsed : super.isCollapsed;\n\t}\n\n\tget anchor() {\n\t\treturn super.anchor || this._document._getDefaultRange().start;\n\t}\n\n\tget focus() {\n\t\treturn super.focus || this._document._getDefaultRange().end;\n\t}\n\n\tget rangeCount() {\n\t\treturn this._ranges.length ? this._ranges.length : 1;\n\t}\n\n\t// Describes whether `LiveSelection` has own range(s) set, or if it is defaulted to\n\t// {@link module:engine/model/document~Document#_getDefaultRange document's default range}.\n\t//\n\t// @readonly\n\t// @type {Boolean}\n\tget hasOwnRange() {\n\t\treturn this._ranges.length > 0;\n\t}\n\n\t// When set to `true` then selection attributes on node before the caret won't be taken\n\t// into consideration while updating selection attributes.\n\t//\n\t// @protected\n\t// @type {Boolean}\n\tget isGravityOverridden() {\n\t\treturn !!this._overriddenGravityRegister.size;\n\t}\n\n\t// Unbinds all events previously bound by live selection.\n\tdestroy() {\n\t\tfor ( let i = 0; i < this._ranges.length; i++ ) {\n\t\t\tthis._ranges[ i ].detach();\n\t\t}\n\n\t\tthis.stopListening();\n\t}\n\n\t* getRanges() {\n\t\tif ( this._ranges.length ) {\n\t\t\tyield* super.getRanges();\n\t\t} else {\n\t\t\tyield this._document._getDefaultRange();\n\t\t}\n\t}\n\n\tgetFirstRange() {\n\t\treturn super.getFirstRange() || this._document._getDefaultRange();\n\t}\n\n\tgetLastRange() {\n\t\treturn super.getLastRange() || this._document._getDefaultRange();\n\t}\n\n\tsetTo( selectable, optionsOrPlaceOrOffset, options ) {\n\t\tsuper.setTo( selectable, optionsOrPlaceOrOffset, options );\n\t\tthis._refreshAttributes();\n\t}\n\n\tsetFocus( itemOrPosition, offset ) {\n\t\tsuper.setFocus( itemOrPosition, offset );\n\t\tthis._refreshAttributes();\n\t}\n\n\tsetAttribute( key, value ) {\n\t\tif ( this._setAttribute( key, value ) ) {\n\t\t\t// Fire event with exact data.\n\t\t\tconst attributeKeys = [ key ];\n\t\t\tthis.fire( 'change:attribute', { attributeKeys, directChange: true } );\n\t\t}\n\t}\n\n\tremoveAttribute( key ) {\n\t\tif ( this._removeAttribute( key ) ) {\n\t\t\t// Fire event with exact data.\n\t\t\tconst attributeKeys = [ key ];\n\t\t\tthis.fire( 'change:attribute', { attributeKeys, directChange: true } );\n\t\t}\n\t}\n\n\toverrideGravity() {\n\t\tconst overrideUid = uid();\n\n\t\t// Remember that another overriding has been requested. It will need to be removed\n\t\t// before the gravity is to be restored.\n\t\tthis._overriddenGravityRegister.add( overrideUid );\n\n\t\tif ( this._overriddenGravityRegister.size === 1 ) {\n\t\t\tthis._refreshAttributes();\n\t\t}\n\n\t\treturn overrideUid;\n\t}\n\n\trestoreGravity( uid ) {\n\t\tif ( !this._overriddenGravityRegister.has( uid ) ) {\n\t\t\t/**\n\t\t\t * Restoring gravity for an unknown UID is not possible. Make sure you are using a correct\n\t\t\t * UID obtained from the {@link module:engine/model/writer~Writer#overrideSelectionGravity} to restore.\n\t\t\t *\n\t\t\t * @error document-selection-gravity-wrong-restore\n\t\t\t * @param {String} uid The unique identifier returned by\n\t\t\t * {@link module:engine/model/documentselection~DocumentSelection#_overrideGravity}.\n\t\t\t */\n\t\t\tthrow new CKEditorError(\n\t\t\t\t'document-selection-gravity-wrong-restore: Attempting to restore the selection gravity for an unknown UID.',\n\t\t\t\t{ uid }\n\t\t\t);\n\t\t}\n\n\t\tthis._overriddenGravityRegister.delete( uid );\n\n\t\t// Restore gravity only when all overriding have been restored.\n\t\tif ( !this.isGravityOverridden ) {\n\t\t\tthis._refreshAttributes();\n\t\t}\n\t}\n\n\t// Removes all attributes from the selection and sets attributes according to the surrounding nodes.\n\t_refreshAttributes() {\n\t\tthis._updateAttributes( true );\n\t}\n\n\t_popRange() {\n\t\tthis._ranges.pop().detach();\n\t}\n\n\t_pushRange( range ) {\n\t\tconst liveRange = this._prepareRange( range );\n\n\t\t// `undefined` is returned when given `range` is in graveyard root.\n\t\tif ( liveRange ) {\n\t\t\tthis._ranges.push( liveRange );\n\t\t}\n\t}\n\n\t// Prepares given range to be added to selection. Checks if it is correct,\n\t// converts it to {@link module:engine/model/liverange~LiveRange LiveRange}\n\t// and sets listeners listening to the range's change event.\n\t//\n\t// @private\n\t// @param {module:engine/model/range~Range} range\n\t_prepareRange( range ) {\n\t\tthis._checkRange( range );\n\n\t\tif ( range.root == this._document.graveyard ) {\n\t\t\t/**\n\t\t\t * Trying to add a Range that is in the graveyard root. Range rejected.\n\t\t\t *\n\t\t\t * @warning model-selection-range-in-graveyard\n\t\t\t */\n\t\t\tlog.warn( 'model-selection-range-in-graveyard: Trying to add a Range that is in the graveyard root. Range rejected.' );\n\n\t\t\treturn;\n\t\t}\n\n\t\tconst liveRange = LiveRange.fromRange( range );\n\n\t\tliveRange.on( 'change:range', ( evt, oldRange, data ) => {\n\t\t\tthis._hasChangedRange = true;\n\n\t\t\t// If `LiveRange` is in whole moved to the graveyard, save necessary data. It will be fixed on `Model#applyOperation` event.\n\t\t\tif ( liveRange.root == this._document.graveyard ) {\n\t\t\t\tthis._fixGraveyardRangesData.push( {\n\t\t\t\t\tliveRange,\n\t\t\t\t\tsourcePosition: data.deletionPosition\n\t\t\t\t} );\n\t\t\t}\n\t\t} );\n\n\t\treturn liveRange;\n\t}\n\n\t// Updates this selection attributes according to its ranges and the {@link module:engine/model/document~Document model document}.\n\t//\n\t// @protected\n\t// @param {Boolean} clearAll\n\t// @fires change:attribute\n\t_updateAttributes( clearAll ) {\n\t\tconst newAttributes = toMap( this._getSurroundingAttributes() );\n\t\tconst oldAttributes = toMap( this.getAttributes() );\n\n\t\tif ( clearAll ) {\n\t\t\t// If `clearAll` remove all attributes and reset priorities.\n\t\t\tthis._attributePriority = new Map();\n\t\t\tthis._attrs = new Map();\n\t\t} else {\n\t\t\t// If not, remove only attributes added with `low` priority.\n\t\t\tfor ( const [ key, priority ] of this._attributePriority ) {\n\t\t\t\tif ( priority == 'low' ) {\n\t\t\t\t\tthis._attrs.delete( key );\n\t\t\t\t\tthis._attributePriority.delete( key );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis._setAttributesTo( newAttributes );\n\n\t\t// Let's evaluate which attributes really changed.\n\t\tconst changed = [];\n\n\t\t// First, loop through all attributes that are set on selection right now.\n\t\t// Check which of them are different than old attributes.\n\t\tfor ( const [ newKey, newValue ] of this.getAttributes() ) {\n\t\t\tif ( !oldAttributes.has( newKey ) || oldAttributes.get( newKey ) !== newValue ) {\n\t\t\t\tchanged.push( newKey );\n\t\t\t}\n\t\t}\n\n\t\t// Then, check which of old attributes got removed.\n\t\tfor ( const [ oldKey ] of oldAttributes ) {\n\t\t\tif ( !this.hasAttribute( oldKey ) ) {\n\t\t\t\tchanged.push( oldKey );\n\t\t\t}\n\t\t}\n\n\t\t// Fire event with exact data (fire only if anything changed).\n\t\tif ( changed.length > 0 ) {\n\t\t\tthis.fire( 'change:attribute', { attributeKeys: changed, directChange: false } );\n\t\t}\n\t}\n\n\t// Internal method for setting `LiveSelection` attribute. Supports attribute priorities (through `directChange`\n\t// parameter).\n\t//\n\t// @private\n\t// @param {String} key Attribute key.\n\t// @param {*} value Attribute value.\n\t// @param {Boolean} [directChange=true] `true` if the change is caused by `Selection` API, `false` if change\n\t// is caused by `Batch` API.\n\t// @returns {Boolean} Whether value has changed.\n\t_setAttribute( key, value, directChange = true ) {\n\t\tconst priority = directChange ? 'normal' : 'low';\n\n\t\tif ( priority == 'low' && this._attributePriority.get( key ) == 'normal' ) {\n\t\t\t// Priority too low.\n\t\t\treturn false;\n\t\t}\n\n\t\tconst oldValue = super.getAttribute( key );\n\n\t\t// Don't do anything if value has not changed.\n\t\tif ( oldValue === value ) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis._attrs.set( key, value );\n\n\t\t// Update priorities map.\n\t\tthis._attributePriority.set( key, priority );\n\n\t\treturn true;\n\t}\n\n\t// Internal method for removing `LiveSelection` attribute. Supports attribute priorities (through `directChange`\n\t// parameter).\n\t//\n\t// NOTE: Even if attribute is not present in the selection but is provided to this method, it's priority will\n\t// be changed according to `directChange` parameter.\n\t//\n\t// @private\n\t// @param {String} key Attribute key.\n\t// @param {Boolean} [directChange=true] `true` if the change is caused by `Selection` API, `false` if change\n\t// is caused by `Batch` API.\n\t// @returns {Boolean} Whether attribute was removed. May not be true if such attributes didn't exist or the\n\t// existing attribute had higher priority.\n\t_removeAttribute( key, directChange = true ) {\n\t\tconst priority = directChange ? 'normal' : 'low';\n\n\t\tif ( priority == 'low' && this._attributePriority.get( key ) == 'normal' ) {\n\t\t\t// Priority too low.\n\t\t\treturn false;\n\t\t}\n\n\t\t// Update priorities map.\n\t\tthis._attributePriority.set( key, priority );\n\n\t\t// Don't do anything if value has not changed.\n\t\tif ( !super.hasAttribute( key ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis._attrs.delete( key );\n\n\t\treturn true;\n\t}\n\n\t// Internal method for setting multiple `LiveSelection` attributes. Supports attribute priorities (through\n\t// `directChange` parameter).\n\t//\n\t// @private\n\t// @param {Map.} attrs Iterable object containing attributes to be set.\n\t// @returns {Set.} Changed attribute keys.\n\t_setAttributesTo( attrs ) {\n\t\tconst changed = new Set();\n\n\t\tfor ( const [ oldKey, oldValue ] of this.getAttributes() ) {\n\t\t\t// Do not remove attribute if attribute with same key and value is about to be set.\n\t\t\tif ( attrs.get( oldKey ) === oldValue ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// All rest attributes will be removed so changed attributes won't change .\n\t\t\tthis._removeAttribute( oldKey, false );\n\t\t}\n\n\t\tfor ( const [ key, value ] of attrs ) {\n\t\t\t// Attribute may not be set because of attributes or because same key/value is already added.\n\t\t\tconst gotAdded = this._setAttribute( key, value, false );\n\n\t\t\tif ( gotAdded ) {\n\t\t\t\tchanged.add( key );\n\t\t\t}\n\t\t}\n\n\t\treturn changed;\n\t}\n\n\t// Returns an iterable that iterates through all selection attributes stored in current selection's parent.\n\t//\n\t// @protected\n\t// @returns {Iterable.<*>}\n\t* _getStoredAttributes() {\n\t\tconst selectionParent = this.getFirstPosition().parent;\n\n\t\tif ( this.isCollapsed && selectionParent.isEmpty ) {\n\t\t\tfor ( const key of selectionParent.getAttributeKeys() ) {\n\t\t\t\tif ( key.startsWith( storePrefix ) ) {\n\t\t\t\t\tconst realKey = key.substr( storePrefix.length );\n\n\t\t\t\t\tyield [ realKey, selectionParent.getAttribute( key ) ];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Checks model text nodes that are closest to the selection's first position and returns attributes of first\n\t// found element. If there are no text nodes in selection's first position parent, it returns selection\n\t// attributes stored in that parent.\n\t//\n\t// @private\n\t// @returns {Iterable.<*>} Collection of attributes.\n\t_getSurroundingAttributes() {\n\t\tconst position = this.getFirstPosition();\n\t\tconst schema = this._model.schema;\n\n\t\tlet attrs = null;\n\n\t\tif ( !this.isCollapsed ) {\n\t\t\t// 1. If selection is a range...\n\t\t\tconst range = this.getFirstRange();\n\n\t\t\t// ...look for a first character node in that range and take attributes from it.\n\t\t\tfor ( const value of range ) {\n\t\t\t\t// If the item is an object, we don't want to get attributes from its children.\n\t\t\t\tif ( value.item.is( 'element' ) && schema.isObject( value.item ) ) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t// This is not an optimal solution because of https://github.com/ckeditor/ckeditor5-engine/issues/454.\n\t\t\t\t// It can be done better by using `break;` instead of checking `attrs === null`.\n\t\t\t\tif ( value.type == 'text' && attrs === null ) {\n\t\t\t\t\tattrs = value.item.getAttributes();\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// 2. If the selection is a caret or the range does not contain a character node...\n\n\t\t\tconst nodeBefore = position.textNode ? position.textNode : position.nodeBefore;\n\t\t\tconst nodeAfter = position.textNode ? position.textNode : position.nodeAfter;\n\n\t\t\t// When gravity is overridden then don't take node before into consideration.\n\t\t\tif ( !this.isGravityOverridden ) {\n\t\t\t\t// ...look at the node before caret and take attributes from it if it is a character node.\n\t\t\t\tattrs = getAttrsIfCharacter( nodeBefore );\n\t\t\t}\n\n\t\t\t// 3. If not, look at the node after caret...\n\t\t\tif ( !attrs ) {\n\t\t\t\tattrs = getAttrsIfCharacter( nodeAfter );\n\t\t\t}\n\n\t\t\t// 4. If not, try to find the first character on the left, that is in the same node.\n\t\t\t// When gravity is overridden then don't take node before into consideration.\n\t\t\tif ( !this.isGravityOverridden && !attrs ) {\n\t\t\t\tlet node = nodeBefore;\n\n\t\t\t\twhile ( node && !attrs ) {\n\t\t\t\t\tnode = node.previousSibling;\n\t\t\t\t\tattrs = getAttrsIfCharacter( node );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 5. If not found, try to find the first character on the right, that is in the same node.\n\t\t\tif ( !attrs ) {\n\t\t\t\tlet node = nodeAfter;\n\n\t\t\t\twhile ( node && !attrs ) {\n\t\t\t\t\tnode = node.nextSibling;\n\t\t\t\t\tattrs = getAttrsIfCharacter( node );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 6. If not found, selection should retrieve attributes from parent.\n\t\t\tif ( !attrs ) {\n\t\t\t\tattrs = this._getStoredAttributes();\n\t\t\t}\n\t\t}\n\n\t\treturn attrs;\n\t}\n\n\t// Fixes a selection range after it ends up in graveyard root.\n\t//\n\t// @private\n\t// @param {module:engine/model/liverange~LiveRange} liveRange The range from selection, that ended up in the graveyard root.\n\t// @param {module:engine/model/position~Position} removedRangeStart Start position of a range which was removed.\n\t_fixGraveyardSelection( liveRange, removedRangeStart ) {\n\t\t// The start of the removed range is the closest position to the `liveRange` - the original selection range.\n\t\t// This is a good candidate for a fixed selection range.\n\t\tconst positionCandidate = removedRangeStart.clone();\n\n\t\t// Find a range that is a correct selection range and is closest to the start of removed range.\n\t\tconst selectionRange = this._model.schema.getNearestSelectionRange( positionCandidate );\n\n\t\t// Remove the old selection range before preparing and adding new selection range. This order is important,\n\t\t// because new range, in some cases, may intersect with old range (it depends on `getNearestSelectionRange()` result).\n\t\tconst index = this._ranges.indexOf( liveRange );\n\t\tthis._ranges.splice( index, 1 );\n\t\tliveRange.detach();\n\n\t\t// If nearest valid selection range has been found - add it in the place of old range.\n\t\tif ( selectionRange ) {\n\t\t\t// Check the range, convert it to live range, bind events, etc.\n\t\t\tconst newRange = this._prepareRange( selectionRange );\n\n\t\t\t// Add new range in the place of old range.\n\t\t\tthis._ranges.splice( index, 0, newRange );\n\t\t}\n\t\t// If nearest valid selection range cannot be found - just removing the old range is fine.\n\t}\n}\n\n// Helper function for {@link module:engine/model/liveselection~LiveSelection#_updateAttributes}.\n//\n// It takes model item, checks whether it is a text node (or text proxy) and, if so, returns it's attributes. If not, returns `null`.\n//\n// @param {module:engine/model/item~Item|null} node\n// @returns {Boolean}\nfunction getAttrsIfCharacter( node ) {\n\tif ( node instanceof TextProxy || node instanceof Text ) {\n\t\treturn node.getAttributes();\n\t}\n\n\treturn null;\n}\n\n// Removes selection attributes from element which is not empty anymore.\n//\n// @private\n// @param {module:engine/model/model~Model} model\n// @param {module:engine/model/batch~Batch} batch\nfunction clearAttributesStoredInElement( model, batch ) {\n\tconst differ = model.document.differ;\n\n\tfor ( const entry of differ.getChanges() ) {\n\t\tif ( entry.type != 'insert' ) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst changeParent = entry.position.parent;\n\t\tconst isNoLongerEmpty = entry.length === changeParent.maxOffset;\n\n\t\tif ( isNoLongerEmpty ) {\n\t\t\tmodel.enqueueChange( batch, writer => {\n\t\t\t\tconst storedAttributes = Array.from( changeParent.getAttributeKeys() )\n\t\t\t\t\t.filter( key => key.startsWith( storePrefix ) );\n\n\t\t\t\tfor ( const key of storedAttributes ) {\n\t\t\t\t\twriter.removeAttribute( key, changeParent );\n\t\t\t\t}\n\t\t\t} );\n\t\t}\n\t}\n}\n","/**\n * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\n/**\n * @module engine/conversion/conversion\n */\n\nimport CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';\n\n/**\n * A utility class that helps add converters to upcast and downcast dispatchers.\n *\n * We recommend reading the {@glink framework/guides/architecture/editing-engine Editing engine architecture} guide first to\n * understand the core concepts of the conversion mechanisms.\n *\n * The instance of the conversion manager is available in the\n * {@link module:core/editor/editor~Editor#conversion `editor.conversion`} property\n * and by default has the following groups of dispatchers (i.e. directions of conversion):\n *\n * * `downcast` (editing and data downcasts)\n * * `editingDowncast`\n * * `dataDowncast`\n * * `upcast`\n *\n * To add a converter to a specific group, use the {@link module:engine/conversion/conversion~Conversion#for `for()`}\n * method:\n *\n *\t\t// Add a converter to editing downcast and data downcast.\n *\t\teditor.conversion.for( 'downcast' ).elementToElement( config ) );\n *\n *\t\t// Add a converter to the data pipepline only:\n *\t\teditor.conversion.for( 'dataDowncast' ).elementToElement( dataConversionConfig ) );\n *\n *\t\t// And a slightly different one for the editing pipeline:\n *\t\teditor.conversion.for( 'editingDowncast' ).elementToElement( editingConversionConfig ) );\n *\n * The functions used in `add()` calls are one-way converters (i.e. you need to remember yourself to add\n * a converter in the other direction, if your feature requires that). They are also called \"conversion helpers\".\n * You can find a set of them in the {@link module:engine/conversion/downcasthelpers} and\n * {@link module:engine/conversion/upcasthelpers} modules.\n *\n * Besides allowing to register converters to specific dispatchers, you can also use methods available in this\n * class to add two-way converters (upcast and downcast):\n *\n * * {@link module:engine/conversion/conversion~Conversion#elementToElement `elementToElement()`} –\n * Model element to view element and vice versa.\n * * {@link module:engine/conversion/conversion~Conversion#attributeToElement `attributeToElement()`} –\n * Model attribute to view element and vice versa.\n * * {@link module:engine/conversion/conversion~Conversion#attributeToAttribute `attributeToAttribute()`} –\n * Model attribute to view element and vice versa.\n */\nexport default class Conversion {\n\t/**\n\t * Creates a new conversion instance.\n\t */\n\tconstructor() {\n\t\t/**\n\t\t * @private\n\t\t * @member {Map}\n\t\t */\n\t\tthis._dispatchersGroups = new Map();\n\t}\n\n\t/**\n\t * Registers one or more converters under a given group name. The group name can then be used to assign a converter\n\t * to multiple dispatchers at once.\n\t *\n\t * If a given group name is used for the second time, the\n\t * {@link module:utils/ckeditorerror~CKEditorError `conversion-register-group-exists` error} is thrown.\n\t *\n\t * @param {Object} options\n\t * @param {String} options.name The name for dispatchers group.\n\t * @param {module:engine/conversion/downcastdispatcher~DowncastDispatcher|\n\t * module:engine/conversion/upcastdispatcher~UpcastDispatcher|Array.} options.dispatcher Dispatcher or array of dispatchers to register\n\t * under the given name.\n\t * @param {module:engine/conversion/downcasthelpers~DowncastHelpers|\n\t * module:engine/conversion/upcasthelpers~UpcastHelpers} helpers\n\t */\n\tregister( name, group ) {\n\t\tif ( this._dispatchersGroups.has( name ) ) {\n\t\t\t/**\n\t\t\t * Trying to register a group name that was already registered.\n\t\t\t *\n\t\t\t * @error conversion-register-group-exists\n\t\t\t */\n\t\t\tthrow new CKEditorError( 'conversion-register-group-exists: Trying to register a group name that was already registered.' );\n\t\t}\n\n\t\tthis._dispatchersGroups.set( name, group );\n\t}\n\n\t/**\n\t * Provides chainable API to assign converters to dispatchers registered under a given group name. Converters are added\n\t * by calling the {@link module:engine/conversion/conversion~ConversionHelpers#add `.add()`} method of an\n\t * {@link module:engine/conversion/conversion~ConversionHelpers conversion helpers} returned by this function.\n\t *\n\t *\t\teditor.conversion.for( 'downcast' )\n\t *\t\t\t.add( conversionHelperA ) // Adds a custom converter A.\n\t *\t\t\t.add( conversionHelperB ) // Adds a custom converter B.\n\t *\t\t\t.elementToElement( config ); // Adds a custom element-to-element downcast converter.\n\t *\n\t * In this example `conversionHelperA` and `conversionHelperB` will be called for all dispatchers from the `'model'` group.\n\t *\n\t * The `.add()` method takes exactly one parameter, which is a function. This function should accept one parameter that\n\t * is a dispatcher instance. The function should add an actual converter to the passed dispatcher instance.\n\t *\n\t * Conversion helpers for most common cases are already provided. They are flexible enough to cover most use cases.\n\t * See the documentation to learn how they can be configured.\n\t *\n\t * For downcast (model-to-view conversion), these are:\n\t *\n\t * * {@link module:engine/conversion/downcasthelpers~DowncastHelpers#elementToElement Downcast element-to-element converter},\n\t * * {@link module:engine/conversion/downcasthelpers~DowncastHelpers#attributeToElement Downcast attribute-to-element converter},\n\t * * {@link module:engine/conversion/downcasthelpers~DowncastHelpers#attributeToAttribute Downcast attribute-to-attribute converter}.\n\t * * {@link module:engine/conversion/downcasthelpers~DowncastHelpers#markerToElement Downcast marker-to-element converter}.\n\t * * {@link module:engine/conversion/downcasthelpers~DowncastHelpers#markerToHighlight Downcast marker-to-highlight converter}.\n\t *\n\t * For upcast (view-to-model conversion), these are:\n\t *\n\t * * {@link module:engine/conversion/upcasthelpers~UpcastHelpers#elementToElement Upcast element-to-element converter},\n\t * * {@link module:engine/conversion/upcasthelpers~UpcastHelpers#elementToAttribute Upcast attribute-to-element converter},\n\t * * {@link module:engine/conversion/upcasthelpers~UpcastHelpers#attributeToAttribute Upcast attribute-to-attribute converter}.\n\t * * {@link module:engine/conversion/upcasthelpers~UpcastHelpers#elementToMarker Upcast element-to-marker converter}.\n\t *\n\t * An example of using conversion helpers to convert the `paragraph` model element to the `p` view element (and back):\n\t *\n\t *\t\t// Define conversion configuration - model element 'paragraph' should be converted to view element 'p'.\n\t *\t\tconst config = { model: 'paragraph', view: 'p' };\n\t *\n\t *\t\t// Add converters to proper dispatchers using conversion helpers.\n\t *\t\teditor.conversion.for( 'downcast' ).elementToElement( config ) );\n\t *\t\teditor.conversion.for( 'upcast' ).elementToElement( config ) );\n\t *\n\t * @param {String} groupName The name of dispatchers group to add the converters to.\n\t * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers|module:engine/conversion/upcasthelpers~UpcastHelpers}\n\t */\n\tfor( groupName ) {\n\t\tconst group = this._getDispatchersGroup( groupName );\n\n\t\treturn group;\n\t}\n\n\t/**\n\t * Sets up converters between the model and the view that convert a model element to a view element (and vice versa).\n\t * For example, the model `Foo` is `
Foo
` in the view.\n\t *\n\t *\t\t// A simple conversion from the `paragraph` model element to the `
` view element (and vice versa).\n\t *\t\tconversion.elementToElement( { model: 'paragraph', view: 'p' } );\n\t *\n\t *\t\t// Override other converters by specifying a converter definition with a higher priority.\n\t *\t\tconversion.elementToElement( { model: 'paragraph', view: 'div', converterPriority: 'high' } );\n\t *\n\t *\t\t// View specified as an object instead of a string.\n\t *\t\tconversion.elementToElement( {\n\t *\t\t\tmodel: 'fancyParagraph',\n\t *\t\t\tview: {\n\t *\t\t\t\tname: 'p',\n\t *\t\t\t\tclasses: 'fancy'\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t *\t\t// Use `upcastAlso` to define other view elements that should also be converted to a `paragraph` element.\n\t *\t\tconversion.elementToElement( {\n\t *\t\t\tmodel: 'paragraph',\n\t *\t\t\tview: 'p',\n\t *\t\t\tupcastAlso: [\n\t *\t\t\t\t'div',\n\t *\t\t\t\t{\n\t *\t\t\t\t\t// Any element with the `display: block` style.\n\t *\t\t\t\t\tstyles: {\n\t *\t\t\t\t\t\tdisplay: 'block'\n\t *\t\t\t\t\t}\n\t *\t\t\t\t}\n\t *\t\t\t]\n\t *\t\t} );\n\t *\n\t *\t\t// `upcastAlso` set as callback enables a conversion of a wide range of different view elements.\n\t *\t\tconversion.elementToElement( {\n\t *\t\t\tmodel: 'heading',\n\t *\t\t\tview: 'h2',\n\t *\t\t\t// Convert \"headling-like\" paragraphs to headings.\n\t *\t\t\tupcastAlso: viewElement => {\n\t *\t\t\t\tconst fontSize = viewElement.getStyle( 'font-size' );\n\t *\n\t *\t\t\t\tif ( !fontSize ) {\n\t *\t\t\t\t\treturn null;\n\t *\t\t\t\t}\n\t *\n\t *\t\t\t\tconst match = fontSize.match( /(\\d+)\\s*px/ );\n\t *\n\t *\t\t\t\tif ( !match ) {\n\t *\t\t\t\t\treturn null;\n\t *\t\t\t\t}\n\t *\n\t *\t\t\t\tconst size = Number( match[ 1 ] );\n\t *\n\t *\t\t\t\tif ( size > 26 ) {\n\t *\t\t\t\t\t// Returned value can be an object with the matched properties.\n\t *\t\t\t\t\t// These properties will be \"consumed\" during the conversion.\n\t *\t\t\t\t\t// See `engine.view.Matcher~MatcherPattern` and `engine.view.Matcher#match` for more details.\n\t *\n\t *\t\t\t\t\treturn { name: true, styles: [ 'font-size' ] };\n\t *\t\t\t\t}\n\t *\n\t *\t\t\t\treturn null;\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t * `definition.model` is a `String` with a model element name to convert from or to.\n\t * See {@link module:engine/conversion/conversion~ConverterDefinition} to learn about other parameters.\n\t *\n\t * @param {module:engine/conversion/conversion~ConverterDefinition} definition The converter definition.\n\t */\n\telementToElement( definition ) {\n\t\t// Set up downcast converter.\n\t\tthis.for( 'downcast' ).elementToElement( definition );\n\n\t\t// Set up upcast converter.\n\t\tfor ( const { model, view } of _getAllUpcastDefinitions( definition ) ) {\n\t\t\tthis.for( 'upcast' )\n\t\t\t\t.elementToElement( {\n\t\t\t\t\tmodel,\n\t\t\t\t\tview,\n\t\t\t\t\tconverterPriority: definition.converterPriority\n\t\t\t\t} );\n\t\t}\n\t}\n\n\t/**\n\t * Sets up converters between the model and the view that convert a model attribute to a view element (and vice versa).\n\t * For example, a model text node with `\"Foo\"` as data and the `bold` attribute is `Foo` in the view.\n\t *\n\t *\t\t// A simple conversion from the `bold=true` attribute to the `` view element (and vice versa).\n\t *\t\tconversion.attributeToElement( { model: 'bold', view: 'strong' } );\n\t *\n\t *\t\t// Override other converters by specifying a converter definition with a higher priority.\n\t *\t\tconversion.attributeToElement( { model: 'bold', view: 'b', converterPriority: 'high' } );\n\t *\n\t *\t\t// View specified as an object instead of a string.\n\t *\t\tconversion.attributeToElement( {\n\t *\t\t\tmodel: 'bold',\n\t *\t\t\tview: {\n\t *\t\t\t\tname: 'span',\n\t *\t\t\t\tclasses: 'bold'\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t *\t\t// Use `config.model.name` to define the conversion only from a given node type, `$text` in this case.\n\t *\t\t// The same attribute on different elements may then be handled by a different converter.\n\t *\t\tconversion.attributeToElement( {\n\t *\t\t\tmodel: {\n\t *\t\t\t\tkey: 'textDecoration',\n\t *\t\t\t\tvalues: [ 'underline', 'lineThrough' ],\n\t *\t\t\t\tname: '$text'\n\t *\t\t\t},\n\t *\t\t\tview: {\n\t *\t\t\t\tunderline: {\n\t *\t\t\t\t\tname: 'span',\n\t *\t\t\t\t\tstyles: {\n\t *\t\t\t\t\t\t'text-decoration': 'underline'\n\t *\t\t\t\t\t}\n\t *\t\t\t\t},\n\t *\t\t\t\tlineThrough: {\n\t *\t\t\t\t\tname: 'span',\n\t *\t\t\t\t\tstyles: {\n\t *\t\t\t\t\t\t'text-decoration': 'line-through'\n\t *\t\t\t\t\t}\n\t *\t\t\t\t}\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t *\t\t// Use `upcastAlso` to define other view elements that should also be converted to the `bold` attribute.\n\t *\t\tconversion.attributeToElement( {\n\t *\t\t\tmodel: 'bold',\n\t *\t\t\tview: 'strong',\n\t *\t\t\tupcastAlso: [\n\t *\t\t\t\t'b',\n\t *\t\t\t\t{\n\t *\t\t\t\t\tname: 'span',\n\t *\t\t\t\t\tclasses: 'bold'\n\t *\t\t\t\t},\n\t *\t\t\t\t{\n\t *\t\t\t\t\tname: 'span',\n\t *\t\t\t\t\tstyles: {\n\t *\t\t\t\t\t\t'font-weight': 'bold'\n\t *\t\t\t\t\t}\n\t *\t\t\t\t},\n\t *\t\t\t\tviewElement => {\n\t *\t\t\t\t\tconst fontWeight = viewElement.getStyle( 'font-weight' );\n\t *\n\t *\t\t\t\t\tif ( viewElement.is( 'span' ) && fontWeight && /\\d+/.test() && Number( fontWeight ) > 500 ) {\n\t *\t\t\t\t\t\t// Returned value can be an object with the matched properties.\n\t *\t\t\t\t\t\t// These properties will be \"consumed\" during the conversion.\n\t *\t\t\t\t\t\t// See `engine.view.Matcher~MatcherPattern` and `engine.view.Matcher#match` for more details.\n\t *\n\t *\t\t\t\t\t\treturn {\n\t *\t\t\t\t\t\t\tname: true,\n\t *\t\t\t\t\t\t\tstyles: [ 'font-weight' ]\n\t *\t\t\t\t\t\t};\n\t *\t\t\t\t\t}\n\t *\t\t\t\t}\n\t *\t\t\t]\n\t *\t\t} );\n\t *\n\t *\t\t// Conversion from and to a model attribute key whose value is an enum (`fontSize=big|small`).\n\t *\t\t// `upcastAlso` set as callback enables a conversion of a wide range of different view elements.\n\t *\t\tconversion.attributeToElement( {\n\t *\t\t\tmodel: {\n\t *\t\t\t\tkey: 'fontSize',\n\t *\t\t\t\tvalues: [ 'big', 'small' ]\n\t *\t\t\t},\n\t *\t\t\tview: {\n\t *\t\t\t\tbig: {\n\t *\t\t\t\t\tname: 'span',\n\t *\t\t\t\t\tstyles: {\n\t *\t\t\t\t\t\t'font-size': '1.2em'\n\t *\t\t\t\t\t}\n\t *\t\t\t\t},\n\t *\t\t\t\tsmall: {\n\t *\t\t\t\t\tname: 'span',\n\t *\t\t\t\t\tstyles: {\n\t *\t\t\t\t\t\t'font-size': '0.8em'\n\t *\t\t\t\t\t}\n\t *\t\t\t\t}\n\t *\t\t\t},\n\t *\t\t\tupcastAlso: {\n\t *\t\t\t\tbig: viewElement => {\n\t *\t\t\t\t\tconst fontSize = viewElement.getStyle( 'font-size' );\n\t *\n\t *\t\t\t\t\tif ( !fontSize ) {\n\t *\t\t\t\t\t\treturn null;\n\t *\t\t\t\t\t}\n\t *\n\t *\t\t\t\t\tconst match = fontSize.match( /(\\d+)\\s*px/ );\n\t *\n\t *\t\t\t\t\tif ( !match ) {\n\t *\t\t\t\t\t\treturn null;\n\t *\t\t\t\t\t}\n\t *\n\t *\t\t\t\t\tconst size = Number( match[ 1 ] );\n\t *\n\t *\t\t\t\t\tif ( viewElement.is( 'span' ) && size > 10 ) {\n\t *\t\t\t\t\t\t// Returned value can be an object with the matched properties.\n\t *\t\t\t\t\t\t// These properties will be \"consumed\" during the conversion.\n\t *\t\t\t\t\t\t// See `engine.view.Matcher~MatcherPattern` and `engine.view.Matcher#match` for more details.\n\t *\n\t *\t\t\t\t\t\treturn { name: true, styles: [ 'font-size' ] };\n\t *\t\t\t\t\t}\n\t *\n\t *\t\t\t\t\treturn null;\n\t *\t\t\t\t},\n\t *\t\t\t\tsmall: viewElement => {\n\t *\t\t\t\t\tconst fontSize = viewElement.getStyle( 'font-size' );\n\t *\n\t *\t\t\t\t\tif ( !fontSize ) {\n\t *\t\t\t\t\t\treturn null;\n\t *\t\t\t\t\t}\n\t *\n\t *\t\t\t\t\tconst match = fontSize.match( /(\\d+)\\s*px/ );\n\t *\n\t *\t\t\t\t\tif ( !match ) {\n\t *\t\t\t\t\t\treturn null;\n\t *\t\t\t\t\t}\n\t *\n\t *\t\t\t\t\tconst size = Number( match[ 1 ] );\n\t *\n\t *\t\t\t\t\tif ( viewElement.is( 'span' ) && size < 10 ) {\n\t *\t\t\t\t\t\t// Returned value can be an object with the matched properties.\n\t *\t\t\t\t\t\t// These properties will be \"consumed\" during the conversion.\n\t *\t\t\t\t\t\t// See `engine.view.Matcher~MatcherPattern` and `engine.view.Matcher#match` for more details.\n\t *\n\t *\t\t\t\t\t\treturn { name: true, styles: [ 'font-size' ] };\n\t *\t\t\t\t\t}\n\t *\n\t *\t\t\t\t\treturn null;\n\t *\t\t\t\t}\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t * The `definition.model` parameter specifies which model attribute should be converted from or to. It can be a `{ key, value }` object\n\t * describing the attribute key and value to convert or a `String` specifying just the attribute key (then `value` is set to `true`).\n\t * See {@link module:engine/conversion/conversion~ConverterDefinition} to learn about other parameters.\n\t *\n\t * @param {module:engine/conversion/conversion~ConverterDefinition} definition The converter definition.\n\t */\n\tattributeToElement( definition ) {\n\t\t// Set up downcast converter.\n\t\tthis.for( 'downcast' ).attributeToElement( definition );\n\n\t\t// Set up upcast converter.\n\t\tfor ( const { model, view } of _getAllUpcastDefinitions( definition ) ) {\n\t\t\tthis.for( 'upcast' )\n\t\t\t\t.elementToAttribute( {\n\t\t\t\t\tview,\n\t\t\t\t\tmodel,\n\t\t\t\t\tconverterPriority: definition.priority\n\t\t\t\t} );\n\t\t}\n\t}\n\n\t/**\n\t * Sets up converters between the model and the view that convert a model attribute to a view attribute (and vice versa).\n\t * For example, `` is converted to `` (the same attribute key and value).\n\t * This type of converters is intended to be used with {@link module:engine/model/element~Element model element} nodes.\n\t * To convert text attributes {@link module:engine/conversion/conversion~Conversion#attributeToElement `attributeToElement converter`}\n\t * should be set up.\n\t *\n\t *\t\t// A simple conversion from the `source` model attribute to the `src` view attribute (and vice versa).\n\t *\t\tconversion.attributeToAttribute( { model: 'source', view: 'src' } );\n\t *\n\t *\t\t// Attribute values are strictly specified.\n\t *\t\tconversion.attributeToAttribute( {\n\t *\t\t\tmodel: {\n\t *\t\t\t\tname: 'image',\n\t *\t\t\t\tkey: 'aside',\n\t *\t\t\t\tvalues: [ 'aside' ]\n\t *\t\t\t},\n\t *\t\t\tview: {\n\t *\t\t\t\taside: {\n\t *\t\t\t\t\tname: 'img',\n\t *\t\t\t\t\tkey: 'class',\n\t *\t\t\t\t\tvalue: [ 'aside', 'half-size' ]\n\t *\t\t\t\t}\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t *\t\t// Set the style attribute.\n\t *\t\tconversion.attributeToAttribute( {\n\t *\t\t\tmodel: {\n\t *\t\t\t\tname: 'image',\n\t *\t\t\t\tkey: 'aside',\n\t *\t\t\t\tvalues: [ 'aside' ]\n\t *\t\t\t},\n\t *\t\t\tview: {\n\t *\t\t\t\taside: {\n\t *\t\t\t\t\tname: 'img',\n\t *\t\t\t\t\tkey: 'style',\n\t *\t\t\t\t\tvalue: {\n\t *\t\t\t\t\t\tfloat: 'right',\n\t *\t\t\t\t\t\twidth: '50%',\n\t *\t\t\t\t\t\tmargin: '5px'\n\t *\t\t\t\t\t}\n\t *\t\t\t\t}\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t *\t\t// Conversion from and to a model attribute key whose value is an enum (`align=right|center`).\n\t *\t\t// Use `upcastAlso` to define other view elements that should also be converted to the `align=right` attribute.\n\t *\t\tconversion.attributeToAttribute( {\n\t *\t\t\tmodel: {\n\t *\t\t\t\tkey: 'align',\n\t *\t\t\t\tvalues: [ 'right', 'center' ]\n\t *\t\t\t},\n\t *\t\t\tview: {\n\t *\t\t\t\tright: {\n\t *\t\t\t\t\tkey: 'class',\n\t *\t\t\t\t\tvalue: 'align-right'\n\t *\t\t\t\t},\n\t *\t\t\t\tcenter: {\n\t *\t\t\t\t\tkey: 'class',\n\t *\t\t\t\t\tvalue: 'align-center'\n\t *\t\t\t\t}\n\t *\t\t\t},\n\t *\t\t\tupcastAlso: {\n\t *\t\t\t\tright: {\n\t *\t\t\t\t\tstyles: {\n\t *\t\t\t\t\t\t'text-align': 'right'\n\t *\t\t\t\t\t}\n\t *\t\t\t\t},\n\t *\t\t\t\tcenter: {\n\t *\t\t\t\t\tstyles: {\n\t *\t\t\t\t\t\t'text-align': 'center'\n\t *\t\t\t\t\t}\n\t *\t\t\t\t}\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t * The `definition.model` parameter specifies which model attribute should be converted from and to.\n\t * It can be a `{ key, [ values ], [ name ] }` object or a `String`, which will be treated like `{ key: definition.model }`.\n\t * The `key` property is the model attribute key to convert from and to.\n\t * The `values` are the possible model attribute values. If `values` is not set, the model attribute value will be the same as the\n\t * view attribute value.\n\t * If `name` is set, the conversion will be set up only for model elements with the given name.\n\t *\n\t * The `definition.view` parameter specifies which view attribute should be converted from and to.\n\t * It can be a `{ key, value, [ name ] }` object or a `String`, which will be treated like `{ key: definition.view }`.\n\t * The `key` property is the view attribute key to convert from and to.\n\t * The `value` is the view attribute value to convert from and to. If `definition.value` is not set, the view attribute value will be\n\t * the same as the model attribute value.\n\t * If `key` is `'class'`, `value` can be a `String` or an array of `String`s.\n\t * If `key` is `'style'`, `value` is an object with key-value pairs.\n\t * In other cases, `value` is a `String`.\n\t * If `name` is set, the conversion will be set up only for model elements with the given name.\n\t * If `definition.model.values` is set, `definition.view` is an object that assigns values from `definition.model.values`\n\t * to `{ key, value, [ name ] }` objects.\n\t *\n\t * `definition.upcastAlso` specifies which other matching view elements should also be upcast to the given model configuration.\n\t * If `definition.model.values` is set, `definition.upcastAlso` should be an object assigning values from `definition.model.values`\n\t * to {@link module:engine/view/matcher~MatcherPattern}s or arrays of {@link module:engine/view/matcher~MatcherPattern}s.\n\t *\n\t * **Note:** `definition.model` and `definition.view` form should be mirrored, so the same types of parameters should\n\t * be given in both parameters.\n\t *\n\t * @param {Object} definition The converter definition.\n\t * @param {String|Object} definition.model The model attribute to convert from and to.\n\t * @param {String|Object} definition.view The view attribute to convert from and to.\n\t * @param {module:engine/view/matcher~MatcherPattern|Array.} [definition.upcastAlso]\n\t * Any view element matching `definition.upcastAlso` will also be converted to the given model attribute. `definition.upcastAlso`\n\t * is used only if `config.model.values` is specified.\n\t */\n\tattributeToAttribute( definition ) {\n\t\t// Set up downcast converter.\n\t\tthis.for( 'downcast' ).attributeToAttribute( definition );\n\n\t\t// Set up upcast converter.\n\t\tfor ( const { model, view } of _getAllUpcastDefinitions( definition ) ) {\n\t\t\tthis.for( 'upcast' )\n\t\t\t\t.attributeToAttribute( {\n\t\t\t\t\tview,\n\t\t\t\t\tmodel\n\t\t\t\t} );\n\t\t}\n\t}\n\n\t/**\n\t * Returns dispatchers group registered under a given group name.\n\t *\n\t * If the given group name has not been registered, the\n\t * {@link module:utils/ckeditorerror~CKEditorError `conversion-for-unknown-group` error} is thrown.\n\t *\n\t * @private\n\t * @param {String} groupName\n\t * @returns {module:engine/conversion/conversion~DispatchersGroup}\n\t */\n\t_getDispatchersGroup( groupName ) {\n\t\tif ( !this._dispatchersGroups.has( groupName ) ) {\n\t\t\t/**\n\t\t\t * Trying to add a converter to an unknown dispatchers group.\n\t\t\t *\n\t\t\t * @error conversion-for-unknown-group\n\t\t\t */\n\t\t\tthrow new CKEditorError( 'conversion-for-unknown-group: Trying to add a converter to an unknown dispatchers group.' );\n\t\t}\n\n\t\treturn this._dispatchersGroups.get( groupName );\n\t}\n}\n\n/**\n * Defines how the model should be converted from and to the view.\n *\n * @typedef {Object} module:engine/conversion/conversion~ConverterDefinition\n *\n * @property {*} [model] The model conversion definition. Describes the model element or model attribute to convert. This parameter differs\n * for different functions that accept `ConverterDefinition`. See the description of the function to learn how to set it.\n * @property {module:engine/view/elementdefinition~ElementDefinition|Object} view The definition of the view element to convert from and\n * to. If `model` describes multiple values, `view` is an object that assigns these values (`view` object keys) to view element definitions\n * (`view` object values).\n * @property {module:engine/view/matcher~MatcherPattern|Array.} [upcastAlso]\n * Any view element matching `upcastAlso` will also be converted to the model. If `model` describes multiple values, `upcastAlso`\n * is an object that assigns these values (`upcastAlso` object keys) to {@link module:engine/view/matcher~MatcherPattern}s\n * (`upcastAlso` object values).\n * @property {module:utils/priorities~PriorityString} [converterPriority] The converter priority.\n */\n\n/**\n * @typedef {Object} module:engine/conversion/conversion~DispatchersGroup\n * @property {String} name Group name\n * @property {Array.} dispatchers\n * @property {module:engine/conversion/downcasthelpers~DowncastHelpers|module:engine/conversion/upcasthelpers~UpcastHelpers} helpers\n */\n\n// Helper function that creates a joint array out of an item passed in `definition.view` and items passed in\n// `definition.upcastAlso`.\n//\n// @param {module:engine/conversion/conversion~ConverterDefinition} definition\n// @returns {Array} Array containing view definitions.\nfunction* _getAllUpcastDefinitions( definition ) {\n\tif ( definition.model.values ) {\n\t\tfor ( const value of definition.model.values ) {\n\t\t\tconst model = { key: definition.model.key, value };\n\t\t\tconst view = definition.view[ value ];\n\t\t\tconst upcastAlso = definition.upcastAlso ? definition.upcastAlso[ value ] : undefined;\n\n\t\t\tyield* _getUpcastDefinition( model, view, upcastAlso );\n\t\t}\n\t} else {\n\t\tyield* _getUpcastDefinition( definition.model, definition.view, definition.upcastAlso );\n\t}\n}\n\nfunction* _getUpcastDefinition( model, view, upcastAlso ) {\n\tyield { model, view };\n\n\tif ( upcastAlso ) {\n\t\tupcastAlso = Array.isArray( upcastAlso ) ? upcastAlso : [ upcastAlso ];\n\n\t\tfor ( const upcastAlsoItem of upcastAlso ) {\n\t\t\tyield { model, view: upcastAlsoItem };\n\t\t}\n\t}\n}\n\n/**\n * Base class for conversion helpers.\n */\nexport class ConversionHelpers {\n\t/**\n\t * Creates ConversionHelpers instance.\n\t *\n\t * @param {Array.} dispatcher\n\t */\n\tconstructor( dispatcher ) {\n\t\tthis._dispatchers = Array.isArray( dispatcher ) ? dispatcher : [ dispatcher ];\n\t}\n\n\t/**\n\t * Registers a conversion helper.\n\t *\n\t * **Note**: See full usage example in the `{@link module:engine/conversion/conversion~Conversion#for conversion.for()}`\n\t * method description\n\t *\n\t * @param {Function} conversionHelper The function to be called on event.\n\t * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers|module:engine/conversion/upcasthelpers~UpcastHelpers}\n\t */\n\tadd( conversionHelper ) {\n\t\tthis._addToDispatchers( conversionHelper );\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Helper function for the `Conversion` `.add()` method.\n\t *\n\t * Calls `conversionHelper` on each dispatcher from the group specified earlier in the `.for()` call, effectively\n\t * adding converters to all specified dispatchers.\n\t *\n\t * @private\n\t * @param {Function} conversionHelper\n\t */\n\t_addToDispatchers( conversionHelper ) {\n\t\tfor ( const dispatcher of this._dispatchers ) {\n\t\t\tconversionHelper( dispatcher );\n\t\t}\n\t}\n}\n","/**\n * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\nimport ModelRange from '../model/range';\nimport ModelSelection from '../model/selection';\nimport ModelElement from '../model/element';\n\nimport ViewAttributeElement from '../view/attributeelement';\nimport DocumentSelection from '../model/documentselection';\nimport { ConversionHelpers } from './conversion';\n\nimport log from '@ckeditor/ckeditor5-utils/src/log';\nimport { cloneDeep } from 'lodash-es';\n\n/**\n * Contains downcast (model-to-view) converters for {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher}.\n *\n * @module engine/conversion/downcasthelpers\n */\n\n/**\n * Downcast conversion helper functions.\n *\n * @extends module:engine/conversion/conversion~ConversionHelpers\n */\nexport default class DowncastHelpers extends ConversionHelpers {\n\t/**\n\t * Model element to view element conversion helper.\n\t *\n\t * This conversion results in creating a view element. For example, model `Foo` becomes `
Foo
` in the view.\n\t *\n\t *\t\teditor.conversion.for( 'downcast' ).elementToElement( {\n\t *\t\t\tmodel: 'paragraph',\n\t *\t\t\tview: 'p'\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'downcast' ).elementToElement( {\n\t *\t\t\tmodel: 'paragraph',\n\t *\t\t\tview: 'div',\n\t *\t\t\tconverterPriority: 'high'\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'downcast' ).elementToElement( {\n\t *\t\t\tmodel: 'fancyParagraph',\n\t *\t\t\tview: {\n\t *\t\t\t\tname: 'p',\n\t *\t\t\t\tclasses: 'fancy'\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'downcast' ).elementToElement( {\n\t *\t\t\tmodel: 'heading',\n\t *\t\t\tview: ( modelElement, viewWriter ) => {\n\t *\t\t\t\treturn viewWriter.createContainerElement( 'h' + modelElement.getAttribute( 'level' ) )\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t * See {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} to learn how to add a converter\n\t * to the conversion process.\n\t *\n\t * @method #elementToElement\n\t * @param {Object} config Conversion configuration.\n\t * @param {String} config.model The name of the model element to convert.\n\t * @param {module:engine/view/elementdefinition~ElementDefinition|Function} config.view A view element definition or a function\n\t * that takes the model element and {@link module:engine/view/downcastwriter~DowncastWriter view downcast writer}\n\t * as parameters and returns a view container element.\n\t * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers}\n\t */\n\telementToElement( config ) {\n\t\treturn this.add( downcastElementToElement( config ) );\n\t}\n\n\t/**\n\t * Model attribute to view element conversion helper.\n\t *\n\t * This conversion results in wrapping view nodes with a view attribute element. For example, a model text node with\n\t * `\"Foo\"` as data and the `bold` attribute becomes `Foo` in the view.\n\t *\n\t *\t\teditor.conversion.for( 'downcast' ).attributeToElement( {\n\t *\t\t\tmodel: 'bold',\n\t *\t\t\tview: 'strong'\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'downcast' ).attributeToElement( {\n\t *\t\t\tmodel: 'bold',\n\t *\t\t\tview: 'b',\n\t *\t\t\tconverterPriority: 'high'\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'downcast' ).attributeToElement( {\n\t *\t\t\tmodel: 'invert',\n\t *\t\t\tview: {\n\t *\t\t\t\tname: 'span',\n\t *\t\t\t\tclasses: [ 'font-light', 'bg-dark' ]\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'downcast' ).attributeToElement( {\n\t *\t\t\tmodel: {\n\t *\t\t\t\tkey: 'fontSize',\n\t *\t\t\t\tvalues: [ 'big', 'small' ]\n\t *\t\t\t},\n\t *\t\t\tview: {\n\t *\t\t\t\tbig: {\n\t *\t\t\t\t\tname: 'span',\n\t *\t\t\t\t\tstyles: {\n\t *\t\t\t\t\t\t'font-size': '1.2em'\n\t *\t\t\t\t\t}\n\t *\t\t\t\t},\n\t *\t\t\t\tsmall: {\n\t *\t\t\t\t\tname: 'span',\n\t *\t\t\t\t\tstyles: {\n\t *\t\t\t\t\t\t'font-size': '0.8em'\n\t *\t\t\t\t\t}\n\t *\t\t\t\t}\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'downcast' ).attributeToElement( {\n\t *\t\t\tmodel: 'bold',\n\t *\t\t\tview: ( modelAttributeValue, viewWriter ) => {\n\t *\t\t\t\treturn viewWriter.createAttributeElement( 'span', {\n\t *\t\t\t\t\tstyle: 'font-weight:' + modelAttributeValue\n\t *\t\t\t\t} );\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'downcast' ).attributeToElement( {\n\t *\t\t\tmodel: {\n\t *\t\t\t\tkey: 'color',\n\t *\t\t\t\tname: '$text'\n\t *\t\t\t},\n\t *\t\t\tview: ( modelAttributeValue, viewWriter ) => {\n\t *\t\t\t\treturn viewWriter.createAttributeElement( 'span', {\n\t *\t\t\t\t\tstyle: 'color:' + modelAttributeValue\n\t *\t\t\t\t} );\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t * See {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} to learn how to add a converter\n\t * to the conversion process.\n\t *\n\t * @method #attributeToElement\n\t * @param {Object} config Conversion configuration.\n\t * @param {String|Object} config.model The key of the attribute to convert from or a `{ key, values }` object. `values` is an array\n\t * of `String`s with possible values if the model attribute is an enumerable.\n\t * @param {module:engine/view/elementdefinition~ElementDefinition|Function|Object} config.view A view element definition or a function\n\t * that takes the model attribute value and {@link module:engine/view/downcastwriter~DowncastWriter view downcast writer}\n\t * as parameters and returns a view attribute element. If `config.model.values` is\n\t * given, `config.view` should be an object assigning values from `config.model.values` to view element definitions or functions.\n\t * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.\n\t * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers}\n\t */\n\tattributeToElement( config ) {\n\t\treturn this.add( downcastAttributeToElement( config ) );\n\t}\n\n\t/**\n\t * Model attribute to view attribute conversion helper.\n\t *\n\t * This conversion results in adding an attribute to a view node, basing on an attribute from a model node. For example,\n\t * `` is converted to ``.\n\t *\n\t *\t\teditor.conversion.for( 'downcast' ).attributeToAttribute( {\n\t *\t\t\tmodel: 'source',\n\t *\t\t\tview: 'src'\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'downcast' ).attributeToAttribute( {\n\t *\t\t\tmodel: 'source',\n\t *\t\t\tview: 'href',\n\t *\t\t\tconverterPriority: 'high'\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'downcast' ).attributeToAttribute( {\n\t *\t\t\tmodel: {\n\t *\t\t\t\tname: 'image',\n\t *\t\t\t\tkey: 'source'\n\t *\t\t\t},\n\t *\t\t\tview: 'src'\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'downcast' ).attributeToAttribute( {\n\t *\t\t\tmodel: {\n\t *\t\t\t\tname: 'styled',\n\t *\t\t\t\tvalues: [ 'dark', 'light' ]\n\t *\t\t\t},\n\t *\t\t\tview: {\n\t *\t\t\t\tdark: {\n\t *\t\t\t\t\tkey: 'class',\n\t *\t\t\t\t\tvalue: [ 'styled', 'styled-dark' ]\n\t *\t\t\t\t},\n\t *\t\t\t\tlight: {\n\t *\t\t\t\t\tkey: 'class',\n\t *\t\t\t\t\tvalue: [ 'styled', 'styled-light' ]\n\t *\t\t\t\t}\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'downcast' ).attributeToAttribute( {\n\t *\t\t\tmodel: 'styled',\n\t *\t\t\tview: modelAttributeValue => ( { key: 'class', value: 'styled-' + modelAttributeValue } )\n\t *\t\t} );\n\t *\n\t * See {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} to learn how to add a converter\n\t * to the conversion process.\n\t *\n\t * @method #attributeToAttribute\n\t * @param {Object} config Conversion configuration.\n\t * @param {String|Object} config.model The key of the attribute to convert from or a `{ key, values, [ name ] }` object describing\n\t * the attribute key, possible values and, optionally, an element name to convert from.\n\t * @param {String|Object|Function} config.view A view attribute key, or a `{ key, value }` object or a function that takes\n\t * the model attribute value and returns a `{ key, value }` object. If `key` is `'class'`, `value` can be a `String` or an\n\t * array of `String`s. If `key` is `'style'`, `value` is an object with key-value pairs. In other cases, `value` is a `String`.\n\t * If `config.model.values` is set, `config.view` should be an object assigning values from `config.model.values` to\n\t * `{ key, value }` objects or a functions.\n\t * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.\n\t * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers}\n\t */\n\tattributeToAttribute( config ) {\n\t\treturn this.add( downcastAttributeToAttribute( config ) );\n\t}\n\n\t/**\n\t * Model marker to view element conversion helper.\n\t *\n\t * This conversion results in creating a view element on the boundaries of the converted marker. If the converted marker\n\t * is collapsed, only one element is created. For example, model marker set like this: `F[oo b]ar`\n\t * becomes `
Foo bar
` in the view.\n\t *\n\t *\t\teditor.conversion.for( 'downcast' ).markerToElement( {\n\t *\t\t\tmodel: 'search',\n\t *\t\t\tview: 'marker-search'\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'downcast' ).markerToElement( {\n\t *\t\t\tmodel: 'search',\n\t *\t\t\tview: 'search-result',\n\t *\t\t\tconverterPriority: 'high'\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'downcast' ).markerToElement( {\n\t *\t\t\tmodel: 'search',\n\t *\t\t\tview: {\n\t *\t\t\t\tname: 'span',\n\t *\t\t\t\tattributes: {\n\t *\t\t\t\t\t'data-marker': 'search'\n\t *\t\t\t\t}\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'downcast' ).markerToElement( {\n\t *\t\t\tmodel: 'search',\n\t *\t\t\tview: ( markerData, viewWriter ) => {\n\t *\t\t\t\treturn viewWriter.createUIElement( 'span', {\n\t *\t\t\t\t\t'data-marker': 'search',\n\t *\t\t\t\t\t'data-start': markerData.isOpening\n\t *\t\t\t\t} );\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t * If a function is passed as the `config.view` parameter, it will be used to generate both boundary elements. The function\n\t * receives the `data` object as a parameter and should return an instance of the\n\t * {@link module:engine/view/uielement~UIElement view UI element}. The `data` and `conversionApi` objects are passed from\n\t * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:addMarker}. Additionally,\n\t * the `data.isOpening` parameter is passed, which is set to `true` for the marker start boundary element, and `false` to\n\t * the marker end boundary element.\n\t *\n\t * This kind of conversion is useful for saving data into the database, so it should be used in the data conversion pipeline.\n\t *\n\t * See {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} to learn how to add a converter\n\t * to the conversion process.\n\t *\n\t * @method #markerToElement\n\t * @param {Object} config Conversion configuration.\n\t * @param {String} config.model The name of the model marker (or model marker group) to convert.\n\t * @param {module:engine/view/elementdefinition~ElementDefinition|Function} config.view A view element definition or a function\n\t * that takes the model marker data as a parameter and returns a view UI element.\n\t * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.\n\t * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers}\n\t */\n\tmarkerToElement( config ) {\n\t\treturn this.add( downcastMarkerToElement( config ) );\n\t}\n\n\t/**\n\t * Model marker to highlight conversion helper.\n\t *\n\t * This conversion results in creating a highlight on view nodes. For this kind of conversion,\n\t * {@link module:engine/conversion/downcasthelpers~HighlightDescriptor} should be provided.\n\t *\n\t * For text nodes, a `` {@link module:engine/view/attributeelement~AttributeElement} is created and it wraps all text nodes\n\t * in the converted marker range. For example, a model marker set like this: `F[oo b]ar` becomes\n\t * `
Foo bar
` in the view.\n\t *\n\t * {@link module:engine/view/containerelement~ContainerElement} may provide a custom way of handling highlight. Most often,\n\t * the element itself is given classes and attributes described in the highlight descriptor (instead of being wrapped in ``).\n\t * For example, a model marker set like this: `[]` becomes ``\n\t * in the view.\n\t *\n\t * For container elements, the conversion is two-step. While the converter processes the highlight descriptor and passes it\n\t * to a container element, it is the container element instance itself that applies values from the highlight descriptor.\n\t * So, in a sense, the converter takes care of stating what should be applied on what, while the element decides how to apply that.\n\t *\n\t *\t\teditor.conversion.for( 'downcast' ).markerToHighlight( { model: 'comment', view: { classes: 'comment' } } );\n\t *\n\t *\t\teditor.conversion.for( 'downcast' ).markerToHighlight( {\n\t *\t\t\tmodel: 'comment',\n\t *\t\t\tview: { classes: 'new-comment' },\n\t *\t\t\tconverterPriority: 'high'\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'downcast' ).markerToHighlight( {\n\t *\t\t\tmodel: 'comment',\n\t *\t\t\tview: data => {\n\t *\t\t\t\t// Assuming that the marker name is in a form of comment:commentType.\n\t *\t\t\t\tconst commentType = data.markerName.split( ':' )[ 1 ];\n\t *\n\t *\t\t\t\treturn {\n\t *\t\t\t\t\tclasses: [ 'comment', 'comment-' + commentType ]\n\t *\t\t\t\t};\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t * If a function is passed as the `config.view` parameter, it will be used to generate the highlight descriptor. The function\n\t * receives the `data` object as a parameter and should return a\n\t * {@link module:engine/conversion/downcasthelpers~HighlightDescriptor highlight descriptor}.\n\t * The `data` object properties are passed from {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:addMarker}.\n\t *\n\t * See {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} to learn how to add a converter\n\t * to the conversion process.\n\t *\n\t * @method #markerToHighlight\n\t * @param {Object} config Conversion configuration.\n\t * @param {String} config.model The name of the model marker (or model marker group) to convert.\n\t * @param {module:engine/conversion/downcasthelpers~HighlightDescriptor|Function} config.view A highlight descriptor\n\t * that will be used for highlighting or a function that takes the model marker data as a parameter and returns a highlight descriptor.\n\t * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.\n\t * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers}\n\t */\n\tmarkerToHighlight( config ) {\n\t\treturn this.add( downcastMarkerToHighlight( config ) );\n\t}\n}\n\n/**\n * Function factory that creates a default downcast converter for text insertion changes.\n *\n * The converter automatically consumes the corresponding value from the consumables list and stops the event (see\n * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher}).\n *\n *\t\tmodelDispatcher.on( 'insert:$text', insertText() );\n *\n * @returns {Function} Insert text event converter.\n */\nexport function insertText() {\n\treturn ( evt, data, conversionApi ) => {\n\t\tif ( !conversionApi.consumable.consume( data.item, 'insert' ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst viewWriter = conversionApi.writer;\n\t\tconst viewPosition = conversionApi.mapper.toViewPosition( data.range.start );\n\t\tconst viewText = viewWriter.createText( data.item.data );\n\n\t\tviewWriter.insert( viewPosition, viewText );\n\t};\n}\n\n/**\n * Function factory that creates a default downcast converter for node remove changes.\n *\n *\t\tmodelDispatcher.on( 'remove', remove() );\n *\n * @returns {Function} Remove event converter.\n */\nexport function remove() {\n\treturn ( evt, data, conversionApi ) => {\n\t\t// Find view range start position by mapping model position at which the remove happened.\n\t\tconst viewStart = conversionApi.mapper.toViewPosition( data.position );\n\n\t\tconst modelEnd = data.position.getShiftedBy( data.length );\n\t\tconst viewEnd = conversionApi.mapper.toViewPosition( modelEnd, { isPhantom: true } );\n\n\t\tconst viewRange = conversionApi.writer.createRange( viewStart, viewEnd );\n\n\t\t// Trim the range to remove in case some UI elements are on the view range boundaries.\n\t\tconst removed = conversionApi.writer.remove( viewRange.getTrimmed() );\n\n\t\t// After the range is removed, unbind all view elements from the model.\n\t\t// Range inside view document fragment is used to unbind deeply.\n\t\tfor ( const child of conversionApi.writer.createRangeIn( removed ).getItems() ) {\n\t\t\tconversionApi.mapper.unbindViewElement( child );\n\t\t}\n\t};\n}\n\n/**\n * Creates a `` {@link module:engine/view/attributeelement~AttributeElement view attribute element} from the information\n * provided by the {@link module:engine/conversion/downcasthelpers~HighlightDescriptor highlight descriptor} object. If a priority\n * is not provided in the descriptor, the default priority will be used.\n *\n * @param {module:engine/conversion/downcasthelpers~HighlightDescriptor} descriptor\n * @returns {module:engine/view/attributeelement~AttributeElement}\n */\nexport function createViewElementFromHighlightDescriptor( descriptor ) {\n\tconst viewElement = new ViewAttributeElement( 'span', descriptor.attributes );\n\n\tif ( descriptor.classes ) {\n\t\tviewElement._addClass( descriptor.classes );\n\t}\n\n\tif ( descriptor.priority ) {\n\t\tviewElement._priority = descriptor.priority;\n\t}\n\n\tviewElement._id = descriptor.id;\n\n\treturn viewElement;\n}\n\n/**\n * Function factory that creates a converter which converts set/change/remove attribute changes from the model to the view.\n * It can also be used to convert selection attributes. In that case, an empty attribute element will be created and the\n * selection will be put inside it.\n *\n * Attributes from the model are converted to a view element that will be wrapping these view nodes that are bound to\n * model elements having the given attribute. This is useful for attributes like `bold` that may be set on text nodes in the model\n * but are represented as an element in the view:\n *\n *\t\t[paragraph] MODEL ====> VIEW
\n *\t\t\t|- a {bold: true} |- \n *\t\t\t|- b {bold: true} | |- ab\n *\t\t\t|- c |- c\n *\n * Passed `Function` will be provided with the attribute value and then all the parameters of the\n * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:attribute `attribute` event}.\n * It is expected that the function returns an {@link module:engine/view/element~Element}.\n * The result of the function will be the wrapping element.\n * When the provided `Function` does not return any element, no conversion will take place.\n *\n * The converter automatically consumes the corresponding value from the consumables list and stops the event (see\n * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher}).\n *\n *\t\tmodelDispatcher.on( 'attribute:bold', wrap( ( modelAttributeValue, viewWriter ) => {\n *\t\t\treturn viewWriter.createAttributeElement( 'strong' );\n *\t\t} );\n *\n * @protected\n * @param {Function} elementCreator Function returning a view element that will be used for wrapping.\n * @returns {Function} Set/change attribute converter.\n */\nexport function wrap( elementCreator ) {\n\treturn ( evt, data, conversionApi ) => {\n\t\t// Recreate current wrapping node. It will be used to unwrap view range if the attribute value has changed\n\t\t// or the attribute was removed.\n\t\tconst oldViewElement = elementCreator( data.attributeOldValue, conversionApi.writer );\n\n\t\t// Create node to wrap with.\n\t\tconst newViewElement = elementCreator( data.attributeNewValue, conversionApi.writer );\n\n\t\tif ( !oldViewElement && !newViewElement ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( !conversionApi.consumable.consume( data.item, evt.name ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst viewWriter = conversionApi.writer;\n\t\tconst viewSelection = viewWriter.document.selection;\n\n\t\tif ( data.item instanceof ModelSelection || data.item instanceof DocumentSelection ) {\n\t\t\t// Selection attribute conversion.\n\t\t\tviewWriter.wrap( viewSelection.getFirstRange(), newViewElement );\n\t\t} else {\n\t\t\t// Node attribute conversion.\n\t\t\tlet viewRange = conversionApi.mapper.toViewRange( data.range );\n\n\t\t\t// First, unwrap the range from current wrapper.\n\t\t\tif ( data.attributeOldValue !== null && oldViewElement ) {\n\t\t\t\tviewRange = viewWriter.unwrap( viewRange, oldViewElement );\n\t\t\t}\n\n\t\t\tif ( data.attributeNewValue !== null && newViewElement ) {\n\t\t\t\tviewWriter.wrap( viewRange, newViewElement );\n\t\t\t}\n\t\t}\n\t};\n}\n\n/**\n * Function factory that creates a converter which converts node insertion changes from the model to the view.\n * The function passed will be provided with all the parameters of the dispatcher's\n * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:insert `insert` event}.\n * It is expected that the function returns an {@link module:engine/view/element~Element}.\n * The result of the function will be inserted into the view.\n *\n * The converter automatically consumes the corresponding value from the consumables list, stops the event (see\n * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher}) and binds the model and view elements.\n *\n *\t\tdowncastDispatcher.on(\n *\t\t\t'insert:myElem',\n *\t\t\tinsertElement( ( modelItem, viewWriter ) => {\n *\t\t\t\tconst text = viewWriter.createText( 'myText' );\n *\t\t\t\tconst myElem = viewWriter.createElement( 'myElem', { myAttr: 'my-' + modelItem.getAttribute( 'myAttr' ) }, text );\n *\n *\t\t\t\t// Do something fancy with `myElem` using `modelItem` or other parameters.\n *\n *\t\t\t\treturn myElem;\n *\t\t\t}\n *\t\t) );\n *\n * @protected\n * @param {Function} elementCreator Function returning a view element, which will be inserted.\n * @returns {Function} Insert element event converter.\n */\nexport function insertElement( elementCreator ) {\n\treturn ( evt, data, conversionApi ) => {\n\t\tconst viewElement = elementCreator( data.item, conversionApi.writer );\n\n\t\tif ( !viewElement ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( !conversionApi.consumable.consume( data.item, 'insert' ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst viewPosition = conversionApi.mapper.toViewPosition( data.range.start );\n\n\t\tconversionApi.mapper.bindElements( data.item, viewElement );\n\t\tconversionApi.writer.insert( viewPosition, viewElement );\n\t};\n}\n\n/**\n * Function factory that creates a converter which converts marker adding change to the\n * {@link module:engine/view/uielement~UIElement view UI element}.\n *\n * The view UI element that will be added to the view depends on the passed parameter. See {@link ~insertElement}.\n * In case of a non-collapsed range, the UI element will not wrap nodes but separate elements will be placed at the beginning\n * and at the end of the range.\n *\n * This converter binds created UI elements with the marker name using {@link module:engine/conversion/mapper~Mapper#bindElementToMarker}.\n *\n * @protected\n * @param {module:engine/view/uielement~UIElement|Function} elementCreator A view UI element or a function returning the view element\n * that will be inserted.\n * @returns {Function} Insert element event converter.\n */\nexport function insertUIElement( elementCreator ) {\n\treturn ( evt, data, conversionApi ) => {\n\t\t// Create two view elements. One will be inserted at the beginning of marker, one at the end.\n\t\t// If marker is collapsed, only \"opening\" element will be inserted.\n\t\tdata.isOpening = true;\n\t\tconst viewStartElement = elementCreator( data, conversionApi.writer );\n\n\t\tdata.isOpening = false;\n\t\tconst viewEndElement = elementCreator( data, conversionApi.writer );\n\n\t\tif ( !viewStartElement || !viewEndElement ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst markerRange = data.markerRange;\n\n\t\t// Marker that is collapsed has consumable build differently that non-collapsed one.\n\t\t// For more information see `addMarker` event description.\n\t\t// If marker's range is collapsed - check if it can be consumed.\n\t\tif ( markerRange.isCollapsed && !conversionApi.consumable.consume( markerRange, evt.name ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// If marker's range is not collapsed - consume all items inside.\n\t\tfor ( const value of markerRange ) {\n\t\t\tif ( !conversionApi.consumable.consume( value.item, evt.name ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tconst mapper = conversionApi.mapper;\n\t\tconst viewWriter = conversionApi.writer;\n\n\t\t// Add \"opening\" element.\n\t\tviewWriter.insert( mapper.toViewPosition( markerRange.start ), viewStartElement );\n\t\tconversionApi.mapper.bindElementToMarker( viewStartElement, data.markerName );\n\n\t\t// Add \"closing\" element only if range is not collapsed.\n\t\tif ( !markerRange.isCollapsed ) {\n\t\t\tviewWriter.insert( mapper.toViewPosition( markerRange.end ), viewEndElement );\n\t\t\tconversionApi.mapper.bindElementToMarker( viewEndElement, data.markerName );\n\t\t}\n\n\t\tevt.stop();\n\t};\n}\n\n// Function factory that returns a default downcast converter for removing a {@link module:engine/view/uielement~UIElement UI element}\n// basing on marker remove change.\n//\n// This converter unbinds elements from the marker name.\n//\n// @returns {Function} Removed UI element converter.\nfunction removeUIElement() {\n\treturn ( evt, data, conversionApi ) => {\n\t\tconst elements = conversionApi.mapper.markerNameToElements( data.markerName );\n\n\t\tif ( !elements ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconversionApi.mapper.unbindElementsFromMarkerName( data.markerName );\n\n\t\tfor ( const element of elements ) {\n\t\t\tconversionApi.writer.clear( conversionApi.writer.createRangeOn( element ), element );\n\t\t}\n\n\t\tconversionApi.writer.clearClonedElementsGroup( data.markerName );\n\n\t\tevt.stop();\n\t};\n}\n\n// Function factory that creates a converter which converts set/change/remove attribute changes from the model to the view.\n//\n// Attributes from the model are converted to the view element attributes in the view. You may provide a custom function to generate\n// a key-value attribute pair to add/change/remove. If not provided, model attributes will be converted to view element\n// attributes on a one-to-one basis.\n//\n// *Note:** The provided attribute creator should always return the same `key` for a given attribute from the model.\n//\n// The converter automatically consumes the corresponding value from the consumables list and stops the event (see\n// {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher}).\n//\n//\t\tmodelDispatcher.on( 'attribute:customAttr:myElem', changeAttribute( ( value, data ) => {\n//\t\t\t// Change attribute key from `customAttr` to `class` in the view.\n//\t\t\tconst key = 'class';\n//\t\t\tlet value = data.attributeNewValue;\n//\n//\t\t\t// Force attribute value to 'empty' if the model element is empty.\n//\t\t\tif ( data.item.childCount === 0 ) {\n//\t\t\t\tvalue = 'empty';\n//\t\t\t}\n//\n//\t\t\t// Return the key-value pair.\n//\t\t\treturn { key, value };\n//\t\t} ) );\n//\n// @param {Function} [attributeCreator] Function returning an object with two properties: `key` and `value`, which\n// represent the attribute key and attribute value to be set on a {@link module:engine/view/element~Element view element}.\n// The function is passed the model attribute value as the first parameter and additional data about the change as the second parameter.\n// @returns {Function} Set/change attribute converter.\nfunction changeAttribute( attributeCreator ) {\n\treturn ( evt, data, conversionApi ) => {\n\t\tconst oldAttribute = attributeCreator( data.attributeOldValue, data );\n\t\tconst newAttribute = attributeCreator( data.attributeNewValue, data );\n\n\t\tif ( !oldAttribute && !newAttribute ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( !conversionApi.consumable.consume( data.item, evt.name ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst viewElement = conversionApi.mapper.toViewElement( data.item );\n\t\tconst viewWriter = conversionApi.writer;\n\n\t\t// If model item cannot be mapped to a view element, it means item is not an `Element` instance but a `TextProxy` node.\n\t\t// Only elements can have attributes in a view so do not proceed for anything else (#1587).\n\t\tif ( !viewElement ) {\n\t\t\t/**\n\t\t\t * This error occurs when a {@link module:engine/model/textproxy~TextProxy text node's} attribute is to be downcasted\n\t\t\t * by {@link module:engine/conversion/conversion~Conversion#attributeToAttribute `Attribute to Attribute converter`}.\n\t\t\t * In most cases it is caused by converters misconfiguration when only \"generic\" converter is defined:\n\t\t\t *\n\t\t\t *\t\teditor.conversion.for( 'downcast' ).attributeToAttribute( {\n\t\t\t *\t\t\tmodel: 'attribute-name',\n\t\t\t *\t\t\tview: 'attribute-name'\n\t\t\t *\t\t} ) );\n\t\t\t *\n\t\t\t * and given attribute is used on text node, for example:\n\t\t\t *\n\t\t\t *\t\tmodel.change( writer => {\n\t\t\t *\t\t\twriter.insertText( 'Foo', { 'attribute-name': 'bar' }, parent, 0 );\n\t\t\t *\t\t} );\n\t\t\t *\n\t\t\t * In such cases, to convert the same attribute for both {@link module:engine/model/element~Element}\n\t\t\t * and {@link module:engine/model/textproxy~TextProxy `Text`} nodes, text specific\n\t\t\t * {@link module:engine/conversion/conversion~Conversion#attributeToElement `Attribute to Element converter`}\n\t\t\t * with higher {@link module:utils/priorities~PriorityString priority} must also be defined:\n\t\t\t *\n\t\t\t *\t\teditor.conversion.for( 'downcast' ).attributeToElement( {\n\t\t\t *\t\t\tmodel: {\n\t\t\t *\t\t\t\tkey: 'attribute-name',\n\t\t\t *\t\t\t\tname: '$text'\n\t\t\t *\t\t\t},\n\t\t\t *\t\t\tview: ( value, writer ) => {\n\t\t\t *\t\t\t\treturn writer.createAttributeElement( 'span', { 'attribute-name': value } );\n\t\t\t *\t\t\t},\n\t\t\t *\t\t\tconverterPriority: 'high'\n\t\t\t *\t\t} ) );\n\t\t\t *\n\t\t\t * @error conversion-attribute-to-attribute-on-text\n\t\t\t */\n\t\t\tlog.warn( 'conversion-attribute-to-attribute-on-text: ' +\n\t\t\t\t'Trying to convert text node\\'s attribute with attribute-to-attribute converter.' );\n\n\t\t\treturn;\n\t\t}\n\n\t\t// First remove the old attribute if there was one.\n\t\tif ( data.attributeOldValue !== null && oldAttribute ) {\n\t\t\tif ( oldAttribute.key == 'class' ) {\n\t\t\t\tconst classes = Array.isArray( oldAttribute.value ) ? oldAttribute.value : [ oldAttribute.value ];\n\n\t\t\t\tfor ( const className of classes ) {\n\t\t\t\t\tviewWriter.removeClass( className, viewElement );\n\t\t\t\t}\n\t\t\t} else if ( oldAttribute.key == 'style' ) {\n\t\t\t\tconst keys = Object.keys( oldAttribute.value );\n\n\t\t\t\tfor ( const key of keys ) {\n\t\t\t\t\tviewWriter.removeStyle( key, viewElement );\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tviewWriter.removeAttribute( oldAttribute.key, viewElement );\n\t\t\t}\n\t\t}\n\n\t\t// Then set the new attribute.\n\t\tif ( data.attributeNewValue !== null && newAttribute ) {\n\t\t\tif ( newAttribute.key == 'class' ) {\n\t\t\t\tconst classes = Array.isArray( newAttribute.value ) ? newAttribute.value : [ newAttribute.value ];\n\n\t\t\t\tfor ( const className of classes ) {\n\t\t\t\t\tviewWriter.addClass( className, viewElement );\n\t\t\t\t}\n\t\t\t} else if ( newAttribute.key == 'style' ) {\n\t\t\t\tconst keys = Object.keys( newAttribute.value );\n\n\t\t\t\tfor ( const key of keys ) {\n\t\t\t\t\tviewWriter.setStyle( key, newAttribute.value[ key ], viewElement );\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tviewWriter.setAttribute( newAttribute.key, newAttribute.value, viewElement );\n\t\t\t}\n\t\t}\n\t};\n}\n\n// Function factory that creates a converter which converts the text inside marker's range. The converter wraps the text with\n// {@link module:engine/view/attributeelement~AttributeElement} created from the provided descriptor.\n// See {link module:engine/conversion/downcasthelpers~createViewElementFromHighlightDescriptor}.\n//\n// It can also be used to convert the selection that is inside a marker. In that case, an empty attribute element will be\n// created and the selection will be put inside it.\n//\n// If the highlight descriptor does not provide the `priority` property, `10` will be used.\n//\n// If the highlight descriptor does not provide the `id` property, the name of the marker will be used.\n//\n// This converter binds the created {@link module:engine/view/attributeelement~AttributeElement attribute elemens} with the marker name\n// using the {@link module:engine/conversion/mapper~Mapper#bindElementToMarker} method.\n//\n// @param {module:engine/conversion/downcasthelpers~HighlightDescriptor|Function} highlightDescriptor\n// @returns {Function}\nfunction highlightText( highlightDescriptor ) {\n\treturn ( evt, data, conversionApi ) => {\n\t\tif ( !data.item ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( !( data.item instanceof ModelSelection || data.item instanceof DocumentSelection ) && !data.item.is( 'textProxy' ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst descriptor = prepareDescriptor( highlightDescriptor, data, conversionApi );\n\n\t\tif ( !descriptor ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( !conversionApi.consumable.consume( data.item, evt.name ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst viewElement = createViewElementFromHighlightDescriptor( descriptor );\n\t\tconst viewWriter = conversionApi.writer;\n\t\tconst viewSelection = viewWriter.document.selection;\n\n\t\tif ( data.item instanceof ModelSelection || data.item instanceof DocumentSelection ) {\n\t\t\tviewWriter.wrap( viewSelection.getFirstRange(), viewElement, viewSelection );\n\t\t} else {\n\t\t\tconst viewRange = conversionApi.mapper.toViewRange( data.range );\n\t\t\tconst rangeAfterWrap = viewWriter.wrap( viewRange, viewElement );\n\n\t\t\tfor ( const element of rangeAfterWrap.getItems() ) {\n\t\t\t\tif ( element.is( 'attributeElement' ) && element.isSimilar( viewElement ) ) {\n\t\t\t\t\tconversionApi.mapper.bindElementToMarker( element, data.markerName );\n\n\t\t\t\t\t// One attribute element is enough, because all of them are bound together by the view writer.\n\t\t\t\t\t// Mapper uses this binding to get all the elements no matter how many of them are registered in the mapper.\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n}\n\n// Converter function factory. It creates a function which applies the marker's highlight to an element inside the marker's range.\n//\n// The converter checks if an element has the `addHighlight` function stored as a\n// {@link module:engine/view/element~Element#_setCustomProperty custom property} and, if so, uses it to apply the highlight.\n// In such case the converter will consume all element's children, assuming that they were handled by the element itself.\n//\n// When the `addHighlight` custom property is not present, the element is not converted in any special way.\n// This means that converters will proceed to convert the element's child nodes.\n//\n// If the highlight descriptor does not provide the `priority` property, `10` will be used.\n//\n// If the highlight descriptor does not provide the `id` property, the name of the marker will be used.\n//\n// This converter binds altered {@link module:engine/view/containerelement~ContainerElement container elements} with the marker name using\n// the {@link module:engine/conversion/mapper~Mapper#bindElementToMarker} method.\n//\n// @param {module:engine/conversion/downcasthelpers~HighlightDescriptor|Function} highlightDescriptor\n// @returns {Function}\nfunction highlightElement( highlightDescriptor ) {\n\treturn ( evt, data, conversionApi ) => {\n\t\tif ( !data.item ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( !( data.item instanceof ModelElement ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst descriptor = prepareDescriptor( highlightDescriptor, data, conversionApi );\n\n\t\tif ( !descriptor ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( !conversionApi.consumable.test( data.item, evt.name ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst viewElement = conversionApi.mapper.toViewElement( data.item );\n\n\t\tif ( viewElement && viewElement.getCustomProperty( 'addHighlight' ) ) {\n\t\t\t// Consume element itself.\n\t\t\tconversionApi.consumable.consume( data.item, evt.name );\n\n\t\t\t// Consume all children nodes.\n\t\t\tfor ( const value of ModelRange._createIn( data.item ) ) {\n\t\t\t\tconversionApi.consumable.consume( value.item, evt.name );\n\t\t\t}\n\n\t\t\tviewElement.getCustomProperty( 'addHighlight' )( viewElement, descriptor, conversionApi.writer );\n\n\t\t\tconversionApi.mapper.bindElementToMarker( viewElement, data.markerName );\n\t\t}\n\t};\n}\n\n// Function factory that creates a converter which converts the removing model marker to the view.\n//\n// Both text nodes and elements are handled by this converter but they are handled a bit differently.\n//\n// Text nodes are unwrapped using the {@link module:engine/view/attributeelement~AttributeElement attribute element} created from the\n// provided highlight descriptor. See {link module:engine/conversion/downcasthelpers~HighlightDescriptor}.\n//\n// For elements, the converter checks if an element has the `removeHighlight` function stored as a\n// {@link module:engine/view/element~Element#_setCustomProperty custom property}. If so, it uses it to remove the highlight.\n// In such case, the children of that element will not be converted.\n//\n// When `removeHighlight` is not present, the element is not converted in any special way.\n// The converter will proceed to convert the element's child nodes instead.\n//\n// If the highlight descriptor does not provide the `priority` property, `10` will be used.\n//\n// If the highlight descriptor does not provide the `id` property, the name of the marker will be used.\n//\n// This converter unbinds elements from the marker name.\n//\n// @param {module:engine/conversion/downcasthelpers~HighlightDescriptor|Function} highlightDescriptor\n// @returns {Function}\nfunction removeHighlight( highlightDescriptor ) {\n\treturn ( evt, data, conversionApi ) => {\n\t\t// This conversion makes sense only for non-collapsed range.\n\t\tif ( data.markerRange.isCollapsed ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst descriptor = prepareDescriptor( highlightDescriptor, data, conversionApi );\n\n\t\tif ( !descriptor ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// View element that will be used to unwrap `AttributeElement`s.\n\t\tconst viewHighlightElement = createViewElementFromHighlightDescriptor( descriptor );\n\n\t\t// Get all elements bound with given marker name.\n\t\tconst elements = conversionApi.mapper.markerNameToElements( data.markerName );\n\n\t\tif ( !elements ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconversionApi.mapper.unbindElementsFromMarkerName( data.markerName );\n\n\t\tfor ( const element of elements ) {\n\t\t\tif ( element.is( 'attributeElement' ) ) {\n\t\t\t\tconversionApi.writer.unwrap( conversionApi.writer.createRangeOn( element ), viewHighlightElement );\n\t\t\t} else {\n\t\t\t\t// if element.is( 'containerElement' ).\n\t\t\t\telement.getCustomProperty( 'removeHighlight' )( element, descriptor.id, conversionApi.writer );\n\t\t\t}\n\t\t}\n\n\t\tconversionApi.writer.clearClonedElementsGroup( data.markerName );\n\n\t\tevt.stop();\n\t};\n}\n\n// Model element to view element conversion helper.\n//\n// See {@link ~DowncastHelpers#elementToElement `.elementToElement()` downcast helper} for examples.\n//\n// @param {Object} config Conversion configuration.\n// @param {String} config.model The name of the model element to convert.\n// @param {module:engine/view/elementdefinition~ElementDefinition|Function} config.view A view element definition or a function\n// that takes the model element and {@link module:engine/view/downcastwriter~DowncastWriter view downcast writer}\n// as parameters and returns a view container element.\n// @returns {Function} Conversion helper.\nfunction downcastElementToElement( config ) {\n\tconfig = cloneDeep( config );\n\n\tconfig.view = normalizeToElementConfig( config.view, 'container' );\n\n\treturn dispatcher => {\n\t\tdispatcher.on( 'insert:' + config.model, insertElement( config.view ), { priority: config.converterPriority || 'normal' } );\n\t};\n}\n\n// Model attribute to view element conversion helper.\n//\n// See {@link ~DowncastHelpers#attributeToElement `.attributeToElement()` downcast helper} for examples.\n//\n// @param {Object} config Conversion configuration.\n// @param {String|Object} config.model The key of the attribute to convert from or a `{ key, values }` object. `values` is an array\n// of `String`s with possible values if the model attribute is an enumerable.\n// @param {module:engine/view/elementdefinition~ElementDefinition|Function|Object} config.view A view element definition or a function\n// that takes the model attribute value and {@link module:engine/view/downcastwriter~DowncastWriter view downcast writer}\n// as parameters and returns a view attribute element. If `config.model.values` is\n// given, `config.view` should be an object assigning values from `config.model.values` to view element definitions or functions.\n// @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.\n// @returns {Function} Conversion helper.\nfunction downcastAttributeToElement( config ) {\n\tconfig = cloneDeep( config );\n\n\tconst modelKey = config.model.key ? config.model.key : config.model;\n\tlet eventName = 'attribute:' + modelKey;\n\n\tif ( config.model.name ) {\n\t\teventName += ':' + config.model.name;\n\t}\n\n\tif ( config.model.values ) {\n\t\tfor ( const modelValue of config.model.values ) {\n\t\t\tconfig.view[ modelValue ] = normalizeToElementConfig( config.view[ modelValue ], 'attribute' );\n\t\t}\n\t} else {\n\t\tconfig.view = normalizeToElementConfig( config.view, 'attribute' );\n\t}\n\n\tconst elementCreator = getFromAttributeCreator( config );\n\n\treturn dispatcher => {\n\t\tdispatcher.on( eventName, wrap( elementCreator ), { priority: config.converterPriority || 'normal' } );\n\t};\n}\n\n// Model attribute to view attribute conversion helper.\n//\n// See {@link ~DowncastHelpers#attributeToAttribute `.attributeToAttribute()` downcast helper} for examples.\n//\n// @param {Object} config Conversion configuration.\n// @param {String|Object} config.model The key of the attribute to convert from or a `{ key, values, [ name ] }` object describing\n// the attribute key, possible values and, optionally, an element name to convert from.\n// @param {String|Object|Function} config.view A view attribute key, or a `{ key, value }` object or a function that takes\n// the model attribute value and returns a `{ key, value }` object. If `key` is `'class'`, `value` can be a `String` or an\n// array of `String`s. If `key` is `'style'`, `value` is an object with key-value pairs. In other cases, `value` is a `String`.\n// If `config.model.values` is set, `config.view` should be an object assigning values from `config.model.values` to\n// `{ key, value }` objects or a functions.\n// @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.\n// @returns {Function} Conversion helper.\nfunction downcastAttributeToAttribute( config ) {\n\tconfig = cloneDeep( config );\n\n\tconst modelKey = config.model.key ? config.model.key : config.model;\n\tlet eventName = 'attribute:' + modelKey;\n\n\tif ( config.model.name ) {\n\t\teventName += ':' + config.model.name;\n\t}\n\n\tif ( config.model.values ) {\n\t\tfor ( const modelValue of config.model.values ) {\n\t\t\tconfig.view[ modelValue ] = normalizeToAttributeConfig( config.view[ modelValue ] );\n\t\t}\n\t} else {\n\t\tconfig.view = normalizeToAttributeConfig( config.view );\n\t}\n\n\tconst elementCreator = getFromAttributeCreator( config );\n\n\treturn dispatcher => {\n\t\tdispatcher.on( eventName, changeAttribute( elementCreator ), { priority: config.converterPriority || 'normal' } );\n\t};\n}\n\n// Model marker to view element conversion helper.\n//\n// See {@link ~DowncastHelpers#markerToElement `.markerToElement()` downcast helper} for examples.\n//\n// @param {Object} config Conversion configuration.\n// @param {String} config.model The name of the model marker (or model marker group) to convert.\n// @param {module:engine/view/elementdefinition~ElementDefinition|Function} config.view A view element definition or a function\n// that takes the model marker data as a parameter and returns a view UI element.\n// @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.\n// @returns {Function} Conversion helper.\nfunction downcastMarkerToElement( config ) {\n\tconfig = cloneDeep( config );\n\n\tconfig.view = normalizeToElementConfig( config.view, 'ui' );\n\n\treturn dispatcher => {\n\t\tdispatcher.on( 'addMarker:' + config.model, insertUIElement( config.view ), { priority: config.converterPriority || 'normal' } );\n\t\tdispatcher.on( 'removeMarker:' + config.model, removeUIElement( config.view ), { priority: config.converterPriority || 'normal' } );\n\t};\n}\n\n// Model marker to highlight conversion helper.\n//\n// See {@link ~DowncastHelpers#markerToElement `.markerToElement()` downcast helper} for examples.\n//\n// @param {Object} config Conversion configuration.\n// @param {String} config.model The name of the model marker (or model marker group) to convert.\n// @param {module:engine/conversion/downcasthelpers~HighlightDescriptor|Function} config.view A highlight descriptor\n// that will be used for highlighting or a function that takes the model marker data as a parameter and returns a highlight descriptor.\n// @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.\n// @returns {Function} Conversion helper.\nfunction downcastMarkerToHighlight( config ) {\n\treturn dispatcher => {\n\t\tdispatcher.on( 'addMarker:' + config.model, highlightText( config.view ), { priority: config.converterPriority || 'normal' } );\n\t\tdispatcher.on( 'addMarker:' + config.model, highlightElement( config.view ), { priority: config.converterPriority || 'normal' } );\n\t\tdispatcher.on( 'removeMarker:' + config.model, removeHighlight( config.view ), { priority: config.converterPriority || 'normal' } );\n\t};\n}\n\n// Takes `config.view`, and if it is an {@link module:engine/view/elementdefinition~ElementDefinition}, converts it\n// to a function (because lower level converters accept only element creator functions).\n//\n// @param {module:engine/view/elementdefinition~ElementDefinition|Function} view View configuration.\n// @param {'container'|'attribute'|'ui'} viewElementType View element type to create.\n// @returns {Function} Element creator function to use in lower level converters.\nfunction normalizeToElementConfig( view, viewElementType ) {\n\tif ( typeof view == 'function' ) {\n\t\t// If `view` is already a function, don't do anything.\n\t\treturn view;\n\t}\n\n\treturn ( modelData, viewWriter ) => createViewElementFromDefinition( view, viewWriter, viewElementType );\n}\n\n// Creates a view element instance from the provided {@link module:engine/view/elementdefinition~ElementDefinition} and class.\n//\n// @param {module:engine/view/elementdefinition~ElementDefinition} viewElementDefinition\n// @param {module:engine/view/downcastwriter~DowncastWriter} viewWriter\n// @param {'container'|'attribute'|'ui'} viewElementType\n// @returns {module:engine/view/element~Element}\nfunction createViewElementFromDefinition( viewElementDefinition, viewWriter, viewElementType ) {\n\tif ( typeof viewElementDefinition == 'string' ) {\n\t\t// If `viewElementDefinition` is given as a `String`, normalize it to an object with `name` property.\n\t\tviewElementDefinition = { name: viewElementDefinition };\n\t}\n\n\tlet element;\n\tconst attributes = Object.assign( {}, viewElementDefinition.attributes );\n\n\tif ( viewElementType == 'container' ) {\n\t\telement = viewWriter.createContainerElement( viewElementDefinition.name, attributes );\n\t} else if ( viewElementType == 'attribute' ) {\n\t\tconst options = {\n\t\t\tpriority: viewElementDefinition.priority || ViewAttributeElement.DEFAULT_PRIORITY\n\t\t};\n\n\t\telement = viewWriter.createAttributeElement( viewElementDefinition.name, attributes, options );\n\t} else {\n\t\t// 'ui'.\n\t\telement = viewWriter.createUIElement( viewElementDefinition.name, attributes );\n\t}\n\n\tif ( viewElementDefinition.styles ) {\n\t\tconst keys = Object.keys( viewElementDefinition.styles );\n\n\t\tfor ( const key of keys ) {\n\t\t\tviewWriter.setStyle( key, viewElementDefinition.styles[ key ], element );\n\t\t}\n\t}\n\n\tif ( viewElementDefinition.classes ) {\n\t\tconst classes = viewElementDefinition.classes;\n\n\t\tif ( typeof classes == 'string' ) {\n\t\t\tviewWriter.addClass( classes, element );\n\t\t} else {\n\t\t\tfor ( const className of classes ) {\n\t\t\t\tviewWriter.addClass( className, element );\n\t\t\t}\n\t\t}\n\t}\n\n\treturn element;\n}\n\nfunction getFromAttributeCreator( config ) {\n\tif ( config.model.values ) {\n\t\treturn ( modelAttributeValue, viewWriter ) => {\n\t\t\tconst view = config.view[ modelAttributeValue ];\n\n\t\t\tif ( view ) {\n\t\t\t\treturn view( modelAttributeValue, viewWriter );\n\t\t\t}\n\n\t\t\treturn null;\n\t\t};\n\t} else {\n\t\treturn config.view;\n\t}\n}\n\n// Takes the configuration, adds default parameters if they do not exist and normalizes other parameters to be used in downcast converters\n// for generating a view attribute.\n//\n// @param {Object} view View configuration.\nfunction normalizeToAttributeConfig( view ) {\n\tif ( typeof view == 'string' ) {\n\t\treturn modelAttributeValue => ( { key: view, value: modelAttributeValue } );\n\t} else if ( typeof view == 'object' ) {\n\t\t// { key, value, ... }\n\t\tif ( view.value ) {\n\t\t\treturn () => view;\n\t\t}\n\t\t// { key, ... }\n\t\telse {\n\t\t\treturn modelAttributeValue => ( { key: view.key, value: modelAttributeValue } );\n\t\t}\n\t} else {\n\t\t// function.\n\t\treturn view;\n\t}\n}\n\n// Helper function for `highlight`. Prepares the actual descriptor object using value passed to the converter.\nfunction prepareDescriptor( highlightDescriptor, data, conversionApi ) {\n\t// If passed descriptor is a creator function, call it. If not, just use passed value.\n\tconst descriptor = typeof highlightDescriptor == 'function' ?\n\t\thighlightDescriptor( data, conversionApi ) :\n\t\thighlightDescriptor;\n\n\tif ( !descriptor ) {\n\t\treturn null;\n\t}\n\n\t// Apply default descriptor priority.\n\tif ( !descriptor.priority ) {\n\t\tdescriptor.priority = 10;\n\t}\n\n\t// Default descriptor id is marker name.\n\tif ( !descriptor.id ) {\n\t\tdescriptor.id = data.markerName;\n\t}\n\n\treturn descriptor;\n}\n\n/**\n * An object describing how the marker highlight should be represented in the view.\n *\n * Each text node contained in a highlighted range will be wrapped in a ``\n * {@link module:engine/view/attributeelement~AttributeElement view attribute element} with CSS class(es), attributes and a priority\n * described by this object.\n *\n * Additionally, each {@link module:engine/view/containerelement~ContainerElement container element} can handle displaying the highlight\n * separately by providing the `addHighlight` and `removeHighlight` custom properties. In this case:\n *\n * * The `HighlightDescriptor` object is passed to the `addHighlight` function upon conversion and should be used to apply the highlight to\n * the element.\n * * The descriptor `id` is passed to the `removeHighlight` function upon conversion and should be used to remove the highlight with the\n * given ID from the element.\n *\n * @typedef {Object} module:engine/conversion/downcasthelpers~HighlightDescriptor\n *\n * @property {String|Array.} classes A CSS class or an array of classes to set. If the descriptor is used to\n * create an {@link module:engine/view/attributeelement~AttributeElement attribute element} over text nodes, these classes will be set\n * on that attribute element. If the descriptor is applied to an element, usually these classes will be set on that element, however,\n * this depends on how the element converts the descriptor.\n *\n * @property {String} [id] Descriptor identifier. If not provided, it defaults to the converted marker's name.\n *\n * @property {Number} [priority] Descriptor priority. If not provided, it defaults to `10`. If the descriptor is used to create\n * an {@link module:engine/view/attributeelement~AttributeElement attribute element}, it will be that element's\n * {@link module:engine/view/attributeelement~AttributeElement#priority priority}. If the descriptor is applied to an element,\n * the priority will be used to determine which descriptor is more important.\n *\n * @property {Object} [attributes] Attributes to set. If the descriptor is used to create\n * an {@link module:engine/view/attributeelement~AttributeElement attribute element} over text nodes, these attributes will be set on that\n * attribute element. If the descriptor is applied to an element, usually these attributes will be set on that element, however,\n * this depends on how the element converts the descriptor.\n */\n","/**\n * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\n/**\n * @module engine/controller/editingcontroller\n */\n\nimport RootEditableElement from '../view/rooteditableelement';\nimport View from '../view/view';\nimport Mapper from '../conversion/mapper';\nimport DowncastDispatcher from '../conversion/downcastdispatcher';\nimport { insertText, remove } from '../conversion/downcasthelpers';\nimport { convertSelectionChange } from '../conversion/upcast-selection-converters';\nimport { clearAttributes, convertCollapsedSelection, convertRangeSelection } from '../conversion/downcast-selection-converters';\n\nimport ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';\nimport mix from '@ckeditor/ckeditor5-utils/src/mix';\n\n/**\n * Controller for the editing pipeline. The editing pipeline controls {@link ~EditingController#model model} rendering,\n * including selection handling. It also creates the {@link ~EditingController#view view} which builds a\n * browser-independent virtualization over the DOM elements. The editing controller also attaches default converters.\n *\n * @mixes module:utils/observablemixin~ObservableMixin\n */\nexport default class EditingController {\n\t/**\n\t * Creates an editing controller instance.\n\t *\n\t * @param {module:engine/model/model~Model} model Editing model.\n\t */\n\tconstructor( model ) {\n\t\t/**\n\t\t * Editor model.\n\t\t *\n\t\t * @readonly\n\t\t * @member {module:engine/model/model~Model}\n\t\t */\n\t\tthis.model = model;\n\n\t\t/**\n\t\t * Editing view controller.\n\t\t *\n\t\t * @readonly\n\t\t * @member {module:engine/view/view~View}\n\t\t */\n\t\tthis.view = new View();\n\n\t\t/**\n\t\t * Mapper which describes the model-view binding.\n\t\t *\n\t\t * @readonly\n\t\t * @member {module:engine/conversion/mapper~Mapper}\n\t\t */\n\t\tthis.mapper = new Mapper();\n\n\t\t/**\n\t\t * Downcast dispatcher that converts changes from the model to {@link #view the editing view}.\n\t\t *\n\t\t * @readonly\n\t\t * @member {module:engine/conversion/downcastdispatcher~DowncastDispatcher} #downcastDispatcher\n\t\t */\n\t\tthis.downcastDispatcher = new DowncastDispatcher( {\n\t\t\tmapper: this.mapper\n\t\t} );\n\n\t\tconst doc = this.model.document;\n\t\tconst selection = doc.selection;\n\t\tconst markers = this.model.markers;\n\n\t\t// When plugins listen on model changes (on selection change, post fixers, etc) and change the view as a result of\n\t\t// model's change, they might trigger view rendering before the conversion is completed (e.g. before the selection\n\t\t// is converted). We disable rendering for the length of the outermost model change() block to prevent that.\n\t\t//\n\t\t// See https://github.com/ckeditor/ckeditor5-engine/issues/1528\n\t\tthis.listenTo( this.model, '_beforeChanges', () => {\n\t\t\tthis.view._renderingDisabled = true;\n\t\t}, { priority: 'highest' } );\n\n\t\tthis.listenTo( this.model, '_afterChanges', () => {\n\t\t\tthis.view._renderingDisabled = false;\n\t\t\tthis.view.render();\n\t\t}, { priority: 'lowest' } );\n\n\t\t// Whenever model document is changed, convert those changes to the view (using model.Document#differ).\n\t\t// Do it on 'low' priority, so changes are converted after other listeners did their job.\n\t\t// Also convert model selection.\n\t\tthis.listenTo( doc, 'change', () => {\n\t\t\tthis.view.change( writer => {\n\t\t\t\tthis.downcastDispatcher.convertChanges( doc.differ, writer );\n\t\t\t\tthis.downcastDispatcher.convertSelection( selection, markers, writer );\n\t\t\t} );\n\t\t}, { priority: 'low' } );\n\n\t\t// Convert selection from the view to the model when it changes in the view.\n\t\tthis.listenTo( this.view.document, 'selectionChange', convertSelectionChange( this.model, this.mapper ) );\n\n\t\t// Attach default model converters.\n\t\tthis.downcastDispatcher.on( 'insert:$text', insertText(), { priority: 'lowest' } );\n\t\tthis.downcastDispatcher.on( 'remove', remove(), { priority: 'low' } );\n\n\t\t// Attach default model selection converters.\n\t\tthis.downcastDispatcher.on( 'selection', clearAttributes(), { priority: 'low' } );\n\t\tthis.downcastDispatcher.on( 'selection', convertRangeSelection(), { priority: 'low' } );\n\t\tthis.downcastDispatcher.on( 'selection', convertCollapsedSelection(), { priority: 'low' } );\n\n\t\t// Binds {@link module:engine/view/document~Document#roots view roots collection} to\n\t\t// {@link module:engine/model/document~Document#roots model roots collection} so creating\n\t\t// model root automatically creates corresponding view root.\n\t\tthis.view.document.roots.bindTo( this.model.document.roots ).using( root => {\n\t\t\t// $graveyard is a special root that has no reflection in the view.\n\t\t\tif ( root.rootName == '$graveyard' ) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst viewRoot = new RootEditableElement( root.name );\n\n\t\t\tviewRoot.rootName = root.rootName;\n\t\t\tviewRoot._document = this.view.document;\n\t\t\tthis.mapper.bindElements( root, viewRoot );\n\n\t\t\treturn viewRoot;\n\t\t} );\n\t}\n\n\t/**\n\t * Removes all event listeners attached to the `EditingController`. Destroys all objects created\n\t * by `EditingController` that need to be destroyed.\n\t */\n\tdestroy() {\n\t\tthis.view.destroy();\n\t\tthis.stopListening();\n\t}\n}\n\nmix( EditingController, ObservableMixin );\n","/**\n * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\n/**\n * Contains {@link module:engine/view/documentselection~DocumentSelection view selection}\n * to {@link module:engine/model/selection~Selection model selection} conversion helpers.\n *\n * @module engine/conversion/upcast-selection-converters\n */\n\nimport ModelSelection from '../model/selection';\n\n/**\n * Function factory, creates a callback function which converts a {@link module:engine/view/selection~Selection\n * view selection} taken from the {@link module:engine/view/document~Document#event:selectionChange} event\n * and sets in on the {@link module:engine/model/document~Document#selection model}.\n *\n * **Note**: because there is no view selection change dispatcher nor any other advanced view selection to model\n * conversion mechanism, the callback should be set directly on view document.\n *\n *\t\tview.document.on( 'selectionChange', convertSelectionChange( modelDocument, mapper ) );\n *\n * @param {module:engine/model/model~Model} model Data model.\n * @param {module:engine/conversion/mapper~Mapper} mapper Conversion mapper.\n * @returns {Function} {@link module:engine/view/document~Document#event:selectionChange} callback function.\n */\nexport function convertSelectionChange( model, mapper ) {\n\treturn ( evt, data ) => {\n\t\tconst viewSelection = data.newSelection;\n\t\tconst modelSelection = new ModelSelection();\n\n\t\tconst ranges = [];\n\n\t\tfor ( const viewRange of viewSelection.getRanges() ) {\n\t\t\tranges.push( mapper.toModelRange( viewRange ) );\n\t\t}\n\n\t\tmodelSelection.setTo( ranges, { backward: viewSelection.isBackward } );\n\n\t\tif ( !modelSelection.isEqual( model.document.selection ) ) {\n\t\t\tmodel.change( writer => {\n\t\t\t\twriter.setSelection( modelSelection );\n\t\t\t} );\n\t\t}\n\t};\n}\n","/**\n * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\n/**\n * Contains {@link module:engine/model/selection~Selection model selection} to\n * {@link module:engine/view/documentselection~DocumentSelection view selection} converters for\n * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher downcast dispatcher}.\n *\n * @module engine/conversion/downcast-selection-converters\n */\n\n/**\n * Function factory that creates a converter which converts a non-collapsed {@link module:engine/model/selection~Selection model selection}\n * to a {@link module:engine/view/documentselection~DocumentSelection view selection}. The converter consumes appropriate\n * value from the `consumable` object and maps model positions from the selection to view positions.\n *\n *\t\tmodelDispatcher.on( 'selection', convertRangeSelection() );\n *\n * @returns {Function} Selection converter.\n */\nexport function convertRangeSelection() {\n\treturn ( evt, data, conversionApi ) => {\n\t\tconst selection = data.selection;\n\n\t\tif ( selection.isCollapsed ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( !conversionApi.consumable.consume( selection, 'selection' ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst viewRanges = [];\n\n\t\tfor ( const range of selection.getRanges() ) {\n\t\t\tconst viewRange = conversionApi.mapper.toViewRange( range );\n\t\t\tviewRanges.push( viewRange );\n\t\t}\n\n\t\tconversionApi.writer.setSelection( viewRanges, { backward: selection.isBackward } );\n\t};\n}\n\n/**\n * Function factory that creates a converter which converts a collapsed {@link module:engine/model/selection~Selection model selection} to\n * a {@link module:engine/view/documentselection~DocumentSelection view selection}. The converter consumes appropriate\n * value from the `consumable` object, maps the model selection position to the view position and breaks\n * {@link module:engine/view/attributeelement~AttributeElement attribute elements} at the selection position.\n *\n *\t\tmodelDispatcher.on( 'selection', convertCollapsedSelection() );\n *\n * An example of the view state before and after converting the collapsed selection:\n *\n *\t\t
f^oobar
\n *\t\t->
f^oobar
\n *\n * By breaking attribute elements like ``, the selection is in a correct element. Then, when the selection attribute is\n * converted, broken attributes might be merged again, or the position where the selection is may be wrapped\n * with different, appropriate attribute elements.\n *\n * See also {@link module:engine/conversion/downcast-selection-converters~clearAttributes} which does a clean-up\n * by merging attributes.\n *\n * @returns {Function} Selection converter.\n */\nexport function convertCollapsedSelection() {\n\treturn ( evt, data, conversionApi ) => {\n\t\tconst selection = data.selection;\n\n\t\tif ( !selection.isCollapsed ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( !conversionApi.consumable.consume( selection, 'selection' ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst viewWriter = conversionApi.writer;\n\t\tconst modelPosition = selection.getFirstPosition();\n\t\tconst viewPosition = conversionApi.mapper.toViewPosition( modelPosition );\n\t\tconst brokenPosition = viewWriter.breakAttributes( viewPosition );\n\n\t\tviewWriter.setSelection( brokenPosition );\n\t};\n}\n\n/**\n * Function factory that creates a converter which clears artifacts after the previous\n * {@link module:engine/model/selection~Selection model selection} conversion. It removes all empty\n * {@link module:engine/view/attributeelement~AttributeElement view attribute elements} and merges sibling attributes at all start and end\n * positions of all ranges.\n *\n *\t\t
^
\n *\t\t->
^
\n *\n *\t\t
foo^barbar
\n *\t\t->
foo^barbar
\n *\n *\t\t
foo^barbar
\n *\t\t->
foo^barbar
\n *\n * This listener should be assigned before any converter for the new selection:\n *\n *\t\tmodelDispatcher.on( 'selection', clearAttributes() );\n *\n * See {@link module:engine/conversion/downcast-selection-converters~convertCollapsedSelection}\n * which does the opposite by breaking attributes in the selection position.\n *\n * @returns {Function} Selection converter.\n */\nexport function clearAttributes() {\n\treturn ( evt, data, conversionApi ) => {\n\t\tconst viewWriter = conversionApi.writer;\n\t\tconst viewSelection = viewWriter.document.selection;\n\n\t\tfor ( const range of viewSelection.getRanges() ) {\n\t\t\t// Not collapsed selection should not have artifacts.\n\t\t\tif ( range.isCollapsed ) {\n\t\t\t\t// Position might be in the node removed by the view writer.\n\t\t\t\tif ( range.end.parent.document ) {\n\t\t\t\t\tconversionApi.writer.mergeAttributes( range.start );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tviewWriter.setSelection( null );\n\t};\n}\n","/**\n * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\n/**\n * @module core/plugincollection\n */\n\nimport CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';\nimport log from '@ckeditor/ckeditor5-utils/src/log';\n\n/**\n * Manages a list of CKEditor plugins, including loading, resolving dependencies and initialization.\n */\nexport default class PluginCollection {\n\t/**\n\t * Creates an instance of the PluginCollection class.\n\t * Allows loading and initializing plugins and their dependencies.\n\t *\n\t * @param {module:core/editor/editor~Editor} editor\n\t * @param {Array.} [availablePlugins] Plugins (constructors) which the collection will be able to use\n\t * when {@link module:core/plugincollection~PluginCollection#load} is used with plugin names (strings, instead of constructors).\n\t * Usually, the editor will pass its built-in plugins to the collection so they can later be\n\t * used in `config.plugins` or `config.removePlugins` by names.\n\t */\n\tconstructor( editor, availablePlugins = [] ) {\n\t\t/**\n\t\t * @protected\n\t\t * @member {module:core/editor/editor~Editor} module:core/plugin~PluginCollection#_editor\n\t\t */\n\t\tthis._editor = editor;\n\n\t\t/**\n\t\t * Map of plugin constructors which can be retrieved by their names.\n\t\t *\n\t\t * @protected\n\t\t * @member {Map.} module:core/plugin~PluginCollection#_availablePlugins\n\t\t */\n\t\tthis._availablePlugins = new Map();\n\n\t\t/**\n\t\t * @protected\n\t\t * @member {Map} module:core/plugin~PluginCollection#_plugins\n\t\t */\n\t\tthis._plugins = new Map();\n\n\t\tfor ( const PluginConstructor of availablePlugins ) {\n\t\t\tthis._availablePlugins.set( PluginConstructor, PluginConstructor );\n\n\t\t\tif ( PluginConstructor.pluginName ) {\n\t\t\t\tthis._availablePlugins.set( PluginConstructor.pluginName, PluginConstructor );\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Iterable interface.\n\t *\n\t * Returns `[ PluginConstructor, pluginInstance ]` pairs.\n\t *\n\t * @returns {Iterable.}\n\t */\n\t* [ Symbol.iterator ]() {\n\t\tfor ( const entry of this._plugins ) {\n\t\t\tif ( typeof entry[ 0 ] == 'function' ) {\n\t\t\t\tyield entry;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Gets the plugin instance by its constructor or name.\n\t *\n\t *\t\t// Check if 'Clipboard' plugin was loaded.\n\t *\t\tif ( editor.plugins.has( 'Clipboard' ) ) {\n\t *\t\t\t// Get clipboard plugin instance\n\t *\t\t\tconst clipboard = editor.plugins.get( 'Clipboard' );\n\t *\n\t *\t\t\tthis.listenTo( clipboard, 'inputTransformation', ( evt, data ) => {\n\t *\t\t\t\t// Do something on clipboard input.\n\t *\t\t\t} );\n\t *\t\t}\n\t *\n\t * **Note**: This method will throw error if plugin is not loaded. Use `{@link #has editor.plugins.has()}`\n\t * to check if plugin is available.\n\t *\n\t * @param {Function|String} key The plugin constructor or {@link module:core/plugin~PluginInterface.pluginName name}.\n\t * @returns {module:core/plugin~PluginInterface}\n\t */\n\tget( key ) {\n\t\tconst plugin = this._plugins.get( key );\n\n\t\tif ( !plugin ) {\n\t\t\t/**\n\t\t\t * The plugin is not loaded and could not be obtained.\n\t\t\t *\n\t\t\t * Plugin classes (constructors) need to be provided to the editor and must be loaded before they can be obtained from\n\t\t\t * the plugin collection.\n\t\t\t * This is usually done in CKEditor 5 builds by setting the {@link module:core/editor/editor~Editor.builtinPlugins}\n\t\t\t * property.\n\t\t\t *\n\t\t\t * **Note**: You can use `{@link module:core/plugincollection~PluginCollection#has editor.plugins.has()}`\n\t\t\t * to check if plugin was loaded.\n\t\t\t *\n\t\t\t * @error plugincollection-plugin-not-loaded\n\t\t\t * @param {String} plugin The name of the plugin which is not loaded.\n\t\t\t */\n\t\t\tconst errorMsg = 'plugincollection-plugin-not-loaded: The requested plugin is not loaded.';\n\n\t\t\tlet pluginName = key;\n\n\t\t\tif ( typeof key == 'function' ) {\n\t\t\t\tpluginName = key.pluginName || key.name;\n\t\t\t}\n\n\t\t\tthrow new CKEditorError( errorMsg, { plugin: pluginName } );\n\t\t}\n\n\t\treturn plugin;\n\t}\n\n\t/**\n\t * Checks if plugin is loaded.\n\t *\n\t *\t\t// Check if 'Clipboard' plugin was loaded.\n\t *\t\tif ( editor.plugins.has( 'Clipboard' ) ) {\n\t *\t\t\t// Now use clipboard plugin instance:\n\t *\t\t\tconst clipboard = editor.plugins.get( 'Clipboard' );\n\t *\n\t *\t\t\t// ...\n\t *\t\t}\n\t *\n\t * @param {Function|String} key The plugin constructor or {@link module:core/plugin~PluginInterface.pluginName name}.\n\t * @returns {Boolean}\n\t */\n\thas( key ) {\n\t\treturn this._plugins.has( key );\n\t}\n\n\t/**\n\t * Loads a set of plugins and adds them to the collection.\n\t *\n\t * @param {Array.} plugins An array of {@link module:core/plugin~PluginInterface plugin constructors}\n\t * or {@link module:core/plugin~PluginInterface.pluginName plugin names}. The second option (names) works only if\n\t * `availablePlugins` were passed to the {@link #constructor}.\n\t * @param {Array.} [removePlugins] Names of plugins or plugin constructors\n\t * that should not be loaded (despite being specified in the `plugins` array).\n\t * @returns {Promise} A promise which gets resolved once all plugins are loaded and available in the\n\t * collection.\n\t * @returns {Promise.>} returns.loadedPlugins The array of loaded plugins.\n\t */\n\tload( plugins, removePlugins = [] ) {\n\t\tconst that = this;\n\t\tconst editor = this._editor;\n\t\tconst loading = new Set();\n\t\tconst loaded = [];\n\n\t\tconst pluginConstructors = mapToAvailableConstructors( plugins );\n\t\tconst removePluginConstructors = mapToAvailableConstructors( removePlugins );\n\t\tconst missingPlugins = getMissingPluginNames( plugins );\n\n\t\tif ( missingPlugins ) {\n\t\t\t/**\n\t\t\t * Some plugins are not available and could not be loaded.\n\t\t\t *\n\t\t\t * Plugin classes (constructors) need to be provided to the editor before they can be loaded by name.\n\t\t\t * This is usually done in CKEditor 5 builds by setting the {@link module:core/editor/editor~Editor.builtinPlugins}\n\t\t\t * property.\n\t\t\t *\n\t\t\t * **If you see this warning when using one of the {@glink builds/index CKEditor 5 Builds}**, it means\n\t\t\t * that you try to enable a plugin which was not included in that build. This may be due to a typo\n\t\t\t * in the plugin name or simply because that plugin is not a part of this build. In the latter scenario,\n\t\t\t * read more about {@glink builds/guides/development/custom-builds custom builds}.\n\t\t\t *\n\t\t\t * **If you see this warning when using one of the editor creators directly** (not a build), then it means\n\t\t\t * that you tried loading plugins by name. However, unlike CKEditor 4, CKEditor 5 does not implement a \"plugin loader\".\n\t\t\t * This means that CKEditor 5 does not know where to load the plugin modules from. Therefore, you need to\n\t\t\t * provide each plugin through reference (as a constructor function). Check out the examples in\n\t\t\t * {@glink builds/guides/integration/advanced-setup#scenario-2-building-from-source \"Building from source\"}.\n\t\t\t *\n\t\t\t * @error plugincollection-plugin-not-found\n\t\t\t * @param {Array.} plugins The name of the plugins which could not be loaded.\n\t\t\t */\n\t\t\tconst errorMsg = 'plugincollection-plugin-not-found: Some plugins are not available and could not be loaded.';\n\n\t\t\t// Log the error so it's more visible on the console. Hopefully, for better DX.\n\t\t\tlog.error( errorMsg, { plugins: missingPlugins } );\n\n\t\t\treturn Promise.reject( new CKEditorError( errorMsg, { plugins: missingPlugins } ) );\n\t\t}\n\n\t\treturn Promise.all( pluginConstructors.map( loadPlugin ) )\n\t\t\t.then( () => loaded );\n\n\t\tfunction loadPlugin( PluginConstructor ) {\n\t\t\tif ( removePluginConstructors.includes( PluginConstructor ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// The plugin is already loaded or being loaded - do nothing.\n\t\t\tif ( that._plugins.has( PluginConstructor ) || loading.has( PluginConstructor ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\treturn instantiatePlugin( PluginConstructor )\n\t\t\t\t.catch( err => {\n\t\t\t\t\t/**\n\t\t\t\t\t * It was not possible to load the plugin.\n\t\t\t\t\t *\n\t\t\t\t\t * This is a generic error logged to the console when a JavaSript error is thrown during the initialization\n\t\t\t\t\t * of one of the plugins.\n\t\t\t\t\t *\n\t\t\t\t\t * If you correctly handled the promise returned by the editor's `create()` method (like shown below)\n\t\t\t\t\t * you will find the original error logged to the console, too:\n\t\t\t\t\t *\n\t\t\t\t\t *\t\tClassicEditor.create( document.getElementById( 'editor' ) )\n\t\t\t\t\t *\t\t\t.then( editor => {\n\t\t\t\t\t *\t\t\t\t// ...\n\t\t\t\t\t * \t\t\t} )\n\t\t\t\t\t *\t\t\t.catch( error => {\n\t\t\t\t\t *\t\t\t\tconsole.error( error );\n\t\t\t\t\t *\t\t\t} );\n\t\t\t\t\t *\n\t\t\t\t\t * @error plugincollection-load\n\t\t\t\t\t * @param {String} plugin The name of the plugin that could not be loaded.\n\t\t\t\t\t */\n\t\t\t\t\tlog.error( 'plugincollection-load: It was not possible to load the plugin.', { plugin: PluginConstructor } );\n\n\t\t\t\t\tthrow err;\n\t\t\t\t} );\n\t\t}\n\n\t\tfunction instantiatePlugin( PluginConstructor ) {\n\t\t\treturn new Promise( resolve => {\n\t\t\t\tloading.add( PluginConstructor );\n\n\t\t\t\tif ( PluginConstructor.requires ) {\n\t\t\t\t\tPluginConstructor.requires.forEach( RequiredPluginConstructorOrName => {\n\t\t\t\t\t\tconst RequiredPluginConstructor = getPluginConstructor( RequiredPluginConstructorOrName );\n\n\t\t\t\t\t\tif ( removePlugins.includes( RequiredPluginConstructor ) ) {\n\t\t\t\t\t\t\t/**\n\t\t\t\t\t\t\t * Cannot load a plugin because one of its dependencies is listed in the `removePlugins` option.\n\t\t\t\t\t\t\t *\n\t\t\t\t\t\t\t * @error plugincollection-required\n\t\t\t\t\t\t\t * @param {Function} plugin The required plugin.\n\t\t\t\t\t\t\t * @param {Function} requiredBy The parent plugin.\n\t\t\t\t\t\t\t */\n\t\t\t\t\t\t\tthrow new CKEditorError(\n\t\t\t\t\t\t\t\t'plugincollection-required: Cannot load a plugin because one of its dependencies is listed in' +\n\t\t\t\t\t\t\t\t'the `removePlugins` option.',\n\t\t\t\t\t\t\t\t{ plugin: RequiredPluginConstructor, requiredBy: PluginConstructor }\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tloadPlugin( RequiredPluginConstructor );\n\t\t\t\t\t} );\n\t\t\t\t}\n\n\t\t\t\tconst plugin = new PluginConstructor( editor );\n\t\t\t\tthat._add( PluginConstructor, plugin );\n\t\t\t\tloaded.push( plugin );\n\n\t\t\t\tresolve();\n\t\t\t} );\n\t\t}\n\n\t\tfunction getPluginConstructor( PluginConstructorOrName ) {\n\t\t\tif ( typeof PluginConstructorOrName == 'function' ) {\n\t\t\t\treturn PluginConstructorOrName;\n\t\t\t}\n\n\t\t\treturn that._availablePlugins.get( PluginConstructorOrName );\n\t\t}\n\n\t\tfunction getMissingPluginNames( plugins ) {\n\t\t\tconst missingPlugins = [];\n\n\t\t\tfor ( const pluginNameOrConstructor of plugins ) {\n\t\t\t\tif ( !getPluginConstructor( pluginNameOrConstructor ) ) {\n\t\t\t\t\tmissingPlugins.push( pluginNameOrConstructor );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn missingPlugins.length ? missingPlugins : null;\n\t\t}\n\n\t\tfunction mapToAvailableConstructors( plugins ) {\n\t\t\treturn plugins\n\t\t\t\t.map( pluginNameOrConstructor => getPluginConstructor( pluginNameOrConstructor ) )\n\t\t\t\t.filter( PluginConstructor => !!PluginConstructor );\n\t\t}\n\t}\n\n\t/**\n\t * Destroys all loaded plugins.\n\t *\n\t * @returns {Promise}\n\t */\n\tdestroy() {\n\t\tconst promises = Array.from( this )\n\t\t\t.map( ( [ , pluginInstance ] ) => pluginInstance )\n\t\t\t.filter( pluginInstance => typeof pluginInstance.destroy == 'function' )\n\t\t\t.map( pluginInstance => pluginInstance.destroy() );\n\n\t\treturn Promise.all( promises );\n\t}\n\n\t/**\n\t * Adds the plugin to the collection. Exposed mainly for testing purposes.\n\t *\n\t * @protected\n\t * @param {Function} PluginConstructor The plugin constructor.\n\t * @param {module:core/plugin~PluginInterface} plugin The instance of the plugin.\n\t */\n\t_add( PluginConstructor, plugin ) {\n\t\tthis._plugins.set( PluginConstructor, plugin );\n\n\t\tconst pluginName = PluginConstructor.pluginName;\n\n\t\tif ( !pluginName ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( this._plugins.has( pluginName ) ) {\n\t\t\t/**\n\t\t\t * Two plugins with the same {@link module:core/plugin~PluginInterface.pluginName} were loaded.\n\t\t\t * This will lead to runtime conflicts between these plugins.\n\t\t\t *\n\t\t\t * In practice, this warning usually means that new plugins were added to an existing CKEditor 5 build.\n\t\t\t * Plugins should always be added to a source version of the editor (`@ckeditor/ckeditor5-editor-*`),\n\t\t\t * not to an editor imported from one of the `@ckeditor/ckeditor5-build-*` packages.\n\t\t\t *\n\t\t\t * Check your import paths and the list of plugins passed to\n\t\t\t * {@link module:core/editor/editor~Editor.create `Editor.create()`}\n\t\t\t * or specified in {@link module:core/editor/editor~Editor.builtinPlugins `Editor.builtinPlugins`}.\n\t\t\t *\n\t\t\t * The second option is that your `node_modules/` directory contains duplicated versions of the same\n\t\t\t * CKEditor 5 packages. Normally, on clean installations, npm deduplicates packages in `node_modules/`, so\n\t\t\t * it may be enough to call `rm -rf node_modules && npm i`. However, if you installed conflicting versions\n\t\t\t * of packages, their dependencies may need to be installed in more than one version which may lead to this\n\t\t\t * warning.\n\t\t\t *\n\t\t\t * Technically speaking, this error occurs because after adding a plugin to an existing editor build\n\t\t\t * dependencies of this plugin are being duplicated.\n\t\t\t * They are already built into that editor build and now get added for the second time as dependencies\n\t\t\t * of the plugin you are installing.\n\t\t\t *\n\t\t\t * Read more about {@glink builds/guides/integration/installing-plugins installing plugins}.\n\t\t\t *\n\t\t\t * @error plugincollection-plugin-name-conflict\n\t\t\t * @param {String} pluginName The duplicated plugin name.\n\t\t\t * @param {Function} plugin1 The first plugin constructor.\n\t\t\t * @param {Function} plugin2 The second plugin constructor.\n\t\t\t */\n\t\t\tlog.warn(\n\t\t\t\t'plugincollection-plugin-name-conflict: Two plugins with the same name were loaded.',\n\t\t\t\t{ pluginName, plugin1: this._plugins.get( pluginName ).constructor, plugin2: PluginConstructor }\n\t\t\t);\n\t\t} else {\n\t\t\tthis._plugins.set( pluginName, plugin );\n\t\t}\n\t}\n}\n","/**\n * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\n/**\n * @module core/commandcollection\n */\n\nimport CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';\n\n/**\n * Collection of commands. Its instance is available in {@link module:core/editor/editor~Editor#commands `editor.commands`}.\n */\nexport default class CommandCollection {\n\t/**\n\t * Creates collection instance.\n\t */\n\tconstructor() {\n\t\t/**\n\t\t * Command map.\n\t\t *\n\t\t * @private\n\t\t * @member {Map}\n\t\t */\n\t\tthis._commands = new Map();\n\t}\n\n\t/**\n\t * Registers a new command.\n\t *\n\t * @param {String} commandName The name of the command.\n\t * @param {module:core/command~Command} command\n\t */\n\tadd( commandName, command ) {\n\t\tthis._commands.set( commandName, command );\n\t}\n\n\t/**\n\t * Retrieves a command from the collection.\n\t *\n\t * @param {String} commandName The name of the command.\n\t * @returns {module:core/command~Command}\n\t */\n\tget( commandName ) {\n\t\treturn this._commands.get( commandName );\n\t}\n\n\t/**\n\t * Executes a command.\n\t *\n\t * @param {String} commandName The name of the command.\n\t * @param {*} [...commandParams] Command parameters.\n\t */\n\texecute( commandName, ...args ) {\n\t\tconst command = this.get( commandName );\n\n\t\tif ( !command ) {\n\t\t\t/**\n\t\t\t * Command does not exist.\n\t\t\t *\n\t\t\t * @error commandcollection-command-not-found\n\t\t\t * @param {String} commandName Name of the command.\n\t\t\t */\n\t\t\tthrow new CKEditorError( 'commandcollection-command-not-found: Command does not exist.', { commandName } );\n\t\t}\n\n\t\tcommand.execute( ...args );\n\t}\n\n\t/**\n\t * Returns iterator of command names.\n\t *\n\t * @returns {Iterable.}\n\t */\n\t* names() {\n\t\tyield* this._commands.keys();\n\t}\n\n\t/**\n\t * Returns iterator of command instances.\n\t *\n\t * @returns {Iterable.}\n\t */\n\t* commands() {\n\t\tyield* this._commands.values();\n\t}\n\n\t/**\n\t * Iterable interface.\n\t *\n\t * Returns `[ commandName, commandInstance ]` pairs.\n\t *\n\t * @returns {Iterable.}\n\t */\n\t[ Symbol.iterator ]() {\n\t\treturn this._commands[ Symbol.iterator ]();\n\t}\n\n\t/**\n\t * Destroys all collection commands.\n\t */\n\tdestroy() {\n\t\tfor ( const command of this.commands() ) {\n\t\t\tcommand.destroy();\n\t\t}\n\t}\n}\n","/**\n * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\n/* globals window */\n\n/**\n * @module utils/translation-service\n */\n\n/* istanbul ignore else */\nif ( !window.CKEDITOR_TRANSLATIONS ) {\n\twindow.CKEDITOR_TRANSLATIONS = {};\n}\n\n/**\n * Adds translations to existing ones.\n * These translations will later be available for the {@link module:utils/translation-service~translate `translate()`} function.\n *\n *\t\tadd( 'pl', {\n *\t\t\t'OK': 'OK',\n *\t\t\t'Cancel [context: reject]': 'Anuluj'\n *\t\t} );\n *\n * If you cannot import this function from this module (e.g. because you use a CKEditor 5 build), then you can\n * still add translations by extending the global `window.CKEDITOR_TRANSLATIONS` object by using a function like\n * the one below:\n *\n *\t\tfunction addTranslations( language, translations ) {\n *\t\t\tif ( !window.CKEDITOR_TRANSLATIONS ) {\n *\t\t\t\twindow.CKEDITOR_TRANSLATIONS = {};\n *\t\t\t}\n *\n *\t\t\tconst dictionary = window.CKEDITOR_TRANSLATIONS[ language ] || ( window.CKEDITOR_TRANSLATIONS[ language ] = {} );\n *\n *\t\t\t// Extend the dictionary for the given language.\n *\t\t\tObject.assign( dictionary, translations );\n *\t\t}\n *\n * @param {String} language Target language.\n * @param {Object.} translations Translations which will be added to the dictionary.\n */\nexport function add( language, translations ) {\n\tconst dictionary = window.CKEDITOR_TRANSLATIONS[ language ] || ( window.CKEDITOR_TRANSLATIONS[ language ] = {} );\n\n\tObject.assign( dictionary, translations );\n}\n\n/**\n * Translates string if the translation of the string was previously added to the dictionary.\n * See {@link module:utils/translation-service Translation Service}.\n * This happens in a multi-language mode were translation modules are created by the bundler.\n *\n * When no translation is defined in the dictionary or the dictionary doesn't exist this function returns\n * the original string without the `'[context: ]'` (happens in development and single-language modes).\n *\n * In a single-language mode (when values passed to `t()` were replaced with target language strings) the dictionary\n * is left empty, so this function will return the original strings always.\n *\n *\t\ttranslate( 'pl', 'Cancel [context: reject]' );\n *\n * @param {String} language Target language.\n * @param {String} translationKey String that will be translated.\n * @returns {String} Translated sentence.\n */\nexport function translate( language, translationKey ) {\n\tconst numberOfLanguages = getNumberOfLanguages();\n\n\tif ( numberOfLanguages === 1 ) {\n\t\t// Override the language to the only supported one.\n\t\t// This can't be done in the `Locale` class, because the translations comes after the `Locale` class initialization.\n\t\tlanguage = Object.keys( window.CKEDITOR_TRANSLATIONS )[ 0 ];\n\t}\n\n\tif ( numberOfLanguages === 0 || !hasTranslation( language, translationKey ) ) {\n\t\treturn translationKey.replace( / \\[context: [^\\]]+\\]$/, '' );\n\t}\n\n\tconst dictionary = window.CKEDITOR_TRANSLATIONS[ language ];\n\n\t// In case of missing translations we still need to cut off the `[context: ]` parts.\n\treturn dictionary[ translationKey ].replace( / \\[context: [^\\]]+\\]$/, '' );\n}\n\n/**\n * Clears dictionaries for test purposes.\n *\n * @protected\n */\nexport function _clear() {\n\twindow.CKEDITOR_TRANSLATIONS = {};\n}\n\n// Checks whether the dictionary exists and translation in that dictionary exists.\nfunction hasTranslation( language, translationKey ) {\n\treturn (\n\t\t( language in window.CKEDITOR_TRANSLATIONS ) &&\n\t\t( translationKey in window.CKEDITOR_TRANSLATIONS[ language ] )\n\t);\n}\n\nfunction getNumberOfLanguages() {\n\treturn Object.keys( window.CKEDITOR_TRANSLATIONS ).length;\n}\n","/**\n * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\n/**\n * @module utils/locale\n */\n\nimport { translate } from './translation-service';\n\n/**\n * Represents the localization services.\n */\nexport default class Locale {\n\t/**\n\t * Creates a new instance of the Locale class.\n\t *\n\t * @param {String} [language='en'] The language code in [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format.\n\t */\n\tconstructor( language ) {\n\t\t/**\n\t\t * The language code in [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format.\n\t\t *\n\t\t * @readonly\n\t\t * @member {String}\n\t\t */\n\t\tthis.language = language || 'en';\n\n\t\t/**\n\t\t * Translates the given string to the {@link #language}. This method is also available in {@link module:core/editor/editor~Editor#t}\n\t\t * and {@link module:ui/view~View#t}.\n\t\t *\n\t\t * The strings may contain placeholders (`%`) for values which are passed as the second argument.\n\t\t * `` is the index in the `values` array.\n\t\t *\n\t\t *\t\teditor.t( 'Created file \"%0\" in %1ms.', [ fileName, timeTaken ] );\n\t\t *\n\t\t * This method's context is statically bound to Locale instance,\n\t\t * so it can be called as a function:\n\t\t *\n\t\t *\t\tconst t = this.t;\n\t\t *\t\tt( 'Label' );\n\t\t *\n\t\t * @method #t\n\t\t * @param {String} str The string to translate.\n\t\t * @param {String[]} [values] Values that should be used to interpolate the string.\n\t\t */\n\t\tthis.t = ( ...args ) => this._t( ...args );\n\t}\n\n\t/**\n\t * Base for the {@link #t} method.\n\t *\n\t * @private\n\t */\n\t_t( str, values ) {\n\t\tlet translatedString = translate( this.language, str );\n\n\t\tif ( values ) {\n\t\t\ttranslatedString = translatedString.replace( /%(\\d+)/g, ( match, index ) => {\n\t\t\t\treturn ( index < values.length ) ? values[ index ] : match;\n\t\t\t} );\n\t\t}\n\n\t\treturn translatedString;\n\t}\n}\n","/**\n * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\n/**\n * @module engine/conversion/viewconsumable\n */\n\nimport { isArray } from 'lodash-es';\nimport CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';\n\n/**\n * Class used for handling consumption of view {@link module:engine/view/element~Element elements},\n * {@link module:engine/view/text~Text text nodes} and {@link module:engine/view/documentfragment~DocumentFragment document fragments}.\n * Element's name and its parts (attributes, classes and styles) can be consumed separately. Consuming an element's name\n * does not consume its attributes, classes and styles.\n * To add items for consumption use {@link module:engine/conversion/viewconsumable~ViewConsumable#add add method}.\n * To test items use {@link module:engine/conversion/viewconsumable~ViewConsumable#test test method}.\n * To consume items use {@link module:engine/conversion/viewconsumable~ViewConsumable#consume consume method}.\n * To revert already consumed items use {@link module:engine/conversion/viewconsumable~ViewConsumable#revert revert method}.\n *\n *\t\tviewConsumable.add( element, { name: true } ); // Adds element's name as ready to be consumed.\n *\t\tviewConsumable.add( textNode ); // Adds text node for consumption.\n *\t\tviewConsumable.add( docFragment ); // Adds document fragment for consumption.\n *\t\tviewConsumable.test( element, { name: true } ); // Tests if element's name can be consumed.\n *\t\tviewConsumable.test( textNode ); // Tests if text node can be consumed.\n *\t\tviewConsumable.test( docFragment ); // Tests if document fragment can be consumed.\n *\t\tviewConsumable.consume( element, { name: true } ); // Consume element's name.\n *\t\tviewConsumable.consume( textNode ); // Consume text node.\n *\t\tviewConsumable.consume( docFragment ); // Consume document fragment.\n *\t\tviewConsumable.revert( element, { name: true } ); // Revert already consumed element's name.\n *\t\tviewConsumable.revert( textNode ); // Revert already consumed text node.\n *\t\tviewConsumable.revert( docFragment ); // Revert already consumed document fragment.\n */\nexport default class ViewConsumable {\n\t/**\n\t * Creates new ViewConsumable.\n\t */\n\tconstructor() {\n\t\t/**\n\t\t * Map of consumable elements. If {@link module:engine/view/element~Element element} is used as a key,\n\t\t * {@link module:engine/conversion/viewconsumable~ViewElementConsumables ViewElementConsumables} instance is stored as value.\n\t\t * For {@link module:engine/view/text~Text text nodes} and\n\t\t * {@link module:engine/view/documentfragment~DocumentFragment document fragments} boolean value is stored as value.\n\t\t *\n\t\t * @protected\n\t\t * @member {Map.}\n\t\t*/\n\t\tthis._consumables = new Map();\n\t}\n\n\t/**\n\t * Adds {@link module:engine/view/element~Element view element}, {@link module:engine/view/text~Text text node} or\n\t * {@link module:engine/view/documentfragment~DocumentFragment document fragment} as ready to be consumed.\n\t *\n\t *\t\tviewConsumable.add( p, { name: true } ); // Adds element's name to consume.\n\t *\t\tviewConsumable.add( p, { attributes: 'name' } ); // Adds element's attribute.\n\t *\t\tviewConsumable.add( p, { classes: 'foobar' } ); // Adds element's class.\n\t *\t\tviewConsumable.add( p, { styles: 'color' } ); // Adds element's style\n\t *\t\tviewConsumable.add( p, { attributes: 'name', styles: 'color' } ); // Adds attribute and style.\n\t *\t\tviewConsumable.add( p, { classes: [ 'baz', 'bar' ] } ); // Multiple consumables can be provided.\n\t *\t\tviewConsumable.add( textNode ); // Adds text node to consume.\n\t *\t\tviewConsumable.add( docFragment ); // Adds document fragment to consume.\n\t *\n\t * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `viewconsumable-invalid-attribute` when `class` or `style`\n\t * attribute is provided - it should be handled separately by providing actual style/class.\n\t *\n\t *\t\tviewConsumable.add( p, { attributes: 'style' } ); // This call will throw an exception.\n\t *\t\tviewConsumable.add( p, { styles: 'color' } ); // This is properly handled style.\n\t *\n\t * @param {module:engine/view/element~Element|module:engine/view/text~Text|module:engine/view/documentfragment~DocumentFragment} element\n\t * @param {Object} [consumables] Used only if first parameter is {@link module:engine/view/element~Element view element} instance.\n\t * @param {Boolean} consumables.name If set to true element's name will be included.\n\t * @param {String|Array.} consumables.attributes Attribute name or array of attribute names.\n\t * @param {String|Array.} consumables.classes Class name or array of class names.\n\t * @param {String|Array.} consumables.styles Style name or array of style names.\n\t */\n\tadd( element, consumables ) {\n\t\tlet elementConsumables;\n\n\t\t// For text nodes and document fragments just mark them as consumable.\n\t\tif ( element.is( 'text' ) || element.is( 'documentFragment' ) ) {\n\t\t\tthis._consumables.set( element, true );\n\n\t\t\treturn;\n\t\t}\n\n\t\t// For elements create new ViewElementConsumables or update already existing one.\n\t\tif ( !this._consumables.has( element ) ) {\n\t\t\telementConsumables = new ViewElementConsumables();\n\t\t\tthis._consumables.set( element, elementConsumables );\n\t\t} else {\n\t\t\telementConsumables = this._consumables.get( element );\n\t\t}\n\n\t\telementConsumables.add( consumables );\n\t}\n\n\t/**\n\t * Tests if {@link module:engine/view/element~Element view element}, {@link module:engine/view/text~Text text node} or\n\t * {@link module:engine/view/documentfragment~DocumentFragment document fragment} can be consumed.\n\t * It returns `true` when all items included in method's call can be consumed. Returns `false` when\n\t * first already consumed item is found and `null` when first non-consumable item is found.\n\t *\n\t *\t\tviewConsumable.test( p, { name: true } ); // Tests element's name.\n\t *\t\tviewConsumable.test( p, { attributes: 'name' } ); // Tests attribute.\n\t *\t\tviewConsumable.test( p, { classes: 'foobar' } ); // Tests class.\n\t *\t\tviewConsumable.test( p, { styles: 'color' } ); // Tests style.\n\t *\t\tviewConsumable.test( p, { attributes: 'name', styles: 'color' } ); // Tests attribute and style.\n\t *\t\tviewConsumable.test( p, { classes: [ 'baz', 'bar' ] } ); // Multiple consumables can be tested.\n\t *\t\tviewConsumable.test( textNode ); // Tests text node.\n\t *\t\tviewConsumable.test( docFragment ); // Tests document fragment.\n\t *\n\t * Testing classes and styles as attribute will test if all added classes/styles can be consumed.\n\t *\n\t *\t\tviewConsumable.test( p, { attributes: 'class' } ); // Tests if all added classes can be consumed.\n\t *\t\tviewConsumable.test( p, { attributes: 'style' } ); // Tests if all added styles can be consumed.\n\t *\n\t * @param {module:engine/view/element~Element|module:engine/view/text~Text|module:engine/view/documentfragment~DocumentFragment} element\n\t * @param {Object} [consumables] Used only if first parameter is {@link module:engine/view/element~Element view element} instance.\n\t * @param {Boolean} consumables.name If set to true element's name will be included.\n\t * @param {String|Array.} consumables.attributes Attribute name or array of attribute names.\n\t * @param {String|Array.} consumables.classes Class name or array of class names.\n\t * @param {String|Array.} consumables.styles Style name or array of style names.\n\t * @returns {Boolean|null} Returns `true` when all items included in method's call can be consumed. Returns `false`\n\t * when first already consumed item is found and `null` when first non-consumable item is found.\n\t */\n\ttest( element, consumables ) {\n\t\tconst elementConsumables = this._consumables.get( element );\n\n\t\tif ( elementConsumables === undefined ) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// For text nodes and document fragments return stored boolean value.\n\t\tif ( element.is( 'text' ) || element.is( 'documentFragment' ) ) {\n\t\t\treturn elementConsumables;\n\t\t}\n\n\t\t// For elements test consumables object.\n\t\treturn elementConsumables.test( consumables );\n\t}\n\n\t/**\n\t * Consumes {@link module:engine/view/element~Element view element}, {@link module:engine/view/text~Text text node} or\n\t * {@link module:engine/view/documentfragment~DocumentFragment document fragment}.\n\t * It returns `true` when all items included in method's call can be consumed, otherwise returns `false`.\n\t *\n\t *\t\tviewConsumable.consume( p, { name: true } ); // Consumes element's name.\n\t *\t\tviewConsumable.consume( p, { attributes: 'name' } ); // Consumes element's attribute.\n\t *\t\tviewConsumable.consume( p, { classes: 'foobar' } ); // Consumes element's class.\n\t *\t\tviewConsumable.consume( p, { styles: 'color' } ); // Consumes element's style.\n\t *\t\tviewConsumable.consume( p, { attributes: 'name', styles: 'color' } ); // Consumes attribute and style.\n\t *\t\tviewConsumable.consume( p, { classes: [ 'baz', 'bar' ] } ); // Multiple consumables can be consumed.\n\t *\t\tviewConsumable.consume( textNode ); // Consumes text node.\n\t *\t\tviewConsumable.consume( docFragment ); // Consumes document fragment.\n\t *\n\t * Consuming classes and styles as attribute will test if all added classes/styles can be consumed.\n\t *\n\t *\t\tviewConsumable.consume( p, { attributes: 'class' } ); // Consume only if all added classes can be consumed.\n\t *\t\tviewConsumable.consume( p, { attributes: 'style' } ); // Consume only if all added styles can be consumed.\n\t *\n\t * @param {module:engine/view/element~Element|module:engine/view/text~Text|module:engine/view/documentfragment~DocumentFragment} element\n\t * @param {Object} [consumables] Used only if first parameter is {@link module:engine/view/element~Element view element} instance.\n\t * @param {Boolean} consumables.name If set to true element's name will be included.\n\t * @param {String|Array.} consumables.attributes Attribute name or array of attribute names.\n\t * @param {String|Array.} consumables.classes Class name or array of class names.\n\t * @param {String|Array.} consumables.styles Style name or array of style names.\n\t * @returns {Boolean} Returns `true` when all items included in method's call can be consumed,\n\t * otherwise returns `false`.\n\t */\n\tconsume( element, consumables ) {\n\t\tif ( this.test( element, consumables ) ) {\n\t\t\tif ( element.is( 'text' ) || element.is( 'documentFragment' ) ) {\n\t\t\t\t// For text nodes and document fragments set value to false.\n\t\t\t\tthis._consumables.set( element, false );\n\t\t\t} else {\n\t\t\t\t// For elements - consume consumables object.\n\t\t\t\tthis._consumables.get( element ).consume( consumables );\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Reverts {@link module:engine/view/element~Element view element}, {@link module:engine/view/text~Text text node} or\n\t * {@link module:engine/view/documentfragment~DocumentFragment document fragment} so they can be consumed once again.\n\t * Method does not revert items that were never previously added for consumption, even if they are included in\n\t * method's call.\n\t *\n\t *\t\tviewConsumable.revert( p, { name: true } ); // Reverts element's name.\n\t *\t\tviewConsumable.revert( p, { attributes: 'name' } ); // Reverts element's attribute.\n\t *\t\tviewConsumable.revert( p, { classes: 'foobar' } ); // Reverts element's class.\n\t *\t\tviewConsumable.revert( p, { styles: 'color' } ); // Reverts element's style.\n\t *\t\tviewConsumable.revert( p, { attributes: 'name', styles: 'color' } ); // Reverts attribute and style.\n\t *\t\tviewConsumable.revert( p, { classes: [ 'baz', 'bar' ] } ); // Multiple names can be reverted.\n\t *\t\tviewConsumable.revert( textNode ); // Reverts text node.\n\t *\t\tviewConsumable.revert( docFragment ); // Reverts document fragment.\n\t *\n\t * Reverting classes and styles as attribute will revert all classes/styles that were previously added for\n\t * consumption.\n\t *\n\t *\t\tviewConsumable.revert( p, { attributes: 'class' } ); // Reverts all classes added for consumption.\n\t *\t\tviewConsumable.revert( p, { attributes: 'style' } ); // Reverts all styles added for consumption.\n\t *\n\t * @param {module:engine/view/element~Element|module:engine/view/text~Text|module:engine/view/documentfragment~DocumentFragment} element\n\t * @param {Object} [consumables] Used only if first parameter is {@link module:engine/view/element~Element view element} instance.\n\t * @param {Boolean} consumables.name If set to true element's name will be included.\n\t * @param {String|Array.} consumables.attributes Attribute name or array of attribute names.\n\t * @param {String|Array.} consumables.classes Class name or array of class names.\n\t * @param {String|Array.} consumables.styles Style name or array of style names.\n\t */\n\trevert( element, consumables ) {\n\t\tconst elementConsumables = this._consumables.get( element );\n\n\t\tif ( elementConsumables !== undefined ) {\n\t\t\tif ( element.is( 'text' ) || element.is( 'documentFragment' ) ) {\n\t\t\t\t// For text nodes and document fragments - set consumable to true.\n\t\t\t\tthis._consumables.set( element, true );\n\t\t\t} else {\n\t\t\t\t// For elements - revert items from consumables object.\n\t\t\t\telementConsumables.revert( consumables );\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Creates consumable object from {@link module:engine/view/element~Element view element}. Consumable object will include\n\t * element's name and all its attributes, classes and styles.\n\t *\n\t * @static\n\t * @param {module:engine/view/element~Element} element\n\t * @returns {Object} consumables\n\t */\n\tstatic consumablesFromElement( element ) {\n\t\tconst consumables = {\n\t\t\tname: true,\n\t\t\tattributes: [],\n\t\t\tclasses: [],\n\t\t\tstyles: []\n\t\t};\n\n\t\tconst attributes = element.getAttributeKeys();\n\n\t\tfor ( const attribute of attributes ) {\n\t\t\t// Skip classes and styles - will be added separately.\n\t\t\tif ( attribute == 'style' || attribute == 'class' ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconsumables.attributes.push( attribute );\n\t\t}\n\n\t\tconst classes = element.getClassNames();\n\n\t\tfor ( const className of classes ) {\n\t\t\tconsumables.classes.push( className );\n\t\t}\n\n\t\tconst styles = element.getStyleNames();\n\n\t\tfor ( const style of styles ) {\n\t\t\tconsumables.styles.push( style );\n\t\t}\n\n\t\treturn consumables;\n\t}\n\n\t/**\n\t * Creates {@link module:engine/conversion/viewconsumable~ViewConsumable ViewConsumable} instance from\n\t * {@link module:engine/view/node~Node node} or {@link module:engine/view/documentfragment~DocumentFragment document fragment}.\n\t * Instance will contain all elements, child nodes, attributes, styles and classes added for consumption.\n\t *\n\t * @static\n\t * @param {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment} from View node or document fragment\n\t * from which `ViewConsumable` will be created.\n\t * @param {module:engine/conversion/viewconsumable~ViewConsumable} [instance] If provided, given `ViewConsumable` instance will be used\n\t * to add all consumables. It will be returned instead of a new instance.\n\t */\n\tstatic createFrom( from, instance ) {\n\t\tif ( !instance ) {\n\t\t\tinstance = new ViewConsumable();\n\t\t}\n\n\t\tif ( from.is( 'text' ) ) {\n\t\t\tinstance.add( from );\n\n\t\t\treturn instance;\n\t\t}\n\n\t\t// Add `from` itself, if it is an element.\n\t\tif ( from.is( 'element' ) ) {\n\t\t\tinstance.add( from, ViewConsumable.consumablesFromElement( from ) );\n\t\t}\n\n\t\tif ( from.is( 'documentFragment' ) ) {\n\t\t\tinstance.add( from );\n\t\t}\n\n\t\tfor ( const child of from.getChildren() ) {\n\t\t\tinstance = ViewConsumable.createFrom( child, instance );\n\t\t}\n\n\t\treturn instance;\n\t}\n}\n\n/**\n * This is a private helper-class for {@link module:engine/conversion/viewconsumable~ViewConsumable}.\n * It represents and manipulates consumable parts of a single {@link module:engine/view/element~Element}.\n *\n * @private\n */\nclass ViewElementConsumables {\n\t/**\n\t * Creates ViewElementConsumables instance.\n\t */\n\tconstructor() {\n\t\t/**\n\t\t * Flag indicating if name of the element can be consumed.\n\t\t *\n\t\t * @private\n\t\t * @member {Boolean}\n\t\t */\n\t\tthis._canConsumeName = null;\n\n\t\t/**\n\t\t * Contains maps of element's consumables: attributes, classes and styles.\n\t\t *\n\t\t * @private\n\t\t * @member {Object}\n\t\t */\n\t\tthis._consumables = {\n\t\t\tattributes: new Map(),\n\t\t\tstyles: new Map(),\n\t\t\tclasses: new Map()\n\t\t};\n\t}\n\n\t/**\n\t * Adds consumable parts of the {@link module:engine/view/element~Element view element}.\n\t * Element's name itself can be marked to be consumed (when element's name is consumed its attributes, classes and\n\t * styles still could be consumed):\n\t *\n\t *\t\tconsumables.add( { name: true } );\n\t *\n\t * Attributes classes and styles:\n\t *\n\t *\t\tconsumables.add( { attributes: 'title', classes: 'foo', styles: 'color' } );\n\t *\t\tconsumables.add( { attributes: [ 'title', 'name' ], classes: [ 'foo', 'bar' ] );\n\t *\n\t * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `viewconsumable-invalid-attribute` when `class` or `style`\n\t * attribute is provided - it should be handled separately by providing `style` and `class` in consumables object.\n\t *\n\t * @param {Object} consumables Object describing which parts of the element can be consumed.\n\t * @param {Boolean} consumables.name If set to `true` element's name will be added as consumable.\n\t * @param {String|Array.} consumables.attributes Attribute name or array of attribute names to add as consumable.\n\t * @param {String|Array.} consumables.classes Class name or array of class names to add as consumable.\n\t * @param {String|Array.} consumables.styles Style name or array of style names to add as consumable.\n\t */\n\tadd( consumables ) {\n\t\tif ( consumables.name ) {\n\t\t\tthis._canConsumeName = true;\n\t\t}\n\n\t\tfor ( const type in this._consumables ) {\n\t\t\tif ( type in consumables ) {\n\t\t\t\tthis._add( type, consumables[ type ] );\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Tests if parts of the {@link module:engine/view/node~Node view node} can be consumed.\n\t *\n\t * Element's name can be tested:\n\t *\n\t *\t\tconsumables.test( { name: true } );\n\t *\n\t * Attributes classes and styles:\n\t *\n\t *\t\tconsumables.test( { attributes: 'title', classes: 'foo', styles: 'color' } );\n\t *\t\tconsumables.test( { attributes: [ 'title', 'name' ], classes: [ 'foo', 'bar' ] );\n\t *\n\t * @param {Object} consumables Object describing which parts of the element should be tested.\n\t * @param {Boolean} consumables.name If set to `true` element's name will be tested.\n\t * @param {String|Array.} consumables.attributes Attribute name or array of attribute names to test.\n\t * @param {String|Array.} consumables.classes Class name or array of class names to test.\n\t * @param {String|Array.} consumables.styles Style name or array of style names to test.\n\t * @returns {Boolean|null} `true` when all tested items can be consumed, `null` when even one of the items\n\t * was never marked for consumption and `false` when even one of the items was already consumed.\n\t */\n\ttest( consumables ) {\n\t\t// Check if name can be consumed.\n\t\tif ( consumables.name && !this._canConsumeName ) {\n\t\t\treturn this._canConsumeName;\n\t\t}\n\n\t\tfor ( const type in this._consumables ) {\n\t\t\tif ( type in consumables ) {\n\t\t\t\tconst value = this._test( type, consumables[ type ] );\n\n\t\t\t\tif ( value !== true ) {\n\t\t\t\t\treturn value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Return true only if all can be consumed.\n\t\treturn true;\n\t}\n\n\t/**\n\t * Consumes parts of {@link module:engine/view/element~Element view element}. This function does not check if consumable item\n\t * is already consumed - it consumes all consumable items provided.\n\t * Element's name can be consumed:\n\t *\n\t *\t\tconsumables.consume( { name: true } );\n\t *\n\t * Attributes classes and styles:\n\t *\n\t *\t\tconsumables.consume( { attributes: 'title', classes: 'foo', styles: 'color' } );\n\t *\t\tconsumables.consume( { attributes: [ 'title', 'name' ], classes: [ 'foo', 'bar' ] );\n\t *\n\t * @param {Object} consumables Object describing which parts of the element should be consumed.\n\t * @param {Boolean} consumables.name If set to `true` element's name will be consumed.\n\t * @param {String|Array.} consumables.attributes Attribute name or array of attribute names to consume.\n\t * @param {String|Array.} consumables.classes Class name or array of class names to consume.\n\t * @param {String|Array.} consumables.styles Style name or array of style names to consume.\n\t */\n\tconsume( consumables ) {\n\t\tif ( consumables.name ) {\n\t\t\tthis._canConsumeName = false;\n\t\t}\n\n\t\tfor ( const type in this._consumables ) {\n\t\t\tif ( type in consumables ) {\n\t\t\t\tthis._consume( type, consumables[ type ] );\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Revert already consumed parts of {@link module:engine/view/element~Element view Element}, so they can be consumed once again.\n\t * Element's name can be reverted:\n\t *\n\t *\t\tconsumables.revert( { name: true } );\n\t *\n\t * Attributes classes and styles:\n\t *\n\t *\t\tconsumables.revert( { attributes: 'title', classes: 'foo', styles: 'color' } );\n\t *\t\tconsumables.revert( { attributes: [ 'title', 'name' ], classes: [ 'foo', 'bar' ] );\n\t *\n\t * @param {Object} consumables Object describing which parts of the element should be reverted.\n\t * @param {Boolean} consumables.name If set to `true` element's name will be reverted.\n\t * @param {String|Array.} consumables.attributes Attribute name or array of attribute names to revert.\n\t * @param {String|Array.} consumables.classes Class name or array of class names to revert.\n\t * @param {String|Array.} consumables.styles Style name or array of style names to revert.\n\t */\n\trevert( consumables ) {\n\t\tif ( consumables.name ) {\n\t\t\tthis._canConsumeName = true;\n\t\t}\n\n\t\tfor ( const type in this._consumables ) {\n\t\t\tif ( type in consumables ) {\n\t\t\t\tthis._revert( type, consumables[ type ] );\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Helper method that adds consumables of a given type: attribute, class or style.\n\t *\n\t * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `viewconsumable-invalid-attribute` when `class` or `style`\n\t * type is provided - it should be handled separately by providing actual style/class type.\n\t *\n\t * @private\n\t * @param {String} type Type of the consumable item: `attributes`, `classes` or `styles`.\n\t * @param {String|Array.} item Consumable item or array of items.\n\t */\n\t_add( type, item ) {\n\t\tconst items = isArray( item ) ? item : [ item ];\n\t\tconst consumables = this._consumables[ type ];\n\n\t\tfor ( const name of items ) {\n\t\t\tif ( type === 'attributes' && ( name === 'class' || name === 'style' ) ) {\n\t\t\t\t/**\n\t\t\t\t * Class and style attributes should be handled separately in\n\t\t\t\t * {@link module:engine/conversion/viewconsumable~ViewConsumable#add `ViewConsumable#add()`}.\n\t\t\t\t *\n\t\t\t\t * What you have done is trying to use:\n\t\t\t\t *\n\t\t\t\t *\t\tconsumables.add( { attributes: [ 'class', 'style' ] } );\n\t\t\t\t *\n\t\t\t\t * While each class and style should be registered separately:\n\t\t\t\t *\n\t\t\t\t *\t\tconsumables.add( { classes: 'some-class', styles: 'font-weight' } );\n\t\t\t\t *\n\t\t\t\t * @error viewconsumable-invalid-attribute\n\t\t\t\t */\n\t\t\t\tthrow new CKEditorError( 'viewconsumable-invalid-attribute: Classes and styles should be handled separately.' );\n\t\t\t}\n\n\t\t\tconsumables.set( name, true );\n\t\t}\n\t}\n\n\t/**\n\t * Helper method that tests consumables of a given type: attribute, class or style.\n\t *\n\t * @private\n\t * @param {String} type Type of the consumable item: `attributes`, `classes` or `styles`.\n\t * @param {String|Array.} item Consumable item or array of items.\n\t * @returns {Boolean|null} Returns `true` if all items can be consumed, `null` when one of the items cannot be\n\t * consumed and `false` when one of the items is already consumed.\n\t */\n\t_test( type, item ) {\n\t\tconst items = isArray( item ) ? item : [ item ];\n\t\tconst consumables = this._consumables[ type ];\n\n\t\tfor ( const name of items ) {\n\t\t\tif ( type === 'attributes' && ( name === 'class' || name === 'style' ) ) {\n\t\t\t\tconst consumableName = name == 'class' ? 'classes' : 'styles';\n\n\t\t\t\t// Check all classes/styles if class/style attribute is tested.\n\t\t\t\tconst value = this._test( consumableName, [ ...this._consumables[ consumableName ].keys() ] );\n\n\t\t\t\tif ( value !== true ) {\n\t\t\t\t\treturn value;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst value = consumables.get( name );\n\t\t\t\t// Return null if attribute is not found.\n\t\t\t\tif ( value === undefined ) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\n\t\t\t\tif ( !value ) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Helper method that consumes items of a given type: attribute, class or style.\n\t *\n\t * @private\n\t * @param {String} type Type of the consumable item: `attributes`, `classes` or `styles`.\n\t * @param {String|Array.} item Consumable item or array of items.\n\t */\n\t_consume( type, item ) {\n\t\tconst items = isArray( item ) ? item : [ item ];\n\t\tconst consumables = this._consumables[ type ];\n\n\t\tfor ( const name of items ) {\n\t\t\tif ( type === 'attributes' && ( name === 'class' || name === 'style' ) ) {\n\t\t\t\tconst consumableName = name == 'class' ? 'classes' : 'styles';\n\n\t\t\t\t// If class or style is provided for consumption - consume them all.\n\t\t\t\tthis._consume( consumableName, [ ...this._consumables[ consumableName ].keys() ] );\n\t\t\t} else {\n\t\t\t\tconsumables.set( name, false );\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Helper method that reverts items of a given type: attribute, class or style.\n\t *\n\t * @private\n\t * @param {String} type Type of the consumable item: `attributes`, `classes` or , `styles`.\n\t * @param {String|Array.} item Consumable item or array of items.\n\t */\n\t_revert( type, item ) {\n\t\tconst items = isArray( item ) ? item : [ item ];\n\t\tconst consumables = this._consumables[ type ];\n\n\t\tfor ( const name of items ) {\n\t\t\tif ( type === 'attributes' && ( name === 'class' || name === 'style' ) ) {\n\t\t\t\tconst consumableName = name == 'class' ? 'classes' : 'styles';\n\n\t\t\t\t// If class or style is provided for reverting - revert them all.\n\t\t\t\tthis._revert( consumableName, [ ...this._consumables[ consumableName ].keys() ] );\n\t\t\t} else {\n\t\t\t\tconst value = consumables.get( name );\n\n\t\t\t\tif ( value === false ) {\n\t\t\t\t\tconsumables.set( name, true );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n","/**\n * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md.\n */\n\n/**\n * @module engine/model/schema\n */\n\nimport CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';\nimport ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';\nimport mix from '@ckeditor/ckeditor5-utils/src/mix';\n\nimport Range from './range';\nimport Position from './position';\nimport Element from './element';\nimport Text from './text';\nimport TreeWalker from './treewalker';\n\n/**\n * The model's schema. It defines allowed and disallowed structures of nodes as well as nodes' attributes.\n * The schema is usually defined by features and based on them the editing framework and features\n * make decisions how to change and process the model.\n *\n * The instance of schema is available in {@link module:engine/model/model~Model#schema `editor.model.schema`}.\n *\n * Read more about the schema in:\n *\n * * {@glink framework/guides/architecture/editing-engine#schema \"Schema\"} section of the\n * {@glink framework/guides/architecture/editing-engine Introduction to the \"Editing engine architecture\"}.\n * * {@glink framework/guides/deep-dive/schema \"Schema\" deep dive} guide.\n *\n * @mixes module:utils/observablemixin~ObservableMixin\n */\nexport default class Schema {\n\t/**\n\t * Creates schema instance.\n\t */\n\tconstructor() {\n\t\tthis._sourceDefinitions = {};\n\n\t\tthis.decorate( 'checkChild' );\n\t\tthis.decorate( 'checkAttribute' );\n\n\t\tthis.on( 'checkAttribute', ( evt, args ) => {\n\t\t\targs[ 0 ] = new SchemaContext( args[ 0 ] );\n\t\t}, { priority: 'highest' } );\n\n\t\tthis.on( 'checkChild', ( evt, args ) => {\n\t\t\targs[ 0 ] = new SchemaContext( args[ 0 ] );\n\t\t\targs[ 1 ] = this.getDefinition( args[ 1 ] );\n\t\t}, { priority: 'highest' } );\n\t}\n\n\t/**\n\t * Registers schema item. Can only be called once for every item name.\n\t *\n\t *\t\tschema.register( 'paragraph', {\n\t *\t\t\tinheritAllFrom: '$block'\n\t *\t\t} );\n\t *\n\t * @param {String} itemName\n\t * @param {module:engine/model/schema~SchemaItemDefinition} definition\n\t */\n\tregister( itemName, definition ) {\n\t\tif ( this._sourceDefinitions[ itemName ] ) {\n\t\t\t// TODO docs\n\t\t\tthrow new CKEditorError( 'schema-cannot-register-item-twice: A single item cannot be registered twice in the schema.', {\n\t\t\t\titemName\n\t\t\t} );\n\t\t}\n\n\t\tthis._sourceDefinitions[ itemName ] = [\n\t\t\tObject.assign( {}, definition )\n\t\t];\n\n\t\tthis._clearCache();\n\t}\n\n\t/**\n\t * Extends a {@link #register registered} item's definition.\n\t *\n\t * Extending properties such as `allowIn` will add more items to the existing properties,\n\t * while redefining properties such as `isBlock` will override the previously defined ones.\n\t *\n\t *\t\tschema.register( 'foo', {\n\t *\t\t\tallowIn: '$root',\n\t *\t\t\tisBlock: true;\n\t *\t\t} );\n\t *\t\tschema.extend( 'foo', {\n\t *\t\t\tallowIn: 'blockQuote',\n\t *\t\t\tisBlock: false\n\t *\t\t} );\n\t *\n\t *\t\tschema.getDefinition( 'foo' );\n\t *\t\t//\t{\n\t *\t\t//\t\tallowIn: [ '$root', 'blockQuote' ],\n\t *\t\t// \t\tisBlock: false\n\t *\t\t//\t}\n\t *\n\t * @param {String} itemName\n\t * @param {module:engine/model/schema~SchemaItemDefinition} definition\n\t */\n\textend( itemName, definition ) {\n\t\tif ( !this._sourceDefinitions[ itemName ] ) {\n\t\t\t// TODO docs\n\t\t\tthrow new CKEditorError( 'schema-cannot-extend-missing-item: Cannot extend an item which was not registered yet.', {\n\t\t\t\titemName\n\t\t\t} );\n\t\t}\n\n\t\tthis._sourceDefinitions[ itemName ].push( Object.assign( {}, definition ) );\n\n\t\tthis._clearCache();\n\t}\n\n\t/**\n\t * Returns all registered items.\n\t *\n\t * @returns {Object.}\n\t */\n\tgetDefinitions() {\n\t\tif ( !this._compiledDefinitions ) {\n\t\t\tthis._compile();\n\t\t}\n\n\t\treturn this._compiledDefinitions;\n\t}\n\n\t/**\n\t * Returns a definition of the given item or `undefined` if item is not registered.\n\t *\n\t * @param {module:engine/model/item~Item|module:engine/model/schema~SchemaContextItem|String} item\n\t * @returns {module:engine/model/schema~SchemaCompiledItemDefinition}\n\t */\n\tgetDefinition( item ) {\n\t\tlet itemName;\n\n\t\tif ( typeof item == 'string' ) {\n\t\t\titemName = item;\n\t\t} else if ( item.is && ( item.is( 'text' ) || item.is( 'textProxy' ) ) ) {\n\t\t\titemName = '$text';\n\t\t}\n\t\t// Element or module:engine/model/schema~SchemaContextItem.\n\t\telse {\n\t\t\titemName = item.name;\n\t\t}\n\n\t\treturn this.getDefinitions()[ itemName ];\n\t}\n\n\t/**\n\t * Returns `true` if the given item is registered in the schema.\n\t *\n\t *\t\tschema.isRegistered( 'paragraph' ); // -> true\n\t *\t\tschema.isRegistered( editor.model.document.getRoot() ); // -> true\n\t *\t\tschema.isRegistered( 'foo' ); // -> false\n\t *\n\t * @param {module:engine/model/item~Item|module:engine/model/schema~SchemaContextItem|String} item\n\t */\n\tisRegistered( item ) {\n\t\treturn !!this.getDefinition( item );\n\t}\n\n\t/**\n\t * Returns `true` if the given item is defined to be\n\t * a block by {@link module:engine/model/schema~SchemaItemDefinition}'s `isBlock` property.\n\t *\n\t *\t\tschema.isBlock( 'paragraph' ); // -> true\n\t *\t\tschema.isBlock( '$root' ); // -> false\n\t *\n\t *\t\tconst paragraphElement = writer.createElement( 'paragraph' );\n\t *\t\tschema.isBlock( paragraphElement ); // -> true\n\t *\n\t * @param {module:engine/model/item~Item|module:engine/model/schema~SchemaContextItem|String} item\n\t */\n\tisBlock( item ) {\n\t\tconst def = this.getDefinition( item );\n\n\t\treturn !!( def && def.isBlock );\n\t}\n\n\t/**\n\t * Returns `true` if the given item is defined to be\n\t * a limit element by {@link module:engine/model/schema~SchemaItemDefinition}'s `isLimit` or `isObject` property\n\t * (all objects are also limits).\n\t *\n\t *\t\tschema.isLimit( 'paragraph' ); // -> false\n\t *\t\tschema.isLimit( '$root' ); // -> true\n\t *\t\tschema.isLimit( editor.model.document.getRoot() ); // -> true\n\t *\t\tschema.isLimit( 'image' ); // -> true\n\t *\n\t * @param {module:engine/model/item~Item|module:engine/model/schema~SchemaContextItem|String} item\n\t */\n\tisLimit( item ) {\n\t\tconst def = this.getDefinition( item );\n\n\t\tif ( !def ) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn !!( def.isLimit || def.isObject );\n\t}\n\n\t/**\n\t * Returns `true` if the given item is defined to be\n\t * a object element by {@link module:engine/model/schema~SchemaItemDefinition}'s `isObject` property.\n\t *\n\t *\t\tschema.isObject( 'paragraph' ); // -> false\n\t *\t\tschema.isObject( 'image' ); // -> true\n\t *\n\t *\t\tconst imageElement = writer.createElement( 'image' );\n\t *\t\tschema.isObject( imageElement ); // -> true\n\t *\n\t * @param {module:engine/model/item~Item|module:engine/model/schema~SchemaContextItem|String} item\n\t */\n\tisObject( item ) {\n\t\tconst def = this.getDefinition( item );\n\n\t\treturn !!( def && def.isObject );\n\t}\n\n\t/**\n\t * Checks whether the given node (`child`) can be a child of the given context.\n\t *\n\t *\t\tschema.checkChild( model.document.getRoot(), paragraph ); // -> false\n\t *\n\t *\t\tschema.register( 'paragraph', {\n\t *\t\t\tallowIn: '$root'\n\t *\t\t} );\n\t *\t\tschema.checkChild( model.document.getRoot(), paragraph ); // -> true\n\t *\n\t * Note: When verifying whether the given node can be a child of the given context, the\n\t * schema also verifies the entire context — from its root to its last element. Therefore, it is possible\n\t * for `checkChild()` to return `false` even though the context's last element can contain the checked child.\n\t * It happens if one of the context's elements does not allow its child.\n\t *\n\t * @fires checkChild\n\t * @param {module:engine/model/schema~SchemaContextDefinition} context The context in which the child will be checked.\n\t * @param {module:engine/model/node~Node|String} def The child to check.\n\t */\n\tcheckChild( context, def ) {\n\t\t// Note: context and child are already normalized here to a SchemaContext and SchemaCompiledItemDefinition.\n\t\tif ( !def ) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn this._checkContextMatch( def, context );\n\t}\n\n\t/**\n\t * Checks whether the given attribute can be applied in the given context (on the last\n\t * item of the context).\n\t *\n\t *\t\tschema.checkAttribute( textNode, 'bold' ); // -> false\n\t *\n\t *\t\tschema.extend( '$text', {\n\t *\t\t\tallowAttributes: 'bold'\n\t *\t\t} );\n\t *\t\tschema.checkAttribute( textNode, 'bold' ); // -> true\n\t *\n\t * @fires checkAttribute\n\t * @param {module:engine/model/schema~SchemaContextDefinition} context The context in which the attribute will be checked.\n\t * @param {String} attributeName\n\t */\n\tcheckAttribute( context, attributeName ) {\n\t\tconst def = this.getDefinition( context.last );\n\n\t\tif ( !def ) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn def.allowAttributes.includes( attributeName );\n\t}\n\n\t/**\n\t * Checks whether the given element (`elementToMerge`) can be merged with the specified base element (`positionOrBaseElement`).\n\t *\n\t * In other words — whether `elementToMerge`'s children {@link #checkChild are allowed} in the `positionOrBaseElement`.\n\t *\n\t * This check ensures that elements merged with {@link module:engine/model/writer~Writer#merge `Writer#merge()`}\n\t * will be valid.\n\t *\n\t * Instead of elements, you can pass the instance of the {@link module:engine/model/position~Position} class as the\n\t * `positionOrBaseElement`. It means that the elements before and after the position will be checked whether they can be merged.\n\t *\n\t * @param {module:engine/model/position~Position|module:engine/model/element~Element} positionOrBaseElement The position or base\n\t * element to which the `elementToMerge` will be merged.\n\t * @param {module:engine/model/element~Element} elementToMerge The element to merge. Required if `positionOrBaseElement` is an element.\n\t * @returns {Boolean}\n\t */\n\tcheckMerge( positionOrBaseElement, elementToMerge = null ) {\n\t\tif ( positionOrBaseElement instanceof Position ) {\n\t\t\tconst nodeBefore = positionOrBaseElement.nodeBefore;\n\t\t\tconst nodeAfter = positionOrBaseElement.nodeAfter;\n\n\t\t\tif ( !( nodeBefore instanceof Element ) ) {\n\t\t\t\t/**\n\t\t\t\t * The node before the merge position must be an element.\n\t\t\t\t *\n\t\t\t\t * @error schema-check-merge-no-element-before\n\t\t\t\t */\n\t\t\t\tthrow new CKEditorError( 'schema-check-merge-no-element-before: The node before the merge position must be an element.' );\n\t\t\t}\n\n\t\t\tif ( !( nodeAfter instanceof Element ) ) {\n\t\t\t\t/**\n\t\t\t\t * The node after the merge position must be an element.\n\t\t\t\t *\n\t\t\t\t * @error schema-check-merge-no-element-after\n\t\t\t\t */\n\t\t\t\tthrow new CKEditorError( 'schema-check-merge-no-element-after: The node after the merge position must be an element.' );\n\t\t\t}\n\n\t\t\treturn this.checkMerge( nodeBefore, nodeAfter );\n\t\t}\n\n\t\tfor ( const child of elementToMerge.getChildren() ) {\n\t\t\tif ( !this.checkChild( positionOrBaseElement, child ) ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Allows registering a callback to the {@link #checkChild} method calls.\n\t *\n\t * Callbacks allow you to implement rules which are not otherwise possible to achieve\n\t * by using the declarative API of {@link module:engine/model/schema~SchemaItemDefinition}.\n\t * For example, by using this method you can disallow elements in specific contexts.\n\t *\n\t * This method is a shorthand for using the {@link #event:checkChild} event. For even better control,\n\t * you can use that event instead.\n\t *\n\t * Example:\n\t *\n\t *\t\t// Disallow heading1 directly inside a blockQuote.\n\t *\t\tschema.addChildCheck( ( context, childDefinition ) => {\n\t *\t\t\tif ( context.endsWith( 'blockQuote' ) && childDefinition.name == 'heading1' ) {\n\t *\t\t\t\treturn false;\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t * Which translates to:\n\t *\n\t *\t\tschema.on( 'checkChild', ( evt, args ) => {\n\t *\t\t\tconst context = args[ 0 ];\n\t *\t\t\tconst childDefinition = args[ 1 ];\n\t *\n\t *\t\t\tif ( context.endsWith( 'blockQuote' ) && childDefinition && childDefinition.name == 'heading1' ) {\n\t *\t\t\t\t// Prevent next listeners from being called.\n\t *\t\t\t\tevt.stop();\n\t *\t\t\t\t// Set the checkChild()'s return value.\n\t *\t\t\t\tevt.return = false;\n\t *\t\t\t}\n\t *\t\t}, { priority: 'high' } );\n\t *\n\t * @param {Function} callback The callback to be called. It is called with two parameters:\n\t * {@link module:engine/model/schema~SchemaContext} (context) instance and\n\t * {@link module:engine/model/schema~SchemaCompiledItemDefinition} (child-to-check definition).\n\t * The callback may return `true/false` to override `checkChild()`'s return value. If it does not return\n\t * a boolean value, the default algorithm (or other callbacks) will define `checkChild()`'s return value.\n\t */\n\taddChildCheck( callback ) {\n\t\tthis.on( 'checkChild', ( evt, [ ctx, childDef ] ) => {\n\t\t\t// checkChild() was called with a non-registered child.\n\t\t\t// In 99% cases such check should return false, so not to overcomplicate all callbacks\n\t\t\t// don't even execute them.\n\t\t\tif ( !childDef ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst retValue = callback( ctx, childDef );\n\n\t\t\tif ( typeof retValue == 'boolean' ) {\n\t\t\t\tevt.stop();\n\t\t\t\tevt.return = retValue;\n\t\t\t}\n\t\t}, { priority: 'high' } );\n\t}\n\n\t/**\n\t * Allows registering a callback to the {@link #checkAttribute} method calls.\n\t *\n\t * Callbacks allow you to implement rules which are not otherwise possible to achieve\n\t * by using the declarative API of {@link module:engine/model/schema~SchemaItemDefinition}.\n\t * For example, by using this method you can disallow attribute if node to which it is applied\n\t * is contained within some other element (e.g. you want to disallow `bold` on `$text` within `heading1`).\n\t *\n\t * This method is a shorthand for using the {@link #event:checkAttribute} event. For even better control,\n\t * you can use that event instead.\n\t *\n\t * Example:\n\t *\n\t *\t\t// Disallow bold on $text inside heading1.\n\t *\t\tschema.addChildCheck( ( context, attributeName ) => {\n\t *\t\t\tif ( context.endsWith( 'heading1 $text' ) && attributeName == 'bold' ) {\n\t *\t\t\t\treturn false;\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t * Which translates to:\n\t *\n\t *\t\tschema.on( 'checkAttribute', ( evt, args ) => {\n\t *\t\t\tconst context = args[ 0 ];\n\t *\t\t\tconst attributeName = args[ 1 ];\n\t *\n\t *\t\t\tif ( context.endsWith( 'heading1 $text' ) && attributeName == 'bold' ) {\n\t *\t\t\t\t// Prevent next listeners from being called.\n\t *\t\t\t\tevt.stop();\n\t *\t\t\t\t// Set the checkAttribute()'s return value.\n\t *\t\t\t\tevt.return = false;\n\t *\t\t\t}\n\t *\t\t}, { priority: 'high' } );\n\t *\n\t * @param {Function} callback The callback to be called. It is called with two parameters:\n\t * {@link module:engine/model/schema~SchemaContext} (context) instance and attribute name.\n\t * The callback may return `true/false` to override `checkAttribute()`'s return value. If it does not return\n\t * a boolean value, the default algorithm (or other callbacks) will define `checkAttribute()`'s return value.\n\t */\n\taddAttributeCheck( callback ) {\n\t\tthis.on( 'checkAttribute', ( evt, [ ctx, attributeName ] ) => {\n\t\t\tconst retValue = callback( ctx, attributeName );\n\n\t\t\tif ( typeof retValue == 'boolean' ) {\n\t\t\t\tevt.stop();\n\t\t\t\tevt.return = retValue;\n\t\t\t}\n\t\t}, { priority: 'high' } );\n\t}\n\n\t/**\n\t * Returns the lowest {@link module:engine/model/schema~Schema#isLimit limit element} containing the entire\n\t * selection/range/position or the root otherwise.\n\t *\n\t * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection|\n\t * module:engine/model/range~Range|module:engine/model/position~Position} selectionOrRangeOrPosition\n\t * The selection/range/position to check.\n\t * @returns {module:engine/model/element~Element} The lowest limit element containing\n\t * the entire `selectionOrRangeOrPosition`.\n\t */\n\tgetLimitElement( selectionOrRangeOrPosition ) {\n\t\tlet element;\n\n\t\tif ( selectionOrRangeOrPosition instanceof Position ) {\n\t\t\telement = selectionOrRangeOrPosition.parent;\n\t\t} else {\n\t\t\tconst ranges = selectionOrRangeOrPosition instanceof Range ?\n\t\t\t\t[ selectionOrRangeOrPosition ] :\n\t\t\t\tArray.from( selectionOrRangeOrPosition.getRanges() );\n\n\t\t\t// Find the common ancestor for all selection's ranges.\n\t\t\telement = ranges\n\t\t\t\t.reduce( ( element, range ) => {\n\t\t\t\t\tconst rangeCommonAncestor = range.getCommonAncestor();\n\n\t\t\t\t\tif ( !element ) {\n\t\t\t\t\t\treturn rangeCommonAncestor;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn element.getCommonAncestor( rangeCommonAncestor, { includeSelf: true } );\n\t\t\t\t}, null );\n\t\t}\n\n\t\twhile ( !this.isLimit( element ) ) {\n\t\t\tif ( element.parent ) {\n\t\t\t\telement = element.parent;\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn element;\n\t}\n\n\t/**\n\t * Checks whether the attribute is allowed in selection:\n\t *\n\t * * if the selection is not collapsed, then checks if the attribute is allowed on any of nodes in that range,\n\t * * if the selection is collapsed, then checks if on the selection position there's a text with the\n\t * specified attribute allowed.\n\t *\n\t * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection\n\t * Selection which will be checked.\n\t * @param {String} attribute The name of the attribute to check.\n\t * @returns {Boolean}\n\t */\n\tcheckAttributeInSelection( selection, attribute ) {\n\t\tif ( selection.isCollapsed ) {\n\t\t\tconst firstPosition = selection.getFirstPosition();\n\t\t\tconst context = [\n\t\t\t\t...firstPosition.getAncestors(),\n\t\t\t\tnew Text( '', selection.getAttributes() )\n\t\t\t];\n\n\t\t\t// Check whether schema allows for a text with the attribute in the selection.\n\t\t\treturn this.checkAttribute( context, attribute );\n\t\t} else {\n\t\t\tconst ranges = selection.getRanges();\n\n\t\t\t// For all ranges, check nodes in them until you find a node that is allowed to have the attribute.\n\t\t\tfor ( const range of ranges ) {\n\t\t\t\tfor ( const value of range ) {\n\t\t\t\t\tif ( this.checkAttribute( value.item, attribute ) ) {\n\t\t\t\t\t\t// If we found a node that is allowed to have the attribute, return true.\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// If we haven't found such node, return false.\n\t\treturn false;\n\t}\n\n\t/**\n\t * Transforms the given set of ranges into a set of ranges where the given attribute is allowed (and can be applied).\n\t *\n\t * @param {Array.} ranges Ranges to be validated.\n\t * @param {String} attribute The name of the attribute to check.\n\t * @returns {Iterator.} Ranges in which the attribute is allowed.\n\t */\n\t* getValidRanges( ranges, attribute ) {\n\t\tranges = convertToMinimalFlatRanges( ranges );\n\n\t\tfor ( const range of ranges ) {\n\t\t\tyield* this._getValidRangesForRange( range, attribute );\n\t\t}\n\t}\n\n\t/**\n\t * Takes a flat range and an attribute name. Traverses the range recursively and deeply to find and return all ranges\n\t * inside the given range on which the attribute can be applied.\n\t *\n\t * This is a helper function for {@link ~Schema#getValidRanges}.\n\t *\n\t * @private\n\t * @param {module:engine/model/range~Range} range Range to process.\n\t * @param {String} attribute The name of the attribute to check.\n\t * @returns {Iterator.} Ranges in which the attribute is allowed.\n\t */\n\t* _getValidRangesForRange( range, attribute ) {\n\t\tlet start = range.start;\n\t\tlet end = range.start;\n\n\t\tfor ( const item of range.getItems( { shallow: true } ) ) {\n\t\t\tif ( item.is( 'element' ) ) {\n\t\t\t\tyield* this._getValidRangesForRange( Range._createIn( item ), attribute );\n\t\t\t}\n\n\t\t\tif ( !this.checkAttribute( item, attribute ) ) {\n\t\t\t\tif ( !start.isEqual( end ) ) {\n\t\t\t\t\tyield new Range( start, end );\n\t\t\t\t}\n\n\t\t\t\tstart = Position._createAfter( item );\n\t\t\t}\n\n\t\t\tend = Position._createAfter( item );\n\t\t}\n\n\t\tif ( !start.isEqual( end ) ) {\n\t\t\tyield new Range( start, end );\n\t\t}\n\t}\n\n\t/**\n\t * Basing on given `position`, finds and returns a {@link module:engine/model/range~Range range} which is\n\t * nearest to that `position` and is a correct range for selection.\n\t *\n\t * The correct selection range might be collapsed when it is located in a position where the text node can be placed.\n\t * Non-collapsed range is returned when selection can be placed around element marked as an \"object\" in\n\t * the {@link module:engine/model/schema~Schema schema}.\n\t *\n\t * Direction of searching for the nearest correct selection range can be specified as:\n\t *\n\t * * `both` - searching will be performed in both ways,\n\t * * `forward` - searching will be performed only forward,\n\t * * `backward` - searching will be performed only backward.\n\t *\n\t * When valid selection range cannot be found, `null` is returned.\n\t *\n\t * @param {module:engine/model/position~Position} position Reference position where new selection range should be looked for.\n\t * @param {'both'|'forward'|'backward'} [direction='both'] Search direction.\n\t * @returns {module:engine/model/range~Range|null} Nearest selection range or `null` if one cannot be found.\n\t */\n\tgetNearestSelectionRange( position, direction = 'both' ) {\n\t\t// Return collapsed range if provided position is valid.\n\t\tif ( this.checkChild( position, '$text' ) ) {\n\t\t\treturn new Range( position );\n\t\t}\n\n\t\tlet backwardWalker, forwardWalker;\n\n\t\tif ( direction == 'both' || direction == 'backward' ) {\n\t\t\tbackwardWalker = new TreeWalker( { startPosition: position, direction: 'backward' } );\n\t\t}\n\n\t\tif ( direction == 'both' || direction == 'forward' ) {\n\t\t\tforwardWalker = new TreeWalker( { startPosition: position } );\n\t\t}\n\n\t\tfor ( const data of combineWalkers( backwardWalker, forwardWalker ) ) {\n\t\t\tconst type = ( data.walker == backwardWalker ? 'elementEnd' : 'elementStart' );\n\t\t\tconst value = data.value;\n\n\t\t\tif ( value.type == type && this.isObject( value.item ) ) {\n\t\t\t\treturn Range._createOn( value.item );\n\t\t\t}\n\n\t\t\tif ( this.checkChild( value.nextPosition, '$text' ) ) {\n\t\t\t\treturn new Range( value.nextPosition );\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Tries to find position ancestors that allows to insert given node.\n\t * It starts searching from the given position and goes node by node to the top of the model tree\n\t * as long as {@link module:engine/model/schema~Schema#isLimit limit element},\n\t * {@link module:engine/model/schema~Schema#isObject object element} or top-most ancestor won't be reached.\n\t *\n\t * @params {module:engine/model/node~Node} node Node for which allowed parent should be found.\n\t * @params {module:engine/model/position~Position} position Position from searching will start.\n\t * @returns {module:engine/model/element~Element|null} element Allowed parent or null if nothing was found.\n\t */\n\tfindAllowedParent( node, position ) {\n\t\tlet parent = position.parent;\n\n\t\twhile ( parent ) {\n\t\t\tif ( this.checkChild( parent, node ) ) {\n\t\t\t\treturn parent;\n\t\t\t}\n\n\t\t\t// Do not split limit elements.\n\t\t\tif ( this.isLimit( parent ) ) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tparent = parent.parent;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Removes attributes disallowed by the schema.\n\t *\n\t * @param {Iterable.} nodes Nodes that will be filtered.\n\t * @param {module:engine/model/writer~Writer} writer\n\t */\n\tremoveDisallowedAttributes( nodes, writer ) {\n\t\tfor ( const node of nodes ) {\n\t\t\tfor ( const attribute of node.getAttributeKeys() ) {\n\t\t\t\tif ( !this.checkAttribute( node, attribute ) ) {\n\t\t\t\t\twriter.removeAttribute( attribute, node );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( node.is( 'element' ) ) {\n\t\t\t\tthis.removeDisallowedAttributes( node.getChildren(), writer );\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Creates an instance of the schema context.\n\t *\n\t * @param {module:engine/model/schema~SchemaContextDefinition} context\n\t * @returns {module:engine/model/schema~SchemaContext}\n\t */\n\tcreateContext( context ) {\n\t\treturn new SchemaContext( context );\n\t}\n\n\t/**\n\t * @private\n\t */\n\t_clearCache() {\n\t\tthis._compiledDefinitions = null;\n\t}\n\n\t/**\n\t * @private\n\t */\n\t_compile() {\n\t\tconst compiledDefinitions = {};\n\t\tconst sourceRules = this._sourceDefinitions;\n\t\tconst itemNames = Object.keys( sourceRules );\n\n\t\tfor ( const itemName of itemNames ) {\n\t\t\tcompiledDefinitions[ itemName ] = compileBaseItemRule( sourceRules[ itemName ], itemName );\n\t\t}\n\n\t\tfor ( const itemName of itemNames ) {\n\t\t\tcompileAllowContentOf( compiledDefinitions, itemName );\n\t\t}\n\n\t\tfor ( const itemName of itemNames ) {\n\t\t\tcompileAllowWhere( compiledDefinitions, itemName );\n\t\t}\n\n\t\tfor ( const itemName of itemNames ) {\n\t\t\tcompileAllowAttributesOf( compiledDefinitions, itemName );\n\t\t\tcompileInheritPropertiesFrom( compiledDefinitions, itemName );\n\t\t}\n\n\t\tfor ( const itemName of itemNames ) {\n\t\t\tcleanUpAllowIn( compiledDefinitions, itemName );\n\t\t\tcleanUpAllowAttributes( compiledDefinitions, itemName );\n\t\t}\n\n\t\tthis._compiledDefinitions = compiledDefinitions;\n\t}\n\n\t/**\n\t * @private\n\t * @param {module:engine/model/schema~SchemaCompiledItemDefinition} def\n\t * @param {module:engine/model/schema~SchemaContext} context\n\t * @param {Number} contextItemIndex\n\t */\n\t_checkContextMatch( def, context, contextItemIndex = context.length - 1 ) {\n\t\tconst contextItem = context.getItem( contextItemIndex );\n\n\t\tif ( def.allowIn.includes( contextItem.name ) ) {\n\t\t\tif ( contextItemIndex == 0 ) {\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\tconst parentRule = this.getDefinition( contextItem );\n\n\t\t\t\treturn this._checkContextMatch( parentRule, context, contextItemIndex - 1 );\n\t\t\t}\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n\nmix( Schema, ObservableMixin );\n\n/**\n * Event fired when the {@link #checkChild} method is called. It allows plugging in\n * additional behavior – e.g. implementing rules which cannot be defined using the declarative\n * {@link module:engine/model/schema~SchemaItemDefinition} interface.\n *\n * **Note:** The {@link #addChildCheck} method is a more handy way to register callbacks. Internally,\n * it registers a listener to this event but comes with a simpler API and it is the recommended choice\n * in most of the cases.\n *\n * The {@link #checkChild} method fires an event because it is\n * {@link module:utils/observablemixin~ObservableMixin#decorate decorated} with it. Thanks to that you can\n * use this event in a various way, but the most important use case is overriding standard behaviour of the\n * `checkChild()` method. Let's see a typical listener template:\n *\n *\t\tschema.on( 'checkChild', ( evt, args ) => {\n *\t\t\tconst context = args[ 0 ];\n *\t\t\tconst childDefinition = args[ 1 ];\n *\t\t}, { priority: 'high' } );\n *\n * The listener is added with a `high` priority to be executed before the default method is really called. The `args` callback\n * parameter contains arguments passed to `checkChild( context, child )`. However, the `context` parameter is already\n * normalized to a {@link module:engine/model/schema~SchemaContext} instance and `child` to a\n * {@link module:engine/model/schema~SchemaCompiledItemDefinition} instance, so you don't have to worry about\n * the various ways how `context` and `child` may be passed to `checkChild()`.\n *\n * **Note:** `childDefinition` may be `undefined` if `checkChild()` was called with a non-registered element.\n *\n * So, in order to implement a rule \"disallow `heading1` in `blockQuote`\" you can add such a listener:\n *\n *\t\tschema.on( 'checkChild', ( evt, args ) => {\n *\t\t\tconst context = args[ 0 ];\n *\t\t\tconst childDefinition = args[ 1 ];\n *\n *\t\t\tif ( context.endsWith( 'blockQuote' ) && childDefinition && childDefinition.name == 'heading1' ) {\n *\t\t\t\t// Prevent next listeners from being called.\n *\t\t\t\tevt.stop();\n *\t\t\t\t// Set the checkChild()'s return value.\n *\t\t\t\tevt.return = false;\n *\t\t\t}\n *\t\t}, { priority: 'high' } );\n *\n * Allowing elements in specific contexts will be a far less common use case, because it's normally handled by\n * `allowIn` rule from {@link module:engine/model/schema~SchemaItemDefinition} but if you have a complex scenario\n * where `listItem` should be allowed only in element `foo` which must be in element `bar`, then this would be the way:\n *\n *\t\tschema.on( 'checkChild', ( evt, args ) => {\n *\t\t\tconst context = args[ 0 ];\n *\t\t\tconst childDefinition = args[ 1 ];\n *\n *\t\t\tif ( context.endsWith( 'bar foo' ) && childDefinition.name == 'listItem' ) {\n *\t\t\t\t// Prevent next listeners from being called.\n *\t\t\t\tevt.stop();\n *\t\t\t\t// Set the checkChild()'s return value.\n *\t\t\t\tevt.return = true;\n *\t\t\t}\n *\t\t}, { priority: 'high' } );\n *\n * @event checkChild\n * @param {Array} args The `checkChild()`'s arguments.\n */\n\n/**\n * Event fired when the {@link #checkAttribute} method is called. It allows plugging in\n * additional behavior – e.g. implementing rules which cannot be defined using the declarative\n * {@link module:engine/model/schema~SchemaItemDefinition} interface.\n *\n * **Note:** The {@link #addAttributeCheck} method is a more handy way to register callbacks. Internally,\n * it registers a listener to this event but comes with a simpler API and it is the recommended choice\n * in most of the cases.\n *\n * The {@link #checkAttribute} method fires an event because it's\n * {@link module:utils/observablemixin~ObservableMixin#decorate decorated} with it. Thanks to that you can\n * use this event in a various way, but the most important use case is overriding standard behaviour of the\n * `checkAttribute()` method. Let's see a typical listener template:\n *\n *\t\tschema.on( 'checkAttribute', ( evt, args ) => {\n *\t\t\tconst context = args[ 0 ];\n *\t\t\tconst attributeName = args[ 1 ];\n *\t\t}, { priority: 'high' } );\n *\n * The listener is added with a `high` priority to be executed before the default method is really called. The `args` callback\n * parameter contains arguments passed to `checkAttribute( context, attributeName )`. However, the `context` parameter is already\n * normalized to a {@link module:engine/model/schema~SchemaContext} instance, so you don't have to worry about\n * the various ways how `context` may be passed to `checkAttribute()`.\n *\n * So, in order to implement a rule \"disallow `bold` in a text which is in a `heading1` you can add such a listener:\n *\n *\t\tschema.on( 'checkAttribute', ( evt, args ) => {\n *\t\t\tconst context = args[ 0 ];\n *\t\t\tconst atributeName = args[ 1 ];\n *\n *\t\t\tif ( context.endsWith( 'heading1 $text' ) && attributeName == 'bold' ) {\n *\t\t\t\t// Prevent next listeners from being called.\n *\t\t\t\tevt.stop();\n *\t\t\t\t// Set the checkAttribute()'s return value.\n *\t\t\t\tevt.return = false;\n *\t\t\t}\n *\t\t}, { priority: 'high' } );\n *\n * Allowing attributes in specific contexts will be a far less common use case, because it's normally handled by\n * `allowAttributes` rule from {@link module:engine/model/schema~SchemaItemDefinition} but if you have a complex scenario\n * where `bold` should be allowed only in element `foo` which must be in element `bar`, then this would be the way:\n *\n *\t\tschema.on( 'checkAttribute', ( evt, args ) => {\n *\t\t\tconst context = args[ 0 ];\n *\t\t\tconst atributeName = args[ 1 ];\n *\n *\t\t\tif ( context.endsWith( 'bar foo $text' ) && attributeName == 'bold' ) {\n *\t\t\t\t// Prevent next listeners from being called.\n *\t\t\t\tevt.stop();\n *\t\t\t\t// Set the checkAttribute()'s return value.\n *\t\t\t\tevt.return = true;\n *\t\t\t}\n *\t\t}, { priority: 'high' } );\n *\n * @event checkAttribute\n * @param {Array} args The `checkAttribute()`'s arguments.\n */\n\n/**\n * A definition of a {@link module:engine/model/schema~Schema schema} item.\n *\n * You can define the following rules:\n *\n * * `allowIn` – A string or an array of strings. Defines in which other items this item will be allowed.\n * * `allowAttributes` – A string or an array of strings. Defines allowed attributes of the given item.\n * * `allowContentOf` – A string or an array of strings. Inherits \"allowed children\" from other items.\n * * `allowWhere` – A string or an array of strings. Inherits \"allowed in\" from other items.\n * * `allowAttributesOf` – A string or an array of strings. Inherits attributes from other items.\n * * `inheritTypesFrom` – A string or an array of strings. Inherits `is*` properties of other items.\n * * `inheritAllFrom` – A string. A shorthand for `allowContentOf`, `allowWhere`, `allowAttributesOf`, `inheritTypesFrom`.\n * * Additionally, you can define the following `is*` properties: `isBlock`, `isLimit`, `isObject`. Read about them below.\n *\n * # The is* properties\n *\n * There are 3 commonly used `is*` properties. Their role is to assign additional semantics to schema items.\n * You can define more properties but you will also need to implement support for them in the existing editor features.\n *\n * * `isBlock` – Whether this item is paragraph-like. Generally speaking, content is usually made out of blocks\n * like paragraphs, list items, images, headings, etc. All these elements are marked as blocks. A block\n * should not allow another block inside. Note: There is also the `$block` generic item which has `isBlock` set to `true`.\n * Most block type items will inherit from `$block` (through `inheritAllFrom`).\n * * `isLimit` – It can be understood as whether this element should not be split by Enter.\n * Examples of limit elements: `$root`, table cell, image caption, etc. In other words, all actions that happen inside\n * a limit element are limited to its content. **Note:** All objects (`isObject`) are treated as limit elements, too.\n * * `isObject` – Whether an item is \"self-contained\" and should be treated as a whole. Examples of object elements:\n * `image`, `table`, `video`, etc. **Note:** An object is also a limit, so\n * {@link module:engine/model/schema~Schema#isLimit `isLimit()`}\n * returns `true` for object elements automatically.\n *\n * # Generic items\n *\n * There are three basic generic items: `$root`, `$block` and `$text`.\n * They are defined as follows:\n *\n *\t\tthis.schema.register( '$root', {\n *\t\t\tisLimit: true\n *\t\t} );\n *\t\tthis.schema.register( '$block', {\n *\t\t\tallowIn: '$root',\n *\t\t\tisBlock: true\n *\t\t} );\n *\t\tthis.schema.register( '$text', {\n *\t\t\tallowIn: '$block'\n *\t\t} );\n *\n * They reflect typical editor content that is contained within one root, consists of several blocks\n * (paragraphs, lists items, headings, images) which, in turn, may contain text inside.\n *\n * By inheriting from the generic items you can define new items which will get extended by other editor features.\n * Read more about generic types in the {@linkTODO Defining schema} guide.\n *\n * # Example definitions\n *\n * Allow `paragraph` in roots and block quotes:\n *\n *\t\tschema.register( 'paragraph', {\n *\t\t\tallowIn: [ '$root', 'blockQuote' ],\n *\t\t\tisBlock: true\n *\t\t} );\n *\n * Allow `paragraph` everywhere where `$block` is allowed (i.e. in `$root`):\n *\n *\t\tschema.register( 'paragraph', {\n *\t\t\tallowWhere: '$block',\n *\t\t\tisBlock: true\n *\t\t} );\n *\n * Make `image` a block object, which is allowed everywhere where `$block` is.\n * Also, allow `src` and `alt` attributes in it:\n *\n *\t\tschema.register( 'image', {\n *\t\t\tallowWhere: '$block',\n *\t\t\tallowAttributes: [ 'src', 'alt' ],\n *\t\t\tisBlock: true,\n *\t\t\tisObject: true\n *\t\t} );\n *\n * Make `caption` allowed in `image` and make it allow all the content of `$block`s (usually, `$text`).\n * Also, mark it as a limit element so it cannot be split:\n *\n *\t\tschema.register( 'caption', {\n *\t\t\tallowIn: 'image',\n *\t\t\tallowContentOf: '$block',\n *\t\t\tisLimit: true\n *\t\t} );\n *\n * Make `listItem` inherit all from `$block` but also allow additional attributes:\n *\n *\t\tschema.register( 'listItem', {\n *\t\t\tinheritAllFrom: '$block',\n *\t\t\tallowAttributes: [ 'listType', 'listIndent' ]\n *\t\t} );\n *\n * Which translates to:\n *\n *\t\tschema.register( 'listItem', {\n *\t\t\tallowWhere: '$block',\n *\t\t\tallowContentOf: '$block',\n *\t\t\tallowAttributesOf: '$block',\n *\t\t\tinheritTypesFrom: '$block',\n *\t\t\tallowAttributes: [ 'listType', 'listIndent' ]\n *\t\t} );\n *\n * # Tips\n *\n * * Check schema definitions of existing features to see how they are defined.\n * * If you want to publish your feature so other developers can use it, try to use\n * generic items as much as possible.\n * * Keep your model clean. Limit it to the actual data and store information in a normalized way.\n * * Remember about definining the `is*` properties. They do not affect the allowed structures, but they can\n * affect how the editor features treat your elements.\n *\n * @typedef {Object} module:engine/model/schema~SchemaItemDefinition\n */\n\n/**\n * A simplified version of {@link module:engine/model/schema~SchemaItemDefinition} after\n * compilation by the {@link module:engine/model/schema~Schema schema}.\n * Rules fed to the schema by {@link module:engine/model/schema~Schema#register}\n * and {@link module:engine/model/schema~Schema#extend} methods are defined in the\n * {@link module:engine/model/schema~SchemaItemDefinition} format.\n * Later on, they are compiled to `SchemaCompiledItemDefition` so when you use e.g.\n * the {@link module:engine/model/schema~Schema#getDefinition} method you get the compiled version.\n *\n * The compiled version contains only the following properties:\n *\n * * The `name` property,\n * * The `is*` properties,\n * * The `allowIn` array,\n * * The `allowAttributes` array.\n *\n * @typedef {Object} module:engine/model/schema~SchemaCompiledItemDefinition\n */\n\n/**\n * A schema context — a list of ancestors of a given position in the document.\n *\n * Considering such position:\n *\n *\t\t<$root>\n *\t\t\t
\n *\t\t\t\t\n *\t\t\t\t\t^\n *\t\t\t\t\n *\t\t\t
\n *\t\t$root>\n *\n * The context of this position is its {@link module:engine/model/position~Position#getAncestors lists of ancestors}:\n *\n *\t\t[ rootElement, blockQuoteElement, paragraphElement ]\n *\n * Contexts are used in the {@link module:engine/model/schema~Schema#event:checkChild `Schema#checkChild`} and\n * {@link module:engine/model/schema~Schema#event:checkAttribute `Schema#checkAttribute`} events as a definition\n * of a place in the document where the check occurs. The context instances are created based on the first arguments\n * of the {@link module:engine/model/schema~Schema#checkChild `Schema#checkChild()`} and\n * {@link module:engine/model/schema~Schema#checkAttribute `Schema#checkAttribute()`} methods so when\n * using these methods you need to use {@link module:engine/model/schema~SchemaContextDefinition}s.\n */\nexport class SchemaContext {\n\t/**\n\t * Creates an instance of the context.\n\t *\n\t * @param {module:engine/model/schema~SchemaContextDefinition} context\n\t */\n\tconstructor( context ) {\n\t\tif ( context instanceof SchemaContext ) {\n\t\t\treturn context;\n\t\t}\n\n\t\tif ( typeof context == 'string' ) {\n\t\t\tcontext = [ context ];\n\t\t} else if ( !Array.isArray( context ) ) {\n\t\t\t// `context` is item or position.\n\t\t\t// Position#getAncestors() doesn't accept any parameters but it works just fine here.\n\t\t\tcontext = context.getAncestors( { includeSelf: true } );\n\t\t}\n\n\t\tif ( context[ 0 ] && typeof context[ 0 ] != 'string' && context[ 0 ].is( 'documentFragment' ) ) {\n\t\t\tcontext.shift();\n\t\t}\n\n\t\tthis._items = context.map( mapContextItem );\n\t}\n\n\t/**\n\t * The number of items.\n\t *\n\t * @type {Number}\n\t */\n\tget length() {\n\t\treturn this._items.length;\n\t}\n\n\t/**\n\t * The last item (the lowest node).\n\t *\n\t * @type {module:engine/model/schema~SchemaContextItem}\n\t */\n\tget last() {\n\t\treturn this._items[ this._items.length - 1 ];\n\t}\n\n\t/**\n\t * Iterable interface.\n\t *\n\t * Iterates over all context items.\n\t *\n\t * @returns {Iterable.}\n\t */\n\t[ Symbol.iterator ]() {\n\t\treturn this._items[ Symbol.iterator ]();\n\t}\n\n\t/**\n\t * Returns a new schema context instance with an additional item.\n\t *\n\t * Item can be added as:\n\t *\n\t * \t\tconst context = new SchemaContext( [ '$root' ] );\n\t *\n\t * \t\t// An element.\n\t * \t\tconst fooElement = writer.createElement( 'fooElement' );\n\t * \t\tconst newContext = context.push( fooElement ); // [ '$root', 'fooElement' ]\n\t *\n\t * \t\t// A text node.\n\t * \t\tconst text = writer.createText( 'foobar' );\n\t * \t\tconst newContext = context.push( text ); // [ '$root', '$text' ]\n\t *\n\t * \t\t// A string (element name).\n\t * \t\tconst newContext = context.push( 'barElement' ); // [ '$root', 'barElement' ]\n\t *\n\t * **Note** {@link module:engine/model/node~Node} that is already in the model tree will be added as the only item\n\t * (without ancestors).\n\t *\n\t * @param {String|module:engine/model/node~Node|Array} item An item that will be added\n\t * to the current context.\n\t * @returns {module:engine/model/schema~SchemaContext} A new schema context instance with an additional item.\n\t */\n\tpush( item ) {\n\t\tconst ctx = new SchemaContext( [ item ] );\n\n\t\tctx._items = [ ...this._items, ...ctx._items ];\n\n\t\treturn ctx;\n\t}\n\n\t/**\n\t * Gets an item on the given index.\n\t *\n\t * @returns {module:engine/model/schema~SchemaContextItem}\n\t */\n\tgetItem( index ) {\n\t\treturn this._items[ index ];\n\t}\n\n\t/**\n\t * Returns the names of items.\n\t *\n\t * @returns {Iterable.}\n\t */\n\t* getNames() {\n\t\tyield* this._items.map( item => item.name );\n\t}\n\n\t/**\n\t * Checks whether the context ends with the given nodes.\n\t *\n\t *\t\tconst ctx = new SchemaContext( [ rootElement, paragraphElement, textNode ] );\n\t *\n\t *\t\tctx.endsWith( '$text' ); // -> true\n\t *\t\tctx.endsWith( 'paragraph $text' ); // -> true\n\t *\t\tctx.endsWith( '$root' ); // -> false\n\t *\t\tctx.endsWith( 'paragraph' ); // -> false\n\t *\n\t * @param {String} query\n\t * @returns {Boolean}\n\t */\n\tendsWith( query ) {\n\t\treturn Array.from( this.getNames() ).join( ' ' ).endsWith( query );\n\t}\n}\n\n/**\n * The definition of a {@link module:engine/model/schema~SchemaContext schema context}.\n *\n * Contexts can be created in multiple ways:\n *\n * * By defining a **node** – in this cases this node and all its ancestors will be used.\n * * By defining a **position** in the document – in this case all its ancestors will be used.\n * * By defining an **array of nodes** – in this case this array defines the entire context.\n * * By defining a **name of node** - in this case node will be \"mocked\". It is not recommended because context\n * will be unrealistic (e.g. attributes of these nodes are not specified). However, at times this may be the only\n * way to define the context (e.g. when checking some hypothetical situation).\n * * By defining an **array of node names** (potentially, mixed with real nodes) – The same as **name of node**\n * but it is possible to create a path.\n * * By defining a {@link module:engine/model/schema~SchemaContext} instance - in this case the same instance as provided\n * will be return.\n *\n * Examples of context definitions passed to the {@link module:engine/model/schema~Schema#checkChild `Schema#checkChild()`}\n * method:\n *\n *\t\t// Assuming that we have a $root > blockQuote > paragraph structure, the following code\n *\t\t// will check node 'foo' in the following context:\n *\t\t// [ rootElement, blockQuoteElement, paragraphElement ]\n *\t\tconst contextDefinition = paragraphElement;\n * \t\tconst childToCheck = 'foo';\n *\t\tschema.checkChild( contextDefinition, childToCheck );\n *\n *\t\t// Also check in [ rootElement, blockQuoteElement, paragraphElement ].\n *\t\tschema.checkChild( model.createPositionAt( paragraphElement, 0 ), 'foo' );\n *\n *\t\t// Check in [ rootElement, paragraphElement ].\n *\t\tschema.checkChild( [ rootElement, paragraphElement ], 'foo' );\n *\n *\t\t// Check only fakeParagraphElement.\n *\t\tschema.checkChild( 'paragraph', 'foo' );\n *\n *\t\t// Check in [ fakeRootElement, fakeBarElement, paragraphElement ].\n *\t\tschema.checkChild( [ '$root', 'bar', paragraphElement ], 'foo' );\n *\n * All these `checkChild()` calls will fire {@link module:engine/model/schema~Schema#event:checkChild `Schema#checkChild`}\n * events in which `args[ 0 ]` is an instance of the context. Therefore, you can write a listener like this:\n *\n *\t\tschema.on( 'checkChild', ( evt, args ) => {\n *\t\t\tconst ctx = args[ 0 ];\n *\n *\t\t\tconsole.log( Array.from( ctx.getNames() ) );\n *\t\t} );\n *\n * Which will log the following:\n *\n *\t\t[ '$root', 'blockQuote', 'paragraph' ]\n *\t\t[ '$root', 'paragraph' ]\n *\t\t[ '$root', 'bar', 'paragraph' ]\n *\n * Note: When using the {@link module:engine/model/schema~Schema#checkAttribute `Schema#checkAttribute()`} method\n * you may want to check whether a text node may have an attribute. A {@link module:engine/model/text~Text} is a\n * correct way to define a context so you can do this:\n *\n *\t\tschema.checkAttribute( textNode, 'bold' );\n *\n * But sometimes you want to check whether a text at a given position might've had some attribute,\n * in which case you can create a context by mising an array of elements with a `'$text'` string:\n *\n *\t\t// Check in [ rootElement, paragraphElement, textNode ].\n *\t\tschema.checkChild( [ ...positionInParagraph.getAncestors(), '$text' ], 'bold' );\n *\n * @typedef {module:engine/model/node~Node|module:engine/model/position~Position|module:engine/model/schema~SchemaContext|\n * String|Array.} module:engine/model/schema~SchemaContextDefinition\n */\n\n/**\n * An item of the {@link module:engine/model/schema~SchemaContext schema context}.\n *\n * It contains 3 properties:\n *\n * * `name` – the name of this item,\n * * `* getAttributeKeys()` – a generator of keys of item attributes,\n * * `getAttribute( keyName )` – a method to get attribute values.\n *\n * The context item interface is a highly simplified version of {@link module:engine/model/node~Node} and its role\n * is to expose only the information which schema checks are able to provide (which is the name of the node and\n * node's attributes).\n *\n *\t\tschema.on( 'checkChild', ( evt, args ) => {\n *\t\t\tconst ctx = args[ 0 ];\n *\t\t\tconst firstItem = ctx.getItem( 0 );\n *\n *\t\t\tconsole.log( firstItem.name ); // -> '$root'\n *\t\t\tconsole.log( firstItem.getAttribute( 'foo' ) ); // -> 'bar'\n *\t\t\tconsole.log( Array.from( firstItem.getAttributeKeys() ) ); // -> [ 'foo', 'faa' ]\n *\t\t} );\n *\n * @typedef {Object} module:engine/model/schema~SchemaContextItem\n */\n\nfunction compileBaseItemRule( sourceItemRules, itemName ) {\n\tconst itemRule = {\n\t\tname: itemName,\n\n\t\tallowIn: [],\n\t\tallowContentOf: [],\n\t\tallowWhere: [],\n\n\t\tallowAttributes: [],\n\t\tallowAttributesOf: [],\n\n\t\tinheritTypesFrom: []\n\t};\n\n\tcopyTypes( sourceItemRules, itemRule );\n\n\tcopyProperty( sourceItemRules, itemRule, 'allowIn' );\n\tcopyProperty( sourceItemRules, itemRule, 'allowContentOf' );\n\tcopyProperty( sourceItemRules, itemRule, 'allowWhere' );\n\n\tcopyProperty( sourceItemRules, itemRule, 'allowAttributes' );\n\tcopyProperty( sourceItemRules, itemRule, 'allowAttributesOf' );\n\n\tcopyProperty( sourceItemRules, itemRule, 'inheritTypesFrom' );\n\n\tmakeInheritAllWork( sourceItemRules, itemRule );\n\n\treturn itemRule;\n}\n\nfunction compileAllowContentOf( compiledDefinitions, itemName ) {\n\tfor ( const allowContentOfItemName of compiledDefinitions[ itemName ].allowContentOf ) {\n\t\t// The allowContentOf property may point to an unregistered element.\n\t\tif ( compiledDefinitions[ allowContentOfItemName ] ) {\n\t\t\tconst allowedChildren = getAllowedChildren( compiledDefinitions, allowContentOfItemName );\n\n\t\t\tallowedChildren.forEach( allowedItem => {\n\t\t\t\tallowedItem.allowIn.push( itemName );\n\t\t\t} );\n\t\t}\n\t}\n\n\tdelete compiledDefinitions[ itemName ].allowContentOf;\n}\n\nfunction compileAllowWhere( compiledDefinitions, itemName ) {\n\tfor ( const allowWhereItemName of compiledDefinitions[ itemName ].allowWhere ) {\n\t\tconst inheritFrom = compiledDefinitions[ allowWhereItemName ];\n\n\t\t// The allowWhere property may point to an unregistered element.\n\t\tif ( inheritFrom ) {\n\t\t\tconst allowedIn = inheritFrom.allowIn;\n\n\t\t\tcompiledDefinitions[ itemName ].allowIn.push( ...allowedIn );\n\t\t}\n\t}\n\n\tdelete compiledDefinitions[ itemName ].allowWhere;\n}\n\nfunction compileAllowAttributesOf( compiledDefinitions, itemName ) {\n\tfor ( const allowAttributeOfItem of compiledDefinitions[ itemName ].allowAttributesOf ) {\n\t\tconst inheritFrom = compiledDefinitions[ allowAttributeOfItem ];\n\n\t\tif ( inheritFrom ) {\n\t\t\tconst inheritAttributes = inheritFrom.allowAttributes;\n\n\t\t\tcompiledDefinitions[ itemName ].allowAttributes.push( ...inheritAttributes );\n\t\t}\n\t}\n\n\tdelete compiledDefinitions[ itemName ].allowAttributesOf;\n}\n\nfunction compileInheritPropertiesFrom( compiledDefinitions, itemName ) {\n\tconst item = compiledDefinitions[ itemName ];\n\n\tfor ( const inheritPropertiesOfItem of item.inheritTypesFrom ) {\n\t\tconst inheritFrom = compiledDefinitions[ inheritPropertiesOfItem ];\n\n\t\tif ( inheritFrom ) {\n\t\t\tconst typeNames = Object.keys( inheritFrom ).filter( name => name.startsWith( 'is' ) );\n\n\t\t\tfor ( const name of typeNames ) {\n\t\t\t\tif ( !( name in item ) ) {\n\t\t\t\t\titem[ name ] = inheritFrom[ name ];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tdelete item.inheritTypesFrom;\n}\n\n// Remove items which weren't registered (because it may break some checks or we'd need to complicate them).\n// Make sure allowIn doesn't contain repeated values.\nfunction cleanUpAllowIn( compiledDefinitions, itemName ) {\n\tconst itemRule = compiledDefinitions[ itemName ];\n\tconst existingItems = itemRule.allowIn.filter( itemToCheck => compiledDefinitions[ itemToCheck ] );\n\n\titemRule.allowIn = Array.from( new Set( existingItems ) );\n}\n\nfunction cleanUpAllowAttributes( compiledDefinitions, itemName ) {\n\tconst itemRule = compiledDefinitions[ itemName ];\n\n\titemRule.allowAttributes = Array.from( new Set( itemRule.allowAttributes ) );\n}\n\nfunction copyTypes( sourceItemRules, itemRule ) {\n\tfor ( const sourceItemRule of sourceItemRules ) {\n\t\tconst typeNames = Object.keys( sourceItemRule ).filter( name => name.startsWith( 'is' ) );\n\n\t\tfor ( const name of typeNames ) {\n\t\t\titemRule[ name ] = sourceItemRule[ name ];\n\t\t}\n\t}\n}\n\nfunction copyProperty( sourceItemRules, itemRule, propertyName ) {\n\tfor ( const sourceItemRule of sourceItemRules ) {\n\t\tif ( typeof sourceItemRule[ propertyName ] == 'string' ) {\n\t\t\titemRule[ propertyName ].push( sourceItemRule[ propertyName ] );\n\t\t} else if ( Array.isArray( sourceItemRule[ propertyName ] ) ) {\n\t\t\titemRule[ propertyName ].push( ...sourceItemRule[ propertyName ] );\n\t\t}\n\t}\n}\n\nfunction makeInheritAllWork( sourceItemRules, itemRule ) {\n\tfor ( const sourceItemRule of sourceItemRules ) {\n\t\tconst inheritFrom = sourceItemRule.inheritAllFrom;\n\n\t\tif ( inheritFrom ) {\n\t\t\titemRule.allowContentOf.push( inheritFrom );\n\t\t\titemRule.allowWhere.push( inheritFrom );\n\t\t\titemRule.allowAttributesOf.push( inheritFrom );\n\t\t\titemRule.inheritTypesFrom.push( inheritFrom );\n\t\t}\n\t}\n}\n\nfunction getAllowedChildren( compiledDefinitions, itemName ) {\n\tconst itemRule = compiledDefinitions[ itemName ];\n\n\treturn getValues( compiledDefinitions ).filter( def => def.allowIn.includes( itemRule.name ) );\n}\n\nfunction getValues( obj ) {\n\treturn Object.keys( obj ).map( key => obj[ key ] );\n}\n\nfunction mapContextItem( ctxItem ) {\n\tif ( typeof ctxItem == 'string' ) {\n\t\treturn {\n\t\t\tname: ctxItem,\n\n\t\t\t* getAttributeKeys() {},\n\n\t\t\tgetAttribute() {}\n\t\t};\n\t} else {\n\t\treturn {\n\t\t\t// '$text' means text nodes and text proxies.\n\t\t\tname: ctxItem.is( 'element' ) ? ctxItem.name : '$text',\n\n\t\t\t* getAttributeKeys() {\n\t\t\t\tyield* ctxItem.getAttributeKeys();\n\t\t\t},\n\n\t\t\tgetAttribute( key ) {\n\t\t\t\treturn ctxItem.getAttribute( key );\n\t\t\t}\n\t\t};\n\t}\n}\n\n// Generator function returning values from provided walkers, switching between them at each iteration. If only one walker\n// is provided it will return data only from that walker.\n//\n// @param {module:engine/module/treewalker~TreeWalker} [backward] Walker iterating in backward direction.\n// @param {module:engine/module/treewalker~TreeWalker} [forward] Walker iterating in forward direction.\n// @returns {Iterable.