{"version":3,"sources":["webpack:///./node_modules/@ckeditor/ckeditor5-link/src/linkcommand.js","webpack:///./node_modules/@ckeditor/ckeditor5-link/src/linkediting.js"],"names":["LinkCommand","editor","_this","Object","classCallCheck","this","_super","call","manualDecorators","Collection","automaticDecorators","AutomaticDecorators","_step","_iterator","_createForOfIteratorHelper","s","n","done","manualDecorator","value","_getDecoratorStateFromModel","id","err","e","f","model","selection","document","selectedElement","getSelectedElement","first","getSelectedBlocks","isLinkableElement","schema","getAttribute","isEnabled","checkAttribute","checkAttributeInSelection","_step2","_iterator2","href","_this2","manualDecoratorIds","arguments","length","undefined","truthyManualDecorators","falsyManualDecorators","name","push","change","writer","isCollapsed","position","getFirstPosition","hasAttribute","linkRange","findAttributeRange","setAttribute","forEach","item","removeAttribute","setSelection","createPositionAfter","end","nodeBefore","attributes","toMap","getAttributes","set","_model$insertContent","insertContent","createText","positionAfter","concat","removeSelectionAttribute","_step3","ranges","getValidRanges","getRanges","allowedRanges","_iterator3","element","createRangeOn","_step4","rangesToUpdate","slice","_iterator4","range","_isRangeToUpdate","_step5","_iterator5","_loop","decoratorName","_step6","_iterator6","allowedRange","containsRange","Command","HIGHLIGHT_CLASS","DECORATOR_AUTOMATIC","DECORATOR_MANUAL","EXTERNAL_LINKS_REGEXP","LinkEditing","config","define","addTargetToExternalLinks","extend","allowAttributes","conversion","for","attributeToElement","view","createLinkElement","conversionApi","ensureSafeUrl","elementToAttribute","key","viewElement","commands","add","UnlinkCommand","linkDecorators","getLocalizedDecorators","t","normalizeDecorators","get","_enableAutomaticDecorators","filter","mode","_enableManualDecorators","twoStepCaretMovementPlugin","plugins","TwoStepCaretMovement","registerAttribute","inlineHighlight","_enableInsertContentSelectionAttributesFixer","_enableClickingAfterLink","_enableTypingOverLink","_handleDeleteContentAfterLink","automaticDecoratorDefinitions","command","callback","url","test","target","rel","getDispatcher","manualDecoratorDefinitions","decorator","ManualDecorator","manualDecoratorName","_ref","createAttributeElement","priority","classes","addClass","styles","setStyle","setCustomProperty","_objectSpread","_createPattern","listenTo","anchor","nodeAfter","removeLinkAttributesFromSelection","getLinkAttributesAllowedOnText","editing","addObserver","MouseObserver","clicked","isTouching","start","selectionAttributes","deletedContent","isTyping","shouldCopyAttributes","evt","_ref2","_ref3","slicedToArray","linkediting_createForOfIteratorHelper","_step$value","attribute","shouldPreserveAttributes","hasBackspacePressed","data","domEvent","keyCode","keyCodes","backspace","linkHref","containsPosition","isEqual","enqueueChange","Input","ClipboardPipeline","Plugin","linkAttributes","firstPosition","lastPosition","getLastPosition","nodeAtFirstPosition","is","nodeAtLastPosition","textNode","createRange","input","isInput","batch","textAttributes","getDefinition","startsWith"],"mappings":"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","file":"js/chunk-2d21f0d8.de39ccce.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 link/linkcommand\n */\n\nimport { Command } from 'ckeditor5/src/core';\nimport { findAttributeRange } from 'ckeditor5/src/typing';\nimport { Collection, first, toMap } from 'ckeditor5/src/utils';\n\nimport AutomaticDecorators from './utils/automaticdecorators';\nimport { isLinkableElement } from './utils';\n\n/**\n * The link command. It is used by the {@link module:link/link~Link link feature}.\n *\n * @extends module:core/command~Command\n */\nexport default class LinkCommand extends Command {\n\t/**\n\t * The value of the `'linkHref'` attribute if the start of the selection is located in a node with this attribute.\n\t *\n\t * @observable\n\t * @readonly\n\t * @member {Object|undefined} #value\n\t */\n\n\tconstructor( editor ) {\n\t\tsuper( editor );\n\n\t\t/**\n\t\t * A collection of {@link module:link/utils~ManualDecorator manual decorators}\n\t\t * corresponding to the {@link module:link/link~LinkConfig#decorators decorator configuration}.\n\t\t *\n\t\t * You can consider it a model with states of manual decorators added to the currently selected link.\n\t\t *\n\t\t * @readonly\n\t\t * @type {module:utils/collection~Collection}\n\t\t */\n\t\tthis.manualDecorators = new Collection();\n\n\t\t/**\n\t\t * An instance of the helper that ties together all {@link module:link/link~LinkDecoratorAutomaticDefinition}\n\t\t * that are used by the {@glink features/link link} and the {@glink features/images/images-linking linking images} features.\n\t\t *\n\t\t * @readonly\n\t\t * @type {module:link/utils~AutomaticDecorators}\n\t\t */\n\t\tthis.automaticDecorators = new AutomaticDecorators();\n\t}\n\n\t/**\n\t * Synchronizes the state of {@link #manualDecorators} with the currently present elements in the model.\n\t */\n\trestoreManualDecoratorStates() {\n\t\tfor ( const manualDecorator of this.manualDecorators ) {\n\t\t\tmanualDecorator.value = this._getDecoratorStateFromModel( manualDecorator.id );\n\t\t}\n\t}\n\n\t/**\n\t * @inheritDoc\n\t */\n\trefresh() {\n\t\tconst model = this.editor.model;\n\t\tconst selection = model.document.selection;\n\t\tconst selectedElement = selection.getSelectedElement() || first( selection.getSelectedBlocks() );\n\n\t\t// A check for any integration that allows linking elements (e.g. `LinkImage`).\n\t\t// Currently the selection reads attributes from text nodes only. See #7429 and #7465.\n\t\tif ( isLinkableElement( selectedElement, model.schema ) ) {\n\t\t\tthis.value = selectedElement.getAttribute( 'linkHref' );\n\t\t\tthis.isEnabled = model.schema.checkAttribute( selectedElement, 'linkHref' );\n\t\t} else {\n\t\t\tthis.value = selection.getAttribute( 'linkHref' );\n\t\t\tthis.isEnabled = model.schema.checkAttributeInSelection( selection, 'linkHref' );\n\t\t}\n\n\t\tfor ( const manualDecorator of this.manualDecorators ) {\n\t\t\tmanualDecorator.value = this._getDecoratorStateFromModel( manualDecorator.id );\n\t\t}\n\t}\n\n\t/**\n\t * Executes the command.\n\t *\n\t * When the selection is non-collapsed, the `linkHref` attribute will be applied to nodes inside the selection, but only to\n\t * those nodes where the `linkHref` attribute is allowed (disallowed nodes will be omitted).\n\t *\n\t * When the selection is collapsed and is not inside the text with the `linkHref` attribute, a\n\t * new {@link module:engine/model/text~Text text node} with the `linkHref` attribute will be inserted in place of the caret, but\n\t * only if such element is allowed in this place. The `_data` of the inserted text will equal the `href` parameter.\n\t * The selection will be updated to wrap the just inserted text node.\n\t *\n\t * When the selection is collapsed and inside the text with the `linkHref` attribute, the attribute value will be updated.\n\t *\n\t * # Decorators and model attribute management\n\t *\n\t * There is an optional argument to this command that applies or removes model\n\t * {@glink framework/guides/architecture/editing-engine#text-attributes text attributes} brought by\n\t * {@link module:link/utils~ManualDecorator manual link decorators}.\n\t *\n\t * Text attribute names in the model correspond to the entries in the {@link module:link/link~LinkConfig#decorators configuration}.\n\t * For every decorator configured, a model text attribute exists with the \"link\" prefix. For example, a `'linkMyDecorator'` attribute\n\t * corresponds to `'myDecorator'` in the configuration.\n\t *\n\t * To learn more about link decorators, check out the {@link module:link/link~LinkConfig#decorators `config.link.decorators`}\n\t * documentation.\n\t *\n\t * Here is how to manage decorator attributes with the link command:\n\t *\n\t *\t\tconst linkCommand = editor.commands.get( 'link' );\n\t *\n\t *\t\t// Adding a new decorator attribute.\n\t *\t\tlinkCommand.execute( 'http://example.com', {\n\t *\t\t\tlinkIsExternal: true\n\t *\t\t} );\n\t *\n\t *\t\t// Removing a decorator attribute from the selection.\n\t *\t\tlinkCommand.execute( 'http://example.com', {\n\t *\t\t\tlinkIsExternal: false\n\t *\t\t} );\n\t *\n\t *\t\t// Adding multiple decorator attributes at the same time.\n\t *\t\tlinkCommand.execute( 'http://example.com', {\n\t *\t\t\tlinkIsExternal: true,\n\t *\t\t\tlinkIsDownloadable: true,\n\t *\t\t} );\n\t *\n\t *\t\t// Removing and adding decorator attributes at the same time.\n\t *\t\tlinkCommand.execute( 'http://example.com', {\n\t *\t\t\tlinkIsExternal: false,\n\t *\t\t\tlinkFoo: true,\n\t *\t\t\tlinkIsDownloadable: false,\n\t *\t\t} );\n\t *\n\t * **Note**: If the decorator attribute name is not specified, its state remains untouched.\n\t *\n\t * **Note**: {@link module:link/unlinkcommand~UnlinkCommand#execute `UnlinkCommand#execute()`} removes all\n\t * decorator attributes.\n\t *\n\t * @fires execute\n\t * @param {String} href Link destination.\n\t * @param {Object} [manualDecoratorIds={}] The information about manual decorator attributes to be applied or removed upon execution.\n\t */\n\texecute( href, manualDecoratorIds = {} ) {\n\t\tconst model = this.editor.model;\n\t\tconst selection = model.document.selection;\n\t\t// Stores information about manual decorators to turn them on/off when command is applied.\n\t\tconst truthyManualDecorators = [];\n\t\tconst falsyManualDecorators = [];\n\n\t\tfor ( const name in manualDecoratorIds ) {\n\t\t\tif ( manualDecoratorIds[ name ] ) {\n\t\t\t\ttruthyManualDecorators.push( name );\n\t\t\t} else {\n\t\t\t\tfalsyManualDecorators.push( name );\n\t\t\t}\n\t\t}\n\n\t\tmodel.change( writer => {\n\t\t\t// If selection is collapsed then update selected link or insert new one at the place of caret.\n\t\t\tif ( selection.isCollapsed ) {\n\t\t\t\tconst position = selection.getFirstPosition();\n\n\t\t\t\t// When selection is inside text with `linkHref` attribute.\n\t\t\t\tif ( selection.hasAttribute( 'linkHref' ) ) {\n\t\t\t\t\t// Then update `linkHref` value.\n\t\t\t\t\tconst linkRange = findAttributeRange( position, 'linkHref', selection.getAttribute( 'linkHref' ), model );\n\n\t\t\t\t\twriter.setAttribute( 'linkHref', href, linkRange );\n\n\t\t\t\t\ttruthyManualDecorators.forEach( item => {\n\t\t\t\t\t\twriter.setAttribute( item, true, linkRange );\n\t\t\t\t\t} );\n\n\t\t\t\t\tfalsyManualDecorators.forEach( item => {\n\t\t\t\t\t\twriter.removeAttribute( item, linkRange );\n\t\t\t\t\t} );\n\n\t\t\t\t\t// Put the selection at the end of the updated link.\n\t\t\t\t\twriter.setSelection( writer.createPositionAfter( linkRange.end.nodeBefore ) );\n\t\t\t\t}\n\t\t\t\t// If not then insert text node with `linkHref` attribute in place of caret.\n\t\t\t\t// However, since selection is collapsed, attribute value will be used as data for text node.\n\t\t\t\t// So, if `href` is empty, do not create text node.\n\t\t\t\telse if ( href !== '' ) {\n\t\t\t\t\tconst attributes = toMap( selection.getAttributes() );\n\n\t\t\t\t\tattributes.set( 'linkHref', href );\n\n\t\t\t\t\ttruthyManualDecorators.forEach( item => {\n\t\t\t\t\t\tattributes.set( item, true );\n\t\t\t\t\t} );\n\n\t\t\t\t\tconst { end: positionAfter } = model.insertContent( writer.createText( href, attributes ), position );\n\n\t\t\t\t\t// Put the selection at the end of the inserted link.\n\t\t\t\t\t// Using end of range returned from insertContent in case nodes with the same attributes got merged.\n\t\t\t\t\twriter.setSelection( positionAfter );\n\t\t\t\t}\n\n\t\t\t\t// Remove the `linkHref` attribute and all link decorators from the selection.\n\t\t\t\t// It stops adding a new content into the link element.\n\t\t\t\t[ 'linkHref', ...truthyManualDecorators, ...falsyManualDecorators ].forEach( item => {\n\t\t\t\t\twriter.removeSelectionAttribute( item );\n\t\t\t\t} );\n\t\t\t} else {\n\t\t\t\t// If selection has non-collapsed ranges, we change attribute on nodes inside those ranges\n\t\t\t\t// omitting nodes where the `linkHref` attribute is disallowed.\n\t\t\t\tconst ranges = model.schema.getValidRanges( selection.getRanges(), 'linkHref' );\n\n\t\t\t\t// But for the first, check whether the `linkHref` attribute is allowed on selected blocks (e.g. the \"image\" element).\n\t\t\t\tconst allowedRanges = [];\n\n\t\t\t\tfor ( const element of selection.getSelectedBlocks() ) {\n\t\t\t\t\tif ( model.schema.checkAttribute( element, 'linkHref' ) ) {\n\t\t\t\t\t\tallowedRanges.push( writer.createRangeOn( element ) );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Ranges that accept the `linkHref` attribute. Since we will iterate over `allowedRanges`, let's clone it.\n\t\t\t\tconst rangesToUpdate = allowedRanges.slice();\n\n\t\t\t\t// For all selection ranges we want to check whether given range is inside an element that accepts the `linkHref` attribute.\n\t\t\t\t// If so, we don't want to propagate applying the attribute to its children.\n\t\t\t\tfor ( const range of ranges ) {\n\t\t\t\t\tif ( this._isRangeToUpdate( range, allowedRanges ) ) {\n\t\t\t\t\t\trangesToUpdate.push( range );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor ( const range of rangesToUpdate ) {\n\t\t\t\t\twriter.setAttribute( 'linkHref', href, range );\n\n\t\t\t\t\ttruthyManualDecorators.forEach( item => {\n\t\t\t\t\t\twriter.setAttribute( item, true, range );\n\t\t\t\t\t} );\n\n\t\t\t\t\tfalsyManualDecorators.forEach( item => {\n\t\t\t\t\t\twriter.removeAttribute( item, range );\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t}\n\n\t/**\n\t * Provides information whether a decorator with a given name is present in the currently processed selection.\n\t *\n\t * @private\n\t * @param {String} decoratorName The name of the manual decorator used in the model\n\t * @returns {Boolean} The information whether a given decorator is currently present in the selection.\n\t */\n\t_getDecoratorStateFromModel( decoratorName ) {\n\t\tconst model = this.editor.model;\n\t\tconst selection = model.document.selection;\n\t\tconst selectedElement = selection.getSelectedElement();\n\n\t\t// A check for the `LinkImage` plugin. If the selection contains an element, get values from the element.\n\t\t// Currently the selection reads attributes from text nodes only. See #7429 and #7465.\n\t\tif ( isLinkableElement( selectedElement, model.schema ) ) {\n\t\t\treturn selectedElement.getAttribute( decoratorName );\n\t\t}\n\n\t\treturn selection.getAttribute( decoratorName );\n\t}\n\n\t/**\n\t * Checks whether specified `range` is inside an element that accepts the `linkHref` attribute.\n\t *\n\t * @private\n\t * @param {module:engine/view/range~Range} range A range to check.\n\t * @param {Array.} allowedRanges An array of ranges created on elements where the attribute is accepted.\n\t * @returns {Boolean}\n\t */\n\t_isRangeToUpdate( range, allowedRanges ) {\n\t\tfor ( const allowedRange of allowedRanges ) {\n\t\t\t// A range is inside an element that will have the `linkHref` attribute. Do not modify its nodes.\n\t\t\tif ( allowedRange.containsRange( range ) ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\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 link/linkediting\n */\n\nimport { Plugin } from 'ckeditor5/src/core';\nimport { MouseObserver } from 'ckeditor5/src/engine';\nimport { Input, TwoStepCaretMovement, inlineHighlight, findAttributeRange } from 'ckeditor5/src/typing';\nimport { ClipboardPipeline } from 'ckeditor5/src/clipboard';\nimport { keyCodes } from 'ckeditor5/src/utils';\n\nimport LinkCommand from './linkcommand';\nimport UnlinkCommand from './unlinkcommand';\nimport ManualDecorator from './utils/manualdecorator';\nimport { createLinkElement, ensureSafeUrl, getLocalizedDecorators, normalizeDecorators } from './utils';\n\nimport '../theme/link.css';\n\nconst HIGHLIGHT_CLASS = 'ck-link_selected';\nconst DECORATOR_AUTOMATIC = 'automatic';\nconst DECORATOR_MANUAL = 'manual';\nconst EXTERNAL_LINKS_REGEXP = /^(https?:)?\\/\\//;\n\n/**\n * The link engine feature.\n *\n * It introduces the `linkHref=\"url\"` attribute in the model which renders to the view as a `` element\n * as well as `'link'` and `'unlink'` commands.\n *\n * @extends module:core/plugin~Plugin\n */\nexport default class LinkEditing extends Plugin {\n\t/**\n\t * @inheritDoc\n\t */\n\tstatic get pluginName() {\n\t\treturn 'LinkEditing';\n\t}\n\n\t/**\n\t * @inheritDoc\n\t */\n\tstatic get requires() {\n\t\t// Clipboard is required for handling cut and paste events while typing over the link.\n\t\treturn [ TwoStepCaretMovement, Input, ClipboardPipeline ];\n\t}\n\n\t/**\n\t * @inheritDoc\n\t */\n\tconstructor( editor ) {\n\t\tsuper( editor );\n\n\t\teditor.config.define( 'link', {\n\t\t\taddTargetToExternalLinks: false\n\t\t} );\n\t}\n\n\t/**\n\t * @inheritDoc\n\t */\n\tinit() {\n\t\tconst editor = this.editor;\n\n\t\t// Allow link attribute on all inline nodes.\n\t\teditor.model.schema.extend( '$text', { allowAttributes: 'linkHref' } );\n\n\t\teditor.conversion.for( 'dataDowncast' )\n\t\t\t.attributeToElement( { model: 'linkHref', view: createLinkElement } );\n\n\t\teditor.conversion.for( 'editingDowncast' )\n\t\t\t.attributeToElement( { model: 'linkHref', view: ( href, conversionApi ) => {\n\t\t\t\treturn createLinkElement( ensureSafeUrl( href ), conversionApi );\n\t\t\t} } );\n\n\t\teditor.conversion.for( 'upcast' )\n\t\t\t.elementToAttribute( {\n\t\t\t\tview: {\n\t\t\t\t\tname: 'a',\n\t\t\t\t\tattributes: {\n\t\t\t\t\t\thref: true\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tmodel: {\n\t\t\t\t\tkey: 'linkHref',\n\t\t\t\t\tvalue: viewElement => viewElement.getAttribute( 'href' )\n\t\t\t\t}\n\t\t\t} );\n\n\t\t// Create linking commands.\n\t\teditor.commands.add( 'link', new LinkCommand( editor ) );\n\t\teditor.commands.add( 'unlink', new UnlinkCommand( editor ) );\n\n\t\tconst linkDecorators = getLocalizedDecorators( editor.t, normalizeDecorators( editor.config.get( 'link.decorators' ) ) );\n\n\t\tthis._enableAutomaticDecorators( linkDecorators.filter( item => item.mode === DECORATOR_AUTOMATIC ) );\n\t\tthis._enableManualDecorators( linkDecorators.filter( item => item.mode === DECORATOR_MANUAL ) );\n\n\t\t// Enable two-step caret movement for `linkHref` attribute.\n\t\tconst twoStepCaretMovementPlugin = editor.plugins.get( TwoStepCaretMovement );\n\t\ttwoStepCaretMovementPlugin.registerAttribute( 'linkHref' );\n\n\t\t// Setup highlight over selected link.\n\t\tinlineHighlight( editor, 'linkHref', 'a', HIGHLIGHT_CLASS );\n\n\t\t// Change the attributes of the selection in certain situations after the link was inserted into the document.\n\t\tthis._enableInsertContentSelectionAttributesFixer();\n\n\t\t// Handle a click at the beginning/end of a link element.\n\t\tthis._enableClickingAfterLink();\n\n\t\t// Handle typing over the link.\n\t\tthis._enableTypingOverLink();\n\n\t\t// Handle removing the content after the link element.\n\t\tthis._handleDeleteContentAfterLink();\n\t}\n\n\t/**\n\t * Processes an array of configured {@link module:link/link~LinkDecoratorAutomaticDefinition automatic decorators}\n\t * and registers a {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher downcast dispatcher}\n\t * for each one of them. Downcast dispatchers are obtained using the\n\t * {@link module:link/utils~AutomaticDecorators#getDispatcher} method.\n\t *\n\t * **Note**: This method also activates the automatic external link decorator if enabled with\n\t * {@link module:link/link~LinkConfig#addTargetToExternalLinks `config.link.addTargetToExternalLinks`}.\n\t *\n\t * @private\n\t * @param {Array.} automaticDecoratorDefinitions\n\t */\n\t_enableAutomaticDecorators( automaticDecoratorDefinitions ) {\n\t\tconst editor = this.editor;\n\t\t// Store automatic decorators in the command instance as we do the same with manual decorators.\n\t\t// Thanks to that, `LinkImageEditing` plugin can re-use the same definitions.\n\t\tconst command = editor.commands.get( 'link' );\n\t\tconst automaticDecorators = command.automaticDecorators;\n\n\t\t// Adds a default decorator for external links.\n\t\tif ( editor.config.get( 'link.addTargetToExternalLinks' ) ) {\n\t\t\tautomaticDecorators.add( {\n\t\t\t\tid: 'linkIsExternal',\n\t\t\t\tmode: DECORATOR_AUTOMATIC,\n\t\t\t\tcallback: url => EXTERNAL_LINKS_REGEXP.test( url ),\n\t\t\t\tattributes: {\n\t\t\t\t\ttarget: '_blank',\n\t\t\t\t\trel: 'noopener noreferrer'\n\t\t\t\t}\n\t\t\t} );\n\t\t}\n\n\t\tautomaticDecorators.add( automaticDecoratorDefinitions );\n\n\t\tif ( automaticDecorators.length ) {\n\t\t\teditor.conversion.for( 'downcast' ).add( automaticDecorators.getDispatcher() );\n\t\t}\n\t}\n\n\t/**\n\t * Processes an array of configured {@link module:link/link~LinkDecoratorManualDefinition manual decorators},\n\t * transforms them into {@link module:link/utils~ManualDecorator} instances and stores them in the\n\t * {@link module:link/linkcommand~LinkCommand#manualDecorators} collection (a model for manual decorators state).\n\t *\n\t * Also registers an {@link module:engine/conversion/downcasthelpers~DowncastHelpers#attributeToElement attribute-to-element}\n\t * converter for each manual decorator and extends the {@link module:engine/model/schema~Schema model's schema}\n\t * with adequate model attributes.\n\t *\n\t * @private\n\t * @param {Array.} manualDecoratorDefinitions\n\t */\n\t_enableManualDecorators( manualDecoratorDefinitions ) {\n\t\tif ( !manualDecoratorDefinitions.length ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst editor = this.editor;\n\t\tconst command = editor.commands.get( 'link' );\n\t\tconst manualDecorators = command.manualDecorators;\n\n\t\tmanualDecoratorDefinitions.forEach( decorator => {\n\t\t\teditor.model.schema.extend( '$text', { allowAttributes: decorator.id } );\n\n\t\t\t// Keeps reference to manual decorator to decode its name to attributes during downcast.\n\t\t\tdecorator = new ManualDecorator( decorator );\n\n\t\t\tmanualDecorators.add( decorator );\n\n\t\t\teditor.conversion.for( 'downcast' ).attributeToElement( {\n\t\t\t\tmodel: decorator.id,\n\t\t\t\tview: ( manualDecoratorName, { writer } ) => {\n\t\t\t\t\tif ( manualDecoratorName ) {\n\t\t\t\t\t\tconst element = writer.createAttributeElement( 'a', decorator.attributes, { priority: 5 } );\n\n\t\t\t\t\t\tif ( decorator.classes ) {\n\t\t\t\t\t\t\twriter.addClass( decorator.classes, element );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfor ( const key in decorator.styles ) {\n\t\t\t\t\t\t\twriter.setStyle( key, decorator.styles[ key ], element );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\twriter.setCustomProperty( 'link', true, element );\n\n\t\t\t\t\t\treturn element;\n\t\t\t\t\t}\n\t\t\t\t} } );\n\n\t\t\teditor.conversion.for( 'upcast' ).elementToAttribute( {\n\t\t\t\tview: {\n\t\t\t\t\tname: 'a',\n\t\t\t\t\t...decorator._createPattern()\n\t\t\t\t},\n\t\t\t\tmodel: {\n\t\t\t\t\tkey: decorator.id\n\t\t\t\t}\n\t\t\t} );\n\t\t} );\n\t}\n\n\t/**\n\t * Starts listening to {@link module:engine/model/model~Model#event:insertContent} and corrects the model\n\t * selection attributes if the selection is at the end of a link after inserting the content.\n\t *\n\t * The purpose of this action is to improve the overall UX because the user is no longer \"trapped\" by the\n\t * `linkHref` attribute of the selection and they can type a \"clean\" (`linkHref`–less) text right away.\n\t *\n\t * See https://github.com/ckeditor/ckeditor5/issues/6053.\n\t *\n\t * @private\n\t */\n\t_enableInsertContentSelectionAttributesFixer() {\n\t\tconst editor = this.editor;\n\t\tconst model = editor.model;\n\t\tconst selection = model.document.selection;\n\n\t\tthis.listenTo( model, 'insertContent', () => {\n\t\t\tconst nodeBefore = selection.anchor.nodeBefore;\n\t\t\tconst nodeAfter = selection.anchor.nodeAfter;\n\n\t\t\t// NOTE: ↰ and ↱ represent the gravity of the selection.\n\n\t\t\t// The only truly valid case is:\n\t\t\t//\n\t\t\t//\t\t ↰\n\t\t\t//\t\t...<$text linkHref=\"foo\">INSERTED[]\n\t\t\t//\n\t\t\t// If the selection is not \"trapped\" by the `linkHref` attribute after inserting, there's nothing\n\t\t\t// to fix there.\n\t\t\tif ( !selection.hasAttribute( 'linkHref' ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Filter out the following case where a link with the same href (e.g. INSERTED) is inserted\n\t\t\t// in the middle of an existing link:\n\t\t\t//\n\t\t\t// Before insertion:\n\t\t\t//\t\t ↰\n\t\t\t//\t\t<$text linkHref=\"foo\">l[]ink\n\t\t\t//\n\t\t\t// Expected after insertion:\n\t\t\t//\t\t ↰\n\t\t\t//\t\t<$text linkHref=\"foo\">lINSERTED[]ink\n\t\t\t//\n\t\t\tif ( !nodeBefore ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Filter out the following case where the selection has the \"linkHref\" attribute because the\n\t\t\t// gravity is overridden and some text with another attribute (e.g. INSERTED) is inserted:\n\t\t\t//\n\t\t\t// Before insertion:\n\t\t\t//\n\t\t\t//\t\t ↱\n\t\t\t//\t\t<$text linkHref=\"foo\">[]link\n\t\t\t//\n\t\t\t// Expected after insertion:\n\t\t\t//\n\t\t\t//\t\t ↱\n\t\t\t//\t\t<$text bold=\"true\">INSERTED<$text linkHref=\"foo\">[]link\n\t\t\t//\n\t\t\tif ( !nodeBefore.hasAttribute( 'linkHref' ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Filter out the following case where a link is a inserted in the middle (or before) another link\n\t\t\t// (different URLs, so they will not merge). In this (let's say weird) case, we can leave the selection\n\t\t\t// attributes as they are because the user will end up writing in one link or another anyway.\n\t\t\t//\n\t\t\t// Before insertion:\n\t\t\t//\n\t\t\t//\t\t ↰\n\t\t\t//\t\t<$text linkHref=\"foo\">l[]ink\n\t\t\t//\n\t\t\t// Expected after insertion:\n\t\t\t//\n\t\t\t//\t\t ↰\n\t\t\t//\t\t<$text linkHref=\"foo\">l<$text linkHref=\"bar\">INSERTED[]<$text linkHref=\"foo\">ink\n\t\t\t//\n\t\t\tif ( nodeAfter && nodeAfter.hasAttribute( 'linkHref' ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tmodel.change( writer => {\n\t\t\t\tremoveLinkAttributesFromSelection( writer, getLinkAttributesAllowedOnText( model.schema ) );\n\t\t\t} );\n\t\t}, { priority: 'low' } );\n\t}\n\n\t/**\n\t * Starts listening to {@link module:engine/view/document~Document#event:mousedown} and\n\t * {@link module:engine/view/document~Document#event:selectionChange} and puts the selection before/after a link node\n\t * if clicked at the beginning/ending of the link.\n\t *\n\t * The purpose of this action is to allow typing around the link node directly after a click.\n\t *\n\t * See https://github.com/ckeditor/ckeditor5/issues/1016.\n\t *\n\t * @private\n\t */\n\t_enableClickingAfterLink() {\n\t\tconst editor = this.editor;\n\t\tconst model = editor.model;\n\n\t\teditor.editing.view.addObserver( MouseObserver );\n\n\t\tlet clicked = false;\n\n\t\t// Detect the click.\n\t\tthis.listenTo( editor.editing.view.document, 'mousedown', () => {\n\t\t\tclicked = true;\n\t\t} );\n\n\t\t// When the selection has changed...\n\t\tthis.listenTo( editor.editing.view.document, 'selectionChange', () => {\n\t\t\tif ( !clicked ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ...and it was caused by the click...\n\t\t\tclicked = false;\n\n\t\t\tconst selection = model.document.selection;\n\n\t\t\t// ...and no text is selected...\n\t\t\tif ( !selection.isCollapsed ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ...and clicked text is the link...\n\t\t\tif ( !selection.hasAttribute( 'linkHref' ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst position = selection.getFirstPosition();\n\t\t\tconst linkRange = findAttributeRange( position, 'linkHref', selection.getAttribute( 'linkHref' ), model );\n\n\t\t\t// ...check whether clicked start/end boundary of the link.\n\t\t\t// If so, remove the `linkHref` attribute.\n\t\t\tif ( position.isTouching( linkRange.start ) || position.isTouching( linkRange.end ) ) {\n\t\t\t\tmodel.change( writer => {\n\t\t\t\t\tremoveLinkAttributesFromSelection( writer, getLinkAttributesAllowedOnText( model.schema ) );\n\t\t\t\t} );\n\t\t\t}\n\t\t} );\n\t}\n\n\t/**\n\t * Starts listening to {@link module:engine/model/model~Model#deleteContent} and {@link module:engine/model/model~Model#insertContent}\n\t * and checks whether typing over the link. If so, attributes of removed text are preserved and applied to the inserted text.\n\t *\n\t * The purpose of this action is to allow modifying a text without loosing the `linkHref` attribute (and other).\n\t *\n\t * See https://github.com/ckeditor/ckeditor5/issues/4762.\n\t *\n\t * @private\n\t */\n\t_enableTypingOverLink() {\n\t\tconst editor = this.editor;\n\t\tconst view = editor.editing.view;\n\n\t\t// Selection attributes when started typing over the link.\n\t\tlet selectionAttributes;\n\n\t\t// Whether pressed `Backspace` or `Delete`. If so, attributes should not be preserved.\n\t\tlet deletedContent;\n\n\t\t// Detect pressing `Backspace` / `Delete`.\n\t\tthis.listenTo( view.document, 'delete', () => {\n\t\t\tdeletedContent = true;\n\t\t}, { priority: 'high' } );\n\n\t\t// Listening to `model#deleteContent` allows detecting whether selected content was a link.\n\t\t// If so, before removing the element, we will copy its attributes.\n\t\tthis.listenTo( editor.model, 'deleteContent', () => {\n\t\t\tconst selection = editor.model.document.selection;\n\n\t\t\t// Copy attributes only if anything is selected.\n\t\t\tif ( selection.isCollapsed ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// When the content was deleted, do not preserve attributes.\n\t\t\tif ( deletedContent ) {\n\t\t\t\tdeletedContent = false;\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Enabled only when typing.\n\t\t\tif ( !isTyping( editor ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ( shouldCopyAttributes( editor.model ) ) {\n\t\t\t\tselectionAttributes = selection.getAttributes();\n\t\t\t}\n\t\t}, { priority: 'high' } );\n\n\t\t// Listening to `model#insertContent` allows detecting the content insertion.\n\t\t// We want to apply attributes that were removed while typing over the link.\n\t\tthis.listenTo( editor.model, 'insertContent', ( evt, [ element ] ) => {\n\t\t\tdeletedContent = false;\n\n\t\t\t// Enabled only when typing.\n\t\t\tif ( !isTyping( editor ) ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ( !selectionAttributes ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\teditor.model.change( writer => {\n\t\t\t\tfor ( const [ attribute, value ] of selectionAttributes ) {\n\t\t\t\t\twriter.setAttribute( attribute, value, element );\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\tselectionAttributes = null;\n\t\t}, { priority: 'high' } );\n\t}\n\n\t/**\n\t * Starts listening to {@link module:engine/model/model~Model#deleteContent} and checks whether\n\t * removing a content right after the \"linkHref\" attribute.\n\t *\n\t * If so, the selection should not preserve the `linkHref` attribute. However, if\n\t * the {@link module:typing/twostepcaretmovement~TwoStepCaretMovement} plugin is active and\n\t * the selection has the \"linkHref\" attribute due to overriden gravity (at the end), the `linkHref` attribute should stay untouched.\n\t *\n\t * The purpose of this action is to allow removing the link text and keep the selection outside the link.\n\t *\n\t * See https://github.com/ckeditor/ckeditor5/issues/7521.\n\t *\n\t * @private\n\t */\n\t_handleDeleteContentAfterLink() {\n\t\tconst editor = this.editor;\n\t\tconst model = editor.model;\n\t\tconst selection = model.document.selection;\n\t\tconst view = editor.editing.view;\n\n\t\t// A flag whether attributes `linkHref` attribute should be preserved.\n\t\tlet shouldPreserveAttributes = false;\n\n\t\t// A flag whether the `Backspace` key was pressed.\n\t\tlet hasBackspacePressed = false;\n\n\t\t// Detect pressing `Backspace`.\n\t\tthis.listenTo( view.document, 'delete', ( evt, data ) => {\n\t\t\thasBackspacePressed = data.domEvent.keyCode === keyCodes.backspace;\n\t\t}, { priority: 'high' } );\n\n\t\t// Before removing the content, check whether the selection is inside a link or at the end of link but with 2-SCM enabled.\n\t\t// If so, we want to preserve link attributes.\n\t\tthis.listenTo( model, 'deleteContent', () => {\n\t\t\t// Reset the state.\n\t\t\tshouldPreserveAttributes = false;\n\n\t\t\tconst position = selection.getFirstPosition();\n\t\t\tconst linkHref = selection.getAttribute( 'linkHref' );\n\n\t\t\tif ( !linkHref ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst linkRange = findAttributeRange( position, 'linkHref', linkHref, model );\n\n\t\t\t// Preserve `linkHref` attribute if the selection is in the middle of the link or\n\t\t\t// the selection is at the end of the link and 2-SCM is activated.\n\t\t\tshouldPreserveAttributes = linkRange.containsPosition( position ) || linkRange.end.isEqual( position );\n\t\t}, { priority: 'high' } );\n\n\t\t// After removing the content, check whether the current selection should preserve the `linkHref` attribute.\n\t\tthis.listenTo( model, 'deleteContent', () => {\n\t\t\t// If didn't press `Backspace`.\n\t\t\tif ( !hasBackspacePressed ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\thasBackspacePressed = false;\n\n\t\t\t// Disable the mechanism if inside a link (`<$text url=\"foo\">F[]oo` or <$text url=\"foo\">Foo[]`).\n\t\t\tif ( shouldPreserveAttributes ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Use `model.enqueueChange()` in order to execute the callback at the end of the changes process.\n\t\t\teditor.model.enqueueChange( writer => {\n\t\t\t\tremoveLinkAttributesFromSelection( writer, getLinkAttributesAllowedOnText( model.schema ) );\n\t\t\t} );\n\t\t}, { priority: 'low' } );\n\t}\n}\n\n// Make the selection free of link-related model attributes.\n// All link-related model attributes start with \"link\". That includes not only \"linkHref\"\n// but also all decorator attributes (they have dynamic names), or even custom plugins.\n//\n// @param {module:engine/model/writer~Writer} writer\n// @param {Array.} linkAttributes\nfunction removeLinkAttributesFromSelection( writer, linkAttributes ) {\n\twriter.removeSelectionAttribute( 'linkHref' );\n\n\tfor ( const attribute of linkAttributes ) {\n\t\twriter.removeSelectionAttribute( attribute );\n\t}\n}\n\n// Checks whether selection's attributes should be copied to the new inserted text.\n//\n// @param {module:engine/model/model~Model} model\n// @returns {Boolean}\nfunction shouldCopyAttributes( model ) {\n\tconst selection = model.document.selection;\n\tconst firstPosition = selection.getFirstPosition();\n\tconst lastPosition = selection.getLastPosition();\n\tconst nodeAtFirstPosition = firstPosition.nodeAfter;\n\n\t// The text link node does not exist...\n\tif ( !nodeAtFirstPosition ) {\n\t\treturn false;\n\t}\n\n\t// ...or it isn't the text node...\n\tif ( !nodeAtFirstPosition.is( '$text' ) ) {\n\t\treturn false;\n\t}\n\n\t// ...or isn't the link.\n\tif ( !nodeAtFirstPosition.hasAttribute( 'linkHref' ) ) {\n\t\treturn false;\n\t}\n\n\t// `textNode` = the position is inside the link element.\n\t// `nodeBefore` = the position is at the end of the link element.\n\tconst nodeAtLastPosition = lastPosition.textNode || lastPosition.nodeBefore;\n\n\t// If both references the same node selection contains a single text node.\n\tif ( nodeAtFirstPosition === nodeAtLastPosition ) {\n\t\treturn true;\n\t}\n\n\t// If nodes are not equal, maybe the link nodes has defined additional attributes inside.\n\t// First, we need to find the entire link range.\n\tconst linkRange = findAttributeRange( firstPosition, 'linkHref', nodeAtFirstPosition.getAttribute( 'linkHref' ), model );\n\n\t// Then we can check whether selected range is inside the found link range. If so, attributes should be preserved.\n\treturn linkRange.containsRange( model.createRange( firstPosition, lastPosition ), true );\n}\n\n// Checks whether provided changes were caused by typing.\n//\n// @params {module:core/editor/editor~Editor} editor\n// @returns {Boolean}\nfunction isTyping( editor ) {\n\tconst input = editor.plugins.get( 'Input' );\n\n\treturn input.isInput( editor.model.change( writer => writer.batch ) );\n}\n\n// Returns an array containing names of the attributes allowed on `$text` that describes the link item.\n//\n// @param {module:engine/model/schema~Schema} schema\n// @returns {Array.}\nfunction getLinkAttributesAllowedOnText( schema ) {\n\tconst textAttributes = schema.getDefinition( '$text' ).allowAttributes;\n\n\treturn textAttributes.filter( attribute => attribute.startsWith( 'link' ) );\n}\n"],"sourceRoot":""}