@ui5/webcomponents-localization 0.0.0-037d08c67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/.eslintignore +6 -0
  2. package/.eslintrc.cjs +3 -0
  3. package/.npsrc.json +3 -0
  4. package/CHANGELOG.md +2261 -0
  5. package/LICENSE.txt +201 -0
  6. package/README.md +51 -0
  7. package/dist/.tsbuildinfo +1 -0
  8. package/dist/Assets-fetch.d.ts +1 -0
  9. package/dist/Assets-fetch.js +3 -0
  10. package/dist/Assets-fetch.js.map +1 -0
  11. package/dist/Assets-node.d.ts +11 -0
  12. package/dist/Assets-node.js +12 -0
  13. package/dist/Assets-node.js.map +1 -0
  14. package/dist/Assets.d.ts +1 -0
  15. package/dist/Assets.js +3 -0
  16. package/dist/Assets.js.map +1 -0
  17. package/dist/CalendarUtils.d.ts +3 -0
  18. package/dist/CalendarUtils.js +6 -0
  19. package/dist/CalendarUtils.js.map +1 -0
  20. package/dist/DateFormat.d.ts +5 -0
  21. package/dist/DateFormat.js +7 -0
  22. package/dist/DateFormat.js.map +1 -0
  23. package/dist/LocaleData.d.ts +5 -0
  24. package/dist/LocaleData.js +7 -0
  25. package/dist/LocaleData.js.map +1 -0
  26. package/dist/NumberFormat.d.ts +5 -0
  27. package/dist/NumberFormat.js +7 -0
  28. package/dist/NumberFormat.js.map +1 -0
  29. package/dist/dates/CalendarDate.d.ts +42 -0
  30. package/dist/dates/CalendarDate.js +193 -0
  31. package/dist/dates/CalendarDate.js.map +1 -0
  32. package/dist/dates/ExtremeDates.d.ts +5 -0
  33. package/dist/dates/ExtremeDates.js +29 -0
  34. package/dist/dates/ExtremeDates.js.map +1 -0
  35. package/dist/dates/UI5Date.d.ts +5 -0
  36. package/dist/dates/UI5Date.js +7 -0
  37. package/dist/dates/UI5Date.js.map +1 -0
  38. package/dist/dates/UniversalDate.d.ts +47 -0
  39. package/dist/dates/UniversalDate.js +5 -0
  40. package/dist/dates/UniversalDate.js.map +1 -0
  41. package/dist/dates/convertMonthNumbersToMonthNames.d.ts +16 -0
  42. package/dist/dates/convertMonthNumbersToMonthNames.js +30 -0
  43. package/dist/dates/convertMonthNumbersToMonthNames.js.map +1 -0
  44. package/dist/dates/getDaysInMonth.d.ts +3 -0
  45. package/dist/dates/getDaysInMonth.js +10 -0
  46. package/dist/dates/getDaysInMonth.js.map +1 -0
  47. package/dist/dates/getRoundedTimestamp.d.ts +7 -0
  48. package/dist/dates/getRoundedTimestamp.js +15 -0
  49. package/dist/dates/getRoundedTimestamp.js.map +1 -0
  50. package/dist/dates/getTodayUTCTimestamp.d.ts +7 -0
  51. package/dist/dates/getTodayUTCTimestamp.js +9 -0
  52. package/dist/dates/getTodayUTCTimestamp.js.map +1 -0
  53. package/dist/dates/modifyDateBy.d.ts +14 -0
  54. package/dist/dates/modifyDateBy.js +55 -0
  55. package/dist/dates/modifyDateBy.js.map +1 -0
  56. package/dist/dates/transformDateToSecondaryType.d.ts +7 -0
  57. package/dist/dates/transformDateToSecondaryType.js +18 -0
  58. package/dist/dates/transformDateToSecondaryType.js.map +1 -0
  59. package/dist/features/calendar/Buddhist.d.ts +1 -0
  60. package/dist/features/calendar/Buddhist.js +2 -0
  61. package/dist/features/calendar/Buddhist.js.map +1 -0
  62. package/dist/features/calendar/Gregorian.d.ts +1 -0
  63. package/dist/features/calendar/Gregorian.js +2 -0
  64. package/dist/features/calendar/Gregorian.js.map +1 -0
  65. package/dist/features/calendar/Islamic.d.ts +1 -0
  66. package/dist/features/calendar/Islamic.js +2 -0
  67. package/dist/features/calendar/Islamic.js.map +1 -0
  68. package/dist/features/calendar/Japanese.d.ts +1 -0
  69. package/dist/features/calendar/Japanese.js +2 -0
  70. package/dist/features/calendar/Japanese.js.map +1 -0
  71. package/dist/features/calendar/Persian.d.ts +1 -0
  72. package/dist/features/calendar/Persian.js +2 -0
  73. package/dist/features/calendar/Persian.js.map +1 -0
  74. package/dist/generated/assets/cldr/Unicode-Data-Files-LICENSE.txt +27 -0
  75. package/dist/generated/assets/cldr/ar.json +7087 -0
  76. package/dist/generated/assets/cldr/ar_EG.json +7087 -0
  77. package/dist/generated/assets/cldr/ar_SA.json +7086 -0
  78. package/dist/generated/assets/cldr/bg.json +5981 -0
  79. package/dist/generated/assets/cldr/ca.json +6083 -0
  80. package/dist/generated/assets/cldr/cnr.json +6169 -0
  81. package/dist/generated/assets/cldr/cs.json +6709 -0
  82. package/dist/generated/assets/cldr/cy.json +6932 -0
  83. package/dist/generated/assets/cldr/da.json +5927 -0
  84. package/dist/generated/assets/cldr/de.json +6048 -0
  85. package/dist/generated/assets/cldr/de_AT.json +6049 -0
  86. package/dist/generated/assets/cldr/de_CH.json +6047 -0
  87. package/dist/generated/assets/cldr/el.json +5832 -0
  88. package/dist/generated/assets/cldr/el_CY.json +5832 -0
  89. package/dist/generated/assets/cldr/en.json +6044 -0
  90. package/dist/generated/assets/cldr/en_AU.json +6084 -0
  91. package/dist/generated/assets/cldr/en_GB.json +6075 -0
  92. package/dist/generated/assets/cldr/en_HK.json +6084 -0
  93. package/dist/generated/assets/cldr/en_IE.json +6075 -0
  94. package/dist/generated/assets/cldr/en_IN.json +6080 -0
  95. package/dist/generated/assets/cldr/en_NZ.json +6075 -0
  96. package/dist/generated/assets/cldr/en_PG.json +6076 -0
  97. package/dist/generated/assets/cldr/en_SG.json +6080 -0
  98. package/dist/generated/assets/cldr/en_ZA.json +6076 -0
  99. package/dist/generated/assets/cldr/es.json +6103 -0
  100. package/dist/generated/assets/cldr/es_AR.json +6106 -0
  101. package/dist/generated/assets/cldr/es_BO.json +6105 -0
  102. package/dist/generated/assets/cldr/es_CL.json +5998 -0
  103. package/dist/generated/assets/cldr/es_CO.json +5998 -0
  104. package/dist/generated/assets/cldr/es_MX.json +6107 -0
  105. package/dist/generated/assets/cldr/es_PE.json +5889 -0
  106. package/dist/generated/assets/cldr/es_UY.json +5891 -0
  107. package/dist/generated/assets/cldr/es_VE.json +5890 -0
  108. package/dist/generated/assets/cldr/et.json +6027 -0
  109. package/dist/generated/assets/cldr/fa.json +5950 -0
  110. package/dist/generated/assets/cldr/fi.json +6195 -0
  111. package/dist/generated/assets/cldr/fr.json +5997 -0
  112. package/dist/generated/assets/cldr/fr_BE.json +5997 -0
  113. package/dist/generated/assets/cldr/fr_CA.json +5991 -0
  114. package/dist/generated/assets/cldr/fr_CH.json +6015 -0
  115. package/dist/generated/assets/cldr/fr_LU.json +5997 -0
  116. package/dist/generated/assets/cldr/he.json +6541 -0
  117. package/dist/generated/assets/cldr/hi.json +5859 -0
  118. package/dist/generated/assets/cldr/hr.json +6196 -0
  119. package/dist/generated/assets/cldr/hu.json +5945 -0
  120. package/dist/generated/assets/cldr/id.json +5730 -0
  121. package/dist/generated/assets/cldr/it.json +5986 -0
  122. package/dist/generated/assets/cldr/it_CH.json +5986 -0
  123. package/dist/generated/assets/cldr/ja.json +5889 -0
  124. package/dist/generated/assets/cldr/kk.json +5939 -0
  125. package/dist/generated/assets/cldr/ko.json +5770 -0
  126. package/dist/generated/assets/cldr/lt.json +6578 -0
  127. package/dist/generated/assets/cldr/lv.json +6114 -0
  128. package/dist/generated/assets/cldr/mk.json +6045 -0
  129. package/dist/generated/assets/cldr/ms.json +5564 -0
  130. package/dist/generated/assets/cldr/nb.json +6035 -0
  131. package/dist/generated/assets/cldr/nl.json +6202 -0
  132. package/dist/generated/assets/cldr/nl_BE.json +6202 -0
  133. package/dist/generated/assets/cldr/pl.json +6589 -0
  134. package/dist/generated/assets/cldr/pt.json +6115 -0
  135. package/dist/generated/assets/cldr/pt_PT.json +6180 -0
  136. package/dist/generated/assets/cldr/ro.json +6200 -0
  137. package/dist/generated/assets/cldr/ru.json +6503 -0
  138. package/dist/generated/assets/cldr/ru_UA.json +6503 -0
  139. package/dist/generated/assets/cldr/sk.json +6432 -0
  140. package/dist/generated/assets/cldr/sl.json +6444 -0
  141. package/dist/generated/assets/cldr/sr.json +6241 -0
  142. package/dist/generated/assets/cldr/sr_Latn.json +6226 -0
  143. package/dist/generated/assets/cldr/sv.json +6076 -0
  144. package/dist/generated/assets/cldr/th.json +5875 -0
  145. package/dist/generated/assets/cldr/tr.json +6094 -0
  146. package/dist/generated/assets/cldr/uk.json +6454 -0
  147. package/dist/generated/assets/cldr/vi.json +5669 -0
  148. package/dist/generated/assets/cldr/zh_CN.json +5717 -0
  149. package/dist/generated/assets/cldr/zh_HK.json +5726 -0
  150. package/dist/generated/assets/cldr/zh_SG.json +5726 -0
  151. package/dist/generated/assets/cldr/zh_TW.json +5793 -0
  152. package/dist/generated/json-imports/LocaleData-fetch.d.ts +1 -0
  153. package/dist/generated/json-imports/LocaleData-fetch.js +93 -0
  154. package/dist/generated/json-imports/LocaleData-fetch.js.map +1 -0
  155. package/dist/generated/json-imports/LocaleData-node.d.ts +1 -0
  156. package/dist/generated/json-imports/LocaleData-node.js +93 -0
  157. package/dist/generated/json-imports/LocaleData-node.js.map +1 -0
  158. package/dist/generated/json-imports/LocaleData.d.ts +1 -0
  159. package/dist/generated/json-imports/LocaleData.js +93 -0
  160. package/dist/generated/json-imports/LocaleData.js.map +1 -0
  161. package/dist/getCachedLocaleDataInstance.d.ts +4 -0
  162. package/dist/getCachedLocaleDataInstance.js +10 -0
  163. package/dist/getCachedLocaleDataInstance.js.map +1 -0
  164. package/dist/locale/getLocaleData.d.ts +11 -0
  165. package/dist/locale/getLocaleData.js +23 -0
  166. package/dist/locale/getLocaleData.js.map +1 -0
  167. package/dist/sap/base/Event.js +59 -0
  168. package/dist/sap/base/Eventing.js +146 -0
  169. package/dist/sap/base/Log.js +3 -0
  170. package/dist/sap/base/assert.js +34 -0
  171. package/dist/sap/base/config/MemoryConfigurationProvider.js +20 -0
  172. package/dist/sap/base/config.js +17 -0
  173. package/dist/sap/base/i18n/Formatting.d.ts +8 -0
  174. package/dist/sap/base/i18n/Formatting.js +11 -0
  175. package/dist/sap/base/i18n/Formatting.js.map +1 -0
  176. package/dist/sap/base/i18n/LanguageTag.js +173 -0
  177. package/dist/sap/base/i18n/Localization.d.ts +4 -0
  178. package/dist/sap/base/i18n/Localization.js +12 -0
  179. package/dist/sap/base/i18n/Localization.js.map +1 -0
  180. package/dist/sap/base/i18n/date/CalendarType.js +43 -0
  181. package/dist/sap/base/i18n/date/CalendarWeekNumbering.js +105 -0
  182. package/dist/sap/base/i18n/date/TimezoneUtils.js +319 -0
  183. package/dist/sap/base/strings/camelize.js +30 -0
  184. package/dist/sap/base/strings/formatMessage.js +93 -0
  185. package/dist/sap/base/util/LoaderExtensions.d.ts +4 -0
  186. package/dist/sap/base/util/LoaderExtensions.js +14 -0
  187. package/dist/sap/base/util/LoaderExtensions.js.map +1 -0
  188. package/dist/sap/base/util/ObjectPath.d.ts +4 -0
  189. package/dist/sap/base/util/ObjectPath.js +6 -0
  190. package/dist/sap/base/util/ObjectPath.js.map +1 -0
  191. package/dist/sap/base/util/Version.js +157 -0
  192. package/dist/sap/base/util/_merge.js +89 -0
  193. package/dist/sap/base/util/array/uniqueSort.js +41 -0
  194. package/dist/sap/base/util/deepClone.js +102 -0
  195. package/dist/sap/base/util/deepEqual.js +83 -0
  196. package/dist/sap/base/util/extend.js +61 -0
  197. package/dist/sap/base/util/isEmptyObject.js +34 -0
  198. package/dist/sap/base/util/isPlainObject.js +52 -0
  199. package/dist/sap/base/util/now.js +28 -0
  200. package/dist/sap/base/util/resolveReference.js +3 -0
  201. package/dist/sap/base/util/uid.js +27 -0
  202. package/dist/sap/ui/base/DataType.js +657 -0
  203. package/dist/sap/ui/base/Interface.js +72 -0
  204. package/dist/sap/ui/base/Metadata.js +483 -0
  205. package/dist/sap/ui/base/Object.js +300 -0
  206. package/dist/sap/ui/core/CalendarType.js +24 -0
  207. package/dist/sap/ui/core/Configuration.d.ts +17 -0
  208. package/dist/sap/ui/core/Configuration.js +23 -0
  209. package/dist/sap/ui/core/Configuration.js.map +1 -0
  210. package/dist/sap/ui/core/Core.d.ts +25 -0
  211. package/dist/sap/ui/core/Core.js +13 -0
  212. package/dist/sap/ui/core/Core.js.map +1 -0
  213. package/dist/sap/ui/core/FormatSettings.d.ts +9 -0
  214. package/dist/sap/ui/core/FormatSettings.js +12 -0
  215. package/dist/sap/ui/core/FormatSettings.js.map +1 -0
  216. package/dist/sap/ui/core/Locale.js +194 -0
  217. package/dist/sap/ui/core/LocaleData.js +2717 -0
  218. package/dist/sap/ui/core/Supportability.js +5 -0
  219. package/dist/sap/ui/core/Theming.js +539 -0
  220. package/dist/sap/ui/core/date/Buddhist.js +196 -0
  221. package/dist/sap/ui/core/date/CalendarUtils.js +65 -0
  222. package/dist/sap/ui/core/date/CalendarWeekNumbering.js +30 -0
  223. package/dist/sap/ui/core/date/Gregorian.js +32 -0
  224. package/dist/sap/ui/core/date/Islamic.js +367 -0
  225. package/dist/sap/ui/core/date/Japanese.js +257 -0
  226. package/dist/sap/ui/core/date/Persian.js +394 -0
  227. package/dist/sap/ui/core/date/UI5Date.js +991 -0
  228. package/dist/sap/ui/core/date/UniversalDate.js +1324 -0
  229. package/dist/sap/ui/core/date/_Calendars.js +22 -0
  230. package/dist/sap/ui/core/format/DateFormat.js +3310 -0
  231. package/dist/sap/ui/core/format/NumberFormat.js +2835 -0
  232. package/dist/sap/ui/core/format/TimezoneUtil.js +24 -0
  233. package/package-scripts.cjs +35 -0
  234. package/package.json +46 -0
  235. package/tsconfig.json +24 -0
  236. package/used-modules.txt +52 -0
@@ -0,0 +1,2835 @@
1
+ /*!
2
+ * OpenUI5
3
+ * (c) Copyright 2009-2024 SAP SE or an SAP affiliate company.
4
+ * Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
5
+ */
6
+ // Provides class sap.ui.core.format.NumberFormat
7
+ import BaseObject from "../../base/Object.js";
8
+ import Locale from "../Locale.js";
9
+ import LocaleData from "../LocaleData.js";
10
+ import Supportability from "../Supportability.js";
11
+ import Log from "../../../base/Log.js";
12
+ import assert from "../../../base/assert.js";
13
+ import extend from "../../../base/util/extend.js";
14
+ import Configuration from "../Configuration.js";
15
+ /**
16
+ * Format classes
17
+ *
18
+ * @namespace
19
+ * @name sap.ui.core.format
20
+ * @public
21
+ */
22
+
23
+ /**
24
+ * Constructor for NumberFormat - must not be used: To get a NumberFormat instance, please use getInstance, getFloatInstance or getIntegerInstance.
25
+ *
26
+ * @class
27
+ * The NumberFormat is a static class for formatting and parsing numeric values according
28
+ * to a set of format options.
29
+ *
30
+ * @public
31
+ * @hideconstructor
32
+ * @alias sap.ui.core.format.NumberFormat
33
+ * @extends sap.ui.base.Object
34
+ */
35
+ var NumberFormat = BaseObject.extend("sap.ui.core.format.NumberFormat", /** @lends sap.ui.core.format.NumberFormat.prototype */{
36
+ constructor: function (oFormatOptions) {
37
+ // Do not use the constructor
38
+ throw new Error();
39
+ }
40
+ });
41
+ var rAllWhiteSpaces = /\s/g,
42
+ rDigit = /\d/,
43
+ // Regex for checking if a number has leading zeros
44
+ rLeadingZeros = /^(-?)0+(\d)/,
45
+ // Not matching Sc (currency symbol) and Z (separator) characters
46
+ // https://www.unicode.org/reports/tr44/#General_Category_Values
47
+ rNotSAndNotZ = /[^\$\xA2-\xA5\u058F\u060B\u09F2\u09F3\u09FB\u0AF1\u0BF9\u0E3F\u17DB\u20A0-\u20BD\uA838\uFDFC\uFE69\uFF04\uFFE0\uFFE1\uFFE5\uFFE6\u0020\xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]/,
48
+ // Regex for matching the number placeholder in pattern
49
+ rNumPlaceHolder = /0+(\.0+)?/,
50
+ // Regex for checking that the given string only consists of '0' characters
51
+ rOnlyZeros = /^0+$/;
52
+
53
+ /*
54
+ * Is used to validate existing grouping separators.
55
+ * e.g. yyy.yyy.yyy -> /^\d+(?:\.?\d{3})*\.?\d{3}$/
56
+ */
57
+ var getGroupingRegExp = function (groupingSeparator, groupingSize, groupingBaseSize) {
58
+ var sGroupingEscaped = quote(groupingSeparator);
59
+ return new RegExp("^\\d+" + "(?:" + sGroupingEscaped + "?" + "\\d{" + groupingSize + "}" + ")*" + "" + sGroupingEscaped + "?" + "\\d{" + groupingBaseSize + "}" + "$");
60
+ };
61
+
62
+ /**
63
+ * Internal enumeration to differentiate number types
64
+ */
65
+ var mNumberType = {
66
+ INTEGER: "integer",
67
+ FLOAT: "float",
68
+ CURRENCY: "currency",
69
+ UNIT: "unit",
70
+ PERCENT: "percent"
71
+ };
72
+
73
+ /**
74
+ * Specifies a rounding behavior for numerical operations capable of discarding precision. Each rounding mode in this object indicates how the least
75
+ * significant returned digits of rounded result is to be calculated.
76
+ *
77
+ * @public
78
+ * @enum {string}
79
+ * @alias sap.ui.core.format.NumberFormat.RoundingMode
80
+ */
81
+ var mRoundingMode = {
82
+ /**
83
+ * Rounding mode to round towards negative infinity
84
+ * @public
85
+ * @type {string}
86
+ */
87
+ FLOOR: "FLOOR",
88
+ /**
89
+ * Rounding mode to round towards positive infinity
90
+ * @public
91
+ * @type {string}
92
+ */
93
+ CEILING: "CEILING",
94
+ /**
95
+ * Rounding mode to round towards zero
96
+ * @public
97
+ * @type {string}
98
+ */
99
+ TOWARDS_ZERO: "TOWARDS_ZERO",
100
+ /**
101
+ * Rounding mode to round away from zero
102
+ * @public
103
+ * @type {string}
104
+ */
105
+ AWAY_FROM_ZERO: "AWAY_FROM_ZERO",
106
+ /**
107
+ * Rounding mode to round towards the nearest neighbor unless both neighbors are equidistant, in which case round towards negative infinity.
108
+ * @public
109
+ * @type {string}
110
+ */
111
+ HALF_FLOOR: "HALF_FLOOR",
112
+ /**
113
+ * Rounding mode to round towards the nearest neighbor unless both neighbors are equidistant, in which case round towards positive infinity.
114
+ * @public
115
+ * @type {string}
116
+ */
117
+ HALF_CEILING: "HALF_CEILING",
118
+ /**
119
+ * Rounding mode to round towards the nearest neighbor unless both neighbors are equidistant, in which case round towards zero.
120
+ * @public
121
+ * @type {string}
122
+ */
123
+ HALF_TOWARDS_ZERO: "HALF_TOWARDS_ZERO",
124
+ /**
125
+ * Rounding mode to round towards the nearest neighbor unless both neighbors are equidistant, in which case round away from zero.
126
+ * @public
127
+ * @type {string}
128
+ */
129
+ HALF_AWAY_FROM_ZERO: "HALF_AWAY_FROM_ZERO"
130
+ };
131
+
132
+ /**
133
+ * Derives the maximal possible decimals from the given format option's <code>maxFractionDigits</code>
134
+ * and <code>decimals</code> properties.
135
+ *
136
+ * If <code>decimals</code> and <code>maxFractionDigits</code> are >= 0, then the minimum of
137
+ * <code>maxFractionDigits</code> and <code>decimals</code> is returned, - otherwise
138
+ * <code>decimals</code> is returned.
139
+ *
140
+ * @param {object} oFormatOptions
141
+ * @param {int} [oFormatOptions.decimals]
142
+ * The number of decimal digits
143
+ * @param {int} [oFormatOptions.maxFractionDigits]
144
+ * The maximum number of decimal digits
145
+ * @returns {int}
146
+ * The maximum decimals to be used
147
+ *
148
+ * @private
149
+ * @static
150
+ */
151
+ NumberFormat.getMaximalDecimals = function ({
152
+ decimals,
153
+ maxFractionDigits
154
+ }) {
155
+ if (maxFractionDigits >= 0 && decimals > 0 && maxFractionDigits < decimals) {
156
+ return maxFractionDigits;
157
+ }
158
+ return decimals;
159
+ };
160
+ var mRoundingFunction = {};
161
+ mRoundingFunction[mRoundingMode.FLOOR] = Math.floor;
162
+ mRoundingFunction[mRoundingMode.CEILING] = Math.ceil;
163
+ mRoundingFunction[mRoundingMode.TOWARDS_ZERO] = function (nValue) {
164
+ return nValue > 0 ? Math.floor(nValue) : Math.ceil(nValue);
165
+ };
166
+ mRoundingFunction[mRoundingMode.AWAY_FROM_ZERO] = function (nValue) {
167
+ return nValue > 0 ? Math.ceil(nValue) : Math.floor(nValue);
168
+ };
169
+ mRoundingFunction[mRoundingMode.HALF_TOWARDS_ZERO] = function (nValue) {
170
+ return nValue > 0 ? Math.ceil(nValue - 0.5) : Math.floor(nValue + 0.5);
171
+ };
172
+ mRoundingFunction[mRoundingMode.HALF_AWAY_FROM_ZERO] = function (nValue) {
173
+ return nValue > 0 ? Math.floor(nValue + 0.5) : Math.ceil(nValue - 0.5);
174
+ };
175
+ mRoundingFunction[mRoundingMode.HALF_FLOOR] = function (nValue) {
176
+ return Math.ceil(nValue - 0.5);
177
+ };
178
+ mRoundingFunction[mRoundingMode.HALF_CEILING] = Math.round;
179
+ NumberFormat.RoundingMode = mRoundingMode;
180
+
181
+ /*
182
+ * Default format options for Integer
183
+ */
184
+ NumberFormat.oDefaultIntegerFormat = {
185
+ minIntegerDigits: 1,
186
+ maxIntegerDigits: 99,
187
+ minFractionDigits: 0,
188
+ maxFractionDigits: 0,
189
+ strictGroupingValidation: false,
190
+ groupingEnabled: false,
191
+ groupingSize: 3,
192
+ groupingSeparator: ",",
193
+ decimalSeparator: ".",
194
+ plusSign: "+",
195
+ minusSign: "-",
196
+ isInteger: true,
197
+ type: mNumberType.INTEGER,
198
+ showMeasure: false,
199
+ style: "standard",
200
+ showNumber: true,
201
+ parseAsString: false,
202
+ preserveDecimals: false,
203
+ roundingMode: NumberFormat.RoundingMode.TOWARDS_ZERO,
204
+ emptyString: NaN,
205
+ showScale: true
206
+ };
207
+
208
+ /*
209
+ * Default format options for Float
210
+ */
211
+ NumberFormat.oDefaultFloatFormat = {
212
+ minIntegerDigits: 1,
213
+ maxIntegerDigits: 99,
214
+ minFractionDigits: 0,
215
+ maxFractionDigits: 99,
216
+ strictGroupingValidation: false,
217
+ groupingEnabled: true,
218
+ groupingSize: 3,
219
+ groupingSeparator: ",",
220
+ decimalSeparator: ".",
221
+ plusSign: "+",
222
+ minusSign: "-",
223
+ isInteger: false,
224
+ type: mNumberType.FLOAT,
225
+ showMeasure: false,
226
+ style: "standard",
227
+ showNumber: true,
228
+ parseAsString: false,
229
+ preserveDecimals: false,
230
+ roundingMode: NumberFormat.RoundingMode.HALF_AWAY_FROM_ZERO,
231
+ emptyString: NaN,
232
+ showScale: true
233
+ };
234
+
235
+ /*
236
+ * Default format options for Percent
237
+ */
238
+ NumberFormat.oDefaultPercentFormat = {
239
+ minIntegerDigits: 1,
240
+ maxIntegerDigits: 99,
241
+ minFractionDigits: 0,
242
+ maxFractionDigits: 99,
243
+ strictGroupingValidation: false,
244
+ groupingEnabled: true,
245
+ groupingSize: 3,
246
+ groupingSeparator: ",",
247
+ decimalSeparator: ".",
248
+ plusSign: "+",
249
+ minusSign: "-",
250
+ percentSign: "%",
251
+ isInteger: false,
252
+ type: mNumberType.PERCENT,
253
+ showMeasure: false,
254
+ style: "standard",
255
+ showNumber: true,
256
+ parseAsString: false,
257
+ preserveDecimals: false,
258
+ roundingMode: NumberFormat.RoundingMode.HALF_AWAY_FROM_ZERO,
259
+ emptyString: NaN,
260
+ showScale: true
261
+ };
262
+
263
+ /*
264
+ * Default format options for Currency
265
+ * @name sap.ui.core.format.NumberFormat.oDefaultCurrencyFormat
266
+ */
267
+ NumberFormat.oDefaultCurrencyFormat = {
268
+ minIntegerDigits: 1,
269
+ maxIntegerDigits: 99,
270
+ // the default value for min/maxFractionDigits is defined in oLocaleData.getCurrencyDigits
271
+ // they need to be left undefined here in order to detect whether they are set from outside
272
+ strictGroupingValidation: false,
273
+ groupingEnabled: true,
274
+ groupingSize: 3,
275
+ groupingSeparator: ",",
276
+ decimalSeparator: ".",
277
+ plusSign: "+",
278
+ minusSign: "-",
279
+ isInteger: false,
280
+ type: mNumberType.CURRENCY,
281
+ showMeasure: true,
282
+ currencyCode: true,
283
+ currencyContext: 'standard',
284
+ style: "standard",
285
+ showNumber: true,
286
+ customCurrencies: undefined,
287
+ parseAsString: false,
288
+ preserveDecimals: false,
289
+ roundingMode: NumberFormat.RoundingMode.HALF_AWAY_FROM_ZERO,
290
+ emptyString: NaN,
291
+ showScale: true,
292
+ // The 'precision' format option is ignored because the number of decimals shouldn't
293
+ // depend on the number of integer part of a number
294
+ ignorePrecision: true
295
+ };
296
+
297
+ /*
298
+ * Default format options for Unit (type is CLDR)
299
+ * @name sap.ui.core.format.NumberFormat.oDefaultUnitFormat
300
+ */
301
+ NumberFormat.oDefaultUnitFormat = {
302
+ minIntegerDigits: 1,
303
+ maxIntegerDigits: 99,
304
+ strictGroupingValidation: false,
305
+ groupingEnabled: true,
306
+ groupingSize: 3,
307
+ groupingSeparator: ",",
308
+ decimalSeparator: ".",
309
+ plusSign: "+",
310
+ minusSign: "-",
311
+ isInteger: false,
312
+ type: mNumberType.UNIT,
313
+ showMeasure: true,
314
+ style: "standard",
315
+ showNumber: true,
316
+ customUnits: undefined,
317
+ allowedUnits: undefined,
318
+ parseAsString: false,
319
+ preserveDecimals: false,
320
+ roundingMode: NumberFormat.RoundingMode.HALF_AWAY_FROM_ZERO,
321
+ emptyString: NaN,
322
+ showScale: true
323
+ };
324
+
325
+ /**
326
+ * An alias for {@link #getFloatInstance}.
327
+ *
328
+ * @param {object} [oFormatOptions] Object which defines the format options. See the documentation of
329
+ * {@link #getFloatInstance} for the parameters
330
+ * @param {sap.ui.core.Locale} [oLocale] Locale to get the formatter for
331
+ * @return {sap.ui.core.format.NumberFormat} float instance of the NumberFormat
332
+ *
333
+ */
334
+ NumberFormat.getInstance = function (oFormatOptions, oLocale) {
335
+ return this.getFloatInstance(oFormatOptions, oLocale);
336
+ };
337
+
338
+ /**
339
+ * Get a float instance of the NumberFormat, which can be used for formatting.
340
+ *
341
+ * If no locale is given, the currently configured
342
+ * {@link sap.ui.core.Configuration.FormatSettings#getFormatLocale formatLocale} will be used.
343
+ *
344
+ * <p>
345
+ * This instance has HALF_AWAY_FROM_ZERO set as default rounding mode.
346
+ * Please set the roundingMode property in oFormatOptions to change the
347
+ * default value.
348
+ * </p>
349
+ *
350
+ * The following example shows how grouping is done:
351
+ * <pre>
352
+ * var oFormat = NumberFormat.getFloatInstance({
353
+ * "groupingEnabled": true, // grouping is enabled
354
+ * "groupingSeparator": '.', // grouping separator is '.'
355
+ * "groupingSize": 3, // the amount of digits to be grouped (here: thousand)
356
+ * "decimalSeparator": "," // the decimal separator must be different from the grouping separator
357
+ * });
358
+ *
359
+ * oFormat.format(1234.56); // "1.234,56"
360
+ * </pre>
361
+ *
362
+ * @param {object} [oFormatOptions] The option object, which supports the following parameters.
363
+ * If no options are given, default values according to the type and locale settings are used.
364
+ * @param {int} [oFormatOptions.decimals] defines the number of decimal digits
365
+ * @param {string} [oFormatOptions.decimalSeparator] defines the character used as decimal separator.
366
+ * Note: <code>decimalSeparator</code> must always be different from <code>groupingSeparator</code>.
367
+ * @param {null|number|string} [oFormatOptions.emptyString=NaN] since 1.30.0 defines what an empty string
368
+ * is parsed as, and what is formatted as an empty string. The allowed values are "" (empty string),
369
+ * NaN, <code>null</code>, or 0.
370
+ * The 'format' and 'parse' functions are done in a symmetric way. For example, when this
371
+ * parameter is set to NaN, an empty string is parsed as NaN, and NaN is formatted as an empty
372
+ * string.
373
+ * @param {int} [oFormatOptions.groupingBaseSize=3] defines the grouping base size in digits if
374
+ * it is different from the grouping size (e.g. Indian grouping)
375
+ * @param {boolean} [oFormatOptions.groupingEnabled=true] defines whether grouping is enabled
376
+ * (grouping separators are shown)
377
+ * @param {string} [oFormatOptions.groupingSeparator] defines the character used as grouping separator.
378
+ * Note: <code>groupingSeparator</code> must always be different from <code>decimalSeparator</code>.
379
+ * @param {int} [oFormatOptions.groupingSize=3] defines the grouping size in digits; the default
380
+ * is <code>3</code>. It must be a positive number.
381
+ * @param {int} [oFormatOptions.maxFractionDigits=99] defines the maximum number of decimal digits
382
+ * @param {int} [oFormatOptions.maxIntegerDigits=99] defines the maximum number of non-decimal digits.
383
+ * If the number exceeds this maximum, e.g. 1e+120, "?" characters are shown instead of digits.
384
+ * @param {int} [oFormatOptions.minFractionDigits=0] defines the minimal number of decimal digits
385
+ * @param {int} [oFormatOptions.minIntegerDigits=1] defines the minimal number of non-decimal digits
386
+ * @param {string} [oFormatOptions.minusSign] defines the used minus symbol
387
+ * @param {boolean} [oFormatOptions.parseAsString=false] since 1.28.2 defines whether to output
388
+ * the string from the parse function in order to keep the precision for big numbers. Numbers
389
+ * in scientific notation are parsed back to standard notation. For example, "5e-3" is parsed
390
+ * to "0.005".
391
+ * @param {string} [oFormatOptions.pattern] CLDR number pattern which is used to format the number
392
+ * @param {string} [oFormatOptions.plusSign] defines the used plus symbol
393
+ * @param {int} [oFormatOptions.precision] defines the numerical precision; the number of decimals
394
+ * is calculated dependent on the integer digits
395
+ * @param {boolean} [oFormatOptions.preserveDecimals=false] Whether {@link #format} preserves
396
+ * decimal digits except trailing zeros in case there are more decimals than the
397
+ * <code>maxFractionDigits</code> format option allows.
398
+ * If decimals are not preserved, the formatted number is rounded to <code>maxFractionDigits</code>.
399
+ * @param {sap.ui.core.format.NumberFormat.RoundingMode} [oFormatOptions.roundingMode=HALF_AWAY_FROM_ZERO]
400
+ * specifies the rounding behavior for discarding the digits after the maximum fraction digits
401
+ * defined by maxFractionDigits. Rounding will only be applied if the passed value is of type <code>number</code>.
402
+ * This can be assigned
403
+ * <ul>
404
+ * <li>by value in {@link sap.ui.core.format.NumberFormat.RoundingMode RoundingMode},</li>
405
+ * <li>via a function that is used for rounding the number and takes two parameters: the number itself, and the number of decimal digits that should be reserved.</li>
406
+ * </ul>
407
+ * @param {int} [oFormatOptions.shortDecimals] defines the number of decimal in the shortened format string. If this isn't specified, the 'decimals' options is used
408
+ * @param {int} [oFormatOptions.shortLimit] only use short number formatting for values above this limit
409
+ * @param {int} [oFormatOptions.shortRefNumber] since 1.40 specifies a number from which the scale factor for 'short' or 'long' style format is generated. The generated scale factor is
410
+ * used for all numbers which are formatted with this format instance. This option has effect only when the option 'style' is set to 'short' or 'long'. This option is by default set
411
+ * with <code>undefined</code> which means the scale factor is selected automatically for each number being formatted.
412
+ * @param {boolean} [oFormatOptions.showScale=true] since 1.40 specifies whether the scale factor is shown in the formatted number. This option takes effect only when the 'style' options is set to either 'short' or 'long'.
413
+ * @param {boolean} [oFormatOptions.strictGroupingValidation=false] whether the positions of grouping separators are validated. Space characters used as grouping separators are not validated.
414
+ * @param {string} [oFormatOptions.style=standard] defines the style of format. Valid values are
415
+ * 'short, 'long' or 'standard' (based on the CLDR decimalFormat). When set to 'short' or 'long',
416
+ * numbers are formatted into compact forms. When this option is set, the default value of the
417
+ * 'precision' option is set to 2. This can be changed by setting either min/maxFractionDigits,
418
+ * decimals, shortDecimals, or the 'precision' option itself.
419
+ * @param {sap.ui.core.Locale} [oLocale] Locale to get the formatter for
420
+ * @return {sap.ui.core.format.NumberFormat} float instance of the NumberFormat
421
+ * @static
422
+ * @public
423
+ */
424
+ NumberFormat.getFloatInstance = function (oFormatOptions, oLocale) {
425
+ var oFormat = this.createInstance(oFormatOptions, oLocale),
426
+ oLocaleFormatOptions = this.getLocaleFormatOptions(oFormat.oLocaleData, mNumberType.FLOAT);
427
+ oFormat.oFormatOptions = extend({}, this.oDefaultFloatFormat, oLocaleFormatOptions, oFormat.oOriginalFormatOptions);
428
+ return oFormat;
429
+ };
430
+
431
+ /**
432
+ * Get an integer instance of the NumberFormat, which can be used for formatting.
433
+ *
434
+ * If no locale is given, the currently configured
435
+ * {@link sap.ui.core.Configuration.FormatSettings#getFormatLocale formatLocale} will be used.
436
+ *
437
+ * <p>
438
+ * This instance has TOWARDS_ZERO set as default rounding mode.
439
+ * Please set the roundingMode property in oFormatOptions to change the
440
+ * default value.
441
+ * </p>
442
+ *
443
+ * The following example shows how grouping is done:
444
+ * <pre>
445
+ * var oFormat = NumberFormat.getIntegerInstance({
446
+ * "groupingEnabled": true, // grouping is enabled
447
+ * "groupingSeparator": '.', // grouping separator is '.'
448
+ * "groupingSize": 3 // the amount of digits to be grouped (here: thousand)
449
+ * });
450
+ *
451
+ * oFormat.format(1234); // "1.234"
452
+ * </pre>
453
+ *
454
+ * @param {object} [oFormatOptions] The option object, which supports the following parameters.
455
+ * If no options are given, default values according to the type and locale settings are used.
456
+ * @param {int} [oFormatOptions.decimals] defines the number of decimal digits
457
+ * @param {string} [oFormatOptions.decimalSeparator] defines the character used as decimal separator.
458
+ * Note: <code>decimalSeparator</code> must always be different from <code>groupingSeparator</code>.
459
+ * @param {null|number|string} [oFormatOptions.emptyString=NaN] since 1.30.0 defines what an empty string
460
+ * is parsed as, and what is formatted as an empty string. The allowed values are "" (empty string)
461
+ * NaN, <code>null</code>, or 0.
462
+ * The 'format' and 'parse' functions are done in a symmetric way. For example, when this
463
+ * parameter is set to NaN, an empty string is parsed as NaN, and NaN is formatted as an empty
464
+ * string.
465
+ * @param {int} [oFormatOptions.groupingBaseSize=3] defines the grouping base size in digits if
466
+ * it is different from the grouping size (e.g. Indian grouping)
467
+ * @param {boolean} [oFormatOptions.groupingEnabled=false] defines whether grouping is enabled
468
+ * (grouping separators are shown)
469
+ * @param {string} [oFormatOptions.groupingSeparator] defines the character used as grouping separator.
470
+ * Note: <code>groupingSeparator</code> must always be different from <code>decimalSeparator</code>.
471
+ * @param {int} [oFormatOptions.groupingSize=3] defines the grouping size in digits; the default
472
+ * is <code>3</code>. It must be a positive number.
473
+ * @param {int} [oFormatOptions.maxFractionDigits=0] defines the maximum number of decimal digits
474
+ * @param {int} [oFormatOptions.maxIntegerDigits=99] defines the maximum number of non-decimal digits.
475
+ * If the number exceeds this maximum, e.g. 1e+120, "?" characters are shown instead of digits.
476
+ * @param {int} [oFormatOptions.minFractionDigits=0] defines the minimal number of decimal digits
477
+ * @param {int} [oFormatOptions.minIntegerDigits=1] defines the minimal number of non-decimal digits
478
+ * @param {string} [oFormatOptions.minusSign] defines the used minus symbol
479
+ * @param {boolean} [oFormatOptions.parseAsString=false] since 1.28.2 defines whether to output
480
+ * the string from the parse function in order to keep the precision for big numbers. Numbers
481
+ * in scientific notation are parsed back to standard notation. For example, "5e+3" is parsed
482
+ * to "5000".
483
+ * @param {string} [oFormatOptions.pattern] CLDR number pattern which is used to format the number
484
+ * @param {string} [oFormatOptions.plusSign] defines the used plus symbol
485
+ * @param {int} [oFormatOptions.precision] defines the numerical precision; the number of decimals
486
+ * is calculated dependent on the integer digits
487
+ * @param {boolean} [oFormatOptions.preserveDecimals=false] Whether {@link #format} preserves
488
+ * decimal digits except trailing zeros in case there are more decimals than the
489
+ * <code>maxFractionDigits</code> format option allows.
490
+ * If decimals are not preserved, the formatted number is rounded to <code>maxFractionDigits</code>.
491
+ * @param {sap.ui.core.format.NumberFormat.RoundingMode} [oFormatOptions.roundingMode=TOWARDS_ZERO]
492
+ * specifies the rounding behavior for discarding the digits after the maximum fraction digits
493
+ * defined by maxFractionDigits. Rounding will only be applied if the passed value is of type <code>number</code>.
494
+ * This can be assigned
495
+ * <ul>
496
+ * <li>by value in {@link sap.ui.core.format.NumberFormat.RoundingMode RoundingMode},</li>
497
+ * <li>via a function that is used for rounding the number and takes two parameters: the number itself, and the number of decimal digits that should be reserved.</li>
498
+ * </ul>
499
+ * @param {int} [oFormatOptions.shortDecimals] defines the number of decimal in the shortened format string. If this isn't specified, the 'decimals' options is used
500
+ * @param {int} [oFormatOptions.shortLimit] only use short number formatting for values above this limit
501
+ * @param {int} [oFormatOptions.shortRefNumber] since 1.40 specifies a number from which the scale factor for 'short' or 'long' style format is generated. The generated scale factor is
502
+ * used for all numbers which are formatted with this format instance. This option has effect only when the option 'style' is set to 'short' or 'long'. This option is by default set
503
+ * with <code>undefined</code> which means the scale factor is selected automatically for each number being formatted.
504
+ * @param {boolean} [oFormatOptions.showScale=true] since 1.40 specifies whether the scale factor is shown in the formatted number. This option takes effect only when the 'style' options is set to either 'short' or 'long'.
505
+ * @param {boolean} [oFormatOptions.strictGroupingValidation=false] whether the positions of grouping separators are validated. Space characters used as grouping separators are not validated.
506
+ * @param {string} [oFormatOptions.style=standard] defines the style of format. Valid values are
507
+ * 'short, 'long' or 'standard' (based on the CLDR decimalFormat). When set to 'short' or 'long',
508
+ * numbers are formatted into compact forms. When this option is set, the default value of the
509
+ * 'precision' option is set to 2. This can be changed by setting either min/maxFractionDigits,
510
+ * decimals, shortDecimals, or the 'precision' option itself.
511
+ * @param {sap.ui.core.Locale} [oLocale] Locale to get the formatter for
512
+ * @return {sap.ui.core.format.NumberFormat} integer instance of the NumberFormat
513
+ * @static
514
+ * @public
515
+ */
516
+ NumberFormat.getIntegerInstance = function (oFormatOptions, oLocale) {
517
+ var oFormat = this.createInstance(oFormatOptions, oLocale),
518
+ oLocaleFormatOptions = this.getLocaleFormatOptions(oFormat.oLocaleData, mNumberType.INTEGER);
519
+ oFormat.oFormatOptions = extend({}, this.oDefaultIntegerFormat, oLocaleFormatOptions, oFormat.oOriginalFormatOptions);
520
+ return oFormat;
521
+ };
522
+
523
+ /**
524
+ * Get a currency instance of the NumberFormat, which can be used for formatting.
525
+ *
526
+ * If no locale is given, the currently configured
527
+ * {@link sap.ui.core.Configuration.FormatSettings#getFormatLocale formatLocale} will be used.
528
+ *
529
+ * <p>
530
+ * This instance has HALF_AWAY_FROM_ZERO set as default rounding mode.
531
+ * Please set the roundingMode property in oFormatOptions to change the
532
+ * default value.
533
+ * </p>
534
+ *
535
+ * The currency instance supports locally defined custom currency exclusive to the created instance.
536
+ * The following example shows how to use custom currencies (e.g. for Bitcoins):
537
+ * <pre>
538
+ * var oFormat = NumberFormat.getCurrencyInstance({
539
+ * "currencyCode": false,
540
+ * "customCurrencies": {
541
+ * "BTC": {
542
+ * "symbol": "\u0243",
543
+ * "decimals": 3
544
+ * }
545
+ * }
546
+ * });
547
+ *
548
+ * oFormat.format(123.4567, "BTC"); // "Ƀ 123.457"
549
+ * </pre>
550
+ *
551
+ * As an alternative to using a fixed <code>symbol</code> for your custom currencies, you can also provide an ISO-Code.
552
+ * The provided ISO-Code will be used to look up the currency symbol in the global configuration,
553
+ * either defined in the CLDR or custom defined on the Format Settings (see {@link sap.ui.core.Configuration.FormatSettings#setCustomCurrencies}, {@link sap.ui.core.Configuration.FormatSettings#addCustomCurrencies}).
554
+ *
555
+ * If no symbol is given at all, the custom currency key is used for formatting.
556
+ *
557
+ * <pre>
558
+ * var oFormat = NumberFormat.getCurrencyInstance({
559
+ * "currencyCode": false,
560
+ * "customCurrencies": {
561
+ * "MyDollar": {
562
+ * "isoCode": "USD",
563
+ * "decimals": 3
564
+ * },
565
+ * "Bitcoin": {
566
+ * "decimals": 2
567
+ * }
568
+ * }
569
+ * });
570
+ *
571
+ * // symbol looked up from global configuration
572
+ * oFormat.format(123.4567, "MyDollar"); // "$123.457"
573
+ *
574
+ * // no symbol available, custom currency key is rendered
575
+ * oFormat.format(777.888, "Bitcoin"); // "Bitcoin 777.89"
576
+ * </pre>
577
+ *
578
+ * @param {object} [oFormatOptions] The option object, which supports the following parameters.
579
+ * If no options are given, default values according to the type and locale settings are used.
580
+ * @param {boolean} [oFormatOptions.currencyCode=true] defines whether the currency is shown as
581
+ * a code in currency format. The currency symbol is displayed when this option is set to
582
+ * <code>false</code> and a symbol has been defined for the given currency code.
583
+ * @param {string} [oFormatOptions.currencyContext=standard] can be set either to 'standard'
584
+ * (the default value) or to 'accounting' for an accounting-specific currency display
585
+ * @param {Object<string,object>} [oFormatOptions.customCurrencies] defines a set of custom currencies exclusive to this NumberFormat instance.
586
+ * Custom currencies must not only consist of digits.
587
+ * If custom currencies are defined on the instance, no other currencies can be formatted and parsed by this instance.
588
+ * Globally available custom currencies can be added via the global configuration.
589
+ * See the above examples.
590
+ * See also {@link sap.ui.core.Configuration.FormatSettings#setCustomCurrencies} and {@link sap.ui.core.Configuration.FormatSettings#addCustomCurrencies}.
591
+ * @param {int} [oFormatOptions.decimals] defines the number of decimal digits
592
+ * @param {string} [oFormatOptions.decimalSeparator] defines the character used as decimal separator.
593
+ * Note: <code>decimalSeparator</code> must always be different from <code>groupingSeparator</code>.
594
+ * @param {null|number|string} [oFormatOptions.emptyString=NaN] since 1.30.0 defines what an empty string
595
+ * is parsed as, and what is formatted as an empty string. The allowed values are "" (empty string),
596
+ * NaN, <code>null</code>, or 0.
597
+ * The 'format' and 'parse' functions are done in a symmetric way. For example, when this
598
+ * parameter is set to NaN, an empty string is parsed as [NaN, undefined], and NaN is
599
+ * formatted as an empty string.
600
+ * @param {int} [oFormatOptions.groupingBaseSize=3] defines the grouping base size in digits if
601
+ * it is different from the grouping size (e.g. Indian grouping)
602
+ * @param {boolean} [oFormatOptions.groupingEnabled=true] defines whether grouping is enabled
603
+ * (grouping separators are shown)
604
+ * @param {string} [oFormatOptions.groupingSeparator] defines the character used as grouping separator.
605
+ * Note: <code>groupingSeparator</code> must always be different from <code>decimalSeparator</code>.
606
+ * @param {int} [oFormatOptions.groupingSize=3] defines the grouping size in digits; the default
607
+ * is <code>3</code>. It must be a positive number.
608
+ * @param {int} [oFormatOptions.maxFractionDigits=99] defines the maximum number of decimal digits
609
+ * @param {int} [oFormatOptions.maxIntegerDigits=99] defines the maximum number of non-decimal digits.
610
+ * If the number exceeds this maximum, e.g. 1e+120, "?" characters are shown instead of digits.
611
+ * @param {int} [oFormatOptions.minFractionDigits=0] defines the minimal number of decimal digits
612
+ * @param {int} [oFormatOptions.minIntegerDigits=1] defines the minimal number of non-decimal digits
613
+ * @param {string} [oFormatOptions.minusSign] defines the used minus symbol
614
+ * @param {boolean} [oFormatOptions.parseAsString=false] since 1.28.2 defines whether to output
615
+ * the string from the parse function in order to keep the precision for big numbers. Numbers
616
+ * in scientific notation are parsed back to standard notation. For example, "5e-3" is parsed
617
+ * to "0.005".
618
+ * @param {string} [oFormatOptions.pattern] CLDR number pattern which is used to format the number
619
+ * @param {string} [oFormatOptions.plusSign] defines the used plus symbol
620
+ * @param {boolean} [oFormatOptions.preserveDecimals=false] Whether {@link #format} preserves
621
+ * decimal digits except trailing zeros in case there are more decimals than the
622
+ * <code>maxFractionDigits</code> format option allows.
623
+ * If decimals are not preserved, the formatted number is rounded to <code>maxFractionDigits</code>.
624
+ * @param {sap.ui.core.format.NumberFormat.RoundingMode} [oFormatOptions.roundingMode=HALF_AWAY_FROM_ZERO]
625
+ * specifies the rounding behavior for discarding the digits after the maximum fraction digits
626
+ * defined by maxFractionDigits. Rounding will only be applied if the passed value is of type <code>number</code>.
627
+ * This can be assigned
628
+ * <ul>
629
+ * <li>by value in {@link sap.ui.core.format.NumberFormat.RoundingMode RoundingMode},</li>
630
+ * <li>via a function that is used for rounding the number and takes two parameters: the number itself, and the number of decimal digits that should be reserved.</li>
631
+ * </ul>
632
+ * @param {int} [oFormatOptions.shortDecimals] defines the number of decimal in the shortened format string. If this isn't specified, the 'decimals' options is used
633
+ * @param {int} [oFormatOptions.shortLimit] only use short number formatting for values above this limit
634
+ * @param {int} [oFormatOptions.shortRefNumber] since 1.40 specifies a number from which the scale factor for 'short' or 'long' style format is generated. The generated scale factor is
635
+ * used for all numbers which are formatted with this format instance. This option has effect only when the option 'style' is set to 'short' or 'long'. This option is by default set
636
+ * with <code>undefined</code> which means the scale factor is selected automatically for each number being formatted.
637
+ * @param {boolean} [oFormatOptions.showMeasure=true] defines whether the currency code/symbol is shown in the formatted string,
638
+ * e.g. true: "1.00 EUR", false: "1.00" for locale "en"
639
+ * If both <code>showMeasure</code> and <code>showNumber</code> are false, an empty string is returned
640
+ * @param {boolean} [oFormatOptions.showNumber=true] defines whether the number is shown as part of the result string,
641
+ * e.g. 1 EUR for locale "en"
642
+ * <code>NumberFormat.getCurrencyInstance({showNumber:true}).format(1, "EUR"); // "1.00 EUR"</code>
643
+ * <code>NumberFormat.getCurrencyInstance({showNumber:false}).format(1, "EUR"); // "EUR"</code>
644
+ * If both <code>showMeasure</code> and <code>showNumber</code> are false, an empty string is returned
645
+ * @param {boolean} [oFormatOptions.showScale=true] since 1.40 specifies whether the scale factor is shown in the formatted number.
646
+ * This option takes effect only when the 'style' options is set to either 'short' or 'long'.
647
+ * @param {boolean} [oFormatOptions.strictGroupingValidation=false] whether the positions of grouping separators are validated. Space characters used as grouping separators are not validated.
648
+ * @param {string} [oFormatOptions.style=standard] defines the style of format. Valid values are
649
+ * 'short, 'long' or 'standard' (based on the CLDR decimalFormat). When set to 'short' or 'long',
650
+ * numbers are formatted into compact forms. When this option is set, the default value of the
651
+ * 'precision' option is set to 2. This can be changed by setting either min/maxFractionDigits,
652
+ * decimals, shortDecimals, or the 'precision' option itself.
653
+ * @param {boolean} [oFormatOptions.trailingCurrencyCode] overrides the global configuration
654
+ * value {@link sap.ui.core.Configuration.FormatSettings#getTrailingCurrencyCode}, which has a
655
+ * default value of <code>true</>.
656
+ * This is ignored if <code>oFormatOptions.currencyCode</code> is set to <code>false</code>,
657
+ * or if <code>oFormatOptions.pattern</code> is supplied.
658
+ * @param {sap.ui.core.Locale} [oLocale] Locale to get the formatter for
659
+ * @return {sap.ui.core.format.NumberFormat} currency instance of the NumberFormat
660
+ * @static
661
+ * @public
662
+ */
663
+ NumberFormat.getCurrencyInstance = function (oFormatOptions, oLocale) {
664
+ var oFormat = this.createInstance(oFormatOptions, oLocale);
665
+ var sContext = oFormat.oOriginalFormatOptions && oFormat.oOriginalFormatOptions.currencyContext;
666
+
667
+ // currency code trailing
668
+ var bShowTrailingCurrencyCode = showTrailingCurrencyCode(oFormat.oOriginalFormatOptions);
669
+
670
+ // prepend "sap-" to pattern params to load (context and short)
671
+ if (bShowTrailingCurrencyCode) {
672
+ sContext = sContext || this.oDefaultCurrencyFormat.style;
673
+ sContext = "sap-" + sContext;
674
+ }
675
+ var oLocaleFormatOptions = this.getLocaleFormatOptions(oFormat.oLocaleData, mNumberType.CURRENCY, sContext);
676
+ oFormat.oFormatOptions = extend({}, this.oDefaultCurrencyFormat, oLocaleFormatOptions, oFormat.oOriginalFormatOptions);
677
+
678
+ // Trailing currency code option
679
+ //
680
+ // The format option "trailingCurrencyCode" is influenced by other options, such as pattern, currencyCode, global config
681
+ // Therefore set it manually without modifying the original oFormatOptions.
682
+ // E.g. the "pattern" option would overwrite this option, even if the "trailingCurrencyCode" option is set
683
+ // oFormatOptions.pattern = "###"
684
+ // oFormatOptions.trailingCurrencyCode = true
685
+ // ->
686
+ // oFormatOptions.trailingCurrencyCode = false
687
+ oFormat.oFormatOptions.trailingCurrencyCode = bShowTrailingCurrencyCode;
688
+ oFormat._defineCustomCurrencySymbols();
689
+ return oFormat;
690
+ };
691
+
692
+ /**
693
+ * Get a unit instance of the NumberFormat, which can be used for formatting units.
694
+ *
695
+ * If no locale is given, the currently configured
696
+ * {@link sap.ui.core.Configuration.FormatSettings#getFormatLocale formatLocale} will be used.
697
+ *
698
+ * <p>
699
+ * This instance has HALF_AWAY_FROM_ZERO set as default rounding mode.
700
+ * Please set the roundingMode property in oFormatOptions to change the
701
+ * default value.
702
+ * </p>
703
+ *
704
+ * @param {object} [oFormatOptions] The option object, which supports the following parameters.
705
+ * If no options are given, default values according to the type and locale settings are used.
706
+ * @param {array} [oFormatOptions.allowedUnits] defines the allowed units for formatting and parsing, e.g. ["size-meter", "volume-liter", ...]
707
+ * @param {Object<string,object>} [oFormatOptions.customUnits] defines a set of custom units, e.g.
708
+ * {"electric-inductance": {
709
+ * "displayName": "henry",
710
+ * "unitPattern-count-one": "{0} H",
711
+ * "unitPattern-count-other": "{0} H",
712
+ * "perUnitPattern": "{0}/H",
713
+ * "decimals": 2,
714
+ * "precision": 4
715
+ * }}
716
+ * @param {int} [oFormatOptions.decimals] defines the number of decimal digits
717
+ * @param {string} [oFormatOptions.decimalSeparator] defines the character used as decimal separator.
718
+ * Note: <code>decimalSeparator</code> must always be different from <code>groupingSeparator</code>.
719
+ * @param {null|number|string} [oFormatOptions.emptyString=NaN] since 1.30.0 defines what an empty string
720
+ * is parsed as, and what is formatted as an empty string. The allowed values are "" (empty string),
721
+ * NaN, <code>null</code>, or 0.
722
+ * The 'format' and 'parse' functions are done in a symmetric way. For example, when this
723
+ * parameter is set to NaN, an empty string is parsed as [NaN, undefined], and NaN is
724
+ * formatted as an empty string.
725
+ * @param {int} [oFormatOptions.groupingBaseSize=3] defines the grouping base size in digits if
726
+ * it is different from the grouping size (e.g. Indian grouping)
727
+ * @param {boolean} [oFormatOptions.groupingEnabled=true] defines whether grouping is enabled
728
+ * (grouping separators are shown)
729
+ * @param {string} [oFormatOptions.groupingSeparator] defines the character used as grouping separator.
730
+ * Note: <code>groupingSeparator</code> must always be different from <code>decimalSeparator</code>.
731
+ * @param {int} [oFormatOptions.groupingSize=3] defines the grouping size in digits; the default
732
+ * is <code>3</code>. It must be a positive number.
733
+ * @param {int} [oFormatOptions.maxFractionDigits=99] defines the maximum number of decimal digits
734
+ * @param {int} [oFormatOptions.maxIntegerDigits=99] defines the maximum number of non-decimal digits.
735
+ * If the number exceeds this maximum, e.g. 1e+120, "?" characters are shown instead of digits.
736
+ * @param {int} [oFormatOptions.minFractionDigits=0] defines the minimal number of decimal digits
737
+ * @param {int} [oFormatOptions.minIntegerDigits=1] defines the minimal number of non-decimal digits
738
+ * @param {string} [oFormatOptions.minusSign] defines the used minus symbol
739
+ * @param {boolean} [oFormatOptions.parseAsString=false] since 1.28.2 defines whether to output
740
+ * the string from the parse function in order to keep the precision for big numbers. Numbers
741
+ * in scientific notation are parsed back to standard notation. For example, "5e-3" is parsed
742
+ * to "0.005".
743
+ * @param {string} [oFormatOptions.pattern] CLDR number pattern which is used to format the number
744
+ * @param {string} [oFormatOptions.plusSign] defines the used plus symbol
745
+ * @param {int} [oFormatOptions.precision] defines the numerical precision; the number of decimals
746
+ * is calculated dependent on the integer digits
747
+ * @param {boolean} [oFormatOptions.preserveDecimals=false] Whether {@link #format} preserves
748
+ * decimal digits except trailing zeros in case there are more decimals than the
749
+ * <code>maxFractionDigits</code> format option allows.
750
+ * If decimals are not preserved, the formatted number is rounded to <code>maxFractionDigits</code>.
751
+ * @param {sap.ui.core.format.NumberFormat.RoundingMode} [oFormatOptions.roundingMode=HALF_AWAY_FROM_ZERO]
752
+ * specifies the rounding behavior for discarding the digits after the maximum fraction digits
753
+ * defined by maxFractionDigits. Rounding will only be applied if the passed value is of type <code>number</code>.
754
+ * This can be assigned
755
+ * <ul>
756
+ * <li>by value in {@link sap.ui.core.format.NumberFormat.RoundingMode RoundingMode},</li>
757
+ * <li>via a function that is used for rounding the number and takes two parameters: the number itself, and the number of decimal digits that should be reserved.</li>
758
+ * </ul>
759
+ * @param {int} [oFormatOptions.shortDecimals] defines the number of decimals in the shortened
760
+ * format string. If this option isn't specified, the 'decimals' option is used instead.
761
+ * @param {int} [oFormatOptions.shortLimit] defines a limit above which only short number formatting is used
762
+ * @param {int} [oFormatOptions.shortRefNumber] since 1.40 specifies a number from which the
763
+ * scale factor for the 'short' or 'long' style format is generated. The generated scale
764
+ * factor is used for all numbers which are formatted with this format instance. This option
765
+ * only takes effect when the 'style' option is set to 'short' or 'long'. This option is
766
+ * set to <code>undefined</code> by default, which means that the scale factor is selected
767
+ * automatically for each number being formatted.
768
+ * @param {boolean} [oFormatOptions.showMeasure=true] defines whether the unit of measure is shown in the formatted string,
769
+ * e.g. for input 1 and "duration-day" true: "1 day", false: "1".
770
+ * If both <code>showMeasure</code> and <code>showNumber</code> are false, an empty string is returned
771
+ * @param {boolean} [oFormatOptions.showNumber=true] defines whether the number is shown as part of the result string,
772
+ * e.g. 1 day for locale "en"
773
+ * <code>NumberFormat.getUnitInstance({showNumber:true}).format(1, "duration-day"); // "1 day"</code>
774
+ * <code>NumberFormat.getUnitInstance({showNumber:false}).format(1, "duration-day"); // "day"</code>
775
+ * e.g. 2 days for locale "en"
776
+ * <code>NumberFormat.getUnitInstance({showNumber:true}).format(2, "duration-day"); // "2 days"</code>
777
+ * <code>NumberFormat.getUnitInstance({showNumber:false}).format(2, "duration-day"); // "days"</code>
778
+ * If both <code>showMeasure</code> and <code>showNumber</code> are false, an empty string is returned
779
+ * @param {boolean} [oFormatOptions.showScale=true] since 1.40 specifies whether the scale factor is shown in the formatted number. This option takes effect only when the 'style' options is set to either 'short' or 'long'.
780
+ * @param {boolean} [oFormatOptions.strictGroupingValidation=false] whether the positions of grouping separators are validated. Space characters used as grouping separators are not validated.
781
+ * @param {string} [oFormatOptions.style=standard] defines the style of format. Valid values are
782
+ * 'short, 'long' or 'standard' (based on the CLDR decimalFormat). When set to 'short' or 'long',
783
+ * numbers are formatted into compact forms. When this option is set, the default value of the
784
+ * 'precision' option is set to 2. This can be changed by setting either min/maxFractionDigits,
785
+ * decimals, shortDecimals, or the 'precision' option itself.
786
+ * @param {sap.ui.core.Locale} [oLocale] Locale to get the formatter for
787
+ * @return {sap.ui.core.format.NumberFormat} unit instance of the NumberFormat
788
+ * @static
789
+ * @public
790
+ */
791
+ NumberFormat.getUnitInstance = function (oFormatOptions, oLocale) {
792
+ var oFormat = this.createInstance(oFormatOptions, oLocale),
793
+ oLocaleFormatOptions = this.getLocaleFormatOptions(oFormat.oLocaleData, mNumberType.UNIT);
794
+ oFormat.oFormatOptions = extend({}, this.oDefaultUnitFormat, oLocaleFormatOptions, oFormat.oOriginalFormatOptions);
795
+ return oFormat;
796
+ };
797
+
798
+ /**
799
+ * Get a percent instance of the NumberFormat, which can be used for formatting.
800
+ *
801
+ * If no locale is given, the currently configured
802
+ * {@link sap.ui.core.Configuration.FormatSettings#getFormatLocale formatLocale} will be used.
803
+ *
804
+ * <p>
805
+ * This instance has HALF_AWAY_FROM_ZERO set as default rounding mode.
806
+ * Please set the roundingMode property in oFormatOptions to change the
807
+ * default value.
808
+ * </p>
809
+ *
810
+ * @param {object} [oFormatOptions] The option object, which supports the following parameters.
811
+ * If no options are given, default values according to the type and locale settings are used.
812
+ * @param {int} [oFormatOptions.decimals] defines the number of decimal digits
813
+ * @param {string} [oFormatOptions.decimalSeparator] defines the character used as decimal separator.
814
+ * Note: <code>decimalSeparator</code> must always be different from <code>groupingSeparator</code>.
815
+ * @param {null|number|string} [oFormatOptions.emptyString=NaN] since 1.30.0 defines what an empty string
816
+ * is parsed as, and what is formatted as an empty string. The allowed values are "" (empty string),
817
+ * NaN, <code>null</code>, or 0.
818
+ * The 'format' and 'parse' functions are done in a symmetric way. For example, when this
819
+ * parameter is set to NaN, an empty string is parsed as NaN, and NaN is formatted as an empty
820
+ * string.
821
+ * @param {int} [oFormatOptions.groupingBaseSize=3] defines the grouping base size in digits if
822
+ * it is different from the grouping size (e.g. Indian grouping)
823
+ * @param {boolean} [oFormatOptions.groupingEnabled=true] defines whether grouping is enabled
824
+ * (grouping separators are shown)
825
+ * @param {string} [oFormatOptions.groupingSeparator] defines the character used as grouping separator.
826
+ * Note: <code>groupingSeparator</code> must always be different from <code>decimalSeparator</code>.
827
+ * @param {int} [oFormatOptions.groupingSize=3] defines the grouping size in digits; the default
828
+ * is <code>3</code>. It must be a positive number.
829
+ * @param {int} [oFormatOptions.maxFractionDigits=99] defines the maximum number of decimal digits
830
+ * @param {int} [oFormatOptions.maxIntegerDigits=99] defines the maximum number of non-decimal digits.
831
+ * If the number exceeds this maximum, e.g. 1e+120, "?" characters are shown instead of digits.
832
+ * @param {int} [oFormatOptions.minFractionDigits=0] defines the minimal number of decimal digits
833
+ * @param {int} [oFormatOptions.minIntegerDigits=1] defines the minimal number of non-decimal digits
834
+ * @param {string} [oFormatOptions.minusSign] defines the used minus symbol
835
+ * @param {boolean} [oFormatOptions.parseAsString=false] since 1.28.2 defines whether to output
836
+ * the string from the parse function in order to keep the precision for big numbers. Numbers
837
+ * in scientific notation are parsed back to standard notation. For example, "5e-3" is parsed
838
+ * to "0.005".
839
+ * @param {string} [oFormatOptions.pattern] CLDR number pattern which is used to format the number
840
+ * @param {string} [oFormatOptions.percentSign] defines the used percent symbol
841
+ * @param {string} [oFormatOptions.plusSign] defines the used plus symbol
842
+ * @param {int} [oFormatOptions.precision] defines the numerical precision; the number of decimals
843
+ * is calculated dependent on the integer digits
844
+ * @param {boolean} [oFormatOptions.preserveDecimals=false] Whether {@link #format} preserves
845
+ * decimal digits except trailing zeros in case there are more decimals than the
846
+ * <code>maxFractionDigits</code> format option allows.
847
+ * If decimals are not preserved, the formatted number is rounded to <code>maxFractionDigits</code>.
848
+ * @param {sap.ui.core.format.NumberFormat.RoundingMode} [oFormatOptions.roundingMode=HALF_AWAY_FROM_ZERO]
849
+ * specifies the rounding behavior for discarding the digits after the maximum fraction digits
850
+ * defined by maxFractionDigits. Rounding will only be applied if the passed value is of type <code>number</code>.
851
+ * This can be assigned
852
+ * <ul>
853
+ * <li>by value in {@link sap.ui.core.format.NumberFormat.RoundingMode RoundingMode},</li>
854
+ * <li>via a function that is used for rounding the number and takes two parameters: the number itself, and the number of decimal digits that should be reserved.</li>
855
+ * </ul>
856
+ * @param {int} [oFormatOptions.shortDecimals] defines the number of decimal in the shortened format string. If this isn't specified, the 'decimals' options is used
857
+ * @param {int} [oFormatOptions.shortLimit] only use short number formatting for values above this limit
858
+ * @param {int} [oFormatOptions.shortRefNumber] since 1.40 specifies a number from which the scale factor for 'short' or 'long' style format is generated. The generated scale factor is
859
+ * used for all numbers which are formatted with this format instance. This option has effect only when the option 'style' is set to 'short' or 'long'. This option is by default set
860
+ * with <code>undefined</code> which means the scale factor is selected automatically for each number being formatted.
861
+ * @param {boolean} [oFormatOptions.showScale=true] since 1.40 specifies whether the scale factor is shown in the formatted number. This option takes effect only when the 'style' options is set to either 'short' or 'long'.
862
+ * @param {boolean} [oFormatOptions.strictGroupingValidation=false] whether the positions of grouping separators are validated. Space characters used as grouping separators are not validated.
863
+ * @param {string} [oFormatOptions.style=standard] defines the style of format. Valid values are
864
+ * 'short, 'long' or 'standard' (based on the CLDR decimalFormat). When set to 'short' or 'long',
865
+ * numbers are formatted into compact forms. When this option is set, the default value of the
866
+ * 'precision' option is set to 2. This can be changed by setting either min/maxFractionDigits,
867
+ * decimals, shortDecimals, or the 'precision' option itself.
868
+ * @param {sap.ui.core.Locale} [oLocale] Locale to get the formatter for
869
+ * @return {sap.ui.core.format.NumberFormat} percentage instance of the NumberFormat
870
+ * @static
871
+ * @public
872
+ */
873
+ NumberFormat.getPercentInstance = function (oFormatOptions, oLocale) {
874
+ var oFormat = this.createInstance(oFormatOptions, oLocale),
875
+ oLocaleFormatOptions = this.getLocaleFormatOptions(oFormat.oLocaleData, mNumberType.PERCENT);
876
+ oFormat.oFormatOptions = extend({}, this.oDefaultPercentFormat, oLocaleFormatOptions, oFormat.oOriginalFormatOptions);
877
+ return oFormat;
878
+ };
879
+
880
+ /**
881
+ * Create an instance of the NumberFormat.
882
+ *
883
+ * @param {object} [oFormatOptions] Object which defines the format options
884
+ * @return {sap.ui.core.format.NumberFormat} integer instance of the NumberFormat
885
+ * @static
886
+ * @private
887
+ */
888
+ NumberFormat.createInstance = function (oFormatOptions, oLocale) {
889
+ var oFormat = Object.create(this.prototype),
890
+ oPatternOptions;
891
+ if (oFormatOptions instanceof Locale) {
892
+ oLocale = oFormatOptions;
893
+ oFormatOptions = undefined;
894
+ }
895
+ if (!oLocale) {
896
+ oLocale = Configuration.getFormatSettings().getFormatLocale();
897
+ }
898
+ oFormat.oLocale = oLocale;
899
+ oFormat.oLocaleData = LocaleData.getInstance(oLocale);
900
+ oFormat.oOriginalFormatOptions = oFormatOptions;
901
+
902
+ // If a pattern is defined in the format option, parse it and add options
903
+ if (oFormatOptions) {
904
+ if (oFormatOptions.pattern) {
905
+ oPatternOptions = this.parseNumberPattern(oFormatOptions.pattern);
906
+ Object.keys(oPatternOptions).forEach(function (sName) {
907
+ oFormatOptions[sName] = oPatternOptions[sName];
908
+ });
909
+ }
910
+ if (oFormatOptions.emptyString !== undefined) {
911
+ assert(oFormatOptions.emptyString === "" || oFormatOptions.emptyString === 0 || oFormatOptions.emptyString === null
912
+ // eslint-disable-next-line no-self-compare -- check if it's NaN (only NaN doesn't equal to itself)
913
+ || oFormatOptions.emptyString !== oFormatOptions.emptyString, "The format option 'emptyString' must be either '', 0, null, or NaN");
914
+ }
915
+ }
916
+ return oFormat;
917
+ };
918
+
919
+ /**
920
+ * Returns a default unit format/parse pattern for the given unit short name.
921
+ * The returned pattern can then be used for custom units, for example as a <code>unitPattern-count-other</code> pattern.
922
+ * The <code>unitPattern-count-other</code> pattern is then used by NumberFormat instances as a fallback in case
923
+ * no other patterns are defined, see the below example:
924
+ *
925
+ * <pre>
926
+ * var oFormat = NumberFormat.getUnitInstance({
927
+ * "customUnits": {
928
+ * "myUnit": {
929
+ * "unitPattern-count-other": NumberFormat.getDefaultUnitPattern("Bottles"); // returns "{0} Bottles"
930
+ * }
931
+ * }
932
+ * });
933
+ * oFormat.format(1234, "myUnit"); // returns "1.234,00 Bottles"
934
+ * </pre>
935
+ *
936
+ * @param {string} sShortName the short name of the unit used in the created pattern
937
+ * @returns {string} a pattern, which can be used for formatting and parsing a custom unit of measure
938
+ * @private
939
+ * @ui5-restricted sap.ui.model.odata.type
940
+ */
941
+ NumberFormat.getDefaultUnitPattern = function (sShortName) {
942
+ return "{0} " + sShortName;
943
+ };
944
+
945
+ /**
946
+ * Get locale dependent default format options.
947
+ *
948
+ * @static
949
+ */
950
+ NumberFormat.getLocaleFormatOptions = function (oLocaleData, iType, sContext) {
951
+ var oLocaleFormatOptions, sNumberPattern;
952
+ switch (iType) {
953
+ case mNumberType.PERCENT:
954
+ sNumberPattern = oLocaleData.getPercentPattern();
955
+ break;
956
+ case mNumberType.CURRENCY:
957
+ sNumberPattern = oLocaleData.getCurrencyPattern(sContext);
958
+ break;
959
+ case mNumberType.UNIT:
960
+ sNumberPattern = oLocaleData.getDecimalPattern();
961
+ break;
962
+ default:
963
+ sNumberPattern = oLocaleData.getDecimalPattern();
964
+ }
965
+ oLocaleFormatOptions = this.parseNumberPattern(sNumberPattern);
966
+ oLocaleFormatOptions.plusSign = oLocaleData.getNumberSymbol("plusSign");
967
+ oLocaleFormatOptions.minusSign = oLocaleData.getNumberSymbol("minusSign");
968
+ oLocaleFormatOptions.decimalSeparator = oLocaleData.getNumberSymbol("decimal");
969
+ oLocaleFormatOptions.groupingSeparator = oLocaleData.getNumberSymbol("group");
970
+ oLocaleFormatOptions.percentSign = oLocaleData.getNumberSymbol("percentSign");
971
+ oLocaleFormatOptions.pattern = sNumberPattern;
972
+
973
+ // Some options need to be overridden to stay compatible with the formatting defaults
974
+ // before pattern parsing was added to the NumberFormat
975
+ switch (iType) {
976
+ case mNumberType.UNIT:
977
+ case mNumberType.FLOAT:
978
+ case mNumberType.PERCENT:
979
+ // Unlimited fraction digits for float and percent values
980
+ oLocaleFormatOptions.minFractionDigits = 0;
981
+ oLocaleFormatOptions.maxFractionDigits = 99;
982
+ break;
983
+ case mNumberType.INTEGER:
984
+ // No fraction digits and no grouping for integer values
985
+ oLocaleFormatOptions.minFractionDigits = 0;
986
+ oLocaleFormatOptions.maxFractionDigits = 0;
987
+ oLocaleFormatOptions.groupingEnabled = false;
988
+ break;
989
+ case mNumberType.CURRENCY:
990
+ // reset the iMin/MaxFractionDigits because the extracted info from the pattern doesn't contain the currency specific info.
991
+ oLocaleFormatOptions.minFractionDigits = undefined;
992
+ oLocaleFormatOptions.maxFractionDigits = undefined;
993
+ break;
994
+ }
995
+ return oLocaleFormatOptions;
996
+ };
997
+
998
+ /**
999
+ * Get digit information from number format.
1000
+ *
1001
+ * @static
1002
+ */
1003
+ NumberFormat.parseNumberPattern = function (sFormatString) {
1004
+ var iMinIntegerDigits = 0,
1005
+ iMinFractionDigits = 0,
1006
+ iMaxFractionDigits = 0,
1007
+ bGroupingEnabled = false,
1008
+ iGroupSize = 0,
1009
+ iBaseGroupSize = 0,
1010
+ iSeparatorPos = sFormatString.indexOf(";"),
1011
+ mSection = {
1012
+ Integer: 0,
1013
+ Fraction: 1
1014
+ },
1015
+ iSection = mSection.Integer;
1016
+
1017
+ // The sFormatString can be ¤#,##0.00;(¤#,##0.00). If the whole string is parsed, the wrong
1018
+ // iMinFractionDigits and iMaxFractionDigits are wrong.
1019
+ // Only the sub string before ';' is taken into consideration.
1020
+ if (iSeparatorPos !== -1) {
1021
+ sFormatString = sFormatString.substring(0, iSeparatorPos);
1022
+ }
1023
+ for (var i = 0; i < sFormatString.length; i++) {
1024
+ var sCharacter = sFormatString[i];
1025
+ switch (sCharacter) {
1026
+ case ",":
1027
+ if (bGroupingEnabled) {
1028
+ iGroupSize = iBaseGroupSize;
1029
+ iBaseGroupSize = 0;
1030
+ }
1031
+ bGroupingEnabled = true;
1032
+ break;
1033
+ case ".":
1034
+ iSection = mSection.Fraction;
1035
+ break;
1036
+ case "0":
1037
+ if (iSection === mSection.Integer) {
1038
+ iMinIntegerDigits++;
1039
+ if (bGroupingEnabled) {
1040
+ iBaseGroupSize++;
1041
+ }
1042
+ } else {
1043
+ iMinFractionDigits++;
1044
+ iMaxFractionDigits++;
1045
+ }
1046
+ break;
1047
+ case "#":
1048
+ if (iSection === mSection.Integer) {
1049
+ if (bGroupingEnabled) {
1050
+ iBaseGroupSize++;
1051
+ }
1052
+ } else {
1053
+ iMaxFractionDigits++;
1054
+ }
1055
+ break;
1056
+ }
1057
+ }
1058
+ if (!iGroupSize) {
1059
+ iGroupSize = iBaseGroupSize;
1060
+ iBaseGroupSize = 0;
1061
+ }
1062
+ return {
1063
+ minIntegerDigits: iMinIntegerDigits,
1064
+ minFractionDigits: iMinFractionDigits,
1065
+ maxFractionDigits: iMaxFractionDigits,
1066
+ groupingEnabled: bGroupingEnabled,
1067
+ groupingSize: iGroupSize,
1068
+ groupingBaseSize: iBaseGroupSize
1069
+ };
1070
+ };
1071
+
1072
+ /**
1073
+ * Compiles a map <code>this.mKnownCurrencySymbols</code>
1074
+ * of all custom currency symbols. Symbols are either defined in
1075
+ * the custom currency object itself, or are looked up on the
1076
+ * LocaleData in case an ISO Code is given.
1077
+ *
1078
+ * It also checks if there are duplicated symbols defined,
1079
+ * which lead to an ambiguous parse result.
1080
+ *
1081
+ * In case there are custom currencies defined on instance level,
1082
+ * it also compiles a map <code>this.mKnownCurrencyCodes</code>
1083
+ * of custom currency codes.
1084
+ *
1085
+ * The function is only used by the Currency formatting.
1086
+ * @private
1087
+ */
1088
+ NumberFormat.prototype._defineCustomCurrencySymbols = function () {
1089
+ var oOptions = this.oFormatOptions;
1090
+ var mCurrencySymbols = this.oLocaleData.getCurrencySymbols();
1091
+ var fnFindDuplicates = function (mSymbols, mResult) {
1092
+ var aUniqueSymbols = [];
1093
+ var sSymbol;
1094
+ for (var sKey in mSymbols) {
1095
+ sSymbol = mSymbols[sKey];
1096
+ if (aUniqueSymbols.indexOf(sSymbol) === -1) {
1097
+ aUniqueSymbols.push(sSymbol);
1098
+ } else if (sSymbol !== undefined) {
1099
+ // Duplicated symbol found
1100
+ mResult[sSymbol] = true;
1101
+ Log.error("Symbol '" + sSymbol + "' is defined multiple times in custom currencies.", undefined, "NumberFormat");
1102
+ }
1103
+ }
1104
+ };
1105
+
1106
+ // process custom currencies on instance-level
1107
+ if (oOptions.customCurrencies && typeof oOptions.customCurrencies === "object") {
1108
+ this.mKnownCurrencySymbols = {};
1109
+ this.mKnownCurrencyCodes = {};
1110
+
1111
+ // get all relevant symbols for custom currencies
1112
+ Object.keys(oOptions.customCurrencies).forEach(function (sKey) {
1113
+ if (oOptions.customCurrencies[sKey].symbol) {
1114
+ this.mKnownCurrencySymbols[sKey] = oOptions.customCurrencies[sKey].symbol;
1115
+ } else {
1116
+ // if no symbol is defined, we make a look up into the locale data with the given isoCode
1117
+ var sIsoCode = oOptions.customCurrencies[sKey].isoCode;
1118
+ if (sIsoCode) {
1119
+ this.mKnownCurrencySymbols[sKey] = mCurrencySymbols[sIsoCode];
1120
+ }
1121
+ }
1122
+
1123
+ // In case no symbol is found during parsing,
1124
+ // we take the custom currency key itself
1125
+ this.mKnownCurrencyCodes[sKey] = sKey;
1126
+ }.bind(this));
1127
+ } else {
1128
+ // find duplicated symbols in global config/CLDR
1129
+ // mCurrencySymbols
1130
+ this.mKnownCurrencySymbols = mCurrencySymbols;
1131
+ this.mKnownCurrencyCodes = this.oLocaleData.getCustomCurrencyCodes();
1132
+ }
1133
+
1134
+ // Find duplicated symbols defined in custom currencies
1135
+ this.mDuplicatedSymbols = {};
1136
+ fnFindDuplicates(this.mKnownCurrencySymbols, this.mDuplicatedSymbols);
1137
+ };
1138
+
1139
+ /**
1140
+ * Removes trailing zero decimals
1141
+ * @param {string} sNumber the number, e.g. "1.23000"
1142
+ * @param {number} minDecimalsPreserved the minimum decimals preserved, e.g. 3
1143
+ * @returns {string} the number with stripped trailing zero decimals, e.g. "1.230"
1144
+ */
1145
+ function stripTrailingZeroDecimals(sNumber, minDecimalsPreserved) {
1146
+ if (sNumber.indexOf(".") >= 0 && !isScientificNotation(sNumber) && sNumber.endsWith("0")) {
1147
+ var iFractionDigitsLength = sNumber.length - sNumber.lastIndexOf(".") - 1;
1148
+ var iFractionsToRemove = iFractionDigitsLength - minDecimalsPreserved;
1149
+ if (iFractionsToRemove > 0) {
1150
+ while (sNumber.endsWith("0") && iFractionsToRemove-- > 0) {
1151
+ sNumber = sNumber.substring(0, sNumber.length - 1);
1152
+ }
1153
+ if (sNumber.endsWith(".")) {
1154
+ sNumber = sNumber.substring(0, sNumber.length - 1);
1155
+ }
1156
+ }
1157
+ }
1158
+ return sNumber;
1159
+ }
1160
+
1161
+ /**
1162
+ * Applies the grouping to the given integer part and returns it.
1163
+ *
1164
+ * @param {string} sIntegerPart
1165
+ * A string with the integer value, e.g. "1234567"
1166
+ * @param {object} oOptions
1167
+ * The format options
1168
+ * @param {int} oOptions.groupingBaseSize
1169
+ * The grouping base size in digits if it is different from the grouping size (e.g. Indian grouping)
1170
+ * @param {string} oOptions.groupingSeparator
1171
+ * The character used as grouping separator
1172
+ * @param {int} oOptions.groupingSize
1173
+ * The grouping size in digits
1174
+ * @returns {string}
1175
+ * The integer part with grouping, e.g. "1.234.567" for locale de-DE
1176
+ * @private
1177
+ */
1178
+ function applyGrouping(sIntegerPart, oOptions) {
1179
+ var iGroupSize = oOptions.groupingSize,
1180
+ iBaseGroupSize = oOptions.groupingBaseSize || iGroupSize,
1181
+ iLength = sIntegerPart.length,
1182
+ iPosition = Math.max(iLength - iBaseGroupSize, 0) % iGroupSize || iGroupSize,
1183
+ sGroupedIntegerPart = sIntegerPart.slice(0, iPosition);
1184
+ while (iLength - iPosition >= iBaseGroupSize) {
1185
+ sGroupedIntegerPart += oOptions.groupingSeparator;
1186
+ sGroupedIntegerPart += sIntegerPart.slice(iPosition, iPosition + iGroupSize);
1187
+ iPosition += iGroupSize;
1188
+ }
1189
+ sGroupedIntegerPart += sIntegerPart.slice(iPosition, iLength);
1190
+ return sGroupedIntegerPart;
1191
+ }
1192
+
1193
+ /**
1194
+ * Format a number according to the given format options.
1195
+ *
1196
+ * @param {number|array} vValue the number to format or an array which contains the number to format and the sMeasure parameter
1197
+ * @param {string} [sMeasure] an optional unit which has an impact on formatting currencies and units
1198
+ * @return {string} the formatted output value
1199
+ * @public
1200
+ */
1201
+ NumberFormat.prototype.format = function (vValue, sMeasure) {
1202
+ if (Array.isArray(vValue)) {
1203
+ sMeasure = vValue[1];
1204
+ vValue = vValue[0];
1205
+ }
1206
+ var sIntegerPart = "",
1207
+ sFractionPart = "",
1208
+ sGroupedIntegerPart = "",
1209
+ sResult = "",
1210
+ sNumber = "",
1211
+ sPattern = "",
1212
+ bNegative = vValue < 0,
1213
+ iDotPos = -1,
1214
+ oOptions = Object.assign({}, this.oFormatOptions),
1215
+ oOrigOptions = this.oOriginalFormatOptions,
1216
+ bIndianCurrency = oOptions.type === mNumberType.CURRENCY && sMeasure === "INR" && this.oLocale.getLanguage() === "en" && this.oLocale.getRegion() === "IN",
1217
+ aPatternParts,
1218
+ oShortFormat,
1219
+ nShortRefNumber,
1220
+ sPluralCategory,
1221
+ mUnitPatterns,
1222
+ sLookupMeasure,
1223
+ bValueIsNullOrUndefined = vValue === undefined || vValue === null;
1224
+ if (oOptions.groupingEnabled && oOptions.groupingSize <= 0) {
1225
+ // invalid grouping size specified
1226
+ Log.error("Grouping requires the 'groupingSize' format option to be a positive number, but it is '" + oOptions.groupingSize + "' instead.");
1227
+ return "";
1228
+ }
1229
+
1230
+ // emptyString is only relevant for the number part (vValue)
1231
+ if (oOptions.showNumber && (vValue === oOptions.emptyString || isNaN(vValue) && isNaN(oOptions.emptyString))) {
1232
+ // if the value equals the 'emptyString' format option, return empty string.
1233
+ // the NaN case has to be checked by using isNaN because NaN !== NaN
1234
+ return "";
1235
+ }
1236
+
1237
+ // sMeasure must be a string if defined
1238
+ if (sMeasure !== undefined && sMeasure !== null && typeof sMeasure !== "string" && !(sMeasure instanceof String)) {
1239
+ return "";
1240
+ }
1241
+ if (!oOptions.showNumber && !sMeasure) {
1242
+ return "";
1243
+ }
1244
+
1245
+ // cannot create number from null or undefined
1246
+ if (bValueIsNullOrUndefined && (!sMeasure || !oOptions.showMeasure || oOptions.showNumber)) {
1247
+ return "";
1248
+ }
1249
+
1250
+ // If custom currencies are defined, we exclusively accept the defined ones,
1251
+ // other currencies are ignored
1252
+ if (sMeasure && oOptions.customCurrencies && !oOptions.customCurrencies[sMeasure]) {
1253
+ Log.error("Currency '" + sMeasure + "' is unknown.");
1254
+ return "";
1255
+ }
1256
+ if (!oOptions.showNumber && !oOptions.showMeasure) {
1257
+ return "";
1258
+ }
1259
+
1260
+ // Recognize the correct unit definition (either custom unit or CLDR unit)
1261
+ if (sMeasure && oOptions.type === mNumberType.UNIT) {
1262
+ if (oOptions.customUnits && typeof oOptions.customUnits === "object") {
1263
+ //custom units are exclusive (no fallback to LocaleData)
1264
+ mUnitPatterns = oOptions.customUnits[sMeasure];
1265
+ } else {
1266
+ //check if there is a unit mapping for the given unit
1267
+ sLookupMeasure = this.oLocaleData.getUnitFromMapping(sMeasure) || sMeasure;
1268
+ mUnitPatterns = this.oLocaleData.getUnitFormat(sLookupMeasure);
1269
+ }
1270
+ if (oOptions.showMeasure) {
1271
+ // a list of allowed unit types is given, so we check if the given measure is ok
1272
+ var bUnitTypeAllowed = !oOptions.allowedUnits || oOptions.allowedUnits.indexOf(sMeasure) >= 0;
1273
+ if (!bUnitTypeAllowed) {
1274
+ return "";
1275
+ }
1276
+ }
1277
+ if (!mUnitPatterns && !oOptions.showNumber) {
1278
+ return this._addOriginInfo(sMeasure);
1279
+ }
1280
+
1281
+ // either take the decimals/precision on the custom units or fallback to the given format-options
1282
+ oOptions.decimals = mUnitPatterns && typeof mUnitPatterns.decimals === "number" && mUnitPatterns.decimals >= 0 ? mUnitPatterns.decimals : oOptions.decimals;
1283
+ oOptions.decimals = NumberFormat.getMaximalDecimals(oOptions);
1284
+ oOptions.precision = mUnitPatterns && typeof mUnitPatterns.precision === "number" && mUnitPatterns.precision >= 0 ? mUnitPatterns.precision : oOptions.precision;
1285
+ }
1286
+ if (oOptions.type == mNumberType.CURRENCY) {
1287
+ // Make sure the "trailingCurrencyCode" mode is only used on currency codes:
1288
+ // The "customCurrencies" format option takes precedence over CLDR and global configuration. If the given measure isn't found
1289
+ // there, we already return an empty string in the check above (look for error log 'Currency "xy" is unknown').
1290
+ // "mKnownCurrencyCodes" either contains the keys of the "customCurrencies" format option or the accumulated currency codes
1291
+ // from CLDR and global configuration. If the given measure isn't found there and does not have the three letter ISO code format,
1292
+ // it shouldn't be formatted with the "trailingCurrencyCode" pattern.
1293
+ if (sMeasure && oOptions.trailingCurrencyCode) {
1294
+ if (!this.mKnownCurrencyCodes[sMeasure] && !/(^[A-Z]{3}$)/.test(sMeasure)) {
1295
+ oOptions.trailingCurrencyCode = false;
1296
+ // Revert to non-"sap-" prefixed (trailing-currency-code) pattern. Also see code in getCurrencyInstance()
1297
+ oOptions.pattern = this.oLocaleData.getCurrencyPattern(oOptions.currencyContext);
1298
+ }
1299
+ }
1300
+ if (!oOptions.showNumber) {
1301
+ // if the number should not be shown, return the sMeasure part standalone, without anything number specific
1302
+ if (!oOptions.currencyCode) {
1303
+ var sSymbol;
1304
+ // custom currencies provided
1305
+ if (oOptions.customCurrencies && typeof oOptions.customCurrencies === "object") {
1306
+ // the custom currency symbol map was preprocessed on instance creation
1307
+ sSymbol = this.mKnownCurrencySymbols[sMeasure];
1308
+ } else {
1309
+ sSymbol = this.oLocaleData.getCurrencySymbol(sMeasure);
1310
+ }
1311
+ if (sSymbol && sSymbol !== sMeasure) {
1312
+ sMeasure = sSymbol;
1313
+ }
1314
+ }
1315
+ return sMeasure;
1316
+ }
1317
+ // if decimals are given on a custom currency, they have precedence over the decimals defined on the format options
1318
+ if (oOptions.customCurrencies && oOptions.customCurrencies[sMeasure]) {
1319
+ // we either take the custom decimals or use decimals defined in the format-options
1320
+ // we check for undefined here, since 0 is an accepted value
1321
+ oOptions.decimals = oOptions.customCurrencies[sMeasure].decimals !== undefined ? oOptions.customCurrencies[sMeasure].decimals : oOptions.decimals;
1322
+ oOptions.decimals = NumberFormat.getMaximalDecimals(oOptions);
1323
+ }
1324
+ }
1325
+
1326
+ // set fraction digits based on the given or derived decimals
1327
+ if (oOptions.decimals !== undefined) {
1328
+ oOptions.minFractionDigits = oOptions.decimals;
1329
+ oOptions.maxFractionDigits = oOptions.decimals;
1330
+ }
1331
+ if (oOptions.shortLimit === undefined || Math.abs(vValue) >= oOptions.shortLimit) {
1332
+ nShortRefNumber = oOptions.shortRefNumber === undefined ? vValue : oOptions.shortRefNumber;
1333
+ oShortFormat = getShortenedFormat(nShortRefNumber, oOptions, this.oLocaleData, bIndianCurrency);
1334
+ if (oShortFormat && oShortFormat.formatString != "0") {
1335
+ vValue = vValue / oShortFormat.magnitude;
1336
+ // If shortDecimals is defined, override the fractionDigits
1337
+ if (oOptions.shortDecimals !== undefined) {
1338
+ oOptions.minFractionDigits = oOptions.shortDecimals;
1339
+ oOptions.maxFractionDigits = oOptions.shortDecimals;
1340
+ } else {
1341
+ if (oOrigOptions.minFractionDigits === undefined && oOrigOptions.maxFractionDigits === undefined && oOrigOptions.decimals === undefined && oOrigOptions.precision === undefined && oOrigOptions.pattern === undefined) {
1342
+ // if none of the options which can affect the decimal digits is set, the default precision is set to 2
1343
+ oOptions.precision = 2;
1344
+ // set the default min/maxFractionDigits after setting the default precision
1345
+ oOptions.minFractionDigits = 0;
1346
+ oOptions.maxFractionDigits = 99;
1347
+ }
1348
+ if (oOrigOptions.maxFractionDigits === undefined && oOrigOptions.decimals === undefined) {
1349
+ // overwrite the default setting of Integer instance because
1350
+ // Integer with short format could have fraction part
1351
+ oOptions.maxFractionDigits = 99;
1352
+ }
1353
+ }
1354
+
1355
+ // Always use HALF_AWAY_FROM_ZERO for short formats
1356
+ oOptions.roundingMode = NumberFormat.RoundingMode.HALF_AWAY_FROM_ZERO;
1357
+ }
1358
+ }
1359
+
1360
+ // Must be done after calculating the short value, as it depends on the value
1361
+ // If short format is enabled or the precision isn't ignored, take the precision
1362
+ // option into consideration
1363
+ if ((oShortFormat || !oOptions.ignorePrecision) && oOptions.precision !== undefined) {
1364
+ // the number of decimal digits is calculated using (precision - number of integer digits)
1365
+ // the maxFractionDigits is adapted if the calculated value is smaller than the maxFractionDigits
1366
+ oOptions.maxFractionDigits = Math.min(oOptions.maxFractionDigits, getDecimals(vValue, oOptions.precision));
1367
+
1368
+ // if the minFractionDigits is greater than the maxFractionDigits, adapt the minFractionDigits with
1369
+ // the same value of the maxFractionDigits
1370
+ oOptions.minFractionDigits = Math.min(oOptions.minFractionDigits, oOptions.maxFractionDigits);
1371
+ }
1372
+ if (oOptions.type == mNumberType.PERCENT) {
1373
+ vValue = NumberFormat._shiftDecimalPoint(vValue, 2);
1374
+ }
1375
+
1376
+ //handle measure
1377
+ if (oOptions.type == mNumberType.CURRENCY) {
1378
+ var iDigits = this.oLocaleData.getCurrencyDigits(sMeasure);
1379
+
1380
+ // decimals might be undefined, yet 0 is accepted of course
1381
+ if (oOptions.customCurrencies && oOptions.customCurrencies[sMeasure] && oOptions.customCurrencies[sMeasure].decimals !== undefined) {
1382
+ iDigits = oOptions.customCurrencies[sMeasure].decimals;
1383
+ }
1384
+ if (oOptions.maxFractionDigits === undefined) {
1385
+ oOptions.maxFractionDigits = iDigits;
1386
+ }
1387
+ if (oOptions.minFractionDigits === undefined) {
1388
+ oOptions.minFractionDigits = iDigits;
1389
+ }
1390
+ }
1391
+
1392
+ // Rounding the value with oOptions.maxFractionDigits and oOptions.roundingMode.
1393
+ //
1394
+ // If the number of fraction digits are equal or less than oOptions.maxFractionDigits, the
1395
+ // number isn't changed. After this operation, the number of fraction digits is
1396
+ // equal or less than oOptions.maxFractionDigits.
1397
+ if (typeof vValue === "number" && !oOptions.preserveDecimals) {
1398
+ vValue = rounding(vValue, oOptions.maxFractionDigits, oOptions.roundingMode);
1399
+ }
1400
+
1401
+ // No sign on zero values
1402
+ if (vValue == 0) {
1403
+ bNegative = false;
1404
+ }
1405
+
1406
+ // strip of trailing zeros in decimals
1407
+ // "1000.00" -> "1000" (maxFractionDigits: 0)
1408
+ // "1000.0" -> "1000.0" (maxFractionDigits: 1)
1409
+ // the intention behind preserveDecimals is to keep the precision in the number.
1410
+ // Trailing zero decimals are not required for the precision (e.g. 1,23000000 EUR).
1411
+ // These zeros are cut off until maxFractionDigits is reached to be backward compatible.
1412
+ // If more trailing decimal zeros are required the option maxFractionDigits can be increased.
1413
+ // Note: default maxFractionDigits for Unit and Float is 99.
1414
+ if (oOptions.preserveDecimals && (typeof vValue === "string" || vValue instanceof String)) {
1415
+ vValue = stripTrailingZeroDecimals(vValue, oOptions.maxFractionDigits);
1416
+ }
1417
+ if (!bValueIsNullOrUndefined) {
1418
+ sNumber = LocaleData.convertToDecimal(vValue);
1419
+ }
1420
+ if (sNumber == "NaN") {
1421
+ return sNumber;
1422
+ }
1423
+
1424
+ // if number is negative remove minus
1425
+ if (bNegative) {
1426
+ sNumber = sNumber.substr(1);
1427
+ }
1428
+
1429
+ // if number contains fraction, extract it
1430
+ iDotPos = sNumber.indexOf(".");
1431
+ if (iDotPos > -1) {
1432
+ sIntegerPart = sNumber.substr(0, iDotPos);
1433
+ sFractionPart = sNumber.substr(iDotPos + 1);
1434
+ } else {
1435
+ sIntegerPart = sNumber;
1436
+ }
1437
+
1438
+ // integer part length
1439
+ if (sIntegerPart.length < oOptions.minIntegerDigits) {
1440
+ sIntegerPart = sIntegerPart.padStart(oOptions.minIntegerDigits, "0");
1441
+ } else if (sIntegerPart.length > oOptions.maxIntegerDigits) {
1442
+ sIntegerPart = "".padStart(oOptions.maxIntegerDigits, "?");
1443
+ }
1444
+
1445
+ // fraction part length
1446
+ if (sFractionPart.length < oOptions.minFractionDigits) {
1447
+ sFractionPart = sFractionPart.padEnd(oOptions.minFractionDigits, "0");
1448
+ } else if (sFractionPart.length > oOptions.maxFractionDigits && !oOptions.preserveDecimals) {
1449
+ sFractionPart = sFractionPart.substr(0, oOptions.maxFractionDigits);
1450
+ }
1451
+ if (oOptions.type === mNumberType.UNIT && !oOptions.showNumber) {
1452
+ if (mUnitPatterns) {
1453
+ // the plural category of a unit pattern is determined for the complete number, maybe as compact
1454
+ // notation, e.g. "1.2M" must check "1.2c6"
1455
+ sPluralCategory = this._getPluralCategory(sIntegerPart, sFractionPart, oShortFormat);
1456
+ sPattern = mUnitPatterns["unitPattern-count-" + sPluralCategory];
1457
+ if (!sPattern) {
1458
+ sPattern = mUnitPatterns["unitPattern-count-other"];
1459
+ }
1460
+ if (!sPattern) {
1461
+ return this._addOriginInfo(sMeasure);
1462
+ }
1463
+ // fallback to "other" pattern if pattern does not include the number placeholder
1464
+ if (sPluralCategory !== "other" && sPattern.indexOf("{0}") === -1) {
1465
+ sPattern = mUnitPatterns["unitPattern-count-other"];
1466
+ if (!sPattern) {
1467
+ return this._addOriginInfo(sMeasure);
1468
+ }
1469
+ }
1470
+
1471
+ // with the current CLDR data this is not possible
1472
+ // but if there is the case when there is no number placeholder, the number cannot be separated from the unit
1473
+ // therefore it does not make sense to return a pattern which contains the number part in any other form as part of the pattern
1474
+ if (sPattern.indexOf("{0}") === -1) {
1475
+ Log.warning("Cannot separate the number from the unit because unitPattern-count-other '" + sPattern + "' does not include the number placeholder '{0}' for unit '" + sMeasure + "'");
1476
+ } else {
1477
+ return this._addOriginInfo(sPattern.replace("{0}", "").trim());
1478
+ }
1479
+ }
1480
+ }
1481
+
1482
+ // grouping
1483
+ if (oOptions.groupingEnabled) {
1484
+ sGroupedIntegerPart = applyGrouping(sIntegerPart, oOptions);
1485
+ } else {
1486
+ sGroupedIntegerPart = sIntegerPart;
1487
+ }
1488
+
1489
+ // combine
1490
+ if (bNegative) {
1491
+ sResult = oOptions.minusSign;
1492
+ }
1493
+ sResult += sGroupedIntegerPart;
1494
+ if (sFractionPart) {
1495
+ sResult += oOptions.decimalSeparator + sFractionPart;
1496
+ }
1497
+ if (oShortFormat && oShortFormat.formatString && oOptions.showScale && oOptions.type !== mNumberType.CURRENCY) {
1498
+ // Get correct format string based on actual decimal/fraction digits
1499
+ // the plural category of a compact number is determined for the reduced short number without compact
1500
+ // notation, e.g. "1.2M" must check "1.2" (see CLDR "decimalFormat-short" and "decimalFormat-long")
1501
+ sPluralCategory = this._getPluralCategory(sIntegerPart, sFractionPart);
1502
+ oShortFormat.formatString = this.oLocaleData.getDecimalFormat(oOptions.style, oShortFormat.key, sPluralCategory);
1503
+ //inject formatted shortValue in the formatString
1504
+ sResult = oShortFormat.formatString.replace(oShortFormat.valueSubString, sResult);
1505
+ //formatString may contain '.' (quoted to differentiate them decimal separator)
1506
+ //which must be replaced with .
1507
+ sResult = sResult.replace(/'.'/g, ".");
1508
+ }
1509
+ if (oOptions.type === mNumberType.CURRENCY) {
1510
+ sPattern = oOptions.pattern;
1511
+ if (oShortFormat && oShortFormat.formatString && oOptions.showScale) {
1512
+ var sStyle;
1513
+
1514
+ // Currency formatting has only short style (no long)
1515
+ if (oOptions.trailingCurrencyCode) {
1516
+ sStyle = "sap-short";
1517
+ } else {
1518
+ sStyle = "short";
1519
+ }
1520
+
1521
+ // Get correct format string based on actual decimal/fraction digits
1522
+ // the plural category of a compact currency is determined for the reduced short number without compact
1523
+ // notation, e.g. "1.2M" must check "1.2" (see CLDR "currencyFormat-short")
1524
+ sPluralCategory = this._getPluralCategory(sIntegerPart, sFractionPart);
1525
+ if (bIndianCurrency) {
1526
+ sPattern = getIndianCurrencyFormat(sStyle, oShortFormat.key, sPluralCategory);
1527
+ } else {
1528
+ sPattern = this.oLocaleData.getCurrencyFormat(sStyle, oShortFormat.key, sPluralCategory);
1529
+ }
1530
+ //formatString may contain '.' (quoted to differentiate them decimal separator)
1531
+ //which must be replaced with .
1532
+ sPattern = sPattern.replace(/'.'/g, ".");
1533
+ }
1534
+
1535
+ // The currency pattern is defined in some locale, for example in "ko", as: ¤#,##0.00;(¤#,##0.00)
1536
+ // where the pattern after ';' should be used for negative numbers.
1537
+ // Therefore it's needed to check whether the pattern contains ';' and use the later part for
1538
+ // negative values
1539
+ aPatternParts = sPattern.split(";");
1540
+ if (aPatternParts.length === 2) {
1541
+ sPattern = bNegative ? aPatternParts[1] : aPatternParts[0];
1542
+ if (bNegative) {
1543
+ sResult = sResult.substring(oOptions.minusSign.length);
1544
+ }
1545
+ }
1546
+
1547
+ // check if we need to render a symbol instead of a currency-code
1548
+ if (!oOptions.currencyCode) {
1549
+ var sSymbol;
1550
+ // custom currencies provided
1551
+ if (oOptions.customCurrencies && typeof oOptions.customCurrencies === "object") {
1552
+ // the custom currency symbol map was preprocessed on instance creation
1553
+ sSymbol = this.mKnownCurrencySymbols[sMeasure];
1554
+ } else {
1555
+ sSymbol = this.oLocaleData.getCurrencySymbol(sMeasure);
1556
+ }
1557
+ if (sSymbol && sSymbol !== sMeasure) {
1558
+ sMeasure = sSymbol;
1559
+ }
1560
+ }
1561
+ sResult = this._composeCurrencyResult(sPattern, sResult, sMeasure, {
1562
+ showMeasure: oOptions.showMeasure,
1563
+ negative: bNegative,
1564
+ minusSign: oOptions.minusSign
1565
+ });
1566
+ }
1567
+
1568
+ // format percent values:
1569
+ if (oOptions.type === mNumberType.PERCENT) {
1570
+ sPattern = oOptions.pattern;
1571
+ sResult = sPattern.replace(/[0#.,]+/, sResult);
1572
+ sResult = sResult.replace(/%/, oOptions.percentSign);
1573
+ }
1574
+ if (oOptions.showMeasure && sMeasure && oOptions.type === mNumberType.UNIT) {
1575
+ // the plural category of a unit pattern is determined for the complete number, maybe as compact
1576
+ // notation, e.g. "1.2M" must check "1.2c6"
1577
+ sPluralCategory = this._getPluralCategory(sIntegerPart, sFractionPart, oShortFormat);
1578
+ if (mUnitPatterns) {
1579
+ sPattern = mUnitPatterns["unitPattern-count-" + sPluralCategory];
1580
+ // some units do not have a pattern for each plural and therefore "other" is used as fallback
1581
+ if (!sPattern) {
1582
+ sPattern = mUnitPatterns["unitPattern-count-other"];
1583
+ }
1584
+ if (!sPattern) {
1585
+ sPattern = NumberFormat.getDefaultUnitPattern(sMeasure);
1586
+ }
1587
+ } else {
1588
+ sPattern = NumberFormat.getDefaultUnitPattern(sMeasure);
1589
+ }
1590
+ sResult = sPattern.replace("{0}", sResult);
1591
+ }
1592
+ return this._addOriginInfo(sResult);
1593
+ };
1594
+
1595
+ /**
1596
+ * Gets the plural category for the given number information. With a given <code>oShortFormat</code>
1597
+ * the category is determined based on the compact notation.
1598
+ *
1599
+ * @param {int} sIntegerPart
1600
+ * The integer part
1601
+ * @param {int} [sFractionPart]
1602
+ * The fraction part
1603
+ * @param {{magnitude: int}} [oShortFormat]
1604
+ * An object containing the <code>magnitude</code> information describing the factor of a compact number
1605
+ * @returns {string}
1606
+ * The plural category
1607
+ *
1608
+ * @private
1609
+ */
1610
+ NumberFormat.prototype._getPluralCategory = function (sIntegerPart, sFractionPart, oShortFormat) {
1611
+ var sNumber = sIntegerPart;
1612
+ if (sFractionPart) {
1613
+ sNumber += "." + sFractionPart;
1614
+ }
1615
+ if (oShortFormat) {
1616
+ sNumber += "c" + oShortFormat.magnitude.toExponential().slice(2);
1617
+ }
1618
+ return this.oLocaleData.getPluralCategory(sNumber);
1619
+ };
1620
+ NumberFormat.prototype._addOriginInfo = function (sResult) {
1621
+ if (Supportability.collectOriginInfo()) {
1622
+ // String object is created on purpose and must not be a string literal
1623
+ // eslint-disable-next-line no-new-wrappers
1624
+ sResult = new String(sResult);
1625
+ sResult.originInfo = {
1626
+ source: "Common Locale Data Repository",
1627
+ locale: this.oLocale.toString()
1628
+ };
1629
+ }
1630
+ return sResult;
1631
+ };
1632
+ NumberFormat.prototype._composeCurrencyResult = function (sPattern, sFormattedNumber, sMeasure, oOptions) {
1633
+ var sMinusSign = oOptions.minusSign;
1634
+ sPattern = sPattern.replace(/[0#.,]+/, sFormattedNumber);
1635
+ if (oOptions.showMeasure && sMeasure) {
1636
+ var sPlaceHolder = "\u00a4",
1637
+ mRegex = {
1638
+ "[:digit:]": rDigit,
1639
+ "[[:^S:]&[:^Z:]]": rNotSAndNotZ
1640
+ },
1641
+ iMeasureStart = sPattern.indexOf(sPlaceHolder),
1642
+ // determine whether the number is before the measure or after it by comparing the position of measure placeholder with half of the length of the pattern string
1643
+ sPosition = iMeasureStart < sPattern.length / 2 ? "after" : "before",
1644
+ oSpacingSetting = this.oLocaleData.getCurrencySpacing(sPosition),
1645
+ sCurrencyChar = sPosition === "after" ? sMeasure.charAt(sMeasure.length - 1) : sMeasure.charAt(0),
1646
+ sNumberChar,
1647
+ rCurrencyChar = mRegex[oSpacingSetting.currencyMatch],
1648
+ rNumberChar = mRegex[oSpacingSetting.surroundingMatch],
1649
+ iInsertPos;
1650
+ sPattern = sPattern.replace(sPlaceHolder, sMeasure);
1651
+ sNumberChar = sPosition === "after" ? sPattern.charAt(iMeasureStart + sMeasure.length) : sPattern.charAt(iMeasureStart - 1);
1652
+ if (rCurrencyChar && rCurrencyChar.test(sCurrencyChar) && rNumberChar && rNumberChar.test(sNumberChar)) {
1653
+ // when both checks are valid, insert the defined space
1654
+
1655
+ if (sPosition === "after") {
1656
+ iInsertPos = iMeasureStart + sMeasure.length;
1657
+ } else {
1658
+ iInsertPos = iMeasureStart;
1659
+ }
1660
+
1661
+ // insert the space char between the measure and the number
1662
+ sPattern = sPattern.slice(0, iInsertPos) + oSpacingSetting.insertBetween + sPattern.slice(iInsertPos);
1663
+ } else if (oOptions.negative && sPosition === "after") {
1664
+ // when no space is inserted between measure and number
1665
+ // and when the number is negative and the measure is shown before the number
1666
+ // a zero-width non-breakable space ("\ufeff") is inserted before the minus sign
1667
+ // in order to prevent the formatted currency number from being wrapped after the
1668
+ // minus sign when the space isn't enough for displaying the currency number within
1669
+ // one line
1670
+ sMinusSign = "\ufeff" + oOptions.minusSign;
1671
+ }
1672
+ } else {
1673
+ // If measure is not shown, also remove whitespace next to the measure symbol
1674
+ sPattern = sPattern.replace(/\s*\u00a4\s*/, "");
1675
+ }
1676
+ if (oOptions.negative) {
1677
+ sPattern = sPattern.replace(/-/, sMinusSign);
1678
+ }
1679
+ return sPattern;
1680
+ };
1681
+
1682
+ /**
1683
+ * Parse a string which is formatted according to the given format options.
1684
+ *
1685
+ * @param {string} sValue the string containing a formatted numeric value
1686
+ * @return {number|array|string|null} the parsed value as:
1687
+ * <ul>
1688
+ * <li>number</li>
1689
+ * <li>array which contains the parsed value and the currency code (symbol) or unit for currency and unit instances</li>
1690
+ * <li>string when option "parseAsString" is <code>true</code></li>
1691
+ * <li><code>NaN</code> if value cannot be parsed</li>
1692
+ * <li><code>null</code> if value is invalid</li>
1693
+ * </ul>
1694
+ * @public
1695
+ */
1696
+ NumberFormat.prototype.parse = function (sValue) {
1697
+ var oOptions = this.oFormatOptions,
1698
+ sPlusSigns = oOptions.plusSign + this.oLocaleData.getLenientNumberSymbols("plusSign"),
1699
+ sMinusSigns = oOptions.minusSign + this.oLocaleData.getLenientNumberSymbols("minusSign"),
1700
+ // Note: the minus sign ('-') needs to be quoted as well such that it is not confused with the range operator, e.g. in [A-Z]
1701
+ sPlusMinusSigns = quote(sPlusSigns + sMinusSigns),
1702
+ sGroupingSeparator = quote(oOptions.groupingSeparator),
1703
+ sDecimalSeparator = quote(oOptions.decimalSeparator),
1704
+ sRegExpFloat = "^\\s*([" + sPlusMinusSigns + "]?(?:[0-9" + sGroupingSeparator + "]+|[0-9" + sGroupingSeparator + "]*" + sDecimalSeparator + "[0-9]*)(?:[eE][+-][0-9]+)?)\\s*$",
1705
+ sRegExpInt = "^\\s*([" + sPlusMinusSigns + "]?[0-9" + sGroupingSeparator + "]+)\\s*$",
1706
+ oGroupingRegExp = new RegExp(sGroupingSeparator, "g"),
1707
+ oDecimalRegExp = new RegExp(sDecimalSeparator, "g"),
1708
+ sPercentSign = this.oLocaleData.getNumberSymbol("percentSign"),
1709
+ bIndianCurrency = oOptions.type === mNumberType.CURRENCY && this.oLocale.getLanguage() === "en" && this.oLocale.getRegion() === "IN",
1710
+ oRegExp,
1711
+ bPercent,
1712
+ sMeasure,
1713
+ sPercentPattern,
1714
+ vResult = 0,
1715
+ oShort,
1716
+ vEmptyParseValue;
1717
+ if (sValue === "") {
1718
+ if (!oOptions.showNumber) {
1719
+ return null;
1720
+ }
1721
+ vEmptyParseValue = oOptions.emptyString;
1722
+ // If the 'emptyString' option is set to 0 or NaN and parseAsString is set to true, the return value should be converted to a string.
1723
+ // Because null is a valid value for string type, therefore null is not converted to a string.
1724
+ if (oOptions.parseAsString && (oOptions.emptyString === 0 || isNaN(oOptions.emptyString))) {
1725
+ vEmptyParseValue = oOptions.emptyString + "";
1726
+ }
1727
+ if (oOptions.type === mNumberType.CURRENCY || oOptions.type === mNumberType.UNIT) {
1728
+ return [vEmptyParseValue, undefined];
1729
+ } else {
1730
+ return vEmptyParseValue;
1731
+ }
1732
+ }
1733
+ if (typeof sValue !== "string" && !(sValue instanceof String)) {
1734
+ return null;
1735
+ }
1736
+ if (oOptions.groupingSeparator === oOptions.decimalSeparator) {
1737
+ Log.error("The grouping and decimal separator both have the same value '" + oOptions.groupingSeparator + "'. " + "They must be different from each other such that values can be parsed correctly.");
1738
+ }
1739
+ sPercentPattern = oOptions.type === mNumberType.PERCENT ? oOptions.pattern : this.oLocaleData.getPercentPattern();
1740
+ if (sPercentPattern.charAt(0) === "%") {
1741
+ sRegExpFloat = sRegExpFloat.slice(0, 1) + "%?" + sRegExpFloat.slice(1);
1742
+ } else if (sPercentPattern.charAt(sPercentPattern.length - 1) === "%") {
1743
+ sRegExpFloat = sRegExpFloat.slice(0, sRegExpFloat.length - 1) + "%?" + sRegExpFloat.slice(sRegExpFloat.length - 1);
1744
+ }
1745
+ var aUnitCode;
1746
+ if (oOptions.type === mNumberType.UNIT && oOptions.showMeasure) {
1747
+ var mUnitPatterns;
1748
+ if (oOptions.customUnits && typeof oOptions.customUnits === "object") {
1749
+ //custom units are exclusive (no fallback to LocaleData)
1750
+ mUnitPatterns = oOptions.customUnits;
1751
+ } else {
1752
+ mUnitPatterns = this.oLocaleData.getUnitFormats();
1753
+ }
1754
+ assert(mUnitPatterns, "Unit patterns cannot be loaded");
1755
+
1756
+ // filter using allowedUnits option
1757
+ if (oOptions.allowedUnits) {
1758
+ var mFilteredUnits = {};
1759
+ for (var i = 0; i < oOptions.allowedUnits.length; i++) {
1760
+ var sUnitType = oOptions.allowedUnits[i];
1761
+ mFilteredUnits[sUnitType] = mUnitPatterns[sUnitType];
1762
+ }
1763
+ mUnitPatterns = mFilteredUnits;
1764
+ }
1765
+ var oPatternAndResult = parseNumberAndUnit(mUnitPatterns, sValue, oOptions.showNumber, this.oLocaleData.sCLDRLocaleId);
1766
+ var bUnitIsAmbiguous = false;
1767
+ aUnitCode = oPatternAndResult.cldrCode;
1768
+ if (aUnitCode.length === 1) {
1769
+ sMeasure = aUnitCode[0];
1770
+ if (!oOptions.showNumber) {
1771
+ return [undefined, sMeasure];
1772
+ }
1773
+ } else if (aUnitCode.length === 0) {
1774
+ // in case showMeasure is set to false or unitOptional is set to true
1775
+ // we only try to parse the numberValue
1776
+ // the currency format behaves the same
1777
+ if (oOptions.unitOptional) {
1778
+ oPatternAndResult.numberValue = sValue;
1779
+ } else {
1780
+ //unit not found
1781
+ return null;
1782
+ }
1783
+ } else {
1784
+ //ambiguous unit
1785
+ assert(aUnitCode.length === 1, "Ambiguous unit [" + aUnitCode.join(", ") + "] for input: '" + sValue + "'");
1786
+ sMeasure = undefined;
1787
+ bUnitIsAmbiguous = true;
1788
+ }
1789
+
1790
+ // TODO: better error handling in strict mode
1791
+ // Next steps will be to implement a more helpful error message for these cases.
1792
+ // Right now we simply return null. For now this will force the types to throw
1793
+ // a default ParseException with a non-descriptive error.
1794
+ if (oOptions.strictParsing) {
1795
+ // two cases:
1796
+ // 1. showMeasure is set to false, but still a unit was parsed
1797
+ // 2. no unit (either none could be found OR the unit is ambiguous, should be separate error logs later on)
1798
+ if (bUnitIsAmbiguous) {
1799
+ return null;
1800
+ }
1801
+ }
1802
+ sValue = oPatternAndResult.numberValue || sValue;
1803
+ }
1804
+ var oResult;
1805
+ if (oOptions.type === mNumberType.CURRENCY && oOptions.showMeasure) {
1806
+ oResult = parseNumberAndCurrency({
1807
+ value: sValue,
1808
+ currencySymbols: this.mKnownCurrencySymbols,
1809
+ customCurrencyCodes: this.mKnownCurrencyCodes,
1810
+ duplicatedSymbols: this.mDuplicatedSymbols,
1811
+ customCurrenciesAvailable: !!oOptions.customCurrencies
1812
+ });
1813
+ if (!oResult) {
1814
+ return null;
1815
+ }
1816
+
1817
+ // TODO: better error handling in strict mode
1818
+ // Next steps will be to implement a more helpful error message for these cases.
1819
+ // Right now we simply return null. For now this will force the types to throw
1820
+ // a default ParseException with a non-descriptive error.
1821
+ if (oOptions.strictParsing) {
1822
+ if (!oResult.currencyCode || oResult.duplicatedSymbolFound) {
1823
+ // here we need an error log for:
1824
+ // 1. missing currency code/symbol (CLDR & custom)
1825
+ // 2. duplicated symbol was found (only custom, CLDR has no duplicates)
1826
+ return null;
1827
+ }
1828
+ }
1829
+ sValue = oResult.numberValue;
1830
+ sMeasure = oResult.currencyCode;
1831
+ if (oOptions.customCurrencies && sMeasure === null) {
1832
+ return null;
1833
+ }
1834
+ if (!oOptions.showNumber) {
1835
+ if (sValue) {
1836
+ return null;
1837
+ }
1838
+ return [undefined, sMeasure];
1839
+ }
1840
+ }
1841
+
1842
+ // remove the RTL special characters before the string is matched with the regex
1843
+ sValue = sValue.replace(/[\u202a\u200e\u202c\u202b\u200f]/g, "");
1844
+
1845
+ // remove all white spaces because when grouping separator is a non-breaking space (russian and french for example)
1846
+ // user will not input it this way. Also white spaces or grouping separator can be ignored by determining the value
1847
+ sValue = sValue.replace(rAllWhiteSpaces, "");
1848
+ oShort = getNumberFromShortened(sValue, this.oLocaleData, bIndianCurrency);
1849
+ if (oShort) {
1850
+ sValue = oShort.number;
1851
+ }
1852
+ var bScientificNotation = isScientificNotation(sValue);
1853
+
1854
+ // Check for valid syntax
1855
+ // integer might be expressed in scientific format, e.g. 1.23e+5
1856
+ // for this case it must be parsed as float
1857
+ if (oOptions.isInteger && !oShort && !bScientificNotation) {
1858
+ oRegExp = new RegExp(sRegExpInt);
1859
+ } else {
1860
+ oRegExp = new RegExp(sRegExpFloat);
1861
+ }
1862
+ if (!oRegExp.test(sValue)) {
1863
+ return oOptions.type === mNumberType.CURRENCY || oOptions.type === mNumberType.UNIT ? null : NaN;
1864
+ }
1865
+
1866
+ // Replace "minus/plus" sign with a parsable symbol
1867
+ // e.g. "➖47" ("➖" or "\u2796" cannot be parsed using parseInt) --> "-47" (can be parsed using parseInt)
1868
+ var iValueLength = sValue.length;
1869
+ for (var iValuePos = 0; iValuePos < iValueLength; iValuePos++) {
1870
+ var sCurrentValueChar = sValue[iValuePos];
1871
+
1872
+ // it can either be a minus or a plus
1873
+ // if one was found break because there can only be one in a value
1874
+ if (sPlusSigns.includes(sCurrentValueChar)) {
1875
+ sValue = sValue.replace(sCurrentValueChar, "+");
1876
+ break;
1877
+ } else if (sMinusSigns.includes(sCurrentValueChar)) {
1878
+ sValue = sValue.replace(sCurrentValueChar, "-");
1879
+ break;
1880
+ }
1881
+ }
1882
+
1883
+ // Remove the leading "+" sign because when "parseAsString" is set to true the "parseInt" or "parseFloat" isn't called and the leading "+" has to be moved manually
1884
+ sValue = sValue.replace(/^\+/, "");
1885
+
1886
+ // remove the percentage sign
1887
+ if (!oOptions.isInteger && sValue.indexOf(sPercentSign) !== -1) {
1888
+ bPercent = true;
1889
+ sValue = sValue.replace(sPercentSign, "");
1890
+ }
1891
+ var sValueWithGrouping = sValue;
1892
+
1893
+ // Remove grouping separator and replace locale dependant decimal separator,
1894
+ // before calling parseInt/parseFloat
1895
+ sValue = sValue.replace(oGroupingRegExp, "");
1896
+
1897
+ // Expanding short value before using parseInt/parseFloat
1898
+ if (oShort) {
1899
+ sValue = sValue.replace(oDecimalRegExp, ".");
1900
+ sValue = NumberFormat._shiftDecimalPoint(sValue, Math.round(Math.log(oShort.factor) / Math.LN10));
1901
+ }
1902
+ if (oOptions.isInteger) {
1903
+ var iInt;
1904
+ // check if it is a valid integer
1905
+ // 1.234567e+5 is 123456.7 is not an integer
1906
+ // 1.234567e+6 is 1234567 is an integer
1907
+ if (bScientificNotation) {
1908
+ sValue = sValue.replace(oDecimalRegExp, ".");
1909
+ iInt = getInteger(sValue);
1910
+ if (iInt === undefined) {
1911
+ return NaN;
1912
+ }
1913
+ } else {
1914
+ iInt = parseInt(sValue);
1915
+ }
1916
+ vResult = oOptions.parseAsString ? sValue : iInt;
1917
+ } else {
1918
+ sValue = sValue.replace(oDecimalRegExp, ".");
1919
+ vResult = oOptions.parseAsString ? sValue : parseFloat(sValue);
1920
+ if (bPercent) {
1921
+ vResult = NumberFormat._shiftDecimalPoint(vResult, -2);
1922
+ }
1923
+ }
1924
+
1925
+ // strict grouping validation
1926
+ var bIsGroupingValid = this._checkGrouping(sValueWithGrouping, oOptions, bScientificNotation);
1927
+ if (!bIsGroupingValid) {
1928
+ // treat invalid grouping the same way as if the value cannot be parsed
1929
+ return oOptions.type === mNumberType.CURRENCY || oOptions.type === mNumberType.UNIT ? null : NaN;
1930
+ }
1931
+
1932
+ // Get rid of leading zeros (percent was already shifted)
1933
+ if (oOptions.parseAsString && !bPercent) {
1934
+ vResult = NumberFormat._shiftDecimalPoint(sValue, 0);
1935
+ }
1936
+ if (oOptions.type === mNumberType.CURRENCY || oOptions.type === mNumberType.UNIT) {
1937
+ return [vResult, sMeasure];
1938
+ }
1939
+ return vResult;
1940
+ };
1941
+
1942
+ /**
1943
+ * Returns the scaling factor which is calculated based on the format options and the current locale being used.
1944
+ *
1945
+ * This function only returns a meaningful scaling factor when the 'style' formatting option is set
1946
+ * to 'short' or 'long', and the 'shortRefNumber' option for calculating the scale factor is set.
1947
+ *
1948
+ * Consider using this function when the 'showScale' option is set to <code>false</code>, which
1949
+ * causes the scale factor not to appear in every formatted number but in a shared place.
1950
+ *
1951
+ * @example thousand (locale "en")
1952
+ *
1953
+ * NumberFormat.getFloatInstance({style: "long", shortRefNumber: 1000}).getScale();
1954
+ * // "thousand"
1955
+ *
1956
+ * @returns {string|undefined} The scale string if it exists based on the given 'shortRefNumber' option. Otherwise it returns <code>undefined</code>.
1957
+ * @since 1.100
1958
+ * @public
1959
+ */
1960
+ NumberFormat.prototype.getScale = function () {
1961
+ if (this.oFormatOptions.style !== "short" && this.oFormatOptions.style !== "long" || this.oFormatOptions.shortRefNumber === undefined) {
1962
+ return;
1963
+ }
1964
+ var oShortFormat = getShortenedFormat(this.oFormatOptions.shortRefNumber, this.oFormatOptions, this.oLocaleData),
1965
+ sScale;
1966
+ if (oShortFormat && oShortFormat.formatString) {
1967
+ // remove the placeholder of number
1968
+ // replace the "'.'" with "."
1969
+ // trim to remove the space and non-breakable space
1970
+ sScale = oShortFormat.formatString.replace(rNumPlaceHolder, "").replace(/'.'/g, ".").trim();
1971
+ if (sScale) {
1972
+ // sScale could be an empty string and undefined should be returned in this case
1973
+ return sScale;
1974
+ }
1975
+ }
1976
+ };
1977
+ NumberFormat._shiftDecimalPoint = function (vValue, iStep) {
1978
+ if (typeof iStep !== "number") {
1979
+ return NaN;
1980
+ }
1981
+ var sMinus = "";
1982
+ var aExpParts = vValue.toString().toLowerCase().split("e");
1983
+ if (typeof vValue === "number") {
1984
+ // Exponential operation is used instead of simply multiply the number by
1985
+ // Math.pow(10, maxFractionDigits) because Exponential operation returns exact float
1986
+ // result but multiply doesn't. For example 1.005*100 = 100.49999999999999.
1987
+
1988
+ iStep = aExpParts[1] ? +aExpParts[1] + iStep : iStep;
1989
+ return +(aExpParts[0] + "e" + iStep);
1990
+ } else if (typeof vValue === "string") {
1991
+ if (parseFloat(vValue) === 0 && iStep >= 0) {
1992
+ // input "00000" should become "0"
1993
+ // input "000.000" should become "0.000" to keep precision of decimals
1994
+ // input "1e-1337" should remain "1e-1337" in order to keep the precision
1995
+ return vValue.replace(rLeadingZeros, "$1$2");
1996
+ }
1997
+ // In case of a negative value the leading minus needs to be cut off before shifting the decimal point.
1998
+ // Otherwise the minus will affect the positioning by index 1.
1999
+ // The minus sign will be added to the final result again.
2000
+ var sFirstChar = aExpParts[0].charAt(0);
2001
+ sMinus = sFirstChar === "-" ? sFirstChar : "";
2002
+ if (sMinus) {
2003
+ aExpParts[0] = aExpParts[0].slice(1);
2004
+ }
2005
+ vValue = aExpParts[0];
2006
+ var iDecimalPos = vValue.indexOf("."),
2007
+ // the expected position after move
2008
+ iAfterMovePos,
2009
+ // the integer part in the final result
2010
+ sInt,
2011
+ // the decimal part in the final result
2012
+ sDecimal;
2013
+ if (iDecimalPos === -1) {
2014
+ // when there's no decimal point, add one to the end
2015
+ vValue = vValue + ".";
2016
+ iDecimalPos = vValue.length - 1;
2017
+ }
2018
+ if (aExpParts[1]) {
2019
+ iDecimalPos += +aExpParts[1];
2020
+ }
2021
+ iAfterMovePos = iDecimalPos + iStep;
2022
+ if (iAfterMovePos <= 0) {
2023
+ // pad 0 to the left when decimal point should be shifted far left
2024
+ vValue = vValue.padStart(vValue.length - iAfterMovePos + 1, '0');
2025
+ iAfterMovePos = 1;
2026
+ } else if (iAfterMovePos >= vValue.length - 1) {
2027
+ // pad 0 to the right
2028
+ vValue = vValue.padEnd(iAfterMovePos + 1, '0');
2029
+ iAfterMovePos = vValue.length - 1;
2030
+ }
2031
+ vValue = vValue.replace(".", "");
2032
+ sInt = vValue.substring(0, iAfterMovePos);
2033
+ sDecimal = vValue.substring(iAfterMovePos);
2034
+
2035
+ // remove unnecessary leading zeros
2036
+ sInt = sInt.replace(rLeadingZeros, "$1$2");
2037
+ return sMinus + sInt + (sDecimal ? "." + sDecimal : "");
2038
+ } else {
2039
+ // can't shift decimal point in this case
2040
+ return null;
2041
+ }
2042
+ };
2043
+ function getShortenedFormat(fValue, oOptions, oLocaleData, bIndianCurrency) {
2044
+ var oShortFormat,
2045
+ iKey,
2046
+ sKey,
2047
+ sCldrFormat,
2048
+ sStyle = oOptions.style,
2049
+ iPrecision = oOptions.precision !== undefined ? oOptions.precision : 2;
2050
+ if (sStyle != "short" && sStyle != "long") {
2051
+ return undefined;
2052
+ }
2053
+ for (var i = 0; i < 15; i++) {
2054
+ iKey = Math.pow(10, i);
2055
+ if (rounding(Math.abs(fValue) / iKey, iPrecision - 1) < 10) {
2056
+ break;
2057
+ }
2058
+ }
2059
+ sKey = iKey.toString();
2060
+
2061
+ // Use "other" format to find the right magnitude, the actual format will be retrieved later
2062
+ // after the value has been calculated
2063
+ if (oOptions.type === mNumberType.CURRENCY) {
2064
+ if (oOptions.trailingCurrencyCode) {
2065
+ sStyle = "sap-short";
2066
+ }
2067
+ if (bIndianCurrency) {
2068
+ sCldrFormat = getIndianCurrencyFormat(sStyle, sKey, "other", true);
2069
+ } else {
2070
+ // Use currency specific format because for some languages there is a difference between the decimalFormat and the currencyFormat
2071
+ sCldrFormat = oLocaleData.getCurrencyFormat(sStyle, sKey, "other");
2072
+ }
2073
+ } else {
2074
+ sCldrFormat = oLocaleData.getDecimalFormat(sStyle, sKey, "other");
2075
+ }
2076
+ if (!sCldrFormat || sCldrFormat == "0") {
2077
+ //no format or special "0" format => number doesn't need to be shortened
2078
+ return undefined;
2079
+ } else {
2080
+ oShortFormat = {};
2081
+ oShortFormat.key = sKey;
2082
+ oShortFormat.formatString = sCldrFormat;
2083
+ var match = sCldrFormat.match(rNumPlaceHolder);
2084
+ if (match) {
2085
+ //to get magnitude, we need to remove from key the number of zeros
2086
+ //contained in valueSubString before decimal separator minus 1
2087
+ // "0.0" => magnitude = key
2088
+ // "00" => magnitude = key / 10
2089
+ // "000" => magnitude = key / 100
2090
+ oShortFormat.valueSubString = match[0];
2091
+ var decimalSeparatorPosition = oShortFormat.valueSubString.indexOf(".");
2092
+ if (decimalSeparatorPosition == -1) {
2093
+ oShortFormat.decimals = 0;
2094
+ oShortFormat.magnitude = iKey * Math.pow(10, 1 - oShortFormat.valueSubString.length);
2095
+ } else {
2096
+ oShortFormat.decimals = oShortFormat.valueSubString.length - decimalSeparatorPosition - 1;
2097
+ oShortFormat.magnitude = iKey * Math.pow(10, 1 - decimalSeparatorPosition);
2098
+ }
2099
+ } else {
2100
+ //value pattern has not be recognized
2101
+ //we cannot shorten
2102
+ return undefined;
2103
+ }
2104
+ }
2105
+ return oShortFormat;
2106
+ }
2107
+ function getNumberFromShortened(sValue, oLocaleData, bIndianCurrency) {
2108
+ var sNumber,
2109
+ iFactor = 1,
2110
+ iKey = 10,
2111
+ aPluralCategories = oLocaleData.getPluralCategories(),
2112
+ sCldrFormat,
2113
+ bestResult = {
2114
+ number: undefined,
2115
+ factor: iFactor
2116
+ },
2117
+ fnGetFactor = function (sPlural, iKey, sStyle, bIndian) {
2118
+ if (bIndian) {
2119
+ sCldrFormat = getIndianCurrencyFormat(sStyle, iKey.toString(), sPlural, true);
2120
+ } else {
2121
+ sCldrFormat = oLocaleData.getDecimalFormat(sStyle, iKey.toString(), sPlural);
2122
+ }
2123
+ if (sCldrFormat) {
2124
+ // Note: CLDR uses a non-breaking space in the format string
2125
+ // remove right-to-left mark u+200f character
2126
+ sCldrFormat = sCldrFormat.replace(/[\s\u00a0\u200F]/g, "");
2127
+ //formatString may contain '.' (quoted to differentiate them decimal separator)
2128
+ //which must be replaced with .
2129
+ sCldrFormat = sCldrFormat.replace(/'.'/g, ".");
2130
+ var match = sCldrFormat.match(rNumPlaceHolder);
2131
+ if (match) {
2132
+ // determine unit -> may be on the beginning e.g. for he
2133
+ var sValueSubString = match[0];
2134
+ var sUnit = sCldrFormat.replace(sValueSubString, "");
2135
+ if (!sUnit) {
2136
+ // If there's no scale defined in the pattern, skip the pattern
2137
+ return;
2138
+ }
2139
+ var iIndex = sValue.indexOf(sUnit);
2140
+ if (iIndex >= 0) {
2141
+ // parse the number part like every other number and then use the factor to get the real number
2142
+ sNumber = sValue.replace(sUnit, "");
2143
+ // remove right-to-left mark u+200f character
2144
+ sNumber = sNumber.replace(/\u200F/g, "");
2145
+ iFactor = iKey;
2146
+ // spanish numbers e.g. for MRD in format for "one" is "00 MRD" therefore factor needs to be adjusted
2147
+ // german numbers e.g. for Mrd. in format for "one" is "0 Mrd." therefore number does not need to be adjusted
2148
+ // "0" => magnitude = key
2149
+ // "00" => magnitude = key / 10
2150
+ // "000" => magnitude = key / 100
2151
+ iFactor *= Math.pow(10, 1 - sValueSubString.length);
2152
+
2153
+ // if best result has no number yet or the new number is smaller that the current one set the new number as best result
2154
+ if (bestResult.number === undefined || sNumber.length < bestResult.number.length) {
2155
+ bestResult.number = sNumber;
2156
+ bestResult.factor = iFactor;
2157
+ }
2158
+ }
2159
+ }
2160
+ }
2161
+ };
2162
+ // iterate over all formats. Max: 100 000 000 000 000
2163
+ // find best result as format can have multiple matches:
2164
+ // * value can be contained one in another (de-DE): "Million" and "Millionen"
2165
+ // * end with each other (es-ES): "mil millones" and "millones"
2166
+ ["long", "short"].forEach(function (sStyle) {
2167
+ iKey = 10;
2168
+ while (iKey < 1e15) {
2169
+ for (var i = 0; i < aPluralCategories.length; i++) {
2170
+ var sPluralCategory = aPluralCategories[i];
2171
+ fnGetFactor(sPluralCategory, iKey, sStyle);
2172
+ }
2173
+ iKey = iKey * 10;
2174
+ }
2175
+ });
2176
+
2177
+ // For india currencies try lakhs/crores
2178
+ if (bIndianCurrency && !sNumber) {
2179
+ iKey = 10;
2180
+ while (iKey < 1e15) {
2181
+ for (var i = 0; i < aPluralCategories.length; i++) {
2182
+ var sPluralCategory = aPluralCategories[i];
2183
+ fnGetFactor(sPluralCategory, iKey, "short", true);
2184
+ }
2185
+ iKey = iKey * 10;
2186
+ }
2187
+ }
2188
+ if (!sNumber) {
2189
+ return;
2190
+ }
2191
+ return bestResult;
2192
+ }
2193
+
2194
+ /**
2195
+ * Based on the format options and the global config, determine whether to display a trailing currency code
2196
+ * @param oFormatOptions
2197
+ * @returns {boolean}
2198
+ */
2199
+ function showTrailingCurrencyCode(oFormatOptions) {
2200
+ var bShowTrailingCurrencyCodes = Configuration.getFormatSettings().getTrailingCurrencyCode();
2201
+ if (oFormatOptions) {
2202
+ // overwritten by instance configuration
2203
+ if (oFormatOptions.trailingCurrencyCode !== undefined) {
2204
+ bShowTrailingCurrencyCodes = oFormatOptions.trailingCurrencyCode;
2205
+ }
2206
+
2207
+ // is false when custom pattern is used
2208
+ if (oFormatOptions.pattern) {
2209
+ bShowTrailingCurrencyCodes = false;
2210
+ }
2211
+
2212
+ // is false when currencyCode is not used
2213
+ if (oFormatOptions.currencyCode === false) {
2214
+ bShowTrailingCurrencyCodes = false;
2215
+ }
2216
+ }
2217
+ return bShowTrailingCurrencyCodes;
2218
+ }
2219
+ function getIndianCurrencyFormat(sStyle, sKey, sPlural, bDecimal) {
2220
+ var sFormat,
2221
+ oCurrencyFormats = {
2222
+ "short": {
2223
+ "1000-one": "\xa40000",
2224
+ "1000-other": "\xa40000",
2225
+ "10000-one": "\xa400000",
2226
+ "10000-other": "\xa400000",
2227
+ "100000-one": "\xa40 Lk",
2228
+ "100000-other": "\xa40 Lk",
2229
+ "1000000-one": "\xa400 Lk",
2230
+ "1000000-other": "\xa400 Lk",
2231
+ "10000000-one": "\xa40 Cr",
2232
+ "10000000-other": "\xa40 Cr",
2233
+ "100000000-one": "\xa400 Cr",
2234
+ "100000000-other": "\xa400 Cr",
2235
+ "1000000000-one": "\xa4000 Cr",
2236
+ "1000000000-other": "\xa4000 Cr",
2237
+ "10000000000-one": "\xa40000 Cr",
2238
+ "10000000000-other": "\xa40000 Cr",
2239
+ "100000000000-one": "\xa400000 Cr",
2240
+ "100000000000-other": "\xa400000 Cr",
2241
+ "1000000000000-one": "\xa40 Lk Cr",
2242
+ "1000000000000-other": "\xa40 Lk Cr",
2243
+ "10000000000000-one": "\xa400 Lk Cr",
2244
+ "10000000000000-other": "\xa400 Lk Cr",
2245
+ "100000000000000-one": "\xa40 Cr Cr",
2246
+ "100000000000000-other": "\xa40 Cr Cr"
2247
+ },
2248
+ "sap-short": {
2249
+ "1000-one": "0000\xa0\xa4",
2250
+ "1000-other": "0000\xa0\xa4",
2251
+ "10000-one": "00000\xa0\xa4",
2252
+ "10000-other": "00000\xa0\xa4",
2253
+ "100000-one": "0 Lk\xa0\xa4",
2254
+ "100000-other": "0 Lk\xa0\xa4",
2255
+ "1000000-one": "00 Lk\xa0\xa4",
2256
+ "1000000-other": "00 Lk\xa0\xa4",
2257
+ "10000000-one": "0 Cr\xa0\xa4",
2258
+ "10000000-other": "0 Cr\xa0\xa4",
2259
+ "100000000-one": "00 Cr\xa0\xa4",
2260
+ "100000000-other": "00 Cr\xa0\xa4",
2261
+ "1000000000-one": "000 Cr\xa0\xa4",
2262
+ "1000000000-other": "000 Cr\xa0\xa4",
2263
+ "10000000000-one": "0000 Cr\xa0\xa4",
2264
+ "10000000000-other": "0000 Cr\xa0\xa4",
2265
+ "100000000000-one": "00000 Cr\xa0\xa4",
2266
+ "100000000000-other": "00000 Cr\xa0\xa4",
2267
+ "1000000000000-one": "0 Lk Cr\xa0\xa4",
2268
+ "1000000000000-other": "0 Lk Cr\xa0\xa4",
2269
+ "10000000000000-one": "00 Lk Cr\xa0\xa4",
2270
+ "10000000000000-other": "00 Lk Cr\xa0\xa4",
2271
+ "100000000000000-one": "0 Cr Cr\xa0\xa4",
2272
+ "100000000000000-other": "0 Cr Cr\xa0\xa4"
2273
+ }
2274
+ },
2275
+ oDecimalFormats = {
2276
+ "short": {
2277
+ "1000-one": "0000",
2278
+ "1000-other": "0000",
2279
+ "10000-one": "00000",
2280
+ "10000-other": "00000",
2281
+ "100000-one": "0 Lk",
2282
+ "100000-other": "0 Lk",
2283
+ "1000000-one": "00 Lk",
2284
+ "1000000-other": "00 Lk",
2285
+ "10000000-one": "0 Cr",
2286
+ "10000000-other": "0 Cr",
2287
+ "100000000-one": "00 Cr",
2288
+ "100000000-other": "00 Cr",
2289
+ "1000000000-one": "000 Cr",
2290
+ "1000000000-other": "000 Cr",
2291
+ "10000000000-one": "0000 Cr",
2292
+ "10000000000-other": "0000 Cr",
2293
+ "100000000000-one": "00000 Cr",
2294
+ "100000000000-other": "00000 Cr",
2295
+ "1000000000000-one": "0 Lk Cr",
2296
+ "1000000000000-other": "0 Lk Cr",
2297
+ "10000000000000-one": "00 Lk Cr",
2298
+ "10000000000000-other": "00 Lk Cr",
2299
+ "100000000000000-one": "0 Cr Cr",
2300
+ "100000000000000-other": "0 Cr Cr"
2301
+ }
2302
+ };
2303
+ // decimal format for short and sap-short is the same
2304
+ oDecimalFormats["sap-short"] = oDecimalFormats["short"];
2305
+
2306
+ // use the appropriate format (either decimal or currency)
2307
+ var oTargetFormat = bDecimal ? oDecimalFormats : oCurrencyFormats;
2308
+ var oStyledFormat = oTargetFormat[sStyle];
2309
+ if (!oStyledFormat) {
2310
+ oStyledFormat = oTargetFormat["short"];
2311
+ }
2312
+ if (sPlural !== "one") {
2313
+ sPlural = "other";
2314
+ }
2315
+ sFormat = oStyledFormat[sKey + "-" + sPlural];
2316
+ return sFormat;
2317
+ }
2318
+
2319
+ /**
2320
+ * Checks if grouping is performed correctly (decimal separator is not confused with grouping separator).
2321
+ * The examples use the German locale.
2322
+ *
2323
+ * Validity:
2324
+ * * The grouping is valid if there are at least 2 grouping separators present.
2325
+ * Because there can only be one decimal separator, and by writing 2 grouping separators there is no confusion.
2326
+ * E.g. 1.2.3
2327
+ * * The grouping is valid if there is a decimal separator and one grouping separator present.
2328
+ * Because the user wrote both, there cannot be a confusion.
2329
+ * (If it was confused, it has already been taken care by the syntax check.)
2330
+ * E.g. 1.2,3
2331
+ *
2332
+ * Invalidity:
2333
+ * * If there is exactly one grouping separator present, no decimal separator, and the grouping
2334
+ * separator at the most right grouping position is wrong.
2335
+ * E.g. 1.2
2336
+ * E.g. 1.234567
2337
+ *
2338
+ * The grouping is checked even if the groupingEnabled format is set to <code>false</code>, because the
2339
+ * input could be copied from external sources which might have wrong grouping separators.
2340
+ *
2341
+ * The empty grouping separator is ignored and <code>true</code> is returned, because it cannot be validated.
2342
+ *
2343
+ * An additional check is performed which invalidates a wrong number syntax
2344
+ * E.g. 0.123
2345
+ * E.g. -.123
2346
+ *
2347
+ * @param {string} sValueWithGrouping the normalized value which only contains the grouping (e.g. "1.000"),
2348
+ * i.e. the following modifications were already applied:
2349
+ * <ul>
2350
+ * <li>remove percent symbol</li>
2351
+ * <li>remove leading plus</li>
2352
+ * <li>remove whitespaces</li>
2353
+ * <li>remove RTL characters</li>
2354
+ * <li>remove short/long format (e.g. "Mio"/"Million")</li>
2355
+ * <li>resolve lenient symbols</li>
2356
+ * </ul>
2357
+ * This means grouping separators which are space characters or RTL characters are not validated.
2358
+ * @param {object} oOptions the format options, relevant are: groupingSeparator, groupingSize, groupingBaseSize and decimalSeparator
2359
+ * @param {boolean} bScientificNotation is scientific notation, e.g. "1.234e+1"
2360
+ * @returns {boolean} true if the grouping is done correctly, e.g. "1.23" is not grouped correctly for grouping separator "." and groupingSize 3
2361
+ * @private
2362
+ */
2363
+ NumberFormat.prototype._checkGrouping = function (sValueWithGrouping, oOptions, bScientificNotation) {
2364
+ if (oOptions.groupingSeparator && sValueWithGrouping.includes(oOptions.groupingSeparator)) {
2365
+ // All following checks are only done, if the value contains at least one (non-falsy) grouping separator.
2366
+ // The examples below use the German locale:
2367
+ // groupingSeparator: '.'
2368
+ // decimalSeparator: ','
2369
+ // groupingSize: 3
2370
+
2371
+ // remove leading minus sign, it is irrelevant for grouping check
2372
+ // "-123.456" -> "123.456"
2373
+ sValueWithGrouping = sValueWithGrouping.replace(/^-/, "");
2374
+
2375
+ // remove leading zeros before non-zero digits
2376
+ // "001.234" -> "1.234"
2377
+ // "0.234" -> "0.234"
2378
+ sValueWithGrouping = sValueWithGrouping.replace(/^0+(\d)/, "$1");
2379
+
2380
+ // if value still starts with 0, or it starts with a grouping separator, it is invalid
2381
+ // e.g. "0.123", ".123" (invalid)
2382
+ if (sValueWithGrouping.startsWith("0") || sValueWithGrouping.startsWith(oOptions.groupingSeparator)) {
2383
+ return false;
2384
+ }
2385
+
2386
+ // remove scientific notation
2387
+ // "1.234e+1" -> "1.234"
2388
+ if (bScientificNotation) {
2389
+ sValueWithGrouping = sValueWithGrouping.replace(/[eE].*/, "");
2390
+ }
2391
+ var bHasDecimalSeparator = sValueWithGrouping.includes(oOptions.decimalSeparator);
2392
+ // Integer types often have identical decimal and grouping separators configured,
2393
+ // therefore we do not remove the decimals part and validate them as if they would not
2394
+ // have decimals
2395
+ if (oOptions.decimalSeparator === oOptions.groupingSeparator) {
2396
+ bHasDecimalSeparator = false;
2397
+ } else if (bHasDecimalSeparator) {
2398
+ // remove decimals part to be able to validate grouping
2399
+ sValueWithGrouping = sValueWithGrouping.split(oOptions.decimalSeparator)[0];
2400
+ }
2401
+
2402
+ // check if decimal and grouping separator were confused.
2403
+ // This check is performed in addition to stricter grouping validation (strictGroupingValidation)
2404
+ // to reduce the confusion between decimal and grouping separator.
2405
+ // e.g. for "de": 1.234567 (is invalid)
2406
+ // Pre-requisites (examples for "de")
2407
+ // * number has exactly one grouping separator, e.g. "1.23"
2408
+ // since there can be only one decimal separator, if there is exactly one grouping
2409
+ // separator they could have been confused
2410
+ // * number has no decimal separator, e.g. 1.23
2411
+ // if there is a decimal separator and a grouping separator present,
2412
+ // there cannot be a confusion
2413
+ var bHasExactlyOneGroupingSeparator = sValueWithGrouping.split(oOptions.groupingSeparator).length === 2;
2414
+ if (bHasExactlyOneGroupingSeparator && !bHasDecimalSeparator) {
2415
+ // find least-significant ("lowest") grouping separator
2416
+ var iLowestGroupingIndex = sValueWithGrouping.length - sValueWithGrouping.lastIndexOf(oOptions.groupingSeparator);
2417
+ var iBaseGroupSize = oOptions.groupingBaseSize || oOptions.groupingSize;
2418
+ // if least-significant grouping size doesn't match grouping base size, the value is invalid
2419
+ // e.g. 12.34 (invalid)
2420
+ if (iLowestGroupingIndex !== iBaseGroupSize + oOptions.groupingSeparator.length) {
2421
+ return false;
2422
+ }
2423
+ }
2424
+
2425
+ /**
2426
+ * With strictGroupingValidation enabled the behaviour is closer to ABAP, the position
2427
+ * of the grouping separators are validated as well.
2428
+ * e.g. for "de" <code>1.2.3</code> becomes invalid
2429
+ */
2430
+ if (oOptions.strictGroupingValidation) {
2431
+ if (!this._rGrouping) {
2432
+ this._rGrouping = getGroupingRegExp(oOptions.groupingSeparator, oOptions.groupingSize, oOptions.groupingBaseSize || oOptions.groupingSize);
2433
+ }
2434
+
2435
+ // e.g. for "de" with valid grouping separators at the correct position
2436
+ // rGrouping: /^\d+(?:\.?\d{3})*\.?\d{3}$/
2437
+ // sValueWithGrouping: 123 456.789
2438
+ // 123 456 789
2439
+ // 123.456.789
2440
+ // Note: spaces are just there for visual aid.
2441
+ if (!this._rGrouping.test(sValueWithGrouping)) {
2442
+ return false;
2443
+ }
2444
+ }
2445
+ }
2446
+ return true;
2447
+ };
2448
+
2449
+ /**
2450
+ * Whether or not the given value is in scientific notation
2451
+ *
2452
+ * @param {string} sValue string value, e.g. "9e+4"
2453
+ * @returns {boolean} <code>true</code> if it is in scientific notation
2454
+ */
2455
+ function isScientificNotation(sValue) {
2456
+ return sValue.indexOf("e") > 0 || sValue.indexOf("E") > 0;
2457
+ }
2458
+
2459
+ /**
2460
+ * Evaluates if the given number is an integer and returns it.
2461
+ * Otherwise returns <code>undefined</code>
2462
+ *
2463
+ * @param {string} sValue string value, e.g. "9e+4" or "1.2345e+25"
2464
+ * @returns {int} if value can be parsed to integer e.g. 90000, <code>undefined</code> otherwise
2465
+ */
2466
+ function getInteger(sValue) {
2467
+ // when resolving the e-notation check if there is still a dot character present and after the dot character there are no zeros
2468
+ var sResolvedENotation = NumberFormat._shiftDecimalPoint(sValue, 0);
2469
+ if (sResolvedENotation.indexOf(".") > 0 && !rOnlyZeros.test(sResolvedENotation.split(".")[1])) {
2470
+ return undefined;
2471
+ }
2472
+ var fFloat = parseFloat(sResolvedENotation);
2473
+ var sFloat = "" + fFloat;
2474
+
2475
+ // parseFloat() still produces the scientific notation output for bigger values such
2476
+ // as "1.2345e+25".
2477
+ // This conversion is required because parseInt() cannot handle scientific notation with
2478
+ // the mantissa being a floating point number, e.g. "1.2345e+25"
2479
+ if (isScientificNotation(sFloat)) {
2480
+ // retrieve the string value from the given float number
2481
+ // "1.2345e+25" becomes "12345000000000000000000000"
2482
+ sFloat = NumberFormat._shiftDecimalPoint(sFloat, 0);
2483
+ }
2484
+ var iInt = parseInt(sFloat);
2485
+ if (iInt !== fFloat) {
2486
+ return undefined;
2487
+ }
2488
+ return iInt;
2489
+ }
2490
+ function rounding(fValue, iMaxFractionDigits, sRoundingMode) {
2491
+ if (typeof fValue !== "number") {
2492
+ return NaN;
2493
+ }
2494
+ sRoundingMode = sRoundingMode || NumberFormat.RoundingMode.HALF_AWAY_FROM_ZERO;
2495
+ iMaxFractionDigits = parseInt(iMaxFractionDigits);
2496
+
2497
+ // only round if it is required (number of fraction digits is bigger than the maxFractionDigits option)
2498
+ var sValue = "" + fValue;
2499
+ if (!isScientificNotation(sValue)) {
2500
+ var iIndexOfPoint = sValue.indexOf(".");
2501
+ if (iIndexOfPoint < 0) {
2502
+ return fValue;
2503
+ }
2504
+ if (sValue.substring(iIndexOfPoint + 1).length <= iMaxFractionDigits) {
2505
+ return fValue;
2506
+ }
2507
+ }
2508
+ if (typeof sRoundingMode === "function") {
2509
+ // Support custom function for rounding the number
2510
+ fValue = sRoundingMode(fValue, iMaxFractionDigits);
2511
+ } else {
2512
+ // The NumberFormat.RoundingMode had all values in lower case before and later changed all values to upper case
2513
+ // to match the key according to the UI5 guideline for defining enum. Therefore it's needed to support both
2514
+ // lower and upper cases. Here checks whether the value has only lower case letters and converts it all to upper
2515
+ // case if so.
2516
+ if (sRoundingMode.match(/^[a-z_]+$/)) {
2517
+ sRoundingMode = sRoundingMode.toUpperCase();
2518
+ }
2519
+ if (!iMaxFractionDigits) {
2520
+ return mRoundingFunction[sRoundingMode](fValue);
2521
+ }
2522
+
2523
+ // First move the decimal point towards right by maxFactionDigits
2524
+ // Then using the rounding function to round the first digit after decimal point
2525
+ // In the end, move the decimal point back to the original position
2526
+ //
2527
+ // For example rounding 1.005 by maxFractionDigits 2
2528
+ // 1. Move the decimal point to right by 2 digits, result 100.5
2529
+ // 2. Using the round function, for example, Math.round(100.5) = 101
2530
+ // 3. Move the decimal point back by 2 digits, result 1.01
2531
+ fValue = NumberFormat._shiftDecimalPoint(mRoundingFunction[sRoundingMode](NumberFormat._shiftDecimalPoint(fValue, iMaxFractionDigits)), -iMaxFractionDigits);
2532
+ }
2533
+ return fValue;
2534
+ }
2535
+ function quote(sRegex) {
2536
+ return sRegex.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
2537
+ }
2538
+ function getDecimals(fValue, iPrecision) {
2539
+ var iIntegerDigits = Math.floor(Math.log(Math.abs(fValue)) / Math.LN10);
2540
+ return Math.max(0, iPrecision - iIntegerDigits - 1);
2541
+ }
2542
+
2543
+ /**
2544
+ * Returns the CLDR code and the number value by checking each pattern and finding the best
2545
+ * match. The best match means most of the unit value matched and the number match is shorter.
2546
+ *
2547
+ * Example input: "12km" matches for the unit postfix "m" and the resulting number value is
2548
+ * "12k" while the unit postfix "km" results in "12". Since unit postfix "km" returns a shorter
2549
+ * result it is considered the best match.
2550
+ *
2551
+ * Note: the CLDR data is not distinct in its patterns.
2552
+ * For example "100 c" could be in "en_gb" either 100 units of "volume-cup" or
2553
+ * "duration-century" both having the same pattern "{0} c". Therefore best matches will be
2554
+ * returned in an array.
2555
+ *
2556
+ * @param {object} mUnitPatterns The unit patterns
2557
+ * @param {string} sValue The given value
2558
+ * @param {boolean} bShowNumber Whether the number is shown
2559
+ * @param {string} sLanguageTag The language tag of the locale for language dependent processing
2560
+ * @return {{cldrCode: string[], numberValue: (string|undefined)}}
2561
+ * An object containing the unit codes and the number value
2562
+ */
2563
+ function parseNumberAndUnit(mUnitPatterns, sValue, bShowNumber, sLanguageTag) {
2564
+ var bContainsNumber,
2565
+ sKey,
2566
+ sNumber,
2567
+ iNumberPatternIndex,
2568
+ sPostfix,
2569
+ sPostfixLowerCase,
2570
+ sPrefix,
2571
+ sPrefixLowerCase,
2572
+ sUnitCode,
2573
+ sUnitPattern,
2574
+ sUnitPatternLowerCase,
2575
+ oBestMatch = {
2576
+ numberValue: undefined,
2577
+ cldrCode: []
2578
+ },
2579
+ aCaseInsensitiveMatches = [],
2580
+ bCaseSensitive = true,
2581
+ bPatternMatchWasCaseSensitive = true,
2582
+ iShortestNumberPartLength = Number.POSITIVE_INFINITY,
2583
+ bShortestNumberPartWasCaseSensitive = true,
2584
+ sValueLowerCase = sValue.toLocaleLowerCase(sLanguageTag);
2585
+ for (sUnitCode in mUnitPatterns) {
2586
+ for (sKey in mUnitPatterns[sUnitCode]) {
2587
+ //use only unit patterns
2588
+ if (!sKey.startsWith("unitPattern")) {
2589
+ continue;
2590
+ }
2591
+ sUnitPattern = mUnitPatterns[sUnitCode][sKey];
2592
+
2593
+ // IMPORTANT:
2594
+ // To increase performance we are using native string operations instead of regex,
2595
+ // to match the patterns against the input.
2596
+ //
2597
+ // sample input: e.g. "mi 12 tsd. ms²"
2598
+ // unit pattern: e.g. "mi {0} ms²"
2599
+
2600
+ // The smallest resulting number (String length) will be the best match
2601
+ iNumberPatternIndex = sUnitPattern.indexOf("{0}");
2602
+ bContainsNumber = iNumberPatternIndex > -1;
2603
+ if (bContainsNumber && !bShowNumber) {
2604
+ sUnitPattern = sUnitPattern.replace("{0}", "").trim();
2605
+ bContainsNumber = false;
2606
+ }
2607
+ sNumber = undefined;
2608
+ bCaseSensitive = true;
2609
+ if (bContainsNumber) {
2610
+ sPrefix = sUnitPattern.substring(0, iNumberPatternIndex);
2611
+ sPrefixLowerCase = sPrefix.toLocaleLowerCase(sLanguageTag);
2612
+ sPostfix = sUnitPattern.substring(iNumberPatternIndex + "{0}".length);
2613
+ sPostfixLowerCase = sPostfix.toLocaleLowerCase(sLanguageTag);
2614
+ if (sValue.startsWith(sPrefix) && sValue.endsWith(sPostfix)) {
2615
+ sNumber = sValue.substring(sPrefix.length, sValue.length - sPostfix.length);
2616
+ } else if (sValueLowerCase.startsWith(sPrefixLowerCase) && sValueLowerCase.endsWith(sPostfixLowerCase)) {
2617
+ bCaseSensitive = false;
2618
+ sNumber = sValue.substring(sPrefixLowerCase.length, sValueLowerCase.length - sPostfixLowerCase.length);
2619
+ }
2620
+ if (sNumber) {
2621
+ //get the match with the shortest result.
2622
+ // e.g. 1km -> (.+)m -> "1k" -> length 2
2623
+ // e.g. 1km -> (.+)km -> "1" -> length 1
2624
+
2625
+ if (sNumber.length < iShortestNumberPartLength) {
2626
+ iShortestNumberPartLength = sNumber.length;
2627
+ bShortestNumberPartWasCaseSensitive = bCaseSensitive;
2628
+ oBestMatch.numberValue = sNumber;
2629
+ oBestMatch.cldrCode = [sUnitCode];
2630
+ } else if (sNumber.length === iShortestNumberPartLength && oBestMatch.cldrCode.indexOf(sUnitCode) === -1) {
2631
+ if (bCaseSensitive && !bShortestNumberPartWasCaseSensitive) {
2632
+ oBestMatch.numberValue = sNumber;
2633
+ oBestMatch.cldrCode = [sUnitCode];
2634
+ bShortestNumberPartWasCaseSensitive = true;
2635
+ } else if (bCaseSensitive || !bShortestNumberPartWasCaseSensitive) {
2636
+ //ambiguous unit (en locale)
2637
+ // e.g. 100 c -> (.+) c -> duration-century
2638
+ // e.g. 100 c -> (.+) c -> volume-cup
2639
+ oBestMatch.cldrCode.push(sUnitCode);
2640
+ }
2641
+ }
2642
+ }
2643
+ } else {
2644
+ sUnitPatternLowerCase = sUnitPattern.toLocaleLowerCase(sLanguageTag);
2645
+ if (sUnitPattern === sValue || sUnitPatternLowerCase === sValueLowerCase) {
2646
+ if (bShowNumber) {
2647
+ //for units which do not have a number representation, get the number from the pattern
2648
+ if (sKey.endsWith("-zero")) {
2649
+ sNumber = "0";
2650
+ } else if (sKey.endsWith("-one")) {
2651
+ sNumber = "1";
2652
+ } else if (sKey.endsWith("-two")) {
2653
+ sNumber = "2";
2654
+ }
2655
+ if (sUnitPattern === sValue) {
2656
+ oBestMatch.numberValue = sNumber;
2657
+ oBestMatch.cldrCode = [sUnitCode];
2658
+ return oBestMatch;
2659
+ } else if (!oBestMatch.cldrCode.includes(sUnitCode)) {
2660
+ bPatternMatchWasCaseSensitive = false;
2661
+ oBestMatch.numberValue = sNumber;
2662
+ oBestMatch.cldrCode.push(sUnitCode);
2663
+ }
2664
+ } else if (oBestMatch.cldrCode.indexOf(sUnitCode) === -1) {
2665
+ if (sUnitPattern === sValue) {
2666
+ oBestMatch.cldrCode.push(sUnitCode);
2667
+ } else if (!aCaseInsensitiveMatches.includes(sUnitCode)) {
2668
+ aCaseInsensitiveMatches.push(sUnitCode);
2669
+ }
2670
+ }
2671
+ }
2672
+ }
2673
+ }
2674
+ }
2675
+ if ((!bShortestNumberPartWasCaseSensitive || !bPatternMatchWasCaseSensitive) && oBestMatch.cldrCode.length > 1) {
2676
+ oBestMatch.numberValue = undefined;
2677
+ }
2678
+ if (!bShowNumber && !oBestMatch.cldrCode.length) {
2679
+ oBestMatch.cldrCode = aCaseInsensitiveMatches;
2680
+ }
2681
+ return oBestMatch;
2682
+ }
2683
+
2684
+ /**
2685
+ * Identify the longest match between a sub string of <code>sValue</code>
2686
+ * and one of the values of the <code>mCollection</code> map.
2687
+ *
2688
+ * @param {string} sValue
2689
+ * The string value which is checked for all currency codes/symbols
2690
+ * @param {Object<string, string>} mCollection
2691
+ * An object mapping a currency code to a either a currency symbol or the currency code itself
2692
+ * @param {boolean} bCaseInsensitive Whether case insensitive matches are allowed
2693
+ * @return {{code: string, recognizedCurrency: string, symbol: string}}
2694
+ * An object with the code, the recognized currency and the symbol found in the given value;
2695
+ * an empty object in case of either conflicting case insensitive matches, or no match
2696
+ */
2697
+ function findLongestMatch(sValue, mCollection, bCaseInsensitive) {
2698
+ var sCode,
2699
+ sCurCode,
2700
+ sCurSymbol,
2701
+ sCurSymbolToUpperCase,
2702
+ iIndex,
2703
+ sLanguageTag,
2704
+ sRecognizedCurrency,
2705
+ sValueSubStr,
2706
+ bDuplicate = false,
2707
+ bExactMatch = false,
2708
+ sSymbol = "";
2709
+ for (sCurCode in mCollection) {
2710
+ sCurSymbol = mCollection[sCurCode];
2711
+ if (!sCurSymbol) {
2712
+ continue;
2713
+ }
2714
+ sCurSymbol = sCurSymbol.replace(rAllWhiteSpaces, "\u0020");
2715
+ if (sValue.indexOf(sCurSymbol) >= 0 && sSymbol.length <= sCurSymbol.length) {
2716
+ sCode = sCurCode;
2717
+ bDuplicate = false;
2718
+ bExactMatch = true;
2719
+ sSymbol = sCurSymbol;
2720
+ sRecognizedCurrency = sCurSymbol;
2721
+ } else if (bCaseInsensitive) {
2722
+ sLanguageTag = Configuration.getLanguageTag();
2723
+ sCurSymbolToUpperCase = sCurSymbol.toLocaleUpperCase(sLanguageTag);
2724
+ iIndex = sValue.toLocaleUpperCase(sLanguageTag).indexOf(sCurSymbolToUpperCase);
2725
+ if (iIndex >= 0) {
2726
+ if (sSymbol.length === sCurSymbol.length && !bExactMatch) {
2727
+ bDuplicate = true;
2728
+ } else if (sSymbol.length < sCurSymbol.length) {
2729
+ sValueSubStr = sValue.substring(iIndex, iIndex + sCurSymbol.length);
2730
+ if (sValueSubStr.toLocaleUpperCase(sLanguageTag) === sCurSymbolToUpperCase) {
2731
+ sCode = sCurCode;
2732
+ bDuplicate = false;
2733
+ bExactMatch = false;
2734
+ sSymbol = sCurSymbol;
2735
+ sRecognizedCurrency = sValueSubStr;
2736
+ }
2737
+ }
2738
+ }
2739
+ }
2740
+ }
2741
+ if (bDuplicate || !sCode) {
2742
+ return {};
2743
+ }
2744
+ return {
2745
+ code: sCode,
2746
+ recognizedCurrency: sRecognizedCurrency,
2747
+ symbol: sSymbol
2748
+ };
2749
+ }
2750
+
2751
+ /**
2752
+ * Parses number and currency.
2753
+ *
2754
+ * Search for the currency symbol first, looking for the longest match. In case no currency
2755
+ * symbol is found, search for a three letter currency code.
2756
+ *
2757
+ * @param {object} oConfig
2758
+ * @param {string} oConfig.value the string value to be parse
2759
+ * @param {object} oConfig.currencySymbols the list of currency symbols to respect during parsing
2760
+ * @param {object} oConfig.customCurrencyCodes the list of currency codes used for parsing in case no symbol was found in the value string
2761
+ * @param {object} oConfig.duplicatedSymbols a list of all duplicated symbols;
2762
+ * In case oFormatOptions.currencyCode is set to false and the value string contains a duplicated symbol,
2763
+ * the value is not parsable. The result will be a parsed number and <code>undefined</code> for the currency.
2764
+ * @param {boolean} oConfig.customCurrenciesAvailable a flag to mark if custom currencies are available on the instance
2765
+ *
2766
+ * @private
2767
+ * @returns {object|undefined} returns object containing numberValue and currencyCode or undefined
2768
+ */
2769
+ function parseNumberAndCurrency(oConfig) {
2770
+ var aIsoMatches,
2771
+ sValue = oConfig.value.replace(rAllWhiteSpaces, "\u0020");
2772
+
2773
+ // Search for known symbols (longest match)
2774
+ // no distinction between default and custom currencies
2775
+ var oMatch = findLongestMatch(sValue, oConfig.currencySymbols);
2776
+
2777
+ // Search for currency code
2778
+ if (!oMatch.code) {
2779
+ // before falling back to the default regex for ISO codes we check the
2780
+ // codes for custom currencies (if defined)
2781
+ oMatch = findLongestMatch(sValue, oConfig.customCurrencyCodes, true);
2782
+ if (!oMatch.code && !oConfig.customCurrenciesAvailable) {
2783
+ // Match 3-letter iso code
2784
+ aIsoMatches = sValue.match(/(^[A-Z]{3}|[A-Z]{3}$)/i);
2785
+ oMatch.code = aIsoMatches && aIsoMatches[0].toLocaleUpperCase(Configuration.getLanguageTag());
2786
+ oMatch.recognizedCurrency = aIsoMatches && aIsoMatches[0];
2787
+ }
2788
+ }
2789
+
2790
+ // Remove symbol/code from value
2791
+ if (oMatch.code) {
2792
+ var iLastCodeIndex = oMatch.recognizedCurrency.length - 1;
2793
+ var sLastCodeChar = oMatch.recognizedCurrency.charAt(iLastCodeIndex);
2794
+ var iDelimiterPos;
2795
+ var rValidDelimiters = /[\-\s]+/;
2796
+
2797
+ // Check whether last character of matched code is a number
2798
+ if (/\d$/.test(sLastCodeChar)) {
2799
+ // Check whether parse string starts with the matched code
2800
+ if (sValue.startsWith(oMatch.recognizedCurrency)) {
2801
+ iDelimiterPos = iLastCodeIndex + 1;
2802
+ // \s matching any whitespace character including
2803
+ // non-breaking ws and invisible non-breaking ws
2804
+ if (!rValidDelimiters.test(sValue.charAt(iDelimiterPos))) {
2805
+ return undefined;
2806
+ }
2807
+ }
2808
+ // Check whether first character of matched code is a number
2809
+ } else if (/^\d/.test(oMatch.recognizedCurrency)) {
2810
+ // Check whether parse string ends with the matched code
2811
+ if (sValue.endsWith(oMatch.recognizedCurrency)) {
2812
+ iDelimiterPos = sValue.indexOf(oMatch.recognizedCurrency) - 1;
2813
+ if (!rValidDelimiters.test(sValue.charAt(iDelimiterPos))) {
2814
+ return undefined;
2815
+ }
2816
+ }
2817
+ }
2818
+ sValue = sValue.replace(oMatch.recognizedCurrency, "");
2819
+ }
2820
+
2821
+ // Set currency code to undefined, as the defined custom currencies
2822
+ // contain multiple currencies having the same symbol.
2823
+ var bDuplicatedSymbolFound = false;
2824
+ if (oConfig.duplicatedSymbols && oConfig.duplicatedSymbols[oMatch.symbol]) {
2825
+ oMatch.code = undefined;
2826
+ bDuplicatedSymbolFound = true;
2827
+ Log.error("The parsed currency symbol '" + oMatch.symbol + "' is defined multiple " + "times in custom currencies.Therefore the result is not distinct.");
2828
+ }
2829
+ return {
2830
+ numberValue: sValue,
2831
+ currencyCode: oMatch.code || undefined,
2832
+ duplicatedSymbolFound: bDuplicatedSymbolFound
2833
+ };
2834
+ }
2835
+ export default NumberFormat;