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 #154 from ckeditor/t/153
Browse files Browse the repository at this point in the history
Fix: Rect utility should work for collapsed DOM Ranges. Closes #153.
  • Loading branch information
Reinmar authored May 4, 2017
2 parents 6bf1741 + 273513d commit 92aff35
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 7 deletions.
43 changes: 37 additions & 6 deletions src/dom/rect.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import global from './global';
import isRange from './isrange';
import isElement from '../lib/lodash/isElement';

const rectProperties = [ 'top', 'right', 'bottom', 'left', 'width', 'height' ];

/**
* A helper class representing a `ClientRect` object, e.g. value returned by
* the native `object.getBoundingClientRect()` method. Provides a set of methods
Expand Down Expand Up @@ -54,11 +52,31 @@ export default class Rect {
enumerable: false
} );

if ( isElement( source ) || isRange( source ) ) {
source = source.getBoundingClientRect();
}
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();

rectProperties.forEach( p => this[ p ] = source[ p ] );
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() );
}
} else {
copyRectProperties( this, source );
}

/**
* The "top" value of the rect.
Expand Down Expand Up @@ -251,3 +269,16 @@ export default class Rect {
} );
}
}

const rectProperties = [ 'top', 'right', 'bottom', 'left', 'width', 'height' ];

// Acquires all the rect properties from the passed source.
//
// @private
// @param {module:utils/dom/rect~Rect} rect
// @param {ClientRect|module:utils/dom/rect~Rect|Object} source
function copyRectProperties( rect, source ) {
for ( const p of rectProperties ) {
rect[ p ] = source[ p ];
}
}
30 changes: 29 additions & 1 deletion tests/dom/rect.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,41 @@ describe( 'Rect', () => {
assertRect( new Rect( element ), geometry );
} );

it( 'should accept Range', () => {
it( 'should accept Range (non–collapsed)', () => {
const range = document.createRange();

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

assertRect( new Rect( range ), geometry );
} );

// https://github.com/ckeditor/ckeditor5-utils/issues/153
it( 'should accept Range (collapsed)', () => {
const range = document.createRange();

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

assertRect( new Rect( range ), geometry );
} );

// https://github.com/ckeditor/ckeditor5-utils/issues/153
it( 'should accept 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.width = 0;

assertRect( new Rect( range ), expectedGeometry );
} );

it( 'should accept Rect', () => {
const sourceRect = new Rect( geometry );
const rect = new Rect( sourceRect );
Expand Down Expand Up @@ -481,6 +508,7 @@ describe( 'Rect', () => {

it( 'should return the visible rect (Range), partially cropped', () => {
range.setStart( ancestorA, 0 );
range.setEnd( ancestorA, 1 );

testUtils.sinon.stub( range, 'getBoundingClientRect' ).returns( {
top: 0,
Expand Down

0 comments on commit 92aff35

Please sign in to comment.