{"version":3,"sources":["webpack:///./node_modules/@ckeditor/ckeditor5-engine/src/view/downcastwriter.js"],"names":["DowncastWriter","document","Object","D_Projects_UA_repo_Source_Client_UA_User_Web_node_modules_babel_runtime_helpers_esm_classCallCheck_js__WEBPACK_IMPORTED_MODULE_12__","this","_cloneGroups","Map","selectable","placeOrOffset","options","selection","_setTo","itemOrPosition","offset","_setFocus","children","DocumentFragment","data","Text","name","attributes","arguments","length","undefined","attributeElement","AttributeElement","priority","_priority","id","_id","containerElement","ContainerElement","isAllowedInsideAttributeElement","_isAllowedInsideAttributeElement","editableElement","EditableElement","_document","emptyElement","EmptyElement","renderFunction","uiElement","UIElement","render","rawElement","RawElement","key","value","element","_setAttribute","_removeAttribute","className","_addClass","_removeClass","property","isPlainObject","_setStyle","_removeStyle","_setCustomProperty","_removeCustomProperty","positionOrRange","Position","_breakAttributes","_breakAttributesRange","position","parent","is","CKEditorError","isAtStart","_createBefore","isAtEnd","newElement","_clone","insert","_createAfter","sourceRange","Range","_createAt","targetPosition","move","positionOffset","positionParent","childCount","index","_remove","_removeFromClonedElementsGroup","mergeAttributes","nodeBefore","getChild","nodeAfter","mergeTextNodes","isSimilar","count","_appendChild","getChildren","prev","next","lastChild","newPosition","_createIn","remove","_createOn","nodes","isIterable","D_Projects_UA_repo_Source_Client_UA_User_Web_node_modules_babel_runtime_helpers_esm_toConsumableArray_js__WEBPACK_IMPORTED_MODULE_8__","validateNodesToInsert","_step","nodeGroups","reduce","groups","node","lastGroup","breakAttributes","push","start","end","_iterator","_createForOfIteratorHelper","s","n","done","_step$value","range","_insertNodes","err","e","f","rangeOrItem","validateRangeContainer","isCollapsed","_step2","_this$_breakAttribute","breakStart","breakEnd","parentContainer","removed","_removeChildren","_iterator2","mergePosition","clone","_step3","walker","getWalker","direction","ignoreElementEnd","_iterator3","current","item","rangeToRemove","nextPosition","isAfter","parentElement","getAncestors","find","ancestor","isBefore","countBefore","attribute","_hasNonUiChildren","getLastMatchingPosition","_wrapPosition","viewSelection","getFirstPosition","isEqual","setSelection","_wrapRange","_this$_breakAttribute2","newRange","_unwrapChildren","newName","viewElement","getAttributes","groupName","delete","Selection","insertionPosition","getParentContainer","breakTextNode","_step4","_insertChild","_iterator4","_addToClonedElementsGroup","endPosition","getShiftedBy","startOffset","endOffset","wrapElement","i","wrapPositions","child","isText","isAttribute","_wrapAttributeElement","shouldABeOutsideB","newAttribute","_wrapChildren","offsetChange","_i","_wrapPositions","_createFromParentsAndOffsets","unwrapElement","unwrapPositions","unwrapped","_unwrapAttributeElement","_i2","_unwrapPositions","_this$_breakAttribute3","movePositionToTextNode","fakePosition","createAttributeElement","Number","POSITIVE_INFINITY","wrapRange","wrap","wrapper","toWrap","canBeJoined","_step5","_iterator5","getAttributeKeys","hasAttribute","getAttribute","_step6","_iterator6","getStyleNames","hasStyle","getStyle","_step7","_iterator7","setAttribute","_step8","_iterator8","setStyle","_step9","_iterator9","getClassNames","hasClass","addClass","toUnwrap","_step10","_iterator10","apply","_step11","_iterator11","_step12","_iterator12","removeAttribute","removeClass","Array","from","removeStyle","forceSplitText","rangeStart","rangeEnd","isContainerOrFragment","offsetAfter","clonedNode","nodesToMove","root","_step13","_iterator13","group","get","Set","set","add","_clonesGroup","_step14","_iterator14","some","a","b","getIdentity","textToMove","slice","_data","t1","t2","nodeBeforeLength","errorContext","_step15","_iterator15","_loop","validNodesToInsert","validNode","startContainer","endContainer"],"mappings":"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","file":"js/chunk-2d21b8d8.50bf82cf.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 module:engine/view/downcastwriter\n */\n\nimport Position from './position';\nimport Range from './range';\nimport Selection from './selection';\nimport ContainerElement from './containerelement';\nimport AttributeElement from './attributeelement';\nimport EmptyElement from './emptyelement';\nimport UIElement from './uielement';\nimport RawElement from './rawelement';\nimport CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';\nimport DocumentFragment from './documentfragment';\nimport isIterable from '@ckeditor/ckeditor5-utils/src/isiterable';\nimport Text from './text';\nimport EditableElement from './editableelement';\nimport { isPlainObject } from 'lodash-es';\n\n/**\n * View downcast writer.\n *\n * It provides a set of methods used to manipulate view nodes.\n *\n * Do not create an instance of this writer manually. To modify a view structure, use\n * the {@link module:engine/view/view~View#change `View#change()`} block.\n *\n * The `DowncastWriter` is designed to work with semantic views which are the views that were/are being downcasted from the model.\n * To work with ordinary views (e.g. parsed from a pasted content) use the\n * {@link module:engine/view/upcastwriter~UpcastWriter upcast writer}.\n *\n * Read more about changing the view in the {@glink framework/guides/architecture/editing-engine#changing-the-view Changing the view}\n * section of the {@glink framework/guides/architecture/editing-engine Editing engine architecture} guide.\n */\nexport default class DowncastWriter {\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 * The view document instance in which this writer operates.\n\t\t *\n\t\t * @readonly\n\t\t * @type {module:engine/view/document~Document}\n\t\t */\n\t\tthis.document = document;\n\n\t\t/**\n\t\t * Holds references to the attribute groups that share the same {@link module:engine/view/attributeelement~AttributeElement#id id}.\n\t\t * The keys are `id`s, the values are `Set`s holding {@link module:engine/view/attributeelement~AttributeElement}s.\n\t\t *\n\t\t * @private\n\t\t * @type {Map.}\n\t\t */\n\t\tthis._cloneGroups = new Map();\n\t}\n\n\t/**\n\t * Sets {@link module:engine/view/documentselection~DocumentSelection selection's} ranges and direction to the\n\t * specified location based on the given {@link module:engine/view/selection~Selectable selectable}.\n\t *\n\t * Usage:\n\t *\n\t *\t\t// Sets selection to the given range.\n\t *\t\tconst range = writer.createRange( start, end );\n\t *\t\twriter.setSelection( range );\n\t *\n\t *\t\t// Sets backward selection to the given range.\n\t *\t\tconst range = writer.createRange( start, end );\n\t *\t\twriter.setSelection( range );\n\t *\n\t *\t\t// Sets selection to given ranges.\n\t * \t\tconst ranges = [ writer.createRange( start1, end2 ), writer.createRange( start2, end2 ) ];\n\t *\t\twriter.setSelection( range );\n\t *\n\t *\t\t// Sets selection to the other selection.\n\t *\t\tconst otherSelection = writer.createSelection();\n\t *\t\twriter.setSelection( otherSelection );\n\t *\n\t * \t\t// Sets collapsed selection at the given position.\n\t *\t\tconst position = writer.createPositionFromPath( root, path );\n\t *\t\twriter.setSelection( position );\n\t *\n\t * \t\t// Sets collapsed selection at the position of given item and offset.\n\t *\t\tconst paragraph = writer.createContainerElement( 'p' );\n\t *\t\twriter.setSelection( paragraph, offset );\n\t *\n\t * Creates a range inside an {@link module:engine/view/element~Element element} which starts before the first child of\n \t * that element and ends after the last child of that element.\n\t *\n\t * \t\twriter.setSelection( paragraph, 'in' );\n\t *\n\t * Creates a range on the {@link module:engine/view/item~Item item} which starts before the item and ends just after the item.\n\t *\n\t *\t\twriter.setSelection( paragraph, 'on' );\n\t *\n\t * \t\t// Removes all ranges.\n\t *\t\twriter.setSelection( null );\n\t *\n\t * `DowncastWriter#setSelection()` allow passing additional options (`backward`, `fake` and `label`) as the last argument.\n\t *\n\t *\t\t// Sets selection as backward.\n\t *\t\twriter.setSelection( range, { backward: true } );\n\t *\n\t *\t\t// Sets selection as fake.\n\t *\t\t// Fake selection does not render as browser native selection over selected elements and is hidden to the user.\n\t * \t\t// This way, no native selection UI artifacts are displayed to the user and selection over elements can be\n\t * \t\t// represented in other way, for example by applying proper CSS class.\n\t *\t\twriter.setSelection( range, { fake: true } );\n\t *\n\t * \t\t// Additionally fake's selection label can be provided. It will be used to describe fake selection in DOM\n\t * \t\t// (and be properly handled by screen readers).\n\t *\t\twriter.setSelection( range, { fake: true, label: 'foo' } );\n\t *\n\t * @param {module:engine/view/selection~Selectable} selectable\n\t * @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Sets place or offset of the selection.\n\t * @param {Object} [options]\n\t * @param {Boolean} [options.backward] Sets this selection instance to be backward.\n\t * @param {Boolean} [options.fake] Sets this selection instance to be marked as `fake`.\n\t * @param {String} [options.label] Label for the fake selection.\n\t */\n\tsetSelection( selectable, placeOrOffset, options ) {\n\t\tthis.document.selection._setTo( selectable, placeOrOffset, options );\n\t}\n\n\t/**\n\t * Moves {@link module:engine/view/documentselection~DocumentSelection#focus selection's focus} to the specified location.\n\t *\n\t * The location can be specified in the same form as {@link module:engine/view/view~View#createPositionAt view.createPositionAt()}\n\t * parameters.\n\t *\n\t * @param {module:engine/view/item~Item|module:engine/view/position~Position} itemOrPosition\n\t * @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when\n\t * first parameter is a {@link module:engine/view/item~Item view item}.\n\t */\n\tsetSelectionFocus( itemOrPosition, offset ) {\n\t\tthis.document.selection._setFocus( itemOrPosition, offset );\n\t}\n\n\t/**\n\t * Creates a new {@link module:engine/view/documentfragment~DocumentFragment} instance.\n\t *\n\t * @param {module:engine/view/node~Node|Iterable.} [children]\n\t * A list of nodes to be inserted into the created document fragment.\n\t * @returns {module:engine/view/documentfragment~DocumentFragment} The created document fragment.\n\t */\n\tcreateDocumentFragment( children ) {\n\t\treturn new DocumentFragment( this.document, children );\n\t}\n\n\t/**\n\t * Creates a new {@link module:engine/view/text~Text text node}.\n\t *\n\t *\t\twriter.createText( 'foo' );\n\t *\n\t * @param {String} data The text's data.\n\t * @returns {module:engine/view/text~Text} The created text node.\n\t */\n\tcreateText( data ) {\n\t\treturn new Text( this.document, data );\n\t}\n\n\t/**\n\t * Creates a new {@link module:engine/view/attributeelement~AttributeElement}.\n\t *\n\t *\t\twriter.createAttributeElement( 'strong' );\n\t *\t\twriter.createAttributeElement( 'a', { href: 'foo.bar' } );\n\t *\n\t *\t\t// Make `` element contain other attributes element so the `` element is not broken.\n\t *\t\twriter.createAttributeElement( 'a', { href: 'foo.bar' }, { priority: 5 } );\n\t *\n\t *\t\t// Set `id` of a marker element so it is not joined or merged with \"normal\" elements.\n\t *\t\twriter.createAttributeElement( 'span', { class: 'my-marker' }, { id: 'marker:my' } );\n\t *\n\t * **Note:** By default an `AttributeElement` is split by a\n\t * {@link module:engine/view/containerelement~ContainerElement `ContainerElement`} but this behavior can be modified\n\t * with `isAllowedInsideAttributeElement` option set while {@link #createContainerElement creating the element}.\n\t *\n\t * @param {String} name Name of the element.\n\t * @param {Object} [attributes] Element's attributes.\n\t * @param {Object} [options] Element's options.\n\t * @param {Number} [options.priority] Element's {@link module:engine/view/attributeelement~AttributeElement#priority priority}.\n\t * @param {Number|String} [options.id] Element's {@link module:engine/view/attributeelement~AttributeElement#id id}.\n\t * @returns {module:engine/view/attributeelement~AttributeElement} Created element.\n\t */\n\tcreateAttributeElement( name, attributes, options = {} ) {\n\t\tconst attributeElement = new AttributeElement( this.document, name, attributes );\n\n\t\tif ( typeof options.priority === 'number' ) {\n\t\t\tattributeElement._priority = options.priority;\n\t\t}\n\n\t\tif ( options.id ) {\n\t\t\tattributeElement._id = options.id;\n\t\t}\n\n\t\treturn attributeElement;\n\t}\n\n\t/**\n\t * Creates a new {@link module:engine/view/containerelement~ContainerElement}.\n\t *\n\t *\t\twriter.createContainerElement( 'p' );\n\t *\n\t *\t\t// Create element with custom attributes.\n\t *\t\twriter.createContainerElement( 'div', { id: 'foo-bar', 'data-baz': '123' } );\n\t *\n\t *\t\t// Create element with custom styles.\n\t *\t\twriter.createContainerElement( 'p', { style: 'font-weight: bold; padding-bottom: 10px' } );\n\t *\n\t *\t\t// Create element with custom classes.\n\t *\t\twriter.createContainerElement( 'p', { class: 'foo bar baz' } );\n\t *\n\t * @param {String} name Name of the element.\n\t * @param {Object} [attributes] Elements attributes.\n\t * @param {Object} [options] Element's options.\n\t * @param {Boolean} [options.isAllowedInsideAttributeElement=false] Whether an element is\n\t * {@link module:engine/view/element~Element#isAllowedInsideAttributeElement allowed inside an AttributeElement} and can be wrapped\n\t * with {@link module:engine/view/attributeelement~AttributeElement} by {@link module:engine/view/downcastwriter~DowncastWriter}.\n\t * @returns {module:engine/view/containerelement~ContainerElement} Created element.\n\t */\n\tcreateContainerElement( name, attributes, options = {} ) {\n\t\tconst containerElement = new ContainerElement( this.document, name, attributes );\n\n\t\tif ( options.isAllowedInsideAttributeElement !== undefined ) {\n\t\t\tcontainerElement._isAllowedInsideAttributeElement = options.isAllowedInsideAttributeElement;\n\t\t}\n\n\t\treturn containerElement;\n\t}\n\n\t/**\n\t * Creates a new {@link module:engine/view/editableelement~EditableElement}.\n\t *\n\t *\t\twriter.createEditableElement( 'div' );\n\t *\t\twriter.createEditableElement( 'div', { id: 'foo-1234' } );\n\t *\n\t * Note: The editable element is to be used in the editing pipeline. Usually, together with\n\t * {@link module:widget/utils~toWidgetEditable `toWidgetEditable()`}.\n\t *\n\t * @param {String} name Name of the element.\n\t * @param {Object} [attributes] Elements attributes.\n\t * @returns {module:engine/view/editableelement~EditableElement} Created element.\n\t */\n\tcreateEditableElement( name, attributes ) {\n\t\tconst editableElement = new EditableElement( this.document, name, attributes );\n\t\teditableElement._document = this.document;\n\n\t\treturn editableElement;\n\t}\n\n\t/**\n\t * Creates a new {@link module:engine/view/emptyelement~EmptyElement}.\n\t *\n\t *\t\twriter.createEmptyElement( 'img' );\n\t *\t\twriter.createEmptyElement( 'img', { id: 'foo-1234' } );\n\t *\n\t * @param {String} name Name of the element.\n\t * @param {Object} [attributes] Elements attributes.\n\t * @param {Object} [options] Element's options.\n\t * @param {Boolean} [options.isAllowedInsideAttributeElement=true] Whether an element is\n\t * {@link module:engine/view/element~Element#isAllowedInsideAttributeElement allowed inside an AttributeElement} and can be wrapped\n\t * with {@link module:engine/view/attributeelement~AttributeElement} by {@link module:engine/view/downcastwriter~DowncastWriter}.\n\t * @returns {module:engine/view/emptyelement~EmptyElement} Created element.\n\t */\n\tcreateEmptyElement( name, attributes, options = {} ) {\n\t\tconst emptyElement = new EmptyElement( this.document, name, attributes );\n\n\t\tif ( options.isAllowedInsideAttributeElement !== undefined ) {\n\t\t\temptyElement._isAllowedInsideAttributeElement = options.isAllowedInsideAttributeElement;\n\t\t}\n\n\t\treturn emptyElement;\n\t}\n\n\t/**\n\t * Creates a new {@link module:engine/view/uielement~UIElement}.\n\t *\n\t *\t\twriter.createUIElement( 'span' );\n\t *\t\twriter.createUIElement( 'span', { id: 'foo-1234' } );\n\t *\n\t * A custom render function can be provided as the third parameter:\n\t *\n\t *\t\twriter.createUIElement( 'span', null, function( domDocument ) {\n\t *\t\t\tconst domElement = this.toDomElement( domDocument );\n\t *\t\t\tdomElement.innerHTML = 'this is ui element';\n\t *\n\t *\t\t\treturn domElement;\n\t *\t\t} );\n\t *\n\t * Unlike {@link #createRawElement raw elements}, UI elements are by no means editor content, for instance,\n\t * they are ignored by the editor selection system.\n\t *\n\t * You should not use UI elements as data containers. Check out {@link #createRawElement} instead.\n\t *\n\t * @param {String} name The name of the element.\n\t * @param {Object} [attributes] Element attributes.\n\t * @param {Function} [renderFunction] A custom render function.\n\t * @param {Object} [options] Element's options.\n\t * @param {Boolean} [options.isAllowedInsideAttributeElement=true] Whether an element is\n\t * {@link module:engine/view/element~Element#isAllowedInsideAttributeElement allowed inside an AttributeElement} and can be wrapped\n\t * with {@link module:engine/view/attributeelement~AttributeElement} by {@link module:engine/view/downcastwriter~DowncastWriter}.\n\t * @returns {module:engine/view/uielement~UIElement} The created element.\n\t */\n\tcreateUIElement( name, attributes, renderFunction, options = {} ) {\n\t\tconst uiElement = new UIElement( this.document, name, attributes );\n\n\t\tif ( renderFunction ) {\n\t\t\tuiElement.render = renderFunction;\n\t\t}\n\n\t\tif ( options.isAllowedInsideAttributeElement !== undefined ) {\n\t\t\tuiElement._isAllowedInsideAttributeElement = options.isAllowedInsideAttributeElement;\n\t\t}\n\n\t\treturn uiElement;\n\t}\n\n\t/**\n\t * Creates a new {@link module:engine/view/rawelement~RawElement}.\n\t *\n\t *\t\twriter.createRawElement( 'span', { id: 'foo-1234' }, function( domElement ) {\n\t *\t\t\tdomElement.innerHTML = 'This is the raw content of the raw element.';\n\t *\t\t} );\n\t *\n\t * Raw elements work as data containers (\"wrappers\", \"sandboxes\") but their children are not managed or\n\t * even recognized by the editor. This encapsulation allows integrations to maintain custom DOM structures\n\t * in the editor content without, for instance, worrying about compatibility with other editor features.\n\t * Raw elements are a perfect tool for integration with external frameworks and data sources.\n\t *\n\t * Unlike {@link #createUIElement UI elements}, raw elements act like \"real\" editor content (similar to\n\t * {@link module:engine/view/containerelement~ContainerElement} or {@link module:engine/view/emptyelement~EmptyElement}),\n\t * and they are considered by the editor selection.\n\t *\n\t * You should not use raw elements to render the UI in the editor content. Check out {@link #createUIElement `#createUIElement()`}\n\t * instead.\n\t *\n\t * @param {String} name The name of the element.\n\t * @param {Object} [attributes] Element attributes.\n\t * @param {Function} [renderFunction] A custom render function.\n\t * @param {Object} [options] Element's options.\n\t * @param {Boolean} [options.isAllowedInsideAttributeElement=true] Whether an element is\n\t * {@link module:engine/view/element~Element#isAllowedInsideAttributeElement allowed inside an AttributeElement} and can be wrapped\n\t * with {@link module:engine/view/attributeelement~AttributeElement} by {@link module:engine/view/downcastwriter~DowncastWriter}.\n\t * @returns {module:engine/view/rawelement~RawElement} The created element.\n\t */\n\tcreateRawElement( name, attributes, renderFunction, options = {} ) {\n\t\tconst rawElement = new RawElement( this.document, name, attributes );\n\n\t\trawElement.render = renderFunction || ( () => {} );\n\n\t\tif ( options.isAllowedInsideAttributeElement !== undefined ) {\n\t\t\trawElement._isAllowedInsideAttributeElement = options.isAllowedInsideAttributeElement;\n\t\t}\n\n\t\treturn rawElement;\n\t}\n\n\t/**\n\t * Adds or overwrites the element's attribute with a specified key and value.\n\t *\n\t *\t\twriter.setAttribute( 'href', 'http://ckeditor.com', linkElement );\n\t *\n\t * @param {String} key The attribute key.\n\t * @param {String} value The attribute value.\n\t * @param {module:engine/view/element~Element} element\n\t */\n\tsetAttribute( key, value, element ) {\n\t\telement._setAttribute( key, value );\n\t}\n\n\t/**\n\t * Removes attribute from the element.\n\t *\n\t *\t\twriter.removeAttribute( 'href', linkElement );\n\t *\n\t * @param {String} key Attribute key.\n\t * @param {module:engine/view/element~Element} element\n\t */\n\tremoveAttribute( key, element ) {\n\t\telement._removeAttribute( key );\n\t}\n\n\t/**\n\t * Adds specified class to the element.\n\t *\n\t *\t\twriter.addClass( 'foo', linkElement );\n\t *\t\twriter.addClass( [ 'foo', 'bar' ], linkElement );\n\t *\n\t * @param {Array.|String} className\n\t * @param {module:engine/view/element~Element} element\n\t */\n\taddClass( className, element ) {\n\t\telement._addClass( className );\n\t}\n\n\t/**\n\t * Removes specified class from the element.\n\t *\n\t *\t\twriter.removeClass( 'foo', linkElement );\n\t *\t\twriter.removeClass( [ 'foo', 'bar' ], linkElement );\n\t *\n\t * @param {Array.|String} className\n\t * @param {module:engine/view/element~Element} element\n\t */\n\tremoveClass( className, element ) {\n\t\telement._removeClass( className );\n\t}\n\n\t/**\n\t * Adds style to the element.\n\t *\n\t *\t\twriter.setStyle( 'color', 'red', element );\n\t *\t\twriter.setStyle( {\n\t *\t\t\tcolor: 'red',\n\t *\t\t\tposition: 'fixed'\n\t *\t\t}, element );\n\t *\n\t * **Note**: The passed style can be normalized if\n\t * {@link module:engine/controller/datacontroller~DataController#addStyleProcessorRules a particular style processor rule is enabled}.\n\t * See {@link module:engine/view/stylesmap~StylesMap#set `StylesMap#set()`} for details.\n\t *\n\t * @param {String|Object} property Property name or object with key - value pairs.\n\t * @param {String} [value] Value to set. This parameter is ignored if object is provided as the first parameter.\n\t * @param {module:engine/view/element~Element} element Element to set styles on.\n\t */\n\tsetStyle( property, value, element ) {\n\t\tif ( isPlainObject( property ) && element === undefined ) {\n\t\t\telement = value;\n\t\t}\n\n\t\telement._setStyle( property, value );\n\t}\n\n\t/**\n\t * Removes specified style from the element.\n\t *\n\t *\t\twriter.removeStyle( 'color', element ); // Removes 'color' style.\n\t *\t\twriter.removeStyle( [ 'color', 'border-top' ], element ); // Removes both 'color' and 'border-top' styles.\n\t *\n\t * **Note**: This method can work with normalized style names if\n\t * {@link module:engine/controller/datacontroller~DataController#addStyleProcessorRules a particular style processor rule is enabled}.\n\t * See {@link module:engine/view/stylesmap~StylesMap#remove `StylesMap#remove()`} for details.\n\t *\n\t * @param {Array.|String} property\n\t * @param {module:engine/view/element~Element} element\n\t */\n\tremoveStyle( property, element ) {\n\t\telement._removeStyle( property );\n\t}\n\n\t/**\n\t * Sets a custom property on element. Unlike attributes, custom properties are not rendered to the DOM,\n\t * so they can be used to add special data to elements.\n\t *\n\t * @param {String|Symbol} key\n\t * @param {*} value\n\t * @param {module:engine/view/element~Element} element\n\t */\n\tsetCustomProperty( key, value, element ) {\n\t\telement._setCustomProperty( key, value );\n\t}\n\n\t/**\n\t * Removes a custom property stored under the given key.\n\t *\n\t * @param {String|Symbol} key\n\t * @param {module:engine/view/element~Element} element\n\t * @returns {Boolean} Returns true if property was removed.\n\t */\n\tremoveCustomProperty( key, element ) {\n\t\treturn element._removeCustomProperty( key );\n\t}\n\n\t/**\n\t * Breaks attribute elements at the provided position or at the boundaries of a provided range. It breaks attribute elements\n\t * up to their first ancestor that is a container element.\n\t *\n\t * In following examples `

` is a container, `` and `` are attribute elements:\n\t *\n\t *\t\t

foobar{}

->

foobar[]

\n\t *\t\t

foo{}bar

->

foo{}bar

\n\t *\t\t

foob{}ar

->

foob[]ar

\n\t *\t\t

fo{oba}r

->

foobar

\n\t *\n\t * **Note:** {@link module:engine/view/documentfragment~DocumentFragment DocumentFragment} is treated like a container.\n\t *\n\t * **Note:** The difference between {@link module:engine/view/downcastwriter~DowncastWriter#breakAttributes breakAttributes()} and\n\t * {@link module:engine/view/downcastwriter~DowncastWriter#breakContainer breakContainer()} is that `breakAttributes()` breaks all\n\t * {@link module:engine/view/attributeelement~AttributeElement attribute elements} that are ancestors of a given `position`,\n\t * up to the first encountered {@link module:engine/view/containerelement~ContainerElement container element}.\n\t * `breakContainer()` assumes that a given `position` is directly in the container element and breaks that container element.\n\t *\n\t * Throws the `view-writer-invalid-range-container` {@link module:utils/ckeditorerror~CKEditorError CKEditorError}\n\t * when the {@link module:engine/view/range~Range#start start}\n\t * and {@link module:engine/view/range~Range#end end} positions of a passed range are not placed inside same parent container.\n\t *\n\t * Throws the `view-writer-cannot-break-empty-element` {@link module:utils/ckeditorerror~CKEditorError CKEditorError}\n\t * when trying to break attributes inside an {@link module:engine/view/emptyelement~EmptyElement EmptyElement}.\n\t *\n\t * Throws the `view-writer-cannot-break-ui-element` {@link module:utils/ckeditorerror~CKEditorError CKEditorError}\n\t * when trying to break attributes inside a {@link module:engine/view/uielement~UIElement UIElement}.\n\t *\n\t * @see module:engine/view/attributeelement~AttributeElement\n\t * @see module:engine/view/containerelement~ContainerElement\n\t * @see module:engine/view/downcastwriter~DowncastWriter#breakContainer\n\t * @param {module:engine/view/position~Position|module:engine/view/range~Range} positionOrRange The position where\n\t * to break attribute elements.\n\t * @returns {module:engine/view/position~Position|module:engine/view/range~Range} The new position or range, after breaking the\n\t * attribute elements.\n\t */\n\tbreakAttributes( positionOrRange ) {\n\t\tif ( positionOrRange instanceof Position ) {\n\t\t\treturn this._breakAttributes( positionOrRange );\n\t\t} else {\n\t\t\treturn this._breakAttributesRange( positionOrRange );\n\t\t}\n\t}\n\n\t/**\n\t * Breaks a {@link module:engine/view/containerelement~ContainerElement container view element} into two, at the given position.\n\t * The position has to be directly inside the container element and cannot be in the root. It does not break the conrainer view element\n\t * if the position is at the beginning or at the end of its parent element.\n\t *\n\t *\t\t

foo^bar

->

foo

bar

\n\t *\t\t

foo

^

bar

->

foo

bar

\n\t *\t\t

^foobar

-> ^

foobar

\n\t *\t\t

foobar^

->

foobar

^\n\t *\n\t * **Note:** The difference between {@link module:engine/view/downcastwriter~DowncastWriter#breakAttributes breakAttributes()} and\n\t * {@link module:engine/view/downcastwriter~DowncastWriter#breakContainer breakContainer()} is that `breakAttributes()` breaks all\n\t * {@link module:engine/view/attributeelement~AttributeElement attribute elements} that are ancestors of a given `position`,\n\t * up to the first encountered {@link module:engine/view/containerelement~ContainerElement container element}.\n\t * `breakContainer()` assumes that the given `position` is directly in the container element and breaks that container element.\n\t *\n\t * @see module:engine/view/attributeelement~AttributeElement\n\t * @see module:engine/view/containerelement~ContainerElement\n\t * @see module:engine/view/downcastwriter~DowncastWriter#breakAttributes\n\t * @param {module:engine/view/position~Position} position The position where to break the element.\n\t * @returns {module:engine/view/position~Position} The position between broken elements. If an element has not been broken,\n\t * the returned position is placed either before or after it.\n\t */\n\tbreakContainer( position ) {\n\t\tconst element = position.parent;\n\n\t\tif ( !( element.is( 'containerElement' ) ) ) {\n\t\t\t/**\n\t\t\t * Trying to break an element which is not a container element.\n\t\t\t *\n\t\t\t * @error view-writer-break-non-container-element\n\t\t\t */\n\t\t\tthrow new CKEditorError( 'view-writer-break-non-container-element', this.document );\n\t\t}\n\n\t\tif ( !element.parent ) {\n\t\t\t/**\n\t\t\t * Trying to break root element.\n\t\t\t *\n\t\t\t * @error view-writer-break-root\n\t\t\t */\n\t\t\tthrow new CKEditorError( 'view-writer-break-root', this.document );\n\t\t}\n\n\t\tif ( position.isAtStart ) {\n\t\t\treturn Position._createBefore( element );\n\t\t} else if ( !position.isAtEnd ) {\n\t\t\tconst newElement = element._clone( false );\n\n\t\t\tthis.insert( Position._createAfter( element ), newElement );\n\n\t\t\tconst sourceRange = new Range( position, Position._createAt( element, 'end' ) );\n\t\t\tconst targetPosition = new Position( newElement, 0 );\n\n\t\t\tthis.move( sourceRange, targetPosition );\n\t\t}\n\n\t\treturn Position._createAfter( element );\n\t}\n\n\t/**\n\t * Merges {@link module:engine/view/attributeelement~AttributeElement attribute elements}. It also merges text nodes if needed.\n\t * Only {@link module:engine/view/attributeelement~AttributeElement#isSimilar similar} attribute elements can be merged.\n\t *\n\t * In following examples `

` is a container and `` is an attribute element:\n\t *\n\t *\t\t

foo[]bar

->

foo{}bar

\n\t *\t\t

foo[]bar

->

foo{}bar

\n\t *\t\t

a[]b

->

a[]b

\n\t *\n\t * It will also take care about empty attributes when merging:\n\t *\n\t *\t\t

[]

->

[]

\n\t *\t\t

foo[]bar

->

foo{}bar

\n\t *\n\t * **Note:** Difference between {@link module:engine/view/downcastwriter~DowncastWriter#mergeAttributes mergeAttributes} and\n\t * {@link module:engine/view/downcastwriter~DowncastWriter#mergeContainers mergeContainers} is that `mergeAttributes` merges two\n\t * {@link module:engine/view/attributeelement~AttributeElement attribute elements} or {@link module:engine/view/text~Text text nodes}\n\t * while `mergeContainer` merges two {@link module:engine/view/containerelement~ContainerElement container elements}.\n\t *\n\t * @see module:engine/view/attributeelement~AttributeElement\n\t * @see module:engine/view/containerelement~ContainerElement\n\t * @see module:engine/view/downcastwriter~DowncastWriter#mergeContainers\n\t * @param {module:engine/view/position~Position} position Merge position.\n\t * @returns {module:engine/view/position~Position} Position after merge.\n\t */\n\tmergeAttributes( position ) {\n\t\tconst positionOffset = position.offset;\n\t\tconst positionParent = position.parent;\n\n\t\t// When inside text node - nothing to merge.\n\t\tif ( positionParent.is( '$text' ) ) {\n\t\t\treturn position;\n\t\t}\n\n\t\t// When inside empty attribute - remove it.\n\t\tif ( positionParent.is( 'attributeElement' ) && positionParent.childCount === 0 ) {\n\t\t\tconst parent = positionParent.parent;\n\t\t\tconst offset = positionParent.index;\n\n\t\t\tpositionParent._remove();\n\t\t\tthis._removeFromClonedElementsGroup( positionParent );\n\n\t\t\treturn this.mergeAttributes( new Position( parent, offset ) );\n\t\t}\n\n\t\tconst nodeBefore = positionParent.getChild( positionOffset - 1 );\n\t\tconst nodeAfter = positionParent.getChild( positionOffset );\n\n\t\t// Position should be placed between two nodes.\n\t\tif ( !nodeBefore || !nodeAfter ) {\n\t\t\treturn position;\n\t\t}\n\n\t\t// When position is between two text nodes.\n\t\tif ( nodeBefore.is( '$text' ) && nodeAfter.is( '$text' ) ) {\n\t\t\treturn mergeTextNodes( nodeBefore, nodeAfter );\n\t\t}\n\t\t// When position is between two same attribute elements.\n\t\telse if ( nodeBefore.is( 'attributeElement' ) && nodeAfter.is( 'attributeElement' ) && nodeBefore.isSimilar( nodeAfter ) ) {\n\t\t\t// Move all children nodes from node placed after selection and remove that node.\n\t\t\tconst count = nodeBefore.childCount;\n\t\t\tnodeBefore._appendChild( nodeAfter.getChildren() );\n\n\t\t\tnodeAfter._remove();\n\t\t\tthis._removeFromClonedElementsGroup( nodeAfter );\n\n\t\t\t// New position is located inside the first node, before new nodes.\n\t\t\t// Call this method recursively to merge again if needed.\n\t\t\treturn this.mergeAttributes( new Position( nodeBefore, count ) );\n\t\t}\n\n\t\treturn position;\n\t}\n\n\t/**\n\t * Merges two {@link module:engine/view/containerelement~ContainerElement container elements} that are before and after given position.\n\t * Precisely, the element after the position is removed and it's contents are moved to element before the position.\n\t *\n\t *\t\t

foo

^

bar

->

foo^bar

\n\t *\t\t
foo
^

bar

->
foo^bar
\n\t *\n\t * **Note:** Difference between {@link module:engine/view/downcastwriter~DowncastWriter#mergeAttributes mergeAttributes} and\n\t * {@link module:engine/view/downcastwriter~DowncastWriter#mergeContainers mergeContainers} is that `mergeAttributes` merges two\n\t * {@link module:engine/view/attributeelement~AttributeElement attribute elements} or {@link module:engine/view/text~Text text nodes}\n\t * while `mergeContainer` merges two {@link module:engine/view/containerelement~ContainerElement container elements}.\n\t *\n\t * @see module:engine/view/attributeelement~AttributeElement\n\t * @see module:engine/view/containerelement~ContainerElement\n\t * @see module:engine/view/downcastwriter~DowncastWriter#mergeAttributes\n\t * @param {module:engine/view/position~Position} position Merge position.\n\t * @returns {module:engine/view/position~Position} Position after merge.\n\t */\n\tmergeContainers( position ) {\n\t\tconst prev = position.nodeBefore;\n\t\tconst next = position.nodeAfter;\n\n\t\tif ( !prev || !next || !prev.is( 'containerElement' ) || !next.is( 'containerElement' ) ) {\n\t\t\t/**\n\t\t\t * Element before and after given position cannot be merged.\n\t\t\t *\n\t\t\t * @error view-writer-merge-containers-invalid-position\n\t\t\t */\n\t\t\tthrow new CKEditorError( 'view-writer-merge-containers-invalid-position', this.document );\n\t\t}\n\n\t\tconst lastChild = prev.getChild( prev.childCount - 1 );\n\t\tconst newPosition = lastChild instanceof Text ? Position._createAt( lastChild, 'end' ) : Position._createAt( prev, 'end' );\n\n\t\tthis.move( Range._createIn( next ), Position._createAt( prev, 'end' ) );\n\t\tthis.remove( Range._createOn( next ) );\n\n\t\treturn newPosition;\n\t}\n\n\t/**\n\t * Inserts a node or nodes at specified position. Takes care about breaking attributes before insertion\n\t * and merging them afterwards.\n\t *\n\t * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-insert-invalid-node` when nodes to insert\n\t * contains instances that are not {@link module:engine/view/text~Text Texts},\n\t * {@link module:engine/view/attributeelement~AttributeElement AttributeElements},\n\t * {@link module:engine/view/containerelement~ContainerElement ContainerElements},\n\t * {@link module:engine/view/emptyelement~EmptyElement EmptyElements},\n\t * {@link module:engine/view/rawelement~RawElement RawElements} or\n\t * {@link module:engine/view/uielement~UIElement UIElements}.\n\t *\n\t * @param {module:engine/view/position~Position} position Insertion position.\n\t * @param {module:engine/view/text~Text|module:engine/view/attributeelement~AttributeElement|\n\t * module:engine/view/containerelement~ContainerElement|module:engine/view/emptyelement~EmptyElement|\n\t * module:engine/view/rawelement~RawElement|module:engine/view/uielement~UIElement|\n\t * Iterable.} nodes Node or nodes to insert.\n\t * @returns {module:engine/view/range~Range} Range around inserted nodes.\n\t */\n\tinsert( position, nodes ) {\n\t\tnodes = isIterable( nodes ) ? [ ...nodes ] : [ nodes ];\n\n\t\t// Check if nodes to insert are instances of AttributeElements, ContainerElements, EmptyElements, UIElements or Text.\n\t\tvalidateNodesToInsert( nodes, this.document );\n\n\t\t// Group nodes in batches of nodes that require or do not require breaking an AttributeElements.\n\t\tconst nodeGroups = nodes.reduce( ( groups, node ) => {\n\t\t\tconst lastGroup = groups[ groups.length - 1 ];\n\n\t\t\t// Break attributes on nodes that do exist in the model tree so they can have attributes, other elements\n\t\t\t// can't have an attribute in model and won't get wrapped with an AttributeElement while down-casted.\n\t\t\tconst breakAttributes = !( node.is( 'uiElement' ) && node.isAllowedInsideAttributeElement );\n\n\t\t\tif ( !lastGroup || lastGroup.breakAttributes != breakAttributes ) {\n\t\t\t\tgroups.push( {\n\t\t\t\t\tbreakAttributes,\n\t\t\t\t\tnodes: [ node ]\n\t\t\t\t} );\n\t\t\t} else {\n\t\t\t\tlastGroup.nodes.push( node );\n\t\t\t}\n\n\t\t\treturn groups;\n\t\t}, [] );\n\n\t\t// Insert nodes in batches.\n\t\tlet start = null;\n\t\tlet end = position;\n\n\t\tfor ( const { nodes, breakAttributes } of nodeGroups ) {\n\t\t\tconst range = this._insertNodes( end, nodes, breakAttributes );\n\n\t\t\tif ( !start ) {\n\t\t\t\tstart = range.start;\n\t\t\t}\n\n\t\t\tend = range.end;\n\t\t}\n\n\t\t// When no nodes were inserted - return collapsed range.\n\t\tif ( !start ) {\n\t\t\treturn new Range( position );\n\t\t}\n\n\t\treturn new Range( start, end );\n\t}\n\n\t/**\n\t * Removes provided range from the container.\n\t *\n\t * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-invalid-range-container` when\n\t * {@link module:engine/view/range~Range#start start} and {@link module:engine/view/range~Range#end end} positions are not placed inside\n\t * same parent container.\n\t *\n\t * @param {module:engine/view/range~Range|module:engine/view/item~Item} rangeOrItem Range to remove from container\n\t * or an {@link module:engine/view/item~Item item} to remove. If range is provided, after removing, it will be updated\n\t * to a collapsed range showing the new position.\n\t * @returns {module:engine/view/documentfragment~DocumentFragment} Document fragment containing removed nodes.\n\t */\n\tremove( rangeOrItem ) {\n\t\tconst range = rangeOrItem instanceof Range ? rangeOrItem : Range._createOn( rangeOrItem );\n\n\t\tvalidateRangeContainer( range, this.document );\n\n\t\t// If range is collapsed - nothing to remove.\n\t\tif ( range.isCollapsed ) {\n\t\t\treturn new DocumentFragment( this.document );\n\t\t}\n\n\t\t// Break attributes at range start and end.\n\t\tconst { start: breakStart, end: breakEnd } = this._breakAttributesRange( range, true );\n\t\tconst parentContainer = breakStart.parent;\n\n\t\tconst count = breakEnd.offset - breakStart.offset;\n\n\t\t// Remove nodes in range.\n\t\tconst removed = parentContainer._removeChildren( breakStart.offset, count );\n\n\t\tfor ( const node of removed ) {\n\t\t\tthis._removeFromClonedElementsGroup( node );\n\t\t}\n\n\t\t// Merge after removing.\n\t\tconst mergePosition = this.mergeAttributes( breakStart );\n\t\trange.start = mergePosition;\n\t\trange.end = mergePosition.clone();\n\n\t\t// Return removed nodes.\n\t\treturn new DocumentFragment( this.document, removed );\n\t}\n\n\t/**\n\t * Removes matching elements from given range.\n\t *\n\t * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-invalid-range-container` when\n\t * {@link module:engine/view/range~Range#start start} and {@link module:engine/view/range~Range#end end} positions are not placed inside\n\t * same parent container.\n\t *\n\t * @param {module:engine/view/range~Range} range Range to clear.\n\t * @param {module:engine/view/element~Element} element Element to remove.\n\t */\n\tclear( range, element ) {\n\t\tvalidateRangeContainer( range, this.document );\n\n\t\t// Create walker on given range.\n\t\t// We walk backward because when we remove element during walk it modifies range end position.\n\t\tconst walker = range.getWalker( {\n\t\t\tdirection: 'backward',\n\t\t\tignoreElementEnd: true\n\t\t} );\n\n\t\t// Let's walk.\n\t\tfor ( const current of walker ) {\n\t\t\tconst item = current.item;\n\t\t\tlet rangeToRemove;\n\n\t\t\t// When current item matches to the given element.\n\t\t\tif ( item.is( 'element' ) && element.isSimilar( item ) ) {\n\t\t\t\t// Create range on this element.\n\t\t\t\trangeToRemove = Range._createOn( item );\n\t\t\t\t// When range starts inside Text or TextProxy element.\n\t\t\t} else if ( !current.nextPosition.isAfter( range.start ) && item.is( '$textProxy' ) ) {\n\t\t\t\t// We need to check if parent of this text matches to given element.\n\t\t\t\tconst parentElement = item.getAncestors().find( ancestor => {\n\t\t\t\t\treturn ancestor.is( 'element' ) && element.isSimilar( ancestor );\n\t\t\t\t} );\n\n\t\t\t\t// If it is then create range inside this element.\n\t\t\t\tif ( parentElement ) {\n\t\t\t\t\trangeToRemove = Range._createIn( parentElement );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If we have found element to remove.\n\t\t\tif ( rangeToRemove ) {\n\t\t\t\t// We need to check if element range stick out of the given range and truncate if it is.\n\t\t\t\tif ( rangeToRemove.end.isAfter( range.end ) ) {\n\t\t\t\t\trangeToRemove.end = range.end;\n\t\t\t\t}\n\n\t\t\t\tif ( rangeToRemove.start.isBefore( range.start ) ) {\n\t\t\t\t\trangeToRemove.start = range.start;\n\t\t\t\t}\n\n\t\t\t\t// At the end we remove range with found element.\n\t\t\t\tthis.remove( rangeToRemove );\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Moves nodes from provided range to target position.\n\t *\n\t * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-invalid-range-container` when\n\t * {@link module:engine/view/range~Range#start start} and {@link module:engine/view/range~Range#end end} positions are not placed inside\n\t * same parent container.\n\t *\n\t * @param {module:engine/view/range~Range} sourceRange Range containing nodes to move.\n\t * @param {module:engine/view/position~Position} targetPosition Position to insert.\n\t * @returns {module:engine/view/range~Range} Range in target container. Inserted nodes are placed between\n\t * {@link module:engine/view/range~Range#start start} and {@link module:engine/view/range~Range#end end} positions.\n\t */\n\tmove( sourceRange, targetPosition ) {\n\t\tlet nodes;\n\n\t\tif ( targetPosition.isAfter( sourceRange.end ) ) {\n\t\t\ttargetPosition = this._breakAttributes( targetPosition, true );\n\n\t\t\tconst parent = targetPosition.parent;\n\t\t\tconst countBefore = parent.childCount;\n\n\t\t\tsourceRange = this._breakAttributesRange( sourceRange, true );\n\n\t\t\tnodes = this.remove( sourceRange );\n\n\t\t\ttargetPosition.offset += ( parent.childCount - countBefore );\n\t\t} else {\n\t\t\tnodes = this.remove( sourceRange );\n\t\t}\n\n\t\treturn this.insert( targetPosition, nodes );\n\t}\n\n\t/**\n\t * Wraps elements within range with provided {@link module:engine/view/attributeelement~AttributeElement AttributeElement}.\n\t * If a collapsed range is provided, it will be wrapped only if it is equal to view selection.\n\t *\n\t * If a collapsed range was passed and is same as selection, the selection\n\t * will be moved to the inside of the wrapped attribute element.\n\t *\n\t * Throws {@link module:utils/ckeditorerror~CKEditorError} `view-writer-invalid-range-container`\n\t * when {@link module:engine/view/range~Range#start}\n\t * and {@link module:engine/view/range~Range#end} positions are not placed inside same parent container.\n\t *\n\t * Throws {@link module:utils/ckeditorerror~CKEditorError} `view-writer-wrap-invalid-attribute` when passed attribute element is not\n\t * an instance of {@link module:engine/view/attributeelement~AttributeElement AttributeElement}.\n\t *\n\t * Throws {@link module:utils/ckeditorerror~CKEditorError} `view-writer-wrap-nonselection-collapsed-range` when passed range\n\t * is collapsed and different than view selection.\n\t *\n\t * **Note:** Attribute elements by default can wrap {@link module:engine/view/text~Text},\n\t * {@link module:engine/view/emptyelement~EmptyElement}, {@link module:engine/view/uielement~UIElement},\n\t * {@link module:engine/view/rawelement~RawElement} and other attribute elements with higher priority. Other elements while placed\n\t * inside an attribute element will split it (or nest it in case of an `AttributeElement`). This behavior can be modified by changing\n\t * the `isAllowedInsideAttributeElement` option while using\n\t * {@link module:engine/view/downcastwriter~DowncastWriter#createContainerElement},\n\t * {@link module:engine/view/downcastwriter~DowncastWriter#createEmptyElement},\n\t * {@link module:engine/view/downcastwriter~DowncastWriter#createUIElement} or\n\t * {@link module:engine/view/downcastwriter~DowncastWriter#createRawElement}.\n\t *\n\t * @param {module:engine/view/range~Range} range Range to wrap.\n\t * @param {module:engine/view/attributeelement~AttributeElement} attribute Attribute element to use as wrapper.\n\t * @returns {module:engine/view/range~Range} range Range after wrapping, spanning over wrapping attribute element.\n\t */\n\twrap( range, attribute ) {\n\t\tif ( !( attribute instanceof AttributeElement ) ) {\n\t\t\tthrow new CKEditorError(\n\t\t\t\t'view-writer-wrap-invalid-attribute',\n\t\t\t\tthis.document\n\t\t\t);\n\t\t}\n\n\t\tvalidateRangeContainer( range, this.document );\n\n\t\tif ( !range.isCollapsed ) {\n\t\t\t// Non-collapsed range. Wrap it with the attribute element.\n\t\t\treturn this._wrapRange( range, attribute );\n\t\t} else {\n\t\t\t// Collapsed range. Wrap position.\n\t\t\tlet position = range.start;\n\n\t\t\tif ( position.parent.is( 'element' ) && !_hasNonUiChildren( position.parent ) ) {\n\t\t\t\tposition = position.getLastMatchingPosition( value => value.item.is( 'uiElement' ) );\n\t\t\t}\n\n\t\t\tposition = this._wrapPosition( position, attribute );\n\t\t\tconst viewSelection = this.document.selection;\n\n\t\t\t// If wrapping position is equal to view selection, move view selection inside wrapping attribute element.\n\t\t\tif ( viewSelection.isCollapsed && viewSelection.getFirstPosition().isEqual( range.start ) ) {\n\t\t\t\tthis.setSelection( position );\n\t\t\t}\n\n\t\t\treturn new Range( position );\n\t\t}\n\t}\n\n\t/**\n\t * Unwraps nodes within provided range from attribute element.\n\t *\n\t * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-invalid-range-container` when\n\t * {@link module:engine/view/range~Range#start start} and {@link module:engine/view/range~Range#end end} positions are not placed inside\n\t * same parent container.\n\t *\n\t * @param {module:engine/view/range~Range} range\n\t * @param {module:engine/view/attributeelement~AttributeElement} attribute\n\t */\n\tunwrap( range, attribute ) {\n\t\tif ( !( attribute instanceof AttributeElement ) ) {\n\t\t\t/**\n\t\t\t * The `attribute` passed to {@link module:engine/view/downcastwriter~DowncastWriter#unwrap `DowncastWriter#unwrap()`}\n\t\t\t * must be an instance of {@link module:engine/view/attributeelement~AttributeElement `AttributeElement`}.\n\t\t\t *\n\t\t\t * @error view-writer-unwrap-invalid-attribute\n\t\t\t */\n\t\t\tthrow new CKEditorError(\n\t\t\t\t'view-writer-unwrap-invalid-attribute',\n\t\t\t\tthis.document\n\t\t\t);\n\t\t}\n\n\t\tvalidateRangeContainer( range, this.document );\n\n\t\t// If range is collapsed - nothing to unwrap.\n\t\tif ( range.isCollapsed ) {\n\t\t\treturn range;\n\t\t}\n\n\t\t// Break attributes at range start and end.\n\t\tconst { start: breakStart, end: breakEnd } = this._breakAttributesRange( range, true );\n\t\tconst parentContainer = breakStart.parent;\n\n\t\t// Unwrap children located between break points.\n\t\tconst newRange = this._unwrapChildren( parentContainer, breakStart.offset, breakEnd.offset, attribute );\n\n\t\t// Merge attributes at the both ends and return a new range.\n\t\tconst start = this.mergeAttributes( newRange.start );\n\n\t\t// If start position was merged - move end position back.\n\t\tif ( !start.isEqual( newRange.start ) ) {\n\t\t\tnewRange.end.offset--;\n\t\t}\n\n\t\tconst end = this.mergeAttributes( newRange.end );\n\n\t\treturn new Range( start, end );\n\t}\n\n\t/**\n\t * Renames element by creating a copy of renamed element but with changed name and then moving contents of the\n\t * old element to the new one. Keep in mind that this will invalidate all {@link module:engine/view/position~Position positions} which\n\t * has renamed element as {@link module:engine/view/position~Position#parent a parent}.\n\t *\n\t * New element has to be created because `Element#tagName` property in DOM is readonly.\n\t *\n\t * Since this function creates a new element and removes the given one, the new element is returned to keep reference.\n\t *\n\t * @param {String} newName New name for element.\n\t * @param {module:engine/view/containerelement~ContainerElement} viewElement Element to be renamed.\n\t * @returns {module:engine/view/containerelement~ContainerElement} Element created due to rename.\n\t */\n\trename( newName, viewElement ) {\n\t\tconst newElement = new ContainerElement( this.document, newName, viewElement.getAttributes() );\n\n\t\tthis.insert( Position._createAfter( viewElement ), newElement );\n\t\tthis.move( Range._createIn( viewElement ), Position._createAt( newElement, 0 ) );\n\t\tthis.remove( Range._createOn( viewElement ) );\n\n\t\treturn newElement;\n\t}\n\n\t/**\n\t * Cleans up memory by removing obsolete cloned elements group from the writer.\n\t *\n\t * Should be used whenever all {@link module:engine/view/attributeelement~AttributeElement attribute elements}\n\t * with the same {@link module:engine/view/attributeelement~AttributeElement#id id} are going to be removed from the view and\n\t * the group will no longer be needed.\n\t *\n\t * Cloned elements group are not removed automatically in case if the group is still needed after all its elements\n\t * were removed from the view.\n\t *\n\t * Keep in mind that group names are equal to the `id` property of the attribute element.\n\t *\n\t * @param {String} groupName Name of the group to clear.\n\t */\n\tclearClonedElementsGroup( groupName ) {\n\t\tthis._cloneGroups.delete( groupName );\n\t}\n\n\t/**\n\t * Creates position at the given location. The location can be specified as:\n\t *\n\t * * a {@link module:engine/view/position~Position position},\n\t * * parent element and offset (offset defaults to `0`),\n\t * * parent element and `'end'` (sets position at the end of that element),\n\t * * {@link module:engine/view/item~Item view item} and `'before'` or `'after'` (sets position before or after given view item).\n\t *\n\t * This method is a shortcut to other constructors such as:\n\t *\n\t * * {@link #createPositionBefore},\n\t * * {@link #createPositionAfter},\n\t *\n\t * @param {module:engine/view/item~Item|module:engine/model/position~Position} itemOrPosition\n\t * @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when\n\t * first parameter is a {@link module:engine/view/item~Item view item}.\n\t * @returns {module:engine/view/position~Position}\n\t */\n\tcreatePositionAt( itemOrPosition, offset ) {\n\t\treturn Position._createAt( itemOrPosition, offset );\n\t}\n\n\t/**\n\t * Creates a new position after given view item.\n\t *\n\t * @param {module:engine/view/item~Item} item View item after which the position should be located.\n\t * @returns {module:engine/view/position~Position}\n\t */\n\tcreatePositionAfter( item ) {\n\t\treturn Position._createAfter( item );\n\t}\n\n\t/**\n\t * Creates a new position before given view item.\n\t *\n\t * @param {module:engine/view/item~Item} item View item before which the position should be located.\n\t * @returns {module:engine/view/position~Position}\n\t */\n\tcreatePositionBefore( item ) {\n\t\treturn Position._createBefore( item );\n\t}\n\n\t/**\n\t * Creates a range spanning from `start` position to `end` position.\n\t *\n\t * **Note:** This factory method creates its own {@link module:engine/view/position~Position} instances basing on passed values.\n\t *\n\t * @param {module:engine/view/position~Position} start Start position.\n\t * @param {module:engine/view/position~Position} [end] End position. If not set, range will be collapsed at `start` position.\n\t * @returns {module:engine/view/range~Range}\n\t */\n\tcreateRange( start, end ) {\n\t\treturn new Range( start, end );\n\t}\n\n\t/**\n\t * Creates a range that starts before given {@link module:engine/view/item~Item view item} and ends after it.\n\t *\n\t * @param {module:engine/view/item~Item} item\n\t * @returns {module:engine/view/range~Range}\n\t */\n\tcreateRangeOn( item ) {\n\t\treturn Range._createOn( item );\n\t}\n\n\t/**\n\t * Creates a range inside an {@link module:engine/view/element~Element element} which starts before the first child of\n\t * that element and ends after the last child of that element.\n\t *\n\t * @param {module:engine/view/element~Element} element Element which is a parent for the range.\n\t * @returns {module:engine/view/range~Range}\n\t */\n\tcreateRangeIn( element ) {\n\t\treturn Range._createIn( element );\n\t}\n\n\t/**\n\t Creates new {@link module:engine/view/selection~Selection} instance.\n\t *\n\t * \t\t// Creates empty selection without ranges.\n\t *\t\tconst selection = writer.createSelection();\n\t *\n\t *\t\t// Creates selection at the given range.\n\t *\t\tconst range = writer.createRange( start, end );\n\t *\t\tconst selection = writer.createSelection( range );\n\t *\n\t *\t\t// Creates selection at the given ranges\n\t * \t\tconst ranges = [ writer.createRange( start1, end2 ), writer.createRange( star2, end2 ) ];\n\t *\t\tconst selection = writer.createSelection( ranges );\n\t *\n\t *\t\t// Creates selection from the other selection.\n\t *\t\tconst otherSelection = writer.createSelection();\n\t *\t\tconst selection = writer.createSelection( otherSelection );\n\t *\n\t *\t\t// Creates selection from the document selection.\n\t *\t\tconst selection = writer.createSelection( editor.editing.view.document.selection );\n\t *\n\t * \t\t// Creates selection at the given position.\n\t *\t\tconst position = writer.createPositionFromPath( root, path );\n\t *\t\tconst selection = writer.createSelection( position );\n\t *\n\t *\t\t// Creates collapsed selection at the position of given item and offset.\n\t *\t\tconst paragraph = writer.createContainerElement( 'p' );\n\t *\t\tconst selection = writer.createSelection( paragraph, offset );\n\t *\n\t *\t\t// Creates a range inside an {@link module:engine/view/element~Element element} which starts before the\n\t *\t\t// first child of that element and ends after the last child of that element.\n\t *\t\tconst selection = writer.createSelection( paragraph, 'in' );\n\t *\n\t *\t\t// Creates a range on an {@link module:engine/view/item~Item item} which starts before the item and ends\n\t *\t\t// just after the item.\n\t *\t\tconst selection = writer.createSelection( paragraph, 'on' );\n\t *\n\t * `Selection`'s constructor allow passing additional options (`backward`, `fake` and `label`) as the last argument.\n\t *\n\t *\t\t// Creates backward selection.\n\t *\t\tconst selection = writer.createSelection( range, { backward: true } );\n\t *\n\t * Fake selection does not render as browser native selection over selected elements and is hidden to the user.\n\t * This way, no native selection UI artifacts are displayed to the user and selection over elements can be\n\t * represented in other way, for example by applying proper CSS class.\n\t *\n\t * Additionally fake's selection label can be provided. It will be used to describe fake selection in DOM\n\t * (and be properly handled by screen readers).\n\t *\n\t *\t\t// Creates fake selection with label.\n\t *\t\tconst selection = writer.createSelection( range, { fake: true, label: 'foo' } );\n\t *\n\t * @param {module:engine/view/selection~Selectable} [selectable=null]\n\t * @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Offset or place when selectable is an `Item`.\n\t * @param {Object} [options]\n\t * @param {Boolean} [options.backward] Sets this selection instance to be backward.\n\t * @param {Boolean} [options.fake] Sets this selection instance to be marked as `fake`.\n\t * @param {String} [options.label] Label for the fake selection.\n\t * @returns {module:engine/view/selection~Selection}\n\t */\n\tcreateSelection( selectable, placeOrOffset, options ) {\n\t\treturn new Selection( selectable, placeOrOffset, options );\n\t}\n\n\t/**\n\t * Inserts a node or nodes at the specified position. Takes care of breaking attributes before insertion\n\t * and merging them afterwards if requested by the breakAttributes param.\n\t *\n\t * @private\n\t * @param {module:engine/view/position~Position} position Insertion position.\n\t * @param {module:engine/view/text~Text|module:engine/view/attributeelement~AttributeElement|\n\t * module:engine/view/containerelement~ContainerElement|module:engine/view/emptyelement~EmptyElement|\n\t * module:engine/view/rawelement~RawElement|module:engine/view/uielement~UIElement|\n\t * Iterable.} nodes Node or nodes to insert.\n\t * @param {Boolean} breakAttributes Whether attributes should be broken.\n\t * @returns {module:engine/view/range~Range} Range around inserted nodes.\n\t */\n\t_insertNodes( position, nodes, breakAttributes ) {\n\t\tlet parentElement;\n\n\t\t// Break attributes on nodes that do exist in the model tree so they can have attributes, other elements\n\t\t// can't have an attribute in model and won't get wrapped with an AttributeElement while down-casted.\n\t\tif ( breakAttributes ) {\n\t\t\tparentElement = getParentContainer( position );\n\t\t} else {\n\t\t\tparentElement = position.parent.is( '$text' ) ? position.parent.parent : position.parent;\n\t\t}\n\n\t\tif ( !parentElement ) {\n\t\t\t/**\n\t\t\t * Position's parent container cannot be found.\n\t\t\t *\n\t\t\t * @error view-writer-invalid-position-container\n\t\t\t */\n\t\t\tthrow new CKEditorError(\n\t\t\t\t'view-writer-invalid-position-container',\n\t\t\t\tthis.document\n\t\t\t);\n\t\t}\n\n\t\tlet insertionPosition;\n\n\t\tif ( breakAttributes ) {\n\t\t\tinsertionPosition = this._breakAttributes( position, true );\n\t\t} else {\n\t\t\tinsertionPosition = position.parent.is( '$text' ) ? breakTextNode( position ) : position;\n\t\t}\n\n\t\tconst length = parentElement._insertChild( insertionPosition.offset, nodes );\n\n\t\tfor ( const node of nodes ) {\n\t\t\tthis._addToClonedElementsGroup( node );\n\t\t}\n\n\t\tconst endPosition = insertionPosition.getShiftedBy( length );\n\t\tconst start = this.mergeAttributes( insertionPosition );\n\n\t\t// If start position was merged - move end position.\n\t\tif ( !start.isEqual( insertionPosition ) ) {\n\t\t\tendPosition.offset--;\n\t\t}\n\n\t\tconst end = this.mergeAttributes( endPosition );\n\n\t\treturn new Range( start, end );\n\t}\n\n\t/**\n\t * Wraps children with provided `wrapElement`. Only children contained in `parent` element between\n\t * `startOffset` and `endOffset` will be wrapped.\n\t *\n\t * @private\n\t * @param {module:engine/view/element~Element} parent\n\t * @param {Number} startOffset\n\t * @param {Number} endOffset\n\t * @param {module:engine/view/element~Element} wrapElement\n\t */\n\t_wrapChildren( parent, startOffset, endOffset, wrapElement ) {\n\t\tlet i = startOffset;\n\t\tconst wrapPositions = [];\n\n\t\twhile ( i < endOffset ) {\n\t\t\tconst child = parent.getChild( i );\n\t\t\tconst isText = child.is( '$text' );\n\t\t\tconst isAttribute = child.is( 'attributeElement' );\n\t\t\tconst isAllowedInsideAttributeElement = child.isAllowedInsideAttributeElement;\n\n\t\t\t//\n\t\t\t// (In all examples, assume that `wrapElement` is `` element.)\n\t\t\t//\n\t\t\t// Check if `wrapElement` can be joined with the wrapped element. One of requirements is having same name.\n\t\t\t// If possible, join elements.\n\t\t\t//\n\t\t\t//

abc

-->

abc

\n\t\t\t//\n\t\t\tif ( isAttribute && this._wrapAttributeElement( wrapElement, child ) ) {\n\t\t\t\twrapPositions.push( new Position( parent, i ) );\n\t\t\t}\n\t\t\t//\n\t\t\t// Wrap the child if it is not an attribute element or if it is an attribute element that should be inside\n\t\t\t// `wrapElement` (due to priority).\n\t\t\t//\n\t\t\t//

abc

-->

abc

\n\t\t\t//

abc

-->

abc

\n\t\t\telse if ( isText || isAllowedInsideAttributeElement || ( isAttribute && shouldABeOutsideB( wrapElement, child ) ) ) {\n\t\t\t\t// Clone attribute.\n\t\t\t\tconst newAttribute = wrapElement._clone();\n\n\t\t\t\t// Wrap current node with new attribute.\n\t\t\t\tchild._remove();\n\t\t\t\tnewAttribute._appendChild( child );\n\n\t\t\t\tparent._insertChild( i, newAttribute );\n\t\t\t\tthis._addToClonedElementsGroup( newAttribute );\n\n\t\t\t\twrapPositions.push( new Position( parent, i ) );\n\t\t\t}\n\t\t\t//\n\t\t\t// If other nested attribute is found and it wasn't wrapped (see above), continue wrapping inside it.\n\t\t\t//\n\t\t\t//
-->

abc

\n\t\t\t//\n\t\t\telse if ( isAttribute ) {\n\t\t\t\tthis._wrapChildren( child, 0, child.childCount, wrapElement );\n\t\t\t}\n\n\t\t\ti++;\n\t\t}\n\n\t\t// Merge at each wrap.\n\t\tlet offsetChange = 0;\n\n\t\tfor ( const position of wrapPositions ) {\n\t\t\tposition.offset -= offsetChange;\n\n\t\t\t// Do not merge with elements outside selected children.\n\t\t\tif ( position.offset == startOffset ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst newPosition = this.mergeAttributes( position );\n\n\t\t\t// If nodes were merged - other merge offsets will change.\n\t\t\tif ( !newPosition.isEqual( position ) ) {\n\t\t\t\toffsetChange++;\n\t\t\t\tendOffset--;\n\t\t\t}\n\t\t}\n\n\t\treturn Range._createFromParentsAndOffsets( parent, startOffset, parent, endOffset );\n\t}\n\n\t/**\n\t * Unwraps children from provided `unwrapElement`. Only children contained in `parent` element between\n\t * `startOffset` and `endOffset` will be unwrapped.\n\t *\n\t * @private\n\t * @param {module:engine/view/element~Element} parent\n\t * @param {Number} startOffset\n\t * @param {Number} endOffset\n\t * @param {module:engine/view/element~Element} unwrapElement\n\t */\n\t_unwrapChildren( parent, startOffset, endOffset, unwrapElement ) {\n\t\tlet i = startOffset;\n\t\tconst unwrapPositions = [];\n\n\t\t// Iterate over each element between provided offsets inside parent.\n\t\t// We don't use tree walker or range iterator because we will be removing and merging potentially multiple nodes,\n\t\t// so it could get messy. It is safer to it manually in this case.\n\t\twhile ( i < endOffset ) {\n\t\t\tconst child = parent.getChild( i );\n\n\t\t\t// Skip all text nodes. There should be no container element's here either.\n\t\t\tif ( !child.is( 'attributeElement' ) ) {\n\t\t\t\ti++;\n\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t//\n\t\t\t// (In all examples, assume that `unwrapElement` is `` element.)\n\t\t\t//\n\t\t\t// If the child is similar to the given attribute element, unwrap it - it will be completely removed.\n\t\t\t//\n\t\t\t//

abcxyz

-->

abcxyz

\n\t\t\t//\n\t\t\tif ( child.isSimilar( unwrapElement ) ) {\n\t\t\t\tconst unwrapped = child.getChildren();\n\t\t\t\tconst count = child.childCount;\n\n\t\t\t\t// Replace wrapper element with its children\n\t\t\t\tchild._remove();\n\t\t\t\tparent._insertChild( i, unwrapped );\n\n\t\t\t\tthis._removeFromClonedElementsGroup( child );\n\n\t\t\t\t// Save start and end position of moved items.\n\t\t\t\tunwrapPositions.push(\n\t\t\t\t\tnew Position( parent, i ),\n\t\t\t\t\tnew Position( parent, i + count )\n\t\t\t\t);\n\n\t\t\t\t// Skip elements that were unwrapped. Assuming there won't be another element to unwrap in child elements.\n\t\t\t\ti += count;\n\t\t\t\tendOffset += count - 1;\n\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t//\n\t\t\t// If the child is not similar but is an attribute element, try partial unwrapping - remove the same attributes/styles/classes.\n\t\t\t// Partial unwrapping will happen only if the elements have the same name.\n\t\t\t//\n\t\t\t//

abcxyz

-->

abcxyz

\n\t\t\t//

abcxyz

-->

abcxyz

\n\t\t\t//\n\t\t\tif ( this._unwrapAttributeElement( unwrapElement, child ) ) {\n\t\t\t\tunwrapPositions.push(\n\t\t\t\t\tnew Position( parent, i ),\n\t\t\t\t\tnew Position( parent, i + 1 )\n\t\t\t\t);\n\n\t\t\t\ti++;\n\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t//\n\t\t\t// If other nested attribute is found, look through it's children for elements to unwrap.\n\t\t\t//\n\t\t\t//

abc

-->

abc

\n\t\t\t//\n\t\t\tthis._unwrapChildren( child, 0, child.childCount, unwrapElement );\n\n\t\t\ti++;\n\t\t}\n\n\t\t// Merge at each unwrap.\n\t\tlet offsetChange = 0;\n\n\t\tfor ( const position of unwrapPositions ) {\n\t\t\tposition.offset -= offsetChange;\n\n\t\t\t// Do not merge with elements outside selected children.\n\t\t\tif ( position.offset == startOffset || position.offset == endOffset ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst newPosition = this.mergeAttributes( position );\n\n\t\t\t// If nodes were merged - other merge offsets will change.\n\t\t\tif ( !newPosition.isEqual( position ) ) {\n\t\t\t\toffsetChange++;\n\t\t\t\tendOffset--;\n\t\t\t}\n\t\t}\n\n\t\treturn Range._createFromParentsAndOffsets( parent, startOffset, parent, endOffset );\n\t}\n\n\t/**\n\t * Helper function for `view.writer.wrap`. Wraps range with provided attribute element.\n\t * This method will also merge newly added attribute element with its siblings whenever possible.\n\t *\n\t * Throws {@link module:utils/ckeditorerror~CKEditorError} `view-writer-wrap-invalid-attribute` when passed attribute element is not\n\t * an instance of {@link module:engine/view/attributeelement~AttributeElement AttributeElement}.\n\t *\n\t * @private\n\t * @param {module:engine/view/range~Range} range\n\t * @param {module:engine/view/attributeelement~AttributeElement} attribute\n\t * @returns {module:engine/view/range~Range} New range after wrapping, spanning over wrapping attribute element.\n\t */\n\t_wrapRange( range, attribute ) {\n\t\t// Break attributes at range start and end.\n\t\tconst { start: breakStart, end: breakEnd } = this._breakAttributesRange( range, true );\n\t\tconst parentContainer = breakStart.parent;\n\n\t\t// Wrap all children with attribute.\n\t\tconst newRange = this._wrapChildren( parentContainer, breakStart.offset, breakEnd.offset, attribute );\n\n\t\t// Merge attributes at the both ends and return a new range.\n\t\tconst start = this.mergeAttributes( newRange.start );\n\n\t\t// If start position was merged - move end position back.\n\t\tif ( !start.isEqual( newRange.start ) ) {\n\t\t\tnewRange.end.offset--;\n\t\t}\n\t\tconst end = this.mergeAttributes( newRange.end );\n\n\t\treturn new Range( start, end );\n\t}\n\n\t/**\n\t * Helper function for {@link #wrap}. Wraps position with provided attribute element.\n\t * This method will also merge newly added attribute element with its siblings whenever possible.\n\t *\n\t * Throws {@link module:utils/ckeditorerror~CKEditorError} `view-writer-wrap-invalid-attribute` when passed attribute element is not\n\t * an instance of {@link module:engine/view/attributeelement~AttributeElement AttributeElement}.\n\t *\n\t * @private\n\t * @param {module:engine/view/position~Position} position\n\t * @param {module:engine/view/attributeelement~AttributeElement} attribute\n\t * @returns {module:engine/view/position~Position} New position after wrapping.\n\t */\n\t_wrapPosition( position, attribute ) {\n\t\t// Return same position when trying to wrap with attribute similar to position parent.\n\t\tif ( attribute.isSimilar( position.parent ) ) {\n\t\t\treturn movePositionToTextNode( position.clone() );\n\t\t}\n\n\t\t// When position is inside text node - break it and place new position between two text nodes.\n\t\tif ( position.parent.is( '$text' ) ) {\n\t\t\tposition = breakTextNode( position );\n\t\t}\n\n\t\t// Create fake element that will represent position, and will not be merged with other attributes.\n\t\tconst fakePosition = this.createAttributeElement();\n\t\tfakePosition._priority = Number.POSITIVE_INFINITY;\n\t\tfakePosition.isSimilar = () => false;\n\n\t\t// Insert fake element in position location.\n\t\tposition.parent._insertChild( position.offset, fakePosition );\n\n\t\t// Range around inserted fake attribute element.\n\t\tconst wrapRange = new Range( position, position.getShiftedBy( 1 ) );\n\n\t\t// Wrap fake element with attribute (it will also merge if possible).\n\t\tthis.wrap( wrapRange, attribute );\n\n\t\t// Remove fake element and place new position there.\n\t\tconst newPosition = new Position( fakePosition.parent, fakePosition.index );\n\t\tfakePosition._remove();\n\n\t\t// If position is placed between text nodes - merge them and return position inside.\n\t\tconst nodeBefore = newPosition.nodeBefore;\n\t\tconst nodeAfter = newPosition.nodeAfter;\n\n\t\tif ( nodeBefore instanceof Text && nodeAfter instanceof Text ) {\n\t\t\treturn mergeTextNodes( nodeBefore, nodeAfter );\n\t\t}\n\n\t\t// If position is next to text node - move position inside.\n\t\treturn movePositionToTextNode( newPosition );\n\t}\n\n\t/**\n\t * \tWraps one {@link module:engine/view/attributeelement~AttributeElement AttributeElement} into another by\n\t * \tmerging them if possible. When merging is possible - all attributes, styles and classes are moved from wrapper\n\t * \telement to element being wrapped.\n\t *\n\t * \t@private\n\t * \t@param {module:engine/view/attributeelement~AttributeElement} wrapper Wrapper AttributeElement.\n\t * \t@param {module:engine/view/attributeelement~AttributeElement} toWrap AttributeElement to wrap using wrapper element.\n\t * \t@returns {Boolean} Returns `true` if elements are merged.\n\t */\n\t_wrapAttributeElement( wrapper, toWrap ) {\n\t\tif ( !canBeJoined( wrapper, toWrap ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Can't merge if name or priority differs.\n\t\tif ( wrapper.name !== toWrap.name || wrapper.priority !== toWrap.priority ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check if attributes can be merged.\n\t\tfor ( const key of wrapper.getAttributeKeys() ) {\n\t\t\t// Classes and styles should be checked separately.\n\t\t\tif ( key === 'class' || key === 'style' ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// If some attributes are different we cannot wrap.\n\t\t\tif ( toWrap.hasAttribute( key ) && toWrap.getAttribute( key ) !== wrapper.getAttribute( key ) ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t// Check if styles can be merged.\n\t\tfor ( const key of wrapper.getStyleNames() ) {\n\t\t\tif ( toWrap.hasStyle( key ) && toWrap.getStyle( key ) !== wrapper.getStyle( key ) ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t// Move all attributes/classes/styles from wrapper to wrapped AttributeElement.\n\t\tfor ( const key of wrapper.getAttributeKeys() ) {\n\t\t\t// Classes and styles should be checked separately.\n\t\t\tif ( key === 'class' || key === 'style' ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Move only these attributes that are not present - other are similar.\n\t\t\tif ( !toWrap.hasAttribute( key ) ) {\n\t\t\t\tthis.setAttribute( key, wrapper.getAttribute( key ), toWrap );\n\t\t\t}\n\t\t}\n\n\t\tfor ( const key of wrapper.getStyleNames() ) {\n\t\t\tif ( !toWrap.hasStyle( key ) ) {\n\t\t\t\tthis.setStyle( key, wrapper.getStyle( key ), toWrap );\n\t\t\t}\n\t\t}\n\n\t\tfor ( const key of wrapper.getClassNames() ) {\n\t\t\tif ( !toWrap.hasClass( key ) ) {\n\t\t\t\tthis.addClass( key, toWrap );\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Unwraps {@link module:engine/view/attributeelement~AttributeElement AttributeElement} from another by removing\n\t * corresponding attributes, classes and styles. All attributes, classes and styles from wrapper should be present\n\t * inside element being unwrapped.\n\t *\n\t * @private\n\t * @param {module:engine/view/attributeelement~AttributeElement} wrapper Wrapper AttributeElement.\n\t * @param {module:engine/view/attributeelement~AttributeElement} toUnwrap AttributeElement to unwrap using wrapper element.\n\t * @returns {Boolean} Returns `true` if elements are unwrapped.\n\t **/\n\t_unwrapAttributeElement( wrapper, toUnwrap ) {\n\t\tif ( !canBeJoined( wrapper, toUnwrap ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Can't unwrap if name or priority differs.\n\t\tif ( wrapper.name !== toUnwrap.name || wrapper.priority !== toUnwrap.priority ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check if AttributeElement has all wrapper attributes.\n\t\tfor ( const key of wrapper.getAttributeKeys() ) {\n\t\t\t// Classes and styles should be checked separately.\n\t\t\tif ( key === 'class' || key === 'style' ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// If some attributes are missing or different we cannot unwrap.\n\t\t\tif ( !toUnwrap.hasAttribute( key ) || toUnwrap.getAttribute( key ) !== wrapper.getAttribute( key ) ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t// Check if AttributeElement has all wrapper classes.\n\t\tif ( !toUnwrap.hasClass( ...wrapper.getClassNames() ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check if AttributeElement has all wrapper styles.\n\t\tfor ( const key of wrapper.getStyleNames() ) {\n\t\t\t// If some styles are missing or different we cannot unwrap.\n\t\t\tif ( !toUnwrap.hasStyle( key ) || toUnwrap.getStyle( key ) !== wrapper.getStyle( key ) ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t// Remove all wrapper's attributes from unwrapped element.\n\t\tfor ( const key of wrapper.getAttributeKeys() ) {\n\t\t\t// Classes and styles should be checked separately.\n\t\t\tif ( key === 'class' || key === 'style' ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tthis.removeAttribute( key, toUnwrap );\n\t\t}\n\n\t\t// Remove all wrapper's classes from unwrapped element.\n\t\tthis.removeClass( Array.from( wrapper.getClassNames() ), toUnwrap );\n\n\t\t// Remove all wrapper's styles from unwrapped element.\n\t\tthis.removeStyle( Array.from( wrapper.getStyleNames() ), toUnwrap );\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Helper function used by other `DowncastWriter` methods. Breaks attribute elements at the boundaries of given range.\n\t *\n\t * @private\n\t * @param {module:engine/view/range~Range} range Range which `start` and `end` positions will be used to break attributes.\n\t * @param {Boolean} [forceSplitText=false] If set to `true`, will break text nodes even if they are directly in container element.\n\t * This behavior will result in incorrect view state, but is needed by other view writing methods which then fixes view state.\n\t * @returns {module:engine/view/range~Range} New range with located at break positions.\n\t */\n\t_breakAttributesRange( range, forceSplitText = false ) {\n\t\tconst rangeStart = range.start;\n\t\tconst rangeEnd = range.end;\n\n\t\tvalidateRangeContainer( range, this.document );\n\n\t\t// Break at the collapsed position. Return new collapsed range.\n\t\tif ( range.isCollapsed ) {\n\t\t\tconst position = this._breakAttributes( range.start, forceSplitText );\n\n\t\t\treturn new Range( position, position );\n\t\t}\n\n\t\tconst breakEnd = this._breakAttributes( rangeEnd, forceSplitText );\n\t\tconst count = breakEnd.parent.childCount;\n\t\tconst breakStart = this._breakAttributes( rangeStart, forceSplitText );\n\n\t\t// Calculate new break end offset.\n\t\tbreakEnd.offset += breakEnd.parent.childCount - count;\n\n\t\treturn new Range( breakStart, breakEnd );\n\t}\n\n\t/**\n\t * Helper function used by other `DowncastWriter` methods. Breaks attribute elements at given position.\n\t *\n\t * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-cannot-break-empty-element` when break position\n\t * is placed inside {@link module:engine/view/emptyelement~EmptyElement EmptyElement}.\n\t *\n\t * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-cannot-break-ui-element` when break position\n\t * is placed inside {@link module:engine/view/uielement~UIElement UIElement}.\n\t *\n\t * @private\n\t * @param {module:engine/view/position~Position} position Position where to break attributes.\n\t * @param {Boolean} [forceSplitText=false] If set to `true`, will break text nodes even if they are directly in container element.\n\t * This behavior will result in incorrect view state, but is needed by other view writing methods which then fixes view state.\n\t * @returns {module:engine/view/position~Position} New position after breaking the attributes.\n\t */\n\t_breakAttributes( position, forceSplitText = false ) {\n\t\tconst positionOffset = position.offset;\n\t\tconst positionParent = position.parent;\n\n\t\t// If position is placed inside EmptyElement - throw an exception as we cannot break inside.\n\t\tif ( position.parent.is( 'emptyElement' ) ) {\n\t\t\t/**\n\t\t\t * Cannot break an `EmptyElement` instance.\n\t\t\t *\n\t\t\t * This error is thrown if\n\t\t\t * {@link module:engine/view/downcastwriter~DowncastWriter#breakAttributes `DowncastWriter#breakAttributes()`}\n\t\t\t * was executed in an incorrect position.\n\t\t\t *\n\t\t\t * @error view-writer-cannot-break-empty-element\n\t\t\t */\n\t\t\tthrow new CKEditorError( 'view-writer-cannot-break-empty-element', this.document );\n\t\t}\n\n\t\t// If position is placed inside UIElement - throw an exception as we cannot break inside.\n\t\tif ( position.parent.is( 'uiElement' ) ) {\n\t\t\t/**\n\t\t\t * Cannot break a `UIElement` instance.\n\t\t\t *\n\t\t\t * This error is thrown if\n\t\t\t * {@link module:engine/view/downcastwriter~DowncastWriter#breakAttributes `DowncastWriter#breakAttributes()`}\n\t\t\t * was executed in an incorrect position.\n\t\t\t *\n\t\t\t * @error view-writer-cannot-break-ui-element\n\t\t\t */\n\t\t\tthrow new CKEditorError( 'view-writer-cannot-break-ui-element', this.document );\n\t\t}\n\n\t\t// If position is placed inside RawElement - throw an exception as we cannot break inside.\n\t\tif ( position.parent.is( 'rawElement' ) ) {\n\t\t\t/**\n\t\t\t * Cannot break a `RawElement` instance.\n\t\t\t *\n\t\t\t * This error is thrown if\n\t\t\t * {@link module:engine/view/downcastwriter~DowncastWriter#breakAttributes `DowncastWriter#breakAttributes()`}\n\t\t\t * was executed in an incorrect position.\n\t\t\t *\n\t\t\t * @error view-writer-cannot-break-raw-element\n\t\t\t */\n\t\t\tthrow new CKEditorError( 'view-writer-cannot-break-raw-element', this.document );\n\t\t}\n\n\t\t// There are no attributes to break and text nodes breaking is not forced.\n\t\tif ( !forceSplitText && positionParent.is( '$text' ) && isContainerOrFragment( positionParent.parent ) ) {\n\t\t\treturn position.clone();\n\t\t}\n\n\t\t// Position's parent is container, so no attributes to break.\n\t\tif ( isContainerOrFragment( positionParent ) ) {\n\t\t\treturn position.clone();\n\t\t}\n\n\t\t// Break text and start again in new position.\n\t\tif ( positionParent.is( '$text' ) ) {\n\t\t\treturn this._breakAttributes( breakTextNode( position ), forceSplitText );\n\t\t}\n\n\t\tconst length = positionParent.childCount;\n\n\t\t//

foobar{}

\n\t\t//

foobar[]

\n\t\t//

foobar[]

\n\t\tif ( positionOffset == length ) {\n\t\t\tconst newPosition = new Position( positionParent.parent, positionParent.index + 1 );\n\n\t\t\treturn this._breakAttributes( newPosition, forceSplitText );\n\t\t} else {\n\t\t\t//

foo{}bar

\n\t\t\t//

foo[]bar

\n\t\t\t//

foo{}bar

\n\t\t\tif ( positionOffset === 0 ) {\n\t\t\t\tconst newPosition = new Position( positionParent.parent, positionParent.index );\n\n\t\t\t\treturn this._breakAttributes( newPosition, forceSplitText );\n\t\t\t}\n\t\t\t//

foob{}ar

\n\t\t\t//

foob[]ar

\n\t\t\t//

foob[]ar

\n\t\t\t//

foob[]ar

\n\t\t\telse {\n\t\t\t\tconst offsetAfter = positionParent.index + 1;\n\n\t\t\t\t// Break element.\n\t\t\t\tconst clonedNode = positionParent._clone();\n\n\t\t\t\t// Insert cloned node to position's parent node.\n\t\t\t\tpositionParent.parent._insertChild( offsetAfter, clonedNode );\n\t\t\t\tthis._addToClonedElementsGroup( clonedNode );\n\n\t\t\t\t// Get nodes to move.\n\t\t\t\tconst count = positionParent.childCount - positionOffset;\n\t\t\t\tconst nodesToMove = positionParent._removeChildren( positionOffset, count );\n\n\t\t\t\t// Move nodes to cloned node.\n\t\t\t\tclonedNode._appendChild( nodesToMove );\n\n\t\t\t\t// Create new position to work on.\n\t\t\t\tconst newPosition = new Position( positionParent.parent, offsetAfter );\n\n\t\t\t\treturn this._breakAttributes( newPosition, forceSplitText );\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Stores the information that an {@link module:engine/view/attributeelement~AttributeElement attribute element} was\n\t * added to the tree. Saves the reference to the group in the given element and updates the group, so other elements\n\t * from the group now keep a reference to the given attribute element.\n\t *\n\t * The clones group can be obtained using {@link module:engine/view/attributeelement~AttributeElement#getElementsWithSameId}.\n\t *\n\t * Does nothing if added element has no {@link module:engine/view/attributeelement~AttributeElement#id id}.\n\t *\n\t * @private\n\t * @param {module:engine/view/attributeelement~AttributeElement} element Attribute element to save.\n\t */\n\t_addToClonedElementsGroup( element ) {\n\t\t// Add only if the element is in document tree.\n\t\tif ( !element.root.is( 'rootElement' ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Traverse the element's children recursively to find other attribute elements that also might got inserted.\n\t\t// The loop is at the beginning so we can make fast returns later in the code.\n\t\tif ( element.is( 'element' ) ) {\n\t\t\tfor ( const child of element.getChildren() ) {\n\t\t\t\tthis._addToClonedElementsGroup( child );\n\t\t\t}\n\t\t}\n\n\t\tconst id = element.id;\n\n\t\tif ( !id ) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet group = this._cloneGroups.get( id );\n\n\t\tif ( !group ) {\n\t\t\tgroup = new Set();\n\t\t\tthis._cloneGroups.set( id, group );\n\t\t}\n\n\t\tgroup.add( element );\n\t\telement._clonesGroup = group;\n\t}\n\n\t/**\n\t * Removes all the information about the given {@link module:engine/view/attributeelement~AttributeElement attribute element}\n\t * from its clones group.\n\t *\n\t * Keep in mind, that the element will still keep a reference to the group (but the group will not keep a reference to it).\n\t * This allows to reference the whole group even if the element was already removed from the tree.\n\t *\n\t * Does nothing if the element has no {@link module:engine/view/attributeelement~AttributeElement#id id}.\n\t *\n\t * @private\n\t * @param {module:engine/view/attributeelement~AttributeElement} element Attribute element to remove.\n\t */\n\t_removeFromClonedElementsGroup( element ) {\n\t\t// Traverse the element's children recursively to find other attribute elements that also got removed.\n\t\t// The loop is at the beginning so we can make fast returns later in the code.\n\t\tif ( element.is( 'element' ) ) {\n\t\t\tfor ( const child of element.getChildren() ) {\n\t\t\t\tthis._removeFromClonedElementsGroup( child );\n\t\t\t}\n\t\t}\n\n\t\tconst id = element.id;\n\n\t\tif ( !id ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst group = this._cloneGroups.get( id );\n\n\t\tif ( !group ) {\n\t\t\treturn;\n\t\t}\n\n\t\tgroup.delete( element );\n\t\t// Not removing group from element on purpose!\n\t\t// If other parts of code have reference to this element, they will be able to get references to other elements from the group.\n\t}\n}\n\n// Helper function for `view.writer.wrap`. Checks if given element has any children that are not ui elements.\nfunction _hasNonUiChildren( parent ) {\n\treturn Array.from( parent.getChildren() ).some( child => !child.is( 'uiElement' ) );\n}\n\n/**\n * The `attribute` passed to {@link module:engine/view/downcastwriter~DowncastWriter#wrap `DowncastWriter#wrap()`}\n * must be an instance of {@link module:engine/view/attributeelement~AttributeElement `AttributeElement`}.\n *\n * @error view-writer-wrap-invalid-attribute\n */\n\n// Returns first parent container of specified {@link module:engine/view/position~Position Position}.\n// Position's parent node is checked as first, then next parents are checked.\n// Note that {@link module:engine/view/documentfragment~DocumentFragment DocumentFragment} is treated like a container.\n//\n// @param {module:engine/view/position~Position} position Position used as a start point to locate parent container.\n// @returns {module:engine/view/containerelement~ContainerElement|module:engine/view/documentfragment~DocumentFragment|undefined}\n// Parent container element or `undefined` if container is not found.\nfunction getParentContainer( position ) {\n\tlet parent = position.parent;\n\n\twhile ( !isContainerOrFragment( parent ) ) {\n\t\tif ( !parent ) {\n\t\t\treturn undefined;\n\t\t}\n\t\tparent = parent.parent;\n\t}\n\n\treturn parent;\n}\n\n// Checks if first {@link module:engine/view/attributeelement~AttributeElement AttributeElement} provided to the function\n// can be wrapped outside second element. It is done by comparing elements'\n// {@link module:engine/view/attributeelement~AttributeElement#priority priorities}, if both have same priority\n// {@link module:engine/view/element~Element#getIdentity identities} are compared.\n//\n// @param {module:engine/view/attributeelement~AttributeElement} a\n// @param {module:engine/view/attributeelement~AttributeElement} b\n// @returns {Boolean}\nfunction shouldABeOutsideB( a, b ) {\n\tif ( a.priority < b.priority ) {\n\t\treturn true;\n\t} else if ( a.priority > b.priority ) {\n\t\treturn false;\n\t}\n\n\t// When priorities are equal and names are different - use identities.\n\treturn a.getIdentity() < b.getIdentity();\n}\n\n// Returns new position that is moved to near text node. Returns same position if there is no text node before of after\n// specified position.\n//\n//\t\t

foo[]

->

foo{}

\n//\t\t

[]foo

->

{}foo

\n//\n// @param {module:engine/view/position~Position} position\n// @returns {module:engine/view/position~Position} Position located inside text node or same position if there is no text nodes\n// before or after position location.\nfunction movePositionToTextNode( position ) {\n\tconst nodeBefore = position.nodeBefore;\n\n\tif ( nodeBefore && nodeBefore.is( '$text' ) ) {\n\t\treturn new Position( nodeBefore, nodeBefore.data.length );\n\t}\n\n\tconst nodeAfter = position.nodeAfter;\n\n\tif ( nodeAfter && nodeAfter.is( '$text' ) ) {\n\t\treturn new Position( nodeAfter, 0 );\n\t}\n\n\treturn position;\n}\n\n// Breaks text node into two text nodes when possible.\n//\n//\t\t

foo{}bar

->

foo[]bar

\n//\t\t

{}foobar

->

[]foobar

\n//\t\t

foobar{}

->

foobar[]

\n//\n// @param {module:engine/view/position~Position} position Position that need to be placed inside text node.\n// @returns {module:engine/view/position~Position} New position after breaking text node.\nfunction breakTextNode( position ) {\n\tif ( position.offset == position.parent.data.length ) {\n\t\treturn new Position( position.parent.parent, position.parent.index + 1 );\n\t}\n\n\tif ( position.offset === 0 ) {\n\t\treturn new Position( position.parent.parent, position.parent.index );\n\t}\n\n\t// Get part of the text that need to be moved.\n\tconst textToMove = position.parent.data.slice( position.offset );\n\n\t// Leave rest of the text in position's parent.\n\tposition.parent._data = position.parent.data.slice( 0, position.offset );\n\n\t// Insert new text node after position's parent text node.\n\tposition.parent.parent._insertChild( position.parent.index + 1, new Text( position.root.document, textToMove ) );\n\n\t// Return new position between two newly created text nodes.\n\treturn new Position( position.parent.parent, position.parent.index + 1 );\n}\n\n// Merges two text nodes into first node. Removes second node and returns merge position.\n//\n// @param {module:engine/view/text~Text} t1 First text node to merge. Data from second text node will be moved at the end of\n// this text node.\n// @param {module:engine/view/text~Text} t2 Second text node to merge. This node will be removed after merging.\n// @returns {module:engine/view/position~Position} Position after merging text nodes.\nfunction mergeTextNodes( t1, t2 ) {\n\t// Merge text data into first text node and remove second one.\n\tconst nodeBeforeLength = t1.data.length;\n\tt1._data += t2.data;\n\tt2._remove();\n\n\treturn new Position( t1, nodeBeforeLength );\n}\n\n// Checks if provided nodes are valid to insert.\n//\n// Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-insert-invalid-node` when nodes to insert\n// contains instances that are not supported ones (see error description for valid ones.\n//\n// @param Iterable. nodes\n// @param {Object} errorContext\nfunction validateNodesToInsert( nodes, errorContext ) {\n\tfor ( const node of nodes ) {\n\t\tif ( !validNodesToInsert.some( ( validNode => node instanceof validNode ) ) ) { // eslint-disable-line no-use-before-define\n\t\t\t/**\n\t\t\t * One of the nodes to be inserted is of an invalid type.\n\t\t\t *\n\t\t\t * Nodes to be inserted with {@link module:engine/view/downcastwriter~DowncastWriter#insert `DowncastWriter#insert()`} should be\n\t\t\t * of the following types:\n\t\t\t *\n\t\t\t * * {@link module:engine/view/attributeelement~AttributeElement AttributeElement},\n\t\t\t * * {@link module:engine/view/containerelement~ContainerElement ContainerElement},\n\t\t\t * * {@link module:engine/view/emptyelement~EmptyElement EmptyElement},\n\t\t\t * * {@link module:engine/view/uielement~UIElement UIElement},\n\t\t\t * * {@link module:engine/view/rawelement~RawElement RawElement},\n\t\t\t * * {@link module:engine/view/text~Text Text}.\n\t\t\t *\n\t\t\t * @error view-writer-insert-invalid-node-type\n\t\t\t */\n\t\t\tthrow new CKEditorError( 'view-writer-insert-invalid-node-type', errorContext );\n\t\t}\n\n\t\tif ( !node.is( '$text' ) ) {\n\t\t\tvalidateNodesToInsert( node.getChildren(), errorContext );\n\t\t}\n\t}\n}\n\nconst validNodesToInsert = [ Text, AttributeElement, ContainerElement, EmptyElement, RawElement, UIElement ];\n\n// Checks if node is ContainerElement or DocumentFragment, because in most cases they should be treated the same way.\n//\n// @param {module:engine/view/node~Node} node\n// @returns {Boolean} Returns `true` if node is instance of ContainerElement or DocumentFragment.\nfunction isContainerOrFragment( node ) {\n\treturn node && ( node.is( 'containerElement' ) || node.is( 'documentFragment' ) );\n}\n\n// Checks if {@link module:engine/view/range~Range#start range start} and {@link module:engine/view/range~Range#end range end} are placed\n// inside same {@link module:engine/view/containerelement~ContainerElement container element}.\n// Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-invalid-range-container` when validation fails.\n//\n// @param {module:engine/view/range~Range} range\n// @param {Object} errorContext\nfunction validateRangeContainer( range, errorContext ) {\n\tconst startContainer = getParentContainer( range.start );\n\tconst endContainer = getParentContainer( range.end );\n\n\tif ( !startContainer || !endContainer || startContainer !== endContainer ) {\n\t\t/**\n\t\t * The container of the given range is invalid.\n\t\t *\n\t\t * This may happen if {@link module:engine/view/range~Range#start range start} and\n\t\t * {@link module:engine/view/range~Range#end range end} positions are not placed inside the same container element or\n\t\t * a parent container for these positions cannot be found.\n\t\t *\n\t\t * Methods like {@link module:engine/view/downcastwriter~DowncastWriter#wrap `DowncastWriter#remove()`},\n\t\t * {@link module:engine/view/downcastwriter~DowncastWriter#wrap `DowncastWriter#clean()`},\n\t\t * {@link module:engine/view/downcastwriter~DowncastWriter#wrap `DowncastWriter#wrap()`},\n\t\t * {@link module:engine/view/downcastwriter~DowncastWriter#wrap `DowncastWriter#unwrap()`} need to be called\n\t\t * on a range that has its start and end positions located in the same container element. Both positions can be\n\t\t * nested within other elements (e.g. an attribute element) but the closest container ancestor must be the same.\n\t\t *\n\t\t * @error view-writer-invalid-range-container\n\t\t */\n\t\tthrow new CKEditorError( 'view-writer-invalid-range-container', errorContext );\n\t}\n}\n\n// Checks if two attribute elements can be joined together. Elements can be joined together if, and only if\n// they do not have ids specified.\n//\n// @private\n// @param {module:engine/view/element~Element} a\n// @param {module:engine/view/element~Element} b\n// @returns {Boolean}\nfunction canBeJoined( a, b ) {\n\treturn a.id === null && b.id === null;\n}\n"],"sourceRoot":""}

abc