Skip to content
This repository was archived by the owner on Jun 26, 2020. It is now read-only.

Commit 4a4a89a

Browse files
authored
Merge pull request #1037 from ckeditor/t/1036
Other: Introduced `options.includeSelf` to `getCommonAncestor()`. Closes #1036. BREAKING CHANGE: The `includeNode` option of `Node#getAncestors()` methods (model and view) was renamed to `includeSelf`. See #1036.
2 parents f913aee + 9f28462 commit 4a4a89a

File tree

11 files changed

+100
-46
lines changed

11 files changed

+100
-46
lines changed

src/model/node.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -252,14 +252,14 @@ export default class Node {
252252
* Returns ancestors array of this node.
253253
*
254254
* @param {Object} options Options object.
255-
* @param {Boolean} [options.includeNode=false] When set to `true` this node will be also included in parent's array.
255+
* @param {Boolean} [options.includeSelf=false] When set to `true` this node will be also included in parent's array.
256256
* @param {Boolean} [options.parentFirst=false] When set to `true`, array will be sorted from node's parent to root element,
257257
* otherwise root element will be the first item in the array.
258258
* @returns {Array} Array with ancestors.
259259
*/
260-
getAncestors( options = { includeNode: false, parentFirst: false } ) {
260+
getAncestors( options = { includeSelf: false, parentFirst: false } ) {
261261
const ancestors = [];
262-
let parent = options.includeNode ? this : this.parent;
262+
let parent = options.includeSelf ? this : this.parent;
263263

264264
while ( parent ) {
265265
ancestors[ options.parentFirst ? 'push' : 'unshift' ]( parent );
@@ -274,11 +274,14 @@ export default class Node {
274274
* which is a common ancestor of both nodes.
275275
*
276276
* @param {module:engine/model/node~Node} node The second node.
277+
* @param {Object} options Options object.
278+
* @param {Boolean} [options.includeSelf=false] When set to `true` both nodes will be considered "ancestors" too.
279+
* Which means that if e.g. node A is inside B, then their common ancestor will be B.
277280
* @returns {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment|null}
278281
*/
279-
getCommonAncestor( node ) {
280-
const ancestorsA = this.getAncestors();
281-
const ancestorsB = node.getAncestors();
282+
getCommonAncestor( node, options = {} ) {
283+
const ancestorsA = this.getAncestors( options );
284+
const ancestorsB = node.getAncestors( options );
282285

283286
let i = 0;
284287

src/model/position.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ export default class Position {
294294
if ( this.parent.is( 'documentFragment' ) ) {
295295
return [ this.parent ];
296296
} else {
297-
return this.parent.getAncestors( { includeNode: true } );
297+
return this.parent.getAncestors( { includeSelf: true } );
298298
}
299299
}
300300

src/model/selection.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,7 @@ function isUnvisitedBlockContainer( element, visited ) {
742742
// Finds the lowest element in position's ancestors which is a block.
743743
// Marks all ancestors as already visited to not include any of them later on.
744744
function getParentBlock( position, visited ) {
745-
const ancestors = position.parent.getAncestors( { parentFirst: true, includeNode: true } );
745+
const ancestors = position.parent.getAncestors( { parentFirst: true, includeSelf: true } );
746746
const block = ancestors.find( element => isUnvisitedBlockContainer( element, visited ) );
747747

748748
// Mark all ancestors of this position's parent, because find() might've stopped early and

src/model/textproxy.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,14 +204,14 @@ export default class TextProxy {
204204
* Returns ancestors array of this text proxy.
205205
*
206206
* @param {Object} options Options object.
207-
* @param {Boolean} [options.includeNode=false] When set to `true` this text proxy will be also included in parent's array.
207+
* @param {Boolean} [options.includeSelf=false] When set to `true` this text proxy will be also included in parent's array.
208208
* @param {Boolean} [options.parentFirst=false] When set to `true`, array will be sorted from text proxy parent to root element,
209209
* otherwise root element will be the first item in the array.
210210
* @returns {Array} Array with ancestors.
211211
*/
212-
getAncestors( options = { includeNode: false, parentFirst: false } ) {
212+
getAncestors( options = { includeSelf: false, parentFirst: false } ) {
213213
const ancestors = [];
214-
let parent = options.includeNode ? this : this.parent;
214+
let parent = options.includeSelf ? this : this.parent;
215215

216216
while ( parent ) {
217217
ancestors[ options.parentFirst ? 'push' : 'unshift' ]( parent );

src/view/node.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,14 @@ export default class Node {
121121
* Returns ancestors array of this node.
122122
*
123123
* @param {Object} options Options object.
124-
* @param {Boolean} [options.includeNode=false] When set to `true` this node will be also included in parent's array.
124+
* @param {Boolean} [options.includeSelf=false] When set to `true` this node will be also included in parent's array.
125125
* @param {Boolean} [options.parentFirst=false] When set to `true`, array will be sorted from node's parent to root element,
126126
* otherwise root element will be the first item in the array.
127127
* @returns {Array} Array with ancestors.
128128
*/
129-
getAncestors( options = { includeNode: false, parentFirst: false } ) {
129+
getAncestors( options = { includeSelf: false, parentFirst: false } ) {
130130
const ancestors = [];
131-
let parent = options.includeNode ? this : this.parent;
131+
let parent = options.includeSelf ? this : this.parent;
132132

133133
while ( parent ) {
134134
ancestors[ options.parentFirst ? 'push' : 'unshift' ]( parent );
@@ -143,11 +143,14 @@ export default class Node {
143143
* which is a common ancestor of both nodes.
144144
*
145145
* @param {module:engine/view/node~Node} node The second node.
146+
* @param {Object} options Options object.
147+
* @param {Boolean} [options.includeSelf=false] When set to `true` both nodes will be considered "ancestors" too.
148+
* Which means that if e.g. node A is inside B, then their common ancestor will be B.
146149
* @returns {module:engine/view/element~Element|module:engine/view/documentfragment~DocumentFragment|null}
147150
*/
148-
getCommonAncestor( node ) {
149-
const ancestorsA = this.getAncestors();
150-
const ancestorsB = node.getAncestors();
151+
getCommonAncestor( node, options = {} ) {
152+
const ancestorsA = this.getAncestors( options );
153+
const ancestorsB = node.getAncestors( options );
151154

152155
let i = 0;
153156

src/view/position.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ export default class Position {
171171
if ( this.parent.is( 'documentFragment' ) ) {
172172
return [ this.parent ];
173173
} else {
174-
return this.parent.getAncestors( { includeNode: true } );
174+
return this.parent.getAncestors( { includeSelf: true } );
175175
}
176176
}
177177

src/view/textproxy.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,14 +146,14 @@ export default class TextProxy {
146146
* Returns ancestors array of this text proxy.
147147
*
148148
* @param {Object} options Options object.
149-
* @param {Boolean} [options.includeNode=false] When set to `true` {#textNode} will be also included in parent's array.
149+
* @param {Boolean} [options.includeSelf=false] When set to `true` {#textNode} will be also included in parent's array.
150150
* @param {Boolean} [options.parentFirst=false] When set to `true`, array will be sorted from text proxy parent to
151151
* root element, otherwise root element will be the first item in the array.
152152
* @returns {Array} Array with ancestors.
153153
*/
154-
getAncestors( options = { includeNode: false, parentFirst: false } ) {
154+
getAncestors( options = { includeSelf: false, parentFirst: false } ) {
155155
const ancestors = [];
156-
let parent = options.includeNode ? this.textNode : this.parent;
156+
let parent = options.includeSelf ? this.textNode : this.parent;
157157

158158
while ( parent !== null ) {
159159
ancestors[ options.parentFirst ? 'push' : 'unshift' ]( parent );

tests/model/node.js

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -225,20 +225,20 @@ describe( 'Node', () => {
225225
expect( textBA.getAncestors() ).to.deep.equal( [ root, two ] );
226226
} );
227227

228-
it( 'should include itself if includeNode option is set to true', () => {
229-
expect( root.getAncestors( { includeNode: true } ) ).to.deep.equal( [ root ] );
230-
expect( two.getAncestors( { includeNode: true } ) ).to.deep.equal( [ root, two ] );
231-
expect( textBA.getAncestors( { includeNode: true } ) ).to.deep.equal( [ root, two, textBA ] );
232-
expect( img.getAncestors( { includeNode: true } ) ).to.deep.equal( [ root, two, img ] );
233-
expect( textR.getAncestors( { includeNode: true } ) ).to.deep.equal( [ root, two, textR ] );
228+
it( 'should include itself if includeSelf option is set to true', () => {
229+
expect( root.getAncestors( { includeSelf: true } ) ).to.deep.equal( [ root ] );
230+
expect( two.getAncestors( { includeSelf: true } ) ).to.deep.equal( [ root, two ] );
231+
expect( textBA.getAncestors( { includeSelf: true } ) ).to.deep.equal( [ root, two, textBA ] );
232+
expect( img.getAncestors( { includeSelf: true } ) ).to.deep.equal( [ root, two, img ] );
233+
expect( textR.getAncestors( { includeSelf: true } ) ).to.deep.equal( [ root, two, textR ] );
234234
} );
235235

236236
it( 'should reverse order if parentFirst option is set to true', () => {
237-
expect( root.getAncestors( { includeNode: true, parentFirst: true } ) ).to.deep.equal( [ root ] );
238-
expect( two.getAncestors( { includeNode: true, parentFirst: true } ) ).to.deep.equal( [ two, root ] );
239-
expect( textBA.getAncestors( { includeNode: true, parentFirst: true } ) ).to.deep.equal( [ textBA, two, root ] );
240-
expect( img.getAncestors( { includeNode: true, parentFirst: true } ) ).to.deep.equal( [ img, two, root ] );
241-
expect( textR.getAncestors( { includeNode: true, parentFirst: true } ) ).to.deep.equal( [ textR, two, root ] );
237+
expect( root.getAncestors( { includeSelf: true, parentFirst: true } ) ).to.deep.equal( [ root ] );
238+
expect( two.getAncestors( { includeSelf: true, parentFirst: true } ) ).to.deep.equal( [ two, root ] );
239+
expect( textBA.getAncestors( { includeSelf: true, parentFirst: true } ) ).to.deep.equal( [ textBA, two, root ] );
240+
expect( img.getAncestors( { includeSelf: true, parentFirst: true } ) ).to.deep.equal( [ img, two, root ] );
241+
expect( textR.getAncestors( { includeSelf: true, parentFirst: true } ) ).to.deep.equal( [ textR, two, root ] );
242242
} );
243243
} );
244244

@@ -247,11 +247,18 @@ describe( 'Node', () => {
247247
expect( img.getCommonAncestor( img ) ).to.equal( two );
248248
} );
249249

250+
it( 'should return the given node for the same node if includeSelf is used', () => {
251+
expect( img.getCommonAncestor( img, { includeSelf: true } ) ).to.equal( img );
252+
} );
253+
250254
it( 'should return null for detached subtrees', () => {
251255
const detached = new Element( 'foo' );
252256

253257
expect( img.getCommonAncestor( detached ) ).to.be.null;
254258
expect( detached.getCommonAncestor( img ) ).to.be.null;
259+
260+
expect( img.getCommonAncestor( detached, { includeSelf: true } ) ).to.be.null;
261+
expect( detached.getCommonAncestor( img, { includeSelf: true } ) ).to.be.null;
255262
} );
256263

257264
it( 'should return null when one of the nodes is a tree root itself', () => {
@@ -260,9 +267,18 @@ describe( 'Node', () => {
260267
expect( root.getCommonAncestor( root ) ).to.be.null;
261268
} );
262269

270+
it( 'should return root when one of the nodes is a tree root itself and includeSelf is used', () => {
271+
expect( root.getCommonAncestor( img, { includeSelf: true } ) ).to.equal( root );
272+
expect( img.getCommonAncestor( root, { includeSelf: true } ) ).to.equal( root );
273+
expect( root.getCommonAncestor( root, { includeSelf: true } ) ).to.equal( root );
274+
} );
275+
263276
it( 'should return parent of the nodes at the same level', () => {
264-
expect( img.getCommonAncestor( textBA ) ).to.equal( two );
265-
expect( textR.getCommonAncestor( textBA ) ).to.equal( two );
277+
expect( img.getCommonAncestor( textBA ), 1 ).to.equal( two );
278+
expect( textR.getCommonAncestor( textBA ), 2 ).to.equal( two );
279+
280+
expect( img.getCommonAncestor( textBA, { includeSelf: true } ), 3 ).to.equal( two );
281+
expect( textR.getCommonAncestor( textBA, { includeSelf: true } ), 4 ).to.equal( two );
266282
} );
267283

268284
it( 'should return proper element for nodes in different branches and on different levels', () => {
@@ -282,6 +298,14 @@ describe( 'Node', () => {
282298
expect( c.getCommonAncestor( b ), 3 ).to.equal( a );
283299
expect( bom.getCommonAncestor( d ), 4 ).to.equal( a );
284300
expect( b.getCommonAncestor( bom ), 5 ).to.equal( a );
301+
expect( b.getCommonAncestor( bar ), 6 ).to.equal( a );
302+
303+
expect( bar.getCommonAncestor( foo, { includeSelf: true } ), 11 ).to.equal( c );
304+
expect( foo.getCommonAncestor( d, { includeSelf: true } ), 12 ).to.equal( c );
305+
expect( c.getCommonAncestor( b, { includeSelf: true } ), 13 ).to.equal( b );
306+
expect( bom.getCommonAncestor( d, { includeSelf: true } ), 14 ).to.equal( a );
307+
expect( b.getCommonAncestor( bom, { includeSelf: true } ), 15 ).to.equal( a );
308+
expect( b.getCommonAncestor( bar, { includeSelf: true } ), 16 ).to.equal( b );
285309
} );
286310

287311
it( 'should return document fragment', () => {

tests/model/textproxy.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,12 +125,12 @@ describe( 'TextProxy', () => {
125125
expect( textProxy.getAncestors() ).to.deep.equal( [ root, element ] );
126126
} );
127127

128-
it( 'should include itself if includeNode option is set to true', () => {
129-
expect( textProxy.getAncestors( { includeNode: true } ) ).to.deep.equal( [ root, element, textProxy ] );
128+
it( 'should include itself if includeSelf option is set to true', () => {
129+
expect( textProxy.getAncestors( { includeSelf: true } ) ).to.deep.equal( [ root, element, textProxy ] );
130130
} );
131131

132132
it( 'should reverse order if parentFirst option is set to true', () => {
133-
expect( textProxy.getAncestors( { includeNode: true, parentFirst: true } ) ).to.deep.equal( [ textProxy, element, root ] );
133+
expect( textProxy.getAncestors( { includeSelf: true, parentFirst: true } ) ).to.deep.equal( [ textProxy, element, root ] );
134134
} );
135135
} );
136136

tests/view/node.js

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ describe( 'Node', () => {
6565
} );
6666

6767
it( 'should return array including node itself if requested', () => {
68-
const result = root.getAncestors( { includeNode: true } );
68+
const result = root.getAncestors( { includeSelf: true } );
6969
expect( result ).to.be.an( 'array' );
7070
expect( result.length ).to.equal( 1 );
7171
expect( result[ 0 ] ).to.equal( root );
@@ -77,7 +77,7 @@ describe( 'Node', () => {
7777
expect( result[ 0 ] ).to.equal( root );
7878
expect( result[ 1 ] ).to.equal( two );
7979

80-
const result2 = charR.getAncestors( { includeNode: true } );
80+
const result2 = charR.getAncestors( { includeSelf: true } );
8181
expect( result2.length ).to.equal( 3 );
8282
expect( result2[ 0 ] ).to.equal( root );
8383
expect( result2[ 1 ] ).to.equal( two );
@@ -90,7 +90,7 @@ describe( 'Node', () => {
9090
expect( result[ 0 ] ).to.equal( two );
9191
expect( result[ 1 ] ).to.equal( root );
9292

93-
const result2 = charR.getAncestors( { includeNode: true, parentFirst: true } );
93+
const result2 = charR.getAncestors( { includeSelf: true, parentFirst: true } );
9494
expect( result2.length ).to.equal( 3 );
9595
expect( result2[ 2 ] ).to.equal( root );
9696
expect( result2[ 1 ] ).to.equal( two );
@@ -114,11 +114,18 @@ describe( 'Node', () => {
114114
expect( img.getCommonAncestor( img ) ).to.equal( two );
115115
} );
116116

117+
it( 'should return the given node for the same node if includeSelf is used', () => {
118+
expect( img.getCommonAncestor( img, { includeSelf: true } ) ).to.equal( img );
119+
} );
120+
117121
it( 'should return null for detached subtrees', () => {
118122
const detached = new Element( 'foo' );
119123

120124
expect( img.getCommonAncestor( detached ) ).to.be.null;
121125
expect( detached.getCommonAncestor( img ) ).to.be.null;
126+
127+
expect( img.getCommonAncestor( detached, { includeSelf: true } ) ).to.be.null;
128+
expect( detached.getCommonAncestor( img, { includeSelf: true } ) ).to.be.null;
122129
} );
123130

124131
it( 'should return null when one of the nodes is a tree root itself', () => {
@@ -127,9 +134,18 @@ describe( 'Node', () => {
127134
expect( root.getCommonAncestor( root ) ).to.be.null;
128135
} );
129136

137+
it( 'should return root when one of the nodes is a tree root itself and includeSelf is used', () => {
138+
expect( root.getCommonAncestor( img, { includeSelf: true } ) ).to.equal( root );
139+
expect( img.getCommonAncestor( root, { includeSelf: true } ) ).to.equal( root );
140+
expect( root.getCommonAncestor( root, { includeSelf: true } ) ).to.equal( root );
141+
} );
142+
130143
it( 'should return parent of the nodes at the same level', () => {
131-
expect( img.getCommonAncestor( charA ) ).to.equal( two );
132-
expect( charB.getCommonAncestor( charA ) ).to.equal( two );
144+
expect( img.getCommonAncestor( charA ), 1 ).to.equal( two );
145+
expect( charB.getCommonAncestor( charA ), 2 ).to.equal( two );
146+
147+
expect( img.getCommonAncestor( charA, { includeSelf: true } ), 3 ).to.equal( two );
148+
expect( charB.getCommonAncestor( charA, { includeSelf: true } ), 4 ).to.equal( two );
133149
} );
134150

135151
it( 'should return proper element for nodes in different branches and on different levels', () => {
@@ -149,6 +165,14 @@ describe( 'Node', () => {
149165
expect( c.getCommonAncestor( b ), 3 ).to.equal( a );
150166
expect( bom.getCommonAncestor( d ), 4 ).to.equal( a );
151167
expect( b.getCommonAncestor( bom ), 5 ).to.equal( a );
168+
expect( b.getCommonAncestor( bar ), 6 ).to.equal( a );
169+
170+
expect( bar.getCommonAncestor( foo, { includeSelf: true } ), 11 ).to.equal( c );
171+
expect( foo.getCommonAncestor( d, { includeSelf: true } ), 12 ).to.equal( c );
172+
expect( c.getCommonAncestor( b, { includeSelf: true } ), 13 ).to.equal( b );
173+
expect( bom.getCommonAncestor( d, { includeSelf: true } ), 14 ).to.equal( a );
174+
expect( b.getCommonAncestor( bom, { includeSelf: true } ), 15 ).to.equal( a );
175+
expect( b.getCommonAncestor( bar, { includeSelf: true } ), 16 ).to.equal( b );
152176
} );
153177

154178
it( 'should return document fragment', () => {

0 commit comments

Comments
 (0)