66 lines
1.9 KiB
JavaScript
66 lines
1.9 KiB
JavaScript
import { escapeChar } from "./html.js";
|
|
|
|
function bufferColToDomOffset(lineText, col) {
|
|
let offset = 0;
|
|
for (let i = 0; i < col && i < lineText.length; i++)
|
|
offset += escapeChar(lineText[i]).length;
|
|
return offset;
|
|
}
|
|
|
|
function findTextNode(parent, targetOffset) {
|
|
let remaining = targetOffset;
|
|
const walker = document.createTreeWalker(parent, NodeFilter.SHOW_TEXT);
|
|
let node;
|
|
while ((node = walker.nextNode())) {
|
|
if (remaining <= node.textContent.length)
|
|
return { node, offset: remaining };
|
|
remaining -= node.textContent.length;
|
|
}
|
|
return { node: parent, offset: parent.childNodes.length };
|
|
}
|
|
|
|
function posToElement(line, col, buffer) {
|
|
const spans = document.querySelectorAll("#text > span");
|
|
if (line >= spans.length) return null;
|
|
const span = spans[line];
|
|
const range = document.createRange();
|
|
const lineText = buffer[line] || "";
|
|
const offset = bufferColToDomOffset(lineText, col);
|
|
const tn = findTextNode(span, offset);
|
|
range.setStart(tn.node, tn.offset);
|
|
range.collapse(true);
|
|
const rect = range.getBoundingClientRect();
|
|
return { x: rect.left, y: rect.top, height: rect.height };
|
|
}
|
|
|
|
export function elementToPos(x, y, buffer) {
|
|
const spans = document.querySelectorAll("#text > span");
|
|
let bestLine = 0;
|
|
let bestDistY = Infinity;
|
|
|
|
for (let i = 0; i < spans.length; i++) {
|
|
const rect = spans[i].getBoundingClientRect();
|
|
const midY = rect.top + rect.height / 2;
|
|
const distY = Math.abs(y - midY);
|
|
if (distY < bestDistY) {
|
|
bestDistY = distY;
|
|
bestLine = i;
|
|
}
|
|
}
|
|
|
|
let bestCol = 0;
|
|
let bestDistX = Infinity;
|
|
const lineText = buffer[bestLine] || "";
|
|
for (let c = 0; c <= lineText.length; c++) {
|
|
const pos = posToElement(bestLine, c, buffer);
|
|
if (!pos) continue;
|
|
const dist = Math.abs(x - pos.x);
|
|
if (dist < bestDistX) {
|
|
bestDistX = dist;
|
|
bestCol = c;
|
|
}
|
|
}
|
|
|
|
return { line: bestLine, col: bestCol };
|
|
}
|