172 lines
5.2 KiB
JavaScript
172 lines
5.2 KiB
JavaScript
|
window.log("loaded " + window.location.href );
|
||
|
|
||
|
document.addEventListener("DOMContentLoaded", function() {
|
||
|
// Add an onclick listener to all anchors, buttons, and inputs to check whether
|
||
|
// this is a download link and if so, install the chart.
|
||
|
let controls = document.querySelectorAll('a:not(.button-disabled):not([disabled]), button:not([disabled]), input:not([disabled])');
|
||
|
controls.forEach(function(control) {
|
||
|
control.addEventListener("click", function() {
|
||
|
let href = control.href;
|
||
|
if (href && href.endsWith("/download")) {
|
||
|
window.log("Installing chart: " + href);
|
||
|
document.body.classList.add("ds-download");
|
||
|
window.setTimeout(function() {
|
||
|
window.installChart(href);
|
||
|
document.body.classList.remove("ds-download");
|
||
|
}, 100);
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
|
||
|
// Add a new stylesheet to the document to style the active control.
|
||
|
let style = document.createElement("style");
|
||
|
style.innerHTML = `
|
||
|
.ds-active {
|
||
|
position: relative;
|
||
|
}
|
||
|
.ds-active::after {
|
||
|
content: "";
|
||
|
border: 2px solid #e22c78;
|
||
|
outline: none;
|
||
|
|
||
|
position: absolute;
|
||
|
top: 0px;
|
||
|
left: 0px;
|
||
|
right: 0px;
|
||
|
bottom: 0px;
|
||
|
pointer-events: none;
|
||
|
}
|
||
|
.ds-download {
|
||
|
position: relative;
|
||
|
}
|
||
|
.ds-download::after {
|
||
|
content: "Downloading...";
|
||
|
font-size: 2em;
|
||
|
color: #e22c78;
|
||
|
position: absolute;
|
||
|
top: 50%;
|
||
|
left: 50%;
|
||
|
transform: translate(-50%, -50%);
|
||
|
pointer-events: none;
|
||
|
font-weight: 200;
|
||
|
}
|
||
|
.ds-download main {
|
||
|
opacity: 0.25;
|
||
|
}
|
||
|
`;
|
||
|
document.head.appendChild(style);
|
||
|
});
|
||
|
|
||
|
// Enable arrow keys for navigation.
|
||
|
document.onkeydown = function(e) {
|
||
|
switch (e.code) {
|
||
|
case "ArrowUp":
|
||
|
e.preventDefault();
|
||
|
getControlsInDirection("up");
|
||
|
break;
|
||
|
case "ArrowDown":
|
||
|
e.preventDefault();
|
||
|
getControlsInDirection("down");
|
||
|
break;
|
||
|
case "ArrowLeft":
|
||
|
e.preventDefault();
|
||
|
getControlsInDirection("left");
|
||
|
break;
|
||
|
case "ArrowRight":
|
||
|
e.preventDefault();
|
||
|
getControlsInDirection("right");
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function getControlsInDirection(direction) {
|
||
|
let controls = document.querySelectorAll('a:not(.button-disabled):not([disabled]), button:not([disabled]), input:not([disabled])');
|
||
|
if (!controls.length)
|
||
|
return;
|
||
|
|
||
|
let activeControl = document.querySelector(".ds-active");
|
||
|
if (!activeControl) {
|
||
|
controls[0].classList.add("ds-active");
|
||
|
controls[0].focus();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
let activeRect = activeControl.getBoundingClientRect();
|
||
|
let activeCenter = 0;
|
||
|
|
||
|
switch (direction) {
|
||
|
case "up":
|
||
|
case "down":
|
||
|
activeCenter = activeRect.top+activeRect.height/2;
|
||
|
break;
|
||
|
case "left":
|
||
|
case "right":
|
||
|
activeCenter = activeRect.left+activeRect.width/2;
|
||
|
break;
|
||
|
default:
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
let closestDistance = Number.MAX_VALUE;
|
||
|
let closestControl = null;
|
||
|
|
||
|
let skipOverlapCheck = false;
|
||
|
|
||
|
function getClosest(control) {
|
||
|
if (control === activeControl)
|
||
|
return;
|
||
|
|
||
|
let rect = control.getBoundingClientRect();
|
||
|
let center = 0;
|
||
|
let distance;
|
||
|
|
||
|
switch (direction) {
|
||
|
case "up":
|
||
|
case "down":
|
||
|
center = rect.top + rect.height / 2;
|
||
|
break;
|
||
|
case "left":
|
||
|
case "right":
|
||
|
center = rect.left + rect.width / 2;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (direction === "up" && (skipOverlapCheck || (activeRect.left < rect.right && activeRect.right > rect.left)))
|
||
|
distance = activeCenter - center;
|
||
|
else if (direction === "down" && (skipOverlapCheck || (activeRect.left < rect.right && activeRect.right > rect.left)))
|
||
|
distance = center - activeCenter;
|
||
|
else if (direction === "left" && (skipOverlapCheck || (activeRect.top < rect.bottom && activeRect.bottom > rect.top)))
|
||
|
distance = activeCenter - center;
|
||
|
else if (direction === "right" && (skipOverlapCheck || (activeRect.top < rect.bottom && activeRect.bottom > rect.top)))
|
||
|
distance = center - activeCenter;
|
||
|
else
|
||
|
return;
|
||
|
|
||
|
if (distance <= 0)
|
||
|
return;
|
||
|
|
||
|
if (distance < closestDistance) {
|
||
|
closestDistance = distance;
|
||
|
closestControl = control;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
controls.forEach(getClosest);
|
||
|
|
||
|
if (closestControl) {
|
||
|
activeControl.classList.remove("ds-active");
|
||
|
closestControl.classList.add("ds-active");
|
||
|
closestControl.focus();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// If no control was found, try again without checking for overlap.
|
||
|
skipOverlapCheck = true;
|
||
|
controls.forEach(getClosest);
|
||
|
|
||
|
if (closestControl) {
|
||
|
activeControl.classList.remove("ds-active");
|
||
|
closestControl.classList.add("ds-active");
|
||
|
closestControl.focus();
|
||
|
}
|
||
|
}
|