ソースを参照

Merge pull request #32 from ckeditor/t/31

Separate converters for image attributes.
Piotrek Koszuliński 9 年 前
コミット
ff937604fb

+ 27 - 14
packages/ckeditor5-image/src/converters.js

@@ -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';
 
@@ -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 );
+	}
 }

+ 20 - 3
packages/ckeditor5-image/src/imageengine.js

@@ -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.
@@ -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() );
@@ -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' ) );
+}

+ 47 - 1
packages/ckeditor5-image/tests/imageengine.js

@@ -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', () => {
@@ -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>' );
+			} );
 		} );
 	} );