浏览代码

Introduces Range and RangeIterator. Changed position from path to node and offset.

Piotr Jasiun 10 年之前
父节点
当前提交
fc8227c0ab

+ 44 - 35
packages/ckeditor5-utils/src/document/position.js

@@ -16,49 +16,58 @@ CKEDITOR.define( function() {
 		/**
 		 * Create a position.
 		 *
-		 * @param {document.node} node Node the position is next to.
-		 * @param {Number} position Possible options: Position.BEFORE or Position.AFTER
+		 * @param {document.element} parent Parents element.
+		 * @param {Number} offset Offset in that element.
 		 */
-		constructor( node, position ) {
-			/**
-			 * Position of the node it the tree. For example:
-			 *
-			 * root          Before: []          After: []
-			 *  |- p         Before: [ 0 ]       After: [ 1 ]
-			 *  |- ul        Before: [ 1 ]       After: [ 2 ]
-			 *     |- li     Before: [ 1, 0 ]    After: [ 1, 1 ]
-			 *     |  |- f   Before: [ 1, 0, 0 ] After: [ 1, 0, 1 ]
-			 *     |  |- o   Before: [ 1, 0, 1 ] After: [ 1, 0, 2 ]
-			 *     |  |- o   Before: [ 1, 0, 2 ] After: [ 1, 0, 3 ]
-			 *     |- li     Before: [ 1, 1 ]    After: [ 1, 2 ]
-			 *        |- b   Before: [ 1, 1, 0 ] After: [ 1, 1, 1 ]
-			 *        |- a   Before: [ 1, 1, 1 ] After: [ 1, 1, 2 ]
-			 *        |- r   Before: [ 1, 1, 2 ] After: [ 1, 1, 3 ]
-			 *
-			 * @type {Array}
-			 */
-			this.position = [];
+		constructor( parent, offset ) {
+			this.parent = parent;
+			this.offset = offset;
+		}
+
+		/**
+		 * Position of the node it the tree. For example:
+		 *
+		 * root          Before: []          After: []
+		 *  |- p         Before: [ 0 ]       After: [ 1 ]
+		 *  |- ul        Before: [ 1 ]       After: [ 2 ]
+		 *     |- li     Before: [ 1, 0 ]    After: [ 1, 1 ]
+		 *     |  |- f   Before: [ 1, 0, 0 ] After: [ 1, 0, 1 ]
+		 *     |  |- o   Before: [ 1, 0, 1 ] After: [ 1, 0, 2 ]
+		 *     |  |- o   Before: [ 1, 0, 2 ] After: [ 1, 0, 3 ]
+		 *     |- li     Before: [ 1, 1 ]    After: [ 1, 2 ]
+		 *        |- b   Before: [ 1, 1, 0 ] After: [ 1, 1, 1 ]
+		 *        |- a   Before: [ 1, 1, 1 ] After: [ 1, 1, 2 ]
+		 *        |- r   Before: [ 1, 1, 2 ] After: [ 1, 1, 3 ]
+		 *
+		 * @type {Array}
+		 */
+		get path() {
+			var path = [];
 
-			var parent = node.parent;
+			var parent = this.parent;
 
-			while ( parent && parent.parent ) {
-				this.position.unshift( parent.positionInParent );
+			while ( parent.parent ) {
+				path.unshift( parent.positionInParent );
 				parent = parent.parent;
 			}
 
-			// Root have position [].
-			if ( node.parent ) {
-				if ( position === Position.BEFORE ) {
-					this.position.push( node.positionInParent );
-				} else {
-					this.position.push( node.positionInParent + 1 );
-				}
-			}
+			path.push( this.offset );
+
+			return path;
+		}
+
+		get nodeBefore() {
+			return this.parent.children[ this.offset - 1 ] || null;
 		}
-	}
 
-	Position.BEFORE = -1;
-	Position.AFTER = 1;
+		get nodeAfter() {
+			return this.parent.children[ this.offset ] || null;
+		}
+
+		equals( otherPossition ) {
+			return this.offset === otherPossition.offset && this.parent === otherPossition.parent;
+		}
+	}
 
 	return Position;
 } );

+ 9 - 1
packages/ckeditor5-utils/src/document/range.js

@@ -5,7 +5,7 @@
 
 'use strict';
 
-CKEDITOR.define( function() {
+CKEDITOR.define( [ 'document/rangeiterator' ], function( RangeIterator ) {
 	/**
 	 * Range class.
 	 *
@@ -33,6 +33,14 @@ CKEDITOR.define( function() {
 			 */
 			this.end = end;
 		}
+
+		equals( otherRange ) {
+			return this.start === otherRange.start && this.end === otherRange.end;
+		}
+
+		[ Symbol.iterator ]() {
+			return new RangeIterator( this );
+		}
 	}
 
 	return Range;

+ 120 - 0
packages/ckeditor5-utils/src/document/rangeiterator.js

@@ -0,0 +1,120 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+CKEDITOR.define( [
+	'document/character',
+	'document/element',
+	'document/position'
+], function( Character, Element, Position ) {
+	var OPENING_TAG = 0;
+	var CLOSING_TAG = 1;
+	var CHARACTER = 2;
+
+	/**
+	 * Range iterator class.
+	 *
+	 * @class document.Range
+	 */
+	class RangeIterator {
+		/**
+		 * Create a range iterator.
+		 *
+		 * @param {document.range} range Range to define boundaries of the iterator.
+		 */
+		constructor( range, iteratorPosition ) {
+			/**
+			 * Start position.
+			 *
+			 * @type {document.Position}
+			 */
+			this.range = range;
+
+			this.position = iteratorPosition || range.start;
+		}
+
+		next() {
+			var position = this.position;
+
+			if ( position.eqals( this.range.end ) ) {
+				return { done: true };
+			}
+
+			var nodeAfter = position.nodeAfter;
+
+			if ( nodeAfter instanceof Element ) {
+				this.position = new Position( nodeAfter, 0 );
+
+				return formatReturnValue( OPENING_TAG, nodeAfter );
+			} else if ( nodeAfter instanceof Character ) {
+				this.position = new Position( position.parent, this.offset + 1 );
+
+				return formatReturnValue( CHARACTER, nodeAfter );
+			} else {
+				this.position = new Position( position.parent.parent, position.parent.positionInParent + 1 );
+
+				return formatReturnValue( CLOSING_TAG, this.position.nodeBefore );
+			}
+		}
+
+		get previous() {
+			var position = this.position;
+
+			if ( position.eqals( this.range.start ) ) {
+				return { done: true };
+			}
+
+			var nodeBefore = this.nodeBefore;
+
+			if ( nodeBefore instanceof Element ) {
+				this.position = new Position( nodeBefore, nodeBefore.children.length );
+
+				return formatReturnValue( CLOSING_TAG, nodeBefore );
+			} else if ( nodeBefore instanceof Character ) {
+				this.position = new Position( position.parent, this.offset - 1 );
+
+				return formatReturnValue( CHARACTER, nodeBefore );
+			} else {
+				this.position = new Position( position.parent.parent, position.parent.positionInParent );
+
+				return formatReturnValue( OPENING_TAG, this.position.nodeAfter );
+			}
+		}
+	}
+
+	function formatReturnValue( type, node ) {
+		return {
+			done: false,
+			value: {
+				type: type,
+				node: node
+			}
+		};
+	}
+
+	/**
+	 * Flag for linear data opening tag.
+	 *
+	 * @readonly
+	 */
+	RangeIterator.OPENING_TAG = OPENING_TAG;
+
+	/**
+	 * Flag for linear data closing tag.
+	 *
+	 * @readonly
+	 */
+	RangeIterator.CLOSING_TAG = CLOSING_TAG;
+
+	/**
+	 * Flag for linear data character.
+	 *
+	 * @readonly
+	 */
+	RangeIterator.CHARACTER = CHARACTER;
+
+	return RangeIterator;
+} );

+ 64 - 24
packages/ckeditor5-utils/tests/document/position.js

@@ -65,47 +65,87 @@ describe( 'position', function() {
 		li2.children.push( r );
 	} );
 
-	it( 'should create positions before elements', function() {
+	it( 'should have path', function() {
 		var Position = modules[ 'document/position' ];
 
-		expect( new Position( root, Position.BEFORE ) ).to.have.property( 'position' ).that.deep.equals( [] );
+		expect( new Position( root, 0 ) ).to.have.property( 'path' ).that.deep.equals( [ 0 ] );
+		expect( new Position( root, 1 ) ).to.have.property( 'path' ).that.deep.equals( [ 1 ] );
+		expect( new Position( root, 2 ) ).to.have.property( 'path' ).that.deep.equals( [ 2 ] );
 
-		expect( new Position( p, Position.BEFORE ) ).to.have.property( 'position' ).that.deep.equals( [ 0 ] );
+		expect( new Position( p, 0 ) ).to.have.property( 'path' ).that.deep.equals( [ 0, 0 ] );
 
-		expect( new Position( ul, Position.BEFORE ) ).to.have.property( 'position' ).that.deep.equals( [ 1 ] );
+		expect( new Position( ul, 0 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0 ] );
+		expect( new Position( ul, 1 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1 ] );
+		expect( new Position( ul, 2 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 2 ] );
 
-		expect( new Position( li1, Position.BEFORE ) ).to.have.property( 'position' ).that.deep.equals( [ 1, 0 ] );
+		expect( new Position( li1, 0 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 0 ] );
+		expect( new Position( li1, 1 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 1 ] );
+		expect( new Position( li1, 2 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 2 ] );
+		expect( new Position( li1, 3 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 3 ] );
+	} );
+
+	it( 'should have nodeBefore', function() {
+		var Position = modules[ 'document/position' ];
+
+		expect( new Position( root, 0 ) ).to.have.property( 'nodeBefore' ).that.is.null;
+		expect( new Position( root, 1 ) ).to.have.property( 'nodeBefore' ).that.equals( p );
+		expect( new Position( root, 2 ) ).to.have.property( 'nodeBefore' ).that.equals( ul );
+
+		expect( new Position( p, 0 ) ).to.have.property( 'nodeBefore' ).that.is.null;
+
+		expect( new Position( ul, 0 ) ).to.have.property( 'nodeBefore' ).that.is.null;
+		expect( new Position( ul, 1 ) ).to.have.property( 'nodeBefore' ).that.equals( li1 );
+		expect( new Position( ul, 2 ) ).to.have.property( 'nodeBefore' ).that.equals( li2 );
+
+		expect( new Position( li1, 0 ) ).to.have.property( 'nodeBefore' ).that.is.null;
+		expect( new Position( li1, 1 ) ).to.have.property( 'nodeBefore' ).that.equals( f );
+		expect( new Position( li1, 2 ) ).to.have.property( 'nodeBefore' ).that.equals( o );
+		expect( new Position( li1, 3 ) ).to.have.property( 'nodeBefore' ).that.equals( z );
+	} );
 
-		expect( new Position( f, Position.BEFORE ) ).to.have.property( 'position' ).that.deep.equals( [ 1, 0, 0 ] );
-		expect( new Position( o, Position.BEFORE ) ).to.have.property( 'position' ).that.deep.equals( [ 1, 0, 1 ] );
-		expect( new Position( z, Position.BEFORE ) ).to.have.property( 'position' ).that.deep.equals( [ 1, 0, 2 ] );
+	it( 'should have nodeAfter', function() {
+		var Position = modules[ 'document/position' ];
+
+		expect( new Position( root, 0 ) ).to.have.property( 'nodeAfter' ).that.equals( p );
+		expect( new Position( root, 1 ) ).to.have.property( 'nodeAfter' ).that.equals( ul );
+		expect( new Position( root, 2 ) ).to.have.property( 'nodeAfter' ).that.is.null;
 
-		expect( new Position( li2, Position.BEFORE ) ).to.have.property( 'position' ).that.deep.equals( [ 1, 1 ] );
+		expect( new Position( p, 0 ) ).to.have.property( 'nodeAfter' ).that.is.null;
 
-		expect( new Position( b, Position.BEFORE ) ).to.have.property( 'position' ).that.deep.equals( [ 1, 1, 0 ] );
-		expect( new Position( a, Position.BEFORE ) ).to.have.property( 'position' ).that.deep.equals( [ 1, 1, 1 ] );
-		expect( new Position( r, Position.BEFORE ) ).to.have.property( 'position' ).that.deep.equals( [ 1, 1, 2 ] );
+		expect( new Position( ul, 0 ) ).to.have.property( 'nodeAfter' ).that.equals( li1 );
+		expect( new Position( ul, 1 ) ).to.have.property( 'nodeAfter' ).that.equals( li2 );
+		expect( new Position( ul, 2 ) ).to.have.property( 'nodeAfter' ).that.is.null;
+
+		expect( new Position( li1, 0 ) ).to.have.property( 'nodeAfter' ).that.equals( f );
+		expect( new Position( li1, 1 ) ).to.have.property( 'nodeAfter' ).that.equals( o );
+		expect( new Position( li1, 2 ) ).to.have.property( 'nodeAfter' ).that.equals( z );
+		expect( new Position( li1, 3 ) ).to.have.property( 'nodeAfter' ).that.is.null;
 	} );
 
-	it( 'should create positions after elements', function() {
+	it( 'should equals another position with the same offset and node', function() {
 		var Position = modules[ 'document/position' ];
 
-		expect( new Position( root, Position.AFTER ) ).to.have.property( 'position' ).that.deep.equals( [] );
+		var position = new Position( root, 0 );
+		var samePosition = new Position( root, 0 );
+
+		expect( position.equals( samePosition ) ).to.be.true;
+	} );
 
-		expect( new Position( p, Position.AFTER ) ).to.have.property( 'position' ).that.deep.equals( [ 1 ] );
+	it( 'should not equals another position with the different offset', function() {
+		var Position = modules[ 'document/position' ];
 
-		expect( new Position( ul, Position.AFTER ) ).to.have.property( 'position' ).that.deep.equals( [ 2 ] );
+		var position = new Position( root, 0 );
+		var differentOffset = new Position( root, 1 );
 
-		expect( new Position( li1, Position.AFTER ) ).to.have.property( 'position' ).that.deep.equals( [ 1, 1 ] );
+		expect( position.equals( differentOffset ) ).to.be.false;
+	} );
 
-		expect( new Position( f, Position.AFTER ) ).to.have.property( 'position' ).that.deep.equals( [ 1, 0, 1 ] );
-		expect( new Position( o, Position.AFTER ) ).to.have.property( 'position' ).that.deep.equals( [ 1, 0, 2 ] );
-		expect( new Position( z, Position.AFTER ) ).to.have.property( 'position' ).that.deep.equals( [ 1, 0, 3 ] );
+	it( 'should not equals another position with the different node', function() {
+		var Position = modules[ 'document/position' ];
 
-		expect( new Position( li2, Position.AFTER ) ).to.have.property( 'position' ).that.deep.equals( [ 1, 2 ] );
+		var position = new Position( root, 0 );
+		var differentNode = new Position( p, 0 );
 
-		expect( new Position( b, Position.AFTER ) ).to.have.property( 'position' ).that.deep.equals( [ 1, 1, 1 ] );
-		expect( new Position( a, Position.AFTER ) ).to.have.property( 'position' ).that.deep.equals( [ 1, 1, 2 ] );
-		expect( new Position( r, Position.AFTER ) ).to.have.property( 'position' ).that.deep.equals( [ 1, 1, 3 ] );
+		expect( position.equals( differentNode ) ).to.be.false;
 	} );
 } );