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

Commit

Permalink
Merge pull request #172 from ckeditor/t/168
Browse files Browse the repository at this point in the history
Feature: Introduced a static `Rect#getDomRangeRects` method for external usage. Closes #168.
  • Loading branch information
Reinmar authored Jul 7, 2017
2 parents f86b1ad + 37bb62b commit f67aea1
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 22 deletions.
50 changes: 31 additions & 19 deletions src/dom/rect.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,25 +55,7 @@ export default class Rect {
if ( isElement( source ) ) {
copyRectProperties( this, source.getBoundingClientRect() );
} else if ( isRange( source ) ) {
// Use getClientRects() when the range is collapsed.
// https://github.com/ckeditor/ckeditor5-utils/issues/153
if ( source.collapsed ) {
const rects = source.getClientRects();

if ( rects.length ) {
copyRectProperties( this, rects[ 0 ] );
}
// If there's no client rects for the Range, use parent container's bounding
// rect instead and adjust rect's width to simulate the actual geometry of such
// range.
// https://github.com/ckeditor/ckeditor5-utils/issues/153
else {
copyRectProperties( this, source.startContainer.getBoundingClientRect() );
this.width = 0;
}
} else {
copyRectProperties( this, source.getBoundingClientRect() );
}
copyRectProperties( this, Rect.getDomRangeRects( source )[ 0 ] );
} else {
copyRectProperties( this, source );
}
Expand Down Expand Up @@ -268,6 +250,36 @@ export default class Rect {
height: innerHeight
} );
}

/**
* Returns an array of rects of the given native DOM Range.
*
* @param {Range} range A native DOM range.
* @returns {Array.<module:utils/dom/rect~Rect>} DOM Range rects.
*/
static getDomRangeRects( range ) {
const rects = [];
// Safari does not iterate over ClientRectList using for...of loop.
const clientRects = Array.from( range.getClientRects() );

if ( clientRects.length ) {
for ( const rect of clientRects ) {
rects.push( new Rect( rect ) );
}
}
// If there's no client rects for the Range, use parent container's bounding rect
// instead and adjust rect's width to simulate the actual geometry of such range.
// https://github.com/ckeditor/ckeditor5-utils/issues/153
else {
const startContainerRect = new Rect( range.startContainer.getBoundingClientRect() );
startContainerRect.right = startContainerRect.left;
startContainerRect.width = 0;

rects.push( startContainerRect );
}

return rects;
}
}

const rectProperties = [ 'top', 'right', 'bottom', 'left', 'width', 'height' ];
Expand Down
54 changes: 51 additions & 3 deletions tests/dom/rect.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe( 'Rect', () => {
const range = document.createRange();

range.selectNode( document.body );
testUtils.sinon.stub( range, 'getBoundingClientRect' ).returns( geometry );
testUtils.sinon.stub( range, 'getClientRects' ).returns( [ geometry ] );

assertRect( new Rect( range ), geometry );
} );
Expand All @@ -71,6 +71,7 @@ describe( 'Rect', () => {
testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );

const expectedGeometry = Object.assign( {}, geometry );
expectedGeometry.right = expectedGeometry.left;
expectedGeometry.width = 0;

assertRect( new Rect( range ), expectedGeometry );
Expand Down Expand Up @@ -510,14 +511,14 @@ describe( 'Rect', () => {
range.setStart( ancestorA, 0 );
range.setEnd( ancestorA, 1 );

testUtils.sinon.stub( range, 'getBoundingClientRect' ).returns( {
testUtils.sinon.stub( range, 'getClientRects' ).returns( [ {
top: 0,
right: 100,
bottom: 100,
left: 0,
width: 100,
height: 100
} );
} ] );

testUtils.sinon.stub( ancestorA, 'getBoundingClientRect' ).returns( {
top: 50,
Expand Down Expand Up @@ -584,6 +585,53 @@ describe( 'Rect', () => {
} );
} );
} );

describe( 'getDomRangeRects() ', () => {
it( 'should return rects for a Range (non–collapsed)', () => {
const range = document.createRange();

range.selectNode( document.body );
testUtils.sinon.stub( range, 'getClientRects' ).returns( [ geometry ] );

const rects = Rect.getDomRangeRects( range );
expect( rects ).to.have.length( 1 );
assertRect( rects[ 0 ], geometry );
} );

// https://github.com/ckeditor/ckeditor5-utils/issues/153
it( 'should return rects for a Range (collapsed)', () => {
const range = document.createRange();
const secondGeometry = { top: 20, right: 80, bottom: 60, left: 40, width: 40, height: 40 };

range.collapse();
testUtils.sinon.stub( range, 'getClientRects' ).returns( [ geometry, secondGeometry ] );

const rects = Rect.getDomRangeRects( range );
expect( rects ).to.have.length( 2 );

assertRect( rects[ 0 ], geometry );
assertRect( rects[ 1 ], secondGeometry );
} );

// https://github.com/ckeditor/ckeditor5-utils/issues/153
it( 'should return rects for a Range (collapsed, no Range rects available)', () => {
const range = document.createRange();
const element = document.createElement( 'div' );

range.setStart( element, 0 );
range.collapse();
testUtils.sinon.stub( range, 'getClientRects' ).returns( [] );
testUtils.sinon.stub( element, 'getBoundingClientRect' ).returns( geometry );

const expectedGeometry = Object.assign( {}, geometry );
expectedGeometry.right = expectedGeometry.left;
expectedGeometry.width = 0;

const rects = Rect.getDomRangeRects( range );
expect( rects ).to.have.length( 1 );
assertRect( rects[ 0 ], expectedGeometry );
} );
} );
} );

function assertRect( rect, expected ) {
Expand Down

0 comments on commit f67aea1

Please sign in to comment.