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

Separate converters for image attributes #32

Merged
merged 3 commits into from
Jan 24, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 27 additions & 14 deletions src/converters.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
* @module image/converters
*/

import ViewContainerElement from '@ckeditor/ckeditor5-engine/src/view/containerelement';
import ViewEmptyElement from '@ckeditor/ckeditor5-engine/src/view/emptyelement';
import ModelElement from '@ckeditor/ckeditor5-engine/src/model/element';
import { isImageWidget } from './utils';

Expand Down Expand Up @@ -93,21 +91,36 @@ export function modelToViewSelection( t ) {
}

/**
* Converts model `image` element to view representation:
* Creates image attribute converter for provided model conversion dispatchers.
*
* <figure class="image"><img src="..." alt="..."></img></figure>
*
* @param {module:engine/model/element~Element} modelElement
* @return {module:engine/view/containerelement~ContainerElement}
* @param {Array.<module:engine/conversion/modelconversiondispatcher~ModelConversionDispatcher>} dispatchers
* @param {String} attributeName
*/
export function modelToViewImage( modelElement ) {
const viewImg = new ViewEmptyElement( 'img', {
src: modelElement.getAttribute( 'src' )
} );
export function createImageAttributeConverter( dispatchers, attributeName ) {
for ( let dispatcher of dispatchers ) {
dispatcher.on( `addAttribute:${ attributeName }:image`, modelToViewAttributeConverter );
dispatcher.on( `changeAttribute:${ attributeName }:image`, modelToViewAttributeConverter );
dispatcher.on( `removeAttribute:${ attributeName }:image`, modelToViewAttributeConverter );
}
}

if ( modelElement.hasAttribute( 'alt' ) ) {
viewImg.setAttribute( 'alt', modelElement.getAttribute( 'alt' ) );
// Model to view image converter converting given attribute, and adding it to `img` element nested inside `figure` element.
//
// @private
function modelToViewAttributeConverter( evt, data, consumable, conversionApi ) {
const parts = evt.name.split( ':' );
const consumableType = parts[ 0 ] + ':' + parts[ 1 ];

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

return new ViewContainerElement( 'figure', { class: 'image' }, viewImg );
const figure = conversionApi.mapper.toViewElement( data.item );
const img = figure.getChild( 0 );

if ( parts[ 0 ] == 'removeAttribute' ) {
img.removeAttribute( data.attributeKey );
} else {
img.setAttribute( data.attributeKey, data.attributeNewValue );
}
}
23 changes: 20 additions & 3 deletions src/imageengine.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
import buildModelConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildmodelconverter';
import WidgetEngine from './widget/widgetengine';
import { modelToViewImage, viewToModelImage, modelToViewSelection } from './converters';
import { viewToModelImage, modelToViewSelection, createImageAttributeConverter } from './converters';
import { toImageWidget } from './utils';
import ViewContainerElement from '@ckeditor/ckeditor5-engine/src/view/containerelement';
import ViewEmptyElement from '@ckeditor/ckeditor5-engine/src/view/emptyelement';

/**
* The image engine plugin.
Expand Down Expand Up @@ -47,12 +49,15 @@ export default class ImageEngine extends Plugin {
// Build converter from model to view for data pipeline.
buildModelConverter().for( data.modelToView )
.fromElement( 'image' )
.toElement( ( data ) => modelToViewImage( data.item ) );
.toElement( () => createImageViewElement() );

// Build converter from model to view for editing pipeline.
buildModelConverter().for( editing.modelToView )
.fromElement( 'image' )
.toElement( ( data ) => toImageWidget( modelToViewImage( data.item ) ) );
.toElement( () => toImageWidget( createImageViewElement() ) );

createImageAttributeConverter( [ editing.modelToView, data.modelToView ], 'src' );
createImageAttributeConverter( [ editing.modelToView, data.modelToView ], 'alt' );

// Converter for figure element from view to model.
data.viewToModel.on( 'element:figure', viewToModelImage() );
Expand All @@ -61,3 +66,15 @@ export default class ImageEngine extends Plugin {
editing.modelToView.on( 'selection', modelToViewSelection( editor.t ), { priority: 'lowest' } );
}
}

// Creates view element representing the image.
//
// <figure class="image"><img></img></figure>
//
// Note that `alt` and `src` attributes are converted separately, so they're not included.
//
// @private
// @return {module:engine/view/containerelement~ContainerElement}
export function createImageViewElement() {
return new ViewContainerElement( 'figure', { class: 'image' }, new ViewEmptyElement( 'img' ) );
}
48 changes: 47 additions & 1 deletion tests/imageengine.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ describe( 'ImageEngine', () => {
it( 'should convert', () => {
setModelData( document, '<image src="foo.png" alt="alt text"></image>' );

expect( editor.getData() ).to.equal( '<figure class="image"><img src="foo.png" alt="alt text"></figure>' );
expect( editor.getData() ).to.equal( '<figure class="image"><img alt="alt text" src="foo.png"></figure>' );
} );

it( 'should convert without alt attribute', () => {
Expand Down Expand Up @@ -151,6 +151,52 @@ describe( 'ImageEngine', () => {
expect( figure.name ).to.equal( 'figure' );
expect( isImageWidget( figure ) ).to.be.true;
} );

it( 'should convert attribute change', () => {
setModelData( document, '<image src="foo.png" alt="alt text"></image>' );
const image = document.getRoot().getChild( 0 );

document.enqueueChanges( () => {
const batch = document.batch();

batch.setAttribute( image, 'alt', 'new text' );
} );

expect( getViewData( viewDocument, { withoutSelection: true } ) )
.to.equal( '<figure class="image ck-widget" contenteditable="false"><img alt="new text" src="foo.png"></img></figure>' );
} );

it( 'should convert attribute removal', () => {
setModelData( document, '<image src="foo.png" alt="alt text"></image>' );
const image = document.getRoot().getChild( 0 );

document.enqueueChanges( () => {
const batch = document.batch();

batch.removeAttribute( image, 'alt' );
} );

expect( getViewData( viewDocument, { withoutSelection: true } ) )
.to.equal( '<figure class="image ck-widget" contenteditable="false"><img src="foo.png"></img></figure>' );
} );

it( 'should not convert change if is already consumed', () => {
setModelData( document, '<image src="foo.png" alt="alt text"></image>' );
const image = document.getRoot().getChild( 0 );

editor.editing.modelToView.on( 'removeAttribute:alt:image', ( evt, data, consumable ) => {
consumable.consume( data.item, 'removeAttribute:alt' );
}, { priority: 'high' } );

document.enqueueChanges( () => {
const batch = document.batch();

batch.removeAttribute( image, 'alt' );
} );

expect( getViewData( viewDocument, { withoutSelection: true } ) )
.to.equal( '<figure class="image ck-widget" contenteditable="false"><img alt="alt text" src="foo.png"></img></figure>' );
} );
} );
} );

Expand Down