Browse Source

Ported the TDM.

Piotrek Koszuliński 10 năm trước cách đây
mục cha
commit
95e4c2aaa1
70 tập tin đã thay đổi với 3950 bổ sung4089 xóa
  1. 91 93
      packages/ckeditor5-engine/src/treemodel/attribute.js
  2. 87 89
      packages/ckeditor5-engine/src/treemodel/attributelist.js
  3. 18 14
      packages/ckeditor5-engine/src/treemodel/batch.js
  4. 20 22
      packages/ckeditor5-engine/src/treemodel/character.js
  5. 133 137
      packages/ckeditor5-engine/src/treemodel/delta/attributedelta.js
  6. 99 101
      packages/ckeditor5-engine/src/treemodel/delta/batch-base.js
  7. 35 39
      packages/ckeditor5-engine/src/treemodel/delta/delta.js
  8. 28 32
      packages/ckeditor5-engine/src/treemodel/delta/insertdelta.js
  9. 57 61
      packages/ckeditor5-engine/src/treemodel/delta/mergedelta.js
  10. 47 51
      packages/ckeditor5-engine/src/treemodel/delta/movedelta.js
  11. 4 5
      packages/ckeditor5-engine/src/treemodel/delta/register.js
  12. 41 45
      packages/ckeditor5-engine/src/treemodel/delta/removedelta.js
  13. 50 54
      packages/ckeditor5-engine/src/treemodel/delta/splitdelta.js
  14. 50 55
      packages/ckeditor5-engine/src/treemodel/delta/unwrapdelta.js
  15. 47 51
      packages/ckeditor5-engine/src/treemodel/delta/weakinsertdelta.js
  16. 60 64
      packages/ckeditor5-engine/src/treemodel/delta/wrapdelta.js
  17. 188 193
      packages/ckeditor5-engine/src/treemodel/document.js
  18. 89 93
      packages/ckeditor5-engine/src/treemodel/element.js
  19. 151 155
      packages/ckeditor5-engine/src/treemodel/liveposition.js
  20. 114 118
      packages/ckeditor5-engine/src/treemodel/liverange.js
  21. 159 163
      packages/ckeditor5-engine/src/treemodel/node.js
  22. 121 125
      packages/ckeditor5-engine/src/treemodel/nodelist.js
  23. 108 112
      packages/ckeditor5-engine/src/treemodel/operation/attributeoperation.js
  24. 48 55
      packages/ckeditor5-engine/src/treemodel/operation/insertoperation.js
  25. 105 109
      packages/ckeditor5-engine/src/treemodel/operation/moveoperation.js
  26. 22 26
      packages/ckeditor5-engine/src/treemodel/operation/nooperation.js
  27. 57 61
      packages/ckeditor5-engine/src/treemodel/operation/operation.js
  28. 20 27
      packages/ckeditor5-engine/src/treemodel/operation/reinsertoperation.js
  29. 29 36
      packages/ckeditor5-engine/src/treemodel/operation/removeoperation.js
  30. 338 338
      packages/ckeditor5-engine/src/treemodel/operation/transform.js
  31. 466 465
      packages/ckeditor5-engine/src/treemodel/position.js
  32. 131 138
      packages/ckeditor5-engine/src/treemodel/positioniterator.js
  33. 358 358
      packages/ckeditor5-engine/src/treemodel/range.js
  34. 19 21
      packages/ckeditor5-engine/src/treemodel/rootelement.js
  35. 190 194
      packages/ckeditor5-engine/src/treemodel/selection.js
  36. 23 27
      packages/ckeditor5-engine/src/treemodel/text.js
  37. 2 2
      packages/ckeditor5-engine/tests/treemodel/attribute.js
  38. 5 8
      packages/ckeditor5-engine/tests/treemodel/attributelist.js
  39. 6 6
      packages/ckeditor5-engine/tests/treemodel/batch.js
  40. 8 11
      packages/ckeditor5-engine/tests/treemodel/character.js
  41. 16 16
      packages/ckeditor5-engine/tests/treemodel/delta/attributedelta.js
  42. 3 2
      packages/ckeditor5-engine/tests/treemodel/delta/delta.js
  43. 8 8
      packages/ckeditor5-engine/tests/treemodel/delta/insertdelta.js
  44. 10 10
      packages/ckeditor5-engine/tests/treemodel/delta/mergedelta.js
  45. 10 10
      packages/ckeditor5-engine/tests/treemodel/delta/movedelta.js
  46. 8 8
      packages/ckeditor5-engine/tests/treemodel/delta/removedelta.js
  47. 10 10
      packages/ckeditor5-engine/tests/treemodel/delta/splitdelta.js
  48. 8 9
      packages/ckeditor5-engine/tests/treemodel/delta/unwrapdelta.js
  49. 6 6
      packages/ckeditor5-engine/tests/treemodel/delta/weakinsertdelta.js
  50. 10 10
      packages/ckeditor5-engine/tests/treemodel/delta/wrapdelta.js
  51. 23 23
      packages/ckeditor5-engine/tests/treemodel/document/change-event.js
  52. 8 8
      packages/ckeditor5-engine/tests/treemodel/document/document.js
  53. 8 11
      packages/ckeditor5-engine/tests/treemodel/element.js
  54. 12 12
      packages/ckeditor5-engine/tests/treemodel/liveposition.js
  55. 12 12
      packages/ckeditor5-engine/tests/treemodel/liverange.js
  56. 12 13
      packages/ckeditor5-engine/tests/treemodel/node.js
  57. 8 8
      packages/ckeditor5-engine/tests/treemodel/nodelist.js
  58. 18 18
      packages/ckeditor5-engine/tests/treemodel/operation/attributeoperation.js
  59. 15 15
      packages/ckeditor5-engine/tests/treemodel/operation/insertoperation.js
  60. 12 12
      packages/ckeditor5-engine/tests/treemodel/operation/moveoperation.js
  61. 4 4
      packages/ckeditor5-engine/tests/treemodel/operation/nooperation.js
  62. 10 10
      packages/ckeditor5-engine/tests/treemodel/operation/reinsertoperation.js
  63. 10 10
      packages/ckeditor5-engine/tests/treemodel/operation/removeoperation.js
  64. 21 21
      packages/ckeditor5-engine/tests/treemodel/operation/transform.js
  65. 12 12
      packages/ckeditor5-engine/tests/treemodel/position.js
  66. 12 12
      packages/ckeditor5-engine/tests/treemodel/positioniterator.js
  67. 10 10
      packages/ckeditor5-engine/tests/treemodel/range.js
  68. 6 9
      packages/ckeditor5-engine/tests/treemodel/rootelement.js
  69. 20 20
      packages/ckeditor5-engine/tests/treemodel/selection.js
  70. 4 6
      packages/ckeditor5-engine/tests/treemodel/text.js

+ 91 - 93
packages/ckeditor5-engine/src/treemodel/attribute.js

@@ -5,121 +5,119 @@
 
 'use strict';
 
-CKEDITOR.define( [ 'utils' ], ( utils ) => {
+import langUtils from '../lib/lodash/lang.js';
+
+/**
+ * Attributes can store any additional information for nodes in the data model.
+ *
+ * @class treeModel.Attribute
+ */
+export default class Attribute {
 	/**
-	 * Attributes can store any additional information for nodes in the data model.
+	 * Creates a new instance of the `Attribute` class. Once attribute is created it is immutable.
 	 *
-	 * @class treeModel.Attribute
+	 * @param {String} key Attribute key.
+	 * @param {Mixed} value Attribute value.
+	 * @constructor
 	 */
-	class Attribute {
+	constructor( key, value ) {
 		/**
-		 * Creates a new instance of the `Attribute` class. Once attribute is created it is immutable.
+		 * Attribute key.
 		 *
-		 * @param {String} key Attribute key.
-		 * @param {Mixed} value Attribute value.
-		 * @constructor
+		 * @readonly
+		 * @property {String} key
 		 */
-		constructor( key, value ) {
-			/**
-			 * Attribute key.
-			 *
-			 * @readonly
-			 * @property {String} key
-			 */
-			this.key = key;
-
-			/**
-			 * Attribute value. Note that value may be any type, including objects.
-			 *
-			 * @readonly
-			 * @property {Mixed} value
-			 */
-			this.value = value;
-
-			/**
-			 * Attribute hash. Used to compare attributes. Two attributes with the same key and value will have the same hash.
-			 *
-			 * @readonly
-			 * @private
-			 * @property {String} _hash
-			 */
-			this._hash = this.key + ': ' + JSON.stringify( this.value, sort );
-
-			// If attribute is already registered the registered one should be returned.
-			if ( Attribute._register[ this._hash ] ) {
-				return Attribute._register[ this._hash ];
-			}
-
-			// We do not care about the order, so collections with the same elements should return the same hash.
-			function sort( key, value ) {
-				if ( !utils.isArray( value ) && utils.isObject( value ) ) {
-					const sorted = {};
-
-					// Sort keys and fill up the sorted object.
-					Object.keys( value ).sort().forEach( ( key ) => {
-						sorted[ key ] = value[ key ];
-					} );
-
-					return sorted;
-				} else {
-					return value;
-				}
-			}
-		}
+		this.key = key;
 
 		/**
-		 * Compares two attributes. Returns `true` if two attributes have the same key and value even if the order of keys
-		 * in the value object is different.
+		 * Attribute value. Note that value may be any type, including objects.
 		 *
-		 *		let attr1 = new Attribute( 'foo', { a: 1, b: 2 } );
-		 *		let attr2 = new Attribute( 'foo', { b: 2, a: 1 } );
-		 *		attr1.isEqual( attr2 ); // true
-		 *
-		 * @param {treeModel.Attribute} otherAttr Attribute to compare with.
-		 * @returns {Boolean} True if attributes are equal to each other.
+		 * @readonly
+		 * @property {Mixed} value
 		 */
-		isEqual( otherAttr ) {
-			return this._hash === otherAttr._hash;
-		}
+		this.value = value;
 
 		/**
-		 * To save memory, commonly used attributes may be registered. If an attribute is registered the constructor will
-		 * always return the same instance of this attribute.
+		 * Attribute hash. Used to compare attributes. Two attributes with the same key and value will have the same hash.
 		 *
-		 * Note that attributes are registered globally.
-		 *
-		 *		let attr1 = Attribute.register( 'bold', true );
-		 *		let attr2 = Attribute.register( 'bold', true );
-		 *		let attr3 = new Attribute( 'bold', true );
-		 *		attr1 === attr2 // true
-		 *		attr1 === attr3 // true
-		 *
-		 * @static
-		 * @param {String} key Attribute key.
-		 * @param {Mixed} value Attribute value.
-		 * @returns {treeModel.Attribute} Registered attribute.
+		 * @readonly
+		 * @private
+		 * @property {String} _hash
 		 */
-		static register( key, value ) {
-			const attr = new Attribute( key, value );
+		this._hash = this.key + ': ' + JSON.stringify( this.value, sort );
 
-			if ( this._register[ attr._hash ] ) {
-				return this._register[ attr._hash ];
-			} else {
-				this._register[ attr._hash ] = attr;
+		// If attribute is already registered the registered one should be returned.
+		if ( Attribute._register[ this._hash ] ) {
+			return Attribute._register[ this._hash ];
+		}
 
-				return attr;
+		// We do not care about the order, so collections with the same elements should return the same hash.
+		function sort( key, value ) {
+			if ( !langUtils.isArray( value ) && langUtils.isObject( value ) ) {
+				const sorted = {};
+
+				// Sort keys and fill up the sorted object.
+				Object.keys( value ).sort().forEach( ( key ) => {
+					sorted[ key ] = value[ key ];
+				} );
+
+				return sorted;
+			} else {
+				return value;
 			}
 		}
 	}
 
 	/**
-	 * Register of attributes in which all registered attributes are stored.
+	 * Compares two attributes. Returns `true` if two attributes have the same key and value even if the order of keys
+	 * in the value object is different.
+	 *
+	 *		let attr1 = new Attribute( 'foo', { a: 1, b: 2 } );
+	 *		let attr2 = new Attribute( 'foo', { b: 2, a: 1 } );
+	 *		attr1.isEqual( attr2 ); // true
+	 *
+	 * @param {treeModel.Attribute} otherAttr Attribute to compare with.
+	 * @returns {Boolean} True if attributes are equal to each other.
+	 */
+	isEqual( otherAttr ) {
+		return this._hash === otherAttr._hash;
+	}
+
+	/**
+	 * To save memory, commonly used attributes may be registered. If an attribute is registered the constructor will
+	 * always return the same instance of this attribute.
+	 *
+	 * Note that attributes are registered globally.
+	 *
+	 *		let attr1 = Attribute.register( 'bold', true );
+	 *		let attr2 = Attribute.register( 'bold', true );
+	 *		let attr3 = new Attribute( 'bold', true );
+	 *		attr1 === attr2 // true
+	 *		attr1 === attr3 // true
 	 *
 	 * @static
-	 * @private
-	 * @property {String} _hash
+	 * @param {String} key Attribute key.
+	 * @param {Mixed} value Attribute value.
+	 * @returns {treeModel.Attribute} Registered attribute.
 	 */
-	Attribute._register = {};
+	static register( key, value ) {
+		const attr = new Attribute( key, value );
+
+		if ( this._register[ attr._hash ] ) {
+			return this._register[ attr._hash ];
+		} else {
+			this._register[ attr._hash ] = attr;
+
+			return attr;
+		}
+	}
+}
 
-	return Attribute;
-} );
+/**
+ * Register of attributes in which all registered attributes are stored.
+ *
+ * @static
+ * @private
+ * @property {String} _hash
+ */
+Attribute._register = {};

+ 87 - 89
packages/ckeditor5-engine/src/treemodel/attributelist.js

@@ -5,116 +5,114 @@
 
 'use strict';
 
-CKEDITOR.define( [ 'treemodel/attribute' ], ( Attribute ) => {
+import Attribute from './attribute.js';
+
+/**
+ * List of attributes. Used to manage a set of attributes added to and removed from an object containing
+ * AttributeList.
+ *
+ * @class treeModel.AttributeList
+ */
+export default class AttributeList {
 	/**
-	 * List of attributes. Used to manage a set of attributes added to and removed from an object containing
-	 * AttributeList.
+	 * Creates a list of attributes.
 	 *
-	 * @class treeModel.AttributeList
+	 * @param {Iterable.<treeModel.Attribute>} [attrs] Attributes to initialize this list with.
+	 * @constructor
 	 */
-	class AttributeList {
+	constructor( attrs ) {
 		/**
-		 * Creates a list of attributes.
+		 * Internal set containing the attributes stored by this list.
 		 *
-		 * @param {Iterable.<treeModel.Attribute>} [attrs] Attributes to initialize this list with.
-		 * @constructor
+		 * @private
+		 * @property {Set.<treeModel.Attribute>} _attrs
 		 */
-		constructor( attrs ) {
-			/**
-			 * Internal set containing the attributes stored by this list.
-			 *
-			 * @private
-			 * @property {Set.<treeModel.Attribute>} _attrs
-			 */
 
-			this.setAttrsTo( attrs );
-		}
+		this.setAttrsTo( attrs );
+	}
 
-		/**
-		 * Returns value of an attribute with given key or null if there are no attributes with given key.
-		 *
-		 * @param {String} key The attribute key.
-		 * @returns {*|null} Value of found attribute or null if attribute with given key has not been found.
-		 */
-		getAttr( key ) {
-			for ( let attr of this._attrs ) {
-				if ( attr.key == key ) {
-					return attr.value;
-				}
+	/**
+	 * Returns value of an attribute with given key or null if there are no attributes with given key.
+	 *
+	 * @param {String} key The attribute key.
+	 * @returns {*|null} Value of found attribute or null if attribute with given key has not been found.
+	 */
+	getAttr( key ) {
+		for ( let attr of this._attrs ) {
+			if ( attr.key == key ) {
+				return attr.value;
 			}
-
-			return null;
 		}
 
-		/**
-		 * Returns attribute iterator.
-		 *
-		 * @returns {Iterable.<treeModel.Attribute>} Attribute iterator.
-		 */
-		getAttrs() {
-			return this._attrs[ Symbol.iterator ]();
-		}
+		return null;
+	}
 
-		/**
-		 * Returns `true` if the object contains given {@link treeModel.Attribute attribute} or
-		 * an attribute with the same key if passed parameter was a string.
-		 *
-		 * @param {treeModel.Attribute|String} attrOrKey An attribute or a key to look for.
-		 * @returns {Boolean} True if object contains given attribute or an attribute with the given key.
-		 */
-		hasAttr( attrOrKey ) {
-			if ( attrOrKey instanceof Attribute ) {
-				for ( let attr of this._attrs ) {
-					if ( attr.isEqual( attrOrKey ) ) {
-						return true;
-					}
-				}
-			} else {
-				for ( let attr of this._attrs ) {
-					if ( attr.key == attrOrKey ) {
-						return true;
-					}
+	/**
+	 * Returns attribute iterator.
+	 *
+	 * @returns {Iterable.<treeModel.Attribute>} Attribute iterator.
+	 */
+	getAttrs() {
+		return this._attrs[ Symbol.iterator ]();
+	}
+
+	/**
+	 * Returns `true` if the object contains given {@link treeModel.Attribute attribute} or
+	 * an attribute with the same key if passed parameter was a string.
+	 *
+	 * @param {treeModel.Attribute|String} attrOrKey An attribute or a key to look for.
+	 * @returns {Boolean} True if object contains given attribute or an attribute with the given key.
+	 */
+	hasAttr( attrOrKey ) {
+		if ( attrOrKey instanceof Attribute ) {
+			for ( let attr of this._attrs ) {
+				if ( attr.isEqual( attrOrKey ) ) {
+					return true;
 				}
 			}
-
-			return false;
-		}
-
-		/**
-		 * Removes attribute from the list of attributes.
-		 *
-		 * @param {String} key The attribute key.
-		 */
-		removeAttr( key ) {
+		} else {
 			for ( let attr of this._attrs ) {
-				if ( attr.key == key ) {
-					this._attrs.delete( attr );
-
-					return;
+				if ( attr.key == attrOrKey ) {
+					return true;
 				}
 			}
 		}
 
-		/**
-		 * Sets a given attribute. If the attribute with the same key already exists it will be removed.
-		 *
-		 * @param {treeModel.Attribute} attr Attribute to set.
-		 */
-		setAttr( attr ) {
-			this.removeAttr( attr.key );
+		return false;
+	}
 
-			this._attrs.add( attr );
-		}
+	/**
+	 * Removes attribute from the list of attributes.
+	 *
+	 * @param {String} key The attribute key.
+	 */
+	removeAttr( key ) {
+		for ( let attr of this._attrs ) {
+			if ( attr.key == key ) {
+				this._attrs.delete( attr );
 
-		/**
-		 * Removes all attributes and sets passed attributes.
-		 *
-		 * @param {Iterable.<treeModel.Attribute>} attrs Array of attributes to set.
-		 */
-		setAttrsTo( attrs ) {
-			this._attrs = new Set( attrs );
+				return;
+			}
 		}
 	}
 
-	return AttributeList;
-} );
+	/**
+	 * Sets a given attribute. If the attribute with the same key already exists it will be removed.
+	 *
+	 * @param {treeModel.Attribute} attr Attribute to set.
+	 */
+	setAttr( attr ) {
+		this.removeAttr( attr.key );
+
+		this._attrs.add( attr );
+	}
+
+	/**
+	 * Removes all attributes and sets passed attributes.
+	 *
+	 * @param {Iterable.<treeModel.Attribute>} attrs Array of attributes to set.
+	 */
+	setAttrsTo( attrs ) {
+		this._attrs = new Set( attrs );
+	}
+}

+ 18 - 14
packages/ckeditor5-engine/src/treemodel/batch.js

@@ -9,17 +9,21 @@
 //
 // 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/weakinsertdelta',
-	'treemodel/delta/movedelta',
-	'treemodel/delta/removedelta',
-	'treemodel/delta/attributedelta',
-	'treemodel/delta/splitdelta',
-	'treemodel/delta/mergedelta',
-	'treemodel/delta/wrapdelta',
-	'treemodel/delta/unwrapdelta'
-], ( Batch ) => {
-	return Batch;
-} );
+
+import Batch from './delta/batch-base';
+
+/* jshint ignore:start */
+
+import d1 from './delta/insertdelta';
+import d2 from './delta/weakinsertdelta';
+import d3 from './delta/movedelta';
+import d4 from './delta/removedelta';
+import d5 from './delta/attributedelta';
+import d6 from './delta/splitdelta';
+import d7 from './delta/mergedelta';
+import d8 from './delta/wrapdelta';
+import d9 from './delta/unwrapdelta';
+
+/* jshint ignore:end */
+
+export default Batch;

+ 20 - 22
packages/ckeditor5-engine/src/treemodel/character.js

@@ -5,32 +5,30 @@
 
 'use strict';
 
-CKEDITOR.define( [ 'treemodel/node' ], ( Node ) => {
+import Node from './node.js';
+
+/**
+ * Data structure for character stored in the tree data model.
+ *
+ * @class treeModel.Character
+ */
+export default class Character extends Node {
 	/**
-	 * Data structure for character stored in the tree data model.
+	 * Creates character linear item.
 	 *
-	 * @class treeModel.Character
+	 * @param {String} character Described character.
+	 * @param {Iterable} attrs Iterable collection of {@link treeModel.Attribute attributes}.
+	 * @constructor
 	 */
-	class Character extends Node {
+	constructor( character, attrs ) {
+		super( attrs );
+
 		/**
-		 * Creates character linear item.
+		 * Described character.
 		 *
-		 * @param {String} character Described character.
-		 * @param {Iterable} attrs Iterable collection of {@link treeModel.Attribute attributes}.
-		 * @constructor
+		 * @readonly
+		 * @property {String} character
 		 */
-		constructor( character, attrs ) {
-			super( attrs );
-
-			/**
-			 * Described character.
-			 *
-			 * @readonly
-			 * @property {String} character
-			 */
-			this.character = character;
-		}
+		this.character = character;
 	}
-
-	return Character;
-} );
+}

+ 133 - 137
packages/ckeditor5-engine/src/treemodel/delta/attributedelta.js

@@ -5,156 +5,152 @@
 
 'use strict';
 
-CKEDITOR.define( [
-	'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 treeModel.Batch#setAttr}
-	 * and {@link treeModel.Batch#removeAttr}) use `AttributeDelta` class which inherits from the `Delta` class and may
-	 * overwrite some methods.
-	 *
-	 * @class treeModel.delta.AttributeDelta
-	 */
-	class AttributeDelta extends Delta {}
-
-	/**
-	 * Sets the value of the attribute of the node or on the range.
-	 *
-	 * @chainable
-	 * @method setAttr
-	 * @memberOf treeModel.Batch
-	 * @param {String} key Attribute key.
-	 * @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 ) {
-		attribute( this, key, value, nodeOrRange );
-
-		return this;
-	} );
-
-	/**
-	 * Removes an attribute from the range.
-	 *
-	 * @chainable
-	 * @method removeAttr
-	 * @memberOf treeModel.Batch
-	 * @param {String} key Attribute key.
-	 * @param {treeModel.Node|treeModel.Range} nodeOrRange Node or range on which the attribute will be removed.
-	 */
-	register( 'removeAttr', function( key, nodeOrRange ) {
-		attribute( this, key, null, nodeOrRange );
-
-		return this;
-	} );
-
-	function attribute( batch, key, value, nodeOrRange ) {
-		const delta = new AttributeDelta();
-
-		if ( nodeOrRange instanceof Range ) {
-			changeRange( batch.doc, delta, key, value, nodeOrRange );
-		} else {
-			changeNode( batch.doc, delta, key, value, nodeOrRange );
-		}
+import Delta from './delta.js';
+import register from './register.js';
+import AttributeOperation from '../operation/attributeoperation.js';
+import Position from '../position.js';
+import Range from '../range.js';
+import PositionIterator from '../positioniterator.js';
+import Attribute from '../attribute.js';
+import Element from '../element.js';
+
+/**
+ * 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 treeModel.delta.AttributeDelta
+ */
+export default class AttributeDelta extends Delta {}
+
+/**
+ * Sets the value of the attribute of the node or on the range.
+ *
+ * @chainable
+ * @method setAttr
+ * @memberOf treeModel.Batch
+ * @param {String} key Attribute key.
+ * @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 ) {
+	attribute( this, key, value, nodeOrRange );
+
+	return this;
+} );
+
+/**
+ * Removes an attribute from the range.
+ *
+ * @chainable
+ * @method removeAttr
+ * @memberOf treeModel.Batch
+ * @param {String} key Attribute key.
+ * @param {treeModel.Node|treeModel.Range} nodeOrRange Node or range on which the attribute will be removed.
+ */
+register( 'removeAttr', function( key, nodeOrRange ) {
+	attribute( this, key, null, nodeOrRange );
+
+	return this;
+} );
+
+function attribute( batch, key, value, nodeOrRange ) {
+	const delta = new AttributeDelta();
 
-		batch.addDelta( delta );
+	if ( nodeOrRange instanceof Range ) {
+		changeRange( batch.doc, delta, key, value, nodeOrRange );
+	} else {
+		changeNode( batch.doc, delta, key, value, nodeOrRange );
 	}
 
-	function changeNode( doc, delta, key, value, node ) {
-		const previousValue = node.getAttr( key );
-		let range;
-
-		if ( previousValue != value ) {
-			if ( node instanceof Element ) {
-				// If we change the attribute of the element, we do not want to change attributes of its children, so
-				// the end on the range can not be put after the closing tag, it should be inside that element with the
-				// offset 0, so the range will contains only the opening tag...
-				range = new Range( Position.createBefore( node ), Position.createFromParentAndOffset( node, 0 ) );
-			} else {
-				// ...but for characters we can not put the range inside it, so we end the range after that character.
-				range = new Range( Position.createBefore( node ), Position.createAfter( node ) );
-			}
+	batch.addDelta( delta );
+}
 
-			const operation = new AttributeOperation(
-					range,
-					previousValue ? new Attribute( key, previousValue ) : null,
-					value ? new Attribute( key, value ) : null,
-					doc.version
-				);
+function changeNode( doc, delta, key, value, node ) {
+	const previousValue = node.getAttr( key );
+	let range;
 
-			doc.applyOperation( operation );
-			delta.addOperation( operation );
+	if ( previousValue != value ) {
+		if ( node instanceof Element ) {
+			// If we change the attribute of the element, we do not want to change attributes of its children, so
+			// the end on the range can not be put after the closing tag, it should be inside that element with the
+			// offset 0, so the range will contains only the opening tag...
+			range = new Range( Position.createBefore( node ), Position.createFromParentAndOffset( node, 0 ) );
+		} else {
+			// ...but for characters we can not put the range inside it, so we end the range after that character.
+			range = new Range( Position.createBefore( node ), Position.createAfter( node ) );
 		}
-	}
 
-	// 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.
-		let lastSplitPosition = range.start;
-
-		// Currently position in the scanning range. Because we need value after the position, it is not a current
-		// position of the iterator but the previous one (we need to iterate one more time to get the value after).
-		let position;
-		// Value before the currently position.
-		let valueBefore;
-		// Value after the currently position.
-		let valueAfter;
-
-		// Because we need not only a node, but also a position, we can not use ( value of range ).
-		const iterator = range[ Symbol.iterator ]();
-		// Iterator state.
-		let next = iterator.next();
-
-		while ( !next.done ) {
-			// 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;
+		const operation = new AttributeOperation(
+				range,
+				previousValue ? new Attribute( key, previousValue ) : null,
+				value ? new Attribute( key, value ) : null,
+				doc.version
+			);
+
+		doc.applyOperation( operation );
+		delta.addOperation( operation );
+	}
+}
+
+// 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.
+	let lastSplitPosition = range.start;
+
+	// Currently position in the scanning range. Because we need value after the position, it is not a current
+	// position of the iterator but the previous one (we need to iterate one more time to get the value after).
+	let position;
+	// Value before the currently position.
+	let valueBefore;
+	// Value after the currently position.
+	let valueAfter;
+
+	// Because we need not only a node, but also a position, we can not use ( value of range ).
+	const iterator = range[ Symbol.iterator ]();
+	// Iterator state.
+	let next = iterator.next();
+
+	while ( !next.done ) {
+		// 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();
 				}
 
-				position = iterator.position;
-				valueBefore = valueAfter;
+				lastSplitPosition = position;
 			}
 
-			next = iterator.next();
+			position = iterator.position;
+			valueBefore = valueAfter;
 		}
 
-		// Because position in the loop is not the iterator position (see let position comment), the last position in
-		// the while loop will be last but one position in the range. We need to check the last position manually.
-		if ( position instanceof Position && position != lastSplitPosition && valueBefore != value ) {
-			addOperation();
-		}
-
-		function addOperation() {
-			const operation = new AttributeOperation(
-					new Range( lastSplitPosition, position ),
-					valueBefore ? new Attribute( key, valueBefore ) : null,
-					value ? new Attribute( key, value ) : null,
-					doc.version
-				);
+		next = iterator.next();
+	}
 
-			doc.applyOperation( operation );
-			delta.addOperation( operation );
-		}
+	// Because position in the loop is not the iterator position (see let position comment), the last position in
+	// the while loop will be last but one position in the range. We need to check the last position manually.
+	if ( position instanceof Position && position != lastSplitPosition && valueBefore != value ) {
+		addOperation();
 	}
 
-	return AttributeDelta;
-} );
+	function addOperation() {
+		const operation = new AttributeOperation(
+				new Range( lastSplitPosition, position ),
+				valueBefore ? new Attribute( key, valueBefore ) : null,
+				value ? new Attribute( key, value ) : null,
+				doc.version
+			);
+
+		doc.applyOperation( operation );
+		delta.addOperation( operation );
+	}
+}

+ 99 - 101
packages/ckeditor5-engine/src/treemodel/delta/batch-base.js

@@ -5,119 +5,117 @@
 
 'use strict';
 
-CKEDITOR.define( [ 'ckeditorerror' ], ( CKEditorError ) => {
+import CKEditorError from '../../ckeditorerror.js';
+
+/**
+ * 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
+ */
+export default class Batch {
 	/**
-	 * 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' );
+	 * Creates Batch instance. Not recommended to use directly, use {@link treeModel.Document#batch} instead.
 	 *
-	 * 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
+	 * @constructor
+	 * @param {treeModel.Document} doc Document which this Batch changes.
 	 */
-	class Batch {
+	constructor( doc ) {
 		/**
-		 * Creates Batch instance. Not recommended to use directly, use {@link treeModel.Document#batch} instead.
+		 * Document which this Batch changes.
 		 *
-		 * @constructor
-		 * @param {treeModel.Document} doc Document which this Batch changes.
+		 * @readonly
+		 * @type {treeModel.Document}
 		 */
-		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 = [];
-		}
+		this.doc = doc;
 
 		/**
-		 * Adds delta to the Batch instance. All modification methods (insert, remove, split, etc.) use this method
-		 * to add created deltas.
+		 * Array of deltas which compose Batch.
 		 *
-		 * @param {treeModel.delta.Delta} delta Delta to add.
-		 * @return {treeModel.delta.Delta} Added delta.
+		 * @readonly
+		 * @type {Array.<treeModel.delta.Delta>}
 		 */
-		addDelta( delta ) {
-			delta.batch = this;
-			this.deltas.push( delta );
+		this.deltas = [];
+	}
 
-			return delta;
-		}
+	/**
+	 * 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 );
 
-		/**
-		 * 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 } );
-			}
+		return delta;
+	}
 
-			Batch.prototype[ name ] = creator;
+	/**
+	 * 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 } );
 		}
-	}
 
-	return Batch;
-} );
+		Batch.prototype[ name ] = creator;
+	}
+}

+ 35 - 39
packages/ckeditor5-engine/src/treemodel/delta/delta.js

@@ -5,54 +5,50 @@
 
 'use strict';
 
-CKEDITOR.define( [], () => {
+/**
+ * Base class for all deltas.
+ *
+ * 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 treeModel.Batch}.
+ *
+ * @class treeModel.delta.Delta
+ */
+export default class Delta {
 	/**
-	 * Base class for all deltas.
-	 *
-	 * 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.
+	 * Creates a delta instance.
 	 *
-	 * Multiple deltas are grouped into a single {@link treeModel.Batch}.
-	 *
-	 * @class treeModel.delta.Delta
+	 * @constructor
 	 */
-	class Delta {
+	constructor() {
 		/**
-		 * Creates a delta instance.
+		 * {@link treeModel.Batch} which delta is a part of. This property is null by default and set by the
+		 * {@link treeModel.Batch#addDelta} method.
 		 *
-		 * @constructor
+		 * @readonly
+		 * @type {treeModel.Batch}
 		 */
-		constructor() {
-			/**
-			 * {@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 {treeModel.Batch}
-			 */
-			this.batch = null;
-
-			/**
-			 * Array of operations which compose delta.
-			 *
-			 * @readonly
-			 * @type {treeModel.operation.Operation[]}
-			 */
-			this.operations = [];
-		}
+		this.batch = null;
 
 		/**
-		 * Add operation to the delta.
+		 * Array of operations which compose delta.
 		 *
-		 * @param {treeModel.operation.Operation} operation Operation instance.
+		 * @readonly
+		 * @type {treeModel.operation.Operation[]}
 		 */
-		addOperation( operation ) {
-			operation.delta = this;
-			this.operations.push( operation );
-
-			return operation;
-		}
+		this.operations = [];
 	}
 
-	return Delta;
-} );
+	/**
+	 * Add operation to the delta.
+	 *
+	 * @param {treeModel.operation.Operation} operation Operation instance.
+	 */
+	addOperation( operation ) {
+		operation.delta = this;
+		this.operations.push( operation );
+
+		return operation;
+	}
+}

+ 28 - 32
packages/ckeditor5-engine/src/treemodel/delta/insertdelta.js

@@ -5,40 +5,36 @@
 
 'use strict';
 
-CKEDITOR.define( [
-	'treemodel/delta/delta',
-	'treemodel/delta/register',
-	'treemodel/operation/insertoperation'
-], ( Delta, register, InsertOperation ) => {
-	/**
-	 * 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 treeModel.delta.InsertDelta
-	 */
-	class InsertDelta extends Delta {}
+import Delta from './delta.js';
+import register from './register.js';
+import InsertOperation from '../operation/insertoperation';
 
-	/**
-	 * Inserts a node or nodes at the given position.
-	 *
-	 * @chainable
-	 * @memberOf treeModel.Batch
-	 * @method insert
-	 * @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();
+/**
+ * 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 treeModel.delta.InsertDelta
+ */
+export default class InsertDelta extends Delta {}
 
-		const operation = new InsertOperation( position, nodes, this.doc.version );
-		this.doc.applyOperation( operation );
-		delta.addOperation( operation );
+/**
+ * Inserts a node or nodes at the given position.
+ *
+ * @chainable
+ * @memberOf treeModel.Batch
+ * @method insert
+ * @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();
 
-		this.addDelta( delta );
+	const operation = new InsertOperation( position, nodes, this.doc.version );
+	this.doc.applyOperation( operation );
+	delta.addOperation( operation );
 
-		return this;
-	} );
+	this.addDelta( delta );
 
-	return InsertDelta;
-} );
+	return this;
+} );

+ 57 - 61
packages/ckeditor5-engine/src/treemodel/delta/mergedelta.js

@@ -5,74 +5,70 @@
 
 'use strict';
 
-CKEDITOR.define( [
-	'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 treeModel.Batch#merge} method
-	 * uses the `MergeDelta` class which inherits from the `Delta` class and may overwrite some methods.
-	 *
-	 * @class treeModel.delta.MergeDelta
-	 */
-	class MergeDelta extends Delta {}
+import Delta from './delta.js';
+import register from './register.js';
+import Position from '../position.js';
+import Element from '../element.js';
+import RemoveOperation from '../operation/removeoperation.js';
+import MoveOperation from '../operation/moveoperation.js';
+import CKEditorError from '../../ckeditorerror.js';
 
-	/**
-	 * Merges two siblings at the given position.
-	 *
-	 * 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 treeModel.Batch
-	 * @param {treeModel.Position} position Position of merge.
-	 */
-	register( 'merge', function( position ) {
-		const delta = new MergeDelta();
-		const nodeBefore = position.nodeBefore;
-		const nodeAfter = position.nodeAfter;
+/**
+ * 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 treeModel.delta.MergeDelta
+ */
+export default class MergeDelta extends Delta {}
 
-		if ( !( nodeBefore instanceof Element ) ) {
-			/**
-			 * Node before merge position must be an element.
-			 *
-			 * @error batch-merge-no-element-before
-			 */
-			throw new CKEditorError(
-				'batch-merge-no-element-before: Node before merge position must be an element.' );
-		}
+/**
+ * Merges two siblings at the given position.
+ *
+ * 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 treeModel.Batch
+ * @param {treeModel.Position} position Position of merge.
+ */
+register( 'merge', function( position ) {
+	const delta = new MergeDelta();
+	const nodeBefore = position.nodeBefore;
+	const nodeAfter = position.nodeAfter;
 
-		if ( !( nodeAfter instanceof Element ) ) {
-			/**
-			 * Node after merge position must be an element.
-			 *
-			 * @error batch-merge-no-element-after
-			 */
-			throw new CKEditorError(
-				'batch-merge-no-element-after: Node after merge position must be an element.' );
-		}
+	if ( !( nodeBefore instanceof Element ) ) {
+		/**
+		 * Node before merge position must be an element.
+		 *
+		 * @error batch-merge-no-element-before
+		 */
+		throw new CKEditorError(
+			'batch-merge-no-element-before: Node before merge position must be an element.' );
+	}
 
-		const positionAfter = Position.createFromParentAndOffset( nodeAfter, 0 );
-		const positionBefore = Position.createFromParentAndOffset( nodeBefore, nodeBefore.getChildCount() );
+	if ( !( nodeAfter instanceof Element ) ) {
+		/**
+		 * Node after merge position must be an element.
+		 *
+		 * @error batch-merge-no-element-after
+		 */
+		throw new CKEditorError(
+			'batch-merge-no-element-after: Node after merge position must be an element.' );
+	}
 
-		const move = new MoveOperation( positionAfter, nodeAfter.getChildCount(), positionBefore, this.doc.version );
-		this.doc.applyOperation( move );
-		delta.addOperation( move );
+	const positionAfter = Position.createFromParentAndOffset( nodeAfter, 0 );
+	const positionBefore = Position.createFromParentAndOffset( nodeBefore, nodeBefore.getChildCount() );
 
-		const remove = new RemoveOperation( position, 1, this.doc.version );
-		this.doc.applyOperation( remove );
-		delta.addOperation( remove );
+	const move = new MoveOperation( positionAfter, nodeAfter.getChildCount(), positionBefore, this.doc.version );
+	this.doc.applyOperation( move );
+	delta.addOperation( move );
 
-		this.addDelta( delta );
+	const remove = new RemoveOperation( position, 1, this.doc.version );
+	this.doc.applyOperation( remove );
+	delta.addOperation( remove );
 
-		return this;
-	} );
+	this.addDelta( delta );
 
-	return MergeDelta;
+	return this;
 } );

+ 47 - 51
packages/ckeditor5-engine/src/treemodel/delta/movedelta.js

@@ -5,59 +5,55 @@
 
 'use strict';
 
-CKEDITOR.define( [
-	'treemodel/delta/delta',
-	'treemodel/delta/register',
-	'treemodel/operation/moveoperation',
-	'treemodel/position',
-	'treemodel/range',
-	'ckeditorerror'
-], ( Delta, register, MoveOperation, Position, Range, CKEditorError ) => {
-	/**
-	 * To provide specific OT behavior and better collisions solving, {@link treeModel.Batch#move} method
-	 * uses the `MoveDelta` class which inherits from the `Delta` class and may overwrite some methods.
-	 *
-	 * @class treeModel.delta.MoveDelta
-	 */
-	class MoveDelta extends Delta {}
-
-	function addMoveOperation( batch, delta, sourcePosition, howMany, targetPosition ) {
-		const operation = new MoveOperation( sourcePosition, howMany, targetPosition, batch.doc.version );
-		batch.doc.applyOperation( operation );
-		delta.addOperation( operation );
-	}
+import Delta from './delta.js';
+import register from './register.js';
+import MoveOperation from '../operation/moveoperation.js';
+import Position from '../position.js';
+import Range from '../range.js';
+import CKEditorError from '../../ckeditorerror.js';
+
+/**
+ * To provide specific OT behavior and better collisions solving, {@link treeModel.Batch#move} method
+ * uses the `MoveDelta` class which inherits from the `Delta` class and may overwrite some methods.
+ *
+ * @class treeModel.delta.MoveDelta
+ */
+export default class MoveDelta extends Delta {}
 
-	/**
-	 * Moves given node or given range of nodes to target position.
-	 *
-	 * @chainable
-	 * @method move
-	 * @memberOf treeModel.Batch
-	 * @param {treeModel.Node|treeModel.Range} nodeOrRange Node or range of nodes to move.
-	 * @param {treeModel.Position} targetPosition Position where moved nodes will be inserted.
-	 */
-	register( 'move', function( nodeOrRange, targetPosition ) {
-		const delta = new MoveDelta();
-
-		if ( nodeOrRange instanceof Range ) {
-			if ( !nodeOrRange.isFlat ) {
-				/**
-				 * Range to move is not flat.
-				 *
-				 * @error batch-move-range-not-flat
-				 */
-				throw new CKEditorError( 'batch-move-range-not-flat: Range to move is not flat.' );
-			}
-
-			addMoveOperation( this, delta, nodeOrRange.start, nodeOrRange.end.offset - nodeOrRange.start.offset, targetPosition );
-		} else {
-			addMoveOperation( this, delta, Position.createBefore( nodeOrRange ), 1, targetPosition );
+function addMoveOperation( batch, delta, sourcePosition, howMany, targetPosition ) {
+	const operation = new MoveOperation( sourcePosition, howMany, targetPosition, batch.doc.version );
+	batch.doc.applyOperation( operation );
+	delta.addOperation( operation );
+}
+
+/**
+ * Moves given node or given range of nodes to target position.
+ *
+ * @chainable
+ * @method move
+ * @memberOf treeModel.Batch
+ * @param {treeModel.Node|treeModel.Range} nodeOrRange Node or range of nodes to move.
+ * @param {treeModel.Position} targetPosition Position where moved nodes will be inserted.
+ */
+register( 'move', function( nodeOrRange, targetPosition ) {
+	const delta = new MoveDelta();
+
+	if ( nodeOrRange instanceof Range ) {
+		if ( !nodeOrRange.isFlat ) {
+			/**
+			 * Range to move is not flat.
+			 *
+			 * @error batch-move-range-not-flat
+			 */
+			throw new CKEditorError( 'batch-move-range-not-flat: Range to move is not flat.' );
 		}
 
-		this.addDelta( delta );
+		addMoveOperation( this, delta, nodeOrRange.start, nodeOrRange.end.offset - nodeOrRange.start.offset, targetPosition );
+	} else {
+		addMoveOperation( this, delta, Position.createBefore( nodeOrRange ), 1, targetPosition );
+	}
 
-		return this;
-	} );
+	this.addDelta( delta );
 
-	return MoveDelta;
-} );
+	return this;
+} );

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

@@ -7,8 +7,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( [
-	'treemodel/delta/batch-base'
-], ( Batch ) => {
-	return Batch.register;
-} );
+
+import Batch from './batch-base.js';
+
+export default Batch.register;

+ 41 - 45
packages/ckeditor5-engine/src/treemodel/delta/removedelta.js

@@ -5,53 +5,49 @@
 
 'use strict';
 
-CKEDITOR.define( [
-	'treemodel/delta/delta',
-	'treemodel/delta/register',
-	'treemodel/operation/removeoperation',
-	'treemodel/position',
-	'treemodel/range'
-], ( Delta, register, RemoveOperation, Position, Range ) => {
-	/**
-	 * 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 treeModel.delta.RemoveDelta
-	 */
-	class RemoveDelta extends Delta {}
-
-	function addRemoveOperation( batch, delta, position, howMany ) {
-		const operation = new RemoveOperation( position, howMany, batch.doc.version );
-		batch.doc.applyOperation( operation );
-		delta.addOperation( operation );
-	}
+import Delta from './delta.js';
+import register from './register.js';
+import RemoveOperation from '../operation/removeoperation.js';
+import Position from '../position.js';
+import Range from '../range.js';
 
-	/**
-	 * Removes given node or range of nodes.
-	 *
-	 * @chainable
-	 * @method remove
-	 * @memberOf treeModel.Batch
-	 * @param {treeModel.Node|treeModel.Range} nodeOrRange Node or range of nodes to remove.
-	 */
-	register( 'remove', function( nodeOrRange ) {
-		const delta = new RemoveDelta();
-
-		if ( nodeOrRange instanceof Range ) {
-			// The array is reversed, so the ranges are correct and do not have to be updated.
-			let ranges = nodeOrRange.getMinimalFlatRanges().reverse();
-
-			for ( let flat of ranges ) {
-				addRemoveOperation( this, delta, flat.start, flat.end.offset - flat.start.offset );
-			}
-		} else {
-			addRemoveOperation( this, delta, Position.createBefore( nodeOrRange ), 1 );
-		}
+/**
+ * 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 treeModel.delta.RemoveDelta
+ */
+export default class RemoveDelta extends Delta {}
+
+function addRemoveOperation( batch, delta, position, howMany ) {
+	const operation = new RemoveOperation( position, howMany, batch.doc.version );
+	batch.doc.applyOperation( operation );
+	delta.addOperation( operation );
+}
 
-		this.addDelta( delta );
+/**
+ * Removes given node or range of nodes.
+ *
+ * @chainable
+ * @method remove
+ * @memberOf treeModel.Batch
+ * @param {treeModel.Node|treeModel.Range} nodeOrRange Node or range of nodes to remove.
+ */
+register( 'remove', function( nodeOrRange ) {
+	const delta = new RemoveDelta();
+
+	if ( nodeOrRange instanceof Range ) {
+		// The array is reversed, so the ranges are correct and do not have to be updated.
+		let ranges = nodeOrRange.getMinimalFlatRanges().reverse();
+
+		for ( let flat of ranges ) {
+			addRemoveOperation( this, delta, flat.start, flat.end.offset - flat.start.offset );
+		}
+	} else {
+		addRemoveOperation( this, delta, Position.createBefore( nodeOrRange ), 1 );
+	}
 
-		return this;
-	} );
+	this.addDelta( delta );
 
-	return RemoveDelta;
+	return this;
 } );

+ 50 - 54
packages/ckeditor5-engine/src/treemodel/delta/splitdelta.js

@@ -5,67 +5,63 @@
 
 'use strict';
 
-CKEDITOR.define( [
-	'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 treeModel.Batch#split} method
-	 * uses `SplitDelta` class which inherits from the `Delta` class and may overwrite some methods.
-	 *
-	 * @class treeModel.delta.SplitDelta
-	 */
-	class SplitDelta extends Delta {}
+import Delta from './delta.js';
+import register from './register.js';
+import Position from '../position.js';
+import Element from '../element.js';
+import InsertOperation from '../operation/insertoperation.js';
+import MoveOperation from '../operation/moveoperation.js';
+import CKEditorError from '../../ckeditorerror.js';
 
-	/**
-	 * Splits a node at the given position.
-	 *
-	 * 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 treeModel.Batch
-	 * @param {treeModel.Position} position Position of split.
-	 */
-	register( 'split', function( position ) {
-		const delta = new SplitDelta();
-		const splitElement = position.parent;
+/**
+ * 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 treeModel.delta.SplitDelta
+ */
+export default class SplitDelta extends Delta {}
 
-		if ( !splitElement.parent ) {
-			/**
-			 * Root element can not be split.
-			 *
-			 * @error batch-split-root
-			 */
-			throw new CKEditorError( 'batch-split-root: Root element can not be split.' );
-		}
+/**
+ * Splits a node at the given position.
+ *
+ * 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 treeModel.Batch
+ * @param {treeModel.Position} position Position of split.
+ */
+register( 'split', function( position ) {
+	const delta = new SplitDelta();
+	const splitElement = position.parent;
 
-		const copy = new Element( splitElement.name, splitElement.getAttrs() );
-		const insert = new InsertOperation( Position.createAfter( splitElement ), copy, this.doc.version );
+	if ( !splitElement.parent ) {
+		/**
+		 * Root element can not be split.
+		 *
+		 * @error batch-split-root
+		 */
+		throw new CKEditorError( 'batch-split-root: Root element can not be split.' );
+	}
 
-		this.doc.applyOperation( insert );
-		delta.addOperation( insert );
+	const copy = new Element( splitElement.name, splitElement.getAttrs() );
+	const insert = new InsertOperation( Position.createAfter( splitElement ), copy, this.doc.version );
 
-		const move = new MoveOperation(
-			position,
-			splitElement.getChildCount() - position.offset,
-			Position.createFromParentAndOffset( copy, 0 ),
-			this.doc.version
-		);
+	this.doc.applyOperation( insert );
+	delta.addOperation( insert );
 
-		this.doc.applyOperation( move );
-		delta.addOperation( move );
+	const move = new MoveOperation(
+		position,
+		splitElement.getChildCount() - position.offset,
+		Position.createFromParentAndOffset( copy, 0 ),
+		this.doc.version
+	);
 
-		this.addDelta( delta );
+	this.doc.applyOperation( move );
+	delta.addOperation( move );
 
-		return this;
-	} );
+	this.addDelta( delta );
 
-	return SplitDelta;
+	return this;
 } );

+ 50 - 55
packages/ckeditor5-engine/src/treemodel/delta/unwrapdelta.js

@@ -5,61 +5,56 @@
 
 'use strict';
 
-CKEDITOR.define( [
-	'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 treeModel.Batch#merge} method
-	 * uses the `UnwrapDelta` class which inherits from the `Delta` class and may overwrite some methods.
-	 *
-	 * @class treeModel.delta.UnwrapDelta
-	 */
-	class UnwrapDelta extends Delta {}
+import Delta from './delta.js';
+import register from './register.js';
+import Position from '../position.js';
+import RemoveOperation from '../operation/removeoperation.js';
+import MoveOperation from '../operation/moveoperation.js';
+import CKEditorError from '../../ckeditorerror.js';
 
-	/**
-	 * Unwraps specified element, that is moves all it's children before it and then removes it. Throws
-	 * error if you try to unwrap an element that does not have a parent.
-	 *
-	 * @chainable
-	 * @method unwrap
-	 * @memberOf treeModel.Batch
-	 * @param {treeModel.Element} position Element to unwrap.
-	 */
-	register( 'unwrap', function( element ) {
-		if ( element.parent === null ) {
-			/**
-			 * Trying to unwrap an element that has no parent.
-			 *
-			 * @error batch-unwrap-element-no-parent
-			 */
-			throw new CKEditorError(
-				'batch-unwrap-element-no-parent: Trying to unwrap an element that has no parent.' );
-		}
-
-		const delta = new UnwrapDelta();
-
-		let sourcePosition = Position.createFromParentAndOffset( element, 0 );
-
-		const move = new MoveOperation( sourcePosition, element.getChildCount(), Position.createBefore( element ), this.doc.version );
-		this.doc.applyOperation( move );
-		delta.addOperation( move );
-
-		// Computing new position because we moved some nodes before `element`.
-		// If we would cache `Position.createBefore( element )` we remove wrong node.
-		const remove = new RemoveOperation( Position.createBefore( element ), 1, this.doc.version );
-		this.doc.applyOperation( remove );
-		delta.addOperation( remove );
-
-		this.addDelta( delta );
-
-		return this;
-	} );
+/**
+ * To provide specific OT behavior and better collisions solving, {@link treeModel.Batch#merge} method
+ * uses the `UnwrapDelta` class which inherits from the `Delta` class and may overwrite some methods.
+ *
+ * @class treeModel.delta.UnwrapDelta
+ */
+export default class UnwrapDelta extends Delta {}
 
-	return UnwrapDelta;
+/**
+ * Unwraps specified element, that is moves all it's children before it and then removes it. Throws
+ * error if you try to unwrap an element that does not have a parent.
+ *
+ * @chainable
+ * @method unwrap
+ * @memberOf treeModel.Batch
+ * @param {treeModel.Element} position Element to unwrap.
+ */
+register( 'unwrap', function( element ) {
+	if ( element.parent === null ) {
+		/**
+		 * Trying to unwrap an element that has no parent.
+		 *
+		 * @error batch-unwrap-element-no-parent
+		 */
+		throw new CKEditorError(
+			'batch-unwrap-element-no-parent: Trying to unwrap an element that has no parent.' );
+	}
+
+	const delta = new UnwrapDelta();
+
+	let sourcePosition = Position.createFromParentAndOffset( element, 0 );
+
+	const move = new MoveOperation( sourcePosition, element.getChildCount(), Position.createBefore( element ), this.doc.version );
+	this.doc.applyOperation( move );
+	delta.addOperation( move );
+
+	// Computing new position because we moved some nodes before `element`.
+	// If we would cache `Position.createBefore( element )` we remove wrong node.
+	const remove = new RemoveOperation( Position.createBefore( element ), 1, this.doc.version );
+	this.doc.applyOperation( remove );
+	delta.addOperation( remove );
+
+	this.addDelta( delta );
+
+	return this;
 } );

+ 47 - 51
packages/ckeditor5-engine/src/treemodel/delta/weakinsertdelta.js

@@ -5,55 +5,51 @@
 
 'use strict';
 
-CKEDITOR.define( [
-	'treemodel/delta/delta',
-	'treemodel/delta/register',
-	'treemodel/operation/insertoperation',
-	'treemodel/nodelist'
-], ( Delta, register, InsertOperation, NodeList ) => {
-	/**
-	 * To provide specific OT behavior and better collisions solving, the {@link treeModel.Batch#insert} method
-	 * uses the `WeakInsertDelta` class which inherits from the `Delta` class and may overwrite some methods.
-	 *
-	 * @class treeModel.delta.WeakInsertDelta
-	 */
-	class WeakInsertDelta extends Delta {}
-
-	/**
-	 * Inserts a node or nodes at the given position. {@link treeModel.Batch#weakInsert} is commonly used for actions
-	 * like typing or plain-text paste (without formatting). There are two differences between
-	 * {@link treeModel.Batch#insert} and {@link treeModel.Batch#weakInsert}:
-	 * * When using `weakInsert`, inserted nodes will have same attributes as the current attributes of
-	 * {@link treeModel.Document#selection document selection}.
-	 * * The above has to be reflected during {@link treeModel.operation.transform operational transformation}. Normal
-	 * behavior is that inserting inside range changed by {@link treeModel.operation.AttributeOperation} splits the operation
-	 * into two operations, which "omit" the inserted nodes. The correct behavior for `WeakInsertDelta` is that
-	 * {@link treeModel.operation.AttributeOperation} does not "break" and also applies attributes for inserted nodes.
-	 *
-	 * @chainable
-	 * @memberOf treeModel.Batch
-	 * @method weakInsert
-	 * @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( 'weakInsert', function( position, nodes ) {
-		const delta = new WeakInsertDelta();
-
-		nodes = new NodeList( nodes );
-
-		for ( let node of nodes ) {
-			node.setAttrsTo( this.doc.selection.getAttrs() );
-		}
-
-		const operation = new InsertOperation( position, nodes, this.doc.version );
-		this.doc.applyOperation( operation );
-		delta.addOperation( operation );
-
-		this.addDelta( delta );
-
-		return this;
-	} );
-
-	return WeakInsertDelta;
+import Delta from './delta.js';
+import register from './register.js';
+import InsertOperation from '../operation/insertoperation.js';
+import NodeList from '../nodelist.js';
+
+/**
+ * To provide specific OT behavior and better collisions solving, the {@link treeModel.Batch#insert} method
+ * uses the `WeakInsertDelta` class which inherits from the `Delta` class and may overwrite some methods.
+ *
+ * @class treeModel.delta.WeakInsertDelta
+ */
+export default class WeakInsertDelta extends Delta {}
+
+/**
+ * Inserts a node or nodes at the given position. {@link treeModel.Batch#weakInsert} is commonly used for actions
+ * like typing or plain-text paste (without formatting). There are two differences between
+ * {@link treeModel.Batch#insert} and {@link treeModel.Batch#weakInsert}:
+ * * When using `weakInsert`, inserted nodes will have same attributes as the current attributes of
+ * {@link treeModel.Document#selection document selection}.
+ * * The above has to be reflected during {@link treeModel.operation.transform operational transformation}. Normal
+ * behavior is that inserting inside range changed by {@link treeModel.operation.AttributeOperation} splits the operation
+ * into two operations, which "omit" the inserted nodes. The correct behavior for `WeakInsertDelta` is that
+ * {@link treeModel.operation.AttributeOperation} does not "break" and also applies attributes for inserted nodes.
+ *
+ * @chainable
+ * @memberOf treeModel.Batch
+ * @method weakInsert
+ * @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( 'weakInsert', function( position, nodes ) {
+	const delta = new WeakInsertDelta();
+
+	nodes = new NodeList( nodes );
+
+	for ( let node of nodes ) {
+		node.setAttrsTo( this.doc.selection.getAttrs() );
+	}
+
+	const operation = new InsertOperation( position, nodes, this.doc.version );
+	this.doc.applyOperation( operation );
+	delta.addOperation( operation );
+
+	this.addDelta( delta );
+
+	return this;
 } );

+ 60 - 64
packages/ckeditor5-engine/src/treemodel/delta/wrapdelta.js

@@ -5,78 +5,74 @@
 
 'use strict';
 
-CKEDITOR.define( [
-	'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, {@link treeModel.Batch#merge} method
-	 * uses the `WrapDelta` class which inherits from the `Delta` class and may overwrite some methods.
-	 *
-	 * @class treeModel.delta.WrapDelta
-	 */
-	class WrapDelta extends Delta {}
+import Delta from './delta.js';
+import register from './register.js';
+import Position from '../position.js';
+import Element from '../element.js';
+import InsertOperation from '../operation/insertoperation.js';
+import MoveOperation from '../operation/moveoperation.js';
+import CKEditorError from '../../ckeditorerror.js';
 
-	/**
-	 * Wraps given range with given element or with a new element of specified name if string has been passed.
-	 * **Note:** given range should be a "flat range" (see {@link treeModel.Range#isFlat}). If not, error will be thrown.
-	 *
-	 * @chainable
-	 * @method wrap
-	 * @memberOf treeModel.Batch
-	 * @param {treeModel.Range} range Range to wrap.
-	 * @param {treeModel.Element|String} elementOrString Element or name of element to wrap the range with.
-	 */
-	register( 'wrap', function( range, elementOrString ) {
-		if ( !range.isFlat ) {
-			/**
-			 * Range to wrap is not flat.
-			 *
-			 * @error batch-wrap-range-not-flat
-			 */
-			throw new CKEditorError( 'batch-wrap-range-not-flat: Range to wrap is not flat.' );
-		}
+/**
+ * To provide specific OT behavior and better collisions solving, {@link treeModel.Batch#merge} method
+ * uses the `WrapDelta` class which inherits from the `Delta` class and may overwrite some methods.
+ *
+ * @class treeModel.delta.WrapDelta
+ */
+export default class WrapDelta extends Delta {}
 
-		let element = elementOrString instanceof Element ? elementOrString : new Element( elementOrString );
+/**
+ * Wraps given range with given element or with a new element of specified name if string has been passed.
+ * **Note:** given range should be a "flat range" (see {@link treeModel.Range#isFlat}). If not, error will be thrown.
+ *
+ * @chainable
+ * @method wrap
+ * @memberOf treeModel.Batch
+ * @param {treeModel.Range} range Range to wrap.
+ * @param {treeModel.Element|String} elementOrString Element or name of element to wrap the range with.
+ */
+register( 'wrap', function( range, elementOrString ) {
+	if ( !range.isFlat ) {
+		/**
+		 * Range to wrap is not flat.
+		 *
+		 * @error batch-wrap-range-not-flat
+		 */
+		throw new CKEditorError( 'batch-wrap-range-not-flat: Range to wrap is not flat.' );
+	}
 
-		if ( element.getChildCount() > 0 ) {
-			/**
-			 * Element to wrap with is not empty.
-			 *
-			 * @error batch-wrap-element-not-empty
-			 */
-			throw new CKEditorError( 'batch-wrap-element-not-empty: Element to wrap with is not empty.' );
-		}
+	let element = elementOrString instanceof Element ? elementOrString : new Element( elementOrString );
 
-		if ( element.parent !== null ) {
-			/**
-			 * Element to wrap with is already attached to a tree model.
-			 *
-			 * @error batch-wrap-element-attached
-			 */
-			throw new CKEditorError( 'batch-wrap-element-attached: Element to wrap with is already attached to tree model.' );
-		}
+	if ( element.getChildCount() > 0 ) {
+		/**
+		 * Element to wrap with is not empty.
+		 *
+		 * @error batch-wrap-element-not-empty
+		 */
+		throw new CKEditorError( 'batch-wrap-element-not-empty: Element to wrap with is not empty.' );
+	}
 
-		const delta = new WrapDelta();
+	if ( element.parent !== null ) {
+		/**
+		 * Element to wrap with is already attached to a tree model.
+		 *
+		 * @error batch-wrap-element-attached
+		 */
+		throw new CKEditorError( 'batch-wrap-element-attached: Element to wrap with is already attached to tree model.' );
+	}
 
-		let insert = new InsertOperation( range.end, element, this.doc.version );
-		this.doc.applyOperation( insert );
-		delta.addOperation( insert );
+	const delta = new WrapDelta();
 
-		let targetPosition = Position.createFromParentAndOffset( element, 0 );
-		let move = new MoveOperation( range.start, range.end.offset - range.start.offset, targetPosition, this.doc.version );
-		this.doc.applyOperation( move );
-		delta.addOperation( move );
+	let insert = new InsertOperation( range.end, element, this.doc.version );
+	this.doc.applyOperation( insert );
+	delta.addOperation( insert );
 
-		this.addDelta( delta );
+	let targetPosition = Position.createFromParentAndOffset( element, 0 );
+	let move = new MoveOperation( range.start, range.end.offset - range.start.offset, targetPosition, this.doc.version );
+	this.doc.applyOperation( move );
+	delta.addOperation( move );
 
-		return this;
-	} );
+	this.addDelta( delta );
 
-	return WrapDelta;
+	return this;
 } );

+ 188 - 193
packages/ckeditor5-engine/src/treemodel/document.js

@@ -5,233 +5,228 @@
 
 'use strict';
 
-CKEDITOR.define( [
-	'treemodel/element',
-	'treemodel/rootelement',
-	'treemodel/batch',
-	'treemodel/selection',
-	'emittermixin',
-	'utils',
-	'ckeditorerror'
-], ( Element, RootElement, Batch, Selection, EmitterMixin, utils, CKEditorError ) => {
-	const graveyardSymbol = Symbol( 'graveyard' );
+import RootElement from './rootelement.js';
+import Batch from './batch.js';
+import Selection from './selection.js';
+import EmitterMixin from '../emittermixin.js';
+import CKEditorError from '../ckeditorerror';
+import objectUtils from '../lib/lodash/object.js';
 
+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 treeModel.operation.Operation operations}. To create operations in
+ * the simple way use use the {@link treeModel.Batch} API, for example:
+ *
+ *		doc.batch().insert( position, nodes ).split( otherPosition );
+ *
+ * @see #batch
+ *
+ * @class treeModel.Document
+ */
+export default class Document {
 	/**
-	 * 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 treeModel.operation.Operation operations}. To create operations in
-	 * the simple way use use the {@link treeModel.Batch} API, for example:
+	 * Creates an empty document instance with no {@link #roots}.
 	 *
-	 *		doc.batch().insert( position, nodes ).split( otherPosition );
-	 *
-	 * @see #batch
-	 *
-	 * @class treeModel.Document
+	 * @constructor
 	 */
-	class Document {
-		/**
-		 * Creates an empty document instance with no {@link #roots}.
-		 *
-		 * @constructor
-		 */
-		constructor() {
-			/**
-			 * List of roots that are owned and managed by this document.
-			 *
-			 * @readonly
-			 * @property {Map} roots
-			 */
-			this.roots = new Map();
-
-			/**
-			 * 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 treeModel.operation.Operation#baseVersion} will
-			 * not match document version the {@link document-applyOperation-wrong-version} error is thrown.
-			 *
-			 * @readonly
-			 * @property {Number} version
-			 */
-			this.version = 0;
-
-			// Graveyard tree root. Document always have a graveyard root, which stores removed nodes.
-			this.createRoot( graveyardSymbol );
-
-			/**
-			 * Array of pending changes. See: {@link #enqueueChanges}.
-			 *
-			 * @private
-			 * @property {Array.<Function>}
-			 */
-			this._pendingChanges = [];
-
-			/**
-			 * Selection done on this document.
-			 *
-			 * @readonly
-			 * @property {treeModel.Selection}
-			 */
-			this.selection = new Selection();
-		}
-
+	constructor() {
 		/**
-		 * Graveyard tree root. Document always have a graveyard root, which stores removed nodes.
+		 * List of roots that are owned and managed by this document.
 		 *
 		 * @readonly
-		 * @property {treeModel.RootElement} graveyard
+		 * @property {Map} roots
 		 */
-		get graveyard() {
-			return this.getRoot( graveyardSymbol );
-		}
+		this.roots = new Map();
 
 		/**
-		 * This is the entry point for all document changes. All changes on the document are done using
-		 * {@link treeModel.operation.Operation operations}. To create operations in the simple way use the
-		 * {@link treeModel.Batch} API available via {@link #batch} method.
-		 *
-		 * This method calls {@link #change} event.
+		 * 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 treeModel.operation.Operation#baseVersion} will
+		 * not match document version the {@link document-applyOperation-wrong-version} error is thrown.
 		 *
-		 * @param {treeModel.operation.Operation} operation Operation to be applied.
+		 * @readonly
+		 * @property {Number} version
 		 */
-		applyOperation( operation ) {
-			if ( operation.baseVersion !== this.version ) {
-				/**
-				 * Only operations with matching versions can be applied.
-				 *
-				 * @error document-applyOperation-wrong-version
-				 * @param {treeModel.operation.Operation} operation
-				 */
-				throw new CKEditorError(
-					'document-applyOperation-wrong-version: Only operations with matching versions can be applied.',
-					{ operation: operation } );
-			}
+		this.version = 0;
 
-			let changes = operation._execute();
-
-			this.version++;
-
-			const batch = operation.delta && operation.delta.batch;
-			this.fire( 'change', operation.type, changes, batch );
-		}
+		// Graveyard tree root. Document always have a graveyard root, which stores removed nodes.
+		this.createRoot( graveyardSymbol );
 
 		/**
-		 * Creates a {@link treeModel.Batch} instance which allows to change the document.
+		 * Array of pending changes. See: {@link #enqueueChanges}.
 		 *
-		 * @returns {treeModel.Batch} Batch instance.
+		 * @private
+		 * @property {Array.<Function>}
 		 */
-		batch() {
-			return new Batch( this );
-		}
+		this._pendingChanges = [];
 
 		/**
-		 * Creates a new top-level root.
+		 * Selection done on this document.
 		 *
-		 * @param {String|Symbol} name Unique root name.
-		 * @returns {treeModel.RootElement} Created root.
+		 * @readonly
+		 * @property {treeModel.Selection}
 		 */
-		createRoot( name ) {
-			if ( this.roots.has( name ) ) {
-				/**
-				 * Root with specified name already exists.
-				 *
-				 * @error document-createRoot-name-exists
-				 * @param {treeModel.Document} doc
-				 * @param {String} name
-				 */
-				throw new CKEditorError(
-					'document-createRoot-name-exists: Root with specified name already exists.',
-					{ name: name }
-				);
-			}
+		this.selection = new Selection();
+	}
 
-			const root = new RootElement( this );
-			this.roots.set( name, root );
+	/**
+	 * Graveyard tree root. Document always have a graveyard root, which stores removed nodes.
+	 *
+	 * @readonly
+	 * @property {treeModel.RootElement} graveyard
+	 */
+	get graveyard() {
+		return this.getRoot( graveyardSymbol );
+	}
 
-			return root;
+	/**
+	 * This is the entry point for all document changes. All changes on the document are done using
+	 * {@link treeModel.operation.Operation operations}. To create operations in the simple way use the
+	 * {@link treeModel.Batch} API available via {@link #batch} method.
+	 *
+	 * This method calls {@link #change} event.
+	 *
+	 * @param {treeModel.operation.Operation} operation Operation to be applied.
+	 */
+	applyOperation( operation ) {
+		if ( operation.baseVersion !== this.version ) {
+			/**
+			 * Only operations with matching versions can be applied.
+			 *
+			 * @error document-applyOperation-wrong-version
+			 * @param {treeModel.operation.Operation} operation
+			 */
+			throw new CKEditorError(
+				'document-applyOperation-wrong-version: Only operations with matching versions can be applied.',
+				{ operation: operation } );
 		}
 
-		/**
-		 * Enqueue a callback with document changes. Any changes to be done on document (mostly using {@link #batch} should
-		 * be placed in the queued callback. If no other plugin is changing document at the moment, the callback will be
-		 * called immediately. Otherwise it will wait for all previously queued changes to finish happening. This way
-		 * queued callback will not interrupt other callbacks.
-		 *
-		 * When all queued changes are done {@link #changesDone} event is fired.
-		 *
-		 * @param {Function} callback Callback to enqueue.
-		 */
-		enqueueChanges( callback ) {
-			this._pendingChanges.push( callback );
+		let changes = operation._execute();
 
-			if ( this._pendingChanges.length == 1 ) {
-				while ( this._pendingChanges.length ) {
-					this._pendingChanges[ 0 ]();
-					this._pendingChanges.shift();
-				}
+		this.version++;
 
-				this.fire( 'changesDone' );
-			}
+		const batch = operation.delta && operation.delta.batch;
+		this.fire( 'change', operation.type, changes, batch );
+	}
+
+	/**
+	 * 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 {treeModel.RootElement} Created root.
+	 */
+	createRoot( name ) {
+		if ( this.roots.has( name ) ) {
+			/**
+			 * Root with specified name already exists.
+			 *
+			 * @error document-createRoot-name-exists
+			 * @param {treeModel.Document} doc
+			 * @param {String} name
+			 */
+			throw new CKEditorError(
+				'document-createRoot-name-exists: Root with specified name already exists.',
+				{ name: name }
+			);
 		}
 
-		/**
-		 * Returns top-level root by it's name.
-		 *
-		 * @param {String|Symbol} name Name of the root to get.
-		 * @returns {treeModel.RootElement} Root registered under given name.
-		 */
-		getRoot( name ) {
-			if ( !this.roots.has( name ) ) {
-				/**
-				 * Root with specified name does not exist.
-				 *
-				 * @error document-createRoot-root-not-exist
-				 * @param {String} name
-				 */
-				throw new CKEditorError(
-					'document-createRoot-root-not-exist: Root with specified name does not exist.',
-					{ name: name }
-				);
+		const root = new RootElement( this );
+		this.roots.set( name, root );
+
+		return root;
+	}
+
+	/**
+	 * Enqueue a callback with document changes. Any changes to be done on document (mostly using {@link #batch} should
+	 * be placed in the queued callback. If no other plugin is changing document at the moment, the callback will be
+	 * called immediately. Otherwise it will wait for all previously queued changes to finish happening. This way
+	 * queued callback will not interrupt other callbacks.
+	 *
+	 * When all queued changes are done {@link #changesDone} event is fired.
+	 *
+	 * @param {Function} callback Callback to enqueue.
+	 */
+	enqueueChanges( callback ) {
+		this._pendingChanges.push( callback );
+
+		if ( this._pendingChanges.length == 1 ) {
+			while ( this._pendingChanges.length ) {
+				this._pendingChanges[ 0 ]();
+				this._pendingChanges.shift();
 			}
 
-			return this.roots.get( name );
+			this.fire( 'changesDone' );
 		}
+	}
 
-		/**
-		 * Fired when document changes by applying an operation.
-		 *
-		 * There are 5 types of change:
-		 *
-		 * * 'insert' when nodes are inserted,
-		 * * 'remove' when nodes are removed,
-		 * * 'reinsert' when remove is undone,
-		 * * 'move' when nodes are moved,
-		 * * 'attr' when attributes change.
-		 *
-		 * Change event is fired after the change is done. This means that any ranges or positions passed in
-		 * `changeInfo` are referencing nodes and paths in updated tree model.
-		 *
-		 * @event change
-		 * @param {String} type Change type, possible option: 'insert', 'remove', 'reinsert', 'move', 'attr'.
-		 * @param {Object} changeInfo Additional information about the change.
-		 * @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 {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.
-		 * @param {treeModel.Batch} {@link treeModel.Batch} of changes which this change is a part of.
-		 */
+	/**
+	 * Returns top-level root by it's name.
+	 *
+	 * @param {String|Symbol} name Name of the root to get.
+	 * @returns {treeModel.RootElement} Root registered under given name.
+	 */
+	getRoot( name ) {
+		if ( !this.roots.has( name ) ) {
+			/**
+			 * Root with specified name does not exist.
+			 *
+			 * @error document-createRoot-root-not-exist
+			 * @param {String} name
+			 */
+			throw new CKEditorError(
+				'document-createRoot-root-not-exist: Root with specified name does not exist.',
+				{ name: name }
+			);
+		}
 
-		/**
-		 * Fired when all queued document changes are done. See {@link #enqueueChanges}.
-		 *
-		 * @event changesDone
-		 */
+		return this.roots.get( name );
 	}
 
-	utils.extend( Document.prototype, EmitterMixin );
+	/**
+	 * Fired when document changes by applying an operation.
+	 *
+	 * There are 5 types of change:
+	 *
+	 * * 'insert' when nodes are inserted,
+	 * * 'remove' when nodes are removed,
+	 * * 'reinsert' when remove is undone,
+	 * * 'move' when nodes are moved,
+	 * * 'attr' when attributes change.
+	 *
+	 * Change event is fired after the change is done. This means that any ranges or positions passed in
+	 * `changeInfo` are referencing nodes and paths in updated tree model.
+	 *
+	 * @event change
+	 * @param {String} type Change type, possible option: 'insert', 'remove', 'reinsert', 'move', 'attr'.
+	 * @param {Object} changeInfo Additional information about the change.
+	 * @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 {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.
+	 * @param {treeModel.Batch} {@link treeModel.Batch} of changes which this change is a part of.
+	 */
+
+	/**
+	 * Fired when all queued document changes are done. See {@link #enqueueChanges}.
+	 *
+	 * @event changesDone
+	 */
+}
 
-	return Document;
-} );
+objectUtils.extend( Document.prototype, EmitterMixin );

+ 89 - 93
packages/ckeditor5-engine/src/treemodel/element.js

@@ -5,117 +5,113 @@
 
 'use strict';
 
-CKEDITOR.define( [
-	'treemodel/node',
-	'treemodel/nodelist'
-], ( Node, NodeList ) => {
+import Node from './node.js';
+import NodeList from './nodelist.js';
+
+/**
+ * Tree data model element.
+ *
+ * @class treeModel.Element
+ */
+export default class Element extends Node {
 	/**
-	 * Tree data model element.
+	 * Creates a tree data model element.
+	 *
+	 * This constructor should be used only internally by the document.
 	 *
-	 * @class treeModel.Element
+	 * @param {String} name Node name.
+	 * @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
 	 */
-	class Element extends Node {
-		/**
-		 * Creates a tree data model element.
-		 *
-		 * This constructor should be used only internally by the document.
-		 *
-		 * @param {String} name Node name.
-		 * @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 ) {
-			super( attrs );
-
-			/**
-			 * Element name.
-			 *
-			 * @readonly
-			 * @property {String} name
-			 */
-			this.name = name;
-
-			/**
-			 * List of children nodes.
-			 *
-			 * @protected
-			 * @property {treeModel.NodeList} _children
-			 */
-			this._children = new NodeList();
-
-			if ( children ) {
-				this.insertChildren( 0, children );
-			}
-		}
+	constructor( name, attrs, children ) {
+		super( attrs );
 
 		/**
-		 * Gets child at the given index.
+		 * Element name.
 		 *
-		 * @param {Number} index Index of child.
-		 * @returns {treeModel.Node} Child node.
+		 * @readonly
+		 * @property {String} name
 		 */
-		getChild( index ) {
-			return this._children.get( index );
-		}
+		this.name = name;
 
 		/**
-		 * Gets the number of element's children.
+		 * List of children nodes.
 		 *
-		 * @returns {Number} The number of element's children.
+		 * @protected
+		 * @property {treeModel.NodeList} _children
 		 */
-		getChildCount() {
-			return this._children.length;
-		}
+		this._children = new NodeList();
 
-		/**
-		 * Gets index of the given child node.
-		 *
-		 * @param {treeModel.Node} node Child node.
-		 * @returns {Number} Index of the child node.
-		 */
-		getChildIndex( node ) {
-			return this._children.indexOf( node );
+		if ( children ) {
+			this.insertChildren( 0, children );
 		}
+	}
 
-		/**
-		 * 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 treeModel.operation.InsertOperation}.
-		 *
-		 * @param {Number} index Position where nodes should be inserted.
-		 * @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 ) );
+	/**
+	 * Gets child at the given index.
+	 *
+	 * @param {Number} index Index of child.
+	 * @returns {treeModel.Node} Child node.
+	 */
+	getChild( index ) {
+		return this._children.get( index );
+	}
 
-			for ( let node of this._children ) {
-				node.parent = this;
-			}
-		}
+	/**
+	 * Gets the number of element's children.
+	 *
+	 * @returns {Number} The number of element's children.
+	 */
+	getChildCount() {
+		return this._children.length;
+	}
 
-		/**
-		 * 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 treeModel.operation.RemoveOperation}.
-		 *
-		 * @param {Number} index Position of the first node to remove.
-		 * @param {Number} number Number of nodes to remove.
-		 * @returns {treeModel.NodeList} The list of removed nodes.
-		 */
+	/**
+	 * Gets index of the given child node.
+	 *
+	 * @param {treeModel.Node} node Child node.
+	 * @returns {Number} Index of the child node.
+	 */
+	getChildIndex( node ) {
+		return this._children.indexOf( node );
+	}
 
-		removeChildren( index, number ) {
-			for ( let i = index; i < index + number; i++ ) {
-				this._children.get( i ).parent = null;
-			}
+	/**
+	 * 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 treeModel.operation.InsertOperation}.
+	 *
+	 * @param {Number} index Position where nodes should be inserted.
+	 * @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 ) );
 
-			return this._children.remove( index, number );
+		for ( let node of this._children ) {
+			node.parent = this;
 		}
 	}
 
-	return Element;
-} );
+	/**
+	 * 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 treeModel.operation.RemoveOperation}.
+	 *
+	 * @param {Number} index Position of the first node to remove.
+	 * @param {Number} number Number of nodes to remove.
+	 * @returns {treeModel.NodeList} The list of removed nodes.
+	 */
+
+	removeChildren( index, number ) {
+		for ( let i = index; i < index + number; i++ ) {
+			this._children.get( i ).parent = null;
+		}
+
+		return this._children.remove( index, number );
+	}
+}

+ 151 - 155
packages/ckeditor5-engine/src/treemodel/liveposition.js

@@ -5,184 +5,180 @@
 
 'use strict';
 
-CKEDITOR.define( [
-	'treemodel/position',
-	'treemodel/range',
-	'emittermixin',
-	'utils'
-], ( Position, Range, EmitterMixin, utils ) => {
-	const STICKS_TO_NEXT = 0;
-	const STICKS_TO_PREVIOUS = 1;
+import Position from './position.js';
+import Range from './range.js';
+import EmitterMixin from '../emittermixin.js';
+import objectUtils from '../lib/lodash/object.js';
 
+const STICKS_TO_NEXT = 0;
+const STICKS_TO_PREVIOUS = 1;
+
+/**
+ * LivePosition is a position in the Tree Model that updates itself as the tree changes. It may be used as a bookmark.
+ * **Note:** Be very careful when dealing with LivePosition. Each LivePosition instance bind events that might
+ * have to be unbound. Use {@link #detach} whenever you don't need LivePosition anymore.
+ *
+ * @class treeModel.LivePosition
+ */
+
+export default class LivePosition extends Position {
 	/**
-	 * LivePosition is a position in the Tree Model that updates itself as the tree changes. It may be used as a bookmark.
-	 * **Note:** Be very careful when dealing with LivePosition. Each LivePosition instance bind events that might
-	 * have to be unbound. Use {@link #detach} whenever you don't need LivePosition anymore.
+	 * Creates a live position.
 	 *
-	 * @class treeModel.LivePosition
+	 * @see {@link treeModel.Position}
+	 * @param root
+	 * @param path
+	 * @param {Number} [stickiness] Flag representing how live position is "sticking" with their neighbour nodes.
+	 * Defaults to {@link #STICKS_TO_NEXT}. See {@link #stickiness}.
+	 * @constructor
 	 */
+	constructor( root, path, stickiness ) {
+		super( root, path );
 
-	class LivePosition extends Position {
 		/**
-		 * Creates a live position.
+		 * Flag representing LivePosition stickiness. LivePosition might be sticking to previous node or next node.
+		 * Whenever some nodes are inserted at the same position as LivePosition, `stickiness` is checked to decide if
+		 * LivePosition should be moved. Similar applies when a range of nodes is moved and one of it's boundary
+		 * position is same as LivePosition.
 		 *
-		 * @see {@link treeModel.Position}
-		 * @param root
-		 * @param path
-		 * @param {Number} [stickiness] Flag representing how live position is "sticking" with their neighbour nodes.
-		 * Defaults to {@link #STICKS_TO_NEXT}. See {@link #stickiness}.
-		 * @constructor
-		 */
-		constructor( root, path, stickiness ) {
-			super( root, path );
-
-			/**
-			 * Flag representing LivePosition stickiness. LivePosition might be sticking to previous node or next node.
-			 * Whenever some nodes are inserted at the same position as LivePosition, `stickiness` is checked to decide if
-			 * LivePosition should be moved. Similar applies when a range of nodes is moved and one of it's boundary
-			 * position is same as LivePosition.
-			 *
-			 * Examples:
-			 * Insert:
-			 * Position is at | and we insert at the same position, marked as ^:
-			 * | sticks to previous node: `<p>f|^oo</p>` => `<p>f|baroo</p>`
-			 * | sticks to next node: `<p>f^|oo</p>` => `<p>fbar|oo</p>`
-			 *
-			 * Move:
-			 * Position is at | and range [ ] is moved to position ^:
-			 * | sticks to previous node: `<p>f|[oo]</p><p>b^ar</p>` => `<p>f|</p><p>booar</p>`
-			 * | sticks to next node: `<p>f|[oo]</p><p>b^ar</p>` => `<p>f</p><p>b|ooar</p>`
-			 *
-			 * Accepted values are {@link #STICKS_TO_PREVIOUS} and {@link #STICKS_TO_NEXT}.
-			 *
-			 * @type {Number}
-			 */
-			this.stickiness = stickiness || STICKS_TO_NEXT;
-
-			bindWithDocument.call( this );
-		}
-
-		/**
-		 * Unbinds all events previously bound by LivePosition. Use it whenever you don't need LivePosition instance
-		 * anymore (i.e. when leaving scope in which it was declared or before re-assigning variable that was
-		 * referring to it).
-		 */
-		detach() {
-			this.stopListening();
-		}
-
-		/**
-		 * @static
-		 * @method createAfter
-		 * @see {@link treeModel.Position#createAfter}
-		 * @param {treeModel.Node} node
-		 * @returns {treeModel.LivePosition}
-		 */
-
-		/**
-		 * @static
-		 * @method createBefore
-		 * @see {@link treeModel.Position#createBefore}
-		 * @param {treeModel.Node} node
-		 * @returns {treeModel.LivePosition}
-		 */
-
-		/**
-		 * @static
-		 * @method createFromParentAndOffset
-		 * @see {@link treeModel.Position#createFromParentAndOffset}
-		 * @param {treeModel.Element} parent
-		 * @param {Number} offset
-		 * @returns {treeModel.LivePosition}
+		 * Examples:
+		 * Insert:
+		 * Position is at | and we insert at the same position, marked as ^:
+		 * | sticks to previous node: `<p>f|^oo</p>` => `<p>f|baroo</p>`
+		 * | sticks to next node: `<p>f^|oo</p>` => `<p>fbar|oo</p>`
+		 *
+		 * Move:
+		 * Position is at | and range [ ] is moved to position ^:
+		 * | sticks to previous node: `<p>f|[oo]</p><p>b^ar</p>` => `<p>f|</p><p>booar</p>`
+		 * | sticks to next node: `<p>f|[oo]</p><p>b^ar</p>` => `<p>f</p><p>b|ooar</p>`
+		 *
+		 * Accepted values are {@link #STICKS_TO_PREVIOUS} and {@link #STICKS_TO_NEXT}.
+		 *
+		 * @type {Number}
 		 */
+		this.stickiness = stickiness || STICKS_TO_NEXT;
 
-		/**
-		 * @static
-		 * @method createFromPosition
-		 * @see {@link treeModel.Position#createFromPosition}
-		 * @param {treeModel.Position} position
-		 * @returns {treeModel.LivePosition}
-		 */
+		bindWithDocument.call( this );
 	}
 
 	/**
-	 * Binds this LivePosition to the {@link treeModel.Document} that owns this position {@link treeModel.RootElement root}.
-	 *
-	 * @private
-	 * @method bindWithDocument
+	 * Unbinds all events previously bound by LivePosition. Use it whenever you don't need LivePosition instance
+	 * anymore (i.e. when leaving scope in which it was declared or before re-assigning variable that was
+	 * referring to it).
 	 */
-	function bindWithDocument() {
-		/*jshint validthis: true */
-
-		this.listenTo(
-			this.root.document,
-			'change',
-			( event, type, changes ) => {
-				transform.call( this, type, changes.range, changes.sourcePosition );
-			},
-			this
-		);
+	detach() {
+		this.stopListening();
 	}
 
 	/**
-	 * Updates this position accordingly to the updates applied to the Tree Model. Bases on change events.
-	 *
-	 * @private
-	 * @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.
+	 * @static
+	 * @method createAfter
+	 * @see {@link treeModel.Position#createAfter}
+	 * @param {treeModel.Node} node
+	 * @returns {treeModel.LivePosition}
 	 */
-	function transform( type, range, position ) {
-		/*jshint validthis: true */
-
-		let howMany = range.end.offset - range.start.offset;
-		let transformed;
 
-		switch ( type ) {
-			case 'insert':
-				let insertBefore = this.stickiness == STICKS_TO_NEXT;
-				transformed = this.getTransformedByInsertion( range.start, howMany, insertBefore );
-				break;
-
-			case 'move':
-			case 'remove':
-			case 'reinsert':
-				let originalRange = Range.createFromPositionAndShift( position, howMany );
-
-				let gotMoved = originalRange.containsPosition( this ) ||
-					( originalRange.start.isEqual( this ) && this.stickiness == STICKS_TO_NEXT ) ||
-					( originalRange.end.isEqual( this ) && this.stickiness == STICKS_TO_PREVIOUS );
-
-				// We can't use .getTransformedByMove() because we have a different if-condition.
-				if ( gotMoved ) {
-					transformed = this._getCombined( position, range.start );
-				} else {
-					let insertBefore = this.stickiness == STICKS_TO_NEXT;
-					transformed = this.getTransformedByMove( position, range.start, howMany, insertBefore );
-				}
-				break;
-		}
-
-		this.path = transformed.path;
-		this.root = transformed.root;
-	}
+	/**
+	 * @static
+	 * @method createBefore
+	 * @see {@link treeModel.Position#createBefore}
+	 * @param {treeModel.Node} node
+	 * @returns {treeModel.LivePosition}
+	 */
 
 	/**
-	 * Flag representing that the position is sticking to the node before it or to the beginning of it's parent node.
-	 *
-	 * @type {Number}
+	 * @static
+	 * @method createFromParentAndOffset
+	 * @see {@link treeModel.Position#createFromParentAndOffset}
+	 * @param {treeModel.Element} parent
+	 * @param {Number} offset
+	 * @returns {treeModel.LivePosition}
 	 */
-	LivePosition.STICKS_TO_PREVIOUS = STICKS_TO_PREVIOUS;
 
 	/**
-	 * Flag representing that the position is sticking to the node after it or to the end of it's parent node.
-	 *
-	 * @type {number}
+	 * @static
+	 * @method createFromPosition
+	 * @see {@link treeModel.Position#createFromPosition}
+	 * @param {treeModel.Position} position
+	 * @returns {treeModel.LivePosition}
 	 */
-	LivePosition.STICKS_TO_NEXT = STICKS_TO_NEXT;
+}
+
+/**
+ * Binds this LivePosition to the {@link treeModel.Document} that owns this position {@link treeModel.RootElement root}.
+ *
+ * @private
+ * @method bindWithDocument
+ */
+function bindWithDocument() {
+	/*jshint validthis: true */
+
+	this.listenTo(
+		this.root.document,
+		'change',
+		( event, type, changes ) => {
+			transform.call( this, type, changes.range, changes.sourcePosition );
+		},
+		this
+	);
+}
 
-	utils.extend( LivePosition.prototype, EmitterMixin );
+/**
+ * Updates this position accordingly to the updates applied to the Tree Model. Bases on change events.
+ *
+ * @private
+ * @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.
+ */
+function transform( type, range, position ) {
+	/*jshint validthis: true */
+
+	let howMany = range.end.offset - range.start.offset;
+	let transformed;
+
+	switch ( type ) {
+		case 'insert':
+			let insertBefore = this.stickiness == STICKS_TO_NEXT;
+			transformed = this.getTransformedByInsertion( range.start, howMany, insertBefore );
+			break;
+
+		case 'move':
+		case 'remove':
+		case 'reinsert':
+			let originalRange = Range.createFromPositionAndShift( position, howMany );
+
+			let gotMoved = originalRange.containsPosition( this ) ||
+				( originalRange.start.isEqual( this ) && this.stickiness == STICKS_TO_NEXT ) ||
+				( originalRange.end.isEqual( this ) && this.stickiness == STICKS_TO_PREVIOUS );
+
+			// We can't use .getTransformedByMove() because we have a different if-condition.
+			if ( gotMoved ) {
+				transformed = this._getCombined( position, range.start );
+			} else {
+				let insertBefore = this.stickiness == STICKS_TO_NEXT;
+				transformed = this.getTransformedByMove( position, range.start, howMany, insertBefore );
+			}
+			break;
+	}
+
+	this.path = transformed.path;
+	this.root = transformed.root;
+}
+
+/**
+ * Flag representing that the position is sticking to the node before it or to the beginning of it's parent node.
+ *
+ * @type {Number}
+ */
+LivePosition.STICKS_TO_PREVIOUS = STICKS_TO_PREVIOUS;
+
+/**
+ * Flag representing that the position is sticking to the node after it or to the end of it's parent node.
+ *
+ * @type {number}
+ */
+LivePosition.STICKS_TO_NEXT = STICKS_TO_NEXT;
 
-	return LivePosition;
-} );
+objectUtils.extend( LivePosition.prototype, EmitterMixin );

+ 114 - 118
packages/ckeditor5-engine/src/treemodel/liverange.js

@@ -5,137 +5,133 @@
 
 'use strict';
 
-CKEDITOR.define( [
-	'treemodel/range',
-	'treemodel/liveposition',
-	'emittermixin',
-	'utils'
-], ( Range, LivePosition, EmitterMixin, utils ) => {
+import Range from './range.js';
+import LivePosition from './liveposition.js';
+import EmitterMixin from '../emittermixin.js';
+import objectUtils from '../lib/lodash/object.js';
+
+/**
+ * LiveRange is a Range in the Tree Model that updates itself as the tree changes. It may be used as a bookmark.
+ * **Note:** Constructor creates it's own {@link treeModel.LivePosition} instances basing on passed values.
+ * **Note:** Be very careful when dealing with LiveRange. Each LiveRange instance bind events that might
+ * have to be unbound. Use {@link #detach} whenever you don't need LiveRange anymore.
+ *
+ * @class treeModel.LiveRange
+ */
+export default class LiveRange extends Range {
 	/**
-	 * LiveRange is a Range in the Tree Model that updates itself as the tree changes. It may be used as a bookmark.
-	 * **Note:** Constructor creates it's own {@link treeModel.LivePosition} instances basing on passed values.
-	 * **Note:** Be very careful when dealing with LiveRange. Each LiveRange instance bind events that might
-	 * have to be unbound. Use {@link #detach} whenever you don't need LiveRange anymore.
+	 * Creates a live range.
 	 *
-	 * @class treeModel.LiveRange
+	 * @see {treeModel.Range}
+	 * @constructor
 	 */
-	class LiveRange extends Range {
-		/**
-		 * Creates a live range.
-		 *
-		 * @see {treeModel.Range}
-		 * @constructor
-		 */
-		constructor( start, end ) {
-			super( start, end );
-
-			this.start = new LivePosition( this.start.root, this.start.path.slice(), LivePosition.STICKS_TO_NEXT );
-			this.end = new LivePosition( this.end.root, this.end.path.slice(), LivePosition.STICKS_TO_PREVIOUS );
-
-			bindWithDocument.call( this );
-		}
+	constructor( start, end ) {
+		super( start, end );
 
-		/**
-		 * Unbinds all events previously bound by LiveRange. Use it whenever you don't need LiveRange instance
-		 * anymore (i.e. when leaving scope in which it was declared or before re-assigning variable that was
-		 * referring to it).
-		 */
-		detach() {
-			this.start.detach();
-			this.end.detach();
-			this.stopListening();
-		}
+		this.start = new LivePosition( this.start.root, this.start.path.slice(), LivePosition.STICKS_TO_NEXT );
+		this.end = new LivePosition( this.end.root, this.end.path.slice(), LivePosition.STICKS_TO_PREVIOUS );
 
-		/**
-		 * @see {@link treeModel.Range#createFromElement}
-		 * @static
-		 * @method createFromElement
-		 * @param {treeModel.Element} element
-		 * @returns {treeModel.LiveRange}
-		 */
-
-		/**
-		 * @see {@link treeModel.Range#createFromPositionAndShift}
-		 * @static
-		 * @method createFromPositionAndShift
-		 * @param {treeModel.Position} position
-		 * @param {Number} shift
-		 * @returns {treeModel.LiveRange}
-		 */
-
-		/**
-		 * @see {@link treeModel.Range#createFromParentsAndOffsets}
-		 * @static
-		 * @method createFromParentsAndOffsets
-		 * @param {treeModel.Element} startElement
-		 * @param {Number} startOffset
-		 * @param {treeModel.Element} endElement
-		 * @param {Number} endOffset
-		 * @returns {treeModel.LiveRange}
-		 */
-
-		/**
-		 * @see {@link treeModel.Range#createFromRange}
-		 * @static
-		 * @method createFromRange
-		 * @param {treeModel.Range} range
-		 * @returns {treeModel.LiveRange}
-		 */
+		bindWithDocument.call( this );
 	}
 
 	/**
-	 * Binds this LiveRange to the {@link treeModel.Document} that owns this range.
-	 *
-	 * @private
-	 * @method bindWithDocument
+	 * Unbinds all events previously bound by LiveRange. Use it whenever you don't need LiveRange instance
+	 * anymore (i.e. when leaving scope in which it was declared or before re-assigning variable that was
+	 * referring to it).
 	 */
-	function bindWithDocument() {
-		/*jshint validthis: true */
-
-		this.listenTo(
-			this.root.document,
-			'change',
-			( event, type, changes ) => {
-				fixBoundaries.call( this, type, changes.range, changes.sourcePosition );
-			},
-			this
-		);
+	detach() {
+		this.start.detach();
+		this.end.detach();
+		this.stopListening();
 	}
 
 	/**
-	 * LiveRange boundaries are instances of {@link treeModel.LivePosition}, so it is updated thanks to them. This method
-	 * additionally fixes the results of updating live positions taking into account that those live positions
-	 * are boundaries of a range. An example case for fixing live positions is end boundary is moved before start boundary.
-	 *
-	 * @private
-	 * @method fixBoundaries
-	 * @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.
+	 * @see {@link treeModel.Range#createFromElement}
+	 * @static
+	 * @method createFromElement
+	 * @param {treeModel.Element} element
+	 * @returns {treeModel.LiveRange}
+	 */
+
+	/**
+	 * @see {@link treeModel.Range#createFromPositionAndShift}
+	 * @static
+	 * @method createFromPositionAndShift
+	 * @param {treeModel.Position} position
+	 * @param {Number} shift
+	 * @returns {treeModel.LiveRange}
+	 */
+
+	/**
+	 * @see {@link treeModel.Range#createFromParentsAndOffsets}
+	 * @static
+	 * @method createFromParentsAndOffsets
+	 * @param {treeModel.Element} startElement
+	 * @param {Number} startOffset
+	 * @param {treeModel.Element} endElement
+	 * @param {Number} endOffset
+	 * @returns {treeModel.LiveRange}
 	 */
-	function fixBoundaries( type, range, position ) {
-		/*jshint validthis: true */
-
-		if ( type == 'move' || type == 'remove' || type == 'reinsert' ) {
-			let containsStart = range.containsPosition( this.start ) || range.start.isEqual( this.start );
-			let containsEnd = range.containsPosition( this.end ) || range.end.isEqual( this.end );
-			position = position.getTransformedByInsertion( range.start, range.end.offset - range.start.offset, true );
-
-			// If the range contains both start and end, don't do anything - LivePositions that are boundaries of
-			// this LiveRange are in correct places, they got correctly transformed.
-			if ( containsStart && !containsEnd && !range.end.isTouching( position ) ) {
-				this.start.path = position.path.slice();
-				this.start.root = position.root;
-			}
-
-			if ( containsEnd && !containsStart && !range.start.isTouching( position ) ) {
-				this.end.path = position.path.slice();
-				this.end.root = position.root;
-			}
+
+	/**
+	 * @see {@link treeModel.Range#createFromRange}
+	 * @static
+	 * @method createFromRange
+	 * @param {treeModel.Range} range
+	 * @returns {treeModel.LiveRange}
+	 */
+}
+
+/**
+ * Binds this LiveRange to the {@link treeModel.Document} that owns this range.
+ *
+ * @private
+ * @method bindWithDocument
+ */
+function bindWithDocument() {
+	/*jshint validthis: true */
+
+	this.listenTo(
+		this.root.document,
+		'change',
+		( event, type, changes ) => {
+			fixBoundaries.call( this, type, changes.range, changes.sourcePosition );
+		},
+		this
+	);
+}
+
+/**
+ * LiveRange boundaries are instances of {@link treeModel.LivePosition}, so it is updated thanks to them. This method
+ * additionally fixes the results of updating live positions taking into account that those live positions
+ * are boundaries of a range. An example case for fixing live positions is end boundary is moved before start boundary.
+ *
+ * @private
+ * @method fixBoundaries
+ * @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.
+ */
+function fixBoundaries( type, range, position ) {
+	/* jshint validthis: true */
+
+	if ( type == 'move' || type == 'remove' || type == 'reinsert' ) {
+		let containsStart = range.containsPosition( this.start ) || range.start.isEqual( this.start );
+		let containsEnd = range.containsPosition( this.end ) || range.end.isEqual( this.end );
+		position = position.getTransformedByInsertion( range.start, range.end.offset - range.start.offset, true );
+
+		// If the range contains both start and end, don't do anything - LivePositions that are boundaries of
+		// this LiveRange are in correct places, they got correctly transformed.
+		if ( containsStart && !containsEnd && !range.end.isTouching( position ) ) {
+			this.start.path = position.path.slice();
+			this.start.root = position.root;
 		}
-	}
 
-	utils.extend( LiveRange.prototype, EmitterMixin );
+		if ( containsEnd && !containsStart && !range.start.isTouching( position ) ) {
+			this.end.path = position.path.slice();
+			this.end.root = position.root;
+		}
+	}
+}
 
-	return LiveRange;
-} );
+objectUtils.extend( LiveRange.prototype, EmitterMixin );

+ 159 - 163
packages/ckeditor5-engine/src/treemodel/node.js

@@ -5,205 +5,201 @@
 
 'use strict';
 
-CKEDITOR.define( [
-	'treemodel/attributelist',
-	'utils',
-	'ckeditorerror'
-], ( AttributeList, utils, CKEditorError ) => {
+import AttributeList from './attributelist.js';
+import langUtils from '../lib/lodash/lang.js';
+import CKEditorError from '../ckeditorerror';
+
+/**
+ * Abstract document tree node class.
+ *
+ * @abstract
+ * @class treeModel.Node
+ */
+export default class Node {
 	/**
-	 * Abstract document tree node class.
+	 * Creates a tree node.
+	 *
+	 * This is an abstract class, so this constructor should not be used directly.
 	 *
-	 * @abstract
-	 * @class treeModel.Node
+	 * @param {Iterable} attrs Iterable collection of {@link treeModel.Attribute attributes}.
+	 * @constructor
 	 */
-	class Node {
+	constructor( attrs ) {
 		/**
-		 * Creates a tree node.
+		 * Parent element. Null by default. Set by {@link treeModel.Element#insertChildren}.
 		 *
-		 * This is an abstract class, so this constructor should not be used directly.
-		 *
-		 * @param {Iterable} attrs Iterable collection of {@link treeModel.Attribute attributes}.
-		 * @constructor
+		 * @readonly
+		 * @property {treeModel.Element|null} parent
 		 */
-		constructor( attrs ) {
-			/**
-			 * Parent element. Null by default. Set by {@link treeModel.Element#insertChildren}.
-			 *
-			 * @readonly
-			 * @property {treeModel.Element|null} parent
-			 */
-			this.parent = null;
-
-			/**
-			 * List of attributes set on this node.
-			 * Attributes of nodes attached to the document can be changed only be the {@link treeModel.operation.AttributeOperation}.
-			 *
-			 * @private
-			 * @property {treeModel.AttributeList} _attrs
-			 */
-			this._attrs = new AttributeList( attrs );
-		}
+		this.parent = null;
 
 		/**
-		 * Depth of the node, which equals to total number of its parents.
+		 * List of attributes set on this node.
+		 * Attributes of nodes attached to the document can be changed only be the {@link treeModel.operation.AttributeOperation}.
 		 *
-		 * @readonly
-		 * @property {Number} depth
+		 * @private
+		 * @property {treeModel.AttributeList} _attrs
 		 */
-		get depth() {
-			let depth = 0;
-			let parent = this.parent;
+		this._attrs = new AttributeList( attrs );
+	}
 
-			while ( parent ) {
-				depth++;
+	/**
+	 * Depth of the node, which equals to total number of its parents.
+	 *
+	 * @readonly
+	 * @property {Number} depth
+	 */
+	get depth() {
+		let depth = 0;
+		let parent = this.parent;
 
-				parent = parent.parent;
-			}
+		while ( parent ) {
+			depth++;
 
-			return depth;
+			parent = parent.parent;
 		}
 
-		/**
-		 * Nodes next sibling or `null` if it is the last child.
-		 *
-		 * @readonly
-		 * @property {treeModel.Node|null} nextSibling
-		 */
-		get nextSibling() {
-			const index = this.getIndex();
-
-			return ( index !== null && this.parent.getChild( index + 1 ) ) || null;
-		}
+		return depth;
+	}
 
-		/**
-		 * Nodes previous sibling or null if it is the last child.
-		 *
-		 * @readonly
-		 * @property {treeModel.Node|null} previousSibling
-		 */
-		get previousSibling() {
-			const index = this.getIndex();
+	/**
+	 * Nodes next sibling or `null` if it is the last child.
+	 *
+	 * @readonly
+	 * @property {treeModel.Node|null} nextSibling
+	 */
+	get nextSibling() {
+		const index = this.getIndex();
 
-			return ( index !== null && this.parent.getChild( index - 1 ) ) || null;
-		}
+		return ( index !== null && this.parent.getChild( index + 1 ) ) || null;
+	}
 
-		/**
-		 * The top parent for the node. If node has no parent it is the root itself.
-		 *
-		 * @readonly
-		 * @property {Number} depth
-		 */
-		get root() {
-			let root = this;
+	/**
+	 * Nodes previous sibling or null if it is the last child.
+	 *
+	 * @readonly
+	 * @property {treeModel.Node|null} previousSibling
+	 */
+	get previousSibling() {
+		const index = this.getIndex();
 
-			while ( root.parent ) {
-				root = root.parent;
-			}
+		return ( index !== null && this.parent.getChild( index - 1 ) ) || null;
+	}
 
-			return root;
-		}
+	/**
+	 * The top parent for the node. If node has no parent it is the root itself.
+	 *
+	 * @readonly
+	 * @property {Number} depth
+	 */
+	get root() {
+		let root = this;
 
-		/**
-		 * @see {@link treeModel.AttributeList#getAttr}
-		 */
-		getAttr( key ) {
-			return this._attrs.getAttr( key );
+		while ( root.parent ) {
+			root = root.parent;
 		}
 
-		/**
-		 * @see {@link treeModel.AttributeList#getAttrs}
-		 */
-		getAttrs() {
-			return this._attrs.getAttrs();
-		}
+		return root;
+	}
 
-		/**
-		 * Index of the node in the parent element or null if the node has no parent.
-		 *
-		 * Throws error if the parent element does not contain this node.
-		 *
-		 * @returns {Number|Null} Index of the node in the parent element or null if the node has not parent.
-		 */
-		getIndex() {
-			let pos;
-
-			if ( !this.parent ) {
-				return null;
-			}
-
-			// No parent or child doesn't exist in parent's children.
-			if ( ( pos = this.parent.getChildIndex( this ) ) == -1 ) {
-				/**
-				 * The node's parent does not contain this node. It means that the document tree is corrupted.
-				 *
-				 * @error node-not-found-in-parent
-				 */
-				throw new CKEditorError( 'node-not-found-in-parent: The node\'s parent does not contain this node.' );
-			}
-
-			return pos;
-		}
+	/**
+	 * @see {@link treeModel.AttributeList#getAttr}
+	 */
+	getAttr( key ) {
+		return this._attrs.getAttr( key );
+	}
 
-		/**
-		 * 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 treeModel.Position}.
-		 *
-		 * @returns {Number[]} The path.
-		 */
-		getPath() {
-			const path = [];
-			let node = this;
+	/**
+	 * @see {@link treeModel.AttributeList#getAttrs}
+	 */
+	getAttrs() {
+		return this._attrs.getAttrs();
+	}
 
-			while ( node.parent ) {
-				path.unshift( node.getIndex() );
-				node = node.parent;
-			}
+	/**
+	 * Index of the node in the parent element or null if the node has no parent.
+	 *
+	 * Throws error if the parent element does not contain this node.
+	 *
+	 * @returns {Number|Null} Index of the node in the parent element or null if the node has not parent.
+	 */
+	getIndex() {
+		let pos;
 
-			return path;
+		if ( !this.parent ) {
+			return null;
 		}
 
-		/**
-		 * @see {@link treeModel.AttributeList#hasAttr}
-		 */
-		hasAttr( key ) {
-			return this._attrs.hasAttr( key );
+		// No parent or child doesn't exist in parent's children.
+		if ( ( pos = this.parent.getChildIndex( this ) ) == -1 ) {
+			/**
+			 * The node's parent does not contain this node. It means that the document tree is corrupted.
+			 *
+			 * @error node-not-found-in-parent
+			 */
+			throw new CKEditorError( 'node-not-found-in-parent: The node\'s parent does not contain this node.' );
 		}
 
-		/**
-		 * @see {@link treeModel.AttributeList#removeAttr}
-		 */
-		removeAttr( key ) {
-			this._attrs.removeAttr( key );
-		}
+		return pos;
+	}
 
-		/**
-		 * @see {@link treeModel.AttributeList#setAttr}
-		 */
-		setAttr( attr ) {
-			this._attrs.setAttr( attr );
-		}
+	/**
+	 * 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 treeModel.Position}.
+	 *
+	 * @returns {Number[]} The path.
+	 */
+	getPath() {
+		const path = [];
+		let node = this;
 
-		/**
-		 * @see {@link treeModel.AttributeList#setAttrsTo}
-		 */
-		setAttrsTo( attrs ) {
-			this._attrs.setAttrsTo( attrs );
+		while ( node.parent ) {
+			path.unshift( node.getIndex() );
+			node = node.parent;
 		}
 
-		/**
-		 * Custom toJSON method to solve child-parent circular dependencies.
-		 *
-		 * @returns {Object} Clone of this object with the parent property replaced with its name.
-		 */
-		toJSON() {
-			const json = utils.clone( this );
+		return path;
+	}
 
-			// Due to circular references we need to remove parent reference.
-			json.parent = this.parent ? this.parent.name : null;
+	/**
+	 * @see {@link treeModel.AttributeList#hasAttr}
+	 */
+	hasAttr( key ) {
+		return this._attrs.hasAttr( key );
+	}
 
-			return json;
-		}
+	/**
+	 * @see {@link treeModel.AttributeList#removeAttr}
+	 */
+	removeAttr( key ) {
+		this._attrs.removeAttr( key );
 	}
 
-	return Node;
-} );
+	/**
+	 * @see {@link treeModel.AttributeList#setAttr}
+	 */
+	setAttr( attr ) {
+		this._attrs.setAttr( attr );
+	}
+
+	/**
+	 * @see {@link treeModel.AttributeList#setAttrsTo}
+	 */
+	setAttrsTo( attrs ) {
+		this._attrs.setAttrsTo( attrs );
+	}
+
+	/**
+	 * Custom toJSON method to solve child-parent circular dependencies.
+	 *
+	 * @returns {Object} Clone of this object with the parent property replaced with its name.
+	 */
+	toJSON() {
+		const json = langUtils.clone( this );
+
+		// Due to circular references we need to remove parent reference.
+		json.parent = this.parent ? this.parent.name : null;
+
+		return json;
+	}
+}

+ 121 - 125
packages/ckeditor5-engine/src/treemodel/nodelist.js

@@ -5,152 +5,148 @@
 
 'use strict';
 
-CKEDITOR.define( [
-	'treemodel/character',
-	'treemodel/text',
-	'treemodel/node',
-	'utils'
-], ( Character, Text, Node, utils ) => {
+import Character from './character.js';
+import Text from './text.js';
+import Node from './node.js';
+import utils from '../utils.js';
+
+/**
+ * List of nodes. It is used to represent multiple nodes with a given order, for example children of
+ * {@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 treeModel.NodeList
+ */
+export default class NodeList {
 	/**
-	 * List of nodes. It is used to represent multiple nodes with a given order, for example children of
-	 * {@link treeModel.Element} object or nodes inserted using {@link treeModel.operation.InsertOperation}.
+	 * Constructor let you create a list of nodes in many ways. See examples:
+	 *
+	 *		let nodeList = new NodeList( [ new Element( p1 ), new Element( p1 ) ] );
+	 *		nodeList.length; // 2
+	 *
+	 *		let nodeList = new NodeList( new Element( p ) );
+	 *		nodeList.length; // 1
 	 *
-	 * Thanks to the constructor, which accepts various arguments, this class lets you easily create desired list of nodes.
+	 *		let nodeList = new NodeList( [ 'foo', new Element( p ), 'bar' ] );
+	 *		nodeList.length; // 7
 	 *
-	 * It also may internally compress nodes.
+	 *		let nodeList = new NodeList( 'foo' );
+	 *		nodeList.length; // 3
 	 *
-	 * @class treeModel.NodeList
+	 *		let nodeList = new NodeList( new Text( 'foo', [ new Attribute( 'bar', 'bom' ) ] ) );
+	 *		nodeList.length; // 3
+	 *		nodeList.get( 0 ).getAttr( 'bar' ); // 'bom'
+	 *		nodeList.get( 1 ).getAttr( 'bar' ); // 'bom'
+	 *		nodeList.get( 2 ).getAttr( 'bar' ); // 'bom'
+	 *
+	 *		let nodeListA = new NodeList( 'foo' );
+	 *		let nodeListB = new NodeList( nodeListA );
+	 *		nodeListA === nodeListB // true
+	 *		nodeListB.length // 3
+	 *
+	 * @param {treeModel.Node|treeModel.Text|treeModel.NodeList|String|Iterable} nodes List of nodes.
+	 * @constructor
 	 */
-	class NodeList {
+	constructor( nodes ) {
+		if ( nodes instanceof NodeList ) {
+			// We do not clone anything.
+			return nodes;
+		}
+
 		/**
-		 * Constructor let you create a list of nodes in many ways. See examples:
+		 * Internal array to store nodes.
 		 *
-		 *		let nodeList = new NodeList( [ new Element( p1 ), new Element( p1 ) ] );
-		 *		nodeList.length; // 2
-		 *
-		 *		let nodeList = new NodeList( new Element( p ) );
-		 *		nodeList.length; // 1
-		 *
-		 *		let nodeList = new NodeList( [ 'foo', new Element( p ), 'bar' ] );
-		 *		nodeList.length; // 7
-		 *
-		 *		let nodeList = new NodeList( 'foo' );
-		 *		nodeList.length; // 3
-		 *
-		 *		let nodeList = new NodeList( new Text( 'foo', [ new Attribute( 'bar', 'bom' ) ] ) );
-		 *		nodeList.length; // 3
-		 *		nodeList.get( 0 ).getAttr( 'bar' ); // 'bom'
-		 *		nodeList.get( 1 ).getAttr( 'bar' ); // 'bom'
-		 *		nodeList.get( 2 ).getAttr( 'bar' ); // 'bom'
-		 *
-		 *		let nodeListA = new NodeList( 'foo' );
-		 *		let nodeListB = new NodeList( nodeListA );
-		 *		nodeListA === nodeListB // true
-		 *		nodeListB.length // 3
-		 *
-		 * @param {treeModel.Node|treeModel.Text|treeModel.NodeList|String|Iterable} nodes List of nodes.
-		 * @constructor
+		 * @private
+		 * @property {Array}
 		 */
-		constructor( nodes ) {
-			if ( nodes instanceof NodeList ) {
-				// We do not clone anything.
-				return nodes;
-			}
+		this._nodes = [];
 
-			/**
-			 * Internal array to store nodes.
-			 *
-			 * @private
-			 * @property {Array}
-			 */
-			this._nodes = [];
+		if ( nodes ) {
+			let node, character;
 
-			if ( nodes ) {
-				let node, character;
+			if ( !utils.isIterable( nodes ) ) {
+				nodes = [ nodes ];
+			}
 
-				if ( !utils.isIterable( nodes ) ) {
-					nodes = [ nodes ];
+			for ( node of nodes ) {
+				// Node.
+				if ( node instanceof Node ) {
+					this._nodes.push( node );
 				}
-
-				for ( node of nodes ) {
-					// Node.
-					if ( node instanceof Node ) {
-						this._nodes.push( node );
-					}
-					// Text.
-					else if ( node instanceof Text ) {
-						for ( character of node.text ) {
-							this._nodes.push( new Character( character, node.attrs ) );
-						}
+				// Text.
+				else if ( node instanceof Text ) {
+					for ( character of node.text ) {
+						this._nodes.push( new Character( character, node.attrs ) );
 					}
-					// String.
-					else {
-						for ( character of node ) {
-							this._nodes.push( new Character( character ) );
-						}
+				}
+				// String.
+				else {
+					for ( character of node ) {
+						this._nodes.push( new Character( character ) );
 					}
 				}
 			}
 		}
+	}
 
-		/**
-		 * Number of nodes in the node list.
-		 *
-		 * @readonly
-		 * @property {Number} length
-		 */
-		get length() {
-			return this._nodes.length;
-		}
-
-		/**
-		 * Node list iterator.
-		 */
-		[ Symbol.iterator ]() {
-			return this._nodes[ Symbol.iterator ]();
-		}
+	/**
+	 * Number of nodes in the node list.
+	 *
+	 * @readonly
+	 * @property {Number} length
+	 */
+	get length() {
+		return this._nodes.length;
+	}
 
-		/**
-		 * Returns node at the given index.
-		 *
-		 * @param {Number} index Node index.
-		 * @returns {treeModel.Node} Node at given index.
-		 */
-		get( index ) {
-			return this._nodes[ index ];
-		}
+	/**
+	 * Node list iterator.
+	 */
+	[ Symbol.iterator ]() {
+		return this._nodes[ Symbol.iterator ]();
+	}
 
-		/**
-		 * Search for the node in the node list.
-		 *
-		 * @param {treeModel.Node} node Node to find.
-		 * @returns {Number} Position of the node in the list.
-		 */
-		indexOf( node ) {
-			return this._nodes.indexOf( node );
-		}
+	/**
+	 * Returns node at the given index.
+	 *
+	 * @param {Number} index Node index.
+	 * @returns {treeModel.Node} Node at given index.
+	 */
+	get( index ) {
+		return this._nodes[ index ];
+	}
 
-		/**
-		 * 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 {treeModel.NodeList} nodeList List of nodes to insert.
-		 */
-		insert( index, nodeList ) {
-			this._nodes.splice.apply( this._nodes, [ index, 0 ].concat( nodeList._nodes ) );
-		}
+	/**
+	 * Search for the node in the node list.
+	 *
+	 * @param {treeModel.Node} node Node to find.
+	 * @returns {Number} Position of the node in the list.
+	 */
+	indexOf( node ) {
+		return this._nodes.indexOf( node );
+	}
 
-		/**
-		 * Removes number of nodes starting at the given index.
-		 *
-		 * @param {Number} index Position of the first node to remove.
-		 * @param {Number} number Number of nodes to remove.
-		 * @returns {treeModel.NodeList} List of removed nodes.
-		 */
-		remove( index, number ) {
-			return new NodeList( this._nodes.splice( index, number ) );
-		}
+	/**
+	 * 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 {treeModel.NodeList} nodeList List of nodes to insert.
+	 */
+	insert( index, nodeList ) {
+		this._nodes.splice.apply( this._nodes, [ index, 0 ].concat( nodeList._nodes ) );
 	}
 
-	return NodeList;
-} );
+	/**
+	 * Removes number of nodes starting at the given index.
+	 *
+	 * @param {Number} index Position of the first node to remove.
+	 * @param {Number} number Number of nodes to remove.
+	 * @returns {treeModel.NodeList} List of removed nodes.
+	 */
+	remove( index, number ) {
+		return new NodeList( this._nodes.splice( index, number ) );
+	}
+}

+ 108 - 112
packages/ckeditor5-engine/src/treemodel/operation/attributeoperation.js

@@ -5,139 +5,135 @@
 
 'use strict';
 
-CKEDITOR.define( [
-	'treemodel/operation/operation',
-	'treemodel/range',
-	'ckeditorerror'
-], ( Operation, Range, CKEditorError ) => {
+import Operation from './operation.js';
+import Range from '../range.js';
+import CKEditorError from '../../ckeditorerror.js';
+
+/**
+ * Operation to change nodes' attribute. Using this class you can add, remove or change value of the attribute.
+ *
+ * @class treeModel.operation.AttributeOperation
+ */
+export default class AttributeOperation extends Operation {
 	/**
-	 * Operation to change nodes' attribute. Using this class you can add, remove or change value of the attribute.
+	 * 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.
+	 *
+	 * If only the old attribute is set, then it will be removed. Note that this attribute must be present in all nodes in
+	 * ranges.
 	 *
-	 * @class treeModel.operation.AttributeOperation
+	 * 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 {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
 	 */
-	class AttributeOperation extends Operation {
+	constructor( range, oldAttr, newAttr, baseVersion ) {
+		super( baseVersion );
+
 		/**
-		 * 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.
+		 * Range on which operation should be applied.
 		 *
-		 * If only the old attribute is set, then it will be removed. Note that this attribute must be present in all nodes in
-		 * ranges.
+		 * @readonly
+		 * @type {treeModel.Range}
+		 */
+		this.range = Range.createFromRange( range );
+
+		/**
+		 * Old attribute to change. Set to `null` if operation inserts a new attribute.
 		 *
-		 * 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.
+		 * @readonly
+		 * @type {treeModel.Attribute|null}
+		 */
+		this.oldAttr = oldAttr;
+
+		/**
+		 * New attribute. Set to `null` if operation removes the attribute.
 		 *
-		 * @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
+		 * @readonly
+		 * @type {treeModel.Attribute|null}
 		 */
-		constructor( range, oldAttr, newAttr, baseVersion ) {
-			super( baseVersion );
+		this.newAttr = newAttr;
+	}
 
-			/**
-			 * Range on which operation should be applied.
-			 *
-			 * @readonly
-			 * @type {treeModel.Range}
-			 */
-			this.range = Range.createFromRange( range );
+	get type() {
+		return 'attr';
+	}
 
-			/**
-			 * Old attribute to change. Set to `null` if operation inserts a new attribute.
-			 *
-			 * @readonly
-			 * @type {treeModel.Attribute|null}
-			 */
-			this.oldAttr = oldAttr;
+	clone() {
+		return new AttributeOperation( this.range, this.oldAttr, this.newAttr, this.baseVersion );
+	}
+
+	getReversed() {
+		return new AttributeOperation( this.range, this.newAttr, this.oldAttr, this.baseVersion + 1 );
+	}
 
+	_execute() {
+		const oldAttr = this.oldAttr;
+		const newAttr = this.newAttr;
+
+		if ( oldAttr !== null && newAttr !== null && oldAttr.key != newAttr.key ) {
 			/**
-			 * New attribute. Set to `null` if operation removes the attribute.
+			 * Old and new attributes should have the same keys.
 			 *
-			 * @readonly
-			 * @type {treeModel.Attribute|null}
+			 * @error operation-attribute-different-keys
+			 * @param {treeModel.Attribute} oldAttr
+			 * @param {treeModel.Attribute} newAttr
 			 */
-			this.newAttr = newAttr;
-		}
-
-		get type() {
-			return 'attr';
-		}
-
-		clone() {
-			return new AttributeOperation( this.range, this.oldAttr, this.newAttr, this.baseVersion );
+			throw new CKEditorError(
+				'operation-attribute-different-keys: Old and new attributes should have the same keys.',
+				{ oldAttr: oldAttr, newAttr: newAttr } );
 		}
 
-		getReversed() {
-			return new AttributeOperation( this.range, this.newAttr, this.oldAttr, this.baseVersion + 1 );
-		}
-
-		_execute() {
-			const oldAttr = this.oldAttr;
-			const newAttr = this.newAttr;
-
-			if ( oldAttr !== null && newAttr !== null && oldAttr.key != newAttr.key ) {
-				/**
-				 * Old and new attributes should have the same keys.
-				 *
-				 * @error operation-attribute-different-keys
-				 * @param {treeModel.Attribute} oldAttr
-				 * @param {treeModel.Attribute} newAttr
-				 */
-				throw new CKEditorError(
-					'operation-attribute-different-keys: Old and new attributes should have the same keys.',
-					{ oldAttr: oldAttr, newAttr: newAttr } );
-			}
+		// Remove or change.
+		if ( oldAttr !== null ) {
+			for ( let node of this.range.getAllNodes() ) {
+				if ( !node.hasAttr( oldAttr ) ) {
+					/**
+					 * The attribute which should be removed does not exists for the given node.
+					 *
+					 * @error operation-attribute-no-attr-to-remove
+					 * @param {treeModel.Node} node
+					 * @param {treeModel.Attribute} attr
+					 */
+					throw new CKEditorError(
+						'operation-attribute-no-attr-to-remove: The attribute which should be removed does not exists for given node.',
+						{ node: node, attr: oldAttr } );
+				}
 
-			// Remove or change.
-			if ( oldAttr !== null ) {
-				for ( let node of this.range.getAllNodes() ) {
-					if ( !node.hasAttr( oldAttr ) ) {
-						/**
-						 * The attribute which should be removed does not exists for the given node.
-						 *
-						 * @error operation-attribute-no-attr-to-remove
-						 * @param {treeModel.Node} node
-						 * @param {treeModel.Attribute} attr
-						 */
-						throw new CKEditorError(
-							'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 ) {
-						node.removeAttr( oldAttr.key );
-					}
+				// 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 ) {
+					node.removeAttr( oldAttr.key );
 				}
 			}
+		}
 
-			// Insert or change.
-			if ( newAttr !== null ) {
-				for ( let node of this.range.getAllNodes() ) {
-					if ( oldAttr === null && node.hasAttr( newAttr.key ) ) {
-						/**
-						 * The attribute with given key already exists for the given node.
-						 *
-						 * @error operation-attribute-attr-exists
-						 * @param {treeModel.Node} node
-						 * @param {treeModel.Attribute} attr
-						 */
-						throw new CKEditorError(
-							'operation-attribute-attr-exists: The attribute with given key already exists.',
-							{ node: node, attr: newAttr } );
-					}
-
-					node.setAttr( newAttr );
+		// Insert or change.
+		if ( newAttr !== null ) {
+			for ( let node of this.range.getAllNodes() ) {
+				if ( oldAttr === null && node.hasAttr( newAttr.key ) ) {
+					/**
+					 * The attribute with given key already exists for the given node.
+					 *
+					 * @error operation-attribute-attr-exists
+					 * @param {treeModel.Node} node
+					 * @param {treeModel.Attribute} attr
+					 */
+					throw new CKEditorError(
+						'operation-attribute-attr-exists: The attribute with given key already exists.',
+						{ node: node, attr: newAttr } );
 				}
-			}
 
-			return { range: this.range, oldAttr: oldAttr, newAttr: newAttr };
+				node.setAttr( newAttr );
+			}
 		}
-	}
 
-	return AttributeOperation;
-} );
+		return { range: this.range, oldAttr: oldAttr, newAttr: newAttr };
+	}
+}

+ 48 - 55
packages/ckeditor5-engine/src/treemodel/operation/insertoperation.js

@@ -5,71 +5,64 @@
 
 'use strict';
 
-CKEDITOR.define( [
-	'treemodel/operation/operation',
-	'treemodel/nodelist',
-	'treemodel/position',
-	'treemodel/range',
-	'treemodel/operation/removeoperation'
-], ( Operation, NodeList, Position, Range ) => {
+import Operation from './operation.js';
+import NodeList from '../nodelist.js';
+import Position from '../position.js';
+import Range from '../range.js';
+import RemoveOperation from './removeoperation.js';
+
+/**
+ * Operation to insert list of nodes on the given position in the tree data model.
+ *
+ * @class treeModel.operation.InsertOperation
+ */
+export default class InsertOperation extends Operation {
 	/**
-	 * Operation to insert list of nodes on the given position in the tree data model.
+	 * Creates an insert operation.
 	 *
-	 * @class treeModel.operation.InsertOperation
+	 * @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
 	 */
-	class InsertOperation extends Operation {
+	constructor( position, nodes, baseVersion ) {
+		super( baseVersion );
+
 		/**
-		 * Creates an insert operation.
+		 * Position of insertion.
 		 *
-		 * @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
+		 * @readonly
+		 * @type {treeModel.Position}
 		 */
-		constructor( position, nodes, baseVersion ) {
-			super( baseVersion );
+		this.position = Position.createFromPosition( position );
 
-			/**
-			 * Position of insertion.
-			 *
-			 * @readonly
-			 * @type {treeModel.Position}
-			 */
-			this.position = Position.createFromPosition( position );
-
-			/**
-			 * List of nodes to insert.
-			 *
-			 * @readonly
-			 * @type {treeModel.NodeList}
-			 */
-			this.nodeList = new NodeList( nodes );
-		}
-
-		get type() {
-			return 'insert';
-		}
+		/**
+		 * List of nodes to insert.
+		 *
+		 * @readonly
+		 * @type {treeModel.NodeList}
+		 */
+		this.nodeList = new NodeList( nodes );
+	}
 
-		clone() {
-			return new InsertOperation( this.position, this.nodeList, this.baseVersion );
-		}
+	get type() {
+		return 'insert';
+	}
 
-		getReversed() {
-			// Because of circular dependencies we need to re-require remove operation here.
-			const RemoveOperation = CKEDITOR.require( 'treemodel/operation/removeoperation' );
+	clone() {
+		return new InsertOperation( this.position, this.nodeList, this.baseVersion );
+	}
 
-			return new RemoveOperation( this.position, this.nodeList.length, this.baseVersion + 1 );
-		}
+	getReversed() {
+		return new RemoveOperation( this.position, this.nodeList.length, this.baseVersion + 1 );
+	}
 
-		_execute() {
-			this.position.parent.insertChildren( this.position.offset, this.nodeList );
+	_execute() {
+		this.position.parent.insertChildren( this.position.offset, this.nodeList );
 
-			return {
-				range: Range.createFromPositionAndShift( this.position, this.nodeList.length )
-			};
-		}
+		return {
+			range: Range.createFromPositionAndShift( this.position, this.nodeList.length )
+		};
 	}
-
-	return InsertOperation;
-} );
+}

+ 105 - 109
packages/ckeditor5-engine/src/treemodel/operation/moveoperation.js

@@ -5,135 +5,131 @@
 
 'use strict';
 
-CKEDITOR.define( [
-	'treemodel/operation/operation',
-	'treemodel/position',
-	'treemodel/range',
-	'ckeditorerror',
-	'utils'
-], ( Operation, Position, Range, CKEditorError, utils ) => {
+import Operation from './operation.js';
+import Position from '../position.js';
+import Range from '../range.js';
+import CKEditorError from '../../ckeditorerror.js';
+import utils from '../../utils.js';
+
+/**
+ * Operation to move list of subsequent nodes from one position in the document to another.
+ *
+ * @class treeModel.operation.MoveOperation
+ */
+export default class MoveOperation extends Operation {
 	/**
-	 * Operation to move list of subsequent nodes from one position in the document to another.
+	 * Creates a move operation.
 	 *
-	 * @class treeModel.operation.MoveOperation
+	 * @param {treeModel.Position} sourcePosition Position before the first node to move.
+	 * @param {Number} howMany How many consecutive nodes to move, starting from `sourcePosition`.
+	 * @param {treeModel.Position} targetPosition Position where moved nodes will be inserted.
+	 * @param {Number} baseVersion {@link treeModel.Document#version} on which operation can be applied.
+	 * @constructor
 	 */
-	class MoveOperation extends Operation {
+	constructor( sourcePosition, howMany, targetPosition, baseVersion ) {
+		super( baseVersion );
+
+		/**
+		 * Source move position.
+		 *
+		 * @type {treeModel.Position}
+		 */
+		this.sourcePosition = Position.createFromPosition( sourcePosition );
+
+		/**
+		 * How many nodes to move.
+		 *
+		 * @type {Number}
+		 */
+		this.howMany = howMany;
+
 		/**
-		 * Creates a move operation.
+		 * Target move position.
 		 *
-		 * @param {treeModel.Position} sourcePosition Position before the first node to move.
-		 * @param {Number} howMany How many consecutive nodes to move, starting from `sourcePosition`.
-		 * @param {treeModel.Position} targetPosition Position where moved nodes will be inserted.
-		 * @param {Number} baseVersion {@link treeModel.Document#version} on which operation can be applied.
-		 * @constructor
+		 * @type {treeModel.Position}
 		 */
-		constructor( sourcePosition, howMany, targetPosition, baseVersion ) {
-			super( baseVersion );
+		this.targetPosition = Position.createFromPosition( targetPosition );
+	}
 
+	get type() {
+		return 'move';
+	}
+
+	clone() {
+		return new this.constructor( this.sourcePosition, this.howMany, this.targetPosition, this.baseVersion );
+	}
+
+	getReversed() {
+		return new this.constructor( this.targetPosition, this.howMany, this.sourcePosition, this.baseVersion + 1 );
+	}
+
+	_execute() {
+		let sourceElement = this.sourcePosition.parent;
+		let targetElement = this.targetPosition.parent;
+		let sourceOffset = this.sourcePosition.offset;
+		let targetOffset = this.targetPosition.offset;
+
+		// Validate whether move operation has correct parameters.
+		// Validation is pretty complex but move operation is one of the core ways to manipulate the document state.
+		// We expect that many errors might be connected with one of scenarios described below.
+		if ( !sourceElement || !targetElement ) {
 			/**
-			 * Source move position.
+			 * Source position or target position is invalid.
 			 *
-			 * @type {treeModel.Position}
+			 * @error operation-move-position-invalid
 			 */
-			this.sourcePosition = Position.createFromPosition( sourcePosition );
-
+			throw new CKEditorError(
+				'operation-move-position-invalid: Source position or target position is invalid.'
+			);
+		} else if ( sourceOffset + this.howMany > sourceElement.getChildCount() ) {
 			/**
-			 * How many nodes to move.
+			 * The nodes which should be moved do not exist.
 			 *
-			 * @type {Number}
+			 * @error operation-move-nodes-do-not-exist
 			 */
-			this.howMany = howMany;
-
+			throw new CKEditorError(
+				'operation-move-nodes-do-not-exist: The nodes which should be moved do not exist.'
+			);
+		} else if ( sourceElement === targetElement && sourceOffset <= targetOffset && targetOffset < sourceOffset + this.howMany ) {
 			/**
-			 * Target move position.
+			 * Trying to move a range of nodes into the middle of that range.
 			 *
-			 * @type {treeModel.Position}
+			 * @error operation-move-range-into-itself
 			 */
-			this.targetPosition = Position.createFromPosition( targetPosition );
-		}
-
-		get type() {
-			return 'move';
-		}
-
-		clone() {
-			return new this.constructor( this.sourcePosition, this.howMany, this.targetPosition, this.baseVersion );
-		}
-
-		getReversed() {
-			return new this.constructor( this.targetPosition, this.howMany, this.sourcePosition, this.baseVersion + 1 );
-		}
-
-		_execute() {
-			let sourceElement = this.sourcePosition.parent;
-			let targetElement = this.targetPosition.parent;
-			let sourceOffset = this.sourcePosition.offset;
-			let targetOffset = this.targetPosition.offset;
-
-			// Validate whether move operation has correct parameters.
-			// Validation is pretty complex but move operation is one of the core ways to manipulate the document state.
-			// We expect that many errors might be connected with one of scenarios described below.
-			if ( !sourceElement || !targetElement ) {
-				/**
-				 * Source position or target position is invalid.
-				 *
-				 * @error operation-move-position-invalid
-				 */
-				throw new CKEditorError(
-					'operation-move-position-invalid: Source position or target position is invalid.'
-				);
-			} else if ( sourceOffset + this.howMany > sourceElement.getChildCount() ) {
-				/**
-				 * The nodes which should be moved do not exist.
-				 *
-				 * @error operation-move-nodes-do-not-exist
-				 */
-				throw new CKEditorError(
-					'operation-move-nodes-do-not-exist: The nodes which should be moved do not exist.'
-				);
-			} else if ( sourceElement === targetElement && sourceOffset <= targetOffset && targetOffset < sourceOffset + this.howMany ) {
-				/**
-				 * Trying to move a range of nodes into the middle of that range.
-				 *
-				 * @error operation-move-range-into-itself
-				 */
-				throw new CKEditorError(
-					'operation-move-range-into-itself: Trying to move a range of nodes to the inside of that range.'
-				);
-			} else {
-				if ( utils.compareArrays( this.sourcePosition.getParentPath(), this.targetPosition.getParentPath() ) == utils.compareArrays.PREFIX ) {
-					let i = this.sourcePosition.path.length - 1;
-
-					if ( this.targetPosition.path[ i ] >= sourceOffset && this.targetPosition.path[ i ] < sourceOffset + this.howMany ) {
-						/**
-						 * Trying to move a range of nodes into one of nodes from that range.
-						 *
-						 * @error operation-move-node-into-itself
-						 */
-						throw new CKEditorError(
-							'operation-move-node-into-itself: Trying to move a range of nodes into one of nodes from that range.'
-						);
-					}
+			throw new CKEditorError(
+				'operation-move-range-into-itself: Trying to move a range of nodes to the inside of that range.'
+			);
+		} else {
+			if ( utils.compareArrays( this.sourcePosition.getParentPath(), this.targetPosition.getParentPath() ) == utils.compareArrays.PREFIX ) {
+				let i = this.sourcePosition.path.length - 1;
+
+				if ( this.targetPosition.path[ i ] >= sourceOffset && this.targetPosition.path[ i ] < sourceOffset + this.howMany ) {
+					/**
+					 * Trying to move a range of nodes into one of nodes from that range.
+					 *
+					 * @error operation-move-node-into-itself
+					 */
+					throw new CKEditorError(
+						'operation-move-node-into-itself: Trying to move a range of nodes into one of nodes from that range.'
+					);
 				}
 			}
-			// End of validation.
+		}
+		// End of validation.
 
-			// If we move children in the same element and we remove elements on the position before the target we
-			// need to update a target offset.
-			if ( sourceElement === targetElement && sourceOffset < targetOffset ) {
-				targetOffset -= this.howMany;
-			}
+		// If we move children in the same element and we remove elements on the position before the target we
+		// need to update a target offset.
+		if ( sourceElement === targetElement && sourceOffset < targetOffset ) {
+			targetOffset -= this.howMany;
+		}
 
-			const removedNodes = sourceElement.removeChildren( sourceOffset, this.howMany );
+		const removedNodes = sourceElement.removeChildren( sourceOffset, this.howMany );
 
-			targetElement.insertChildren( targetOffset, removedNodes );
+		targetElement.insertChildren( targetOffset, removedNodes );
 
-			return {
-				sourcePosition: this.sourcePosition,
-				range: Range.createFromPositionAndShift( this.targetPosition, this.howMany )
-			};
-		}
+		return {
+			sourcePosition: this.sourcePosition,
+			range: Range.createFromPositionAndShift( this.targetPosition, this.howMany )
+		};
 	}
-
-	return MoveOperation;
-} );
+}

+ 22 - 26
packages/ckeditor5-engine/src/treemodel/operation/nooperation.js

@@ -5,33 +5,29 @@
 
 'use strict';
 
-CKEDITOR.define( [
-	'treemodel/operation/operation'
-], ( Operation ) => {
-	/**
-	 * Operation which is doing nothing ("empty operation", "do-nothing operation", "noop").
-	 * 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 treeModel.operation.NoOperation} it means that changes done by the transformed operation
-	 * have already been applied.
-	 *
-	 * @class treeModel.operation.NoOperation
-	 */
-	class NoOperation extends Operation {
-		clone() {
-			return new NoOperation( this.baseVersion );
-		}
+import Operation from './operation.js';
 
-		getReversed() {
-			return new NoOperation( this.baseVersion + 1 );
-		}
+/**
+ * Operation which is doing nothing ("empty operation", "do-nothing operation", "noop").
+ * 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 treeModel.operation.NoOperation} it means that changes done by the transformed operation
+ * have already been applied.
+ *
+ * @class treeModel.operation.NoOperation
+ */
+export default class NoOperation extends Operation {
+	clone() {
+		return new NoOperation( this.baseVersion );
+	}
 
-		_execute() {
-			// Do nothing.
-		}
+	getReversed() {
+		return new NoOperation( this.baseVersion + 1 );
 	}
 
-	return NoOperation;
-} );
+	_execute() {
+		// Do nothing.
+	}
+}

+ 57 - 61
packages/ckeditor5-engine/src/treemodel/operation/operation.js

@@ -5,76 +5,72 @@
 
 'use strict';
 
-CKEDITOR.define( [], () => {
+/**
+ * Abstract base operation class.
+ *
+ * @abstract
+ * @class treeModel.operation.Operation
+ */
+export default class Operation {
 	/**
-	 * Abstract base operation class.
+	 * Base operation constructor.
 	 *
-	 * @abstract
-	 * @class treeModel.operation.Operation
+	 * @param {Number} baseVersion {@link treeModel.Document#version} on which the operation can be applied.
+	 * @constructor
 	 */
-	class Operation {
+	constructor( baseVersion ) {
 		/**
-		 * Base operation constructor.
+		 * {@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.
 		 *
-		 * @param {Number} baseVersion {@link treeModel.Document#version} on which the operation can be applied.
-		 * @constructor
+		 * @property {Number}
 		 */
-		constructor( baseVersion ) {
-			/**
-			 * {@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}
-			 */
-			this.baseVersion = baseVersion;
+		this.baseVersion = baseVersion;
 
-			/**
-			 * Operation type.
-			 *
-			 * @property {String} type
-			 */
+		/**
+		 * Operation type.
+		 *
+		 * @property {String} type
+		 */
 
-			/**
-			 * {@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 {treeModel.Delta} delta
-			 */
+		/**
+		 * {@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 {treeModel.Delta} delta
+		 */
 
-			/**
-			 * Creates and returns an operation that has the same parameters as this operation.
-			 *
-			 * @method clone
-			 * @returns {treeModel.operation.Operation} Clone of this operation.
-			 */
+		/**
+		 * Creates and returns an operation that has the same parameters as this operation.
+		 *
+		 * @method clone
+		 * @returns {treeModel.operation.Operation} Clone of this operation.
+		 */
 
-			/**
-			 * Creates and returns a reverse operation. Reverse operation when executed right after
-			 * the original operation will bring back tree model state to the point before the original
-			 * operation execution. In other words, it reverses changes done by the original operation.
-			 *
-			 * 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 treeModel.operation.transform} it by all operations that were executed after the original operation.
-			 *
-			 * @method getReversed
-			 * @returns {treeModel.operation.Operation} Reversed operation.
-			 */
+		/**
+		 * Creates and returns a reverse operation. Reverse operation when executed right after
+		 * the original operation will bring back tree model state to the point before the original
+		 * operation execution. In other words, it reverses changes done by the original operation.
+		 *
+		 * 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 treeModel.operation.transform} it by all operations that were executed after the original operation.
+		 *
+		 * @method getReversed
+		 * @returns {treeModel.operation.Operation} Reversed operation.
+		 */
 
-			/**
-			 * Executes the operation - modifications described by the operation attributes
-			 * will be applied to the tree model.
-			 *
-			 * @protected
-			 * @method _execute
-			 * @returns {Object} Object with additional information about the applied changes. Always has `range`
-			 * property containing changed nodes. May have additional properties depending on the operation type.
-			 */
-		}
+		/**
+		 * Executes the operation - modifications described by the operation attributes
+		 * will be applied to the tree model.
+		 *
+		 * @protected
+		 * @method _execute
+		 * @returns {Object} Object with additional information about the applied changes. Always has `range`
+		 * property containing changed nodes. May have additional properties depending on the operation type.
+		 */
 	}
-
-	return Operation;
-} );
+}

+ 20 - 27
packages/ckeditor5-engine/src/treemodel/operation/reinsertoperation.js

@@ -5,33 +5,26 @@
 
 'use strict';
 
-CKEDITOR.define( [
-	'treemodel/operation/moveoperation',
-	'treemodel/operation/removeoperation'
-], ( MoveOperation ) => {
-	/**
-	 * Operation to reinsert previously removed nodes back to the non-graveyard root.
-	 * 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 treeModel.operation.ReinsertOperation
-	 */
-	class ReinsertOperation extends MoveOperation {
-		getReversed() {
-			// Because of circular dependencies we need to re-require reinsert operation here.
-			const RemoveOperation = CKEDITOR.require( 'treemodel/operation/removeoperation' );
+import MoveOperation from './moveoperation.js';
+import RemoveOperation from './removeoperation.js';
 
-			return new RemoveOperation( this.targetPosition, this.howMany, this.baseVersion + 1 );
-		}
-
-		get type() {
-			return 'reinsert';
-		}
+/**
+ * Operation to reinsert previously removed nodes back to the non-graveyard root.
+ * 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 treeModel.operation.ReinsertOperation
+ */
+export default class ReinsertOperation extends MoveOperation {
+	getReversed() {
+		return new RemoveOperation( this.targetPosition, this.howMany, this.baseVersion + 1 );
 	}
 
-	return ReinsertOperation;
-} );
+	get type() {
+		return 'reinsert';
+	}
+}

+ 29 - 36
packages/ckeditor5-engine/src/treemodel/operation/removeoperation.js

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

+ 338 - 338
packages/ckeditor5-engine/src/treemodel/operation/transform.js

@@ -43,381 +43,381 @@
  * @returns {Array.<treeModel.operation.Operation>} Result of the transformation.
  */
 
-CKEDITOR.define( [
-	'treemodel/operation/insertoperation',
-	'treemodel/operation/attributeoperation',
-	'treemodel/operation/moveoperation',
-	'treemodel/operation/nooperation',
-	'treemodel/position',
-	'treemodel/range',
-	'utils'
-], ( InsertOperation, AttributeOperation, MoveOperation, NoOperation, Position, Range, utils ) => {
-	const ot = {
-		InsertOperation: {
-			// Transforms InsertOperation `a` by InsertOperation `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.
-			InsertOperation( a, b, isStrong ) {
-				// Transformed operations are always new instances, not references to the original operations.
-				const transformed = a.clone();
-
-				// Transform insert position by the other operation position.
-				transformed.position = transformed.position.getTransformedByInsertion( b.position, b.nodeList.length, !isStrong );
-
-				return [ transformed ];
-			},
-
-			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.
-			MoveOperation( a, b, isStrong ) {
-				const transformed = a.clone();
-
-				// Transform insert position by the other operation parameters.
-				transformed.position = a.position.getTransformedByMove( b.sourcePosition, b.targetPosition, b.howMany, !isStrong );
-
-				return [ transformed ];
-			}
+import InsertOperation from './insertoperation.js';
+import AttributeOperation from './attributeoperation.js';
+import MoveOperation from './moveoperation.js';
+import NoOperation from './nooperation.js';
+import Position from '../position.js';
+import Range from '../range.js';
+import utils from '../../utils.js';
+
+export default transform;
+
+const ot = {
+	InsertOperation: {
+		// Transforms InsertOperation `a` by InsertOperation `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.
+		InsertOperation( a, b, isStrong ) {
+			// Transformed operations are always new instances, not references to the original operations.
+			const transformed = a.clone();
+
+			// Transform insert position by the other operation position.
+			transformed.position = transformed.position.getTransformedByInsertion( b.position, b.nodeList.length, !isStrong );
+
+			return [ transformed ];
 		},
 
-		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 AttributeOperation(
-						range,
-						a.oldAttr,
-						a.newAttr,
-						a.baseVersion
-					);
-				} );
-			},
-
-			// 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.
-			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 = [];
-
-					// 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 AttributeOperation( range, a.oldAttr, a.newAttr, a.baseVersion );
-						} )
-					);
-
-					if ( isStrong ) {
-						// If this operation is more important, we want also want to apply change to the part of the
-						// original range that has already been changed by the other operation. Since that range
-						// got changed we have to update oldAttr.
-						const common = a.range.getIntersection( b.range );
-
-						if ( common !== null ) {
-							operations.push( new AttributeOperation( common, b.oldAttr, a.newAttr, a.baseVersion ) );
-						}
-					}
+		AttributeOperation: doNotUpdate,
 
-					// If no operations has been added nothing should get updated, but since we need to return
-					// an instance of Operation we add NoOperation to the array.
-					if ( operations.length === 0 ) {
-						operations.push( new NoOperation( a.baseVersion ) );
-					}
+		// 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.
+		MoveOperation( a, b, isStrong ) {
+			const transformed = a.clone();
 
-					return operations;
-				} else {
-					// If operations don't conflict simply, return an array containing just a clone of this operation.
-					return [ a.clone() ];
-				}
-			},
-
-			// 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.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 );
-
-				// This will aggregate transformed ranges.
-				let ranges = [];
-
-				// 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.
-				const difference = joinRanges( a.range.getDifference( rangeB ) );
-
-				// Common is a range of nodes that is affected by MoveOperation. So it got moved to other place.
-				const common = a.range.getIntersection( rangeB );
-
-				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 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 );
-
-					// MoveOperation pastes nodes into target position. We acknowledge this by proper transformation.
-					// Note that since we operate on transformed difference range, we should transform by
-					// previously transformed target position.
-					// Note that we do not use Position.getTransformedByMove on range boundaries because we need to
-					// transform by insertion a range as a whole, since newTargetPosition might be inside that range.
-					ranges = difference.getTransformedByInsertion( newTargetPosition, b.howMany, false ).reverse();
-				}
+			// Transform insert position by the other operation parameters.
+			transformed.position = a.position.getTransformedByMove( b.sourcePosition, b.targetPosition, b.howMany, !isStrong );
 
-				if ( common !== null ) {
-					// Here we do not need to worry that newTargetPosition is inside moved range, because that
-					// would mean that the MoveOperation targets into itself, and that is incorrect operation.
-					// Instead, we calculate the new position of that part of original range.
-					common.start = common.start._getCombined( b.sourcePosition, newTargetPosition );
-					common.end = common.end._getCombined( b.sourcePosition, newTargetPosition );
+			return [ transformed ];
+		}
+	},
+
+	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 AttributeOperation(
+					range,
+					a.oldAttr,
+					a.newAttr,
+					a.baseVersion
+				);
+			} );
+		},
 
-					ranges.push( common );
+		// 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.
+		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 = [];
+
+				// 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 AttributeOperation( range, a.oldAttr, a.newAttr, a.baseVersion );
+					} )
+				);
+
+				if ( isStrong ) {
+					// If this operation is more important, we want also want to apply change to the part of the
+					// original range that has already been changed by the other operation. Since that range
+					// got changed we have to update oldAttr.
+					const common = a.range.getIntersection( b.range );
+
+					if ( common !== null ) {
+						operations.push( new AttributeOperation( common, b.oldAttr, a.newAttr, a.baseVersion ) );
+					}
 				}
 
-				// Map transformed range(s) to operations and return them.
-				return ranges.map( ( range ) => {
-					return new AttributeOperation(
-						range,
-						a.oldAttr,
-						a.newAttr,
-						a.baseVersion
-					);
-				} );
+				// If no operations has been added nothing should get updated, but since we need to return
+				// an instance of Operation we add NoOperation to the array.
+				if ( operations.length === 0 ) {
+					operations.push( new NoOperation( a.baseVersion ) );
+				}
+
+				return operations;
+			} else {
+				// If operations don't conflict simply, return an array containing just a clone of this operation.
+				return [ a.clone() ];
 			}
 		},
 
-		MoveOperation: {
-			// Transforms MoveOperation `a` by InsertOperation `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.
-			InsertOperation( a, b, isStrong ) {
-				// Get target position from the state "after" nodes are inserted by InsertOperation.
-				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.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,
-						range.end.offset - range.start.offset,
-						Position.createFromPosition( newTargetPosition ),
-						a.baseVersion
-					);
-				} );
-			},
-
-			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.
-			MoveOperation( a, b, isStrong ) {
-				// Special case when both move operations' target positions are inside nodes that are
-				// being moved by the other move operation. So in other words, we move ranges into inside of each other.
-				// This case can't be solved reasonably (on the other hand, it should not happen often).
-				if ( moveTargetIntoMovedRange( a, b ) && moveTargetIntoMovedRange( b, a ) ) {
-					// Instead of transforming operation, we return a reverse of the operation that we transform by.
-					// So when the results of this "transformation" will be applied, `b` MoveOperation will get reversed.
-					return [ b.getReversed() ];
-				}
+		// 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.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 );
+
+			// This will aggregate transformed ranges.
+			let ranges = [];
+
+			// 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.
+			const difference = joinRanges( a.range.getDifference( rangeB ) );
+
+			// Common is a range of nodes that is affected by MoveOperation. So it got moved to other place.
+			const common = a.range.getIntersection( rangeB );
+
+			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 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 );
+
+				// MoveOperation pastes nodes into target position. We acknowledge this by proper transformation.
+				// Note that since we operate on transformed difference range, we should transform by
+				// previously transformed target position.
+				// Note that we do not use Position.getTransformedByMove on range boundaries because we need to
+				// transform by insertion a range as a whole, since newTargetPosition might be inside that range.
+				ranges = difference.getTransformedByInsertion( newTargetPosition, b.howMany, false ).reverse();
+			}
 
-				// Create ranges from MoveOperations properties.
-				const rangeA = Range.createFromPositionAndShift( a.sourcePosition, a.howMany );
-				const rangeB = Range.createFromPositionAndShift( b.sourcePosition, b.howMany );
+			if ( common !== null ) {
+				// Here we do not need to worry that newTargetPosition is inside moved range, because that
+				// would mean that the MoveOperation targets into itself, and that is incorrect operation.
+				// Instead, we calculate the new position of that part of original range.
+				common.start = common.start._getCombined( b.sourcePosition, newTargetPosition );
+				common.end = common.end._getCombined( b.sourcePosition, newTargetPosition );
 
-				// 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.
-				// Without this we would have 3 or 4 operations and the transformation result would probably be not intuitive.
-				if ( rangeA.containsRange( rangeB ) && rangeA.containsPosition( b.targetPosition ) ) {
-					return [ a.clone() ];
-				}
-				// Mirror situation for the case above - now transformed range is wholly contained in the other
-				// operation's range and also targets to that range. Without this special treatment we would
-				// transform this operation into NoOperation, but this would not be compatible with the result
-				// generated by the special case above.
-				else if ( rangeB.containsRange( rangeA ) && rangeB.containsPosition( a.targetPosition ) ) {
-					return [
-						new MoveOperation(
-							a.sourcePosition._getCombined( b.sourcePosition, b.targetPosition ),
-							a.howMany,
-							a.targetPosition._getCombined( b.sourcePosition, b.targetPosition ),
-							a.baseVersion
-						)
-					];
-				}
+				ranges.push( common );
+			}
 
-				// All the other non-special cases are treated by generic algorithm below.
+			// Map transformed range(s) to operations and return them.
+			return ranges.map( ( range ) => {
+				return new AttributeOperation(
+					range,
+					a.oldAttr,
+					a.newAttr,
+					a.baseVersion
+				);
+			} );
+		}
+	},
+
+	MoveOperation: {
+		// Transforms MoveOperation `a` by InsertOperation `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.
+		InsertOperation( a, b, isStrong ) {
+			// Get target position from the state "after" nodes are inserted by InsertOperation.
+			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.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,
+					range.end.offset - range.start.offset,
+					Position.createFromPosition( newTargetPosition ),
+					a.baseVersion
+				);
+			} );
+		},
 
-				const differenceSet = rangeA.getDifference( rangeB );
-				const common = rangeA.getIntersection( rangeB );
+		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.
+		MoveOperation( a, b, isStrong ) {
+			// Special case when both move operations' target positions are inside nodes that are
+			// being moved by the other move operation. So in other words, we move ranges into inside of each other.
+			// This case can't be solved reasonably (on the other hand, it should not happen often).
+			if ( moveTargetIntoMovedRange( a, b ) && moveTargetIntoMovedRange( b, a ) ) {
+				// Instead of transforming operation, we return a reverse of the operation that we transform by.
+				// So when the results of this "transformation" will be applied, `b` MoveOperation will get reversed.
+				return [ b.getReversed() ];
+			}
 
-				// This will aggregate transformed ranges.
-				let ranges = [];
+			// Create ranges from MoveOperations properties.
+			const rangeA = Range.createFromPositionAndShift( a.sourcePosition, a.howMany );
+			const rangeB = Range.createFromPositionAndShift( b.sourcePosition, b.howMany );
 
-				// Get target position from the state "after" nodes specified by other MoveOperation are "detached".
-				const moveTargetPosition = b.targetPosition.getTransformedByDeletion( 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.
+			// Without this we would have 3 or 4 operations and the transformation result would probably be not intuitive.
+			if ( rangeA.containsRange( rangeB ) && rangeA.containsPosition( b.targetPosition ) ) {
+				return [ a.clone() ];
+			}
+			// Mirror situation for the case above - now transformed range is wholly contained in the other
+			// operation's range and also targets to that range. Without this special treatment we would
+			// transform this operation into NoOperation, but this would not be compatible with the result
+			// generated by the special case above.
+			else if ( rangeB.containsRange( rangeA ) && rangeB.containsPosition( a.targetPosition ) ) {
+				return [
+					new MoveOperation(
+						a.sourcePosition._getCombined( b.sourcePosition, b.targetPosition ),
+						a.howMany,
+						a.targetPosition._getCombined( b.sourcePosition, b.targetPosition ),
+						a.baseVersion
+					)
+				];
+			}
 
-				// First, we take care of that part of the range that is only modified by transformed operation.
-				for ( let i = 0; i < differenceSet.length; i++ ) {
-					// 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.
-					differenceSet[ i ].start = differenceSet[ i ].start.getTransformedByDeletion( b.sourcePosition, b.howMany );
-					differenceSet[ i ].end = differenceSet[ i ].end.getTransformedByDeletion( b.sourcePosition, b.howMany );
+			// All the other non-special cases are treated by generic algorithm below.
 
-					// MoveOperation pastes nodes into target position. We acknowledge this by proper transformation.
-					// Note that since we operate on transformed difference range, we should transform by
-					// previously transformed target position.
-					// Note that we do not use Position.getTransformedByMove on range boundaries because we need to
-					// transform by insertion a range as a whole, since newTargetPosition might be inside that range.
-					ranges = ranges.concat( differenceSet[ i ].getTransformedByInsertion( moveTargetPosition, b.howMany, true ) );
-				}
+			const differenceSet = rangeA.getDifference( rangeB );
+			const common = rangeA.getIntersection( rangeB );
 
-				// Then, we have to manage the common part of both move ranges.
-				// If MoveOperations has common range it can be one of two:
-				// * on the same tree level - it means that we move the same nodes into different places
-				// * on deeper tree level - it means that we move nodes that are inside moved nodes
-				// The operations are conflicting only if they try to move exactly same nodes, so only in the first case.
-				// So, we will handle common range if it is "deeper" or if transformed operation is more important.
-				let isDeeper = utils.compareArrays( b.sourcePosition.getParentPath(), a.sourcePosition.getParentPath() ) == utils.compareArrays.PREFIX;
-
-				if ( common !== null && ( isDeeper || isStrong ) ) {
-					// Here we do not need to worry that newTargetPosition is inside moved range, because that
-					// would mean that the MoveOperation targets into itself, and that is incorrect operation.
-					// Instead, we calculate the new position of that part of original range.
-					common.start = common.start._getCombined( b.sourcePosition, moveTargetPosition );
-					common.end = common.end._getCombined( b.sourcePosition, moveTargetPosition );
-
-					// We have to take care of proper range order.
-					// Note that both push, splice and unshift do the same if there are no ranges in the array.
-					if ( rangeB.end.isAfter( rangeA.end ) ) {
-						ranges.push( common );
-					} else if ( rangeB.start.isBefore( rangeA.start ) ) {
-						ranges.unshift( common );
-					} else {
-						ranges.splice( 1, 0, common );
-					}
-				}
+			// This will aggregate transformed ranges.
+			let ranges = [];
 
-				// At this point we transformed this operation's source ranges it means that nothing should be changed.
-				// But since we need to return an instance of Operation we return an array with NoOperation.
-				if ( ranges.length === 0 ) {
-					return [ new NoOperation( a.baseVersion ) ];
-				}
+			// Get target position from the state "after" nodes specified by other MoveOperation are "detached".
+			const moveTargetPosition = b.targetPosition.getTransformedByDeletion( b.sourcePosition, b.howMany );
 
-				// Target position also could be affected by the other MoveOperation. We will transform it.
-				let newTargetPosition = a.targetPosition.getTransformedByMove( b.sourcePosition, moveTargetPosition, b.howMany, !isStrong );
+			// First, we take care of that part of the range that is only modified by transformed operation.
+			for ( let i = 0; i < differenceSet.length; i++ ) {
+				// 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.
+				differenceSet[ i ].start = differenceSet[ i ].start.getTransformedByDeletion( b.sourcePosition, b.howMany );
+				differenceSet[ i ].end = differenceSet[ i ].end.getTransformedByDeletion( b.sourcePosition, b.howMany );
 
-				// Map transformed range(s) to operations and return them.
-				return ranges.reverse().map( ( range ) => {
-					return new MoveOperation(
-						range.start,
-						range.end.offset - range.start.offset,
-						newTargetPosition,
-						a.baseVersion
-					);
-				} );
+				// MoveOperation pastes nodes into target position. We acknowledge this by proper transformation.
+				// Note that since we operate on transformed difference range, we should transform by
+				// previously transformed target position.
+				// Note that we do not use Position.getTransformedByMove on range boundaries because we need to
+				// transform by insertion a range as a whole, since newTargetPosition might be inside that range.
+				ranges = ranges.concat( differenceSet[ i ].getTransformedByInsertion( moveTargetPosition, b.howMany, true ) );
 			}
-		}
-	};
-
-	return ( a, b, isStrong ) => {
-		let group;
-		let algorithm;
-
-		if ( a instanceof InsertOperation ) {
-			group = ot.InsertOperation;
-		} else if ( a instanceof AttributeOperation ) {
-			group = ot.AttributeOperation;
-		} else if ( a instanceof MoveOperation ) {
-			group = ot.MoveOperation;
-		} else {
-			algorithm = doNotUpdate;
-		}
 
-		if ( group ) {
-			if ( b instanceof InsertOperation ) {
-				algorithm = group.InsertOperation;
-			} else if ( b instanceof AttributeOperation ) {
-				algorithm = group.AttributeOperation;
-			} else if ( b instanceof MoveOperation ) {
-				algorithm = group.MoveOperation;
-			} else {
-				algorithm = doNotUpdate;
+			// Then, we have to manage the common part of both move ranges.
+			// If MoveOperations has common range it can be one of two:
+			// * on the same tree level - it means that we move the same nodes into different places
+			// * on deeper tree level - it means that we move nodes that are inside moved nodes
+			// The operations are conflicting only if they try to move exactly same nodes, so only in the first case.
+			// So, we will handle common range if it is "deeper" or if transformed operation is more important.
+			let isDeeper = utils.compareArrays( b.sourcePosition.getParentPath(), a.sourcePosition.getParentPath() ) == utils.compareArrays.PREFIX;
+
+			if ( common !== null && ( isDeeper || isStrong ) ) {
+				// Here we do not need to worry that newTargetPosition is inside moved range, because that
+				// would mean that the MoveOperation targets into itself, and that is incorrect operation.
+				// Instead, we calculate the new position of that part of original range.
+				common.start = common.start._getCombined( b.sourcePosition, moveTargetPosition );
+				common.end = common.end._getCombined( b.sourcePosition, moveTargetPosition );
+
+				// We have to take care of proper range order.
+				// Note that both push, splice and unshift do the same if there are no ranges in the array.
+				if ( rangeB.end.isAfter( rangeA.end ) ) {
+					ranges.push( common );
+				} else if ( rangeB.start.isBefore( rangeA.start ) ) {
+					ranges.unshift( common );
+				} else {
+					ranges.splice( 1, 0, common );
+				}
 			}
-		}
-
-		let transformed = algorithm( a, b, isStrong );
 
-		return updateBaseVersions( a.baseVersion, transformed );
-	};
+			// At this point we transformed this operation's source ranges it means that nothing should be changed.
+			// But since we need to return an instance of Operation we return an array with NoOperation.
+			if ( ranges.length === 0 ) {
+				return [ new NoOperation( a.baseVersion ) ];
+			}
 
-	// When we don't want to update an operation, we create and return a clone of it.
-	// Returns the operation in "unified format" - wrapped in an Array.
-	function doNotUpdate( operation ) {
-		return [ operation.clone() ];
+			// Target position also could be affected by the other MoveOperation. We will transform it.
+			let newTargetPosition = a.targetPosition.getTransformedByMove( b.sourcePosition, moveTargetPosition, b.howMany, !isStrong );
+
+			// Map transformed range(s) to operations and return them.
+			return ranges.reverse().map( ( range ) => {
+				return new MoveOperation(
+					range.start,
+					range.end.offset - range.start.offset,
+					newTargetPosition,
+					a.baseVersion
+				);
+			} );
+		}
+	}
+};
+
+function transform( a, b, isStrong ) {
+	let group;
+	let algorithm;
+
+	if ( a instanceof InsertOperation ) {
+		group = ot.InsertOperation;
+	} else if ( a instanceof AttributeOperation ) {
+		group = ot.AttributeOperation;
+	} else if ( a instanceof MoveOperation ) {
+		group = ot.MoveOperation;
+	} else {
+		algorithm = doNotUpdate;
 	}
 
-	// Takes an Array of operations and sets consecutive base versions for them, starting from given base version.
-	// Returns the passed array.
-	function updateBaseVersions( baseVersion, operations ) {
-		for ( let i = 0; i < operations.length; i++ ) {
-			operations[ i ].baseVersion = baseVersion + i + 1;
+	if ( group ) {
+		if ( b instanceof InsertOperation ) {
+			algorithm = group.InsertOperation;
+		} else if ( b instanceof AttributeOperation ) {
+			algorithm = group.AttributeOperation;
+		} else if ( b instanceof MoveOperation ) {
+			algorithm = group.MoveOperation;
+		} else {
+			algorithm = doNotUpdate;
 		}
-
-		return operations;
 	}
 
-	// Checks whether MoveOperation targetPosition is inside a node from the moved range of the other MoveOperation.
-	function moveTargetIntoMovedRange( a, b ) {
-		return a.targetPosition.getTransformedByDeletion( b.sourcePosition, b.howMany ) === null;
-	}
+	let transformed = algorithm( a, b, isStrong );
 
-	// 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.
-	function haveConflictingAttributes( a, b ) {
-		// Keeping in mind that newAttr or oldAttr might be null.
-		// We will retrieve the key from whichever parameter is set.
-		const keyA = ( a.newAttr || a.oldAttr ).key;
-		const keyB = ( b.newAttr || b.oldAttr ).key;
-
-		if ( keyA != keyB ) {
-			// Different keys - not conflicting.
-			return false;
-		}
+	return updateBaseVersions( a.baseVersion, transformed );
+}
 
-		if ( a.newAttr === null && b.newAttr === null ) {
-			// Both remove the attribute - not conflicting.
-			return false;
-		}
+// When we don't want to update an operation, we create and return a clone of it.
+// Returns the operation in "unified format" - wrapped in an Array.
+function doNotUpdate( operation ) {
+	return [ operation.clone() ];
+}
 
-		// Check if they set different value or one of them removes the attribute.
-		return ( a.newAttr === null && b.newAttr !== null ) ||
-			( a.newAttr !== null && b.newAttr === null ) ||
-			( !a.newAttr.isEqual( b.newAttr ) );
+// Takes an Array of operations and sets consecutive base versions for them, starting from given base version.
+// Returns the passed array.
+function updateBaseVersions( baseVersion, operations ) {
+	for ( let i = 0; i < operations.length; i++ ) {
+		operations[ i ].baseVersion = baseVersion + i + 1;
 	}
 
-	// Gets an array of Ranges and produces one Range out of it. The root of a new range will be same as
-	// the root of the first range in the array. If any of given ranges has different root than the first range,
-	// it will be discarded.
-	function joinRanges( ranges ) {
-		if ( ranges.length === 0 ) {
-			return null;
-		} else if ( ranges.length == 1 ) {
-			return ranges[ 0 ];
-		} else {
-			ranges[ 0 ].end = ranges[ ranges.length - 1 ].end;
+	return operations;
+}
+
+// Checks whether MoveOperation targetPosition is inside a node from the moved range of the other MoveOperation.
+function moveTargetIntoMovedRange( a, b ) {
+	return a.targetPosition.getTransformedByDeletion( b.sourcePosition, b.howMany ) === null;
+}
+
+// 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.
+function haveConflictingAttributes( a, b ) {
+	// Keeping in mind that newAttr or oldAttr might be null.
+	// We will retrieve the key from whichever parameter is set.
+	const keyA = ( a.newAttr || a.oldAttr ).key;
+	const keyB = ( b.newAttr || b.oldAttr ).key;
+
+	if ( keyA != keyB ) {
+		// Different keys - not conflicting.
+		return false;
+	}
 
-			return ranges[ 0 ];
-		}
+	if ( a.newAttr === null && b.newAttr === null ) {
+		// Both remove the attribute - not conflicting.
+		return false;
+	}
+
+	// Check if they set different value or one of them removes the attribute.
+	return ( a.newAttr === null && b.newAttr !== null ) ||
+		( a.newAttr !== null && b.newAttr === null ) ||
+		( !a.newAttr.isEqual( b.newAttr ) );
+}
+
+// Gets an array of Ranges and produces one Range out of it. The root of a new range will be same as
+// the root of the first range in the array. If any of given ranges has different root than the first range,
+// it will be discarded.
+function joinRanges( ranges ) {
+	if ( ranges.length === 0 ) {
+		return null;
+	} else if ( ranges.length == 1 ) {
+		return ranges[ 0 ];
+	} else {
+		ranges[ 0 ].end = ranges[ ranges.length - 1 ].end;
+
+		return ranges[ 0 ];
 	}
-} );
+}

+ 466 - 465
packages/ckeditor5-engine/src/treemodel/position.js

@@ -5,554 +5,555 @@
 
 'use strict';
 
-CKEDITOR.define( [ 'treemodel/rootelement', 'utils', 'ckeditorerror' ], ( RootElement, utils, CKEditorError ) => {
-	const SAME = 0;
-	const AFTER = 1;
-	const BEFORE = 2;
-	const DIFFERENT = 3;
+import RootElement from './rootelement.js';
+import CKEditorError from '../ckeditorerror.js';
+import arrayUtils from '../lib/lodash/array.js';
+import utils from '../utils.js';
 
+const SAME = 0;
+const AFTER = 1;
+const BEFORE = 2;
+const DIFFERENT = 3;
+
+/**
+ * Position in the tree. Position is always located before or after a node.
+ * See {@link #path} property for more information.
+ *
+ * @class treeModel.Position
+ */
+export default class Position {
 	/**
-	 * Position in the tree. Position is always located before or after a node.
-	 * See {@link #path} property for more information.
+	 * Creates a position.
 	 *
-	 * @class treeModel.Position
+	 * @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
 	 */
-	class Position {
-		/**
-		 * Creates a position.
-		 *
-		 * @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( 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 } );
-			}
-
+	constructor( root, path ) {
+		if ( !( root instanceof RootElement ) ) {
 			/**
-			 * Root element for the path. Note that this element can not have a parent.
+			 * Position root has to be an instance of RootElement.
 			 *
-			 * @type {treeModel.RootElement}
+			 * @error position-root-not-rootelement
+			 * @param root
 			 */
-			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:
-			 *
-			 * root
-			 *  |- p         Before: [ 0 ]       After: [ 1 ]
-			 *  |- ul        Before: [ 1 ]       After: [ 2 ]
-			 *     |- li     Before: [ 1, 0 ]    After: [ 1, 1 ]
-			 *     |  |- f   Before: [ 1, 0, 0 ] After: [ 1, 0, 1 ]
-			 *     |  |- o   Before: [ 1, 0, 1 ] After: [ 1, 0, 2 ]
-			 *     |  |- o   Before: [ 1, 0, 2 ] After: [ 1, 0, 3 ]
-			 *     |- li     Before: [ 1, 1 ]    After: [ 1, 2 ]
-			 *        |- b   Before: [ 1, 1, 0 ] After: [ 1, 1, 1 ]
-			 *        |- a   Before: [ 1, 1, 1 ] After: [ 1, 1, 2 ]
-			 *        |- r   Before: [ 1, 1, 2 ] After: [ 1, 1, 3 ]
-			 *
-			 * @type {Number[]}
-			 */
-			this.path = path;
+			throw new CKEditorError( 'position-root-not-rootelement: Position root has to be an instance of RootElement.', { root: root } );
 		}
 
 		/**
-		 * Node directly after the position.
+		 * Root element for the path. Note that this element can not have a parent.
 		 *
-		 * @readonly
-		 * @property {treeModel.Node}
+		 * @type {treeModel.RootElement}
 		 */
-		get nodeAfter() {
-			return this.parent.getChild( this.offset ) || null;
-		}
+		this.root = root;
 
-		/**
-		 * Node directly before the position.
-		 *
-		 * @readonly
-		 * @type {treeModel.Node}
-		 */
-		get nodeBefore() {
-			return this.parent.getChild( this.offset - 1 ) || null;
+		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 } );
 		}
 
 		/**
-		 * Offset at which the position is located in the {@link #parent}.
-		 *
-		 * @readonly
-		 * @property {Number} offset
+		 * Position of the node it the tree. For example:
+		 *
+		 * root
+		 *  |- p         Before: [ 0 ]       After: [ 1 ]
+		 *  |- ul        Before: [ 1 ]       After: [ 2 ]
+		 *     |- li     Before: [ 1, 0 ]    After: [ 1, 1 ]
+		 *     |  |- f   Before: [ 1, 0, 0 ] After: [ 1, 0, 1 ]
+		 *     |  |- o   Before: [ 1, 0, 1 ] After: [ 1, 0, 2 ]
+		 *     |  |- o   Before: [ 1, 0, 2 ] After: [ 1, 0, 3 ]
+		 *     |- li     Before: [ 1, 1 ]    After: [ 1, 2 ]
+		 *        |- b   Before: [ 1, 1, 0 ] After: [ 1, 1, 1 ]
+		 *        |- a   Before: [ 1, 1, 1 ] After: [ 1, 1, 2 ]
+		 *        |- r   Before: [ 1, 1, 2 ] After: [ 1, 1, 3 ]
+		 *
+		 * @type {Number[]}
 		 */
-		get offset() {
-			return utils.last( this.path );
-		}
+		this.path = path;
+	}
 
-		/**
-		 * Sets offset in the parent, which is the last element of the path.
-		 */
-		set offset( newOffset ) {
-			this.path[ this.path.length - 1 ] = newOffset;
-		}
+	/**
+	 * Node directly after the position.
+	 *
+	 * @readonly
+	 * @property {treeModel.Node}
+	 */
+	get nodeAfter() {
+		return this.parent.getChild( this.offset ) || null;
+	}
 
-		/**
-		 * Parent element of the position. The position is located at {@link #offset} in this element.
-		 *
-		 * @readonly
-		 * @property {treeModel.Element} parent
-		 */
-		get parent() {
-			let parent = this.root;
+	/**
+	 * Node directly before the position.
+	 *
+	 * @readonly
+	 * @type {treeModel.Node}
+	 */
+	get nodeBefore() {
+		return this.parent.getChild( this.offset - 1 ) || null;
+	}
 
-			let i, len;
+	/**
+	 * Offset at which the position is located in the {@link #parent}.
+	 *
+	 * @readonly
+	 * @property {Number} offset
+	 */
+	get offset() {
+		return arrayUtils.last( this.path );
+	}
 
-			for ( i = 0, len = this.path.length - 1; i < len; i++ ) {
-				parent = parent.getChild( this.path[ i ] );
-			}
+	/**
+	 * Sets offset in the parent, which is the last element of the path.
+	 */
+	set offset( newOffset ) {
+		this.path[ this.path.length - 1 ] = newOffset;
+	}
 
-			return parent;
+	/**
+	 * Parent element of the position. The position is located at {@link #offset} in this element.
+	 *
+	 * @readonly
+	 * @property {treeModel.Element} parent
+	 */
+	get parent() {
+		let parent = this.root;
+
+		let i, len;
+
+		for ( i = 0, len = this.path.length - 1; i < len; i++ ) {
+			parent = parent.getChild( this.path[ i ] );
 		}
 
-		/**
-		 * Checks whether this position is before or after given position.
-		 *
-		 * @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.
-		 */
-		compareWith( otherPosition ) {
-			if ( this.root != otherPosition.root ) {
-				return DIFFERENT;
-			}
+		return parent;
+	}
 
-			const result = utils.compareArrays( this.path, otherPosition.path );
+	/**
+	 * Checks whether this position is before or after given position.
+	 *
+	 * @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.
+	 */
+	compareWith( otherPosition ) {
+		if ( this.root != otherPosition.root ) {
+			return DIFFERENT;
+		}
 
-			switch ( result ) {
-				case utils.compareArrays.SAME:
-					return SAME;
+		const result = utils.compareArrays( this.path, otherPosition.path );
 
-				case utils.compareArrays.PREFIX:
-					return BEFORE;
+		switch ( result ) {
+			case utils.compareArrays.SAME:
+				return SAME;
 
-				case utils.compareArrays.EXTENSION:
-					return AFTER;
+			case utils.compareArrays.PREFIX:
+				return BEFORE;
 
-				default:
-					if ( this.path[ result ] < otherPosition.path[ result ] ) {
-						return BEFORE;
-					} else {
-						return AFTER;
-					}
-			}
-		}
+			case utils.compareArrays.EXTENSION:
+				return AFTER;
 
-		/**
-		 * 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.
-		 *
-		 * @returns {Number[]} Path to the parent.
-		 */
-		getParentPath() {
-			return this.path.slice( 0, -1 );
+			default:
+				if ( this.path[ result ] < otherPosition.path[ result ] ) {
+					return BEFORE;
+				} else {
+					return AFTER;
+				}
 		}
+	}
 
-		/**
-		 * Returns a new instance of Position with offset incremented by `shift` value.
-		 *
-		 * @param {Number} shift How position offset should get changed. Accepts negative values.
-		 * @returns {treeModel.Position} Shifted position.
-		 */
-		getShiftedBy( shift ) {
-			let shifted = Position.createFromPosition( this );
-
-			let offset = shifted.offset + shift;
-			shifted.offset = offset < 0 ? 0 : offset;
+	/**
+	 * 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.
+	 *
+	 * @returns {Number[]} Path to the parent.
+	 */
+	getParentPath() {
+		return this.path.slice( 0, -1 );
+	}
 
-			return shifted;
-		}
+	/**
+	 * Returns a new instance of Position with offset incremented by `shift` value.
+	 *
+	 * @param {Number} shift How position offset should get changed. Accepts negative values.
+	 * @returns {treeModel.Position} Shifted position.
+	 */
+	getShiftedBy( shift ) {
+		let shifted = Position.createFromPosition( this );
 
-		/**
-		 * 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 {treeModel.Position} deletePosition Position before the first removed node.
-		 * @param {Number} howMany How many nodes are removed.
-		 * @returns {treeModel.Position|null} Transformed position or `null`.
-		 */
-		getTransformedByDeletion( deletePosition, howMany ) {
-			let transformed = Position.createFromPosition( this );
+		let offset = shifted.offset + shift;
+		shifted.offset = offset < 0 ? 0 : offset;
 
-			// This position can't be affected if deletion was in a different root.
-			if ( this.root != deletePosition.root ) {
-				return transformed;
-			}
+		return shifted;
+	}
 
-			if ( utils.compareArrays( deletePosition.getParentPath(), this.getParentPath() ) == utils.compareArrays.SAME ) {
-				// If nodes are removed from the node that is pointed by this position...
-				if ( deletePosition.offset < this.offset ) {
-					// And are removed from before an offset of that position...
-					// Decrement the offset accordingly.
-					if ( deletePosition.offset + howMany > this.offset ) {
-						transformed.offset = deletePosition.offset;
-					} else {
-						transformed.offset -= howMany;
-					}
-				}
-			} else if ( utils.compareArrays( deletePosition.getParentPath(), this.getParentPath() ) == utils.compareArrays.PREFIX ) {
-				// If nodes are removed from a node that is on a path to this position...
-				const i = deletePosition.path.length - 1;
-
-				if ( deletePosition.offset < this.path[ i ] ) {
-					// And are removed from before next node of that path...
-					if ( deletePosition.offset + howMany > this.path[ i ] ) {
-						// If the next node of that path is removed return null
-						// because the node containing this position got removed.
-						return null;
-					} else {
-						// Otherwise, decrement index on that path.
-						transformed.path[ i ] -= howMany;
-					}
-				}
-			}
+	/**
+	 * 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 {treeModel.Position} deletePosition Position before the first removed node.
+	 * @param {Number} howMany How many nodes are removed.
+	 * @returns {treeModel.Position|null} Transformed position or `null`.
+	 */
+	getTransformedByDeletion( deletePosition, howMany ) {
+		let transformed = Position.createFromPosition( this );
 
+		// This position can't be affected if deletion was in a different root.
+		if ( this.root != deletePosition.root ) {
 			return transformed;
 		}
 
-		/**
-		 * Returns this position after being updated by inserting `howMany` nodes at `insertPosition`.
-		 *
-		 * @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 {treeModel.Position} Transformed position.
-		 */
-		getTransformedByInsertion( insertPosition, howMany, insertBefore ) {
-			let transformed = Position.createFromPosition( this );
-
-			// This position can't be affected if insertion was in a different root.
-			if ( this.root != insertPosition.root ) {
-				return transformed;
-			}
-
-			if ( utils.compareArrays( insertPosition.getParentPath(), this.getParentPath() ) == utils.compareArrays.SAME ) {
-				// If nodes are inserted in the node that is pointed by this position...
-				if ( insertPosition.offset < this.offset || ( insertPosition.offset == this.offset && insertBefore ) ) {
-					// And are inserted before an offset of that position...
-					// "Push" this positions offset.
-					transformed.offset += howMany;
+		if ( utils.compareArrays( deletePosition.getParentPath(), this.getParentPath() ) == utils.compareArrays.SAME ) {
+			// If nodes are removed from the node that is pointed by this position...
+			if ( deletePosition.offset < this.offset ) {
+				// And are removed from before an offset of that position...
+				// Decrement the offset accordingly.
+				if ( deletePosition.offset + howMany > this.offset ) {
+					transformed.offset = deletePosition.offset;
+				} else {
+					transformed.offset -= howMany;
 				}
-			} else if ( utils.compareArrays( insertPosition.getParentPath(), this.getParentPath() ) == utils.compareArrays.PREFIX ) {
-				// If nodes are inserted in a node that is on a path to this position...
-				const i = insertPosition.path.length - 1;
-
-				if ( insertPosition.offset <= this.path[ i ] ) {
-					// And are inserted before next node of that path...
-					// "Push" the index on that path.
-					transformed.path[ i ] += howMany;
+			}
+		} else if ( utils.compareArrays( deletePosition.getParentPath(), this.getParentPath() ) == utils.compareArrays.PREFIX ) {
+			// If nodes are removed from a node that is on a path to this position...
+			const i = deletePosition.path.length - 1;
+
+			if ( deletePosition.offset < this.path[ i ] ) {
+				// And are removed from before next node of that path...
+				if ( deletePosition.offset + howMany > this.path[ i ] ) {
+					// If the next node of that path is removed return null
+					// because the node containing this position got removed.
+					return null;
+				} else {
+					// Otherwise, decrement index on that path.
+					transformed.path[ i ] -= howMany;
 				}
 			}
-
-			return transformed;
 		}
 
-		/**
-		 * Returns this position after being updated by moving `howMany` attributes from `sourcePosition` to `targetPosition`.
-		 *
-		 * @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 {treeModel.Position} Transformed position.
-		 */
-		getTransformedByMove( sourcePosition, targetPosition, howMany, insertBefore ) {
-			// Moving a range removes nodes from their original position. We acknowledge this by proper transformation.
-			let transformed = this.getTransformedByDeletion( sourcePosition, howMany );
-
-			if ( transformed !== null ) {
-				// This position is not inside a removed node.
-				// Next step is to reflect pasting nodes, which might further affect the position.
-				transformed = transformed.getTransformedByInsertion( targetPosition, howMany, insertBefore );
-			} else {
-				// This position is inside a removed node. In this case, we are unable to simply transform it by range insertion.
-				// Instead, we calculate a combination of this position, move source position and target position.
-				transformed = this._getCombined( sourcePosition, targetPosition );
-			}
+		return transformed;
+	}
+
+	/**
+	 * Returns this position after being updated by inserting `howMany` nodes at `insertPosition`.
+	 *
+	 * @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 {treeModel.Position} Transformed position.
+	 */
+	getTransformedByInsertion( insertPosition, howMany, insertBefore ) {
+		let transformed = Position.createFromPosition( this );
 
+		// This position can't be affected if insertion was in a different root.
+		if ( this.root != insertPosition.root ) {
 			return transformed;
 		}
 
-		/**
-		 * Checks whether this position is after given position.
-		 *
-		 * **Note:** see {treeModel.Position#isBefore}.
-		 *
-		 * @param {treeModel.Position} otherPosition Position to compare with.
-		 * @returns {Boolean} True if this position is after given position.
-		 */
-		isAfter( otherPosition ) {
-			return this.compareWith( otherPosition ) == AFTER;
+		if ( utils.compareArrays( insertPosition.getParentPath(), this.getParentPath() ) == utils.compareArrays.SAME ) {
+			// If nodes are inserted in the node that is pointed by this position...
+			if ( insertPosition.offset < this.offset || ( insertPosition.offset == this.offset && insertBefore ) ) {
+				// And are inserted before an offset of that position...
+				// "Push" this positions offset.
+				transformed.offset += howMany;
+			}
+		} else if ( utils.compareArrays( insertPosition.getParentPath(), this.getParentPath() ) == utils.compareArrays.PREFIX ) {
+			// If nodes are inserted in a node that is on a path to this position...
+			const i = insertPosition.path.length - 1;
+
+			if ( insertPosition.offset <= this.path[ i ] ) {
+				// And are inserted before next node of that path...
+				// "Push" the index on that path.
+				transformed.path[ i ] += howMany;
+			}
 		}
 
-		/**
-		 * Checks whether this position is before given position.
-		 *
-		 * **Note:** watch out when using negation of the value returned by this method, because the negation will also
-		 * be `true` if positions are in different roots and you might not expect this. You should probably use
-		 * `a.isAfter( b ) || a.isEqual( b )` or `!a.isBefore( p ) && a.root == b.root` in most scenarios. If your
-		 * condition uses multiple `isAfter` and `isBefore` checks, build them so they do not use negated values, i.e.:
-		 *
-		 *  if ( a.isBefore( b ) && c.isAfter( d ) ) {
-		 *    // do A.
-		 *  } else {
-		 *    // do B.
-		 *  }
-		 *
-		 * or, if you have only one if-branch:
-		 *
-		 *  if ( !( a.isBefore( b ) && c.isAfter( d ) ) {
-		 *    // do B.
-		 *  }
-		 *
-		 * rather than:
-		 *
-		 *  if ( !a.isBefore( b ) || && !c.isAfter( d ) ) {
-		 *    // do B.
-		 *  } else {
-		 *    // do A.
-		 *  }
-		 *
-		 * @param {treeModel.Position} otherPosition Position to compare with.
-		 * @returns {Boolean} True if this position is before given position.
-		 */
-		isBefore( otherPosition ) {
-			return this.compareWith( otherPosition ) == BEFORE;
-		}
+		return transformed;
+	}
 
-		/**
-		 * Checks whether this position equals given position.
-		 *
-		 * @param {treeModel.Position} otherPosition Position to compare with.
-		 * @returns {Boolean} True if positions are same.
-		 */
-		isEqual( otherPosition ) {
-			return this.compareWith( otherPosition ) == SAME;
+	/**
+	 * Returns this position after being updated by moving `howMany` attributes from `sourcePosition` to `targetPosition`.
+	 *
+	 * @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 {treeModel.Position} Transformed position.
+	 */
+	getTransformedByMove( sourcePosition, targetPosition, howMany, insertBefore ) {
+		// Moving a range removes nodes from their original position. We acknowledge this by proper transformation.
+		let transformed = this.getTransformedByDeletion( sourcePosition, howMany );
+
+		if ( transformed !== null ) {
+			// This position is not inside a removed node.
+			// Next step is to reflect pasting nodes, which might further affect the position.
+			transformed = transformed.getTransformedByInsertion( targetPosition, howMany, insertBefore );
+		} else {
+			// This position is inside a removed node. In this case, we are unable to simply transform it by range insertion.
+			// Instead, we calculate a combination of this position, move source position and target position.
+			transformed = this._getCombined( sourcePosition, targetPosition );
 		}
 
-		/**
-		 * Checks whether this position is touching given position. Positions touch when there are no characters
-		 * or empty nodes in a range between them. Technically, those positions are not equal but in many cases
-		 * they are very similar or even indistinguishable when they touch.
-		 *
-		 * @param {treeModel.Position} otherPosition Position to compare with.
-		 * @returns {Boolean} True if positions touch.
-		 */
-		isTouching( otherPosition ) {
-			let left = null;
-			let right = null;
-			let compare = this.compareWith( otherPosition );
-
-			switch ( compare ) {
-				case SAME:
-					return true;
-
-				case BEFORE:
-					left = this;
-					right = otherPosition;
-					break;
-
-				case AFTER:
-					left = otherPosition;
-					right = this;
-					break;
-
-				default:
-					return false;
-			}
-
-			while ( left.path.length + right.path.length ) {
-				if ( left.isEqual( right ) ) {
-					return true;
-				}
-
-				if ( left.path.length > right.path.length ) {
-					if ( left.nodeAfter !== null ) {
-						return false;
-					}
+		return transformed;
+	}
 
-					left.path = left.path.slice( 0, -1 );
-					left.offset++;
-				} else {
-					if ( right.nodeBefore !== null ) {
-						return false;
-					}
+	/**
+	 * Checks whether this position is after given position.
+	 *
+	 * **Note:** see {treeModel.Position#isBefore}.
+	 *
+	 * @param {treeModel.Position} otherPosition Position to compare with.
+	 * @returns {Boolean} True if this position is after given position.
+	 */
+	isAfter( otherPosition ) {
+		return this.compareWith( otherPosition ) == AFTER;
+	}
 
-					right.path = right.path.slice( 0, -1 );
-				}
-			}
-		}
+	/**
+	 * Checks whether this position is before given position.
+	 *
+	 * **Note:** watch out when using negation of the value returned by this method, because the negation will also
+	 * be `true` if positions are in different roots and you might not expect this. You should probably use
+	 * `a.isAfter( b ) || a.isEqual( b )` or `!a.isBefore( p ) && a.root == b.root` in most scenarios. If your
+	 * condition uses multiple `isAfter` and `isBefore` checks, build them so they do not use negated values, i.e.:
+	 *
+	 *  if ( a.isBefore( b ) && c.isAfter( d ) ) {
+	 *    // do A.
+	 *  } else {
+	 *    // do B.
+	 *  }
+	 *
+	 * or, if you have only one if-branch:
+	 *
+	 *  if ( !( a.isBefore( b ) && c.isAfter( d ) ) {
+	 *    // do B.
+	 *  }
+	 *
+	 * rather than:
+	 *
+	 *  if ( !a.isBefore( b ) || && !c.isAfter( d ) ) {
+	 *    // do B.
+	 *  } else {
+	 *    // do A.
+	 *  }
+	 *
+	 * @param {treeModel.Position} otherPosition Position to compare with.
+	 * @returns {Boolean} True if this position is before given position.
+	 */
+	isBefore( otherPosition ) {
+		return this.compareWith( otherPosition ) == BEFORE;
+	}
 
-		/**
-		 * Creates a new position after given node.
-		 *
-		 * @param {treeModel.Node} node Node the position should be directly after.
-		 * @returns {treeModel.Position}
-		 */
-		static createAfter( node ) {
-			if ( !node.parent ) {
-				/**
-				 * You can not make position after root.
-				 *
-				 * @error position-after-root
-				 * @param {treeModel.Node} root
-				 */
-				throw new CKEditorError( 'position-after-root: You can not make position after root.', { root: node } );
-			}
+	/**
+	 * Checks whether this position equals given position.
+	 *
+	 * @param {treeModel.Position} otherPosition Position to compare with.
+	 * @returns {Boolean} True if positions are same.
+	 */
+	isEqual( otherPosition ) {
+		return this.compareWith( otherPosition ) == SAME;
+	}
 
-			return this.createFromParentAndOffset( node.parent, node.getIndex() + 1 );
+	/**
+	 * Checks whether this position is touching given position. Positions touch when there are no characters
+	 * or empty nodes in a range between them. Technically, those positions are not equal but in many cases
+	 * they are very similar or even indistinguishable when they touch.
+	 *
+	 * @param {treeModel.Position} otherPosition Position to compare with.
+	 * @returns {Boolean} True if positions touch.
+	 */
+	isTouching( otherPosition ) {
+		let left = null;
+		let right = null;
+		let compare = this.compareWith( otherPosition );
+
+		switch ( compare ) {
+			case SAME:
+				return true;
+
+			case BEFORE:
+				left = this;
+				right = otherPosition;
+				break;
+
+			case AFTER:
+				left = otherPosition;
+				right = this;
+				break;
+
+			default:
+				return false;
 		}
 
-		/**
-		 * Creates a new position before the given node.
-		 *
-		 * @param {treeModel.node} node Node the position should be directly before.
-		 * @returns {treeModel.Position}
-		 */
-		static createBefore( node ) {
-			if ( !node.parent ) {
-				/**
-				 * You can not make position before root.
-				 *
-				 * @error position-before-root
-				 * @param {treeModel.Node} root
-				 */
-				throw new CKEditorError( 'position-before-root: You can not make position before root.', { root: node } );
+		while ( left.path.length + right.path.length ) {
+			if ( left.isEqual( right ) ) {
+				return true;
 			}
 
-			return this.createFromParentAndOffset( node.parent, node.getIndex() );
-		}
-
-		/**
-		 * Creates a new position from the parent element and the offset in that element.
-		 *
-		 * @param {treeModel.Element} parent Position parent element.
-		 * @param {Number} offset Position offset.
-		 * @returns {treeModel.Position}
-		 */
-		static createFromParentAndOffset( parent, offset ) {
-			const path = parent.getPath();
+			if ( left.path.length > right.path.length ) {
+				if ( left.nodeAfter !== null ) {
+					return false;
+				}
 
-			path.push( offset );
+				left.path = left.path.slice( 0, -1 );
+				left.offset++;
+			} else {
+				if ( right.nodeBefore !== null ) {
+					return false;
+				}
 
-			return new this( parent.root, path );
+				right.path = right.path.slice( 0, -1 );
+			}
 		}
+	}
 
-		/**
-		 * Creates and returns a new instance of Position, which is equal to passed position.
-		 *
-		 * @param {treeModel.Position} position Position to be cloned.
-		 * @returns {treeModel.Position}
-		 */
-		static createFromPosition( position ) {
-			return new this( position.root, position.path.slice() );
+	/**
+	 * Creates a new position after given node.
+	 *
+	 * @param {treeModel.Node} node Node the position should be directly after.
+	 * @returns {treeModel.Position}
+	 */
+	static createAfter( node ) {
+		if ( !node.parent ) {
+			/**
+			 * You can not make position after root.
+			 *
+			 * @error position-after-root
+			 * @param {treeModel.Node} root
+			 */
+			throw new CKEditorError( 'position-after-root: You can not make position after root.', { root: node } );
 		}
 
-		/**
-		 * Returns a new position that is a combination of this position and given positions. The combined
-		 * position is this position transformed by moving a range starting at `from` to `to` position.
-		 * It is expected that this position is inside the moved range.
-		 *
-		 * In other words, this method in a smart way "cuts out" `source` path from this position and
-		 * injects `target` path in it's place, while doing necessary fixes in order to get a correct path.
-		 *
-		 * Example:
-		 *
-		 * 	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
-		 *
-		 * Explanation:
-		 *
-		 * We have a position `[ 2, 3, 1 ]` and move some nodes from `[ 2, 2 ]` to `[ 1, 1, 3 ]`. The original position
-		 * was inside moved nodes and now should point to the new place. The moved nodes will be after
-		 * positions `[ 1, 1, 3 ]`, `[ 1, 1, 4 ]`, `[ 1, 1, 5 ]`. Since our position was in the second moved node,
-		 * the transformed position will be in a sub-tree of a node at `[ 1, 1, 4 ]`. Looking at original path, we
-		 * took care of `[ 2, 3 ]` part of it. Now we have to add the rest of the original path to the transformed path.
-		 * Finally, the transformed position will point to `[ 1, 1, 4, 1 ]`.
-		 *
-		 * @protected
-		 * @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;
-
-			// The first part of a path to combined position is a path to the place where nodes were moved.
-			let combined = Position.createFromPosition( target );
-
-			// Then we have to update the rest of the path.
-
-			// Fix the offset because this position might be after `from` position and we have to reflect that.
-			combined.offset = combined.offset + this.path[ i ] - source.offset;
-
-			// Then, add the rest of the path.
-			// If this position is at the same level as `from` position nothing will get added.
-			combined.path = combined.path.concat( this.path.slice( i + 1 ) );
-
-			return combined;
-		}
+		return this.createFromParentAndOffset( node.parent, node.getIndex() + 1 );
 	}
 
 	/**
-	 * Flag for "is after" relation between Positions.
+	 * Creates a new position before the given node.
 	 *
-	 * @type {Number}
+	 * @param {treeModel.node} node Node the position should be directly before.
+	 * @returns {treeModel.Position}
 	 */
-	Position.AFTER = AFTER;
+	static createBefore( node ) {
+		if ( !node.parent ) {
+			/**
+			 * You can not make position before root.
+			 *
+			 * @error position-before-root
+			 * @param {treeModel.Node} root
+			 */
+			throw new CKEditorError( 'position-before-root: You can not make position before root.', { root: node } );
+		}
+
+		return this.createFromParentAndOffset( node.parent, node.getIndex() );
+	}
 
 	/**
-	 * Flag for "is before" relation between Positions.
+	 * Creates a new position from the parent element and the offset in that element.
 	 *
-	 * @type {Number}
+	 * @param {treeModel.Element} parent Position parent element.
+	 * @param {Number} offset Position offset.
+	 * @returns {treeModel.Position}
 	 */
-	Position.BEFORE = BEFORE;
+	static createFromParentAndOffset( parent, offset ) {
+		const path = parent.getPath();
+
+		path.push( offset );
+
+		return new this( parent.root, path );
+	}
 
 	/**
-	 * Flag for "are in different roots" relation between Positions.
+	 * Creates and returns a new instance of Position, which is equal to passed position.
 	 *
-	 * @type {Number}
+	 * @param {treeModel.Position} position Position to be cloned.
+	 * @returns {treeModel.Position}
 	 */
-	Position.DIFFERENT = DIFFERENT;
+	static createFromPosition( position ) {
+		return new this( position.root, position.path.slice() );
+	}
 
 	/**
-	 * Flag for "are same" relation between Positions.
+	 * Returns a new position that is a combination of this position and given positions. The combined
+	 * position is this position transformed by moving a range starting at `from` to `to` position.
+	 * It is expected that this position is inside the moved range.
+	 *
+	 * In other words, this method in a smart way "cuts out" `source` path from this position and
+	 * injects `target` path in it's place, while doing necessary fixes in order to get a correct path.
+	 *
+	 * Example:
+	 *
+	 * 	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
 	 *
-	 * @type {Number}
+	 * Explanation:
+	 *
+	 * We have a position `[ 2, 3, 1 ]` and move some nodes from `[ 2, 2 ]` to `[ 1, 1, 3 ]`. The original position
+	 * was inside moved nodes and now should point to the new place. The moved nodes will be after
+	 * positions `[ 1, 1, 3 ]`, `[ 1, 1, 4 ]`, `[ 1, 1, 5 ]`. Since our position was in the second moved node,
+	 * the transformed position will be in a sub-tree of a node at `[ 1, 1, 4 ]`. Looking at original path, we
+	 * took care of `[ 2, 3 ]` part of it. Now we have to add the rest of the original path to the transformed path.
+	 * Finally, the transformed position will point to `[ 1, 1, 4, 1 ]`.
+	 *
+	 * @protected
+	 * @param {treeModel.Position} source Beginning of the moved range.
+	 * @param {treeModel.Position} target Position where the range is moved.
+	 * @returns {treeModel.Position} Combined position.
 	 */
-	Position.SAME = SAME;
+	_getCombined( source, target ) {
+		const i = source.path.length - 1;
+
+		// The first part of a path to combined position is a path to the place where nodes were moved.
+		let combined = Position.createFromPosition( target );
+
+		// Then we have to update the rest of the path.
 
-	return Position;
-} );
+		// Fix the offset because this position might be after `from` position and we have to reflect that.
+		combined.offset = combined.offset + this.path[ i ] - source.offset;
+
+		// Then, add the rest of the path.
+		// If this position is at the same level as `from` position nothing will get added.
+		combined.path = combined.path.concat( this.path.slice( i + 1 ) );
+
+		return combined;
+	}
+}
+
+/**
+ * Flag for "is after" relation between Positions.
+ *
+ * @type {Number}
+ */
+Position.AFTER = AFTER;
+
+/**
+ * Flag for "is before" relation between Positions.
+ *
+ * @type {Number}
+ */
+Position.BEFORE = BEFORE;
+
+/**
+ * Flag for "are in different roots" relation between Positions.
+ *
+ * @type {Number}
+ */
+Position.DIFFERENT = DIFFERENT;
+
+/**
+ * Flag for "are same" relation between Positions.
+ *
+ * @type {Number}
+ */
+Position.SAME = SAME;

+ 131 - 138
packages/ckeditor5-engine/src/treemodel/positioniterator.js

@@ -5,174 +5,167 @@
 
 'use strict';
 
-CKEDITOR.define( [
-	'treemodel/character',
-	'treemodel/element',
-	'treemodel/position'
-], ( Character, Element, Position ) => {
-	const ELEMENT_ENTER = 0;
-	const ELEMENT_LEAVE = 1;
-	const CHARACTER = 2;
+import Character from './character.js';
+import Element from './element.js';
+import Position from './position.js';
 
+const ELEMENT_ENTER = 0;
+const ELEMENT_LEAVE = 1;
+const CHARACTER = 2;
+
+/**
+ * Position iterator class. It allows to iterate forward and backward over the tree document.
+ *
+ * @class treeModel.PositionIterator
+ */
+export default class PositionIterator {
 	/**
-	 * Position iterator class. It allows to iterate forward and backward over the tree document.
+	 * Creates a range iterator.
 	 *
-	 * @class treeModel.PositionIterator
+	 * @param {treeModel.Range} [boundaries] Range to define boundaries of the iterator.
+	 * @param {treeModel.Position} [iteratorPosition] Starting position.
+	 * @constructor
 	 */
-	class PositionIterator {
+	constructor( boundaries, iteratorPosition ) {
+		if ( boundaries instanceof Position ) {
+			this.position = boundaries;
+		} else {
+			this.boundaries =  boundaries;
+			this.position = iteratorPosition || boundaries.start;
+		}
+
 		/**
-		 * Creates a range iterator.
+		 * Iterator position.
 		 *
-		 * @param {treeModel.Range} [boundaries] Range to define boundaries of the iterator.
-		 * @param {treeModel.Position} [iteratorPosition] Starting position.
-		 * @constructor
+		 * @property {treeModel.Position} position
 		 */
-		constructor( boundaries, iteratorPosition ) {
-			if ( boundaries instanceof Position ) {
-				this.position = boundaries;
-			} else {
-				this.boundaries =  boundaries;
-				this.position = iteratorPosition || boundaries.start;
-			}
-
-			/**
-			 * Iterator position.
-			 *
-			 * @property {treeModel.Position} position
-			 */
-
-			/**
-			 * Iterator boundaries.
-			 *
-			 * When the {@link #next} method is called on the end boundary or the {@link #previous} method
-			 * on the start boundary, then `{ done: true }` is returned.
-			 *
-			 * If boundaries are not defined they are set before first and after last child of the root node.
-			 *
-			 * @property {treeModel.Range} boundaries
-			 */
-		}
 
 		/**
-		 * Moves the {@link #position} to the next position and returns the enctountered value.
+		 * Iterator boundaries.
 		 *
-		 * @returns {Object} Value between the previous and the new {@link #position}.
-		 * @returns {Boolean} return.done True if iterator is done.
-		 * @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 {treeModel.Node} return.value.node Encountered node.
+		 * When the {@link #next} method is called on the end boundary or the {@link #previous} method
+		 * on the start boundary, then `{ done: true }` is returned.
+		 *
+		 * If boundaries are not defined they are set before first and after last child of the root node.
+		 *
+		 * @property {treeModel.Range} boundaries
 		 */
-		next() {
-			const position = this.position;
-			const parent = position.parent;
+	}
 
-			// Ugh... added here because of circular deps in AMD ;<.
-			Element = CKEDITOR.require( 'treemodel/element' );
+	/**
+	 * Moves the {@link #position} to the next position and returns the enctountered value.
+	 *
+	 * @returns {Object} Value between the previous and the new {@link #position}.
+	 * @returns {Boolean} return.done True if iterator is done.
+	 * @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 {treeModel.Node} return.value.node Encountered node.
+	 */
+	next() {
+		const position = this.position;
+		const parent = position.parent;
 
-			// We are at the end of the root.
-			if ( parent.parent === null && position.offset === parent.getChildCount() ) {
-				return { done: true };
-			}
+		// We are at the end of the root.
+		if ( parent.parent === null && position.offset === parent.getChildCount() ) {
+			return { done: true };
+		}
 
-			if ( this.boundaries && position.isEqual( this.boundaries.end ) ) {
-				return { done: true };
-			}
+		if ( this.boundaries && position.isEqual( this.boundaries.end ) ) {
+			return { done: true };
+		}
 
-			const nodeAfter = position.nodeAfter;
+		const nodeAfter = position.nodeAfter;
 
-			if ( nodeAfter instanceof Element ) {
-				this.position = Position.createFromParentAndOffset( nodeAfter, 0 );
+		if ( nodeAfter instanceof Element ) {
+			this.position = Position.createFromParentAndOffset( nodeAfter, 0 );
 
-				return formatReturnValue( ELEMENT_ENTER, nodeAfter );
-			} else if ( nodeAfter instanceof Character ) {
-				this.position = Position.createFromParentAndOffset( parent, position.offset + 1 );
+			return formatReturnValue( ELEMENT_ENTER, nodeAfter );
+		} else if ( nodeAfter instanceof Character ) {
+			this.position = Position.createFromParentAndOffset( parent, position.offset + 1 );
 
-				return formatReturnValue( CHARACTER, nodeAfter );
-			} else {
-				this.position = Position.createFromParentAndOffset( parent.parent, parent.getIndex() + 1 );
+			return formatReturnValue( CHARACTER, nodeAfter );
+		} else {
+			this.position = Position.createFromParentAndOffset( parent.parent, parent.getIndex() + 1 );
 
-				return formatReturnValue( ELEMENT_LEAVE, this.position.nodeBefore );
-			}
+			return formatReturnValue( ELEMENT_LEAVE, this.position.nodeBefore );
 		}
+	}
 
-		/**
-		 * Moves the {@link #position} to the previous position and returns the encountered value.
-		 *
-		 * @returns {Object} Value between the previous and the new {@link #position}.
-		 * @returns {Boolean} return.done True if iterator is done.
-		 * @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 {treeModel.Node} return.value.node Scanned node.
-		 */
-		previous() {
-			const position = this.position;
-			const parent = position.parent;
+	/**
+	 * Moves the {@link #position} to the previous position and returns the encountered value.
+	 *
+	 * @returns {Object} Value between the previous and the new {@link #position}.
+	 * @returns {Boolean} return.done True if iterator is done.
+	 * @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 {treeModel.Node} return.value.node Scanned node.
+	 */
+	previous() {
+		const position = this.position;
+		const parent = position.parent;
 
-			// We are at the beginning of the root.
-			if ( parent.parent === null && position.offset === 0 ) {
-				return { done: true };
-			}
+		// We are at the beginning of the root.
+		if ( parent.parent === null && position.offset === 0 ) {
+			return { done: true };
+		}
 
-			if ( this.boundaries && position.isEqual( this.boundaries.start ) ) {
-				return { done: true };
-			}
+		if ( this.boundaries && position.isEqual( this.boundaries.start ) ) {
+			return { done: true };
+		}
 
-			const nodeBefore = position.nodeBefore;
+		const nodeBefore = position.nodeBefore;
 
-			if ( nodeBefore instanceof Element ) {
-				this.position = Position.createFromParentAndOffset( nodeBefore, nodeBefore.getChildCount() );
+		if ( nodeBefore instanceof Element ) {
+			this.position = Position.createFromParentAndOffset( nodeBefore, nodeBefore.getChildCount() );
 
-				return formatReturnValue( ELEMENT_LEAVE, nodeBefore );
-			} else if ( nodeBefore instanceof Character ) {
-				this.position = Position.createFromParentAndOffset( parent, position.offset - 1 );
+			return formatReturnValue( ELEMENT_LEAVE, nodeBefore );
+		} else if ( nodeBefore instanceof Character ) {
+			this.position = Position.createFromParentAndOffset( parent, position.offset - 1 );
 
-				return formatReturnValue( CHARACTER, nodeBefore );
-			} else {
-				this.position = Position.createFromParentAndOffset( parent.parent, parent.getIndex() );
+			return formatReturnValue( CHARACTER, nodeBefore );
+		} else {
+			this.position = Position.createFromParentAndOffset( parent.parent, parent.getIndex() );
 
-				return formatReturnValue( ELEMENT_ENTER, this.position.nodeAfter );
-			}
+			return formatReturnValue( ELEMENT_ENTER, this.position.nodeAfter );
 		}
 	}
+}
+
+function formatReturnValue( type, node ) {
+	return {
+		done: false,
+		value: {
+			type: type,
+			node: node
+		}
+	};
+}
 
-	function formatReturnValue( type, node ) {
-		return {
-			done: false,
-			value: {
-				type: type,
-				node: node
-			}
-		};
-	}
-
-	/**
-	 * Flag for character.
-	 *
-	 * @static
-	 * @readonly
-	 * @property {Number}
-	 */
-	PositionIterator.CHARACTER = CHARACTER;
-
-	/**
-	 * Flag for entering element.
-	 *
-	 * @static
-	 * @readonly
-	 * @property {Number}
-	 */
-	PositionIterator.ELEMENT_ENTER = ELEMENT_ENTER;
+/**
+ * Flag for character.
+ *
+ * @static
+ * @readonly
+ * @property {Number}
+ */
+PositionIterator.CHARACTER = CHARACTER;
 
-	/**
-	 * Flag for leaving element.
-	 *
-	 * @static
-	 * @readonly
-	 * @property {Number}
-	 */
-	PositionIterator.ELEMENT_LEAVE = ELEMENT_LEAVE;
+/**
+ * Flag for entering element.
+ *
+ * @static
+ * @readonly
+ * @property {Number}
+ */
+PositionIterator.ELEMENT_ENTER = ELEMENT_ENTER;
 
-	return PositionIterator;
-} );
+/**
+ * Flag for leaving element.
+ *
+ * @static
+ * @readonly
+ * @property {Number}
+ */
+PositionIterator.ELEMENT_LEAVE = ELEMENT_LEAVE;

+ 358 - 358
packages/ckeditor5-engine/src/treemodel/range.js

@@ -5,414 +5,414 @@
 
 'use strict';
 
-CKEDITOR.define( [ 'treemodel/position', 'treemodel/positioniterator', 'utils' ], ( Position, PositionIterator, utils ) => {
+import Position from './position.js';
+import PositionIterator from './positioniterator.js';
+import utils from '../utils.js';
+
+/**
+ * Range class. Range is iterable.
+ *
+ * @class treeModel.Range
+ */
+export default class Range {
 	/**
-	 * Range class. Range is iterable.
+	 * Creates a range spanning from `start` position to `end` position.
+	 * **Note:** Constructor creates it's own {@link treeModel.Position} instances basing on passed values.
 	 *
-	 * @class treeModel.Range
+	 * @param {treeModel.Position} start Start position.
+	 * @param {treeModel.Position} end End position.
+	 * @constructor
 	 */
-	class Range {
+	constructor( start, end ) {
 		/**
-		 * Creates a range spanning from `start` position to `end` position.
-		 * **Note:** Constructor creates it's own {@link treeModel.Position} instances basing on passed values.
+		 * Start position.
 		 *
-		 * @param {treeModel.Position} start Start position.
-		 * @param {treeModel.Position} end End position.
-		 * @constructor
+		 * @property {treeModel.Position}
 		 */
-		constructor( start, end ) {
-			/**
-			 * Start position.
-			 *
-			 * @property {treeModel.Position}
-			 */
-			this.start = Position.createFromPosition( start );
-
-			/**
-			 * End position.
-			 *
-			 * @property {treeModel.Position}
-			 */
-			this.end = Position.createFromPosition( end );
-		}
+		this.start = Position.createFromPosition( start );
 
 		/**
-		 * Returns whether the range is collapsed, that is it start and end positions are equal.
+		 * End position.
 		 *
-		 * @property {Boolean}
+		 * @property {treeModel.Position}
 		 */
-		get isCollapsed() {
-			return this.start.isEqual( this.end );
-		}
+		this.end = Position.createFromPosition( end );
+	}
 
-		/**
-		 * Returns whether this range is flat, that is if start position and end position are in the same parent.
-		 *
-		 * @returns {Boolean}
-		 */
-		get isFlat() {
-			return this.start.parent === this.end.parent;
-		}
+	/**
+	 * Returns whether the range is collapsed, that is it start and end positions are equal.
+	 *
+	 * @property {Boolean}
+	 */
+	get isCollapsed() {
+		return this.start.isEqual( this.end );
+	}
 
-		/**
-		 * Range root element. Equals to the root of start position (which should be same as root of end position).
-		 *
-		 * @property {treeModel.RootElement}
-		 */
-		get root() {
-			return this.start.root;
-		}
+	/**
+	 * Returns whether this range is flat, that is if start position and end position are in the same parent.
+	 *
+	 * @returns {Boolean}
+	 */
+	get isFlat() {
+		return this.start.parent === this.end.parent;
+	}
 
-		/**
-		 * Range iterator.
-		 *
-		 * @see treeModel.PositionIterator
-		 */
-		[ Symbol.iterator ]() {
-			return new PositionIterator( this );
-		}
+	/**
+	 * Range root element. Equals to the root of start position (which should be same as root of end position).
+	 *
+	 * @property {treeModel.RootElement}
+	 */
+	get root() {
+		return this.start.root;
+	}
 
-		/**
-		 * Checks whether this contains given {@link treeModel.Position position}.
-		 *
-		 * @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 );
-		}
+	/**
+	 * Range iterator.
+	 *
+	 * @see treeModel.PositionIterator
+	 */
+	[ Symbol.iterator ]() {
+		return new PositionIterator( this );
+	}
 
-		/**
-		 * Checks whether this range contains given {@link treeModel.Range 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 );
-		}
+	/**
+	 * Checks whether this contains given {@link treeModel.Position position}.
+	 *
+	 * @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 );
+	}
 
-		/**
-		 * 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( 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( 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( 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 {treeModel.Range} otherRange Range to differentiate against.
-		 * @returns {Array.<treeModel.Range>} The difference between ranges.
-		 */
-		getDifference( otherRange ) {
-			const ranges = [];
-
-			if ( this.isIntersecting( otherRange ) ) {
-				// Ranges intersect.
-
-				if ( this.containsPosition( otherRange.start ) ) {
-					// Given range start is inside this range. This means that we have to
-					// add shrunken range - from the start to the middle of this range.
-					ranges.push( new Range( this.start, otherRange.start ) );
-				}
-
-				if ( this.containsPosition( otherRange.end ) ) {
-					// Given range end is inside this range. This means that we have to
-					// add shrunken range - from the middle of this range to the end.
-					ranges.push( new Range( otherRange.end, this.end ) );
-				}
-			} else {
-				// Ranges do not intersect, return the original range.
-				ranges.push( Range.createFromRange( this ) );
+	/**
+	 * Checks whether this range contains given {@link treeModel.Range 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 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( 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( 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( 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 {treeModel.Range} otherRange Range to differentiate against.
+	 * @returns {Array.<treeModel.Range>} The difference between ranges.
+	 */
+	getDifference( otherRange ) {
+		const ranges = [];
+
+		if ( this.isIntersecting( otherRange ) ) {
+			// Ranges intersect.
+
+			if ( this.containsPosition( otherRange.start ) ) {
+				// Given range start is inside this range. This means that we have to
+				// add shrunken range - from the start to the middle of this range.
+				ranges.push( new Range( this.start, otherRange.start ) );
 			}
 
-			return ranges;
+			if ( this.containsPosition( otherRange.end ) ) {
+				// Given range end is inside this range. This means that we have to
+				// add shrunken range - from the middle of this range to the end.
+				ranges.push( new Range( otherRange.end, this.end ) );
+			}
+		} else {
+			// Ranges do not intersect, return the original range.
+			ranges.push( Range.createFromRange( this ) );
 		}
 
-		/**
-		 * 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( 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( root, [ 3 ] ), new Position( root, [ 5 ] ) );
-		 *		transformed = range.getIntersection( otherRange ); // range from [ 3 ] to [ 4, 0, 1 ]
-		 *
-		 * @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.isIntersecting( otherRange ) ) {
-				// Ranges intersect, so a common range will be returned.
-				// At most, it will be same as this range.
-				let commonRangeStart = this.start;
-				let commonRangeEnd = this.end;
-
-				if ( this.containsPosition( otherRange.start ) ) {
-					// Given range start is inside this range. This means thaNt we have to
-					// shrink common range to the given range start.
-					commonRangeStart = otherRange.start;
-				}
-
-				if ( this.containsPosition( otherRange.end ) ) {
-					// Given range end is inside this range. This means that we have to
-					// shrink common range to the given range end.
-					commonRangeEnd = otherRange.end;
-				}
-
-				return new Range( commonRangeStart, commonRangeEnd );
+		return ranges;
+	}
+
+	/**
+	 * 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( 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( root, [ 3 ] ), new Position( root, [ 5 ] ) );
+	 *		transformed = range.getIntersection( otherRange ); // range from [ 3 ] to [ 4, 0, 1 ]
+	 *
+	 * @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.isIntersecting( otherRange ) ) {
+			// Ranges intersect, so a common range will be returned.
+			// At most, it will be same as this range.
+			let commonRangeStart = this.start;
+			let commonRangeEnd = this.end;
+
+			if ( this.containsPosition( otherRange.start ) ) {
+				// Given range start is inside this range. This means thaNt we have to
+				// shrink common range to the given range start.
+				commonRangeStart = otherRange.start;
+			}
+
+			if ( this.containsPosition( otherRange.end ) ) {
+				// Given range end is inside this range. This means that we have to
+				// shrink common range to the given range end.
+				commonRangeEnd = otherRange.end;
 			}
 
-			// Ranges do not intersect, so they do not have common part.
-			return null;
+			return new Range( commonRangeStart, commonRangeEnd );
 		}
 
-		/**
-		 * Computes and returns the smallest set of {@link #isFlat flat} ranges, that covers this range in whole.
-		 * Assuming that tree model model structure is ("[" and "]" are range boundaries):
-		 *
-		 *		root                                                            root
-		 *		 |- element DIV                         DIV             P2              P3             DIV
-		 *		 |   |- element H                   H        P1        f o o           b a r       H         P4
-		 *		 |   |   |- "fir[st"             fir[st     lorem                               se]cond     ipsum
-		 *		 |   |- element P1
-		 *		 |   |   |- "lorem"                                              ||
-		 *		 |- element P2                                                   ||
-		 *		 |   |- "foo"                                                    VV
-		 *		 |- element P3
-		 *		 |   |- "bar"                                                   root
-		 *		 |- element DIV                         DIV             [P2             P3]             DIV
-		 *		 |   |- element H                   H       [P1]       f o o           b a r        H         P4
-		 *		 |   |   |- "se]cond"            fir[st]    lorem                               [se]cond     ipsum
-		 *		 |   |- element P4
-		 *		 |   |   |- "ipsum"
-		 *
-		 * Flat ranges for range `( [ 0, 0, 3 ], [ 3, 0, 2 ] )` would be:
-		 *
-		 *		( [ 0, 0, 3 ], [ 0, 0, 5 ] ) = "st"
-		 *		( [ 0, 1 ], [ 0, 2 ] ) = element P1
-		 *		( [ 1 ], [ 3 ] ) = element P2, element P3
-		 *		( [ 3, 0, 0 ], [ 3, 0, 2 ] ) = "se"
-		 *
-		 * **Note:** this method is not returning flat ranges that contain no nodes. It may also happen that not-collapsed
-		 * range will return an empty array of flat ranges.
-		 *
-		 * @returns {Array.<treeModel.Range>} Array of flat ranges.
-		 */
-		getMinimalFlatRanges() {
-			let ranges = [];
+		// Ranges do not intersect, so they do not have common part.
+		return null;
+	}
 
-			// We find on which tree-level start and end have the lowest common ancestor
-			let cmp = utils.compareArrays( this.start.path, this.end.path );
-			let diffAt = ( cmp < 0 ) ? Math.min( this.start.path.length, this.end.path.length ) : cmp;
+	/**
+	 * Computes and returns the smallest set of {@link #isFlat flat} ranges, that covers this range in whole.
+	 * Assuming that tree model model structure is ("[" and "]" are range boundaries):
+	 *
+	 *		root                                                            root
+	 *		 |- element DIV                         DIV             P2              P3             DIV
+	 *		 |   |- element H                   H        P1        f o o           b a r       H         P4
+	 *		 |   |   |- "fir[st"             fir[st     lorem                               se]cond     ipsum
+	 *		 |   |- element P1
+	 *		 |   |   |- "lorem"                                              ||
+	 *		 |- element P2                                                   ||
+	 *		 |   |- "foo"                                                    VV
+	 *		 |- element P3
+	 *		 |   |- "bar"                                                   root
+	 *		 |- element DIV                         DIV             [P2             P3]             DIV
+	 *		 |   |- element H                   H       [P1]       f o o           b a r        H         P4
+	 *		 |   |   |- "se]cond"            fir[st]    lorem                               [se]cond     ipsum
+	 *		 |   |- element P4
+	 *		 |   |   |- "ipsum"
+	 *
+	 * Flat ranges for range `( [ 0, 0, 3 ], [ 3, 0, 2 ] )` would be:
+	 *
+	 *		( [ 0, 0, 3 ], [ 0, 0, 5 ] ) = "st"
+	 *		( [ 0, 1 ], [ 0, 2 ] ) = element P1
+	 *		( [ 1 ], [ 3 ] ) = element P2, element P3
+	 *		( [ 3, 0, 0 ], [ 3, 0, 2 ] ) = "se"
+	 *
+	 * **Note:** this method is not returning flat ranges that contain no nodes. It may also happen that not-collapsed
+	 * range will return an empty array of flat ranges.
+	 *
+	 * @returns {Array.<treeModel.Range>} Array of flat ranges.
+	 */
+	getMinimalFlatRanges() {
+		let ranges = [];
 
-			let pos = Position.createFromPosition( this.start );
+		// We find on which tree-level start and end have the lowest common ancestor
+		let cmp = utils.compareArrays( this.start.path, this.end.path );
+		let diffAt = ( cmp < 0 ) ? Math.min( this.start.path.length, this.end.path.length ) : cmp;
 
-			// go up
-			while ( pos.path.length > diffAt + 1 ) {
-				let howMany = pos.parent.getChildCount() - pos.offset;
+		let pos = Position.createFromPosition( this.start );
 
-				if ( howMany !== 0 ) {
-					ranges.push( new Range( pos, pos.getShiftedBy( howMany ) ) );
-				}
+		// go up
+		while ( pos.path.length > diffAt + 1 ) {
+			let howMany = pos.parent.getChildCount() - pos.offset;
 
-				pos.path = pos.path.slice( 0, -1 );
-				pos.offset++;
+			if ( howMany !== 0 ) {
+				ranges.push( new Range( pos, pos.getShiftedBy( howMany ) ) );
 			}
 
-			// go down
-			while ( pos.path.length <= this.end.path.length ) {
-				let offset = this.end.path[ pos.path.length - 1 ];
-				let howMany = offset - pos.offset;
+			pos.path = pos.path.slice( 0, -1 );
+			pos.offset++;
+		}
 
-				if ( howMany !== 0 ) {
-					ranges.push( new Range( pos, pos.getShiftedBy( howMany ) ) );
-				}
+		// go down
+		while ( pos.path.length <= this.end.path.length ) {
+			let offset = this.end.path[ pos.path.length - 1 ];
+			let howMany = offset - pos.offset;
 
-				pos.offset = offset;
-				pos.path.push( 0 );
+			if ( howMany !== 0 ) {
+				ranges.push( new Range( pos, pos.getShiftedBy( howMany ) ) );
 			}
 
-			return ranges;
+			pos.offset = offset;
+			pos.path.push( 0 );
 		}
 
-		/**
-		 * 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:** 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 {Iterable.<treeModel.Node>}
-		 */
-		*getAllNodes() {
-			for ( let value of this ) {
-				if ( value.type != PositionIterator.ELEMENT_LEAVE ) {
-					yield value.node;
-				}
+		return ranges;
+	}
+
+	/**
+	 * 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:** 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 {Iterable.<treeModel.Node>}
+	 */
+	*getAllNodes() {
+		for ( let value of this ) {
+			if ( value.type != PositionIterator.ELEMENT_LEAVE ) {
+				yield value.node;
 			}
 		}
+	}
 
-		/**
-		 * Returns an iterator that iterates over all {@link treeModel.Node nodes} that are top-level nodes in this range
-		 * and returns them. A node is a top-level node when it is in the range but it's parent is not. In other words,
-		 * this function splits the range into separate sub-trees and iterates over their roots.
-		 *
-		 * @returns {Iterable.<treeModel.Node>}
-		 */
-		*getTopLevelNodes() {
-			let flatRanges = this.getMinimalFlatRanges();
-
-			for ( let range of flatRanges ) {
-				let node = range.start.nodeAfter;
-				let offset = range.end.offset - range.start.offset;
+	/**
+	 * Returns an iterator that iterates over all {@link treeModel.Node nodes} that are top-level nodes in this range
+	 * and returns them. A node is a top-level node when it is in the range but it's parent is not. In other words,
+	 * this function splits the range into separate sub-trees and iterates over their roots.
+	 *
+	 * @returns {Iterable.<treeModel.Node>}
+	 */
+	*getTopLevelNodes() {
+		let flatRanges = this.getMinimalFlatRanges();
 
-				for ( let i = 0; i < offset; i++ ) {
-					yield node;
-					node = node.nextSibling;
-				}
-			}
-		}
+		for ( let range of flatRanges ) {
+			let node = range.start.nodeAfter;
+			let offset = range.end.offset - range.start.offset;
 
-		/**
-		 * 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( 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( 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( root, [ 3, 2 ] ), 4, true );
-		 *		// transformed array has one range which is equal to `range`. This is because of spreadOnlyOnSameLevel flag.
-		 *
-		 * @param {treeModel.Position} insertPosition Position where nodes are inserted.
-		 * @param {Number} howMany How many nodes are inserted.
-		 * @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.
-			const areOnSameLevel = utils.compareArrays( this.start.getParentPath(), this.end.getParentPath() ) == utils.compareArrays.SAME &&
-				utils.compareArrays( this.start.getParentPath(), insertPosition.getParentPath() ) == utils.compareArrays.SAME;
-
-			if ( this.containsPosition( insertPosition ) && ( !spreadOnlyOnSameLevel || areOnSameLevel ) ) {
-				// Range has to be spread. The first part is from original start to the spread point.
-				// The other part is from spread point to the original end, but transformed by
-				// insertion to reflect insertion changes.
-
-				return [
-					new Range( this.start, insertPosition ),
-					new Range(
-						insertPosition.getTransformedByInsertion( insertPosition, howMany, true ),
-						this.end.getTransformedByInsertion( insertPosition, howMany, true )
-					)
-				];
-			} else {
-				// If insertion is not inside the range, simply transform range boundaries (positions) by the insertion.
-				// Both, one or none of them might be affected by the insertion.
-
-				const range = Range.createFromRange( this );
-
-				range.start = range.start.getTransformedByInsertion( insertPosition, howMany, true );
-				range.end = range.end.getTransformedByInsertion( insertPosition, howMany, false );
-
-				return [ range ];
+			for ( let i = 0; i < offset; i++ ) {
+				yield node;
+				node = node.nextSibling;
 			}
 		}
+	}
 
-		/**
-		 * Two ranges equal if their start and end positions equal.
-		 *
-		 * @param {treeModel.Range} otherRange Range to compare with.
-		 * @returns {Boolean} True if ranges equal.
-		 */
-		isEqual( otherRange ) {
-			return this.start.isEqual( otherRange.start ) && this.end.isEqual( otherRange.end );
+	/**
+	 * 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( 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( 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( root, [ 3, 2 ] ), 4, true );
+	 *		// transformed array has one range which is equal to `range`. This is because of spreadOnlyOnSameLevel flag.
+	 *
+	 * @param {treeModel.Position} insertPosition Position where nodes are inserted.
+	 * @param {Number} howMany How many nodes are inserted.
+	 * @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.
+		const areOnSameLevel = utils.compareArrays( this.start.getParentPath(), this.end.getParentPath() ) == utils.compareArrays.SAME &&
+			utils.compareArrays( this.start.getParentPath(), insertPosition.getParentPath() ) == utils.compareArrays.SAME;
+
+		if ( this.containsPosition( insertPosition ) && ( !spreadOnlyOnSameLevel || areOnSameLevel ) ) {
+			// Range has to be spread. The first part is from original start to the spread point.
+			// The other part is from spread point to the original end, but transformed by
+			// insertion to reflect insertion changes.
+
+			return [
+				new Range( this.start, insertPosition ),
+				new Range(
+					insertPosition.getTransformedByInsertion( insertPosition, howMany, true ),
+					this.end.getTransformedByInsertion( insertPosition, howMany, true )
+				)
+			];
+		} else {
+			// If insertion is not inside the range, simply transform range boundaries (positions) by the insertion.
+			// Both, one or none of them might be affected by the insertion.
+
+			const range = Range.createFromRange( this );
+
+			range.start = range.start.getTransformedByInsertion( insertPosition, howMany, true );
+			range.end = range.end.getTransformedByInsertion( insertPosition, howMany, false );
+
+			return [ range ];
 		}
+	}
 
-		/**
-		 * Checks and returns whether this range intersects with given range.
-		 *
-		 * @param {treeModel.Range} otherRange Range to compare with.
-		 * @returns {Boolean} True if ranges intersect.
-		 */
-		isIntersecting( otherRange ) {
-			return this.start.isBefore( otherRange.end ) && this.end.isAfter( otherRange.start );
-		}
+	/**
+	 * Two ranges equal if their start and end positions equal.
+	 *
+	 * @param {treeModel.Range} otherRange Range to compare with.
+	 * @returns {Boolean} True if ranges equal.
+	 */
+	isEqual( otherRange ) {
+		return this.start.isEqual( otherRange.start ) && this.end.isEqual( otherRange.end );
+	}
 
-		/**
-		 * Creates a range inside an element which starts before the first child and ends after the last child.
-		 *
-		 * @param {treeModel.Element} element Element which is a parent for the range.
-		 * @returns {treeModel.Range} Created range.
-		 */
-		static createFromElement( element ) {
-			return this.createFromParentsAndOffsets( element, 0, element, element.getChildCount() );
-		}
+	/**
+	 * Checks and returns whether this range intersects with given range.
+	 *
+	 * @param {treeModel.Range} otherRange Range to compare with.
+	 * @returns {Boolean} True if ranges intersect.
+	 */
+	isIntersecting( otherRange ) {
+		return this.start.isBefore( otherRange.end ) && this.end.isAfter( otherRange.start );
+	}
 
-		/**
-		 * Creates a new range spreading from specified position to the same position moved by given shift.
-		 *
-		 * @param {treeModel.Position} position Beginning of the range.
-		 * @param {Number} shift How long the range should be.
-		 * @returns {treeModel.Range}
-		 */
-		static createFromPositionAndShift( position, shift ) {
-			return new this( position, position.getShiftedBy( shift ) );
-		}
+	/**
+	 * Creates a range inside an element which starts before the first child and ends after the last child.
+	 *
+	 * @param {treeModel.Element} element Element which is a parent for the range.
+	 * @returns {treeModel.Range} Created range.
+	 */
+	static createFromElement( element ) {
+		return this.createFromParentsAndOffsets( element, 0, element, element.getChildCount() );
+	}
 
-		/**
-		 * Creates a range from given parents and offsets.
-		 *
-		 * @param {treeModel.Element} startElement Start position parent element.
-		 * @param {Number} startOffset Start position offset.
-		 * @param {treeModel.Element} endElement End position parent element.
-		 * @param {Number} endOffset End position offset.
-		 * @returns {treeModel.Range} Created range.
-		 */
-		static createFromParentsAndOffsets( startElement, startOffset, endElement, endOffset ) {
-			return new this(
-				Position.createFromParentAndOffset( startElement, startOffset ),
-				Position.createFromParentAndOffset( endElement, endOffset )
-			);
-		}
+	/**
+	 * Creates a new range spreading from specified position to the same position moved by given shift.
+	 *
+	 * @param {treeModel.Position} position Beginning of the range.
+	 * @param {Number} shift How long the range should be.
+	 * @returns {treeModel.Range}
+	 */
+	static createFromPositionAndShift( position, shift ) {
+		return new this( position, position.getShiftedBy( shift ) );
+	}
 
-		/**
-		 * Creates and returns a new instance of Range which is equal to passed range.
-		 *
-		 * @param {treeModel.Range} range Range to clone.
-		 * @returns {treeModel.Range}
-		 */
-		static createFromRange( range ) {
-			return new this( range.start, range.end );
-		}
+	/**
+	 * Creates a range from given parents and offsets.
+	 *
+	 * @param {treeModel.Element} startElement Start position parent element.
+	 * @param {Number} startOffset Start position offset.
+	 * @param {treeModel.Element} endElement End position parent element.
+	 * @param {Number} endOffset End position offset.
+	 * @returns {treeModel.Range} Created range.
+	 */
+	static createFromParentsAndOffsets( startElement, startOffset, endElement, endOffset ) {
+		return new this(
+			Position.createFromParentAndOffset( startElement, startOffset ),
+			Position.createFromParentAndOffset( endElement, endOffset )
+		);
 	}
 
-	return Range;
-} );
+	/**
+	 * Creates and returns a new instance of Range which is equal to passed range.
+	 *
+	 * @param {treeModel.Range} range Range to clone.
+	 * @returns {treeModel.Range}
+	 */
+	static createFromRange( range ) {
+		return new this( range.start, range.end );
+	}
+}

+ 19 - 21
packages/ckeditor5-engine/src/treemodel/rootelement.js

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

+ 190 - 194
packages/ckeditor5-engine/src/treemodel/selection.js

@@ -5,244 +5,240 @@
 
 'use strict';
 
-CKEDITOR.define( [
-	'treemodel/liverange',
-	'treemodel/attributelist',
-	'emittermixin',
-	'utils',
-	'ckeditorerror'
-], ( LiveRange, AttributeList, EmitterMixin, utils, CKEditorError ) => {
+import LiveRange from './liverange.js';
+import AttributeList from './attributelist.js';
+import EmitterMixin from '../emittermixin.js';
+import CKEditorError from '../ckeditorerror.js';
+import objectUtils from '../lib/lodash/object.js';
+
+/**
+ * Represents a selection that is made on nodes in {@link treeModel.Document}. Selection instance is
+ * created by {@link treeModel.Document}. In most scenarios you should not need to create an instance of Selection.
+ *
+ * @class treeModel.Selection
+ */
+export default class Selection {
 	/**
-	 * Represents a selection that is made on nodes in {@link treeModel.Document}. Selection instance is
-	 * created by {@link treeModel.Document}. In most scenarios you should not need to create an instance of Selection.
+	 * Creates an empty selection.
 	 *
-	 * @class treeModel.Selection
+	 * @constructor
 	 */
-	class Selection {
+	constructor() {
 		/**
-		 * Creates an empty selection.
+		 * List of attributes set on current selection.
 		 *
-		 * @constructor
+		 * @private
+		 * @property {treeModel.AttributeList} _attrs
 		 */
-		constructor() {
-			/**
-			 * List of attributes set on current selection.
-			 *
-			 * @private
-			 * @property {treeModel.AttributeList} _attrs
-			 */
-			this._attrs = new AttributeList();
-
-			/**
-			 * Stores all ranges that are selected.
-			 *
-			 * @private
-			 * @property {Array.<LiveRange>}
-			 */
-			this._ranges = [];
-
-			/**
-			 * Specifies whether the last added range was added as a backward or forward range.
-			 *
-			 * @private
-			 * @property {Boolean}
-			 */
-			this._lastRangeBackward = false;
-		}
+		this._attrs = new AttributeList();
 
 		/**
-		 * Selection anchor. Anchor may be described as a position where the selection starts.
-		 * Together with {@link #focus} they define the direction of selection, which is important
-		 * when expanding/shrinking selection. When there are no ranges in selection anchor is null.
-		 * Anchor is always a start or end of the most recent added range. It may be a bit unintuitive when
-		 * there are multiple ranges in selection.
+		 * Stores all ranges that are selected.
 		 *
-		 * @property {treeModel.LivePosition|null}
+		 * @private
+		 * @property {Array.<LiveRange>}
 		 */
-		get anchor() {
-			if ( this._ranges.length > 0 ) {
-				let range = this._ranges[ this._ranges.length - 1 ];
-
-				return this._lastRangeBackward ? range.end : range.start;
-			}
-
-			return null;
-		}
+		this._ranges = [];
 
 		/**
-		 * Selection focus. Focus is a position where the selection ends. When there are no ranges in selection,
-		 * focus is null.
+		 * Specifies whether the last added range was added as a backward or forward range.
 		 *
-		 * @see {#anchor}
-		 * @property {treeModel.LivePosition|null}
+		 * @private
+		 * @property {Boolean}
 		 */
-		get focus() {
-			if ( this._ranges.length > 0 ) {
-				let range = this._ranges[ this._ranges.length - 1 ];
+		this._lastRangeBackward = false;
+	}
 
-				return this._lastRangeBackward ? range.start : range.end;
-			}
+	/**
+	 * Selection anchor. Anchor may be described as a position where the selection starts.
+	 * Together with {@link #focus} they define the direction of selection, which is important
+	 * when expanding/shrinking selection. When there are no ranges in selection anchor is null.
+	 * Anchor is always a start or end of the most recent added range. It may be a bit unintuitive when
+	 * there are multiple ranges in selection.
+	 *
+	 * @property {treeModel.LivePosition|null}
+	 */
+	get anchor() {
+		if ( this._ranges.length > 0 ) {
+			let range = this._ranges[ this._ranges.length - 1 ];
 
-			return null;
+			return this._lastRangeBackward ? range.end : range.start;
 		}
 
-		/**
-		 * Returns whether the selection is collapsed. Selection is collapsed when all it's ranges are collapsed.
-		 *
-		 * @property {Boolean}
-		 */
-		get isCollapsed() {
-			for ( let i = 0; i < this._ranges.length; i++ ) {
-				if ( !this._ranges[ i ].isCollapsed ) {
-					return false;
-				}
-			}
-
-			return true;
-		}
+		return null;
+	}
 
-		/**
-		 * Adds a range to the selection. Added range is copied and converted to {@link treeModel.LiveRange}. This means
-		 * that passed range is not saved in the Selection instance and you can safely operate on it. Accepts a flag
-		 * describing in which way the selection is made - passed range might be selected from {@link treeModel.Range#start}
-		 * to {@link treeModel.Range#end} or from {@link treeModel.Range#start} to {@link treeModel.Range#end}. The flag
-		 * is used to set {@link #anchor} and {@link #focus} properties.
-		 *
-		 * @param {treeModel.Range} range Range to add.
-		 * @param {Boolean} [isBackward] Flag describing if added range was selected forward - from start to end (`false`)
-		 * or backward - from end to start (`true`). Defaults to `false`.
-		 */
-		addRange( range, isBackward ) {
-			pushRange.call( this, range );
-			this._lastRangeBackward = !!isBackward;
+	/**
+	 * Selection focus. Focus is a position where the selection ends. When there are no ranges in selection,
+	 * focus is null.
+	 *
+	 * @see {#anchor}
+	 * @property {treeModel.LivePosition|null}
+	 */
+	get focus() {
+		if ( this._ranges.length > 0 ) {
+			let range = this._ranges[ this._ranges.length - 1 ];
 
-			this.fire( 'update' );
+			return this._lastRangeBackward ? range.start : range.end;
 		}
 
-		/**
-		 * Unbinds all events previously bound by this selection and objects created by this selection.
-		 */
-		detach() {
-			for ( let i = 0; i < this._ranges.length; i++ ) {
-				this._ranges[ i ].detach();
+		return null;
+	}
+
+	/**
+	 * Returns whether the selection is collapsed. Selection is collapsed when all it's ranges are collapsed.
+	 *
+	 * @property {Boolean}
+	 */
+	get isCollapsed() {
+		for ( let i = 0; i < this._ranges.length; i++ ) {
+			if ( !this._ranges[ i ].isCollapsed ) {
+				return false;
 			}
 		}
 
-		/**
-		 * @see {@link treeModel.AttributeList#getAttr}
-		 */
-		getAttr( key ) {
-			return this._attrs.getAttr( key );
-		}
+		return true;
+	}
 
-		/**
-		 * @see {@link treeModel.AttributeList#getAttrs}
-		 */
-		getAttrs() {
-			return this._attrs.getAttrs();
-		}
+	/**
+	 * Adds a range to the selection. Added range is copied and converted to {@link treeModel.LiveRange}. This means
+	 * that passed range is not saved in the Selection instance and you can safely operate on it. Accepts a flag
+	 * describing in which way the selection is made - passed range might be selected from {@link treeModel.Range#start}
+	 * to {@link treeModel.Range#end} or from {@link treeModel.Range#start} to {@link treeModel.Range#end}. The flag
+	 * is used to set {@link #anchor} and {@link #focus} properties.
+	 *
+	 * @param {treeModel.Range} range Range to add.
+	 * @param {Boolean} [isBackward] Flag describing if added range was selected forward - from start to end (`false`)
+	 * or backward - from end to start (`true`). Defaults to `false`.
+	 */
+	addRange( range, isBackward ) {
+		pushRange.call( this, range );
+		this._lastRangeBackward = !!isBackward;
 
-		/**
-		 * Returns an array of ranges added to the selection. The method returns a copy of internal array, so
-		 * it will not change when ranges get added or removed from selection.
-		 *
-		 * @returns {Array.<LiveRange>}
-		 */
-		getRanges() {
-			return this._ranges.slice();
-		}
+		this.fire( 'update' );
+	}
 
-		/**
-		 * @see {@link treeModel.AttributeList#hasAttr}
-		 */
-		hasAttr( key ) {
-			return this._attrs.hasAttr( key );
+	/**
+	 * Unbinds all events previously bound by this selection and objects created by this selection.
+	 */
+	detach() {
+		for ( let i = 0; i < this._ranges.length; i++ ) {
+			this._ranges[ i ].detach();
 		}
+	}
 
-		/**
-		 * @see {@link treeModel.AttributeList#removeAttr}
-		 */
-		removeAttr( key ) {
-			this._attrs.removeAttr( key );
-		}
+	/**
+	 * @see {@link treeModel.AttributeList#getAttr}
+	 */
+	getAttr( key ) {
+		return this._attrs.getAttr( key );
+	}
 
-		/**
-		 * Removes all ranges that were added to the selection. Fires update event.
-		 */
-		removeAllRanges() {
-			this.detach();
-			this._ranges = [];
+	/**
+	 * @see {@link treeModel.AttributeList#getAttrs}
+	 */
+	getAttrs() {
+		return this._attrs.getAttrs();
+	}
 
-			this.fire( 'update' );
-		}
+	/**
+	 * Returns an array of ranges added to the selection. The method returns a copy of internal array, so
+	 * it will not change when ranges get added or removed from selection.
+	 *
+	 * @returns {Array.<LiveRange>}
+	 */
+	getRanges() {
+		return this._ranges.slice();
+	}
 
-		/**
-		 * @see {@link treeModel.AttributeList#setAttr}
-		 */
-		setAttr( attr ) {
-			this._attrs.setAttr( attr );
-		}
+	/**
+	 * @see {@link treeModel.AttributeList#hasAttr}
+	 */
+	hasAttr( key ) {
+		return this._attrs.hasAttr( key );
+	}
 
-		/**
-		 * @see {@link treeModel.AttributeList#setAttrsTo}
-		 */
-		setAttrsTo( attrs ) {
-			this._attrs.setAttrsTo( attrs );
-		}
+	/**
+	 * @see {@link treeModel.AttributeList#removeAttr}
+	 */
+	removeAttr( key ) {
+		this._attrs.removeAttr( key );
+	}
 
-		/**
-		 * Replaces all ranges that were added to the selection with given array of ranges. Last range of the array
-		 * is treated like the last added range and is used to set {@link #anchor} and {@link #focus}. Accepts a flag
-		 * describing in which way the selection is made (see {@link #addRange}).
-		 *
-		 * @param {Array.<treeModel.Range>} newRanges Array of ranges to set.
-		 * @param {Boolean} [isLastBackward] Flag describing if last added range was selected forward - from start to end (`false`)
-		 * or backward - from end to start (`true`). Defaults to `false`.
-		 */
-		setRanges( newRanges, isLastBackward ) {
-			this.detach();
-			this._ranges = [];
+	/**
+	 * Removes all ranges that were added to the selection. Fires update event.
+	 */
+	removeAllRanges() {
+		this.detach();
+		this._ranges = [];
 
-			for ( let i = 0; i < newRanges.length; i++ ) {
-				pushRange.call( this, newRanges[ i ] );
-			}
+		this.fire( 'update' );
+	}
 
-			this._lastRangeBackward = !!isLastBackward;
-			this.fire( 'update' );
-		}
+	/**
+	 * @see {@link treeModel.AttributeList#setAttr}
+	 */
+	setAttr( attr ) {
+		this._attrs.setAttr( attr );
 	}
 
 	/**
-	 * Converts given range to {@link treeModel.LiveRange} and adds it to internal ranges array. Throws an error
-	 * if given range is intersecting with any range that is already stored in this selection.
+	 * @see {@link treeModel.AttributeList#setAttrsTo}
+	 */
+	setAttrsTo( attrs ) {
+		this._attrs.setAttrsTo( attrs );
+	}
+
+	/**
+	 * Replaces all ranges that were added to the selection with given array of ranges. Last range of the array
+	 * is treated like the last added range and is used to set {@link #anchor} and {@link #focus}. Accepts a flag
+	 * describing in which way the selection is made (see {@link #addRange}).
 	 *
-	 * @private
-	 * @method pushRange
-	 * @memberOf {treeModel.Selection}
-	 * @param {treeModel.Range} range Range to add.
+	 * @param {Array.<treeModel.Range>} newRanges Array of ranges to set.
+	 * @param {Boolean} [isLastBackward] Flag describing if last added range was selected forward - from start to end (`false`)
+	 * or backward - from end to start (`true`). Defaults to `false`.
 	 */
-	function pushRange( range ) {
-		/* jshint validthis: true */
-		for ( let i = 0; i < this._ranges.length ; i++ ) {
-			if ( range.isIntersecting( this._ranges[ i ] ) ) {
-				/**
-				 * Trying to add a range that intersects with another range from selection.
-				 *
-				 * @error selection-range-intersects
-				 * @param {treeModel.Range} addedRange Range that was added to the selection.
-				 * @param {treeModel.Range} intersectingRange Range from selection that intersects with `addedRange`.
-				 */
-				throw new CKEditorError(
-					'selection-range-intersects: Trying to add a range that intersects with another range from selection.',
-					{ addedRange: range, intersectingRange: this._ranges[ i ] }
-				);
-			}
+	setRanges( newRanges, isLastBackward ) {
+		this.detach();
+		this._ranges = [];
+
+		for ( let i = 0; i < newRanges.length; i++ ) {
+			pushRange.call( this, newRanges[ i ] );
 		}
 
-		this._ranges.push( LiveRange.createFromRange( range ) );
+		this._lastRangeBackward = !!isLastBackward;
+		this.fire( 'update' );
+	}
+}
+
+/**
+ * Converts given range to {@link treeModel.LiveRange} and adds it to internal ranges array. Throws an error
+ * if given range is intersecting with any range that is already stored in this selection.
+ *
+ * @private
+ * @method pushRange
+ * @memberOf {treeModel.Selection}
+ * @param {treeModel.Range} range Range to add.
+ */
+function pushRange( range ) {
+	/* jshint validthis: true */
+	for ( let i = 0; i < this._ranges.length ; i++ ) {
+		if ( range.isIntersecting( this._ranges[ i ] ) ) {
+			/**
+			 * Trying to add a range that intersects with another range from selection.
+			 *
+			 * @error selection-range-intersects
+			 * @param {treeModel.Range} addedRange Range that was added to the selection.
+			 * @param {treeModel.Range} intersectingRange Range from selection that intersects with `addedRange`.
+			 */
+			throw new CKEditorError(
+				'selection-range-intersects: Trying to add a range that intersects with another range from selection.',
+				{ addedRange: range, intersectingRange: this._ranges[ i ] }
+			);
+		}
 	}
 
-	utils.extend( Selection.prototype, EmitterMixin );
+	this._ranges.push( LiveRange.createFromRange( range ) );
+}
 
-	return Selection;
-} );
+objectUtils.extend( Selection.prototype, EmitterMixin );

+ 23 - 27
packages/ckeditor5-engine/src/treemodel/text.js

@@ -5,38 +5,34 @@
 
 'use strict';
 
-CKEDITOR.define( [], () => {
+/**
+ * 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 treeModel.Text
+ */
+export default class Text {
 	/**
-	 * 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}.
+	 * Creates a text with attributes.
 	 *
-	 * @class treeModel.Text
+	 * @param {String} text Described text.
+	 * @param {Iterable} attrs Iterable collection of {@link treeModel.Attribute attributes}.
+	 * @constructor
 	 */
-	class Text {
+	constructor( text, attrs ) {
 		/**
-		 * Creates a text with attributes.
+		 * Text.
 		 *
-		 * @param {String} text Described text.
-		 * @param {Iterable} attrs Iterable collection of {@link treeModel.Attribute attributes}.
-		 * @constructor
+		 * @readonly
+		 * @property {String}
 		 */
-		constructor( text, attrs ) {
-			/**
-			 * Text.
-			 *
-			 * @readonly
-			 * @property {String}
-			 */
-			this.text = text;
+		this.text = text;
 
-			/**
-			 * Iterable collection of {@link treeModel.Attribute attributes}.
-			 *
-			 * @property {Iterable}
-			 */
-			this.attrs = attrs;
-		}
+		/**
+		 * Iterable collection of {@link treeModel.Attribute attributes}.
+		 *
+		 * @property {Iterable}
+		 */
+		this.attrs = attrs;
 	}
-
-	return Text;
-} );
+}

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

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

+ 5 - 8
packages/ckeditor5-engine/tests/treemodel/attributelist.js

@@ -4,7 +4,6 @@
  */
 
 /* bender-tags: treemodel */
-
 /* bender-include: ../_tools/tools.js */
 
 'use strict';
@@ -12,18 +11,16 @@
 const getIteratorCount = bender.tools.core.getIteratorCount;
 
 const modules = bender.amd.require(
-	'treemodel/attributelist',
-	'treemodel/attribute',
-	'utils'
+	'core/treemodel/attributelist',
+	'core/treemodel/attribute'
 );
 
 describe( 'AttributeList', () => {
-	let AttributeList, Attribute, utils;
+	let AttributeList, Attribute;
 
 	before( () => {
-		AttributeList = modules[ 'treemodel/attributelist' ];
-		Attribute = modules[ 'treemodel/attribute' ];
-		utils = modules.utils;
+		AttributeList = modules[ 'core/treemodel/attributelist' ];
+		Attribute = modules[ 'core/treemodel/attribute' ];
 	} );
 
 	let list, attrFooBar;

+ 6 - 6
packages/ckeditor5-engine/tests/treemodel/batch.js

@@ -8,18 +8,18 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'treemodel/batch',
-	'treemodel/delta/delta',
-	'ckeditorerror'
+	'core/treemodel/batch',
+	'core/treemodel/delta/delta',
+	'core/ckeditorerror'
 );
 
 describe( 'Batch', () => {
 	let Batch, Delta, CKEditorError;
 
 	before( () => {
-		Batch = modules[ 'treemodel/batch' ];
-		Delta = modules[ 'treemodel/delta/delta' ];
-		CKEditorError = modules.ckeditorerror;
+		Batch = modules[ 'core/treemodel/batch' ];
+		Delta = modules[ 'core/treemodel/delta/delta' ];
+		CKEditorError = modules[ 'core/ckeditorerror' ];
 	} );
 
 	it( 'should have registered basic methods', () => {

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

@@ -3,10 +3,7 @@
  * For licensing, see LICENSE.md.
  */
 
-/* jshint expr: true */
-
 /* bender-tags: treemodel */
-
 /* bender-include: ../_tools/tools.js */
 
 'use strict';
@@ -14,20 +11,20 @@
 const getIteratorCount = bender.tools.core.getIteratorCount;
 
 const modules = bender.amd.require(
-	'treemodel/character',
-	'treemodel/node',
-	'treemodel/element',
-	'treemodel/attribute'
+	'core/treemodel/character',
+	'core/treemodel/node',
+	'core/treemodel/element',
+	'core/treemodel/attribute'
 );
 
 describe( 'Character', () => {
 	let Element, Character, Node, Attribute;
 
 	before( () => {
-		Element = modules[ 'treemodel/element' ];
-		Character = modules[ 'treemodel/character' ];
-		Node = modules[ 'treemodel/node' ];
-		Attribute = modules[ 'treemodel/attribute' ];
+		Element = modules[ 'core/treemodel/element' ];
+		Character = modules[ 'core/treemodel/character' ];
+		Node = modules[ 'core/treemodel/node' ];
+		Attribute = modules[ 'core/treemodel/attribute' ];
 	} );
 
 	describe( 'constructor', () => {

+ 16 - 16
packages/ckeditor5-engine/tests/treemodel/delta/attributedelta.js

@@ -12,14 +12,14 @@
 const getIteratorCount = bender.tools.core.getIteratorCount;
 
 const modules = bender.amd.require(
-	'treemodel/batch',
-	'treemodel/document',
-	'treemodel/text',
-	'treemodel/attribute',
-	'treemodel/range',
-	'treemodel/position',
-	'treemodel/element',
-	'treemodel/character' );
+	'core/treemodel/batch',
+	'core/treemodel/document',
+	'core/treemodel/text',
+	'core/treemodel/attribute',
+	'core/treemodel/range',
+	'core/treemodel/position',
+	'core/treemodel/element',
+	'core/treemodel/character' );
 
 describe( 'Batch', () => {
 	let Batch, Document, Text, Attribute, Range, Position, Element, Character;
@@ -27,14 +27,14 @@ describe( 'Batch', () => {
 	let doc, root, batch;
 
 	before( () => {
-		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' ];
+		Batch = modules[ 'core/treemodel/batch' ];
+		Document = modules[ 'core/treemodel/document' ];
+		Text = modules[ 'core/treemodel/text' ];
+		Attribute = modules[ 'core/treemodel/attribute' ];
+		Range = modules[ 'core/treemodel/range' ];
+		Position = modules[ 'core/treemodel/position' ];
+		Element = modules[ 'core/treemodel/element' ];
+		Character = modules[ 'core/treemodel/character' ];
 	} );
 
 	beforeEach( () => {

+ 3 - 2
packages/ckeditor5-engine/tests/treemodel/delta/delta.js

@@ -12,13 +12,14 @@
 const getIteratorCount = bender.tools.core.getIteratorCount;
 
 const modules = bender.amd.require(
-	'treemodel/delta/delta' );
+	'core/treemodel/delta/delta'
+);
 
 describe( 'Delta', () => {
 	let Delta;
 
 	before( () => {
-		Delta = modules[ 'treemodel/delta/delta' ];
+		Delta = modules[ 'core/treemodel/delta/delta' ];
 	} );
 
 	describe( 'constructor', () => {

+ 8 - 8
packages/ckeditor5-engine/tests/treemodel/delta/insertdelta.js

@@ -8,20 +8,20 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'treemodel/document',
-	'treemodel/element',
-	'treemodel/position',
-	'treemodel/delta/insertdelta'
+	'core/treemodel/document',
+	'core/treemodel/element',
+	'core/treemodel/position',
+	'core/treemodel/delta/insertdelta'
 );
 
 describe( 'Batch', () => {
 	let Document, Element, Position, InsertDelta;
 
 	before( () => {
-		Document = modules[ 'treemodel/document' ];
-		Element = modules[ 'treemodel/element' ];
-		Position = modules[ 'treemodel/position' ];
-		InsertDelta = modules[ 'treemodel/delta/insertdelta' ];
+		Document = modules[ 'core/treemodel/document' ];
+		Element = modules[ 'core/treemodel/element' ];
+		Position = modules[ 'core/treemodel/position' ];
+		InsertDelta = modules[ 'core/treemodel/delta/insertdelta' ];
 	} );
 
 	let doc, root, batch, p, ul, chain;

+ 10 - 10
packages/ckeditor5-engine/tests/treemodel/delta/mergedelta.js

@@ -12,11 +12,11 @@
 const getIteratorCount = bender.tools.core.getIteratorCount;
 
 const modules = bender.amd.require(
-	'treemodel/document',
-	'treemodel/position',
-	'treemodel/element',
-	'treemodel/attribute',
-	'ckeditorerror' );
+	'core/treemodel/document',
+	'core/treemodel/position',
+	'core/treemodel/element',
+	'core/treemodel/attribute',
+	'core/ckeditorerror' );
 
 describe( 'Batch', () => {
 	let Document, Position, Element, Attribute, CKEditorError;
@@ -24,11 +24,11 @@ describe( 'Batch', () => {
 	let doc, root, p1, p2;
 
 	before( () => {
-		Document = modules[ 'treemodel/document' ];
-		Position = modules[ 'treemodel/position' ];
-		Element = modules[ 'treemodel/element' ];
-		Attribute = modules[ 'treemodel/attribute' ];
-		CKEditorError = modules.ckeditorerror;
+		Document = modules[ 'core/treemodel/document' ];
+		Position = modules[ 'core/treemodel/position' ];
+		Element = modules[ 'core/treemodel/element' ];
+		Attribute = modules[ 'core/treemodel/attribute' ];
+		CKEditorError = modules[ 'core/ckeditorerror' ];
 	} );
 
 	beforeEach( () => {

+ 10 - 10
packages/ckeditor5-engine/tests/treemodel/delta/movedelta.js

@@ -11,11 +11,11 @@
 const getNodesAndText = bender.tools.treemodel.getNodesAndText;
 
 const modules = bender.amd.require(
-	'treemodel/document',
-	'treemodel/position',
-	'treemodel/range',
-	'treemodel/element',
-	'ckeditorerror'
+	'core/treemodel/document',
+	'core/treemodel/position',
+	'core/treemodel/range',
+	'core/treemodel/element',
+	'core/ckeditorerror'
 );
 
 describe( 'Batch', () => {
@@ -24,11 +24,11 @@ describe( 'Batch', () => {
 	let doc, root, div, p, batch, chain;
 
 	before( () => {
-		Document = modules[ 'treemodel/document' ];
-		Position = modules[ 'treemodel/position' ];
-		Range = modules[ 'treemodel/range' ];
-		Element = modules[ 'treemodel/element' ];
-		CKEditorError = modules.ckeditorerror;
+		Document = modules[ 'core/treemodel/document' ];
+		Position = modules[ 'core/treemodel/position' ];
+		Range = modules[ 'core/treemodel/range' ];
+		Element = modules[ 'core/treemodel/element' ];
+		CKEditorError = modules[ 'core/ckeditorerror' ];
 	} );
 
 	beforeEach( () => {

+ 8 - 8
packages/ckeditor5-engine/tests/treemodel/delta/removedelta.js

@@ -11,10 +11,10 @@
 const getNodesAndText = bender.tools.treemodel.getNodesAndText;
 
 const modules = bender.amd.require(
-	'treemodel/document',
-	'treemodel/position',
-	'treemodel/range',
-	'treemodel/element'
+	'core/treemodel/document',
+	'core/treemodel/position',
+	'core/treemodel/range',
+	'core/treemodel/element'
 );
 
 describe( 'Batch', () => {
@@ -23,10 +23,10 @@ describe( 'Batch', () => {
 	let doc, root, div, p, batch, chain, range;
 
 	before( () => {
-		Document = modules[ 'treemodel/document' ];
-		Position = modules[ 'treemodel/position' ];
-		Range = modules[ 'treemodel/range' ];
-		Element = modules[ 'treemodel/element' ];
+		Document = modules[ 'core/treemodel/document' ];
+		Position = modules[ 'core/treemodel/position' ];
+		Range = modules[ 'core/treemodel/range' ];
+		Element = modules[ 'core/treemodel/element' ];
 	} );
 
 	beforeEach( () => {

+ 10 - 10
packages/ckeditor5-engine/tests/treemodel/delta/splitdelta.js

@@ -12,11 +12,11 @@
 const getIteratorCount = bender.tools.core.getIteratorCount;
 
 const modules = bender.amd.require(
-	'treemodel/document',
-	'treemodel/position',
-	'treemodel/element',
-	'treemodel/attribute',
-	'ckeditorerror' );
+	'core/treemodel/document',
+	'core/treemodel/position',
+	'core/treemodel/element',
+	'core/treemodel/attribute',
+	'core/ckeditorerror' );
 
 describe( 'Batch', () => {
 	let Document, Position, Element, Attribute, CKEditorError;
@@ -24,11 +24,11 @@ describe( 'Batch', () => {
 	let doc, root, p;
 
 	before( () => {
-		Document = modules[ 'treemodel/document' ];
-		Position = modules[ 'treemodel/position' ];
-		Element = modules[ 'treemodel/element' ];
-		Attribute = modules[ 'treemodel/attribute' ];
-		CKEditorError = modules.ckeditorerror;
+		Document = modules[ 'core/treemodel/document' ];
+		Position = modules[ 'core/treemodel/position' ];
+		Element = modules[ 'core/treemodel/element' ];
+		Attribute = modules[ 'core/treemodel/attribute' ];
+		CKEditorError = modules[ 'core/ckeditorerror' ];
 	} );
 
 	beforeEach( () => {

+ 8 - 9
packages/ckeditor5-engine/tests/treemodel/delta/unwrapdelta.js

@@ -10,11 +10,10 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'treemodel/document',
-	'treemodel/position',
-	'treemodel/range',
-	'treemodel/element',
-	'ckeditorerror'
+	'core/treemodel/document',
+	'core/treemodel/position',
+	'core/treemodel/element',
+	'core/ckeditorerror'
 );
 
 describe( 'Batch', () => {
@@ -23,10 +22,10 @@ describe( 'Batch', () => {
 	let doc, root, p;
 
 	before( () => {
-		Document = modules[ 'treemodel/document' ];
-		Position = modules[ 'treemodel/position' ];
-		Element = modules[ 'treemodel/element' ];
-		CKEditorError = modules.ckeditorerror;
+		Document = modules[ 'core/treemodel/document' ];
+		Position = modules[ 'core/treemodel/position' ];
+		Element = modules[ 'core/treemodel/element' ];
+		CKEditorError = modules[ 'core/ckeditorerror' ];
 	} );
 
 	beforeEach( () => {

+ 6 - 6
packages/ckeditor5-engine/tests/treemodel/delta/weakinsertdelta.js

@@ -8,18 +8,18 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'treemodel/document',
-	'treemodel/attribute',
-	'treemodel/position'
+	'core/treemodel/document',
+	'core/treemodel/attribute',
+	'core/treemodel/position'
 );
 
 describe( 'Batch', () => {
 	let Document, Attribute, Position;
 
 	before( () => {
-		Document = modules[ 'treemodel/document' ];
-		Attribute = modules[ 'treemodel/attribute' ];
-		Position = modules[ 'treemodel/position' ];
+		Document = modules[ 'core/treemodel/document' ];
+		Attribute = modules[ 'core/treemodel/attribute' ];
+		Position = modules[ 'core/treemodel/position' ];
 	} );
 
 	let doc, root, batch, chain, attrs;

+ 10 - 10
packages/ckeditor5-engine/tests/treemodel/delta/wrapdelta.js

@@ -10,11 +10,11 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'treemodel/document',
-	'treemodel/position',
-	'treemodel/range',
-	'treemodel/element',
-	'ckeditorerror'
+	'core/treemodel/document',
+	'core/treemodel/position',
+	'core/treemodel/range',
+	'core/treemodel/element',
+	'core/ckeditorerror'
 );
 
 describe( 'Batch', () => {
@@ -23,11 +23,11 @@ describe( 'Batch', () => {
 	let doc, root, range;
 
 	before( () => {
-		Document = modules[ 'treemodel/document' ];
-		Position = modules[ 'treemodel/position' ];
-		Range = modules[ 'treemodel/range' ];
-		Element = modules[ 'treemodel/element' ];
-		CKEditorError = modules.ckeditorerror;
+		Document = modules[ 'core/treemodel/document' ];
+		Position = modules[ 'core/treemodel/position' ];
+		Range = modules[ 'core/treemodel/range' ];
+		Element = modules[ 'core/treemodel/element' ];
+		CKEditorError = modules[ 'core/ckeditorerror' ];
 	} );
 
 	beforeEach( () => {

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

@@ -8,17 +8,17 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'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'
+	'core/treemodel/document',
+	'core/treemodel/element',
+	'core/treemodel/rootelement',
+	'core/treemodel/attribute',
+	'core/treemodel/position',
+	'core/treemodel/range',
+	'core/treemodel/operation/attributeoperation',
+	'core/treemodel/operation/insertoperation',
+	'core/treemodel/operation/moveoperation',
+	'core/treemodel/operation/reinsertoperation',
+	'core/treemodel/operation/removeoperation'
 );
 
 describe( 'Document change event', () => {
@@ -26,18 +26,18 @@ describe( 'Document change event', () => {
 	let AttributeOperation, InsertOperation, MoveOperation, ReinsertOperation, RemoveOperation, Attribute;
 
 	before( () => {
-		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' ];
+		Document = modules[ 'core/treemodel/document' ];
+		Element = modules[ 'core/treemodel/element' ];
+		RootElement = modules[ 'core/treemodel/rootelement' ];
+		Attribute = modules[ 'core/treemodel/attribute' ];
+		Position = modules[ 'core/treemodel/position' ];
+		Range = modules[ 'core/treemodel/range' ];
+
+		InsertOperation = modules[ 'core/treemodel/operation/insertoperation' ];
+		AttributeOperation = modules[ 'core/treemodel/operation/attributeoperation' ];
+		MoveOperation = modules[ 'core/treemodel/operation/moveoperation' ];
+		ReinsertOperation = modules[ 'core/treemodel/operation/reinsertoperation' ];
+		RemoveOperation = modules[ 'core/treemodel/operation/removeoperation' ];
 	} );
 
 	let doc, root, graveyard, types, changes;

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

@@ -8,20 +8,20 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'treemodel/document',
-	'treemodel/rootelement',
-	'treemodel/batch',
-	'ckeditorerror'
+	'core/treemodel/document',
+	'core/treemodel/rootelement',
+	'core/treemodel/batch',
+	'core/ckeditorerror'
 );
 
 describe( 'Document', () => {
 	let Document, RootElement, Batch, CKEditorError;
 
 	before( () => {
-		Document = modules[ 'treemodel/document' ];
-		RootElement = modules[ 'treemodel/rootelement' ];
-		Batch = modules[ 'treemodel/batch' ];
-		CKEditorError = modules.ckeditorerror;
+		Document = modules[ 'core/treemodel/document' ];
+		RootElement = modules[ 'core/treemodel/rootelement' ];
+		Batch = modules[ 'core/treemodel/batch' ];
+		CKEditorError = modules[ 'core/ckeditorerror' ];
 	} );
 
 	let doc;

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

@@ -3,10 +3,7 @@
  * For licensing, see LICENSE.md.
  */
 
-/* jshint expr: true */
-
 /* bender-tags: treemodel */
-
 /* bender-include: ../_tools/tools.js */
 
 'use strict';
@@ -14,20 +11,20 @@
 const getIteratorCount = bender.tools.core.getIteratorCount;
 
 const modules = bender.amd.require(
-	'treemodel/node',
-	'treemodel/nodelist',
-	'treemodel/element',
-	'treemodel/attribute'
+	'core/treemodel/node',
+	'core/treemodel/nodelist',
+	'core/treemodel/element',
+	'core/treemodel/attribute'
 );
 
 describe( 'Element', () => {
 	let Element, Node, NodeList, Attribute;
 
 	before( () => {
-		Element = modules[ 'treemodel/element' ];
-		Node = modules[ 'treemodel/node' ];
-		NodeList = modules[ 'treemodel/nodelist' ];
-		Attribute = modules[ 'treemodel/attribute' ];
+		Element = modules[ 'core/treemodel/element' ];
+		Node = modules[ 'core/treemodel/node' ];
+		NodeList = modules[ 'core/treemodel/nodelist' ];
+		Attribute = modules[ 'core/treemodel/attribute' ];
 	} );
 
 	describe( 'constructor', () => {

+ 12 - 12
packages/ckeditor5-engine/tests/treemodel/liveposition.js

@@ -8,12 +8,12 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'treemodel/document',
-	'treemodel/element',
-	'treemodel/position',
-	'treemodel/liveposition',
-	'treemodel/range',
-	'emittermixin'
+	'core/treemodel/document',
+	'core/treemodel/element',
+	'core/treemodel/position',
+	'core/treemodel/liveposition',
+	'core/treemodel/range',
+	'core/emittermixin'
 );
 
 describe( 'LivePosition', () => {
@@ -21,12 +21,12 @@ describe( 'LivePosition', () => {
 	let doc, root, ul, p, li1, li2;
 
 	before( () => {
-		Document = modules[ 'treemodel/document' ];
-		Element = modules[ 'treemodel/element' ];
-		Position = modules[ 'treemodel/position' ];
-		LivePosition = modules[ 'treemodel/liveposition' ];
-		Range = modules[ 'treemodel/range' ];
-		EmitterMixin = modules.emittermixin;
+		Document = modules[ 'core/treemodel/document' ];
+		Element = modules[ 'core/treemodel/element' ];
+		Position = modules[ 'core/treemodel/position' ];
+		LivePosition = modules[ 'core/treemodel/liveposition' ];
+		Range = modules[ 'core/treemodel/range' ];
+		EmitterMixin = modules[ 'core/emittermixin' ];
 
 		doc = new Document();
 		root = doc.createRoot( 'root' );

+ 12 - 12
packages/ckeditor5-engine/tests/treemodel/liverange.js

@@ -8,12 +8,12 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'treemodel/document',
-	'treemodel/element',
-	'treemodel/position',
-	'treemodel/liverange',
-	'treemodel/range',
-	'emittermixin'
+	'core/treemodel/document',
+	'core/treemodel/element',
+	'core/treemodel/position',
+	'core/treemodel/liverange',
+	'core/treemodel/range',
+	'core/emittermixin'
 );
 
 describe( 'LiveRange', () => {
@@ -21,12 +21,12 @@ describe( 'LiveRange', () => {
 	let doc, root, ul, p;
 
 	before( () => {
-		Document = modules[ 'treemodel/document' ];
-		Element = modules[ 'treemodel/element' ];
-		Position = modules[ 'treemodel/position' ];
-		LiveRange = modules[ 'treemodel/liverange' ];
-		Range = modules[ 'treemodel/range' ];
-		EmitterMixin = modules.emittermixin;
+		Document = modules[ 'core/treemodel/document' ];
+		Element = modules[ 'core/treemodel/element' ];
+		Position = modules[ 'core/treemodel/position' ];
+		LiveRange = modules[ 'core/treemodel/liverange' ];
+		Range = modules[ 'core/treemodel/range' ];
+		EmitterMixin = modules[ 'core/emittermixin' ];
 
 		doc = new Document();
 		root = doc.createRoot( 'root' );

+ 12 - 13
packages/ckeditor5-engine/tests/treemodel/node.js

@@ -4,7 +4,6 @@
  */
 
 /* bender-tags: treemodel */
-
 /* bender-include: ../_tools/tools.js */
 
 'use strict';
@@ -12,12 +11,12 @@
 const getIteratorCount = bender.tools.core.getIteratorCount;
 
 const modules = bender.amd.require(
-	'treemodel/element',
-	'treemodel/character',
-	'treemodel/attribute',
-	'treemodel/attributelist',
-	'treemodel/nodelist',
-	'ckeditorerror'
+	'core/treemodel/element',
+	'core/treemodel/character',
+	'core/treemodel/attribute',
+	'core/treemodel/attributelist',
+	'core/treemodel/nodelist',
+	'core/ckeditorerror'
 );
 
 describe( 'Node', () => {
@@ -29,12 +28,12 @@ describe( 'Node', () => {
 	let attrFooBar;
 
 	before( () => {
-		Element = modules[ 'treemodel/element' ];
-		Character = modules[ 'treemodel/character' ];
-		Attribute = modules[ 'treemodel/attribute' ];
-		AttributeList = modules[ 'treemodel/attributelist' ];
-		NodeList = modules[ 'treemodel/nodelist' ];
-		CKEditorError = modules.ckeditorerror;
+		Element = modules[ 'core/treemodel/element' ];
+		Character = modules[ 'core/treemodel/character' ];
+		Attribute = modules[ 'core/treemodel/attribute' ];
+		AttributeList = modules[ 'core/treemodel/attributelist' ];
+		NodeList = modules[ 'core/treemodel/nodelist' ];
+		CKEditorError = modules[ 'core/ckeditorerror' ];
 
 		charB = new Character( 'b' );
 		charA = new Character( 'a' );

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

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

+ 18 - 18
packages/ckeditor5-engine/tests/treemodel/operation/attributeoperation.js

@@ -13,30 +13,30 @@
 const getIteratorCount = bender.tools.core.getIteratorCount;
 
 const modules = bender.amd.require(
-	'treemodel/document',
-	'treemodel/element',
-	'treemodel/operation/attributeoperation',
-	'treemodel/position',
-	'treemodel/range',
-	'treemodel/character',
-	'treemodel/attribute',
-	'treemodel/text',
-	'ckeditorerror'
+	'core/treemodel/document',
+	'core/treemodel/element',
+	'core/treemodel/operation/attributeoperation',
+	'core/treemodel/position',
+	'core/treemodel/range',
+	'core/treemodel/character',
+	'core/treemodel/attribute',
+	'core/treemodel/text',
+	'core/ckeditorerror'
 );
 
 describe( 'AttributeOperation', () => {
 	let Document, Element, AttributeOperation, Position, Range, Character, Attribute, Text, CKEditorError;
 
 	before( () => {
-		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;
+		Document = modules[ 'core/treemodel/document' ];
+		Element = modules[ 'core/treemodel/element' ];
+		AttributeOperation = modules[ 'core/treemodel/operation/attributeoperation' ];
+		Position = modules[ 'core/treemodel/position' ];
+		Range = modules[ 'core/treemodel/range' ];
+		Character = modules[ 'core/treemodel/character' ];
+		Attribute = modules[ 'core/treemodel/attribute' ];
+		Text = modules[ 'core/treemodel/text' ];
+		CKEditorError = modules[ 'core/ckeditorerror' ];
 	} );
 
 	let doc, root;

+ 15 - 15
packages/ckeditor5-engine/tests/treemodel/operation/insertoperation.js

@@ -9,27 +9,27 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'treemodel/document',
-	'treemodel/node',
-	'treemodel/nodelist',
-	'treemodel/operation/insertoperation',
-	'treemodel/operation/removeoperation',
-	'treemodel/position',
-	'treemodel/character',
-	'treemodel/nodelist'
+	'core/treemodel/document',
+	'core/treemodel/node',
+	'core/treemodel/nodelist',
+	'core/treemodel/operation/insertoperation',
+	'core/treemodel/operation/removeoperation',
+	'core/treemodel/position',
+	'core/treemodel/character',
+	'core/treemodel/nodelist'
 );
 
 describe( 'InsertOperation', () => {
 	let Document, Node, NodeList, InsertOperation, RemoveOperation, Position, Character;
 
 	before( () => {
-		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' ];
+		Document = modules[ 'core/treemodel/document' ];
+		Node = modules[ 'core/treemodel/node' ];
+		NodeList = modules[ 'core/treemodel/nodelist' ];
+		InsertOperation = modules[ 'core/treemodel/operation/insertoperation' ];
+		RemoveOperation = modules[ 'core/treemodel/operation/removeoperation' ];
+		Position = modules[ 'core/treemodel/position' ];
+		Character = modules[ 'core/treemodel/character' ];
 	} );
 
 	let doc, root;

+ 12 - 12
packages/ckeditor5-engine/tests/treemodel/operation/moveoperation.js

@@ -9,24 +9,24 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'treemodel/document',
-	'treemodel/operation/moveoperation',
-	'treemodel/position',
-	'treemodel/element',
-	'treemodel/nodelist',
-	'ckeditorerror'
+	'core/treemodel/document',
+	'core/treemodel/operation/moveoperation',
+	'core/treemodel/position',
+	'core/treemodel/element',
+	'core/treemodel/nodelist',
+	'core/ckeditorerror'
 );
 
 describe( 'MoveOperation', () => {
 	let Document, MoveOperation, Position, Element, NodeList, CKEditorError;
 
 	before( () => {
-		Document = modules[ 'treemodel/document' ];
-		MoveOperation = modules[ 'treemodel/operation/moveoperation' ];
-		Position = modules[ 'treemodel/position' ];
-		Element = modules[ 'treemodel/element' ];
-		NodeList = modules[ 'treemodel/nodelist' ];
-		CKEditorError = modules.ckeditorerror;
+		Document = modules[ 'core/treemodel/document' ];
+		MoveOperation = modules[ 'core/treemodel/operation/moveoperation' ];
+		Position = modules[ 'core/treemodel/position' ];
+		Element = modules[ 'core/treemodel/element' ];
+		NodeList = modules[ 'core/treemodel/nodelist' ];
+		CKEditorError = modules[ 'core/ckeditorerror' ];
 	} );
 
 	let doc, root;

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

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

+ 10 - 10
packages/ckeditor5-engine/tests/treemodel/operation/reinsertoperation.js

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

+ 10 - 10
packages/ckeditor5-engine/tests/treemodel/operation/removeoperation.js

@@ -8,22 +8,22 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'treemodel/document',
-	'treemodel/operation/reinsertoperation',
-	'treemodel/operation/removeoperation',
-	'treemodel/operation/moveoperation',
-	'treemodel/position'
+	'core/treemodel/document',
+	'core/treemodel/operation/reinsertoperation',
+	'core/treemodel/operation/removeoperation',
+	'core/treemodel/operation/moveoperation',
+	'core/treemodel/position'
 );
 
 describe( 'RemoveOperation', () => {
 	let Document, ReinsertOperation, RemoveOperation, MoveOperation, Position;
 
 	before( () => {
-		Document = modules[ 'treemodel/document' ];
-		ReinsertOperation = modules[ 'treemodel/operation/reinsertoperation' ];
-		RemoveOperation = modules[ 'treemodel/operation/removeoperation' ];
-		MoveOperation = modules[ 'treemodel/operation/removeoperation' ];
-		Position = modules[ 'treemodel/position' ];
+		Document = modules[ 'core/treemodel/document' ];
+		ReinsertOperation = modules[ 'core/treemodel/operation/reinsertoperation' ];
+		RemoveOperation = modules[ 'core/treemodel/operation/removeoperation' ];
+		MoveOperation = modules[ 'core/treemodel/operation/removeoperation' ];
+		Position = modules[ 'core/treemodel/position' ];
 	} );
 
 	let doc, root, graveyard;

+ 21 - 21
packages/ckeditor5-engine/tests/treemodel/operation/transform.js

@@ -11,16 +11,16 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'treemodel/rootelement',
-	'treemodel/node',
-	'treemodel/position',
-	'treemodel/range',
-	'treemodel/attribute',
-	'treemodel/operation/transform',
-	'treemodel/operation/insertoperation',
-	'treemodel/operation/attributeoperation',
-	'treemodel/operation/moveoperation',
-	'treemodel/operation/nooperation'
+	'core/treemodel/rootelement',
+	'core/treemodel/node',
+	'core/treemodel/position',
+	'core/treemodel/range',
+	'core/treemodel/attribute',
+	'core/treemodel/operation/transform',
+	'core/treemodel/operation/insertoperation',
+	'core/treemodel/operation/attributeoperation',
+	'core/treemodel/operation/moveoperation',
+	'core/treemodel/operation/nooperation'
 );
 
 describe( 'transform', () => {
@@ -28,17 +28,17 @@ describe( 'transform', () => {
 	let transform;
 
 	before( () => {
-		RootElement = modules[ 'treemodel/rootelement' ];
-		Node = modules[ 'treemodel/node' ];
-		Position = modules[ 'treemodel/position' ];
-		Range = modules[ 'treemodel/range' ];
-		Attribute = modules[ 'treemodel/attribute' ];
-		InsertOperation = modules[ 'treemodel/operation/insertoperation' ];
-		AttributeOperation = modules[ 'treemodel/operation/attributeoperation' ];
-		MoveOperation = modules[ 'treemodel/operation/moveoperation' ];
-		NoOperation = modules[ 'treemodel/operation/nooperation' ];
-
-		transform = modules[ 'treemodel/operation/transform' ];
+		RootElement = modules[ 'core/treemodel/rootelement' ];
+		Node = modules[ 'core/treemodel/node' ];
+		Position = modules[ 'core/treemodel/position' ];
+		Range = modules[ 'core/treemodel/range' ];
+		Attribute = modules[ 'core/treemodel/attribute' ];
+		InsertOperation = modules[ 'core/treemodel/operation/insertoperation' ];
+		AttributeOperation = modules[ 'core/treemodel/operation/attributeoperation' ];
+		MoveOperation = modules[ 'core/treemodel/operation/moveoperation' ];
+		NoOperation = modules[ 'core/treemodel/operation/nooperation' ];
+
+		transform = modules[ 'core/treemodel/operation/transform' ];
 	} );
 
 	let root, op, nodeA, nodeB, expected, baseVersion;

+ 12 - 12
packages/ckeditor5-engine/tests/treemodel/position.js

@@ -8,12 +8,12 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'treemodel/element',
-	'treemodel/character',
-	'treemodel/position',
-	'treemodel/document',
-	'ckeditorerror',
-	'treemodel/nodelist'
+	'core/treemodel/element',
+	'core/treemodel/character',
+	'core/treemodel/position',
+	'core/treemodel/document',
+	'core/ckeditorerror',
+	'core/treemodel/nodelist'
 );
 
 describe( 'position', () => {
@@ -33,12 +33,12 @@ describe( 'position', () => {
 	//        |- a   Before: [ 1, 1, 1 ] After: [ 1, 1, 2 ]
 	//        |- r   Before: [ 1, 1, 2 ] After: [ 1, 1, 3 ]
 	before( () => {
-		Element = modules[ 'treemodel/element' ];
-		Character = modules[ 'treemodel/character' ];
-		Document = modules[ 'treemodel/document' ];
-		NodeList = modules[ 'treemodel/nodelist' ];
-		Position = modules[ 'treemodel/position' ];
-		CKEditorError = modules.ckeditorerror;
+		Element = modules[ 'core/treemodel/element' ];
+		Character = modules[ 'core/treemodel/character' ];
+		Document = modules[ 'core/treemodel/document' ];
+		NodeList = modules[ 'core/treemodel/nodelist' ];
+		Position = modules[ 'core/treemodel/position' ];
+		CKEditorError = modules[ 'core/ckeditorerror' ];
 
 		doc = new Document();
 

+ 12 - 12
packages/ckeditor5-engine/tests/treemodel/positioniterator.js

@@ -8,12 +8,12 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'treemodel/document',
-	'treemodel/element',
-	'treemodel/character',
-	'treemodel/positioniterator',
-	'treemodel/position',
-	'treemodel/range'
+	'core/treemodel/document',
+	'core/treemodel/element',
+	'core/treemodel/character',
+	'core/treemodel/positioniterator',
+	'core/treemodel/position',
+	'core/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[ 'treemodel/document' ];
-		Element = modules[ 'treemodel/element' ];
-		Character = modules[ 'treemodel/character' ];
-		PositionIterator = modules[ 'treemodel/positioniterator' ];
-		Position = modules[ 'treemodel/position' ];
-		Range = modules[ 'treemodel/range' ];
+		Document = modules[ 'core/treemodel/document' ];
+		Element = modules[ 'core/treemodel/element' ];
+		Character = modules[ 'core/treemodel/character' ];
+		PositionIterator = modules[ 'core/treemodel/positioniterator' ];
+		Position = modules[ 'core/treemodel/position' ];
+		Range = modules[ 'core/treemodel/range' ];
 
 		ELEMENT_ENTER = PositionIterator.ELEMENT_ENTER;
 		ELEMENT_LEAVE = PositionIterator.ELEMENT_LEAVE;

+ 10 - 10
packages/ckeditor5-engine/tests/treemodel/range.js

@@ -8,22 +8,22 @@
 'use strict';
 
 const modules = bender.amd.require(
-	'treemodel/range',
-	'treemodel/position',
-	'treemodel/element',
-	'treemodel/character',
-	'treemodel/document'
+	'core/treemodel/range',
+	'core/treemodel/position',
+	'core/treemodel/element',
+	'core/treemodel/character',
+	'core/treemodel/document'
 );
 
 describe( 'Range', () => {
 	let Range, Position, Element, Character, Document;
 
 	before( () => {
-		Position = modules[ 'treemodel/position' ];
-		Range = modules[ 'treemodel/range' ];
-		Element = modules[ 'treemodel/element' ];
-		Character = modules[ 'treemodel/character' ];
-		Document = modules[ 'treemodel/document' ];
+		Position = modules[ 'core/treemodel/position' ];
+		Range = modules[ 'core/treemodel/range' ];
+		Element = modules[ 'core/treemodel/element' ];
+		Character = modules[ 'core/treemodel/character' ];
+		Document = modules[ 'core/treemodel/document' ];
 	} );
 
 	let range, start, end, root, otherRoot;

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

@@ -3,10 +3,7 @@
  * For licensing, see LICENSE.md.
  */
 
-/* jshint expr: true */
-
 /* bender-tags: treemodel */
-
 /* bender-include: ../_tools/tools.js */
 
 'use strict';
@@ -14,18 +11,18 @@
 const getIteratorCount = bender.tools.core.getIteratorCount;
 
 const modules = bender.amd.require(
-	'treemodel/document',
-	'treemodel/element',
-	'treemodel/rootelement'
+	'core/treemodel/document',
+	'core/treemodel/element',
+	'core/treemodel/rootelement'
 );
 
 describe( 'Element', () => {
 	let Document, Element, RootElement;
 
 	before( () => {
-		Document = modules[ 'treemodel/document' ];
-		Element = modules[ 'treemodel/element' ];
-		RootElement = modules[ 'treemodel/rootelement' ];
+		Document = modules[ 'core/treemodel/document' ];
+		Element = modules[ 'core/treemodel/element' ];
+		RootElement = modules[ 'core/treemodel/rootelement' ];
 	} );
 
 	describe( 'constructor', () => {

+ 20 - 20
packages/ckeditor5-engine/tests/treemodel/selection.js

@@ -12,16 +12,16 @@
 const getIteratorCount = bender.tools.core.getIteratorCount;
 
 const modules = bender.amd.require(
-	'treemodel/document',
-	'treemodel/attribute',
-	'treemodel/element',
-	'treemodel/range',
-	'treemodel/position',
-	'treemodel/liverange',
-	'treemodel/selection',
-	'treemodel/operation/insertoperation',
-	'treemodel/operation/moveoperation',
-	'ckeditorerror'
+	'core/treemodel/document',
+	'core/treemodel/attribute',
+	'core/treemodel/element',
+	'core/treemodel/range',
+	'core/treemodel/position',
+	'core/treemodel/liverange',
+	'core/treemodel/selection',
+	'core/treemodel/operation/insertoperation',
+	'core/treemodel/operation/moveoperation',
+	'core/ckeditorerror'
 );
 
 describe( 'Selection', () => {
@@ -29,16 +29,16 @@ describe( 'Selection', () => {
 	let attrFooBar;
 
 	before( () => {
-		Document = modules[ 'treemodel/document' ];
-		Attribute = modules[ 'treemodel/attribute' ];
-		Element = modules[ 'treemodel/element' ];
-		Range = modules[ 'treemodel/range' ];
-		Position = modules[ 'treemodel/position' ];
-		LiveRange = modules[ 'treemodel/liverange' ];
-		Selection = modules[ 'treemodel/selection' ];
-		InsertOperation = modules[ 'treemodel/operation/insertoperation' ];
-		MoveOperation = modules[ 'treemodel/operation/moveoperation' ];
-		CKEditorError = modules.ckeditorerror;
+		Document = modules[ 'core/treemodel/document' ];
+		Attribute = modules[ 'core/treemodel/attribute' ];
+		Element = modules[ 'core/treemodel/element' ];
+		Range = modules[ 'core/treemodel/range' ];
+		Position = modules[ 'core/treemodel/position' ];
+		LiveRange = modules[ 'core/treemodel/liverange' ];
+		Selection = modules[ 'core/treemodel/selection' ];
+		InsertOperation = modules[ 'core/treemodel/operation/insertoperation' ];
+		MoveOperation = modules[ 'core/treemodel/operation/moveoperation' ];
+		CKEditorError = modules[ 'core/ckeditorerror' ];
 
 		attrFooBar = new Attribute( 'foo', 'bar' );
 	} );

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

@@ -3,22 +3,20 @@
  * For licensing, see LICENSE.md.
  */
 
-/* jshint expr: true */
-
 /* bender-tags: treemodel */
 
 'use strict';
 
 const modules = bender.amd.require(
-	'treemodel/text',
-	'treemodel/attribute'
+	'core/treemodel/text',
+	'core/treemodel/attribute'
 );
 
 describe( 'Text', () => {
 	describe( 'constructor', () => {
 		it( 'should create character without attributes', () => {
-			const Text = modules[ 'treemodel/text' ];
-			const Attribute = modules[ 'treemodel/attribute' ];
+			const Text = modules[ 'core/treemodel/text' ];
+			const Attribute = modules[ 'core/treemodel/attribute' ];
 
 			let attrs = [ new Attribute( 'bold', true ) ];
 			let text = new Text( 'bar', attrs );