{"version":3,"sources":["webpack:///./node_modules/@ckeditor/ckeditor5-engine/src/model/rootelement.js","webpack:///./node_modules/@ckeditor/ckeditor5-engine/src/model/selection.js"],"names":["RootElement","document","name","_this","rootName","arguments","length","undefined","Object","D_Projects_UA_repo_Source_Client_UA_User_Web_node_modules_babel_runtime_helpers_esm_classCallCheck_js__WEBPACK_IMPORTED_MODULE_2__","this","_super","call","_document","type","Element","Selection","selectable","placeOrOffset","options","D_Projects_UA_repo_Source_Client_UA_User_Web_node_modules_babel_runtime_helpers_esm_classCallCheck_js__WEBPACK_IMPORTED_MODULE_12__","_lastRangeBackward","_ranges","_attrs","Map","setTo","range","end","start","isCollapsed","otherSelection","rangeCount","anchor","isEqual","focus","_step","_iterator","_createForOfIteratorHelper","s","n","done","_step2","thisRange","value","found","_iterator2","otherRange","err","e","f","getRanges","_iterator3","_step3","regeneratorRuntime","wrap","_context","prev","next","Range","t0","finish","stop","_step4","first","_iterator4","isBefore","_step5","last","_iterator5","isAfter","getFirstRange","clone","lastRange","getLastRange","_setRanges","isBackward","backward","Position","Node","_createIn","_createOn","CKEditorError","_createAt","isIterable","newRanges","isLastBackward","Array","from","anyNewRange","some","newRange","every","oldRange","_removeAllRanges","_step6","_iterator6","_pushRange","fire","directChange","itemOrPosition","offset","newFocus","compareWith","_popRange","key","get","entries","keys","has","hasAttribute","delete","attributeKeys","getAttribute","set","getContainedElement","getSelectedBlocks","visited","_iterator7","_step7","startBlock","_iterator8","_step8","block","endBlock","_context2","WeakSet","getParentBlock","isTopBlockInRange","getWalker","item","isUnvisitedTopBlock","isTouching","t1","element","root","limitStartPosition","limitEndPosition","getFirstPosition","getLastPosition","_checkRange","push","i","isIntersecting","addedRange","intersectingRange","pop","isUnvisitedBlock","add","model","schema","isBlock","parent","position","ancestors","getAncestors","parentFirst","includeSelf","hasParentLimit","find","isLimit","forEach","parentBlock","findAncestorBlock","isParentInRange","containsRange","node","mix","EmitterMixin"],"mappings":"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","file":"js/chunk-252b727f.b20a3c4b.js","sourcesContent":["/**\n * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * @module engine/model/rootelement\n */\n\nimport Element from './element';\n\n/**\n * Type of {@link module:engine/model/element~Element} that is a root of a model tree.\n * @extends module:engine/model/element~Element\n */\nexport default class RootElement extends Element {\n\t/**\n\t * Creates root element.\n\t *\n\t * @param {module:engine/model/document~Document} document Document that is an owner of this root.\n\t * @param {String} name Node name.\n\t * @param {String} [rootName='main'] Unique root name used to identify this root\n\t * element by {@link module:engine/model/document~Document}.\n\t */\n\tconstructor( document, name, rootName = 'main' ) {\n\t\tsuper( name );\n\n\t\t/**\n\t\t * Document that is an owner of this root.\n\t\t *\n\t\t * @private\n\t\t * @member {module:engine/model/document~Document}\n\t\t */\n\t\tthis._document = document;\n\n\t\t/**\n\t\t * Unique root name used to identify this root element by {@link module:engine/model/document~Document}.\n\t\t *\n\t\t * @readonly\n\t\t * @member {String}\n\t\t */\n\t\tthis.rootName = rootName;\n\t}\n\n\t/**\n\t * {@link module:engine/model/document~Document Document} that owns this root element.\n\t *\n\t * @readonly\n\t * @type {module:engine/model/document~Document|null}\n\t */\n\tget document() {\n\t\treturn this._document;\n\t}\n\n\t/**\n\t * Checks whether this object is of the given.\n\t *\n\t *\t\trootElement.is( 'rootElement' ); // -> true\n\t *\t\trootElement.is( 'element' ); // -> true\n\t *\t\trootElement.is( 'node' ); // -> true\n\t *\t\trootElement.is( 'model:rootElement' ); // -> true\n\t *\t\trootElement.is( 'model:element' ); // -> true\n\t *\t\trootElement.is( 'model:node' ); // -> true\n\t *\n\t *\t\trootElement.is( 'view:element' ); // -> false\n\t *\t\trootElement.is( 'documentFragment' ); // -> false\n\t *\n\t * Assuming that the object being checked is an element, you can also check its\n\t * {@link module:engine/model/element~Element#name name}:\n\t *\n\t *\t\trootElement.is( 'rootElement', '$root' ); // -> same as above\n\t *\n\t * {@link module:engine/model/node~Node#is Check the entire list of model objects} which implement the `is()` method.\n\t *\n\t * @param {String} type Type to check.\n\t * @param {String} [name] Element name.\n\t * @returns {Boolean}\n\t */\n\tis( type, name ) {\n\t\tif ( !name ) {\n\t\t\treturn type === 'rootElement' || type === 'model:rootElement' ||\n\t\t\t\t// From super.is(). This is highly utilised method and cannot call super. See ckeditor/ckeditor5#6529.\n\t\t\t\ttype === 'element' || type === 'model:element' ||\n\t\t\t\ttype === 'node' || type === 'model:node';\n\t\t}\n\n\t\treturn name === this.name && (\n\t\t\ttype === 'rootElement' || type === 'model:rootElement' ||\n\t\t\t// From super.is(). This is highly utilised method and cannot call super. See ckeditor/ckeditor5#6529.\n\t\t\ttype === 'element' || type === 'model:element'\n\t\t);\n\t}\n\n\t/**\n\t * Converts `RootElement` instance to `String` containing it's name.\n\t *\n\t * @returns {String} `RootElement` instance converted to `String`.\n\t */\n\ttoJSON() {\n\t\treturn this.rootName;\n\t}\n\n\t// @if CK_DEBUG_ENGINE // toString() {\n\t// @if CK_DEBUG_ENGINE // \treturn this.rootName;\n\t// @if CK_DEBUG_ENGINE // }\n\n\t// @if CK_DEBUG_ENGINE // log() {\n\t// @if CK_DEBUG_ENGINE // \tconsole.log( 'ModelRootElement: ' + this );\n\t// @if CK_DEBUG_ENGINE // }\n}\n\n","/**\n * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * @module engine/model/selection\n */\n\nimport Position from './position';\nimport Node from './node';\nimport Range from './range';\nimport EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';\nimport CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';\nimport mix from '@ckeditor/ckeditor5-utils/src/mix';\nimport isIterable from '@ckeditor/ckeditor5-utils/src/isiterable';\n\n/**\n * Selection is a set of {@link module:engine/model/range~Range ranges}. It has a direction specified by its\n * {@link module:engine/model/selection~Selection#anchor anchor} and {@link module:engine/model/selection~Selection#focus focus}\n * (it can be {@link module:engine/model/selection~Selection#isBackward forward or backward}).\n * Additionally, selection may have its own attributes (think – whether text typed in in this selection\n * should have those attributes – e.g. whether you type a bolded text).\n *\n * @mixes module:utils/emittermixin~EmitterMixin\n */\nexport default class Selection {\n\t/**\n\t * Creates a new selection instance based on the given {@link module:engine/model/selection~Selectable selectable}\n\t * or creates an empty selection if no arguments were passed.\n\t *\n\t *\t\t// Creates empty selection without ranges.\n\t *\t\tconst selection = writer.createSelection();\n\t *\n\t *\t\t// Creates selection at the given range.\n\t *\t\tconst range = writer.createRange( start, end );\n\t *\t\tconst selection = writer.createSelection( range );\n\t *\n\t *\t\t// Creates selection at the given ranges\n\t *\t\tconst ranges = [ writer.createRange( start1, end2 ), writer.createRange( star2, end2 ) ];\n\t *\t\tconst selection = writer.createSelection( ranges );\n\t *\n\t *\t\t// Creates selection from the other selection.\n\t *\t\t// Note: It doesn't copies selection attributes.\n\t *\t\tconst otherSelection = writer.createSelection();\n\t *\t\tconst selection = writer.createSelection( otherSelection );\n\t *\n\t *\t\t// Creates selection from the given document selection.\n\t *\t\t// Note: It doesn't copies selection attributes.\n\t *\t\tconst documentSelection = model.document.selection;\n\t *\t\tconst selection = writer.createSelection( documentSelection );\n\t *\n\t *\t\t// Creates selection at the given position.\n\t *\t\tconst position = writer.createPositionFromPath( root, path );\n\t *\t\tconst selection = writer.createSelection( position );\n\t *\n\t *\t\t// Creates selection at the given offset in the given element.\n\t *\t\tconst paragraph = writer.createElement( 'paragraph' );\n\t *\t\tconst selection = writer.createSelection( paragraph, offset );\n\t *\n\t *\t\t// Creates a range inside an {@link module:engine/model/element~Element element} which starts before the\n\t *\t\t// first child of that element and ends after the last child of that element.\n\t *\t\tconst selection = writer.createSelection( paragraph, 'in' );\n\t *\n\t *\t\t// Creates a range on an {@link module:engine/model/item~Item item} which starts before the item and ends\n\t *\t\t// just after the item.\n\t *\t\tconst selection = writer.createSelection( paragraph, 'on' );\n\t *\n\t * Selection's constructor allow passing additional options (`'backward'`) as the last argument.\n\t *\n\t *\t\t// Creates backward selection.\n\t *\t\tconst selection = writer.createSelection( range, { backward: true } );\n\t *\n\t * @param {module:engine/model/selection~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 */\n\tconstructor( selectable, placeOrOffset, options ) {\n\t\t/**\n\t\t * Specifies whether the last added range was added as a backward or forward range.\n\t\t *\n\t\t * @private\n\t\t * @type {Boolean}\n\t\t */\n\t\tthis._lastRangeBackward = false;\n\n\t\t/**\n\t\t * Stores selection ranges.\n\t\t *\n\t\t * @protected\n\t\t * @type {Array.}\n\t\t */\n\t\tthis._ranges = [];\n\n\t\t/**\n\t\t * List of attributes set on current selection.\n\t\t *\n\t\t * @protected\n\t\t * @type {Map.}\n\t\t */\n\t\tthis._attrs = new Map();\n\n\t\tif ( selectable ) {\n\t\t\tthis.setTo( selectable, placeOrOffset, options );\n\t\t}\n\t}\n\n\t/**\n\t * Selection anchor. Anchor is the position from which the selection was started. If a user is making a selection\n\t * by dragging the mouse, the anchor is where the user pressed the mouse button (the beginning of the selection).\n\t *\n\t * Anchor and {@link #focus} define the direction of the selection, which is important\n\t * when expanding/shrinking selection. The focus moves, while the anchor should remain in the same place.\n\t *\n\t * Anchor is always set to the {@link module:engine/model/range~Range#start start} or\n\t * {@link module:engine/model/range~Range#end end} position of the last of selection's ranges. Whether it is\n\t * the `start` or `end` depends on the specified `options.backward`. See the {@link #setTo `setTo()`} method.\n\t *\n\t * May be set to `null` if there are no ranges in the selection.\n\t *\n\t * @see #focus\n\t * @readonly\n\t * @type {module:engine/model/position~Position|null}\n\t */\n\tget anchor() {\n\t\tif ( this._ranges.length > 0 ) {\n\t\t\tconst range = this._ranges[ this._ranges.length - 1 ];\n\n\t\t\treturn this._lastRangeBackward ? range.end : range.start;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Selection focus. Focus is the position where the selection ends. If a user is making a selection\n\t * by dragging the mouse, the focus is where the mouse cursor is.\n\t *\n\t * May be set to `null` if there are no ranges in the selection.\n\t *\n\t * @see #anchor\n\t * @readonly\n\t * @type {module:engine/model/position~Position|null}\n\t */\n\tget focus() {\n\t\tif ( this._ranges.length > 0 ) {\n\t\t\tconst range = this._ranges[ this._ranges.length - 1 ];\n\n\t\t\treturn this._lastRangeBackward ? range.start : range.end;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Whether the selection is collapsed. Selection is collapsed when there is exactly one range in it\n\t * and it is collapsed.\n\t *\n\t * @readonly\n\t * @type {Boolean}\n\t */\n\tget isCollapsed() {\n\t\tconst length = this._ranges.length;\n\n\t\tif ( length === 1 ) {\n\t\t\treturn this._ranges[ 0 ].isCollapsed;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of ranges in the selection.\n\t *\n\t * @readonly\n\t * @type {Number}\n\t */\n\tget rangeCount() {\n\t\treturn this._ranges.length;\n\t}\n\n\t/**\n\t * Specifies whether the selection's {@link #focus} precedes the selection's {@link #anchor}.\n\t *\n\t * @readonly\n\t * @type {Boolean}\n\t */\n\tget isBackward() {\n\t\treturn !this.isCollapsed && this._lastRangeBackward;\n\t}\n\n\t/**\n\t * Checks whether this selection is equal to the given selection. Selections are equal if they have the same directions,\n\t * the same number of ranges and all ranges from one selection equal to ranges from the another selection.\n\t *\n\t * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} otherSelection\n\t * Selection to compare with.\n\t * @returns {Boolean} `true` if selections are equal, `false` otherwise.\n\t */\n\tisEqual( otherSelection ) {\n\t\tif ( this.rangeCount != otherSelection.rangeCount ) {\n\t\t\treturn false;\n\t\t} else if ( this.rangeCount === 0 ) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif ( !this.anchor.isEqual( otherSelection.anchor ) || !this.focus.isEqual( otherSelection.focus ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfor ( const thisRange of this._ranges ) {\n\t\t\tlet found = false;\n\n\t\t\tfor ( const otherRange of otherSelection._ranges ) {\n\t\t\t\tif ( thisRange.isEqual( otherRange ) ) {\n\t\t\t\t\tfound = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( !found ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Returns an iterable object that iterates over copies of selection ranges.\n\t *\n\t * @returns {Iterable.}\n\t */\n\t* getRanges() {\n\t\tfor ( const range of this._ranges ) {\n\t\t\tyield new Range( range.start, range.end );\n\t\t}\n\t}\n\n\t/**\n\t * Returns a copy of the first range in the selection.\n\t * First range is the one which {@link module:engine/model/range~Range#start start} position\n\t * {@link module:engine/model/position~Position#isBefore is before} start position of all other ranges\n\t * (not to confuse with the first range added to the selection).\n\t *\n\t * Returns `null` if there are no ranges in selection.\n\t *\n\t * @returns {module:engine/model/range~Range|null}\n\t */\n\tgetFirstRange() {\n\t\tlet first = null;\n\n\t\tfor ( const range of this._ranges ) {\n\t\t\tif ( !first || range.start.isBefore( first.start ) ) {\n\t\t\t\tfirst = range;\n\t\t\t}\n\t\t}\n\n\t\treturn first ? new Range( first.start, first.end ) : null;\n\t}\n\n\t/**\n\t * Returns a copy of the last range in the selection.\n\t * Last range is the one which {@link module:engine/model/range~Range#end end} position\n\t * {@link module:engine/model/position~Position#isAfter is after} end position of all other ranges (not to confuse with the range most\n\t * recently added to the selection).\n\t *\n\t * Returns `null` if there are no ranges in selection.\n\t *\n\t * @returns {module:engine/model/range~Range|null}\n\t */\n\tgetLastRange() {\n\t\tlet last = null;\n\n\t\tfor ( const range of this._ranges ) {\n\t\t\tif ( !last || range.end.isAfter( last.end ) ) {\n\t\t\t\tlast = range;\n\t\t\t}\n\t\t}\n\n\t\treturn last ? new Range( last.start, last.end ) : null;\n\t}\n\n\t/**\n\t * Returns the first position in the selection.\n\t * First position is the position that {@link module:engine/model/position~Position#isBefore is before}\n\t * any other position in the selection.\n\t *\n\t * Returns `null` if there are no ranges in selection.\n\t *\n\t * @returns {module:engine/model/position~Position|null}\n\t */\n\tgetFirstPosition() {\n\t\tconst first = this.getFirstRange();\n\n\t\treturn first ? first.start.clone() : null;\n\t}\n\n\t/**\n\t * Returns the last position in the selection.\n\t * Last position is the position that {@link module:engine/model/position~Position#isAfter is after}\n\t * any other position in the selection.\n\t *\n\t * Returns `null` if there are no ranges in selection.\n\t *\n\t * @returns {module:engine/model/position~Position|null}\n\t */\n\tgetLastPosition() {\n\t\tconst lastRange = this.getLastRange();\n\n\t\treturn lastRange ? lastRange.end.clone() : null;\n\t}\n\n\t/**\n\t * Sets this selection's ranges and direction to the specified location based on the given\n\t * {@link module:engine/model/selection~Selectable selectable}.\n\t *\n\t *\t\t// Removes all selection's ranges.\n\t *\t\tselection.setTo( null );\n\t *\n\t *\t\t// Sets selection to the given range.\n\t *\t\tconst range = writer.createRange( start, end );\n\t *\t\tselection.setTo( range );\n\t *\n\t *\t\t// Sets selection to given ranges.\n\t *\t\tconst ranges = [ writer.createRange( start1, end2 ), writer.createRange( star2, end2 ) ];\n\t *\t\tselection.setTo( ranges );\n\t *\n\t *\t\t// Sets selection to other selection.\n\t *\t\t// Note: It doesn't copies selection attributes.\n\t *\t\tconst otherSelection = writer.createSelection();\n\t *\t\tselection.setTo( otherSelection );\n\t *\n\t *\t\t// Sets selection to the given document selection.\n\t *\t\t// Note: It doesn't copies selection attributes.\n\t *\t\tconst documentSelection = new DocumentSelection( doc );\n\t *\t\tselection.setTo( documentSelection );\n\t *\n\t *\t\t// Sets collapsed selection at the given position.\n\t *\t\tconst position = writer.createPositionFromPath( root, path );\n\t *\t\tselection.setTo( position );\n\t *\n\t *\t\t// Sets collapsed selection at the position of the given node and an offset.\n\t *\t\tselection.setTo( paragraph, offset );\n\t *\n\t * Creates a range inside an {@link module:engine/model/element~Element element} which starts before the first child of\n \t * that element and ends after the last child of that element.\n\t *\n\t *\t\tselection.setTo( paragraph, 'in' );\n\t *\n\t * Creates a range on an {@link module:engine/model/item~Item item} which starts before the item and ends just after the item.\n\t *\n\t *\t\tselection.setTo( paragraph, 'on' );\n\t *\n\t * `Selection#setTo()`' method allow passing additional options (`backward`) as the last argument.\n\t *\n\t *\t\t// Sets backward selection.\n\t *\t\tconst selection = writer.createSelection( range, { backward: true } );\n\t *\n\t * @param {module:engine/model/selection~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 */\n\tsetTo( selectable, placeOrOffset, options ) {\n\t\tif ( selectable === null ) {\n\t\t\tthis._setRanges( [] );\n\t\t} else if ( selectable instanceof Selection ) {\n\t\t\tthis._setRanges( selectable.getRanges(), selectable.isBackward );\n\t\t} else if ( selectable && typeof selectable.getRanges == 'function' ) {\n\t\t\t// We assume that the selectable is a DocumentSelection.\n\t\t\t// It can't be imported here, because it would lead to circular imports.\n\t\t\tthis._setRanges( selectable.getRanges(), selectable.isBackward );\n\t\t} else if ( selectable instanceof Range ) {\n\t\t\tthis._setRanges( [ selectable ], !!placeOrOffset && !!placeOrOffset.backward );\n\t\t} else if ( selectable instanceof Position ) {\n\t\t\tthis._setRanges( [ new Range( selectable ) ] );\n\t\t} else if ( selectable instanceof Node ) {\n\t\t\tconst backward = !!options && !!options.backward;\n\t\t\tlet range;\n\n\t\t\tif ( placeOrOffset == 'in' ) {\n\t\t\t\trange = Range._createIn( selectable );\n\t\t\t} else if ( placeOrOffset == 'on' ) {\n\t\t\t\trange = Range._createOn( selectable );\n\t\t\t} else if ( placeOrOffset !== undefined ) {\n\t\t\t\trange = new Range( Position._createAt( selectable, placeOrOffset ) );\n\t\t\t} else {\n\t\t\t\t/**\n\t\t\t\t * selection.setTo requires the second parameter when the first parameter is a node.\n\t\t\t\t *\n\t\t\t\t * @error model-selection-setto-required-second-parameter\n\t\t\t\t */\n\t\t\t\tthrow new CKEditorError( 'model-selection-setto-required-second-parameter', [ this, selectable ] );\n\t\t\t}\n\n\t\t\tthis._setRanges( [ range ], backward );\n\t\t} else if ( isIterable( selectable ) ) {\n\t\t\t// We assume that the selectable is an iterable of ranges.\n\t\t\tthis._setRanges( selectable, placeOrOffset && !!placeOrOffset.backward );\n\t\t} else {\n\t\t\t/**\n\t\t\t * Cannot set the selection to the given place.\n\t\t\t *\n\t\t\t * Invalid parameters were specified when setting the selection. Common issues:\n\t\t\t *\n\t\t\t * * A {@link module:engine/model/textproxy~TextProxy} instance was passed instead of\n\t\t\t * a real {@link module:engine/model/text~Text}.\n\t\t\t * * View nodes were passed instead of model nodes.\n\t\t\t * * `null`/`undefined` was passed.\n\t\t\t *\n\t\t\t * @error model-selection-setto-not-selectable\n\t\t\t */\n\t\t\tthrow new CKEditorError( 'model-selection-setto-not-selectable', [ this, selectable ] );\n\t\t}\n\t}\n\n\t/**\n\t * Replaces all ranges that were added to the selection with given array of ranges. Last range of the array\n\t * is treated like the last added range and is used to set {@link module:engine/model/selection~Selection#anchor} and\n\t * {@link module:engine/model/selection~Selection#focus}. Accepts a flag describing in which direction the selection is made.\n\t *\n\t * @protected\n\t * @fires change:range\n\t * @param {Iterable.} newRanges Ranges to set.\n\t * @param {Boolean} [isLastBackward=false] Flag describing if last added range was selected forward - from start to end (`false`)\n\t * or backward - from end to start (`true`).\n\t */\n\t_setRanges( newRanges, isLastBackward = false ) {\n\t\tnewRanges = Array.from( newRanges );\n\n\t\t// Check whether there is any range in new ranges set that is different than all already added ranges.\n\t\tconst anyNewRange = newRanges.some( newRange => {\n\t\t\tif ( !( newRange instanceof Range ) ) {\n\t\t\t\t/**\n\t\t\t\t * Selection range set to an object that is not an instance of {@link module:engine/model/range~Range}.\n\t\t\t\t *\n\t\t\t\t * Only {@link module:engine/model/range~Range} instances can be used to set a selection.\n\t\t\t\t * Common mistakes leading to this error are:\n\t\t\t\t *\n\t\t\t\t * * using DOM `Range` object,\n\t\t\t\t * * incorrect CKEditor 5 installation with multiple `ckeditor5-engine` packages having different versions.\n\t\t\t\t *\n\t\t\t\t * @error model-selection-set-ranges-not-range\n\t\t\t\t */\n\t\t\t\tthrow new CKEditorError(\n\t\t\t\t\t'model-selection-set-ranges-not-range',\n\t\t\t\t\t[ this, newRanges ]\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn this._ranges.every( oldRange => {\n\t\t\t\treturn !oldRange.isEqual( newRange );\n\t\t\t} );\n\t\t} );\n\n\t\t// Don't do anything if nothing changed.\n\t\tif ( newRanges.length === this._ranges.length && !anyNewRange ) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._removeAllRanges();\n\n\t\tfor ( const range of newRanges ) {\n\t\t\tthis._pushRange( range );\n\t\t}\n\n\t\tthis._lastRangeBackward = !!isLastBackward;\n\n\t\tthis.fire( 'change:range', { directChange: true } );\n\t}\n\n\t/**\n\t * Moves {@link module:engine/model/selection~Selection#focus} to the specified location.\n\t *\n\t * The location can be specified in the same form as\n\t * {@link module:engine/model/writer~Writer#createPositionAt writer.createPositionAt()} parameters.\n\t *\n\t * @fires change:range\n\t * @param {module:engine/model/item~Item|module:engine/model/position~Position} itemOrPosition\n\t * @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when\n\t * first parameter is a {@link module:engine/model/item~Item model item}.\n\t */\n\tsetFocus( itemOrPosition, offset ) {\n\t\tif ( this.anchor === null ) {\n\t\t\t/**\n\t\t\t * Cannot set selection focus if there are no ranges in selection.\n\t\t\t *\n\t\t\t * @error model-selection-setfocus-no-ranges\n\t\t\t */\n\t\t\tthrow new CKEditorError( 'model-selection-setfocus-no-ranges', [ this, itemOrPosition ] );\n\t\t}\n\n\t\tconst newFocus = Position._createAt( itemOrPosition, offset );\n\n\t\tif ( newFocus.compareWith( this.focus ) == 'same' ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst anchor = this.anchor;\n\n\t\tif ( this._ranges.length ) {\n\t\t\tthis._popRange();\n\t\t}\n\n\t\tif ( newFocus.compareWith( anchor ) == 'before' ) {\n\t\t\tthis._pushRange( new Range( newFocus, anchor ) );\n\t\t\tthis._lastRangeBackward = true;\n\t\t} else {\n\t\t\tthis._pushRange( new Range( anchor, newFocus ) );\n\t\t\tthis._lastRangeBackward = false;\n\t\t}\n\n\t\tthis.fire( 'change:range', { directChange: true } );\n\t}\n\n\t/**\n\t * Gets an attribute value for given key or `undefined` if that attribute is not set on the selection.\n\t *\n\t * @param {String} key Key of attribute to look for.\n\t * @returns {*} Attribute value or `undefined`.\n\t */\n\tgetAttribute( key ) {\n\t\treturn this._attrs.get( key );\n\t}\n\n\t/**\n\t * Returns iterable that iterates over this selection's attributes.\n\t *\n\t * Attributes are returned as arrays containing two items. First one is attribute key and second is attribute value.\n\t * This format is accepted by native `Map` object and also can be passed in `Node` constructor.\n\t *\n\t * @returns {Iterable.<*>}\n\t */\n\tgetAttributes() {\n\t\treturn this._attrs.entries();\n\t}\n\n\t/**\n\t * Returns iterable that iterates over this selection's attribute keys.\n\t *\n\t * @returns {Iterable.}\n\t */\n\tgetAttributeKeys() {\n\t\treturn this._attrs.keys();\n\t}\n\n\t/**\n\t * Checks if the selection has an attribute for given key.\n\t *\n\t * @param {String} key Key of attribute to check.\n\t * @returns {Boolean} `true` if attribute with given key is set on selection, `false` otherwise.\n\t */\n\thasAttribute( key ) {\n\t\treturn this._attrs.has( key );\n\t}\n\n\t/**\n\t * Removes an attribute with given key from the selection.\n\t *\n\t * If given attribute was set on the selection, fires the {@link #event:change:range} event with\n\t * removed attribute key.\n\t *\n\t * @fires change:attribute\n\t * @param {String} key Key of attribute to remove.\n\t */\n\tremoveAttribute( key ) {\n\t\tif ( this.hasAttribute( key ) ) {\n\t\t\tthis._attrs.delete( key );\n\n\t\t\tthis.fire( 'change:attribute', { attributeKeys: [ key ], directChange: true } );\n\t\t}\n\t}\n\n\t/**\n\t * Sets attribute on the selection. If attribute with the same key already is set, it's value is overwritten.\n\t *\n\t * If the attribute value has changed, fires the {@link #event:change:range} event with\n\t * the attribute key.\n\t *\n\t * @fires change:attribute\n\t * @param {String} key Key of attribute to set.\n\t * @param {*} value Attribute value.\n\t */\n\tsetAttribute( key, value ) {\n\t\tif ( this.getAttribute( key ) !== value ) {\n\t\t\tthis._attrs.set( key, value );\n\n\t\t\tthis.fire( 'change:attribute', { attributeKeys: [ key ], directChange: true } );\n\t\t}\n\t}\n\n\t/**\n\t * Returns the selected element. {@link module:engine/model/element~Element Element} is considered as selected if there is only\n\t * one range in the selection, and that range contains exactly one element.\n\t * Returns `null` if there is no selected element.\n\t *\n\t * @returns {module:engine/model/element~Element|null}\n\t */\n\tgetSelectedElement() {\n\t\tif ( this.rangeCount !== 1 ) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn this.getFirstRange().getContainedElement();\n\t}\n\n\t/**\n\t * Checks whether this object is of the given.\n\t *\n\t *\t\tselection.is( 'selection' ); // -> true\n\t *\t\tselection.is( 'model:selection' ); // -> true\n\t *\n\t *\t\tselection.is( 'view:selection' ); // -> false\n\t *\t\tselection.is( 'range' ); // -> false\n\t *\n\t * {@link module:engine/model/node~Node#is Check the entire list of model objects} which implement the `is()` method.\n\t *\n\t * @param {String} type\n\t * @returns {Boolean}\n\t */\n\tis( type ) {\n\t\treturn type === 'selection' || type === 'model:selection';\n\t}\n\n\t/**\n\t * Gets elements of type {@link module:engine/model/schema~Schema#isBlock \"block\"} touched by the selection.\n\t *\n\t * This method's result can be used for example to apply block styling to all blocks covered by this selection.\n\t *\n\t * **Note:** `getSelectedBlocks()` returns blocks that are nested in other non-block elements\n\t * but will not return blocks nested in other blocks.\n\t *\n\t * In this case the function will return exactly all 3 paragraphs (note: `
` is not a block itself):\n\t *\n\t *\t\t[a\n\t *\t\t
\n\t *\t\t\tb\n\t *\t\t
\n\t *\t\tc]d\n\t *\n\t * In this case the paragraph will also be returned, despite the collapsed selection:\n\t *\n\t *\t\t[]a\n\t *\n\t * In such a scenario, however, only blocks A, B & E will be returned as blocks C & D are nested in block B:\n\t *\n\t *\t\t[\n\t *\t\t\n\t *\t\t\t\n\t *\t\t\t\n\t *\t\t\n\t *\t\t]\n\t *\n\t * If the selection is inside a block all the inner blocks (A & B) are returned:\n\t *\n\t * \t\t\n\t *\t\t\t[a\n\t * \t\t\tb]\n\t * \t\t\n\t *\n\t * **Special case**: If a selection ends at the beginning of a block, that block is not returned as from user perspective\n\t * this block wasn't selected. See [#984](https://github.com/ckeditor/ckeditor5-engine/issues/984) for more details.\n\t *\n\t *\t\t[a\n\t *\t\tb\n\t *\t\t]c // this block will not be returned\n\t *\n\t * @returns {Iterable.}\n\t */\n\t* getSelectedBlocks() {\n\t\tconst visited = new WeakSet();\n\n\t\tfor ( const range of this.getRanges() ) {\n\t\t\t// Get start block of range in case of a collapsed range.\n\t\t\tconst startBlock = getParentBlock( range.start, visited );\n\n\t\t\tif ( startBlock && isTopBlockInRange( startBlock, range ) ) {\n\t\t\t\tyield startBlock;\n\t\t\t}\n\n\t\t\tfor ( const value of range.getWalker() ) {\n\t\t\t\tconst block = value.item;\n\n\t\t\t\tif ( value.type == 'elementEnd' && isUnvisitedTopBlock( block, visited, range ) ) {\n\t\t\t\t\tyield block;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst endBlock = getParentBlock( range.end, visited );\n\n\t\t\t// #984. Don't return the end block if the range ends right at its beginning.\n\t\t\tif ( endBlock && !range.end.isTouching( Position._createAt( endBlock, 0 ) ) && isTopBlockInRange( endBlock, range ) ) {\n\t\t\t\tyield endBlock;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Checks whether the selection contains the entire content of the given element. This means that selection must start\n\t * at a position {@link module:engine/model/position~Position#isTouching touching} the element's start and ends at position\n\t * touching the element's end.\n\t *\n\t * By default, this method will check whether the entire content of the selection's current root is selected.\n\t * Useful to check if e.g. the user has just pressed Ctrl + A.\n\t *\n\t * @param {module:engine/model/element~Element} [element=this.anchor.root]\n\t * @returns {Boolean}\n\t */\n\tcontainsEntireContent( element = this.anchor.root ) {\n\t\tconst limitStartPosition = Position._createAt( element, 0 );\n\t\tconst limitEndPosition = Position._createAt( element, 'end' );\n\n\t\treturn limitStartPosition.isTouching( this.getFirstPosition() ) &&\n\t\t\tlimitEndPosition.isTouching( this.getLastPosition() );\n\t}\n\n\t/**\n\t * Adds given range to internal {@link #_ranges ranges array}. Throws an error\n\t * if given range is intersecting with any range that is already stored in this selection.\n\t *\n\t * @protected\n\t * @param {module:engine/model/range~Range} range Range to add.\n\t */\n\t_pushRange( range ) {\n\t\tthis._checkRange( range );\n\t\tthis._ranges.push( new Range( range.start, range.end ) );\n\t}\n\n\t/**\n\t * Checks if given range intersects with ranges that are already in the selection. Throws an error if it does.\n\t *\n\t * @protected\n\t * @param {module:engine/model/range~Range} range Range to check.\n\t */\n\t_checkRange( range ) {\n\t\tfor ( let i = 0; i < this._ranges.length; i++ ) {\n\t\t\tif ( range.isIntersecting( this._ranges[ i ] ) ) {\n\t\t\t\t/**\n\t\t\t\t * Trying to add a range that intersects with another range in the selection.\n\t\t\t\t *\n\t\t\t\t * @error model-selection-range-intersects\n\t\t\t\t * @param {module:engine/model/range~Range} addedRange Range that was added to the selection.\n\t\t\t\t * @param {module:engine/model/range~Range} intersectingRange Range in the selection that intersects with `addedRange`.\n\t\t\t\t */\n\t\t\t\tthrow new CKEditorError(\n\t\t\t\t\t'model-selection-range-intersects',\n\t\t\t\t\t[ this, range ],\n\t\t\t\t\t{ addedRange: range, intersectingRange: this._ranges[ i ] }\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Deletes ranges from internal range array. Uses {@link #_popRange _popRange} to\n\t * ensure proper ranges removal.\n\t *\n\t * @protected\n\t */\n\t_removeAllRanges() {\n\t\twhile ( this._ranges.length > 0 ) {\n\t\t\tthis._popRange();\n\t\t}\n\t}\n\n\t/**\n\t * Removes most recently added range from the selection.\n\t *\n\t * @protected\n\t */\n\t_popRange() {\n\t\tthis._ranges.pop();\n\t}\n\n\t/**\n\t * Fired when selection range(s) changed.\n\t *\n\t * @event change:range\n\t * @param {Boolean} directChange In case of {@link module:engine/model/selection~Selection} class it is always set\n\t * to `true` which indicates that the selection change was caused by a direct use of selection's API.\n\t * The {@link module:engine/model/documentselection~DocumentSelection}, however, may change because its position\n\t * was directly changed through the {@link module:engine/model/writer~Writer writer} or because its position was\n\t * changed because the structure of the model has been changed (which means an indirect change).\n\t * The indirect change does not occur in case of normal (detached) selections because they are \"static\" (as \"not live\")\n\t * which mean that they are not updated once the document changes.\n\t */\n\n\t/**\n\t * Fired when selection attribute changed.\n\t *\n\t * @event change:attribute\n\t * @param {Boolean} directChange In case of {@link module:engine/model/selection~Selection} class it is always set\n\t * to `true` which indicates that the selection change was caused by a direct use of selection's API.\n\t * The {@link module:engine/model/documentselection~DocumentSelection}, however, may change because its attributes\n\t * were directly changed through the {@link module:engine/model/writer~Writer writer} or because its position was\n\t * changed in the model and its attributes were refreshed (which means an indirect change).\n\t * The indirect change does not occur in case of normal (detached) selections because they are \"static\" (as \"not live\")\n\t * which mean that they are not updated once the document changes.\n\t * @param {Array.} attributeKeys Array containing keys of attributes that changed.\n\t */\n}\n\nmix( Selection, EmitterMixin );\n\n// Checks whether the given element extends $block in the schema and has a parent (is not a root).\n// Marks it as already visited.\nfunction isUnvisitedBlock( element, visited ) {\n\tif ( visited.has( element ) ) {\n\t\treturn false;\n\t}\n\n\tvisited.add( element );\n\n\treturn element.root.document.model.schema.isBlock( element ) && element.parent;\n}\n\n// Checks if the given element is a $block was not previously visited and is a top block in a range.\nfunction isUnvisitedTopBlock( element, visited, range ) {\n\treturn isUnvisitedBlock( element, visited ) && isTopBlockInRange( element, range );\n}\n\n// Finds the lowest element in position's ancestors which is a block.\n// It will search until first ancestor that is a limit element.\n// Marks all ancestors as already visited to not include any of them later on.\nfunction getParentBlock( position, visited ) {\n\tconst element = position.parent;\n\tconst schema = element.root.document.model.schema;\n\n\tconst ancestors = position.parent.getAncestors( { parentFirst: true, includeSelf: true } );\n\n\tlet hasParentLimit = false;\n\n\tconst block = ancestors.find( element => {\n\t\t// Stop searching after first parent node that is limit element.\n\t\tif ( hasParentLimit ) {\n\t\t\treturn false;\n\t\t}\n\n\t\thasParentLimit = schema.isLimit( element );\n\n\t\treturn !hasParentLimit && isUnvisitedBlock( element, visited );\n\t} );\n\n\t// Mark all ancestors of this position's parent, because find() might've stopped early and\n\t// the found block may be a child of another block.\n\tancestors.forEach( element => visited.add( element ) );\n\n\treturn block;\n}\n\n// Checks if the blocks is not nested in other block inside a range.\n//\n// @param {module:engine/model/element~Element} block Block to check.\n// @param {module:engine/model/range~Range} range Range to check.\nfunction isTopBlockInRange( block, range ) {\n\tconst parentBlock = findAncestorBlock( block );\n\n\tif ( !parentBlock ) {\n\t\treturn true;\n\t}\n\n\t// Add loose flag to check as parentRange can be equal to range.\n\tconst isParentInRange = range.containsRange( Range._createOn( parentBlock ), true );\n\n\treturn !isParentInRange;\n}\n\n// Returns first ancestor block of a node.\n//\n// @param {module:engine/model/node~Node} node\n// @returns {module:engine/model/node~Node|undefined}\nfunction findAncestorBlock( node ) {\n\tconst schema = node.root.document.model.schema;\n\n\tlet parent = node.parent;\n\n\twhile ( parent ) {\n\t\tif ( schema.isBlock( parent ) ) {\n\t\t\treturn parent;\n\t\t}\n\n\t\tparent = parent.parent;\n\t}\n}\n\n/**\n * An entity that is used to set selection.\n *\n * See also {@link module:engine/model/selection~Selection#setTo}\n *\n * @typedef {\n * module:engine/model/selection~Selection|\n * module:engine/model/documentselection~DocumentSelection|\n * module:engine/model/position~Position|\n * module:engine/model/range~Range|\n * module:engine/model/node~Node|\n * Iterable.|\n * null\n * } module:engine/model/selection~Selectable\n */\n"],"sourceRoot":""}