"use strict;" function luhnValidate(cardNumber) { cardNumber = String(cardNumber).replace(/[\s]/g, ""); let odd = false; let total = 0; let position, doubledPos; if (!/^[0-9]+$/.test(cardNumber)) { return false; } for (var i = cardNumber.length; i > 0; i--) { position = parseInt(cardNumber[i - 1]); if (!odd) { total += position; } else { doubledPos = position * 2; if (doubledPos > 9) { doubledPos -= 9; } total += doubledPos; } odd = !odd; } return (total !== 0 && (total % 10) === 0); } function ibanLatinCharId(chr) { c = chr.charCodeAt(0); if (c > 0x40 && c <= 0x5a) { return chr.charCodeAt(0) - 0x40 + 9; } else if (c > 0x60 && c <= 0x7a) { return chr.charCodeAt(0) - 0x60 + 9; } return false; } function ibanValidate(iban) { iban = iban.toUpperCase(); countryId = iban.slice(0, 2); checksum = iban.slice(2, 4); countryDigits = countryId.split('').map(ibanLatinCharId).join(''); body = iban.substring(4); processing = `${body}${countryDigits}${checksum}`; resultingNumber = BigInt(processing, 10); mod = resultingNumber % 97n; return mod == 1; } function putError(s) { document.querySelector('#errors').textContent += s; } function clearError() { document.querySelector('#errors').textContent = ""; } async function saveValueIfNotEmpty(k, v) { if (v !== "") { return browser.storage.sync.set({ k: v }) } return false } async function saveOptions(ev) { clearError(); ev.preventDefault(); let bcNum = this.querySelector("#bcnum").value; if (bcNum.length !== 16 || !bcNum.startsWith("70814") || !luhnValidate(bcNum)) { putError("Your BahnCard number is not valid. Please make sure you haven't made any typos."); return; } let bDay = this.querySelector('#birthday').value; if (bDay == "") { putError("Please enter a birthday."); return; } let enable = this.querySelector('#enable').checked; let defaultAction = this.querySelector('#default_action').value; let title = this.querySelector('#title').value; let title_addl = this.querySelector('#title_addl').value; let firstName = this.querySelector('#firstname').value; let surName = this.querySelector('#surname').value; let emailAddress = this.querySelector('#email').value; let streetAddr = this.querySelector('#addr1').value; let postCode = this.querySelector('#postcode').value; let placeName = this.querySelector('#placename').value; let iban = this.querySelector('#iban').value.replaceAll(" ", ""); let country = this.querySelector("#country").value; let allowChangelogMessages = this.querySelector("#showChangelog").checked; if (iban != "" && !ibanValidate(iban)) { putError("Please check your IBAN for typing errors."); return; } let bic = this.querySelector('#bic').value; let options = { bcnum: bcNum, bday: bDay, enable: enable, defaultAction: defaultAction, addr__appellation: title, addr__title: title_addl, addr__firstName: firstName, addr__surName: surName, addr__email: emailAddress, addr__street: streetAddr, addr__postcode: postCode, addr__placename: placeName, addr__country: country, pymt__iban: iban, pymt__bic: bic, showChangelog: allowChangelogMessages, }; // list of options that may be explicitly empty const EMPTYABLE_OPTIONS = ["defaultAction"]; options = Object.fromEntries(Object.entries(options).filter(([k, v]) => v !== "" || EMPTYABLE_OPTIONS.includes(k))) console.info("saving", options); browser.storage.sync.set(options).then(() => { document.querySelector('#success').textContent = "✔"; setTimeout(() => { document.querySelector('#success').textContent = ''; }, 5000); }) } function boolDefaultTrue(dict, key){ if (key in dict){ return dict[key]; } return true; } async function restoreOptions() { // We do not save user data in managed storage. // let res = await browser.storage.managed.get('colour'); // document.querySelector("#managed-colour").innerText = res.colour; let settings = await browser.storage.sync.get(); console.info("restoring", settings); document.querySelector('#enable').checked = boolDefaultTrue(settings, "enable"); document.querySelector("#bcnum").value = settings.bcnum || ""; document.querySelector("#birthday").value = settings.bday || ""; document.querySelector("#default_action").value = settings.defaultAction || ""; document.querySelector('#title').value = settings.addr__appellation || ""; document.querySelector('#title_addl').value = settings.addr__title || ""; document.querySelector('#firstname').value = settings.addr__firstName || ""; document.querySelector('#surname').value = settings.addr__surName || ""; document.querySelector('#email').value = settings.addr__email || ""; document.querySelector('#addr1').value = settings.addr__street || ""; document.querySelector('#postcode').value = settings.addr__postcode || ""; document.querySelector('#placename').value = settings.addr__placename || ""; document.querySelector('#country').value = settings.addr__country || ""; document.querySelector('#iban').value = settings.pymt__iban || ""; document.querySelector('#bic').value = settings.pymt__bic || ""; document.querySelector('#showChangelog').checked = boolDefaultTrue(settings, "showChangelog"); } let bahncardWindow; function startBcnumAcquisition(ev) { browser.storage.sync.set({ "acquireBahncard": true }).then(() => { browser.storage.sync.onChanged.addListener(changes => { console.debug(changes); for (const [key, changeSet] of Object.entries(changes)) { if (key === "bcnum") { browser.storage.sync.onChanged.removeListener(this); } } }) bahncardWindow = window.open("https://www.bahn.de/buchung/kundenkonto/bahncard", "_blank", "popup") if (bahncardWindow instanceof Window) { bahncardWindow.postMessage("__WINDOW_OPENED_FOR_BC_ACQUISITION__"); } }) } function startAllAcquisition(ev) { browser.storage.sync.set({ "acquireAll": true }).then(() => { browser.storage.sync.onChanged.addListener(changes => { console.debug(changes); if ("acquireAll" in changes) { document.location.reload(); } }) bahncardWindow = window.open("https://www.bahn.de/buchung/kundenkonto/bahncard", "_blank", "popup") if (bahncardWindow instanceof Window) { bahncardWindow.postMessage("__WINDOW_OPENED_FOR_BC_ACQUISITION__"); } }) } document.addEventListener('DOMContentLoaded', restoreOptions); document.querySelector("form").addEventListener("submit", saveOptions); document.querySelector("#bcnum-get-from-profile").addEventListener("click", startBcnumAcquisition) document.querySelector("#all-get-from-profile").addEventListener("click", startAllAcquisition)