Browse Source

Added option to create different opening and closing marker stamp.

Oskar Wróbel 8 years ago
parent
commit
6ce59f04de

+ 24 - 1
packages/ckeditor5-engine/src/conversion/buildmodelconverter.js

@@ -273,8 +273,21 @@ class ModelConverterBuilder {
 	 *			.fromMarker( 'search' )
 	 *			.toStamp( ( data ) => new UIElement( 'span', { 'data-name': data.marker.getName() ) );
 	 *
+	 * Creator function provides parameter `isOpening` which defined if currently converted element is a beginning or end of
+	 * the marker range. This makes possible to create different opening and closing stamp.
+	 *
+	 *		buildModelConverter().for( dispatcher )
+	 *			.fromMarker( 'search' )
+	 *			.toStamp( ( data, isOpening ) => {
+	 *				if ( isOpening ) {
+	 *					return new UIElement( 'span', { 'data-name': data.marker.getName(), 'data-start': true ) );
+	 *				}
+	 *
+	 *				return new UIElement( 'span', { 'data-name': data.marker.getName(), 'data-end': true ) );
+	 *			}
+	 *
 	 * Creator function provides
-	 * {@link module:engine/conversion/modelconversiondispatcher~ModelConversionDispatcher#event:addMarker addMarker event} parameters.
+	 * {@link module:engine/conversion/buildmodelconverter~ModelConverterBuilder#StampCreatorData} parameters.
 	 *
 	 * See how markers {module:engine/model/buildviewconverter~ViewConverterBuilder#toMarker view -> model serialization}
 	 * works to find out what view element format is the best for you.
@@ -389,3 +402,13 @@ class ModelConverterBuilder {
 export default function buildModelConverter() {
 	return new ModelConverterBuilder();
 }
+
+/**
+ * @typedef {StampCreatorData} {module:engine/conversion/buildmodelconverter~ModelConverterBuilder#StampCreatorData}
+ * @param {Object} data Additional information about the change.
+ * @param {String} data.name Marker name.
+ * @param {module:engine/model/range~Range} data.range Marker range.
+ * @param {Boolean} isOpening Defines if currently converted element is a beginning or end of the marker range.
+ * @param {module:engine/conversion/modelconsumable~ModelConsumable} consumable Values to consume.
+ * @param {Object} conversionApi Conversion interface to be used by callback, passed in `ModelConversionDispatcher` constructor.
+ */

+ 31 - 11
packages/ckeditor5-engine/src/conversion/model-to-view-converters.js

@@ -103,11 +103,18 @@ export function insertText() {
  */
 export function insertUIElement( elementCreator ) {
 	return ( evt, data, consumable, conversionApi ) => {
-		const viewElement = ( elementCreator instanceof ViewElement ) ?
-			elementCreator.clone( true ) :
-			elementCreator( data, consumable, conversionApi );
+		let viewStartElement, viewEndElement;
+
+		if ( elementCreator instanceof ViewElement ) {
+			viewStartElement = elementCreator.clone( true );
+			viewEndElement = elementCreator.clone( true );
+		} else {
+			const isOpening = true;
+			viewStartElement = elementCreator( data, isOpening, consumable, conversionApi );
+			viewEndElement = elementCreator( data, !isOpening, consumable, conversionApi );
+		}
 
-		if ( !viewElement ) {
+		if ( !viewStartElement || !viewEndElement ) {
 			return;
 		}
 
@@ -117,10 +124,10 @@ export function insertUIElement( elementCreator ) {
 
 		const mapper = conversionApi.mapper;
 
-		viewWriter.insert( mapper.toViewPosition( data.range.start ), viewElement );
+		viewWriter.insert( mapper.toViewPosition( data.range.start ), viewStartElement );
 
 		if ( !data.range.isCollapsed ) {
-			viewWriter.insert( mapper.toViewPosition( data.range.end ), viewElement.clone( true ) );
+			viewWriter.insert( mapper.toViewPosition( data.range.end ), viewEndElement );
 		}
 	};
 }
@@ -445,11 +452,18 @@ export function remove() {
  */
 export function removeUIElement( elementCreator ) {
 	return ( evt, data, consumable, conversionApi ) => {
-		const viewElement = ( elementCreator instanceof ViewElement ) ?
-			elementCreator.clone( true ) :
-			elementCreator( data, consumable, conversionApi );
+		let viewStartElement, viewEndElement;
+
+		if ( elementCreator instanceof ViewElement ) {
+			viewStartElement = elementCreator.clone( true );
+			viewEndElement = elementCreator.clone( true );
+		} else {
+			const isOpening = true;
+			viewStartElement = elementCreator( data, isOpening, consumable, conversionApi );
+			viewEndElement = elementCreator( data, !isOpening, consumable, conversionApi );
+		}
 
-		if ( !viewElement ) {
+		if ( !viewStartElement || !viewEndElement ) {
 			return;
 		}
 
@@ -459,7 +473,13 @@ export function removeUIElement( elementCreator ) {
 
 		const viewRange = conversionApi.mapper.toViewRange( data.range );
 
-		viewWriter.clear( viewRange.getEnlarged(), viewElement );
+		// First remove closing element.
+		viewWriter.clear( viewRange.getEnlarged(), viewEndElement );
+
+		// If closing and opening elements are not the same then remove opening element.
+		if ( !viewStartElement.isSimilar( viewEndElement ) ) {
+			viewWriter.clear( viewRange.getEnlarged(), viewStartElement );
+		}
 	};
 }
 

+ 23 - 0
packages/ckeditor5-engine/tests/conversion/model-to-view-converters.js

@@ -877,6 +877,29 @@ describe( 'model-to-view-converters', () => {
 
 				expect( viewToString( viewRoot ) ).to.equal( '<div><p>foobar</p></div>' );
 			} );
+
+			it( 'should insert and remove different opening and ending element', () => {
+				function creator( data, isOpening ) {
+					if ( isOpening ) {
+						return new ViewUIElement( 'span', { 'class': data.name, 'data-start': true } );
+					}
+
+					return new ViewUIElement( 'span', { 'class': data.name, 'data-end': true } );
+				}
+
+				dispatcher.on( 'addMarker:marker', insertUIElement( creator ) );
+				dispatcher.on( 'removeMarker:marker', removeUIElement( creator ) );
+
+				dispatcher.convertMarker( 'addMarker', 'marker', range );
+
+				expect( viewToString( viewRoot ) ).to.equal(
+					'<div><p>fo<span class="marker" data-start="true"></span>oba<span class="marker" data-end="true"></span>r</p></div>'
+				);
+
+				dispatcher.convertMarker( 'removeMarker', 'marker', range );
+
+				expect( viewToString( viewRoot ) ).to.equal( '<div><p>foobar</p></div>' );
+			} );
 		} );
 	} );