Browse Source

Use path to describe position instead of node + offset. Minor changes.

Piotr Jasiun 10 years ago
parent
commit
0507f422e0

+ 12 - 0
packages/ckeditor5-utils/src/document/node.js

@@ -96,6 +96,18 @@ CKEDITOR.define( function() {
 
 			return ( pos !== null && this.parent.children[ pos - 1 ] ) || null;
 		}
+
+		getPath() {
+			var path = [];
+			var node = this; // jscs:ignore safeContextKeyword
+
+			while ( node.parent ) {
+				path.unshift( node.positionInParent );
+				node = node.parent;
+			}
+
+			return path;
+		}
 	}
 
 	return Node;

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

@@ -5,70 +5,123 @@
 
 'use strict';
 
-CKEDITOR.define( function() {
+CKEDITOR.define( [ 'utils' ], function( utils ) {
 	/**
 	 * Position is always before of after a node.
+	 * See {@link #path} property for more information.
 	 *
 	 * @class document.Position
 	 */
 	class Position {
 		/**
-		 * Create a position.
+		 * Creates a position.
 		 *
-		 * @param {document.element} parent Parents element.
-		 * @param {Number} offset Offset in that element.
+		 * @param {Array} path Position path. See {@link #path} property for more information.
+		 * @param {document.Document} document Document which position refers to.
 		 */
-		constructor( parent, offset ) {
+		constructor( path, document ) {
 			/**
-			 * Parent element.
+			 * Position of the node it the tree. For example:
 			 *
-			 * @type {document.Element}
+			 * 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 {Array}
 			 */
-			this.parent = parent;
+			this.path = path;
 
 			/**
-			 * Node offset in the parent element.
+			 * Document which position refers to.
 			 *
-			 * @type {Number}
+			 * @type {document.Document}
 			 */
-			this.offset = offset;
+			this.document = document;
 		}
 
 		/**
-		 * Position of the node it the tree. For example:
+		 * Create position from the parent element and the offset in that element.
 		 *
-		 * root          Before: []          After: []
-		 *  |- p         Before: [ 0 ]       After: [ 1 ]
-		 *  |- ul        Before: [ 1 ]       After: [ 2 ]
-		 *     |- li     Before: [ 1, 0 ]    After: [ 1, 1 ]
-		 *     |  |- f   Before: [ 1, 0, 0 ] After: [ 1, 0, 1 ]
-		 *     |  |- o   Before: [ 1, 0, 1 ] After: [ 1, 0, 2 ]
-		 *     |  |- o   Before: [ 1, 0, 2 ] After: [ 1, 0, 3 ]
-		 *     |- li     Before: [ 1, 1 ]    After: [ 1, 2 ]
-		 *        |- b   Before: [ 1, 1, 0 ] After: [ 1, 1, 1 ]
-		 *        |- a   Before: [ 1, 1, 1 ] After: [ 1, 1, 2 ]
-		 *        |- r   Before: [ 1, 1, 2 ] After: [ 1, 1, 3 ]
+		 * @param {document.Element} parent Position parent element.
+		 * @param {Number} offset Position offset.
+		 * @param {document.Document} document Document which position refers to.
+		 */
+		static makePositionFromParentAndOffset( parent, offset, document ) {
+			var path = parent.getPath();
+
+			path.push( offset );
+
+			return new Position( path, document );
+		}
+
+		/**
+		 * Set the position before given node.
 		 *
-		 * @type {Array}
+		 * @param {document.node} node Node the position should be directly before.
+		 * @param {document.Document} document Document which position refers to.
 		 */
-		get path() {
-			var path = [];
+		static makePositionBefore( node, document ) {
+			if ( !node.parent ) {
+				throw 'You can not make position before root.';
+			}
 
-			var parent = this.parent;
+			return Position.makePositionFromParentAndOffset( node.parent, node.positionInParent, document );
+		}
 
-			while ( parent.parent ) {
-				path.unshift( parent.positionInParent );
-				parent = parent.parent;
+		/**
+		 * Set the position after given node.
+		 *
+		 * @param {document.node} node Node the position should be directly after.
+		 * @param {document.Document} document Document which position refers to.
+		 */
+		static makePositionAfter( node, document ) {
+			if ( !node.parent ) {
+				throw 'You can not make position after root.';
 			}
 
-			path.push( this.offset );
+			return Position.makePositionFromParentAndOffset( node.parent, node.positionInParent + 1, document );
+		}
 
-			return path;
+		/**
+		 * Element which is a parent of the position.
+		 *
+		 * @readonly
+		 * @property {document.Element} parent
+		 */
+		get parent() {
+			var parent = this.document.root;
+
+			var i, len;
+
+			for ( i = 0, len = this.path.length - 1; i < len; i++ ) {
+				parent = parent.children[ this.path[ i ] ];
+			}
+
+			return parent;
+		}
+
+		/**
+		 * Position offset in the parent, which is the last element of the path.
+		 *
+		 * @readonly
+		 * @property {Number} offset
+		 */
+		get offset() {
+			return utils.last( this.path );
 		}
 
 		/**
 		 * Node directly before the position.
 		 *
+		 * @readonly
 		 * @type {Node}
 		 */
 		get nodeBefore() {
@@ -78,6 +131,7 @@ CKEDITOR.define( function() {
 		/**
 		 * Node directly after the position.
 		 *
+		 * @readonly
 		 * @type {Node}
 		 */
 		get nodeAfter() {
@@ -85,13 +139,13 @@ CKEDITOR.define( function() {
 		}
 
 		/**
-		 * Two positions equals if parent and offset equal.
+		 * Two positions equals if paths equal.
 		 *
 		 * @param {document.Position} otherPosition Position to compare.
 		 * @returns {Boolean} true if positions equal.
 		 */
-		equals( otherPosition ) {
-			return this.offset === otherPosition.offset && this.parent === otherPosition.parent;
+		isEqual( otherPosition ) {
+			return utils.isEqual( this.path, otherPosition.path );
 		}
 	}
 

+ 12 - 10
packages/ckeditor5-utils/src/document/positioniterator.js

@@ -61,28 +61,29 @@ CKEDITOR.define( [
 		 */
 		next() {
 			var position = this.position;
+			var parent = position.parent;
 
 			// We are at the end of the root.
-			if ( position.parent.parent === null && position.offset === position.parent.children.length ) {
+			if ( parent.parent === null && position.offset === parent.children.length ) {
 				return { done: true };
 			}
 
-			if ( this.boundaries && position.equals( this.boundaries.end ) ) {
+			if ( this.boundaries && position.isEqual( this.boundaries.end ) ) {
 				return { done: true };
 			}
 
 			var nodeAfter = position.nodeAfter;
 
 			if ( nodeAfter instanceof Element ) {
-				this.position = new Position( nodeAfter, 0 );
+				this.position = Position.makePositionFromParentAndOffset( nodeAfter, 0, position.document );
 
 				return formatReturnValue( OPENING_TAG, nodeAfter );
 			} else if ( nodeAfter instanceof Character ) {
-				this.position = new Position( position.parent, position.offset + 1 );
+				this.position = Position.makePositionFromParentAndOffset( parent, position.offset + 1, position.document );
 
 				return formatReturnValue( CHARACTER, nodeAfter );
 			} else {
-				this.position = new Position( position.parent.parent, position.parent.positionInParent + 1 );
+				this.position = Position.makePositionFromParentAndOffset( parent.parent, parent.positionInParent + 1, position.document );
 
 				return formatReturnValue( CLOSING_TAG, this.position.nodeBefore );
 			}
@@ -100,28 +101,29 @@ CKEDITOR.define( [
 		 */
 		previous() {
 			var position = this.position;
+			var parent = position.parent;
 
 			// We are at the begging of the root.
-			if ( position.parent.parent === null && position.offset === 0 ) {
+			if ( parent.parent === null && position.offset === 0 ) {
 				return { done: true };
 			}
 
-			if ( this.boundaries && position.equals( this.boundaries.start ) ) {
+			if ( this.boundaries && position.isEqual( this.boundaries.start ) ) {
 				return { done: true };
 			}
 
 			var nodeBefore = position.nodeBefore;
 
 			if ( nodeBefore instanceof Element ) {
-				this.position = new Position( nodeBefore, nodeBefore.children.length );
+				this.position = Position.makePositionFromParentAndOffset( nodeBefore, nodeBefore.children.length, position.document );
 
 				return formatReturnValue( CLOSING_TAG, nodeBefore );
 			} else if ( nodeBefore instanceof Character ) {
-				this.position = new Position( position.parent, position.offset - 1 );
+				this.position = Position.makePositionFromParentAndOffset( parent, position.offset - 1, position.document );
 
 				return formatReturnValue( CHARACTER, nodeBefore );
 			} else {
-				this.position = new Position( position.parent.parent, position.parent.positionInParent );
+				this.position = Position.makePositionFromParentAndOffset( parent.parent, parent.positionInParent, position.document );
 
 				return formatReturnValue( OPENING_TAG, this.position.nodeAfter );
 			}

+ 2 - 2
packages/ckeditor5-utils/src/document/range.js

@@ -40,8 +40,8 @@ CKEDITOR.define( [ 'document/positioniterator' ], function( PositionIterator ) {
 		 * @param {document.Range} otherRange Range to compare.
 		 * @returns {Boolean} true if ranges equal.
 		 */
-		equals( otherRange ) {
-			return this.start.equals( otherRange.start ) && this.end.equals( otherRange.end );
+		isEqual( otherRange ) {
+			return this.start.isEqual( otherRange.start ) && this.end.isEqual( otherRange.end );
 		}
 
 		/**

+ 380 - 1
packages/ckeditor5-utils/src/lib/lodash/lodash-ckeditor.js

@@ -1,7 +1,7 @@
 /**
  * @license
  * lodash 3.10.1 (Custom Build) <https://lodash.com/>
- * Build: `lodash modern exports="amd" include="clone,extend,isPlainObject,isObject,isArray" --development --output src/lib/lodash/lodash-ckeditor.js`
+ * Build: `lodash modern exports="amd" include="clone,extend,isPlainObject,isObject,isArray,last,isEqual" --development --output src/lib/lodash/lodash-ckeditor.js`
  * Copyright 2012-2015 The Dojo Foundation <http://dojofoundation.org/>
  * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
  * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
@@ -53,6 +53,21 @@
   /** Used to detect unsigned integer values. */
   var reIsUint = /^\d+$/;
 
+  /** Used to identify `toStringTag` values of typed arrays. */
+  var typedArrayTags = {};
+  typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
+  typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
+  typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
+  typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
+  typedArrayTags[uint32Tag] = true;
+  typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
+  typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
+  typedArrayTags[dateTag] = typedArrayTags[errorTag] =
+  typedArrayTags[funcTag] = typedArrayTags[mapTag] =
+  typedArrayTags[numberTag] = typedArrayTags[objectTag] =
+  typedArrayTags[regexpTag] = typedArrayTags[setTag] =
+  typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false;
+
   /** Used to identify `toStringTag` values supported by `_.clone`. */
   var cloneableTags = {};
   cloneableTags[argsTag] = cloneableTags[arrayTag] =
@@ -294,6 +309,28 @@
   }
 
   /**
+   * A specialized version of `_.some` for arrays without support for callback
+   * shorthands and `this` binding.
+   *
+   * @private
+   * @param {Array} array The array to iterate over.
+   * @param {Function} predicate The function invoked per iteration.
+   * @returns {boolean} Returns `true` if any element passes the predicate check,
+   *  else `false`.
+   */
+  function arraySome(array, predicate) {
+    var index = -1,
+        length = array.length;
+
+    while (++index < length) {
+      if (predicate(array[index], index, array)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
    * A specialized version of `_.assign` for customizing assigned values without
    * support for argument juggling, multiple sources, and `this` binding `customizer`
    * functions.
@@ -467,6 +504,107 @@
   }
 
   /**
+   * The base implementation of `_.isEqual` without support for `this` binding
+   * `customizer` functions.
+   *
+   * @private
+   * @param {*} value The value to compare.
+   * @param {*} other The other value to compare.
+   * @param {Function} [customizer] The function to customize comparing values.
+   * @param {boolean} [isLoose] Specify performing partial comparisons.
+   * @param {Array} [stackA] Tracks traversed `value` objects.
+   * @param {Array} [stackB] Tracks traversed `other` objects.
+   * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+   */
+  function baseIsEqual(value, other, customizer, isLoose, stackA, stackB) {
+    if (value === other) {
+      return true;
+    }
+    if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) {
+      return value !== value && other !== other;
+    }
+    return baseIsEqualDeep(value, other, baseIsEqual, customizer, isLoose, stackA, stackB);
+  }
+
+  /**
+   * A specialized version of `baseIsEqual` for arrays and objects which performs
+   * deep comparisons and tracks traversed objects enabling objects with circular
+   * references to be compared.
+   *
+   * @private
+   * @param {Object} object The object to compare.
+   * @param {Object} other The other object to compare.
+   * @param {Function} equalFunc The function to determine equivalents of values.
+   * @param {Function} [customizer] The function to customize comparing objects.
+   * @param {boolean} [isLoose] Specify performing partial comparisons.
+   * @param {Array} [stackA=[]] Tracks traversed `value` objects.
+   * @param {Array} [stackB=[]] Tracks traversed `other` objects.
+   * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+   */
+  function baseIsEqualDeep(object, other, equalFunc, customizer, isLoose, stackA, stackB) {
+    var objIsArr = isArray(object),
+        othIsArr = isArray(other),
+        objTag = arrayTag,
+        othTag = arrayTag;
+
+    if (!objIsArr) {
+      objTag = objToString.call(object);
+      if (objTag == argsTag) {
+        objTag = objectTag;
+      } else if (objTag != objectTag) {
+        objIsArr = isTypedArray(object);
+      }
+    }
+    if (!othIsArr) {
+      othTag = objToString.call(other);
+      if (othTag == argsTag) {
+        othTag = objectTag;
+      } else if (othTag != objectTag) {
+        othIsArr = isTypedArray(other);
+      }
+    }
+    var objIsObj = objTag == objectTag,
+        othIsObj = othTag == objectTag,
+        isSameTag = objTag == othTag;
+
+    if (isSameTag && !(objIsArr || objIsObj)) {
+      return equalByTag(object, other, objTag);
+    }
+    if (!isLoose) {
+      var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
+          othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
+
+      if (objIsWrapped || othIsWrapped) {
+        return equalFunc(objIsWrapped ? object.value() : object, othIsWrapped ? other.value() : other, customizer, isLoose, stackA, stackB);
+      }
+    }
+    if (!isSameTag) {
+      return false;
+    }
+    // Assume cyclic values are equal.
+    // For more information on detecting circular references see https://es5.github.io/#JO.
+    stackA || (stackA = []);
+    stackB || (stackB = []);
+
+    var length = stackA.length;
+    while (length--) {
+      if (stackA[length] == object) {
+        return stackB[length] == other;
+      }
+    }
+    // Add `object` and `other` to the stack of traversed objects.
+    stackA.push(object);
+    stackB.push(other);
+
+    var result = (objIsArr ? equalArrays : equalObjects)(object, other, equalFunc, customizer, isLoose, stackA, stackB);
+
+    stackA.pop();
+    stackB.pop();
+
+    return result;
+  }
+
+  /**
    * The base implementation of `_.property` without support for deep paths.
    *
    * @private
@@ -591,6 +729,151 @@
   }
 
   /**
+   * A specialized version of `baseIsEqualDeep` for arrays with support for
+   * partial deep comparisons.
+   *
+   * @private
+   * @param {Array} array The array to compare.
+   * @param {Array} other The other array to compare.
+   * @param {Function} equalFunc The function to determine equivalents of values.
+   * @param {Function} [customizer] The function to customize comparing arrays.
+   * @param {boolean} [isLoose] Specify performing partial comparisons.
+   * @param {Array} [stackA] Tracks traversed `value` objects.
+   * @param {Array} [stackB] Tracks traversed `other` objects.
+   * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
+   */
+  function equalArrays(array, other, equalFunc, customizer, isLoose, stackA, stackB) {
+    var index = -1,
+        arrLength = array.length,
+        othLength = other.length;
+
+    if (arrLength != othLength && !(isLoose && othLength > arrLength)) {
+      return false;
+    }
+    // Ignore non-index properties.
+    while (++index < arrLength) {
+      var arrValue = array[index],
+          othValue = other[index],
+          result = customizer ? customizer(isLoose ? othValue : arrValue, isLoose ? arrValue : othValue, index) : undefined;
+
+      if (result !== undefined) {
+        if (result) {
+          continue;
+        }
+        return false;
+      }
+      // Recursively compare arrays (susceptible to call stack limits).
+      if (isLoose) {
+        if (!arraySome(other, function(othValue) {
+              return arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB);
+            })) {
+          return false;
+        }
+      } else if (!(arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB))) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * A specialized version of `baseIsEqualDeep` for comparing objects of
+   * the same `toStringTag`.
+   *
+   * **Note:** This function only supports comparing values with tags of
+   * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
+   *
+   * @private
+   * @param {Object} object The object to compare.
+   * @param {Object} other The other object to compare.
+   * @param {string} tag The `toStringTag` of the objects to compare.
+   * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+   */
+  function equalByTag(object, other, tag) {
+    switch (tag) {
+      case boolTag:
+      case dateTag:
+        // Coerce dates and booleans to numbers, dates to milliseconds and booleans
+        // to `1` or `0` treating invalid dates coerced to `NaN` as not equal.
+        return +object == +other;
+
+      case errorTag:
+        return object.name == other.name && object.message == other.message;
+
+      case numberTag:
+        // Treat `NaN` vs. `NaN` as equal.
+        return (object != +object)
+          ? other != +other
+          : object == +other;
+
+      case regexpTag:
+      case stringTag:
+        // Coerce regexes to strings and treat strings primitives and string
+        // objects as equal. See https://es5.github.io/#x15.10.6.4 for more details.
+        return object == (other + '');
+    }
+    return false;
+  }
+
+  /**
+   * A specialized version of `baseIsEqualDeep` for objects with support for
+   * partial deep comparisons.
+   *
+   * @private
+   * @param {Object} object The object to compare.
+   * @param {Object} other The other object to compare.
+   * @param {Function} equalFunc The function to determine equivalents of values.
+   * @param {Function} [customizer] The function to customize comparing values.
+   * @param {boolean} [isLoose] Specify performing partial comparisons.
+   * @param {Array} [stackA] Tracks traversed `value` objects.
+   * @param {Array} [stackB] Tracks traversed `other` objects.
+   * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+   */
+  function equalObjects(object, other, equalFunc, customizer, isLoose, stackA, stackB) {
+    var objProps = keys(object),
+        objLength = objProps.length,
+        othProps = keys(other),
+        othLength = othProps.length;
+
+    if (objLength != othLength && !isLoose) {
+      return false;
+    }
+    var index = objLength;
+    while (index--) {
+      var key = objProps[index];
+      if (!(isLoose ? key in other : hasOwnProperty.call(other, key))) {
+        return false;
+      }
+    }
+    var skipCtor = isLoose;
+    while (++index < objLength) {
+      key = objProps[index];
+      var objValue = object[key],
+          othValue = other[key],
+          result = customizer ? customizer(isLoose ? othValue : objValue, isLoose? objValue : othValue, key) : undefined;
+
+      // Recursively compare objects (susceptible to call stack limits).
+      if (!(result === undefined ? equalFunc(objValue, othValue, customizer, isLoose, stackA, stackB) : result)) {
+        return false;
+      }
+      skipCtor || (skipCtor = key == 'constructor');
+    }
+    if (!skipCtor) {
+      var objCtor = object.constructor,
+          othCtor = other.constructor;
+
+      // Non `Object` object instances with different constructors are not equal.
+      if (objCtor != othCtor &&
+          ('constructor' in object && 'constructor' in other) &&
+          !(typeof objCtor == 'function' && objCtor instanceof objCtor &&
+            typeof othCtor == 'function' && othCtor instanceof othCtor)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
    * Gets the "length" property value of `object`.
    *
    * **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792)
@@ -791,6 +1074,26 @@
   /*------------------------------------------------------------------------*/
 
   /**
+   * Gets the last element of `array`.
+   *
+   * @static
+   * @memberOf _
+   * @category Array
+   * @param {Array} array The array to query.
+   * @returns {*} Returns the last element of `array`.
+   * @example
+   *
+   * _.last([1, 2, 3]);
+   * // => 3
+   */
+  function last(array) {
+    var length = array ? array.length : 0;
+    return length ? array[length - 1] : undefined;
+  }
+
+  /*------------------------------------------------------------------------*/
+
+  /**
    * Creates a function that invokes `func` with the `this` binding of the
    * created function and arguments from `start` and beyond provided as an array.
    *
@@ -950,6 +1253,56 @@
   };
 
   /**
+   * Performs a deep comparison between two values to determine if they are
+   * equivalent. If `customizer` is provided it's invoked to compare values.
+   * If `customizer` returns `undefined` comparisons are handled by the method
+   * instead. The `customizer` is bound to `thisArg` and invoked with up to
+   * three arguments: (value, other [, index|key]).
+   *
+   * **Note:** This method supports comparing arrays, booleans, `Date` objects,
+   * numbers, `Object` objects, regexes, and strings. Objects are compared by
+   * their own, not inherited, enumerable properties. Functions and DOM nodes
+   * are **not** supported. Provide a customizer function to extend support
+   * for comparing other values.
+   *
+   * @static
+   * @memberOf _
+   * @alias eq
+   * @category Lang
+   * @param {*} value The value to compare.
+   * @param {*} other The other value to compare.
+   * @param {Function} [customizer] The function to customize value comparisons.
+   * @param {*} [thisArg] The `this` binding of `customizer`.
+   * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+   * @example
+   *
+   * var object = { 'user': 'fred' };
+   * var other = { 'user': 'fred' };
+   *
+   * object == other;
+   * // => false
+   *
+   * _.isEqual(object, other);
+   * // => true
+   *
+   * // using a customizer callback
+   * var array = ['hello', 'goodbye'];
+   * var other = ['hi', 'goodbye'];
+   *
+   * _.isEqual(array, other, function(value, other) {
+   *   if (_.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/)) {
+   *     return true;
+   *   }
+   * });
+   * // => true
+   */
+  function isEqual(value, other, customizer, thisArg) {
+    customizer = typeof customizer == 'function' ? bindCallback(customizer, thisArg, 3) : undefined;
+    var result = customizer ? customizer(value, other) : undefined;
+    return  result === undefined ? baseIsEqual(value, other, customizer) : !!result;
+  }
+
+  /**
    * Checks if `value` is classified as a `Function` object.
    *
    * @static
@@ -1076,6 +1429,26 @@
     return result === undefined || hasOwnProperty.call(value, result);
   }
 
+  /**
+   * Checks if `value` is classified as a typed array.
+   *
+   * @static
+   * @memberOf _
+   * @category Lang
+   * @param {*} value The value to check.
+   * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+   * @example
+   *
+   * _.isTypedArray(new Uint8Array);
+   * // => true
+   *
+   * _.isTypedArray([]);
+   * // => false
+   */
+  function isTypedArray(value) {
+    return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[objToString.call(value)];
+  }
+
   /*------------------------------------------------------------------------*/
 
   /**
@@ -1242,10 +1615,16 @@
   lodash.identity = identity;
   lodash.isArguments = isArguments;
   lodash.isArray = isArray;
+  lodash.isEqual = isEqual;
   lodash.isFunction = isFunction;
   lodash.isNative = isNative;
   lodash.isObject = isObject;
   lodash.isPlainObject = isPlainObject;
+  lodash.isTypedArray = isTypedArray;
+  lodash.last = last;
+
+  // Add aliases.
+  lodash.eq = isEqual;
 
   /*------------------------------------------------------------------------*/
 

+ 17 - 1
packages/ckeditor5-utils/src/utils-lodash.js

@@ -55,7 +55,23 @@
 		 * @member utils
 		 * @method isArray
 		 */
-		'isArray'
+		'isArray',
+
+		/**
+		 * See Lo-Dash: https://lodash.com/docs#last
+		 *
+		 * @member utils
+		 * @method last
+		 */
+		'last',
+
+		/**
+		 * See Lo-Dash: https://lodash.com/docs#isEqual
+		 *
+		 * @member utils
+		 * @method isEqual
+		 */
+		'isEqual'
 	];
 
 	// Make this compatible with CommonJS as well so it can be used in Node (e.g. "grunt lodash").

+ 150 - 52
packages/ckeditor5-utils/tests/document/position.js

@@ -10,12 +10,13 @@
 var modules = bender.amd.require(
 	'document/element',
 	'document/character',
-	'document/position' );
+	'document/position',
+	'document/document' );
 
 describe( 'position', function() {
-	var Element, Character;
+	var Element, Character, Document;
 
-	var root, p, ul, li1, li2, f, o, z, b, a, r;
+	var doc, root, p, ul, li1, li2, f, o, z, b, a, r;
 
 	// root
 	//  |- p         Before: [ 0 ]       After: [ 1 ]
@@ -31,8 +32,11 @@ describe( 'position', function() {
 	before( function() {
 		Element = modules[ 'document/element' ];
 		Character = modules[ 'document/character' ];
+		Document = modules[ 'document/document' ];
 
-		root = new Element();
+		doc = new Document();
+
+		root = doc.root;
 
 		p = new Element( root, 'p' );
 
@@ -65,87 +69,181 @@ describe( 'position', function() {
 		li2.children.push( r );
 	} );
 
-	it( 'should have path', function() {
+	it( 'should create a position with path and document', function() {
+		var Position = modules[ 'document/position' ];
+
+		var position = new Position( [ 0 ], doc );
+
+		expect( position ).to.have.property( 'path' ).that.deep.equals( [ 0 ] );
+		expect( position ).to.have.property( 'document' ).that.equals( doc );
+	} );
+
+	it( 'should make positions form node and offset', function() {
 		var Position = modules[ 'document/position' ];
 
-		expect( new Position( root, 0 ) ).to.have.property( 'path' ).that.deep.equals( [ 0 ] );
-		expect( new Position( root, 1 ) ).to.have.property( 'path' ).that.deep.equals( [ 1 ] );
-		expect( new Position( root, 2 ) ).to.have.property( 'path' ).that.deep.equals( [ 2 ] );
+		expect( Position.makePositionFromParentAndOffset( root, 0, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 0 ] );
+		expect( Position.makePositionFromParentAndOffset( root, 1, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1 ] );
+		expect( Position.makePositionFromParentAndOffset( root, 2, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 2 ] );
 
-		expect( new Position( p, 0 ) ).to.have.property( 'path' ).that.deep.equals( [ 0, 0 ] );
+		expect( Position.makePositionFromParentAndOffset( p, 0, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 0, 0 ] );
 
-		expect( new Position( ul, 0 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0 ] );
-		expect( new Position( ul, 1 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1 ] );
-		expect( new Position( ul, 2 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 2 ] );
+		expect( Position.makePositionFromParentAndOffset( ul, 0, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0 ] );
+		expect( Position.makePositionFromParentAndOffset( ul, 1, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1 ] );
+		expect( Position.makePositionFromParentAndOffset( ul, 2, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 2 ] );
 
-		expect( new Position( li1, 0 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 0 ] );
-		expect( new Position( li1, 1 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 1 ] );
-		expect( new Position( li1, 2 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 2 ] );
-		expect( new Position( li1, 3 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 3 ] );
+		expect( Position.makePositionFromParentAndOffset( li1, 0, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 0 ] );
+		expect( Position.makePositionFromParentAndOffset( li1, 1, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 1 ] );
+		expect( Position.makePositionFromParentAndOffset( li1, 2, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 2 ] );
+		expect( Position.makePositionFromParentAndOffset( li1, 3, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 3 ] );
 	} );
 
-	it( 'should have nodeBefore', function() {
+	it( 'should make positions before elements', function() {
 		var Position = modules[ 'document/position' ];
 
-		expect( new Position( root, 0 ) ).to.have.property( 'nodeBefore' ).that.is.null;
-		expect( new Position( root, 1 ) ).to.have.property( 'nodeBefore' ).that.equals( p );
-		expect( new Position( root, 2 ) ).to.have.property( 'nodeBefore' ).that.equals( ul );
+		expect( Position.makePositionBefore( p, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 0 ] );
+
+		expect( Position.makePositionBefore( ul, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1 ] );
+
+		expect( Position.makePositionBefore( li1, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0 ] );
 
-		expect( new Position( p, 0 ) ).to.have.property( 'nodeBefore' ).that.is.null;
+		expect( Position.makePositionBefore( f, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 0 ] );
+		expect( Position.makePositionBefore( o, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 1 ] );
+		expect( Position.makePositionBefore( z, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 2 ] );
 
-		expect( new Position( ul, 0 ) ).to.have.property( 'nodeBefore' ).that.is.null;
-		expect( new Position( ul, 1 ) ).to.have.property( 'nodeBefore' ).that.equals( li1 );
-		expect( new Position( ul, 2 ) ).to.have.property( 'nodeBefore' ).that.equals( li2 );
+		expect( Position.makePositionBefore( li2, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1 ] );
 
-		expect( new Position( li1, 0 ) ).to.have.property( 'nodeBefore' ).that.is.null;
-		expect( new Position( li1, 1 ) ).to.have.property( 'nodeBefore' ).that.equals( f );
-		expect( new Position( li1, 2 ) ).to.have.property( 'nodeBefore' ).that.equals( o );
-		expect( new Position( li1, 3 ) ).to.have.property( 'nodeBefore' ).that.equals( z );
+		expect( Position.makePositionBefore( b, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 0 ] );
+		expect( Position.makePositionBefore( a, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 1 ] );
+		expect( Position.makePositionBefore( r, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 2 ] );
 	} );
 
-	it( 'should have nodeAfter', function() {
+	it( 'should throw error if one try to make positions before root', function() {
 		var Position = modules[ 'document/position' ];
 
-		expect( new Position( root, 0 ) ).to.have.property( 'nodeAfter' ).that.equals( p );
-		expect( new Position( root, 1 ) ).to.have.property( 'nodeAfter' ).that.equals( ul );
-		expect( new Position( root, 2 ) ).to.have.property( 'nodeAfter' ).that.is.null;
+		expect( function() {
+			Position.makePositionBefore( root, doc );
+		} ).to.throw( 'You can not make position before root.' );
+	} );
 
-		expect( new Position( p, 0 ) ).to.have.property( 'nodeAfter' ).that.is.null;
+	it( 'should make positions after elements', function() {
+		var Position = modules[ 'document/position' ];
 
-		expect( new Position( ul, 0 ) ).to.have.property( 'nodeAfter' ).that.equals( li1 );
-		expect( new Position( ul, 1 ) ).to.have.property( 'nodeAfter' ).that.equals( li2 );
-		expect( new Position( ul, 2 ) ).to.have.property( 'nodeAfter' ).that.is.null;
+		expect( Position.makePositionAfter( p, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1 ] );
 
-		expect( new Position( li1, 0 ) ).to.have.property( 'nodeAfter' ).that.equals( f );
-		expect( new Position( li1, 1 ) ).to.have.property( 'nodeAfter' ).that.equals( o );
-		expect( new Position( li1, 2 ) ).to.have.property( 'nodeAfter' ).that.equals( z );
-		expect( new Position( li1, 3 ) ).to.have.property( 'nodeAfter' ).that.is.null;
+		expect( Position.makePositionAfter( ul, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 2 ] );
+
+		expect( Position.makePositionAfter( li1, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1 ] );
+
+		expect( Position.makePositionAfter( f, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 1 ] );
+		expect( Position.makePositionAfter( o, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 2 ] );
+		expect( Position.makePositionAfter( z, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 3 ] );
+
+		expect( Position.makePositionAfter( li2, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 2 ] );
+
+		expect( Position.makePositionAfter( b, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 1 ] );
+		expect( Position.makePositionAfter( a, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 2 ] );
+		expect( Position.makePositionAfter( r, doc ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 3 ] );
 	} );
 
-	it( 'should equals another position with the same offset and node', function() {
+	it( 'should throw error if one try to make positions after root', function() {
 		var Position = modules[ 'document/position' ];
 
-		var position = new Position( root, 0 );
-		var samePosition = new Position( root, 0 );
+		expect( function() {
+			Position.makePositionAfter( root, doc );
+		} ).to.throw( 'You can not make position after root.' );
+	} );
+
+	it( 'should have parent', function() {
+		var Position = modules[ 'document/position' ];
+
+		expect( new Position( [ 0 ], doc ) ).to.have.property( 'parent' ).that.equals( root );
+		expect( new Position( [ 1 ], doc ) ).to.have.property( 'parent' ).that.equals( root );
+		expect( new Position( [ 2 ], doc ) ).to.have.property( 'parent' ).that.equals( root );
+
+		expect( new Position( [ 0, 0 ], doc ) ).to.have.property( 'parent' ).that.equals( p );
+
+		expect( new Position( [ 1, 0 ], doc ) ).to.have.property( 'parent' ).that.equals( ul );
+		expect( new Position( [ 1, 1 ], doc ) ).to.have.property( 'parent' ).that.equals( ul );
+		expect( new Position( [ 1, 2 ], doc ) ).to.have.property( 'parent' ).that.equals( ul );
+
+		expect( new Position( [ 1, 0, 0 ], doc ) ).to.have.property( 'parent' ).that.equals( li1 );
+		expect( new Position( [ 1, 0, 1 ], doc ) ).to.have.property( 'parent' ).that.equals( li1 );
+		expect( new Position( [ 1, 0, 2 ], doc ) ).to.have.property( 'parent' ).that.equals( li1 );
+		expect( new Position( [ 1, 0, 3 ], doc ) ).to.have.property( 'parent' ).that.equals( li1 );
+	} );
+
+	it( 'should have offset', function() {
+		var Position = modules[ 'document/position' ];
+
+		expect( new Position( [ 0 ], doc ) ).to.have.property( 'offset' ).that.equals( 0 );
+		expect( new Position( [ 1 ], doc ) ).to.have.property( 'offset' ).that.equals( 1 );
+		expect( new Position( [ 2 ], doc ) ).to.have.property( 'offset' ).that.equals( 2 );
+
+		expect( new Position( [ 0, 0 ], doc ) ).to.have.property( 'offset' ).that.equals( 0 );
+
+		expect( new Position( [ 1, 0 ], doc ) ).to.have.property( 'offset' ).that.equals( 0 );
+		expect( new Position( [ 1, 1 ], doc ) ).to.have.property( 'offset' ).that.equals( 1 );
+		expect( new Position( [ 1, 2 ], doc ) ).to.have.property( 'offset' ).that.equals( 2 );
+
+		expect( new Position( [ 1, 0, 0 ], doc ) ).to.have.property( 'offset' ).that.equals( 0 );
+		expect( new Position( [ 1, 0, 1 ], doc ) ).to.have.property( 'offset' ).that.equals( 1 );
+		expect( new Position( [ 1, 0, 2 ], doc ) ).to.have.property( 'offset' ).that.equals( 2 );
+		expect( new Position( [ 1, 0, 3 ], doc ) ).to.have.property( 'offset' ).that.equals( 3 );
+	} );
+
+	it( 'should have nodeBefore', function() {
+		var Position = modules[ 'document/position' ];
+
+		expect( new Position( [ 0 ], doc ) ).to.have.property( 'nodeBefore' ).that.is.null;
+		expect( new Position( [ 1 ], doc ) ).to.have.property( 'nodeBefore' ).that.equals( p );
+		expect( new Position( [ 2 ], doc ) ).to.have.property( 'nodeBefore' ).that.equals( ul );
+
+		expect( new Position( [ 0, 0 ], doc ) ).to.have.property( 'nodeBefore' ).that.is.null;
+
+		expect( new Position( [ 1, 0 ], doc ) ).to.have.property( 'nodeBefore' ).that.is.null;
+		expect( new Position( [ 1, 1 ], doc ) ).to.have.property( 'nodeBefore' ).that.equals( li1 );
+		expect( new Position( [ 1, 2 ], doc ) ).to.have.property( 'nodeBefore' ).that.equals( li2 );
+
+		expect( new Position( [ 1, 0, 0 ], doc ) ).to.have.property( 'nodeBefore' ).that.is.null;
+		expect( new Position( [ 1, 0, 1 ], doc ) ).to.have.property( 'nodeBefore' ).that.equals( f );
+		expect( new Position( [ 1, 0, 2 ], doc ) ).to.have.property( 'nodeBefore' ).that.equals( o );
+		expect( new Position( [ 1, 0, 3 ], doc ) ).to.have.property( 'nodeBefore' ).that.equals( z );
+	} );
+
+	it( 'should have nodeAfter', function() {
+		var Position = modules[ 'document/position' ];
+
+		expect( new Position( [ 0 ], doc ) ).to.have.property( 'nodeAfter' ).that.equals( p );
+		expect( new Position( [ 1 ], doc ) ).to.have.property( 'nodeAfter' ).that.equals( ul );
+		expect( new Position( [ 2 ], doc ) ).to.have.property( 'nodeAfter' ).that.is.null;
+
+		expect( new Position( [ 0, 0 ], doc ) ).to.have.property( 'nodeAfter' ).that.is.null;
+
+		expect( new Position( [ 1, 0 ], doc ) ).to.have.property( 'nodeAfter' ).that.equals( li1 );
+		expect( new Position( [ 1, 1 ], doc ) ).to.have.property( 'nodeAfter' ).that.equals( li2 );
+		expect( new Position( [ 1, 2 ], doc ) ).to.have.property( 'nodeAfter' ).that.is.null;
 
-		expect( position.equals( samePosition ) ).to.be.true;
+		expect( new Position( [ 1, 0, 0 ], doc ) ).to.have.property( 'nodeAfter' ).that.equals( f );
+		expect( new Position( [ 1, 0, 1 ], doc ) ).to.have.property( 'nodeAfter' ).that.equals( o );
+		expect( new Position( [ 1, 0, 2 ], doc ) ).to.have.property( 'nodeAfter' ).that.equals( z );
+		expect( new Position( [ 1, 0, 3 ], doc ) ).to.have.property( 'nodeAfter' ).that.is.null;
 	} );
 
-	it( 'should not equals another position with the different offset', function() {
+	it( 'should equals another position with the same path', function() {
 		var Position = modules[ 'document/position' ];
 
-		var position = new Position( root, 0 );
-		var differentOffset = new Position( root, 1 );
+		var position = new Position( [ 1, 1, 2 ], doc );
+		var samePosition = new Position( [ 1, 1, 2 ], doc );
 
-		expect( position.equals( differentOffset ) ).to.be.false;
+		expect( position.isEqual( samePosition ) ).to.be.true;
 	} );
 
-	it( 'should not equals another position with the different node', function() {
+	it( 'should not equals another position with the different path', function() {
 		var Position = modules[ 'document/position' ];
 
-		var position = new Position( root, 0 );
-		var differentNode = new Position( p, 0 );
+		var position = new Position( [ 1, 1, 1 ], doc );
+		var differentNode = new Position( [ 1, 2, 2 ], doc );
 
-		expect( position.equals( differentNode ) ).to.be.false;
+		expect( position.isEqual( differentNode ) ).to.be.false;
 	} );
 } );

+ 8 - 8
packages/ckeditor5-utils/tests/document/positioniterator.js

@@ -85,7 +85,7 @@ describe( 'range iterator', function() {
 		var PositionIterator = modules[ 'document/positioniterator' ];
 		var Position = modules[ 'document/position' ];
 
-		var iterator = new PositionIterator( new Position( root, 0 ) );
+		var iterator = new PositionIterator( new Position( [ 0 ], document ) ); // begging of root
 		var i, len;
 
 		for ( i = 0, len = expectedItems.length; i < len; i++ ) {
@@ -98,7 +98,7 @@ describe( 'range iterator', function() {
 		var PositionIterator = modules[ 'document/positioniterator' ];
 		var Position = modules[ 'document/position' ];
 
-		var iterator = new PositionIterator( new Position( root, 2 ) );
+		var iterator = new PositionIterator( new Position( [ 2 ], document ) ); // ending of root
 
 		for ( var i = expectedItems.length - 1; i >= 0; i-- ) {
 			expect( iterator.previous() ).to.deep.equal( { done: false, value: expectedItems[ i ] } );
@@ -111,8 +111,8 @@ describe( 'range iterator', function() {
 		var Position = modules[ 'document/position' ];
 		var Range = modules[ 'document/range' ];
 
-		var start = new Position( paragraph, 0 );
-		var end = new Position( img2, 0 );
+		var start = new Position( [ 1, 0 ], document ); // p, 0
+		var end = new Position( [ 1, 3, 0 ], document ); // img, 0
 
 		var iterator = new PositionIterator( new Range( start, end ) );
 
@@ -129,8 +129,8 @@ describe( 'range iterator', function() {
 		var Position = modules[ 'document/position' ];
 		var Range = modules[ 'document/range' ];
 
-		var start = new Position( paragraph, 0 );
-		var end = new Position( img2, 0 );
+		var start = new Position( [ 1, 0 ], document ); // p, 0
+		var end = new Position( [ 1, 3, 0 ], document ); // img, 0
 
 		var iterator = new PositionIterator( new Range( start, end ), end );
 
@@ -146,8 +146,8 @@ describe( 'range iterator', function() {
 		var Position = modules[ 'document/position' ];
 		var Range = modules[ 'document/range' ];
 
-		var start = new Position( root, 0 );
-		var end = new Position( root, 2 );
+		var start = new Position( [ 0 ], document ); // begging of root
+		var end = new Position( [ 2 ], document ); // ending of root
 		var range = new Range( start, end );
 
 		var i = 0;

+ 17 - 34
packages/ckeditor5-utils/tests/document/range.js

@@ -9,19 +9,15 @@
 
 var modules = bender.amd.require(
 	'document/range',
-	'document/element',
 	'document/position' );
 
 describe( 'range', function() {
 	it( 'should create a range with given positions', function() {
-		var Element = modules[ 'document/element' ];
 		var Position = modules[ 'document/position' ];
 		var Range = modules[ 'document/range' ];
 
-		var elem = new Element();
-
-		var start = new Position( elem, 0 );
-		var end = new Position( elem, 0 );
+		var start = new Position( [ 0 ] );
+		var end = new Position( [ 1 ] );
 
 		var range = new Range( start, end );
 
@@ -30,66 +26,53 @@ describe( 'range', function() {
 	} );
 
 	it( 'should be equals same range', function() {
-		var Element = modules[ 'document/element' ];
 		var Position = modules[ 'document/position' ];
 		var Range = modules[ 'document/range' ];
 
-		var elem = new Element();
-
-		var start = new Position( elem, 0 );
-		var end = new Position( elem, 0 );
+		var start = new Position( [ 0 ] );
+		var end = new Position( [ 1 ] );
 
 		var range = new Range( start, end );
 
-		var sameStart = new Position( elem, 0 );
-		var sameEnd = new Position( elem, 0 );
+		var sameStart = new Position( [ 0 ] );
+		var sameEnd = new Position( [ 1 ] );
 
 		var sameRange = new Range( sameStart, sameEnd );
 
-		expect( range.equals( sameRange ) ).to.be.true;
+		expect( range.isEqual( sameRange ) ).to.be.true;
 	} );
 
 	it( 'should not be equals if the start position is different', function() {
-		var Element = modules[ 'document/element' ];
 		var Position = modules[ 'document/position' ];
 		var Range = modules[ 'document/range' ];
 
-		var elem = new Element();
-		var childElem = new Element( elem );
-		elem.children.push( childElem );
-
-		var start = new Position( elem, 0 );
-		var end = new Position( elem, 1 );
+		var start = new Position( [ 0 ] );
+		var end = new Position( [ 1 ] );
 
 		var range = new Range( start, end );
 
-		var sameStart = new Position( elem, 1 );
-		var sameEnd = new Position( elem, 1 );
+		var sameStart = new Position( [ 1 ] );
+		var sameEnd = new Position( [ 1 ] );
 
 		var sameRange = new Range( sameStart, sameEnd );
 
-		expect( range.equals( sameRange ) ).to.not.be.true;
+		expect( range.isEqual( sameRange ) ).to.not.be.true;
 	} );
 
 	it( 'should not be equals if the end position is different', function() {
-		var Element = modules[ 'document/element' ];
 		var Position = modules[ 'document/position' ];
 		var Range = modules[ 'document/range' ];
 
-		var elem = new Element();
-		var childElem = new Element( elem );
-		elem.children.push( childElem );
-
-		var start = new Position( elem, 0 );
-		var end = new Position( elem, 1 );
+		var start = new Position( [ 0 ] );
+		var end = new Position( [ 1 ] );
 
 		var range = new Range( start, end );
 
-		var sameStart = new Position( elem, 0 );
-		var sameEnd = new Position( elem, 0 );
+		var sameStart = new Position( [ 0 ] );
+		var sameEnd = new Position( [ 0 ] );
 
 		var sameRange = new Range( sameStart, sameEnd );
 
-		expect( range.equals( sameRange ) ).to.not.be.true;
+		expect( range.isEqual( sameRange ) ).to.not.be.true;
 	} );
 } );