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

Added srcset and sizes handling to ImageEngine.

Szymon Kupś 8 лет назад
Родитель
Сommit
22dcdd6f8f

+ 29 - 18
packages/ckeditor5-image/src/image/converters.js

@@ -69,34 +69,45 @@ export function viewFigureToModel() {
  *
  * @param {Array.<module:engine/conversion/modelconversiondispatcher~ModelConversionDispatcher>} dispatchers
  * @param {String} attributeName
+ * @param {function} [conversionCallback] Function that will be called each time given attribute conversion is performed.
+ * It will be called with two params: a view image element and type of the conversion: 'addAttribute`, `changeAttribute` or
+ * `removeAttribute`. This callback can be used to perform additional processing on view image element.
  */
-export function createImageAttributeConverter( dispatchers, attributeName ) {
+export function createImageAttributeConverter( dispatchers, attributeName, conversionCallback ) {
 	for ( const dispatcher of dispatchers ) {
-		dispatcher.on( `addAttribute:${ attributeName }:image`, modelToViewAttributeConverter );
-		dispatcher.on( `changeAttribute:${ attributeName }:image`, modelToViewAttributeConverter );
-		dispatcher.on( `removeAttribute:${ attributeName }:image`, modelToViewAttributeConverter );
+		dispatcher.on( `addAttribute:${ attributeName }:image`, modelToViewAttributeConverter( conversionCallback ) );
+		dispatcher.on( `changeAttribute:${ attributeName }:image`, modelToViewAttributeConverter( conversionCallback ) );
+		dispatcher.on( `removeAttribute:${ attributeName }:image`, modelToViewAttributeConverter( conversionCallback ) );
 	}
 }
 
-// Model to view image converter converting given attribute, and adding it to `img` element nested inside `figure` element.
+// Returns model to view image converter converting given attribute, and adding it to `img` element nested inside `figure` element.
+// Allows to provide `conversionCallback` called each time conversion is made.
 //
 // @private
-function modelToViewAttributeConverter( evt, data, consumable, conversionApi ) {
-	const parts = evt.name.split( ':' );
-	const consumableType = parts[ 0 ] + ':' + parts[ 1 ];
+function modelToViewAttributeConverter( conversionCallback ) {
+	return ( evt, data, consumable, conversionApi ) => {
+		const parts = evt.name.split( ':' );
+		const consumableType = parts[ 0 ] + ':' + parts[ 1 ];
 
-	if ( !consumable.consume( data.item, consumableType ) ) {
-		return;
-	}
+		if ( !consumable.consume( data.item, consumableType ) ) {
+			return;
+		}
 
-	const figure = conversionApi.mapper.toViewElement( data.item );
-	const img = figure.getChild( 0 );
+		const figure = conversionApi.mapper.toViewElement( data.item );
+		const img = figure.getChild( 0 );
+		const type = parts[ 0 ];
 
-	if ( parts[ 0 ] == 'removeAttribute' ) {
-		img.removeAttribute( data.attributeKey );
-	} else {
-		img.setAttribute( data.attributeKey, data.attributeNewValue );
-	}
+		if ( type == 'removeAttribute' ) {
+			img.removeAttribute( data.attributeKey );
+		} else {
+			img.setAttribute( data.attributeKey, data.attributeNewValue );
+		}
+
+		if ( conversionCallback ) {
+			conversionCallback( img, type );
+		}
+	};
 }
 
 // Holds all images that were converted for autohoisting.

+ 16 - 1
packages/ckeditor5-image/src/image/imageengine.js

@@ -38,7 +38,7 @@ export default class ImageEngine extends Plugin {
 		// Configure schema.
 		schema.registerItem( 'image' );
 		schema.requireAttributes( 'image', [ 'src' ] );
-		schema.allow( { name: 'image', attributes: [ 'alt', 'src' ], inside: '$root' } );
+		schema.allow( { name: 'image', attributes: [ 'alt', 'src', 'srcset' ], inside: '$root' } );
 		schema.objects.add( 'image' );
 
 		// Build converter from model to view for data pipeline.
@@ -54,6 +54,15 @@ export default class ImageEngine extends Plugin {
 		createImageAttributeConverter( [ editing.modelToView, data.modelToView ], 'src' );
 		createImageAttributeConverter( [ editing.modelToView, data.modelToView ], 'alt' );
 
+		// Convert `srcset` attribute changes and add or remove `sizes` attribute when necessary.
+		createImageAttributeConverter( [ editing.modelToView, data.modelToView ], 'srcset', ( viewImg, type ) => {
+			if ( type == 'removeAttribute' ) {
+				viewImg.removeAttribute( 'sizes' );
+			} else {
+				viewImg.setAttribute( 'sizes', '100vw' );
+			}
+		} );
+
 		// Build converter for view img element to model image element.
 		buildViewConverter().for( data.viewToModel )
 			.from( { name: 'img', attribute: { src: /./ } } )
@@ -70,6 +79,12 @@ export default class ImageEngine extends Plugin {
 			.consuming( { attribute: [ 'alt' ] } )
 			.toAttribute( viewImage => ( { key: 'alt', value: viewImage.getAttribute( 'alt' ) } ) );
 
+		// Build converter for srcset attribute.
+		buildViewConverter().for( data.viewToModel )
+			.from( { name: 'img', attribute: { srcset: /./ } } )
+			.consuming( { attribute: [ 'srcset' ] } )
+			.toAttribute( viewImage => ( { key: 'srcset', value: viewImage.getAttribute( 'srcset' ) } ) );
+
 		// Converter for figure element from view to model.
 		data.viewToModel.on( 'element:figure', viewFigureToModel() );
 	}

+ 59 - 0
packages/ckeditor5-image/tests/image/imageengine.js

@@ -47,6 +47,16 @@ describe( 'ImageEngine', () => {
 
 				expect( editor.getData() ).to.equal( '<figure class="image"><img src="foo.png"></figure>' );
 			} );
+
+			it( 'should convert with srcset attribute and add sizes attribute', () => {
+				setModelData( document, '<image src="foo.png" alt="alt text" srcset="small 148w, big.png 1024w"></image>' );
+
+				expect( editor.getData() ).to.equal(
+					'<figure class="image">' +
+						'<img srcset="small 148w, big.png 1024w" sizes="100vw" alt="alt text" src="foo.png">' +
+					'</figure>'
+				);
+			} );
 		} );
 
 		describe( 'view to model', () => {
@@ -174,6 +184,28 @@ describe( 'ImageEngine', () => {
 					.to.equal( '<image src="bar.jpg"></image><image src="foo.jpg">abc</image>' );
 			} );
 
+			it( 'should convert image with srcset attribute', () => {
+				editor.setData(
+					'<figure class="image">' +
+						'<img src="foo.png" alt="alt text" srcset="small 148w, big.png 1024w" />' +
+					'</figure>'
+				);
+
+				expect( getModelData( document, { withoutSelection: true } ) )
+					.to.equal( '<image alt="alt text" src="foo.png" srcset="small 148w, big.png 1024w"></image>' );
+			} );
+
+			it( 'should ignore sizes attribute', () => {
+				editor.setData(
+					'<figure class="image">' +
+						'<img src="foo.png" alt="alt text" srcset="small 148w, big.png 1024w" sizes="50vw" />' +
+					'</figure>'
+				);
+
+				expect( getModelData( document, { withoutSelection: true } ) )
+					.to.equal( '<image alt="alt text" src="foo.png" srcset="small 148w, big.png 1024w"></image>' );
+			} );
+
 			describe( 'should autohoist images', () => {
 				beforeEach( () => {
 					document.schema.registerItem( 'div', '$block' );
@@ -351,6 +383,33 @@ describe( 'ImageEngine', () => {
 					'<figure class="image ck-widget" contenteditable="false"><img alt="alt text" src="foo.png"></img></figure>'
 				);
 			} );
+
+			it( 'should convert srcset attribute to srcset and sizes', () => {
+				setModelData( document, '<image src="foo.png" alt="alt text" srcset="small 148w, big.png 1024w"></image>' );
+
+				expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal(
+					'<figure class="image ck-widget" contenteditable="false">' +
+						'<img alt="alt text" sizes="100vw" src="foo.png" srcset="small 148w, big.png 1024w"></img>' +
+					'</figure>'
+				);
+			} );
+
+			it( 'should remove sizes attribute when srcset attribute is removed', () => {
+				setModelData( document, '<image src="foo.png" srcset="small 148w, big.png 1024w"></image>' );
+				const image = document.getRoot().getChild( 0 );
+
+				document.enqueueChanges( () => {
+					const batch = document.batch();
+
+					batch.removeAttribute( image, 'srcset' );
+				} );
+
+				expect( getViewData( viewDocument, { withoutSelection: true } ) ).to.equal(
+					'<figure class="image ck-widget" contenteditable="false">' +
+						'<img src="foo.png"></img>' +
+					'</figure>'
+				);
+			} );
 		} );
 	} );
 } );