{"version":3,"sources":["webpack:///./node_modules/@ckeditor/ckeditor5-engine/src/conversion/mapper.js","webpack:///./node_modules/@ckeditor/ckeditor5-engine/src/dataprocessor/basichtmlwriter.js","webpack:///./node_modules/@ckeditor/ckeditor5-engine/src/dataprocessor/htmldataprocessor.js","webpack:///./node_modules/@ckeditor/ckeditor5-engine/src/conversion/modelconsumable.js","webpack:///./node_modules/@ckeditor/ckeditor5-engine/src/conversion/viewconsumable.js","webpack:///./node_modules/@ckeditor/ckeditor5-engine/src/conversion/upcastdispatcher.js","webpack:///./node_modules/@ckeditor/ckeditor5-engine/src/conversion/upcasthelpers.js"],"names":["Mapper","this","_modelToViewMapping","WeakMap","_viewToModelMapping","_viewToModelLengthCallbacks","Map","_markerNameToElements","_elementToMarkerNames","_unboundMarkerNames","Set","on","evt","data","viewPosition","viewContainer","get","modelPosition","parent","findPositionIn","offset","priority","viewBlock","findMappedViewAncestor","modelParent","modelOffset","_toModelOffset","ModelPosition","_createAt","modelElement","viewElement","set","toModelElement","delete","has","markerName","add","toViewElement","element","name","elements","names","nameToElements","size","elementToNames","markerNames","Array","from","clear","viewRange","ModelRange","toModelPosition","start","end","modelRange","ViewRange","toViewPosition","mapper","fire","options","isPhantom","boundElements","is","getElementsWithSameId","clone","viewElementName","lengthCallback","viewParent","viewOffset","offsetToParentStart","index","offsetInParent","i","getModelLength","getChild","viewNode","callback","length","len","getChildren","child","expectedOffset","lastLength","ViewPosition","_moveViewPositionToTextNode","nodeBefore","nodeAfter","ViewText","mix","EmitterMixin","BasicHtmlWriter","fragment","doc","document","implementation","createHTMLDocument","container","createElement","appendChild","innerHTML","HtmlDataProcessor","_domParser","DOMParser","_domConverter","DomConverter","blockFillerMode","_htmlWriter","viewFragment","domFragment","viewToDom","getHtml","_toDom","domToView","pattern","registerRawContentMatcher","parseFromString","createDocumentFragment","nodes","body","childNodes","ModelConsumable","_consumable","_textProxyRegistry","item","type","_normalizeConsumableType","TextProxy","_getSymbolForTextProxy","test","itemConsumables","undefined","value","textProxy","symbol","startMap","startOffset","endMap","endOffset","_addSymbolForTextProxy","Symbol","parts","split","ViewConsumable","_consumables","consumables","elementConsumables","ViewElementConsumables","consume","revert","attributes","classes","styles","getAttributeKeys","attribute","push","getClassNames","className","getStyleNames","style","instance","consumablesFromElement","createFrom","_canConsumeName","_add","_test","_consume","_revert","items","isArray","CKEditorError","stylesProcessor","getRelatedStyles","alsoName","consumableName","keys","toConsume","UpcastDispatcher","conversionApi","_splitParts","_cursorParents","_modelCursor","Object","assign","convertItem","_convertItem","bind","convertChildren","_convertChildren","safeInsert","_safeInsert","updateConversionResult","_updateConversionResult","splitToAllowedParent","_splitToAllowedParent","getSplitParts","_getSplitParts","viewItem","writer","context","createContextTree","consumable","store","documentFragment","_removeEmptyElements","append","markers","extractMarkersFromModelFragment","modelCursor","elementOrModelCursor","nextModelCursor","viewChild","result","position","splitResult","insert","createRange","createPositionBefore","createPositionAfter","savedCursorParent","createPositionAt","node","schema","allowedParent","findAllowedParent","getAncestors","includes","isParagraphable","wrapInParagraph","stack","range","getWalker","treeWalkerValue","originalPart","pop","splitPart","_registerSplitPair","cursorParent","list","anyRemoved","isEmpty","remove","modelItem","markerElements","_createIn","getItems","markerElement","getAttribute","currentPosition","contextDefinition","SchemaContext","key","current","UpcastHelpers","config","upcastElementToElement","upcastElementToAttribute","upcastAttributeToAttribute","logWarning","upcastElementToMarker","upcastDataToMarker","ConversionHelpers","convertToModelFragment","convertText","checkChild","text","createText","getShiftedBy","offsetSize","convertSelectionChange","model","viewSelection","newSelection","ranges","getRanges","toModelRange","modelSelection","createSelection","backward","isBackward","isEqual","selection","change","setSelection","cloneDeep","converter","prepareToElementConverter","elementName","getViewElementNameFromConfig","view","eventName","dispatcher","converterPriority","normalizeModelAttributeConfig","prepareToAttributeConverter","viewKey","normalizeViewAttributeKeyValueConfig","normalizeElementToMarkerConfig","converterStart","normalizeDataToMarkerConfig","converterEnd","basePriority","priorities","maxPriority","priorityFactor","upcastAttributeToMarker","attrName","addMarkerElements","markerViewNames","markerViewName","_getTransformedByInsertion","viewConfig","matcher","Matcher","matcherResult","match","getModelElement","input","Function","normalized","keyName","viewAttributeKeyToCopy","defaultModelValue","shallow","modelKey","modelValue","onlyViewNameIsDefined","attributeWasSet","setAttributeOn","configToTest","modelAttribute","checkAttribute","setAttribute","oldModel","configForElements","viewName"],"mappings":"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","file":"js/chunk-vendors~a98903dd.3801da23.js","sourcesContent":["/**\n * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\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 * The instance of the Mapper used for the editing pipeline is available in\n * {@link module:engine/controller/editingcontroller~EditingController#mapper `editor.editing.mapper`}.\n *\n * Mapper uses 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/**\n\t\t * View element to model marker names mapping.\n\t\t *\n\t\t * This is reverse to {@link ~Mapper#_markerNameToElements} map.\n\t\t *\n\t\t * @private\n\t\t * @member {Map}\n\t\t */\n\t\tthis._elementToMarkerNames = new Map();\n\n\t\t/**\n\t\t * Stores marker names of markers which has changed due to unbinding a view element (so it is assumed that the view element\n\t\t * has been removed, moved or renamed).\n\t\t *\n\t\t * @private\n\t\t * @member {Set.}\n\t\t */\n\t\tthis._unboundMarkerNames = new Set();\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\tconst viewBlock = this.findMappedViewAncestor( data.viewPosition );\n\t\t\tconst modelParent = this._viewToModelMapping.get( viewBlock );\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._elementToMarkerNames.has( viewElement ) ) {\n\t\t\tfor ( const markerName of this._elementToMarkerNames.get( viewElement ) ) {\n\t\t\t\tthis._unboundMarkerNames.add( markerName );\n\t\t\t}\n\t\t}\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\t\telements.add( element );\n\n\t\tconst names = this._elementToMarkerNames.get( element ) || new Set();\n\t\tnames.add( name );\n\n\t\tthis._markerNameToElements.set( name, elements );\n\t\tthis._elementToMarkerNames.set( element, names );\n\t}\n\n\t/**\n\t * Unbinds an element from given marker name.\n\t *\n\t * @param {module:engine/view/element~Element} element Element to unbind.\n\t * @param {String} name Marker name.\n\t */\n\tunbindElementFromMarkerName( element, name ) {\n\t\tconst nameToElements = this._markerNameToElements.get( name );\n\n\t\tif ( nameToElements ) {\n\t\t\tnameToElements.delete( element );\n\n\t\t\tif ( nameToElements.size == 0 ) {\n\t\t\t\tthis._markerNameToElements.delete( name );\n\t\t\t}\n\t\t}\n\n\t\tconst elementToNames = this._elementToMarkerNames.get( element );\n\n\t\tif ( elementToNames ) {\n\t\t\telementToNames.delete( name );\n\n\t\t\tif ( elementToNames.size == 0 ) {\n\t\t\t\tthis._elementToMarkerNames.delete( element );\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Returns all marker names of markers which has changed due to unbinding a view element (so it is assumed that the view element\n\t * has been removed, moved or renamed) since the last flush. After returning, the marker names list is cleared.\n\t *\n\t * @returns {Array.}\n\t */\n\tflushUnboundMarkerNames() {\n\t\tconst markerNames = Array.from( this._unboundMarkerNames );\n\n\t\tthis._unboundMarkerNames.clear();\n\n\t\treturn markerNames;\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\tthis._elementToMarkerNames = new Map();\n\t\tthis._unboundMarkerNames = new Set();\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 * For given `viewPosition`, finds and returns the closest ancestor of this position that has a mapping to\n\t * the model.\n\t *\n\t * @param {module:engine/view/position~Position} viewPosition Position for which mapped ancestor should be found.\n\t * @returns {module:engine/view/element~Element}\n\t */\n\tfindMappedViewAncestor( viewPosition ) {\n\t\tlet parent = viewPosition.parent;\n\n\t\twhile ( !this._viewToModelMapping.has( parent ) ) {\n\t\t\tparent = parent.parent;\n\t\t}\n\n\t\treturn parent;\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

fobarbom

-> expected offset: 4\n\t *\n\t *\t\tfindPositionIn( p, 4 ):\n\t *\t\t

|fobarbom

-> expected offset: 4, actual offset: 0\n\t *\t\t

fo|barbom

-> expected offset: 4, actual offset: 2\n\t *\t\t

fobar|bom

-> expected offset: 4, actual offset: 5 -> we are too far\n\t *\n\t *\t\tfindPositionIn( 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\tfindPositionIn( 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 * @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\tfindPositionIn( 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( 'element', '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-2021, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * @module engine/dataprocessor/basichtmlwriter\n */\n\n/* globals document */\n\n/**\n * Basic HTML writer. It uses the native `innerHTML` property for basic conversion\n * from a document fragment to an HTML string.\n *\n * @implements module:engine/dataprocessor/htmlwriter~HtmlWriter\n */\nexport default class BasicHtmlWriter {\n\t/**\n\t * Returns an HTML string created from the document fragment.\n\t *\n\t * @param {DocumentFragment} fragment\n\t * @returns {String}\n\t */\n\tgetHtml( fragment ) {\n\t\tconst doc = document.implementation.createHTMLDocument( '' );\n\t\tconst container = doc.createElement( 'div' );\n\t\tcontainer.appendChild( fragment );\n\n\t\treturn container.innerHTML;\n\t}\n}\n","/**\n * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * @module engine/dataprocessor/htmldataprocessor\n */\n\n/* globals document, DOMParser */\n\nimport BasicHtmlWriter from './basichtmlwriter';\nimport DomConverter from '../view/domconverter';\n\n/**\n * The HTML data processor class.\n * This data processor implementation uses HTML as input and output data.\n *\n * @implements module:engine/dataprocessor/dataprocessor~DataProcessor\n */\nexport default class HtmlDataProcessor {\n\t/**\n\t * Creates a new instance of the HTML data processor class.\n\t *\n\t * @param {module:engine/view/document~Document} document The view document instance.\n\t */\n\tconstructor( document ) {\n\t\t/**\n\t\t * A DOM parser instance used to parse an HTML string to an HTML document.\n\t\t *\n\t\t * @private\n\t\t * @member {DOMParser}\n\t\t */\n\t\tthis._domParser = new DOMParser();\n\n\t\t/**\n\t\t * A DOM converter used to convert DOM elements to view elements.\n\t\t *\n\t\t * @private\n\t\t * @member {module:engine/view/domconverter~DomConverter}\n\t\t */\n\t\tthis._domConverter = new DomConverter( document, { blockFillerMode: 'nbsp' } );\n\n\t\t/**\n\t\t * A basic HTML writer instance used to convert DOM elements to an HTML string.\n\t\t *\n\t\t * @private\n\t\t * @member {module:engine/dataprocessor/basichtmlwriter~BasicHtmlWriter}\n\t\t */\n\t\tthis._htmlWriter = new BasicHtmlWriter();\n\t}\n\n\t/**\n\t * Converts a provided {@link module:engine/view/documentfragment~DocumentFragment document fragment}\n\t * to data format — in this case to an HTML string.\n\t *\n\t * @param {module:engine/view/documentfragment~DocumentFragment} viewFragment\n\t * @returns {String} HTML string.\n\t */\n\ttoData( viewFragment ) {\n\t\t// Convert view DocumentFragment to DOM DocumentFragment.\n\t\tconst domFragment = this._domConverter.viewToDom( viewFragment, document );\n\n\t\t// Convert DOM DocumentFragment to HTML output.\n\t\treturn this._htmlWriter.getHtml( domFragment );\n\t}\n\n\t/**\n\t * Converts the provided HTML string to a view tree.\n\t *\n\t * @param {String} data An HTML string.\n\t * @returns {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment|null} A converted view element.\n\t */\n\ttoView( data ) {\n\t\t// Convert input HTML data to DOM DocumentFragment.\n\t\tconst domFragment = this._toDom( data );\n\n\t\t// Convert DOM DocumentFragment to view DocumentFragment.\n\t\treturn this._domConverter.domToView( domFragment );\n\t}\n\n\t/**\n\t * Registers a {@link module:engine/view/matcher~MatcherPattern} for view elements whose content should be treated as raw data\n\t * and not processed during the conversion from the DOM to the view elements.\n\t *\n\t * The raw data can be later accessed by a\n\t * {@link module:engine/view/element~Element#getCustomProperty custom property of a view element} called `\"$rawContent\"`.\n\t *\n\t * @param {module:engine/view/matcher~MatcherPattern} pattern Pattern matching all view elements whose content should\n\t * be treated as raw data.\n\t */\n\tregisterRawContentMatcher( pattern ) {\n\t\tthis._domConverter.registerRawContentMatcher( pattern );\n\t}\n\n\t/**\n\t * Converts an HTML string to its DOM representation. Returns a document fragment containing nodes parsed from\n\t * the provided data.\n\t *\n\t * @private\n\t * @param {String} data\n\t * @returns {DocumentFragment}\n\t */\n\t_toDom( data ) {\n\t\tconst document = this._domParser.parseFromString( data, 'text/html' );\n\t\tconst fragment = document.createDocumentFragment();\n\t\tconst nodes = document.body.childNodes;\n\n\t\twhile ( nodes.length > 0 ) {\n\t\t\tfragment.appendChild( nodes[ 0 ] );\n\t\t}\n\n\t\treturn fragment;\n\t}\n}\n","/**\n * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\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/downcasthelpers 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-2021, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\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( element );\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\telement,\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( from );\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\t * @param {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment} from View node or document fragment\n\t * from which `ViewElementConsumables` is being created.\n\t */\n\tconstructor( from ) {\n\t\t/**\n\t\t * @readonly\n\t\t * @member {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment}\n\t\t */\n\t\tthis.element = from;\n\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', this );\n\t\t\t}\n\n\t\t\tconsumables.set( name, true );\n\n\t\t\tif ( type === 'styles' ) {\n\t\t\t\tfor ( const alsoName of this.element.document.stylesProcessor.getRelatedStyles( name ) ) {\n\t\t\t\t\tconsumables.set( alsoName, true );\n\t\t\t\t}\n\t\t\t}\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\n\t\t\t\tif ( type == 'styles' ) {\n\t\t\t\t\tfor ( const toConsume of this.element.document.stylesProcessor.getRelatedStyles( name ) ) {\n\t\t\t\t\t\tconsumables.set( toConsume, false );\n\t\t\t\t\t}\n\t\t\t\t}\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-2021, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * @module engine/conversion/upcastdispatcher\n */\n\nimport ViewConsumable from './viewconsumable';\nimport ModelRange from '../model/range';\nimport ModelPosition from '../model/position';\nimport { SchemaContext } from '../model/schema';\nimport { isParagraphable, wrapInParagraph } from '../model/utils/autoparagraphing';\n\nimport CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';\nimport EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';\nimport mix from '@ckeditor/ckeditor5-utils/src/mix';\n\n/**\n * Upcast dispatcher is a central point of the view-to-model conversion, which is a process of\n * converting a given {@link module:engine/view/documentfragment~DocumentFragment view document fragment} or\n * {@link module:engine/view/element~Element view element} into a correct model structure.\n *\n * During the conversion process, the dispatcher fires events for all {@link module:engine/view/node~Node view nodes}\n * from the converted view document fragment.\n * Special callbacks called \"converters\" should listen to these events in order to convert the view nodes.\n *\n * The second parameter of the callback is the `data` object with the following properties:\n *\n * * `data.viewItem` contains a {@link module:engine/view/node~Node view node} or a\n * {@link module:engine/view/documentfragment~DocumentFragment view document fragment}\n * that is converted at the moment and might be handled by the callback.\n * * `data.modelRange` is used to point to the result\n * of the current conversion (e.g. the element that is being inserted)\n * and is always a {@link module:engine/model/range~Range} when the conversion succeeds.\n * * `data.modelCursor` is a {@link module:engine/model/position~Position position} on which the converter should insert\n * the newly created items.\n *\n * The third parameter of the callback is an instance of {@link module:engine/conversion/upcastdispatcher~UpcastConversionApi}\n * which provides additional tools for converters.\n *\n * You can read more about conversion in the following guides:\n *\n * * {@glink framework/guides/deep-dive/conversion/conversion-introduction Advanced conversion concepts — attributes}\n * * {@glink framework/guides/deep-dive/conversion/custom-element-conversion Custom element conversion}\n *\n * Examples of event-based converters:\n *\n *\t\t// A converter for links ().\n *\t\teditor.data.upcastDispatcher.on( 'element:a', ( evt, data, conversionApi ) => {\n *\t\t\tif ( conversionApi.consumable.consume( data.viewItem, { name: true, attributes: [ 'href' ] } ) ) {\n *\t\t\t\t// The element is inline and is represented by an attribute in the model.\n *\t\t\t\t// This is why you need to convert only children.\n *\t\t\t\tconst { modelRange } = conversionApi.convertChildren( data.viewItem, data.modelCursor );\n *\n *\t\t\t\tfor ( let item of modelRange.getItems() ) {\n *\t\t\t\t\tif ( conversionApi.schema.checkAttribute( item, 'linkHref' ) ) {\n *\t\t\t\t\t\tconversionApi.writer.setAttribute( 'linkHref', data.viewItem.getAttribute( 'href' ), item );\n *\t\t\t\t\t}\n *\t\t\t\t}\n *\t\t\t}\n *\t\t} );\n *\n *\t\t// Convert

element's font-size style.\n *\t\t// Note: You should use a low-priority observer in order to ensure that\n *\t\t// it is executed after the element-to-element converter.\n *\t\teditor.data.upcastDispatcher.on( 'element:p', ( evt, data, conversionApi ) => {\n *\t\t\tconst { consumable, schema, writer } = conversionApi;\n *\n *\t\t\tif ( !consumable.consume( data.viewItem, { style: 'font-size' } ) ) {\n *\t\t\t\treturn;\n *\t\t\t}\n *\n *\t\t\tconst fontSize = data.viewItem.getStyle( 'font-size' );\n *\n *\t\t\t// Do not go for the model element after data.modelCursor because it might happen\n *\t\t\t// that a single view element was converted to multiple model elements. Get all of them.\n *\t\t\tfor ( const item of data.modelRange.getItems( { shallow: true } ) ) {\n *\t\t\t\tif ( schema.checkAttribute( item, 'fontSize' ) ) {\n *\t\t\t\t\twriter.setAttribute( 'fontSize', fontSize, item );\n *\t\t\t\t}\n *\t\t\t}\n *\t\t}, { priority: 'low' } );\n *\n *\t\t// Convert all elements which have no custom converter into a paragraph (autoparagraphing).\n *\t\teditor.data.upcastDispatcher.on( 'element', ( evt, data, conversionApi ) => {\n *\t\t\t// Check if an element can be converted.\n *\t\t\tif ( !conversionApi.consumable.test( data.viewItem, { name: data.viewItem.name } ) ) {\n *\t\t\t\t// When an element is already consumed by higher priority converters, do nothing.\n *\t\t\t\treturn;\n *\t\t\t}\n *\n *\t\t\tconst paragraph = conversionApi.writer.createElement( 'paragraph' );\n *\n *\t\t\t// Try to safely insert a paragraph at the model cursor - it will find an allowed parent for the current element.\n *\t\t\tif ( !conversionApi.safeInsert( paragraph, data.modelCursor ) ) {\n *\t\t\t\t// When an element was not inserted, it means that you cannot insert a paragraph at this position.\n *\t\t\t\treturn;\n *\t\t\t}\n *\n *\t\t\t// Consume the inserted element.\n *\t\t\tconversionApi.consumable.consume( data.viewItem, { name: data.viewItem.name } ) );\n *\n *\t\t\t// Convert the children to a paragraph.\n *\t\t\tconst { modelRange } = conversionApi.convertChildren( data.viewItem, paragraph ) );\n *\n *\t\t\t// Update `modelRange` and `modelCursor` in the `data` as a conversion result.\n *\t\t\tconversionApi.updateConversionResult( paragraph, data );\n *\t\t}, { priority: 'low' } );\n *\n * @mixes module:utils/emittermixin~EmitterMixin\n * @fires viewCleanup\n * @fires element\n * @fires text\n * @fires documentFragment\n */\nexport default class UpcastDispatcher {\n\t/**\n\t * Creates an upcast dispatcher that operates using the passed API.\n\t *\n\t * @see module:engine/conversion/upcastdispatcher~UpcastConversionApi\n\t * @param {Object} [conversionApi] Additional properties for an interface that will be passed to events fired\n\t * by the upcast dispatcher.\n\t */\n\tconstructor( conversionApi = {} ) {\n\t\t/**\n\t\t * The list of elements that were created during splitting.\n\t\t *\n\t\t * After the conversion process the list is cleared.\n\t\t *\n\t\t * @private\n\t\t * @type {Map.>}\n\t\t */\n\t\tthis._splitParts = new Map();\n\n\t\t/**\n\t\t * The list of cursor parent elements that were created during splitting.\n\t\t *\n\t\t * After the conversion process the list is cleared.\n\t\t *\n\t\t * @private\n\t\t * @type {Map.>}\n\t\t */\n\t\tthis._cursorParents = new Map();\n\n\t\t/**\n\t\t * The position in the temporary structure where the converted content is inserted. The structure reflects the context of\n\t\t * the target position where the content will be inserted. This property is built based on the context parameter of the\n\t\t * convert method.\n\t\t *\n\t\t * @private\n\t\t * @type {module:engine/model/position~Position|null}\n\t\t */\n\t\tthis._modelCursor = null;\n\n\t\t/**\n\t\t * An interface passed by the dispatcher to the event callbacks.\n\t\t *\n\t\t * @member {module:engine/conversion/upcastdispatcher~UpcastConversionApi}\n\t\t */\n\t\tthis.conversionApi = Object.assign( {}, conversionApi );\n\n\t\t// The below methods are bound to this `UpcastDispatcher` instance and set on `conversionApi`.\n\t\t// This way only a part of `UpcastDispatcher` API is exposed.\n\t\tthis.conversionApi.convertItem = this._convertItem.bind( this );\n\t\tthis.conversionApi.convertChildren = this._convertChildren.bind( this );\n\t\tthis.conversionApi.safeInsert = this._safeInsert.bind( this );\n\t\tthis.conversionApi.updateConversionResult = this._updateConversionResult.bind( this );\n\t\t// Advanced API - use only if custom position handling is needed.\n\t\tthis.conversionApi.splitToAllowedParent = this._splitToAllowedParent.bind( this );\n\t\tthis.conversionApi.getSplitParts = this._getSplitParts.bind( this );\n\t}\n\n\t/**\n\t * Starts the conversion process. The entry point for the conversion.\n\t *\n\t * @fires element\n\t * @fires text\n\t * @fires documentFragment\n\t * @param {module:engine/view/documentfragment~DocumentFragment|module:engine/view/element~Element} viewItem\n\t * The part of the view to be converted.\n\t * @param {module:engine/model/writer~Writer} writer An instance of the model writer.\n\t * @param {module:engine/model/schema~SchemaContextDefinition} [context=['$root']] Elements will be converted according to this context.\n\t * @returns {module:engine/model/documentfragment~DocumentFragment} Model data that is the result of the conversion process\n\t * wrapped in `DocumentFragment`. Converted marker elements will be set as the document fragment's\n\t * {@link module:engine/model/documentfragment~DocumentFragment#markers static markers map}.\n\t */\n\tconvert( viewItem, writer, context = [ '$root' ] ) {\n\t\tthis.fire( 'viewCleanup', viewItem );\n\n\t\t// Create context tree and set position in the top element.\n\t\t// Items will be converted according to this position.\n\t\tthis._modelCursor = createContextTree( context, writer );\n\n\t\t// Store writer in conversion as a conversion API\n\t\t// to be sure that conversion process will use the same batch.\n\t\tthis.conversionApi.writer = writer;\n\n\t\t// Create consumable values list for conversion process.\n\t\tthis.conversionApi.consumable = ViewConsumable.createFrom( viewItem );\n\n\t\t// Custom data stored by converter for conversion process.\n\t\tthis.conversionApi.store = {};\n\n\t\t// Do the conversion.\n\t\tconst { modelRange } = this._convertItem( viewItem, this._modelCursor );\n\n\t\t// Conversion result is always a document fragment so let's create it.\n\t\tconst documentFragment = writer.createDocumentFragment();\n\n\t\t// When there is a conversion result.\n\t\tif ( modelRange ) {\n\t\t\t// Remove all empty elements that were create while splitting.\n\t\t\tthis._removeEmptyElements();\n\n\t\t\t// Move all items that were converted in context tree to the document fragment.\n\t\t\tfor ( const item of Array.from( this._modelCursor.parent.getChildren() ) ) {\n\t\t\t\twriter.append( item, documentFragment );\n\t\t\t}\n\n\t\t\t// Extract temporary markers elements from model and set as static markers collection.\n\t\t\tdocumentFragment.markers = extractMarkersFromModelFragment( documentFragment, writer );\n\t\t}\n\n\t\t// Clear context position.\n\t\tthis._modelCursor = null;\n\n\t\t// Clear split elements & parents lists.\n\t\tthis._splitParts.clear();\n\t\tthis._cursorParents.clear();\n\n\t\t// Clear conversion API.\n\t\tthis.conversionApi.writer = null;\n\t\tthis.conversionApi.store = null;\n\n\t\t// Return fragment as conversion result.\n\t\treturn documentFragment;\n\t}\n\n\t/**\n\t * @private\n\t * @see module:engine/conversion/upcastdispatcher~UpcastConversionApi#convertItem\n\t */\n\t_convertItem( viewItem, modelCursor ) {\n\t\tconst data = Object.assign( { viewItem, modelCursor, modelRange: null } );\n\n\t\tif ( viewItem.is( 'element' ) ) {\n\t\t\tthis.fire( 'element:' + viewItem.name, data, this.conversionApi );\n\t\t} else if ( viewItem.is( '$text' ) ) {\n\t\t\tthis.fire( 'text', data, this.conversionApi );\n\t\t} else {\n\t\t\tthis.fire( 'documentFragment', data, this.conversionApi );\n\t\t}\n\n\t\t// Handle incorrect conversion result.\n\t\tif ( data.modelRange && !( data.modelRange instanceof ModelRange ) ) {\n\t\t\t/**\n\t\t\t * Incorrect conversion result was dropped.\n\t\t\t *\n\t\t\t * {@link module:engine/model/range~Range Model range} should be a conversion result.\n\t\t\t *\n\t\t\t * @error view-conversion-dispatcher-incorrect-result\n\t\t\t */\n\t\t\tthrow new CKEditorError( 'view-conversion-dispatcher-incorrect-result', this );\n\t\t}\n\n\t\treturn { modelRange: data.modelRange, modelCursor: data.modelCursor };\n\t}\n\n\t/**\n\t * @private\n\t * @see module:engine/conversion/upcastdispatcher~UpcastConversionApi#convertChildren\n\t */\n\t_convertChildren( viewItem, elementOrModelCursor ) {\n\t\tlet nextModelCursor = elementOrModelCursor.is( 'position' ) ?\n\t\t\telementOrModelCursor : ModelPosition._createAt( elementOrModelCursor, 0 );\n\n\t\tconst modelRange = new ModelRange( nextModelCursor );\n\n\t\tfor ( const viewChild of Array.from( viewItem.getChildren() ) ) {\n\t\t\tconst result = this._convertItem( viewChild, nextModelCursor );\n\n\t\t\tif ( result.modelRange instanceof ModelRange ) {\n\t\t\t\tmodelRange.end = result.modelRange.end;\n\t\t\t\tnextModelCursor = result.modelCursor;\n\t\t\t}\n\t\t}\n\n\t\treturn { modelRange, modelCursor: nextModelCursor };\n\t}\n\n\t/**\n\t * @private\n\t * @see module:engine/conversion/upcastdispatcher~UpcastConversionApi#safeInsert\n\t */\n\t_safeInsert( modelElement, position ) {\n\t\t// Find allowed parent for element that we are going to insert.\n\t\t// If current parent does not allow to insert element but one of the ancestors does\n\t\t// then split nodes to allowed parent.\n\t\tconst splitResult = this._splitToAllowedParent( modelElement, position );\n\n\t\t// When there is no split result it means that we can't insert element to model tree, so let's skip it.\n\t\tif ( !splitResult ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Insert element on allowed position.\n\t\tthis.conversionApi.writer.insert( modelElement, splitResult.position );\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * @private\n\t * @see module:engine/conversion/upcastdispatcher~UpcastConversionApi#updateConversionResult\n\t */\n\t_updateConversionResult( modelElement, data ) {\n\t\tconst parts = this._getSplitParts( modelElement );\n\n\t\tconst writer = this.conversionApi.writer;\n\n\t\t// Set conversion result range - only if not set already.\n\t\tif ( !data.modelRange ) {\n\t\t\tdata.modelRange = writer.createRange(\n\t\t\t\twriter.createPositionBefore( modelElement ),\n\t\t\t\twriter.createPositionAfter( parts[ parts.length - 1 ] )\n\t\t\t);\n\t\t}\n\n\t\tconst savedCursorParent = this._cursorParents.get( modelElement );\n\n\t\t// Now we need to check where the `modelCursor` should be.\n\t\tif ( savedCursorParent ) {\n\t\t\t// If we split parent to insert our element then we want to continue conversion in the new part of the split parent.\n\t\t\t//\n\t\t\t// before: foo[]\n\t\t\t// after: foo []\n\n\t\t\tdata.modelCursor = writer.createPositionAt( savedCursorParent, 0 );\n\t\t} else {\n\t\t\t// Otherwise just continue after inserted element.\n\n\t\t\tdata.modelCursor = data.modelRange.end;\n\t\t}\n\t}\n\n\t/**\n\t * @private\n\t * @see module:engine/conversion/upcastdispatcher~UpcastConversionApi#splitToAllowedParent\n\t */\n\t_splitToAllowedParent( node, modelCursor ) {\n\t\tconst { schema, writer } = this.conversionApi;\n\n\t\t// Try to find allowed parent.\n\t\tlet allowedParent = schema.findAllowedParent( modelCursor, node );\n\n\t\tif ( allowedParent ) {\n\t\t\t// When current position parent allows to insert node then return this position.\n\t\t\tif ( allowedParent === modelCursor.parent ) {\n\t\t\t\treturn { position: modelCursor };\n\t\t\t}\n\n\t\t\t// When allowed parent is in context tree (it's outside the converted tree).\n\t\t\tif ( this._modelCursor.parent.getAncestors().includes( allowedParent ) ) {\n\t\t\t\tallowedParent = null;\n\t\t\t}\n\t\t}\n\n\t\tif ( !allowedParent ) {\n\t\t\t// Check if the node wrapped with a paragraph would be accepted by the schema.\n\t\t\tif ( !isParagraphable( modelCursor, node, schema ) ) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tposition: wrapInParagraph( modelCursor, writer )\n\t\t\t};\n\t\t}\n\n\t\t// Split element to allowed parent.\n\t\tconst splitResult = this.conversionApi.writer.split( modelCursor, allowedParent );\n\n\t\t// Using the range returned by `model.Writer#split`, we will pair original elements with their split parts.\n\t\t//\n\t\t// The range returned from the writer spans \"over the split\" or, precisely saying, from the end of the original element (the one\n\t\t// that got split) to the beginning of the other part of that element:\n\t\t//\n\t\t// X[]Y ->\n\t\t// X[]Y\n\t\t//\n\t\t// After the split there cannot be any full node between the positions in `splitRange`. The positions are touching.\n\t\t// Also, because of how splitting works, it is easy to notice, that \"closing tags\" are in the reverse order than \"opening tags\".\n\t\t// Also, since we split all those elements, each of them has to have the other part.\n\t\t//\n\t\t// With those observations in mind, we will pair the original elements with their split parts by saving \"closing tags\" and matching\n\t\t// them with \"opening tags\" in the reverse order. For that we can use a stack.\n\t\tconst stack = [];\n\n\t\tfor ( const treeWalkerValue of splitResult.range.getWalker() ) {\n\t\t\tif ( treeWalkerValue.type == 'elementEnd' ) {\n\t\t\t\tstack.push( treeWalkerValue.item );\n\t\t\t} else {\n\t\t\t\t// There should not be any text nodes after the element is split, so the only other value is `elementStart`.\n\t\t\t\tconst originalPart = stack.pop();\n\t\t\t\tconst splitPart = treeWalkerValue.item;\n\n\t\t\t\tthis._registerSplitPair( originalPart, splitPart );\n\t\t\t}\n\t\t}\n\n\t\tconst cursorParent = splitResult.range.end.parent;\n\t\tthis._cursorParents.set( node, cursorParent );\n\n\t\treturn {\n\t\t\tposition: splitResult.position,\n\t\t\tcursorParent\n\t\t};\n\t}\n\n\t/**\n\t * Registers that a `splitPart` element is a split part of the `originalPart` element.\n\t *\n\t * The data set by this method is used by {@link #_getSplitParts} and {@link #_removeEmptyElements}.\n\t *\n\t * @private\n\t * @param {module:engine/model/element~Element} originalPart\n\t * @param {module:engine/model/element~Element} splitPart\n\t */\n\t_registerSplitPair( originalPart, splitPart ) {\n\t\tif ( !this._splitParts.has( originalPart ) ) {\n\t\t\tthis._splitParts.set( originalPart, [ originalPart ] );\n\t\t}\n\n\t\tconst list = this._splitParts.get( originalPart );\n\n\t\tthis._splitParts.set( splitPart, list );\n\t\tlist.push( splitPart );\n\t}\n\n\t/**\n\t * @private\n\t * @see module:engine/conversion/upcastdispatcher~UpcastConversionApi#getSplitParts\n\t */\n\t_getSplitParts( element ) {\n\t\tlet parts;\n\n\t\tif ( !this._splitParts.has( element ) ) {\n\t\t\tparts = [ element ];\n\t\t} else {\n\t\t\tparts = this._splitParts.get( element );\n\t\t}\n\n\t\treturn parts;\n\t}\n\n\t/**\n\t * Checks if there are any empty elements created while splitting and removes them.\n\t *\n\t * This method works recursively to re-check empty elements again after at least one element was removed in the initial call,\n\t * as some elements might have become empty after other empty elements were removed from them.\n\t *\n\t * @private\n\t */\n\t_removeEmptyElements() {\n\t\tlet anyRemoved = false;\n\n\t\tfor ( const element of this._splitParts.keys() ) {\n\t\t\tif ( element.isEmpty ) {\n\t\t\t\tthis.conversionApi.writer.remove( element );\n\t\t\t\tthis._splitParts.delete( element );\n\n\t\t\t\tanyRemoved = true;\n\t\t\t}\n\t\t}\n\n\t\tif ( anyRemoved ) {\n\t\t\tthis._removeEmptyElements();\n\t\t}\n\t}\n\n\t/**\n\t * Fired before the first conversion event, at the beginning of the upcast (view-to-model conversion) process.\n\t *\n\t * @event viewCleanup\n\t * @param {module:engine/view/documentfragment~DocumentFragment|module:engine/view/element~Element}\n\t * viewItem A part of the view to be converted.\n\t */\n\n\t/**\n\t * Fired when an {@link module:engine/view/element~Element} is converted.\n\t *\n\t * `element` is a namespace event for a class of events. Names of actually called events follow the pattern of\n\t * `element:` where `elementName` is the name of the converted element. This way listeners may listen to\n\t * a conversion of all or just specific elements.\n\t *\n\t * @event element\n\t * @param {module:engine/conversion/upcastdispatcher~UpcastConversionData} data The conversion data. Keep in mind that this object is\n\t * shared by reference between all callbacks that will be called. This means that callbacks can override values if needed, and these\n\t * values will be available in other callbacks.\n\t * @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi Conversion utilities to be used by the\n\t * callback.\n\t */\n\n\t/**\n\t * Fired when a {@link module:engine/view/text~Text} is converted.\n\t *\n\t * @event text\n\t * @see #event:element\n\t */\n\n\t/**\n\t * Fired when a {@link module:engine/view/documentfragment~DocumentFragment} is converted.\n\t *\n\t * @event documentFragment\n\t * @see #event:element\n\t */\n}\n\nmix( UpcastDispatcher, EmitterMixin );\n\n// Traverses given model item and searches elements which marks marker range. Found element is removed from\n// DocumentFragment but path of this element is stored in a Map which is then returned.\n//\n// @param {module:engine/view/documentfragment~DocumentFragment|module:engine/view/node~Node} modelItem Fragment of model.\n// @returns {Map} List of static markers.\nfunction extractMarkersFromModelFragment( modelItem, writer ) {\n\tconst markerElements = new Set();\n\tconst markers = new Map();\n\n\t// Create ModelTreeWalker.\n\tconst range = ModelRange._createIn( modelItem ).getItems();\n\n\t// Walk through DocumentFragment and collect marker elements.\n\tfor ( const item of range ) {\n\t\t// Check if current element is a marker.\n\t\tif ( item.name == '$marker' ) {\n\t\t\tmarkerElements.add( item );\n\t\t}\n\t}\n\n\t// Walk through collected marker elements store its path and remove its from the DocumentFragment.\n\tfor ( const markerElement of markerElements ) {\n\t\tconst markerName = markerElement.getAttribute( 'data-name' );\n\t\tconst currentPosition = writer.createPositionBefore( markerElement );\n\n\t\t// When marker of given name is not stored it means that we have found the beginning of the range.\n\t\tif ( !markers.has( markerName ) ) {\n\t\t\tmarkers.set( markerName, new ModelRange( currentPosition.clone() ) );\n\t\t// Otherwise is means that we have found end of the marker range.\n\t\t} else {\n\t\t\tmarkers.get( markerName ).end = currentPosition.clone();\n\t\t}\n\n\t\t// Remove marker element from DocumentFragment.\n\t\twriter.remove( markerElement );\n\t}\n\n\treturn markers;\n}\n\n// Creates model fragment according to given context and returns position in the bottom (the deepest) element.\nfunction createContextTree( contextDefinition, writer ) {\n\tlet position;\n\n\tfor ( const item of new SchemaContext( contextDefinition ) ) {\n\t\tconst attributes = {};\n\n\t\tfor ( const key of item.getAttributeKeys() ) {\n\t\t\tattributes[ key ] = item.getAttribute( key );\n\t\t}\n\n\t\tconst current = writer.createElement( item.name, attributes );\n\n\t\tif ( position ) {\n\t\t\twriter.append( current, position );\n\t\t}\n\n\t\tposition = ModelPosition._createAt( current, 0 );\n\t}\n\n\treturn position;\n}\n\n/**\n * A set of conversion utilities available as the third parameter of the\n * {@link module:engine/conversion/upcastdispatcher~UpcastDispatcher upcast dispatcher}'s events.\n *\n * @interface module:engine/conversion/upcastdispatcher~UpcastConversionApi\n */\n\n/**\n * Starts the conversion of a given item by firing an appropriate event.\n *\n * Every fired event is passed (as the first parameter) an object with the `modelRange` property. Every event may set and/or\n * modify that property. When all callbacks are done, the final value of the `modelRange` property is returned by this method.\n * The `modelRange` must be a {@link module:engine/model/range~Range model range} or `null` (as set by default).\n *\n * @method #convertItem\n * @fires module:engine/conversion/upcastdispatcher~UpcastDispatcher#event:element\n * @fires module:engine/conversion/upcastdispatcher~UpcastDispatcher#event:text\n * @fires module:engine/conversion/upcastdispatcher~UpcastDispatcher#event:documentFragment\n * @param {module:engine/view/item~Item} viewItem Item to convert.\n * @param {module:engine/model/position~Position} modelCursor The conversion position.\n * @returns {Object} result The conversion result.\n * @returns {module:engine/model/range~Range|null} result.modelRange The model range containing the result of the item conversion,\n * created and modified by callbacks attached to the fired event, or `null` if the conversion result was incorrect.\n * @returns {module:engine/model/position~Position} result.modelCursor The position where the conversion should be continued.\n */\n\n/**\n * Starts the conversion of all children of a given item by firing appropriate events for all the children.\n *\n * @method #convertChildren\n * @fires module:engine/conversion/upcastdispatcher~UpcastDispatcher#event:element\n * @fires module:engine/conversion/upcastdispatcher~UpcastDispatcher#event:text\n * @fires module:engine/conversion/upcastdispatcher~UpcastDispatcher#event:documentFragment\n * @param {module:engine/view/item~Item} viewItem An element whose children should be converted.\n * @param {module:engine/model/position~Position|module:engine/model/element~Element} positionOrElement A position or an element of\n * the conversion.\n * @returns {Object} result The conversion result.\n * @returns {module:engine/model/range~Range} result.modelRange The model range containing the results of the conversion of all children\n * of the given item. When no child was converted, the range is collapsed.\n * @returns {module:engine/model/position~Position} result.modelCursor The position where the conversion should be continued.\n */\n\n/**\n * Safely inserts an element to the document, checking the {@link module:engine/model/schema~Schema schema} to find an allowed parent for\n * an element that you are going to insert, starting from the given position. If the current parent does not allow to insert the element\n * but one of the ancestors does, then splits the nodes to allowed parent.\n *\n * If the schema allows to insert the node in a given position, nothing is split.\n *\n * If it was not possible to find an allowed parent, `false` is returned and nothing is split.\n *\n * Otherwise, ancestors are split.\n *\n * For instance, if `` is not allowed in `` but is allowed in `$root`:\n *\n *\t\tfoo[]bar\n *\n *\t\t-> safe insert for `` will split ->\n *\n *\t\tfoo[]bar\n *\n * Example usage:\n *\n *\t\tconst myElement = conversionApi.writer.createElement( 'myElement' );\n *\n *\t\tif ( !conversionApi.safeInsert( myElement, data.modelCursor ) ) {\n *\t\t\treturn;\n *\t\t}\n *\n * The split result is saved and {@link #updateConversionResult} should be used to update the\n * {@link module:engine/conversion/upcastdispatcher~UpcastConversionData conversion data}.\n *\n * @method #safeInsert\n * @param {module:engine/model/node~Node} node The node to insert.\n * @param {module:engine/model/position~Position} position The position where an element is going to be inserted.\n * @returns {Boolean} The split result. If it was not possible to find an allowed position, `false` is returned.\n */\n\n/**\n * Updates the conversion result and sets a proper {@link module:engine/conversion/upcastdispatcher~UpcastConversionData#modelRange} and\n * the next {@link module:engine/conversion/upcastdispatcher~UpcastConversionData#modelCursor} after the conversion.\n * Used together with {@link #safeInsert}, it enables you to easily convert elements without worrying if the node was split\n * during the conversion of its children.\n *\n * A usage example in converter code:\n *\n *\t\tconst myElement = conversionApi.writer.createElement( 'myElement' );\n *\n *\t\tif ( !conversionApi.safeInsert( myElement, data.modelCursor ) ) {\n *\t\t\treturn;\n *\t\t}\n *\n *\t\t// Children conversion may split `myElement`.\n *\t\tconversionApi.convertChildren( data.viewItem, myElement );\n *\n *\t\tconversionApi.updateConversionResult( myElement, data );\n *\n * @method #updateConversionResult\n * @param {module:engine/model/element~Element} element\n * @param {module:engine/conversion/upcastdispatcher~UpcastConversionData} data Conversion data.\n * @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi Conversion utilities to be used by the callback.\n */\n\n/**\n * Checks the {@link module:engine/model/schema~Schema schema} to find an allowed parent for an element that is going to be inserted\n * starting from the given position. If the current parent does not allow inserting an element but one of the ancestors does, the method\n * splits nodes to allowed parent.\n *\n * If the schema allows inserting the node in the given position, nothing is split and an object with that position is returned.\n *\n * If it was not possible to find an allowed parent, `null` is returned and nothing is split.\n *\n * Otherwise, ancestors are split and an object with a position and the copy of the split element is returned.\n *\n * For instance, if `` is not allowed in `` but is allowed in `$root`:\n *\n *\t\tfoo[]bar\n *\n *\t\t-> split for `` ->\n *\n *\t\tfoo[]bar\n *\n * In the example above, the position between `` elements will be returned as `position` and the second `paragraph`\n * as `cursorParent`.\n *\n * **Note:** This is an advanced method. For most cases {@link #safeInsert} and {@link #updateConversionResult} should be used.\n *\n * @method #splitToAllowedParent\n * @param {module:engine/model/position~Position} position The position where the element is going to be inserted.\n * @param {module:engine/model/node~Node} node The node to insert.\n * @returns {Object|null} The split result. If it was not possible to find an allowed position, `null` is returned.\n * @returns {module:engine/model/position~Position} The position between split elements.\n * @returns {module:engine/model/element~Element} [cursorParent] The element inside which the cursor should be placed to\n * continue the conversion. When the element is not defined it means that there was no split.\n */\n\n/**\n * Returns all the split parts of the given `element` that were created during upcasting through using {@link #splitToAllowedParent}.\n * It enables you to easily track these elements and continue processing them after they are split during the conversion of their children.\n *\n *\t\tFoobarbaz ->\n *\t\tFoobarbaz\n *\n * For a reference to any of above paragraphs, the function will return all three paragraphs (the original element included),\n * sorted in the order of their creation (the original element is the first one).\n *\n * If the given `element` was not split, an array with a single element is returned.\n *\n * A usage example in the converter code:\n *\n *\t\tconst myElement = conversionApi.writer.createElement( 'myElement' );\n *\n *\t\t// Children conversion may split `myElement`.\n *\t\tconversionApi.convertChildren( data.viewItem, data.modelCursor );\n *\n *\t\tconst splitParts = conversionApi.getSplitParts( myElement );\n *\t\tconst lastSplitPart = splitParts[ splitParts.length - 1 ];\n *\n *\t\t// Setting `data.modelRange` basing on split parts:\n *\t\tdata.modelRange = conversionApi.writer.createRange(\n *\t\t\tconversionApi.writer.createPositionBefore( myElement ),\n *\t\t\tconversionApi.writer.createPositionAfter( lastSplitPart )\n *\t\t);\n *\n *\t\t// Setting `data.modelCursor` to continue after the last split element:\n *\t\tdata.modelCursor = conversionApi.writer.createPositionAfter( lastSplitPart );\n *\n * **Tip:** If you are unable to get a reference to the original element (for example because the code is split into multiple converters\n * or even classes) but it has already been converted, you may want to check the first element in `data.modelRange`. This is a common\n * situation if an attribute converter is separated from an element converter.\n *\n * **Note:** This is an advanced method. For most cases {@link #safeInsert} and {@link #updateConversionResult} should be used.\n *\n * @method #getSplitParts\n * @param {module:engine/model/element~Element} element\n * @returns {Array.}\n */\n\n/**\n * Stores information about what parts of the processed view item are still waiting to be handled. After a piece of view item\n * was converted, an appropriate consumable value should be\n * {@link module:engine/conversion/viewconsumable~ViewConsumable#consume consumed}.\n *\n * @member {module:engine/conversion/viewconsumable~ViewConsumable} #consumable\n */\n\n/**\n * Custom data stored by converters for the conversion process. Custom properties of this object can be defined and use to\n * pass parameters between converters.\n *\n * The difference between this property and the `data` parameter of\n * {@link module:engine/conversion/upcastdispatcher~UpcastDispatcher#event:element} is that the `data` parameters allow you\n * to pass parameters within a single event and `store` within the whole conversion.\n *\n * @member {Object} #store\n */\n\n/**\n * The model's schema instance.\n *\n * @member {module:engine/model/schema~Schema} #schema\n */\n\n/**\n * The {@link module:engine/model/writer~Writer} instance used to manipulate the data during conversion.\n *\n * @member {module:engine/model/writer~Writer} #writer\n */\n\n/**\n * Conversion data.\n *\n * **Note:** Keep in mind that this object is shared by reference between all conversion callbacks that will be called.\n * This means that callbacks can override values if needed, and these values will be available in other callbacks.\n *\n * @typedef {Object} module:engine/conversion/upcastdispatcher~UpcastConversionData\n *\n * @property {module:engine/view/item~Item} viewItem The converted item.\n * @property {module:engine/model/position~Position} modelCursor The position where the converter should start changes.\n * Change this value for the next converter to tell where the conversion should continue.\n * @property {module:engine/model/range~Range} [modelRange] The current state of conversion result. Every change to\n * the converted element should be reflected by setting or modifying this property.\n */\n","/**\n * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\nimport Matcher from '../view/matcher';\nimport ConversionHelpers from './conversionhelpers';\n\nimport { cloneDeep } from 'lodash-es';\nimport { logWarning } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';\n\nimport priorities from '@ckeditor/ckeditor5-utils/src/priorities';\nimport { isParagraphable, wrapInParagraph } from '../model/utils/autoparagraphing';\n\n/**\n * Contains {@link module:engine/view/view view} to {@link module:engine/model/model model} converters for\n * {@link module:engine/conversion/upcastdispatcher~UpcastDispatcher}.\n *\n * @module engine/conversion/upcasthelpers\n */\n\n/**\n * Upcast conversion helper functions.\n *\n * @extends module:engine/conversion/conversionhelpers~ConversionHelpers\n */\nexport default class UpcastHelpers extends ConversionHelpers {\n\t/**\n\t * View element to model element conversion helper.\n\t *\n\t * This conversion results in creating a model element. For example,\n\t * view `

Foo

` becomes `Foo` in the model.\n\t *\n\t * Keep in mind that the element will be inserted only if it is allowed\n\t * by {@link module:engine/model/schema~Schema schema} configuration.\n\t *\n\t *\t\teditor.conversion.for( 'upcast' ).elementToElement( {\n\t *\t\t\tview: 'p',\n\t *\t\t\tmodel: 'paragraph'\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'upcast' ).elementToElement( {\n\t *\t\t\tview: 'p',\n\t *\t\t\tmodel: 'paragraph',\n\t *\t\t\tconverterPriority: 'high'\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'upcast' ).elementToElement( {\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\tmodel: 'fancyParagraph'\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'upcast' ).elementToElement( {\n\t * \t\t\tview: {\n\t *\t\t\t\tname: 'p',\n\t *\t\t\t\tclasses: 'heading'\n\t * \t\t\t},\n\t * \t\t\tmodel: ( viewElement, conversionApi ) => {\n\t * \t\t\t\tconst modelWriter = conversionApi.writer;\n\t *\n\t * \t\t\t\treturn modelWriter.createElement( 'heading', { level: viewElement.getAttribute( 'data-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 {module:engine/view/matcher~MatcherPattern} [config.view] Pattern matching all view elements which should be converted. If not\n\t * set, the converter will fire for every view element.\n\t * @param {String|module:engine/model/element~Element|Function} config.model Name of the model element, a model element instance or a\n\t * function that takes a view element and {@link module:engine/conversion/upcastdispatcher~UpcastConversionApi upcast conversion API}\n\t * and returns a model element. The model element will be inserted in the model.\n\t * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.\n\t * @returns {module:engine/conversion/upcasthelpers~UpcastHelpers}\n\t */\n\telementToElement( config ) {\n\t\treturn this.add( upcastElementToElement( config ) );\n\t}\n\n\t/**\n\t * View element to model attribute conversion helper.\n\t *\n\t * This conversion results in setting an attribute on a model node. For example, view `Foo` becomes\n\t * `Foo` {@link module:engine/model/text~Text model text node} with `bold` attribute set to `true`.\n\t *\n\t * This helper is meant to set a model attribute on all the elements that are inside the converted element:\n\t *\n\t *\t\tFoo -->

Foo

--> <$text bold=\"true\">Foo\n\t *\n\t * Above is a sample of HTML code, that goes through autoparagraphing (first step) and then is converted (second step).\n\t * Even though `` is over `

` element, `bold=\"true\"` was added to the text. See\n\t * {@link module:engine/conversion/upcasthelpers~UpcastHelpers#attributeToAttribute} for comparison.\n\t *\n\t * Keep in mind that the attribute will be set only if it is allowed by {@link module:engine/model/schema~Schema schema} configuration.\n\t *\n\t *\t\teditor.conversion.for( 'upcast' ).elementToAttribute( {\n\t *\t\t\tview: 'strong',\n\t *\t\t\tmodel: 'bold'\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'upcast' ).elementToAttribute( {\n\t *\t\t\tview: 'strong',\n\t *\t\t\tmodel: 'bold',\n\t *\t\t\tconverterPriority: 'high'\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'upcast' ).elementToAttribute( {\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\tmodel: 'bold'\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'upcast' ).elementToAttribute( {\n\t *\t\t\tview: {\n\t *\t\t\t\tname: 'span',\n\t *\t\t\t\tclasses: [ 'styled', 'styled-dark' ]\n\t *\t\t\t},\n\t *\t\t\tmodel: {\n\t *\t\t\t\tkey: 'styled',\n\t *\t\t\t\tvalue: 'dark'\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t * \t\teditor.conversion.for( 'upcast' ).elementToAttribute( {\n\t *\t\t\tview: {\n\t *\t\t\t\tname: 'span',\n\t *\t\t\t\tstyles: {\n\t *\t\t\t\t\t'font-size': /[\\s\\S]+/\n\t *\t\t\t\t}\n\t *\t\t\t},\n\t *\t\t\tmodel: {\n\t *\t\t\t\tkey: 'fontSize',\n\t *\t\t\t\tvalue: ( viewElement, conversionApi ) => {\n\t *\t\t\t\t\tconst fontSize = viewElement.getStyle( 'font-size' );\n\t *\t\t\t\t\tconst value = fontSize.substr( 0, fontSize.length - 2 );\n\t *\n\t *\t\t\t\t\tif ( value <= 10 ) {\n\t *\t\t\t\t\t\treturn 'small';\n\t *\t\t\t\t\t} else if ( value > 12 ) {\n\t *\t\t\t\t\t\treturn 'big';\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 * 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 #elementToAttribute\n\t * @param {Object} config Conversion configuration.\n\t * @param {module:engine/view/matcher~MatcherPattern} config.view Pattern matching all view elements which should be converted.\n\t * @param {String|Object} config.model Model attribute key or an object with `key` and `value` properties, describing\n\t * the model attribute. `value` property may be set as a function that takes a view element and\n\t * {@link module:engine/conversion/upcastdispatcher~UpcastConversionApi upcast conversion API} and returns the value.\n\t * If `String` is given, the model attribute value will be set to `true`.\n\t * @param {module:utils/priorities~PriorityString} [config.converterPriority='low'] Converter priority.\n\t * @returns {module:engine/conversion/upcasthelpers~UpcastHelpers}\n\t */\n\telementToAttribute( config ) {\n\t\treturn this.add( upcastElementToAttribute( config ) );\n\t}\n\n\t/**\n\t * View attribute to model attribute conversion helper.\n\t *\n\t * This conversion results in setting an attribute on a model node. For example, view `` becomes\n\t * `` in the model.\n\t *\n\t * This helper is meant to convert view attributes from view elements which got converted to the model, so the view attribute\n\t * is set only on the corresponding model node:\n\t *\n\t *\t\t

foo
-->
foo
\n\t *\n\t * Above, `class=\"dark\"` attribute is added only to the `
` elements that has it. This is in contrary to\n\t * {@link module:engine/conversion/upcasthelpers~UpcastHelpers#elementToAttribute} which sets attributes for\n\t * all the children in the model:\n\t *\n\t *\t\tFoo -->

Foo

--> <$text bold=\"true\">Foo\n\t *\n\t * Above is a sample of HTML code, that goes through autoparagraphing (first step) and then is converted (second step).\n\t * Even though `` is over `

` element, `bold=\"true\"` was added to the text.\n\t *\n\t * Keep in mind that the attribute will be set only if it is allowed by {@link module:engine/model/schema~Schema schema} configuration.\n\t *\n\t *\t\teditor.conversion.for( 'upcast' ).attributeToAttribute( {\n\t *\t\t\tview: 'src',\n\t *\t\t\tmodel: 'source'\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'upcast' ).attributeToAttribute( {\n\t *\t\t\tview: { key: 'src' },\n\t *\t\t\tmodel: 'source'\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'upcast' ).attributeToAttribute( {\n\t *\t\t\tview: { key: 'src' },\n\t *\t\t\tmodel: 'source',\n\t *\t\t\tconverterPriority: 'normal'\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'upcast' ).attributeToAttribute( {\n\t *\t\t\tview: {\n\t *\t\t\t\tkey: 'data-style',\n\t *\t\t\t\tvalue: /[\\s\\S]+/\n\t *\t\t\t},\n\t *\t\t\tmodel: 'styled'\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'upcast' ).attributeToAttribute( {\n\t *\t\t\tview: {\n\t *\t\t\t\tname: 'img',\n\t *\t\t\t\tkey: 'class',\n\t *\t\t\t\tvalue: 'styled-dark'\n\t *\t\t\t},\n\t *\t\t\tmodel: {\n\t *\t\t\t\tkey: 'styled',\n\t *\t\t\t\tvalue: 'dark'\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'upcast' ).attributeToAttribute( {\n\t *\t\t\tview: {\n\t *\t\t\t\tkey: 'class',\n\t *\t\t\t\tvalue: /styled-[\\S]+/\n\t *\t\t\t},\n\t *\t\t\tmodel: {\n\t *\t\t\t\tkey: 'styled'\n\t *\t\t\t\tvalue: ( viewElement, conversionApi ) => {\n\t *\t\t\t\t\tconst regexp = /styled-([\\S]+)/;\n\t *\t\t\t\t\tconst match = viewElement.getAttribute( 'class' ).match( regexp );\n\t *\n\t *\t\t\t\t\treturn match[ 1 ];\n\t *\t\t\t\t}\n\t *\t\t\t}\n\t *\t\t} );\n\t *\n\t * Converting styles works a bit differently as it requires `view.styles` to be an object and by default\n\t * a model attribute will be set to `true` by such a converter. You can set the model attribute to any value by providing the `value`\n\t * callback that returns the desired value.\n\t *\n\t *\t\t// Default conversion of font-weight style will result in setting bold attribute to true.\n\t *\t\teditor.conversion.for( 'upcast' ).attributeToAttribute( {\n\t *\t\t\tview: {\n\t *\t\t\t\tstyles: {\n\t *\t\t\t\t\t'font-weight': 'bold'\n\t *\t\t\t\t}\n\t *\t\t\t},\n\t *\t\t\tmodel: 'bold'\n\t *\t\t} );\n\t *\n\t *\t\t// This converter will pass any style value to the `lineHeight` model attribute.\n\t *\t\teditor.conversion.for( 'upcast' ).attributeToAttribute( {\n\t *\t\t\tview: {\n\t *\t\t\t\tstyles: {\n\t *\t\t\t\t\t'line-height': /[\\s\\S]+/\n\t *\t\t\t\t}\n\t *\t\t\t},\n\t *\t\t\tmodel: {\n\t *\t\t\t\tkey: 'lineHeight',\n\t *\t\t\t\tvalue: ( viewElement, conversionApi ) => viewElement.getStyle( 'line-height' )\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 #attributeToAttribute\n\t * @param {Object} config Conversion configuration.\n\t * @param {String|Object} config.view Specifies which view attribute will be converted. If a `String` is passed,\n\t * attributes with given key will be converted. If an `Object` is passed, it must have a required `key` property,\n\t * specifying view attribute key, and may have an optional `value` property, specifying view attribute value and optional `name`\n\t * property specifying a view element name from/on which the attribute should be converted. `value` can be given as a `String`,\n\t * a `RegExp` or a function callback, that takes view attribute value as the only parameter and returns `Boolean`.\n\t * @param {String|Object} config.model Model attribute key or an object with `key` and `value` properties, describing\n\t * the model attribute. `value` property may be set as a function that takes a view element and\n\t * {@link module:engine/conversion/upcastdispatcher~UpcastConversionApi upcast conversion API} and returns the value.\n\t * If `String` is given, the model attribute value will be same as view attribute value.\n\t * @param {module:utils/priorities~PriorityString} [config.converterPriority='low'] Converter priority.\n\t * @returns {module:engine/conversion/upcasthelpers~UpcastHelpers}\n\t */\n\tattributeToAttribute( config ) {\n\t\treturn this.add( upcastAttributeToAttribute( config ) );\n\t}\n\n\t/**\n\t * View element to model marker conversion helper.\n\t *\n\t * **Note**: This method was deprecated. Please use {@link #dataToMarker} instead.\n\t *\n\t * This conversion results in creating a model marker. For example, if the marker was stored in a view as an element:\n\t * `

Foo

Bar

`,\n\t * after the conversion is done, the marker will be available in\n\t * {@link module:engine/model/model~Model#markers model document markers}.\n\t *\n\t *\t\teditor.conversion.for( 'upcast' ).elementToMarker( {\n\t *\t\t\tview: 'marker-search',\n\t *\t\t\tmodel: 'search'\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'upcast' ).elementToMarker( {\n\t *\t\t\tview: 'marker-search',\n\t *\t\t\tmodel: 'search',\n\t *\t\t\tconverterPriority: 'high'\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'upcast' ).elementToMarker( {\n\t *\t\t\tview: 'marker-search',\n\t *\t\t\tmodel: ( viewElement, conversionApi ) => 'comment:' + viewElement.getAttribute( 'data-comment-id' )\n\t *\t\t} );\n\t *\n\t *\t\teditor.conversion.for( 'upcast' ).elementToMarker( {\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\tmodel: 'search'\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 * @deprecated\n\t * @method #elementToMarker\n\t * @param {Object} config Conversion configuration.\n\t * @param {module:engine/view/matcher~MatcherPattern} config.view Pattern matching all view elements which should be converted.\n\t * @param {String|Function} config.model Name of the model marker, or a function that takes a view element and returns\n\t * a model marker name.\n\t * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.\n\t * @returns {module:engine/conversion/upcasthelpers~UpcastHelpers}\n\t */\n\telementToMarker( config ) {\n\t\t/**\n\t\t * The {@link module:engine/conversion/upcasthelpers~UpcastHelpers#elementToMarker `UpcastHelpers#elementToMarker()`}\n\t\t * method was deprecated and will be removed in the near future.\n\t\t * Please use {@link module:engine/conversion/upcasthelpers~UpcastHelpers#dataToMarker `UpcastHelpers#dataToMarker()`} instead.\n\t\t *\n\t\t * @error upcast-helpers-element-to-marker-deprecated\n\t\t */\n\t\tlogWarning( 'upcast-helpers-element-to-marker-deprecated' );\n\n\t\treturn this.add( upcastElementToMarker( config ) );\n\t}\n\n\t/**\n\t * View-to-model marker conversion helper.\n\t *\n\t * Converts view data created by {@link module:engine/conversion/downcasthelpers~DowncastHelpers#markerToData `#markerToData()`}\n\t * back to a model marker.\n\t *\n\t * This converter looks for specific view elements and view attributes that mark marker boundaries. See\n\t * {@link module:engine/conversion/downcasthelpers~DowncastHelpers#markerToData `#markerToData()`} to learn what view data\n\t * is expected by this converter.\n\t *\n\t * The `config.view` property is equal to the marker group name to convert.\n\t *\n\t * By default, this converter creates markers with the `group:name` name convention (to match the default `markerToData` conversion).\n\t *\n\t * The conversion configuration can take a function that will generate a marker name.\n\t * If such function is set as the `config.model` parameter, it is passed the `name` part from the view element or attribute and it is\n\t * expected to return a string with the marker name.\n\t *\n\t * Basic usage:\n\t *\n\t *\t\t// Using the default conversion.\n\t *\t\t// In this case, all markers from the `comment` group will be converted.\n\t *\t\t// The conversion will look for `` and `` tags and\n\t *\t\t// `data-comment-start-before`, `data-comment-start-after`,\n\t *\t\t// `data-comment-end-before` and `data-comment-end-after` attributes.\n\t *\t\teditor.conversion.for( 'upcast' ).dataToMarker( {\n\t *\t\t\tview: 'comment'\n\t *\t\t} );\n\t *\n\t * An example of a model that may be generated by this conversion:\n\t *\n\t *\t\t// View:\n\t *\t\t

Foobar

\n\t *\t\t
\n\t *\n\t *\t\t// Model:\n\t *\t\tFoo[bar\n\t *\t\t]\n\t *\n\t * Where `[]` are boundaries of a marker that will receive the `comment:commentId:uid` name.\n\t *\n\t * Other examples of usage:\n\t *\n\t *\t\t// Using a custom function which is the same as the default conversion:\n\t *\t\teditor.conversion.for( 'upcast' ).dataToMarker( {\n\t *\t\t\tview: 'comment',\n\t *\t\t\tmodel: ( name, conversionApi ) => 'comment:' + name,\n\t *\t\t} );\n\t *\n\t *\t\t// Using the converter priority:\n\t *\t\teditor.conversion.for( 'upcast' ).dataToMarker( {\n\t *\t\t\tview: 'comment',\n\t *\t\t\tmodel: ( name, conversionApi ) => 'comment:' + name,\n\t *\t\t\tconverterPriority: 'high'\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 #dataToMarker\n\t * @param {Object} config Conversion configuration.\n\t * @param {String} config.view The marker group name to convert.\n\t * @param {Function} [config.model] A function that takes the `name` part from the view element or attribute and\n\t * {@link module:engine/conversion/upcastdispatcher~UpcastConversionApi upcast conversion API} and returns the marker name.\n\t * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.\n\t * @returns {module:engine/conversion/upcasthelpers~UpcastHelpers}\n\t */\n\tdataToMarker( config ) {\n\t\treturn this.add( upcastDataToMarker( config ) );\n\t}\n}\n\n/**\n * Function factory, creates a converter that converts {@link module:engine/view/documentfragment~DocumentFragment view document fragment}\n * or all children of {@link module:engine/view/element~Element} into\n * {@link module:engine/model/documentfragment~DocumentFragment model document fragment}.\n * This is the \"entry-point\" converter for upcast (view to model conversion). This converter starts the conversion of all children\n * of passed view document fragment. Those children {@link module:engine/view/node~Node view nodes} are then handled by other converters.\n *\n * This also a \"default\", last resort converter for all view elements that has not been converted by other converters.\n * When a view element is being converted to the model but it does not have converter specified, that view element\n * will be converted to {@link module:engine/model/documentfragment~DocumentFragment model document fragment} and returned.\n *\n * @returns {Function} Universal converter for view {@link module:engine/view/documentfragment~DocumentFragment fragments} and\n * {@link module:engine/view/element~Element elements} that returns\n * {@link module:engine/model/documentfragment~DocumentFragment model fragment} with children of converted view item.\n */\nexport function convertToModelFragment() {\n\treturn ( evt, data, conversionApi ) => {\n\t\t// Second argument in `consumable.consume` is discarded for ViewDocumentFragment but is needed for ViewElement.\n\t\tif ( !data.modelRange && conversionApi.consumable.consume( data.viewItem, { name: true } ) ) {\n\t\t\tconst { modelRange, modelCursor } = conversionApi.convertChildren( data.viewItem, data.modelCursor );\n\n\t\t\tdata.modelRange = modelRange;\n\t\t\tdata.modelCursor = modelCursor;\n\t\t}\n\t};\n}\n\n/**\n * Function factory, creates a converter that converts {@link module:engine/view/text~Text} to {@link module:engine/model/text~Text}.\n *\n * @returns {Function} {@link module:engine/view/text~Text View text} converter.\n */\nexport function convertText() {\n\treturn ( evt, data, { schema, consumable, writer } ) => {\n\t\tlet position = data.modelCursor;\n\n\t\t// When node is already converted then do nothing.\n\t\tif ( !consumable.test( data.viewItem ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( !schema.checkChild( position, '$text' ) ) {\n\t\t\tif ( !isParagraphable( position, '$text', schema ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tposition = wrapInParagraph( position, writer );\n\t\t}\n\n\t\tconsumable.consume( data.viewItem );\n\n\t\tconst text = writer.createText( data.viewItem.data );\n\n\t\twriter.insert( text, position );\n\n\t\tdata.modelRange = writer.createRange(\n\t\t\tposition,\n\t\t\tposition.getShiftedBy( text.offsetSize )\n\t\t);\n\t\tdata.modelCursor = data.modelRange.end;\n\t};\n}\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\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\tconst modelSelection = model.createSelection( 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// View element to model element conversion helper.\n//\n// See {@link ~UpcastHelpers#elementToElement `.elementToElement()` upcast helper} for examples.\n//\n// @param {Object} config Conversion configuration.\n// @param {module:engine/view/matcher~MatcherPattern} [config.view] Pattern matching all view elements which should be converted. If not\n// set, the converter will fire for every view element.\n// @param {String|module:engine/model/element~Element|Function} config.model Name of the model element, a model element\n// instance or a function that takes a view element and returns a model element. The model element will be inserted in the model.\n// @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.\n// @returns {Function} Conversion helper.\nfunction upcastElementToElement( config ) {\n\tconfig = cloneDeep( config );\n\n\tconst converter = prepareToElementConverter( config );\n\n\tconst elementName = getViewElementNameFromConfig( config.view );\n\tconst eventName = elementName ? 'element:' + elementName : 'element';\n\n\treturn dispatcher => {\n\t\tdispatcher.on( eventName, converter, { priority: config.converterPriority || 'normal' } );\n\t};\n}\n\n// View element to model attribute conversion helper.\n//\n// See {@link ~UpcastHelpers#elementToAttribute `.elementToAttribute()` upcast helper} for examples.\n//\n// @param {Object} config Conversion configuration.\n// @param {module:engine/view/matcher~MatcherPattern} config.view Pattern matching all view elements which should be converted.\n// @param {String|Object} config.model Model attribute key or an object with `key` and `value` properties, describing\n// the model attribute. `value` property may be set as a function that takes a view element and returns the value.\n// If `String` is given, the model attribute value will be set to `true`.\n// @param {module:utils/priorities~PriorityString} [config.converterPriority='low'] Converter priority.\n// @returns {Function} Conversion helper.\nfunction upcastElementToAttribute( config ) {\n\tconfig = cloneDeep( config );\n\n\tnormalizeModelAttributeConfig( config );\n\n\tconst converter = prepareToAttributeConverter( config, false );\n\n\tconst elementName = getViewElementNameFromConfig( config.view );\n\tconst eventName = elementName ? 'element:' + elementName : 'element';\n\n\treturn dispatcher => {\n\t\tdispatcher.on( eventName, converter, { priority: config.converterPriority || 'low' } );\n\t};\n}\n\n// View attribute to model attribute conversion helper.\n//\n// See {@link ~UpcastHelpers#attributeToAttribute `.attributeToAttribute()` upcast helper} for examples.\n//\n// @param {Object} config Conversion configuration.\n// @param {String|Object} config.view Specifies which view attribute will be converted. If a `String` is passed,\n// attributes with given key will be converted. If an `Object` is passed, it must have a required `key` property,\n// specifying view attribute key, and may have an optional `value` property, specifying view attribute value and optional `name`\n// property specifying a view element name from/on which the attribute should be converted. `value` can be given as a `String`,\n// a `RegExp` or a function callback, that takes view attribute value as the only parameter and returns `Boolean`.\n// @param {String|Object} config.model Model attribute key or an object with `key` and `value` properties, describing\n// the model attribute. `value` property may be set as a function that takes a view element and returns the value.\n// If `String` is given, the model attribute value will be same as view attribute value.\n// @param {module:utils/priorities~PriorityString} [config.converterPriority='low'] Converter priority.\n// @returns {Function} Conversion helper.\nfunction upcastAttributeToAttribute( config ) {\n\tconfig = cloneDeep( config );\n\n\tlet viewKey = null;\n\n\tif ( typeof config.view == 'string' || config.view.key ) {\n\t\tviewKey = normalizeViewAttributeKeyValueConfig( config );\n\t}\n\n\tnormalizeModelAttributeConfig( config, viewKey );\n\n\tconst converter = prepareToAttributeConverter( config, true );\n\n\treturn dispatcher => {\n\t\tdispatcher.on( 'element', converter, { priority: config.converterPriority || 'low' } );\n\t};\n}\n\n// View element to model marker conversion helper.\n//\n// See {@link ~UpcastHelpers#elementToMarker `.elementToMarker()` upcast helper} for examples.\n//\n// @param {Object} config Conversion configuration.\n// @param {module:engine/view/matcher~MatcherPattern} config.view Pattern matching all view elements which should be converted.\n// @param {String|Function} config.model Name of the model marker, or a function that takes a view element and returns\n// a model marker name.\n// @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.\n// @returns {Function} Conversion helper.\nfunction upcastElementToMarker( config ) {\n\tconfig = cloneDeep( config );\n\n\tnormalizeElementToMarkerConfig( config );\n\n\treturn upcastElementToElement( config );\n}\n\n// View data to model marker conversion helper.\n//\n// See {@link ~UpcastHelpers#dataToMarker} to learn more.\n//\n// @param {Object} config\n// @param {String} config.view\n// @param {Function} [config.model]\n// @param {module:utils/priorities~PriorityString} [config.converterPriority='normal']\n// @returns {Function} Conversion helper.\nfunction upcastDataToMarker( config ) {\n\tconfig = cloneDeep( config );\n\n\t// Default conversion.\n\tif ( !config.model ) {\n\t\tconfig.model = name => {\n\t\t\treturn name ? config.view + ':' + name : config.view;\n\t\t};\n\t}\n\n\tconst converterStart = prepareToElementConverter( normalizeDataToMarkerConfig( config, 'start' ) );\n\tconst converterEnd = prepareToElementConverter( normalizeDataToMarkerConfig( config, 'end' ) );\n\n\treturn dispatcher => {\n\t\tdispatcher.on( 'element:' + config.view + '-start', converterStart, { priority: config.converterPriority || 'normal' } );\n\t\tdispatcher.on( 'element:' + config.view + '-end', converterEnd, { priority: config.converterPriority || 'normal' } );\n\n\t\t// Below is a hack that is needed to properly handle `converterPriority` for both elements and attributes.\n\t\t// Attribute conversion needs to be performed *after* element conversion.\n\t\t// This converter handles both element conversion and attribute conversion, which means that if a single\n\t\t// `config.converterPriority` is used, it will lead to problems. For example, if `'high'` priority is used,\n\t\t// then attribute conversion will be performed before a lot of element upcast converters.\n\t\t// On the other hand we want to support `config.converterPriority` and overwriting conveters.\n\t\t//\n\t\t// To have it work, we need to do some extra processing for priority for attribute converter.\n\t\t// Priority `'low'` value should be the base value and then we will change it depending on `config.converterPriority` value.\n\t\t//\n\t\t// This hack probably would not be needed if attributes are upcasted separately.\n\t\t//\n\t\tconst basePriority = priorities.get( 'low' );\n\t\tconst maxPriority = priorities.get( 'highest' );\n\t\tconst priorityFactor = priorities.get( config.converterPriority ) / maxPriority; // Number in range [ -1, 1 ].\n\n\t\tdispatcher.on( 'element', upcastAttributeToMarker( config ), { priority: basePriority + priorityFactor } );\n\t};\n}\n\n// Function factory, returns a callback function which converts view attributes to a model marker.\n//\n// The converter looks for elements with `data-group-start-before`, `data-group-start-after`, `data-group-end-before`\n// and `data-group-end-after` attributes and inserts `$marker` model elements before/after those elements.\n// `group` part is specified in `config.view`.\n//\n// @param {Object} config\n// @param {String} config.view\n// @param {Function} [config.model]\n// @returns {Function} Marker converter.\nfunction upcastAttributeToMarker( config ) {\n\treturn ( evt, data, conversionApi ) => {\n\t\tconst attrName = `data-${ config.view }`;\n\n\t\t// This converter wants to add a model element, marking a marker, before/after an element (or maybe even group of elements).\n\t\t// To do that, we can use `data.modelRange` which is set on an element (or a group of elements) that has been upcasted.\n\t\t// But, if the processed view element has not been upcasted yet (it does not have been converted), we need to\n\t\t// fire conversion for its children first, then we will have `data.modelRange` available.\n\t\tif ( !data.modelRange ) {\n\t\t\tdata = Object.assign( data, conversionApi.convertChildren( data.viewItem, data.modelCursor ) );\n\t\t}\n\n\t\tif ( conversionApi.consumable.consume( data.viewItem, { attributes: attrName + '-end-after' } ) ) {\n\t\t\taddMarkerElements( data.modelRange.end, data.viewItem.getAttribute( attrName + '-end-after' ).split( ',' ) );\n\t\t}\n\n\t\tif ( conversionApi.consumable.consume( data.viewItem, { attributes: attrName + '-start-after' } ) ) {\n\t\t\taddMarkerElements( data.modelRange.end, data.viewItem.getAttribute( attrName + '-start-after' ).split( ',' ) );\n\t\t}\n\n\t\tif ( conversionApi.consumable.consume( data.viewItem, { attributes: attrName + '-end-before' } ) ) {\n\t\t\taddMarkerElements( data.modelRange.start, data.viewItem.getAttribute( attrName + '-end-before' ).split( ',' ) );\n\t\t}\n\n\t\tif ( conversionApi.consumable.consume( data.viewItem, { attributes: attrName + '-start-before' } ) ) {\n\t\t\taddMarkerElements( data.modelRange.start, data.viewItem.getAttribute( attrName + '-start-before' ).split( ',' ) );\n\t\t}\n\n\t\tfunction addMarkerElements( position, markerViewNames ) {\n\t\t\tfor ( const markerViewName of markerViewNames ) {\n\t\t\t\tconst markerName = config.model( markerViewName, conversionApi );\n\t\t\t\tconst element = conversionApi.writer.createElement( '$marker', { 'data-name': markerName } );\n\n\t\t\t\tconversionApi.writer.insert( element, position );\n\n\t\t\t\tif ( data.modelCursor.isEqual( position ) ) {\n\t\t\t\t\tdata.modelCursor = data.modelCursor.getShiftedBy( 1 );\n\t\t\t\t} else {\n\t\t\t\t\tdata.modelCursor = data.modelCursor._getTransformedByInsertion( position, 1 );\n\t\t\t\t}\n\n\t\t\t\tdata.modelRange = data.modelRange._getTransformedByInsertion( position, 1 )[ 0 ];\n\t\t\t}\n\t\t}\n\t};\n}\n\n// Helper function for from-view-element conversion. Checks if `config.view` directly specifies converted view element's name\n// and if so, returns it.\n//\n// @param {Object} config Conversion view config.\n// @returns {String|null} View element name or `null` if name is not directly set.\nfunction getViewElementNameFromConfig( viewConfig ) {\n\tif ( typeof viewConfig == 'string' ) {\n\t\treturn viewConfig;\n\t}\n\n\tif ( typeof viewConfig == 'object' && typeof viewConfig.name == 'string' ) {\n\t\treturn viewConfig.name;\n\t}\n\n\treturn null;\n}\n\n// Helper for to-model-element conversion. Takes a config object and returns a proper converter function.\n//\n// @param {Object} config Conversion configuration.\n// @returns {Function} View to model converter.\nfunction prepareToElementConverter( config ) {\n\tconst matcher = new Matcher( config.view );\n\n\treturn ( evt, data, conversionApi ) => {\n\t\tconst matcherResult = matcher.match( data.viewItem );\n\n\t\tif ( !matcherResult ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst match = matcherResult.match;\n\n\t\t// Force consuming element's name.\n\t\tmatch.name = true;\n\n\t\tif ( !conversionApi.consumable.test( data.viewItem, match ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst modelElement = getModelElement( config.model, data.viewItem, conversionApi );\n\n\t\tif ( !modelElement ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( !conversionApi.safeInsert( modelElement, data.modelCursor ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconversionApi.consumable.consume( data.viewItem, match );\n\t\tconversionApi.convertChildren( data.viewItem, modelElement );\n\t\tconversionApi.updateConversionResult( modelElement, data );\n\t};\n}\n\n// Helper function for upcasting-to-element converter. Takes the model configuration, the converted view element\n// and a writer instance and returns a model element instance to be inserted in the model.\n//\n// @param {String|Function|module:engine/model/element~Element} model Model conversion configuration.\n// @param {module:engine/view/node~Node} input The converted view node.\n// @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi The upcast conversion API.\nfunction getModelElement( model, input, conversionApi ) {\n\tif ( model instanceof Function ) {\n\t\treturn model( input, conversionApi );\n\t} else {\n\t\treturn conversionApi.writer.createElement( model );\n\t}\n}\n\n// Helper function view-attribute-to-model-attribute helper. Normalizes `config.view` which was set as `String` or\n// as an `Object` with `key`, `value` and `name` properties. Normalized `config.view` has is compatible with\n// {@link module:engine/view/matcher~MatcherPattern}.\n//\n// @param {Object} config Conversion config.\n// @returns {String} Key of the converted view attribute.\nfunction normalizeViewAttributeKeyValueConfig( config ) {\n\tif ( typeof config.view == 'string' ) {\n\t\tconfig.view = { key: config.view };\n\t}\n\n\tconst key = config.view.key;\n\tlet normalized;\n\n\tif ( key == 'class' || key == 'style' ) {\n\t\tconst keyName = key == 'class' ? 'classes' : 'styles';\n\n\t\tnormalized = {\n\t\t\t[ keyName ]: config.view.value\n\t\t};\n\t} else {\n\t\tconst value = typeof config.view.value == 'undefined' ? /[\\s\\S]*/ : config.view.value;\n\n\t\tnormalized = {\n\t\t\tattributes: {\n\t\t\t\t[ key ]: value\n\t\t\t}\n\t\t};\n\t}\n\n\tif ( config.view.name ) {\n\t\tnormalized.name = config.view.name;\n\t}\n\n\tconfig.view = normalized;\n\n\treturn key;\n}\n\n// Helper function that normalizes `config.model` in from-model-attribute conversion. `config.model` can be set\n// as a `String`, an `Object` with only `key` property or an `Object` with `key` and `value` properties. Normalized\n// `config.model` is an `Object` with `key` and `value` properties.\n//\n// @param {Object} config Conversion config.\n// @param {String} viewAttributeKeyToCopy Key of the converted view attribute. If it is set, model attribute value\n// will be equal to view attribute value.\nfunction normalizeModelAttributeConfig( config, viewAttributeKeyToCopy = null ) {\n\tconst defaultModelValue = viewAttributeKeyToCopy === null ? true : viewElement => viewElement.getAttribute( viewAttributeKeyToCopy );\n\n\tconst key = typeof config.model != 'object' ? config.model : config.model.key;\n\tconst value = typeof config.model != 'object' || typeof config.model.value == 'undefined' ? defaultModelValue : config.model.value;\n\n\tconfig.model = { key, value };\n}\n\n// Helper for to-model-attribute conversion. Takes the model attribute name and conversion configuration and returns\n// a proper converter function.\n//\n// @param {String} modelAttributeKey The key of the model attribute to set on a model node.\n// @param {Object|Array.} config Conversion configuration. It is possible to provide multiple configurations in an array.\n// @param {Boolean} shallow If set to `true` the attribute will be set only on top-level nodes. Otherwise, it will be set\n// on all elements in the range.\nfunction prepareToAttributeConverter( config, shallow ) {\n\tconst matcher = new Matcher( config.view );\n\n\treturn ( evt, data, conversionApi ) => {\n\t\tconst match = matcher.match( data.viewItem );\n\n\t\t// If there is no match, this callback should not do anything.\n\t\tif ( !match ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst modelKey = config.model.key;\n\t\tconst modelValue = typeof config.model.value == 'function' ?\n\t\t\tconfig.model.value( data.viewItem, conversionApi ) : config.model.value;\n\n\t\t// Do not convert if attribute building function returned falsy value.\n\t\tif ( modelValue === null ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( onlyViewNameIsDefined( config.view, data.viewItem ) ) {\n\t\t\tmatch.match.name = true;\n\t\t} else {\n\t\t\t// Do not test or consume `name` consumable.\n\t\t\tdelete match.match.name;\n\t\t}\n\n\t\t// Try to consume appropriate values from consumable values list.\n\t\tif ( !conversionApi.consumable.test( data.viewItem, match.match ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Since we are converting to attribute we need an range on which we will set the attribute.\n\t\t// If the range is not created yet, we will create it.\n\t\tif ( !data.modelRange ) {\n\t\t\t// Convert children and set conversion result as a current data.\n\t\t\tdata = Object.assign( data, conversionApi.convertChildren( data.viewItem, data.modelCursor ) );\n\t\t}\n\n\t\t// Set attribute on current `output`. `Schema` is checked inside this helper function.\n\t\tconst attributeWasSet = setAttributeOn( data.modelRange, { key: modelKey, value: modelValue }, shallow, conversionApi );\n\n\t\tif ( attributeWasSet ) {\n\t\t\tconversionApi.consumable.consume( data.viewItem, match.match );\n\t\t}\n\t};\n}\n\n// Helper function that checks if element name should be consumed in attribute converters.\n//\n// @param {Object} config Conversion view config.\n// @returns {Boolean}\nfunction onlyViewNameIsDefined( viewConfig, viewItem ) {\n\t// https://github.com/ckeditor/ckeditor5-engine/issues/1786\n\tconst configToTest = typeof viewConfig == 'function' ? viewConfig( viewItem ) : viewConfig;\n\n\tif ( typeof configToTest == 'object' && !getViewElementNameFromConfig( configToTest ) ) {\n\t\treturn false;\n\t}\n\n\treturn !configToTest.classes && !configToTest.attributes && !configToTest.styles;\n}\n\n// Helper function for to-model-attribute converter. Sets model attribute on given range. Checks {@link module:engine/model/schema~Schema}\n// to ensure proper model structure.\n//\n// @param {module:engine/model/range~Range} modelRange Model range on which attribute should be set.\n// @param {Object} modelAttribute Model attribute to set.\n// @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi Conversion API.\n// @param {Boolean} shallow If set to `true` the attribute will be set only on top-level nodes. Otherwise, it will be set\n// on all elements in the range.\n// @returns {Boolean} `true` if attribute was set on at least one node from given `modelRange`.\nfunction setAttributeOn( modelRange, modelAttribute, shallow, conversionApi ) {\n\tlet result = false;\n\n\t// Set attribute on each item in range according to Schema.\n\tfor ( const node of Array.from( modelRange.getItems( { shallow } ) ) ) {\n\t\tif ( conversionApi.schema.checkAttribute( node, modelAttribute.key ) ) {\n\t\t\tconversionApi.writer.setAttribute( modelAttribute.key, modelAttribute.value, node );\n\n\t\t\tresult = true;\n\t\t}\n\t}\n\n\treturn result;\n}\n\n// Helper function for upcasting-to-marker conversion. Takes the config in a format requested by `upcastElementToMarker()`\n// function and converts it to a format that is supported by `upcastElementToElement()` function.\n//\n// @param {Object} config Conversion configuration.\nfunction normalizeElementToMarkerConfig( config ) {\n\tconst oldModel = config.model;\n\n\tconfig.model = ( viewElement, conversionApi ) => {\n\t\tconst markerName = typeof oldModel == 'string' ? oldModel : oldModel( viewElement, conversionApi );\n\n\t\treturn conversionApi.writer.createElement( '$marker', { 'data-name': markerName } );\n\t};\n}\n\n// Helper function for upcasting-to-marker conversion. Takes the config in a format requested by `upcastDataToMarker()`\n// function and converts it to a format that is supported by `upcastElementToElement()` function.\n//\n// @param {Object} config Conversion configuration.\nfunction normalizeDataToMarkerConfig( config, type ) {\n\tconst configForElements = {};\n\n\t// Upcast and elements.\n\tconfigForElements.view = config.view + '-' + type;\n\n\tconfigForElements.model = ( viewElement, conversionApi ) => {\n\t\tconst viewName = viewElement.getAttribute( 'name' );\n\t\tconst markerName = config.model( viewName, conversionApi );\n\n\t\treturn conversionApi.writer.createElement( '$marker', { 'data-name': markerName } );\n\t};\n\n\treturn configForElements;\n}\n"],"sourceRoot":""}