(function () { "use strict"; var TWITCH_CHANNEL = "duda_xd"; /* Rendert die Video-IDs aus videos.js als iframes. * Standard-iframe laut YouTube-Empfehlung. Bewusst OHNE * referrerpolicy und OHNE ?si-Parameter: Dann sendet der Browser * immer einen Referer (auch bei HTTP-Seite), den YouTube braucht, * um den Embedder zu erkennen (sonst Fehler 152/153). */ function renderYouTubeGrid(videoIds) { var grid = document.getElementById("yt-grid"); if (!grid) return; if (!videoIds || !videoIds.length) { grid.innerHTML = '
Noch keine Videos vorhanden.
'; return; } var html = ""; for (var i = 0; i < videoIds.length; i++) { var id = String(videoIds[i]).trim(); if (!id) continue; html += '
' + '
' + '' + '
' + '
'; } grid.innerHTML = html || '
Keine Videos gefunden.
'; } /* ------------------------------------------------------------------ * YouTube: Liest die Video-IDs aus videos.js, die auf dem Server * von fetch_videos.py (einmal am Tag per Cron) mit den aktuellen * Video-IDs deines Kanals aktualisiert wird. * * Dein API-Key ist dabei NIE im Browser - er bleibt komplett auf * dem Server versteckt (siehe fetch_videos.py). * ------------------------------------------------------------------ */ function loadYouTube() { var ids = (typeof DUDA17_VIDEOS !== "undefined") ? DUDA17_VIDEOS : []; renderYouTubeGrid(ids); } /* ------------------------------------------------------------------ * Twitch: Live-Player einbetten. * ------------------------------------------------------------------ */ function loadTwitch() { var wrap = document.getElementById("twitch-embed"); if (!wrap) return; var domain = window.location.hostname || "localhost"; var iframe = document.createElement("iframe"); iframe.src = "https://player.twitch.tv/?channel=" + TWITCH_CHANNEL + "&parent=" + domain + "&muted=true"; iframe.setAttribute("title", "Twitch Stream"); iframe.setAttribute("allowfullscreen", "true"); iframe.setAttribute("scrolling", "no"); iframe.setAttribute("allow", "autoplay; fullscreen"); wrap.appendChild(iframe); } /* ------------------------------------------------------------------ * Sticky Navigation & Back-to-Top * ------------------------------------------------------------------ */ function initNavigation() { var nav = document.getElementById("sticky-nav"); var backToTop = document.getElementById("back-to-top"); var navLinks = document.querySelectorAll(".nav-link[data-section]"); var sections = []; navLinks.forEach(function (link) { var id = link.getAttribute("data-section"); var el = document.getElementById(id); if (el) sections.push({ id: id, el: el, link: link }); }); function onScroll() { var scrollY = window.scrollY || window.pageYOffset; if (scrollY > 300) { nav.classList.add("visible"); backToTop.classList.add("visible"); } else { nav.classList.remove("visible"); backToTop.classList.remove("visible"); } var active = null; for (var i = 0; i < sections.length; i++) { var rect = sections[i].el.getBoundingClientRect(); if (rect.top <= 120) active = sections[i]; } navLinks.forEach(function (l) { l.classList.remove("active"); }); if (active) active.link.classList.add("active"); } window.addEventListener("scroll", onScroll, { passive: true }); onScroll(); backToTop.addEventListener("click", function () { window.scrollTo({ top: 0, behavior: "smooth" }); }); navLinks.forEach(function (link) { link.addEventListener("click", function (e) { e.preventDefault(); var id = this.getAttribute("data-section"); var target = document.getElementById(id); if (target) { target.scrollIntoView({ behavior: "smooth" }); } }); }); var brandLink = document.querySelector(".nav-brand"); if (brandLink) { brandLink.addEventListener("click", function (e) { e.preventDefault(); window.scrollTo({ top: 0, behavior: "smooth" }); }); } } document.addEventListener("DOMContentLoaded", function () { loadYouTube(); loadTwitch(); initNavigation(); }); })();