import { testIban } from "/js/iban.js"; // 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); } const changeLanguage = (l) => { const lang = i18nSupported.includes(l) ? l : "en"; console.log(`Switching language to ${lang}`); document.querySelectorAll("[data-i18n]").forEach(el => { el.innerHTML = i18n[el.dataset.i18n][lang]; }); document.getElementById("current-language").innerHTML = `${i18n._flags[lang]} ${lang.toUpperCase()}`; document.getElementById("input-purpose").placeholder = i18n["purpose-example"][lang]; document.documentElement.lang = lang; saveFormData(); }; const 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 const validateName = (target) => { if (target.value.length > 0) { if (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); } } } else { // empty field with no validation icon target.classList.remove("is-danger"); document.querySelector("#name div span").innerHTML = ""; if (document.querySelector("#name p")) { document.querySelector("#name p").remove(); } } } const validateBic = (target) => { if (target.value.length > 0) { 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); } } } else { target.classList.remove("is-danger"); document.querySelector("#bic div span").innerHTML = ""; if (document.querySelector("#bic p")) { document.querySelector("#bic p").remove(); } } } const validateIban = (target) => { // IBANs are fucking complicated // If you want to know more look here: // https://www.swift.com/standards/data-standards/iban-international-bank-account-number // Validates ISO 13616 compliant countries (v99, Dec 2024) // ISO 3166-1 country code + 2 check digits + up to 30 alphanumeric chars if (target.value.length > 4) { const message = testIban(target.value); if (message === "") { 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 = message; document.querySelector("#iban").appendChild(help); } } } else { // empty field with no validation icon target.classList.remove("is-danger"); document.querySelector("#iban div span").innerHTML = ""; if (document.querySelector("#iban p")) { document.querySelector("#iban p").remove(); } } } const validateAmount = (target) => { console.log(`${target.value} = ${parseFloat(target.value.replace(",", ".")).toFixed(2)}`); if (target.value.length > 0) { const val = parseFloat(target.value.replace(",", ".")) if (val.toFixed(2) == val && val >= 0) { 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 = "❌"; } } else { target.classList.remove("is-danger"); document.querySelector("#amount div span.is-right").innerHTML = ""; if (document.querySelector("#amount p")) { document.querySelector("#amount p").remove(); } } } const validatePurpose = (target) => { if (target.value.length > 0) { const re = /[a-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); } } } else { target.classList.remove("is-danger"); document.querySelector("#purpose div span.is-right").innerHTML = ""; if (document.querySelector("#purpose p")) { document.querySelector("#purpose p").remove(); } } } 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) => { // Remove invalid characters //event.target.value = event.target.value.replace(/[^0-9.]/g, ''); if (event.target.value.length > 0 && !isNaN(parseFloat(event.target.value))) { event.target.value = parseFloat(event.target.value).toFixed(2); } 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 const 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)); }