Parcourir la source

Added Position.isTouching.

Szymon Cofalik il y a 10 ans
Parent
commit
7955b43b9b

+ 55 - 3
packages/ckeditor5-engine/src/treemodel/position.js

@@ -354,6 +354,58 @@ CKEDITOR.define( [ 'treemodel/rootelement', 'utils', 'ckeditorerror' ], ( RootEl
 			return this.compareWith( otherPosition ) == SAME;
 		}
 
+		/**
+		 * Checks whether this position is touching given position. Positions touch when there are no characters
+		 * or empty nodes in a range between them. Technically, those positions are not equal but in many cases
+		 * they are very similar or even indistinguishable when they touch.
+		 *
+		 * @param {treeModel.Position} otherPosition Position to compare with.
+		 * @returns {Boolean} True if positions touch.
+		 */
+		isTouching( otherPosition ) {
+			let left = null;
+			let right = null;
+			let compare = this.compareWith( otherPosition );
+
+			switch ( compare ) {
+				case SAME:
+					return true;
+
+				case BEFORE:
+					left = this;
+					right = otherPosition;
+					break;
+
+				case AFTER:
+					left = otherPosition;
+					right = this;
+					break;
+
+				default:
+					return false;
+			}
+
+			while ( left.path.length + right.path.length ) {
+				if ( left.isEqual( right ) ) {
+					return true;
+				}
+
+				if ( left.path.length > right.path.length ) {
+					if ( left.nodeAfter !== null ) {
+						return false;
+					}
+
+					left = Position.createAfter( left.parent );
+				} else {
+					if ( right.nodeBefore !== null ) {
+						return false;
+					}
+
+					right = Position.createBefore( right.parent );
+				}
+			}
+		}
+
 		/**
 		 * Creates a new position after given node.
 		 *
@@ -371,7 +423,7 @@ CKEDITOR.define( [ 'treemodel/rootelement', 'utils', 'ckeditorerror' ], ( RootEl
 				throw new CKEditorError( 'position-after-root: You can not make position after root.', { root: node } );
 			}
 
-			return Position.createFromParentAndOffset( node.parent, node.getIndex() + 1 );
+			return this.createFromParentAndOffset( node.parent, node.getIndex() + 1 );
 		}
 
 		/**
@@ -391,7 +443,7 @@ CKEDITOR.define( [ 'treemodel/rootelement', 'utils', 'ckeditorerror' ], ( RootEl
 				throw new CKEditorError( 'position-before-root: You can not make position before root.', { root: node } );
 			}
 
-			return Position.createFromParentAndOffset( node.parent, node.getIndex() );
+			return this.createFromParentAndOffset( node.parent, node.getIndex() );
 		}
 
 		/**
@@ -406,7 +458,7 @@ CKEDITOR.define( [ 'treemodel/rootelement', 'utils', 'ckeditorerror' ], ( RootEl
 
 			path.push( offset );
 
-			return new Position( parent.root, path );
+			return new this( parent.root, path );
 		}
 
 		/**

+ 57 - 0
packages/ckeditor5-engine/tests/treemodel/position.js

@@ -316,6 +316,63 @@ describe( 'position', () => {
 		} );
 	} );
 
+	describe( 'isTouching', () => {
+		it( 'should return true if positions are same', () => {
+			let position = new Position( root, [ 1, 1, 1 ] );
+			let result = position.isTouching( new Position( root, [ 1, 1, 1 ] ) );
+
+			expect( result ).to.be.true;
+		} );
+
+		it( 'should return true if given position is in next node and there are no whole nodes before it', () => {
+			let positionA = new Position( root, [ 1 ] );
+			let positionB = new Position( root, [ 1, 0, 0 ] );
+
+			expect( positionA.isTouching( positionB ) ).to.be.true;
+			expect( positionB.isTouching( positionA ) ).to.be.true;
+		} );
+
+		it( 'should return true if given position is in previous node and there are no whole nodes after it', () => {
+			let positionA = new Position( root, [ 2 ] );
+			let positionB = new Position( root, [ 1, 1, 3 ] );
+
+			expect( positionA.isTouching( positionB ) ).to.be.true;
+			expect( positionB.isTouching( positionA ) ).to.be.true;
+		} );
+
+		it( 'should return true if positions are in different sub-trees but there are no whole nodes between them', () => {
+			let positionA = new Position( root, [ 1, 0, 3 ] );
+			let positionB = new Position( root, [ 1, 1, 0 ] );
+
+			expect( positionA.isTouching( positionB ) ).to.be.true;
+			expect( positionB.isTouching( positionA ) ).to.be.true;
+		} );
+
+		it( 'should return false if there are whole nodes between positions', () => {
+			let positionA = new Position( root, [ 2 ] );
+			let positionB = new Position( root, [ 1, 0, 3 ] );
+
+			expect( positionA.isTouching( positionB ) ).to.be.false;
+			expect( positionB.isTouching( positionA ) ).to.be.false;
+		} );
+
+		it( 'should return false if there are whole nodes between positions', () => {
+			let positionA = new Position( root, [ 1, 0, 3 ] );
+			let positionB = new Position( root, [ 1, 1, 1 ] );
+
+			expect( positionA.isTouching( positionB ) ).to.be.false;
+			expect( positionB.isTouching( positionA ) ).to.be.false;
+		} );
+
+		it( 'should return false if positions are in different roots', () => {
+			let positionA = new Position( root, [ 1, 0, 3 ] );
+			let positionB = new Position( otherRoot, [ 1, 1, 0 ] );
+
+			expect( positionA.isTouching( positionB ) ).to.be.false;
+			expect( positionB.isTouching( positionA ) ).to.be.false;
+		} );
+	} );
+
 	describe( 'compareWith', () => {
 		it( 'should return Position.SAME if positions are same', () => {
 			const position = new Position( root, [ 1, 2, 3 ] );