number.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. /**
  2. * DevExtreme (localization/number.js)
  3. * Version: 19.1.16
  4. * Build date: Tue Oct 18 2022
  5. *
  6. * Copyright (c) 2012 - 2022 Developer Express Inc. ALL RIGHTS RESERVED
  7. * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
  8. */
  9. "use strict";
  10. function _slicedToArray(arr, i) {
  11. return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest()
  12. }
  13. function _nonIterableRest() {
  14. throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")
  15. }
  16. function _unsupportedIterableToArray(o, minLen) {
  17. if (!o) {
  18. return
  19. }
  20. if ("string" === typeof o) {
  21. return _arrayLikeToArray(o, minLen)
  22. }
  23. var n = Object.prototype.toString.call(o).slice(8, -1);
  24. if ("Object" === n && o.constructor) {
  25. n = o.constructor.name
  26. }
  27. if ("Map" === n || "Set" === n) {
  28. return Array.from(o)
  29. }
  30. if ("Arguments" === n || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) {
  31. return _arrayLikeToArray(o, minLen)
  32. }
  33. }
  34. function _arrayLikeToArray(arr, len) {
  35. if (null == len || len > arr.length) {
  36. len = arr.length
  37. }
  38. for (var i = 0, arr2 = new Array(len); i < len; i++) {
  39. arr2[i] = arr[i]
  40. }
  41. return arr2
  42. }
  43. function _iterableToArrayLimit(arr, i) {
  44. var _i = null == arr ? null : "undefined" !== typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"];
  45. if (null == _i) {
  46. return
  47. }
  48. var _arr = [];
  49. var _n = true;
  50. var _d = false;
  51. var _s, _e;
  52. try {
  53. for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
  54. _arr.push(_s.value);
  55. if (i && _arr.length === i) {
  56. break
  57. }
  58. }
  59. } catch (err) {
  60. _d = true;
  61. _e = err
  62. } finally {
  63. try {
  64. if (!_n && null != _i.return) {
  65. _i.return()
  66. }
  67. } finally {
  68. if (_d) {
  69. throw _e
  70. }
  71. }
  72. }
  73. return _arr
  74. }
  75. function _arrayWithHoles(arr) {
  76. if (Array.isArray(arr)) {
  77. return arr
  78. }
  79. }
  80. var dependencyInjector = require("../core/utils/dependency_injector");
  81. var inArray = require("../core/utils/array").inArray;
  82. var escapeRegExp = require("../core/utils/common").escapeRegExp;
  83. var each = require("../core/utils/iterator").each;
  84. var isPlainObject = require("../core/utils/type").isPlainObject;
  85. var ldmlNumber = require("./ldml/number");
  86. var config = require("../core/config");
  87. var errors = require("../core/errors");
  88. var toFixed = require("./utils").toFixed;
  89. var MAX_LARGE_NUMBER_POWER = 4;
  90. var DECIMAL_BASE = 10;
  91. var NUMERIC_FORMATS = ["currency", "fixedpoint", "exponential", "percent", "decimal"];
  92. var LargeNumberFormatPostfixes = {
  93. 1: "K",
  94. 2: "M",
  95. 3: "B",
  96. 4: "T"
  97. };
  98. var LargeNumberFormatPowers = {
  99. largenumber: "auto",
  100. thousands: 1,
  101. millions: 2,
  102. billions: 3,
  103. trillions: 4
  104. };
  105. var numberLocalization = dependencyInjector({
  106. numericFormats: NUMERIC_FORMATS,
  107. defaultLargeNumberFormatPostfixes: LargeNumberFormatPostfixes,
  108. _parseNumberFormatString: function(formatType) {
  109. var formatObject = {};
  110. if (!formatType || "string" !== typeof formatType) {
  111. return
  112. }
  113. var formatList = formatType.toLowerCase().split(" ");
  114. each(formatList, function(index, value) {
  115. if (inArray(value, NUMERIC_FORMATS) > -1) {
  116. formatObject.formatType = value
  117. } else {
  118. if (value in LargeNumberFormatPowers) {
  119. formatObject.power = LargeNumberFormatPowers[value]
  120. }
  121. }
  122. });
  123. if (formatObject.power && !formatObject.formatType) {
  124. formatObject.formatType = "fixedpoint"
  125. }
  126. if (formatObject.formatType) {
  127. return formatObject
  128. }
  129. },
  130. _calculateNumberPower: function(value, base, minPower, maxPower) {
  131. var number = Math.abs(value);
  132. var power = 0;
  133. if (number > 1) {
  134. while (number && number >= base && (void 0 === maxPower || power < maxPower)) {
  135. power++;
  136. number /= base
  137. }
  138. } else {
  139. if (number > 0 && number < 1) {
  140. while (number < 1 && (void 0 === minPower || power > minPower)) {
  141. power--;
  142. number *= base
  143. }
  144. }
  145. }
  146. return power
  147. },
  148. _getNumberByPower: function(number, power, base) {
  149. var result = number;
  150. while (power > 0) {
  151. result /= base;
  152. power--
  153. }
  154. while (power < 0) {
  155. result *= base;
  156. power++
  157. }
  158. return result
  159. },
  160. _formatNumber: function(value, formatObject, formatConfig) {
  161. var result;
  162. if ("auto" === formatObject.power) {
  163. formatObject.power = this._calculateNumberPower(value, 1e3, 0, MAX_LARGE_NUMBER_POWER)
  164. }
  165. if (formatObject.power) {
  166. value = this._getNumberByPower(value, formatObject.power, 1e3)
  167. }
  168. var powerPostfix = this.defaultLargeNumberFormatPostfixes[formatObject.power] || "";
  169. result = this._formatNumberCore(value, formatObject.formatType, formatConfig);
  170. result = result.replace(/(\d|.$)(\D*)$/, "$1" + powerPostfix + "$2");
  171. return result
  172. },
  173. _formatNumberExponential: function(value, formatConfig) {
  174. var power = this._calculateNumberPower(value, DECIMAL_BASE);
  175. var number = this._getNumberByPower(value, power, DECIMAL_BASE);
  176. if (void 0 === formatConfig.precision) {
  177. formatConfig.precision = 1
  178. }
  179. if (number.toFixed(formatConfig.precision || 0) >= DECIMAL_BASE) {
  180. power++;
  181. number /= DECIMAL_BASE
  182. }
  183. var powString = (power >= 0 ? "+" : "") + power.toString();
  184. return this._formatNumberCore(number, "fixedpoint", formatConfig) + "E" + powString
  185. },
  186. _addZeroes: function(value, precision) {
  187. var multiplier = Math.pow(10, precision);
  188. var sign = value < 0 ? "-" : "";
  189. value = (Math.abs(value) * multiplier >>> 0) / multiplier;
  190. var result = value.toString();
  191. while (result.length < precision) {
  192. result = "0" + result
  193. }
  194. return sign + result
  195. },
  196. _addGroupSeparators: function(value) {
  197. var parts = value.toString().split(".");
  198. return parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, config().thousandsSeparator) + (parts[1] ? config().decimalSeparator + parts[1] : "")
  199. },
  200. _formatNumberCore: function(value, format, formatConfig) {
  201. if ("exponential" === format) {
  202. return this._formatNumberExponential(value, formatConfig)
  203. }
  204. if ("decimal" !== format && null !== formatConfig.precision) {
  205. formatConfig.precision = formatConfig.precision || 0
  206. }
  207. if ("percent" === format) {
  208. value = 100 * value
  209. }
  210. if (void 0 !== formatConfig.precision) {
  211. if ("decimal" === format) {
  212. value = this._addZeroes(value, formatConfig.precision)
  213. } else {
  214. value = null === formatConfig.precision ? value.toPrecision() : toFixed(value, formatConfig.precision)
  215. }
  216. }
  217. if ("decimal" !== format) {
  218. value = this._addGroupSeparators(value)
  219. } else {
  220. value = value.toString().replace(".", config().decimalSeparator)
  221. }
  222. if ("percent" === format) {
  223. value += "%"
  224. }
  225. return value
  226. },
  227. _normalizeFormat: function(format) {
  228. if (!format) {
  229. return {}
  230. }
  231. if ("function" === typeof format) {
  232. return format
  233. }
  234. if (!isPlainObject(format)) {
  235. format = {
  236. type: format
  237. }
  238. }
  239. return format
  240. },
  241. _getSeparators: function() {
  242. return {
  243. decimalSeparator: this.getDecimalSeparator(),
  244. thousandsSeparator: this.getThousandsSeparator()
  245. }
  246. },
  247. getThousandsSeparator: function() {
  248. return this.format(1e4, "fixedPoint")[2]
  249. },
  250. getDecimalSeparator: function() {
  251. return this.format(1.2, {
  252. type: "fixedPoint",
  253. precision: 1
  254. })[1]
  255. },
  256. convertDigits: function(value, toStandard) {
  257. var digits = this.format(90, "decimal");
  258. if ("string" !== typeof value || "0" === digits[1]) {
  259. return value
  260. }
  261. var fromFirstDigit = toStandard ? digits[1] : "0";
  262. var toFirstDigit = toStandard ? "0" : digits[1];
  263. var fromLastDigit = toStandard ? digits[0] : "9";
  264. var regExp = new RegExp("[" + fromFirstDigit + "-" + fromLastDigit + "]", "g");
  265. return value.replace(regExp, function(char) {
  266. return String.fromCharCode(char.charCodeAt(0) + (toFirstDigit.charCodeAt(0) - fromFirstDigit.charCodeAt(0)))
  267. })
  268. },
  269. getSign: function(text, format) {
  270. if ("-" === text.replace(/[^0-9-]/g, "").charAt(0)) {
  271. return -1
  272. }
  273. if (!format) {
  274. return 1
  275. }
  276. var separators = this._getSeparators();
  277. var regExp = new RegExp("[0-9" + escapeRegExp(separators.decimalSeparator + separators.thousandsSeparator) + "]+", "g");
  278. var negativeEtalon = this.format(-1, format).replace(regExp, "1");
  279. var cleanedText = text.replace(regExp, "1");
  280. return cleanedText === negativeEtalon ? -1 : 1
  281. },
  282. format: function(value, _format) {
  283. if ("number" !== typeof value) {
  284. return value
  285. }
  286. if ("number" === typeof _format) {
  287. return value
  288. }
  289. _format = _format && _format.formatter || _format;
  290. if ("function" === typeof _format) {
  291. return _format(value)
  292. }
  293. _format = this._normalizeFormat(_format);
  294. if (!_format.type) {
  295. _format.type = "decimal"
  296. }
  297. var numberConfig = this._parseNumberFormatString(_format.type);
  298. if (!numberConfig) {
  299. return this.convertDigits(ldmlNumber.getFormatter(_format.type, this._getSeparators())(value))
  300. }
  301. return this._formatNumber(value, numberConfig, _format)
  302. },
  303. parse: function(text, format) {
  304. if (!text) {
  305. return
  306. }
  307. if (format && format.parser) {
  308. return format.parser(text)
  309. }
  310. text = this.convertDigits(text, true);
  311. if (format && "string" !== typeof format) {
  312. errors.log("W0011")
  313. }
  314. var decimalSeparator = this.getDecimalSeparator();
  315. var regExp = new RegExp("[^0-9" + escapeRegExp(decimalSeparator) + "]", "g");
  316. var cleanedText = text.replace(regExp, "").replace(decimalSeparator, ".").replace(/\.$/g, "");
  317. if ("." === cleanedText || "" === cleanedText) {
  318. return null
  319. }
  320. if (this._calcSignificantDigits(cleanedText) > 15) {
  321. return NaN
  322. }
  323. var parsed = +cleanedText;
  324. return parsed * this.getSign(text, format)
  325. },
  326. _calcSignificantDigits: function(text) {
  327. var _text$split = text.split("."),
  328. _text$split2 = _slicedToArray(_text$split, 2),
  329. integer = _text$split2[0],
  330. fractional = _text$split2[1];
  331. var calcDigitsAfterLeadingZeros = function(digits) {
  332. var index = -1;
  333. for (var i = 0; i < digits.length; i++) {
  334. if ("0" !== digits[i]) {
  335. index = i;
  336. break
  337. }
  338. }
  339. return index > -1 ? digits.length - index : 0
  340. };
  341. var result = 0;
  342. if (integer) {
  343. result += calcDigitsAfterLeadingZeros(integer.split(""))
  344. }
  345. if (fractional) {
  346. result += calcDigitsAfterLeadingZeros(fractional.split("").reverse())
  347. }
  348. return result
  349. }
  350. });
  351. module.exports = numberLocalization;