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, utils) { | 11 (function(global, shared, exports) { |
12 | 12 |
13 "use strict"; | 13 "use strict"; |
14 | 14 |
15 %CheckIsBootstrapping(); | 15 %CheckIsBootstrapping(); |
16 | 16 |
17 // ------------------------------------------------------------------- | |
18 // Imports | |
19 | |
20 var GlobalBoolean = global.Boolean; | 17 var GlobalBoolean = global.Boolean; |
21 var GlobalDate = global.Date; | 18 var GlobalDate = global.Date; |
22 var GlobalNumber = global.Number; | 19 var GlobalNumber = global.Number; |
23 var GlobalRegExp = global.RegExp; | 20 var GlobalRegExp = global.RegExp; |
24 var GlobalString = global.String; | 21 var GlobalString = global.String; |
25 | 22 |
26 var MathFloor; | 23 var undefined = global.undefined; |
27 | |
28 utils.Import(function(from) { | |
29 MathFloor = from.MathFloor; | |
30 }); | |
31 | |
32 // ------------------------------------------------------------------- | |
33 | 24 |
34 var Intl = {}; | 25 var Intl = {}; |
35 | 26 |
36 %AddNamedProperty(global, "Intl", Intl, DONT_ENUM); | 27 %AddNamedProperty(global, "Intl", Intl, DONT_ENUM); |
37 | 28 |
38 /** | 29 /** |
39 * Caches available locales for each service. | 30 * Caches available locales for each service. |
40 */ | 31 */ |
41 var AVAILABLE_LOCALES = { | 32 var AVAILABLE_LOCALES = { |
42 'collator': UNDEFINED, | 33 'collator': undefined, |
43 'numberformat': UNDEFINED, | 34 'numberformat': undefined, |
44 'dateformat': UNDEFINED, | 35 'dateformat': undefined, |
45 'breakiterator': UNDEFINED | 36 'breakiterator': undefined |
46 }; | 37 }; |
47 | 38 |
48 /** | 39 /** |
49 * Caches default ICU locale. | 40 * Caches default ICU locale. |
50 */ | 41 */ |
51 var DEFAULT_ICU_LOCALE = UNDEFINED; | 42 var DEFAULT_ICU_LOCALE = undefined; |
52 | 43 |
53 /** | 44 /** |
54 * Unicode extension regular expression. | 45 * Unicode extension regular expression. |
55 */ | 46 */ |
56 var UNICODE_EXTENSION_RE = UNDEFINED; | 47 var UNICODE_EXTENSION_RE = undefined; |
57 | 48 |
58 function GetUnicodeExtensionRE() { | 49 function GetUnicodeExtensionRE() { |
59 if (IS_UNDEFINED(UNDEFINED)) { | 50 if (UNICODE_EXTENSION_RE === undefined) { |
60 UNICODE_EXTENSION_RE = new GlobalRegExp('-u(-[a-z0-9]{2,8})+', 'g'); | 51 UNICODE_EXTENSION_RE = new GlobalRegExp('-u(-[a-z0-9]{2,8})+', 'g'); |
61 } | 52 } |
62 return UNICODE_EXTENSION_RE; | 53 return UNICODE_EXTENSION_RE; |
63 } | 54 } |
64 | 55 |
65 /** | 56 /** |
66 * Matches any Unicode extension. | 57 * Matches any Unicode extension. |
67 */ | 58 */ |
68 var ANY_EXTENSION_RE = UNDEFINED; | 59 var ANY_EXTENSION_RE = undefined; |
69 | 60 |
70 function GetAnyExtensionRE() { | 61 function GetAnyExtensionRE() { |
71 if (IS_UNDEFINED(ANY_EXTENSION_RE)) { | 62 if (ANY_EXTENSION_RE === undefined) { |
72 ANY_EXTENSION_RE = new GlobalRegExp('-[a-z0-9]{1}-.*', 'g'); | 63 ANY_EXTENSION_RE = new GlobalRegExp('-[a-z0-9]{1}-.*', 'g'); |
73 } | 64 } |
74 return ANY_EXTENSION_RE; | 65 return ANY_EXTENSION_RE; |
75 } | 66 } |
76 | 67 |
77 /** | 68 /** |
78 * Replace quoted text (single quote, anything but the quote and quote again). | 69 * Replace quoted text (single quote, anything but the quote and quote again). |
79 */ | 70 */ |
80 var QUOTED_STRING_RE = UNDEFINED; | 71 var QUOTED_STRING_RE = undefined; |
81 | 72 |
82 function GetQuotedStringRE() { | 73 function GetQuotedStringRE() { |
83 if (IS_UNDEFINED(QUOTED_STRING_RE)) { | 74 if (QUOTED_STRING_RE === undefined) { |
84 QUOTED_STRING_RE = new GlobalRegExp("'[^']+'", 'g'); | 75 QUOTED_STRING_RE = new GlobalRegExp("'[^']+'", 'g'); |
85 } | 76 } |
86 return QUOTED_STRING_RE; | 77 return QUOTED_STRING_RE; |
87 } | 78 } |
88 | 79 |
89 /** | 80 /** |
90 * Matches valid service name. | 81 * Matches valid service name. |
91 */ | 82 */ |
92 var SERVICE_RE = UNDEFINED; | 83 var SERVICE_RE = undefined; |
93 | 84 |
94 function GetServiceRE() { | 85 function GetServiceRE() { |
95 if (IS_UNDEFINED(SERVICE_RE)) { | 86 if (SERVICE_RE === undefined) { |
96 SERVICE_RE = | 87 SERVICE_RE = |
97 new GlobalRegExp('^(collator|numberformat|dateformat|breakiterator)$'); | 88 new GlobalRegExp('^(collator|numberformat|dateformat|breakiterator)$'); |
98 } | 89 } |
99 return SERVICE_RE; | 90 return SERVICE_RE; |
100 } | 91 } |
101 | 92 |
102 /** | 93 /** |
103 * Validates a language tag against bcp47 spec. | 94 * Validates a language tag against bcp47 spec. |
104 * Actual value is assigned on first run. | 95 * Actual value is assigned on first run. |
105 */ | 96 */ |
106 var LANGUAGE_TAG_RE = UNDEFINED; | 97 var LANGUAGE_TAG_RE = undefined; |
107 | 98 |
108 function GetLanguageTagRE() { | 99 function GetLanguageTagRE() { |
109 if (IS_UNDEFINED(LANGUAGE_TAG_RE)) { | 100 if (LANGUAGE_TAG_RE === undefined) { |
110 BuildLanguageTagREs(); | 101 BuildLanguageTagREs(); |
111 } | 102 } |
112 return LANGUAGE_TAG_RE; | 103 return LANGUAGE_TAG_RE; |
113 } | 104 } |
114 | 105 |
115 /** | 106 /** |
116 * Helps find duplicate variants in the language tag. | 107 * Helps find duplicate variants in the language tag. |
117 */ | 108 */ |
118 var LANGUAGE_VARIANT_RE = UNDEFINED; | 109 var LANGUAGE_VARIANT_RE = undefined; |
119 | 110 |
120 function GetLanguageVariantRE() { | 111 function GetLanguageVariantRE() { |
121 if (IS_UNDEFINED(LANGUAGE_VARIANT_RE)) { | 112 if (LANGUAGE_VARIANT_RE === undefined) { |
122 BuildLanguageTagREs(); | 113 BuildLanguageTagREs(); |
123 } | 114 } |
124 return LANGUAGE_VARIANT_RE; | 115 return LANGUAGE_VARIANT_RE; |
125 } | 116 } |
126 | 117 |
127 /** | 118 /** |
128 * Helps find duplicate singletons in the language tag. | 119 * Helps find duplicate singletons in the language tag. |
129 */ | 120 */ |
130 var LANGUAGE_SINGLETON_RE = UNDEFINED; | 121 var LANGUAGE_SINGLETON_RE = undefined; |
131 | 122 |
132 function GetLanguageSingletonRE() { | 123 function GetLanguageSingletonRE() { |
133 if (IS_UNDEFINED(LANGUAGE_SINGLETON_RE)) { | 124 if (LANGUAGE_SINGLETON_RE === undefined) { |
134 BuildLanguageTagREs(); | 125 BuildLanguageTagREs(); |
135 } | 126 } |
136 return LANGUAGE_SINGLETON_RE; | 127 return LANGUAGE_SINGLETON_RE; |
137 } | 128 } |
138 | 129 |
139 /** | 130 /** |
140 * Matches valid IANA time zone names. | 131 * Matches valid IANA time zone names. |
141 */ | 132 */ |
142 var TIMEZONE_NAME_CHECK_RE = UNDEFINED; | 133 var TIMEZONE_NAME_CHECK_RE = undefined; |
143 | 134 |
144 function GetTimezoneNameCheckRE() { | 135 function GetTimezoneNameCheckRE() { |
145 if (IS_UNDEFINED(TIMEZONE_NAME_CHECK_RE)) { | 136 if (TIMEZONE_NAME_CHECK_RE === undefined) { |
146 TIMEZONE_NAME_CHECK_RE = | 137 TIMEZONE_NAME_CHECK_RE = |
147 new GlobalRegExp('^([A-Za-z]+)/([A-Za-z]+)(?:_([A-Za-z]+))*$'); | 138 new GlobalRegExp('^([A-Za-z]+)/([A-Za-z]+)(?:_([A-Za-z]+))*$'); |
148 } | 139 } |
149 return TIMEZONE_NAME_CHECK_RE; | 140 return TIMEZONE_NAME_CHECK_RE; |
150 } | 141 } |
151 | 142 |
152 /** | 143 /** |
153 * Adds bound method to the prototype of the given object. | 144 * Adds bound method to the prototype of the given object. |
154 */ | 145 */ |
155 function addBoundMethod(obj, methodName, implementation, length) { | 146 function addBoundMethod(obj, methodName, implementation, length) { |
156 function getter() { | 147 function getter() { |
157 if (!%IsInitializedIntlObject(this)) { | 148 if (!%IsInitializedIntlObject(this)) { |
158 throw MakeTypeError(kMethodCalledOnWrongObject, methodName); | 149 throw MakeTypeError(kMethodCalledOnWrongObject, methodName); |
159 } | 150 } |
160 var internalName = '__bound' + methodName + '__'; | 151 var internalName = '__bound' + methodName + '__'; |
161 if (IS_UNDEFINED(this[internalName])) { | 152 if (this[internalName] === undefined) { |
162 var that = this; | 153 var that = this; |
163 var boundMethod; | 154 var boundMethod; |
164 if (IS_UNDEFINED(length) || length === 2) { | 155 if (length === undefined || length === 2) { |
165 boundMethod = function(x, y) { | 156 boundMethod = function(x, y) { |
166 if (%_IsConstructCall()) { | 157 if (%_IsConstructCall()) { |
167 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); | 158 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); |
168 } | 159 } |
169 return implementation(that, x, y); | 160 return implementation(that, x, y); |
170 } | 161 } |
171 } else if (length === 1) { | 162 } else if (length === 1) { |
172 boundMethod = function(x) { | 163 boundMethod = function(x) { |
173 if (%_IsConstructCall()) { | 164 if (%_IsConstructCall()) { |
174 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); | 165 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
213 /** | 204 /** |
214 * Returns an intersection of locales and service supported locales. | 205 * Returns an intersection of locales and service supported locales. |
215 * Parameter locales is treated as a priority list. | 206 * Parameter locales is treated as a priority list. |
216 */ | 207 */ |
217 function supportedLocalesOf(service, locales, options) { | 208 function supportedLocalesOf(service, locales, options) { |
218 if (IS_NULL(service.match(GetServiceRE()))) { | 209 if (IS_NULL(service.match(GetServiceRE()))) { |
219 throw MakeError(kWrongServiceType, service); | 210 throw MakeError(kWrongServiceType, service); |
220 } | 211 } |
221 | 212 |
222 // Provide defaults if matcher was not specified. | 213 // Provide defaults if matcher was not specified. |
223 if (IS_UNDEFINED(options)) { | 214 if (options === undefined) { |
224 options = {}; | 215 options = {}; |
225 } else { | 216 } else { |
226 options = $toObject(options); | 217 options = $toObject(options); |
227 } | 218 } |
228 | 219 |
229 var matcher = options.localeMatcher; | 220 var matcher = options.localeMatcher; |
230 if (!IS_UNDEFINED(matcher)) { | 221 if (matcher !== undefined) { |
231 matcher = GlobalString(matcher); | 222 matcher = GlobalString(matcher); |
232 if (matcher !== 'lookup' && matcher !== 'best fit') { | 223 if (matcher !== 'lookup' && matcher !== 'best fit') { |
233 throw MakeRangeError(kLocaleMatcher, matcher); | 224 throw MakeRangeError(kLocaleMatcher, matcher); |
234 } | 225 } |
235 } else { | 226 } else { |
236 matcher = 'best fit'; | 227 matcher = 'best fit'; |
237 } | 228 } |
238 | 229 |
239 var requestedLocales = initializeLocaleList(locales); | 230 var requestedLocales = initializeLocaleList(locales); |
240 | 231 |
241 // Cache these, they don't ever change per service. | 232 // Cache these, they don't ever change per service. |
242 if (IS_UNDEFINED(AVAILABLE_LOCALES[service])) { | 233 if (AVAILABLE_LOCALES[service] === undefined) { |
243 AVAILABLE_LOCALES[service] = getAvailableLocalesOf(service); | 234 AVAILABLE_LOCALES[service] = getAvailableLocalesOf(service); |
244 } | 235 } |
245 | 236 |
246 // Use either best fit or lookup algorithm to match locales. | 237 // Use either best fit or lookup algorithm to match locales. |
247 if (matcher === 'best fit') { | 238 if (matcher === 'best fit') { |
248 return initializeLocaleList(bestFitSupportedLocalesOf( | 239 return initializeLocaleList(bestFitSupportedLocalesOf( |
249 requestedLocales, AVAILABLE_LOCALES[service])); | 240 requestedLocales, AVAILABLE_LOCALES[service])); |
250 } | 241 } |
251 | 242 |
252 return initializeLocaleList(lookupSupportedLocalesOf( | 243 return initializeLocaleList(lookupSupportedLocalesOf( |
253 requestedLocales, AVAILABLE_LOCALES[service])); | 244 requestedLocales, AVAILABLE_LOCALES[service])); |
254 } | 245 } |
255 | 246 |
256 | 247 |
257 /** | 248 /** |
258 * Returns the subset of the provided BCP 47 language priority list for which | 249 * Returns the subset of the provided BCP 47 language priority list for which |
259 * this service has a matching locale when using the BCP 47 Lookup algorithm. | 250 * this service has a matching locale when using the BCP 47 Lookup algorithm. |
260 * Locales appear in the same order in the returned list as in the input list. | 251 * Locales appear in the same order in the returned list as in the input list. |
261 */ | 252 */ |
262 function lookupSupportedLocalesOf(requestedLocales, availableLocales) { | 253 function lookupSupportedLocalesOf(requestedLocales, availableLocales) { |
263 var matchedLocales = []; | 254 var matchedLocales = []; |
264 for (var i = 0; i < requestedLocales.length; ++i) { | 255 for (var i = 0; i < requestedLocales.length; ++i) { |
265 // Remove -u- extension. | 256 // Remove -u- extension. |
266 var locale = requestedLocales[i].replace(GetUnicodeExtensionRE(), ''); | 257 var locale = requestedLocales[i].replace(GetUnicodeExtensionRE(), ''); |
267 do { | 258 do { |
268 if (!IS_UNDEFINED(availableLocales[locale])) { | 259 if (availableLocales[locale] !== undefined) { |
269 // Push requested locale not the resolved one. | 260 // Push requested locale not the resolved one. |
270 matchedLocales.push(requestedLocales[i]); | 261 matchedLocales.push(requestedLocales[i]); |
271 break; | 262 break; |
272 } | 263 } |
273 // Truncate locale if possible, if not break. | 264 // Truncate locale if possible, if not break. |
274 var pos = locale.lastIndexOf('-'); | 265 var pos = locale.lastIndexOf('-'); |
275 if (pos === -1) { | 266 if (pos === -1) { |
276 break; | 267 break; |
277 } | 268 } |
278 locale = locale.substring(0, pos); | 269 locale = locale.substring(0, pos); |
(...skipping 15 matching lines...) Expand all Loading... |
294 return lookupSupportedLocalesOf(requestedLocales, availableLocales); | 285 return lookupSupportedLocalesOf(requestedLocales, availableLocales); |
295 } | 286 } |
296 | 287 |
297 | 288 |
298 /** | 289 /** |
299 * Returns a getOption function that extracts property value for given | 290 * Returns a getOption function that extracts property value for given |
300 * options object. If property is missing it returns defaultValue. If value | 291 * options object. If property is missing it returns defaultValue. If value |
301 * is out of range for that property it throws RangeError. | 292 * is out of range for that property it throws RangeError. |
302 */ | 293 */ |
303 function getGetOption(options, caller) { | 294 function getGetOption(options, caller) { |
304 if (IS_UNDEFINED(options)) throw MakeError(kDefaultOptionsMissing, caller); | 295 if (options === undefined) throw MakeError(kDefaultOptionsMissing, caller); |
305 | 296 |
306 var getOption = function getOption(property, type, values, defaultValue) { | 297 var getOption = function getOption(property, type, values, defaultValue) { |
307 if (!IS_UNDEFINED(options[property])) { | 298 if (options[property] !== undefined) { |
308 var value = options[property]; | 299 var value = options[property]; |
309 switch (type) { | 300 switch (type) { |
310 case 'boolean': | 301 case 'boolean': |
311 value = GlobalBoolean(value); | 302 value = GlobalBoolean(value); |
312 break; | 303 break; |
313 case 'string': | 304 case 'string': |
314 value = GlobalString(value); | 305 value = GlobalString(value); |
315 break; | 306 break; |
316 case 'number': | 307 case 'number': |
317 value = GlobalNumber(value); | 308 value = GlobalNumber(value); |
318 break; | 309 break; |
319 default: | 310 default: |
320 throw MakeError(kWrongValueType); | 311 throw MakeError(kWrongValueType); |
321 } | 312 } |
322 if (!IS_UNDEFINED(values) && values.indexOf(value) === -1) { | 313 if (values !== undefined && values.indexOf(value) === -1) { |
323 throw MakeRangeError(kValueOutOfRange, value, caller, property); | 314 throw MakeRangeError(kValueOutOfRange, value, caller, property); |
324 } | 315 } |
325 | 316 |
326 return value; | 317 return value; |
327 } | 318 } |
328 | 319 |
329 return defaultValue; | 320 return defaultValue; |
330 } | 321 } |
331 | 322 |
332 return getOption; | 323 return getOption; |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
366 /** | 357 /** |
367 * Returns best matched supported locale and extension info using basic | 358 * Returns best matched supported locale and extension info using basic |
368 * lookup algorithm. | 359 * lookup algorithm. |
369 */ | 360 */ |
370 function lookupMatcher(service, requestedLocales) { | 361 function lookupMatcher(service, requestedLocales) { |
371 if (IS_NULL(service.match(GetServiceRE()))) { | 362 if (IS_NULL(service.match(GetServiceRE()))) { |
372 throw MakeError(kWrongServiceType, service); | 363 throw MakeError(kWrongServiceType, service); |
373 } | 364 } |
374 | 365 |
375 // Cache these, they don't ever change per service. | 366 // Cache these, they don't ever change per service. |
376 if (IS_UNDEFINED(AVAILABLE_LOCALES[service])) { | 367 if (AVAILABLE_LOCALES[service] === undefined) { |
377 AVAILABLE_LOCALES[service] = getAvailableLocalesOf(service); | 368 AVAILABLE_LOCALES[service] = getAvailableLocalesOf(service); |
378 } | 369 } |
379 | 370 |
380 for (var i = 0; i < requestedLocales.length; ++i) { | 371 for (var i = 0; i < requestedLocales.length; ++i) { |
381 // Remove all extensions. | 372 // Remove all extensions. |
382 var locale = requestedLocales[i].replace(GetAnyExtensionRE(), ''); | 373 var locale = requestedLocales[i].replace(GetAnyExtensionRE(), ''); |
383 do { | 374 do { |
384 if (!IS_UNDEFINED(AVAILABLE_LOCALES[service][locale])) { | 375 if (AVAILABLE_LOCALES[service][locale] !== undefined) { |
385 // Return the resolved locale and extension. | 376 // Return the resolved locale and extension. |
386 var extensionMatch = requestedLocales[i].match(GetUnicodeExtensionRE()); | 377 var extensionMatch = requestedLocales[i].match(GetUnicodeExtensionRE()); |
387 var extension = IS_NULL(extensionMatch) ? '' : extensionMatch[0]; | 378 var extension = IS_NULL(extensionMatch) ? '' : extensionMatch[0]; |
388 return {'locale': locale, 'extension': extension, 'position': i}; | 379 return {'locale': locale, 'extension': extension, 'position': i}; |
389 } | 380 } |
390 // Truncate locale if possible. | 381 // Truncate locale if possible. |
391 var pos = locale.lastIndexOf('-'); | 382 var pos = locale.lastIndexOf('-'); |
392 if (pos === -1) { | 383 if (pos === -1) { |
393 break; | 384 break; |
394 } | 385 } |
395 locale = locale.substring(0, pos); | 386 locale = locale.substring(0, pos); |
396 } while (true); | 387 } while (true); |
397 } | 388 } |
398 | 389 |
399 // Didn't find a match, return default. | 390 // Didn't find a match, return default. |
400 if (IS_UNDEFINED(DEFAULT_ICU_LOCALE)) { | 391 if (DEFAULT_ICU_LOCALE === undefined) { |
401 DEFAULT_ICU_LOCALE = %GetDefaultICULocale(); | 392 DEFAULT_ICU_LOCALE = %GetDefaultICULocale(); |
402 } | 393 } |
403 | 394 |
404 return {'locale': DEFAULT_ICU_LOCALE, 'extension': '', 'position': -1}; | 395 return {'locale': DEFAULT_ICU_LOCALE, 'extension': '', 'position': -1}; |
405 } | 396 } |
406 | 397 |
407 | 398 |
408 /** | 399 /** |
409 * Returns best matched supported locale and extension info using | 400 * Returns best matched supported locale and extension info using |
410 * implementation dependend algorithm. | 401 * implementation dependend algorithm. |
(...skipping 14 matching lines...) Expand all Loading... |
425 | 416 |
426 // Assume ['', 'u', ...] input, but don't throw. | 417 // Assume ['', 'u', ...] input, but don't throw. |
427 if (extensionSplit.length <= 2 || | 418 if (extensionSplit.length <= 2 || |
428 (extensionSplit[0] !== '' && extensionSplit[1] !== 'u')) { | 419 (extensionSplit[0] !== '' && extensionSplit[1] !== 'u')) { |
429 return {}; | 420 return {}; |
430 } | 421 } |
431 | 422 |
432 // Key is {2}alphanum, value is {3,8}alphanum. | 423 // Key is {2}alphanum, value is {3,8}alphanum. |
433 // Some keys may not have explicit values (booleans). | 424 // Some keys may not have explicit values (booleans). |
434 var extensionMap = {}; | 425 var extensionMap = {}; |
435 var previousKey = UNDEFINED; | 426 var previousKey = undefined; |
436 for (var i = 2; i < extensionSplit.length; ++i) { | 427 for (var i = 2; i < extensionSplit.length; ++i) { |
437 var length = extensionSplit[i].length; | 428 var length = extensionSplit[i].length; |
438 var element = extensionSplit[i]; | 429 var element = extensionSplit[i]; |
439 if (length === 2) { | 430 if (length === 2) { |
440 extensionMap[element] = UNDEFINED; | 431 extensionMap[element] = undefined; |
441 previousKey = element; | 432 previousKey = element; |
442 } else if (length >= 3 && length <=8 && !IS_UNDEFINED(previousKey)) { | 433 } else if (length >= 3 && length <=8 && previousKey !== undefined) { |
443 extensionMap[previousKey] = element; | 434 extensionMap[previousKey] = element; |
444 previousKey = UNDEFINED; | 435 previousKey = undefined; |
445 } else { | 436 } else { |
446 // There is a value that's too long, or that doesn't have a key. | 437 // There is a value that's too long, or that doesn't have a key. |
447 return {}; | 438 return {}; |
448 } | 439 } |
449 } | 440 } |
450 | 441 |
451 return extensionMap; | 442 return extensionMap; |
452 } | 443 } |
453 | 444 |
454 | 445 |
(...skipping 12 matching lines...) Expand all Loading... |
467 | 458 |
468 var updateExtension = function updateExtension(key, value) { | 459 var updateExtension = function updateExtension(key, value) { |
469 return '-' + key + '-' + GlobalString(value); | 460 return '-' + key + '-' + GlobalString(value); |
470 } | 461 } |
471 | 462 |
472 var updateProperty = function updateProperty(property, type, value) { | 463 var updateProperty = function updateProperty(property, type, value) { |
473 if (type === 'boolean' && (typeof value === 'string')) { | 464 if (type === 'boolean' && (typeof value === 'string')) { |
474 value = (value === 'true') ? true : false; | 465 value = (value === 'true') ? true : false; |
475 } | 466 } |
476 | 467 |
477 if (!IS_UNDEFINED(property)) { | 468 if (property !== undefined) { |
478 defineWEProperty(outOptions, property, value); | 469 defineWEProperty(outOptions, property, value); |
479 } | 470 } |
480 } | 471 } |
481 | 472 |
482 for (var key in keyValues) { | 473 for (var key in keyValues) { |
483 if (keyValues.hasOwnProperty(key)) { | 474 if (keyValues.hasOwnProperty(key)) { |
484 var value = UNDEFINED; | 475 var value = undefined; |
485 var map = keyValues[key]; | 476 var map = keyValues[key]; |
486 if (!IS_UNDEFINED(map.property)) { | 477 if (map.property !== undefined) { |
487 // This may return true if user specifies numeric: 'false', since | 478 // This may return true if user specifies numeric: 'false', since |
488 // Boolean('nonempty') === true. | 479 // Boolean('nonempty') === true. |
489 value = getOption(map.property, map.type, map.values); | 480 value = getOption(map.property, map.type, map.values); |
490 } | 481 } |
491 if (!IS_UNDEFINED(value)) { | 482 if (value !== undefined) { |
492 updateProperty(map.property, map.type, value); | 483 updateProperty(map.property, map.type, value); |
493 extension += updateExtension(key, value); | 484 extension += updateExtension(key, value); |
494 continue; | 485 continue; |
495 } | 486 } |
496 // User options didn't have it, check Unicode extension. | 487 // User options didn't have it, check Unicode extension. |
497 // Here we want to convert strings 'true', 'false' into proper Boolean | 488 // Here we want to convert strings 'true', 'false' into proper Boolean |
498 // values (not a user error). | 489 // values (not a user error). |
499 if (extensionMap.hasOwnProperty(key)) { | 490 if (extensionMap.hasOwnProperty(key)) { |
500 value = extensionMap[key]; | 491 value = extensionMap[key]; |
501 if (!IS_UNDEFINED(value)) { | 492 if (value !== undefined) { |
502 updateProperty(map.property, map.type, value); | 493 updateProperty(map.property, map.type, value); |
503 extension += updateExtension(key, value); | 494 extension += updateExtension(key, value); |
504 } else if (map.type === 'boolean') { | 495 } else if (map.type === 'boolean') { |
505 // Boolean keys are allowed not to have values in Unicode extension. | 496 // Boolean keys are allowed not to have values in Unicode extension. |
506 // Those default to true. | 497 // Those default to true. |
507 updateProperty(map.property, map.type, true); | 498 updateProperty(map.property, map.type, true); |
508 extension += updateExtension(key, true); | 499 extension += updateExtension(key, true); |
509 } | 500 } |
510 } | 501 } |
511 } | 502 } |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
592 $objectDefineProperty(object, property, | 583 $objectDefineProperty(object, property, |
593 {value: value, writable: true, enumerable: true}); | 584 {value: value, writable: true, enumerable: true}); |
594 } | 585 } |
595 | 586 |
596 | 587 |
597 /** | 588 /** |
598 * Adds property to an object if the value is not undefined. | 589 * Adds property to an object if the value is not undefined. |
599 * Sets configurable descriptor to false. | 590 * Sets configurable descriptor to false. |
600 */ | 591 */ |
601 function addWEPropertyIfDefined(object, property, value) { | 592 function addWEPropertyIfDefined(object, property, value) { |
602 if (!IS_UNDEFINED(value)) { | 593 if (value !== undefined) { |
603 defineWEProperty(object, property, value); | 594 defineWEProperty(object, property, value); |
604 } | 595 } |
605 } | 596 } |
606 | 597 |
607 | 598 |
608 /** | 599 /** |
609 * Defines a property and sets writable, enumerable and configurable to true. | 600 * Defines a property and sets writable, enumerable and configurable to true. |
610 */ | 601 */ |
611 function defineWECProperty(object, property, value) { | 602 function defineWECProperty(object, property, value) { |
612 $objectDefineProperty(object, property, {value: value, | 603 $objectDefineProperty(object, property, {value: value, |
613 writable: true, | 604 writable: true, |
614 enumerable: true, | 605 enumerable: true, |
615 configurable: true}); | 606 configurable: true}); |
616 } | 607 } |
617 | 608 |
618 | 609 |
619 /** | 610 /** |
620 * Adds property to an object if the value is not undefined. | 611 * Adds property to an object if the value is not undefined. |
621 * Sets all descriptors to true. | 612 * Sets all descriptors to true. |
622 */ | 613 */ |
623 function addWECPropertyIfDefined(object, property, value) { | 614 function addWECPropertyIfDefined(object, property, value) { |
624 if (!IS_UNDEFINED(value)) { | 615 if (value !== undefined) { |
625 defineWECProperty(object, property, value); | 616 defineWECProperty(object, property, value); |
626 } | 617 } |
627 } | 618 } |
628 | 619 |
629 | 620 |
630 /** | 621 /** |
631 * Returns titlecased word, aMeRricA -> America. | 622 * Returns titlecased word, aMeRricA -> America. |
632 */ | 623 */ |
633 function toTitleCaseWord(word) { | 624 function toTitleCaseWord(word) { |
634 return word.substr(0, 1).toUpperCase() + word.substr(1).toLowerCase(); | 625 return word.substr(0, 1).toUpperCase() + word.substr(1).toLowerCase(); |
(...skipping 27 matching lines...) Expand all Loading... |
662 return tag; | 653 return tag; |
663 } | 654 } |
664 | 655 |
665 | 656 |
666 /** | 657 /** |
667 * Returns an array where all locales are canonicalized and duplicates removed. | 658 * Returns an array where all locales are canonicalized and duplicates removed. |
668 * Throws on locales that are not well formed BCP47 tags. | 659 * Throws on locales that are not well formed BCP47 tags. |
669 */ | 660 */ |
670 function initializeLocaleList(locales) { | 661 function initializeLocaleList(locales) { |
671 var seen = []; | 662 var seen = []; |
672 if (IS_UNDEFINED(locales)) { | 663 if (locales === undefined) { |
673 // Constructor is called without arguments. | 664 // Constructor is called without arguments. |
674 seen = []; | 665 seen = []; |
675 } else { | 666 } else { |
676 // We allow single string localeID. | 667 // We allow single string localeID. |
677 if (typeof locales === 'string') { | 668 if (typeof locales === 'string') { |
678 seen.push(canonicalizeLanguageTag(locales)); | 669 seen.push(canonicalizeLanguageTag(locales)); |
679 return freezeArray(seen); | 670 return freezeArray(seen); |
680 } | 671 } |
681 | 672 |
682 var o = $toObject(locales); | 673 var o = $toObject(locales); |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
795 | 786 |
796 /** | 787 /** |
797 * Initializes the given object so it's a valid Collator instance. | 788 * Initializes the given object so it's a valid Collator instance. |
798 * Useful for subclassing. | 789 * Useful for subclassing. |
799 */ | 790 */ |
800 function initializeCollator(collator, locales, options) { | 791 function initializeCollator(collator, locales, options) { |
801 if (%IsInitializedIntlObject(collator)) { | 792 if (%IsInitializedIntlObject(collator)) { |
802 throw MakeTypeError(kReinitializeIntl, "Collator"); | 793 throw MakeTypeError(kReinitializeIntl, "Collator"); |
803 } | 794 } |
804 | 795 |
805 if (IS_UNDEFINED(options)) { | 796 if (options === undefined) { |
806 options = {}; | 797 options = {}; |
807 } | 798 } |
808 | 799 |
809 var getOption = getGetOption(options, 'collator'); | 800 var getOption = getGetOption(options, 'collator'); |
810 | 801 |
811 var internalOptions = {}; | 802 var internalOptions = {}; |
812 | 803 |
813 defineWEProperty(internalOptions, 'usage', getOption( | 804 defineWEProperty(internalOptions, 'usage', getOption( |
814 'usage', 'string', ['sort', 'search'], 'sort')); | 805 'usage', 'string', ['sort', 'search'], 'sort')); |
815 | 806 |
816 var sensitivity = getOption('sensitivity', 'string', | 807 var sensitivity = getOption('sensitivity', 'string', |
817 ['base', 'accent', 'case', 'variant']); | 808 ['base', 'accent', 'case', 'variant']); |
818 if (IS_UNDEFINED(sensitivity) && internalOptions.usage === 'sort') { | 809 if (sensitivity === undefined && internalOptions.usage === 'sort') { |
819 sensitivity = 'variant'; | 810 sensitivity = 'variant'; |
820 } | 811 } |
821 defineWEProperty(internalOptions, 'sensitivity', sensitivity); | 812 defineWEProperty(internalOptions, 'sensitivity', sensitivity); |
822 | 813 |
823 defineWEProperty(internalOptions, 'ignorePunctuation', getOption( | 814 defineWEProperty(internalOptions, 'ignorePunctuation', getOption( |
824 'ignorePunctuation', 'boolean', UNDEFINED, false)); | 815 'ignorePunctuation', 'boolean', undefined, false)); |
825 | 816 |
826 var locale = resolveLocale('collator', locales, options); | 817 var locale = resolveLocale('collator', locales, options); |
827 | 818 |
828 // 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 |
829 // them as options. | 820 // them as options. |
830 // 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 |
831 // usage: sort, and its value can't be 'standard' or 'search'. | 822 // usage: sort, and its value can't be 'standard' or 'search'. |
832 var extensionMap = parseExtension(locale.extension); | 823 var extensionMap = parseExtension(locale.extension); |
833 | 824 |
834 /** | 825 /** |
(...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1000 currency.match(/[^A-Za-z]/) == null; | 991 currency.match(/[^A-Za-z]/) == null; |
1001 } | 992 } |
1002 | 993 |
1003 | 994 |
1004 /** | 995 /** |
1005 * Returns the valid digit count for a property, or throws RangeError on | 996 * Returns the valid digit count for a property, or throws RangeError on |
1006 * a value out of the range. | 997 * a value out of the range. |
1007 */ | 998 */ |
1008 function getNumberOption(options, property, min, max, fallback) { | 999 function getNumberOption(options, property, min, max, fallback) { |
1009 var value = options[property]; | 1000 var value = options[property]; |
1010 if (!IS_UNDEFINED(value)) { | 1001 if (value !== undefined) { |
1011 value = GlobalNumber(value); | 1002 value = GlobalNumber(value); |
1012 if ($isNaN(value) || value < min || value > max) { | 1003 if ($isNaN(value) || value < min || value > max) { |
1013 throw MakeRangeError(kPropertyValueOutOfRange, property); | 1004 throw MakeRangeError(kPropertyValueOutOfRange, property); |
1014 } | 1005 } |
1015 return MathFloor(value); | 1006 return $floor(value); |
1016 } | 1007 } |
1017 | 1008 |
1018 return fallback; | 1009 return fallback; |
1019 } | 1010 } |
1020 | 1011 |
1021 | 1012 |
1022 /** | 1013 /** |
1023 * Initializes the given object so it's a valid NumberFormat instance. | 1014 * Initializes the given object so it's a valid NumberFormat instance. |
1024 * Useful for subclassing. | 1015 * Useful for subclassing. |
1025 */ | 1016 */ |
1026 function initializeNumberFormat(numberFormat, locales, options) { | 1017 function initializeNumberFormat(numberFormat, locales, options) { |
1027 if (%IsInitializedIntlObject(numberFormat)) { | 1018 if (%IsInitializedIntlObject(numberFormat)) { |
1028 throw MakeTypeError(kReinitializeIntl, "NumberFormat"); | 1019 throw MakeTypeError(kReinitializeIntl, "NumberFormat"); |
1029 } | 1020 } |
1030 | 1021 |
1031 if (IS_UNDEFINED(options)) { | 1022 if (options === undefined) { |
1032 options = {}; | 1023 options = {}; |
1033 } | 1024 } |
1034 | 1025 |
1035 var getOption = getGetOption(options, 'numberformat'); | 1026 var getOption = getGetOption(options, 'numberformat'); |
1036 | 1027 |
1037 var locale = resolveLocale('numberformat', locales, options); | 1028 var locale = resolveLocale('numberformat', locales, options); |
1038 | 1029 |
1039 var internalOptions = {}; | 1030 var internalOptions = {}; |
1040 defineWEProperty(internalOptions, 'style', getOption( | 1031 defineWEProperty(internalOptions, 'style', getOption( |
1041 'style', 'string', ['decimal', 'percent', 'currency'], 'decimal')); | 1032 'style', 'string', ['decimal', 'percent', 'currency'], 'decimal')); |
1042 | 1033 |
1043 var currency = getOption('currency', 'string'); | 1034 var currency = getOption('currency', 'string'); |
1044 if (!IS_UNDEFINED(currency) && !isWellFormedCurrencyCode(currency)) { | 1035 if (currency !== undefined && !isWellFormedCurrencyCode(currency)) { |
1045 throw MakeRangeError(kInvalidCurrencyCode, currency); | 1036 throw MakeRangeError(kInvalidCurrencyCode, currency); |
1046 } | 1037 } |
1047 | 1038 |
1048 if (internalOptions.style === 'currency' && IS_UNDEFINED(currency)) { | 1039 if (internalOptions.style === 'currency' && currency === undefined) { |
1049 throw MakeTypeError(kCurrencyCode); | 1040 throw MakeTypeError(kCurrencyCode); |
1050 } | 1041 } |
1051 | 1042 |
1052 var currencyDisplay = getOption( | 1043 var currencyDisplay = getOption( |
1053 'currencyDisplay', 'string', ['code', 'symbol', 'name'], 'symbol'); | 1044 'currencyDisplay', 'string', ['code', 'symbol', 'name'], 'symbol'); |
1054 if (internalOptions.style === 'currency') { | 1045 if (internalOptions.style === 'currency') { |
1055 defineWEProperty(internalOptions, 'currency', currency.toUpperCase()); | 1046 defineWEProperty(internalOptions, 'currency', currency.toUpperCase()); |
1056 defineWEProperty(internalOptions, 'currencyDisplay', currencyDisplay); | 1047 defineWEProperty(internalOptions, 'currencyDisplay', currencyDisplay); |
1057 } | 1048 } |
1058 | 1049 |
1059 // Digit ranges. | 1050 // Digit ranges. |
1060 var mnid = getNumberOption(options, 'minimumIntegerDigits', 1, 21, 1); | 1051 var mnid = getNumberOption(options, 'minimumIntegerDigits', 1, 21, 1); |
1061 defineWEProperty(internalOptions, 'minimumIntegerDigits', mnid); | 1052 defineWEProperty(internalOptions, 'minimumIntegerDigits', mnid); |
1062 | 1053 |
1063 var mnfd = getNumberOption(options, 'minimumFractionDigits', 0, 20, 0); | 1054 var mnfd = getNumberOption(options, 'minimumFractionDigits', 0, 20, 0); |
1064 defineWEProperty(internalOptions, 'minimumFractionDigits', mnfd); | 1055 defineWEProperty(internalOptions, 'minimumFractionDigits', mnfd); |
1065 | 1056 |
1066 var mxfd = getNumberOption(options, 'maximumFractionDigits', mnfd, 20, 3); | 1057 var mxfd = getNumberOption(options, 'maximumFractionDigits', mnfd, 20, 3); |
1067 defineWEProperty(internalOptions, 'maximumFractionDigits', mxfd); | 1058 defineWEProperty(internalOptions, 'maximumFractionDigits', mxfd); |
1068 | 1059 |
1069 var mnsd = options['minimumSignificantDigits']; | 1060 var mnsd = options['minimumSignificantDigits']; |
1070 var mxsd = options['maximumSignificantDigits']; | 1061 var mxsd = options['maximumSignificantDigits']; |
1071 if (!IS_UNDEFINED(mnsd) || !IS_UNDEFINED(mxsd)) { | 1062 if (mnsd !== undefined || mxsd !== undefined) { |
1072 mnsd = getNumberOption(options, 'minimumSignificantDigits', 1, 21, 0); | 1063 mnsd = getNumberOption(options, 'minimumSignificantDigits', 1, 21, 0); |
1073 defineWEProperty(internalOptions, 'minimumSignificantDigits', mnsd); | 1064 defineWEProperty(internalOptions, 'minimumSignificantDigits', mnsd); |
1074 | 1065 |
1075 mxsd = getNumberOption(options, 'maximumSignificantDigits', mnsd, 21, 21); | 1066 mxsd = getNumberOption(options, 'maximumSignificantDigits', mnsd, 21, 21); |
1076 defineWEProperty(internalOptions, 'maximumSignificantDigits', mxsd); | 1067 defineWEProperty(internalOptions, 'maximumSignificantDigits', mxsd); |
1077 } | 1068 } |
1078 | 1069 |
1079 // Grouping. | 1070 // Grouping. |
1080 defineWEProperty(internalOptions, 'useGrouping', getOption( | 1071 defineWEProperty(internalOptions, 'useGrouping', getOption( |
1081 'useGrouping', 'boolean', UNDEFINED, true)); | 1072 'useGrouping', 'boolean', undefined, true)); |
1082 | 1073 |
1083 // 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 |
1084 // number format, so we need to build that. | 1075 // number format, so we need to build that. |
1085 var extensionMap = parseExtension(locale.extension); | 1076 var extensionMap = parseExtension(locale.extension); |
1086 | 1077 |
1087 /** | 1078 /** |
1088 * Map of Unicode extensions to option properties, and their values and types, | 1079 * Map of Unicode extensions to option properties, and their values and types, |
1089 * for a number format. | 1080 * for a number format. |
1090 */ | 1081 */ |
1091 var NUMBER_FORMAT_KEY_MAP = { | 1082 var NUMBER_FORMAT_KEY_MAP = { |
1092 'nu': {'property': UNDEFINED, 'type': 'string'} | 1083 'nu': {'property': undefined, 'type': 'string'} |
1093 }; | 1084 }; |
1094 | 1085 |
1095 var extension = setOptions(options, extensionMap, NUMBER_FORMAT_KEY_MAP, | 1086 var extension = setOptions(options, extensionMap, NUMBER_FORMAT_KEY_MAP, |
1096 getOption, internalOptions); | 1087 getOption, internalOptions); |
1097 | 1088 |
1098 var requestedLocale = locale.locale + extension; | 1089 var requestedLocale = locale.locale + extension; |
1099 var resolved = $objectDefineProperties({}, { | 1090 var resolved = $objectDefineProperties({}, { |
1100 currency: {writable: true}, | 1091 currency: {writable: true}, |
1101 currencyDisplay: {writable: true}, | 1092 currencyDisplay: {writable: true}, |
1102 locale: {writable: true}, | 1093 locale: {writable: true}, |
1103 maximumFractionDigits: {writable: true}, | 1094 maximumFractionDigits: {writable: true}, |
1104 minimumFractionDigits: {writable: true}, | 1095 minimumFractionDigits: {writable: true}, |
1105 minimumIntegerDigits: {writable: true}, | 1096 minimumIntegerDigits: {writable: true}, |
1106 numberingSystem: {writable: true}, | 1097 numberingSystem: {writable: true}, |
1107 requestedLocale: {value: requestedLocale, writable: true}, | 1098 requestedLocale: {value: requestedLocale, writable: true}, |
1108 style: {value: internalOptions.style, writable: true}, | 1099 style: {value: internalOptions.style, writable: true}, |
1109 useGrouping: {writable: true} | 1100 useGrouping: {writable: true} |
1110 }); | 1101 }); |
1111 if (internalOptions.hasOwnProperty('minimumSignificantDigits')) { | 1102 if (internalOptions.hasOwnProperty('minimumSignificantDigits')) { |
1112 defineWEProperty(resolved, 'minimumSignificantDigits', UNDEFINED); | 1103 defineWEProperty(resolved, 'minimumSignificantDigits', undefined); |
1113 } | 1104 } |
1114 if (internalOptions.hasOwnProperty('maximumSignificantDigits')) { | 1105 if (internalOptions.hasOwnProperty('maximumSignificantDigits')) { |
1115 defineWEProperty(resolved, 'maximumSignificantDigits', UNDEFINED); | 1106 defineWEProperty(resolved, 'maximumSignificantDigits', undefined); |
1116 } | 1107 } |
1117 var formatter = %CreateNumberFormat(requestedLocale, | 1108 var formatter = %CreateNumberFormat(requestedLocale, |
1118 internalOptions, | 1109 internalOptions, |
1119 resolved); | 1110 resolved); |
1120 | 1111 |
1121 // We can't get information about number or currency style from ICU, so we | 1112 // We can't get information about number or currency style from ICU, so we |
1122 // assume user request was fulfilled. | 1113 // assume user request was fulfilled. |
1123 if (internalOptions.style === 'currency') { | 1114 if (internalOptions.style === 'currency') { |
1124 $objectDefineProperty(resolved, 'currencyDisplay', {value: currencyDisplay, | 1115 $objectDefineProperty(resolved, 'currencyDisplay', {value: currencyDisplay, |
1125 writable: true}); | 1116 writable: true}); |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1274 ['2-digit', 'numeric', 'narrow', 'short', 'long']); | 1265 ['2-digit', 'numeric', 'narrow', 'short', 'long']); |
1275 ldmlString += appendToLDMLString(option, {'2-digit': 'MM', 'numeric': 'M', | 1266 ldmlString += appendToLDMLString(option, {'2-digit': 'MM', 'numeric': 'M', |
1276 'narrow': 'MMMMM', 'short': 'MMM', 'long': 'MMMM'}); | 1267 'narrow': 'MMMMM', 'short': 'MMM', 'long': 'MMMM'}); |
1277 | 1268 |
1278 option = getOption('day', 'string', ['2-digit', 'numeric']); | 1269 option = getOption('day', 'string', ['2-digit', 'numeric']); |
1279 ldmlString += appendToLDMLString( | 1270 ldmlString += appendToLDMLString( |
1280 option, {'2-digit': 'dd', 'numeric': 'd'}); | 1271 option, {'2-digit': 'dd', 'numeric': 'd'}); |
1281 | 1272 |
1282 var hr12 = getOption('hour12', 'boolean'); | 1273 var hr12 = getOption('hour12', 'boolean'); |
1283 option = getOption('hour', 'string', ['2-digit', 'numeric']); | 1274 option = getOption('hour', 'string', ['2-digit', 'numeric']); |
1284 if (IS_UNDEFINED(hr12)) { | 1275 if (hr12 === undefined) { |
1285 ldmlString += appendToLDMLString(option, {'2-digit': 'jj', 'numeric': 'j'}); | 1276 ldmlString += appendToLDMLString(option, {'2-digit': 'jj', 'numeric': 'j'}); |
1286 } else if (hr12 === true) { | 1277 } else if (hr12 === true) { |
1287 ldmlString += appendToLDMLString(option, {'2-digit': 'hh', 'numeric': 'h'}); | 1278 ldmlString += appendToLDMLString(option, {'2-digit': 'hh', 'numeric': 'h'}); |
1288 } else { | 1279 } else { |
1289 ldmlString += appendToLDMLString(option, {'2-digit': 'HH', 'numeric': 'H'}); | 1280 ldmlString += appendToLDMLString(option, {'2-digit': 'HH', 'numeric': 'H'}); |
1290 } | 1281 } |
1291 | 1282 |
1292 option = getOption('minute', 'string', ['2-digit', 'numeric']); | 1283 option = getOption('minute', 'string', ['2-digit', 'numeric']); |
1293 ldmlString += appendToLDMLString(option, {'2-digit': 'mm', 'numeric': 'm'}); | 1284 ldmlString += appendToLDMLString(option, {'2-digit': 'mm', 'numeric': 'm'}); |
1294 | 1285 |
1295 option = getOption('second', 'string', ['2-digit', 'numeric']); | 1286 option = getOption('second', 'string', ['2-digit', 'numeric']); |
1296 ldmlString += appendToLDMLString(option, {'2-digit': 'ss', 'numeric': 's'}); | 1287 ldmlString += appendToLDMLString(option, {'2-digit': 'ss', 'numeric': 's'}); |
1297 | 1288 |
1298 option = getOption('timeZoneName', 'string', ['short', 'long']); | 1289 option = getOption('timeZoneName', 'string', ['short', 'long']); |
1299 ldmlString += appendToLDMLString(option, {short: 'z', long: 'zzzz'}); | 1290 ldmlString += appendToLDMLString(option, {short: 'z', long: 'zzzz'}); |
1300 | 1291 |
1301 return ldmlString; | 1292 return ldmlString; |
1302 } | 1293 } |
1303 | 1294 |
1304 | 1295 |
1305 /** | 1296 /** |
1306 * Returns either LDML equivalent of the current option or empty string. | 1297 * Returns either LDML equivalent of the current option or empty string. |
1307 */ | 1298 */ |
1308 function appendToLDMLString(option, pairs) { | 1299 function appendToLDMLString(option, pairs) { |
1309 if (!IS_UNDEFINED(option)) { | 1300 if (option !== undefined) { |
1310 return pairs[option]; | 1301 return pairs[option]; |
1311 } else { | 1302 } else { |
1312 return ''; | 1303 return ''; |
1313 } | 1304 } |
1314 } | 1305 } |
1315 | 1306 |
1316 | 1307 |
1317 /** | 1308 /** |
1318 * Returns object that matches LDML representation of the date. | 1309 * Returns object that matches LDML representation of the date. |
1319 */ | 1310 */ |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1373 options = appendToDateTimeObject( | 1364 options = appendToDateTimeObject( |
1374 options, 'timeZoneName', match, {z: 'short', zzzz: 'long'}); | 1365 options, 'timeZoneName', match, {z: 'short', zzzz: 'long'}); |
1375 | 1366 |
1376 return options; | 1367 return options; |
1377 } | 1368 } |
1378 | 1369 |
1379 | 1370 |
1380 function appendToDateTimeObject(options, option, match, pairs) { | 1371 function appendToDateTimeObject(options, option, match, pairs) { |
1381 if (IS_NULL(match)) { | 1372 if (IS_NULL(match)) { |
1382 if (!options.hasOwnProperty(option)) { | 1373 if (!options.hasOwnProperty(option)) { |
1383 defineWEProperty(options, option, UNDEFINED); | 1374 defineWEProperty(options, option, undefined); |
1384 } | 1375 } |
1385 return options; | 1376 return options; |
1386 } | 1377 } |
1387 | 1378 |
1388 var property = match[0]; | 1379 var property = match[0]; |
1389 defineWEProperty(options, option, pairs[property]); | 1380 defineWEProperty(options, option, pairs[property]); |
1390 | 1381 |
1391 return options; | 1382 return options; |
1392 } | 1383 } |
1393 | 1384 |
1394 | 1385 |
1395 /** | 1386 /** |
1396 * Returns options with at least default values in it. | 1387 * Returns options with at least default values in it. |
1397 */ | 1388 */ |
1398 function toDateTimeOptions(options, required, defaults) { | 1389 function toDateTimeOptions(options, required, defaults) { |
1399 if (IS_UNDEFINED(options)) { | 1390 if (options === undefined) { |
1400 options = {}; | 1391 options = {}; |
1401 } else { | 1392 } else { |
1402 options = TO_OBJECT_INLINE(options); | 1393 options = TO_OBJECT_INLINE(options); |
1403 } | 1394 } |
1404 | 1395 |
1405 var needsDefault = true; | 1396 var needsDefault = true; |
1406 if ((required === 'date' || required === 'any') && | 1397 if ((required === 'date' || required === 'any') && |
1407 (!IS_UNDEFINED(options.weekday) || !IS_UNDEFINED(options.year) || | 1398 (options.weekday !== undefined || options.year !== undefined || |
1408 !IS_UNDEFINED(options.month) || !IS_UNDEFINED(options.day))) { | 1399 options.month !== undefined || options.day !== undefined)) { |
1409 needsDefault = false; | 1400 needsDefault = false; |
1410 } | 1401 } |
1411 | 1402 |
1412 if ((required === 'time' || required === 'any') && | 1403 if ((required === 'time' || required === 'any') && |
1413 (!IS_UNDEFINED(options.hour) || !IS_UNDEFINED(options.minute) || | 1404 (options.hour !== undefined || options.minute !== undefined || |
1414 !IS_UNDEFINED(options.second))) { | 1405 options.second !== undefined)) { |
1415 needsDefault = false; | 1406 needsDefault = false; |
1416 } | 1407 } |
1417 | 1408 |
1418 if (needsDefault && (defaults === 'date' || defaults === 'all')) { | 1409 if (needsDefault && (defaults === 'date' || defaults === 'all')) { |
1419 $objectDefineProperty(options, 'year', {value: 'numeric', | 1410 $objectDefineProperty(options, 'year', {value: 'numeric', |
1420 writable: true, | 1411 writable: true, |
1421 enumerable: true, | 1412 enumerable: true, |
1422 configurable: true}); | 1413 configurable: true}); |
1423 $objectDefineProperty(options, 'month', {value: 'numeric', | 1414 $objectDefineProperty(options, 'month', {value: 'numeric', |
1424 writable: true, | 1415 writable: true, |
(...skipping 27 matching lines...) Expand all Loading... |
1452 /** | 1443 /** |
1453 * Initializes the given object so it's a valid DateTimeFormat instance. | 1444 * Initializes the given object so it's a valid DateTimeFormat instance. |
1454 * Useful for subclassing. | 1445 * Useful for subclassing. |
1455 */ | 1446 */ |
1456 function initializeDateTimeFormat(dateFormat, locales, options) { | 1447 function initializeDateTimeFormat(dateFormat, locales, options) { |
1457 | 1448 |
1458 if (%IsInitializedIntlObject(dateFormat)) { | 1449 if (%IsInitializedIntlObject(dateFormat)) { |
1459 throw MakeTypeError(kReinitializeIntl, "DateTimeFormat"); | 1450 throw MakeTypeError(kReinitializeIntl, "DateTimeFormat"); |
1460 } | 1451 } |
1461 | 1452 |
1462 if (IS_UNDEFINED(options)) { | 1453 if (options === undefined) { |
1463 options = {}; | 1454 options = {}; |
1464 } | 1455 } |
1465 | 1456 |
1466 var locale = resolveLocale('dateformat', locales, options); | 1457 var locale = resolveLocale('dateformat', locales, options); |
1467 | 1458 |
1468 options = toDateTimeOptions(options, 'any', 'date'); | 1459 options = toDateTimeOptions(options, 'any', 'date'); |
1469 | 1460 |
1470 var getOption = getGetOption(options, 'dateformat'); | 1461 var getOption = getGetOption(options, 'dateformat'); |
1471 | 1462 |
1472 // We implement only best fit algorithm, but still need to check | 1463 // We implement only best fit algorithm, but still need to check |
(...skipping 12 matching lines...) Expand all Loading... |
1485 // 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 |
1486 // we need to build that. | 1477 // we need to build that. |
1487 var internalOptions = {}; | 1478 var internalOptions = {}; |
1488 var extensionMap = parseExtension(locale.extension); | 1479 var extensionMap = parseExtension(locale.extension); |
1489 | 1480 |
1490 /** | 1481 /** |
1491 * Map of Unicode extensions to option properties, and their values and types, | 1482 * Map of Unicode extensions to option properties, and their values and types, |
1492 * for a date/time format. | 1483 * for a date/time format. |
1493 */ | 1484 */ |
1494 var DATETIME_FORMAT_KEY_MAP = { | 1485 var DATETIME_FORMAT_KEY_MAP = { |
1495 'ca': {'property': UNDEFINED, 'type': 'string'}, | 1486 'ca': {'property': undefined, 'type': 'string'}, |
1496 'nu': {'property': UNDEFINED, 'type': 'string'} | 1487 'nu': {'property': undefined, 'type': 'string'} |
1497 }; | 1488 }; |
1498 | 1489 |
1499 var extension = setOptions(options, extensionMap, DATETIME_FORMAT_KEY_MAP, | 1490 var extension = setOptions(options, extensionMap, DATETIME_FORMAT_KEY_MAP, |
1500 getOption, internalOptions); | 1491 getOption, internalOptions); |
1501 | 1492 |
1502 var requestedLocale = locale.locale + extension; | 1493 var requestedLocale = locale.locale + extension; |
1503 var resolved = $objectDefineProperties({}, { | 1494 var resolved = $objectDefineProperties({}, { |
1504 calendar: {writable: true}, | 1495 calendar: {writable: true}, |
1505 day: {writable: true}, | 1496 day: {writable: true}, |
1506 era: {writable: true}, | 1497 era: {writable: true}, |
1507 hour12: {writable: true}, | 1498 hour12: {writable: true}, |
1508 hour: {writable: true}, | 1499 hour: {writable: true}, |
1509 locale: {writable: true}, | 1500 locale: {writable: true}, |
1510 minute: {writable: true}, | 1501 minute: {writable: true}, |
1511 month: {writable: true}, | 1502 month: {writable: true}, |
1512 numberingSystem: {writable: true}, | 1503 numberingSystem: {writable: true}, |
1513 pattern: {writable: true}, | 1504 pattern: {writable: true}, |
1514 requestedLocale: {value: requestedLocale, writable: true}, | 1505 requestedLocale: {value: requestedLocale, writable: true}, |
1515 second: {writable: true}, | 1506 second: {writable: true}, |
1516 timeZone: {writable: true}, | 1507 timeZone: {writable: true}, |
1517 timeZoneName: {writable: true}, | 1508 timeZoneName: {writable: true}, |
1518 tz: {value: tz, writable: true}, | 1509 tz: {value: tz, writable: true}, |
1519 weekday: {writable: true}, | 1510 weekday: {writable: true}, |
1520 year: {writable: true} | 1511 year: {writable: true} |
1521 }); | 1512 }); |
1522 | 1513 |
1523 var formatter = %CreateDateTimeFormat( | 1514 var formatter = %CreateDateTimeFormat( |
1524 requestedLocale, {skeleton: ldmlString, timeZone: tz}, resolved); | 1515 requestedLocale, {skeleton: ldmlString, timeZone: tz}, resolved); |
1525 | 1516 |
1526 if (!IS_UNDEFINED(tz) && tz !== resolved.timeZone) { | 1517 if (tz !== undefined && tz !== resolved.timeZone) { |
1527 throw MakeRangeError(kUnsupportedTimeZone, tz); | 1518 throw MakeRangeError(kUnsupportedTimeZone, tz); |
1528 } | 1519 } |
1529 | 1520 |
1530 %MarkAsInitializedIntlObjectOfType(dateFormat, 'dateformat', formatter); | 1521 %MarkAsInitializedIntlObjectOfType(dateFormat, 'dateformat', formatter); |
1531 $objectDefineProperty(dateFormat, 'resolved', {value: resolved}); | 1522 $objectDefineProperty(dateFormat, 'resolved', {value: resolved}); |
1532 | 1523 |
1533 return dateFormat; | 1524 return dateFormat; |
1534 } | 1525 } |
1535 | 1526 |
1536 | 1527 |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1582 'chinese': 'chinese', | 1573 'chinese': 'chinese', |
1583 'indian': 'indian', | 1574 'indian': 'indian', |
1584 'coptic': 'coptic', | 1575 'coptic': 'coptic', |
1585 'ethiopic': 'ethiopic', | 1576 'ethiopic': 'ethiopic', |
1586 'ethiopic-amete-alem': 'ethioaa' | 1577 'ethiopic-amete-alem': 'ethioaa' |
1587 }; | 1578 }; |
1588 | 1579 |
1589 var format = this; | 1580 var format = this; |
1590 var fromPattern = fromLDMLString(format.resolved.pattern); | 1581 var fromPattern = fromLDMLString(format.resolved.pattern); |
1591 var userCalendar = ICU_CALENDAR_MAP[format.resolved.calendar]; | 1582 var userCalendar = ICU_CALENDAR_MAP[format.resolved.calendar]; |
1592 if (IS_UNDEFINED(userCalendar)) { | 1583 if (userCalendar === undefined) { |
1593 // 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 |
1594 // it would be too strict to throw for this. | 1585 // it would be too strict to throw for this. |
1595 userCalendar = format.resolved.calendar; | 1586 userCalendar = format.resolved.calendar; |
1596 } | 1587 } |
1597 | 1588 |
1598 var locale = getOptimalLanguageTag(format.resolved.requestedLocale, | 1589 var locale = getOptimalLanguageTag(format.resolved.requestedLocale, |
1599 format.resolved.locale); | 1590 format.resolved.locale); |
1600 | 1591 |
1601 var result = { | 1592 var result = { |
1602 locale: locale, | 1593 locale: locale, |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1646 %SetNativeFlag(Intl.DateTimeFormat.supportedLocalesOf); | 1637 %SetNativeFlag(Intl.DateTimeFormat.supportedLocalesOf); |
1647 | 1638 |
1648 | 1639 |
1649 /** | 1640 /** |
1650 * Returns a String value representing the result of calling ToNumber(date) | 1641 * Returns a String value representing the result of calling ToNumber(date) |
1651 * according to the effective locale and the formatting options of this | 1642 * according to the effective locale and the formatting options of this |
1652 * DateTimeFormat. | 1643 * DateTimeFormat. |
1653 */ | 1644 */ |
1654 function formatDate(formatter, dateValue) { | 1645 function formatDate(formatter, dateValue) { |
1655 var dateMs; | 1646 var dateMs; |
1656 if (IS_UNDEFINED(dateValue)) { | 1647 if (dateValue === undefined) { |
1657 dateMs = GlobalDate.now(); | 1648 dateMs = GlobalDate.now(); |
1658 } else { | 1649 } else { |
1659 dateMs = $toNumber(dateValue); | 1650 dateMs = $toNumber(dateValue); |
1660 } | 1651 } |
1661 | 1652 |
1662 if (!$isFinite(dateMs)) throw MakeRangeError(kDateRange); | 1653 if (!$isFinite(dateMs)) throw MakeRangeError(kDateRange); |
1663 | 1654 |
1664 return %InternalDateFormat(%GetImplFromInitializedIntlObject(formatter), | 1655 return %InternalDateFormat(%GetImplFromInitializedIntlObject(formatter), |
1665 new GlobalDate(dateMs)); | 1656 new GlobalDate(dateMs)); |
1666 } | 1657 } |
(...skipping 15 matching lines...) Expand all Loading... |
1682 addBoundMethod(Intl.DateTimeFormat, 'format', formatDate, 0); | 1673 addBoundMethod(Intl.DateTimeFormat, 'format', formatDate, 0); |
1683 addBoundMethod(Intl.DateTimeFormat, 'v8Parse', parseDate, 1); | 1674 addBoundMethod(Intl.DateTimeFormat, 'v8Parse', parseDate, 1); |
1684 | 1675 |
1685 | 1676 |
1686 /** | 1677 /** |
1687 * Returns canonical Area/Location name, or throws an exception if the zone | 1678 * Returns canonical Area/Location name, or throws an exception if the zone |
1688 * name is invalid IANA name. | 1679 * name is invalid IANA name. |
1689 */ | 1680 */ |
1690 function canonicalizeTimeZoneID(tzID) { | 1681 function canonicalizeTimeZoneID(tzID) { |
1691 // Skip undefined zones. | 1682 // Skip undefined zones. |
1692 if (IS_UNDEFINED(tzID)) { | 1683 if (tzID === undefined) { |
1693 return tzID; | 1684 return tzID; |
1694 } | 1685 } |
1695 | 1686 |
1696 // Special case handling (UTC, GMT). | 1687 // Special case handling (UTC, GMT). |
1697 var upperID = tzID.toUpperCase(); | 1688 var upperID = tzID.toUpperCase(); |
1698 if (upperID === 'UTC' || upperID === 'GMT' || | 1689 if (upperID === 'UTC' || upperID === 'GMT' || |
1699 upperID === 'ETC/UTC' || upperID === 'ETC/GMT') { | 1690 upperID === 'ETC/UTC' || upperID === 'ETC/GMT') { |
1700 return 'UTC'; | 1691 return 'UTC'; |
1701 } | 1692 } |
1702 | 1693 |
1703 // We expect only _ and / beside ASCII letters. | 1694 // We expect only _ and / beside ASCII letters. |
1704 // All inputs should conform to Area/Location from now on. | 1695 // All inputs should conform to Area/Location from now on. |
1705 var match = GetTimezoneNameCheckRE().exec(tzID); | 1696 var match = GetTimezoneNameCheckRE().exec(tzID); |
1706 if (IS_NULL(match)) throw MakeRangeError(kExpectedLocation, tzID); | 1697 if (IS_NULL(match)) throw MakeRangeError(kExpectedLocation, tzID); |
1707 | 1698 |
1708 var result = toTitleCaseWord(match[1]) + '/' + toTitleCaseWord(match[2]); | 1699 var result = toTitleCaseWord(match[1]) + '/' + toTitleCaseWord(match[2]); |
1709 var i = 3; | 1700 var i = 3; |
1710 while (!IS_UNDEFINED(match[i]) && i < match.length) { | 1701 while (match[i] !== undefined && i < match.length) { |
1711 result = result + '_' + toTitleCaseWord(match[i]); | 1702 result = result + '_' + toTitleCaseWord(match[i]); |
1712 i++; | 1703 i++; |
1713 } | 1704 } |
1714 | 1705 |
1715 return result; | 1706 return result; |
1716 } | 1707 } |
1717 | 1708 |
1718 /** | 1709 /** |
1719 * Initializes the given object so it's a valid BreakIterator instance. | 1710 * Initializes the given object so it's a valid BreakIterator instance. |
1720 * Useful for subclassing. | 1711 * Useful for subclassing. |
1721 */ | 1712 */ |
1722 function initializeBreakIterator(iterator, locales, options) { | 1713 function initializeBreakIterator(iterator, locales, options) { |
1723 if (%IsInitializedIntlObject(iterator)) { | 1714 if (%IsInitializedIntlObject(iterator)) { |
1724 throw MakeTypeError(kReinitializeIntl, "v8BreakIterator"); | 1715 throw MakeTypeError(kReinitializeIntl, "v8BreakIterator"); |
1725 } | 1716 } |
1726 | 1717 |
1727 if (IS_UNDEFINED(options)) { | 1718 if (options === undefined) { |
1728 options = {}; | 1719 options = {}; |
1729 } | 1720 } |
1730 | 1721 |
1731 var getOption = getGetOption(options, 'breakiterator'); | 1722 var getOption = getGetOption(options, 'breakiterator'); |
1732 | 1723 |
1733 var internalOptions = {}; | 1724 var internalOptions = {}; |
1734 | 1725 |
1735 defineWEProperty(internalOptions, 'type', getOption( | 1726 defineWEProperty(internalOptions, 'type', getOption( |
1736 'type', 'string', ['character', 'word', 'sentence', 'line'], 'word')); | 1727 'type', 'string', ['character', 'word', 'sentence', 'line'], 'word')); |
1737 | 1728 |
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1880 'numberformat': Intl.NumberFormat, | 1871 'numberformat': Intl.NumberFormat, |
1881 'dateformatall': Intl.DateTimeFormat, | 1872 'dateformatall': Intl.DateTimeFormat, |
1882 'dateformatdate': Intl.DateTimeFormat, | 1873 'dateformatdate': Intl.DateTimeFormat, |
1883 'dateformattime': Intl.DateTimeFormat | 1874 'dateformattime': Intl.DateTimeFormat |
1884 }; | 1875 }; |
1885 | 1876 |
1886 | 1877 |
1887 // Default (created with undefined locales and options parameters) collator, | 1878 // Default (created with undefined locales and options parameters) collator, |
1888 // number and date format instances. They'll be created as needed. | 1879 // number and date format instances. They'll be created as needed. |
1889 var defaultObjects = { | 1880 var defaultObjects = { |
1890 'collator': UNDEFINED, | 1881 'collator': undefined, |
1891 'numberformat': UNDEFINED, | 1882 'numberformat': undefined, |
1892 'dateformatall': UNDEFINED, | 1883 'dateformatall': undefined, |
1893 'dateformatdate': UNDEFINED, | 1884 'dateformatdate': undefined, |
1894 'dateformattime': UNDEFINED, | 1885 'dateformattime': undefined, |
1895 }; | 1886 }; |
1896 | 1887 |
1897 | 1888 |
1898 /** | 1889 /** |
1899 * Returns cached or newly created instance of a given service. | 1890 * Returns cached or newly created instance of a given service. |
1900 * We cache only default instances (where no locales or options are provided). | 1891 * We cache only default instances (where no locales or options are provided). |
1901 */ | 1892 */ |
1902 function cachedOrNewService(service, locales, options, defaults) { | 1893 function cachedOrNewService(service, locales, options, defaults) { |
1903 var useOptions = (IS_UNDEFINED(defaults)) ? options : defaults; | 1894 var useOptions = (defaults === undefined) ? options : defaults; |
1904 if (IS_UNDEFINED(locales) && IS_UNDEFINED(options)) { | 1895 if (locales === undefined && options === undefined) { |
1905 if (IS_UNDEFINED(defaultObjects[service])) { | 1896 if (defaultObjects[service] === undefined) { |
1906 defaultObjects[service] = new savedObjects[service](locales, useOptions); | 1897 defaultObjects[service] = new savedObjects[service](locales, useOptions); |
1907 } | 1898 } |
1908 return defaultObjects[service]; | 1899 return defaultObjects[service]; |
1909 } | 1900 } |
1910 return new savedObjects[service](locales, useOptions); | 1901 return new savedObjects[service](locales, useOptions); |
1911 } | 1902 } |
1912 | 1903 |
1913 | 1904 |
1914 /** | 1905 /** |
1915 * Compares this and that, and returns less than 0, 0 or greater than 0 value. | 1906 * Compares this and that, and returns less than 0, 0 or greater than 0 value. |
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2049 } | 2040 } |
2050 | 2041 |
2051 var locales = %_Arguments(0); | 2042 var locales = %_Arguments(0); |
2052 var options = %_Arguments(1); | 2043 var options = %_Arguments(1); |
2053 return toLocaleDateTime( | 2044 return toLocaleDateTime( |
2054 this, locales, options, 'time', 'time', 'dateformattime'); | 2045 this, locales, options, 'time', 'time', 'dateformattime'); |
2055 } | 2046 } |
2056 ); | 2047 ); |
2057 | 2048 |
2058 }) | 2049 }) |
OLD | NEW |