@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.
- package/.eslintignore +6 -0
- package/.eslintrc.cjs +3 -0
- package/.npsrc.json +3 -0
- package/CHANGELOG.md +2261 -0
- package/LICENSE.txt +201 -0
- package/README.md +51 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/Assets-fetch.d.ts +1 -0
- package/dist/Assets-fetch.js +3 -0
- package/dist/Assets-fetch.js.map +1 -0
- package/dist/Assets-node.d.ts +11 -0
- package/dist/Assets-node.js +12 -0
- package/dist/Assets-node.js.map +1 -0
- package/dist/Assets.d.ts +1 -0
- package/dist/Assets.js +3 -0
- package/dist/Assets.js.map +1 -0
- package/dist/CalendarUtils.d.ts +3 -0
- package/dist/CalendarUtils.js +6 -0
- package/dist/CalendarUtils.js.map +1 -0
- package/dist/DateFormat.d.ts +5 -0
- package/dist/DateFormat.js +7 -0
- package/dist/DateFormat.js.map +1 -0
- package/dist/LocaleData.d.ts +5 -0
- package/dist/LocaleData.js +7 -0
- package/dist/LocaleData.js.map +1 -0
- package/dist/NumberFormat.d.ts +5 -0
- package/dist/NumberFormat.js +7 -0
- package/dist/NumberFormat.js.map +1 -0
- package/dist/dates/CalendarDate.d.ts +42 -0
- package/dist/dates/CalendarDate.js +193 -0
- package/dist/dates/CalendarDate.js.map +1 -0
- package/dist/dates/ExtremeDates.d.ts +5 -0
- package/dist/dates/ExtremeDates.js +29 -0
- package/dist/dates/ExtremeDates.js.map +1 -0
- package/dist/dates/UI5Date.d.ts +5 -0
- package/dist/dates/UI5Date.js +7 -0
- package/dist/dates/UI5Date.js.map +1 -0
- package/dist/dates/UniversalDate.d.ts +47 -0
- package/dist/dates/UniversalDate.js +5 -0
- package/dist/dates/UniversalDate.js.map +1 -0
- package/dist/dates/convertMonthNumbersToMonthNames.d.ts +16 -0
- package/dist/dates/convertMonthNumbersToMonthNames.js +30 -0
- package/dist/dates/convertMonthNumbersToMonthNames.js.map +1 -0
- package/dist/dates/getDaysInMonth.d.ts +3 -0
- package/dist/dates/getDaysInMonth.js +10 -0
- package/dist/dates/getDaysInMonth.js.map +1 -0
- package/dist/dates/getRoundedTimestamp.d.ts +7 -0
- package/dist/dates/getRoundedTimestamp.js +15 -0
- package/dist/dates/getRoundedTimestamp.js.map +1 -0
- package/dist/dates/getTodayUTCTimestamp.d.ts +7 -0
- package/dist/dates/getTodayUTCTimestamp.js +9 -0
- package/dist/dates/getTodayUTCTimestamp.js.map +1 -0
- package/dist/dates/modifyDateBy.d.ts +14 -0
- package/dist/dates/modifyDateBy.js +55 -0
- package/dist/dates/modifyDateBy.js.map +1 -0
- package/dist/dates/transformDateToSecondaryType.d.ts +7 -0
- package/dist/dates/transformDateToSecondaryType.js +18 -0
- package/dist/dates/transformDateToSecondaryType.js.map +1 -0
- package/dist/features/calendar/Buddhist.d.ts +1 -0
- package/dist/features/calendar/Buddhist.js +2 -0
- package/dist/features/calendar/Buddhist.js.map +1 -0
- package/dist/features/calendar/Gregorian.d.ts +1 -0
- package/dist/features/calendar/Gregorian.js +2 -0
- package/dist/features/calendar/Gregorian.js.map +1 -0
- package/dist/features/calendar/Islamic.d.ts +1 -0
- package/dist/features/calendar/Islamic.js +2 -0
- package/dist/features/calendar/Islamic.js.map +1 -0
- package/dist/features/calendar/Japanese.d.ts +1 -0
- package/dist/features/calendar/Japanese.js +2 -0
- package/dist/features/calendar/Japanese.js.map +1 -0
- package/dist/features/calendar/Persian.d.ts +1 -0
- package/dist/features/calendar/Persian.js +2 -0
- package/dist/features/calendar/Persian.js.map +1 -0
- package/dist/generated/assets/cldr/Unicode-Data-Files-LICENSE.txt +27 -0
- package/dist/generated/assets/cldr/ar.json +7087 -0
- package/dist/generated/assets/cldr/ar_EG.json +7087 -0
- package/dist/generated/assets/cldr/ar_SA.json +7086 -0
- package/dist/generated/assets/cldr/bg.json +5981 -0
- package/dist/generated/assets/cldr/ca.json +6083 -0
- package/dist/generated/assets/cldr/cnr.json +6169 -0
- package/dist/generated/assets/cldr/cs.json +6709 -0
- package/dist/generated/assets/cldr/cy.json +6932 -0
- package/dist/generated/assets/cldr/da.json +5927 -0
- package/dist/generated/assets/cldr/de.json +6048 -0
- package/dist/generated/assets/cldr/de_AT.json +6049 -0
- package/dist/generated/assets/cldr/de_CH.json +6047 -0
- package/dist/generated/assets/cldr/el.json +5832 -0
- package/dist/generated/assets/cldr/el_CY.json +5832 -0
- package/dist/generated/assets/cldr/en.json +6044 -0
- package/dist/generated/assets/cldr/en_AU.json +6084 -0
- package/dist/generated/assets/cldr/en_GB.json +6075 -0
- package/dist/generated/assets/cldr/en_HK.json +6084 -0
- package/dist/generated/assets/cldr/en_IE.json +6075 -0
- package/dist/generated/assets/cldr/en_IN.json +6080 -0
- package/dist/generated/assets/cldr/en_NZ.json +6075 -0
- package/dist/generated/assets/cldr/en_PG.json +6076 -0
- package/dist/generated/assets/cldr/en_SG.json +6080 -0
- package/dist/generated/assets/cldr/en_ZA.json +6076 -0
- package/dist/generated/assets/cldr/es.json +6103 -0
- package/dist/generated/assets/cldr/es_AR.json +6106 -0
- package/dist/generated/assets/cldr/es_BO.json +6105 -0
- package/dist/generated/assets/cldr/es_CL.json +5998 -0
- package/dist/generated/assets/cldr/es_CO.json +5998 -0
- package/dist/generated/assets/cldr/es_MX.json +6107 -0
- package/dist/generated/assets/cldr/es_PE.json +5889 -0
- package/dist/generated/assets/cldr/es_UY.json +5891 -0
- package/dist/generated/assets/cldr/es_VE.json +5890 -0
- package/dist/generated/assets/cldr/et.json +6027 -0
- package/dist/generated/assets/cldr/fa.json +5950 -0
- package/dist/generated/assets/cldr/fi.json +6195 -0
- package/dist/generated/assets/cldr/fr.json +5997 -0
- package/dist/generated/assets/cldr/fr_BE.json +5997 -0
- package/dist/generated/assets/cldr/fr_CA.json +5991 -0
- package/dist/generated/assets/cldr/fr_CH.json +6015 -0
- package/dist/generated/assets/cldr/fr_LU.json +5997 -0
- package/dist/generated/assets/cldr/he.json +6541 -0
- package/dist/generated/assets/cldr/hi.json +5859 -0
- package/dist/generated/assets/cldr/hr.json +6196 -0
- package/dist/generated/assets/cldr/hu.json +5945 -0
- package/dist/generated/assets/cldr/id.json +5730 -0
- package/dist/generated/assets/cldr/it.json +5986 -0
- package/dist/generated/assets/cldr/it_CH.json +5986 -0
- package/dist/generated/assets/cldr/ja.json +5889 -0
- package/dist/generated/assets/cldr/kk.json +5939 -0
- package/dist/generated/assets/cldr/ko.json +5770 -0
- package/dist/generated/assets/cldr/lt.json +6578 -0
- package/dist/generated/assets/cldr/lv.json +6114 -0
- package/dist/generated/assets/cldr/mk.json +6045 -0
- package/dist/generated/assets/cldr/ms.json +5564 -0
- package/dist/generated/assets/cldr/nb.json +6035 -0
- package/dist/generated/assets/cldr/nl.json +6202 -0
- package/dist/generated/assets/cldr/nl_BE.json +6202 -0
- package/dist/generated/assets/cldr/pl.json +6589 -0
- package/dist/generated/assets/cldr/pt.json +6115 -0
- package/dist/generated/assets/cldr/pt_PT.json +6180 -0
- package/dist/generated/assets/cldr/ro.json +6200 -0
- package/dist/generated/assets/cldr/ru.json +6503 -0
- package/dist/generated/assets/cldr/ru_UA.json +6503 -0
- package/dist/generated/assets/cldr/sk.json +6432 -0
- package/dist/generated/assets/cldr/sl.json +6444 -0
- package/dist/generated/assets/cldr/sr.json +6241 -0
- package/dist/generated/assets/cldr/sr_Latn.json +6226 -0
- package/dist/generated/assets/cldr/sv.json +6076 -0
- package/dist/generated/assets/cldr/th.json +5875 -0
- package/dist/generated/assets/cldr/tr.json +6094 -0
- package/dist/generated/assets/cldr/uk.json +6454 -0
- package/dist/generated/assets/cldr/vi.json +5669 -0
- package/dist/generated/assets/cldr/zh_CN.json +5717 -0
- package/dist/generated/assets/cldr/zh_HK.json +5726 -0
- package/dist/generated/assets/cldr/zh_SG.json +5726 -0
- package/dist/generated/assets/cldr/zh_TW.json +5793 -0
- package/dist/generated/json-imports/LocaleData-fetch.d.ts +1 -0
- package/dist/generated/json-imports/LocaleData-fetch.js +93 -0
- package/dist/generated/json-imports/LocaleData-fetch.js.map +1 -0
- package/dist/generated/json-imports/LocaleData-node.d.ts +1 -0
- package/dist/generated/json-imports/LocaleData-node.js +93 -0
- package/dist/generated/json-imports/LocaleData-node.js.map +1 -0
- package/dist/generated/json-imports/LocaleData.d.ts +1 -0
- package/dist/generated/json-imports/LocaleData.js +93 -0
- package/dist/generated/json-imports/LocaleData.js.map +1 -0
- package/dist/getCachedLocaleDataInstance.d.ts +4 -0
- package/dist/getCachedLocaleDataInstance.js +10 -0
- package/dist/getCachedLocaleDataInstance.js.map +1 -0
- package/dist/locale/getLocaleData.d.ts +11 -0
- package/dist/locale/getLocaleData.js +23 -0
- package/dist/locale/getLocaleData.js.map +1 -0
- package/dist/sap/base/Event.js +59 -0
- package/dist/sap/base/Eventing.js +146 -0
- package/dist/sap/base/Log.js +3 -0
- package/dist/sap/base/assert.js +34 -0
- package/dist/sap/base/config/MemoryConfigurationProvider.js +20 -0
- package/dist/sap/base/config.js +17 -0
- package/dist/sap/base/i18n/Formatting.d.ts +8 -0
- package/dist/sap/base/i18n/Formatting.js +11 -0
- package/dist/sap/base/i18n/Formatting.js.map +1 -0
- package/dist/sap/base/i18n/LanguageTag.js +173 -0
- package/dist/sap/base/i18n/Localization.d.ts +4 -0
- package/dist/sap/base/i18n/Localization.js +12 -0
- package/dist/sap/base/i18n/Localization.js.map +1 -0
- package/dist/sap/base/i18n/date/CalendarType.js +43 -0
- package/dist/sap/base/i18n/date/CalendarWeekNumbering.js +105 -0
- package/dist/sap/base/i18n/date/TimezoneUtils.js +319 -0
- package/dist/sap/base/strings/camelize.js +30 -0
- package/dist/sap/base/strings/formatMessage.js +93 -0
- package/dist/sap/base/util/LoaderExtensions.d.ts +4 -0
- package/dist/sap/base/util/LoaderExtensions.js +14 -0
- package/dist/sap/base/util/LoaderExtensions.js.map +1 -0
- package/dist/sap/base/util/ObjectPath.d.ts +4 -0
- package/dist/sap/base/util/ObjectPath.js +6 -0
- package/dist/sap/base/util/ObjectPath.js.map +1 -0
- package/dist/sap/base/util/Version.js +157 -0
- package/dist/sap/base/util/_merge.js +89 -0
- package/dist/sap/base/util/array/uniqueSort.js +41 -0
- package/dist/sap/base/util/deepClone.js +102 -0
- package/dist/sap/base/util/deepEqual.js +83 -0
- package/dist/sap/base/util/extend.js +61 -0
- package/dist/sap/base/util/isEmptyObject.js +34 -0
- package/dist/sap/base/util/isPlainObject.js +52 -0
- package/dist/sap/base/util/now.js +28 -0
- package/dist/sap/base/util/resolveReference.js +3 -0
- package/dist/sap/base/util/uid.js +27 -0
- package/dist/sap/ui/base/DataType.js +657 -0
- package/dist/sap/ui/base/Interface.js +72 -0
- package/dist/sap/ui/base/Metadata.js +483 -0
- package/dist/sap/ui/base/Object.js +300 -0
- package/dist/sap/ui/core/CalendarType.js +24 -0
- package/dist/sap/ui/core/Configuration.d.ts +17 -0
- package/dist/sap/ui/core/Configuration.js +23 -0
- package/dist/sap/ui/core/Configuration.js.map +1 -0
- package/dist/sap/ui/core/Core.d.ts +25 -0
- package/dist/sap/ui/core/Core.js +13 -0
- package/dist/sap/ui/core/Core.js.map +1 -0
- package/dist/sap/ui/core/FormatSettings.d.ts +9 -0
- package/dist/sap/ui/core/FormatSettings.js +12 -0
- package/dist/sap/ui/core/FormatSettings.js.map +1 -0
- package/dist/sap/ui/core/Locale.js +194 -0
- package/dist/sap/ui/core/LocaleData.js +2717 -0
- package/dist/sap/ui/core/Supportability.js +5 -0
- package/dist/sap/ui/core/Theming.js +539 -0
- package/dist/sap/ui/core/date/Buddhist.js +196 -0
- package/dist/sap/ui/core/date/CalendarUtils.js +65 -0
- package/dist/sap/ui/core/date/CalendarWeekNumbering.js +30 -0
- package/dist/sap/ui/core/date/Gregorian.js +32 -0
- package/dist/sap/ui/core/date/Islamic.js +367 -0
- package/dist/sap/ui/core/date/Japanese.js +257 -0
- package/dist/sap/ui/core/date/Persian.js +394 -0
- package/dist/sap/ui/core/date/UI5Date.js +991 -0
- package/dist/sap/ui/core/date/UniversalDate.js +1324 -0
- package/dist/sap/ui/core/date/_Calendars.js +22 -0
- package/dist/sap/ui/core/format/DateFormat.js +3310 -0
- package/dist/sap/ui/core/format/NumberFormat.js +2835 -0
- package/dist/sap/ui/core/format/TimezoneUtil.js +24 -0
- package/package-scripts.cjs +35 -0
- package/package.json +46 -0
- package/tsconfig.json +24 -0
- 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;
|