Ver Fonte

Move the link highlight code to the -typing,

rename findLinkRange to findAttributeRange,
so it could be shared between attributes.
Tomek Wytrębowicz há 5 anos atrás
pai
commit
a6d3f01369

+ 2 - 2
packages/ckeditor5-link/src/linkcommand.js

@@ -8,7 +8,7 @@
  */
  */
 
 
 import Command from '@ckeditor/ckeditor5-core/src/command';
 import Command from '@ckeditor/ckeditor5-core/src/command';
-import findLinkRange from './findlinkrange';
+import findAttributeRange from '@ckeditor/ckeditor5-typing/src/utils/findattributerange';
 import toMap from '@ckeditor/ckeditor5-utils/src/tomap';
 import toMap from '@ckeditor/ckeditor5-utils/src/tomap';
 import Collection from '@ckeditor/ckeditor5-utils/src/collection';
 import Collection from '@ckeditor/ckeditor5-utils/src/collection';
 import first from '@ckeditor/ckeditor5-utils/src/first';
 import first from '@ckeditor/ckeditor5-utils/src/first';
@@ -160,7 +160,7 @@ export default class LinkCommand extends Command {
 				// When selection is inside text with `linkHref` attribute.
 				// When selection is inside text with `linkHref` attribute.
 				if ( selection.hasAttribute( 'linkHref' ) ) {
 				if ( selection.hasAttribute( 'linkHref' ) ) {
 					// Then update `linkHref` value.
 					// Then update `linkHref` value.
-					const linkRange = findLinkRange( position, selection.getAttribute( 'linkHref' ), model );
+					const linkRange = findAttributeRange( position, selection.getAttribute( 'linkHref' ), model );
 
 
 					writer.setAttribute( 'linkHref', href, linkRange );
 					writer.setAttribute( 'linkHref', href, linkRange );
 
 

+ 4 - 64
packages/ckeditor5-link/src/linkediting.js

@@ -10,11 +10,12 @@
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 import MouseObserver from '@ckeditor/ckeditor5-engine/src/view/observer/mouseobserver';
 import MouseObserver from '@ckeditor/ckeditor5-engine/src/view/observer/mouseobserver';
 import TwoStepCaretMovement from '@ckeditor/ckeditor5-typing/src/twostepcaretmovement';
 import TwoStepCaretMovement from '@ckeditor/ckeditor5-typing/src/twostepcaretmovement';
+import setupLinkHighlight from '@ckeditor/ckeditor5-typing/src/utils/inlinehighlight';
 import LinkCommand from './linkcommand';
 import LinkCommand from './linkcommand';
 import UnlinkCommand from './unlinkcommand';
 import UnlinkCommand from './unlinkcommand';
 import AutomaticDecorators from './utils/automaticdecorators';
 import AutomaticDecorators from './utils/automaticdecorators';
 import ManualDecorator from './utils/manualdecorator';
 import ManualDecorator from './utils/manualdecorator';
-import findLinkRange from './findlinkrange';
+import findAttributeRange from '@ckeditor/ckeditor5-typing/src/utils/findattributerange';
 import { createLinkElement, ensureSafeUrl, getLocalizedDecorators, normalizeDecorators } from './utils';
 import { createLinkElement, ensureSafeUrl, getLocalizedDecorators, normalizeDecorators } from './utils';
 
 
 import '../theme/link.css';
 import '../theme/link.css';
@@ -103,7 +104,7 @@ export default class LinkEditing extends Plugin {
 		twoStepCaretMovementPlugin.registerAttribute( 'linkHref' );
 		twoStepCaretMovementPlugin.registerAttribute( 'linkHref' );
 
 
 		// Setup highlight over selected link.
 		// Setup highlight over selected link.
-		this._setupLinkHighlight();
+		setupLinkHighlight( editor, HIGHLIGHT_CLASS );
 
 
 		// Change the attributes of the selection in certain situations after the link was inserted into the document.
 		// Change the attributes of the selection in certain situations after the link was inserted into the document.
 		this._enableInsertContentSelectionAttributesFixer();
 		this._enableInsertContentSelectionAttributesFixer();
@@ -199,67 +200,6 @@ export default class LinkEditing extends Plugin {
 		} );
 		} );
 	}
 	}
 
 
-	/**
-	 * Adds a visual highlight style to a link in which the selection is anchored.
-	 * Together with two-step caret movement, they indicate that the user is typing inside the link.
-	 *
-	 * Highlight is turned on by adding the `.ck-link_selected` class to the link in the view:
-	 *
-	 * * The class is removed before the conversion has started, as callbacks added with the `'highest'` priority
-	 * to {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher} events.
-	 * * The class is added in the view post fixer, after other changes in the model tree were converted to the view.
-	 *
-	 * This way, adding and removing the highlight does not interfere with conversion.
-	 *
-	 * @private
-	 */
-	_setupLinkHighlight() {
-		const editor = this.editor;
-		const view = editor.editing.view;
-		const highlightedLinks = new Set();
-
-		// Adding the class.
-		view.document.registerPostFixer( writer => {
-			const selection = editor.model.document.selection;
-			let changed = false;
-
-			if ( selection.hasAttribute( 'linkHref' ) ) {
-				const modelRange = findLinkRange( selection.getFirstPosition(), selection.getAttribute( 'linkHref' ), editor.model );
-				const viewRange = editor.editing.mapper.toViewRange( modelRange );
-
-				// There might be multiple `a` elements in the `viewRange`, for example, when the `a` element is
-				// broken by a UIElement.
-				for ( const item of viewRange.getItems() ) {
-					if ( item.is( 'a' ) && !item.hasClass( HIGHLIGHT_CLASS ) ) {
-						writer.addClass( HIGHLIGHT_CLASS, item );
-						highlightedLinks.add( item );
-						changed = true;
-					}
-				}
-			}
-
-			return changed;
-		} );
-
-		// Removing the class.
-		editor.conversion.for( 'editingDowncast' ).add( dispatcher => {
-			// Make sure the highlight is removed on every possible event, before conversion is started.
-			dispatcher.on( 'insert', removeHighlight, { priority: 'highest' } );
-			dispatcher.on( 'remove', removeHighlight, { priority: 'highest' } );
-			dispatcher.on( 'attribute', removeHighlight, { priority: 'highest' } );
-			dispatcher.on( 'selection', removeHighlight, { priority: 'highest' } );
-
-			function removeHighlight() {
-				view.change( writer => {
-					for ( const item of highlightedLinks.values() ) {
-						writer.removeClass( HIGHLIGHT_CLASS, item );
-						highlightedLinks.delete( item );
-					}
-				} );
-			}
-		} );
-	}
-
 	/**
 	/**
 	 * Starts listening to {@link module:engine/model/model~Model#event:insertContent} and corrects the model
 	 * Starts listening to {@link module:engine/model/model~Model#event:insertContent} and corrects the model
 	 * selection attributes if the selection is at the end of a link after inserting the content.
 	 * selection attributes if the selection is at the end of a link after inserting the content.
@@ -399,7 +339,7 @@ export default class LinkEditing extends Plugin {
 			}
 			}
 
 
 			const position = selection.getFirstPosition();
 			const position = selection.getFirstPosition();
-			const linkRange = findLinkRange( position, selection.getAttribute( 'linkHref' ), editor.model );
+			const linkRange = findAttributeRange( position, selection.getAttribute( 'linkHref' ), editor.model );
 
 
 			// ...check whether clicked start/end boundary of the link.
 			// ...check whether clicked start/end boundary of the link.
 			// If so, remove the `linkHref` attribute.
 			// If so, remove the `linkHref` attribute.

+ 2 - 2
packages/ckeditor5-link/src/unlinkcommand.js

@@ -8,7 +8,7 @@
  */
  */
 
 
 import Command from '@ckeditor/ckeditor5-core/src/command';
 import Command from '@ckeditor/ckeditor5-core/src/command';
-import findLinkRange from './findlinkrange';
+import findAttributeRange from '@ckeditor/ckeditor5-typing/src/utils/findattributerange';
 import first from '@ckeditor/ckeditor5-utils/src/first';
 import first from '@ckeditor/ckeditor5-utils/src/first';
 
 
 /**
 /**
@@ -57,7 +57,7 @@ export default class UnlinkCommand extends Command {
 		model.change( writer => {
 		model.change( writer => {
 			// Get ranges to unlink.
 			// Get ranges to unlink.
 			const rangesToUnlink = selection.isCollapsed ?
 			const rangesToUnlink = selection.isCollapsed ?
-				[ findLinkRange( selection.getFirstPosition(), selection.getAttribute( 'linkHref' ), model ) ] : selection.getRanges();
+				[ findAttributeRange( selection.getFirstPosition(), selection.getAttribute( 'linkHref' ), model ) ] : selection.getRanges();
 
 
 			// Remove `linkHref` attribute from specified ranges.
 			// Remove `linkHref` attribute from specified ranges.
 			for ( const range of rangesToUnlink ) {
 			for ( const range of rangesToUnlink ) {

+ 2 - 2
packages/ckeditor5-link/src/findlinkrange.js → packages/ckeditor5-typing/src/utils/findattributerange.js

@@ -4,7 +4,7 @@
  */
  */
 
 
 /**
 /**
- * @module link/findlinkrange
+ * @module typing/utils/findattributerange
  */
  */
 
 
 /**
 /**
@@ -17,7 +17,7 @@
  * @param {String} value The `linkHref` attribute value.
  * @param {String} value The `linkHref` attribute value.
  * @returns {module:engine/model/range~Range} The link range.
  * @returns {module:engine/model/range~Range} The link range.
  */
  */
-export default function findLinkRange( position, value, model ) {
+export default function findAttributeRange( position, value, model ) {
 	return model.createRange( _findBound( position, value, true, model ), _findBound( position, value, false, model ) );
 	return model.createRange( _findBound( position, value, true, model ), _findBound( position, value, false, model ) );
 }
 }
 
 

+ 70 - 0
packages/ckeditor5-typing/src/utils/inlinehighlight.js

@@ -0,0 +1,70 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+import findAttributeRange from './findattributerange';
+
+/**
+ * @module typing/utils/inlinehighlight
+ */
+
+/**
+ * Adds a visual highlight style to a link in which the selection is anchored.
+ * Together with two-step caret movement, they indicate that the user is typing inside the link.
+ *
+ * Highlight is turned on by adding the given class to the link in the view:
+ *
+ * * The class is removed before the conversion has started, as callbacks added with the `'highest'` priority
+ * to {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher} events.
+ * * The class is added in the view post fixer, after other changes in the model tree were converted to the view.
+ *
+ * This way, adding and removing the highlight does not interfere with conversion.
+ *
+ * @param {String} className The class name to apply in the view.
+ */
+export default function setupLinkHighlight( editor, className ) {
+	const view = editor.editing.view;
+	const highlightedLinks = new Set();
+
+	// Adding the class.
+	view.document.registerPostFixer( writer => {
+		const selection = editor.model.document.selection;
+		let changed = false;
+
+		if ( selection.hasAttribute( 'linkHref' ) ) {
+			const modelRange = findAttributeRange( selection.getFirstPosition(), selection.getAttribute( 'linkHref' ), editor.model );
+			const viewRange = editor.editing.mapper.toViewRange( modelRange );
+
+			// There might be multiple `a` elements in the `viewRange`, for example, when the `a` element is
+			// broken by a UIElement.
+			for ( const item of viewRange.getItems() ) {
+				if ( item.is( 'a' ) && !item.hasClass( className ) ) {
+					writer.addClass( className, item );
+					highlightedLinks.add( item );
+					changed = true;
+				}
+			}
+		}
+
+		return changed;
+	} );
+
+	// Removing the class.
+	editor.conversion.for( 'editingDowncast' ).add( dispatcher => {
+		// Make sure the highlight is removed on every possible event, before conversion is started.
+		dispatcher.on( 'insert', removeHighlight, { priority: 'highest' } );
+		dispatcher.on( 'remove', removeHighlight, { priority: 'highest' } );
+		dispatcher.on( 'attribute', removeHighlight, { priority: 'highest' } );
+		dispatcher.on( 'selection', removeHighlight, { priority: 'highest' } );
+
+		function removeHighlight() {
+			view.change( writer => {
+				for ( const item of highlightedLinks.values() ) {
+					writer.removeClass( className, item );
+					highlightedLinks.delete( item );
+				}
+			} );
+		}
+	} );
+}

+ 12 - 12
packages/ckeditor5-link/tests/findlinkrange.js → packages/ckeditor5-typing/tests/utils/findattributerange.js

@@ -3,12 +3,12 @@
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
  */
 
 
-import findLinkRange from '../src/findlinkrange';
+import findAttributeRange from '../../src/utils/findattributerange';
 import Model from '@ckeditor/ckeditor5-engine/src/model/model';
 import Model from '@ckeditor/ckeditor5-engine/src/model/model';
 import Range from '@ckeditor/ckeditor5-engine/src/model/range';
 import Range from '@ckeditor/ckeditor5-engine/src/model/range';
 import { setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
 import { setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
 
 
-describe( 'findLinkRange', () => {
+describe( 'findAttributeRange', () => {
 	let model, document, root;
 	let model, document, root;
 
 
 	beforeEach( () => {
 	beforeEach( () => {
@@ -23,7 +23,7 @@ describe( 'findLinkRange', () => {
 		setData( model, '<$text linkHref="url">foobar</$text>' );
 		setData( model, '<$text linkHref="url">foobar</$text>' );
 
 
 		const startPosition = model.createPositionAt( root, [ 3 ] );
 		const startPosition = model.createPositionAt( root, [ 3 ] );
-		const result = findLinkRange( startPosition, 'url', model );
+		const result = findAttributeRange( startPosition, 'url', model );
 
 
 		expect( result ).to.instanceOf( Range );
 		expect( result ).to.instanceOf( Range );
 		expect( result.isEqual( model.createRange( model.createPositionAt( root, 0 ), model.createPositionAt( root, 6 ) ) ) ).to.true;
 		expect( result.isEqual( model.createRange( model.createPositionAt( root, 0 ), model.createPositionAt( root, 6 ) ) ) ).to.true;
@@ -33,7 +33,7 @@ describe( 'findLinkRange', () => {
 		setData( model, 'abc <$text linkHref="url">foobar</$text> abc' );
 		setData( model, 'abc <$text linkHref="url">foobar</$text> abc' );
 
 
 		const startPosition = model.createPositionAt( root, [ 7 ] );
 		const startPosition = model.createPositionAt( root, [ 7 ] );
-		const result = findLinkRange( startPosition, 'url', model );
+		const result = findAttributeRange( startPosition, 'url', model );
 
 
 		expect( result ).to.instanceOf( Range );
 		expect( result ).to.instanceOf( Range );
 		expect( result.isEqual( model.createRange( model.createPositionAt( root, 4 ), model.createPositionAt( root, 10 ) ) ) ).to.true;
 		expect( result.isEqual( model.createRange( model.createPositionAt( root, 4 ), model.createPositionAt( root, 10 ) ) ) ).to.true;
@@ -43,7 +43,7 @@ describe( 'findLinkRange', () => {
 		setData( model, '<$text linkHref="url">foobar</$text>' );
 		setData( model, '<$text linkHref="url">foobar</$text>' );
 
 
 		const startPosition = model.createPositionAt( root, [ 0 ] );
 		const startPosition = model.createPositionAt( root, [ 0 ] );
-		const result = findLinkRange( startPosition, 'url', model );
+		const result = findAttributeRange( startPosition, 'url', model );
 
 
 		expect( result ).to.instanceOf( Range );
 		expect( result ).to.instanceOf( Range );
 		expect( result.isEqual( model.createRange( model.createPositionAt( root, 0 ), model.createPositionAt( root, 6 ) ) ) ).to.true;
 		expect( result.isEqual( model.createRange( model.createPositionAt( root, 0 ), model.createPositionAt( root, 6 ) ) ) ).to.true;
@@ -53,7 +53,7 @@ describe( 'findLinkRange', () => {
 		setData( model, 'abc <$text linkHref="url">foobar</$text> abc' );
 		setData( model, 'abc <$text linkHref="url">foobar</$text> abc' );
 
 
 		const startPosition = model.createPositionAt( root, [ 4 ] );
 		const startPosition = model.createPositionAt( root, [ 4 ] );
-		const result = findLinkRange( startPosition, 'url', model );
+		const result = findAttributeRange( startPosition, 'url', model );
 
 
 		expect( result ).to.instanceOf( Range );
 		expect( result ).to.instanceOf( Range );
 		expect( result.isEqual( model.createRange( model.createPositionAt( root, 4 ), model.createPositionAt( root, 10 ) ) ) ).to.true;
 		expect( result.isEqual( model.createRange( model.createPositionAt( root, 4 ), model.createPositionAt( root, 10 ) ) ) ).to.true;
@@ -63,7 +63,7 @@ describe( 'findLinkRange', () => {
 		setData( model, '<$text linkHref="url">foobar</$text>' );
 		setData( model, '<$text linkHref="url">foobar</$text>' );
 
 
 		const startPosition = model.createPositionAt( root, [ 6 ] );
 		const startPosition = model.createPositionAt( root, [ 6 ] );
-		const result = findLinkRange( startPosition, 'url', model );
+		const result = findAttributeRange( startPosition, 'url', model );
 
 
 		expect( result ).to.instanceOf( Range );
 		expect( result ).to.instanceOf( Range );
 		expect( result.isEqual( model.createRange( model.createPositionAt( root, 0 ), model.createPositionAt( root, 6 ) ) ) ).to.true;
 		expect( result.isEqual( model.createRange( model.createPositionAt( root, 0 ), model.createPositionAt( root, 6 ) ) ) ).to.true;
@@ -73,7 +73,7 @@ describe( 'findLinkRange', () => {
 		setData( model, 'abc <$text linkHref="url">foobar</$text> abc' );
 		setData( model, 'abc <$text linkHref="url">foobar</$text> abc' );
 
 
 		const startPosition = model.createPositionAt( root, [ 10 ] );
 		const startPosition = model.createPositionAt( root, [ 10 ] );
-		const result = findLinkRange( startPosition, 'url', model );
+		const result = findAttributeRange( startPosition, 'url', model );
 
 
 		expect( result ).to.instanceOf( Range );
 		expect( result ).to.instanceOf( Range );
 		expect( result.isEqual( model.createRange( model.createPositionAt( root, 4 ), model.createPositionAt( root, 10 ) ) ) ).to.true;
 		expect( result.isEqual( model.createRange( model.createPositionAt( root, 4 ), model.createPositionAt( root, 10 ) ) ) ).to.true;
@@ -83,7 +83,7 @@ describe( 'findLinkRange', () => {
 		setData( model, '<$text linkHref="other">abc</$text><$text linkHref="url">foobar</$text><$text linkHref="other">abc</$text>' );
 		setData( model, '<$text linkHref="other">abc</$text><$text linkHref="url">foobar</$text><$text linkHref="other">abc</$text>' );
 
 
 		const startPosition = model.createPositionAt( root, [ 6 ] );
 		const startPosition = model.createPositionAt( root, [ 6 ] );
-		const result = findLinkRange( startPosition, 'url', model );
+		const result = findAttributeRange( startPosition, 'url', model );
 
 
 		expect( result ).to.instanceOf( Range );
 		expect( result ).to.instanceOf( Range );
 		expect( result.isEqual( model.createRange( model.createPositionAt( root, 3 ), model.createPositionAt( root, 9 ) ) ) ).to.true;
 		expect( result.isEqual( model.createRange( model.createPositionAt( root, 3 ), model.createPositionAt( root, 9 ) ) ) ).to.true;
@@ -93,7 +93,7 @@ describe( 'findLinkRange', () => {
 		setData( model, '<$text linkHref="other">abc</$text><$text linkHref="url">foobar</$text><$text linkHref="other">abc</$text>' );
 		setData( model, '<$text linkHref="other">abc</$text><$text linkHref="url">foobar</$text><$text linkHref="other">abc</$text>' );
 
 
 		const startPosition = model.createPositionAt( root, [ 3 ] );
 		const startPosition = model.createPositionAt( root, [ 3 ] );
-		const result = findLinkRange( startPosition, 'url', model );
+		const result = findAttributeRange( startPosition, 'url', model );
 
 
 		expect( result ).to.instanceOf( Range );
 		expect( result ).to.instanceOf( Range );
 		expect( result.isEqual( model.createRange( model.createPositionAt( root, 3 ), model.createPositionAt( root, 9 ) ) ) ).to.true;
 		expect( result.isEqual( model.createRange( model.createPositionAt( root, 3 ), model.createPositionAt( root, 9 ) ) ) ).to.true;
@@ -103,7 +103,7 @@ describe( 'findLinkRange', () => {
 		setData( model, '<$text linkHref="other">abc</$text><$text linkHref="url">foobar</$text><$text linkHref="other">abc</$text>' );
 		setData( model, '<$text linkHref="other">abc</$text><$text linkHref="url">foobar</$text><$text linkHref="other">abc</$text>' );
 
 
 		const startPosition = model.createPositionAt( root, [ 9 ] );
 		const startPosition = model.createPositionAt( root, [ 9 ] );
-		const result = findLinkRange( startPosition, 'url', model );
+		const result = findAttributeRange( startPosition, 'url', model );
 
 
 		expect( result ).to.instanceOf( Range );
 		expect( result ).to.instanceOf( Range );
 		expect( result.isEqual( model.createRange( model.createPositionAt( root, 3 ), model.createPositionAt( root, 9 ) ) ) ).to.true;
 		expect( result.isEqual( model.createRange( model.createPositionAt( root, 3 ), model.createPositionAt( root, 9 ) ) ) ).to.true;
@@ -118,7 +118,7 @@ describe( 'findLinkRange', () => {
 		);
 		);
 
 
 		const startPosition = model.createPositionAt( root.getNodeByPath( [ 1 ] ), 3 );
 		const startPosition = model.createPositionAt( root.getNodeByPath( [ 1 ] ), 3 );
-		const result = findLinkRange( startPosition, 'url', model );
+		const result = findAttributeRange( startPosition, 'url', model );
 
 
 		expect( result ).to.instanceOf( Range );
 		expect( result ).to.instanceOf( Range );
 		const expectedRange = model.createRange(
 		const expectedRange = model.createRange(