瀏覽代碼

Fixed the manual decorator converter for linked images. It should not be executed for normal links.

Kamil Piechaczek 5 年之前
父節點
當前提交
65f3519592
共有 2 個文件被更改,包括 164 次插入3 次删除
  1. 13 3
      packages/ckeditor5-link/src/linkimageediting.js
  2. 151 0
      packages/ckeditor5-link/tests/linkimageediting.js

+ 13 - 3
packages/ckeditor5-link/src/linkimageediting.js

@@ -118,11 +118,11 @@ function upcastLink() {
 			}
 
 			// A full definition of the image feature.
-			// figure > a > img: parent of the link element is an image element.
+			// figure > a > img: parent of the view link element is an image element (figure).
 			let modelElement = data.modelCursor.parent;
 
 			if ( !modelElement.is( 'element', 'image' ) ) {
-				// a > img: parent of the link is not the image element. We need to convert it manually.
+				// a > img: parent of the view link is not the image (figure) element. We need to convert it manually.
 				const conversionResult = conversionApi.convertItem( imageInLink, data.modelCursor );
 
 				// Set image range as conversion result.
@@ -229,6 +229,13 @@ function upcastImageLinkManualDecorator( manualDecorators, decorator ) {
 	return dispatcher => {
 		dispatcher.on( 'element:a', ( evt, data, conversionApi ) => {
 			const viewLink = data.viewItem;
+			const imageInLink = Array.from( viewLink.getChildren() ).find( child => child.name === 'img' );
+
+			// We need to check whether an image is inside a link because the converter handles
+			// only manual decorators for linked images. See #7975.
+			if ( !imageInLink ) {
+				return;
+			}
 
 			const consumableAttributes = {
 				attributes: manualDecorators.get( decorator.id ).attributes
@@ -248,7 +255,10 @@ function upcastImageLinkManualDecorator( manualDecorators, decorator ) {
 			}
 
 			// At this stage we can assume that we have the `<image>` element.
-			const modelElement = data.modelCursor.parent;
+			// `nodeBefore` comes after conversion: `<a><img></a>`.
+			// `parent` comes with full image definition: `<figure><a><img></a></figure>.
+			// See a body of the `upcastLink()` function.
+			const modelElement = data.modelCursor.nodeBefore || data.modelCursor.parent;
 
 			conversionApi.writer.setAttribute( decorator.id, true, modelElement );
 		}, { priority: 'high' } );

+ 151 - 0
packages/ckeditor5-link/tests/linkimageediting.js

@@ -711,4 +711,155 @@ describe( 'LinkImageEditing', () => {
 			} );
 		} );
 	} );
+
+	describe( 'integration', () => {
+		let editor, model;
+
+		beforeEach( () => {
+			return VirtualTestEditor
+				.create( {
+					plugins: [ Paragraph, LinkImageEditing ],
+					link: {
+						decorators: {
+							isExternal: {
+								mode: 'manual',
+								label: 'Open in a new tab',
+								attributes: {
+									target: '_blank',
+									rel: 'noopener noreferrer'
+								}
+							},
+							isDownloadable: {
+								mode: 'manual',
+								label: 'Downloadable',
+								attributes: {
+									download: 'download'
+								}
+							},
+							isGallery: {
+								mode: 'manual',
+								label: 'Gallery link',
+								attributes: {
+									class: 'gallery'
+								}
+							}
+						}
+					}
+				} )
+				.then( newEditor => {
+					editor = newEditor;
+					model = editor.model;
+				} );
+		} );
+
+		afterEach( () => {
+			return editor.destroy();
+		} );
+
+		// See: #7975.
+		describe( 'manual decorators: linked text and linked image', () => {
+			it( 'should upcast the decorators when linked image (figure > a > img)', () => {
+				editor.setData(
+					'<figure class="image">' +
+						'<a class="gallery" href="https://cksource.com" target="_blank" rel="noopener noreferrer" download="download">' +
+							'<img src="sample.jpg" alt="bar">' +
+						'</a>' +
+						'<figcaption>Caption</figcaption>' +
+					'</figure>' +
+					'<p>' +
+						'<a href="https://cksource.com" target="_blank" rel="noopener noreferrer" download="download">' +
+							'https://cksource.com' +
+						'</a>' +
+					'</p>'
+				);
+
+				expect( getModelData( model, { withoutSelection: true } ) ).to.equal(
+					'<image alt="bar" ' +
+						'linkHref="https://cksource.com" ' +
+						'linkIsDownloadable="true" ' +
+						'linkIsExternal="true" ' +
+						'linkIsGallery="true" ' +
+						'src="sample.jpg">' +
+					'</image>' +
+					'<paragraph>' +
+						'<$text linkHref="https://cksource.com" linkIsDownloadable="true" linkIsExternal="true">' +
+							'https://cksource.com' +
+						'</$text>' +
+					'</paragraph>'
+				);
+			} );
+
+			it( 'should upcast the decorators when linked image (a > img)', () => {
+				editor.setData(
+					'<a class="gallery" href="https://cksource.com" target="_blank" rel="noopener noreferrer" download="download">' +
+						'<img src="sample.jpg" alt="bar">' +
+					'</a>' +
+					'<p>' +
+						'<a href="https://cksource.com" target="_blank" rel="noopener noreferrer" download="download">' +
+							'https://cksource.com' +
+						'</a>' +
+					'</p>'
+				);
+
+				expect( getModelData( model, { withoutSelection: true } ) ).to.equal(
+					'<image alt="bar" ' +
+						'linkHref="https://cksource.com" ' +
+						'linkIsDownloadable="true" ' +
+						'linkIsExternal="true" ' +
+						'linkIsGallery="true" ' +
+						'src="sample.jpg">' +
+					'</image>' +
+					'<paragraph>' +
+						'<$text linkHref="https://cksource.com" linkIsDownloadable="true" linkIsExternal="true">' +
+							'https://cksource.com' +
+						'</$text>' +
+					'</paragraph>'
+				);
+			} );
+
+			it( 'should downcast the decorators after applying a change', () => {
+				setModelData( model,
+					'<image alt="bar" src="sample.jpg"></image>' +
+					'<paragraph>' +
+						'<$text>https://cksource.com</$text>' +
+					'</paragraph>'
+				);
+
+				model.change( writer => {
+					// <image>
+					writer.setSelection( model.document.getRoot().getChild( 0 ), 'on' );
+				} );
+
+				editor.execute( 'link', 'https://cksource.com', {
+					linkIsDownloadable: true,
+					linkIsExternal: true,
+					linkIsGallery: true
+				} );
+
+				model.change( writer => {
+					// <$text>
+					writer.setSelection( model.document.getRoot().getChild( 1 ).getChild( 0 ), 'on' );
+				} );
+
+				editor.execute( 'link', 'https://cksource.com', {
+					linkIsDownloadable: true,
+					linkIsExternal: true,
+					linkIsGallery: true
+				} );
+
+				expect( editor.getData() ).to.equal(
+					'<figure class="image">' +
+						'<a class="gallery" href="https://cksource.com" download="download" target="_blank" rel="noopener noreferrer">' +
+							'<img src="sample.jpg" alt="bar">' +
+						'</a>' +
+					'</figure>' +
+					'<p>' +
+						'<a class="gallery" href="https://cksource.com" download="download" target="_blank" rel="noopener noreferrer">' +
+							'https://cksource.com' +
+						'</a>' +
+					'</p>'
+				);
+			} );
+		} );
+	} );
 } );