js: Refactor line range handling
Important changes that should make reading the diff easier: * Script now only works on line numbers. Elements are queried when necessary * handleLineRange was refactored into functions that only validate and return line number(s), it's now the callers responsibility to highlight the range
This commit is contained in:
parent
558f75e3f5
commit
54bd3bc0a4
1 changed files with 66 additions and 72 deletions
138
static/script.js
138
static/script.js
|
|
@ -113,48 +113,51 @@ function setupSidebarSwitch() {
|
|||
});
|
||||
}
|
||||
|
||||
// Parses URL hash (anchor) in format La-Lb where a and b are line numbers,
|
||||
// highlights range between (and including) line numbers, scrolls to the
|
||||
// first line number in range.
|
||||
function handleLineRange(hashStr) {
|
||||
// Parse and validate line identifier in format L${number}
|
||||
function parseLineId(lineId) {
|
||||
if (lineId[0] != "L") {
|
||||
return;
|
||||
}
|
||||
|
||||
let lineIdNum = parseInt(lineId.substring(1));
|
||||
console.assert(!isNaN(lineIdNum), "Invalid line id");
|
||||
|
||||
let lineElement = document.getElementById(lineId);
|
||||
if (lineElement === null || lineElement.tagName !== "A") {
|
||||
return;
|
||||
}
|
||||
|
||||
return lineIdNum;
|
||||
}
|
||||
|
||||
// Parse and validate line range anchor in format #L${lineRangeStart}-L${lineRangeEnd}
|
||||
function parseLineRangeAnchor(hashStr) {
|
||||
const hash = hashStr.substring(1).split("-");
|
||||
if (hash.length != 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if hash format is valid
|
||||
if (hash[0][0] != "L" || hash[1][0] != "L") {
|
||||
return;
|
||||
}
|
||||
let firstLine = parseLineId(hash[0]);
|
||||
let lastLine = parseLineId(hash[1]);
|
||||
|
||||
const firstLineElement = document.getElementById(hash[0]);
|
||||
const lastLineElement = document.getElementById(hash[1]);
|
||||
if (firstLineElement === undefined || lastLineElement === undefined) {
|
||||
if (firstLine === undefined || lastLine === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
highlightFromTo(firstLineElement, lastLineElement);
|
||||
firstLineElement.scrollIntoView();
|
||||
// Swap line numbers to support "#L2-L1" format. Postel's law.
|
||||
if (firstLine > lastLine) {
|
||||
const lineTmp = lastLine;
|
||||
lastLine = firstLine;
|
||||
firstLine = lineTmp;
|
||||
}
|
||||
|
||||
return [firstLineElement, lastLineElement];
|
||||
return [firstLine, lastLine];
|
||||
}
|
||||
|
||||
// Highlights line number elements from firstLineElement to lastLineElement
|
||||
function highlightFromTo(firstLineElement, lastLineElement) {
|
||||
let firstLine = parseInt(firstLineElement.id.substring(1));
|
||||
let lastLine = parseInt(lastLineElement.id.substring(1));
|
||||
console.assert(!isNaN(firstLine) && !isNaN(lastLine),
|
||||
"Elements to highlight have invalid numbers in ids");
|
||||
|
||||
if (firstLine > lastLine) {
|
||||
const elementTmp = firstLineElement;
|
||||
firstLineElement = lastLineElement;
|
||||
lastLineElement = elementTmp;
|
||||
|
||||
const lineTmp = firstLine;
|
||||
firstLine = lastLine;
|
||||
lastLine = lineTmp;
|
||||
}
|
||||
// Highlights line number elements from firstLine to lastLine
|
||||
function highlightFromTo(firstLine, lastLine) {
|
||||
const firstLineElement = document.getElementById(`L${ firstLine }`);
|
||||
const lastLineElement = document.getElementById(`L${ lastLine }`);
|
||||
|
||||
const firstCodeLine = document.getElementById(`codeline-${ firstLine }`);
|
||||
const lastCodeLine = document.getElementById(`codeline-${ lastLine }`);
|
||||
|
|
@ -182,19 +185,18 @@ function setupLineRangeHandlers() {
|
|||
return;
|
||||
}
|
||||
|
||||
let rangeStart, rangeEnd;
|
||||
let rangeStartLine, rangeEndLine;
|
||||
|
||||
const highlightedRange = handleLineRange(window.location.hash);
|
||||
const highlightedRange = parseLineRangeAnchor(window.location.hash);
|
||||
// Set range start/end to elements from hash
|
||||
if (highlightedRange !== undefined) {
|
||||
rangeStart = highlightedRange[0];
|
||||
rangeEnd = highlightedRange[1];
|
||||
rangeStartLine = highlightedRange[0];
|
||||
rangeEndLine = highlightedRange[1];
|
||||
highlightFromTo(rangeStartLine, rangeEndLine);
|
||||
document.getElementById(`L${rangeStartLine}`).scrollIntoView();
|
||||
} else if (location.hash !== "" && location.hash[1] === "L") {
|
||||
const lineNum = parseInt(location.hash.substring(2));
|
||||
const hashElement = document.getElementById(location.hash.substring(1));
|
||||
if (hashElement !== null && hashElement.tagName === "A" && !isNaN(lineNum)) {
|
||||
rangeStart = hashElement;
|
||||
}
|
||||
const lineNum = parseLineId(location.hash.substring(1));
|
||||
rangeStartLine = document.getElementById(`L${lineNum}`);
|
||||
}
|
||||
|
||||
linenodiv.addEventListener("click", ev => {
|
||||
|
|
@ -216,55 +218,47 @@ function setupLineRangeHandlers() {
|
|||
el.classList.remove("line-highlight");
|
||||
}
|
||||
|
||||
if (rangeStart === undefined || !ev.shiftKey) {
|
||||
rangeStart = el;
|
||||
rangeStart.classList.add("line-highlight");
|
||||
rangeEnd = undefined;
|
||||
window.location.hash = rangeStart.id;
|
||||
if (rangeStartLine === undefined || !ev.shiftKey) {
|
||||
rangeStartLine = parseLineId(el.id);
|
||||
el.classList.add("line-highlight");
|
||||
rangeEndLine = undefined;
|
||||
window.location.hash = el.id;
|
||||
} else if(ev.shiftKey) {
|
||||
if (rangeEnd === undefined) {
|
||||
rangeEnd = el;
|
||||
if (rangeEndLine === undefined) {
|
||||
rangeEndLine = parseLineId(el.id);
|
||||
}
|
||||
|
||||
let rangeStartNumber = parseInt(rangeStart.id.substring(1));
|
||||
let rangeEndNumber = parseInt(rangeEnd.id.substring(1));
|
||||
const elNumber = parseInt(el.id.substring(1));
|
||||
console.assert(!isNaN(rangeStartNumber) && !isNaN(rangeEndNumber) && !isNaN(elNumber),
|
||||
"Elements to highlight have invalid numbers in ids");
|
||||
const newLine = parseLineId(el.id);
|
||||
console.assert(newLine !== undefined, "parseLineId for clicked line is undefined");
|
||||
|
||||
// Swap range elements to support "#L2-L1" format. Postel's law.
|
||||
if (rangeStartNumber > rangeEndNumber) {
|
||||
const rangeTmp = rangeStart;
|
||||
rangeStart = rangeEnd;
|
||||
rangeEnd = rangeTmp;
|
||||
|
||||
const numberTmp = rangeStartNumber;
|
||||
rangeStartNumber = rangeEndNumber;
|
||||
rangeEndNumber = numberTmp;
|
||||
// Swap range elements if range end that was previously undefined is now
|
||||
// before range start
|
||||
if (rangeStartLine > rangeEndLine) {
|
||||
const lineTmp = rangeStartLine;
|
||||
rangeStartLine = rangeEndLine;
|
||||
rangeEndLine = lineTmp;
|
||||
}
|
||||
|
||||
if (elNumber < rangeStartNumber) {
|
||||
if (newLine < rangeStartLine) {
|
||||
// Expand if element above range
|
||||
rangeStart = el;
|
||||
} else if (elNumber > rangeEndNumber) {
|
||||
rangeStartLine = newLine;
|
||||
} else if (newLine > rangeEndLine) {
|
||||
// Expand if element below range
|
||||
rangeEnd = el;
|
||||
rangeEndLine = newLine;
|
||||
} else {
|
||||
// Shrink moving the edge that's closest to the selection.
|
||||
// Move end if center was selected.
|
||||
const distanceFromStart = Math.abs(rangeStartNumber-elNumber);
|
||||
const distanceFromEnd = Math.abs(rangeEndNumber-elNumber);
|
||||
const distanceFromStart = Math.abs(rangeStartLine-newLine);
|
||||
const distanceFromEnd = Math.abs(rangeEndLine-newLine);
|
||||
if (distanceFromStart < distanceFromEnd) {
|
||||
rangeStart = el;
|
||||
} else if (distanceFromStart > distanceFromEnd) {
|
||||
rangeEnd = el;
|
||||
rangeStartLine = newLine;
|
||||
} else {
|
||||
rangeEnd = el;
|
||||
rangeEndLine = newLine;
|
||||
}
|
||||
}
|
||||
|
||||
highlightFromTo(rangeStart, rangeEnd);
|
||||
window.location.hash = `${rangeStart.id}-${rangeEnd.id}`;
|
||||
highlightFromTo(rangeStartLine, rangeEndLine);
|
||||
window.location.hash = `L${rangeStartLine}-L${rangeEndLine}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue