function readStorageJson(key, fallbackValue) {
const raw = localStorage.getItem(key);
if (!raw || raw === "undefined" || raw === "null") {
return fallbackValue;
}
try {
return JSON.parse(raw);
} catch (error) {
console.warn(`Konnte LocalStorage-Wert fuer ${key} nicht lesen.`, error);
return fallbackValue;
}
}
function normalizeUser(user) {
if (!user || typeof user !== "object") {
return null;
}
return {
salutation: user.salutation || "",
firstName: user.firstName || "",
lastName: user.lastName || "",
birthday: user.birthday || "",
street: user.street || "",
houseNumber: user.houseNumber || "",
zip: user.zip || "",
city: user.city || "",
country: user.country || "Deutschland",
email: user.email || "",
passwordHash: user.passwordHash || (user.password ? hashPassword(user.password) : ""),
orders: Array.isArray(user.orders) ? user.orders : [],
paymentMethods: Array.isArray(user.paymentMethods) ? user.paymentMethods : []
};
}
function hashPassword(password) {
let hashA = 0xdeadbeef;
let hashB = 0x41c6ce57;
const text = String(password || "");
for (let index = 0; index < text.length; index += 1) {
const charCode = text.charCodeAt(index);
hashA = Math.imul(hashA ^ charCode, 2654435761);
hashB = Math.imul(hashB ^ charCode, 1597334677);
}
hashA = Math.imul(hashA ^ (hashA >>> 16), 2246822507) ^ Math.imul(hashB ^ (hashB >>> 13), 3266489909);
hashB = Math.imul(hashB ^ (hashB >>> 16), 2246822507) ^ Math.imul(hashA ^ (hashA >>> 13), 3266489909);
return `hash$${(hashB >>> 0).toString(16).padStart(8, "0")}${(hashA >>> 0).toString(16).padStart(8, "0")}`;
}
function escapeHtml(value) {
return String(value || "")
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll('"', """)
.replaceAll("'", "'");
}
function formatEuro(value) {
return `${Number(value || 0).toFixed(2).replace(".", ",")} EUR`;
}
function persistUsers() {
localStorage.setItem("eagleUsers", JSON.stringify(users));
}
function persistCurrentUser() {
if (currentUser) {
localStorage.setItem("currentUser", JSON.stringify(currentUser));
} else {
localStorage.removeItem("currentUser");
}
}
let users = readStorageJson("eagleUsers", []);
if (!Array.isArray(users)) {
users = [];
}
users = users.map(normalizeUser).filter(Boolean);
persistUsers();
let currentUser = normalizeUser(readStorageJson("currentUser", null));
if (currentUser && currentUser.email) {
const storedMatch = users.find((user) => user.email === currentUser.email);
if (storedMatch) {
currentUser = storedMatch;
persistCurrentUser();
} else {
users.push(currentUser);
persistUsers();
persistCurrentUser();
}
}
function registerUser() {
if (!validateRegisterForm()) {
return;
}
const salutation = document.getElementById("reg-salutation")?.value.trim() || "";
const firstName = document.getElementById("reg-firstname")?.value.trim() || "";
const lastName = document.getElementById("reg-lastname")?.value.trim() || "";
const birthday = document.getElementById("reg-birthday")?.value.trim() || "";
const street = document.getElementById("reg-street")?.value.trim() || "";
const houseNumber = document.getElementById("reg-house-number")?.value.trim() || "";
const zip = document.getElementById("reg-zip")?.value.trim() || "";
const city = document.getElementById("reg-city")?.value.trim() || "";
const country = document.getElementById("reg-country")?.value.trim() || "";
const email = (document.getElementById("reg-email")?.value.trim() || "").toLowerCase();
const password = document.getElementById("reg-password")?.value || "";
const existingUser = users.find((user) => user.email.toLowerCase() === email);
if (existingUser) {
setFieldError(document.getElementById("reg-email"), "Diese E-Mail ist bereits registriert.");
return;
}
const newUser = {
salutation,
firstName,
lastName,
birthday,
street,
houseNumber,
zip,
city,
country,
email,
passwordHash: hashPassword(password),
orders: [],
paymentMethods: []
};
users.push(newUser);
currentUser = newUser;
persistUsers();
persistCurrentUser();
document.getElementById("register-modal")?.classList.add("hidden");
openAccountDashboard();
}
function loginUser() {
if (!validateLoginForm()) {
return;
}
const email = (document.getElementById("login-email")?.value.trim() || "").toLowerCase();
const password = document.getElementById("login-password")?.value || "";
const passwordHash = hashPassword(password);
const user = users.find(
(entry) => entry.email.toLowerCase() === email && entry.passwordHash === passwordHash
);
if (!user) {
document.getElementById("login-error")?.classList.remove("hidden");
setFieldError(document.getElementById("login-email"), "E-Mail oder Passwort stimmen nicht.");
setFieldError(document.getElementById("login-password"), "E-Mail oder Passwort stimmen nicht.");
return;
}
currentUser = user;
persistCurrentUser();
openAccountDashboard();
}
function openAccountDashboard() {
const accountView = document.getElementById("account-view");
if (!accountView) {
return;
}
if (!currentUser) {
accountView.innerHTML = "
Mein Konto
Bitte melde dich an oder registriere dich.
";
return;
}
accountView.innerHTML = `
`;
renderPersonalInfo();
}
function renderPersonalInfo(isEditing = false) {
const target = document.getElementById("account-tab-content");
if (!target || !currentUser) {
return;
}
if (isEditing) {
target.innerHTML = `
`;
return;
}
target.innerHTML = `
Persönliche Daten
Anrede${escapeHtml(currentUser.salutation || "-")}
Vorname${escapeHtml(currentUser.firstName || "-")}
Nachname${escapeHtml(currentUser.lastName || "-")}
Geburtstag${escapeHtml(currentUser.birthday || "-")}
Straße${escapeHtml(currentUser.street || "-")}
Nr.${escapeHtml(currentUser.houseNumber || "-")}
PLZ${escapeHtml(currentUser.zip || "-")}
Ort${escapeHtml(currentUser.city || "-")}
Land${escapeHtml(currentUser.country || "-")}
E-Mail${escapeHtml(currentUser.email || "-")}
`;
}
function savePersonalInfo() {
if (!currentUser) {
return;
}
const requiredFields = ["account-edit-firstname", "account-edit-lastname"];
const isValid = requiredFields
.map((id) => validateAccountField(document.getElementById(id)))
.every(Boolean);
if (!isValid) {
return;
}
currentUser.salutation = document.getElementById("account-edit-salutation")?.value || "";
currentUser.firstName = document.getElementById("account-edit-firstname")?.value.trim() || "";
currentUser.lastName = document.getElementById("account-edit-lastname")?.value.trim() || "";
currentUser.birthday = document.getElementById("account-edit-birthday")?.value || "";
currentUser.street = document.getElementById("account-edit-street")?.value.trim() || "";
currentUser.houseNumber = document.getElementById("account-edit-house-number")?.value.trim() || "";
currentUser.zip = document.getElementById("account-edit-zip")?.value.trim() || "";
currentUser.city = document.getElementById("account-edit-city")?.value.trim() || "";
currentUser.country = document.getElementById("account-edit-country")?.value || "Deutschland";
const userIndex = users.findIndex((user) => user.email === currentUser.email);
if (userIndex >= 0) {
users[userIndex] = currentUser;
}
persistUsers();
persistCurrentUser();
renderPersonalInfo();
}
function getFieldWrapper(input) {
if (!input) {
return null;
}
return input.closest(".auth-field") || document.querySelector(`[data-field-for="${input.id}"]`);
}
function setFieldError(input, message) {
const wrapper = getFieldWrapper(input);
if (!wrapper) {
return;
}
input.dataset.touched = "true";
wrapper.classList.add("is-invalid");
const error = wrapper.querySelector(".field-error");
if (error) {
error.textContent = message;
}
}
function clearFieldError(input) {
const wrapper = getFieldWrapper(input);
if (!wrapper) {
return;
}
wrapper.classList.remove("is-invalid");
const error = wrapper.querySelector(".field-error");
if (error) {
error.textContent = "";
}
}
function validateAccountField(input) {
if (!input) {
return true;
}
const value = (input.value || "").trim();
if (input.required && !value) {
setFieldError(input, input.dataset.requiredMessage || "Dieses Feld ist ein Pflichtfeld.");
return false;
}
if (input.type === "email" && value && !value.includes("@")) {
setFieldError(input, input.dataset.emailMessage || "Bitte gib eine gültige E-Mail-Adresse ein.");
return false;
}
if (input.type === "password" && value && (value.length < 8 || value.length > 32)) {
setFieldError(input, input.dataset.passwordMessage || "Passwort muss 8 bis 32 Zeichen haben.");
return false;
}
clearFieldError(input);
return true;
}
function validateRegisterForm() {
const fieldIds = [
"reg-salutation",
"reg-firstname",
"reg-lastname",
"reg-birthday",
"reg-street",
"reg-house-number",
"reg-zip",
"reg-city",
"reg-country",
"reg-email",
"reg-password"
];
return fieldIds
.map((id) => validateAccountField(document.getElementById(id)))
.every(Boolean);
}
function validateLoginForm() {
return ["login-email", "login-password"]
.map((id) => validateAccountField(document.getElementById(id)))
.every(Boolean);
}
function initAccountFieldValidation() {
const fieldSelector = [
"#login-email",
"#login-password",
"#reg-salutation",
"#reg-firstname",
"#reg-lastname",
"#reg-birthday",
"#reg-street",
"#reg-house-number",
"#reg-zip",
"#reg-city",
"#reg-country",
"#reg-email",
"#reg-password"
].join(",");
document.querySelectorAll(fieldSelector).forEach((input) => {
input.addEventListener("blur", () => {
input.dataset.touched = "true";
validateAccountField(input);
});
input.addEventListener("input", () => {
input.dataset.touched = "true";
if (input.type !== "hidden") {
validateAccountField(input);
}
});
input.addEventListener("change", () => {
input.dataset.touched = "true";
validateAccountField(input);
});
});
document.querySelectorAll(".gender-selector button").forEach((button) => {
button.addEventListener("click", () => {
const salutationInput = document.getElementById("reg-salutation");
if (!salutationInput) {
return;
}
salutationInput.value = button.dataset.gender || "";
salutationInput.dataset.touched = "true";
button.closest(".gender-selector")?.querySelectorAll("button").forEach((item) => {
item.classList.toggle("active", item === button);
});
validateAccountField(salutationInput);
});
});
}
function renderOrders() {
const target = document.getElementById("account-tab-content");
if (!target || !currentUser) {
return;
}
const orders = Array.isArray(currentUser.orders) ? currentUser.orders : [];
if (!orders.length) {
target.innerHTML = `
Meine Bestellungen
Noch keine Bestellungen vorhanden.
`;
return;
}
const orderHtml = orders
.map((order, index) => {
const movieItems = Array.isArray(order.items)
? order.items.filter((item) => item.category === "movie")
: [];
const previewItem = movieItems[0] || (Array.isArray(order.items) ? order.items[0] : null);
const previewTitle = previewItem?.title || "Bestellung";
const ticketsCount = movieItems.length || (Array.isArray(order.items) ? order.items.length : 0);
return `
`;
})
.join("");
target.innerHTML = `
Meine Bestellungen
Klicke auf eine Bestellung, um genauere Informationen zu sehen.
${orderHtml}
`;
const detailTarget = document.getElementById("order-ticket-details");
const orderButtons = Array.from(target.querySelectorAll(".order-item-btn"));
const renderOrderTicket = (orderIndex) => {
const order = orders[orderIndex];
if (!order || !detailTarget) {
return;
}
const movieItems = Array.isArray(order.items)
? order.items.filter((item) => item.category === "movie")
: [];
const primaryMovie = movieItems[0] || (Array.isArray(order.items) ? order.items[0] : null);
const poster = primaryMovie?.img || "";
const seats = movieItems.map((item) => item.seatId).filter(Boolean).join(", ") || "-";
const ticketCount = movieItems.length || (Array.isArray(order.items) ? order.items.length : 0);
const hall = primaryMovie?.hall || "-";
const time = primaryMovie?.time ? `${primaryMovie.time} Uhr` : "-";
detailTarget.innerHTML = `
${poster
? `
})
`
: `
Kein Poster
`}
EAGLE'S IMAX | Bestell-Details
${escapeHtml(primaryMovie?.title || "Bestellung")}
Datum${escapeHtml(order.date || "-")}
Saal${escapeHtml(hall)}
Uhrzeit${escapeHtml(time)}
Tickets${ticketCount}x
Sitze${escapeHtml(seats)}
Gesamt${formatEuro(order.total || 0)}
`;
detailTarget.classList.remove("hidden");
orderButtons.forEach((button) => {
button.classList.toggle("active", Number(button.dataset.orderIndex) === orderIndex);
});
};
orderButtons.forEach((button) => {
button.addEventListener("click", () => {
const orderIndex = Number(button.dataset.orderIndex || -1);
if (orderIndex >= 0) {
renderOrderTicket(orderIndex);
}
});
});
}
function renderPayments() {
const target = document.getElementById("account-tab-content");
if (!target || !currentUser) {
return;
}
target.innerHTML = `
Zahlungsmethoden
Hier kannst du deine bevorzugten Zahlungsmethoden verwalten.
Kreditkarte hinterlegen
`;
const modals = Array.from(target.querySelectorAll(".pay-modal-overlay"));
const triggers = Array.from(target.querySelectorAll(".account-pay-trigger"));
const closeButtons = Array.from(target.querySelectorAll("[data-pay-close]"));
const closeAllPaymentModals = () => {
modals.forEach((modal) => modal.classList.add("hidden"));
document.body.style.overflow = "auto";
};
triggers.forEach((trigger) => {
trigger.addEventListener("click", () => {
closeAllPaymentModals();
const targetId = trigger.getAttribute("data-pay-modal");
const modal = targetId ? target.querySelector(`#${targetId}`) : null;
if (modal) {
modal.classList.remove("hidden");
document.body.style.overflow = "hidden";
}
});
});
closeButtons.forEach((button) => {
button.addEventListener("click", closeAllPaymentModals);
});
modals.forEach((modal) => {
modal.addEventListener("click", (event) => {
if (event.target === modal) {
closeAllPaymentModals();
}
});
});
}
function logoutUser() {
currentUser = null;
persistCurrentUser();
window.location.reload();
}