upfront-editable
Version:
Friendly contenteditable API
119 lines (98 loc) • 3.48 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.binaryCursorSearch = binaryCursorSearch;
var _element = require("./element");
/**
* This is a binary search algorithm implementation aimed at finding
* a character offset position in a consecutive strings of characters
* over several lines.
*
* Refer to this page in order to learn more about binary search:
* https://en.wikipedia.org/wiki/Binary_search_algorithm
*
* @returns {object}
* - object with boolean `wasFound` indicating if the binary search found an offset and `offset` to indicate the actual character offset
*/
function binaryCursorSearch(_ref) {
var host = _ref.host,
requiredOnFirstLine = _ref.requiredOnFirstLine,
requiredOnLastLine = _ref.requiredOnLastLine,
positionX = _ref.positionX;
var hostRange = host.ownerDocument.createRange();
hostRange.selectNodeContents(host);
var hostCoords = hostRange.getBoundingClientRect();
var totalCharCount = (0, _element.getTotalCharCount)(host);
var textNodes = (0, _element.textNodesUnder)(host); // early terminate on empty editables
if (totalCharCount === 0) return {
wasFound: false
};
var data = {
currentOffset: Math.floor(totalCharCount / 2),
leftLimit: 0,
rightLimit: totalCharCount
};
var offset = data.currentOffset;
var distance;
var safety = 20;
while (data.leftLimit < data.rightLimit && safety > 0) {
safety = safety -= 1;
offset = data.currentOffset;
var _range = createRangeAtCharacterOffset({
textNodes: textNodes,
offset: data.currentOffset
});
var _coords = _range.getBoundingClientRect();
distance = Math.abs(_coords.left - positionX); // up / down axis
if (requiredOnFirstLine && hostCoords.top !== _coords.top) {
moveLeft(data);
continue;
} else if (requiredOnLastLine && hostCoords.bottom !== _coords.bottom) {
moveRight(data);
continue;
} // left / right axis
if (positionX < _coords.left) {
moveLeft(data);
} else {
moveRight(data);
}
}
var range = createRangeAtCharacterOffset({
textNodes: textNodes,
offset: data.currentOffset
});
var coords = range.getBoundingClientRect();
var finalDistance = Math.abs(coords.left - positionX); // Decide if last or second last offset is closest
if (finalDistance < distance) {
distance = finalDistance;
offset = data.currentOffset;
}
return {
distance: distance,
offset: offset,
wasFound: true
};
} // move the binary search index in between the current position and the left limit
function moveLeft(data) {
data.rightLimit = data.currentOffset;
data.currentOffset = Math.floor((data.currentOffset + data.leftLimit) / 2);
} // move the binary search index in between the current position and the right limit
function moveRight(data) {
data.leftLimit = data.currentOffset;
data.currentOffset = Math.ceil((data.currentOffset + data.rightLimit) / 2);
}
function createRangeAtCharacterOffset(_ref2) {
var textNodes = _ref2.textNodes,
offset = _ref2.offset;
var _getTextNodeAndRelati = (0, _element.getTextNodeAndRelativeOffset)({
textNodes: textNodes,
absOffset: offset
}),
node = _getTextNodeAndRelati.node,
relativeOffset = _getTextNodeAndRelati.relativeOffset;
var newRange = node.ownerDocument.createRange();
newRange.setStart(node, relativeOffset);
newRange.collapse(true);
return newRange;
}