138 lines
3.4 KiB
JavaScript
138 lines
3.4 KiB
JavaScript
const $ = (selector, root = document) => root.querySelector(selector);
|
|
const $$ = (selector, root = document) => [...root.querySelectorAll(selector)];
|
|
|
|
const THEME_KEY = "obyte-theme";
|
|
const themeToggle = $("#themeToggle");
|
|
const toast = $("#toast");
|
|
let toastTimer = null;
|
|
|
|
function showToast(message) {
|
|
if (!toast) return;
|
|
toast.textContent = message;
|
|
toast.classList.add("is-on");
|
|
clearTimeout(toastTimer);
|
|
toastTimer = setTimeout(() => toast.classList.remove("is-on"), 1400);
|
|
}
|
|
|
|
async function copyText(value) {
|
|
try {
|
|
await navigator.clipboard.writeText(value);
|
|
showToast("Kopiert: " + value);
|
|
} catch {
|
|
const fallback = document.createElement("textarea");
|
|
fallback.value = value;
|
|
fallback.style.position = "fixed";
|
|
fallback.style.left = "-9999px";
|
|
document.body.appendChild(fallback);
|
|
fallback.select();
|
|
document.execCommand("copy");
|
|
fallback.remove();
|
|
showToast("Kopiert: " + value);
|
|
}
|
|
}
|
|
|
|
document.addEventListener("click", (event) => {
|
|
const trigger = event.target.closest("[data-copy-value],[data-copy-block]");
|
|
if (!trigger) return;
|
|
|
|
const directValue = trigger.getAttribute("data-copy-value");
|
|
const blockSelector = trigger.getAttribute("data-copy-block");
|
|
|
|
if (directValue) {
|
|
copyText(directValue);
|
|
return;
|
|
}
|
|
|
|
if (blockSelector) {
|
|
const block = $(blockSelector);
|
|
if (block) {
|
|
copyText(block.innerText.trim());
|
|
}
|
|
}
|
|
});
|
|
|
|
function applyTheme(theme) {
|
|
const isDark = theme === "dark";
|
|
document.documentElement.setAttribute("data-theme", isDark ? "dark" : "light");
|
|
|
|
if (themeToggle) {
|
|
themeToggle.setAttribute("aria-pressed", String(isDark));
|
|
themeToggle.textContent = isDark ? "Hell" : "Dunkel";
|
|
}
|
|
}
|
|
|
|
function loadInitialTheme() {
|
|
let savedTheme = null;
|
|
|
|
try {
|
|
savedTheme = localStorage.getItem(THEME_KEY);
|
|
} catch {
|
|
savedTheme = null;
|
|
}
|
|
|
|
if (savedTheme === "light" || savedTheme === "dark") {
|
|
applyTheme(savedTheme);
|
|
return;
|
|
}
|
|
|
|
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
applyTheme(prefersDark ? "dark" : "light");
|
|
}
|
|
|
|
themeToggle?.addEventListener("click", () => {
|
|
const currentTheme = document.documentElement.getAttribute("data-theme");
|
|
const nextTheme = currentTheme === "dark" ? "light" : "dark";
|
|
|
|
applyTheme(nextTheme);
|
|
|
|
try {
|
|
localStorage.setItem(THEME_KEY, nextTheme);
|
|
} catch {
|
|
// Ignore storage errors in restrictive browser contexts.
|
|
}
|
|
});
|
|
|
|
loadInitialTheme();
|
|
|
|
const navToggle = $("#navToggle");
|
|
const navLinks = $("#navLinks");
|
|
|
|
navToggle?.addEventListener("click", () => {
|
|
const open = navLinks?.classList.toggle("is-open");
|
|
navToggle.setAttribute("aria-expanded", String(open));
|
|
});
|
|
|
|
$$(".navlink").forEach((link) => {
|
|
link.addEventListener("click", () => {
|
|
if (window.matchMedia("(max-width: 980px)").matches) {
|
|
navLinks?.classList.remove("is-open");
|
|
navToggle?.setAttribute("aria-expanded", "false");
|
|
}
|
|
});
|
|
});
|
|
|
|
const sections = $$(".section");
|
|
const links = $$(".navlink");
|
|
|
|
function setActive(id) {
|
|
links.forEach((link) => {
|
|
link.classList.toggle("is-active", link.getAttribute("href") === "#" + id);
|
|
});
|
|
}
|
|
|
|
const observer = new IntersectionObserver(
|
|
(entries) => {
|
|
entries.forEach((entry) => {
|
|
if (entry.isIntersecting && entry.target.id) {
|
|
setActive(entry.target.id);
|
|
}
|
|
});
|
|
},
|
|
{ threshold: 0.28 }
|
|
);
|
|
|
|
sections.forEach((section) => observer.observe(section));
|
|
|
|
if (location.hash) {
|
|
setActive(location.hash.replace("#", ""));
|
|
} |