{"version":3,"sources":["webpack:///./node_modules/@ckeditor/ckeditor5-typing/src/utils/inlinehighlight.js","webpack:///./node_modules/@ckeditor/ckeditor5-typing/src/utils/changebuffer.js","webpack:///./node_modules/@ckeditor/ckeditor5-typing/src/utils/findattributerange.js","webpack:///./node_modules/@ckeditor/ckeditor5-typing/src/utils/utils.js","webpack:///./node_modules/@ckeditor/ckeditor5-typing/src/utils/injecttypingmutationshandling.js","webpack:///./node_modules/@ckeditor/ckeditor5-typing/src/utils/injectunsafekeystrokeshandling.js","webpack:///./node_modules/@ckeditor/ckeditor5-typing/src/utils/getlasttextline.js"],"names":["inlineHighlight","editor","attributeName","tagName","className","view","editing","highlightedElements","Set","document","registerPostFixer","writer","selection","model","changed","hasAttribute","_step","modelRange","findAttributeRange","getFirstPosition","getAttribute","viewRange","mapper","toViewRange","_iterator","_createForOfIteratorHelper","getItems","s","n","done","item","value","is","hasClass","addClass","add","err","e","f","conversion","for","dispatcher","removeHighlight","change","_step2","_iterator2","values","removeClass","delete","on","priority","ChangeBuffer","_this","this","limit","arguments","length","undefined","Object","D_Projects_UA_repo_Source_Client_UA_User_Web_node_modules_babel_runtime_helpers_esm_classCallCheck_js__WEBPACK_IMPORTED_MODULE_0__","size","isLocked","_changeCallback","evt","batch","type","_batch","_reset","_selectionChangeCallback","createBatch","changeCount","off","ignoreLock","position","createRange","_findBound","lookBack","node","textNode","nodeBefore","nodeAfter","lastNode","previousSibling","nextSibling","createPositionAt","__webpack_require__","d","__webpack_exports__","containerChildrenMutated","mutations","mutation","getSingleTextNodeChange","newChildren","oldChildren","diffResult","diff","compareChildNodes","changes","diffToChanges","oldChild","newChild","data","injectTypingMutationsHandling","viewSelection","MutationHandler","handle","classCallCheck","_handleContainerChildrenMutations","injecttypingmutationshandling_createForOfIteratorHelper","_handleTextMutation","_handleTextNodeInsertion","mutationsCommonAncestor","getMutationsContainer","domConverter","domMutationCommonAncestor","mapViewToDom","freshDomConverter","DomConverter","modelFromCurrentDom","toModel","domToView","getChild","currentModel","toModelElement","modelFromDomChildren","Array","from","getChildren","currentModelChildren","lastDomChild","lastCurrentChild","isLastDomChildSoftBreak","isLastCurrentChildSoftBreak","pop","schema","isSafeForTextMutation","newText","map","join","replace","oldText","_calculateChanges","calculateChanges","firstChangeAt","insertions","deletions","modelSelectionRange","toModelRange","getFirstRange","insertText","substr","removeRange","execute","text","range","resultRange","_calculateChanges2","viewPos","modelPos","toModelPosition","getShiftedBy","index","insertedText","lca","reduce","commonAncestor","getCommonAncestor","includeSelf","getAncestors","parentFirst","find","element","children","every","child","isInline","lastChangeAt","i","injectUnsafeKeystrokesHandling","latestCompositionSelection","inputCommand","commands","get","handleUnsafeKeystroke","evtData","doc","isComposing","isSelectionUnchanged","isEqual","isEnabled","isNonTypingKeystroke","isCollapsed","keyCode","deleteSelectionContent","handleCompositionStart","isFlatSelection","rangeCount","isFlat","buffer","lock","_batches","enqueueChange","deleteContent","unlock","env","isAndroid","createSelection","safeKeycodes","getCode","code","push","keyData","ctrlKey","metaKey","includes","getLastTextLine","start","rangeText","createPositionAfter","end"],"mappings":"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","file":"js/chunk-d14d9f84.bba33fa9.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\nimport findAttributeRange from './findattributerange';\n\n/**\n * @module typing/utils/inlinehighlight\n */\n\n/**\n * Adds a visual highlight style to an attribute element in which the selection is anchored.\n * Together with two-step caret movement, they indicate that the user is typing inside the element.\n *\n * Highlight is turned on by adding the given class to the attribute element in the view:\n *\n * * The class is removed before the conversion has started, as callbacks added with the `'highest'` priority\n * to {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher} events.\n * * The class is added in the view post fixer, after other changes in the model tree were converted to the view.\n *\n * This way, adding and removing the highlight does not interfere with conversion.\n *\n * Usage:\n *\n *\t\timport inlineHighlight from '@ckeditor/ckeditor5-typing/src/utils/inlinehighlight';\n *\n *\t\t// Make `ck-link_selected` class be applied on an `a` element\n *\t\t// whenever the corresponding `linkHref` attribute element is selected.\n *\t\tinlineHighlight( editor, 'linkHref', 'a', 'ck-link_selected' );\n *\n * @param {module:core/editor/editor~Editor} editor The editor instance.\n * @param {String} attributeName The attribute name to check.\n * @param {String} tagName The tagName of a view item.\n * @param {String} className The class name to apply in the view.\n */\nexport default function inlineHighlight( editor, attributeName, tagName, className ) {\n\tconst view = editor.editing.view;\n\tconst highlightedElements = new Set();\n\n\t// Adding the class.\n\tview.document.registerPostFixer( writer => {\n\t\tconst selection = editor.model.document.selection;\n\t\tlet changed = false;\n\n\t\tif ( selection.hasAttribute( attributeName ) ) {\n\t\t\tconst modelRange = findAttributeRange(\n\t\t\t\tselection.getFirstPosition(),\n\t\t\t\tattributeName,\n\t\t\t\tselection.getAttribute( attributeName ),\n\t\t\t\teditor.model\n\t\t\t);\n\t\t\tconst viewRange = editor.editing.mapper.toViewRange( modelRange );\n\n\t\t\t// There might be multiple view elements in the `viewRange`, for example, when the `a` element is\n\t\t\t// broken by a UIElement.\n\t\t\tfor ( const item of viewRange.getItems() ) {\n\t\t\t\tif ( item.is( 'element', tagName ) && !item.hasClass( className ) ) {\n\t\t\t\t\twriter.addClass( className, item );\n\t\t\t\t\thighlightedElements.add( item );\n\t\t\t\t\tchanged = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn changed;\n\t} );\n\n\t// Removing the class.\n\teditor.conversion.for( 'editingDowncast' ).add( dispatcher => {\n\t\t// Make sure the highlight is removed on every possible event, before conversion is started.\n\t\tdispatcher.on( 'insert', removeHighlight, { priority: 'highest' } );\n\t\tdispatcher.on( 'remove', removeHighlight, { priority: 'highest' } );\n\t\tdispatcher.on( 'attribute', removeHighlight, { priority: 'highest' } );\n\t\tdispatcher.on( 'selection', removeHighlight, { priority: 'highest' } );\n\n\t\tfunction removeHighlight() {\n\t\t\tview.change( writer => {\n\t\t\t\tfor ( const item of highlightedElements.values() ) {\n\t\t\t\t\twriter.removeClass( className, item );\n\t\t\t\t\thighlightedElements.delete( item );\n\t\t\t\t}\n\t\t\t} );\n\t\t}\n\t} );\n}\n","/**\n * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * @module typing/utils/changebuffer\n */\n\n/**\n * Change buffer allows to group atomic changes (like characters that have been typed) into\n * {@link module:engine/model/batch~Batch batches}.\n *\n * Batches represent single undo steps, hence changes added to one single batch are undone together.\n *\n * The buffer has a configurable limit of atomic changes that it can accommodate. After the limit was\n * exceeded (see {@link ~ChangeBuffer#input}), a new batch is created in {@link ~ChangeBuffer#batch}.\n *\n * To use the change buffer you need to let it know about the number of changes that were added to the batch:\n *\n *\t\tconst buffer = new ChangeBuffer( model, LIMIT );\n *\n *\t\t// Later on in your feature:\n *\t\tbuffer.batch.insert( pos, insertedCharacters );\n *\t\tbuffer.input( insertedCharacters.length );\n *\n */\nexport default class ChangeBuffer {\n\t/**\n\t * Creates a new instance of the change buffer.\n\t *\n\t * @param {module:engine/model/model~Model} model\n\t * @param {Number} [limit=20] The maximum number of atomic changes which can be contained in one batch.\n\t */\n\tconstructor( model, limit = 20 ) {\n\t\t/**\n\t\t * The model instance.\n\t\t *\n\t\t * @readonly\n\t\t * @member {module:engine/model/model~Model} #model\n\t\t */\n\t\tthis.model = model;\n\n\t\t/**\n\t\t * The number of atomic changes in the buffer. Once it exceeds the {@link #limit},\n\t\t * the {@link #batch batch} is set to a new one.\n\t\t *\n\t\t * @readonly\n\t\t * @member {Number} #size\n\t\t */\n\t\tthis.size = 0;\n\n\t\t/**\n\t\t * The maximum number of atomic changes which can be contained in one batch.\n\t\t *\n\t\t * @readonly\n\t\t * @member {Number} #limit\n\t\t */\n\t\tthis.limit = limit;\n\n\t\t/**\n\t\t * Whether the buffer is locked. A locked buffer cannot be reset unless it gets unlocked.\n\t\t *\n\t\t * @readonly\n\t\t * @member {Boolean} #isLocked\n\t\t */\n\t\tthis.isLocked = false;\n\n\t\t// The function to be called in order to notify the buffer about batches which appeared in the document.\n\t\t// The callback will check whether it is a new batch and in that case the buffer will be flushed.\n\t\t//\n\t\t// The reason why the buffer needs to be flushed whenever a new batch appears is that the changes added afterwards\n\t\t// should be added to a new batch. For instance, when the user types, then inserts an image, and then types again,\n\t\t// the characters typed after inserting the image should be added to a different batch than the characters typed before.\n\t\tthis._changeCallback = ( evt, batch ) => {\n\t\t\tif ( batch.type != 'transparent' && batch !== this._batch ) {\n\t\t\t\tthis._reset( true );\n\t\t\t}\n\t\t};\n\n\t\tthis._selectionChangeCallback = () => {\n\t\t\tthis._reset();\n\t\t};\n\n\t\tthis.model.document.on( 'change', this._changeCallback );\n\n\t\tthis.model.document.selection.on( 'change:range', this._selectionChangeCallback );\n\t\tthis.model.document.selection.on( 'change:attribute', this._selectionChangeCallback );\n\n\t\t/**\n\t\t * The current batch instance.\n\t\t *\n\t\t * @private\n\t\t * @member #_batch\n\t\t */\n\n\t\t/**\n\t\t * The callback to document the change event which later needs to be removed.\n\t\t *\n\t\t * @private\n\t\t * @member #_changeCallback\n\t\t */\n\n\t\t/**\n\t\t * The callback to document selection `change:attribute` and `change:range` events which resets the buffer.\n\t\t *\n\t\t * @private\n\t\t * @member #_selectionChangeCallback\n\t\t */\n\t}\n\n\t/**\n\t * The current batch to which a feature should add its operations. Once the {@link #size}\n\t * is reached or exceeds the {@link #limit}, the batch is set to a new instance and the size is reset.\n\t *\n\t * @type {module:engine/model/batch~Batch}\n\t */\n\tget batch() {\n\t\tif ( !this._batch ) {\n\t\t\tthis._batch = this.model.createBatch();\n\t\t}\n\n\t\treturn this._batch;\n\t}\n\n\t/**\n\t * The input number of changes into the buffer. Once the {@link #size} is\n\t * reached or exceeds the {@link #limit}, the batch is set to a new instance and the size is reset.\n\t *\n\t * @param {Number} changeCount The number of atomic changes to input.\n\t */\n\tinput( changeCount ) {\n\t\tthis.size += changeCount;\n\n\t\tif ( this.size >= this.limit ) {\n\t\t\tthis._reset( true );\n\t\t}\n\t}\n\n\t/**\n\t * Locks the buffer.\n\t */\n\tlock() {\n\t\tthis.isLocked = true;\n\t}\n\n\t/**\n\t * Unlocks the buffer.\n\t */\n\tunlock() {\n\t\tthis.isLocked = false;\n\t}\n\n\t/**\n\t * Destroys the buffer.\n\t */\n\tdestroy() {\n\t\tthis.model.document.off( 'change', this._changeCallback );\n\t\tthis.model.document.selection.off( 'change:range', this._selectionChangeCallback );\n\t\tthis.model.document.selection.off( 'change:attribute', this._selectionChangeCallback );\n\t}\n\n\t/**\n\t * Resets the change buffer.\n\t *\n\t * @private\n\t * @param {Boolean} [ignoreLock] Whether internal lock {@link #isLocked} should be ignored.\n\t */\n\t_reset( ignoreLock ) {\n\t\tif ( !this.isLocked || ignoreLock ) {\n\t\t\tthis._batch = null;\n\t\t\tthis.size = 0;\n\t\t}\n\t}\n}\n","/**\n * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * @module typing/utils/findattributerange\n */\n\n/**\n * Returns a model range that covers all consecutive nodes with the same `attributeName` and its `value`\n * that intersect the given `position`.\n *\n * It can be used e.g. to get the entire range on which the `linkHref` attribute needs to be changed when having a\n * selection inside a link.\n *\n * @param {module:engine/model/position~Position} position The start position.\n * @param {String} attributeName The attribute name.\n * @param {String} value The attribute value.\n * @param {module:engine/model/model~Model} model The model instance.\n * @returns {module:engine/model/range~Range} The link range.\n */\nexport default function findAttributeRange( position, attributeName, value, model ) {\n\treturn model.createRange(\n\t\t_findBound( position, attributeName, value, true, model ),\n\t\t_findBound( position, attributeName, value, false, model )\n\t);\n}\n\n// Walks forward or backward (depends on the `lookBack` flag), node by node, as long as they have the same attribute value\n// and returns a position just before or after (depends on the `lookBack` flag) the last matched node.\n//\n// @param {module:engine/model/position~Position} position The start position.\n// @param {String} attributeName The attribute name.\n// @param {String} value The attribute value.\n// @param {Boolean} lookBack Whether the walk direction is forward (`false`) or backward (`true`).\n// @returns {module:engine/model/position~Position} The position just before the last matched node.\nfunction _findBound( position, attributeName, value, lookBack, model ) {\n\t// Get node before or after position (depends on `lookBack` flag).\n\t// When position is inside text node then start searching from text node.\n\tlet node = position.textNode || ( lookBack ? position.nodeBefore : position.nodeAfter );\n\n\tlet lastNode = null;\n\n\twhile ( node && node.getAttribute( attributeName ) == value ) {\n\t\tlastNode = node;\n\t\tnode = lookBack ? node.previousSibling : node.nextSibling;\n\t}\n\n\treturn lastNode ? model.createPositionAt( lastNode, lookBack ? 'before' : 'after' ) : position;\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 typing/utils/utils\n */\n\nimport diff from '@ckeditor/ckeditor5-utils/src/diff';\nimport diffToChanges from '@ckeditor/ckeditor5-utils/src/difftochanges';\n\n/**\n * Returns true if container children have mutated or more than a single text node was changed.\n *\n * @private\n * @param {Array.} mutations\n * @returns {Boolean}\n */\nexport function containerChildrenMutated( mutations ) {\n\tif ( mutations.length == 0 ) {\n\t\treturn false;\n\t}\n\n\t// Check if there is any mutation of `children` type or any mutation that changes more than one text node.\n\tfor ( const mutation of mutations ) {\n\t\tif ( mutation.type === 'children' && !getSingleTextNodeChange( mutation ) ) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Returns change made to a single text node.\n *\n * @private\n * @param {module:engine/view/observer/mutationobserver~MutatedText|\n * module:engine/view/observer/mutationobserver~MutatedChildren} mutation\n * @returns {Object|undefined} Change object (see {@link module:utils/difftochanges~diffToChanges} output)\n * or undefined if more than a single text node was changed.\n */\nexport function getSingleTextNodeChange( mutation ) {\n\t// One new node.\n\tif ( mutation.newChildren.length - mutation.oldChildren.length != 1 ) {\n\t\treturn;\n\t}\n\n\t// Which is text.\n\tconst diffResult = diff( mutation.oldChildren, mutation.newChildren, compareChildNodes );\n\tconst changes = diffToChanges( diffResult, mutation.newChildren );\n\n\t// In case of [ delete, insert, insert ] the previous check will not exit.\n\tif ( changes.length > 1 ) {\n\t\treturn;\n\t}\n\n\tconst change = changes[ 0 ];\n\n\t// Which is text.\n\tif ( !( !!change.values[ 0 ] && change.values[ 0 ].is( '$text' ) ) ) {\n\t\treturn;\n\t}\n\n\treturn change;\n}\n\n/**\n * Checks whether two view nodes are identical, which means they are the same object\n * or contain exactly same data (in case of text nodes).\n *\n * @private\n * @param {module:engine/view/node~Node} oldChild\n * @param {module:engine/view/node~Node} newChild\n * @returns {Boolean}\n */\nexport function compareChildNodes( oldChild, newChild ) {\n\tif ( !!oldChild && oldChild.is( '$text' ) && !!newChild && newChild.is( '$text' ) ) {\n\t\treturn oldChild.data === newChild.data;\n\t} else {\n\t\treturn oldChild === newChild;\n\t}\n}\n","/**\n * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * @module typing/utils/injecttypingmutationshandling\n */\n\nimport diff from '@ckeditor/ckeditor5-utils/src/diff';\nimport DomConverter from '@ckeditor/ckeditor5-engine/src/view/domconverter';\n\nimport { getSingleTextNodeChange, containerChildrenMutated } from './utils';\n\n/**\n * Handles mutations caused by normal typing.\n *\n * @param {module:core/editor/editor~Editor} editor The editor instance.\n */\nexport default function injectTypingMutationsHandling( editor ) {\n\teditor.editing.view.document.on( 'mutations', ( evt, mutations, viewSelection ) => {\n\t\tnew MutationHandler( editor ).handle( mutations, viewSelection );\n\t} );\n}\n\n/**\n * Helper class for translating DOM mutations into model changes.\n *\n * @private\n */\nclass MutationHandler {\n\t/**\n\t * Creates an instance of the mutation handler.\n\t *\n\t * @param {module:core/editor/editor~Editor} editor\n\t */\n\tconstructor( editor ) {\n\t\t/**\n\t\t * Editor instance for which mutations are handled.\n\t\t *\n\t\t * @readonly\n\t\t * @member {module:core/editor/editor~Editor} #editor\n\t\t */\n\t\tthis.editor = editor;\n\n\t\t/**\n\t\t * The editing controller.\n\t\t *\n\t\t * @readonly\n\t\t * @member {module:engine/controller/editingcontroller~EditingController} #editing\n\t\t */\n\t\tthis.editing = this.editor.editing;\n\t}\n\n\t/**\n\t * Handles given mutations.\n\t *\n\t * @param {Array.} mutations\n\t * @param {module:engine/view/selection~Selection|null} viewSelection\n\t */\n\thandle( mutations, viewSelection ) {\n\t\tif ( containerChildrenMutated( mutations ) ) {\n\t\t\tthis._handleContainerChildrenMutations( mutations, viewSelection );\n\t\t} else {\n\t\t\tfor ( const mutation of mutations ) {\n\t\t\t\t// Fortunately it will never be both.\n\t\t\t\tthis._handleTextMutation( mutation, viewSelection );\n\t\t\t\tthis._handleTextNodeInsertion( mutation );\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Handles situations when container's children mutated during input. This can happen when\n\t * the browser is trying to \"fix\" DOM in certain situations. For example, when the user starts to type\n\t * in `

Link{}

`, the browser might change the order of elements\n\t * to `

Linkx{}

`. A similar situation happens when the spell checker\n\t * replaces a word wrapped with `` with a word wrapped with a `` element.\n\t *\n\t * To handle such situations, the common DOM ancestor of all mutations is converted to the model representation\n\t * and then compared with the current model to calculate the proper text change.\n\t *\n\t * Note: Single text node insertion is handled in {@link #_handleTextNodeInsertion} and text node mutation is handled\n\t * in {@link #_handleTextMutation}).\n\t *\n\t * @private\n\t * @param {Array.} mutations\n\t * @param {module:engine/view/selection~Selection|null} viewSelection\n\t */\n\t_handleContainerChildrenMutations( mutations, viewSelection ) {\n\t\t// Get common ancestor of all mutations.\n\t\tconst mutationsCommonAncestor = getMutationsContainer( mutations );\n\n\t\t// Quit if there is no common ancestor.\n\t\tif ( !mutationsCommonAncestor ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst domConverter = this.editor.editing.view.domConverter;\n\n\t\t// Get common ancestor in DOM.\n\t\tconst domMutationCommonAncestor = domConverter.mapViewToDom( mutationsCommonAncestor );\n\n\t\t// Create fresh DomConverter so it will not use existing mapping and convert current DOM to model.\n\t\t// This wouldn't be needed if DomConverter would allow to create fresh view without checking any mappings.\n\t\tconst freshDomConverter = new DomConverter( this.editor.editing.view.document );\n\t\tconst modelFromCurrentDom = this.editor.data.toModel(\n\t\t\tfreshDomConverter.domToView( domMutationCommonAncestor )\n\t\t).getChild( 0 );\n\n\t\t// Current model.\n\t\tconst currentModel = this.editor.editing.mapper.toModelElement( mutationsCommonAncestor );\n\n\t\t// If common ancestor is not mapped, do not do anything. It probably is a parent of another view element.\n\t\t// That means that we would need to diff model elements (see `if` below). Better return early instead of\n\t\t// trying to get a reasonable model ancestor. It will fell into the `if` below anyway.\n\t\t// This situation happens for example for lists. If `
    ` is a common ancestor, `currentModel` is `undefined`\n\t\t// because `
      ` is not mapped (`
    • `s are).\n\t\t// See https://github.com/ckeditor/ckeditor5/issues/718.\n\t\tif ( !currentModel ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Get children from both ancestors.\n\t\tconst modelFromDomChildren = Array.from( modelFromCurrentDom.getChildren() );\n\t\tconst currentModelChildren = Array.from( currentModel.getChildren() );\n\n\t\t// Remove the last `` from the end of `modelFromDomChildren` if there is no `` in current model.\n\t\t// If the described scenario happened, it means that this is a bogus `
      ` added by a browser.\n\t\tconst lastDomChild = modelFromDomChildren[ modelFromDomChildren.length - 1 ];\n\t\tconst lastCurrentChild = currentModelChildren[ currentModelChildren.length - 1 ];\n\n\t\tconst isLastDomChildSoftBreak = lastDomChild && lastDomChild.is( 'element', 'softBreak' );\n\t\tconst isLastCurrentChildSoftBreak = lastCurrentChild && !lastCurrentChild.is( 'element', 'softBreak' );\n\n\t\tif ( isLastDomChildSoftBreak && isLastCurrentChildSoftBreak ) {\n\t\t\tmodelFromDomChildren.pop();\n\t\t}\n\n\t\tconst schema = this.editor.model.schema;\n\n\t\t// Skip situations when common ancestor has any container elements.\n\t\tif ( !isSafeForTextMutation( modelFromDomChildren, schema ) || !isSafeForTextMutation( currentModelChildren, schema ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Replace   inserted by the browser with normal space. See comment in `_handleTextMutation`.\n\t\t// Replace non-texts with any character. This is potentially dangerous but passes in manual tests. The thing is\n\t\t// that we need to take care of proper indexes so we cannot simply remove non-text elements from the content.\n\t\t// By inserting a character we keep all the real texts on their indexes.\n\t\tconst newText = modelFromDomChildren.map( item => item.is( '$text' ) ? item.data : '@' ).join( '' ).replace( /\\u00A0/g, ' ' );\n\t\tconst oldText = currentModelChildren.map( item => item.is( '$text' ) ? item.data : '@' ).join( '' ).replace( /\\u00A0/g, ' ' );\n\n\t\t// Do nothing if mutations created same text.\n\t\tif ( oldText === newText ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst diffResult = diff( oldText, newText );\n\n\t\tconst { firstChangeAt, insertions, deletions } = calculateChanges( diffResult );\n\n\t\t// Try setting new model selection according to passed view selection.\n\t\tlet modelSelectionRange = null;\n\n\t\tif ( viewSelection ) {\n\t\t\tmodelSelectionRange = this.editing.mapper.toModelRange( viewSelection.getFirstRange() );\n\t\t}\n\n\t\tconst insertText = newText.substr( firstChangeAt, insertions );\n\t\tconst removeRange = this.editor.model.createRange(\n\t\t\tthis.editor.model.createPositionAt( currentModel, firstChangeAt ),\n\t\t\tthis.editor.model.createPositionAt( currentModel, firstChangeAt + deletions )\n\t\t);\n\n\t\tthis.editor.execute( 'input', {\n\t\t\ttext: insertText,\n\t\t\trange: removeRange,\n\t\t\tresultRange: modelSelectionRange\n\t\t} );\n\t}\n\n\t/**\n\t * @private\n\t */\n\t_handleTextMutation( mutation, viewSelection ) {\n\t\tif ( mutation.type != 'text' ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Replace   inserted by the browser with normal space.\n\t\t// We want only normal spaces in the model and in the view. Renderer and DOM Converter will be then responsible\n\t\t// for rendering consecutive spaces using  , but the model and the view has to be clear.\n\t\t// Other feature may introduce inserting non-breakable space on specific key stroke (for example shift + space).\n\t\t// However then it will be handled outside of mutations, like enter key is.\n\t\t// The replacing is here because it has to be done before `diff` and `diffToChanges` functions, as they\n\t\t// take `newText` and compare it to (cleaned up) view.\n\t\t// It could also be done in mutation observer too, however if any outside plugin would like to\n\t\t// introduce additional events for mutations, they would get already cleaned up version (this may be good or not).\n\t\tconst newText = mutation.newText.replace( /\\u00A0/g, ' ' );\n\t\t// To have correct `diffResult`, we also compare view node text data with   replaced by space.\n\t\tconst oldText = mutation.oldText.replace( /\\u00A0/g, ' ' );\n\n\t\t// Do nothing if mutations created same text.\n\t\tif ( oldText === newText ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst diffResult = diff( oldText, newText );\n\n\t\tconst { firstChangeAt, insertions, deletions } = calculateChanges( diffResult );\n\n\t\t// Try setting new model selection according to passed view selection.\n\t\tlet modelSelectionRange = null;\n\n\t\tif ( viewSelection ) {\n\t\t\tmodelSelectionRange = this.editing.mapper.toModelRange( viewSelection.getFirstRange() );\n\t\t}\n\n\t\t// Get the position in view and model where the changes will happen.\n\t\tconst viewPos = this.editing.view.createPositionAt( mutation.node, firstChangeAt );\n\t\tconst modelPos = this.editing.mapper.toModelPosition( viewPos );\n\t\tconst removeRange = this.editor.model.createRange( modelPos, modelPos.getShiftedBy( deletions ) );\n\t\tconst insertText = newText.substr( firstChangeAt, insertions );\n\n\t\tthis.editor.execute( 'input', {\n\t\t\ttext: insertText,\n\t\t\trange: removeRange,\n\t\t\tresultRange: modelSelectionRange\n\t\t} );\n\t}\n\n\t/**\n\t * @private\n\t */\n\t_handleTextNodeInsertion( mutation ) {\n\t\tif ( mutation.type != 'children' ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst change = getSingleTextNodeChange( mutation );\n\t\tconst viewPos = this.editing.view.createPositionAt( mutation.node, change.index );\n\t\tconst modelPos = this.editing.mapper.toModelPosition( viewPos );\n\t\tconst insertedText = change.values[ 0 ].data;\n\n\t\tthis.editor.execute( 'input', {\n\t\t\t// Replace   inserted by the browser with normal space.\n\t\t\t// See comment in `_handleTextMutation`.\n\t\t\t// In this case we don't need to do this before `diff` because we diff whole nodes.\n\t\t\t// Just change   in case there are some.\n\t\t\ttext: insertedText.replace( /\\u00A0/g, ' ' ),\n\t\t\trange: this.editor.model.createRange( modelPos )\n\t\t} );\n\t}\n}\n\n// Returns first common ancestor of all mutations that is either {@link module:engine/view/containerelement~ContainerElement}\n// or {@link module:engine/view/rootelement~RootElement}.\n//\n// @private\n// @param {Array.} mutations\n// @returns {module:engine/view/containerelement~ContainerElement|engine/view/rootelement~RootElement|undefined}\nfunction getMutationsContainer( mutations ) {\n\tconst lca = mutations\n\t\t.map( mutation => mutation.node )\n\t\t.reduce( ( commonAncestor, node ) => {\n\t\t\treturn commonAncestor.getCommonAncestor( node, { includeSelf: true } );\n\t\t} );\n\n\tif ( !lca ) {\n\t\treturn;\n\t}\n\n\t// We need to look for container and root elements only, so check all LCA's\n\t// ancestors (starting from itself).\n\treturn lca.getAncestors( { includeSelf: true, parentFirst: true } )\n\t\t.find( element => element.is( 'containerElement' ) || element.is( 'rootElement' ) );\n}\n\n// Returns true if provided array contains content that won't be problematic during diffing and text mutation handling.\n//\n// @param {Array.} children\n// @param {module:engine/model/schema~Schema} schema\n// @returns {Boolean}\nfunction isSafeForTextMutation( children, schema ) {\n\treturn children.every( child => schema.isInline( child ) );\n}\n\n// Calculates first change index and number of characters that should be inserted and deleted starting from that index.\n//\n// @private\n// @param diffResult\n// @returns {{insertions: number, deletions: number, firstChangeAt: *}}\nfunction calculateChanges( diffResult ) {\n\t// Index where the first change happens. Used to set the position from which nodes will be removed and where will be inserted.\n\tlet firstChangeAt = null;\n\t// Index where the last change happens. Used to properly count how many characters have to be removed and inserted.\n\tlet lastChangeAt = null;\n\n\t// Get `firstChangeAt` and `lastChangeAt`.\n\tfor ( let i = 0; i < diffResult.length; i++ ) {\n\t\tconst change = diffResult[ i ];\n\n\t\tif ( change != 'equal' ) {\n\t\t\tfirstChangeAt = firstChangeAt === null ? i : firstChangeAt;\n\t\t\tlastChangeAt = i;\n\t\t}\n\t}\n\n\t// How many characters, starting from `firstChangeAt`, should be removed.\n\tlet deletions = 0;\n\t// How many characters, starting from `firstChangeAt`, should be inserted.\n\tlet insertions = 0;\n\n\tfor ( let i = firstChangeAt; i <= lastChangeAt; i++ ) {\n\t\t// If there is no change (equal) or delete, the character is existing in `oldText`. We count it for removing.\n\t\tif ( diffResult[ i ] != 'insert' ) {\n\t\t\tdeletions++;\n\t\t}\n\n\t\t// If there is no change (equal) or insert, the character is existing in `newText`. We count it for inserting.\n\t\tif ( diffResult[ i ] != 'delete' ) {\n\t\t\tinsertions++;\n\t\t}\n\t}\n\n\treturn { insertions, deletions, firstChangeAt };\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 typing/utils/injectunsafekeystrokeshandling\n */\n\nimport { getCode } from '@ckeditor/ckeditor5-utils/src/keyboard';\nimport env from '@ckeditor/ckeditor5-utils/src/env';\n\n/**\n * Handles keystrokes which are unsafe for typing. This handler's logic is explained\n * in https://github.com/ckeditor/ckeditor5-typing/issues/83#issuecomment-398690251.\n *\n * @param {module:core/editor/editor~Editor} editor The editor instance.\n */\nexport default function injectUnsafeKeystrokesHandling( editor ) {\n\tlet latestCompositionSelection = null;\n\n\tconst model = editor.model;\n\tconst view = editor.editing.view;\n\tconst inputCommand = editor.commands.get( 'input' );\n\n\t// For Android, we want to handle keystrokes on `beforeinput` to be sure that code in `DeleteObserver` already had a chance to be fired.\n\tif ( env.isAndroid ) {\n\t\tview.document.on( 'beforeinput', ( evt, evtData ) => handleUnsafeKeystroke( evtData ), { priority: 'lowest' } );\n\t} else {\n\t\tview.document.on( 'keydown', ( evt, evtData ) => handleUnsafeKeystroke( evtData ), { priority: 'lowest' } );\n\t}\n\n\tview.document.on( 'compositionstart', handleCompositionStart, { priority: 'lowest' } );\n\n\tview.document.on( 'compositionend', () => {\n\t\tlatestCompositionSelection = model.createSelection( model.document.selection );\n\t}, { priority: 'lowest' } );\n\n\t// Handles the keydown event. We need to guess whether such keystroke is going to result\n\t// in typing. If so, then before character insertion happens, any selected content needs\n\t// to be deleted. Otherwise the default browser deletion mechanism would be\n\t// triggered, resulting in:\n\t//\n\t// * Hundreds of mutations which could not be handled.\n\t// * But most importantly, loss of control over how the content is being deleted.\n\t//\n\t// The method is used in a low-priority listener, hence allowing other listeners (e.g. delete or enter features)\n\t// to handle the event.\n\t//\n\t// @param {module:engine/view/observer/keyobserver~KeyEventData} evtData\n\tfunction handleUnsafeKeystroke( evtData ) {\n\t\tconst doc = model.document;\n\t\tconst isComposing = view.document.isComposing;\n\t\tconst isSelectionUnchanged = latestCompositionSelection && latestCompositionSelection.isEqual( doc.selection );\n\n\t\t// Reset stored composition selection.\n\t\tlatestCompositionSelection = null;\n\n\t\t// By relying on the state of the input command we allow disabling the entire input easily\n\t\t// by just disabling the input command. We could’ve used here the delete command but that\n\t\t// would mean requiring the delete feature which would block loading one without the other.\n\t\t// We could also check the editor.isReadOnly property, but that wouldn't allow to block\n\t\t// the input without blocking other features.\n\t\tif ( !inputCommand.isEnabled ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( isNonTypingKeystroke( evtData ) || doc.selection.isCollapsed ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// If during composition, deletion should be prevented as it may remove composed sequence (#83).\n\t\tif ( isComposing && evtData.keyCode === 229 ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// If there is a `keydown` event fired with '229' keycode it might be related\n\t\t// to recent composition. Check if selection is the same as upon ending recent composition,\n\t\t// if so do not remove selected content as it will remove composed sequence (#83).\n\t\tif ( !isComposing && evtData.keyCode === 229 && isSelectionUnchanged ) {\n\t\t\treturn;\n\t\t}\n\n\t\tdeleteSelectionContent();\n\t}\n\n\t// Handles the `compositionstart` event. It is used only in special cases to remove the contents\n\t// of a non-collapsed selection so composition itself does not result in complex mutations.\n\t//\n\t// The special case mentioned above is a situation in which the `keydown` event is fired after\n\t// `compositionstart` event. In such cases {@link #handleKeydown} cannot clear current selection\n\t// contents (because it is too late and will break the composition) so the composition handler takes care of it.\n\tfunction handleCompositionStart() {\n\t\tconst doc = model.document;\n\t\tconst isFlatSelection = doc.selection.rangeCount === 1 ? doc.selection.getFirstRange().isFlat : true;\n\n\t\t// If on `compositionstart` there is a non-collapsed selection which start and end have different parents\n\t\t// it means the `handleKeydown()` method did not remove its contents. It happens usually because\n\t\t// of different order of events (`compositionstart` before `keydown` - in Safari). In such cases\n\t\t// we need to remove selection contents on composition start (#83).\n\t\tif ( doc.selection.isCollapsed || isFlatSelection ) {\n\t\t\treturn;\n\t\t}\n\n\t\tdeleteSelectionContent();\n\t}\n\n\tfunction deleteSelectionContent() {\n\t\tconst buffer = inputCommand.buffer;\n\n\t\tbuffer.lock();\n\n\t\tconst batch = buffer.batch;\n\t\tinputCommand._batches.add( batch );\n\n\t\tmodel.enqueueChange( batch, () => {\n\t\t\tmodel.deleteContent( model.document.selection );\n\t\t} );\n\n\t\tbuffer.unlock();\n\t}\n}\n\nconst safeKeycodes = [\n\tgetCode( 'arrowUp' ),\n\tgetCode( 'arrowRight' ),\n\tgetCode( 'arrowDown' ),\n\tgetCode( 'arrowLeft' ),\n\t9, // Tab\n\t16, // Shift\n\t17, // Ctrl\n\t18, // Alt\n\t19, // Pause\n\t20, // CapsLock\n\t27, // Escape\n\t33, // PageUp\n\t34, // PageDown\n\t35, // Home\n\t36, // End,\n\t45, // Insert,\n\t91, // Windows,\n\t93, // Menu key,\n\t144, // NumLock\n\t145, // ScrollLock,\n\t173, // Mute/Unmute\n\t174, // Volume up\n\t175, // Volume down,\n\t176, // Next song,\n\t177, // Previous song,\n\t178, // Stop,\n\t179, // Play/Pause,\n\t255 // Display brightness (increase and decrease)\n];\n\n// Function keys.\nfor ( let code = 112; code <= 135; code++ ) {\n\tsafeKeycodes.push( code );\n}\n\n/**\n * Returns `true` if a keystroke will **not** result in \"typing\".\n *\n * For instance, keystrokes that result in typing are letters \"a-zA-Z\", numbers \"0-9\", delete, backspace, etc.\n *\n * Keystrokes that do not cause typing are, for instance, Fn keys (F5, F8, etc.), arrow keys (←, →, ↑, ↓),\n * Tab (↹), \"Windows logo key\" (⊞ Win), etc.\n *\n * Note: This implementation is very simple and will need to be refined with time.\n *\n * @param {module:engine/view/observer/keyobserver~KeyEventData} keyData\n * @returns {Boolean}\n */\nexport function isNonTypingKeystroke( keyData ) {\n\t// Keystrokes which contain Ctrl or Cmd don't represent typing.\n\tif ( keyData.ctrlKey || keyData.metaKey ) {\n\t\treturn true;\n\t}\n\n\treturn safeKeycodes.includes( keyData.keyCode );\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 typing/utils/getlasttextline\n */\n\n/**\n * Returns the last text line from the given range.\n *\n * \"The last text line\" is understood as text (from one or more text nodes) which is limited either by a parent block\n * or by inline elements (e.g. ``).\n *\n *\t\tconst rangeToCheck = model.createRange(\n *\t\t\tmodel.createPositionAt( paragraph, 0 ),\n *\t\t\tmodel.createPositionAt( paragraph, 'end' )\n *\t\t);\n *\n *\t\tconst { text, range } = getLastTextLine( rangeToCheck, model );\n *\n * For model below, the returned `text` will be \"Foo bar baz\" and `range` will be set on whole `` content:\n *\n *\t\tFoo bar baz\n *\n * However, in below case, `text` will be set to \"baz\" and `range` will be set only on \"baz\".\n *\n *\t\tFoobarbaz\n *\n * @protected\n * @param {module:engine/model/range~Range} range\n * @param {module:engine/model/model~Model} model\n * @returns {module:typing/utils/getlasttextline~LastTextLineData}\n */\nexport default function getLastTextLine( range, model ) {\n\tlet start = range.start;\n\n\tconst text = Array.from( range.getItems() ).reduce( ( rangeText, node ) => {\n\t\t// Trim text to a last occurrence of an inline element and update range start.\n\t\tif ( !( node.is( '$text' ) || node.is( '$textProxy' ) ) ) {\n\t\t\tstart = model.createPositionAfter( node );\n\n\t\t\treturn '';\n\t\t}\n\n\t\treturn rangeText + node.data;\n\t}, '' );\n\n\treturn { text, range: model.createRange( start, range.end ) };\n}\n\n/**\n * The value returned by {@link module:typing/utils/getlasttextline~getLastTextLine}.\n *\n * @typedef {Object} module:typing/utils/getlasttextline~LastTextLineData\n *\n * @property {String} text The text from the text nodes in the last text line.\n * @property {module:engine/model/range~Range} range The range set on the text nodes in the last text line.\n */\n"],"sourceRoot":""}