8
0
Просмотр исходного кода

The mechanism should work when mixed attributes are in the link node.

Kamil Piechaczek 5 лет назад
Родитель
Сommit
bc5915fef2

+ 18 - 7
packages/ckeditor5-link/src/linkediting.js

@@ -468,7 +468,7 @@ export default class LinkEditing extends Plugin {
 				return;
 			}
 
-			if ( shouldCopyAttributes( selection.getFirstPosition(), selection.getLastPosition() ) ) {
+			if ( shouldCopyAttributes( editor.model ) ) {
 				selectionAttributes = selection.getAttributes();
 			}
 		}, { priority: 'high' } );
@@ -500,11 +500,13 @@ export default class LinkEditing extends Plugin {
 
 // Checks whether selection's attributes should be copied to the new inserted text.
 //
-// @param {module:engine/model/position~Position} positionA
-// @param {module:engine/model/position~Position} positionB
+// @param {module:engine/model/model~Model} model
 // @returns {Boolean}
-function shouldCopyAttributes( positionA, positionB ) {
-	const nodeAtFirstPosition = positionA.nodeAfter;
+function shouldCopyAttributes( model ) {
+	const selection = model.document.selection;
+	const firstPosition = selection.getFirstPosition();
+	const lastPosition = selection.getLastPosition();
+	const nodeAtFirstPosition = firstPosition.nodeAfter;
 
 	// The text link node does not exist...
 	if ( !nodeAtFirstPosition ) {
@@ -523,10 +525,19 @@ function shouldCopyAttributes( positionA, positionB ) {
 
 	// `textNode` = the position is inside the link element.
 	// `nodeBefore` = the position is at the end of the link element.
-	const nodeAtLastPosition = positionB.textNode || positionB.nodeBefore;
+	const nodeAtLastPosition = lastPosition.textNode || lastPosition.nodeBefore;
 
 	// Both nodes must indicate the same node in the model tree.
-	return nodeAtFirstPosition === nodeAtLastPosition;
+	if ( nodeAtFirstPosition === nodeAtLastPosition ) {
+		return true;
+	}
+
+	// If nodes are not equal, maybe the link nodes has defined additional attributes inside.
+	// First, we need to find the entire link range.
+	const linkRange = findLinkRange( firstPosition, nodeAtFirstPosition.getAttribute( 'linkHref' ), model );
+
+	// Then we can check whether selected range is inside the found link range. If so, attributes should be preserved.
+	return linkRange.containsRange( model.createRange( firstPosition, lastPosition ), true );
 }
 
 // Checks whether provided changes were caused by typing.

+ 77 - 12
packages/ckeditor5-link/tests/linkediting.js

@@ -10,6 +10,7 @@ import UnlinkCommand from '../src/unlinkcommand';
 import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 import BoldEditing from '@ckeditor/ckeditor5-basic-styles/src/bold/boldediting';
+import ItalicEditing from '@ckeditor/ckeditor5-basic-styles/src/italic/italicediting';
 import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
 import Enter from '@ckeditor/ckeditor5-enter/src/enter';
 import DomEventData from '@ckeditor/ckeditor5-engine/src/view/observer/domeventdata';
@@ -1066,7 +1067,7 @@ describe( 'LinkEditing', () => {
 
 		beforeEach( async () => {
 			editor = await ClassicTestEditor.create( element, {
-				plugins: [ Paragraph, LinkEditing, Enter, BoldEditing, ImageEditing ],
+				plugins: [ Paragraph, LinkEditing, Enter, BoldEditing, ItalicEditing, ImageEditing ],
 				link: {
 					decorators: {
 						isFoo: {
@@ -1108,7 +1109,7 @@ describe( 'LinkEditing', () => {
 			expect( LinkEditing.requires.includes( Input ) ).to.equal( true );
 		} );
 
-		it( 'should preserve the `linkHref` attribute when the entire link is selected', () => {
+		it( 'should preserve selection attributes when the entire link is selected', () => {
 			setModelData( model,
 				'<paragraph>This is [<$text linkHref="foo">Foo</$text>] from <$text linkHref="bar">Bar</$text>.</paragraph>'
 			);
@@ -1122,7 +1123,39 @@ describe( 'LinkEditing', () => {
 			);
 		} );
 
-		it( 'should preserve the `linkHref` attribute when the selection starts at the beginning of the link', () => {
+		it( 'should preserve selection attributes when the entire link is selected (mixed attributes in the link)', () => {
+			setModelData( model,
+				'<paragraph>' +
+					'This is [' +
+					'<$text linkHref="foo" italic="true">F</$text>' +
+					'<$text linkHref="foo" bold="true">o</$text>' +
+					'<$text linkHref="foo" bold="true" italic="true">o</$text>' +
+					'<$text linkHref="foo" bold="true">B</$text>' +
+					'<$text linkHref="foo" bold="true" italic="true">a</$text>' +
+					'<$text linkHref="foo">r</$text>]' +
+					' from ' +
+					'<$text linkHref="bar">Bar</$text>' +
+					'.' +
+				'</paragraph>'
+			);
+
+			editor.execute( 'input', {
+				text: 'Abcde'
+			} );
+
+			expect( getModelData( model ) ).to.equal(
+				'<paragraph>' +
+					'This is ' +
+					'<$text italic="true" linkHref="foo">Abcde</$text>' +
+					'<$text italic="true">[]</$text>' +
+					' from ' +
+					'<$text linkHref="bar">Bar</$text>' +
+					'.' +
+				'</paragraph>'
+			);
+		} );
+
+		it( 'should preserve selection attributes when the selection starts at the beginning of the link', () => {
 			setModelData( model,
 				'<paragraph>This is [<$text linkHref="foo">Fo]o</$text> from <$text linkHref="bar">Bar</$text>.</paragraph>'
 			);
@@ -1136,6 +1169,38 @@ describe( 'LinkEditing', () => {
 			);
 		} );
 
+		it( 'should preserve selection attributes when it starts at the beginning of the link (mixed attributes in the link)', () => {
+			setModelData( model,
+				'<paragraph>' +
+					'This is [' +
+					'<$text linkHref="foo" italic="true">F</$text>' +
+					'<$text linkHref="foo" bold="true">o</$text>' +
+					'<$text linkHref="foo" bold="true" italic="true">o</$text>' +
+					'<$text linkHref="foo" bold="true">B</$text>' +
+					'<$text linkHref="foo" bold="true" italic="true">a]</$text>' +
+					'<$text linkHref="foo">r</$text>' +
+					' from ' +
+					'<$text linkHref="bar">Bar</$text>' +
+					'.' +
+				'</paragraph>'
+			);
+
+			editor.execute( 'input', {
+				text: 'Abcde'
+			} );
+
+			expect( getModelData( model ) ).to.equal(
+				'<paragraph>' +
+					'This is ' +
+					'<$text italic="true" linkHref="foo">Abcde[]</$text>' +
+					'<$text linkHref="foo">r</$text>' +
+					' from ' +
+					'<$text linkHref="bar">Bar</$text>' +
+					'.' +
+				'</paragraph>'
+			);
+		} );
+
 		it( 'should preserve all attributes of the link node (decorators check)', () => {
 			setModelData( model,
 				'<paragraph>' +
@@ -1162,7 +1227,7 @@ describe( 'LinkEditing', () => {
 			);
 		} );
 
-		it( 'should not preserve the `linkHref` attribute when the changes are not caused by typing', () => {
+		it( 'should not preserve selection attributes when the changes are not caused by typing', () => {
 			setModelData( model,
 				'<paragraph>This is [<$text linkHref="foo">Foo</$text>] from <$text linkHref="bar">Bar</$text>.</paragraph>'
 			);
@@ -1177,7 +1242,7 @@ describe( 'LinkEditing', () => {
 			);
 		} );
 
-		it( 'should not preserve the `linkHref` attribute when the changes are not caused by typing (pasting check)', () => {
+		it( 'should not preserve selection attributes when the changes are not caused by typing (pasting check)', () => {
 			setModelData( model,
 				'<paragraph>This is [<$text linkHref="foo">Foo</$text>] from <$text linkHref="bar">Bar</$text>.</paragraph>'
 			);
@@ -1196,7 +1261,7 @@ describe( 'LinkEditing', () => {
 			);
 		} );
 
-		it( 'should not preserve the `linkHref` attribute when typed after cutting the content', () => {
+		it( 'should not preserve selection attributes when typed after cutting the content', () => {
 			setModelData( model,
 				'<paragraph>This is [<$text linkHref="foo">Foo</$text>] from <$text linkHref="bar">Bar</$text>.</paragraph>'
 			);
@@ -1230,7 +1295,7 @@ describe( 'LinkEditing', () => {
 			);
 		} );
 
-		it( 'should not preserve anything if selected text does not have the `linkHref` attribute', () => {
+		it( 'should not preserve anything if selected text does not have the `linkHref` attribute`', () => {
 			setModelData( model,
 				'<paragraph>This is [<$text bold="foo">Foo</$text>] from <$text linkHref="bar">Bar</$text>.</paragraph>'
 			);
@@ -1244,7 +1309,7 @@ describe( 'LinkEditing', () => {
 			);
 		} );
 
-		it( 'should not preserve the `linkHref` attribute when the entire link is selected and pressed "Backspace"', () => {
+		it( 'should not preserve selection attributes when the entire link is selected and pressed "Backspace"', () => {
 			setModelData( model,
 				'<paragraph>This is [<$text linkHref="foo">Foo</$text>] from <$text linkHref="bar">Bar</$text>.</paragraph>'
 			);
@@ -1263,7 +1328,7 @@ describe( 'LinkEditing', () => {
 			);
 		} );
 
-		it( 'should not preserve the `linkHref` attribute when the entire link is selected and pressed "Delete"', () => {
+		it( 'should not preserve selection attributes when the entire link is selected and pressed "Delete"', () => {
 			setModelData( model,
 				'<paragraph>This is [<$text linkHref="foo">Foo</$text>] from <$text linkHref="bar">Bar</$text>.</paragraph>'
 			);
@@ -1282,7 +1347,7 @@ describe( 'LinkEditing', () => {
 			);
 		} );
 
-		it( 'should not preserve the `linkHref` attribute when selected different links', () => {
+		it( 'should not preserve selection attributes when selected different links', () => {
 			setModelData( model,
 				'<paragraph>This is <$text linkHref="foo">[Foo</$text> from <$text linkHref="bar">Bar]</$text>.</paragraph>'
 			);
@@ -1294,7 +1359,7 @@ describe( 'LinkEditing', () => {
 			expect( getModelData( model ) ).to.equal( '<paragraph>This is Abcde[].</paragraph>' );
 		} );
 
-		it( 'should not preserve the `linkHref` attribute when selected more than single link (start of the selection)', () => {
+		it( 'should not preserve selection attributes when selected more than single link (start of the selection)', () => {
 			setModelData( model,
 				'<paragraph>This is[ <$text linkHref="foo">Foo]</$text> from <$text linkHref="bar">Bar</$text>.</paragraph>'
 			);
@@ -1308,7 +1373,7 @@ describe( 'LinkEditing', () => {
 			);
 		} );
 
-		it( 'should not preserve the `linkHref` attribute when selected more than single link (end of the selection)', () => {
+		it( 'should not preserve selection attributes when selected more than single link (end of the selection)', () => {
 			setModelData( model,
 				'<paragraph>This is <$text linkHref="foo">[Foo</$text> ]from <$text linkHref="bar">Bar</$text>.</paragraph>'
 			);