OLD | NEW |
1 // Copyright 2013 the V8 project authors. All rights reserved. | 1 // Copyright 2013 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // ECMAScript 402 API implementation. | 5 // ECMAScript 402 API implementation. |
6 | 6 |
7 /** | 7 /** |
8 * Intl object is a single object that has some named properties, | 8 * Intl object is a single object that has some named properties, |
9 * all of which are constructors. | 9 * all of which are constructors. |
10 */ | 10 */ |
11 (function(global, shared, exports) { | 11 (function(global, shared, exports) { |
12 | 12 |
13 "use strict"; | 13 "use strict"; |
14 | 14 |
15 %CheckIsBootstrapping(); | 15 %CheckIsBootstrapping(); |
16 | 16 |
17 var GlobalBoolean = global.Boolean; | 17 var GlobalBoolean = global.Boolean; |
18 var GlobalDate = global.Date; | 18 var GlobalDate = global.Date; |
19 var GlobalNumber = global.Number; | 19 var GlobalNumber = global.Number; |
20 var GlobalRegExp = global.RegExp; | 20 var GlobalRegExp = global.RegExp; |
21 var GlobalString = global.String; | 21 var GlobalString = global.String; |
22 | 22 |
23 var undefined = global.undefined; | 23 var undefined = global.undefined; |
24 | 24 |
25 var Intl = {}; | 25 var Intl = {}; |
26 | 26 |
27 %AddNamedProperty(global, "Intl", Intl, DONT_ENUM); | 27 %AddNamedProperty(global, "Intl", Intl, DONT_ENUM); |
28 | 28 |
29 var AVAILABLE_SERVICES = ['collator', | |
30 'numberformat', | |
31 'dateformat', | |
32 'breakiterator']; | |
33 | |
34 var NORMALIZATION_FORMS = ['NFC', | |
35 'NFD', | |
36 'NFKC', | |
37 'NFKD']; | |
38 | |
39 /** | 29 /** |
40 * Caches available locales for each service. | 30 * Caches available locales for each service. |
41 */ | 31 */ |
42 var AVAILABLE_LOCALES = { | 32 var AVAILABLE_LOCALES = { |
43 'collator': undefined, | 33 'collator': undefined, |
44 'numberformat': undefined, | 34 'numberformat': undefined, |
45 'dateformat': undefined, | 35 'dateformat': undefined, |
46 'breakiterator': undefined | 36 'breakiterator': undefined |
47 }; | 37 }; |
48 | 38 |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
144 | 134 |
145 function GetTimezoneNameCheckRE() { | 135 function GetTimezoneNameCheckRE() { |
146 if (TIMEZONE_NAME_CHECK_RE === undefined) { | 136 if (TIMEZONE_NAME_CHECK_RE === undefined) { |
147 TIMEZONE_NAME_CHECK_RE = | 137 TIMEZONE_NAME_CHECK_RE = |
148 new GlobalRegExp('^([A-Za-z]+)/([A-Za-z]+)(?:_([A-Za-z]+))*$'); | 138 new GlobalRegExp('^([A-Za-z]+)/([A-Za-z]+)(?:_([A-Za-z]+))*$'); |
149 } | 139 } |
150 return TIMEZONE_NAME_CHECK_RE; | 140 return TIMEZONE_NAME_CHECK_RE; |
151 } | 141 } |
152 | 142 |
153 /** | 143 /** |
154 * Maps ICU calendar names into LDML type. | |
155 */ | |
156 var ICU_CALENDAR_MAP = { | |
157 'gregorian': 'gregory', | |
158 'japanese': 'japanese', | |
159 'buddhist': 'buddhist', | |
160 'roc': 'roc', | |
161 'persian': 'persian', | |
162 'islamic-civil': 'islamicc', | |
163 'islamic': 'islamic', | |
164 'hebrew': 'hebrew', | |
165 'chinese': 'chinese', | |
166 'indian': 'indian', | |
167 'coptic': 'coptic', | |
168 'ethiopic': 'ethiopic', | |
169 'ethiopic-amete-alem': 'ethioaa' | |
170 }; | |
171 | |
172 /** | |
173 * Map of Unicode extensions to option properties, and their values and types, | |
174 * for a collator. | |
175 */ | |
176 var COLLATOR_KEY_MAP = { | |
177 'kn': {'property': 'numeric', 'type': 'boolean'}, | |
178 'kf': {'property': 'caseFirst', 'type': 'string', | |
179 'values': ['false', 'lower', 'upper']} | |
180 }; | |
181 | |
182 /** | |
183 * Map of Unicode extensions to option properties, and their values and types, | |
184 * for a number format. | |
185 */ | |
186 var NUMBER_FORMAT_KEY_MAP = { | |
187 'nu': {'property': undefined, 'type': 'string'} | |
188 }; | |
189 | |
190 /** | |
191 * Map of Unicode extensions to option properties, and their values and types, | |
192 * for a date/time format. | |
193 */ | |
194 var DATETIME_FORMAT_KEY_MAP = { | |
195 'ca': {'property': undefined, 'type': 'string'}, | |
196 'nu': {'property': undefined, 'type': 'string'} | |
197 }; | |
198 | |
199 /** | |
200 * Allowed -u-co- values. List taken from: | |
201 * http://unicode.org/repos/cldr/trunk/common/bcp47/collation.xml | |
202 */ | |
203 var ALLOWED_CO_VALUES = [ | |
204 'big5han', 'dict', 'direct', 'ducet', 'gb2312', 'phonebk', 'phonetic', | |
205 'pinyin', 'reformed', 'searchjl', 'stroke', 'trad', 'unihan', 'zhuyin' | |
206 ]; | |
207 | |
208 /** | |
209 * Error message for when function object is created with new and it's not | |
210 * a constructor. | |
211 */ | |
212 var ORDINARY_FUNCTION_CALLED_AS_CONSTRUCTOR = | |
213 'Function object that\'s not a constructor was created with new'; | |
214 | |
215 | |
216 /** | |
217 * Adds bound method to the prototype of the given object. | 144 * Adds bound method to the prototype of the given object. |
218 */ | 145 */ |
219 function addBoundMethod(obj, methodName, implementation, length) { | 146 function addBoundMethod(obj, methodName, implementation, length) { |
220 function getter() { | 147 function getter() { |
221 if (!%IsInitializedIntlObject(this)) { | 148 if (!%IsInitializedIntlObject(this)) { |
222 throw MakeTypeError(kMethodCalledOnWrongObject, methodName); | 149 throw MakeTypeError(kMethodCalledOnWrongObject, methodName); |
223 } | 150 } |
224 var internalName = '__bound' + methodName + '__'; | 151 var internalName = '__bound' + methodName + '__'; |
225 if (this[internalName] === undefined) { | 152 if (this[internalName] === undefined) { |
226 var that = this; | 153 var that = this; |
(...skipping 660 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
887 defineWEProperty(internalOptions, 'ignorePunctuation', getOption( | 814 defineWEProperty(internalOptions, 'ignorePunctuation', getOption( |
888 'ignorePunctuation', 'boolean', undefined, false)); | 815 'ignorePunctuation', 'boolean', undefined, false)); |
889 | 816 |
890 var locale = resolveLocale('collator', locales, options); | 817 var locale = resolveLocale('collator', locales, options); |
891 | 818 |
892 // ICU can't take kb, kc... parameters through localeID, so we need to pass | 819 // ICU can't take kb, kc... parameters through localeID, so we need to pass |
893 // them as options. | 820 // them as options. |
894 // One exception is -co- which has to be part of the extension, but only for | 821 // One exception is -co- which has to be part of the extension, but only for |
895 // usage: sort, and its value can't be 'standard' or 'search'. | 822 // usage: sort, and its value can't be 'standard' or 'search'. |
896 var extensionMap = parseExtension(locale.extension); | 823 var extensionMap = parseExtension(locale.extension); |
| 824 |
| 825 /** |
| 826 * Map of Unicode extensions to option properties, and their values and types, |
| 827 * for a collator. |
| 828 */ |
| 829 var COLLATOR_KEY_MAP = { |
| 830 'kn': {'property': 'numeric', 'type': 'boolean'}, |
| 831 'kf': {'property': 'caseFirst', 'type': 'string', |
| 832 'values': ['false', 'lower', 'upper']} |
| 833 }; |
| 834 |
897 setOptions( | 835 setOptions( |
898 options, extensionMap, COLLATOR_KEY_MAP, getOption, internalOptions); | 836 options, extensionMap, COLLATOR_KEY_MAP, getOption, internalOptions); |
899 | 837 |
900 var collation = 'default'; | 838 var collation = 'default'; |
901 var extension = ''; | 839 var extension = ''; |
902 if (extensionMap.hasOwnProperty('co') && internalOptions.usage === 'sort') { | 840 if (extensionMap.hasOwnProperty('co') && internalOptions.usage === 'sort') { |
| 841 |
| 842 /** |
| 843 * Allowed -u-co- values. List taken from: |
| 844 * http://unicode.org/repos/cldr/trunk/common/bcp47/collation.xml |
| 845 */ |
| 846 var ALLOWED_CO_VALUES = [ |
| 847 'big5han', 'dict', 'direct', 'ducet', 'gb2312', 'phonebk', 'phonetic', |
| 848 'pinyin', 'reformed', 'searchjl', 'stroke', 'trad', 'unihan', 'zhuyin' |
| 849 ]; |
| 850 |
903 if (ALLOWED_CO_VALUES.indexOf(extensionMap.co) !== -1) { | 851 if (ALLOWED_CO_VALUES.indexOf(extensionMap.co) !== -1) { |
904 extension = '-u-co-' + extensionMap.co; | 852 extension = '-u-co-' + extensionMap.co; |
905 // ICU can't tell us what the collation is, so save user's input. | 853 // ICU can't tell us what the collation is, so save user's input. |
906 collation = extensionMap.co; | 854 collation = extensionMap.co; |
907 } | 855 } |
908 } else if (internalOptions.usage === 'search') { | 856 } else if (internalOptions.usage === 'search') { |
909 extension = '-u-co-search'; | 857 extension = '-u-co-search'; |
910 } | 858 } |
911 defineWEProperty(internalOptions, 'collation', collation); | 859 defineWEProperty(internalOptions, 'collation', collation); |
912 | 860 |
(...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1119 defineWEProperty(internalOptions, 'maximumSignificantDigits', mxsd); | 1067 defineWEProperty(internalOptions, 'maximumSignificantDigits', mxsd); |
1120 } | 1068 } |
1121 | 1069 |
1122 // Grouping. | 1070 // Grouping. |
1123 defineWEProperty(internalOptions, 'useGrouping', getOption( | 1071 defineWEProperty(internalOptions, 'useGrouping', getOption( |
1124 'useGrouping', 'boolean', undefined, true)); | 1072 'useGrouping', 'boolean', undefined, true)); |
1125 | 1073 |
1126 // ICU prefers options to be passed using -u- extension key/values for | 1074 // ICU prefers options to be passed using -u- extension key/values for |
1127 // number format, so we need to build that. | 1075 // number format, so we need to build that. |
1128 var extensionMap = parseExtension(locale.extension); | 1076 var extensionMap = parseExtension(locale.extension); |
| 1077 |
| 1078 /** |
| 1079 * Map of Unicode extensions to option properties, and their values and types, |
| 1080 * for a number format. |
| 1081 */ |
| 1082 var NUMBER_FORMAT_KEY_MAP = { |
| 1083 'nu': {'property': undefined, 'type': 'string'} |
| 1084 }; |
| 1085 |
1129 var extension = setOptions(options, extensionMap, NUMBER_FORMAT_KEY_MAP, | 1086 var extension = setOptions(options, extensionMap, NUMBER_FORMAT_KEY_MAP, |
1130 getOption, internalOptions); | 1087 getOption, internalOptions); |
1131 | 1088 |
1132 var requestedLocale = locale.locale + extension; | 1089 var requestedLocale = locale.locale + extension; |
1133 var resolved = $objectDefineProperties({}, { | 1090 var resolved = $objectDefineProperties({}, { |
1134 currency: {writable: true}, | 1091 currency: {writable: true}, |
1135 currencyDisplay: {writable: true}, | 1092 currencyDisplay: {writable: true}, |
1136 locale: {writable: true}, | 1093 locale: {writable: true}, |
1137 maximumFractionDigits: {writable: true}, | 1094 maximumFractionDigits: {writable: true}, |
1138 minimumFractionDigits: {writable: true}, | 1095 minimumFractionDigits: {writable: true}, |
(...skipping 374 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1513 | 1470 |
1514 // Filter out supported extension keys so we know what to put in resolved | 1471 // Filter out supported extension keys so we know what to put in resolved |
1515 // section later on. | 1472 // section later on. |
1516 // We need to pass calendar and number system to the method. | 1473 // We need to pass calendar and number system to the method. |
1517 var tz = canonicalizeTimeZoneID(options.timeZone); | 1474 var tz = canonicalizeTimeZoneID(options.timeZone); |
1518 | 1475 |
1519 // ICU prefers options to be passed using -u- extension key/values, so | 1476 // ICU prefers options to be passed using -u- extension key/values, so |
1520 // we need to build that. | 1477 // we need to build that. |
1521 var internalOptions = {}; | 1478 var internalOptions = {}; |
1522 var extensionMap = parseExtension(locale.extension); | 1479 var extensionMap = parseExtension(locale.extension); |
| 1480 |
| 1481 /** |
| 1482 * Map of Unicode extensions to option properties, and their values and types, |
| 1483 * for a date/time format. |
| 1484 */ |
| 1485 var DATETIME_FORMAT_KEY_MAP = { |
| 1486 'ca': {'property': undefined, 'type': 'string'}, |
| 1487 'nu': {'property': undefined, 'type': 'string'} |
| 1488 }; |
| 1489 |
1523 var extension = setOptions(options, extensionMap, DATETIME_FORMAT_KEY_MAP, | 1490 var extension = setOptions(options, extensionMap, DATETIME_FORMAT_KEY_MAP, |
1524 getOption, internalOptions); | 1491 getOption, internalOptions); |
1525 | 1492 |
1526 var requestedLocale = locale.locale + extension; | 1493 var requestedLocale = locale.locale + extension; |
1527 var resolved = $objectDefineProperties({}, { | 1494 var resolved = $objectDefineProperties({}, { |
1528 calendar: {writable: true}, | 1495 calendar: {writable: true}, |
1529 day: {writable: true}, | 1496 day: {writable: true}, |
1530 era: {writable: true}, | 1497 era: {writable: true}, |
1531 hour12: {writable: true}, | 1498 hour12: {writable: true}, |
1532 hour: {writable: true}, | 1499 hour: {writable: true}, |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1584 */ | 1551 */ |
1585 %AddNamedProperty(Intl.DateTimeFormat.prototype, 'resolvedOptions', function() { | 1552 %AddNamedProperty(Intl.DateTimeFormat.prototype, 'resolvedOptions', function() { |
1586 if (%_IsConstructCall()) { | 1553 if (%_IsConstructCall()) { |
1587 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); | 1554 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); |
1588 } | 1555 } |
1589 | 1556 |
1590 if (!%IsInitializedIntlObjectOfType(this, 'dateformat')) { | 1557 if (!%IsInitializedIntlObjectOfType(this, 'dateformat')) { |
1591 throw MakeTypeError(kResolvedOptionsCalledOnNonObject, "DateTimeFormat"); | 1558 throw MakeTypeError(kResolvedOptionsCalledOnNonObject, "DateTimeFormat"); |
1592 } | 1559 } |
1593 | 1560 |
| 1561 /** |
| 1562 * Maps ICU calendar names into LDML type. |
| 1563 */ |
| 1564 var ICU_CALENDAR_MAP = { |
| 1565 'gregorian': 'gregory', |
| 1566 'japanese': 'japanese', |
| 1567 'buddhist': 'buddhist', |
| 1568 'roc': 'roc', |
| 1569 'persian': 'persian', |
| 1570 'islamic-civil': 'islamicc', |
| 1571 'islamic': 'islamic', |
| 1572 'hebrew': 'hebrew', |
| 1573 'chinese': 'chinese', |
| 1574 'indian': 'indian', |
| 1575 'coptic': 'coptic', |
| 1576 'ethiopic': 'ethiopic', |
| 1577 'ethiopic-amete-alem': 'ethioaa' |
| 1578 }; |
| 1579 |
1594 var format = this; | 1580 var format = this; |
1595 var fromPattern = fromLDMLString(format.resolved.pattern); | 1581 var fromPattern = fromLDMLString(format.resolved.pattern); |
1596 var userCalendar = ICU_CALENDAR_MAP[format.resolved.calendar]; | 1582 var userCalendar = ICU_CALENDAR_MAP[format.resolved.calendar]; |
1597 if (userCalendar === undefined) { | 1583 if (userCalendar === undefined) { |
1598 // Use ICU name if we don't have a match. It shouldn't happen, but | 1584 // Use ICU name if we don't have a match. It shouldn't happen, but |
1599 // it would be too strict to throw for this. | 1585 // it would be too strict to throw for this. |
1600 userCalendar = format.resolved.calendar; | 1586 userCalendar = format.resolved.calendar; |
1601 } | 1587 } |
1602 | 1588 |
1603 var locale = getOptimalLanguageTag(format.resolved.requestedLocale, | 1589 var locale = getOptimalLanguageTag(format.resolved.requestedLocale, |
(...skipping 342 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1946 */ | 1932 */ |
1947 $overrideFunction(GlobalString.prototype, 'normalize', function(that) { | 1933 $overrideFunction(GlobalString.prototype, 'normalize', function(that) { |
1948 if (%_IsConstructCall()) { | 1934 if (%_IsConstructCall()) { |
1949 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); | 1935 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); |
1950 } | 1936 } |
1951 | 1937 |
1952 CHECK_OBJECT_COERCIBLE(this, "String.prototype.normalize"); | 1938 CHECK_OBJECT_COERCIBLE(this, "String.prototype.normalize"); |
1953 | 1939 |
1954 var form = GlobalString(%_Arguments(0) || 'NFC'); | 1940 var form = GlobalString(%_Arguments(0) || 'NFC'); |
1955 | 1941 |
| 1942 var NORMALIZATION_FORMS = ['NFC', 'NFD', 'NFKC', 'NFKD']; |
| 1943 |
1956 var normalizationForm = NORMALIZATION_FORMS.indexOf(form); | 1944 var normalizationForm = NORMALIZATION_FORMS.indexOf(form); |
1957 if (normalizationForm === -1) { | 1945 if (normalizationForm === -1) { |
1958 throw MakeRangeError(kNormalizationForm, NORMALIZATION_FORMS.join(', ')); | 1946 throw MakeRangeError(kNormalizationForm, NORMALIZATION_FORMS.join(', ')); |
1959 } | 1947 } |
1960 | 1948 |
1961 return %StringNormalize(this, normalizationForm); | 1949 return %StringNormalize(this, normalizationForm); |
1962 } | 1950 } |
1963 ); | 1951 ); |
1964 | 1952 |
1965 | 1953 |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2052 } | 2040 } |
2053 | 2041 |
2054 var locales = %_Arguments(0); | 2042 var locales = %_Arguments(0); |
2055 var options = %_Arguments(1); | 2043 var options = %_Arguments(1); |
2056 return toLocaleDateTime( | 2044 return toLocaleDateTime( |
2057 this, locales, options, 'time', 'time', 'dateformattime'); | 2045 this, locales, options, 'time', 'time', 'dateformattime'); |
2058 } | 2046 } |
2059 ); | 2047 ); |
2060 | 2048 |
2061 }) | 2049 }) |
OLD | NEW |