133 lines
4.1 KiB
JavaScript
133 lines
4.1 KiB
JavaScript
// ================== Konfiguration / State ==================
|
||
const DEFAULT_CODE = [4, 2, 7, 9];
|
||
let code = JSON.parse(localStorage.getItem("escape_code") || "null") || DEFAULT_CODE;
|
||
let solvedText = localStorage.getItem("escape_message") || "GEHEIMNIS GEÖFFNET";
|
||
|
||
const pad = document.getElementById("pad");
|
||
|
||
// Merke dir das Original-HTML, damit wir es nach einem "Clear" wiederherstellen können
|
||
const padTemplate = pad.innerHTML;
|
||
|
||
// Startwerte der 4 Stellen (0–9)
|
||
let digits = [1, 2, 3, 4];
|
||
|
||
// Elemente-Cache für die Ziffern
|
||
let dEls = [];
|
||
function cacheDigitEls() {
|
||
dEls = [0, 1, 2, 3].map((i) => document.getElementById("d" + i));
|
||
}
|
||
cacheDigitEls();
|
||
|
||
// ================== Rendering & UI-Aufbau ==================
|
||
const render = () => digits.forEach((v, i) => dEls[i] && (dEls[i].textContent = v));
|
||
|
||
function wireOpenButton() {
|
||
const btn = document.getElementById("openBtn");
|
||
if (!btn) return;
|
||
btn.addEventListener("click", onOpenClick);
|
||
}
|
||
wireOpenButton();
|
||
|
||
// Delegierter Klick-Handler für die Münzen (↑/↓)
|
||
pad.addEventListener("click", (ev) => {
|
||
const btn = ev.target.closest(".coin");
|
||
if (!btn || !pad.contains(btn)) return;
|
||
const idx = +btn.dataset.idx;
|
||
const dir = btn.dataset.dir;
|
||
|
||
if (dir === "up") digits[idx] = (digits[idx] + 1) % 10;
|
||
if (dir === "down") digits[idx] = (digits[idx] + 9) % 10; // -1 modulo 10
|
||
render();
|
||
});
|
||
|
||
function onOpenClick() {
|
||
const ok = digits.every((v, i) => v === code[i]);
|
||
if (ok) showSolved();
|
||
else {
|
||
pad.classList.remove("pad--shake");
|
||
void pad.offsetWidth;
|
||
pad.classList.add("pad--shake");
|
||
}
|
||
}
|
||
|
||
function showSolved() {
|
||
pad.classList.remove("pad--shake");
|
||
pad.classList.add("pad--cleared");
|
||
pad.innerHTML = `
|
||
<div class="solvedText digit" role="status" aria-live="polite">
|
||
${escapeHtml(solvedText)}
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
// UI wieder in den „Zahlenmodus“ versetzen (z. B. nach neuem Code)
|
||
function rebuildPadUI() {
|
||
pad.classList.remove("pad--cleared");
|
||
pad.innerHTML = padTemplate;
|
||
cacheDigitEls();
|
||
wireOpenButton();
|
||
render();
|
||
}
|
||
|
||
// ================== Shortcuts zum Setzen von Nachricht & Code ==================
|
||
document.addEventListener("keydown", (e) => {
|
||
// Strg (oder Cmd) + Ä -> Nachricht setzen
|
||
if ((e.ctrlKey || e.metaKey) && isKey(e, ["ä", "Ä", "m"])) {
|
||
e.preventDefault();
|
||
const msg = prompt('Nachricht (wird nach dem Öffnen angezeigt):', solvedText);
|
||
if (msg !== null) {
|
||
const clean = msg.trim();
|
||
if (clean) {
|
||
solvedText = clean;
|
||
localStorage.setItem("escape_message", solvedText);
|
||
// Wenn bereits gelöst, die sichtbare Nachricht aktualisieren
|
||
if (pad.classList.contains("pad--cleared")) showSolved();
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
// Strg (oder Cmd) + Ö -> Code setzen
|
||
if ((e.ctrlKey || e.metaKey) && isKey(e, ["ö", "Ö", "k"])) {
|
||
e.preventDefault();
|
||
const input = prompt('Neuen 4-stelligen Code eingeben (z. B. 4279 oder 4,2,7,9):', code.join(""));
|
||
if (input !== null) {
|
||
const parsed = parseCode(input);
|
||
if (parsed) {
|
||
code = parsed;
|
||
localStorage.setItem("escape_code", JSON.stringify(code));
|
||
// Falls wir gerade die solved-Ansicht zeigen: UI wieder zum Schloss zurücksetzen
|
||
if (pad.classList.contains("pad--cleared")) rebuildPadUI();
|
||
alert("Neuer Code gesetzt: " + code.join(""));
|
||
} else {
|
||
alert("Ungültiges Format. Bitte genau 4 Ziffern (0–9) angeben.");
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
// ================== Utilities ==================
|
||
function parseCode(str) {
|
||
if (!str) return null;
|
||
// Erlaubt: "4279", "4 2 7 9", "4,2,7,9" etc.
|
||
const onlyDigits = str.match(/\d/g);
|
||
if (!onlyDigits || onlyDigits.length !== 4) return null;
|
||
return onlyDigits.map((d) => +d);
|
||
}
|
||
|
||
// Minimaler HTML-Escape für die Nachricht
|
||
function escapeHtml(s) {
|
||
return s.replace(/[&<>"']/g, (c) =>
|
||
({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c])
|
||
);
|
||
}
|
||
|
||
// Hilfsfunktion: trifft auch Fallback-Tasten (M/K)
|
||
function isKey(e, keys) {
|
||
const k = (e.key || "").toLowerCase();
|
||
return keys.some((x) => k === x.toLowerCase());
|
||
}
|
||
|
||
// Initiales Rendern
|
||
render();
|