Commit 8cbdc1e2 authored by Robert Knight's avatar Robert Knight Committed by Nick Stenning

Fix the implementation of `isNodeInRange()` (#3522)

This function failed to account for the case where the node was
contained by, but not equal to, the range's startContainer.

In new browsers we could just use `Range.isPointInRange()`. Since that
is not available in IE 10/11, add an implementation adapted from
Blink's `Range::isPointInRange()` C++ impl, minus support for the
`offset` parameter.

This fixes an issue where the adder could fail to show up when making
certain selections because `getTextBoundingBoxes()` could incorrectly
fail to find the text nodes within certan ranges.

Fixes #3521
parent 6c7533d8
......@@ -29,8 +29,10 @@ function isSelectionBackwards(selection) {
}
/**
* Returns true if `node` is between the `startContainer` and `endContainer` of
* the given `range`, inclusive.
* Returns true if `node` lies within a range.
*
* This is a simplified version of `Range.isPointInRange()` for compatibility
* with IE.
*
* @param {Range} range
* @param {Node} node
......@@ -40,13 +42,14 @@ function isNodeInRange(range, node) {
return true;
}
/* jshint -W016 */
var isAfterStart = range.startContainer.compareDocumentPosition(node) &
Node.DOCUMENT_POSITION_FOLLOWING;
var isBeforeEnd = range.endContainer.compareDocumentPosition(node) &
Node.DOCUMENT_POSITION_PRECEDING;
return isAfterStart && isBeforeEnd;
var nodeRange = node.ownerDocument.createRange();
nodeRange.selectNode(node);
var isAtOrBeforeStart =
range.compareBoundaryPoints(Range.START_TO_START, nodeRange) <= 0;
var isAtOrAfterEnd =
range.compareBoundaryPoints(Range.END_TO_END, nodeRange) >= 0;
nodeRange.detach();
return isAtOrBeforeStart && isAtOrAfterEnd;
}
/**
......@@ -141,6 +144,8 @@ function selectionFocusRect(selection) {
}
module.exports = {
getTextBoundingBoxes: getTextBoundingBoxes,
isNodeInRange: isNodeInRange,
isSelectionBackwards: isSelectionBackwards,
selectionFocusRect: selectionFocusRect,
};
......@@ -2,6 +2,13 @@
var rangeUtil = require('../range-util');
function createRange(node, start, end) {
var range = node.ownerDocument.createRange();
range.setStart(node, start);
range.setEnd(node, end);
return range;
}
describe('range-util', function () {
var selection;
var testNode;
......@@ -25,6 +32,41 @@ describe('range-util', function () {
selection.addRange(range);
}
describe('#isNodeInRange', function () {
it('is true for a node in the range', function () {
var rng = createRange(testNode, 0, 1);
assert.equal(rangeUtil.isNodeInRange(rng, testNode.firstChild), true);
});
it('is false for a node before the range', function () {
testNode.innerHTML = 'one <b>two</b> three';
var rng = createRange(testNode, 1, 2);
assert.equal(rangeUtil.isNodeInRange(rng, testNode.firstChild), false);
});
it('is false for a node after the range', function () {
testNode.innerHTML = 'one <b>two</b> three';
var rng = createRange(testNode, 1, 2);
assert.equal(rangeUtil.isNodeInRange(rng, testNode.childNodes.item(2)), false);
});
});
describe('#getTextBoundingBoxes', function () {
it('gets the bounding box of a range in a text node', function () {
testNode.innerHTML = 'plain text';
var rng = createRange(testNode.firstChild, 0, 5);
var boxes = rangeUtil.getTextBoundingBoxes(rng);
assert.ok(boxes.length);
});
it('gets the bounding box of a range containing a text node', function () {
testNode.innerHTML = 'plain text';
var rng = createRange(testNode, 0, 1);
var boxes = rangeUtil.getTextBoundingBoxes(rng);
assert.ok(boxes.length);
});
});
describe('#selectionFocusRect', function () {
it('returns null if the selection is empty', function () {
assert.isNull(rangeUtil.selectionFocusRect(selection));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment