瀏覽代碼

Adding missing image caption in the view during model to view conversion.

Szymon Kupś 8 年之前
父節點
當前提交
4414921faf

+ 52 - 23
packages/ckeditor5-image/src/imagecaption/imagecaptionengine.js

@@ -19,7 +19,7 @@ import ModelPosition from '@ckeditor/ckeditor5-engine/src/model/position';
 import buildViewConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildviewconverter';
 import ViewMatcher from '@ckeditor/ckeditor5-engine/src/view/matcher';
 import { isImage, isImageWidget } from '../image/utils';
-import { captionElementCreator, isCaption, getCaptionFromImage } from './utils';
+import { captionElementCreator, isCaption, getCaptionFromImage, isInsideCaption } from './utils';
 
 /**
  * The image caption engine plugin.
@@ -39,6 +39,7 @@ export default class ImageCaptionEngine extends Plugin {
 		const schema = document.schema;
 		const data = editor.data;
 		const editing = editor.editing;
+		const mapper = editing.mapper;
 
 		/**
 		 * Last selected caption editable.
@@ -47,6 +48,14 @@ export default class ImageCaptionEngine extends Plugin {
 		 * @member {module:image/imagecaption/imagecaptionengine~ImageCaptionEngine} #_lastSelectedEditable
 		 */
 
+		/**
+		 * Function used to create caption element in editing view.
+		 *
+		 * @private
+		 * @member {Function}
+		 */
+		this._captionCreator = captionElementCreator( viewDocument );
+
 		// Schema configuration.
 		schema.registerItem( 'caption' );
 		schema.allow( { name: '$inline', inside: 'caption' } );
@@ -57,33 +66,37 @@ export default class ImageCaptionEngine extends Plugin {
 		document.on( 'change', insertMissingCaptionElement );
 
 		// View to model converter for data pipeline.
-		const matcher = new ViewMatcher( ( element ) => {
-			const parent = element.parent;
-
-			// Convert only captions for images.
-			if ( element.name == 'figcaption' && parent && parent.name == 'figure' && parent.hasClass( 'image' ) ) {
-				return { name: true };
-			}
-
-			return null;
-		} );
-
 		buildViewConverter()
 			.for( data.viewToModel )
-			.from( matcher )
+			.from( new ViewMatcher( matchImageCaption ) )
 			.toElement( 'caption' );
 
 		// Model to view converter for data pipeline.
-		data.modelToView.on(
-			'insert:caption',
-			captionModelToView( new ViewContainerElement( 'figcaption' ) )
-		);
+		data.modelToView.on( 'insert:caption', captionModelToView( new ViewContainerElement( 'figcaption' ) ) );
 
 		// Model to view converter for editing pipeline.
+		editing.modelToView.on( 'insert:caption', captionModelToView( this._captionCreator ) );
+
 		editing.modelToView.on(
-			'insert:caption',
-			captionModelToView( captionElementCreator( viewDocument ) )
-		);
+			'insert',
+			( evt, data ) => {
+				if ( isInsideCaption( data.item ) ) {
+					const caption = data.item.parent;
+					const image = caption.parent;
+
+					const viewImage = mapper.toViewElement( image );
+					let viewCaption =  mapper.toViewElement( caption );
+
+					// Image should be already converted to the view.
+					if ( viewImage && !viewCaption ) {
+						viewCaption = this._captionCreator();
+						const viewPosition = ViewPosition.createAt( viewImage, 'end' );
+
+						mapper.bindElements( caption, viewCaption );
+						viewWriter.insert( viewPosition, viewCaption );
+					}
+				}
+			}, { priority: 'high' } );
 
 		// Adding / removing caption element when there is no text in the model.
 		const selection = viewDocument.selection;
@@ -153,7 +166,6 @@ export default class ImageCaptionEngine extends Plugin {
 		const selection = editing.view.selection;
 		const imageFigure = selection.getSelectedElement();
 		const mapper = editing.mapper;
-		const editableCreator = captionElementCreator( editing.view );
 
 		if ( imageFigure && isImageWidget( imageFigure ) ) {
 			const modelImage = mapper.toModelElement( imageFigure );
@@ -161,7 +173,7 @@ export default class ImageCaptionEngine extends Plugin {
 			let viewCaption =  mapper.toViewElement( modelCaption );
 
 			if ( !viewCaption ) {
-				viewCaption = editableCreator();
+				viewCaption = this._captionCreator();
 
 				const viewPosition = ViewPosition.createAt( imageFigure, 'end' );
 				mapper.bindElements( modelCaption, viewCaption );
@@ -215,7 +227,7 @@ function captionModelToView( elementCreator ) {
 			const imageFigure = conversionApi.mapper.toViewElement( data.range.start.parent );
 			const viewElement = ( elementCreator instanceof ViewElement ) ?
 				elementCreator.clone( true ) :
-				elementCreator( data, consumable, conversionApi );
+				elementCreator();
 
 			const viewPosition = ViewPosition.createAt( imageFigure, 'end' );
 			conversionApi.mapper.bindElements( data.item, viewElement );
@@ -223,3 +235,20 @@ function captionModelToView( elementCreator ) {
 		}
 	};
 }
+
+// Checks if given element is `figcaption` element and is placed inside image `figure` element.
+//
+// @private
+// @param {module:engine/view/element~Element} element
+// @returns {Object|null} Returns object accepted by {@link module:engine/view/matcher~Matcher} or `null` if element
+// cannot be matched.
+function matchImageCaption( element ) {
+	const parent = element.parent;
+
+	// Convert only captions for images.
+	if ( element.name == 'figcaption' && parent && parent.name == 'figure' && parent.hasClass( 'image' ) ) {
+		return { name: true };
+	}
+
+	return null;
+}

+ 4 - 0
packages/ckeditor5-image/src/imagecaption/utils.js

@@ -61,3 +61,7 @@ export function getCaptionFromImage( imageModelElement ) {
 
 	return null;
 }
+
+export function isInsideCaption( node ) {
+	return node.parent && node.parent.name == 'caption' && node.parent.parent && node.parent.parent.name == 'image';
+}