diff --git a/src/numbers/custom-number-format.js b/src/numbers/custom-number-format.js index 98bed114..2343b160 100644 --- a/src/numbers/custom-number-format.js +++ b/src/numbers/custom-number-format.js @@ -1,34 +1,16 @@ import { CURRENCY, PERCENT, LIST_SEPARATOR, GROUP_SEPARATOR, CURRENCY_PLACEHOLDER, PERCENT_PLACEHOLDER, POINT, EMPTY } from '../common/constants'; import isNegativeZero from '../common/is-negative-zero'; -import formatCurrencySymbol from './format-currency-symbol'; import groupInteger from './group-integer'; import round from '../common/round'; - -const PLACEHOLDER = "__??__"; +import { setStyleOptions, setFormatLiterals, replaceLiterals } from './utils'; const SHARP = "#"; const ZERO = "0"; -const literalRegExp = /(\\.)|(['][^']*[']?)|(["][^"]*["]?)/g; const trailingZerosRegExp = /(\.(?:[0-9]*[1-9])?)0+$/g; const trailingPointRegExp = /\.$/; const commaRegExp = /\,/g; -function setFormatLiterals(formatOptions) { - let format = formatOptions.format; - if (format.indexOf("'") > -1 || format.indexOf("\"") > -1 || format.indexOf("\\") > -1) { - const literals = formatOptions.literals = []; - formatOptions.format = format.replace(literalRegExp, function(match) { - const quoteChar = match.charAt(0).replace("\\", EMPTY); - const literal = match.slice(1).replace(quoteChar, EMPTY); - - literals.push(literal); - - return PLACEHOLDER; - }); - } -} - function trimTrailingZeros(value, lastZero) { let trimRegex; @@ -119,22 +101,6 @@ function setValueSpecificFormat(formatOptions) { formatOptions.format = format; } -function setStyleOptions(formatOptions, info) { - const format = formatOptions.format; - - //multiply number if the format has percent - if (format.indexOf(PERCENT_PLACEHOLDER) !== -1) { - formatOptions.style = PERCENT; - formatOptions.symbol = info.numbers.symbols.percentSign; - formatOptions.number *= 100; - } - - if (format.indexOf(CURRENCY_PLACEHOLDER) !== -1) { - formatOptions.style = CURRENCY; - formatOptions.symbol = formatCurrencySymbol(info); - } -} - function setGroupOptions(formatOptions) { formatOptions.hasGroup = formatOptions.format.indexOf(GROUP_SEPARATOR) > -1; if (formatOptions.hasGroup) { @@ -187,17 +153,6 @@ function replaceStyleSymbols(number, style, symbol) { return result; } -function replaceLiterals(number, literals) { - let result = number; - if (literals) { - const length = literals.length; - for (let idx = 0; idx < length; idx++) { - result = result.replace(PLACEHOLDER, literals[idx]); - } - } - return result; -} - function replacePlaceHolders(formatOptions, info) { const { start, end, negative, negativeZero, format, decimalIndex, lastZeroIndex, hasNegativeFormat, hasGroup } = formatOptions; let number = formatOptions.number; diff --git a/src/numbers/parse-number.js b/src/numbers/parse-number.js index 35b67f48..99c97a10 100644 --- a/src/numbers/parse-number.js +++ b/src/numbers/parse-number.js @@ -1,8 +1,10 @@ import { localeInfo, localeCurrency, currencyDisplays } from '../cldr'; import { PERCENT, NUMBER_PLACEHOLDER, CURRENCY_PLACEHOLDER, DEFAULT_LOCALE, EMPTY, POINT } from '../common/constants'; +import { setStyleOptions, setFormatLiterals } from './utils'; import isNumber from '../common/is-number'; import isCurrencyStyle from './is-currency-style'; import formatOptions from './format-options'; +import isString from '../common/is-string'; const exponentRegExp = /[eE][\-+]?[0-9]+/; const nonBreakingSpaceRegExp = /\u00A0/g; @@ -55,6 +57,19 @@ function cleanCurrencyNumber(value, info, format) { }; } +function cleanLiterals(number, formatOptions) { + const literals = formatOptions.literals; + let result = number; + + if (literals) { + for (let idx = 0; idx < literals.length; idx++) { + result = result.replace(literals[idx], EMPTY); + } + } + + return result; +} + export default function parseNumber(value, locale = DEFAULT_LOCALE, format = {}) { if (!value && value !== 0) { return null; @@ -68,29 +83,39 @@ export default function parseNumber(value, locale = DEFAULT_LOCALE, format = {}) const symbols = info.numbers.symbols; let number = value.toString(); + let formatOptions = format || {}; let isPercent; + if (isString(format)) { + formatOptions = { format: format }; + setFormatLiterals(formatOptions); + number = cleanLiterals(number, formatOptions); + + setStyleOptions(formatOptions, info); + } + + if (formatOptions.style === PERCENT || number.indexOf(symbols.percentSign) > -1) { + number = number.replace(symbols.percentSign, EMPTY); + isPercent = true; + } + if (exponentRegExp.test(number)) { number = parseFloat(number.replace(symbols.decimal, POINT)); return isNaN(number) ? null : number; } + const { negative: negativeCurrency, number: currencyNumber } = cleanCurrencyNumber(number, info, formatOptions); + number = String(currencyNumber).trim(); + const negativeSignIndex = number.indexOf("-"); if (negativeSignIndex > 0) { return null; } let isNegative = negativeSignIndex > -1; - const { negative: negativeCurrency, number: newNumber } = cleanCurrencyNumber(number, info, format); - number = newNumber; isNegative = negativeCurrency !== undefined ? negativeCurrency : isNegative; - if (format.style === PERCENT || number.indexOf(symbols.percentSign) > -1) { - number = number.replace(symbols.percentSign, EMPTY); - isPercent = true; - } - number = number.replace("-", EMPTY) .replace(nonBreakingSpaceRegExp, " ") .split(symbols.group.replace(nonBreakingSpaceRegExp, " ")).join(EMPTY) diff --git a/src/numbers/utils.js b/src/numbers/utils.js new file mode 100644 index 00000000..ebd00c49 --- /dev/null +++ b/src/numbers/utils.js @@ -0,0 +1,47 @@ +import { PERCENT_PLACEHOLDER, CURRENCY_PLACEHOLDER, CURRENCY, PERCENT, EMPTY } from '../common/constants'; +import formatCurrencySymbol from './format-currency-symbol'; + +const literalRegExp = /(\\.)|(['][^']*[']?)|(["][^"]*["]?)/g; +const PLACEHOLDER = "__??__"; + +export function setStyleOptions(formatOptions, info) { + const format = formatOptions.format; + + //multiply number if the format has percent + if (format.indexOf(PERCENT_PLACEHOLDER) !== -1) { + formatOptions.style = PERCENT; + formatOptions.symbol = info.numbers.symbols.percentSign; + formatOptions.number *= 100; + } + + if (format.indexOf(CURRENCY_PLACEHOLDER) !== -1) { + formatOptions.style = CURRENCY; + formatOptions.symbol = formatCurrencySymbol(info); + } +} + +export function setFormatLiterals(formatOptions) { + let format = formatOptions.format; + if (format.indexOf("'") > -1 || format.indexOf("\"") > -1 || format.indexOf("\\") > -1) { + const literals = formatOptions.literals = []; + formatOptions.format = format.replace(literalRegExp, function(match) { + const quoteChar = match.charAt(0).replace("\\", EMPTY); + const literal = match.slice(1).replace(quoteChar, EMPTY); + + literals.push(literal); + + return PLACEHOLDER; + }); + } +} + +export function replaceLiterals(number, literals) { + let result = number; + if (literals) { + const length = literals.length; + for (let idx = 0; idx < length; idx++) { + result = result.replace(PLACEHOLDER, literals[idx]); + } + } + return result; +} \ No newline at end of file diff --git a/test/numbers.js b/test/numbers.js index 34914fb9..d65b7964 100644 --- a/test/numbers.js +++ b/test/numbers.js @@ -736,6 +736,21 @@ describe('parseNumber', () => { expect(parseNumber("-1 123 112,13 лв.", "bg")).toEqual(-1123112.13); }); + it("parses currency with custom format", () => { + expect(parseNumber("$12", 'en', '$#.#')).toEqual(12); + expect(parseNumber("-$12", 'en', '$#.#')).toEqual(-12); + }); + + it("parses percent with custom format", () => { + expect(parseNumber("% 12", 'en', '% #.#')).toEqual(0.12); + expect(parseNumber("% -12", 'en', '% #.#')).toEqual(-0.12); + }); + + it("parses number with custom format literals", () => { + expect(parseNumber("foo12", 'en', '"foo"#.#')).toEqual(12); + expect(parseNumber("T12", 'en', '\\T#.#')).toEqual(12); + }); + it("parses currency numbers with negative format", () => { loadCustom({ currencyPattern: "¤#,##0.00;(¤#,##0.00)", currencies: { USD: { symbol: "$" }}}); expect(parseNumber("($1,123,112.13)", "custom", { style: "currency", currency: "USD" })).toEqual(-1123112.13);