// site.js — Interaktivität der Mehrseiten-Website "Operations Research mit // Python": Dark-Mode-Toggle, mobile Sidebar, Volltextsuche, Lernfortschritt, // Code-Kopieren/Herunterladen. Reines Vanilla-JS, keine Abhaengigkeiten. (function () { "use strict"; var LS_THEME = "or-theme"; var LS_FORTSCHRITT = "or-fortschritt"; function ladeFortschritt() { try { return JSON.parse(localStorage.getItem(LS_FORTSCHRITT) || "{}"); } catch (e) { return {}; } } function speichereFortschritt(daten) { try { localStorage.setItem(LS_FORTSCHRITT, JSON.stringify(daten)); } catch (e) {} } // --- Dark-Mode-Toggle ------------------------------------------------- function initTheme() { var btn = document.getElementById("theme-toggle"); if (!btn) return; btn.addEventListener("click", function () { var aktuell = document.documentElement.getAttribute("data-theme"); var dunkelSystem = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches; var istDunkel = aktuell ? aktuell === "dark" : dunkelSystem; var neu = istDunkel ? "light" : "dark"; document.documentElement.setAttribute("data-theme", neu); try { localStorage.setItem(LS_THEME, neu); } catch (e) {} }); } // --- Mobile Sidebar ----------------------------------------------------- function initSidebar() { var sidebar = document.getElementById("sidebar"); var overlay = document.getElementById("sidebar-overlay"); var knopf = document.getElementById("sidebar-toggle"); if (!sidebar || !overlay || !knopf) return; function schliessen() { sidebar.classList.remove("offen"); overlay.classList.remove("offen"); overlay.hidden = true; } knopf.addEventListener("click", function () { var offen = sidebar.classList.toggle("offen"); overlay.hidden = !offen; overlay.classList.toggle("offen", offen); }); overlay.addEventListener("click", schliessen); sidebar.querySelectorAll("a").forEach(function (a) { a.addEventListener("click", schliessen); }); // Aktuelle Kapitelgruppe aufklappen, falls noch nicht offen. var aktiv = sidebar.querySelector("li.aktiv"); if (aktiv) { var details = aktiv.closest("details"); if (details) details.open = true; // Nur den Sidebar-eigenen Scrollbereich verschieben, NICHT // aktiv.scrollIntoView() verwenden: das wuerde auch das ganze Fenster // scrollen und einen #anker-Sprung aus der URL (z.B. von einem // Querverweis) wieder zunichtemachen. sidebar.scrollTop = Math.max(0, aktiv.offsetTop - sidebar.clientHeight / 2); } } // --- Lernfortschritt ------------------------------------------------ function initFortschritt() { var daten = ladeFortschritt(); document.querySelectorAll('.sidebar li[data-kapitel]').forEach(function (li) { if (daten[li.dataset.kapitel]) li.dataset.erledigt = "1"; }); var knopf = document.querySelector(".fortschritt-knopf"); if (!knopf) return; var kapitel = knopf.dataset.kapitel; function aktualisiereKnopf() { var erledigt = !!daten[kapitel]; knopf.classList.toggle("erledigt", erledigt); knopf.querySelector("span").textContent = erledigt ? "Als gelesen markiert" : "Als gelesen markieren"; var eintrag = document.querySelector( '.sidebar li[data-kapitel="' + kapitel + '"]'); if (eintrag) eintrag.dataset.erledigt = erledigt ? "1" : "0"; } aktualisiereKnopf(); knopf.addEventListener("click", function () { daten[kapitel] = !daten[kapitel]; speichereFortschritt(daten); aktualisiereKnopf(); }); } // --- Suche ------------------------------------------------------------ function initSuche() { var eingabe = document.getElementById("suche-eingabe"); var ergebnisBox = document.getElementById("suche-ergebnisse"); if (!eingabe || !ergebnisBox) return; // window.OR_SEARCH_INDEX kommt aus assets/search-index.js (als normales //