Browse Source

Fixed issues with writring ranges order in view test tools.

Szymon Kupś 9 years ago
parent
commit
39158e82e0

+ 61 - 1
packages/ckeditor5-engine/tests/_utils-tests/view.js

@@ -71,6 +71,15 @@ describe( 'view test utils', () => {
 			expect( getData( p, selection ) ).to.equal( '<p><b>f{ooba}r</b><b>bazqux</b></p>' );
 		} );
 
+		it( 'should write collapsed selection ranges inside texts', () => {
+			const text = new Text( 'foobar' );
+			const p = new Element( 'p', null, text );
+			const range = Range.createFromParentsAndOffsets( text, 0, text, 0 );
+			const selection = new Selection();
+			selection.addRange( range );
+			expect( getData( p, selection ) ).to.equal( '<p>{}foobar</p>' );
+		} );
+
 		it( 'should write ranges that start inside text end ends between elements', () => {
 			const text1 = new Text( 'foobar' );
 			const text2 = new Text( 'bazqux' );
@@ -110,6 +119,57 @@ describe( 'view test utils', () => {
 			expect( getData( fragment, null ) ).to.equal( '<b>foobar</b><b>bazqux</b>' );
 		} );
 
-		// TODO: test when range is outside element/text
+		it( 'should not write ranges outside elements', () => {
+			const text = new Text( 'foobar' );
+			const b = new Element( 'b', null, text );
+			const p = new Element( 'p', null, b );
+			const range1 = Range.createFromParentsAndOffsets( p, 0, p, 5 );
+			const range2 = Range.createFromParentsAndOffsets( p, -1, p, 1 );
+			const range3 = Range.createFromParentsAndOffsets( text, 0, text, 7 );
+			const range4 = Range.createFromParentsAndOffsets( text, -1, text, 2 );
+			const range5 = Range.createFromParentsAndOffsets( text, 6, text, 8 );
+			const selection = new Selection();
+			selection.addRange( range1 );
+			expect( getData( p, selection ) ).to.equal( '<p>[<b>foobar</b></p>' );
+			selection.setRanges( [ range2 ] );
+			expect( getData( p, selection ) ).to.equal( '<p><b>foobar</b>]</p>' );
+			selection.setRanges( [ range3 ] );
+			expect( getData( p, selection ) ).to.equal( '<p><b>{foobar</b></p>' );
+			selection.setRanges( [ range4 ] );
+			expect( getData( p, selection ) ).to.equal( '<p><b>fo}obar</b></p>' );
+			selection.setRanges( [ range5 ] );
+			expect( getData( p, selection ) ).to.equal( '<p><b>foobar{</b></p>' );
+		} );
+
+		it( 'should write multiple ranges from selection #1', () => {
+			const text1 = new Text( 'foobar' );
+			const text2 = new Text( 'bazqux' );
+			const b1 = new Element( 'b', null, text1 );
+			const b2 = new Element( 'b', null, text2 );
+			const p = new Element( 'p', null, [ b1, b2 ] );
+			const range1 = Range.createFromParentsAndOffsets( p, 0, p, 1 );
+			const range2 = Range.createFromParentsAndOffsets( p, 1, p, 1 );
+			// const range3 = Range.createFromParentsAndOffsets( text2, 3, text2, 4 );
+			// const range4 = Range.createFromParentsAndOffsets( p, 1, p, 1 );
+			const selection = new Selection();
+			selection.setRanges( [ range2, range1 ] );
+
+			expect( getData( p, selection ) ).to.equal( '<p>[<b>foobar</b>][]<b>bazqux</b></p>' );
+		} );
+
+		it( 'should write multiple ranges from selection #2', () => {
+			const text1 = new Text( 'foobar' );
+			const text2 = new Text( 'bazqux' );
+			const b = new Element( 'b', null, text1 );
+			const p = new Element( 'p', null, [ b, text2 ] );
+			const range1 = Range.createFromParentsAndOffsets( p, 0, p, 1 );
+			const range2 = Range.createFromParentsAndOffsets( text2, 0, text2, 3 );
+			const range3 = Range.createFromParentsAndOffsets( text2, 3, text2, 4 );
+			const range4 = Range.createFromParentsAndOffsets( p, 1, p, 1 );
+			const selection = new Selection();
+			selection.setRanges( [ range1, range2, range3, range4 ] );
+
+			expect( getData( p, selection ) ).to.equal( '<p>[<b>foobar</b>][]{baz}{q}ux</p>' );
+		} );
 	} );
 } );

+ 48 - 40
packages/ckeditor5-engine/tests/_utils/view.js

@@ -129,7 +129,7 @@ class ViewStringify {
 	 * Calls `callback` with parsed chunks of string data.
 	 *
 	 * @private
-	 * @param root
+	 * @param {engine.treeView.DocumentFragment|engine.treeView.Element|engine.treeView.Text} root
 	 * @param {Function} callback
 	 */
 	_walkView( root, callback ) {
@@ -141,12 +141,12 @@ class ViewStringify {
 			}
 
 			let offset = 0;
-			this._checkElementRanges( root, offset, callback );
+			callback( this._stringifyElementRanges( root, offset ) );
 
 			for ( let child of root.getChildren() ) {
 				this._walkView( child, callback );
 				offset++;
-				this._checkElementRanges( root, offset, callback );
+				callback( this._stringifyElementRanges( root, offset ) );
 			}
 
 			if ( isElement ) {
@@ -155,30 +155,38 @@ class ViewStringify {
 		}
 
 		if ( root instanceof Text ) {
-			callback( this._checkTextRanges( root ) );
+			callback( this._stringifyTextRanges( root ) );
 		}
 	}
 
 	/**
 	 * Checks if given {@link engine.treeView.Element Element} has {@link engine.treeView.Range#start range start} or
-	 * {@link engine.treeView.Range#start range end} placed at given offset. Calls `callback` function each time range
-	 * start or end is found.
+	 * {@link engine.treeView.Range#start range end} placed at given offset and returns its string representation.
 	 *
 	 * @private
 	 * @param {engine.treeView.Element} element
 	 * @param {Number} offset
-	 * @param {Function} callback
 	 */
-	_checkElementRanges( element, offset, callback ) {
+	_stringifyElementRanges( element, offset ) {
+		let start = '';
+		let end = '';
+		let collapsed = '';
+
 		for ( let range of this.ranges ) {
 			if ( range.start.parent == element && range.start.offset === offset ) {
-				callback( ELEMENT_RANGE_START_TOKEN );
+				if ( range.isCollapsed ) {
+					collapsed += ELEMENT_RANGE_START_TOKEN + ELEMENT_RANGE_END_TOKEN;
+				} else {
+					start += ELEMENT_RANGE_START_TOKEN;
+				}
 			}
 
-			if ( range.end.parent === element && range.end.offset === offset ) {
-				callback( ELEMENT_RANGE_END_TOKEN );
+			if ( range.end.parent === element && range.end.offset === offset && !range.isCollapsed ) {
+				end += ELEMENT_RANGE_END_TOKEN;
 			}
 		}
+
+		return end + collapsed + start;
 	}
 
 	/**
@@ -187,25 +195,43 @@ class ViewStringify {
 	 * with range delimiters placed inside.
 	 *
 	 * @private
-	 * @param {engine.treeView.Text} textNode
+	 * @param {engine.treeView.Text} node
 	 */
-	_checkTextRanges( textNode ) {
-		let result = textNode.data;
-		let textOffset = 0;
+	_stringifyTextRanges( node ) {
+		const length = node.data.length;
+		let result = node.data.split( '' );
+
+		// Add one more element for ranges ending after last character in text.
+		result[ length ] = '';
+
+		// Represent each letter as object with information about opening/closing ranges at each offset.
+		result = result.map( ( letter ) => {
+			return {
+				letter: letter,
+				start: '',
+				end: '',
+				collapsed: ''
+			};
+		}  );
 
 		for ( let range of this.ranges ) {
-			if ( range.start.parent == textNode ) {
-				result = ViewStringify._insertToString( result, range.start.offset + textOffset, TEXT_RANGE_START_TOKEN );
-				textOffset++;
+			const start = range.start;
+			const end = range.end;
+
+			if ( start.parent == node && start.offset >= 0 && start.offset <= length ) {
+				if ( range.isCollapsed ) {
+					result[ end.offset ].collapsed += TEXT_RANGE_START_TOKEN + TEXT_RANGE_END_TOKEN;
+				} else {
+					result[ start.offset ].start += TEXT_RANGE_START_TOKEN;
+				}
 			}
 
-			if ( range.end.parent == textNode && !range.isCollapsed ) {
-				result = ViewStringify._insertToString( result, range.end.offset + textOffset, TEXT_RANGE_END_TOKEN );
-				textOffset++;
+			if ( end.parent == node && end.offset >= 0 && end.offset <= length && !range.isCollapsed  ) {
+				result[ end.offset ].end += TEXT_RANGE_END_TOKEN;
 			}
 		}
 
-		return result;
+		return result.map( item => item.end + item.collapsed + item.start + item.letter ).join( '' );
 	}
 
 	/**
@@ -303,22 +329,4 @@ class ViewStringify {
 
 		return attributes.join( ' ' );
 	}
-
-	/**
-	 * Inserts given text at specified index in input text.
-	 * If index is outside input text boundaries - returns same, unmodified string.
-	 *
-	 * @private
-	 * @param {String} input Input string.
-	 * @param {Number} index Index where to insert inside input string.
-	 * @param {String} insert Text to insert.
-	 * @returns {string}
-	 */
-	static _insertToString( input, index, insert ) {
-		if ( index < 0 || index > input.length ) {
-			return input;
-		}
-
-		return input.substr( 0, index ) + insert + input.substr( index );
-	}
 }