import { getById, h } from "/common.module.js"; function writeInfo(label, msg) { log.appendChild( h("div", [h("div", {class: "info"}, [h("span", {class: "noselect"}, label), h("span", msg)])] ), ); } export function onLoad() { const gen_bsn = getById("bsn"); gen_bsn.addEventListener("click", generateBsn); const gen_iban = getById("iban"); gen_iban.addEventListener("click", generateIban); } function generateBsn() { const nr9 = Math.floor(Math.random() * 7); const nr8 = Math.floor(Math.random() * 10); const nr7 = Math.floor(Math.random() * 10); const nr6 = Math.floor(Math.random() * 10); const nr5 = Math.floor(Math.random() * 10); const nr4 = Math.floor(Math.random() * 10); const nr3 = Math.floor(Math.random() * 10); let nr2 = Math.floor(Math.random() * 10); const bsnNumber = 9 * nr9 + 8 * nr8 + 7 * nr7 + 6 * nr6 + 5 * nr5 + 4 * nr4 + 3 * nr3 + 2 * nr2; let nr1 = Math.floor(bsnNumber - Math.floor(bsnNumber / 11) * 11); if (nr1 > 9) { if (nr2 > 0) { nr2 -= 1; nr1 = 8; } else { nr2 += 1; nr1 = 1; } } const BSNString = "" + nr9 + nr8 + nr7 + nr6 + nr5 + nr4 + nr3 + nr2 + nr1; writeInfo("BSN: ", BSNString); } // Bank identifiers of a few Dutch banks, as they appear in an IBAN. const BANK_CODES = ["ABNA", "ASNB", "BUNQ", "INGB", "KNAB", "RABO", "RBRB", "SNSB", "TRIO"]; /** @param {number} count @returns {string} */ function randomDigits(count) { let digits = ""; for (let i = 0; i < count; i++) { digits += Math.floor(Math.random() * 10); } return digits; } /** * An old-style Dutch account number: 9 digits that pass the "elfproef", where * the digits weighted 9, 8, ... 1 add up to a multiple of 11. IBANs pad those * to 10 digits with a leading zero. * @returns {string} */ function randomAccountNumber() { while (true) { const digits = randomDigits(8); let sum = 0; for (let i = 0; i < 8; i++) { sum += (9 - i) * Number(digits[i]); } // The ninth digit weighs 1, so it has to make up the rest on its own. const last = (11 - (sum % 11)) % 11; if (last < 10) { return "0" + digits + last; } // It would have to be a 10 to balance out; draw eight new digits. } } /** * Letters count as A=10 .. Z=35 in the ISO 7064 MOD 97-10 check. * @param {string} letters * @returns {string} */ function lettersToDigits(letters) { return [...letters].map((c) => c.charCodeAt(0) - 55).join(""); } /** * Remainder of a number too large for a JS number, taken digit by digit. * @param {string} digits * @returns {number} */ function mod97(digits) { let rest = 0; for (const digit of digits) { rest = (rest * 10 + Number(digit)) % 97; } return rest; } function generateIban() { const bank = BANK_CODES[Math.floor(Math.random() * BANK_CODES.length)]; const account = randomAccountNumber(); // ISO 13616: move "NL" + the check digits to the back, then pick the check // digits that make the whole thing leave a remainder of 1 modulo 97. const rest = mod97(lettersToDigits(bank) + account + lettersToDigits("NL") + "00"); const check = String(98 - rest).padStart(2, "0"); writeInfo("IBAN: ", "NL" + check + bank + account); }