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 #146 from ckeditor/t/145
Browse files Browse the repository at this point in the history
Feature: Replaced srcset attribute in the model by responsive attribute which is now converted to three attributes in the view: srcset, sizes and width. Closes #145. Closes ckeditor/ckeditor5-easy-image#4.

BREAKING CHANGE: The srcset attribute is now replaced by responsive attribute in the model.
  • Loading branch information
Piotr Jasiun authored Sep 29, 2017
2 parents 8607379 + 08d21e6 commit 9ca651e
Show file tree
Hide file tree
Showing 4 changed files with 253 additions and 37 deletions.
71 changes: 56 additions & 15 deletions src/image/converters.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,32 +69,77 @@ export function viewFigureToModel() {
*
* @param {Array.<module:engine/conversion/modelconversiondispatcher~ModelConversionDispatcher>} dispatchers
* @param {String} attributeName
* @param {function} [conversionCallback] The function that will be called each time given attribute conversion is performed.
* It will be called with two parameters: a view image element and the type of the conversion: 'addAttribute`, `changeAttribute` or
* `removeAttribute`. This callback can be used to perform additional processing on view image element.
* @param {Function} [converter] Custom converter for the attribute - default one converts attribute from model `image` element
* to the same attribute in `img` in the view.
*/
export function createImageAttributeConverter( dispatchers, attributeName, conversionCallback ) {
export function createImageAttributeConverter( dispatchers, attributeName, converter = modelToViewAttributeConverter ) {
for ( const dispatcher of dispatchers ) {
dispatcher.on( `addAttribute:${ attributeName }:image`, modelToViewAttributeConverter( conversionCallback ) );
dispatcher.on( `changeAttribute:${ attributeName }:image`, modelToViewAttributeConverter( conversionCallback ) );
dispatcher.on( `removeAttribute:${ attributeName }:image`, modelToViewAttributeConverter( conversionCallback ) );
dispatcher.on( `addAttribute:${ attributeName }:image`, converter() );
dispatcher.on( `changeAttribute:${ attributeName }:image`, converter() );
dispatcher.on( `removeAttribute:${ attributeName }:image`, converter() );
}
}

/**
* Converter used to convert `responsive` image's attribute to `srcset`, `sizes` and `width` attributes in the view.
*
* @return {Function}
*/
export function responsiveAttributeConverter() {
return ( evt, data, consumable, conversionApi ) => {
const parts = evt.name.split( ':' );
const consumableType = parts[ 0 ] + ':' + parts[ 1 ];
const modelImage = data.item;

if ( !consumable.consume( modelImage, consumableType ) ) {
return;
}

const figure = conversionApi.mapper.toViewElement( modelImage );
const img = figure.getChild( 0 );
const type = parts[ 0 ];

if ( type == 'removeAttribute' ) {
const oldData = data.attributeOldValue;

if ( oldData.srcset ) {
img.removeAttribute( 'srcset' );
img.removeAttribute( 'sizes' );

if ( oldData.width ) {
img.removeAttribute( 'width' );
}
}
} else {
const newData = data.attributeNewValue;

if ( newData.srcset ) {
img.setAttribute( 'srcset', newData.srcset );
// Always outputting `100vw`. See https://github.com/ckeditor/ckeditor5-image/issues/2.
img.setAttribute( 'sizes', '100vw' );

if ( newData.width ) {
img.setAttribute( 'width', newData.width );
}
}
}
};
}

// 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( conversionCallback ) {
function modelToViewAttributeConverter() {
return ( evt, data, consumable, conversionApi ) => {
const parts = evt.name.split( ':' );
const consumableType = parts[ 0 ] + ':' + parts[ 1 ];
const modelImage = data.item;

if ( !consumable.consume( data.item, consumableType ) ) {
if ( !consumable.consume( modelImage, consumableType ) ) {
return;
}

const figure = conversionApi.mapper.toViewElement( data.item );
const figure = conversionApi.mapper.toViewElement( modelImage );
const img = figure.getChild( 0 );
const type = parts[ 0 ];

Expand All @@ -103,10 +148,6 @@ function modelToViewAttributeConverter( conversionCallback ) {
} else {
img.setAttribute( data.attributeKey, data.attributeNewValue );
}

if ( conversionCallback ) {
conversionCallback( img, type );
}
};
}

Expand Down
36 changes: 24 additions & 12 deletions src/image/imageengine.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,21 @@
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
import buildModelConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildmodelconverter';
import buildViewConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildviewconverter';
import { viewFigureToModel, createImageAttributeConverter, convertHoistableImage, hoistImageThroughElement } from './converters';
import {
viewFigureToModel,
createImageAttributeConverter,
convertHoistableImage,
hoistImageThroughElement,
responsiveAttributeConverter
} from './converters';
import { toImageWidget } from './utils';
import ModelElement from '@ckeditor/ckeditor5-engine/src/model/element';
import ViewContainerElement from '@ckeditor/ckeditor5-engine/src/view/containerelement';
import ViewEmptyElement from '@ckeditor/ckeditor5-engine/src/view/emptyelement';

/**
* The image engine plugin.
* Registers `<image>` as a block element in the document schema and allows it to have two attributes: `src` and `alt`.
* Registers `<image>` as a block element in the document schema, and allows `alt`, `src` and `responsive` attributes.
* Registers converters for editing and data pipelines.
*
* @extends module:core/plugin~Plugin
Expand All @@ -38,7 +44,7 @@ export default class ImageEngine extends Plugin {
// Configure schema.
schema.registerItem( 'image' );
schema.requireAttributes( 'image', [ 'src' ] );
schema.allow( { name: 'image', attributes: [ 'alt', 'src', 'srcset' ], inside: '$root' } );
schema.allow( { name: 'image', attributes: [ 'alt', 'src', 'responsive' ], inside: '$root' } );
schema.objects.add( 'image' );

// Build converter from model to view for data pipeline.
Expand All @@ -55,14 +61,7 @@ export default class ImageEngine extends Plugin {
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' );
}
} );
createImageAttributeConverter( [ editing.modelToView, data.modelToView ], 'responsive', responsiveAttributeConverter );

// Build converter for view img element to model image element.
buildViewConverter().for( data.viewToModel )
Expand All @@ -84,7 +83,20 @@ export default class ImageEngine extends Plugin {
buildViewConverter().for( data.viewToModel )
.from( { name: 'img', attribute: { srcset: /./ } } )
.consuming( { attribute: [ 'srcset' ] } )
.toAttribute( viewImage => ( { key: 'srcset', value: viewImage.getAttribute( 'srcset' ) } ) );
.toAttribute( viewImage => {
const value = {
srcset: viewImage.getAttribute( 'srcset' )
};

if ( viewImage.hasAttribute( 'width' ) ) {
value.width = viewImage.getAttribute( 'width' );
}

return {
key: 'responsive',
value
};
} );

// Converter for figure element from view to model.
data.viewToModel.on( 'element:figure', viewFigureToModel() );
Expand Down
Loading

0 comments on commit 9ca651e

Please sign in to comment.