Explorar o código

Ported the TDM.

Piotrek Koszuliński %!s(int64=10) %!d(string=hai) anos
pai
achega
95e4c2aaa1
Modificáronse 70 ficheiros con 3950 adicións e 4089 borrados
  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 );