Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Commit

Permalink
Merge pull request #120 from ckeditor/t/2
Browse files Browse the repository at this point in the history
Feature: Introduced support for responsive image's `srcset` attribute. Closes #2.
  • Loading branch information
Reinmar authored Jun 23, 2017
2 parents e6f842a + 75b8b41 commit 5b433d2
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 19 deletions.
47 changes: 29 additions & 18 deletions src/image/converters.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
18 changes: 17 additions & 1 deletion src/image/imageengine.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -54,6 +54,16 @@ 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 {
// Always outputting `100vw`. See https://github.com/ckeditor/ckeditor5-image/issues/2.
viewImg.setAttribute( 'sizes', '100vw' );
}
} );

// Build converter for view img element to model image element.
buildViewConverter().for( data.viewToModel )
.from( { name: 'img', attribute: { src: /./ } } )
Expand All @@ -70,6 +80,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() );
}
Expand Down
59 changes: 59 additions & 0 deletions tests/image/imageengine.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.png 148w, big.png 1024w"></image>' );

expect( editor.getData() ).to.equal(
'<figure class="image">' +
'<img srcset="small.png 148w, big.png 1024w" sizes="100vw" alt="alt text" src="foo.png">' +
'</figure>'
);
} );
} );

describe( 'view to model', () => {
Expand Down Expand Up @@ -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.png 148w, big.png 1024w" />' +
'</figure>'
);

expect( getModelData( document, { withoutSelection: true } ) )
.to.equal( '<image alt="alt text" src="foo.png" srcset="small.png 148w, big.png 1024w"></image>' );
} );

it( 'should ignore sizes attribute', () => {
editor.setData(
'<figure class="image">' +
'<img src="foo.png" alt="alt text" srcset="small.png 148w, big.png 1024w" sizes="50vw" />' +
'</figure>'
);

expect( getModelData( document, { withoutSelection: true } ) )
.to.equal( '<image alt="alt text" src="foo.png" srcset="small.png 148w, big.png 1024w"></image>' );
} );

describe( 'should autohoist images', () => {
beforeEach( () => {
document.schema.registerItem( 'div', '$block' );
Expand Down Expand Up @@ -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.png 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.png 148w, big.png 1024w"></img>' +
'</figure>'
);
} );

it( 'should remove sizes attribute when srcset attribute is removed', () => {
setModelData( document, '<image src="foo.png" srcset="small.png 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>'
);
} );
} );
} );
} );

0 comments on commit 5b433d2

Please sign in to comment.