diff --git a/js/play.js b/js/play.js index a73dd06..ced5d3d 100644 --- a/js/play.js +++ b/js/play.js @@ -1,379 +1,436 @@ -(function() { - // --- Konfiguration --- - const MEMORIZE_TIME_SECONDS = 15; +(function () { + // --- Konfiguration --- + const MEMORIZE_TIME_SECONDS = 15; - // Bausteine fuer den zufaelligen Rundentext. Alles bleibt lokal, damit das Spiel ohne Backend starten kann. - const TEXT_PARTS = { - subjects: [ - "Der flinke Entwickler", - "Die neugierige Studentin", - "Ein mueder Professor", - "Das kleine Frontend", - "Der mutige Browser", - "Eine schlaue Funktion", - "Der vergessliche Server", - "Die kreative Gruppe" - ], - actions: [ - "sortiert leise", - "debuggt geduldig", - "vergleicht heimlich", - "speichert vorsichtig", - "rendert ploetzlich", - "zaehlt konzentriert", - "testet neugierig", - "kompiliert langsam" - ], - objects: [ - "sieben blaue Buttons", - "drei lange Variablen", - "neun goldene Woerter", - "vier kaputte Formulare", - "acht schnelle Requests", - "zwei leuchtende Karten", - "fuenf stille Fehlermeldungen", - "sechs winzige Icons" - ], - places: [ - "im hellen Dashboard", - "unter dem dunklen Navbar", - "neben dem alten Footer", - "zwischen Login und Leaderboard", - "vor dem ersten Kaffee", - "waehrend der Lernphase", - "hinter dem lokalen Server", - "mitten im Semesterprojekt" - ], - endings: [ - "Danach lacht der Code, weil alles endlich funktioniert.", - "Am Ende merkt sich niemand die Semikolons, aber alle die Punkte.", - "Kurz darauf blinkt die Konsole und behauptet, sie sei unschuldig.", - "Spaeter landet der Score im Ranking und wartet auf Applaus.", - "Dabei bleibt die Seite ruhig, obwohl der Timer dramatisch tickt.", - "Zum Schluss gewinnt, wer die Woerter sauber in Reihenfolge bringt." - ] + // Bausteine fuer den zufaelligen Rundentext. Alles bleibt lokal, damit das Spiel ohne Backend starten kann. + const TEXT_PARTS = { + subjects: [ + "Der flinke Entwickler", + "Die neugierige Studentin", + "Ein mueder Professor", + "Das kleine Frontend", + "Der mutige Browser", + "Eine schlaue Funktion", + "Der vergessliche Server", + "Die kreative Gruppe", + ], + actions: [ + "sortiert leise", + "debuggt geduldig", + "vergleicht heimlich", + "speichert vorsichtig", + "rendert ploetzlich", + "zaehlt konzentriert", + "testet neugierig", + "kompiliert langsam", + ], + objects: [ + "sieben blaue Buttons", + "drei lange Variablen", + "neun goldene Woerter", + "vier kaputte Formulare", + "acht schnelle Requests", + "zwei leuchtende Karten", + "fuenf stille Fehlermeldungen", + "sechs winzige Icons", + ], + places: [ + "im hellen Dashboard", + "unter dem dunklen Navbar", + "neben dem alten Footer", + "zwischen Login und Leaderboard", + "vor dem ersten Kaffee", + "waehrend der Lernphase", + "hinter dem lokalen Server", + "mitten im Semesterprojekt", + ], + endings: [ + "Danach lacht der Code, weil alles endlich funktioniert.", + "Am Ende merkt sich niemand die Semikolons, aber alle die Punkte.", + "Kurz darauf blinkt die Konsole und behauptet, sie sei unschuldig.", + "Spaeter landet der Score im Ranking und wartet auf Applaus.", + "Dabei bleibt die Seite ruhig, obwohl der Timer dramatisch tickt.", + "Zum Schluss gewinnt, wer die Woerter sauber in Reihenfolge bringt.", + ], + }; + + let timerInterval; + let currentTime = 0; + + // Der aktuell angezeigte Text muss bis zur Auswertung stabil bleiben. + let currentGameText = ""; + let lastGeneratedText = ""; + + // DOM-Referenzen werden erst gesetzt, nachdem pages/play.html dynamisch geladen wurde. + let phaseStart; + let phaseMemorize; + let phaseInput; + let phaseResult; + let targetTextDisplay; + let timerDisplay; + let userTextInput; + let resultScore; + let resultOriginal; + let resultInput; + let gameStatus; + let scoreSaveFeedback; + let btnSubmitScore; + + // --- Funktionen --- + + function getRandomItem(items) { + return items[Math.floor(Math.random() * items.length)]; + } + + // Erstellt pro Runde zwei zufaellige Saetze plus Schluss-Satz und vermeidet direkte Wiederholungen. + function generateGameText() { + let generatedText = ""; + + do { + const firstSentence = + [ + getRandomItem(TEXT_PARTS.subjects), + getRandomItem(TEXT_PARTS.actions), + getRandomItem(TEXT_PARTS.objects), + getRandomItem(TEXT_PARTS.places), + ].join(" ") + "."; + + const secondSentence = + [ + getRandomItem(TEXT_PARTS.subjects), + getRandomItem(TEXT_PARTS.actions), + getRandomItem(TEXT_PARTS.objects), + getRandomItem(TEXT_PARTS.places), + ].join(" ") + "."; + + generatedText = + firstSentence + + " " + + secondSentence + + " " + + getRandomItem(TEXT_PARTS.endings); + } while (generatedText === lastGeneratedText); + + lastGeneratedText = generatedText; + return generatedText; + } + + function startGame() { + if (!phaseStart || !phaseMemorize) return; + + // Startansicht ausblenden und den neu generierten Text fuer die Lernphase anzeigen. + phaseStart.classList.add("d-none"); + phaseMemorize.classList.remove("d-none"); + + if (gameStatus) { + gameStatus.textContent = "Lernphase"; + gameStatus.className = "badge fs-6 px-3 py-2"; + gameStatus.style.backgroundColor = "#ffd166"; + gameStatus.style.color = "#1b1b2f"; + } + + currentGameText = generateGameText(); + if (targetTextDisplay) targetTextDisplay.textContent = currentGameText; + + // Nach Ablauf des Timers wird automatisch zur Eingabe gewechselt. + currentTime = MEMORIZE_TIME_SECONDS; + if (timerDisplay) timerDisplay.textContent = currentTime; + + timerInterval = setInterval(() => { + currentTime--; + if (timerDisplay) timerDisplay.textContent = currentTime; + + if (currentTime <= 0) { + endMemorizePhase(); + } + }, 1000); + } + + function endMemorizePhase() { + clearInterval(timerInterval); + + if (!phaseMemorize || !phaseInput) return; + + // Text verschwindet, Eingabefeld erscheint: ab hier zaehlt nur noch das Gedaechtnis. + phaseMemorize.classList.add("d-none"); + phaseInput.classList.remove("d-none"); + + if (gameStatus) { + gameStatus.textContent = "Eingabe"; + gameStatus.className = "badge fs-6 px-3 py-2"; + gameStatus.style.backgroundColor = "#4a6fa5"; + gameStatus.style.color = "#fff"; + } + + if (userTextInput) { + userTextInput.value = ""; + userTextInput.focus(); + } + } + + // Entfernt alles, was beim Vergleichen nicht zaehlen soll. + function normalizeWord(word) { + return word.toLowerCase().replace(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g, ""); + } + + // Behält die sichtbaren Woerter separat, damit Satzzeichen in der Ergebnisanzeige erhalten bleiben. + function getWords(text) { + return text.split(/\s+/).filter((word) => word.length > 0); + } + + function calculateScore(original, input) { + if (!original || !input) return 0; + + // Score-Regel: gleiche Woerter an gleicher Position, Satzzeichen und Grossschreibung ignoriert. + const cleanOriginal = getWords(original) + .map(normalizeWord) + .filter((word) => word.length > 0); + const cleanInput = getWords(input) + .map(normalizeWord) + .filter((word) => word.length > 0); + + let correctWords = 0; + const limit = Math.min(cleanOriginal.length, cleanInput.length); + + for (let i = 0; i < limit; i++) { + if (cleanInput[i] === cleanOriginal[i]) { + correctWords++; + } + } + + return correctWords; + } + + // Baut ein einzelnes farbiges Wort-Label fuer den Ergebnisvergleich. + function createWordBadge(word, isCorrect) { + const badge = document.createElement("span"); + badge.className = + "badge me-1 mb-1 " + (isCorrect ? "text-bg-success" : "text-bg-danger"); + badge.textContent = word; + return badge; + } + + // Markiert Original und Eingabe nach derselben Positionslogik wie calculateScore(). + function renderWordComparison(original, input) { + if (!resultOriginal || !resultInput) return; + + const originalWords = getWords(original); + const inputWords = getWords(input); + + resultOriginal.innerHTML = ""; + resultInput.innerHTML = ""; + + // Original: rot, wenn das eingegebene Wort an dieser Position fehlt oder falsch ist. + originalWords.forEach((word, index) => { + const isCorrect = + normalizeWord(word) === normalizeWord(inputWords[index] || ""); + resultOriginal.appendChild(createWordBadge(word, isCorrect)); + }); + + // Eingabe: rot, wenn das Wort nicht zum Originalwort an derselben Position passt. + inputWords.forEach((word, index) => { + const isCorrect = + normalizeWord(word) === normalizeWord(originalWords[index] || ""); + resultInput.appendChild(createWordBadge(word, isCorrect)); + }); + } + + function getAuth() { + if (!window.AppAuth || typeof window.AppAuth.getAuth !== "function") { + return null; + } + + return window.AppAuth.getAuth(); + } + + function getScoreService() { + if (!window.config || !window.ScoreService) { + return null; + } + + return new window.ScoreService(window.config); + } + + function showScoreSaveFeedback(message, type) { + if (!scoreSaveFeedback) return; + + scoreSaveFeedback.className = "alert alert-" + type + " mb-4"; + scoreSaveFeedback.textContent = message; + scoreSaveFeedback.classList.remove("d-none"); + } + + async function saveScore(scoreData) { + const auth = getAuth(); + if (!auth || !auth.username || !auth.password) { + showScoreSaveFeedback( + "Score wurde nur lokal berechnet. Bitte einloggen, damit er im Leaderboard gespeichert wird.", + "warning", + ); + return; + } + + // Auth-Daten kommen aus login.js; der ScoreService setzt daraus die Backend-Header. + const scoreService = getScoreService(); + if (!scoreService) { + showScoreSaveFeedback( + "Score-Service konnte nicht geladen werden.", + "danger", + ); + return; + } + + showScoreSaveFeedback("Score wird gespeichert...", "info"); + + const result = await scoreService.postScore( + auth.username, + auth.password, + scoreData.score, + scoreData.time, + scoreData.text, + scoreData.userWrittenText, + ); + + if (result.ok) { + const place = + result.body && result.body.place + ? " Platz " + result.body.place + "." + : ""; + showScoreSaveFeedback( + "Score erfolgreich gespeichert." + place, + "success", + ); + return; + } + + if (result.status === 401) { + showScoreSaveFeedback( + "Score konnte nicht gespeichert werden: Login ist nicht gültig.", + "danger", + ); + return; + } + + showScoreSaveFeedback( + "Score konnte nicht gespeichert werden (Status " + result.status + ").", + "danger", + ); + } + + async function submitScore() { + if (!userTextInput) return; + + const userInput = userTextInput.value.trim(); + if (!userInput) { + alert("Bitte geben Sie einen Text ein."); + return; + } + + if (btnSubmitScore) { + btnSubmitScore.disabled = true; + btnSubmitScore.textContent = "Wird ausgewertet..."; + } + + const score = calculateScore(currentGameText, userInput); + + // Ergebnis sofort anzeigen; das Speichern im Backend passiert danach asynchron. + if (phaseInput) phaseInput.classList.add("d-none"); + if (phaseResult) phaseResult.classList.remove("d-none"); + + if (gameStatus) { + gameStatus.textContent = "Abgeschlossen"; + gameStatus.className = "badge fs-6 px-3 py-2"; + gameStatus.style.backgroundColor = "#28a745"; + gameStatus.style.color = "#fff"; + } + + if (resultScore) resultScore.textContent = score; + renderWordComparison(currentGameText, userInput); + + // Genau dieser Rundentext wird gespeichert, damit Leaderboard/Score-Details nachvollziehbar bleiben. + const scoreData = { + score: score, + time: MEMORIZE_TIME_SECONDS, + text: currentGameText, + userWrittenText: userInput, }; - let timerInterval; - let currentTime = 0; + console.log("Score bereit zum Senden:", scoreData); - // Der aktuell angezeigte Text muss bis zur Auswertung stabil bleiben. - let currentGameText = ""; - let lastGeneratedText = ""; + try { + await saveScore(scoreData); + } catch (error) { + console.error("Fehler beim Speichern des Scores:", error); + showScoreSaveFeedback( + "Score konnte wegen eines technischen Fehlers nicht gespeichert werden.", + "danger", + ); + } finally { + if (btnSubmitScore) { + btnSubmitScore.disabled = false; + btnSubmitScore.textContent = "Auswerten & Absenden"; + } + } + } - // DOM-Referenzen werden erst gesetzt, nachdem pages/play.html dynamisch geladen wurde. - let phaseStart; - let phaseMemorize; - let phaseInput; - let phaseResult; - let targetTextDisplay; - let timerDisplay; - let userTextInput; - let resultScore; - let resultOriginal; - let resultInput; - let gameStatus; - let scoreSaveFeedback; - let btnSubmitScore; + window.initPlayPage = function initPlayPage() { + clearInterval(timerInterval); - // --- Funktionen --- + // Die Navigation laedt play.html per fetch; deshalb werden die Elemente erst hier gesucht. + phaseStart = document.getElementById("phaseStart"); + phaseMemorize = document.getElementById("phaseMemorize"); + phaseInput = document.getElementById("phaseInput"); + phaseResult = document.getElementById("phaseResult"); - function getRandomItem(items) { - return items[Math.floor(Math.random() * items.length)]; + targetTextDisplay = document.getElementById("targetTextDisplay"); + timerDisplay = document.getElementById("timerDisplay"); + userTextInput = document.getElementById("userTextInput"); + resultScore = document.getElementById("resultScore"); + resultOriginal = document.getElementById("resultOriginal"); + resultInput = document.getElementById("resultInput"); + gameStatus = document.getElementById("gameStatus"); + scoreSaveFeedback = document.getElementById("scoreSaveFeedback"); + + const btnStart = document.getElementById("btnStartGame"); + btnSubmitScore = document.getElementById("btnSubmitScore"); + const btnRestart = document.getElementById("btnRestart"); + const btnLeaderboard = document.getElementById("btnLeaderboard"); + + if (btnStart) btnStart.addEventListener("click", startGame); + if (btnSubmitScore) btnSubmitScore.addEventListener("click", submitScore); + if (btnRestart) + btnRestart.addEventListener("click", () => + window.loadPage("play", "nav-play"), + ); + if (btnLeaderboard) + btnLeaderboard.addEventListener("click", () => { + const navLink = document.getElementById("nav-leaderboard"); + if (navLink) { + navLink.click(); // Nutzt die bestehende Navigation inklusive Active-State. + } else { + console.warn("Sidebar Link #nav-leaderboard nicht gefunden."); + } + }); + + if (userTextInput) { + userTextInput.addEventListener("keydown", (e) => { + const key = e.key.toLowerCase(); + if ( + (e.ctrlKey || e.metaKey) && + (key === "c" || key === "v" || key === "x" || key === "a") + ) { + e.preventDefault(); + alert("Yeah, cheating is not sooo nice..."); + } + }); + + userTextInput.addEventListener("paste", (e) => e.preventDefault()); + userTextInput.addEventListener("copy", (e) => e.preventDefault()); + userTextInput.addEventListener("cut", (e) => e.preventDefault()); } - // Erstellt pro Runde zwei zufaellige Saetze plus Schluss-Satz und vermeidet direkte Wiederholungen. - function generateGameText() { - let generatedText = ""; - - do { - const firstSentence = [ - getRandomItem(TEXT_PARTS.subjects), - getRandomItem(TEXT_PARTS.actions), - getRandomItem(TEXT_PARTS.objects), - getRandomItem(TEXT_PARTS.places) - ].join(" ") + "."; - - const secondSentence = [ - getRandomItem(TEXT_PARTS.subjects), - getRandomItem(TEXT_PARTS.actions), - getRandomItem(TEXT_PARTS.objects), - getRandomItem(TEXT_PARTS.places) - ].join(" ") + "."; - - generatedText = firstSentence + " " + secondSentence + " " + getRandomItem(TEXT_PARTS.endings); - } while (generatedText === lastGeneratedText); - - lastGeneratedText = generatedText; - return generatedText; + if (targetTextDisplay) { + targetTextDisplay.addEventListener("copy", (e) => e.preventDefault()); + targetTextDisplay.style.userSelect = "none"; } - - function startGame() { - if (!phaseStart || !phaseMemorize) return; - - // Startansicht ausblenden und den neu generierten Text fuer die Lernphase anzeigen. - phaseStart.classList.add('d-none'); - phaseMemorize.classList.remove('d-none'); - - if(gameStatus) { - gameStatus.textContent = "Lernphase"; - gameStatus.className = "badge fs-6 px-3 py-2"; - gameStatus.style.backgroundColor = "#ffd166"; - gameStatus.style.color = "#1b1b2f"; - } - - currentGameText = generateGameText(); - if(targetTextDisplay) targetTextDisplay.textContent = currentGameText; - - // Nach Ablauf des Timers wird automatisch zur Eingabe gewechselt. - currentTime = MEMORIZE_TIME_SECONDS; - if(timerDisplay) timerDisplay.textContent = currentTime; - - timerInterval = setInterval(() => { - currentTime--; - if(timerDisplay) timerDisplay.textContent = currentTime; - - if (currentTime <= 0) { - endMemorizePhase(); - } - }, 1000); - } - - function endMemorizePhase() { - clearInterval(timerInterval); - - if (!phaseMemorize || !phaseInput) return; - - // Text verschwindet, Eingabefeld erscheint: ab hier zaehlt nur noch das Gedaechtnis. - phaseMemorize.classList.add('d-none'); - phaseInput.classList.remove('d-none'); - - if(gameStatus) { - gameStatus.textContent = "Eingabe"; - gameStatus.className = "badge fs-6 px-3 py-2"; - gameStatus.style.backgroundColor = "#4a6fa5"; - gameStatus.style.color = "#fff"; - } - - if(userTextInput) { - userTextInput.value = ""; - userTextInput.focus(); - } - } - - // Entfernt alles, was beim Vergleichen nicht zaehlen soll. - function normalizeWord(word) { - return word.toLowerCase().replace(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g, ""); - } - - // Behält die sichtbaren Woerter separat, damit Satzzeichen in der Ergebnisanzeige erhalten bleiben. - function getWords(text) { - return text.split(/\s+/).filter(word => word.length > 0); - } - - function calculateScore(original, input) { - if (!original || !input) return 0; - - // Score-Regel: gleiche Woerter an gleicher Position, Satzzeichen und Grossschreibung ignoriert. - const cleanOriginal = getWords(original).map(normalizeWord).filter(word => word.length > 0); - const cleanInput = getWords(input).map(normalizeWord).filter(word => word.length > 0); - - let correctWords = 0; - const limit = Math.min(cleanOriginal.length, cleanInput.length); - - for (let i = 0; i < limit; i++) { - if (cleanInput[i] === cleanOriginal[i]) { - correctWords++; - } - } - - return correctWords; - } - - // Baut ein einzelnes farbiges Wort-Label fuer den Ergebnisvergleich. - function createWordBadge(word, isCorrect) { - const badge = document.createElement("span"); - badge.className = "badge me-1 mb-1 " + (isCorrect ? "text-bg-success" : "text-bg-danger"); - badge.textContent = word; - return badge; - } - - // Markiert Original und Eingabe nach derselben Positionslogik wie calculateScore(). - function renderWordComparison(original, input) { - if (!resultOriginal || !resultInput) return; - - const originalWords = getWords(original); - const inputWords = getWords(input); - - resultOriginal.innerHTML = ""; - resultInput.innerHTML = ""; - - // Original: rot, wenn das eingegebene Wort an dieser Position fehlt oder falsch ist. - originalWords.forEach((word, index) => { - const isCorrect = normalizeWord(word) === normalizeWord(inputWords[index] || ""); - resultOriginal.appendChild(createWordBadge(word, isCorrect)); - }); - - // Eingabe: rot, wenn das Wort nicht zum Originalwort an derselben Position passt. - inputWords.forEach((word, index) => { - const isCorrect = normalizeWord(word) === normalizeWord(originalWords[index] || ""); - resultInput.appendChild(createWordBadge(word, isCorrect)); - }); - } - - function getAuth() { - if (!window.AppAuth || typeof window.AppAuth.getAuth !== "function") { - return null; - } - - return window.AppAuth.getAuth(); - } - - function getScoreService() { - if (!window.config || !window.ScoreService) { - return null; - } - - return new window.ScoreService(window.config); - } - - function showScoreSaveFeedback(message, type) { - if (!scoreSaveFeedback) return; - - scoreSaveFeedback.className = "alert alert-" + type + " mb-4"; - scoreSaveFeedback.textContent = message; - scoreSaveFeedback.classList.remove("d-none"); - } - - async function saveScore(scoreData) { - const auth = getAuth(); - if (!auth || !auth.username || !auth.password) { - showScoreSaveFeedback( - "Score wurde nur lokal berechnet. Bitte einloggen, damit er im Leaderboard gespeichert wird.", - "warning" - ); - return; - } - - // Auth-Daten kommen aus login.js; der ScoreService setzt daraus die Backend-Header. - const scoreService = getScoreService(); - if (!scoreService) { - showScoreSaveFeedback("Score-Service konnte nicht geladen werden.", "danger"); - return; - } - - showScoreSaveFeedback("Score wird gespeichert...", "info"); - - const result = await scoreService.postScore( - auth.username, - auth.password, - scoreData.score, - scoreData.time, - scoreData.text, - scoreData.userWrittenText - ); - - if (result.ok) { - const place = result.body && result.body.place ? " Platz " + result.body.place + "." : ""; - showScoreSaveFeedback("Score erfolgreich gespeichert." + place, "success"); - return; - } - - if (result.status === 401) { - showScoreSaveFeedback("Score konnte nicht gespeichert werden: Login ist nicht gültig.", "danger"); - return; - } - - showScoreSaveFeedback("Score konnte nicht gespeichert werden (Status " + result.status + ").", "danger"); - } - - async function submitScore() { - if (!userTextInput) return; - - const userInput = userTextInput.value.trim(); - if (!userInput) { - alert("Bitte geben Sie einen Text ein."); - return; - } - - if (btnSubmitScore) { - btnSubmitScore.disabled = true; - btnSubmitScore.textContent = "Wird ausgewertet..."; - } - - const score = calculateScore(currentGameText, userInput); - - // Ergebnis sofort anzeigen; das Speichern im Backend passiert danach asynchron. - if (phaseInput) phaseInput.classList.add('d-none'); - if (phaseResult) phaseResult.classList.remove('d-none'); - - if(gameStatus) { - gameStatus.textContent = "Abgeschlossen"; - gameStatus.className = "badge fs-6 px-3 py-2"; - gameStatus.style.backgroundColor = "#28a745"; - gameStatus.style.color = "#fff"; - } - - if (resultScore) resultScore.textContent = score; - renderWordComparison(currentGameText, userInput); - - // Genau dieser Rundentext wird gespeichert, damit Leaderboard/Score-Details nachvollziehbar bleiben. - const scoreData = { - score: score, - time: MEMORIZE_TIME_SECONDS, - text: currentGameText, - userWrittenText: userInput - }; - - console.log("Score bereit zum Senden:", scoreData); - - try { - await saveScore(scoreData); - } catch (error) { - console.error("Fehler beim Speichern des Scores:", error); - showScoreSaveFeedback("Score konnte wegen eines technischen Fehlers nicht gespeichert werden.", "danger"); - } finally { - if (btnSubmitScore) { - btnSubmitScore.disabled = false; - btnSubmitScore.textContent = "Auswerten & Absenden"; - } - } - } - - window.initPlayPage = function initPlayPage() { - clearInterval(timerInterval); - - // Die Navigation laedt play.html per fetch; deshalb werden die Elemente erst hier gesucht. - phaseStart = document.getElementById('phaseStart'); - phaseMemorize = document.getElementById('phaseMemorize'); - phaseInput = document.getElementById('phaseInput'); - phaseResult = document.getElementById('phaseResult'); - - targetTextDisplay = document.getElementById('targetTextDisplay'); - timerDisplay = document.getElementById('timerDisplay'); - userTextInput = document.getElementById('userTextInput'); - resultScore = document.getElementById('resultScore'); - resultOriginal = document.getElementById('resultOriginal'); - resultInput = document.getElementById('resultInput'); - gameStatus = document.getElementById('gameStatus'); - scoreSaveFeedback = document.getElementById('scoreSaveFeedback'); - - const btnStart = document.getElementById('btnStartGame'); - btnSubmitScore = document.getElementById('btnSubmitScore'); - const btnRestart = document.getElementById('btnRestart'); - const btnLeaderboard = document.getElementById('btnLeaderboard'); - - if (btnStart) btnStart.addEventListener('click', startGame); - if (btnSubmitScore) btnSubmitScore.addEventListener('click', submitScore); - if (btnRestart) btnRestart.addEventListener('click', () => window.loadPage("play", "nav-play")); - if (btnLeaderboard) btnLeaderboard.addEventListener('click', () => { - const navLink = document.getElementById('nav-leaderboard'); - if (navLink) { - navLink.click(); // Nutzt die bestehende Navigation inklusive Active-State. - } else { - console.warn("Sidebar Link #nav-leaderboard nicht gefunden."); - } - }); - }; - + }; })();