瀏覽代碼

Merge pull request #103 from ckeditor/t/96

T/96 Tree Model refactor
Piotr Jasiun 10 年之前
父節點
當前提交
ff9450221b
共有 58 個文件被更改,包括 1674 次插入1444 次删除
  1. 0 124
      packages/ckeditor5-engine/src/document/delta/transaction-base.js
  2. 0 21
      packages/ckeditor5-engine/src/document/transaction.js
  3. 3 3
      packages/ckeditor5-engine/src/treemodel/attribute.js
  4. 21 0
      packages/ckeditor5-engine/src/treemodel/batch.js
  5. 3 3
      packages/ckeditor5-engine/src/treemodel/character.js
  6. 46 41
      packages/ckeditor5-engine/src/treemodel/delta/attributedelta.js
  7. 123 0
      packages/ckeditor5-engine/src/treemodel/delta/batch-base.js
  8. 9 9
      packages/ckeditor5-engine/src/treemodel/delta/delta.js
  9. 10 10
      packages/ckeditor5-engine/src/treemodel/delta/insertdelta.js
  10. 18 18
      packages/ckeditor5-engine/src/treemodel/delta/mergedelta.js
  11. 4 4
      packages/ckeditor5-engine/src/treemodel/delta/register.js
  12. 8 8
      packages/ckeditor5-engine/src/treemodel/delta/removedelta.js
  13. 15 15
      packages/ckeditor5-engine/src/treemodel/delta/splitdelta.js
  14. 34 35
      packages/ckeditor5-engine/src/treemodel/document.js
  15. 14 14
      packages/ckeditor5-engine/src/treemodel/element.js
  16. 13 13
      packages/ckeditor5-engine/src/treemodel/node.js
  17. 10 10
      packages/ckeditor5-engine/src/treemodel/nodelist.js
  18. 34 35
      packages/ckeditor5-engine/src/treemodel/operation/attributeoperation.js
  19. 13 13
      packages/ckeditor5-engine/src/treemodel/operation/insertoperation.js
  20. 17 17
      packages/ckeditor5-engine/src/treemodel/operation/moveoperation.js
  21. 4 4
      packages/ckeditor5-engine/src/treemodel/operation/nooperation.js
  22. 12 12
      packages/ckeditor5-engine/src/treemodel/operation/operation.js
  23. 6 6
      packages/ckeditor5-engine/src/treemodel/operation/reinsertoperation.js
  24. 10 12
      packages/ckeditor5-engine/src/treemodel/operation/removeoperation.js
  25. 42 42
      packages/ckeditor5-engine/src/treemodel/operation/transform.js
  26. 67 57
      packages/ckeditor5-engine/src/treemodel/position.js
  27. 11 11
      packages/ckeditor5-engine/src/treemodel/positioniterator.js
  28. 83 61
      packages/ckeditor5-engine/src/treemodel/range.js
  29. 5 5
      packages/ckeditor5-engine/src/treemodel/rootelement.js
  30. 82 0
      packages/ckeditor5-engine/src/treemodel/smartrange.js
  31. 6 6
      packages/ckeditor5-engine/src/treemodel/text.js
  32. 0 82
      packages/ckeditor5-engine/tests/document/transaction.js
  33. 2 2
      packages/ckeditor5-engine/tests/treemodel/attribute.js
  34. 83 0
      packages/ckeditor5-engine/tests/treemodel/batch.js
  35. 8 8
      packages/ckeditor5-engine/tests/treemodel/character.js
  36. 99 73
      packages/ckeditor5-engine/tests/treemodel/delta/attributedelta.js
  37. 4 4
      packages/ckeditor5-engine/tests/treemodel/delta/delta.js
  38. 12 12
      packages/ckeditor5-engine/tests/treemodel/delta/insertdelta.js
  39. 18 18
      packages/ckeditor5-engine/tests/treemodel/delta/mergedelta.js
  40. 14 14
      packages/ckeditor5-engine/tests/treemodel/delta/removedelta.js
  41. 17 17
      packages/ckeditor5-engine/tests/treemodel/delta/splitdelta.js
  42. 34 34
      packages/ckeditor5-engine/tests/treemodel/document/change-event.js
  43. 14 14
      packages/ckeditor5-engine/tests/treemodel/document/document.js
  44. 8 8
      packages/ckeditor5-engine/tests/treemodel/element.js
  45. 8 8
      packages/ckeditor5-engine/tests/treemodel/node.js
  46. 8 8
      packages/ckeditor5-engine/tests/treemodel/nodelist.js
  47. 94 52
      packages/ckeditor5-engine/tests/treemodel/operation/attributeoperation.js
  48. 24 24
      packages/ckeditor5-engine/tests/treemodel/operation/insertoperation.js
  49. 36 36
      packages/ckeditor5-engine/tests/treemodel/operation/moveoperation.js
  50. 4 4
      packages/ckeditor5-engine/tests/treemodel/operation/nooperation.js
  51. 14 14
      packages/ckeditor5-engine/tests/treemodel/operation/reinsertoperation.js
  52. 16 16
      packages/ckeditor5-engine/tests/treemodel/operation/removeoperation.js
  53. 187 174
      packages/ckeditor5-engine/tests/treemodel/operation/transform.js
  54. 137 127
      packages/ckeditor5-engine/tests/treemodel/position.js
  55. 20 20
      packages/ckeditor5-engine/tests/treemodel/positioniterator.js
  56. 80 56
      packages/ckeditor5-engine/tests/treemodel/range.js
  57. 6 6
      packages/ckeditor5-engine/tests/treemodel/rootelement.js
  58. 4 4
      packages/ckeditor5-engine/tests/treemodel/text.js

+ 0 - 124
packages/ckeditor5-engine/src/document/delta/transaction-base.js

@@ -1,124 +0,0 @@
-/**
- * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-'use strict';
-
-CKEDITOR.define( [ 'ckeditorerror' ], ( CKEditorError ) => {
-	/**
-	 * The transaction class groups document changes (deltas). All deltas grouped in a single transactions can be
-	 * reverted together, so you can think about the transaction as a single undo step. If you want to extend one
-	 * undo step you can call another method on the same transaction object. If you want to create a separate undo step
-	 * you can create a new transaction.
-	 *
-	 * For example to create two separate undo steps you can call:
-	 *
-	 *		doc.createTransaction().insert( firstPosition, 'foo' );
-	 *		doc.createTransaction().insert( secondPosition, 'bar' );
-	 *
-	 * To create a single undo step:
-	 *
-	 *		const transaction = doc.createTransaction()
-	 *		transaction.insert( firstPosition, 'foo' );
-	 *		transaction.insert( secontPosition, 'bar' );
-	 *
-	 * Note that all document modification methods (insert, remove, split, etc.) are chainable so you can shorten code to:
-	 *
-	 *		doc.createTransaction().insert( firstPosition, 'foo' ).insert( secontPosition, 'bar' );
-	 *
-	 * @class document.Transaction
-	 */
-	class Transaction {
-		/**
-		 * Creates transaction instance. Not recommended to use directly, use {@link document.Document#createTransaction}
-		 * instead.
-		 *
-		 * @constructor
-		 * @param {document.Document} doc Document which this transaction changes.
-		 */
-		constructor( doc ) {
-			/**
-			 * Document which this transaction changes.
-			 *
-			 * @readonly
-			 * @type {document.Document}
-			 */
-			this.doc = doc;
-
-			/**
-			 * Array of deltas which compose transaction.
-			 *
-			 * @readonly
-			 * @type {document.delta.Delta[]}
-			 */
-			this.deltas = [];
-		}
-
-		/**
-		 * Adds delta to the transaction instance. All modification methods (insert, remove, split, etc.) use this method
-		 * to add created deltas.
-		 *
-		 * @param {document.delta.Delta} delta Delta to add.
-		 * @return {document.delta.Delta} Added delta.
-		 */
-		addDelta( delta ) {
-			delta.transaction = this;
-			this.deltas.push( delta );
-
-			return delta;
-		}
-
-		/**
-		 * Static method to register transaction methods. To make code scalable transaction do not have modification
-		 * methods built in. They can be registered using this method.
-		 *
-		 * This method checks if there is no naming collision and throw `transaction-register-taken` if the method name
-		 * is already taken.
-		 *
-		 * Beside that no magic happens here, the method is added to the `Transaction` class prototype.
-		 *
-		 * For example:
-		 *
-		 *		Transaction.register( 'insert', function( position, nodes ) {
-		 *			// You can use a class inherit from Delta if that class should handle OT in the special way.
-		 *			const delta = new Delta();
-		 *
-		 * 			// Create operations which should be components of this delta.
-		 *			const operation = new InsertOperation( position, nodes, this.doc.version );
-		 *
-		 *			// Remember to apply every operation, no magic, you need to do it manually.
-		 *			this.doc.applyOperation( operation );
-		 *
-		 *			// Add operation to the delta.
-		 *			delta.addOperation( operation );
-		 *
-		 *			// Add delta to the transaction instance.
-		 *			this.addDelta( delta );
-		 *
-		 * 			// Make this method chainable.
-		 * 			return this;
-		 *		} );
-		 *
-		 * @param {String} name Method name.
-		 * @param {Fuction} creator Method body.
-		 */
-		static register( name, creator ) {
-			if ( Transaction.prototype[ name ] ) {
-				/**
-				 * This transaction method is already taken.
-				 *
-				 * @error transaction-register-taken
-				 * @param {String} name
-				 */
-				throw new CKEditorError(
-					'transaction-register-taken: This transaction method is already taken.',
-					{ name: name } );
-			}
-
-			Transaction.prototype[ name ] = creator;
-		}
-	}
-
-	return Transaction;
-} );

+ 0 - 21
packages/ckeditor5-engine/src/document/transaction.js

@@ -1,21 +0,0 @@
-/**
- * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-'use strict';
-
-// All deltas need to be loaded so they can register themselves as transaction methods.
-//
-// To solve circular dependencies (deltas need to require transaction class), transaction class body is moved
-// to document/delta/transaction-base.
-CKEDITOR.define( [
-	'document/delta/transaction-base',
-	'document/delta/insertdelta',
-	'document/delta/removedelta',
-	'document/delta/changedelta',
-	'document/delta/splitdelta',
-	'document/delta/mergedelta'
-], ( Transaction ) => {
-	return Transaction;
-} );

+ 3 - 3
packages/ckeditor5-engine/src/document/attribute.js → packages/ckeditor5-engine/src/treemodel/attribute.js

@@ -9,7 +9,7 @@ CKEDITOR.define( [ 'utils' ], ( utils ) => {
 	/**
 	 * Attributes can store any additional information for nodes in the data model.
 	 *
-	 * @class document.Attribute
+	 * @class treeModel.Attribute
 	 */
 	class Attribute {
 		/**
@@ -75,7 +75,7 @@ CKEDITOR.define( [ 'utils' ], ( utils ) => {
 		 *		let attr2 = new Attribute( 'foo', { b: 2, a: 1 } );
 		 *		attr1.isEqual( attr2 ); // true
 		 *
-		 * @param {document.Attribute} otherAttr Attribute to compare with.
+		 * @param {treeModel.Attribute} otherAttr Attribute to compare with.
 		 * @returns {Boolean} True if attributes are equal to each other.
 		 */
 		isEqual( otherAttr ) {
@@ -97,7 +97,7 @@ CKEDITOR.define( [ 'utils' ], ( utils ) => {
 		 * @static
 		 * @param {String} key Attribute key.
 		 * @param {Mixed} value Attribute value.
-		 * @returns {document.Attribute} Registered attribute.
+		 * @returns {treeModel.Attribute} Registered attribute.
 		 */
 		static register( key, value ) {
 			const attr = new Attribute( key, value );

+ 21 - 0
packages/ckeditor5-engine/src/treemodel/batch.js

@@ -0,0 +1,21 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+// All deltas need to be loaded so they can register themselves as Batch methods.
+//
+// To solve circular dependencies (deltas need to require Batch class), Batch class body is moved
+// to treemodel/delta/batch-base.
+CKEDITOR.define( [
+	'treemodel/delta/batch-base',
+	'treemodel/delta/insertdelta',
+	'treemodel/delta/removedelta',
+	'treemodel/delta/attributedelta',
+	'treemodel/delta/splitdelta',
+	'treemodel/delta/mergedelta'
+], ( Batch ) => {
+	return Batch;
+} );

+ 3 - 3
packages/ckeditor5-engine/src/document/character.js → packages/ckeditor5-engine/src/treemodel/character.js

@@ -5,18 +5,18 @@
 
 'use strict';
 
-CKEDITOR.define( [ 'document/node' ], ( Node ) => {
+CKEDITOR.define( [ 'treemodel/node' ], ( Node ) => {
 	/**
 	 * Data structure for character stored in the tree data model.
 	 *
-	 * @class Character
+	 * @class treeModel.Character
 	 */
 	class Character extends Node {
 		/**
 		 * Creates character linear item.
 		 *
 		 * @param {String} character Described character.
-		 * @param {Iterable} attrs Iterable collection of {@link document.Attribute attributes}.
+		 * @param {Iterable} attrs Iterable collection of {@link treeModel.Attribute attributes}.
 		 * @constructor
 		 */
 		constructor( character, attrs ) {

+ 46 - 41
packages/ckeditor5-engine/src/document/delta/changedelta.js → packages/ckeditor5-engine/src/treemodel/delta/attributedelta.js

@@ -6,35 +6,36 @@
 'use strict';
 
 CKEDITOR.define( [
-	'document/delta/delta',
-	'document/delta/register',
-	'document/operation/changeoperation',
-	'document/position',
-	'document/range',
-	'document/attribute',
-	'document/element'
-], ( Delta, register, ChangeOperation, Position, Range, Attribute, Element ) => {
+	'treemodel/delta/delta',
+	'treemodel/delta/register',
+	'treemodel/operation/attributeoperation',
+	'treemodel/position',
+	'treemodel/range',
+	'treemodel/positioniterator',
+	'treemodel/attribute',
+	'treemodel/element'
+], ( Delta, register, AttributeOperation, Position, Range, PositionIterator, Attribute, Element ) => {
 	/**
-	 * To provide specific OT behavior and better collisions solving, change methods ({@link document.Transaction#setAttr}
-	 * and {@link document.Transaction#removeAttr}) use `ChangeDelta` class which inherits from the `Delta` class and may
+	 * To provide specific OT behavior and better collisions solving, change methods ({@link treeModel.Batch#setAttr}
+	 * and {@link treeModel.Batch#removeAttr}) use `AttributeDelta` class which inherits from the `Delta` class and may
 	 * overwrite some methods.
 	 *
-	 * @class document.delta.ChangeDelta
+	 * @class treeModel.delta.AttributeDelta
 	 */
-	class ChangeDelta extends Delta {}
+	class AttributeDelta extends Delta {}
 
 	/**
 	 * Sets the value of the attribute of the node or on the range.
 	 *
 	 * @chainable
 	 * @method setAttr
-	 * @memberOf document.Transaction
+	 * @memberOf treeModel.Batch
 	 * @param {String} key Attribute key.
-	 * @param {Mixed} value Attribute new value.
-	 * @param {document.Node|document.Range} nodeOrRange Node or range on which the attribute will be set.
+	 * @param {*} value Attribute new value.
+	 * @param {treeModel.Node|treeModel.Range} nodeOrRange Node or range on which the attribute will be set.
 	 */
 	register( 'setAttr', function( key, value, nodeOrRange ) {
-		change( this, key, value, nodeOrRange );
+		attribute( this, key, value, nodeOrRange );
 
 		return this;
 	} );
@@ -44,26 +45,26 @@ CKEDITOR.define( [
 	 *
 	 * @chainable
 	 * @method removeAttr
-	 * @memberOf document.Transaction
+	 * @memberOf treeModel.Batch
 	 * @param {String} key Attribute key.
-	 * @param {document.Node|document.Range} nodeOrRange Node or range on which the attribute will be removed.
+	 * @param {treeModel.Node|treeModel.Range} nodeOrRange Node or range on which the attribute will be removed.
 	 */
 	register( 'removeAttr', function( key, nodeOrRange ) {
-		change( this, key, null, nodeOrRange );
+		attribute( this, key, null, nodeOrRange );
 
 		return this;
 	} );
 
-	function change( transaction, key, value, nodeOrRange ) {
-		const delta = new ChangeDelta();
+	function attribute( batch, key, value, nodeOrRange ) {
+		const delta = new AttributeDelta();
 
 		if ( nodeOrRange instanceof Range ) {
-			changeRange( transaction.doc, delta, key, value, nodeOrRange );
+			changeRange( batch.doc, delta, key, value, nodeOrRange );
 		} else {
-			changeNode( transaction.doc, delta, key, value, nodeOrRange );
+			changeNode( batch.doc, delta, key, value, nodeOrRange );
 		}
 
-		transaction.addDelta( delta );
+		batch.addDelta( delta );
 	}
 
 	function changeNode( doc, delta, key, value, node ) {
@@ -81,7 +82,7 @@ CKEDITOR.define( [
 				range = new Range( Position.createBefore( node ), Position.createAfter( node ) );
 			}
 
-			const operation = new ChangeOperation(
+			const operation = new AttributeOperation(
 					range,
 					previousValue ? new Attribute( key, previousValue ) : null,
 					value ? new Attribute( key, value ) : null,
@@ -93,7 +94,7 @@ CKEDITOR.define( [
 		}
 	}
 
-	// Because change operation needs to have the same attribute value on the whole range, this function split the range
+	// Because attribute operation needs to have the same attribute value on the whole range, this function split the range
 	// into smaller parts.
 	function changeRange( doc, delta, key, value, range ) {
 		// Position of the last split, the beginning of the new range.
@@ -113,22 +114,26 @@ CKEDITOR.define( [
 		let next = iterator.next();
 
 		while ( !next.done ) {
-			valueAfter = next.value.node.getAttr( key );
-
-			// At the first run of the iterator the position in undefined. We also do not have a valueBefore, but
-			// because valueAfter may be null, valueBefore may be equal valueAfter ( undefined == null ).
-			if ( position && valueBefore != valueAfter ) {
-				// if valueBefore == value there is nothing to change, so we add operation only if these values are different.
-				if ( valueBefore != value ) {
-					addOperation();
+			// We check values only when the range contains given element, that is when the iterator "enters" the element.
+			// To prevent double-checking or not needed checking, we filter-out iterator values for ELEMENT_LEAVE position.
+			if ( next.value.type != PositionIterator.ELEMENT_LEAVE ) {
+				valueAfter = next.value.node.getAttr( key );
+
+				// At the first run of the iterator the position in undefined. We also do not have a valueBefore, but
+				// because valueAfter may be null, valueBefore may be equal valueAfter ( undefined == null ).
+				if ( position && valueBefore != valueAfter ) {
+					// if valueBefore == value there is nothing to change, so we add operation only if these values are different.
+					if ( valueBefore != value ) {
+						addOperation();
+					}
+
+					lastSplitPosition = position;
 				}
 
-				lastSplitPosition = position;
+				position = iterator.position;
+				valueBefore = valueAfter;
 			}
 
-			position = iterator.position;
-			valueBefore = valueAfter;
-
 			next = iterator.next();
 		}
 
@@ -139,7 +144,7 @@ CKEDITOR.define( [
 		}
 
 		function addOperation() {
-			const operation = new ChangeOperation(
+			const operation = new AttributeOperation(
 					new Range( lastSplitPosition, position ),
 					valueBefore ? new Attribute( key, valueBefore ) : null,
 					value ? new Attribute( key, value ) : null,
@@ -151,5 +156,5 @@ CKEDITOR.define( [
 		}
 	}
 
-	return ChangeDelta;
-} );
+	return AttributeDelta;
+} );

+ 123 - 0
packages/ckeditor5-engine/src/treemodel/delta/batch-base.js

@@ -0,0 +1,123 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+CKEDITOR.define( [ 'ckeditorerror' ], ( CKEditorError ) => {
+	/**
+	 * The Batch class groups document changes (deltas). All deltas grouped in a single Batch can be
+	 * reverted together, so you can think about the Batch as a single undo step. If you want to extend one
+	 * undo step you can call another method on the same Batch object. If you want to create a separate undo step
+	 * you can create a new Batch.
+	 *
+	 * For example to create two separate undo steps you can call:
+	 *
+	 *		doc.batch().insert( firstPosition, 'foo' );
+	 *		doc.batch().insert( secondPosition, 'bar' );
+	 *
+	 * To create a single undo step:
+	 *
+	 *		const batch = doc.batch();
+	 *		batch.insert( firstPosition, 'foo' );
+	 *		batch.insert( secondPosition, 'bar' );
+	 *
+	 * Note that all document modification methods (insert, remove, split, etc.) are chainable so you can shorten code to:
+	 *
+	 *		doc.batch().insert( firstPosition, 'foo' ).insert( secondPosition, 'bar' );
+	 *
+	 * @class treeModel.Batch
+	 */
+	class Batch {
+		/**
+		 * Creates Batch instance. Not recommended to use directly, use {@link treeModel.Document#batch} instead.
+		 *
+		 * @constructor
+		 * @param {treeModel.Document} doc Document which this Batch changes.
+		 */
+		constructor( doc ) {
+			/**
+			 * Document which this Batch changes.
+			 *
+			 * @readonly
+			 * @type {treeModel.Document}
+			 */
+			this.doc = doc;
+
+			/**
+			 * Array of deltas which compose Batch.
+			 *
+			 * @readonly
+			 * @type {Array.<treeModel.delta.Delta>}
+			 */
+			this.deltas = [];
+		}
+
+		/**
+		 * Adds delta to the Batch instance. All modification methods (insert, remove, split, etc.) use this method
+		 * to add created deltas.
+		 *
+		 * @param {treeModel.delta.Delta} delta Delta to add.
+		 * @return {treeModel.delta.Delta} Added delta.
+		 */
+		addDelta( delta ) {
+			delta.batch = this;
+			this.deltas.push( delta );
+
+			return delta;
+		}
+
+		/**
+		 * Static method to register Batch methods. To make code scalable Batch do not have modification
+		 * methods built in. They can be registered using this method.
+		 *
+		 * This method checks if there is no naming collision and throws `batch-register-taken` if the method name
+		 * is already taken.
+		 *
+		 * Besides that no magic happens here, the method is added to the `Batch` class prototype.
+		 *
+		 * For example:
+		 *
+		 *		Batch.register( 'insert', function( position, nodes ) {
+		 *			// You can use a class inherit from Delta if that class should handle OT in the special way.
+		 *			const delta = new Delta();
+		 *
+		 * 			// Create operations which should be components of this delta.
+		 *			const operation = new InsertOperation( position, nodes, this.doc.version );
+		 *
+		 *			// Remember to apply every operation, no magic, you need to do it manually.
+		 *			this.doc.applyOperation( operation );
+		 *
+		 *			// Add operation to the delta.
+		 *			delta.addOperation( operation );
+		 *
+		 *			// Add delta to the Batch instance.
+		 *			this.addDelta( delta );
+		 *
+		 * 			// Make this method chainable.
+		 * 			return this;
+		 *		} );
+		 *
+		 * @param {String} name Method name.
+		 * @param {Function} creator Method body.
+		 */
+		static register( name, creator ) {
+			if ( Batch.prototype[ name ] ) {
+				/**
+				 * This batch method name is already taken.
+				 *
+				 * @error batch-register-taken
+				 * @param {String} name
+				 */
+				throw new CKEditorError(
+					'batch-register-taken: This batch method name is already taken.',
+					{ name: name } );
+			}
+
+			Batch.prototype[ name ] = creator;
+		}
+	}
+
+	return Batch;
+} );

+ 9 - 9
packages/ckeditor5-engine/src/document/delta/delta.js → packages/ckeditor5-engine/src/treemodel/delta/delta.js

@@ -12,9 +12,9 @@ CKEDITOR.define( [], () => {
 	 * Delta is a single, from the user action point of view, change in the editable document, like insert, split or
 	 * rename element. Delta is composed of operations, which are unit changes needed to be done to execute user action.
 	 *
-	 * Multiple deltas are grouped into a single {@link document.Transaction}.
+	 * Multiple deltas are grouped into a single {@link treeModel.Batch}.
 	 *
-	 * @class document.delta.Delta
+	 * @class treeModel.delta.Delta
 	 */
 	class Delta {
 		/**
@@ -24,19 +24,19 @@ CKEDITOR.define( [], () => {
 		 */
 		constructor() {
 			/**
-			 * {@link document.Transaction} which delta is a part of. This property is null by default and set by the
-			 * {@link Document.Transaction#addDelta} method.
+			 * {@link treeModel.Batch} which delta is a part of. This property is null by default and set by the
+			 * {@link treeModel.Batch#addDelta} method.
 			 *
 			 * @readonly
-			 * @type {document.Transaction}
+			 * @type {treeModel.Batch}
 			 */
-			this.transaction = null;
+			this.batch = null;
 
 			/**
 			 * Array of operations which compose delta.
 			 *
 			 * @readonly
-			 * @type {document.operation.Operation[]}
+			 * @type {treeModel.operation.Operation[]}
 			 */
 			this.operations = [];
 		}
@@ -44,7 +44,7 @@ CKEDITOR.define( [], () => {
 		/**
 		 * Add operation to the delta.
 		 *
-		 * @param {document.operation.Operation} operation Operation instance.
+		 * @param {treeModel.operation.Operation} operation Operation instance.
 		 */
 		addOperation( operation ) {
 			operation.delta = this;
@@ -55,4 +55,4 @@ CKEDITOR.define( [], () => {
 	}
 
 	return Delta;
-} );
+} );

+ 10 - 10
packages/ckeditor5-engine/src/document/delta/insertdelta.js → packages/ckeditor5-engine/src/treemodel/delta/insertdelta.js

@@ -6,15 +6,15 @@
 'use strict';
 
 CKEDITOR.define( [
-	'document/delta/delta',
-	'document/delta/register',
-	'document/operation/insertoperation'
+	'treemodel/delta/delta',
+	'treemodel/delta/register',
+	'treemodel/operation/insertoperation'
 ], ( Delta, register, InsertOperation ) => {
 	/**
-	 * To provide specific OT behavior and better collisions solving, the {@link document.Transaction#insert} method
+	 * To provide specific OT behavior and better collisions solving, the {@link treeModel.Batch#insert} method
 	 * uses the `InsertDelta` class which inherits from the `Delta` class and may overwrite some methods.
 	 *
-	 * @class document.delta.InsertDelta
+	 * @class treeModel.delta.InsertDelta
 	 */
 	class InsertDelta extends Delta {}
 
@@ -22,11 +22,11 @@ CKEDITOR.define( [
 	 * Inserts a node or nodes at the given position.
 	 *
 	 * @chainable
-	 * @memberOf document.Transaction
+	 * @memberOf treeModel.Batch
 	 * @method insert
-	 * @param {document.Position} position Position of insertion.
-	 * @param {document.Node|document.Text|document.NodeList|String|Iterable} nodes The list of nodes to be inserted.
-	 * List of nodes can be of any type accepted by the {@link document.NodeList} constructor.
+	 * @param {treeModel.Position} position Position of insertion.
+	 * @param {treeModel.Node|treeModel.Text|treeModel.NodeList|String|Iterable} nodes The list of nodes to be inserted.
+	 * List of nodes can be of any type accepted by the {@link treeModel.NodeList} constructor.
 	 */
 	register( 'insert', function( position, nodes ) {
 		const delta = new InsertDelta();
@@ -41,4 +41,4 @@ CKEDITOR.define( [
 	} );
 
 	return InsertDelta;
-} );
+} );

+ 18 - 18
packages/ckeditor5-engine/src/document/delta/mergedelta.js → packages/ckeditor5-engine/src/treemodel/delta/mergedelta.js

@@ -6,32 +6,32 @@
 'use strict';
 
 CKEDITOR.define( [
-	'document/delta/delta',
-	'document/delta/register',
-	'document/position',
-	'document/element',
-	'document/operation/removeoperation',
-	'document/operation/moveoperation',
+	'treemodel/delta/delta',
+	'treemodel/delta/register',
+	'treemodel/position',
+	'treemodel/element',
+	'treemodel/operation/removeoperation',
+	'treemodel/operation/moveoperation',
 	'ckeditorerror'
 ], ( Delta, register, Position, Element, RemoveOperation, MoveOperation, CKEditorError ) => {
 	/**
-	 * To provide specific OT behavior and better collisions solving, {@link document.Transaction#merge} method
+	 * To provide specific OT behavior and better collisions solving, {@link treeModel.Batch#merge} method
 	 * uses the `MergeDelta` class which inherits from the `Delta` class and may overwrite some methods.
 	 *
-	 * @class document.delta.MergeDelta
+	 * @class treeModel.delta.MergeDelta
 	 */
 	class MergeDelta extends Delta {}
 
 	/**
 	 * Merges two siblings at the given position.
 	 *
-	 * Node before and after the position have to be an element. Otherwise `transaction-merge-no-element-before` or
-	 * `transaction-merge-no-element-after` error will be thrown.
+	 * Node before and after the position have to be an element. Otherwise `batch-merge-no-element-before` or
+	 * `batch-merge-no-element-after` error will be thrown.
 	 *
 	 * @chainable
 	 * @method merge
-	 * @memberOf document.Transaction
-	 * @param {document.Position} position Position of merge.
+	 * @memberOf treeModel.Batch
+	 * @param {treeModel.Position} position Position of merge.
 	 */
 	register( 'merge', function( position ) {
 		const delta = new MergeDelta();
@@ -42,26 +42,26 @@ CKEDITOR.define( [
 			/**
 			 * Node before merge position must be an element.
 			 *
-			 * @error transaction-merge-no-element-before
+			 * @error batch-merge-no-element-before
 			 */
 			throw new CKEditorError(
-				'transaction-merge-no-element-before: Node before merge position must be an element.' );
+				'batch-merge-no-element-before: Node before merge position must be an element.' );
 		}
 
 		if ( !( nodeAfter instanceof Element ) ) {
 			/**
 			 * Node after merge position must be an element.
 			 *
-			 * @error transaction-merge-no-element-after
+			 * @error batch-merge-no-element-after
 			 */
 			throw new CKEditorError(
-				'transaction-merge-no-element-after: Node after merge position must be an element.' );
+				'batch-merge-no-element-after: Node after merge position must be an element.' );
 		}
 
 		const positionAfter = Position.createFromParentAndOffset( nodeAfter, 0 );
 		const positionBefore = Position.createFromParentAndOffset( nodeBefore, nodeBefore.getChildCount() );
 
-		const move = new MoveOperation( positionAfter, positionBefore, nodeAfter.getChildCount(), this.doc.version );
+		const move = new MoveOperation( positionAfter, nodeAfter.getChildCount(), positionBefore, this.doc.version );
 		this.doc.applyOperation( move );
 		delta.addOperation( move );
 
@@ -75,4 +75,4 @@ CKEDITOR.define( [
 	} );
 
 	return MergeDelta;
-} );
+} );

+ 4 - 4
packages/ckeditor5-engine/src/document/delta/register.js → packages/ckeditor5-engine/src/treemodel/delta/register.js

@@ -8,7 +8,7 @@
 // Register method exposed for deltas, which needs only this method, to make code simpler, more beautiful and, first of
 // all, to solve circular dependencies.
 CKEDITOR.define( [
-	'document/delta/transaction-base'
-], ( Transaction ) => {
-	return Transaction.register;
-} );
+	'treemodel/delta/batch-base'
+], ( Batch ) => {
+	return Batch.register;
+} );

+ 8 - 8
packages/ckeditor5-engine/src/document/delta/removedelta.js → packages/ckeditor5-engine/src/treemodel/delta/removedelta.js

@@ -6,15 +6,15 @@
 'use strict';
 
 CKEDITOR.define( [
-	'document/delta/delta',
-	'document/delta/register',
-	'document/operation/removeoperation'
+	'treemodel/delta/delta',
+	'treemodel/delta/register',
+	'treemodel/operation/removeoperation'
 ], ( Delta, register, RemoveOperation ) => {
 	/**
-	 * To provide specific OT behavior and better collisions solving, {@link document.Transaction#remove} method
+	 * To provide specific OT behavior and better collisions solving, {@link treeModel.Batch#remove} method
 	 * uses the `RemoveDelta` class which inherits from the `Delta` class and may overwrite some methods.
 	 *
-	 * @class document.delta.RemoveDelta
+	 * @class treeModel.delta.RemoveDelta
 	 */
 	class RemoveDelta extends Delta {}
 
@@ -23,8 +23,8 @@ CKEDITOR.define( [
 	 *
 	 * @chainable
 	 * @method remove
-	 * @memberOf document.Transaction
-	 * @param {document.Position} position Position before the first node to remove.
+	 * @memberOf treeModel.Batch
+	 * @param {treeModel.Position} position Position before the first node to remove.
 	 * @param {Number} howMany How many nodes to remove.
 	 */
 	register( 'remove', function( position, howMany ) {
@@ -44,4 +44,4 @@ CKEDITOR.define( [
 	} );
 
 	return RemoveDelta;
-} );
+} );

+ 15 - 15
packages/ckeditor5-engine/src/document/delta/splitdelta.js → packages/ckeditor5-engine/src/treemodel/delta/splitdelta.js

@@ -6,32 +6,32 @@
 'use strict';
 
 CKEDITOR.define( [
-	'document/delta/delta',
-	'document/delta/register',
-	'document/position',
-	'document/element',
-	'document/operation/insertoperation',
-	'document/operation/moveoperation',
+	'treemodel/delta/delta',
+	'treemodel/delta/register',
+	'treemodel/position',
+	'treemodel/element',
+	'treemodel/operation/insertoperation',
+	'treemodel/operation/moveoperation',
 	'ckeditorerror'
 ], ( Delta, register, Position, Element, InsertOperation, MoveOperation, CKEditorError ) => {
 	/**
-	 * To provide specific OT behavior and better collisions solving, the {@link document.Transaction#split} method
+	 * To provide specific OT behavior and better collisions solving, the {@link treeModel.Batch#split} method
 	 * uses `SplitDelta` class which inherits from the `Delta` class and may overwrite some methods.
 	 *
-	 * @class document.delta.SplitDelta
+	 * @class treeModel.delta.SplitDelta
 	 */
 	class SplitDelta extends Delta {}
 
 	/**
 	 * Splits a node at the given position.
 	 *
-	 * This cannot be a position inside the root element. The `transaction-split-root` error will be thrown if
+	 * This cannot be a position inside the root element. The `batch-split-root` error will be thrown if
 	 * you try to split the root element.
 	 *
 	 * @chainable
 	 * @method split
-	 * @memberOf document.Transaction
-	 * @param {document.Position} position Position of split.
+	 * @memberOf treeModel.Batch
+	 * @param {treeModel.Position} position Position of split.
 	 */
 	register( 'split', function( position ) {
 		const delta = new SplitDelta();
@@ -41,9 +41,9 @@ CKEDITOR.define( [
 			/**
 			 * Root element can not be split.
 			 *
-			 * @error transaction-split-root
+			 * @error batch-split-root
 			 */
-			throw new CKEditorError( 'transaction-split-root: Root element can not be split.' );
+			throw new CKEditorError( 'batch-split-root: Root element can not be split.' );
 		}
 
 		const copy = new Element( splitElement.name, splitElement.getAttrs() );
@@ -54,8 +54,8 @@ CKEDITOR.define( [
 
 		const move = new MoveOperation(
 			position,
-			Position.createFromParentAndOffset( copy, 0 ),
 			splitElement.getChildCount() - position.offset,
+			Position.createFromParentAndOffset( copy, 0 ),
 			this.doc.version
 		);
 
@@ -68,4 +68,4 @@ CKEDITOR.define( [
 	} );
 
 	return SplitDelta;
-} );
+} );

+ 34 - 35
packages/ckeditor5-engine/src/document/document.js → packages/ckeditor5-engine/src/treemodel/document.js

@@ -6,27 +6,27 @@
 'use strict';
 
 CKEDITOR.define( [
-	'document/element',
-	'document/rootelement',
-	'document/transaction',
+	'treemodel/element',
+	'treemodel/rootelement',
+	'treemodel/batch',
 	'emittermixin',
 	'utils',
 	'ckeditorerror'
-], ( Element, RootElement, Transaction, EmitterMixin, utils, CKEditorError ) => {
+], ( Element, RootElement, Batch, EmitterMixin, utils, CKEditorError ) => {
 	const graveyardSymbol = Symbol( 'graveyard' );
 
 	/**
 	 * Document tree model describes all editable data in the editor. It may contain multiple {@link #roots root elements},
 	 * for example if the editor have multiple editable areas, each area will be represented by the separate root.
 	 *
-	 * All changes in the document are done by {@link document.operation.Operation operations}. To create operations in
-	 * the simple way use use the {@link document.Transaction transaction} API, for example:
+	 * All changes in the document are done by {@link treeModel.operation.Operation operations}. To create operations in
+	 * the simple way use use the {@link treeModel.Batch} API, for example:
 	 *
-	 *		document.createTransaction().insert( position, nodes ).split( otherPosition );
+	 *		doc.batch().insert( position, nodes ).split( otherPosition );
 	 *
-	 * @see #createTransaction
+	 * @see #batch
 	 *
-	 * @class document.Document
+	 * @class treeModel.Document
 	 */
 	class Document {
 		/**
@@ -48,7 +48,7 @@ CKEDITOR.define( [
 
 			/**
 			 * Document version. It starts from `0` and every operation increases the version number. It is used to ensure that
-			 * operations are applied on the proper document version. If the {@link document.operation.Operation#baseVersion} will
+			 * operations are applied on the proper document version. If the {@link treeModel.operation.Operation#baseVersion} will
 			 * not match document version the {@link document-applyOperation-wrong-version} error is thrown.
 			 *
 			 * @readonly
@@ -60,20 +60,19 @@ CKEDITOR.define( [
 		/**
 		 * Graveyard tree root. Document always have a graveyard root, which stores removed nodes.
 		 *
-		 * @protected
 		 * @readonly
-		 * @property {document.RootElement} _graveyard
+		 * @property {treeModel.RootElement} graveyard
 		 */
-		get _graveyard() {
+		get graveyard() {
 			return this.getRoot( graveyardSymbol );
 		}
 
 		/**
 		 * This is the entry point for all document changes. All changes on the document are done using
-		 * {@link document.operation.Operation operations}. To create operations in the simple way use the
-		 * {@link document.Transaction} API available via {@link #createTransaction} method.
+		 * {@link treeModel.operation.Operation operations}. To create operations in the simple way use the
+		 * {@link treeModel.Batch} API available via {@link #batch} method.
 		 *
-		 * @param {document.operation.Operation} operation Operation to be applied.
+		 * @param {treeModel.operation.Operation} operation Operation to be applied.
 		 */
 		applyOperation( operation ) {
 			if ( operation.baseVersion !== this.version ) {
@@ -81,7 +80,7 @@ CKEDITOR.define( [
 				 * Only operations with matching versions can be applied.
 				 *
 				 * @error document-applyOperation-wrong-version
-				 * @param {document.operation.Operation} operation
+				 * @param {treeModel.operation.Operation} operation
 				 */
 				throw new CKEditorError(
 					'document-applyOperation-wrong-version: Only operations with matching versions can be applied.',
@@ -95,11 +94,20 @@ CKEDITOR.define( [
 			this.fire( 'change', operation.type, changes );
 		}
 
+		/**
+		 * Creates a {@link treeModel.Batch} instance which allows to change the document.
+		 *
+		 * @returns {treeModel.Batch} Batch instance.
+		 */
+		batch() {
+			return new Batch( this );
+		}
+
 		/**
 		 * Creates a new top-level root.
 		 *
 		 * @param {String|Symbol} name Unique root name.
-		 * @returns {document.RootElement} Created root.
+		 * @returns {treeModel.RootElement} Created root.
 		 */
 		createRoot( name ) {
 			if ( this.roots.has( name ) ) {
@@ -107,7 +115,7 @@ CKEDITOR.define( [
 				 * Root with specified name already exists.
 				 *
 				 * @error document-createRoot-name-exists
-				 * @param {document.Document} doc
+				 * @param {treeModel.Document} doc
 				 * @param {String} name
 				 */
 				throw new CKEditorError(
@@ -122,20 +130,11 @@ CKEDITOR.define( [
 			return root;
 		}
 
-		/**
-		 * Creates a {@link document.Transaction} instance which allows to change the document.
-		 *
-		 * @returns {document.Transaction} Transaction instance.
-		 */
-		createTransaction() {
-			return new Transaction( this );
-		}
-
 		/**
 		 * Returns top-level root by it's name.
 		 *
 		 * @param {String|Symbol} name Name of the root to get.
-		 * @returns (document.RootElement} Root registered under given name.
+		 * @returns {treeModel.RootElement} Root registered under given name.
 		 */
 		getRoot( name ) {
 			if ( !this.roots.has( name ) ) {
@@ -171,13 +170,13 @@ CKEDITOR.define( [
 		 * @event change
 		 * @param {String} type Change type, possible option: 'insert', 'remove', 'reinsert', 'move', 'attr'.
 		 * @param {Object} changeInfo Additional information about the change.
-		 * @param {document.Range} changeInfo.range Range containing changed nodes. Note that for 'remove' the range will be in the
-		 * {@link #_graveyard graveyard root}.
-		 * @param {document.Position} [changeInfo.sourcePosition] Change source position. Exists for 'remove', 'reinsert' and 'move'.
-		 * Note that for 'reinsert' the source position will be in the {@link #_graveyard graveyard root}.
-		 * @param {document.Attribute} [changeInfo.oldAttr] Only for 'attr' type. If the type is 'attr' and `oldAttr`
+		 * @param {treeModel.Range} changeInfo.range Range containing changed nodes. Note that for 'remove' the range will be in the
+		 * {@link #graveyard graveyard root}.
+		 * @param {treeModel.Position} [changeInfo.sourcePosition] Change source position. Exists for 'remove', 'reinsert' and 'move'.
+		 * Note that for 'reinsert' the source position will be in the {@link #graveyard graveyard root}.
+		 * @param {treeModel.Attribute} [changeInfo.oldAttr] Only for 'attr' type. If the type is 'attr' and `oldAttr`
 		 * is `undefined` it means that new attribute was inserted. Otherwise it contains changed or removed attribute.
-		 * @param {document.Attribute} [changeInfo.newAttr] Only for 'attr' type. If the type is 'attr' and `newAttr`
+		 * @param {treeModel.Attribute} [changeInfo.newAttr] Only for 'attr' type. If the type is 'attr' and `newAttr`
 		 * is `undefined` it means that attribute was removed. Otherwise it contains changed or inserted attribute.
 		 */
 	}

+ 14 - 14
packages/ckeditor5-engine/src/document/element.js → packages/ckeditor5-engine/src/treemodel/element.js

@@ -6,13 +6,13 @@
 'use strict';
 
 CKEDITOR.define( [
-	'document/node',
-	'document/nodelist'
+	'treemodel/node',
+	'treemodel/nodelist'
 ], ( Node, NodeList ) => {
 	/**
 	 * Tree data model element.
 	 *
-	 * @class document.Element
+	 * @class treeModel.Element
 	 */
 	class Element extends Node {
 		/**
@@ -21,9 +21,9 @@ CKEDITOR.define( [
 		 * This constructor should be used only internally by the document.
 		 *
 		 * @param {String} name Node name.
-		 * @param {Iterable} attrs Iterable collection of {@link document.Attribute attributes}.
-		 * @param {document.Node|document.Text|document.NodeList|String|Iterable} children List of nodes to be inserted
-		 * into created element. List of nodes can be of any type accepted by the {@link document.NodeList} constructor.
+		 * @param {Iterable} attrs Iterable collection of {@link treeModel.Attribute attributes}.
+		 * @param {treeModel.Node|treeModel.Text|treeModel.NodeList|String|Iterable} children List of nodes to be inserted
+		 * into created element. List of nodes can be of any type accepted by the {@link treeModel.NodeList} constructor.
 		 * @constructor
 		 */
 		constructor( name, attrs, children ) {
@@ -41,7 +41,7 @@ CKEDITOR.define( [
 			 * List of children nodes.
 			 *
 			 * @protected
-			 * @property {document.NodeList} _children
+			 * @property {treeModel.NodeList} _children
 			 */
 			this._children = new NodeList();
 
@@ -54,7 +54,7 @@ CKEDITOR.define( [
 		 * Gets child at the given index.
 		 *
 		 * @param {Number} index Index of child.
-		 * @returns {document.Node} Child node.
+		 * @returns {treeModel.Node} Child node.
 		 */
 		getChild( index ) {
 			return this._children.get( index );
@@ -72,7 +72,7 @@ CKEDITOR.define( [
 		/**
 		 * Gets index of the given child node.
 		 *
-		 * @param {document.Node} node Child node.
+		 * @param {treeModel.Node} node Child node.
 		 * @returns {Number} Index of the child node.
 		 */
 		getChildIndex( node ) {
@@ -83,11 +83,11 @@ CKEDITOR.define( [
 		 * Inserts a list of child nodes on the given index and sets the parent of these nodes to this element.
 		 *
 		 * Note that the list of children can be modified only in elements not yet attached to the document.
-		 * All attached nodes should be modified using the {@link document.operation.InsertOperation}.
+		 * All attached nodes should be modified using the {@link treeModel.operation.InsertOperation}.
 		 *
 		 * @param {Number} index Position where nodes should be inserted.
-		 * @param {document.Node|document.Text|document.NodeList|String|Iterable} nodes The list of nodes to be inserted.
-		 * The list of nodes can be of any type accepted by the {@link document.NodeList} constructor.
+		 * @param {treeModel.Node|treeModel.Text|treeModel.NodeList|String|Iterable} nodes The list of nodes to be inserted.
+		 * The list of nodes can be of any type accepted by the {@link treeModel.NodeList} constructor.
 		 */
 		insertChildren( index, nodes ) {
 			this._children.insert( index, new NodeList( nodes ) );
@@ -101,11 +101,11 @@ CKEDITOR.define( [
 		 * Removes number of child nodes starting at the given index and set the parent of these nodes to `null`.
 		 *
 		 * Note that the list of children can be modified only in elements not yet attached to the document.
-		 * All attached nodes should be modified using the {@link document.operation.RemoveOperation}.
+		 * All attached nodes should be modified using the {@link treeModel.operation.RemoveOperation}.
 		 *
 		 * @param {Number} index Position of the first node to remove.
 		 * @param {Number} number Number of nodes to remove.
-		 * @returns {document.NodeList} The list of removed nodes.
+		 * @returns {treeModel.NodeList} The list of removed nodes.
 		 */
 
 		removeChildren( index, number ) {

+ 13 - 13
packages/ckeditor5-engine/src/document/node.js → packages/ckeditor5-engine/src/treemodel/node.js

@@ -5,12 +5,12 @@
 
 'use strict';
 
-CKEDITOR.define( [ 'document/attribute', 'utils', 'ckeditorerror' ], ( Attribute, utils, CKEditorError ) => {
+CKEDITOR.define( [ 'treemodel/attribute', 'utils', 'ckeditorerror' ], ( Attribute, utils, CKEditorError ) => {
 	/**
 	 * Abstract document tree node class.
 	 *
 	 * @abstract
-	 * @class document.Node
+	 * @class treeModel.Node
 	 */
 	class Node {
 		/**
@@ -18,22 +18,22 @@ CKEDITOR.define( [ 'document/attribute', 'utils', 'ckeditorerror' ], ( Attribute
 		 *
 		 * This is an abstract class, so this constructor should not be used directly.
 		 *
-		 * @param {Iterable} attrs Iterable collection of {@link document.Attribute attributes}.
+		 * @param {Iterable} attrs Iterable collection of {@link treeModel.Attribute attributes}.
 		 * @constructor
 		 */
 		constructor( attrs ) {
 			/**
-			 * Parent element. Null by default. Set by {@link document.Element#insertChildren}.
+			 * Parent element. Null by default. Set by {@link treeModel.Element#insertChildren}.
 			 *
 			 * @readonly
-			 * @property {document.Element|null} parent
+			 * @property {treeModel.Element|null} parent
 			 */
 			this.parent = null;
 
 			/**
 			 * Attributes set.
 			 *
-			 * Attributes of nodes attached to the document can be changed only be the {@link document.operation.ChangeOperation}.
+			 * Attributes of nodes attached to the document can be changed only be the {@link treeModel.operation.AttributeOperation}.
 			 *
 			 * @private
 			 * @property {Set} _attrs
@@ -64,7 +64,7 @@ CKEDITOR.define( [ 'document/attribute', 'utils', 'ckeditorerror' ], ( Attribute
 		 * Nodes next sibling or `null` if it is the last child.
 		 *
 		 * @readonly
-		 * @property {document.Node|null} nextSibling
+		 * @property {treeModel.Node|null} nextSibling
 		 */
 		get nextSibling() {
 			const index = this.getIndex();
@@ -76,7 +76,7 @@ CKEDITOR.define( [ 'document/attribute', 'utils', 'ckeditorerror' ], ( Attribute
 		 * Nodes previous sibling or null if it is the last child.
 		 *
 		 * @readonly
-		 * @property {document.Node|null} previousSibling
+		 * @property {treeModel.Node|null} previousSibling
 		 */
 		get previousSibling() {
 			const index = this.getIndex();
@@ -104,7 +104,7 @@ CKEDITOR.define( [ 'document/attribute', 'utils', 'ckeditorerror' ], ( Attribute
 		 * Finds an attribute by a key.
 		 *
 		 * @param {String} attr The attribute key.
-		 * @returns {document.Attribute} The found attribute.
+		 * @returns {treeModel.Attribute} The found attribute.
 		 */
 		getAttr( key ) {
 			for ( let attr of this._attrs ) {
@@ -121,7 +121,7 @@ CKEDITOR.define( [ 'document/attribute', 'utils', 'ckeditorerror' ], ( Attribute
 		 *
 		 *		const copy = new Element( element.name, element.getAttrs() );
 		 *
-		 * @returns {Iterable.<document.Attribute>} Attribute iterator.
+		 * @returns {Iterable.<treeModel.Attribute>} Attribute iterator.
 		 */
 		getAttrs() {
 			return this._attrs[ Symbol.iterator ]();
@@ -156,7 +156,7 @@ CKEDITOR.define( [ 'document/attribute', 'utils', 'ckeditorerror' ], ( Attribute
 
 		/**
 		 * Gets path to the node. For example if the node is the second child of the first child of the root then the path
-		 * will be `[ 1, 2 ]`. This path can be used as a parameter of {@link document.Position}.
+		 * will be `[ 1, 2 ]`. This path can be used as a parameter of {@link treeModel.Position}.
 		 *
 		 * @returns {Number[]} The path.
 		 */
@@ -176,7 +176,7 @@ CKEDITOR.define( [ 'document/attribute', 'utils', 'ckeditorerror' ], ( Attribute
 		 * Returns `true` if the node contains an attribute with the same key and value as given or the same key if the
 		 * given parameter is a string.
 		 *
-		 * @param {document.Attribute|String} key An attribute or a key to compare.
+		 * @param {treeModel.Attribute|String} key An attribute or a key to compare.
 		 * @returns {Boolean} True if node contains given attribute or an attribute with the given key.
 		 */
 		hasAttr( key ) {
@@ -220,7 +220,7 @@ CKEDITOR.define( [ 'document/attribute', 'utils', 'ckeditorerror' ], ( Attribute
 		/**
 		 * Sets a given attribute. If the attribute with the same key already exists it will be removed.
 		 *
-		 * @param {document.Attribute} attr Attribute to set.
+		 * @param {treeModel.Attribute} attr Attribute to set.
 		 */
 		setAttr( attr ) {
 			this.removeAttr( attr.key );

+ 10 - 10
packages/ckeditor5-engine/src/document/nodelist.js → packages/ckeditor5-engine/src/treemodel/nodelist.js

@@ -6,20 +6,20 @@
 'use strict';
 
 CKEDITOR.define( [
-	'document/character',
-	'document/text',
-	'document/node',
+	'treemodel/character',
+	'treemodel/text',
+	'treemodel/node',
 	'utils'
 ], ( Character, Text, Node, utils ) => {
 	/**
 	 * List of nodes. It is used to represent multiple nodes with a given order, for example children of
-	 * {@link document.Element} object or nodes inserted using {@link document.operation.InsertOperation}.
+	 * {@link treeModel.Element} object or nodes inserted using {@link treeModel.operation.InsertOperation}.
 	 *
 	 * Thanks to the constructor, which accepts various arguments, this class lets you easily create desired list of nodes.
 	 *
 	 * It also may internally compress nodes.
 	 *
-	 * @class document.NodeList
+	 * @class treeModel.NodeList
 	 */
 	class NodeList {
 		/**
@@ -48,7 +48,7 @@ CKEDITOR.define( [
 		 *		nodeListA === nodeListB // true
 		 *		nodeListB.length // 3
 		 *
-		 * @param {document.Node|document.Text|document.NodeList|String|Iterable} nodes List of nodes.
+		 * @param {treeModel.Node|treeModel.Text|treeModel.NodeList|String|Iterable} nodes List of nodes.
 		 * @constructor
 		 */
 		constructor( nodes ) {
@@ -114,7 +114,7 @@ CKEDITOR.define( [
 		 * Returns node at the given index.
 		 *
 		 * @param {Number} index Node index.
-		 * @returns {document.Node} Node at given index.
+		 * @returns {treeModel.Node} Node at given index.
 		 */
 		get( index ) {
 			return this._nodes[ index ];
@@ -123,7 +123,7 @@ CKEDITOR.define( [
 		/**
 		 * Search for the node in the node list.
 		 *
-		 * @param {document.Node} node Node to find.
+		 * @param {treeModel.Node} node Node to find.
 		 * @returns {Number} Position of the node in the list.
 		 */
 		indexOf( node ) {
@@ -134,7 +134,7 @@ CKEDITOR.define( [
 		 * Inserts nodes from the given node list into this node list at the given index.
 		 *
 		 * @param {Number} index Position where nodes should be inserted.
-		 * @param {document.NodeList} nodeList List of nodes to insert.
+		 * @param {treeModel.NodeList} nodeList List of nodes to insert.
 		 */
 		insert( index, nodeList ) {
 			this._nodes.splice.apply( this._nodes, [ index, 0 ].concat( nodeList._nodes ) );
@@ -145,7 +145,7 @@ CKEDITOR.define( [
 		 *
 		 * @param {Number} index Position of the first node to remove.
 		 * @param {Number} number Number of nodes to remove.
-		 * @returns {document.NodeList} List of removed nodes.
+		 * @returns {treeModel.NodeList} List of removed nodes.
 		 */
 		remove( index, number ) {
 			return new NodeList( this._nodes.splice( index, number ) );

+ 34 - 35
packages/ckeditor5-engine/src/document/operation/changeoperation.js → packages/ckeditor5-engine/src/treemodel/operation/attributeoperation.js

@@ -6,17 +6,17 @@
 'use strict';
 
 CKEDITOR.define( [
-	'document/operation/operation',
+	'treemodel/operation/operation',
 	'ckeditorerror'
 ], ( Operation, CKEditorError ) => {
 	/**
 	 * Operation to change nodes' attribute. Using this class you can add, remove or change value of the attribute.
 	 *
-	 * @class document.operation.ChangeOperation
+	 * @class treeModel.operation.AttributeOperation
 	 */
-	class ChangeOperation extends Operation {
+	class AttributeOperation extends Operation {
 		/**
-		 * Creates a change operation.
+		 * Creates an operation that changes, removes or adds attributes.
 		 *
 		 * If only the new attribute is set, then it will be inserted. Note that in all nodes in ranges there must be
 		 * no attributes with the same key as the new attribute.
@@ -27,10 +27,10 @@ CKEDITOR.define( [
 		 * If both new and old attributes are set, then the operation will change the attribute value. Note that both new and
 		 * old attributes have to have the same key and the old attribute must be present in all nodes in ranges.
 		 *
-		 * @param {document.Range} range Range on which the operation should be applied.
-		 * @param {document.Attribute|null} oldAttr Attribute to be removed. If `null`, then the operation just inserts a new attribute.
-		 * @param {document.Attribute|null} newAttr Attribute to be added. If `null`, then the operation just removes the attribute.
-		 * @param {Number} baseVersion {@link document.Document#version} on which the operation can be applied.
+		 * @param {treeModel.Range} range Range on which the operation should be applied.
+		 * @param {treeModel.Attribute|null} oldAttr Attribute to be removed. If `null`, then the operation just inserts a new attribute.
+		 * @param {treeModel.Attribute|null} newAttr Attribute to be added. If `null`, then the operation just removes the attribute.
+		 * @param {Number} baseVersion {@link treeModel.Document#version} on which the operation can be applied.
 		 * @constructor
 		 */
 		constructor( range, oldAttr, newAttr, baseVersion ) {
@@ -40,7 +40,7 @@ CKEDITOR.define( [
 			 * Range on which operation should be applied.
 			 *
 			 * @readonly
-			 * @type {document.Range}
+			 * @type {treeModel.Range}
 			 */
 			this.range = range;
 
@@ -48,7 +48,7 @@ CKEDITOR.define( [
 			 * Old attribute to change. Set to `null` if operation inserts a new attribute.
 			 *
 			 * @readonly
-			 * @type {document.Attribute|null}
+			 * @type {treeModel.Attribute|null}
 			 */
 			this.oldAttr = oldAttr;
 
@@ -56,7 +56,7 @@ CKEDITOR.define( [
 			 * New attribute. Set to `null` if operation removes the attribute.
 			 *
 			 * @readonly
-			 * @type {document.Attribute|null}
+			 * @type {treeModel.Attribute|null}
 			 */
 			this.newAttr = newAttr;
 		}
@@ -66,72 +66,71 @@ CKEDITOR.define( [
 		}
 
 		clone() {
-			return new ChangeOperation( this.range.clone(), this.oldAttr, this.newAttr, this.baseVersion );
+			return new AttributeOperation( this.range.clone(), this.oldAttr, this.newAttr, this.baseVersion );
 		}
 
 		getReversed() {
-			return new ChangeOperation( this.range, this.newAttr, this.oldAttr, this.baseVersion + 1 );
+			return new AttributeOperation( this.range, this.newAttr, this.oldAttr, this.baseVersion + 1 );
 		}
 
 		_execute() {
 			const oldAttr = this.oldAttr;
 			const newAttr = this.newAttr;
-			let value;
 
 			if ( oldAttr !== null && newAttr !== null && oldAttr.key != newAttr.key ) {
 				/**
 				 * Old and new attributes should have the same keys.
 				 *
-				 * @error operation-change-different-keys
-				 * @param {document.Attribute} oldAttr
-				 * @param {document.Attribute} newAttr
+				 * @error operation-attribute-different-keys
+				 * @param {treeModel.Attribute} oldAttr
+				 * @param {treeModel.Attribute} newAttr
 				 */
 				throw new CKEditorError(
-					'operation-change-different-keys: Old and new attributes should have the same keys.',
+					'operation-attribute-different-keys: Old and new attributes should have the same keys.',
 					{ oldAttr: oldAttr, newAttr: newAttr } );
 			}
 
 			// Remove or change.
 			if ( oldAttr !== null ) {
-				for ( value of this.range ) {
-					if ( !value.node.hasAttr( oldAttr ) ) {
+				for ( let node of this.range.getNodes() ) {
+					if ( !node.hasAttr( oldAttr ) ) {
 						/**
 						 * The attribute which should be removed does not exists for the given node.
 						 *
-						 * @error operation-change-no-attr-to-remove
-						 * @param {document.Node} node
-						 * @param {document.Attribute} attr
+						 * @error operation-attribute-no-attr-to-remove
+						 * @param {treeModel.Node} node
+						 * @param {treeModel.Attribute} attr
 						 */
 						throw new CKEditorError(
-							'operation-change-no-attr-to-remove: The attribute which should be removed does not exists for given node.',
-							{ node: value.node, attr: oldAttr } );
+							'operation-attribute-no-attr-to-remove: The attribute which should be removed does not exists for given node.',
+							{ node: node, attr: oldAttr } );
 					}
 
 					// There is no use in removing attribute if we will overwrite it later.
 					// Still it is profitable to run through the loop to check if all nodes in the range has old attribute.
 					if ( newAttr === null ) {
-						value.node.removeAttr( oldAttr.key );
+						node.removeAttr( oldAttr.key );
 					}
 				}
 			}
 
 			// Insert or change.
 			if ( newAttr !== null ) {
-				for ( value of this.range ) {
-					if ( oldAttr === null && value.node.hasAttr( newAttr.key ) ) {
+				for ( let node of this.range.getNodes() ) {
+					if ( oldAttr === null && node.hasAttr( newAttr.key ) ) {
 						/**
 						 * The attribute with given key already exists for the given node.
 						 *
-						 * @error operation-change-attr-exists
-						 * @param {document.Node} node
-						 * @param {document.Attribute} attr
+						 * @error operation-attribute-attr-exists
+						 * @param {treeModel.Node} node
+						 * @param {treeModel.Attribute} attr
 						 */
 						throw new CKEditorError(
-							'operation-change-attr-exists: The attribute with given key already exists.',
-							{ node: value.node, attr: newAttr } );
+							'operation-attribute-attr-exists: The attribute with given key already exists.',
+							{ node: node, attr: newAttr } );
 					}
 
-					value.node.setAttr( newAttr );
+					node.setAttr( newAttr );
 				}
 			}
 
@@ -139,5 +138,5 @@ CKEDITOR.define( [
 		}
 	}
 
-	return ChangeOperation;
+	return AttributeOperation;
 } );

+ 13 - 13
packages/ckeditor5-engine/src/document/operation/insertoperation.js → packages/ckeditor5-engine/src/treemodel/operation/insertoperation.js

@@ -6,24 +6,24 @@
 'use strict';
 
 CKEDITOR.define( [
-	'document/operation/operation',
-	'document/nodelist',
-	'document/range',
-	'document/operation/removeoperation'
+	'treemodel/operation/operation',
+	'treemodel/nodelist',
+	'treemodel/range',
+	'treemodel/operation/removeoperation'
 ], ( Operation, NodeList, Range ) => {
 	/**
 	 * Operation to insert list of nodes on the given position in the tree data model.
 	 *
-	 * @class document.operation.InsertOperation
+	 * @class treeModel.operation.InsertOperation
 	 */
 	class InsertOperation extends Operation {
 		/**
 		 * Creates an insert operation.
 		 *
-		 * @param {document.Position} position Position of insertion.
-		 * @param {document.Node|document.Text|document.NodeList|String|Iterable} nodes The list of nodes to be inserted.
-		 * List of nodes can be any type accepted by the {@link document.NodeList} constructor.
-		 * @param {Number} baseVersion {@link document.Document#version} on which operation can be applied.
+		 * @param {treeModel.Position} position Position of insertion.
+		 * @param {treeModel.Node|treeModel.Text|treeModel.NodeList|String|Iterable} nodes The list of nodes to be inserted.
+		 * List of nodes can be any type accepted by the {@link treeModel.NodeList} constructor.
+		 * @param {Number} baseVersion {@link treeModel.Document#version} on which operation can be applied.
 		 * @constructor
 		 */
 		constructor( position, nodes, baseVersion ) {
@@ -33,7 +33,7 @@ CKEDITOR.define( [
 			 * Position of insertion.
 			 *
 			 * @readonly
-			 * @type {document.Position}
+			 * @type {treeModel.Position}
 			 */
 			this.position = position;
 
@@ -41,7 +41,7 @@ CKEDITOR.define( [
 			 * List of nodes to insert.
 			 *
 			 * @readonly
-			 * @type {document.NodeList}
+			 * @type {treeModel.NodeList}
 			 */
 			this.nodeList = new NodeList( nodes );
 		}
@@ -56,7 +56,7 @@ CKEDITOR.define( [
 
 		getReversed() {
 			// Because of circular dependencies we need to re-require remove operation here.
-			const RemoveOperation = CKEDITOR.require( 'document/operation/removeoperation' );
+			const RemoveOperation = CKEDITOR.require( 'treemodel/operation/removeoperation' );
 
 			return new RemoveOperation( this.position, this.nodeList.length, this.baseVersion + 1 );
 		}
@@ -65,7 +65,7 @@ CKEDITOR.define( [
 			this.position.parent.insertChildren( this.position.offset, this.nodeList );
 
 			return {
-				range: Range.createFromPositionAndOffset( this.position, this.nodeList.length )
+				range: Range.createFromPositionAndShift( this.position, this.nodeList.length )
 			};
 		}
 	}

+ 17 - 17
packages/ckeditor5-engine/src/document/operation/moveoperation.js → packages/ckeditor5-engine/src/treemodel/operation/moveoperation.js

@@ -6,49 +6,49 @@
 'use strict';
 
 CKEDITOR.define( [
-	'document/operation/operation',
-	'document/range',
+	'treemodel/operation/operation',
+	'treemodel/range',
 	'ckeditorerror',
 	'utils'
 ], ( Operation, Range, CKEditorError, utils ) => {
 	/**
 	 * Operation to move list of subsequent nodes from one position in the document to another.
 	 *
-	 * @class document.operation.MoveOperation
+	 * @class treeModel.operation.MoveOperation
 	 */
 	class MoveOperation extends Operation {
 		/**
 		 * Creates a move operation.
 		 *
-		 * @param {document.Position} sourcePosition Position before the first element to move.
-		 * @param {document.Position} targetPosition Position where moved elements will be inserted.
+		 * @param {treeModel.Position} sourcePosition Position before the first element to move.
 		 * @param {Number} howMany How many consecutive nodes to move, starting from `sourcePosition`.
-		 * @param {Number} baseVersion {@link document.Document#version} on which operation can be applied.
+		 * @param {treeModel.Position} targetPosition Position where moved elements will be inserted.
+		 * @param {Number} baseVersion {@link treeModel.Document#version} on which operation can be applied.
 		 * @constructor
 		 */
-		constructor( sourcePosition, targetPosition, howMany, baseVersion ) {
+		constructor( sourcePosition, howMany, targetPosition, baseVersion ) {
 			super( baseVersion );
 
 			/**
 			 * Source move position.
 			 *
-			 * @type {document.Position}
+			 * @type {treeModel.Position}
 			 */
 			this.sourcePosition = sourcePosition;
 
 			/**
-			 * Target move position.
+			 * How many nodes to move.
 			 *
-			 * @type {document.Position}
+			 * @type {Number}
 			 */
-			this.targetPosition = targetPosition;
+			this.howMany = howMany;
 
 			/**
-			 * How many nodes to move.
+			 * Target move position.
 			 *
-			 * @type {Number}
+			 * @type {treeModel.Position}
 			 */
-			this.howMany = howMany;
+			this.targetPosition = targetPosition;
 		}
 
 		get type() {
@@ -56,11 +56,11 @@ CKEDITOR.define( [
 		}
 
 		clone() {
-			return new MoveOperation( this.sourcePosition.clone(), this.targetPosition.clone(), this.howMany, this.baseVersion );
+			return new MoveOperation( this.sourcePosition.clone(), this.howMany, this.targetPosition.clone(), this.baseVersion );
 		}
 
 		getReversed() {
-			return new MoveOperation( this.targetPosition.clone(), this.sourcePosition.clone(), this.howMany, this.baseVersion + 1 );
+			return new MoveOperation( this.targetPosition.clone(), this.howMany, this.sourcePosition.clone(), this.baseVersion + 1 );
 		}
 
 		_execute() {
@@ -129,7 +129,7 @@ CKEDITOR.define( [
 
 			return {
 				sourcePosition: this.sourcePosition,
-				range: Range.createFromPositionAndOffset( this.targetPosition, this.howMany )
+				range: Range.createFromPositionAndShift( this.targetPosition, this.howMany )
 			};
 		}
 	}

+ 4 - 4
packages/ckeditor5-engine/src/document/operation/nooperation.js → packages/ckeditor5-engine/src/treemodel/operation/nooperation.js

@@ -6,18 +6,18 @@
 'use strict';
 
 CKEDITOR.define( [
-	'document/operation/operation'
+	'treemodel/operation/operation'
 ], ( Operation ) => {
 	/**
 	 * Operation which is doing nothing ("empty operation", "do-nothing operation", "noop").
-	 * This is an operation, which executed does not change the tree model.
+	 * This is an operation, which when executed does not change the tree model.
 	 * It still has some parameters defined for transformation purposes.
 	 *
 	 * In most cases this operation is a result of transforming operations. When transformation returns
-	 * {@link document.operation.NoOperation} it means that changes done by the transformed operation
+	 * {@link treeModel.operation.NoOperation} it means that changes done by the transformed operation
 	 * have already been applied.
 	 *
-	 * @class document.operation.NoOperation
+	 * @class treeModel.operation.NoOperation
 	 */
 	class NoOperation extends Operation {
 		clone() {

+ 12 - 12
packages/ckeditor5-engine/src/document/operation/operation.js → packages/ckeditor5-engine/src/treemodel/operation/operation.js

@@ -10,20 +10,20 @@ CKEDITOR.define( [], () => {
 	 * Abstract base operation class.
 	 *
 	 * @abstract
-	 * @class document.operation.Operation
+	 * @class treeModel.operation.Operation
 	 */
 	class Operation {
 		/**
 		 * Base operation constructor.
 		 *
-		 * @param {Number} baseVersion {@link document.Document#version} on which the operation can be applied.
+		 * @param {Number} baseVersion {@link treeModel.Document#version} on which the operation can be applied.
 		 * @constructor
 		 */
 		constructor( baseVersion ) {
 			/**
-			 * {@link document.Document#version} on which operation can be applied. If you try to
-			 * {@link document.Document#applyOperation apply} operation with different base version than the
-			 * {@link document.Document#version document version} the {@link document-applyOperation-wrong-version}
+			 * {@link treeModel.Document#version} on which operation can be applied. If you try to
+			 * {@link treeModel.Document#applyOperation apply} operation with different base version than the
+			 * {@link treeModel.Document#version document version} the {@link document-applyOperation-wrong-version}
 			 * error is thrown.
 			 *
 			 * @property {Number}
@@ -37,18 +37,18 @@ CKEDITOR.define( [], () => {
 			 */
 
 			/**
-			 * {@link Document.Delta Delta} which the operation is a part of. This property is set by the
-			 * {@link Document.Delta delta} when the operations is added to it by the
-			 * {@link Document.Delta#addOperation} method.
+			 * {@link treeModel.Delta Delta} which the operation is a part of. This property is set by the
+			 * {@link treeModel.Delta delta} when the operations is added to it by the
+			 * {@link treeModel.Delta#addOperation} method.
 			 *
-			 * @property {Document.Delta} delta
+			 * @property {treeModel.Delta} delta
 			 */
 
 			/**
 			 * Creates and returns an operation that has the same parameters as this operation.
 			 *
 			 * @method clone
-			 * @returns {document.operation.Operation} Clone of this operation.
+			 * @returns {treeModel.operation.Operation} Clone of this operation.
 			 */
 
 			/**
@@ -58,10 +58,10 @@ CKEDITOR.define( [], () => {
 			 *
 			 * Keep in mind that tree model state may change since executing the original operation,
 			 * so reverse operation will be "outdated". In that case you will need to
-			 * {@link document.operation.transform} it by all operations that were executed after the original operation.
+			 * {@link treeModel.operation.transform} it by all operations that were executed after the original operation.
 			 *
 			 * @method getReversed
-			 * @returns {document.operation.Operation} Reversed operation.
+			 * @returns {treeModel.operation.Operation} Reversed operation.
 			 */
 
 			/**

+ 6 - 6
packages/ckeditor5-engine/src/document/operation/reinsertoperation.js → packages/ckeditor5-engine/src/treemodel/operation/reinsertoperation.js

@@ -6,24 +6,24 @@
 'use strict';
 
 CKEDITOR.define( [
-	'document/operation/moveoperation',
-	'document/operation/removeoperation'
+	'treemodel/operation/moveoperation',
+	'treemodel/operation/removeoperation'
 ], ( MoveOperation ) => {
 	/**
 	 * Operation to reinsert previously removed nodes back to the non-graveyard root.
-	 * This is basically {@link document.operation.MoveOperation} but it returns
-	 * {@link document.operation.RemoveOperation} when reversed.
+	 * This is basically {@link treeModel.operation.MoveOperation} but it returns
+	 * {@link treeModel.operation.RemoveOperation} when reversed.
 	 *
 	 * With this class, we achieve two goals: by having separate classes it's easier to distinguish whether move
 	 * operation is actually a remove/reinsert operation and fire proper events. Also it
 	 * will be easier to expand if we need to change operation's behavior if it is remove/reinsert.
 	 *
-	 * @class document.operation.ReinsertOperation
+	 * @class treeModel.operation.ReinsertOperation
 	 */
 	class ReinsertOperation extends MoveOperation {
 		getReversed() {
 			// Because of circular dependencies we need to re-require reinsert operation here.
-			const RemoveOperation = CKEDITOR.require( 'document/operation/removeoperation' );
+			const RemoveOperation = CKEDITOR.require( 'treemodel/operation/removeoperation' );
 
 			return new RemoveOperation( this.targetPosition, this.howMany, this.baseVersion + 1 );
 		}

+ 10 - 12
packages/ckeditor5-engine/src/document/operation/removeoperation.js → packages/ckeditor5-engine/src/treemodel/operation/removeoperation.js

@@ -6,31 +6,29 @@
 'use strict';
 
 CKEDITOR.define( [
-	'document/operation/moveoperation',
-	'document/position',
-	'document/operation/reinsertoperation'
+	'treemodel/operation/moveoperation',
+	'treemodel/position',
+	'treemodel/operation/reinsertoperation'
 ], ( MoveOperation, Position ) => {
 	/**
 	 * Operation to remove a range of nodes.
 	 *
-	 * @class document.operation.RemoveOperation
+	 * @class treeModel.operation.RemoveOperation
 	 */
 	class RemoveOperation extends MoveOperation {
 		/**
 		 * Creates a remove operation.
 		 *
-		 * @param {document.Position} position Position before the first node to remove.
+		 * @param {treeModel.Position} position Position before the first node to remove.
 		 * @param {Number} howMany How many nodes to remove.
-		 * @param {Number} baseVersion {@link document.Document#version} on which operation can be applied.
+		 * @param {Number} baseVersion {@link treeModel.Document#version} on which operation can be applied.
 		 * @constructor
 		 */
 		constructor( position, howMany, baseVersion ) {
-			const graveyard = position.root.document._graveyard;
-
 			// Position in a graveyard where nodes were moved.
-			const graveyardPosition = Position.createFromParentAndOffset( graveyard, 0 );
+			const graveyardPosition = Position.createFromParentAndOffset( position.root.document.graveyard, 0 );
 
-			super( position, graveyardPosition, howMany, baseVersion );
+			super( position, howMany, graveyardPosition, baseVersion );
 		}
 
 		get type() {
@@ -39,9 +37,9 @@ CKEDITOR.define( [
 
 		getReversed() {
 			// Because of circular dependencies we need to re-require reinsert operation here.
-			const ReinsertOperation = CKEDITOR.require( 'document/operation/reinsertoperation' );
+			const ReinsertOperation = CKEDITOR.require( 'treemodel/operation/reinsertoperation' );
 
-			return new ReinsertOperation( this.targetPosition, this.sourcePosition, this.howMany, this.baseVersion + 1 );
+			return new ReinsertOperation( this.targetPosition, this.howMany, this.sourcePosition, this.baseVersion + 1 );
 		}
 	}
 

+ 42 - 42
packages/ckeditor5-engine/src/document/operation/transform.js → packages/ckeditor5-engine/src/treemodel/operation/transform.js

@@ -6,19 +6,19 @@
 'use strict';
 
 /**
- * Transforms given {document.operation.Operation} by another {document.operation.Operation} and returns the result of
- * that transformation as an array containing one or more {document.operation.Operation} elements.
+ * Transforms given {treeModel.operation.Operation} by another {treeModel.operation.Operation} and returns the result of
+ * that transformation as an array containing one or more {treeModel.operation.Operation} elements.
  *
- * Operations work on specified positions, passed to them when they are created. Whenever {@link document.Document document}
+ * Operations work on specified positions, passed to them when they are created. Whenever {@link treeModel.Document document}
  * changes, we have to reflect those modifications by updating or "transforming" operations which are not yet applied.
  * When an operation is transformed, its parameters may change based on the operation by which it is transformed.
  * If the transform-by operation applied any modifications to the Tree Data Model which affect positions or nodes
  * connected with transformed operation, those changes will be reflected in the parameters of the returned operation(s).
  *
- * Whenever the {@link document.Document document} has different {@link document.Document#baseVersion}
- * than the operation you want to {@link document.Document#applyOperation apply}, you need to transform that
- * operation by all operations which were already applied to the {@link document.Document document} and have greater
- * {@link document.Document#baseVersion} than the operation being applied. Transform them in the same order as those
+ * Whenever the {@link treeModel.Document document} has different {@link treeModel.Document#baseVersion}
+ * than the operation you want to {@link treeModel.Document#applyOperation apply}, you need to transform that
+ * operation by all operations which were already applied to the {@link treeModel.Document document} and have greater
+ * {@link treeModel.Document#baseVersion} than the operation being applied. Transform them in the same order as those
  * operations which were applied. This way all modifications done to the Tree Data Model will be reflected
  * in the operation parameters and the operation will "operate" on "up-to-date" version of the Tree Data Model.
  * This is mostly the case with Operational Transformations but it might be needed in particular features as well.
@@ -35,22 +35,22 @@
  * `a` by `b` and also `b` by `a`. In both transformations the same operation has to be the important one. If we assume
  * that first or the second passed operation is always more important we won't be able to solve this case.
  *
- * @function document.operation.transform
- * @param {document.operation.Operation} a Operation that will be transformed.
- * @param {document.operation.Operation} b Operation to transform by.
+ * @function treeModel.operation.transform
+ * @param {treeModel.operation.Operation} a Operation that will be transformed.
+ * @param {treeModel.operation.Operation} b Operation to transform by.
  * @param {Boolean} isAMoreImportantThanB Flag indicating whether the operation which will be transformed (`a`) should be treated
  * as more important when resolving conflicts.
- * @returns {Array.<document.operation.Operation>} Result of the transformation.
+ * @returns {Array.<treeModel.operation.Operation>} Result of the transformation.
  */
 
 CKEDITOR.define( [
-	'document/operation/insertoperation',
-	'document/operation/changeoperation',
-	'document/operation/moveoperation',
-	'document/operation/nooperation',
-	'document/range',
+	'treemodel/operation/insertoperation',
+	'treemodel/operation/attributeoperation',
+	'treemodel/operation/moveoperation',
+	'treemodel/operation/nooperation',
+	'treemodel/range',
 	'utils'
-], ( InsertOperation, ChangeOperation, MoveOperation, NoOperation, Range, utils ) => {
+], ( InsertOperation, AttributeOperation, MoveOperation, NoOperation, Range, utils ) => {
 	const ot = {
 		InsertOperation: {
 			// Transforms InsertOperation `a` by InsertOperation `b`. Accepts a flag stating whether `a` is more important
@@ -65,7 +65,7 @@ CKEDITOR.define( [
 				return [ transformed ];
 			},
 
-			ChangeOperation: doNotUpdate,
+			AttributeOperation: doNotUpdate,
 
 			// Transforms InsertOperation `a` by MoveOperation `b`. Accepts a flag stating whether `a` is more important
 			// than `b` when it comes to resolving conflicts. Returns results as an array of operations.
@@ -79,15 +79,15 @@ CKEDITOR.define( [
 			}
 		},
 
-		ChangeOperation: {
-			// Transforms ChangeOperation `a` by InsertOperation `b`. Returns results as an array of operations.
+		AttributeOperation: {
+			// Transforms AttributeOperation `a` by InsertOperation `b`. Returns results as an array of operations.
 			InsertOperation( a, b ) {
 				// Transform this operation's range.
 				const ranges = a.range.getTransformedByInsertion( b.position, b.nodeList.length );
 
 				// Map transformed range(s) to operations and return them.
 				return ranges.reverse().map( ( range ) => {
-					return new ChangeOperation(
+					return new AttributeOperation(
 						range,
 						a.oldAttr,
 						a.newAttr,
@@ -96,9 +96,9 @@ CKEDITOR.define( [
 				} );
 			},
 
-			// Transforms ChangeOperation `a` by ChangeOperation `b`. Accepts a flag stating whether `a` is more important
+			// Transforms AttributeOperation `a` by AttributeOperation `b`. Accepts a flag stating whether `a` is more important
 			// than `b` when it comes to resolving conflicts. Returns results as an array of operations.
-			ChangeOperation( a, b, isStrong ) {
+			AttributeOperation( a, b, isStrong ) {
 				if ( haveConflictingAttributes( a, b ) ) {
 					// If operations attributes are in conflict, check if their ranges intersect and manage them properly.
 					let operations = [];
@@ -106,7 +106,7 @@ CKEDITOR.define( [
 					// First, we want to apply change to the part of a range that has not been changed by the other operation.
 					operations = operations.concat(
 						a.range.getDifference( b.range ).map( ( range ) => {
-							return new ChangeOperation( range, a.oldAttr, a.newAttr, a.baseVersion );
+							return new AttributeOperation( range, a.oldAttr, a.newAttr, a.baseVersion );
 						} )
 					);
 
@@ -117,7 +117,7 @@ CKEDITOR.define( [
 						const common = a.range.getIntersection( b.range );
 
 						if ( common !== null ) {
-							operations.push( new ChangeOperation( common, b.oldAttr, a.newAttr, a.baseVersion ) );
+							operations.push( new AttributeOperation( common, b.oldAttr, a.newAttr, a.baseVersion ) );
 						}
 					}
 
@@ -134,10 +134,10 @@ CKEDITOR.define( [
 				}
 			},
 
-			// Transforms ChangeOperation `a` by MoveOperation `b`. Returns results as an array of operations.
+			// Transforms AttributeOperation `a` by MoveOperation `b`. Returns results as an array of operations.
 			MoveOperation( a, b ) {
 				// Convert MoveOperation properties into a range.
-				const rangeB = Range.createFromPositionAndOffset( b.sourcePosition, b.howMany );
+				const rangeB = Range.createFromPositionAndShift( b.sourcePosition, b.howMany );
 
 				// Get target position from the state "after" nodes specified by MoveOperation are "detached".
 				const newTargetPosition = b.targetPosition.getTransformedByDeletion( b.sourcePosition, b.howMany );
@@ -145,7 +145,7 @@ CKEDITOR.define( [
 				// This will aggregate transformed ranges.
 				let ranges = [];
 
-				// Difference is a part of changed range that is modified by ChangeOperation but are not affected
+				// Difference is a part of changed range that is modified by AttributeOperation but are not affected
 				// by MoveOperation. This can be zero, one or two ranges (if moved range is inside changed range).
 				// If two ranges were returned it means that rangeB was inside rangeA. We will cover rangeB later.
 				// Right now we will make a simplification and join difference ranges and transform them as one.
@@ -157,7 +157,7 @@ CKEDITOR.define( [
 				if ( difference !== null ) {
 					// MoveOperation removes nodes from their original position. We acknowledge this by proper transformation.
 					// Take the start and the end of the range and transform them by deletion of moved nodes.
-					// Note that if rangeB was inside ChangeOperation range, only difference.end will be transformed.
+					// Note that if rangeB was inside AttributeOperation range, only difference.end will be transformed.
 					// This nicely covers the joining simplification we did in the previous step.
 					difference.start = difference.start.getTransformedByDeletion( b.sourcePosition, b.howMany );
 					difference.end = difference.end.getTransformedByDeletion( b.sourcePosition, b.howMany );
@@ -182,7 +182,7 @@ CKEDITOR.define( [
 
 				// Map transformed range(s) to operations and return them.
 				return ranges.map( ( range ) => {
-					return new ChangeOperation(
+					return new AttributeOperation(
 						range,
 						a.oldAttr,
 						a.newAttr,
@@ -200,21 +200,21 @@ CKEDITOR.define( [
 				const newTargetPosition = a.targetPosition.getTransformedByInsertion( b.position, b.nodeList.length, !isStrong );
 
 				// Create range from MoveOperation properties and transform it by insertion as well.
-				const rangeB = Range.createFromPositionAndOffset( a.sourcePosition, a.howMany );
+				const rangeB = Range.createFromPositionAndShift( a.sourcePosition, a.howMany );
 				const ranges = rangeB.getTransformedByInsertion( b.position, b.nodeList.length, true );
 
 				// Map transformed range(s) to operations and return them.
 				return ranges.reverse().map( ( range ) => {
 					return new MoveOperation(
 						range.start,
-						newTargetPosition.clone(),
 						range.end.offset - range.start.offset,
+						newTargetPosition.clone(),
 						a.baseVersion
 					);
 				} );
 			},
 
-			ChangeOperation: doNotUpdate,
+			AttributeOperation: doNotUpdate,
 
 			// Transforms MoveOperation `a` by MoveOperation `b`. Accepts a flag stating whether `a` is more important
 			// than `b` when it comes to resolving conflicts. Returns results as an array of operations.
@@ -229,8 +229,8 @@ CKEDITOR.define( [
 				}
 
 				// Create ranges from MoveOperations properties.
-				const rangeA = Range.createFromPositionAndOffset( a.sourcePosition, a.howMany );
-				const rangeB = Range.createFromPositionAndOffset( b.sourcePosition, b.howMany );
+				const rangeA = Range.createFromPositionAndShift( a.sourcePosition, a.howMany );
+				const rangeB = Range.createFromPositionAndShift( b.sourcePosition, b.howMany );
 
 				// Special case when transformed range contains both the other operation's whole range and target.
 				// In such case, operations are not really conflicting and we should leave transformed operation as it is.
@@ -246,8 +246,8 @@ CKEDITOR.define( [
 					return [
 						new MoveOperation(
 							a.sourcePosition._getCombined( b.sourcePosition, b.targetPosition ),
-							a.targetPosition._getCombined( b.sourcePosition, b.targetPosition ),
 							a.howMany,
+							a.targetPosition._getCombined( b.sourcePosition, b.targetPosition ),
 							a.baseVersion
 						)
 					];
@@ -318,8 +318,8 @@ CKEDITOR.define( [
 				return ranges.reverse().map( ( range ) => {
 					return new MoveOperation(
 						range.start,
-						newTargetPosition,
 						range.end.offset - range.start.offset,
+						newTargetPosition,
 						a.baseVersion
 					);
 				} );
@@ -333,8 +333,8 @@ CKEDITOR.define( [
 
 		if ( a instanceof InsertOperation ) {
 			group = ot.InsertOperation;
-		} else if ( a instanceof ChangeOperation ) {
-			group = ot.ChangeOperation;
+		} else if ( a instanceof AttributeOperation ) {
+			group = ot.AttributeOperation;
 		} else if ( a instanceof MoveOperation ) {
 			group = ot.MoveOperation;
 		} else {
@@ -344,8 +344,8 @@ CKEDITOR.define( [
 		if ( group ) {
 			if ( b instanceof InsertOperation ) {
 				algorithm = group.InsertOperation;
-			} else if ( b instanceof ChangeOperation ) {
-				algorithm = group.ChangeOperation;
+			} else if ( b instanceof AttributeOperation ) {
+				algorithm = group.AttributeOperation;
 			} else if ( b instanceof MoveOperation ) {
 				algorithm = group.MoveOperation;
 			} else {
@@ -379,7 +379,7 @@ CKEDITOR.define( [
 		return a.targetPosition.getTransformedByDeletion( b.sourcePosition, b.howMany ) === null;
 	}
 
-	// Takes two ChangeOperations and checks whether their attributes are in conflict.
+	// Takes two AttributeOperations and checks whether their attributes are in conflict.
 	// This happens when both operations changes an attribute with the same key and they either set different
 	// values for this attribute or one of them removes it while the other one sets it.
 	// Returns true if attributes are in conflict.

+ 67 - 57
packages/ckeditor5-engine/src/document/position.js → packages/ckeditor5-engine/src/treemodel/position.js

@@ -5,7 +5,7 @@
 
 'use strict';
 
-CKEDITOR.define( [ 'document/rootelement', 'utils', 'ckeditorerror' ], ( RootElement, utils, CKEditorError ) => {
+CKEDITOR.define( [ 'treemodel/rootelement', 'utils', 'ckeditorerror' ], ( RootElement, utils, CKEditorError ) => {
 	const SAME = 0;
 	const AFTER = 1;
 	const BEFORE = 2;
@@ -15,17 +15,44 @@ CKEDITOR.define( [ 'document/rootelement', 'utils', 'ckeditorerror' ], ( RootEle
 	 * Position in the tree. Position is always located before or after a node.
 	 * See {@link #path} property for more information.
 	 *
-	 * @class document.Position
+	 * @class treeModel.Position
 	 */
 	class Position {
 		/**
 		 * Creates a position.
 		 *
-		 * @param {Array} path Position path. See {@link #path} property for more information.
-		 * @param {document.RootElement} root Root element for the path. Note that this element can not have a parent.
+		 * @param {treeModel.RootElement} root Root element for the path. Note that this element can not have a parent.
+		 * @param {Array.<Number>} path Position path. Must contain at least one item. See {@link #path} property for more information.
 		 * @constructor
 		 */
-		constructor( path, root ) {
+		constructor( root, path ) {
+			if ( !( root instanceof RootElement ) ) {
+				/**
+				 * Position root has to be an instance of RootElement.
+				 *
+				 * @error position-root-not-rootelement
+				 * @param root
+				 */
+				throw new CKEditorError( 'position-root-not-rootelement: Position root has to be an instance of RootElement.', { root: root } );
+			}
+
+			/**
+			 * Root element for the path. Note that this element can not have a parent.
+			 *
+			 * @type {treeModel.RootElement}
+			 */
+			this.root = root;
+
+			if ( !( path instanceof Array ) || path.length === 0 ) {
+				/**
+				 * Position path must be an Array with at least one item.
+				 *
+				 * @error position-path-incorrect
+				 * @param path
+				 */
+				throw new CKEditorError( 'position-path-incorrect: Position path must be an Array with at least one item.', { path: path } );
+			}
+
 			/**
 			 * Position of the node it the tree. For example:
 			 *
@@ -44,30 +71,13 @@ CKEDITOR.define( [ 'document/rootelement', 'utils', 'ckeditorerror' ], ( RootEle
 			 * @type {Number[]}
 			 */
 			this.path = path;
-
-			if ( !( root instanceof RootElement ) ) {
-				/**
-				 * Position root has to be an instance of RootElement.
-				 *
-				 * @error position-root-not-rootelement
-				 * @param root
-				 */
-				throw new CKEditorError( 'position-root-not-rootelement: Position root has to be an instance of RootElement.', { root: root } );
-			}
-
-			/**
-			 * Root element for the path. Note that this element can not have a parent.
-			 *
-			 * @type {document.RootElement}
-			 */
-			this.root = root;
 		}
 
 		/**
 		 * Node directly after the position.
 		 *
 		 * @readonly
-		 * @property {document.Node}
+		 * @property {treeModel.Node}
 		 */
 		get nodeAfter() {
 			return this.parent.getChild( this.offset ) || null;
@@ -77,7 +87,7 @@ CKEDITOR.define( [ 'document/rootelement', 'utils', 'ckeditorerror' ], ( RootEle
 		 * Node directly before the position.
 		 *
 		 * @readonly
-		 * @type {document.Node}
+		 * @type {treeModel.Node}
 		 */
 		get nodeBefore() {
 			return this.parent.getChild( this.offset - 1 ) || null;
@@ -104,7 +114,7 @@ CKEDITOR.define( [ 'document/rootelement', 'utils', 'ckeditorerror' ], ( RootEle
 		 * Parent element of the position. The position is located at {@link #offset} in this element.
 		 *
 		 * @readonly
-		 * @property {document.Element} parent
+		 * @property {treeModel.Element} parent
 		 */
 		get parent() {
 			let parent = this.root;
@@ -119,19 +129,19 @@ CKEDITOR.define( [ 'document/rootelement', 'utils', 'ckeditorerror' ], ( RootEle
 		}
 
 		/**
-		 * Creates and returns a new instance of {@link document.Position}
-		 * which is equal to this {@link document.Position position}.
+		 * Creates and returns a new instance of {@link treeModel.Position}
+		 * which is equal to this {@link treeModel.Position position}.
 		 *
-		 * @returns {document.Position} Cloned {@link document.Position position}.
+		 * @returns {treeModel.Position} Cloned {@link treeModel.Position position}.
 		 */
 		clone() {
-			return new Position( this.path.slice(), this.root );
+			return new Position( this.root, this.path.slice() );
 		}
 
 		/**
 		 * Checks whether this position is before or after given position.
 		 *
-		 * @param {document.Position} otherPosition Position to compare with.
+		 * @param {treeModel.Position} otherPosition Position to compare with.
 		 * @returns {Number} A flag indicating whether this position is {@link #BEFORE} or
 		 * {@link #AFTER} or {@link #SAME} as given position. If positions are in different roots,
 		 * {@link #DIFFERENT} flag is returned.
@@ -163,7 +173,7 @@ CKEDITOR.define( [ 'document/rootelement', 'utils', 'ckeditorerror' ], ( RootEle
 		}
 
 		/**
-		 * Returns the path to the parent, which is the {@link document.Position#path} without the last element.
+		 * Returns the path to the parent, which is the {@link treeModel.Position#path} without the last element.
 		 *
 		 * This method returns the parent path even if the parent does not exists.
 		 *
@@ -177,9 +187,9 @@ CKEDITOR.define( [ 'document/rootelement', 'utils', 'ckeditorerror' ], ( RootEle
 		 * Returns this position after being updated by removing `howMany` nodes starting from `deletePosition`.
 		 * It may happen that this position is in a removed node. If that is the case, `null` is returned instead.
 		 *
-		 * @param {document.Position} deletePosition Position before the first removed node.
+		 * @param {treeModel.Position} deletePosition Position before the first removed node.
 		 * @param {Number} howMany How many nodes are removed.
-		 * @returns {document.Position|null} Transformed position or `null`.
+		 * @returns {treeModel.Position|null} Transformed position or `null`.
 		 */
 		getTransformedByDeletion( deletePosition, howMany ) {
 			let transformed = this.clone();
@@ -223,12 +233,12 @@ CKEDITOR.define( [ 'document/rootelement', 'utils', 'ckeditorerror' ], ( RootEle
 		/**
 		 * Returns this position after being updated by inserting `howMany` nodes at `insertPosition`.
 		 *
-		 * @param {document.Position} insertPosition Position where nodes are inserted.
+		 * @param {treeModel.Position} insertPosition Position where nodes are inserted.
 		 * @param {Number} howMany How many nodes are inserted.
 		 * @param {Boolean} insertBefore Flag indicating whether nodes are inserted before or after `insertPosition`.
 		 * This is important only when `insertPosition` and this position are same. If that is the case and the flag is
 		 * set to `true`, this position will get transformed. If the flag is set to `false`, it won't.
-		 * @returns {document.Position} Transformed position.
+		 * @returns {treeModel.Position} Transformed position.
 		 */
 		getTransformedByInsertion( insertPosition, howMany, insertBefore ) {
 			let transformed = this.clone();
@@ -262,13 +272,13 @@ CKEDITOR.define( [ 'document/rootelement', 'utils', 'ckeditorerror' ], ( RootEle
 		/**
 		 * Returns this position after being updated by moving `howMany` attributes from `sourcePosition` to `targetPosition`.
 		 *
-		 * @param {document.Position} sourcePosition Position before the first element to move.
-		 * @param {document.Position} targetPosition Position where moved elements will be inserted.
+		 * @param {treeModel.Position} sourcePosition Position before the first element to move.
+		 * @param {treeModel.Position} targetPosition Position where moved elements will be inserted.
 		 * @param {Number} howMany How many consecutive nodes to move, starting from `sourcePosition`.
 		 * @param {Boolean} insertBefore Flag indicating whether moved nodes are pasted before or after `insertPosition`.
 		 * This is important only when `targetPosition` and this position are same. If that is the case and the flag is
 		 * set to `true`, this position will get transformed by range insertion. If the flag is set to `false`, it won't.
-		 * @returns {document.Position} Transformed position.
+		 * @returns {treeModel.Position} Transformed position.
 		 */
 		getTransformedByMove( sourcePosition, targetPosition, howMany, insertBefore ) {
 			// Moving a range removes nodes from their original position. We acknowledge this by proper transformation.
@@ -290,9 +300,9 @@ CKEDITOR.define( [ 'document/rootelement', 'utils', 'ckeditorerror' ], ( RootEle
 		/**
 		 * Checks whether this position is after given position.
 		 *
-		 * **Note:** see {document.Position#isBefore}.
+		 * **Note:** see {treeModel.Position#isBefore}.
 		 *
-		 * @param {document.Position} otherPosition Position to compare with.
+		 * @param {treeModel.Position} otherPosition Position to compare with.
 		 * @returns {Boolean} True if this position is after given position.
 		 */
 		isAfter( otherPosition ) {
@@ -327,7 +337,7 @@ CKEDITOR.define( [ 'document/rootelement', 'utils', 'ckeditorerror' ], ( RootEle
 		 *    // do A.
 		 *  }
 		 *
-		 * @param {document.Position} otherPosition Position to compare with.
+		 * @param {treeModel.Position} otherPosition Position to compare with.
 		 * @returns {Boolean} True if this position is before given position.
 		 */
 		isBefore( otherPosition ) {
@@ -337,7 +347,7 @@ CKEDITOR.define( [ 'document/rootelement', 'utils', 'ckeditorerror' ], ( RootEle
 		/**
 		 * Checks whether this position equals given position.
 		 *
-		 * @param {document.Position} otherPosition Position to compare with.
+		 * @param {treeModel.Position} otherPosition Position to compare with.
 		 * @returns {Boolean} True if positions are same.
 		 */
 		isEqual( otherPosition ) {
@@ -347,8 +357,8 @@ CKEDITOR.define( [ 'document/rootelement', 'utils', 'ckeditorerror' ], ( RootEle
 		/**
 		 * Creates a new position after given node.
 		 *
-		 * @param {document.Node} node Node the position should be directly after.
-		 * @returns {document.Position}
+		 * @param {treeModel.Node} node Node the position should be directly after.
+		 * @returns {treeModel.Position}
 		 */
 		static createAfter( node ) {
 			if ( !node.parent ) {
@@ -356,7 +366,7 @@ CKEDITOR.define( [ 'document/rootelement', 'utils', 'ckeditorerror' ], ( RootEle
 				 * You can not make position after root.
 				 *
 				 * @error position-after-root
-				 * @param {document.Node} root
+				 * @param {treeModel.Node} root
 				 */
 				throw new CKEditorError( 'position-after-root: You can not make position after root.', { root: node } );
 			}
@@ -367,8 +377,8 @@ CKEDITOR.define( [ 'document/rootelement', 'utils', 'ckeditorerror' ], ( RootEle
 		/**
 		 * Creates a new position before the given node.
 		 *
-		 * @param {document.node} node Node the position should be directly before.
-		 * @returns {document.Position}
+		 * @param {treeModel.node} node Node the position should be directly before.
+		 * @returns {treeModel.Position}
 		 */
 		static createBefore( node ) {
 			if ( !node.parent ) {
@@ -376,7 +386,7 @@ CKEDITOR.define( [ 'document/rootelement', 'utils', 'ckeditorerror' ], ( RootEle
 				 * You can not make position before root.
 				 *
 				 * @error position-before-root
-				 * @param {document.Node} root
+				 * @param {treeModel.Node} root
 				 */
 				throw new CKEditorError( 'position-before-root: You can not make position before root.', { root: node } );
 			}
@@ -387,16 +397,16 @@ CKEDITOR.define( [ 'document/rootelement', 'utils', 'ckeditorerror' ], ( RootEle
 		/**
 		 * Creates a new position from the parent element and the offset in that element.
 		 *
-		 * @param {document.Element} parent Position parent element.
+		 * @param {treeModel.Element} parent Position parent element.
 		 * @param {Number} offset Position offset.
-		 * @returns {document.Position}
+		 * @returns {treeModel.Position}
 		 */
 		static createFromParentAndOffset( parent, offset ) {
 			const path = parent.getPath();
 
 			path.push( offset );
 
-			return new Position( path, parent.root );
+			return new Position( parent.root, path );
 		}
 
 		/**
@@ -409,9 +419,9 @@ CKEDITOR.define( [ 'document/rootelement', 'utils', 'ckeditorerror' ], ( RootEle
 		 *
 		 * Example:
 		 *
-		 * 	let original = new Position( [ 2, 3, 1 ], root );
-		 * 	let source = new Position( [ 2, 2 ], root );
-		 * 	let target = new Position( [ 1, 1, 3 ], otherRoot );
+		 * 	let original = new Position( root, [ 2, 3, 1 ] );
+		 * 	let source = new Position( root, [ 2, 2 ] );
+		 * 	let target = new Position( otherRoot, [ 1, 1, 3 ] );
 		 * 	let combined = original.getCombined( source, target );
 		 * 	// combined.path is [ 1, 1, 4, 1 ], combined.root is otherRoot
 		 *
@@ -425,9 +435,9 @@ CKEDITOR.define( [ 'document/rootelement', 'utils', 'ckeditorerror' ], ( RootEle
 		 * Finally, the transformed position will point to `[ 1, 1, 4, 1 ]`.
 		 *
 		 * @protected
-		 * @param {document.Position} source Beginning of the moved range.
-		 * @param {document.Position} target Position where the range is moved.
-		 * @returns {document.Position} Combined position.
+		 * @param {treeModel.Position} source Beginning of the moved range.
+		 * @param {treeModel.Position} target Position where the range is moved.
+		 * @returns {treeModel.Position} Combined position.
 		 */
 		_getCombined( source, target ) {
 			const i = source.path.length - 1;

+ 11 - 11
packages/ckeditor5-engine/src/document/positioniterator.js → packages/ckeditor5-engine/src/treemodel/positioniterator.js

@@ -6,9 +6,9 @@
 'use strict';
 
 CKEDITOR.define( [
-	'document/character',
-	'document/element',
-	'document/position'
+	'treemodel/character',
+	'treemodel/element',
+	'treemodel/position'
 ], ( Character, Element, Position ) => {
 	const ELEMENT_ENTER = 0;
 	const ELEMENT_LEAVE = 1;
@@ -17,14 +17,14 @@ CKEDITOR.define( [
 	/**
 	 * Position iterator class. It allows to iterate forward and backward over the tree document.
 	 *
-	 * @class document.PositionIterator
+	 * @class treeModel.PositionIterator
 	 */
 	class PositionIterator {
 		/**
 		 * Creates a range iterator.
 		 *
-		 * @param {document.Range} [boundaries] Range to define boundaries of the iterator.
-		 * @param {document.Position} [iteratorPosition] Starting position.
+		 * @param {treeModel.Range} [boundaries] Range to define boundaries of the iterator.
+		 * @param {treeModel.Position} [iteratorPosition] Starting position.
 		 * @constructor
 		 */
 		constructor( boundaries, iteratorPosition ) {
@@ -38,7 +38,7 @@ CKEDITOR.define( [
 			/**
 			 * Iterator position.
 			 *
-			 * @property {document.Position} position
+			 * @property {treeModel.Position} position
 			 */
 
 			/**
@@ -49,7 +49,7 @@ CKEDITOR.define( [
 			 *
 			 * If boundaries are not defined they are set before first and after last child of the root node.
 			 *
-			 * @property {document.Range} boundaries
+			 * @property {treeModel.Range} boundaries
 			 */
 		}
 
@@ -61,14 +61,14 @@ CKEDITOR.define( [
 		 * @returns {Object} return.value
 		 * @returns {Number} return.value.type Encountered value type, possible options: {@link PositionIterator#ELEMENT_ENTER},
 		 * {@link PositionIterator#ELEMENT_LEAVE} or {@link PositionIterator#CHARACTER}.
-		 * @returns {document.Node} return.value.node Encountered node.
+		 * @returns {treeModel.Node} return.value.node Encountered node.
 		 */
 		next() {
 			const position = this.position;
 			const parent = position.parent;
 
 			// Ugh... added here because of circular deps in AMD ;<.
-			Element = CKEDITOR.require( 'document/element' );
+			Element = CKEDITOR.require( 'treemodel/element' );
 
 			// We are at the end of the root.
 			if ( parent.parent === null && position.offset === parent.getChildCount() ) {
@@ -104,7 +104,7 @@ CKEDITOR.define( [
 		 * @returns {Object} return.value
 		 * @returns {Number} return.value.type Encountered value type, possible options: {@link PositionIterator#ELEMENT_ENTER},
 		 * {@link PositionIterator#ELEMENT_LEAVE} or {@link PositionIterator#CHARACTER}.
-		 * @returns {document.Node} return.value.node Scanned node.
+		 * @returns {treeModel.Node} return.value.node Scanned node.
 		 */
 		previous() {
 			const position = this.position;

+ 83 - 61
packages/ckeditor5-engine/src/document/range.js → packages/ckeditor5-engine/src/treemodel/range.js

@@ -5,32 +5,32 @@
 
 'use strict';
 
-CKEDITOR.define( [ 'document/position', 'document/positioniterator', 'utils' ], ( Position, PositionIterator, utils ) => {
+CKEDITOR.define( [ 'treemodel/position', 'treemodel/positioniterator', 'utils' ], ( Position, PositionIterator, utils ) => {
 	/**
 	 * Range class. Range is iterable.
 	 *
-	 * @class document.Range
+	 * @class treeModel.Range
 	 */
 	class Range {
 		/**
 		 * Creates a range.
 		 *
-		 * @param {document.Position} start Start position.
-		 * @param {document.Position} end End position.
+		 * @param {treeModel.Position} start Start position.
+		 * @param {treeModel.Position} end End position.
 		 * @constructor
 		 */
 		constructor( start, end ) {
 			/**
 			 * Start position.
 			 *
-			 * @property {document.Position}
+			 * @property {treeModel.Position}
 			 */
 			this.start = start;
 
 			/**
 			 * End position.
 			 *
-			 * @property {document.Position}
+			 * @property {treeModel.Position}
 			 */
 			this.end = end;
 		}
@@ -38,63 +38,63 @@ CKEDITOR.define( [ 'document/position', 'document/positioniterator', 'utils' ],
 		/**
 		 * Range iterator.
 		 *
-		 * @see document.PositionIterator
+		 * @see treeModel.PositionIterator
 		 */
 		[ Symbol.iterator ]() {
 			return new PositionIterator( this );
 		}
 
 		/**
-		 * Creates and returns a new instance of {@link document.Range}
-		 * which is equal to this {@link document.Range range}.
+		 * Creates and returns a new instance of {@link treeModel.Range}
+		 * which is equal to this {@link treeModel.Range range}.
 		 *
-		 * @returns {document.Position} Cloned {@link document.Range range}.
+		 * @returns {treeModel.Position} Cloned {@link treeModel.Range range}.
 		 */
 		clone() {
 			return new Range( this.start.clone(), this.end.clone() );
 		}
 
 		/**
-		 * Checks whether this contains given {@link document.Position position}.
+		 * Checks whether this contains given {@link treeModel.Position position}.
 		 *
-		 * @param {document.Position} position Position to check.
-		 * @returns {Boolean} True if given {@link document.Position position} is contained.
+		 * @param {treeModel.Position} position Position to check.
+		 * @returns {Boolean} True if given {@link treeModel.Position position} is contained.
 		 */
 		containsPosition( position ) {
 			return position.isAfter( this.start ) && position.isBefore( this.end );
 		}
 
 		/**
-		 * Checks whether this range contains given {@link document.Range range}.
+		 * Checks whether this range contains given {@link treeModel.Range range}.
 		 *
-		 * @param {document.Range} otherRange Range to check.
-		 * @returns {Boolean} True if given {@link document.Range range} boundaries are contained by this range.
+		 * @param {treeModel.Range} otherRange Range to check.
+		 * @returns {Boolean} True if given {@link treeModel.Range range} boundaries are contained by this range.
 		 */
 		containsRange( otherRange ) {
 			return this.containsPosition( otherRange.start ) && this.containsPosition( otherRange.end );
 		}
 
 		/**
-		 * Gets a part of this {@link document.Range range} which is not a part of given {@link document.Range range}. Returned
-		 * array contains zero, one or two {@link document.Range ranges}.
+		 * Gets a part of this {@link treeModel.Range range} which is not a part of given {@link treeModel.Range range}. Returned
+		 * array contains zero, one or two {@link treeModel.Range ranges}.
 		 *
 		 * Examples:
 		 *
-		 * 	let range = new Range( new Position( [ 2, 7 ], root ), new Position( [ 4, 0, 1 ], root ) );
-		 * 	let otherRange = new Range( new Position( [ 1 ], root ), new Position( [ 5 ], root ) );
-		 * 	let transformed = range.getDifference( otherRange );
-		 * 	// transformed array has no ranges because `otherRange` contains `range`
+		 *		let range = new Range( new Position( root, [ 2, 7 ] ), new Position( root, [ 4, 0, 1 ] ) );
+		 *		let otherRange = new Range( new Position( root, [ 1 ] ), new Position( root, [ 5 ] ) );
+		 *		let transformed = range.getDifference( otherRange );
+		 *		// transformed array has no ranges because `otherRange` contains `range`
 		 *
-		 * 	otherRange = new Range( new Position( [ 1 ], root ), new Position( [ 3 ], root ) );
-		 * 	transformed = range.getDifference( otherRange );
-		 * 	// transformed array has one range: from [ 3 ] to [ 4, 0, 1 ]
+		 *		otherRange = new Range( new Position( root, [ 1 ] ), new Position( root, [ 3 ] ) );
+		 *		transformed = range.getDifference( otherRange );
+		 *		// transformed array has one range: from [ 3 ] to [ 4, 0, 1 ]
 		 *
-		 * 	otherRange = new Range( new Position( [ 3 ], root ), new Position( [ 4 ], root ) );
-		 * 	transformed = range.getDifference( otherRange );
-		 * 	// transformed array has two ranges: from [ 2, 7 ] to [ 3 ] and from [ 4 ] to [ 4, 0, 1 ]
+		 *		otherRange = new Range( new Position( root, [ 3 ] ), new Position( root, [ 4 ] ) );
+		 *		transformed = range.getDifference( otherRange );
+		 *		// transformed array has two ranges: from [ 2, 7 ] to [ 3 ] and from [ 4 ] to [ 4, 0, 1 ]
 		 *
-		 * @param {document.Range} otherRange Range to differentiate against.
-		 * @returns {Array.<document.Range>} The difference between ranges.
+		 * @param {treeModel.Range} otherRange Range to differentiate against.
+		 * @returns {Array.<treeModel.Range>} The difference between ranges.
 		 */
 		getDifference( otherRange ) {
 			const ranges = [];
@@ -132,20 +132,20 @@ CKEDITOR.define( [ 'document/position', 'document/positioniterator', 'utils' ],
 		}
 
 		/**
-		 * Returns an intersection of this {@link document.Range range} and given {@link document.Range range}. Intersection
+		 * Returns an intersection of this {@link treeModel.Range range} and given {@link treeModel.Range range}. Intersection
 		 * is a common part of both of those ranges. If ranges has no common part, returns `null`.
 		 *
 		 * Examples:
 		 *
-		 * 	let range = new Range( new Position( [ 2, 7 ], root ), new Position( [ 4, 0, 1 ], root ) );
-		 * 	let otherRange = new Range( new Position( [ 1 ], root ), new Position( [ 2 ], root ) );
-		 * 	let transformed = range.getIntersection( otherRange ); // null - ranges have no common part
+		 *		let range = new Range( new Position( root, [ 2, 7 ] ), new Position( root, [ 4, 0, 1 ] ) );
+		 *		let otherRange = new Range( new Position( root, [ 1 ] ), new Position( root, [ 2 ] ) );
+		 *		let transformed = range.getIntersection( otherRange ); // null - ranges have no common part
 		 *
-		 * 	otherRange = new Range( new Position( [ 3 ], root ), new Position( [ 5 ], root ) );
-		 * 	transformed = range.getIntersection( otherRange ); // range from [ 3 ] to [ 4, 0, 1 ]
+		 *		otherRange = new Range( new Position( root, [ 3 ] ), new Position( root, [ 5 ] ) );
+		 *		transformed = range.getIntersection( otherRange ); // range from [ 3 ] to [ 4, 0, 1 ]
 		 *
-		 * @param {document.Range} otherRange Range to check for intersection.
-		 * @returns {document.Range|null} A common part of given ranges or null if ranges have no common part.
+		 * @param {treeModel.Range} otherRange Range to check for intersection.
+		 * @returns {treeModel.Range|null} A common part of given ranges or null if ranges have no common part.
 		 */
 		getIntersection( otherRange ) {
 			if ( this.start.isBefore( otherRange.end ) && this.end.isAfter( otherRange.start ) ) {
@@ -174,27 +174,49 @@ CKEDITOR.define( [ 'document/position', 'document/positioniterator', 'utils' ],
 		}
 
 		/**
-		 * Returns an array containing one or two {document.Range ranges} that are a result of transforming this
-		 * {@link document.Range range} by inserting `howMany` nodes at `insertPosition`. Two {@link document.Range ranges} are
-		 * returned if the insertion was inside this {@link document.Range range}.
+		 * Returns an iterator that iterates over all {@link treeModel.Node nodes} that are in this range and returns them.
+		 * A node is in the range when it is after a {@link treeModel.Position position} contained in this range.
+		 * In other words, this iterates over all {@link treeModel.Character}s that are inside the range and
+		 * all the {@link treeModel.Element}s we enter into when iterating over this range.
+		 *
+		 * Note, that this method will not return a parent node of start position. This is in contrary to {@link treeModel.PositionIterator}
+		 * which will return that node with {@link treeModel.PositionIterator#ELEMENT_LEAVE} type. This method, also, returns each
+		 * {@link treeModel.Element} once, while iterator return it twice: for {@link treeModel.PositionIterator#ELEMENT_ENTER} and
+		 * {@link treeModel.PositionIterator#ELEMENT_LEAVE}.
+		 *
+		 * @see {treeModel.PositionIterator}
+		 * @returns {treeModel.Node}
+		 */
+		*getNodes() {
+			for ( let value of this ) {
+				if ( value.type != PositionIterator.ELEMENT_LEAVE ) {
+					yield value.node;
+				}
+			}
+		}
+
+		/**
+		 * Returns an array containing one or two {treeModel.Range ranges} that are a result of transforming this
+		 * {@link treeModel.Range range} by inserting `howMany` nodes at `insertPosition`. Two {@link treeModel.Range ranges} are
+		 * returned if the insertion was inside this {@link treeModel.Range range}.
 		 *
 		 * Examples:
 		 *
-		 * 	let range = new Range( new Position( [ 2, 7 ], root ), new Position( [ 4, 0, 1 ], root ) );
-		 * 	let transformed = range.getTransformedByInsertion( new Position( [ 1 ], root ), 2 );
-		 * 	// transformed array has one range from [ 4, 7 ] to [ 6, 0, 1 ]
+		 *		let range = new Range( new Position( root, [ 2, 7 ] ), new Position( root, [ 4, 0, 1 ] ) );
+		 *		let transformed = range.getTransformedByInsertion( new Position( root, [ 1 ] ), 2 );
+		 *		// transformed array has one range from [ 4, 7 ] to [ 6, 0, 1 ]
 		 *
-		 * 	transformed = range.getTransformedByInsertion( new Position( [ 3, 2 ], root ), 4 );
-		 * 	// transformed array has two ranges: from [ 2, 7 ] to [ 3, 2 ] and from [ 3, 6 ] to [ 4, 0, 1 ]
+		 *		transformed = range.getTransformedByInsertion( new Position( root, [ 3, 2 ] ), 4 );
+		 *		// transformed array has two ranges: from [ 2, 7 ] to [ 3, 2 ] and from [ 3, 6 ] to [ 4, 0, 1 ]
 		 *
-		 * 	transformed = range.getTransformedByInsertion( new Position( [ 3, 2 ], root ), 4, true );
-		 * 	// transformed array has one range which is equal to `range`. This is because of spreadOnlyOnSameLevel flag.
+		 *		transformed = range.getTransformedByInsertion( new Position( root, [ 3, 2 ] ), 4, true );
+		 *		// transformed array has one range which is equal to `range`. This is because of spreadOnlyOnSameLevel flag.
 		 *
-		 * @param {document.Position} insertPosition Position where nodes are inserted.
+		 * @param {treeModel.Position} insertPosition Position where nodes are inserted.
 		 * @param {Number} howMany How many nodes are inserted.
-		 * @param {Boolean} spreadOnlyOnSameLevel Flag indicating whether this {document.Range range} should be spread
-		 * if insertion was inside a node from this {document.Range range} but not in the range itself.
-		 * @returns {Array.<document.Range>} Result of the transformation.
+		 * @param {Boolean} spreadOnlyOnSameLevel Flag indicating whether this {treeModel.Range range} should be spread
+		 * if insertion was inside a node from this {treeModel.Range range} but not in the range itself.
+		 * @returns {Array.<treeModel.Range>} Result of the transformation.
 		 */
 		getTransformedByInsertion( insertPosition, howMany, spreadOnlyOnSameLevel ) {
 			// Flag indicating whether this whole range and given insertPosition is on the same tree level.
@@ -232,7 +254,7 @@ CKEDITOR.define( [ 'document/position', 'document/positioniterator', 'utils' ],
 		/**
 		 * Two ranges equal if their start and end positions equal.
 		 *
-		 * @param {document.Range} otherRange Range to compare with.
+		 * @param {treeModel.Range} otherRange Range to compare with.
 		 * @returns {Boolean} True if ranges equal.
 		 */
 		isEqual( otherRange ) {
@@ -242,8 +264,8 @@ CKEDITOR.define( [ 'document/position', 'document/positioniterator', 'utils' ],
 		/**
 		 * Creates a range inside an element which starts before the first child and ends after the last child.
 		 *
-		 * @param {document.Element} element Element which is a parent for the range.
-		 * @returns {document.Range} Created range.
+		 * @param {treeModel.Element} element Element which is a parent for the range.
+		 * @returns {treeModel.Range} Created range.
 		 */
 		static createFromElement( element ) {
 			return Range.createFromParentsAndOffsets( element, 0, element, element.getChildCount() );
@@ -252,11 +274,11 @@ CKEDITOR.define( [ 'document/position', 'document/positioniterator', 'utils' ],
 		/**
 		 * Creates a new range spreading from specified position to the same position moved by given offset.
 		 *
-		 * @param {document.Position} position Beginning of the range.
+		 * @param {treeModel.Position} position Beginning of the range.
 		 * @param {Number} offset How long the range should be.
-		 * @returns {document.Range}
+		 * @returns {treeModel.Range}
 		 */
-		static createFromPositionAndOffset( position, offset ) {
+		static createFromPositionAndShift( position, offset ) {
 			let endPosition = position.clone();
 			endPosition.offset += offset;
 
@@ -266,11 +288,11 @@ CKEDITOR.define( [ 'document/position', 'document/positioniterator', 'utils' ],
 		/**
 		 * Creates a range from given parents and offsets.
 		 *
-		 * @param {document.Element} startElement Start position parent element.
+		 * @param {treeModel.Element} startElement Start position parent element.
 		 * @param {Number} startOffset Start position offset.
-		 * @param {document.Element} endElement End position parent element.
+		 * @param {treeModel.Element} endElement End position parent element.
 		 * @param {Number} endOffset End position offset.
-		 * @returns {document.Range} Created range.
+		 * @returns {treeModel.Range} Created range.
 		 */
 		static createFromParentsAndOffsets( startElement, startOffset, endElement, endOffset ) {
 			return new Range(

+ 5 - 5
packages/ckeditor5-engine/src/document/rootelement.js → packages/ckeditor5-engine/src/treemodel/rootelement.js

@@ -5,27 +5,27 @@
 
 'use strict';
 
-CKEDITOR.define( [ 'document/element' ], ( Element ) => {
+CKEDITOR.define( [ 'treemodel/element' ], ( Element ) => {
 	/**
 	 * Class for nodes that are roots of trees in tree data model.
 	 *
-	 * @class document.RootElement
+	 * @class treeModel.RootElement
 	 */
 	class RootElement extends Element {
 		/**
 		 * Creates tree root node.
 		 *
-		 * @param {document.Document} doc {@link document.Document} that is an owner of the root.
+		 * @param {treeModel.Document} doc {@link treeModel.Document} that is an owner of the root.
 		 * @constructor
 		 */
 		constructor( doc ) {
 			super( 'root' );
 
 			/**
-			 * {@link document.Document} that is an owner of this root.
+			 * {@link treeModel.Document} that is an owner of this root.
 			 *
 			 * @readonly
-			 * @property {document.Document}
+			 * @property {treeModel.Document}
 			 */
 			this.document = doc;
 		}

+ 82 - 0
packages/ckeditor5-engine/src/treemodel/smartrange.js

@@ -0,0 +1,82 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+CKEDITOR.define( [ 'treemodel/range', 'emittermixin', 'utils' ], ( Range, EmitterMixin, utils ) => {
+	/**
+	 * SmartRange is a Range in the Tree Model that updates itself as the tree changes. It may be used as a bookmark.
+	 * SmartRange object may fire 'update' event whenever it gets changed by internal mechanisms.
+	 *
+	 * @class treeModel.SmartRange
+	 */
+	class SmartRange extends Range {
+		/**
+		 * Creates a smart range.
+		 *
+		 * @see {treeModel.Range}
+		 * @constructor
+		 */
+		constructor( start, end ) {
+			super( start, end );
+
+			this.listenTo( this.root.document, 'update', transform, this );
+		}
+	}
+
+	/**
+	 * Updates this position accordingly to the updates applied to the Tree Model. Bases on change events.
+	 *
+	 * @method transform
+	 * @param {String} type Type of changes applied to the Tree Model.
+	 * @param {treeModel.Range} range Range containing the result of applied change.
+	 * @param {treeModel.Position} [position] Additional position parameter provided by some change events.
+	 * @private
+	 */
+	function transform( type, range, position ) {
+		/*jshint validthis: true */
+
+		let howMany = range.end.offset - range.start.offset;
+		let newStart, newEnd;
+
+		switch ( type ) {
+			case 'insert':
+				newStart = this.start.getTransformedByInsertion( range.start, howMany, true );
+				newEnd = this.end.getTransformedByInsertion( range.start, howMany, false );
+				break;
+
+			case 'move':
+			case 'remove':
+			case 'reinsert':
+				let differenceSet = this.getDifference( Range.createFromPositionAndShift( position, howMany ) );
+
+				if ( differenceSet.length > 0 ) {
+					let diff = differenceSet[ 0 ];
+
+					if ( differenceSet.length > 1 ) {
+						diff.end = differenceSet[ 1 ].end.clone();
+					}
+
+					newStart = diff.start.getTransformedByDeletion( position, howMany ).getTransformedByInsertion( range.start, howMany );
+					newEnd = diff.end.getTransformedByDeletion( position, howMany ).getTransformedByInsertion( range.start, howMany );
+				} else {
+					newStart = this.start._getCombined( position, range.start );
+					newEnd = this.end._getCombined( position, range.start );
+				}
+
+				break;
+		}
+
+		if ( !newStart.isEqual( this.start ) || !newEnd.isEqual( this.end ) ) {
+			this.start = newStart;
+			this.end = newEnd;
+			this.fire( 'update' );
+		}
+	}
+
+	utils.extend( SmartRange.prototype, EmitterMixin );
+
+	return SmartRange;
+} );

+ 6 - 6
packages/ckeditor5-engine/src/document/text.js → packages/ckeditor5-engine/src/treemodel/text.js

@@ -7,17 +7,17 @@
 
 CKEDITOR.define( [], () => {
 	/**
-	 * Data structure for text with attributes. Note that the `Text` is not a {@link document.Node},
-	 * because it will never be part of the document tree. {@link document.Character is a node}.
+	 * Data structure for text with attributes. Note that the `Text` is not a {@link treeModel.Node},
+	 * because it will never be part of the document tree. {@link treeModel.Character is a node}.
 	 *
-	 * @class document.Text
+	 * @class treeModel.Text
 	 */
 	class Text {
 		/**
 		 * Creates a text with attributes.
 		 *
 		 * @param {String} text Described text.
-		 * @param {Iterable} attrs Iterable collection of {@link document.Attribute attributes}.
+		 * @param {Iterable} attrs Iterable collection of {@link treeModel.Attribute attributes}.
 		 * @constructor
 		 */
 		constructor( text, attrs ) {
@@ -30,7 +30,7 @@ CKEDITOR.define( [], () => {
 			this.text = text;
 
 			/**
-			 * Iterable collection of {@link document.Attribute attributes}.
+			 * Iterable collection of {@link treeModel.Attribute attributes}.
 			 *
 			 * @property {Iterable}
 			 */
@@ -39,4 +39,4 @@ CKEDITOR.define( [], () => {
 	}
 
 	return Text;
-} );
+} );

+ 0 - 82
packages/ckeditor5-engine/tests/document/transaction.js

@@ -1,82 +0,0 @@
-/**
- * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/* bender-tags: document, delta */
-
-'use strict';
-
-const modules = bender.amd.require(
-	'document/transaction',
-	'document/delta/delta',
-	'ckeditorerror' );
-
-describe( 'Transaction', () => {
-	let Transaction, Delta, CKEditorError;
-
-	before( () => {
-		Transaction = modules[ 'document/transaction' ];
-		Delta = modules[ 'document/delta/delta' ];
-		CKEditorError = modules.ckeditorerror;
-	} );
-
-	it( 'should have registered basic methods', () => {
-		const transaction = new Transaction();
-
-		expect( transaction.setAttr ).to.be.a( 'function' );
-		expect( transaction.removeAttr ).to.be.a( 'function' );
-	} );
-
-	describe( 'Transaction.register', () => {
-		let TestDelta;
-
-		before( () => {
-			TestDelta = class extends Delta {
-				constructor( transaction ) {
-					super( transaction, [] );
-				}
-			};
-		} );
-
-		afterEach( () => {
-			delete Transaction.prototype.foo;
-		} );
-
-		it( 'should register function which return an delta', () => {
-			Transaction.register( 'foo', function() {
-				this.addDelta( new TestDelta() );
-			} );
-
-			const transaction = new Transaction();
-
-			transaction.foo();
-
-			expect( transaction.deltas.length ).to.equal( 1 );
-			expect( transaction.deltas[ 0 ] ).to.be.instanceof( TestDelta );
-		} );
-
-		it( 'should register function which return an multiple deltas', () => {
-			Transaction.register( 'foo', function() {
-				this.addDelta( new TestDelta() );
-				this.addDelta( new TestDelta() );
-			} );
-
-			const transaction = new Transaction();
-
-			transaction.foo();
-
-			expect( transaction.deltas.length ).to.equal( 2 );
-			expect( transaction.deltas[ 0 ] ).to.be.instanceof( TestDelta );
-			expect( transaction.deltas[ 1 ] ).to.be.instanceof( TestDelta );
-		} );
-
-		it( 'should throw if one try to register the same transaction twice', () => {
-			Transaction.register( 'foo', () => {} );
-
-			expect( () => {
-				Transaction.register( 'foo', () => {} );
-			} ).to.throw( CKEditorError, /^transaction-register-taken/ );
-		} );
-	} );
-} );

+ 2 - 2
packages/ckeditor5-engine/tests/document/attribute.js → packages/ckeditor5-engine/tests/treemodel/attribute.js

@@ -7,13 +7,13 @@
 
 'use strict';
 
-const modules = bender.amd.require( 'document/attribute' );
+const modules = bender.amd.require( 'treemodel/attribute' );
 
 describe( 'Attribute', () => {
 	let Attribute;
 
 	before( () => {
-		Attribute = modules[ 'document/attribute' ];
+		Attribute = modules[ 'treemodel/attribute' ];
 	} );
 
 	beforeEach( () => {

+ 83 - 0
packages/ckeditor5-engine/tests/treemodel/batch.js

@@ -0,0 +1,83 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* bender-tags: document, delta */
+
+'use strict';
+
+const modules = bender.amd.require(
+	'treemodel/batch',
+	'treemodel/delta/delta',
+	'ckeditorerror'
+);
+
+describe( 'Batch', () => {
+	let Batch, Delta, CKEditorError;
+
+	before( () => {
+		Batch = modules[ 'treemodel/batch' ];
+		Delta = modules[ 'treemodel/delta/delta' ];
+		CKEditorError = modules.ckeditorerror;
+	} );
+
+	it( 'should have registered basic methods', () => {
+		const batch = new Batch();
+
+		expect( batch.setAttr ).to.be.a( 'function' );
+		expect( batch.removeAttr ).to.be.a( 'function' );
+	} );
+
+	describe( 'Batch.register', () => {
+		let TestDelta;
+
+		before( () => {
+			TestDelta = class extends Delta {
+				constructor( batch ) {
+					super( batch, [] );
+				}
+			};
+		} );
+
+		afterEach( () => {
+			delete Batch.prototype.foo;
+		} );
+
+		it( 'should register function which return an delta', () => {
+			Batch.register( 'foo', function() {
+				this.addDelta( new TestDelta() );
+			} );
+
+			const batch = new Batch();
+
+			batch.foo();
+
+			expect( batch.deltas.length ).to.equal( 1 );
+			expect( batch.deltas[ 0 ] ).to.be.instanceof( TestDelta );
+		} );
+
+		it( 'should register function which return an multiple deltas', () => {
+			Batch.register( 'foo', function() {
+				this.addDelta( new TestDelta() );
+				this.addDelta( new TestDelta() );
+			} );
+
+			const batch = new Batch();
+
+			batch.foo();
+
+			expect( batch.deltas.length ).to.equal( 2 );
+			expect( batch.deltas[ 0 ] ).to.be.instanceof( TestDelta );
+			expect( batch.deltas[ 1 ] ).to.be.instanceof( TestDelta );
+		} );
+
+		it( 'should throw if one try to register the same batch twice', () => {
+			Batch.register( 'foo', () => {} );
+
+			expect( () => {
+				Batch.register( 'foo', () => {} );
+			} ).to.throw( CKEditorError, /^batch-register-taken/ );
+		} );
+	} );
+} );

+ 8 - 8
packages/ckeditor5-engine/tests/document/character.js → packages/ckeditor5-engine/tests/treemodel/character.js

@@ -14,20 +14,20 @@
 const getIteratorCount = bender.tools.core.getIteratorCount;
 
 const modules = bender.amd.require(
-	'document/character',
-	'document/node',
-	'document/element',
-	'document/attribute'
+	'treemodel/character',
+	'treemodel/node',
+	'treemodel/element',
+	'treemodel/attribute'
 );
 
 describe( 'Character', () => {
 	let Element, Character, Node, Attribute;
 
 	before( () => {
-		Element = modules[ 'document/element' ];
-		Character = modules[ 'document/character' ];
-		Node = modules[ 'document/node' ];
-		Attribute = modules[ 'document/attribute' ];
+		Element = modules[ 'treemodel/element' ];
+		Character = modules[ 'treemodel/character' ];
+		Node = modules[ 'treemodel/node' ];
+		Attribute = modules[ 'treemodel/attribute' ];
 	} );
 
 	describe( 'constructor', () => {

+ 99 - 73
packages/ckeditor5-engine/tests/document/deltas/changedelta.js → packages/ckeditor5-engine/tests/treemodel/delta/attributedelta.js

@@ -12,41 +12,41 @@
 const getIteratorCount = bender.tools.core.getIteratorCount;
 
 const modules = bender.amd.require(
-	'document/transaction',
-	'document/document',
-	'document/text',
-	'document/attribute',
-	'document/range',
-	'document/position',
-	'document/element',
-	'document/character' );
+	'treemodel/batch',
+	'treemodel/document',
+	'treemodel/text',
+	'treemodel/attribute',
+	'treemodel/range',
+	'treemodel/position',
+	'treemodel/element',
+	'treemodel/character' );
 
-describe( 'Transaction', () => {
-	let Transaction, Document, Text, Attribute, Range, Position, Element, Character;
+describe( 'Batch', () => {
+	let Batch, Document, Text, Attribute, Range, Position, Element, Character;
 
-	let doc, root, transaction;
+	let doc, root, batch;
 
 	before( () => {
-		Transaction = modules[ 'document/transaction' ];
-		Document = modules[ 'document/document' ];
-		Text = modules[ 'document/text' ];
-		Attribute = modules[ 'document/attribute' ];
-		Range = modules[ 'document/range' ];
-		Position = modules[ 'document/position' ];
-		Element = modules[ 'document/element' ];
-		Character = modules[ 'document/character' ];
+		Batch = modules[ 'treemodel/batch' ];
+		Document = modules[ 'treemodel/document' ];
+		Text = modules[ 'treemodel/text' ];
+		Attribute = modules[ 'treemodel/attribute' ];
+		Range = modules[ 'treemodel/range' ];
+		Position = modules[ 'treemodel/position' ];
+		Element = modules[ 'treemodel/element' ];
+		Character = modules[ 'treemodel/character' ];
 	} );
 
 	beforeEach( () => {
 		doc = new Document();
 		root = doc.createRoot( 'root' );
-		transaction = doc.createTransaction();
+		batch = doc.batch();
 	} );
 
 	function getOperationsCount() {
 		let count = 0;
 
-		for ( let delta of transaction.deltas ) {
+		for ( let delta of batch.deltas ) {
 			count += getIteratorCount( delta.operations );
 		}
 
@@ -64,62 +64,62 @@ describe( 'Transaction', () => {
 
 		describe( 'setAttr', () => {
 			it( 'should create the attribute on element', () => {
-				transaction.setAttr( 'b', 2, node );
+				batch.setAttr( 'b', 2, node );
 				expect( getOperationsCount() ).to.equal( 1 );
 				expect( node.getAttr( 'b' ) ).to.equal( 2 );
 			} );
 
 			it( 'should change the attribute of element', () => {
-				transaction.setAttr( 'a', 2, node );
+				batch.setAttr( 'a', 2, node );
 				expect( getOperationsCount() ).to.equal( 1 );
 				expect( node.getAttr( 'a' ) ).to.equal( 2 );
 			} );
 
 			it( 'should create the attribute on character', () => {
-				transaction.setAttr( 'b', 2, character );
+				batch.setAttr( 'b', 2, character );
 				expect( getOperationsCount() ).to.equal( 1 );
 				expect( character.getAttr( 'b' ) ).to.equal( 2 );
 			} );
 
 			it( 'should change the attribute of character', () => {
-				transaction.setAttr( 'a', 2, character );
+				batch.setAttr( 'a', 2, character );
 				expect( getOperationsCount() ).to.equal( 1 );
 				expect( character.getAttr( 'a' ) ).to.equal( 2 );
 			} );
 
 			it( 'should do nothing if the attribute value is the same', () => {
-				transaction.setAttr( 'a', 1, node );
+				batch.setAttr( 'a', 1, node );
 				expect( getOperationsCount() ).to.equal( 0 );
 				expect( node.getAttr( 'a' ) ).to.equal( 1 );
 			} );
 
 			it( 'should be chainable', () => {
-				const chain = transaction.setAttr( 'b', 2, node );
-				expect( chain ).to.equal( transaction );
+				const chain = batch.setAttr( 'b', 2, node );
+				expect( chain ).to.equal( batch );
 			} );
 		} );
 
 		describe( 'removeAttr', () => {
 			it( 'should remove the attribute from element', () => {
-				transaction.removeAttr( 'a', node );
+				batch.removeAttr( 'a', node );
 				expect( getOperationsCount() ).to.equal( 1 );
 				expect( node.getAttr( 'a' ) ).to.be.null;
 			} );
 
 			it( 'should remove the attribute from character', () => {
-				transaction.removeAttr( 'a', character );
+				batch.removeAttr( 'a', character );
 				expect( getOperationsCount() ).to.equal( 1 );
 				expect( character.getAttr( 'a' ) ).to.be.null;
 			} );
 
 			it( 'should do nothing if the attribute is not set', () => {
-				transaction.removeAttr( 'b', node );
+				batch.removeAttr( 'b', node );
 				expect( getOperationsCount() ).to.equal( 0 );
 			} );
 
 			it( 'should be chainable', () => {
-				const chain = transaction.removeAttr( 'a', node );
-				expect( chain ).to.equal( transaction );
+				const chain = batch.removeAttr( 'a', node );
+				expect( chain ).to.equal( batch );
 			} );
 		} );
 	} );
@@ -132,7 +132,9 @@ describe( 'Transaction', () => {
 				new Text( 'xxx', [ new Attribute( 'a', 1 ) ] ),
 				new Text( 'xxx', [ new Attribute( 'a', 2 ) ] ),
 				'xxx',
-				new Text( 'xxx', [ new Attribute( 'a', 1 ) ] )
+				new Text( 'xxx', [ new Attribute( 'a', 1 ) ] ),
+				new Element( 'e', [ new Attribute( 'a', 2 ) ], 'xxx' ),
+				'xxx'
 			] );
 		} );
 
@@ -146,9 +148,9 @@ describe( 'Transaction', () => {
 		function getChangesAttrsCount() {
 			let count = 0;
 
-			for ( let delta of transaction.deltas ) {
+			for ( let delta of batch.deltas ) {
 				for ( let operation of delta.operations ) {
-					count += getIteratorCount( operation.range );
+					count += getIteratorCount( operation.range.getNodes() );
 				}
 			}
 
@@ -156,120 +158,144 @@ describe( 'Transaction', () => {
 		}
 
 		function getCompressedAttrs() {
-			// default: 111---111222---111
+			// default: 111---111222---1112------
 			const range = Range.createFromElement( root );
 
-			return Array.from( range ).map( value => value.node.getAttr( 'a' ) || '-' ).join( '' );
+			return Array.from( range.getNodes() ).map( node => node.getAttr( 'a' ) || '-' ).join( '' );
 		}
 
 		describe( 'setAttr', () => {
 			it( 'should set the attribute on the range', () => {
-				transaction.setAttr( 'a', 3, getRange( 3, 6 ) );
+				batch.setAttr( 'a', 3, getRange( 3, 6 ) );
 				expect( getOperationsCount() ).to.equal( 1 );
 				expect( getChangesAttrsCount() ).to.equal( 3 );
-				expect( getCompressedAttrs() ).to.equal( '111333111222---111' );
+				expect( getCompressedAttrs() ).to.equal( '111333111222---1112------' );
 			} );
 
 			it( 'should split the operations if parts of the range have different attributes', () => {
-				transaction.setAttr( 'a', 3, getRange( 4, 14 ) );
+				batch.setAttr( 'a', 3, getRange( 4, 14 ) );
 				expect( getOperationsCount() ).to.equal( 4 );
 				expect( getChangesAttrsCount() ).to.equal( 10 );
-				expect( getCompressedAttrs() ).to.equal( '111-3333333333-111' );
+				expect( getCompressedAttrs() ).to.equal( '111-3333333333-1112------' );
 			} );
 
 			it( 'should split the operations if parts of the part of the range have the attribute', () => {
-				transaction.setAttr( 'a', 2, getRange( 4, 14 ) );
+				batch.setAttr( 'a', 2, getRange( 4, 14 ) );
 				expect( getOperationsCount() ).to.equal( 3 );
 				expect( getChangesAttrsCount() ).to.equal( 7 );
-				expect( getCompressedAttrs() ).to.equal( '111-2222222222-111' );
+				expect( getCompressedAttrs() ).to.equal( '111-2222222222-1112------' );
 			} );
 
 			it( 'should strip the range if the beginning have the attribute', () => {
-				transaction.setAttr( 'a', 1, getRange( 1, 5 ) );
+				batch.setAttr( 'a', 1, getRange( 1, 5 ) );
 				expect( getOperationsCount() ).to.equal( 1 );
 				expect( getChangesAttrsCount() ).to.equal( 2 );
-				expect( getCompressedAttrs() ).to.equal( '11111-111222---111' );
+				expect( getCompressedAttrs() ).to.equal( '11111-111222---1112------' );
 			} );
 
 			it( 'should strip the range if the ending have the attribute', () => {
-				transaction.setAttr( 'a', 1, getRange( 13, 17 ) );
+				batch.setAttr( 'a', 1, getRange( 13, 17 ) );
 				expect( getOperationsCount() ).to.equal( 1 );
 				expect( getChangesAttrsCount() ).to.equal( 2 );
-				expect( getCompressedAttrs() ).to.equal( '111---111222-11111' );
+				expect( getCompressedAttrs() ).to.equal( '111---111222-111112------' );
 			} );
 
 			it( 'should do nothing if the range has attribute', () => {
-				transaction.setAttr( 'a', 1, getRange( 0, 3 ) );
+				batch.setAttr( 'a', 1, getRange( 0, 3 ) );
 				expect( getOperationsCount() ).to.equal( 0 );
-				expect( getCompressedAttrs() ).to.equal( '111---111222---111' );
+				expect( getCompressedAttrs() ).to.equal( '111---111222---1112------' );
+			} );
+
+			it( 'should not check range\'s start position node when creating operations', () => {
+				let range = new Range(
+					new Position( root, [ 18, 1 ] ),
+					new Position( root, [ 19 ] )
+				);
+
+				batch.setAttr( 'a', 1, range );
+				expect( getOperationsCount() ).to.equal( 1 );
+				expect( getChangesAttrsCount() ).to.equal( 2 );
+				expect( getCompressedAttrs() ).to.equal( '111---111222---1112-11---' );
 			} );
 
 			it( 'should create a proper operations for the mixed range', () => {
-				transaction.setAttr( 'a', 1, getRange( 0, 18 ) );
-				expect( getOperationsCount() ).to.equal( 3 );
-				expect( getChangesAttrsCount() ).to.equal( 9 );
-				expect( getCompressedAttrs() ).to.equal( '111111111111111111' );
+				batch.setAttr( 'a', 1, getRange( 0, 20 ) );
+				expect( getOperationsCount() ).to.equal( 5 );
+				expect( getChangesAttrsCount() ).to.equal( 14 );
+				expect( getCompressedAttrs() ).to.equal( '11111111111111111111111--' );
 			} );
 
 			it( 'should be chainable', () => {
-				const chain = transaction.setAttr( 'a', 3, getRange( 3, 6 ) );
-				expect( chain ).to.equal( transaction );
+				const chain = batch.setAttr( 'a', 3, getRange( 3, 6 ) );
+				expect( chain ).to.equal( batch );
 			} );
 		} );
 
 		describe( 'removeAttr', () => {
 			it( 'should remove the attribute on the range', () => {
-				transaction.removeAttr( 'a', getRange( 0, 2 ) );
+				batch.removeAttr( 'a', getRange( 0, 2 ) );
 				expect( getOperationsCount() ).to.equal( 1 );
 				expect( getChangesAttrsCount() ).to.equal( 2 );
-				expect( getCompressedAttrs() ).to.equal( '--1---111222---111' );
+				expect( getCompressedAttrs() ).to.equal( '--1---111222---1112------' );
 			} );
 
 			it( 'should split the operations if parts of the range have different attributes', () => {
-				transaction.removeAttr( 'a', getRange( 7, 11 ) );
+				batch.removeAttr( 'a', getRange( 7, 11 ) );
 				expect( getOperationsCount() ).to.equal( 2 );
 				expect( getChangesAttrsCount() ).to.equal( 4 );
-				expect( getCompressedAttrs() ).to.equal( '111---1----2---111' );
+				expect( getCompressedAttrs() ).to.equal( '111---1----2---1112------' );
 			} );
 
 			it( 'should split the operations if parts of the part of the range have no attribute', () => {
-				transaction.removeAttr( 'a', getRange( 1, 7 ) );
+				batch.removeAttr( 'a', getRange( 1, 7 ) );
 				expect( getOperationsCount() ).to.equal( 2 );
 				expect( getChangesAttrsCount() ).to.equal( 3 );
-				expect( getCompressedAttrs() ).to.equal( '1------11222---111' );
+				expect( getCompressedAttrs() ).to.equal( '1------11222---1112------' );
 			} );
 
 			it( 'should strip the range if the beginning have no attribute', () => {
-				transaction.removeAttr( 'a', getRange( 4, 12 ) );
+				batch.removeAttr( 'a', getRange( 4, 12 ) );
 				expect( getOperationsCount() ).to.equal( 2 );
 				expect( getChangesAttrsCount() ).to.equal( 6 );
-				expect( getCompressedAttrs() ).to.equal( '111------------111' );
+				expect( getCompressedAttrs() ).to.equal( '111------------1112------' );
 			} );
 
 			it( 'should strip the range if the ending have no attribute', () => {
-				transaction.removeAttr( 'a', getRange( 7, 15 ) );
+				batch.removeAttr( 'a', getRange( 7, 15 ) );
 				expect( getOperationsCount() ).to.equal( 2 );
 				expect( getChangesAttrsCount() ).to.equal( 5 );
-				expect( getCompressedAttrs() ).to.equal( '111---1--------111' );
+				expect( getCompressedAttrs() ).to.equal( '111---1--------1112------' );
 			} );
 
 			it( 'should do nothing if the range has no attribute', () => {
-				transaction.removeAttr( 'a', getRange( 4, 5 ) );
+				batch.removeAttr( 'a', getRange( 4, 5 ) );
+				expect( getOperationsCount() ).to.equal( 0 );
+				expect( getCompressedAttrs() ).to.equal( '111---111222---1112------' );
+			} );
+
+			it( 'should not check range\'s start position node when creating operations', () => {
+				let range = new Range(
+					new Position( root, [ 18, 3 ] ),
+					new Position( root, [ 19 ] )
+				);
+
+				batch.removeAttr( 'a', range );
 				expect( getOperationsCount() ).to.equal( 0 );
-				expect( getCompressedAttrs() ).to.equal( '111---111222---111' );
+				expect( getChangesAttrsCount() ).to.equal( 0 );
+				expect( getCompressedAttrs() ).to.equal( '111---111222---1112------' );
 			} );
 
 			it( 'should create a proper operations for the mixed range', () => {
-				transaction.removeAttr( 'a', getRange( 3, 15 ) );
+				batch.removeAttr( 'a', getRange( 3, 15 ) );
 				expect( getOperationsCount() ).to.equal( 2 );
 				expect( getChangesAttrsCount() ).to.equal( 6 );
-				expect( getCompressedAttrs() ).to.equal( '111------------111' );
+				expect( getCompressedAttrs() ).to.equal( '111------------1112------' );
 			} );
 
 			it( 'should be chainable', () => {
-				const chain = transaction.removeAttr( 'a', getRange( 0, 2 ) );
-				expect( chain ).to.equal( transaction );
+				const chain = batch.removeAttr( 'a', getRange( 0, 2 ) );
+				expect( chain ).to.equal( batch );
 			} );
 		} );
 	} );
-} );
+} );

+ 4 - 4
packages/ckeditor5-engine/tests/document/deltas/delta.js → packages/ckeditor5-engine/tests/treemodel/delta/delta.js

@@ -12,20 +12,20 @@
 const getIteratorCount = bender.tools.core.getIteratorCount;
 
 const modules = bender.amd.require(
-	'document/delta/delta' );
+	'treemodel/delta/delta' );
 
 describe( 'Delta', () => {
 	let Delta;
 
 	before( () => {
-		Delta = modules[ 'document/delta/delta' ];
+		Delta = modules[ 'treemodel/delta/delta' ];
 	} );
 
 	describe( 'constructor', () => {
 		it( 'should create an delta with empty properties', () => {
 			const delta = new Delta();
 
-			expect( delta ).to.have.property( 'transaction' ).that.is.null;
+			expect( delta ).to.have.property( 'batch' ).that.is.null;
 			expect( delta ).to.have.property( 'operations' ).that.a( 'array' ).and.have.length( 0 );
 		} );
 	} );
@@ -64,4 +64,4 @@ describe( 'Delta', () => {
 			expect( count ).to.equal( 3 );
 		} );
 	} );
-} );
+} );

+ 12 - 12
packages/ckeditor5-engine/tests/document/deltas/insertdelta.js → packages/ckeditor5-engine/tests/treemodel/delta/insertdelta.js

@@ -8,17 +8,17 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'document/document',
-	'document/position' );
+	'treemodel/document',
+	'treemodel/position' );
 
-describe( 'Transaction', () => {
+describe( 'Batch', () => {
 	let Document, Position;
 
 	let doc, root;
 
 	before( () => {
-		Document = modules[ 'document/document' ];
-		Position = modules[ 'document/position' ];
+		Document = modules[ 'treemodel/document' ];
+		Position = modules[ 'treemodel/position' ];
 	} );
 
 	beforeEach( () => {
@@ -27,8 +27,8 @@ describe( 'Transaction', () => {
 	} );
 
 	it( 'should insert text', () => {
-		const position = new Position( [ 0 ], root );
-		doc.createTransaction().insert( position, 'foo' );
+		const position = new Position( root, [ 0 ] );
+		doc.batch().insert( position, 'foo' );
 
 		expect( root.getChildCount() ).to.equal( 3 );
 		expect( root.getChild( 0 ).character ).to.equal( 'f' );
@@ -37,10 +37,10 @@ describe( 'Transaction', () => {
 	} );
 
 	it( 'should be chainable', () => {
-		const position = new Position( [ 0 ], root );
-		const transaction = doc.createTransaction();
+		const position = new Position( root, [ 0 ] );
+		const batch = doc.batch();
 
-		const chain = transaction.insert( position, 'foo' );
-		expect( chain ).to.equal( transaction );
+		const chain = batch.insert( position, 'foo' );
+		expect( chain ).to.equal( batch );
 	} );
-} );
+} );

+ 18 - 18
packages/ckeditor5-engine/tests/document/deltas/mergedelta.js → packages/ckeditor5-engine/tests/treemodel/delta/mergedelta.js

@@ -12,22 +12,22 @@
 const getIteratorCount = bender.tools.core.getIteratorCount;
 
 const modules = bender.amd.require(
-	'document/document',
-	'document/position',
-	'document/element',
-	'document/attribute',
+	'treemodel/document',
+	'treemodel/position',
+	'treemodel/element',
+	'treemodel/attribute',
 	'ckeditorerror' );
 
-describe( 'Transaction', () => {
+describe( 'Batch', () => {
 	let Document, Position, Element, Attribute, CKEditorError;
 
 	let doc, root, p1, p2;
 
 	before( () => {
-		Document = modules[ 'document/document' ];
-		Position = modules[ 'document/position' ];
-		Element = modules[ 'document/element' ];
-		Attribute = modules[ 'document/attribute' ];
+		Document = modules[ 'treemodel/document' ];
+		Position = modules[ 'treemodel/position' ];
+		Element = modules[ 'treemodel/element' ];
+		Attribute = modules[ 'treemodel/attribute' ];
 		CKEditorError = modules.ckeditorerror;
 	} );
 
@@ -43,7 +43,7 @@ describe( 'Transaction', () => {
 
 	describe( 'merge', () => {
 		it( 'should merge foo and bar into foobar', () => {
-			doc.createTransaction().merge( new Position( [ 1 ], root ) );
+			doc.batch().merge( new Position( root, [ 1 ] ) );
 
 			expect( root.getChildCount() ).to.equal( 1 );
 			expect( root.getChild( 0 ).name ).to.equal( 'p' );
@@ -60,21 +60,21 @@ describe( 'Transaction', () => {
 
 		it( 'should throw if there is no element after', () => {
 			expect( () => {
-				doc.createTransaction().merge( new Position( [ 2 ], root ) );
-			} ).to.throw( CKEditorError, /^transaction-merge-no-element-after/ );
+				doc.batch().merge( new Position( root, [ 2 ] ) );
+			} ).to.throw( CKEditorError, /^batch-merge-no-element-after/ );
 		} );
 
 		it( 'should throw if there is no element before', () => {
 			expect( () => {
-				doc.createTransaction().merge( new Position( [ 0, 2 ], root ) );
-			} ).to.throw( CKEditorError, /^transaction-merge-no-element-before/ );
+				doc.batch().merge( new Position( root, [ 0, 2 ] ) );
+			} ).to.throw( CKEditorError, /^batch-merge-no-element-before/ );
 		} );
 
 		it( 'should be chainable', () => {
-			const transaction = doc.createTransaction();
+			const batch = doc.batch();
 
-			const chain = transaction.merge( new Position( [ 1 ], root ) );
-			expect( chain ).to.equal( transaction );
+			const chain = batch.merge( new Position( root, [ 1 ] ) );
+			expect( chain ).to.equal( batch );
 		} );
 	} );
-} );
+} );

+ 14 - 14
packages/ckeditor5-engine/tests/document/deltas/removedelta.js → packages/ckeditor5-engine/tests/treemodel/delta/removedelta.js

@@ -8,17 +8,17 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'document/document',
-	'document/position' );
+	'treemodel/document',
+	'treemodel/position' );
 
-describe( 'Transaction', () => {
+describe( 'Batch', () => {
 	let Document, Position;
 
 	let doc, root;
 
 	before( () => {
-		Document = modules[ 'document/document' ];
-		Position = modules[ 'document/position' ];
+		Document = modules[ 'treemodel/document' ];
+		Position = modules[ 'treemodel/position' ];
 	} );
 
 	beforeEach( () => {
@@ -29,8 +29,8 @@ describe( 'Transaction', () => {
 
 	describe( 'remove', () => {
 		it( 'should remove one element', () => {
-			const position = new Position( [ 1 ], root );
-			doc.createTransaction().remove( position );
+			const position = new Position( root, [ 1 ] );
+			doc.batch().remove( position );
 
 			expect( root.getChildCount() ).to.equal( 5 );
 			expect( root.getChild( 0 ).character ).to.equal( 'f' );
@@ -41,8 +41,8 @@ describe( 'Transaction', () => {
 		} );
 
 		it( 'should remove 3 elements', () => {
-			const position = new Position( [ 1 ], root );
-			doc.createTransaction().remove( position, 3 );
+			const position = new Position( root, [ 1 ] );
+			doc.batch().remove( position, 3 );
 
 			expect( root.getChildCount() ).to.equal( 3 );
 			expect( root.getChild( 0 ).character ).to.equal( 'f' );
@@ -51,11 +51,11 @@ describe( 'Transaction', () => {
 		} );
 
 		it( 'should be chainable', () => {
-			const position = new Position( [ 1 ], root );
-			const transaction = doc.createTransaction();
+			const position = new Position( root, [ 1 ] );
+			const batch = doc.batch();
 
-			const chain = transaction.remove( position );
-			expect( chain ).to.equal( transaction );
+			const chain = batch.remove( position );
+			expect( chain ).to.equal( batch );
 		} );
 	} );
-} );
+} );

+ 17 - 17
packages/ckeditor5-engine/tests/document/deltas/splitdelta.js → packages/ckeditor5-engine/tests/treemodel/delta/splitdelta.js

@@ -12,22 +12,22 @@
 const getIteratorCount = bender.tools.core.getIteratorCount;
 
 const modules = bender.amd.require(
-	'document/document',
-	'document/position',
-	'document/element',
-	'document/attribute',
+	'treemodel/document',
+	'treemodel/position',
+	'treemodel/element',
+	'treemodel/attribute',
 	'ckeditorerror' );
 
-describe( 'Transaction', () => {
+describe( 'Batch', () => {
 	let Document, Position, Element, Attribute, CKEditorError;
 
 	let doc, root, p;
 
 	before( () => {
-		Document = modules[ 'document/document' ];
-		Position = modules[ 'document/position' ];
-		Element = modules[ 'document/element' ];
-		Attribute = modules[ 'document/attribute' ];
+		Document = modules[ 'treemodel/document' ];
+		Position = modules[ 'treemodel/position' ];
+		Element = modules[ 'treemodel/element' ];
+		Attribute = modules[ 'treemodel/attribute' ];
 		CKEditorError = modules.ckeditorerror;
 	} );
 
@@ -42,7 +42,7 @@ describe( 'Transaction', () => {
 
 	describe( 'split', () => {
 		it( 'should split foobar to foo and bar', () => {
-			doc.createTransaction().split( new Position( [ 0, 3 ], root ) );
+			doc.batch().split( new Position( root, [ 0, 3 ] ) );
 
 			expect( root.getChildCount() ).to.equal( 2 );
 
@@ -64,7 +64,7 @@ describe( 'Transaction', () => {
 		} );
 
 		it( 'should create an empty paragraph if we split at the end', () => {
-			doc.createTransaction().split( new Position( [ 0, 6 ], root ) );
+			doc.batch().split( new Position( root, [ 0, 6 ] ) );
 
 			expect( root.getChildCount() ).to.equal( 2 );
 
@@ -87,15 +87,15 @@ describe( 'Transaction', () => {
 
 		it( 'should throw if we try to split a root', () => {
 			expect( () => {
-				doc.createTransaction().split( new Position( [ 0 ], root ) );
-			} ).to.throw( CKEditorError, /^transaction-split-root/ );
+				doc.batch().split( new Position( root, [ 0 ] ) );
+			} ).to.throw( CKEditorError, /^batch-split-root/ );
 		} );
 
 		it( 'should be chainable', () => {
-			const transaction = doc.createTransaction();
+			const batch = doc.batch();
 
-			const chain = transaction.split( new Position( [ 0, 3 ], root ) );
-			expect( chain ).to.equal( transaction );
+			const chain = batch.split( new Position( root, [ 0, 3 ] ) );
+			expect( chain ).to.equal( batch );
 		} );
 	} );
-} );
+} );

+ 34 - 34
packages/ckeditor5-engine/tests/document/document/change-event.js → packages/ckeditor5-engine/tests/treemodel/document/change-event.js

@@ -8,36 +8,36 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'document/document',
-	'document/element',
-	'document/rootelement',
-	'document/attribute',
-	'document/position',
-	'document/range',
-	'document/operation/changeoperation',
-	'document/operation/insertoperation',
-	'document/operation/moveoperation',
-	'document/operation/reinsertoperation',
-	'document/operation/removeoperation'
+	'treemodel/document',
+	'treemodel/element',
+	'treemodel/rootelement',
+	'treemodel/attribute',
+	'treemodel/position',
+	'treemodel/range',
+	'treemodel/operation/attributeoperation',
+	'treemodel/operation/insertoperation',
+	'treemodel/operation/moveoperation',
+	'treemodel/operation/reinsertoperation',
+	'treemodel/operation/removeoperation'
 );
 
 describe( 'Document change event', () => {
 	let Document, RootElement, Element, Range, Position;
-	let ChangeOperation, InsertOperation, MoveOperation, ReinsertOperation, RemoveOperation, Attribute;
+	let AttributeOperation, InsertOperation, MoveOperation, ReinsertOperation, RemoveOperation, Attribute;
 
 	before( () => {
-		Document = modules[ 'document/document' ];
-		Element = modules[ 'document/element' ];
-		RootElement = modules[ 'document/rootelement' ];
-		Attribute = modules[ 'document/attribute' ];
-		Position = modules[ 'document/position' ];
-		Range = modules[ 'document/range' ];
-
-		InsertOperation = modules[ 'document/operation/insertoperation' ];
-		ChangeOperation = modules[ 'document/operation/changeoperation' ];
-		MoveOperation = modules[ 'document/operation/moveoperation' ];
-		ReinsertOperation = modules[ 'document/operation/reinsertoperation' ];
-		RemoveOperation = modules[ 'document/operation/removeoperation' ];
+		Document = modules[ 'treemodel/document' ];
+		Element = modules[ 'treemodel/element' ];
+		RootElement = modules[ 'treemodel/rootelement' ];
+		Attribute = modules[ 'treemodel/attribute' ];
+		Position = modules[ 'treemodel/position' ];
+		Range = modules[ 'treemodel/range' ];
+
+		InsertOperation = modules[ 'treemodel/operation/insertoperation' ];
+		AttributeOperation = modules[ 'treemodel/operation/attributeoperation' ];
+		MoveOperation = modules[ 'treemodel/operation/moveoperation' ];
+		ReinsertOperation = modules[ 'treemodel/operation/reinsertoperation' ];
+		RemoveOperation = modules[ 'treemodel/operation/removeoperation' ];
 	} );
 
 	let doc, root, graveyard, types, changes;
@@ -45,7 +45,7 @@ describe( 'Document change event', () => {
 	beforeEach( () => {
 		doc = new Document();
 		root = doc.createRoot( 'root' );
-		graveyard = doc._graveyard;
+		graveyard = doc.graveyard;
 
 		types = [];
 		changes = [];
@@ -57,7 +57,7 @@ describe( 'Document change event', () => {
 	} );
 
 	it( 'should be fired when text is inserted', () => {
-		doc.applyOperation( new InsertOperation( new Position( [ 0 ], root ), 'foo', doc.version ) );
+		doc.applyOperation( new InsertOperation( new Position( root, [ 0 ] ), 'foo', doc.version ) );
 
 		expect( changes ).to.have.length( 1 );
 		expect( types[ 0 ] ).to.equal( 'insert' );
@@ -66,7 +66,7 @@ describe( 'Document change event', () => {
 
 	it( 'should be fired when element is inserted', () => {
 		const element = new Element( 'p' );
-		doc.applyOperation( new InsertOperation( new Position( [ 0 ], root ), element, doc.version ) );
+		doc.applyOperation( new InsertOperation( new Position( root, [ 0 ] ), element, doc.version ) );
 
 		expect( changes ).to.have.length( 1 );
 		expect( types[ 0 ] ).to.equal( 'insert' );
@@ -75,7 +75,7 @@ describe( 'Document change event', () => {
 
 	it( 'should be fired when nodes are inserted', () => {
 		const element = new Element( 'p' );
-		doc.applyOperation( new InsertOperation( new Position( [ 0 ], root ), [ element, 'foo' ], doc.version ) );
+		doc.applyOperation( new InsertOperation( new Position( root, [ 0 ] ), [ element, 'foo' ], doc.version ) );
 
 		expect( changes ).to.have.length( 1 );
 		expect( types[ 0 ] ).to.equal( 'insert' );
@@ -92,9 +92,9 @@ describe( 'Document change event', () => {
 
 		doc.applyOperation(
 			new MoveOperation(
-				new Position( [ 0, 0 ], root ),
-				new Position( [ 1, 0 ], root ),
+				new Position( root, [ 0, 0 ] ),
 				3,
+				new Position( root, [ 1, 0 ] ),
 				doc.version
 			)
 		);
@@ -108,7 +108,7 @@ describe( 'Document change event', () => {
 	it( 'should be fired when multiple nodes are removed and reinserted', () => {
 		root.insertChildren( 0, 'foo' );
 
-		const removeOperation = new RemoveOperation( new Position( [ 0 ], root ), 3, doc.version );
+		const removeOperation = new RemoveOperation( new Position( root, [ 0 ] ), 3, doc.version );
 		doc.applyOperation( removeOperation );
 
 		const reinsertOperation = removeOperation.getReversed();
@@ -129,7 +129,7 @@ describe( 'Document change event', () => {
 		root.insertChildren( 0, 'foo' );
 
 		doc.applyOperation(
-			new ChangeOperation(
+			new AttributeOperation(
 				Range.createFromParentsAndOffsets( root, 0, root, 3 ),
 				null,
 				new Attribute( 'key', 'new' ),
@@ -149,7 +149,7 @@ describe( 'Document change event', () => {
 		root.insertChildren( 0, elem );
 
 		doc.applyOperation(
-			new ChangeOperation(
+			new AttributeOperation(
 				Range.createFromParentsAndOffsets( root, 0, elem, 0 ),
 				new Attribute( 'key', 'old' ),
 				null,
@@ -169,7 +169,7 @@ describe( 'Document change event', () => {
 		root.insertChildren( 0, elem );
 
 		doc.applyOperation(
-			new ChangeOperation(
+			new AttributeOperation(
 				Range.createFromParentsAndOffsets( root, 0, elem, 0 ),
 				new Attribute( 'key', 'old' ),
 				new Attribute( 'key', 'new' ),

+ 14 - 14
packages/ckeditor5-engine/tests/document/document/document.js → packages/ckeditor5-engine/tests/treemodel/document/document.js

@@ -8,19 +8,19 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'document/document',
-	'document/rootelement',
-	'document/transaction',
+	'treemodel/document',
+	'treemodel/rootelement',
+	'treemodel/batch',
 	'ckeditorerror'
 );
 
 describe( 'Document', () => {
-	let Document, RootElement, Transaction, CKEditorError;
+	let Document, RootElement, Batch, CKEditorError;
 
 	before( () => {
-		Document = modules[ 'document/document' ];
-		RootElement = modules[ 'document/rootelement' ];
-		Transaction = modules[ 'document/transaction' ];
+		Document = modules[ 'treemodel/document' ];
+		RootElement = modules[ 'treemodel/rootelement' ];
+		Batch = modules[ 'treemodel/batch' ];
 		CKEditorError = modules.ckeditorerror;
 	} );
 
@@ -34,8 +34,8 @@ describe( 'Document', () => {
 		it( 'should create Document with no data and empty graveyard', () => {
 			expect( document ).to.have.property( 'roots' ).that.is.instanceof( Map );
 			expect( document.roots.size ).to.equal( 1 );
-			expect( document._graveyard ).to.be.instanceof( RootElement );
-			expect( document._graveyard.getChildCount() ).to.equal( 0 );
+			expect( document.graveyard ).to.be.instanceof( RootElement );
+			expect( document.graveyard.getChildCount() ).to.equal( 0 );
 		} );
 	} );
 
@@ -112,12 +112,12 @@ describe( 'Document', () => {
 		} );
 	} );
 
-	describe( 'createTransaction', () => {
-		it( 'should create a new transaction with the document property', () => {
-			const transaction = document.createTransaction();
+	describe( 'batch', () => {
+		it( 'should create a new batch with the document property', () => {
+			const batch = document.batch();
 
-			expect( transaction ).to.be.instanceof( Transaction );
-			expect( transaction ).to.have.property( 'doc' ).that.equals( document );
+			expect( batch ).to.be.instanceof( Batch );
+			expect( batch ).to.have.property( 'doc' ).that.equals( document );
 		} );
 	} );
 } );

+ 8 - 8
packages/ckeditor5-engine/tests/document/element.js → packages/ckeditor5-engine/tests/treemodel/element.js

@@ -14,20 +14,20 @@
 const getIteratorCount = bender.tools.core.getIteratorCount;
 
 const modules = bender.amd.require(
-	'document/node',
-	'document/nodelist',
-	'document/element',
-	'document/attribute'
+	'treemodel/node',
+	'treemodel/nodelist',
+	'treemodel/element',
+	'treemodel/attribute'
 );
 
 describe( 'Element', () => {
 	let Element, Node, NodeList, Attribute;
 
 	before( () => {
-		Element = modules[ 'document/element' ];
-		Node = modules[ 'document/node' ];
-		NodeList = modules[ 'document/nodelist' ];
-		Attribute = modules[ 'document/attribute' ];
+		Element = modules[ 'treemodel/element' ];
+		Node = modules[ 'treemodel/node' ];
+		NodeList = modules[ 'treemodel/nodelist' ];
+		Attribute = modules[ 'treemodel/attribute' ];
 	} );
 
 	describe( 'constructor', () => {

+ 8 - 8
packages/ckeditor5-engine/tests/document/node.js → packages/ckeditor5-engine/tests/treemodel/node.js

@@ -12,10 +12,10 @@
 const getIteratorCount = bender.tools.core.getIteratorCount;
 
 const modules = bender.amd.require(
-	'document/element',
-	'document/character',
-	'document/attribute',
-	'document/nodelist',
+	'treemodel/element',
+	'treemodel/character',
+	'treemodel/attribute',
+	'treemodel/nodelist',
 	'ckeditorerror'
 );
 
@@ -27,10 +27,10 @@ describe( 'Node', () => {
 	let charB, charA, charR, img;
 
 	before( () => {
-		Element = modules[ 'document/element' ];
-		Character = modules[ 'document/character' ];
-		Attribute = modules[ 'document/attribute' ];
-		NodeList = modules[ 'document/nodelist' ];
+		Element = modules[ 'treemodel/element' ];
+		Character = modules[ 'treemodel/character' ];
+		Attribute = modules[ 'treemodel/attribute' ];
+		NodeList = modules[ 'treemodel/nodelist' ];
 		CKEditorError = modules.ckeditorerror;
 
 		charB = new Character( 'b' );

+ 8 - 8
packages/ckeditor5-engine/tests/document/nodelist.js → packages/ckeditor5-engine/tests/treemodel/nodelist.js

@@ -8,20 +8,20 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'document/nodelist',
-	'document/character',
-	'document/text',
-	'document/attribute'
+	'treemodel/nodelist',
+	'treemodel/character',
+	'treemodel/text',
+	'treemodel/attribute'
 );
 
 describe( 'NodeList', () => {
 	let NodeList, Character, Text, Attribute;
 
 	before( () => {
-		NodeList = modules[ 'document/nodelist' ];
-		Character = modules[ 'document/character' ];
-		Text = modules[ 'document/text' ];
-		Attribute = modules[ 'document/attribute' ];
+		NodeList = modules[ 'treemodel/nodelist' ];
+		Character = modules[ 'treemodel/character' ];
+		Text = modules[ 'treemodel/text' ];
+		Attribute = modules[ 'treemodel/attribute' ];
 	} );
 
 	describe( 'constructor', () => {

+ 94 - 52
packages/ckeditor5-engine/tests/document/operation/changeoperation.js → packages/ckeditor5-engine/tests/treemodel/operation/attributeoperation.js

@@ -13,27 +13,29 @@
 const getIteratorCount = bender.tools.core.getIteratorCount;
 
 const modules = bender.amd.require(
-	'document/document',
-	'document/operation/changeoperation',
-	'document/position',
-	'document/range',
-	'document/character',
-	'document/attribute',
-	'document/text',
+	'treemodel/document',
+	'treemodel/element',
+	'treemodel/operation/attributeoperation',
+	'treemodel/position',
+	'treemodel/range',
+	'treemodel/character',
+	'treemodel/attribute',
+	'treemodel/text',
 	'ckeditorerror'
 );
 
-describe( 'ChangeOperation', () => {
-	let Document, ChangeOperation, Position, Range, Character, Attribute, Text, CKEditorError;
+describe( 'AttributeOperation', () => {
+	let Document, Element, AttributeOperation, Position, Range, Character, Attribute, Text, CKEditorError;
 
 	before( () => {
-		Document = modules[ 'document/document' ];
-		ChangeOperation = modules[ 'document/operation/changeoperation' ];
-		Position = modules[ 'document/position' ];
-		Range = modules[ 'document/range' ];
-		Character = modules[ 'document/character' ];
-		Attribute = modules[ 'document/attribute' ];
-		Text = modules[ 'document/text' ];
+		Document = modules[ 'treemodel/document' ];
+		Element = modules[ 'treemodel/element' ];
+		AttributeOperation = modules[ 'treemodel/operation/attributeoperation' ];
+		Position = modules[ 'treemodel/position' ];
+		Range = modules[ 'treemodel/range' ];
+		Character = modules[ 'treemodel/character' ];
+		Attribute = modules[ 'treemodel/attribute' ];
+		Text = modules[ 'treemodel/text' ];
 		CKEditorError = modules.ckeditorerror;
 	} );
 
@@ -45,14 +47,14 @@ describe( 'ChangeOperation', () => {
 	} );
 
 	it( 'should have proper type', () => {
-		const opp = new ChangeOperation(
-			new Range( new Position( [ 0 ], root ), new Position( [ 2 ], root ) ),
+		const op = new AttributeOperation(
+			new Range( new Position( root, [ 0 ] ), new Position( root, [ 2 ] ) ),
 			null,
 			new Attribute( 'isNew', true ),
 			doc.version
 		);
 
-		expect( opp.type ).to.equal( 'attr' );
+		expect( op.type ).to.equal( 'attr' );
 	} );
 
 	it( 'should insert attribute to the set of nodes', () => {
@@ -61,8 +63,8 @@ describe( 'ChangeOperation', () => {
 		root.insertChildren( 0, 'bar' );
 
 		doc.applyOperation(
-			new ChangeOperation(
-				new Range( new Position( [ 0 ], root ), new Position( [ 2 ], root ) ),
+			new AttributeOperation(
+				new Range( new Position( root, [ 0 ] ), new Position( root, [ 2 ] ) ),
 				null,
 				newAttr,
 				doc.version
@@ -84,8 +86,8 @@ describe( 'ChangeOperation', () => {
 		root.insertChildren( 0, new Character( 'x', [ fooAttr, barAttr ] ) );
 
 		doc.applyOperation(
-			new ChangeOperation(
-				new Range( new Position( [ 0 ], root ), new Position( [ 1 ], root ) ),
+			new AttributeOperation(
+				new Range( new Position( root, [ 0 ] ), new Position( root, [ 1 ] ) ),
 				null,
 				newAttr,
 				doc.version
@@ -107,8 +109,8 @@ describe( 'ChangeOperation', () => {
 		root.insertChildren( 0, new Text( 'bar', [ oldAttr ] ) );
 
 		doc.applyOperation(
-			new ChangeOperation(
-				new Range( new Position( [ 0 ], root ), new Position( [ 2 ], root ) ),
+			new AttributeOperation(
+				new Range( new Position( root, [ 0 ] ), new Position( root, [ 2 ] ) ),
 				oldAttr,
 				newAttr,
 				doc.version
@@ -134,8 +136,8 @@ describe( 'ChangeOperation', () => {
 		root.insertChildren( 0, new Character( 'x', [ fooAttr, x1Attr, barAttr ] ) );
 
 		doc.applyOperation(
-			new ChangeOperation(
-				new Range( new Position( [ 0 ], root ), new Position( [ 1 ], root ) ),
+			new AttributeOperation(
+				new Range( new Position( root, [ 0 ] ), new Position( root, [ 1 ] ) ),
 				x1Attr,
 				x2Attr,
 				doc.version
@@ -158,8 +160,8 @@ describe( 'ChangeOperation', () => {
 		root.insertChildren( 0, new Character( 'x', [ fooAttr, xAttr, barAttr ] ) );
 
 		doc.applyOperation(
-			new ChangeOperation(
-				new Range( new Position( [ 0 ], root ), new Position( [ 1 ], root ) ),
+			new AttributeOperation(
+				new Range( new Position( root, [ 0 ] ), new Position( root, [ 1 ] ) ),
 				xAttr,
 				null,
 				doc.version
@@ -173,14 +175,14 @@ describe( 'ChangeOperation', () => {
 		expect( root.getChild( 0 ).hasAttr( barAttr ) ).to.be.true;
 	} );
 
-	it( 'should create a change operation as a reverse', () => {
+	it( 'should create an AttributeOperation as a reverse', () => {
 		let oldAttr = new Attribute( 'x', 'old' );
 		let newAttr = new Attribute( 'x', 'new' );
-		let range = new Range( new Position( [ 0 ], root ), new Position( [ 3 ], root ) );
-		let operation = new ChangeOperation( range, oldAttr, newAttr, doc.version );
+		let range = new Range( new Position( root, [ 0 ] ), new Position( root, [ 3 ] ) );
+		let operation = new AttributeOperation( range, oldAttr, newAttr, doc.version );
 		let reverse = operation.getReversed();
 
-		expect( reverse ).to.be.an.instanceof( ChangeOperation );
+		expect( reverse ).to.be.an.instanceof( AttributeOperation );
 		expect( reverse.baseVersion ).to.equal( 1 );
 		expect( reverse.range ).to.equal( range );
 		expect( reverse.oldAttr ).to.equal( newAttr );
@@ -192,8 +194,8 @@ describe( 'ChangeOperation', () => {
 
 		root.insertChildren( 0, 'bar' );
 
-		let operation = new ChangeOperation(
-			new Range( new Position( [ 0 ], root ), new Position( [ 3 ], root ) ),
+		let operation = new AttributeOperation(
+			new Range( new Position( root, [ 0 ] ), new Position( root, [ 3 ] ) ),
 			null,
 			newAttr,
 			doc.version
@@ -211,14 +213,54 @@ describe( 'ChangeOperation', () => {
 		expect( getIteratorCount( root.getChild( 2 ).getAttrs() ) ).to.equal( 0 );
 	} );
 
+	it( 'should not set attribute of element if change range starts in the middle of that element', () => {
+		let fooAttr = new Attribute( 'foo', true );
+
+		let eleA = new Element( 'a', [], 'abc' );
+		let eleB = new Element( 'b', [], 'xyz' );
+
+		root.insertChildren( 0, [ eleA, eleB ] );
+
+		doc.applyOperation(
+			new AttributeOperation(
+				new Range( new Position( root, [ 0, 2 ] ), new Position( root, [ 1, 2 ] ) ),
+				null,
+				fooAttr,
+				doc.version
+			)
+		);
+
+		expect( root.getChild( 0 ).hasAttr( fooAttr ) ).to.be.false;
+	} );
+
+	it( 'should not remove attribute of element if change range starts in the middle of that element', () => {
+		let fooAttr = new Attribute( 'foo', true );
+
+		let eleA = new Element( 'a', [ fooAttr ], 'abc' );
+		let eleB = new Element( 'b', [ fooAttr ], 'xyz' );
+
+		root.insertChildren( 0, [ eleA, eleB ] );
+
+		doc.applyOperation(
+			new AttributeOperation(
+				new Range( new Position( root, [ 0, 3 ] ), new Position( root, [ 1, 0 ] ) ),
+				fooAttr,
+				null,
+				doc.version
+			)
+		);
+
+		expect( root.getChild( 0 ).hasAttr( fooAttr ) ).to.be.true;
+	} );
+
 	it( 'should undo changing attribute by applying reverse operation', () => {
 		let oldAttr = new Attribute( 'isNew', false );
 		let newAttr = new Attribute( 'isNew', true );
 
 		root.insertChildren( 0, new Text( 'bar', [ oldAttr ] ) );
 
-		let operation = new ChangeOperation(
-			new Range( new Position( [ 0 ], root ), new Position( [ 3 ], root ) ),
+		let operation = new AttributeOperation(
+			new Range( new Position( root, [ 0 ] ), new Position( root, [ 3 ] ) ),
 			oldAttr,
 			newAttr,
 			doc.version
@@ -245,8 +287,8 @@ describe( 'ChangeOperation', () => {
 
 		root.insertChildren( 0, new Text( 'bar', [ fooAttr ] ) );
 
-		let operation = new ChangeOperation(
-			new Range( new Position( [ 0 ], root ), new Position( [ 3 ], root ) ),
+		let operation = new AttributeOperation(
+			new Range( new Position( root, [ 0 ] ), new Position( root, [ 3 ] ) ),
 			fooAttr,
 			null,
 			doc.version
@@ -275,14 +317,14 @@ describe( 'ChangeOperation', () => {
 
 		expect( () => {
 			doc.applyOperation(
-				new ChangeOperation(
-					new Range( new Position( [ 0 ], root ), new Position( [ 1 ], root ) ),
+				new AttributeOperation(
+					new Range( new Position( root, [ 0 ] ), new Position( root, [ 1 ] ) ),
 					fooAttr,
 					null,
 					doc.version
 				)
 			);
-		} ).to.throw( CKEditorError, /operation-change-no-attr-to-remove/ );
+		} ).to.throw( CKEditorError, /operation-attribute-no-attr-to-remove/ );
 	} );
 
 	it( 'should throw an error when one try to insert and the attribute already exists', () => {
@@ -293,14 +335,14 @@ describe( 'ChangeOperation', () => {
 
 		expect( () => {
 			doc.applyOperation(
-				new ChangeOperation(
-					new Range( new Position( [ 0 ], root ), new Position( [ 1 ], root ) ),
+				new AttributeOperation(
+					new Range( new Position( root, [ 0 ] ), new Position( root, [ 1 ] ) ),
 					null,
 					x2Attr,
 					doc.version
 				)
 			);
-		} ).to.throw( CKEditorError, /operation-change-attr-exists/ );
+		} ).to.throw( CKEditorError, /operation-attribute-attr-exists/ );
 	} );
 
 	it( 'should throw an error when one try to change and the new and old attributes have different keys', () => {
@@ -311,30 +353,30 @@ describe( 'ChangeOperation', () => {
 
 		expect( () => {
 			doc.applyOperation(
-				new ChangeOperation(
-					new Range( new Position( [ 0 ], root ), new Position( [ 1 ], root ) ),
+				new AttributeOperation(
+					new Range( new Position( root, [ 0 ] ), new Position( root, [ 1 ] ) ),
 					fooAttr,
 					barAttr,
 					doc.version
 				)
 			);
-		} ).to.throw( CKEditorError, /operation-change-different-keys/ );
+		} ).to.throw( CKEditorError, /operation-attribute-different-keys/ );
 	} );
 
-	it( 'should create operation with the same parameters when cloned', () => {
-		let range = new Range( new Position( [ 0 ], root ), new Position( [ 1 ], root ) );
+	it( 'should create an AttributeOperation with the same parameters when cloned', () => {
+		let range = new Range( new Position( root, [ 0 ] ), new Position( root, [ 1 ] ) );
 		let oldAttr = new Attribute( 'foo', 'old' );
 		let newAttr = new Attribute( 'foo', 'bar' );
 		let baseVersion = doc.version;
 
-		let op = new ChangeOperation( range, oldAttr, newAttr, baseVersion );
+		let op = new AttributeOperation( range, oldAttr, newAttr, baseVersion );
 
 		let clone = op.clone();
 
 		// New instance rather than a pointer to the old instance.
 		expect( clone ).not.to.be.equal( op );
 
-		expect( clone ).to.be.instanceof( ChangeOperation );
+		expect( clone ).to.be.instanceof( AttributeOperation );
 		expect( clone.range.isEqual( range ) ).to.be.true;
 		expect( clone.oldAttr.isEqual( oldAttr ) ).to.be.true;
 		expect( clone.newAttr.isEqual( newAttr ) ).to.be.true;

+ 24 - 24
packages/ckeditor5-engine/tests/document/operation/insertoperation.js → packages/ckeditor5-engine/tests/treemodel/operation/insertoperation.js

@@ -9,27 +9,27 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'document/document',
-	'document/node',
-	'document/nodelist',
-	'document/operation/insertoperation',
-	'document/operation/removeoperation',
-	'document/position',
-	'document/character',
-	'document/nodelist'
+	'treemodel/document',
+	'treemodel/node',
+	'treemodel/nodelist',
+	'treemodel/operation/insertoperation',
+	'treemodel/operation/removeoperation',
+	'treemodel/position',
+	'treemodel/character',
+	'treemodel/nodelist'
 );
 
 describe( 'InsertOperation', () => {
 	let Document, Node, NodeList, InsertOperation, RemoveOperation, Position, Character;
 
 	before( () => {
-		Document = modules[ 'document/document' ];
-		Node = modules[ 'document/node' ];
-		NodeList = modules[ 'document/nodelist' ];
-		InsertOperation = modules[ 'document/operation/insertoperation' ];
-		RemoveOperation = modules[ 'document/operation/removeoperation' ];
-		Position = modules[ 'document/position' ];
-		Character = modules[ 'document/character' ];
+		Document = modules[ 'treemodel/document' ];
+		Node = modules[ 'treemodel/node' ];
+		NodeList = modules[ 'treemodel/nodelist' ];
+		InsertOperation = modules[ 'treemodel/operation/insertoperation' ];
+		RemoveOperation = modules[ 'treemodel/operation/removeoperation' ];
+		Position = modules[ 'treemodel/position' ];
+		Character = modules[ 'treemodel/character' ];
 	} );
 
 	let doc, root;
@@ -41,7 +41,7 @@ describe( 'InsertOperation', () => {
 
 	it( 'should have proper type', () => {
 		const opp = new InsertOperation(
-			new Position( [ 0 ], root ),
+			new Position( root, [ 0 ] ),
 			new Character( 'x' ),
 			doc.version
 		);
@@ -52,7 +52,7 @@ describe( 'InsertOperation', () => {
 	it( 'should insert node', () => {
 		doc.applyOperation(
 			new InsertOperation(
-				new Position( [ 0 ], root ),
+				new Position( root, [ 0 ] ),
 				new Character( 'x' ),
 				doc.version
 			)
@@ -66,7 +66,7 @@ describe( 'InsertOperation', () => {
 	it( 'should insert set of nodes', () => {
 		doc.applyOperation(
 			new InsertOperation(
-				new Position( [ 0 ], root ),
+				new Position( root, [ 0 ] ),
 				'bar',
 				doc.version
 			)
@@ -84,7 +84,7 @@ describe( 'InsertOperation', () => {
 
 		doc.applyOperation(
 			new InsertOperation(
-				new Position( [ 1 ], root ),
+				new Position( root, [ 1 ] ),
 				'bar',
 				doc.version
 			)
@@ -102,7 +102,7 @@ describe( 'InsertOperation', () => {
 	it( 'should insert text', () => {
 		doc.applyOperation(
 			new InsertOperation(
-				new Position( [ 0 ], root ),
+				new Position( root, [ 0 ] ),
 				[ 'foo', new Character( 'x' ), 'bar' ],
 				doc.version
 			)
@@ -120,7 +120,7 @@ describe( 'InsertOperation', () => {
 	} );
 
 	it( 'should create a remove operation as a reverse', () => {
-		let position = new Position( [ 0 ], root );
+		let position = new Position( root, [ 0 ] );
 		let operation = new InsertOperation(
 			position,
 			[ 'foo', new Character( 'x' ), 'bar' ],
@@ -137,7 +137,7 @@ describe( 'InsertOperation', () => {
 
 	it( 'should undo insert node by applying reverse operation', () => {
 		let operation = new InsertOperation(
-			new Position( [ 0 ], root ),
+			new Position( root, [ 0 ] ),
 			new Character( 'x' ),
 			doc.version
 		);
@@ -156,7 +156,7 @@ describe( 'InsertOperation', () => {
 
 	it( 'should undo insert set of nodes by applying reverse operation', () => {
 		let operation = new InsertOperation(
-			new Position( [ 0 ], root ),
+			new Position( root, [ 0 ] ),
 			'bar',
 			doc.version
 		);
@@ -174,7 +174,7 @@ describe( 'InsertOperation', () => {
 	} );
 
 	it( 'should create operation with the same parameters when cloned', () => {
-		let position = new Position( [ 0 ], root );
+		let position = new Position( root, [ 0 ] );
 		let nodeA = new Node();
 		let nodeB = new Node();
 		let nodes = new NodeList( [ nodeA, nodeB ] );

+ 36 - 36
packages/ckeditor5-engine/tests/document/operation/moveoperation.js → packages/ckeditor5-engine/tests/treemodel/operation/moveoperation.js

@@ -9,11 +9,11 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'document/document',
-	'document/operation/moveoperation',
-	'document/position',
-	'document/element',
-	'document/nodelist',
+	'treemodel/document',
+	'treemodel/operation/moveoperation',
+	'treemodel/position',
+	'treemodel/element',
+	'treemodel/nodelist',
 	'ckeditorerror'
 );
 
@@ -21,11 +21,11 @@ describe( 'MoveOperation', () => {
 	let Document, MoveOperation, Position, Element, NodeList, CKEditorError;
 
 	before( () => {
-		Document = modules[ 'document/document' ];
-		MoveOperation = modules[ 'document/operation/moveoperation' ];
-		Position = modules[ 'document/position' ];
-		Element = modules[ 'document/element' ];
-		NodeList = modules[ 'document/nodelist' ];
+		Document = modules[ 'treemodel/document' ];
+		MoveOperation = modules[ 'treemodel/operation/moveoperation' ];
+		Position = modules[ 'treemodel/position' ];
+		Element = modules[ 'treemodel/element' ];
+		NodeList = modules[ 'treemodel/nodelist' ];
 		CKEditorError = modules.ckeditorerror;
 	} );
 
@@ -38,8 +38,8 @@ describe( 'MoveOperation', () => {
 
 	it( 'should have proper type', () => {
 		const opp = new MoveOperation(
-			new Position( [ 0, 0 ], root ),
-			new Position( [ 1, 0 ], root ),
+			new Position( root, [ 0, 0 ] ),
+			new Position( root, [ 1, 0 ] ),
 			1,
 			doc.version
 		);
@@ -55,9 +55,9 @@ describe( 'MoveOperation', () => {
 
 		doc.applyOperation(
 			new MoveOperation(
-				new Position( [ 0, 0 ], root ),
-				new Position( [ 1, 0 ], root ),
+				new Position( root, [ 0, 0 ] ),
 				1,
+				new Position( root, [ 1, 0 ] ),
 				doc.version
 			)
 		);
@@ -76,9 +76,9 @@ describe( 'MoveOperation', () => {
 
 		doc.applyOperation(
 			new MoveOperation(
-				new Position( [ 2 ], root ),
-				new Position( [ 1 ], root ),
+				new Position( root, [ 2 ] ),
 				2,
+				new Position( root, [ 1 ] ),
 				doc.version
 			)
 		);
@@ -97,9 +97,9 @@ describe( 'MoveOperation', () => {
 
 		doc.applyOperation(
 			new MoveOperation(
-				new Position( [ 1 ], root ),
-				new Position( [ 4 ], root ),
+				new Position( root, [ 1 ] ),
 				2,
+				new Position( root, [ 4 ] ),
 				doc.version
 			)
 		);
@@ -116,10 +116,10 @@ describe( 'MoveOperation', () => {
 	it( 'should create a move operation as a reverse', () => {
 		let nodeList = new NodeList( 'bar' );
 
-		let sourcePosition = new Position( [ 0 ], root );
-		let targetPosition = new Position( [ 4 ], root );
+		let sourcePosition = new Position( root, [ 0 ] );
+		let targetPosition = new Position( root, [ 4 ] );
 
-		let operation = new MoveOperation( sourcePosition, targetPosition, nodeList.length, doc.version );
+		let operation = new MoveOperation( sourcePosition, nodeList.length, targetPosition, doc.version );
 
 		let reverse = operation.getReversed();
 
@@ -137,9 +137,9 @@ describe( 'MoveOperation', () => {
 		root.insertChildren( 0, [ p1, p2 ] );
 
 		let operation = new MoveOperation(
-			new Position( [ 0, 0 ], root ),
-			new Position( [ 1, 0 ], root ),
+			new Position( root, [ 0, 0 ] ),
 			1,
+			new Position( root, [ 1, 0 ] ),
 			doc.version
 		);
 
@@ -164,9 +164,9 @@ describe( 'MoveOperation', () => {
 		root.insertChildren( 0, 'xbarx' );
 
 		let operation = new MoveOperation(
-			new Position( [ 3 ], root ),
-			new Position( [ 1 ], root ),
+			new Position( root, [ 3 ] ),
 			3,
+			new Position( root, [ 1 ] ),
 			doc.version
 		);
 
@@ -179,9 +179,9 @@ describe( 'MoveOperation', () => {
 		root.insertChildren( 0, [ 'ab', p ] );
 
 		let operation = new MoveOperation(
-			new Position( [ 2, 0 ], root ),
-			new Position( [ 1 ], root ),
+			new Position( root, [ 2, 0 ] ),
 			3,
+			new Position( root, [ 1 ] ),
 			doc.version
 		);
 
@@ -194,9 +194,9 @@ describe( 'MoveOperation', () => {
 		root.insertChildren( 0, 'xbarx' );
 
 		let operation = new MoveOperation(
-			new Position( [ 1 ], root ),
-			new Position( [ 2 ], root ),
+			new Position( root, [ 1 ] ),
 			3,
+			new Position( root, [ 2 ] ),
 			doc.version
 		);
 
@@ -208,9 +208,9 @@ describe( 'MoveOperation', () => {
 		root.insertChildren( 0, [ 'ab', p, 'xy' ] );
 
 		let operation = new MoveOperation(
-			new Position( [ 1 ], root ),
-			new Position( [ 2, 0, 0 ], root ),
+			new Position( root, [ 1 ] ),
 			3,
+			new Position( root, [ 2, 0, 0 ] ),
 			doc.version
 		);
 
@@ -222,9 +222,9 @@ describe( 'MoveOperation', () => {
 		root.insertChildren( 0, [ 'ab', p, 'xy' ] );
 
 		let operation = new MoveOperation(
-			new Position( [ 1 ], root ),
-			new Position( [ 2, 0 ], root ),
+			new Position( root, [ 1 ] ),
 			1,
+			new Position( root, [ 2, 0 ] ),
 			doc.version
 		);
 
@@ -240,12 +240,12 @@ describe( 'MoveOperation', () => {
 	} );
 
 	it( 'should create operation with the same parameters when cloned', () => {
-		let sourcePosition = new Position( [ 0 ], root );
-		let targetPosition = new Position( [ 1 ], root );
+		let sourcePosition = new Position( root, [ 0 ] );
+		let targetPosition = new Position( root, [ 1 ] );
 		let howMany = 4;
 		let baseVersion = doc.version;
 
-		let op = new MoveOperation( sourcePosition, targetPosition, howMany, baseVersion );
+		let op = new MoveOperation( sourcePosition, howMany, targetPosition, baseVersion );
 
 		let clone = op.clone();
 

+ 4 - 4
packages/ckeditor5-engine/tests/document/operation/nooperation.js → packages/ckeditor5-engine/tests/treemodel/operation/nooperation.js

@@ -8,16 +8,16 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'document/document',
-	'document/operation/nooperation'
+	'treemodel/document',
+	'treemodel/operation/nooperation'
 );
 
 describe( 'NoOperation', () => {
 	let Document, NoOperation;
 
 	before( function() {
-		Document = modules[ 'document/document' ];
-		NoOperation = modules[ 'document/operation/nooperation' ];
+		Document = modules[ 'treemodel/document' ];
+		NoOperation = modules[ 'treemodel/operation/nooperation' ];
 	} );
 
 	let noop, doc, root;

+ 14 - 14
packages/ckeditor5-engine/tests/document/operation/reinsertoperation.js → packages/ckeditor5-engine/tests/treemodel/operation/reinsertoperation.js

@@ -8,22 +8,22 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'document/document',
-	'document/operation/reinsertoperation',
-	'document/operation/removeoperation',
-	'document/operation/moveoperation',
-	'document/position'
+	'treemodel/document',
+	'treemodel/operation/reinsertoperation',
+	'treemodel/operation/removeoperation',
+	'treemodel/operation/moveoperation',
+	'treemodel/position'
 );
 
 describe( 'ReinsertOperation', () => {
 	let Document, ReinsertOperation, RemoveOperation, MoveOperation, Position;
 
 	before( () => {
-		Document = modules[ 'document/document' ];
-		ReinsertOperation = modules[ 'document/operation/reinsertoperation' ];
-		RemoveOperation = modules[ 'document/operation/removeoperation' ];
-		MoveOperation = modules[ 'document/operation/moveoperation' ];
-		Position = modules[ 'document/position' ];
+		Document = modules[ 'treemodel/document' ];
+		ReinsertOperation = modules[ 'treemodel/operation/reinsertoperation' ];
+		RemoveOperation = modules[ 'treemodel/operation/removeoperation' ];
+		MoveOperation = modules[ 'treemodel/operation/moveoperation' ];
+		Position = modules[ 'treemodel/position' ];
 	} );
 
 	let doc, root, graveyard, operation, graveyardPosition, rootPosition;
@@ -31,15 +31,15 @@ describe( 'ReinsertOperation', () => {
 	beforeEach( () => {
 		doc = new Document();
 		root = doc.createRoot( 'root' );
-		graveyard = doc._graveyard;
+		graveyard = doc.graveyard;
 
-		graveyardPosition = new Position( [ 0 ], graveyard );
-		rootPosition = new Position( [ 0 ], root );
+		graveyardPosition = new Position( graveyard, [ 0 ] );
+		rootPosition = new Position( root, [ 0 ] );
 
 		operation = new ReinsertOperation(
 			graveyardPosition,
-			rootPosition,
 			2,
+			rootPosition,
 			doc.version
 		);
 	} );

+ 16 - 16
packages/ckeditor5-engine/tests/document/operation/removeoperation.js → packages/ckeditor5-engine/tests/treemodel/operation/removeoperation.js

@@ -8,22 +8,22 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'document/document',
-	'document/operation/reinsertoperation',
-	'document/operation/removeoperation',
-	'document/operation/moveoperation',
-	'document/position'
+	'treemodel/document',
+	'treemodel/operation/reinsertoperation',
+	'treemodel/operation/removeoperation',
+	'treemodel/operation/moveoperation',
+	'treemodel/position'
 );
 
 describe( 'RemoveOperation', () => {
 	let Document, ReinsertOperation, RemoveOperation, MoveOperation, Position;
 
 	before( () => {
-		Document = modules[ 'document/document' ];
-		ReinsertOperation = modules[ 'document/operation/reinsertoperation' ];
-		RemoveOperation = modules[ 'document/operation/removeoperation' ];
-		MoveOperation = modules[ 'document/operation/removeoperation' ];
-		Position = modules[ 'document/position' ];
+		Document = modules[ 'treemodel/document' ];
+		ReinsertOperation = modules[ 'treemodel/operation/reinsertoperation' ];
+		RemoveOperation = modules[ 'treemodel/operation/removeoperation' ];
+		MoveOperation = modules[ 'treemodel/operation/removeoperation' ];
+		Position = modules[ 'treemodel/position' ];
 	} );
 
 	let doc, root, graveyard;
@@ -31,12 +31,12 @@ describe( 'RemoveOperation', () => {
 	beforeEach( () => {
 		doc = new Document();
 		root = doc.createRoot( 'root' );
-		graveyard = doc._graveyard;
+		graveyard = doc.graveyard;
 	} );
 
 	it( 'should have proper type', () => {
 		const opp = new RemoveOperation(
-			new Position( [ 2 ], root ),
+			new Position( root, [ 2 ] ),
 			2,
 			doc.version
 		);
@@ -46,7 +46,7 @@ describe( 'RemoveOperation', () => {
 
 	it( 'should extend MoveOperation class', () => {
 		let operation = new RemoveOperation(
-			new Position( [ 2 ], root ),
+			new Position( root, [ 2 ] ),
 			2,
 			doc.version
 		);
@@ -63,7 +63,7 @@ describe( 'RemoveOperation', () => {
 
 		doc.applyOperation(
 			new RemoveOperation(
-				new Position( [ 2 ], root ),
+				new Position( root, [ 2 ] ),
 				2,
 				doc.version
 			)
@@ -79,7 +79,7 @@ describe( 'RemoveOperation', () => {
 	} );
 
 	it( 'should create a reinsert operation as a reverse', () => {
-		let position = new Position( [ 0 ], root );
+		let position = new Position( root, [ 0 ] );
 		let operation = new RemoveOperation( position, 2, 0 );
 		let reverse = operation.getReversed();
 
@@ -91,7 +91,7 @@ describe( 'RemoveOperation', () => {
 	} );
 
 	it( 'should undo remove set of nodes by applying reverse operation', () => {
-		let position = new Position( [ 0 ], root );
+		let position = new Position( root, [ 0 ] );
 		let operation = new RemoveOperation( position, 3, 0 );
 		let reverse = operation.getReversed();
 

文件差異過大導致無法顯示
+ 187 - 174
packages/ckeditor5-engine/tests/treemodel/operation/transform.js


+ 137 - 127
packages/ckeditor5-engine/tests/document/position.js → packages/ckeditor5-engine/tests/treemodel/position.js

@@ -8,12 +8,12 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'document/element',
-	'document/character',
-	'document/position',
-	'document/document',
+	'treemodel/element',
+	'treemodel/character',
+	'treemodel/position',
+	'treemodel/document',
 	'ckeditorerror',
-	'document/nodelist'
+	'treemodel/nodelist'
 );
 
 describe( 'position', () => {
@@ -33,11 +33,11 @@ describe( 'position', () => {
 	//        |- a   Before: [ 1, 1, 1 ] After: [ 1, 1, 2 ]
 	//        |- r   Before: [ 1, 1, 2 ] After: [ 1, 1, 3 ]
 	before( () => {
-		Element = modules[ 'document/element' ];
-		Character = modules[ 'document/character' ];
-		Document = modules[ 'document/document' ];
-		NodeList = modules[ 'document/nodelist' ];
-		Position = modules[ 'document/position' ];
+		Element = modules[ 'treemodel/element' ];
+		Character = modules[ 'treemodel/character' ];
+		Document = modules[ 'treemodel/document' ];
+		NodeList = modules[ 'treemodel/nodelist' ];
+		Position = modules[ 'treemodel/position' ];
 		CKEditorError = modules.ckeditorerror;
 
 		doc = new Document();
@@ -65,19 +65,29 @@ describe( 'position', () => {
 	} );
 
 	it( 'should create a position with path and document', () => {
-		let position = new Position( [ 0 ], root );
+		let position = new Position( root, [ 0 ] );
 
 		expect( position ).to.have.property( 'path' ).that.deep.equals( [ 0 ] );
 		expect( position ).to.have.property( 'root' ).that.equals( root );
 	} );
 
+	it( 'should throw error if given path is incorrect', () => {
+		expect( () => {
+			new Position( root, {} );
+		} ).to.throw( CKEditorError, /position-path-incorrect/ );
+
+		expect( () => {
+			new Position( root, [] );
+		} ).to.throw( CKEditorError, /position-path-incorrect/ );
+	} );
+
 	it( 'should throw error if given root is not a RootElement instance', () => {
 		expect( () => {
-			new Position( [ 0 ] );
+			new Position();
 		} ).to.throw( CKEditorError, /position-root-not-rootelement/ );
 
 		expect( () => {
-			new Position( [ 0 ], new Element( 'p' ) );
+			new Position( new Element( 'p' ), [ 0 ] );
 		} ).to.throw( CKEditorError, /position-root-not-rootelement/ );
 	} );
 
@@ -147,41 +157,41 @@ describe( 'position', () => {
 	} );
 
 	it( 'should have parent', () => {
-		expect( new Position( [ 0 ], root ) ).to.have.property( 'parent' ).that.equals( root );
-		expect( new Position( [ 1 ], root ) ).to.have.property( 'parent' ).that.equals( root );
-		expect( new Position( [ 2 ], root ) ).to.have.property( 'parent' ).that.equals( root );
+		expect( new Position( root, [ 0 ] ) ).to.have.property( 'parent' ).that.equals( root );
+		expect( new Position( root, [ 1 ] ) ).to.have.property( 'parent' ).that.equals( root );
+		expect( new Position( root, [ 2 ] ) ).to.have.property( 'parent' ).that.equals( root );
 
-		expect( new Position( [ 0, 0 ], root ) ).to.have.property( 'parent' ).that.equals( p );
+		expect( new Position( root, [ 0, 0 ] ) ).to.have.property( 'parent' ).that.equals( p );
 
-		expect( new Position( [ 1, 0 ], root ) ).to.have.property( 'parent' ).that.equals( ul );
-		expect( new Position( [ 1, 1 ], root ) ).to.have.property( 'parent' ).that.equals( ul );
-		expect( new Position( [ 1, 2 ], root ) ).to.have.property( 'parent' ).that.equals( ul );
+		expect( new Position( root, [ 1, 0 ] ) ).to.have.property( 'parent' ).that.equals( ul );
+		expect( new Position( root, [ 1, 1 ] ) ).to.have.property( 'parent' ).that.equals( ul );
+		expect( new Position( root, [ 1, 2 ] ) ).to.have.property( 'parent' ).that.equals( ul );
 
-		expect( new Position( [ 1, 0, 0 ], root ) ).to.have.property( 'parent' ).that.equals( li1 );
-		expect( new Position( [ 1, 0, 1 ], root ) ).to.have.property( 'parent' ).that.equals( li1 );
-		expect( new Position( [ 1, 0, 2 ], root ) ).to.have.property( 'parent' ).that.equals( li1 );
-		expect( new Position( [ 1, 0, 3 ], root ) ).to.have.property( 'parent' ).that.equals( li1 );
+		expect( new Position( root, [ 1, 0, 0 ] ) ).to.have.property( 'parent' ).that.equals( li1 );
+		expect( new Position( root, [ 1, 0, 1 ] ) ).to.have.property( 'parent' ).that.equals( li1 );
+		expect( new Position( root, [ 1, 0, 2 ] ) ).to.have.property( 'parent' ).that.equals( li1 );
+		expect( new Position( root, [ 1, 0, 3 ] ) ).to.have.property( 'parent' ).that.equals( li1 );
 	} );
 
 	it( 'should have offset', () => {
-		expect( new Position( [ 0 ], root ) ).to.have.property( 'offset' ).that.equals( 0 );
-		expect( new Position( [ 1 ], root ) ).to.have.property( 'offset' ).that.equals( 1 );
-		expect( new Position( [ 2 ], root ) ).to.have.property( 'offset' ).that.equals( 2 );
+		expect( new Position( root, [ 0 ] ) ).to.have.property( 'offset' ).that.equals( 0 );
+		expect( new Position( root, [ 1 ] ) ).to.have.property( 'offset' ).that.equals( 1 );
+		expect( new Position( root, [ 2 ] ) ).to.have.property( 'offset' ).that.equals( 2 );
 
-		expect( new Position( [ 0, 0 ], root ) ).to.have.property( 'offset' ).that.equals( 0 );
+		expect( new Position( root, [ 0, 0 ] ) ).to.have.property( 'offset' ).that.equals( 0 );
 
-		expect( new Position( [ 1, 0 ], root ) ).to.have.property( 'offset' ).that.equals( 0 );
-		expect( new Position( [ 1, 1 ], root ) ).to.have.property( 'offset' ).that.equals( 1 );
-		expect( new Position( [ 1, 2 ], root ) ).to.have.property( 'offset' ).that.equals( 2 );
+		expect( new Position( root, [ 1, 0 ] ) ).to.have.property( 'offset' ).that.equals( 0 );
+		expect( new Position( root, [ 1, 1 ] ) ).to.have.property( 'offset' ).that.equals( 1 );
+		expect( new Position( root, [ 1, 2 ] ) ).to.have.property( 'offset' ).that.equals( 2 );
 
-		expect( new Position( [ 1, 0, 0 ], root ) ).to.have.property( 'offset' ).that.equals( 0 );
-		expect( new Position( [ 1, 0, 1 ], root ) ).to.have.property( 'offset' ).that.equals( 1 );
-		expect( new Position( [ 1, 0, 2 ], root ) ).to.have.property( 'offset' ).that.equals( 2 );
-		expect( new Position( [ 1, 0, 3 ], root ) ).to.have.property( 'offset' ).that.equals( 3 );
+		expect( new Position( root, [ 1, 0, 0 ] ) ).to.have.property( 'offset' ).that.equals( 0 );
+		expect( new Position( root, [ 1, 0, 1 ] ) ).to.have.property( 'offset' ).that.equals( 1 );
+		expect( new Position( root, [ 1, 0, 2 ] ) ).to.have.property( 'offset' ).that.equals( 2 );
+		expect( new Position( root, [ 1, 0, 3 ] ) ).to.have.property( 'offset' ).that.equals( 3 );
 	} );
 
 	it( 'should be able to set offset', () => {
-		let position = new Position( [ 1, 0, 2 ], root );
+		let position = new Position( root, [ 1, 0, 2 ] );
 		position.offset = 4;
 
 		expect( position.offset ).to.equal( 4 );
@@ -189,47 +199,47 @@ describe( 'position', () => {
 	} );
 
 	it( 'should have nodeBefore', () => {
-		expect( new Position( [ 0 ], root ) ).to.have.property( 'nodeBefore' ).that.is.null;
-		expect( new Position( [ 1 ], root ) ).to.have.property( 'nodeBefore' ).that.equals( p );
-		expect( new Position( [ 2 ], root ) ).to.have.property( 'nodeBefore' ).that.equals( ul );
+		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( [ 0, 0 ], root ) ).to.have.property( 'nodeBefore' ).that.is.null;
+		expect( new Position( root, [ 0, 0 ] ) ).to.have.property( 'nodeBefore' ).that.is.null;
 
-		expect( new Position( [ 1, 0 ], root ) ).to.have.property( 'nodeBefore' ).that.is.null;
-		expect( new Position( [ 1, 1 ], root ) ).to.have.property( 'nodeBefore' ).that.equals( li1 );
-		expect( new Position( [ 1, 2 ], root ) ).to.have.property( 'nodeBefore' ).that.equals( li2 );
+		expect( new Position( root, [ 1, 0 ] ) ).to.have.property( 'nodeBefore' ).that.is.null;
+		expect( new Position( root, [ 1, 1 ] ) ).to.have.property( 'nodeBefore' ).that.equals( li1 );
+		expect( new Position( root, [ 1, 2 ] ) ).to.have.property( 'nodeBefore' ).that.equals( li2 );
 
-		expect( new Position( [ 1, 0, 0 ], root ) ).to.have.property( 'nodeBefore' ).that.is.null;
-		expect( new Position( [ 1, 0, 1 ], root ) ).to.have.property( 'nodeBefore' ).that.equals( f );
-		expect( new Position( [ 1, 0, 2 ], root ) ).to.have.property( 'nodeBefore' ).that.equals( o );
-		expect( new Position( [ 1, 0, 3 ], root ) ).to.have.property( 'nodeBefore' ).that.equals( z );
+		expect( new Position( root, [ 1, 0, 0 ] ) ).to.have.property( 'nodeBefore' ).that.is.null;
+		expect( new Position( root, [ 1, 0, 1 ] ) ).to.have.property( 'nodeBefore' ).that.equals( f );
+		expect( new Position( root, [ 1, 0, 2 ] ) ).to.have.property( 'nodeBefore' ).that.equals( o );
+		expect( new Position( root, [ 1, 0, 3 ] ) ).to.have.property( 'nodeBefore' ).that.equals( z );
 	} );
 
 	it( 'should have nodeAfter', () => {
-		expect( new Position( [ 0 ], root ) ).to.have.property( 'nodeAfter' ).that.equals( p );
-		expect( new Position( [ 1 ], root ) ).to.have.property( 'nodeAfter' ).that.equals( ul );
-		expect( new Position( [ 2 ], root ) ).to.have.property( 'nodeAfter' ).that.is.null;
+		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( [ 0, 0 ], root ) ).to.have.property( 'nodeAfter' ).that.is.null;
+		expect( new Position( root, [ 0, 0 ] ) ).to.have.property( 'nodeAfter' ).that.is.null;
 
-		expect( new Position( [ 1, 0 ], root ) ).to.have.property( 'nodeAfter' ).that.equals( li1 );
-		expect( new Position( [ 1, 1 ], root ) ).to.have.property( 'nodeAfter' ).that.equals( li2 );
-		expect( new Position( [ 1, 2 ], root ) ).to.have.property( 'nodeAfter' ).that.is.null;
+		expect( new Position( root, [ 1, 0 ] ) ).to.have.property( 'nodeAfter' ).that.equals( li1 );
+		expect( new Position( root, [ 1, 1 ] ) ).to.have.property( 'nodeAfter' ).that.equals( li2 );
+		expect( new Position( root, [ 1, 2 ] ) ).to.have.property( 'nodeAfter' ).that.is.null;
 
-		expect( new Position( [ 1, 0, 0 ], root ) ).to.have.property( 'nodeAfter' ).that.equals( f );
-		expect( new Position( [ 1, 0, 1 ], root ) ).to.have.property( 'nodeAfter' ).that.equals( o );
-		expect( new Position( [ 1, 0, 2 ], root ) ).to.have.property( 'nodeAfter' ).that.equals( z );
-		expect( new Position( [ 1, 0, 3 ], root ) ).to.have.property( 'nodeAfter' ).that.is.null;
+		expect( new Position( root, [ 1, 0, 0 ] ) ).to.have.property( 'nodeAfter' ).that.equals( f );
+		expect( new Position( root, [ 1, 0, 1 ] ) ).to.have.property( 'nodeAfter' ).that.equals( o );
+		expect( new Position( root, [ 1, 0, 2 ] ) ).to.have.property( 'nodeAfter' ).that.equals( z );
+		expect( new Position( root, [ 1, 0, 3 ] ) ).to.have.property( 'nodeAfter' ).that.is.null;
 	} );
 
 	it( 'should have proper parent path', () => {
-		let position = new Position( [ 1, 2, 3 ], root );
+		let position = new Position( root, [ 1, 2, 3 ] );
 
 		expect( position.getParentPath() ).to.deep.equal( [ 1, 2 ] );
 	} );
 
 	it( 'should return a new, equal position when cloned', () => {
-		const position = new Position( [ 1, 2, 3 ], root );
+		const position = new Position( root, [ 1, 2, 3 ] );
 		const clone = position.clone();
 
 		expect( clone ).not.to.be.equal( position ); // clone is not pointing to the same object as position
@@ -239,22 +249,22 @@ describe( 'position', () => {
 
 	describe( 'isBefore', () => {
 		it( 'should return true if given position has same root and is before this position', () => {
-			let position = new Position( [ 1, 1, 2 ], root );
-			let beforePosition = new Position( [ 1, 0 ], root );
+			let position = new Position( root, [ 1, 1, 2 ] );
+			let beforePosition = new Position( root, [ 1, 0 ] );
 
 			expect( position.isAfter( beforePosition ) ).to.be.true;
 		} );
 
 		it( 'should return false if given position has same root and is not before this position', () => {
-			let position = new Position( [ 1, 1, 2 ], root );
-			let afterPosition = new Position( [ 1, 2 ], root );
+			let position = new Position( root, [ 1, 1, 2 ] );
+			let afterPosition = new Position( root, [ 1, 2 ] );
 
 			expect( position.isAfter( afterPosition ) ).to.be.false;
 		} );
 
 		it( 'should return false if given position has different root', () => {
-			let position = new Position( [ 1, 1, 2 ], root );
-			let differentPosition = new Position( [ 1, 0 ], otherRoot );
+			let position = new Position( root, [ 1, 1, 2 ] );
+			let differentPosition = new Position( otherRoot, [ 1, 0 ] );
 
 			expect( position.isAfter( differentPosition ) ).to.be.false;
 		} );
@@ -262,22 +272,22 @@ describe( 'position', () => {
 
 	describe( 'isEqual', () => {
 		it( 'should return true if given position has same path and root', () => {
-			let position = new Position( [ 1, 1, 2 ], root );
-			let samePosition = new Position( [ 1, 1, 2 ], root );
+			let position = new Position( root, [ 1, 1, 2 ] );
+			let samePosition = new Position( root, [ 1, 1, 2 ] );
 
 			expect( position.isEqual( samePosition ) ).to.be.true;
 		} );
 
 		it( 'should return false if given position has different path', () => {
-			let position = new Position( [ 1, 1, 1 ], root );
-			let differentPosition = new Position( [ 1, 2, 2 ], root );
+			let position = new Position( root, [ 1, 1, 1 ] );
+			let differentPosition = new Position( root, [ 1, 2, 2 ] );
 
 			expect( position.isEqual( differentPosition ) ).to.be.false;
 		} );
 
 		it( 'should return false if given position has different root', () => {
-			let position = new Position( [ 1, 1, 1 ], root );
-			let differentPosition = new Position( [ 1, 1, 1 ], otherRoot );
+			let position = new Position( root, [ 1, 1, 1 ] );
+			let differentPosition = new Position( otherRoot, [ 1, 1, 1 ] );
 
 			expect( position.isEqual( differentPosition ) ).to.be.false;
 		} );
@@ -285,22 +295,22 @@ describe( 'position', () => {
 
 	describe( 'isAfter', () => {
 		it( 'should return true if given position has same root and is after this position', () => {
-			let position = new Position( [ 1, 1, 2 ], root );
-			let afterPosition = new Position( [ 1, 2 ], root );
+			let position = new Position( root, [ 1, 1, 2 ] );
+			let afterPosition = new Position( root, [ 1, 2 ] );
 
 			expect( position.isBefore( afterPosition ) ).to.be.true;
 		} );
 
 		it( 'should return false if given position has same root and is not after this position', () => {
-			let position = new Position( [ 1, 1, 2 ], root );
-			let beforePosition = new Position( [ 1, 0 ], root );
+			let position = new Position( root, [ 1, 1, 2 ] );
+			let beforePosition = new Position( root, [ 1, 0 ] );
 
 			expect( position.isBefore( beforePosition ) ).to.be.false;
 		} );
 
 		it( 'should return false if given position has different root', () => {
-			let position = new Position( [ 1, 1, 2 ], root );
-			let differentPosition = new Position( [ 1, 2 ], otherRoot );
+			let position = new Position( root, [ 1, 1, 2 ] );
+			let differentPosition = new Position( otherRoot, [ 1, 2 ] );
 
 			expect( position.isBefore( differentPosition ) ).to.be.false;
 		} );
@@ -308,29 +318,29 @@ describe( 'position', () => {
 
 	describe( 'compareWith', () => {
 		it( 'should return Position.SAME if positions are same', () => {
-			const position = new Position( [ 1, 2, 3 ], root );
-			const compared = new Position( [ 1, 2, 3 ], root );
+			const position = new Position( root, [ 1, 2, 3 ] );
+			const compared = new Position( root, [ 1, 2, 3 ] );
 
 			expect( position.compareWith( compared ) ).to.equal( Position.SAME );
 		} );
 
 		it( 'should return Position.BEFORE if the position is before compared one', () => {
-			const position = new Position( [ 1, 2, 3 ], root );
-			const compared = new Position( [ 1, 3 ], root );
+			const position = new Position( root, [ 1, 2, 3 ] );
+			const compared = new Position( root, [ 1, 3 ] );
 
 			expect( position.compareWith( compared ) ).to.equal( Position.BEFORE );
 		} );
 
 		it( 'should return Position.AFTER if the position is after compared one', () => {
-			const position = new Position( [ 1, 2, 3, 4 ], root );
-			const compared = new Position( [ 1, 2, 3 ], root );
+			const position = new Position( root, [ 1, 2, 3, 4 ] );
+			const compared = new Position( root, [ 1, 2, 3 ] );
 
 			expect( position.compareWith( compared ) ).to.equal( Position.AFTER );
 		} );
 
 		it( 'should return Position.DIFFERENT if positions are in different roots', () => {
-			const position = new Position( [ 1, 2, 3 ], root );
-			const compared = new Position( [ 1, 2, 3 ], otherRoot );
+			const position = new Position( root, [ 1, 2, 3 ] );
+			const compared = new Position( otherRoot, [ 1, 2, 3 ] );
 
 			expect( position.compareWith( compared ) ).to.equal( Position.DIFFERENT );
 		} );
@@ -338,58 +348,58 @@ describe( 'position', () => {
 
 	describe( 'getTransformedByInsertion', () => {
 		it( 'should return a new Position instance', () => {
-			const position = new Position( [ 0 ], root );
-			const transformed = position.getTransformedByInsertion( new Position( [ 2 ], root ), 4, false );
+			const position = new Position( root, [ 0 ] );
+			const transformed = position.getTransformedByInsertion( new Position( root, [ 2 ] ), 4, false );
 
 			expect( transformed ).not.to.equal( position );
 			expect( transformed ).to.be.instanceof( Position );
 		} );
 
 		it( 'should increment offset if insertion is in the same parent and closer offset', () => {
-			const position = new Position( [ 1, 2, 3 ], root );
-			const transformed = position.getTransformedByInsertion( new Position( [ 1, 2, 2 ], root ), 2, false );
+			const position = new Position( root, [ 1, 2, 3 ] );
+			const transformed = position.getTransformedByInsertion( new Position( root, [ 1, 2, 2 ] ), 2, false );
 
 			expect( transformed.offset ).to.equal( 5 );
 		} );
 
 		it( 'should not increment offset if insertion position is in different root', () => {
-			const position = new Position( [ 1, 2, 3 ], root );
-			const transformed = position.getTransformedByInsertion( new Position( [ 1, 2, 2 ], otherRoot ), 2, false );
+			const position = new Position( root, [ 1, 2, 3 ] );
+			const transformed = position.getTransformedByInsertion( new Position( otherRoot, [ 1, 2, 2 ] ), 2, false );
 
 			expect( transformed.offset ).to.equal( 3 );
 		} );
 
 		it( 'should not increment offset if insertion is in the same parent and the same offset', () => {
-			const position = new Position( [ 1, 2, 3 ], root );
-			const transformed = position.getTransformedByInsertion( new Position( [ 1, 2, 3 ], root ), 2, false );
+			const position = new Position( root, [ 1, 2, 3 ] );
+			const transformed = position.getTransformedByInsertion( new Position( root, [ 1, 2, 3 ] ), 2, false );
 
 			expect( transformed.offset ).to.equal( 3 );
 		} );
 
 		it( 'should increment offset if insertion is in the same parent and the same offset and it is inserted before', () => {
-			const position = new Position( [ 1, 2, 3 ], root );
-			const transformed = position.getTransformedByInsertion( new Position( [ 1, 2, 3 ], root ), 2, true );
+			const position = new Position( root, [ 1, 2, 3 ] );
+			const transformed = position.getTransformedByInsertion( new Position( root, [ 1, 2, 3 ] ), 2, true );
 
 			expect( transformed.offset ).to.equal( 5 );
 		} );
 
 		it( 'should not increment offset if insertion is in the same parent and further offset', () => {
-			const position = new Position( [ 1, 2, 3 ], root );
-			const transformed = position.getTransformedByInsertion( new Position( [ 1, 2, 4 ], root ), 2, false );
+			const position = new Position( root, [ 1, 2, 3 ] );
+			const transformed = position.getTransformedByInsertion( new Position( root, [ 1, 2, 4 ] ), 2, false );
 
 			expect( transformed.offset ).to.equal( 3 );
 		} );
 
 		it( 'should update path if insertion position parent is a node from that path and offset is before next node on that path', () => {
-			const position = new Position( [ 1, 2, 3 ], root );
-			const transformed = position.getTransformedByInsertion( new Position( [ 1, 2 ], root ), 2, false );
+			const position = new Position( root, [ 1, 2, 3 ] );
+			const transformed = position.getTransformedByInsertion( new Position( root, [ 1, 2 ] ), 2, false );
 
 			expect( transformed.path ).to.deep.equal( [ 1, 4, 3 ] );
 		} );
 
 		it( 'should not update path if insertion position parent is a node from that path and offset is after next node on that path', () => {
-			const position = new Position( [ 1, 2, 3 ], root );
-			const transformed = position.getTransformedByInsertion( new Position( [ 1, 3 ], root ), 2, false );
+			const position = new Position( root, [ 1, 2, 3 ] );
+			const transformed = position.getTransformedByInsertion( new Position( root, [ 1, 3 ] ), 2, false );
 
 			expect( transformed.path ).to.deep.equal( [ 1, 2, 3 ] );
 		} );
@@ -397,58 +407,58 @@ describe( 'position', () => {
 
 	describe( 'getTransformedByDeletion', () => {
 		it( 'should return a new Position instance', () => {
-			const position = new Position( [ 0 ], root );
-			const transformed = position.getTransformedByDeletion( new Position( [ 2 ], root ), 4 );
+			const position = new Position( root, [ 0 ] );
+			const transformed = position.getTransformedByDeletion( new Position( root, [ 2 ] ), 4 );
 
 			expect( transformed ).not.to.equal( position );
 			expect( transformed ).to.be.instanceof( Position );
 		} );
 
 		it( 'should return null if original position is inside one of removed nodes', () => {
-			const position = new Position( [ 1, 2 ], root );
-			const transformed = position.getTransformedByDeletion( new Position( [ 0 ], root ), 2 );
+			const position = new Position( root, [ 1, 2 ] );
+			const transformed = position.getTransformedByDeletion( new Position( root, [ 0 ] ), 2 );
 
 			expect( transformed ).to.be.null;
 		} );
 
 		it( 'should decrement offset if deletion is in the same parent and closer offset', () => {
-			const position = new Position( [ 1, 2, 7 ], root );
-			const transformed = position.getTransformedByDeletion( new Position( [ 1, 2, 2 ], root ), 2 );
+			const position = new Position( root, [ 1, 2, 7 ] );
+			const transformed = position.getTransformedByDeletion( new Position( root, [ 1, 2, 2 ] ), 2 );
 
 			expect( transformed.offset ).to.equal( 5 );
 		} );
 
 		it( 'should set position offset to deletion offset if position is between removed nodes', () => {
-			const position = new Position( [ 1, 2, 4 ], root );
-			const transformed = position.getTransformedByDeletion( new Position( [ 1, 2, 3 ], root ), 5 );
+			const position = new Position( root, [ 1, 2, 4 ] );
+			const transformed = position.getTransformedByDeletion( new Position( root, [ 1, 2, 3 ] ), 5 );
 
 			expect( transformed.offset ).to.equal( 3 );
 		} );
 
 		it( 'should not decrement offset if deletion position is in different root', () => {
-			const position = new Position( [ 1, 2, 3 ], root );
-			const transformed = position.getTransformedByDeletion( new Position( [ 1, 2, 1 ], otherRoot ), 2 );
+			const position = new Position( root, [ 1, 2, 3 ] );
+			const transformed = position.getTransformedByDeletion( new Position( otherRoot, [ 1, 2, 1 ] ), 2 );
 
 			expect( transformed.offset ).to.equal( 3 );
 		} );
 
 		it( 'should not decrement offset if deletion is in the same parent and further offset', () => {
-			const position = new Position( [ 1, 2, 3 ], root );
-			const transformed = position.getTransformedByDeletion( new Position( [ 1, 2, 4 ], root ), 2 );
+			const position = new Position( root, [ 1, 2, 3 ] );
+			const transformed = position.getTransformedByDeletion( new Position( root, [ 1, 2, 4 ] ), 2 );
 
 			expect( transformed.offset ).to.equal( 3 );
 		} );
 
 		it( 'should update path if deletion position parent is a node from that path and offset is before next node on that path', () => {
-			const position = new Position( [ 1, 2, 3 ], root );
-			const transformed = position.getTransformedByDeletion( new Position( [ 1, 0 ], root ), 2 );
+			const position = new Position( root, [ 1, 2, 3 ] );
+			const transformed = position.getTransformedByDeletion( new Position( root, [ 1, 0 ] ), 2 );
 
 			expect( transformed.path ).to.deep.equal( [ 1, 0, 3 ] );
 		} );
 
 		it( 'should not update path if deletion position parent is a node from that path and offset is after next node on that path', () => {
-			const position = new Position( [ 1, 2, 3 ], root );
-			const transformed = position.getTransformedByDeletion( new Position( [ 1, 3 ], root ), 2 );
+			const position = new Position( root, [ 1, 2, 3 ] );
+			const transformed = position.getTransformedByDeletion( new Position( root, [ 1, 3 ] ), 2 );
 
 			expect( transformed.path ).to.deep.equal( [ 1, 2, 3 ] );
 		} );
@@ -456,22 +466,22 @@ describe( 'position', () => {
 
 	describe( 'getTransformedByMove', () => {
 		it( 'should increment offset if a range was moved to the same parent and closer offset', () => {
-			const position = new Position( [ 1, 2, 3 ], root );
-			const transformed = position.getTransformedByMove( new Position( [ 2 ], root ), new Position( [ 1, 2, 0 ], root ), 3, false );
+			const position = new Position( root, [ 1, 2, 3 ] );
+			const transformed = position.getTransformedByMove( new Position( root, [ 2 ] ), new Position( root, [ 1, 2, 0 ] ), 3, false );
 
 			expect( transformed.path ).to.deep.equal( [ 1, 2, 6 ] );
 		} );
 
 		it( 'should decrement offset if a range was moved from the same parent and closer offset', () => {
-			const position = new Position( [ 1, 2, 3 ], root );
-			const transformed = position.getTransformedByMove( new Position( [ 1, 2, 0 ], root ), new Position( [ 2 ], root ), 3, false );
+			const position = new Position( root, [ 1, 2, 3 ] );
+			const transformed = position.getTransformedByMove( new Position( root, [ 1, 2, 0 ] ), new Position( root, [ 2 ] ), 3, false );
 
 			expect( transformed.path ).to.deep.equal( [ 1, 2, 0 ] );
 		} );
 
 		it( 'should update path if a range contained this position', () => {
-			const position = new Position( [ 1, 2, 3 ], root );
-			const transformed = position.getTransformedByMove( new Position( [ 1, 1 ], root ), new Position( [ 2, 1 ], root ), 3, false );
+			const position = new Position( root, [ 1, 2, 3 ] );
+			const transformed = position.getTransformedByMove( new Position( root, [ 1, 1 ] ), new Position( root, [ 2, 1 ] ), 3, false );
 
 			expect( transformed.path ).to.deep.equal( [ 2, 2, 3 ] );
 		} );
@@ -479,9 +489,9 @@ describe( 'position', () => {
 
 	describe( '_getCombined', () => {
 		it( 'should return correct combination of this and given positions', () => {
-			const position = new Position( [ 1, 3, 4, 2 ], root );
-			const sourcePosition = new Position( [ 1, 1 ], root );
-			const targetPosition = new Position( [ 2, 5 ], root );
+			const position = new Position( root, [ 1, 3, 4, 2 ] );
+			const sourcePosition = new Position( root, [ 1, 1 ] );
+			const targetPosition = new Position( root, [ 2, 5 ] );
 
 			const combined = position._getCombined( sourcePosition, targetPosition );
 

+ 20 - 20
packages/ckeditor5-engine/tests/document/positioniterator.js → packages/ckeditor5-engine/tests/treemodel/positioniterator.js

@@ -8,12 +8,12 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'document/document',
-	'document/element',
-	'document/character',
-	'document/positioniterator',
-	'document/position',
-	'document/range'
+	'treemodel/document',
+	'treemodel/element',
+	'treemodel/character',
+	'treemodel/positioniterator',
+	'treemodel/position',
+	'treemodel/range'
 );
 
 describe( 'range iterator', () => {
@@ -23,12 +23,12 @@ describe( 'range iterator', () => {
 	let doc, expectedItems, root, img1, paragraph, b, a, r, img2, x;
 
 	before( () => {
-		Document = modules[ 'document/document' ];
-		Element = modules[ 'document/element' ];
-		Character = modules[ 'document/character' ];
-		PositionIterator = modules[ 'document/positioniterator' ];
-		Position = modules[ 'document/position' ];
-		Range = modules[ 'document/range' ];
+		Document = modules[ 'treemodel/document' ];
+		Element = modules[ 'treemodel/element' ];
+		Character = modules[ 'treemodel/character' ];
+		PositionIterator = modules[ 'treemodel/positioniterator' ];
+		Position = modules[ 'treemodel/position' ];
+		Range = modules[ 'treemodel/range' ];
 
 		ELEMENT_ENTER = PositionIterator.ELEMENT_ENTER;
 		ELEMENT_LEAVE = PositionIterator.ELEMENT_LEAVE;
@@ -74,7 +74,7 @@ describe( 'range iterator', () => {
 	} );
 
 	it( 'should return next position', () => {
-		let iterator = new PositionIterator( new Position( [ 0 ], root ) ); // beginning of root
+		let iterator = new PositionIterator( new Position( root, [ 0 ] ) ); // beginning of root
 		let i, len;
 
 		for ( i = 0, len = expectedItems.length; i < len; i++ ) {
@@ -84,7 +84,7 @@ describe( 'range iterator', () => {
 	} );
 
 	it( 'should return previous position', () => {
-		let iterator = new PositionIterator( new Position( [ 2 ], root ) ); // ending of root
+		let iterator = new PositionIterator( new Position( root, [ 2 ] ) ); // ending of root
 
 		for ( let i = expectedItems.length - 1; i >= 0; i-- ) {
 			expect( iterator.previous() ).to.deep.equal( { done: false, value: expectedItems[ i ] } );
@@ -93,8 +93,8 @@ describe( 'range iterator', () => {
 	} );
 
 	it( 'should return next position in the boundaries', () => {
-		let start = new Position( [ 1, 0 ], root ); // p, 0
-		let end = new Position( [ 1, 3, 0 ], root ); // img, 0
+		let start = new Position( root, [ 1, 0 ] ); // p, 0
+		let end = new Position( root, [ 1, 3, 0 ] ); // img, 0
 
 		let iterator = new PositionIterator( new Range( start, end ) );
 
@@ -107,8 +107,8 @@ describe( 'range iterator', () => {
 	} );
 
 	it( 'should return previous position in the boundaries', () => {
-		let start = new Position( [ 1, 0 ], root ); // p, 0
-		let end = new Position( [ 1, 3, 0 ], root ); // img, 0
+		let start = new Position( root, [ 1, 0 ] ); // p, 0
+		let end = new Position( root, [ 1, 3, 0 ] ); // img, 0
 
 		let iterator = new PositionIterator( new Range( start, end ), end );
 
@@ -121,8 +121,8 @@ describe( 'range iterator', () => {
 	} );
 
 	it( 'should return iterate over the range', () => {
-		let start = new Position( [ 0 ], root ); // begging of root
-		let end = new Position( [ 2 ], root ); // ending of root
+		let start = new Position( root, [ 0 ] ); // begging of root
+		let end = new Position( root, [ 2 ] ); // ending of root
 		let range = new Range( start, end );
 
 		let i = 0;

+ 80 - 56
packages/ckeditor5-engine/tests/document/range.js → packages/ckeditor5-engine/tests/treemodel/range.js

@@ -8,34 +8,33 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'document/range',
-	'document/position',
-	'document/element',
-	'document/character',
-	'document/document'
+	'treemodel/range',
+	'treemodel/position',
+	'treemodel/element',
+	'treemodel/character',
+	'treemodel/document'
 );
 
 describe( 'Range', () => {
 	let Range, Position, Element, Character, Document;
-	let start, end, root;
 
 	before( () => {
-		Position = modules[ 'document/position' ];
-		Range = modules[ 'document/range' ];
-		Element = modules[ 'document/element' ];
-		Character = modules[ 'document/character' ];
-		Document = modules[ 'document/document' ];
+		Position = modules[ 'treemodel/position' ];
+		Range = modules[ 'treemodel/range' ];
+		Element = modules[ 'treemodel/element' ];
+		Character = modules[ 'treemodel/character' ];
+		Document = modules[ 'treemodel/document' ];
+	} );
+
+	let range, start, end, root;
 
+	beforeEach( () => {
 		let doc = new Document();
 		root = doc.createRoot( 'root' );
 
-		start = new Position( [ 0 ], root );
-		end = new Position( [ 1 ], root );
-	} );
-
-	let range;
+		start = new Position( root, [ 0 ] );
+		end = new Position( root, [ 1 ] );
 
-	beforeEach( () => {
 		range = new Range( start, end );
 	} );
 
@@ -48,8 +47,8 @@ describe( 'Range', () => {
 
 	describe( 'isEqual', () => {
 		it( 'should return true if the ranges are the same', () => {
-			let sameStart = new Position( [ 0 ], root );
-			let sameEnd = new Position( [ 1 ], root );
+			let sameStart = new Position( root, [ 0 ] );
+			let sameEnd = new Position( root, [ 1 ] );
 
 			let sameRange = new Range( sameStart, sameEnd );
 
@@ -59,8 +58,8 @@ describe( 'Range', () => {
 		it( 'should return false if the start position is different', () => {
 			let range = new Range( start, end );
 
-			let diffStart = new Position( [ 1 ], root );
-			let sameEnd = new Position( [ 1 ], root );
+			let diffStart = new Position( root, [ 1 ] );
+			let sameEnd = new Position( root, [ 1 ] );
 
 			let diffRange = new Range( diffStart, sameEnd );
 
@@ -68,8 +67,8 @@ describe( 'Range', () => {
 		} );
 
 		it( 'should return false if the end position is different', () => {
-			let sameStart = new Position( [ 0 ], root );
-			let diffEnd = new Position( [ 0 ], root );
+			let sameStart = new Position( root, [ 0 ] );
+			let diffEnd = new Position( root, [ 0 ] );
 
 			let diffRange = new Range( sameStart, diffEnd );
 
@@ -78,7 +77,7 @@ describe( 'Range', () => {
 	} );
 
 	describe( 'static constructors', () => {
-		let doc, root, p, f, o, z;
+		let p, f, o, z;
 
 		// root
 		//  |- p
@@ -86,10 +85,6 @@ describe( 'Range', () => {
 		//     |- o
 		//     |- z
 		before( () => {
-			doc = new Document();
-
-			root = doc.createRoot( 'root' );
-
 			f = new Character( 'f' );
 			o = new Character( 'o' );
 			z = new Character( 'z' );
@@ -117,10 +112,10 @@ describe( 'Range', () => {
 			} );
 		} );
 
-		describe( 'createFromPositionAndOffset', () => {
+		describe( 'createFromPositionAndShift', () => {
 			it( 'should make range from start position and offset', () => {
-				const position = new Position( [ 1, 2, 3 ], root );
-				const range = Range.createFromPositionAndOffset( position, 4 );
+				const position = new Position( root, [ 1, 2, 3 ] );
+				const range = Range.createFromPositionAndShift( position, 4 );
 
 				expect( range ).to.be.instanceof( Range );
 				expect( range.start.isEqual( position ) ).to.be.true;
@@ -130,6 +125,35 @@ describe( 'Range', () => {
 		} );
 	} );
 
+	describe( 'getNodes', () => {
+		it( 'should iterate over all nodes which "starts" in the range', () => {
+			let nodes = [];
+
+			const a = new Character( 'a' );
+			const b = new Character( 'b' );
+			const x = new Character( 'x' );
+			const y = new Character( 'y' );
+
+			const e1 = new Element( 'e1' );
+			const e2 = new Element( 'e2' );
+
+			e1.insertChildren( 0, [ a, b ] );
+			e2.insertChildren( 0, [ x, y ] );
+			root.insertChildren( 0, [ e1, e2 ] );
+
+			let range = new Range(
+				new Position( root, [ 0, 1 ] ),
+				new Position( root, [ 1, 1 ] )
+			);
+
+			for ( let node of range.getNodes() ) {
+				nodes.push( node );
+			}
+
+			expect( nodes ).to.deep.equal( [ b, e2, x ] );
+		} );
+	} );
+
 	describe( 'clone', () => {
 		it( 'should return a new, equal position', () => {
 			const clone = range.clone();
@@ -141,23 +165,23 @@ describe( 'Range', () => {
 
 	describe( 'containsPosition', () => {
 		beforeEach( () => {
-			range = new Range( new Position( [ 1 ], root ), new Position( [ 3 ], root ) );
+			range = new Range( new Position( root, [ 1 ] ), new Position( root, [ 3 ] ) );
 		} );
 
 		it( 'should return false if position is before range', () => {
-			const position = new Position( [ 0, 4 ], root );
+			const position = new Position( root, [ 0, 4 ] );
 
 			expect( range.containsPosition( position ) ).to.be.false;
 		} );
 
 		it( 'should return false if position is after range', () => {
-			const position = new Position( [ 3, 0 ], root );
+			const position = new Position( root, [ 3, 0 ] );
 
 			expect( range.containsPosition( position ) ).to.be.false;
 		} );
 
 		it( 'should return true if position is inside range', () => {
-			const position = new Position( [ 2, 2 ], root );
+			const position = new Position( root, [ 2, 2 ] );
 
 			expect( range.containsPosition( position ) ).to.be.true;
 		} );
@@ -165,32 +189,32 @@ describe( 'Range', () => {
 
 	describe( 'getTransformedByInsertion', () => {
 		it( 'should return an array of Range objects', () => {
-			const range = new Range( new Position( [ 0 ], root ), new Position( [ 1 ], root ) );
-			const transformed = range.getTransformedByInsertion( new Position( [ 2 ], root ), 2 );
+			const range = new Range( new Position( root, [ 0 ] ), new Position( root, [ 1 ] ) );
+			const transformed = range.getTransformedByInsertion( new Position( root, [ 2 ] ), 2 );
 
 			expect( transformed ).to.be.instanceof( Array );
 			expect( transformed[ 0 ] ).to.be.instanceof( Range );
 		} );
 
 		it( 'should update it\'s positions offsets if insertion is before range and they are affected', () => {
-			const range = new Range( new Position( [ 3, 2 ], root ), new Position( [ 3, 4 ], root ) );
-			const transformed = range.getTransformedByInsertion( new Position( [ 3, 1 ], root ), 2 );
+			const range = new Range( new Position( root, [ 3, 2 ] ), new Position( root, [ 3, 4 ] ) );
+			const transformed = range.getTransformedByInsertion( new Position( root, [ 3, 1 ] ), 2 );
 
 			expect( transformed[ 0 ].start.offset ).to.equal( 4 );
 			expect( transformed[ 0 ].end.offset ).to.equal( 6 );
 		} );
 
 		it( 'should update it\'s positions paths if insertion is before range and they are affected', () => {
-			const range = new Range( new Position( [ 3, 2 ], root ), new Position( [ 4, 4 ], root ) );
-			const transformed = range.getTransformedByInsertion( new Position( [ 0 ], root ), 2 );
+			const range = new Range( new Position( root, [ 3, 2 ] ), new Position( root, [ 4, 4 ] ) );
+			const transformed = range.getTransformedByInsertion( new Position( root, [ 0 ] ), 2 );
 
 			expect( transformed[ 0 ].start.path[ 0 ] ).to.equal( 5 );
 			expect( transformed[ 0 ].end.path[ 0 ] ).to.equal( 6 );
 		} );
 
 		it( 'should return array with two ranges and updated positions if insertion was in the middle of range', () => {
-			const range = new Range( new Position( [ 3, 2 ], root ), new Position( [ 5, 4 ], root ) );
-			const transformed = range.getTransformedByInsertion( new Position( [ 4, 1, 6 ], root ), 4 );
+			const range = new Range( new Position( root, [ 3, 2 ] ), new Position( root, [ 5, 4 ] ) );
+			const transformed = range.getTransformedByInsertion( new Position( root, [ 4, 1, 6 ] ), 4 );
 
 			expect( transformed.length ).to.equal( 2 );
 
@@ -202,8 +226,8 @@ describe( 'Range', () => {
 		} );
 
 		it( 'should not updated positions if insertion is after range', () => {
-			const range = new Range( new Position( [ 3, 2 ], root ), new Position( [ 4, 4 ], root ) );
-			const transformed = range.getTransformedByInsertion( new Position( [ 4, 4 ], root ), 3 );
+			const range = new Range( new Position( root, [ 3, 2 ] ), new Position( root, [ 4, 4 ] ) );
+			const transformed = range.getTransformedByInsertion( new Position( root, [ 4, 4 ] ), 3 );
 
 			expect( transformed[ 0 ].start.path ).to.deep.equal( [ 3, 2 ] );
 			expect( transformed[ 0 ].end.path ).to.deep.equal( [ 4, 4 ] );
@@ -214,11 +238,11 @@ describe( 'Range', () => {
 		let range;
 
 		beforeEach( () => {
-			range = new Range( new Position( [ 3, 2 ], root ), new Position( [ 5, 4 ], root ) );
+			range = new Range( new Position( root, [ 3, 2 ] ), new Position( root, [ 5, 4 ] ) );
 		} );
 
 		it( 'should return an array of Range objects', () => {
-			const otherRange = new Range( new Position( [ 6 ], root ), new Position( [ 7 ], root ) );
+			const otherRange = new Range( new Position( root, [ 6 ] ), new Position( root, [ 7 ] ) );
 			const diff = range.getDifference( otherRange );
 
 			expect( diff ).to.be.instanceof( Array );
@@ -226,7 +250,7 @@ describe( 'Range', () => {
 		} );
 
 		it( 'should return original range if other range does not intersect with it', () => {
-			const otherRange = new Range( new Position( [ 6 ], root ), new Position( [ 7 ], root ) );
+			const otherRange = new Range( new Position( root, [ 6 ] ), new Position( root, [ 7 ] ) );
 			const diff = range.getDifference( otherRange );
 
 			expect( diff[ 0 ].start.path ).to.deep.equal( [ 3, 2 ] );
@@ -234,7 +258,7 @@ describe( 'Range', () => {
 		} );
 
 		it( 'should return shrunken range if other range intersects with it', () => {
-			const otherRange = new Range( new Position( [ 4, 1 ], root ), new Position( [ 7 ], root ) );
+			const otherRange = new Range( new Position( root, [ 4, 1 ] ), new Position( root, [ 7 ] ) );
 			const diff = range.getDifference( otherRange );
 
 			expect( diff[ 0 ].start.path ).to.deep.equal( [ 3, 2 ] );
@@ -242,14 +266,14 @@ describe( 'Range', () => {
 		} );
 
 		it( 'should return an empty array if other range contains or is same as the original range', () => {
-			const otherRange = new Range( new Position( [ 2 ], root ), new Position( [ 6 ], root ) );
+			const otherRange = new Range( new Position( root, [ 2 ] ), new Position( root, [ 6 ] ) );
 			const diff = range.getDifference( otherRange );
 
 			expect( diff.length ).to.equal( 0 );
 		} );
 
 		it( 'should two ranges if other range is contained by the original range', () => {
-			const otherRange = new Range( new Position( [ 3, 7 ], root ), new Position( [ 4, 1 ], root ) );
+			const otherRange = new Range( new Position( root, [ 3, 7 ] ), new Position( root, [ 4, 1 ] ) );
 			const diff = range.getDifference( otherRange );
 
 			expect( diff.length ).to.equal( 2 );
@@ -266,32 +290,32 @@ describe( 'Range', () => {
 		let range;
 
 		beforeEach( () => {
-			range = new Range( new Position( [ 3, 2 ], root ), new Position( [ 5, 4 ], root ) );
+			range = new Range( new Position( root, [ 3, 2 ] ), new Position( root, [ 5, 4 ] ) );
 		} );
 
 		it( 'should return null if ranges do not intersect', () => {
-			const otherRange = new Range( new Position( [ 5, 4 ], root ), new Position( [ 7 ], root ) );
+			const otherRange = new Range( new Position( root, [ 5, 4 ] ), new Position( root, [ 7 ] ) );
 			const common = range.getIntersection( otherRange );
 
 			expect( common ).to.be.null;
 		} );
 
 		it( 'should return a range equal to the common part of ranges - original range contains the other range', () => {
-			const otherRange = new Range( new Position( [ 4 ], root ), new Position( [ 5 ], root ) );
+			const otherRange = new Range( new Position( root, [ 4 ] ), new Position( root, [ 5 ] ) );
 			const common = range.getIntersection( otherRange );
 
 			expect( common.isEqual( otherRange ) ).to.be.true;
 		} );
 
 		it( 'should return a range equal to the common part of ranges - original range is contained by the other range', () => {
-			const otherRange = new Range( new Position( [ 3 ], root ), new Position( [ 6 ], root ) );
+			const otherRange = new Range( new Position( root, [ 3 ] ), new Position( root, [ 6 ] ) );
 			const common = range.getIntersection( otherRange );
 
 			expect( common.isEqual( range ) ).to.be.true;
 		} );
 
 		it( 'should return a range equal to the common part of ranges - original range intersects with the other range', () => {
-			const otherRange = new Range( new Position( [ 3 ], root ), new Position( [ 4, 7 ], root ) );
+			const otherRange = new Range( new Position( root, [ 3 ] ), new Position( root, [ 4, 7 ] ) );
 			const common = range.getIntersection( otherRange );
 
 			expect( common.start.path ).to.deep.equal( [ 3, 2 ] );

+ 6 - 6
packages/ckeditor5-engine/tests/document/rootelement.js → packages/ckeditor5-engine/tests/treemodel/rootelement.js

@@ -14,18 +14,18 @@
 const getIteratorCount = bender.tools.core.getIteratorCount;
 
 const modules = bender.amd.require(
-	'document/document',
-	'document/element',
-	'document/rootelement'
+	'treemodel/document',
+	'treemodel/element',
+	'treemodel/rootelement'
 );
 
 describe( 'Element', () => {
 	let Document, Element, RootElement;
 
 	before( () => {
-		Document = modules[ 'document/document' ];
-		Element = modules[ 'document/element' ];
-		RootElement = modules[ 'document/rootelement' ];
+		Document = modules[ 'treemodel/document' ];
+		Element = modules[ 'treemodel/element' ];
+		RootElement = modules[ 'treemodel/rootelement' ];
 	} );
 
 	describe( 'constructor', () => {

+ 4 - 4
packages/ckeditor5-engine/tests/document/text.js → packages/ckeditor5-engine/tests/treemodel/text.js

@@ -10,15 +10,15 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'document/text',
-	'document/attribute'
+	'treemodel/text',
+	'treemodel/attribute'
 );
 
 describe( 'Text', () => {
 	describe( 'constructor', () => {
 		it( 'should create character without attributes', () => {
-			const Text = modules[ 'document/text' ];
-			const Attribute = modules[ 'document/attribute' ];
+			const Text = modules[ 'treemodel/text' ];
+			const Attribute = modules[ 'treemodel/attribute' ];
 
 			let attrs = [ new Attribute( 'bold', true ) ];
 			let text = new Text( 'bar', attrs );

部分文件因文件數量過多而無法顯示