Parcourir la source

Changed: conversion.Mapper recognizes view.UIElement and returns 0 as its model length.

Szymon Cofalik il y a 8 ans
Parent
commit
ab4e688a59

+ 19 - 10
packages/ckeditor5-engine/src/conversion/mapper.js

@@ -13,6 +13,7 @@ import ModelRange from '../model/range';
 import ViewPosition from '../view/position';
 import ViewRange from '../view/range';
 import ViewText from '../view/text';
+import ViewUIElement from '../view/uielement';
 
 import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
 import mix from '@ckeditor/ckeditor5-utils/src/mix';
@@ -110,6 +111,8 @@ export default class Mapper {
 	/**
 	 * Gets the corresponding model element.
 	 *
+	 * **Note:** {@link module:engine/view/uielement~UIElement} does not have corresponding element in model.
+	 *
 	 * @param {module:engine/view/element~Element} viewElement View element.
 	 * @returns {module:engine/model/element~Element|undefined} Corresponding model element or `undefined` if not found.
 	 */
@@ -312,29 +315,35 @@ export default class Mapper {
 	 * Gets the length of the view element in the model.
 	 *
 	 * The length is calculated as follows:
-	 * * length of a {@link module:engine/view/text~Text text node}
-	 * is equal to the length of it's {@link module:engine/view/text~Text#data data},
-	 * * length of a mapped {@link module:engine/view/element~Element element} is equal to 1 or to the value evaluated by the callback
-	 * added through {@link module:engine/conversion/mapper~Mapper#registerViewToModelLength},
+	 * * if {@link ~registerViewToModelLength length mapping callback} is provided for given `viewNode` it is used to
+	 * evaluate model length (`viewNode` is used as first and only parameter passed to the callback),
+	 * * length of a {@link module:engine/view/text~Text text node} is equal to the length of it's
+	 * {@link module:engine/view/text~Text#data data},
+	 * * length of a {@link module:engine/view/uielement~UIElement ui element} is equal to 0,
+	 * * length of a mapped {@link module:engine/view/element~Element element} is equal to 1,
 	 * * length of a not-mapped {@link module:engine/view/element~Element element} is equal to the length of it's children.
 	 *
 	 * Examples:
 	 *
-	 *		foo                     -> 3 // Text length is equal to it's data length.
-	 *		<p>foo</p>              -> 1 // Length of an element which is mapped is by default equal to 1.
-	 *		<b>foo</b>              -> 3 // Length of an element which is not mapped is a length of its children.
-	 *		<div><p>x</p><p>y</p>   -> 2 // Assuming that <div> is not mapped and <p> are mapped.
+	 *		foo                          -> 3 // Text length is equal to it's data length.
+	 *		<p>foo</p>                   -> 1 // Length of an element which is mapped is by default equal to 1.
+	 *		<b>foo</b>                   -> 3 // Length of an element which is not mapped is a length of its children.
+	 *		<div><p>x</p><p>y</p></div>  -> 2 // Assuming that <div> is not mapped and <p> are mapped.
 	 *
 	 * @param {module:engine/view/element~Element} viewNode View node.
 	 * @returns {Number} Length of the node in the tree model.
 	 */
 	getModelLength( viewNode ) {
-		if ( this._viewToModelMapping.has( viewNode ) ) {
+		if ( this._viewToModelLengthCallbacks.get( viewNode.name ) ) {
 			const callback = this._viewToModelLengthCallbacks.get( viewNode.name );
 
-			return callback ? callback( viewNode ) : 1;
+			return callback( viewNode );
+		} else if ( this._viewToModelMapping.has( viewNode ) ) {
+			return 1;
 		} else if ( viewNode instanceof ViewText ) {
 			return viewNode.data.length;
+		} else if ( viewNode instanceof ViewUIElement ) {
+			return 0;
 		} else {
 			let len = 0;
 

+ 52 - 1
packages/ckeditor5-engine/tests/conversion/mapper.js

@@ -12,6 +12,7 @@ import ModelPosition from '../../src/model/position';
 import ModelRange from '../../src/model/range';
 
 import ViewElement from '../../src/view/element';
+import ViewUIElement from '../../src/view/uielement';
 import ViewText from '../../src/view/text';
 import ViewPosition from '../../src/view/position';
 import ViewRange from '../../src/view/range';
@@ -469,7 +470,7 @@ describe( 'Mapper', () => {
 		} );
 	} );
 
-	describe( 'List mapping (test registerViewToModelLengthCallback)', () => {
+	describe( 'List mapping (test registerViewToModelLength)', () => {
 		function createToModelTest( viewElement, viewOffset, modelElement, modelOffset ) {
 			const viewPosition = new ViewPosition( viewElement, viewOffset );
 			const modelPosition = mapper.toModelPosition( viewPosition );
@@ -538,4 +539,54 @@ describe( 'Mapper', () => {
 			it( 'should transform viewList 2', () => createToModelTest( viewList, 2, modelRoot, 4 ) );
 		} );
 	} );
+
+	describe( 'getModelLength', () => {
+		let mapper;
+
+		beforeEach( () => {
+			mapper = new Mapper();
+		} );
+
+		it( 'should return length according to callback added by registerViewToModelLength', () => {
+			const viewElement = new ViewElement( 'span' );
+
+			mapper.registerViewToModelLength( 'span', () => 4 );
+
+			expect( mapper.getModelLength( viewElement ) ).to.equal( 4 );
+		} );
+
+		it( 'should return 1 for mapped elements', () => {
+			const viewElement = new ViewElement( 'span' );
+			const modelElement = new ModelElement( 'span' );
+			mapper.bindElements( modelElement, viewElement );
+
+			expect( mapper.getModelLength( viewElement ) ).to.equal( 1 );
+		} );
+
+		it( 'should return 0 for ui elements', () => {
+			const viewUiElement = new ViewUIElement( 'span' );
+
+			expect( mapper.getModelLength( viewUiElement ) ).to.equal( 0 );
+		} );
+
+		it( 'should return length of data for text nodes', () => {
+			const viewText = new ViewText( 'foo' );
+
+			expect( mapper.getModelLength( viewText ) ).to.equal( 3 );
+		} );
+
+		it( 'should return sum of length of children for unmapped element', () => {
+			const modelP = new ModelElement( 'p' );
+			const viewP = new ViewElement( 'p' );
+			const viewUi = new ViewUIElement( 'span' );
+			const viewFoo = new ViewText( 'foo' );
+			const viewCallback = new ViewElement( 'xxx' );
+			const viewDiv = new ViewElement( 'div', null, [ viewP, viewUi, viewFoo, viewCallback ] );
+
+			mapper.bindElements( modelP, viewP );
+			mapper.registerViewToModelLength( 'xxx', () => 2 );
+
+			expect( mapper.getModelLength( viewDiv ) ).to.equal( 6 );
+		} );
+	} );
 } );