{"version":3,"sources":["webpack:///./node_modules/@ckeditor/ckeditor5-engine/src/model/utils/selection-post-fixer.js"],"names":["injectSelectionPostFixer","model","document","registerPostFixer","writer","selectionPostFixer","_step","selection","schema","ranges","wasFixed","_iterator","_createForOfIteratorHelper","getRanges","s","n","done","modelRange","value","correctedRange","tryFixingRange","isEqual","push","err","e","f","setSelection","mergeIntersectingRanges","backward","isBackward","range","isCollapsed","tryFixingCollapsedRange","tryFixingNonCollapsedRage","originalPosition","start","nearestSelectionRange","getNearestSelectionRange","fixedPosition","Range","end","isTextAllowedOnStart","checkChild","isTextAllowedOnEnd","startLimitElement","getLimitElement","endLimitElement","checkSelectionOnNonLimitElements","isStartBeforeSelectable","nodeAfter","isSelectable","fixedStart","isEndAfterSelectable","nodeBefore","fixedEnd","rangeStart","rangeEnd","isStartInLimit","is","isEndInLimit","bothInSameParent","parent","expandStart","expandEnd","Position","_createBefore","findOutermostLimitAncestor","_createAfter","startingNode","isLimitNode","isLimit","startIsOnBlock","endIsOnBlock","nonIntersectingRanges","shift","_step2","_iterator2","previousRange","pop","isIntersecting","isAfter","merged","node"],"mappings":"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","file":"js/chunk-2d22e108.f05201f4.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/utils/selection-post-fixer\n */\n\nimport Range from '../range';\nimport Position from '../position';\n\n/**\n * Injects selection post-fixer to the model.\n *\n * The role of the selection post-fixer is to ensure that the selection is in a correct place\n * after a {@link module:engine/model/model~Model#change `change()`} block was executed.\n *\n * The correct position means that:\n *\n * * All collapsed selection ranges are in a place where the {@link module:engine/model/schema~Schema}\n * allows a `$text`.\n * * None of the selection's non-collapsed ranges crosses a {@link module:engine/model/schema~Schema#isLimit limit element}\n * boundary (a range must be rooted within one limit element).\n * * Only {@link module:engine/model/schema~Schema#isSelectable selectable elements} can be selected from the outside\n * (e.g. `[foo]` is invalid). This rule applies independently to both selection ends, so this\n * selection is correct: `f[oo]`.\n *\n * If the position is not correct, the post-fixer will automatically correct it.\n *\n * ## Fixing a non-collapsed selection\n *\n * See as an example a selection that starts in a P1 element and ends inside the text of a TD element\n * (`[` and `]` are range boundaries and `(l)` denotes an element defined as `isLimit=true`):\n *\n *\t\troot\n *\t\t |- element P1\n *\t\t | |- \"foo\" root\n *\t\t |- element TABLE (l) P1 TABLE P2\n *\t\t | |- element TR (l) f o[o TR TR b a r\n *\t\t | | |- element TD (l) TD TD\n *\t\t | | |- \"aaa\" a]a a b b b\n *\t\t | |- element TR (l)\n *\t\t | | |- element TD (l) ||\n *\t\t | | |- \"bbb\" ||\n *\t\t |- element P2 VV\n *\t\t | |- \"bar\"\n *\t\t root\n *\t\t P1 TABLE] P2\n *\t\t f o[o TR TR b a r\n *\t\t TD TD\n *\t\t a a a b b b\n *\n * In the example above, the TABLE, TR and TD are defined as `isLimit=true` in the schema. The range which is not contained within\n * a single limit element must be expanded to select the outermost limit element. The range end is inside the text node of the TD element.\n * As the TD element is a child of the TR and TABLE elements, where both are defined as `isLimit=true` in the schema, the range must be\n * expanded to select the whole TABLE element.\n *\n * **Note** If the selection contains multiple ranges, the method returns a minimal set of ranges that are not intersecting after expanding\n * them to select `isLimit=true` elements.\n *\n * @param {module:engine/model/model~Model} model\n */\nexport function injectSelectionPostFixer( model ) {\n\tmodel.document.registerPostFixer( writer => selectionPostFixer( writer, model ) );\n}\n\n// The selection post-fixer.\n//\n// @param {module:engine/model/writer~Writer} writer\n// @param {module:engine/model/model~Model} model\nfunction selectionPostFixer( writer, model ) {\n\tconst selection = model.document.selection;\n\tconst schema = model.schema;\n\n\tconst ranges = [];\n\n\tlet wasFixed = false;\n\n\tfor ( const modelRange of selection.getRanges() ) {\n\t\t// Go through all ranges in selection and try fixing each of them.\n\t\t// Those ranges might overlap but will be corrected later.\n\t\tconst correctedRange = tryFixingRange( modelRange, schema );\n\n\t\t// \"Selection fixing\" algorithms sometimes get lost. In consequence, it may happen\n\t\t// that a new range is returned but, in fact, it has the same positions as the original\n\t\t// range anyway. If this range is not discarded, a new selection will be set and that,\n\t\t// for instance, would destroy the selection attributes. Let's make sure that the post-fixer\n\t\t// actually worked first before setting a new selection.\n\t\t//\n\t\t// https://github.com/ckeditor/ckeditor5/issues/6693\n\t\tif ( correctedRange && !correctedRange.isEqual( modelRange ) ) {\n\t\t\tranges.push( correctedRange );\n\t\t\twasFixed = true;\n\t\t} else {\n\t\t\tranges.push( modelRange );\n\t\t}\n\t}\n\n\t// If any of ranges were corrected update the selection.\n\tif ( wasFixed ) {\n\t\twriter.setSelection( mergeIntersectingRanges( ranges ), { backward: selection.isBackward } );\n\t}\n}\n\n// Tries fixing a range if it's incorrect.\n//\n// @param {module:engine/model/range~Range} range\n// @param {module:engine/model/schema~Schema} schema\n// @returns {module:engine/model/range~Range|null} Returns fixed range or null if range is valid.\nfunction tryFixingRange( range, schema ) {\n\tif ( range.isCollapsed ) {\n\t\treturn tryFixingCollapsedRange( range, schema );\n\t}\n\n\treturn tryFixingNonCollapsedRage( range, schema );\n}\n\n// Tries to fix collapsed ranges.\n//\n// * Fixes situation when a range is in a place where $text is not allowed\n//\n// @param {module:engine/model/range~Range} range Collapsed range to fix.\n// @param {module:engine/model/schema~Schema} schema\n// @returns {module:engine/model/range~Range|null} Returns fixed range or null if range is valid.\nfunction tryFixingCollapsedRange( range, schema ) {\n\tconst originalPosition = range.start;\n\n\tconst nearestSelectionRange = schema.getNearestSelectionRange( originalPosition );\n\n\t// This might be null ie when editor data is empty.\n\t// In such cases there is no need to fix the selection range.\n\tif ( !nearestSelectionRange ) {\n\t\treturn null;\n\t}\n\n\tif ( !nearestSelectionRange.isCollapsed ) {\n\t\treturn nearestSelectionRange;\n\t}\n\n\tconst fixedPosition = nearestSelectionRange.start;\n\n\t// Fixed position is the same as original - no need to return corrected range.\n\tif ( originalPosition.isEqual( fixedPosition ) ) {\n\t\treturn null;\n\t}\n\n\treturn new Range( fixedPosition );\n}\n\n// Tries to fix an expanded range.\n//\n// @param {module:engine/model/range~Range} range Expanded range to fix.\n// @param {module:engine/model/schema~Schema} schema\n// @returns {module:engine/model/range~Range|null} Returns fixed range or null if range is valid.\nfunction tryFixingNonCollapsedRage( range, schema ) {\n\tconst { start, end } = range;\n\n\tconst isTextAllowedOnStart = schema.checkChild( start, '$text' );\n\tconst isTextAllowedOnEnd = schema.checkChild( end, '$text' );\n\n\tconst startLimitElement = schema.getLimitElement( start );\n\tconst endLimitElement = schema.getLimitElement( end );\n\n\t// Ranges which both end are inside the same limit element (or root) might needs only minor fix.\n\tif ( startLimitElement === endLimitElement ) {\n\t\t// Range is valid when both position allows to place a text:\n\t\t// - f[oobarba]z\n\t\t// This would be \"fixed\" by a next check but as it will be the same it's better to return null so the selection stays the same.\n\t\tif ( isTextAllowedOnStart && isTextAllowedOnEnd ) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Range that is on non-limit element (or is partially) must be fixed so it is placed inside the block around $text:\n\t\t// - [foo] -> [foo]\n\t\t// - [foo] -> [foo]\n\t\t// - f[oo] -> f[oo]\n\t\t// - [foo] -> [foo]\n\t\tif ( checkSelectionOnNonLimitElements( start, end, schema ) ) {\n\t\t\tconst isStartBeforeSelectable = start.nodeAfter && schema.isSelectable( start.nodeAfter );\n\t\t\tconst fixedStart = isStartBeforeSelectable ? null : schema.getNearestSelectionRange( start, 'forward' );\n\n\t\t\tconst isEndAfterSelectable = end.nodeBefore && schema.isSelectable( end.nodeBefore );\n\t\t\tconst fixedEnd = isEndAfterSelectable ? null : schema.getNearestSelectionRange( end, 'backward' );\n\n\t\t\t// The schema.getNearestSelectionRange might return null - if that happens use original position.\n\t\t\tconst rangeStart = fixedStart ? fixedStart.start : start;\n\t\t\tconst rangeEnd = fixedEnd ? fixedEnd.end : end;\n\n\t\t\treturn new Range( rangeStart, rangeEnd );\n\t\t}\n\t}\n\n\tconst isStartInLimit = startLimitElement && !startLimitElement.is( 'rootElement' );\n\tconst isEndInLimit = endLimitElement && !endLimitElement.is( 'rootElement' );\n\n\t// At this point we eliminated valid positions on text nodes so if one of range positions is placed inside a limit element\n\t// then the range crossed limit element boundaries and needs to be fixed.\n\tif ( isStartInLimit || isEndInLimit ) {\n\t\tconst bothInSameParent = ( start.nodeAfter && end.nodeBefore ) && start.nodeAfter.parent === end.nodeBefore.parent;\n\n\t\tconst expandStart = isStartInLimit && ( !bothInSameParent || !isSelectable( start.nodeAfter, schema ) );\n\t\tconst expandEnd = isEndInLimit && ( !bothInSameParent || !isSelectable( end.nodeBefore, schema ) );\n\n\t\t// Although we've already found limit element on start/end positions we must find the outer-most limit element.\n\t\t// as limit elements might be nested directly inside (ie table > tableRow > tableCell).\n\t\tlet fixedStart = start;\n\t\tlet fixedEnd = end;\n\n\t\tif ( expandStart ) {\n\t\t\tfixedStart = Position._createBefore( findOutermostLimitAncestor( startLimitElement, schema ) );\n\t\t}\n\n\t\tif ( expandEnd ) {\n\t\t\tfixedEnd = Position._createAfter( findOutermostLimitAncestor( endLimitElement, schema ) );\n\t\t}\n\n\t\treturn new Range( fixedStart, fixedEnd );\n\t}\n\n\t// Range was not fixed at this point so it is valid - ie it was placed around limit element already.\n\treturn null;\n}\n\n// Finds the outer-most ancestor.\n//\n// @param {module:engine/model/node~Node} startingNode\n// @param {module:engine/model/schema~Schema} schema\n// @param {String} expandToDirection Direction of expansion - either 'start' or 'end' of the range.\n// @returns {module:engine/model/node~Node}\nfunction findOutermostLimitAncestor( startingNode, schema ) {\n\tlet isLimitNode = startingNode;\n\tlet parent = isLimitNode;\n\n\t// Find outer most isLimit block as such blocks might be nested (ie. in tables).\n\twhile ( schema.isLimit( parent ) && parent.parent ) {\n\t\tisLimitNode = parent;\n\t\tparent = parent.parent;\n\t}\n\n\treturn isLimitNode;\n}\n\n// Checks whether any of range boundaries is placed around non-limit elements.\n//\n// @param {module:engine/model/position~Position} start\n// @param {module:engine/model/position~Position} end\n// @param {module:engine/model/schema~Schema} schema\n// @returns {Boolean}\nfunction checkSelectionOnNonLimitElements( start, end, schema ) {\n\tconst startIsOnBlock = ( start.nodeAfter && !schema.isLimit( start.nodeAfter ) ) || schema.checkChild( start, '$text' );\n\tconst endIsOnBlock = ( end.nodeBefore && !schema.isLimit( end.nodeBefore ) ) || schema.checkChild( end, '$text' );\n\n\t// We should fix such selection when one of those nodes needs fixing.\n\treturn startIsOnBlock || endIsOnBlock;\n}\n\n// Returns a minimal non-intersecting array of ranges.\n//\n// @param {Array.} ranges\n// @returns {Array.}\nfunction mergeIntersectingRanges( ranges ) {\n\tconst nonIntersectingRanges = [];\n\n\t// First range will always be fine.\n\tnonIntersectingRanges.push( ranges.shift() );\n\n\tfor ( const range of ranges ) {\n\t\tconst previousRange = nonIntersectingRanges.pop();\n\n\t\tif ( range.isEqual( previousRange ) ) {\n\t\t\t// Use only one of two identical ranges.\n\t\t\tnonIntersectingRanges.push( previousRange );\n\t\t} else if ( range.isIntersecting( previousRange ) ) {\n\t\t\t// Get the sum of two ranges.\n\t\t\tconst start = previousRange.start.isAfter( range.start ) ? range.start : previousRange.start;\n\t\t\tconst end = previousRange.end.isAfter( range.end ) ? previousRange.end : range.end;\n\n\t\t\tconst merged = new Range( start, end );\n\t\t\tnonIntersectingRanges.push( merged );\n\t\t} else {\n\t\t\tnonIntersectingRanges.push( previousRange );\n\t\t\tnonIntersectingRanges.push( range );\n\t\t}\n\t}\n\n\treturn nonIntersectingRanges;\n}\n\n// Checks if node exists and if it's a selectable.\n//\n// @param {module:engine/model/node~Node} node\n// @param {module:engine/model/schema~Schema} schema\n// @returns {Boolean}\nfunction isSelectable( node, schema ) {\n\treturn node && schema.isSelectable( node );\n}\n"],"sourceRoot":""}