// bulma hamburger menu document.addEventListener('DOMContentLoaded', () => { // Get all "navbar-burger" elements const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0); // Add a click event on each of them $navbarBurgers.forEach(el => { el.addEventListener('click', () => { // Get the target from the "data-target" attribute const target = el.dataset.target; const $target = document.getElementById(target); // Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu" el.classList.toggle('is-active'); $target.classList.toggle('is-active'); }); }); }); // language selector document.getElementById("current-language").addEventListener("click", (e) => { e.preventDefault(); document.getElementById("language-dropdown").classList.toggle("is-active"); }); const i18n = JSON.parse(document.getElementById("i18n-data").textContent); const i18nSupported = Object.keys(i18n._flags) // build language selector const langSelector = document.getElementById("language-selector") for (const l of i18nSupported) { const a = document.createElement("a"); a.classList.add("navbar-item"); a.innerHTML = `${i18n._flags[l]} ${l.toUpperCase()}`; a.href = "#"; a.addEventListener("click", (e) => { changeLanguage(e.target.textContent.slice(-2).toLowerCase()); document.getElementById("language-dropdown").classList.remove("is-active"); }); langSelector.appendChild(a); } changeLanguage = (l) => { const lang = i18nSupported.includes(l) ? l : "en"; document.querySelectorAll("[data-i18n]").forEach(el => { el.innerHTML = i18n[el.dataset.i18n][lang]; }); document.getElementById("current-language").innerHTML = `${i18n._flags[lang]} ${lang.toUpperCase()}`; console.log(`Switching language to ${lang}`); }; saveFormData = () => { localStorage.setItem("bezahlbrudiFormData", JSON.stringify({ name: document.querySelector("#name input").value, iban: document.querySelector("#iban input").value.toUpperCase(), bic: document.querySelector("#bic input").value.toUpperCase(), amount: parseFloat(document.querySelector("#amount input").value).toFixed(2), purpose: document.querySelector("#purpose input").value, lang: document.getElementById("current-language").textContent.slice(-2).toLowerCase() })); } // Field data checks validateName = (target) => { if (target.value.length == 0) return if (target.value.length > 0 && target.value.length <= 70) { target.classList.remove("is-danger") document.querySelector("#name div span").innerHTML = "✅"; if (document.querySelector("#name p")) { document.querySelector("#name p").remove(); } } else { target.classList.add("is-danger") document.querySelector("#name div span").innerHTML = "❌"; if (!document.querySelector("#name p")) { const help = document.createElement("p"); help.classList.add("help", "is-danger"); help.textContent = "Name can only have 70 characters."; document.querySelector("#name").appendChild(help); } } } validateBic = (target) => { if (target.value.length == 0) return const re = /^[a-z]{6}[2-9a-z][0-9a-np-z]([a-z0-9]{3}|x{3})?$/i; if (re.test(target.value)) { target.classList.remove("is-danger") document.querySelector("#bic div span").innerHTML = "✅"; if (document.querySelector("#bic p")) { document.querySelector("#bic p").remove(); } } else { target.classList.add("is-danger") document.querySelector("#bic div span").innerHTML = "❌"; if (!document.querySelector("#bic p")) { const help = document.createElement("p"); help.classList.add("help", "is-danger"); help.textContent = "BIC is invalid"; document.querySelector("#bic").appendChild(help); } } } validateIban = (target) => { if (target.value.length == 0) return // IBANs are fucking complicated. this is a hugely simplified check // If you want to know more look here: // https://www.swift.com/standards/data-standards/iban-international-bank-account-number const re = /^[a-z]{2}[0-9a-z]+$/i; if (re.test(target.value)) { target.classList.remove("is-danger") document.querySelector("#iban div span").innerHTML = "✅"; if (document.querySelector("#iban p")) { document.querySelector("#iban p").remove(); } } else { target.classList.add("is-danger") document.querySelector("#iban div span").innerHTML = "❌"; if (!document.querySelector("#iban p")) { const help = document.createElement("p"); help.classList.add("help", "is-danger"); help.textContent = "IBAN is invalid"; document.querySelector("#iban").appendChild(help); } } } validateAmount = (target) => { if (target.value == 0) return if (parseFloat(target.value) > 0 && target.value.toString() === parseFloat(target.value).toFixed(2).toString()) { target.value = parseFloat(target.value).toFixed(2); target.classList.remove("is-danger") document.querySelector("#amount div span.is-right").innerHTML = "✅"; } else { target.classList.add("is-danger") document.querySelector("#amount div span.is-right").innerHTML = "❌"; } } validatePurpose = (target) => { if (target.value.length == 0) return const re = /[a-zA-Z0-9\/?:().,'+\- ]{1,140}/i; if (re.test(target.value)) { target.classList.remove("is-danger") document.querySelector("#purpose div span").innerHTML = "✅"; if (document.querySelector("#purpose p")) { document.querySelector("#purpose p").remove(); } } else { target.classList.add("is-danger") document.querySelector("#purpose div span").innerHTML = "❌"; if (!document.querySelector("#purpose p")) { const help = document.createElement("p"); help.classList.add("help", "is-danger"); help.textContent = "Purpose can only have 140 characters and only include A-Z, a-z, 0-9, /?:().,'+- and spaces."; document.querySelector("#purpose").appendChild(help); } } } document.querySelector("#name input").addEventListener("input", (event) => { validateName(event.target); regenerate(); }); document.querySelector("#iban input").addEventListener("input", (event) => { validateIban(event.target); regenerate(); }); document.querySelector("#bic input").addEventListener("input", (event) => { validateBic(event.target); regenerate(); }); document.querySelector("#amount input").addEventListener("input", (event) => { validateAmount(event.target); regenerate(); }); document.querySelector("#purpose input").addEventListener("input", (event) => { validatePurpose(event.target); regenerate(); }); // Remember inputs document.querySelector("#rememberme").addEventListener("change", (event) => { if (event.target.checked) { saveFormData(); } else { localStorage.clear(); } }) // Regenerate on color-scheme change window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (event) => { regenerate() }); // qr code generator regenerate = () => { document.querySelector('#qr-code').innerHTML = ""; if (document.querySelector("#name input").classList.contains("is-danger")) { console.error("Error in name"); } else if (document.querySelector("#iban input").classList.contains("is-danger")) { console.error("Error in IBAN"); } else if (document.querySelector("#bic input").classList.contains("is-danger")) { console.error("Error in BIC"); } else if (document.querySelector("#amount input").classList.contains("is-danger")) { console.error("Error in Amount"); } else if (document.querySelector("#purpose input").classList.contains("is-danger")) { console.error("Error in Purpose"); } else { console.log("Generating new QR code..."); const data = `BCD 002 1 SCT ${document.querySelector("#bic input").value.toUpperCase()} ${document.querySelector("#name input").value} ${document.querySelector("#iban input").value.toUpperCase()} EUR${document.querySelector("#amount input").value} ${document.querySelector("#purpose input").value} `; if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { // dark mode QrCreator.render({ text: data, radius: 0.25, ecLevel: "H", fill: "#fff", background: "#000", size: 512 }, document.querySelector('#qr-code')); } else { // light mode QrCreator.render({ text: data, radius: 0.25, ecLevel: "H", fill: "#000", background: "#fff", size: 512 }, document.querySelector('#qr-code')); } if (document.querySelector("#rememberme").checked) { saveFormData(); } } } // Fill in form from local storage const savedData = localStorage.getItem("bezahlbrudiFormData"); if (savedData) { const formData = JSON.parse(savedData); document.querySelector("#name input").value = formData["name"]; document.querySelector("#iban input").value = formData["iban"]; document.querySelector("#bic input").value = formData["bic"]; document.querySelector("#amount input").value = formData["amount"]; document.querySelector("#purpose input").value = formData["purpose"]; document.querySelector("#rememberme").checked = true; changeLanguage(formData["lang"]); } else { // get browser language let lang = "en"; // default fallback for (const l of navigator.languages) { const base = l.split("-")[0]; if (i18nSupported.includes(base)) { lang = base; break; } } changeLanguage(lang); } // Validate all fields to set css properties validateName(document.querySelector("#name input")); validateIban(document.querySelector("#iban input")); validateBic(document.querySelector("#bic input")); validateAmount(document.querySelector("#amount input")); validatePurpose(document.querySelector("#purpose input")); // generate qr code on first visit regenerate(); if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/js/service-worker.js') .catch(err => console.error('Service Worker registration failed:', err)); }