Procházet zdrojové kódy

Refactored using of converters in model utils.

Oskar Wrobel před 9 roky
rodič
revize
93bc6d4130

+ 1 - 1
packages/ckeditor5-engine/src/conversion/model-selection-to-view-converters.js

@@ -161,7 +161,7 @@ export function convertSelectionAttribute( elementCreator ) {
 
 		const viewElement = elementCreator instanceof ViewElement ?
 				elementCreator.clone( true ) :
-				elementCreator( data.value, selection, consumable, conversionApi );
+				elementCreator( data.value, data, selection, consumable, conversionApi );
 
 		viewPosition = viewWriter.wrapPosition( viewPosition, viewElement );
 

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

@@ -195,6 +195,7 @@ export function removeAttribute( attributeCreator ) {
  * the copy will become the wrapping element. If `Function` is provided, it is passed all the parameters of the
  * {@link engine.conversion.ModelConversionDispatcher#event:setAttribute setAttribute event}. It's expected that the
  * function returns a {@link engine.view.Element}. The result of the function will be the wrapping element.
+ * When provided `Function` does not return element, then will be no conversion.
  *
  * The converter automatically consumes corresponding value from consumables list, stops the event (see
  * {@link engine.conversion.ModelConversionDispatcher}).
@@ -209,25 +210,27 @@ export function removeAttribute( attributeCreator ) {
  */
 export function wrap( elementCreator ) {
 	return ( evt, data, consumable, conversionApi ) => {
-		consumable.consume( data.item, eventNameToConsumableType( evt.name ) );
-
 		let viewRange = conversionApi.mapper.toViewRange( data.range );
 
 		const viewElement = ( elementCreator instanceof ViewElement ) ?
 			elementCreator.clone( true ) :
 			elementCreator( data.attributeNewValue, data, consumable, conversionApi );
 
-		// If this is a change event (because old value is not empty) and the creator is a function (so
-		// it may create different view elements basing on attribute value) we have to create
-		// view element basing on old value and unwrap it before wrapping with a newly created view element.
-		if ( data.attributeOldValue !== null && !( elementCreator instanceof ViewElement ) ) {
-			const oldViewElement = elementCreator( data.attributeOldValue, data, consumable, conversionApi );
-			viewRange = viewWriter.unwrap( viewRange, oldViewElement, evt.priority );
-		}
+		if ( viewElement ) {
+			consumable.consume( data.item, eventNameToConsumableType( evt.name ) );
 
-		viewWriter.wrap( viewRange, viewElement, evt.priority );
+			// If this is a change event (because old value is not empty) and the creator is a function (so
+			// it may create different view elements basing on attribute value) we have to create
+			// view element basing on old value and unwrap it before wrapping with a newly created view element.
+			if ( data.attributeOldValue !== null && !( elementCreator instanceof ViewElement ) ) {
+				const oldViewElement = elementCreator( data.attributeOldValue, data, consumable, conversionApi );
+				viewRange = viewWriter.unwrap( viewRange, oldViewElement, evt.priority );
+			}
 
-		evt.stop();
+			viewWriter.wrap( viewRange, viewElement );
+
+			evt.stop();
+		}
 	};
 }
 

+ 4 - 7
packages/ckeditor5-engine/tests/_utils-tests/model.js

@@ -223,9 +223,8 @@ describe( 'model test utils', () => {
 			] );
 
 			expect( stringify( root ) ).to.equal(
-				'<$text bold="true">foo</$text>' +
-				'bar' +
-				'<$text bold="true" italic="true">bom</$text>' +
+				// Because of https://github.com/ckeditor/ckeditor5-engine/issues/562 attributes are not merged
+				'<$text bold="true">foo</$text>bar<$text italic="true"><$text bold="true">bom</$text></$text>' +
 				'<a><$text bold="true" underline="true">pom</$text></a>'
 			);
 		} );
@@ -286,7 +285,7 @@ describe( 'model test utils', () => {
 				selection.collapse( elB, 'before' );
 
 				expect( stringify( root, selection ) ).to.equal(
-					'<a></a>foo<$text bold="true">bar</$text>[]<b></b>'
+					'<a></a>foo<$text bold="true">bar[]</$text><b></b>'
 				);
 				expect( selection.getAttribute( 'bold' ) ).to.true;
 				expect( count( selection.getAttributes() ) ).to.equal( 1 );
@@ -495,9 +494,7 @@ describe( 'model test utils', () => {
 					italic: true
 				} } );
 
-				expect( stringify( result.model, result.selection ) ).to.equal( 'foo[]bar' );
-				expect( result.selection.getAttribute( 'italic' ) ).to.be.true;
-				expect( result.selection.getAttribute( 'bold' ) ).to.be.true;
+				expect( stringify( result.model, result.selection ) ).to.equal( 'foo<$text bold="true" italic="true">[]</$text>bar' );
 			} );
 
 			test( 'sets collapsed selection between text and text with attributes', {

+ 16 - 27
packages/ckeditor5-engine/tests/_utils/model.js

@@ -4,10 +4,13 @@
  */
 
 import Mapper from '/ckeditor5/engine/conversion/mapper.js';
-import count from '/ckeditor5/utils/count.js';
 import { parse as viewParse, stringify as viewStringify } from '/tests/engine/_utils/view.js';
-import { convertRangeSelection, convertCollapsedSelection } from '/ckeditor5/engine/conversion/model-selection-to-view-converters.js';
-import { insertElement } from '/ckeditor5/engine/conversion/model-to-view-converters.js';
+import {
+	convertRangeSelection,
+	convertCollapsedSelection,
+	convertSelectionAttribute
+} from '/ckeditor5/engine/conversion/model-selection-to-view-converters.js';
+import { insertText, insertElement, wrap } from '/ckeditor5/engine/conversion/model-to-view-converters.js';
 
 import RootElement from '/ckeditor5/engine/model/rootelement.js';
 import ModelDocument from '/ckeditor5/engine/model/document.js';
@@ -18,14 +21,14 @@ import ModelSelection from '/ckeditor5/engine/model/selection.js';
 import ModelDocumentFragment from '/ckeditor5/engine/model/documentfragment.js';
 import ModelElement from '/ckeditor5/engine/model/element.js';
 import ModelText from '/ckeditor5/engine/model/text.js';
+import ModelTextProxy from '/ckeditor5/engine/model/textproxy.js';
 import modelWriter from '/ckeditor5/engine/model/writer.js';
 
 import ViewConversionDispatcher from '/ckeditor5/engine/conversion/viewconversiondispatcher.js';
 import ViewSelection from '/ckeditor5/engine/view/selection.js';
 import ViewDocumentFragment from '/ckeditor5/engine/view/documentfragment.js';
 import ViewElement from '/ckeditor5/engine/view/containerelement.js';
-import ViewText from '/ckeditor5/engine/view/text.js';
-import viewWriter from '/ckeditor5/engine/view/writer.js';
+import ViewAttributeElement from '/ckeditor5/engine/view/attributeelement.js';
 
 /**
  * Writes the contents of the {@link engine.model.Document Document} to an HTML-like string.
@@ -194,9 +197,17 @@ export function stringify( node, selectionOrPositionOrRange = null ) {
 	mapper.bindElements( node.root, viewDocumentFragment );
 
 	modelToView.on( 'insert:$text', insertText(), 'lowest' );
+	modelToView.on( 'addAttribute', wrap( ( value, data ) => {
+		if ( data.item instanceof ModelTextProxy ) {
+			return new ViewAttributeElement( 'model-text-with-attributes', { [ data.attributeKey ]: value } );
+		}
+	} ), 'lowest' );
 	modelToView.on( 'insert', insertElement( data => new ViewElement( data.item.name, data.item.getAttributes() )  ), 'lowest' );
 	modelToView.on( 'selection', convertRangeSelection(), 'lowest' );
 	modelToView.on( 'selection', convertCollapsedSelection(), 'lowest' );
+	modelToView.on( 'selectionAttribute', convertSelectionAttribute( ( value, data ) => {
+		return new ViewAttributeElement( 'model-text-with-attributes', { [ data.key ]: value } );
+	} ), 'lowest' );
 
 	// Convert model to view.
 	modelToView.convertInsertion( range );
@@ -363,25 +374,3 @@ function convertToModelText( withAttributes = false ) {
 		evt.stop();
 	};
 }
-
-// -- converters model -> view -----------------------------------------------------
-
-function insertText() {
-	return ( evt, data, consumable, conversionApi ) => {
-		consumable.consume( data.item, 'insert' );
-
-		const viewPosition = conversionApi.mapper.toViewPosition( data.range.start );
-		const viewText = new ViewText( data.item.data );
-		let node;
-
-		if ( count( data.item.getAttributes() ) ) {
-			node = new ViewElement( 'model-text-with-attributes', data.item.getAttributes(), [ viewText ] );
-		} else {
-			node = viewText;
-		}
-
-		viewWriter.insert( viewPosition, node );
-
-		evt.stop();
-	};
-}

+ 1 - 1
packages/ckeditor5-engine/tests/model/composer/deletecontents.js

@@ -94,7 +94,7 @@ describe( 'Delete utils', () => {
 
 				deleteContents( document.batch(), document.selection );
 
-				expect( getData( document ) ).to.equal( '<$text bold="true">fo</$text>[]ar' );
+				expect( getData( document ) ).to.equal( '<$text bold="true">fo[]</$text>ar' );
 				expect( document.selection.getAttribute( 'bold' ) ).to.equal( 'true' );
 			} );
 

+ 1 - 1
packages/ckeditor5-engine/tests/model/composer/modifyselection.js

@@ -303,7 +303,7 @@ describe( 'Delete utils', () => {
 
 					modifySelection( document.selection, { direction: 'backward' } );
 
-					expect( stringify( document.getRoot(), document.selection ) ).to.equal( '<p><$text bold="true">foo</$text>[]b</p>' );
+					expect( stringify( document.getRoot(), document.selection ) ).to.equal( '<p><$text bold="true">foo[]</$text>b</p>' );
 					expect( document.selection.getAttribute( 'bold' ) ).to.equal( 'true' );
 				} );
 			} );