devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
320 lines (318 loc) • 12.5 kB
JavaScript
/**
* DevExtreme (cjs/__internal/core/localization/number.js)
* Version: 25.2.3
* Build date: Fri Dec 12 2025
*
* Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _config = _interopRequireDefault(require("../../../core/config"));
var _errors = _interopRequireDefault(require("../../../core/errors"));
var _currency = _interopRequireDefault(require("../../core/localization/currency"));
var _number = _interopRequireDefault(require("../../core/localization/intl/number"));
var _number2 = require("../../core/localization/ldml/number");
var _utils = require("../../core/localization/utils");
var _m_common = require("../../core/utils/m_common");
var _m_dependency_injector = require("../../core/utils/m_dependency_injector");
var _m_iterator = require("../../core/utils/m_iterator");
var _m_type = require("../../core/utils/m_type");
function _interopRequireDefault(e) {
return e && e.__esModule ? e : {
default: e
}
}
const hasIntl = "undefined" !== typeof Intl;
const MAX_LARGE_NUMBER_POWER = 4;
const DECIMAL_BASE = 10;
const NUMERIC_FORMATS = ["currency", "fixedpoint", "exponential", "percent", "decimal"];
const LargeNumberFormatPostfixes = {
1: "K",
2: "M",
3: "B",
4: "T"
};
const LargeNumberFormatPowers = {
largenumber: "auto",
thousands: 1,
millions: 2,
billions: 3,
trillions: 4
};
const numberLocalization = (0, _m_dependency_injector.injector)({
engine: () => "base",
numericFormats: NUMERIC_FORMATS,
defaultLargeNumberFormatPostfixes: LargeNumberFormatPostfixes,
_parseNumberFormatString(formatType) {
const formatObject = {};
if (!formatType || "string" !== typeof formatType) {
return
}
const formatList = formatType.toLowerCase().split(" ");
(0, _m_iterator.each)(formatList, ((_, value) => {
if (NUMERIC_FORMATS.includes(value)) {
formatObject.formatType = value
} else if (value in LargeNumberFormatPowers) {
formatObject.power = LargeNumberFormatPowers[value]
}
}));
if (formatObject.power && !formatObject.formatType) {
formatObject.formatType = "fixedpoint"
}
if ((rule = formatObject, "formatType" in rule) && formatObject.formatType) {
return formatObject
}
var rule;
return
},
_calculateNumberPower(value, base, minPower, maxPower) {
let number = Math.abs(value);
let power = 0;
if (number > 1) {
while (number && number >= base && (void 0 === maxPower || power < maxPower)) {
power += 1;
number /= base
}
} else if (number > 0 && number < 1) {
while (number < 1 && (void 0 === minPower || power > minPower)) {
power -= 1;
number *= base
}
}
return power
},
_getNumberByPower(number, power, base) {
let result = number;
while (power > 0) {
result /= base;
power -= 1
}
while (power < 0) {
result *= base;
power += 1
}
return result
},
_formatNumber(value, formatObject, formatConfig) {
if ("auto" === formatObject.power) {
formatObject.power = this._calculateNumberPower(value, 1e3, 0, 4)
}
if (formatObject.power) {
value = this._getNumberByPower(value, formatObject.power, 1e3)
}
const powerPostfix = formatObject.power ? this.defaultLargeNumberFormatPostfixes[formatObject.power] || "" : "";
let result = this._formatNumberCore(value, formatObject.formatType, formatConfig);
result = result.replace(/(\d|.$)(\D*)$/, `$1${powerPostfix}$2`);
return result
},
_formatNumberExponential(value, formatConfig) {
let power = this._calculateNumberPower(value, 10);
let number = this._getNumberByPower(value, power, 10);
if (void 0 === formatConfig.precision) {
formatConfig.precision = 1
}
if (number.toFixed(formatConfig.precision || 0) >= 10) {
power += 1;
number /= 10
}
const powString = (power >= 0 ? "+" : "") + power.toString();
return `${this._formatNumberCore(number,"fixedpoint",formatConfig)}E${powString}`
},
_addZeroes(value, precision) {
const multiplier = 10 ** precision;
const sign = value < 0 ? "-" : "";
value = (Math.abs(value) * multiplier >>> 0) / multiplier;
let result = value.toString();
while (result.length < precision) {
result = `0${result}`
}
return sign + result
},
_addGroupSeparators(value) {
const parts = value.toString().split(".");
return parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, (0, _config.default)().thousandsSeparator) + (parts[1] ? (0, _config.default)().decimalSeparator + parts[1] : "")
},
_formatNumberCore(value, format, formatConfig) {
if ("exponential" === format) {
return this._formatNumberExponential(value, formatConfig)
}
if ("decimal" !== format && null !== formatConfig.precision) {
formatConfig.precision = formatConfig.precision || 0
}
if ("percent" === format) {
value *= 100
}
let result = `${value}`;
if (void 0 !== formatConfig.precision) {
if ("decimal" === format) {
result = this._addZeroes(value, formatConfig.precision)
} else {
result = null === formatConfig.precision ? value.toPrecision() : (0, _utils.toFixed)(value, formatConfig.precision)
}
}
if ("decimal" !== format) {
result = this._addGroupSeparators(result)
} else {
result = result.toString().replace(".", (0, _config.default)().decimalSeparator)
}
if ("percent" === format) {
result += "%"
}
return result
},
_normalizeFormat(format) {
if (!format) {
return {}
}
if ("function" === typeof format) {
return format
}
if (!(0, _m_type.isPlainObject)(format)) {
format = {
type: format
}
}
return format
},
_getSeparators() {
return {
decimalSeparator: this.getDecimalSeparator(),
thousandsSeparator: this.getThousandsSeparator()
}
},
getThousandsSeparator() {
return this.format(1e4, "fixedPoint")[2]
},
getDecimalSeparator() {
return this.format(1.2, {
type: "fixedPoint",
precision: 1
})[1]
},
convertDigits(value, toStandard) {
const digits = this.format(90, "decimal");
if ("string" !== typeof value || "0" === digits[1]) {
return value
}
const fromFirstDigit = toStandard ? digits[1] : "0";
const toFirstDigit = toStandard ? "0" : digits[1];
const fromLastDigit = toStandard ? digits[0] : "9";
const regExp = new RegExp(`[${fromFirstDigit}-${fromLastDigit}]`, "g");
return value.replace(regExp, (char => String.fromCharCode(char.charCodeAt(0) + (toFirstDigit.charCodeAt(0) - fromFirstDigit.charCodeAt(0)))))
},
getNegativeEtalonRegExp(format) {
const separators = this._getSeparators();
const digitalRegExp = new RegExp(`[0-9${(0,_m_common.escapeRegExp)(separators.decimalSeparator+separators.thousandsSeparator)}]+`, "g");
let negativeEtalon = this.format(-1, format).replace(digitalRegExp, "1");
["\\", "(", ")", "[", "]", "*", "+", "$", "^", "?", "|", "{", "}"].forEach((char => {
negativeEtalon = negativeEtalon.replace(new RegExp(`\\${char}`, "g"), `\\${char}`)
}));
negativeEtalon = negativeEtalon.replace(/ /g, "\\s");
negativeEtalon = negativeEtalon.replace(/1/g, ".*");
return new RegExp(negativeEtalon, "g")
},
getSign(text, format) {
if (!format) {
if (text.replace(/[^0-9-]/g, "").startsWith("-")) {
return -1
}
return 1
}
const negativeEtalon = this.getNegativeEtalonRegExp(format);
return text.match(negativeEtalon) ? -1 : 1
},
format(value, format) {
var _format;
if ("number" !== typeof value) {
return value
}
if ("number" === typeof format) {
return value
}
format = (null === (_format = format) || void 0 === _format ? void 0 : _format.formatter) || format;
if ("function" === typeof format) {
return format(value)
}
format = this._normalizeFormat(format);
if (!format.type) {
format.type = "decimal"
}
const numberConfig = this._parseNumberFormatString(format.type);
if (!numberConfig) {
const formatterConfig = this._getSeparators();
formatterConfig.unlimitedIntegerDigits = format.unlimitedIntegerDigits;
const formattedValue = (0, _number2.getFormatter)(format.type, formatterConfig)(value);
return this.convertDigits(formattedValue)
}
return this._formatNumber(value, numberConfig, format)
},
parse(text, format) {
var _format2;
if (!text) {
return
}
if ("string" !== typeof format && null !== (_format2 = format) && void 0 !== _format2 && _format2.parser) {
return format.parser(text)
}
text = this.convertDigits(text, true);
if (format && "string" !== typeof format) {
_errors.default.log("W0011")
}
const decimalSeparator = this.getDecimalSeparator();
const regExp = new RegExp(`[^0-9${(0,_m_common.escapeRegExp)(decimalSeparator)}]`, "g");
const cleanedText = text.replace(regExp, "").replace(decimalSeparator, ".").replace(/\.$/g, "");
if ("." === cleanedText || "" === cleanedText) {
return null
}
if (this._calcSignificantDigits(cleanedText) > 15) {
return NaN
}
let parsed = +cleanedText * this.getSign(text, format);
format = this._normalizeFormat(format);
const formatConfig = this._parseNumberFormatString(format.type);
let power = null === formatConfig || void 0 === formatConfig ? void 0 : formatConfig.power;
if (power) {
if ("auto" === power) {
const match = /\d(K|M|B|T)/.exec(text);
if (match) {
power = Object.keys(LargeNumberFormatPostfixes).map(Number).find((p => LargeNumberFormatPostfixes[p] === match[1]))
}
}
parsed *= 10 ** (3 * power)
}
if ("percent" === (null === formatConfig || void 0 === formatConfig ? void 0 : formatConfig.formatType)) {
parsed /= 100
}
return parsed
},
_calcSignificantDigits(text) {
const [integer, fractional] = text.split(".");
const calcDigitsAfterLeadingZeros = digits => {
let index = -1;
for (let i = 0; i < digits.length; i += 1) {
if ("0" !== digits[i]) {
index = i;
break
}
}
return index > -1 ? digits.length - index : 0
};
let result = 0;
if (integer) {
result += calcDigitsAfterLeadingZeros(integer.split(""))
}
if (fractional) {
result += calcDigitsAfterLeadingZeros(fractional.split("").reverse())
}
return result
}
});
numberLocalization.inject(_currency.default);
if (hasIntl) {
numberLocalization.inject(_number.default)
}
var _default = exports.default = numberLocalization;