UNPKG

@bitrix24/b24jssdk

Version:

Bitrix24 REST API JavaScript SDK

305 lines (302 loc) 9.67 kB
/** * @package @bitrix24/b24jssdk * @version 1.1.0 * @copyright (c) 2026 Bitrix24 * @license MIT * @see https://github.com/bitrix24/b24jssdk * @see https://bitrix24.github.io/b24jssdk/ */ import { Type } from '../type.mjs'; var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); class IbanSpecification { static { __name(this, "IbanSpecification"); } /** * the code of the country */ countryCode; /** * the length of the IBAN */ length; /** * the structure of the underlying BBAN (for validation and formatting) */ structure; /** * an example valid IBAN */ example; _cachedRegex = null; constructor(countryCode, length, structure, example) { this.countryCode = countryCode; this.length = length; this.structure = structure; this.example = example; } /** * Check if the passed iban is valid, according to this specification. * * @param {string} iban the iban to validate * @returns {boolean} true if valid, false otherwise */ isValid(iban) { return this.length === iban.length && this.countryCode === iban.slice(0, 2) && this._regex().test(iban.slice(4)) && this._iso7064Mod9710(this._iso13616Prepare(iban)) == 1; } /** * Convert the passed IBAN to a country-specific BBAN. * * @param iban the IBAN to convert * @param separator the separator to use between BBAN blocks * @returns {string} the BBAN */ toBBAN(iban, separator) { return (this._regex().exec(iban.slice(4) || "") || []).slice(1).join(separator); } /** * Convert the passed BBAN to an IBAN for this country specification. * Please note that <i>"generation of the IBAN shall be the exclusive responsibility of the bank/branch servicing the account"</i>. * This method implements the preferred algorithm described in http://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_IBAN_check_digits * * @param bban the BBAN to convert to IBAN * @returns {string} the IBAN */ fromBBAN(bban) { if (!this.isValidBBAN(bban)) { throw new Error("Invalid BBAN"); } const remainder = this._iso7064Mod9710( this._iso13616Prepare(this.countryCode + "00" + bban) ); const checkDigit = ("0" + (98 - remainder)).slice(-2); return this.countryCode + checkDigit + bban; } /** * Check of the passed BBAN is valid. * This function only checks the format of the BBAN (length and compliance with alphanumeric specifications) but does not * verify the check digit. * * @param bban the BBAN to validate * @returns {boolean} true if the passed bban is a valid BBAN, according to this specification, false otherwise */ isValidBBAN(bban) { return this.length - 4 === bban.length && this._regex().test(bban); } /** * Lazy-loaded regex (parse the structure and construct the regular expression the first time we need it for validation) */ _regex() { if (null === this._cachedRegex) { this._cachedRegex = this._parseStructure(this.structure); } return this._cachedRegex; } /** * Parse the BBAN structure used to configure each IBAN Specification and returns a matching regular expression. * A structure is composed of blocks of three characters (one letter and two digits). * Each block represents * a logical group in the typical representation of the BBAN. * For each group, the letter indicates which characters * are allowed in this group, and the following 2-digits number tells the length of the group. * * @param {string} structure the structure to parse * @returns {RegExp} */ _parseStructure(structure) { const regex = (structure.match(/(.{3})/g) || []).map( (block) => { let format; const pattern = block.slice(0, 1); const repeats = Number.parseInt(block.slice(1), 10); switch (pattern) { case "A": format = "0-9A-Za-z"; break; case "B": format = "0-9A-Z"; break; case "C": format = "A-Za-z"; break; case "F": format = "0-9"; break; case "L": format = "a-z"; break; case "U": format = "A-Z"; break; case "W": format = "0-9a-z"; break; } return "([" + format + "]{" + repeats + "})"; } ); return new RegExp("^" + regex.join("") + "$"); } /** * Prepare an IBAN for mod 97 computation by moving the first 4 chars to the end and transforming the letters to * numbers (A = 10, B = 11, ..., Z = 35), as specified in ISO13616. * * @param {string} iban the IBAN * @returns {string} the prepared IBAN */ _iso13616Prepare(iban) { const A = "A".charCodeAt(0); const Z = "Z".charCodeAt(0); iban = iban.toUpperCase(); iban = iban.substring(4) + iban.substring(0, 4); return iban.split("").map((n) => { const code = n.charCodeAt(0); if (code >= A && code <= Z) { return (code - A + 10).toString(); } else { return n; } }).join(""); } /** * Calculates MOD 97 10 of the passed IBAN as specified in ISO7064. * * @param iban * @returns {number} MOD */ _iso7064Mod9710(iban) { let remainder = iban; let block; while (remainder.length > 2) { block = remainder.slice(0, 9); remainder = Number.parseInt(block, 10) % 97 + remainder.slice(block.length); } return Number.parseInt(remainder, 10) % 97; } } class FormatterIban { static { __name(this, "FormatterIban"); } static isInternalConstructing = false; static instance = null; _countries; // region Init //// constructor() { if (!FormatterIban.isInternalConstructing) { throw new TypeError("FormatterIban is not constructable"); } FormatterIban.isInternalConstructing = false; this._countries = /* @__PURE__ */ new Map(); } /** * @return FormatterIban */ static getInstance() { if (!FormatterIban.instance) { FormatterIban.isInternalConstructing = true; FormatterIban.instance = new FormatterIban(); } return FormatterIban.instance; } addSpecification(IBAN) { this._countries.set(IBAN.countryCode, IBAN); } // endregion //// // region IBAN //// /** * Check if an IBAN is valid. * * @param {string} iban the IBAN to validate. * @returns {boolean} true if the passed IBAN is valid, false otherwise */ isValid(iban) { if (!Type.isString(iban)) { return false; } iban = this.electronicFormat(iban); const countryCode = iban.slice(0, 2); if (!this._countries.has(countryCode)) { throw new Error(`No country with code ${countryCode}`); } const countryStructure = this._countries.get(countryCode); return !!countryStructure && countryStructure.isValid(iban); } printFormat(iban, separator) { if (typeof separator == "undefined") { separator = " "; } const EVERY_FOUR_CHARS = /(.{4})(?!$)/g; return this.electronicFormat(iban).replace( EVERY_FOUR_CHARS, "$1" + separator ); } electronicFormat(iban) { const NON_ALPHANUM = /[^a-z0-9]/gi; return iban.replace(NON_ALPHANUM, "").toUpperCase(); } // endregion //// // region BBAN //// /** * Convert an IBAN to a BBAN. * * @param iban * @param {string} [separator] the separator to use between the blocks of the BBAN, defaults to ' ' * @returns {string|*} Convert an IBAN to a BBAN. */ toBBAN(iban, separator) { if (typeof separator == "undefined") { separator = " "; } iban = this.electronicFormat(iban); const countryCode = iban.slice(0, 2); if (!this._countries.has(countryCode)) { throw new Error(`No country with code ${countryCode}`); } const countryStructure = this._countries.get(countryCode); if (!countryStructure) { throw new Error(`No country with code ${countryCode}`); } return countryStructure.toBBAN(iban, separator); } /** * Convert the passed BBAN to an IBAN for this country specification. * Please note that <i>"generation of the IBAN shall be the exclusive responsibility of the bank/branch servicing the account"</i>. * This method implements the preferred algorithm described in http://en.wikipedia.org/wiki/International_Bank_Account_Number#Generating_IBAN_check_digits * * @param countryCode the country of the BBAN * @param bban the BBAN to convert to IBAN * @returns {string} the IBAN */ fromBBAN(countryCode, bban) { if (!this._countries.has(countryCode)) { throw new Error(`No country with code ${countryCode}`); } const countryStructure = this._countries.get(countryCode); if (!countryStructure) { throw new Error(`No country with code ${countryCode}`); } return countryStructure.fromBBAN(this.electronicFormat(bban)); } /** * Check the validity of the passed BBAN. * * @param countryCode the country of the BBAN * @param bban the BBAN to check the validity of */ isValidBBAN(countryCode, bban) { if (!Type.isString(bban)) { return false; } if (!this._countries.has(countryCode)) { throw new Error(`No country with code ${countryCode}`); } const countryStructure = this._countries.get(countryCode); return !!countryStructure && countryStructure.isValidBBAN(this.electronicFormat(bban)); } // endregion //// } export { FormatterIban, IbanSpecification }; //# sourceMappingURL=iban.mjs.map