UNPKG

accounting-js

Version:

Number, money and currency formatting library.

1 lines 13 kB
{"version":3,"sources":["../src/settings.ts","../src/unformat.ts","../src/toFixed.ts","../src/internal/stripInsignificantZeros.ts","../src/formatNumber.ts","../src/internal/checkCurrencyFormat.ts","../src/formatMoney.ts"],"sourcesContent":["import { Settings } from './types';\n\n/**\n * The library's default settings configuration object.\n * Contains default parameters for currency and number formatting.\n *\n * @type {Settings} settings\n */\nexport const settings: Settings = {\n symbol: '$',\n format: '%s%v',\n decimal: '.',\n thousand: ',',\n precision: 2,\n grouping: 3,\n stripZeros: false,\n fallback: 0,\n round: 0,\n};\n","import { NestedArray } from './types';\nimport { settings } from './settings';\n\n/**\n * Takes a string/array of strings, removes all formatting/cruft and returns the raw float value.\n *\n * Decimal must be included in the regular expression to match floats (defaults to\n * `settings.decimal`), so if the number uses a non-standard decimal\n * separator, provide it as the second argument.\n *\n * Also matches bracketed negatives (eg. `'$ (1.99)' => -1.99`).\n *\n * Doesn't throw any errors (`NaN`s become 0 or provided by fallback value).\n *\n * **Usage:**\n *\n * ```js\n * unformat('£ 12,345,678.90 GBP');\n * // => 12345678.9\n * ```\n *\n * @param {String} value - String containing the number to parse\n * @param {String} [decimal=settings.decimal] - The character used to represent the decimal separator\n * @param {Float} [fallback=settings.fallback] - Value returned on unformat() failure\n * @returns {Float} - Parsed number\n */\n\nexport function unformat(value: string, decimal: string = settings.decimal!, fallback: number = settings.fallback!): number {\n // Return the value as-is if it's already a number\n // if (typeof value === 'number') return value;\n\n // Build regex to strip out everything except digits, decimal point and minus sign\n const regex = new RegExp(`[^0-9-(-)-${decimal}]`, 'g');\n const unformattedValueString =\n ('' + value)\n .replace(regex, '') // strip out any cruft\n .replace(decimal, '.') // make sure decimal point is standard\n .replace(/\\(([-]*\\d*[^)]?\\d+)\\)/g, '-$1') // replace bracketed values with negatives\n .replace(/\\((.*)\\)/, ''); // remove any brackets that do not have numeric value\n\n /**\n * Handling -ve number and bracket, eg.\n * (-100) = 100, -(100) = 100, --100 = 100\n */\n const negative = (unformattedValueString.match(/-/g) || '').length % 2;\n const absUnformatted = parseFloat(unformattedValueString.replace(/-/g, ''));\n const unformatted = absUnformatted * ((negative) ? -1 : 1);\n\n // This will fail silently which may cause trouble, let's wait and see\n return !isNaN(unformatted) ? unformatted : fallback;\n}\n\nexport function unformatArray(value: NestedArray<string>, decimal: string = settings.decimal!, fallback: number = settings.fallback!): NestedArray<number> {\n // Recursively unformat arrays:\n if (Array.isArray(value)) {\n return value.map((val) => unformatArray(val, decimal, fallback));\n }\n\n return unformat(value, decimal, fallback);\n}\n","import { settings } from './settings';\n\n/**\n * Implementation of toFixed() that treats floats more like decimals.\n *\n * Fixes binary rounding issues (eg. (0.615).toFixed(2) === '0.61') that present\n * problems for accounting- and finance-related software.\n *\n * **Usage:**\n *\n * ```js\n * // Native toFixed has rounding issues\n * (0.615).toFixed(2);\n * // => '0.61'\n *\n * // With accounting-js\n * toFixed(0.615, 2);\n * // => '0.62'\n * ```\n *\n * @param {Float} value - Float to be treated as a decimal number\n * @param {Number} [precision=settings.precision] - Number of decimal digits to keep\n * @param {Number} [round=settings.round] - Decide round direction\n * @returns {String} - Given number transformed into a string with the given precission\n */\nexport function toFixed(value: number, precision?: number, round?: number): string {\n precision ??= settings.precision!;\n round ??= settings.round!;\n const power = Math.pow(10, precision);\n\n let roundMethod;\n if (round > 0) {\n roundMethod = Math.ceil;\n } else if (round < 0) {\n roundMethod = Math.floor;\n } else {\n roundMethod = Math.round;\n }\n // Multiply up by precision, round accurately, then divide and use native toFixed()\n return (roundMethod((value + 1e-8) * power) / power).toFixed(precision);\n}\n","\r\nexport function stripInsignificantZeros(str: string, decimal: string): string {\r\n const parts = str.split(decimal);\r\n let result = '';\r\n\r\n if (parts[0] !== undefined) {\r\n result = parts[0];\r\n }\r\n\r\n if (parts[1] !== undefined) {\r\n const decimalPart = parts[1].replace(/0+$/, '');\r\n\r\n if (decimalPart.length > 0) {\r\n result = result + decimal + decimalPart;\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n","import { NestedArray, Settings } from './types';\nimport { stripInsignificantZeros } from './internal/stripInsignificantZeros';\nimport { settings } from './settings';\nimport { toFixed } from './toFixed';\n\n/**\n * Format a number, with comma-separated thousands and custom precision/decimal places.\n *\n * **Usage:**\n *\n * ```js\n * // Default usage\n * formatNumber(5318008);\n * // => 5,318,008\n *\n * // Custom format\n * formatNumber(9876543.21, { precision: 3, thousand: \" \" });\n * // => 9 876 543.210\n * ```\n *\n * @param {Number} number - Number to be formatted\n * @param {Object} [opts={}] - Object containing all the options of the method\n * @returns {String} - Given number properly formatted\n */\nexport function formatNumber(number: number, opts: Settings = {}): string {\n // Build options object from second param (if object) or all params, extending defaults\n opts = Object.assign({},\n settings,\n opts\n );\n\n // Do some calc\n const negative = number < 0 ? '-' : '';\n const base = parseInt(toFixed(Math.abs(number), opts.precision, opts.round), 10) + '';\n const mod = base.length > 3 ? base.length % 3 : 0;\n\n // Format the number\n const formatted = negative +\n (mod ? base.substr(0, mod) + opts.thousand : '') +\n base.substr(mod).replace(/(\\d{3})(?=\\d)/g, '$1' + opts.thousand) +\n (opts.precision! > 0 ? opts.decimal! + toFixed(Math.abs(number), opts.precision).split('.')[1] : '');\n\n return opts.stripZeros ? stripInsignificantZeros(formatted, opts.decimal!) : formatted;\n}\n\nexport function formatNumberArray(number: NestedArray<number>, opts: Settings = {}): NestedArray<string> {\n // Resursively format arrays:\n if (Array.isArray(number)) {\n return number.map((val) => formatNumberArray(val, opts));\n }\n\n return formatNumber(number, opts);\n}\n","import { CurrencyFormat } from '../types';\r\n\r\n/**\r\n * Parses a format string or object and returns format obj for use in rendering.\r\n *\r\n * `format` is either a string with the default (positive) format, or object\r\n * containing `pos` (required), `neg` and `zero` values.\r\n *\r\n * Either string or format.pos must contain '%v' (value) to be valid.\r\n *\r\n * @private\r\n * @param {String} [format='%s%v'] - String with the format to apply, where %s is the currency symbol and %v is the value\r\n * @return {Object} object represnting format (with pos, neg and zero attributes)\r\n */\r\nexport function checkCurrencyFormat(format: string | CurrencyFormat): CurrencyFormat {\r\n // Format could be a string, in which case `value` ('%v') must be present\r\n if (typeof format === 'string' && format.match('%v')) {\r\n // Create and return positive, negative and zero formats\r\n return {\r\n pos: format,\r\n neg: format.replace('-', '').replace('%v', '-%v'),\r\n zero: format,\r\n };\r\n }\r\n\r\n // Otherwise, assume format was fine\r\n return format as CurrencyFormat;\r\n}\r\n","import { NestedArray, Settings } from './types';\nimport { checkCurrencyFormat } from './internal/checkCurrencyFormat';\nimport { settings } from './settings';\nimport { formatNumber } from './formatNumber';\n\n/**\n * Format a number into currency.\n *\n * **Usage:**\n *\n * ```js\n * // Default usage\n * formatMoney(12345678);\n * // => $12,345,678.00\n *\n * // European formatting (custom symbol and separators)\n * formatMoney(4999.99, { symbol: \"\", precision: 2, thousand: \".\", decimal: \",\" });\n * // => €4.999,99\n *\n * // Negative values can be formatted nicely\n * formatMoney(-500000, { symbol: \"£ \", precision: 0 });\n * // => £ -500,000\n *\n * // Simple `format` string allows control of symbol position (%v = value, %s = symbol)\n * formatMoney(5318008, { symbol: \"GBP\", format: \"%v %s\" });\n * // => 5,318,008.00 GBP\n * ```\n *\n * @param {Number} amount - Amount to be formatted\n * @param {Object} [opts={}] - Object containing all the options of the method\n * @returns {String} - Given number properly formatted as money\n */\nexport function formatMoney(amount: number, opts: Settings = {}): string {\n // Build options object from second param (if object) or all params, extending defaults\n opts = Object.assign({},\n settings,\n opts\n );\n\n // Check format (returns object with pos, neg and zero)\n const formats = checkCurrencyFormat(opts.format!);\n\n // Choose which format to use for this value\n let useFormat;\n\n if (amount > 0) {\n useFormat = formats.pos;\n } else if (amount < 0) {\n useFormat = formats.neg!;\n } else {\n useFormat = formats.zero!;\n }\n\n // Return with currency symbol added\n return useFormat\n .replace('%s', opts.symbol!)\n .replace('%v', formatNumber(Math.abs(amount), opts));\n}\n\nexport function formatMoneyArray(amount: NestedArray<number>, opts: Settings = {}): NestedArray<string> {\n // Resursively format arrays\n if (Array.isArray(amount)) {\n return amount.map((value) => formatMoneyArray(value, opts));\n }\n\n return formatMoney(amount, opts);\n}\n"],"mappings":"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","names":["settings","unformat","value","decimal","settings","fallback","regex","unformattedValueString","negative","unformatted","unformatArray","val","toFixed","value","precision","round","settings","power","roundMethod","stripInsignificantZeros","str","decimal","parts","result","decimalPart","formatNumber","number","opts","settings","negative","base","toFixed","mod","formatted","stripInsignificantZeros","formatNumberArray","val","checkCurrencyFormat","format","formatMoney","amount","opts","settings","formats","checkCurrencyFormat","useFormat","formatNumber","formatMoneyArray","value"]}