@@ -11,6 +11,7 @@ import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
1111import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin' ;
1212import mix from '@ckeditor/ckeditor5-utils/src/mix' ;
1313import clone from '@ckeditor/ckeditor5-utils/src/lib/lodash/clone' ;
14+ import compareArrays from '@ckeditor/ckeditor5-utils/src/comparearrays' ;
1415
1516/**
1617 * Abstract tree view node class.
@@ -118,6 +119,33 @@ export default class Node {
118119 }
119120 }
120121
122+ /**
123+ * Gets a path to the node. The path is an array containing indices of consecutive ancestors of this node,
124+ * beginning from {@link module:engine/view/node~Node#root root}, down to this node's index.
125+ *
126+ * const abc = new Text( 'abc' );
127+ * const foo = new Text( 'foo' );
128+ * const h1 = new Element( 'h1', null, new Text( 'header' ) );
129+ * const p = new Element( 'p', null, [ abc, foo ] );
130+ * const div = new Element( 'div', null, [ h1, p ] );
131+ * foo.getPath(); // Returns [ 1, 3 ]. `foo` is in `p` which is in `div`. `p` starts at offset 1, while `foo` at 3.
132+ * h1.getPath(); // Returns [ 0 ].
133+ * div.getPath(); // Returns [].
134+ *
135+ * @returns {Array.<Number> } The path.
136+ */
137+ getPath ( ) {
138+ const path = [ ] ;
139+ let node = this ; // eslint-disable-line consistent-this
140+
141+ while ( node . parent ) {
142+ path . unshift ( node . index ) ;
143+ node = node . parent ;
144+ }
145+
146+ return path ;
147+ }
148+
121149 /**
122150 * Returns ancestors array of this node.
123151 *
@@ -162,6 +190,63 @@ export default class Node {
162190 return i === 0 ? null : ancestorsA [ i - 1 ] ;
163191 }
164192
193+ /**
194+ * Returns whether this node is before given node. `false` is returned if nodes are in different trees (for example,
195+ * in different {@link module:engine/view/documentfragment~DocumentFragment}s).
196+ *
197+ * @param {module:engine/view/node~Node } node Node to compare with.
198+ * @returns {Boolean }
199+ */
200+ isBefore ( node ) {
201+ // Given node is not before this node if they are same.
202+ if ( this == node ) {
203+ return false ;
204+ }
205+
206+ // Return `false` if it is impossible to compare nodes.
207+ if ( this . root !== node . root ) {
208+ return false ;
209+ }
210+
211+ const thisPath = this . getPath ( ) ;
212+ const nodePath = node . getPath ( ) ;
213+
214+ const result = compareArrays ( thisPath , nodePath ) ;
215+
216+ switch ( result ) {
217+ case 'prefix' :
218+ return true ;
219+
220+ case 'extension' :
221+ return false ;
222+
223+ default :
224+ return thisPath [ result ] < nodePath [ result ] ;
225+ }
226+ }
227+
228+ /**
229+ * Returns whether this node is after given node. `false` is returned if nodes are in different trees (for example,
230+ * in different {@link module:engine/view/documentfragment~DocumentFragment}s).
231+ *
232+ * @param {module:engine/view/node~Node } node Node to compare with.
233+ * @returns {Boolean }
234+ */
235+ isAfter ( node ) {
236+ // Given node is not before this node if they are same.
237+ if ( this == node ) {
238+ return false ;
239+ }
240+
241+ // Return `false` if it is impossible to compare nodes.
242+ if ( this . root !== node . root ) {
243+ return false ;
244+ }
245+
246+ // In other cases, just check if the `node` is before, and return the opposite.
247+ return ! this . isBefore ( node ) ;
248+ }
249+
165250 /**
166251 * Removes node from parent.
167252 *
@@ -198,28 +283,14 @@ export default class Node {
198283 return json ;
199284 }
200285
201- /**
202- * Clones this node.
203- *
204- * @method #clone
205- * @returns {module:engine/view/node~Node } Clone of this node.
206- */
207-
208- /**
209- * Checks if provided node is similar to this node.
210- *
211- * @method #isSimilar
212- * @returns {Boolean } True if nodes are similar.
213- */
214-
215286 /**
216287 * Checks whether given view tree object is of given type.
217288 *
218289 * This method is useful when processing view tree objects that are of unknown type. For example, a function
219290 * may return {@link module:engine/view/documentfragment~DocumentFragment} or {@link module:engine/view/node~Node}
220291 * that can be either text node or element. This method can be used to check what kind of object is returned.
221292 *
222- * obj.is( 'node' ); // true for any node, false for document fragment
293+ * obj.is( 'node' ); // true for any node, false for document fragment and text fragment
223294 * obj.is( 'documentFragment' ); // true for document fragment, false for any node
224295 * obj.is( 'element' ); // true for any element, false for text node or document fragment
225296 * obj.is( 'element', 'p' ); // true only for element which name is 'p'
@@ -231,6 +302,24 @@ export default class Node {
231302 * 'rootElement'|'documentFragment'|'text'|'textProxy'} type
232303 * @returns {Boolean }
233304 */
305+ is ( type ) {
306+ return type == 'node' ;
307+ }
308+
309+ /**
310+ * Clones this node.
311+ *
312+ * @protected
313+ * @method #_clone
314+ * @returns {module:engine/view/node~Node } Clone of this node.
315+ */
316+
317+ /**
318+ * Checks if provided node is similar to this node.
319+ *
320+ * @method #isSimilar
321+ * @returns {Boolean } True if nodes are similar.
322+ */
234323}
235324
236325/**
0 commit comments