Files
Sven Steinert f4511b9213 MD Umbau
2026-05-13 11:57:52 +02:00

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("#", ""));
}