Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6)

Side by Side Diff: src/i18n.js

Issue 1398733002: Move builtin JavaScript sources into own directory. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Also move macros.py file. Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/harmony-typedarray.js ('k') | src/iterator-prototype.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 // ECMAScript 402 API implementation.
6
7 /**
8 * Intl object is a single object that has some named properties,
9 * all of which are constructors.
10 */
11 (function(global, utils) {
12
13 "use strict";
14
15 %CheckIsBootstrapping();
16
17 // -------------------------------------------------------------------
18 // Imports
19
20 var ArrayIndexOf;
21 var ArrayJoin;
22 var ArrayPush;
23 var IsFinite;
24 var IsNaN;
25 var GlobalBoolean = global.Boolean;
26 var GlobalDate = global.Date;
27 var GlobalNumber = global.Number;
28 var GlobalRegExp = global.RegExp;
29 var GlobalString = global.String;
30 var MathFloor;
31 var ObjectDefineProperties = utils.ImportNow("ObjectDefineProperties");
32 var ObjectDefineProperty = utils.ImportNow("ObjectDefineProperty");
33 var RegExpTest;
34 var StringIndexOf;
35 var StringLastIndexOf;
36 var StringMatch;
37 var StringReplace;
38 var StringSplit;
39 var StringSubstr;
40 var StringSubstring;
41
42 utils.Import(function(from) {
43 ArrayIndexOf = from.ArrayIndexOf;
44 ArrayJoin = from.ArrayJoin;
45 ArrayPush = from.ArrayPush;
46 IsFinite = from.IsFinite;
47 IsNaN = from.IsNaN;
48 MathFloor = from.MathFloor;
49 RegExpTest = from.RegExpTest;
50 StringIndexOf = from.StringIndexOf;
51 StringLastIndexOf = from.StringLastIndexOf;
52 StringMatch = from.StringMatch;
53 StringReplace = from.StringReplace;
54 StringSplit = from.StringSplit;
55 StringSubstr = from.StringSubstr;
56 StringSubstring = from.StringSubstring;
57 });
58
59 // -------------------------------------------------------------------
60
61 var Intl = {};
62
63 %AddNamedProperty(global, "Intl", Intl, DONT_ENUM);
64
65 /**
66 * Caches available locales for each service.
67 */
68 var AVAILABLE_LOCALES = {
69 'collator': UNDEFINED,
70 'numberformat': UNDEFINED,
71 'dateformat': UNDEFINED,
72 'breakiterator': UNDEFINED
73 };
74
75 /**
76 * Caches default ICU locale.
77 */
78 var DEFAULT_ICU_LOCALE = UNDEFINED;
79
80 /**
81 * Unicode extension regular expression.
82 */
83 var UNICODE_EXTENSION_RE = UNDEFINED;
84
85 function GetUnicodeExtensionRE() {
86 if (IS_UNDEFINED(UNDEFINED)) {
87 UNICODE_EXTENSION_RE = new GlobalRegExp('-u(-[a-z0-9]{2,8})+', 'g');
88 }
89 return UNICODE_EXTENSION_RE;
90 }
91
92 /**
93 * Matches any Unicode extension.
94 */
95 var ANY_EXTENSION_RE = UNDEFINED;
96
97 function GetAnyExtensionRE() {
98 if (IS_UNDEFINED(ANY_EXTENSION_RE)) {
99 ANY_EXTENSION_RE = new GlobalRegExp('-[a-z0-9]{1}-.*', 'g');
100 }
101 return ANY_EXTENSION_RE;
102 }
103
104 /**
105 * Replace quoted text (single quote, anything but the quote and quote again).
106 */
107 var QUOTED_STRING_RE = UNDEFINED;
108
109 function GetQuotedStringRE() {
110 if (IS_UNDEFINED(QUOTED_STRING_RE)) {
111 QUOTED_STRING_RE = new GlobalRegExp("'[^']+'", 'g');
112 }
113 return QUOTED_STRING_RE;
114 }
115
116 /**
117 * Matches valid service name.
118 */
119 var SERVICE_RE = UNDEFINED;
120
121 function GetServiceRE() {
122 if (IS_UNDEFINED(SERVICE_RE)) {
123 SERVICE_RE =
124 new GlobalRegExp('^(collator|numberformat|dateformat|breakiterator)$');
125 }
126 return SERVICE_RE;
127 }
128
129 /**
130 * Validates a language tag against bcp47 spec.
131 * Actual value is assigned on first run.
132 */
133 var LANGUAGE_TAG_RE = UNDEFINED;
134
135 function GetLanguageTagRE() {
136 if (IS_UNDEFINED(LANGUAGE_TAG_RE)) {
137 BuildLanguageTagREs();
138 }
139 return LANGUAGE_TAG_RE;
140 }
141
142 /**
143 * Helps find duplicate variants in the language tag.
144 */
145 var LANGUAGE_VARIANT_RE = UNDEFINED;
146
147 function GetLanguageVariantRE() {
148 if (IS_UNDEFINED(LANGUAGE_VARIANT_RE)) {
149 BuildLanguageTagREs();
150 }
151 return LANGUAGE_VARIANT_RE;
152 }
153
154 /**
155 * Helps find duplicate singletons in the language tag.
156 */
157 var LANGUAGE_SINGLETON_RE = UNDEFINED;
158
159 function GetLanguageSingletonRE() {
160 if (IS_UNDEFINED(LANGUAGE_SINGLETON_RE)) {
161 BuildLanguageTagREs();
162 }
163 return LANGUAGE_SINGLETON_RE;
164 }
165
166 /**
167 * Matches valid IANA time zone names.
168 */
169 var TIMEZONE_NAME_CHECK_RE = UNDEFINED;
170
171 function GetTimezoneNameCheckRE() {
172 if (IS_UNDEFINED(TIMEZONE_NAME_CHECK_RE)) {
173 TIMEZONE_NAME_CHECK_RE =
174 new GlobalRegExp('^([A-Za-z]+)/([A-Za-z]+)(?:_([A-Za-z]+))*$');
175 }
176 return TIMEZONE_NAME_CHECK_RE;
177 }
178
179 /**
180 * Adds bound method to the prototype of the given object.
181 */
182 function addBoundMethod(obj, methodName, implementation, length) {
183 %CheckIsBootstrapping();
184 function getter() {
185 if (!%IsInitializedIntlObject(this)) {
186 throw MakeTypeError(kMethodCalledOnWrongObject, methodName);
187 }
188 var internalName = '__bound' + methodName + '__';
189 if (IS_UNDEFINED(this[internalName])) {
190 var that = this;
191 var boundMethod;
192 if (IS_UNDEFINED(length) || length === 2) {
193 boundMethod = function(x, y) {
194 if (%_IsConstructCall()) {
195 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
196 }
197 return implementation(that, x, y);
198 }
199 } else if (length === 1) {
200 boundMethod = function(x) {
201 if (%_IsConstructCall()) {
202 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
203 }
204 return implementation(that, x);
205 }
206 } else {
207 boundMethod = function() {
208 if (%_IsConstructCall()) {
209 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
210 }
211 // DateTimeFormat.format needs to be 0 arg method, but can stil
212 // receive optional dateValue param. If one was provided, pass it
213 // along.
214 if (%_ArgumentsLength() > 0) {
215 return implementation(that, %_Arguments(0));
216 } else {
217 return implementation(that);
218 }
219 }
220 }
221 %FunctionSetName(boundMethod, internalName);
222 %FunctionRemovePrototype(boundMethod);
223 %SetNativeFlag(boundMethod);
224 this[internalName] = boundMethod;
225 }
226 return this[internalName];
227 }
228
229 %FunctionSetName(getter, methodName);
230 %FunctionRemovePrototype(getter);
231 %SetNativeFlag(getter);
232
233 ObjectDefineProperty(obj.prototype, methodName, {
234 get: getter,
235 enumerable: false,
236 configurable: true
237 });
238 }
239
240
241 /**
242 * Returns an intersection of locales and service supported locales.
243 * Parameter locales is treated as a priority list.
244 */
245 function supportedLocalesOf(service, locales, options) {
246 if (IS_NULL(%_CallFunction(service, GetServiceRE(), StringMatch))) {
247 throw MakeError(kWrongServiceType, service);
248 }
249
250 // Provide defaults if matcher was not specified.
251 if (IS_UNDEFINED(options)) {
252 options = {};
253 } else {
254 options = TO_OBJECT(options);
255 }
256
257 var matcher = options.localeMatcher;
258 if (!IS_UNDEFINED(matcher)) {
259 matcher = GlobalString(matcher);
260 if (matcher !== 'lookup' && matcher !== 'best fit') {
261 throw MakeRangeError(kLocaleMatcher, matcher);
262 }
263 } else {
264 matcher = 'best fit';
265 }
266
267 var requestedLocales = initializeLocaleList(locales);
268
269 // Cache these, they don't ever change per service.
270 if (IS_UNDEFINED(AVAILABLE_LOCALES[service])) {
271 AVAILABLE_LOCALES[service] = getAvailableLocalesOf(service);
272 }
273
274 // Use either best fit or lookup algorithm to match locales.
275 if (matcher === 'best fit') {
276 return initializeLocaleList(bestFitSupportedLocalesOf(
277 requestedLocales, AVAILABLE_LOCALES[service]));
278 }
279
280 return initializeLocaleList(lookupSupportedLocalesOf(
281 requestedLocales, AVAILABLE_LOCALES[service]));
282 }
283
284
285 /**
286 * Returns the subset of the provided BCP 47 language priority list for which
287 * this service has a matching locale when using the BCP 47 Lookup algorithm.
288 * Locales appear in the same order in the returned list as in the input list.
289 */
290 function lookupSupportedLocalesOf(requestedLocales, availableLocales) {
291 var matchedLocales = [];
292 for (var i = 0; i < requestedLocales.length; ++i) {
293 // Remove -u- extension.
294 var locale = %_CallFunction(requestedLocales[i], GetUnicodeExtensionRE(),
295 '', StringReplace);
296 do {
297 if (!IS_UNDEFINED(availableLocales[locale])) {
298 // Push requested locale not the resolved one.
299 %_CallFunction(matchedLocales, requestedLocales[i], ArrayPush);
300 break;
301 }
302 // Truncate locale if possible, if not break.
303 var pos = %_CallFunction(locale, '-', StringLastIndexOf);
304 if (pos === -1) {
305 break;
306 }
307 locale = %_CallFunction(locale, 0, pos, StringSubstring);
308 } while (true);
309 }
310
311 return matchedLocales;
312 }
313
314
315 /**
316 * Returns the subset of the provided BCP 47 language priority list for which
317 * this service has a matching locale when using the implementation
318 * dependent algorithm.
319 * Locales appear in the same order in the returned list as in the input list.
320 */
321 function bestFitSupportedLocalesOf(requestedLocales, availableLocales) {
322 // TODO(cira): implement better best fit algorithm.
323 return lookupSupportedLocalesOf(requestedLocales, availableLocales);
324 }
325
326
327 /**
328 * Returns a getOption function that extracts property value for given
329 * options object. If property is missing it returns defaultValue. If value
330 * is out of range for that property it throws RangeError.
331 */
332 function getGetOption(options, caller) {
333 if (IS_UNDEFINED(options)) throw MakeError(kDefaultOptionsMissing, caller);
334
335 var getOption = function getOption(property, type, values, defaultValue) {
336 if (!IS_UNDEFINED(options[property])) {
337 var value = options[property];
338 switch (type) {
339 case 'boolean':
340 value = GlobalBoolean(value);
341 break;
342 case 'string':
343 value = GlobalString(value);
344 break;
345 case 'number':
346 value = GlobalNumber(value);
347 break;
348 default:
349 throw MakeError(kWrongValueType);
350 }
351
352 if (!IS_UNDEFINED(values) &&
353 %_CallFunction(values, value, ArrayIndexOf) === -1) {
354 throw MakeRangeError(kValueOutOfRange, value, caller, property);
355 }
356
357 return value;
358 }
359
360 return defaultValue;
361 }
362
363 return getOption;
364 }
365
366
367 /**
368 * Compares a BCP 47 language priority list requestedLocales against the locales
369 * in availableLocales and determines the best available language to meet the
370 * request. Two algorithms are available to match the locales: the Lookup
371 * algorithm described in RFC 4647 section 3.4, and an implementation dependent
372 * best-fit algorithm. Independent of the locale matching algorithm, options
373 * specified through Unicode locale extension sequences are negotiated
374 * separately, taking the caller's relevant extension keys and locale data as
375 * well as client-provided options into consideration. Returns an object with
376 * a locale property whose value is the language tag of the selected locale,
377 * and properties for each key in relevantExtensionKeys providing the selected
378 * value for that key.
379 */
380 function resolveLocale(service, requestedLocales, options) {
381 requestedLocales = initializeLocaleList(requestedLocales);
382
383 var getOption = getGetOption(options, service);
384 var matcher = getOption('localeMatcher', 'string',
385 ['lookup', 'best fit'], 'best fit');
386 var resolved;
387 if (matcher === 'lookup') {
388 resolved = lookupMatcher(service, requestedLocales);
389 } else {
390 resolved = bestFitMatcher(service, requestedLocales);
391 }
392
393 return resolved;
394 }
395
396
397 /**
398 * Returns best matched supported locale and extension info using basic
399 * lookup algorithm.
400 */
401 function lookupMatcher(service, requestedLocales) {
402 if (IS_NULL(%_CallFunction(service, GetServiceRE(), StringMatch))) {
403 throw MakeError(kWrongServiceType, service);
404 }
405
406 // Cache these, they don't ever change per service.
407 if (IS_UNDEFINED(AVAILABLE_LOCALES[service])) {
408 AVAILABLE_LOCALES[service] = getAvailableLocalesOf(service);
409 }
410
411 for (var i = 0; i < requestedLocales.length; ++i) {
412 // Remove all extensions.
413 var locale = %_CallFunction(requestedLocales[i], GetAnyExtensionRE(), '',
414 StringReplace);
415 do {
416 if (!IS_UNDEFINED(AVAILABLE_LOCALES[service][locale])) {
417 // Return the resolved locale and extension.
418 var extensionMatch =
419 %_CallFunction(requestedLocales[i], GetUnicodeExtensionRE(),
420 StringMatch);
421 var extension = IS_NULL(extensionMatch) ? '' : extensionMatch[0];
422 return {'locale': locale, 'extension': extension, 'position': i};
423 }
424 // Truncate locale if possible.
425 var pos = %_CallFunction(locale, '-', StringLastIndexOf);
426 if (pos === -1) {
427 break;
428 }
429 locale = %_CallFunction(locale, 0, pos, StringSubstring);
430 } while (true);
431 }
432
433 // Didn't find a match, return default.
434 if (IS_UNDEFINED(DEFAULT_ICU_LOCALE)) {
435 DEFAULT_ICU_LOCALE = %GetDefaultICULocale();
436 }
437
438 return {'locale': DEFAULT_ICU_LOCALE, 'extension': '', 'position': -1};
439 }
440
441
442 /**
443 * Returns best matched supported locale and extension info using
444 * implementation dependend algorithm.
445 */
446 function bestFitMatcher(service, requestedLocales) {
447 // TODO(cira): implement better best fit algorithm.
448 return lookupMatcher(service, requestedLocales);
449 }
450
451
452 /**
453 * Parses Unicode extension into key - value map.
454 * Returns empty object if the extension string is invalid.
455 * We are not concerned with the validity of the values at this point.
456 */
457 function parseExtension(extension) {
458 var extensionSplit = %_CallFunction(extension, '-', StringSplit);
459
460 // Assume ['', 'u', ...] input, but don't throw.
461 if (extensionSplit.length <= 2 ||
462 (extensionSplit[0] !== '' && extensionSplit[1] !== 'u')) {
463 return {};
464 }
465
466 // Key is {2}alphanum, value is {3,8}alphanum.
467 // Some keys may not have explicit values (booleans).
468 var extensionMap = {};
469 var previousKey = UNDEFINED;
470 for (var i = 2; i < extensionSplit.length; ++i) {
471 var length = extensionSplit[i].length;
472 var element = extensionSplit[i];
473 if (length === 2) {
474 extensionMap[element] = UNDEFINED;
475 previousKey = element;
476 } else if (length >= 3 && length <=8 && !IS_UNDEFINED(previousKey)) {
477 extensionMap[previousKey] = element;
478 previousKey = UNDEFINED;
479 } else {
480 // There is a value that's too long, or that doesn't have a key.
481 return {};
482 }
483 }
484
485 return extensionMap;
486 }
487
488
489 /**
490 * Populates internalOptions object with boolean key-value pairs
491 * from extensionMap and options.
492 * Returns filtered extension (number and date format constructors use
493 * Unicode extensions for passing parameters to ICU).
494 * It's used for extension-option pairs only, e.g. kn-normalization, but not
495 * for 'sensitivity' since it doesn't have extension equivalent.
496 * Extensions like nu and ca don't have options equivalent, so we place
497 * undefined in the map.property to denote that.
498 */
499 function setOptions(inOptions, extensionMap, keyValues, getOption, outOptions) {
500 var extension = '';
501
502 var updateExtension = function updateExtension(key, value) {
503 return '-' + key + '-' + GlobalString(value);
504 }
505
506 var updateProperty = function updateProperty(property, type, value) {
507 if (type === 'boolean' && (typeof value === 'string')) {
508 value = (value === 'true') ? true : false;
509 }
510
511 if (!IS_UNDEFINED(property)) {
512 defineWEProperty(outOptions, property, value);
513 }
514 }
515
516 for (var key in keyValues) {
517 if (%HasOwnProperty(keyValues, key)) {
518 var value = UNDEFINED;
519 var map = keyValues[key];
520 if (!IS_UNDEFINED(map.property)) {
521 // This may return true if user specifies numeric: 'false', since
522 // Boolean('nonempty') === true.
523 value = getOption(map.property, map.type, map.values);
524 }
525 if (!IS_UNDEFINED(value)) {
526 updateProperty(map.property, map.type, value);
527 extension += updateExtension(key, value);
528 continue;
529 }
530 // User options didn't have it, check Unicode extension.
531 // Here we want to convert strings 'true', 'false' into proper Boolean
532 // values (not a user error).
533 if (%HasOwnProperty(extensionMap, key)) {
534 value = extensionMap[key];
535 if (!IS_UNDEFINED(value)) {
536 updateProperty(map.property, map.type, value);
537 extension += updateExtension(key, value);
538 } else if (map.type === 'boolean') {
539 // Boolean keys are allowed not to have values in Unicode extension.
540 // Those default to true.
541 updateProperty(map.property, map.type, true);
542 extension += updateExtension(key, true);
543 }
544 }
545 }
546 }
547
548 return extension === ''? '' : '-u' + extension;
549 }
550
551
552 /**
553 * Converts all OwnProperties into
554 * configurable: false, writable: false, enumerable: true.
555 */
556 function freezeArray(array) {
557 var l = array.length;
558 for (var i = 0; i < l; i++) {
559 if (i in array) {
560 ObjectDefineProperty(array, i, {value: array[i],
561 configurable: false,
562 writable: false,
563 enumerable: true});
564 }
565 }
566
567 ObjectDefineProperty(array, 'length', {value: l, writable: false});
568 return array;
569 }
570
571
572 /**
573 * It's sometimes desireable to leave user requested locale instead of ICU
574 * supported one (zh-TW is equivalent to zh-Hant-TW, so we should keep shorter
575 * one, if that was what user requested).
576 * This function returns user specified tag if its maximized form matches ICU
577 * resolved locale. If not we return ICU result.
578 */
579 function getOptimalLanguageTag(original, resolved) {
580 // Returns Array<Object>, where each object has maximized and base properties.
581 // Maximized: zh -> zh-Hans-CN
582 // Base: zh-CN-u-ca-gregory -> zh-CN
583 // Take care of grandfathered or simple cases.
584 if (original === resolved) {
585 return original;
586 }
587
588 var locales = %GetLanguageTagVariants([original, resolved]);
589 if (locales[0].maximized !== locales[1].maximized) {
590 return resolved;
591 }
592
593 // Preserve extensions of resolved locale, but swap base tags with original.
594 var resolvedBase = new GlobalRegExp('^' + locales[1].base);
595 return %_CallFunction(resolved, resolvedBase, locales[0].base, StringReplace);
596 }
597
598
599 /**
600 * Returns an Object that contains all of supported locales for a given
601 * service.
602 * In addition to the supported locales we add xx-ZZ locale for each xx-Yyyy-ZZ
603 * that is supported. This is required by the spec.
604 */
605 function getAvailableLocalesOf(service) {
606 var available = %AvailableLocalesOf(service);
607
608 for (var i in available) {
609 if (%HasOwnProperty(available, i)) {
610 var parts = %_CallFunction(i, /^([a-z]{2,3})-([A-Z][a-z]{3})-([A-Z]{2})$/,
611 StringMatch);
612 if (parts !== null) {
613 // Build xx-ZZ. We don't care about the actual value,
614 // as long it's not undefined.
615 available[parts[1] + '-' + parts[3]] = null;
616 }
617 }
618 }
619
620 return available;
621 }
622
623
624 /**
625 * Defines a property and sets writable and enumerable to true.
626 * Configurable is false by default.
627 */
628 function defineWEProperty(object, property, value) {
629 ObjectDefineProperty(object, property,
630 {value: value, writable: true, enumerable: true});
631 }
632
633
634 /**
635 * Adds property to an object if the value is not undefined.
636 * Sets configurable descriptor to false.
637 */
638 function addWEPropertyIfDefined(object, property, value) {
639 if (!IS_UNDEFINED(value)) {
640 defineWEProperty(object, property, value);
641 }
642 }
643
644
645 /**
646 * Defines a property and sets writable, enumerable and configurable to true.
647 */
648 function defineWECProperty(object, property, value) {
649 ObjectDefineProperty(object, property, {value: value,
650 writable: true,
651 enumerable: true,
652 configurable: true});
653 }
654
655
656 /**
657 * Adds property to an object if the value is not undefined.
658 * Sets all descriptors to true.
659 */
660 function addWECPropertyIfDefined(object, property, value) {
661 if (!IS_UNDEFINED(value)) {
662 defineWECProperty(object, property, value);
663 }
664 }
665
666
667 /**
668 * Returns titlecased word, aMeRricA -> America.
669 */
670 function toTitleCaseWord(word) {
671 return %StringToUpperCase(%_CallFunction(word, 0, 1, StringSubstr)) +
672 %StringToLowerCase(%_CallFunction(word, 1, StringSubstr));
673 }
674
675 /**
676 * Canonicalizes the language tag, or throws in case the tag is invalid.
677 */
678 function canonicalizeLanguageTag(localeID) {
679 // null is typeof 'object' so we have to do extra check.
680 if (typeof localeID !== 'string' && typeof localeID !== 'object' ||
681 IS_NULL(localeID)) {
682 throw MakeTypeError(kLanguageID);
683 }
684
685 var localeString = GlobalString(localeID);
686
687 if (isValidLanguageTag(localeString) === false) {
688 throw MakeRangeError(kInvalidLanguageTag, localeString);
689 }
690
691 // This call will strip -kn but not -kn-true extensions.
692 // ICU bug filled - http://bugs.icu-project.org/trac/ticket/9265.
693 // TODO(cira): check if -u-kn-true-kc-true-kh-true still throws after
694 // upgrade to ICU 4.9.
695 var tag = %CanonicalizeLanguageTag(localeString);
696 if (tag === 'invalid-tag') {
697 throw MakeRangeError(kInvalidLanguageTag, localeString);
698 }
699
700 return tag;
701 }
702
703
704 /**
705 * Returns an array where all locales are canonicalized and duplicates removed.
706 * Throws on locales that are not well formed BCP47 tags.
707 */
708 function initializeLocaleList(locales) {
709 var seen = [];
710 if (IS_UNDEFINED(locales)) {
711 // Constructor is called without arguments.
712 seen = [];
713 } else {
714 // We allow single string localeID.
715 if (typeof locales === 'string') {
716 %_CallFunction(seen, canonicalizeLanguageTag(locales), ArrayPush);
717 return freezeArray(seen);
718 }
719
720 var o = TO_OBJECT(locales);
721 var len = TO_UINT32(o.length);
722
723 for (var k = 0; k < len; k++) {
724 if (k in o) {
725 var value = o[k];
726
727 var tag = canonicalizeLanguageTag(value);
728
729 if (%_CallFunction(seen, tag, ArrayIndexOf) === -1) {
730 %_CallFunction(seen, tag, ArrayPush);
731 }
732 }
733 }
734 }
735
736 return freezeArray(seen);
737 }
738
739
740 /**
741 * Validates the language tag. Section 2.2.9 of the bcp47 spec
742 * defines a valid tag.
743 *
744 * ICU is too permissible and lets invalid tags, like
745 * hant-cmn-cn, through.
746 *
747 * Returns false if the language tag is invalid.
748 */
749 function isValidLanguageTag(locale) {
750 // Check if it's well-formed, including grandfadered tags.
751 if (!%_CallFunction(GetLanguageTagRE(), locale, RegExpTest)) {
752 return false;
753 }
754
755 // Just return if it's a x- form. It's all private.
756 if (%_CallFunction(locale, 'x-', StringIndexOf) === 0) {
757 return true;
758 }
759
760 // Check if there are any duplicate variants or singletons (extensions).
761
762 // Remove private use section.
763 locale = %_CallFunction(locale, /-x-/, StringSplit)[0];
764
765 // Skip language since it can match variant regex, so we start from 1.
766 // We are matching i-klingon here, but that's ok, since i-klingon-klingon
767 // is not valid and would fail LANGUAGE_TAG_RE test.
768 var variants = [];
769 var extensions = [];
770 var parts = %_CallFunction(locale, /-/, StringSplit);
771 for (var i = 1; i < parts.length; i++) {
772 var value = parts[i];
773 if (%_CallFunction(GetLanguageVariantRE(), value, RegExpTest) &&
774 extensions.length === 0) {
775 if (%_CallFunction(variants, value, ArrayIndexOf) === -1) {
776 %_CallFunction(variants, value, ArrayPush);
777 } else {
778 return false;
779 }
780 }
781
782 if (%_CallFunction(GetLanguageSingletonRE(), value, RegExpTest)) {
783 if (%_CallFunction(extensions, value, ArrayIndexOf) === -1) {
784 %_CallFunction(extensions, value, ArrayPush);
785 } else {
786 return false;
787 }
788 }
789 }
790
791 return true;
792 }
793
794
795 /**
796 * Builds a regular expresion that validates the language tag
797 * against bcp47 spec.
798 * Uses http://tools.ietf.org/html/bcp47, section 2.1, ABNF.
799 * Runs on load and initializes the global REs.
800 */
801 function BuildLanguageTagREs() {
802 var alpha = '[a-zA-Z]';
803 var digit = '[0-9]';
804 var alphanum = '(' + alpha + '|' + digit + ')';
805 var regular = '(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|' +
806 'zh-min|zh-min-nan|zh-xiang)';
807 var irregular = '(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|' +
808 'i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|' +
809 'i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)';
810 var grandfathered = '(' + irregular + '|' + regular + ')';
811 var privateUse = '(x(-' + alphanum + '{1,8})+)';
812
813 var singleton = '(' + digit + '|[A-WY-Za-wy-z])';
814 LANGUAGE_SINGLETON_RE = new GlobalRegExp('^' + singleton + '$', 'i');
815
816 var extension = '(' + singleton + '(-' + alphanum + '{2,8})+)';
817
818 var variant = '(' + alphanum + '{5,8}|(' + digit + alphanum + '{3}))';
819 LANGUAGE_VARIANT_RE = new GlobalRegExp('^' + variant + '$', 'i');
820
821 var region = '(' + alpha + '{2}|' + digit + '{3})';
822 var script = '(' + alpha + '{4})';
823 var extLang = '(' + alpha + '{3}(-' + alpha + '{3}){0,2})';
824 var language = '(' + alpha + '{2,3}(-' + extLang + ')?|' + alpha + '{4}|' +
825 alpha + '{5,8})';
826 var langTag = language + '(-' + script + ')?(-' + region + ')?(-' +
827 variant + ')*(-' + extension + ')*(-' + privateUse + ')?';
828
829 var languageTag =
830 '^(' + langTag + '|' + privateUse + '|' + grandfathered + ')$';
831 LANGUAGE_TAG_RE = new GlobalRegExp(languageTag, 'i');
832 }
833
834 /**
835 * Initializes the given object so it's a valid Collator instance.
836 * Useful for subclassing.
837 */
838 function initializeCollator(collator, locales, options) {
839 if (%IsInitializedIntlObject(collator)) {
840 throw MakeTypeError(kReinitializeIntl, "Collator");
841 }
842
843 if (IS_UNDEFINED(options)) {
844 options = {};
845 }
846
847 var getOption = getGetOption(options, 'collator');
848
849 var internalOptions = {};
850
851 defineWEProperty(internalOptions, 'usage', getOption(
852 'usage', 'string', ['sort', 'search'], 'sort'));
853
854 var sensitivity = getOption('sensitivity', 'string',
855 ['base', 'accent', 'case', 'variant']);
856 if (IS_UNDEFINED(sensitivity) && internalOptions.usage === 'sort') {
857 sensitivity = 'variant';
858 }
859 defineWEProperty(internalOptions, 'sensitivity', sensitivity);
860
861 defineWEProperty(internalOptions, 'ignorePunctuation', getOption(
862 'ignorePunctuation', 'boolean', UNDEFINED, false));
863
864 var locale = resolveLocale('collator', locales, options);
865
866 // ICU can't take kb, kc... parameters through localeID, so we need to pass
867 // them as options.
868 // One exception is -co- which has to be part of the extension, but only for
869 // usage: sort, and its value can't be 'standard' or 'search'.
870 var extensionMap = parseExtension(locale.extension);
871
872 /**
873 * Map of Unicode extensions to option properties, and their values and types,
874 * for a collator.
875 */
876 var COLLATOR_KEY_MAP = {
877 'kn': {'property': 'numeric', 'type': 'boolean'},
878 'kf': {'property': 'caseFirst', 'type': 'string',
879 'values': ['false', 'lower', 'upper']}
880 };
881
882 setOptions(
883 options, extensionMap, COLLATOR_KEY_MAP, getOption, internalOptions);
884
885 var collation = 'default';
886 var extension = '';
887 if (%HasOwnProperty(extensionMap, 'co') && internalOptions.usage === 'sort') {
888
889 /**
890 * Allowed -u-co- values. List taken from:
891 * http://unicode.org/repos/cldr/trunk/common/bcp47/collation.xml
892 */
893 var ALLOWED_CO_VALUES = [
894 'big5han', 'dict', 'direct', 'ducet', 'gb2312', 'phonebk', 'phonetic',
895 'pinyin', 'reformed', 'searchjl', 'stroke', 'trad', 'unihan', 'zhuyin'
896 ];
897
898 if (%_CallFunction(ALLOWED_CO_VALUES, extensionMap.co, ArrayIndexOf) !==
899 -1) {
900 extension = '-u-co-' + extensionMap.co;
901 // ICU can't tell us what the collation is, so save user's input.
902 collation = extensionMap.co;
903 }
904 } else if (internalOptions.usage === 'search') {
905 extension = '-u-co-search';
906 }
907 defineWEProperty(internalOptions, 'collation', collation);
908
909 var requestedLocale = locale.locale + extension;
910
911 // We define all properties C++ code may produce, to prevent security
912 // problems. If malicious user decides to redefine Object.prototype.locale
913 // we can't just use plain x.locale = 'us' or in C++ Set("locale", "us").
914 // ObjectDefineProperties will either succeed defining or throw an error.
915 var resolved = ObjectDefineProperties({}, {
916 caseFirst: {writable: true},
917 collation: {value: internalOptions.collation, writable: true},
918 ignorePunctuation: {writable: true},
919 locale: {writable: true},
920 numeric: {writable: true},
921 requestedLocale: {value: requestedLocale, writable: true},
922 sensitivity: {writable: true},
923 strength: {writable: true},
924 usage: {value: internalOptions.usage, writable: true}
925 });
926
927 var internalCollator = %CreateCollator(requestedLocale,
928 internalOptions,
929 resolved);
930
931 // Writable, configurable and enumerable are set to false by default.
932 %MarkAsInitializedIntlObjectOfType(collator, 'collator', internalCollator);
933 ObjectDefineProperty(collator, 'resolved', {value: resolved});
934
935 return collator;
936 }
937
938
939 /**
940 * Constructs Intl.Collator object given optional locales and options
941 * parameters.
942 *
943 * @constructor
944 */
945 %AddNamedProperty(Intl, 'Collator', function() {
946 var locales = %_Arguments(0);
947 var options = %_Arguments(1);
948
949 if (!this || this === Intl) {
950 // Constructor is called as a function.
951 return new Intl.Collator(locales, options);
952 }
953
954 return initializeCollator(TO_OBJECT(this), locales, options);
955 },
956 DONT_ENUM
957 );
958
959
960 /**
961 * Collator resolvedOptions method.
962 */
963 %AddNamedProperty(Intl.Collator.prototype, 'resolvedOptions', function() {
964 if (%_IsConstructCall()) {
965 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
966 }
967
968 if (!%IsInitializedIntlObjectOfType(this, 'collator')) {
969 throw MakeTypeError(kResolvedOptionsCalledOnNonObject, "Collator");
970 }
971
972 var coll = this;
973 var locale = getOptimalLanguageTag(coll.resolved.requestedLocale,
974 coll.resolved.locale);
975
976 return {
977 locale: locale,
978 usage: coll.resolved.usage,
979 sensitivity: coll.resolved.sensitivity,
980 ignorePunctuation: coll.resolved.ignorePunctuation,
981 numeric: coll.resolved.numeric,
982 caseFirst: coll.resolved.caseFirst,
983 collation: coll.resolved.collation
984 };
985 },
986 DONT_ENUM
987 );
988 %FunctionSetName(Intl.Collator.prototype.resolvedOptions, 'resolvedOptions');
989 %FunctionRemovePrototype(Intl.Collator.prototype.resolvedOptions);
990 %SetNativeFlag(Intl.Collator.prototype.resolvedOptions);
991
992
993 /**
994 * Returns the subset of the given locale list for which this locale list
995 * has a matching (possibly fallback) locale. Locales appear in the same
996 * order in the returned list as in the input list.
997 * Options are optional parameter.
998 */
999 %AddNamedProperty(Intl.Collator, 'supportedLocalesOf', function(locales) {
1000 if (%_IsConstructCall()) {
1001 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
1002 }
1003
1004 return supportedLocalesOf('collator', locales, %_Arguments(1));
1005 },
1006 DONT_ENUM
1007 );
1008 %FunctionSetName(Intl.Collator.supportedLocalesOf, 'supportedLocalesOf');
1009 %FunctionRemovePrototype(Intl.Collator.supportedLocalesOf);
1010 %SetNativeFlag(Intl.Collator.supportedLocalesOf);
1011
1012
1013 /**
1014 * When the compare method is called with two arguments x and y, it returns a
1015 * Number other than NaN that represents the result of a locale-sensitive
1016 * String comparison of x with y.
1017 * The result is intended to order String values in the sort order specified
1018 * by the effective locale and collation options computed during construction
1019 * of this Collator object, and will be negative, zero, or positive, depending
1020 * on whether x comes before y in the sort order, the Strings are equal under
1021 * the sort order, or x comes after y in the sort order, respectively.
1022 */
1023 function compare(collator, x, y) {
1024 return %InternalCompare(%GetImplFromInitializedIntlObject(collator),
1025 GlobalString(x), GlobalString(y));
1026 };
1027
1028
1029 addBoundMethod(Intl.Collator, 'compare', compare, 2);
1030
1031 /**
1032 * Verifies that the input is a well-formed ISO 4217 currency code.
1033 * Don't uppercase to test. It could convert invalid code into a valid one.
1034 * For example \u00DFP (Eszett+P) becomes SSP.
1035 */
1036 function isWellFormedCurrencyCode(currency) {
1037 return typeof currency == "string" &&
1038 currency.length == 3 &&
1039 %_CallFunction(currency, /[^A-Za-z]/, StringMatch) == null;
1040 }
1041
1042
1043 /**
1044 * Returns the valid digit count for a property, or throws RangeError on
1045 * a value out of the range.
1046 */
1047 function getNumberOption(options, property, min, max, fallback) {
1048 var value = options[property];
1049 if (!IS_UNDEFINED(value)) {
1050 value = GlobalNumber(value);
1051 if (IsNaN(value) || value < min || value > max) {
1052 throw MakeRangeError(kPropertyValueOutOfRange, property);
1053 }
1054 return MathFloor(value);
1055 }
1056
1057 return fallback;
1058 }
1059
1060
1061 /**
1062 * Initializes the given object so it's a valid NumberFormat instance.
1063 * Useful for subclassing.
1064 */
1065 function initializeNumberFormat(numberFormat, locales, options) {
1066 if (%IsInitializedIntlObject(numberFormat)) {
1067 throw MakeTypeError(kReinitializeIntl, "NumberFormat");
1068 }
1069
1070 if (IS_UNDEFINED(options)) {
1071 options = {};
1072 }
1073
1074 var getOption = getGetOption(options, 'numberformat');
1075
1076 var locale = resolveLocale('numberformat', locales, options);
1077
1078 var internalOptions = {};
1079 defineWEProperty(internalOptions, 'style', getOption(
1080 'style', 'string', ['decimal', 'percent', 'currency'], 'decimal'));
1081
1082 var currency = getOption('currency', 'string');
1083 if (!IS_UNDEFINED(currency) && !isWellFormedCurrencyCode(currency)) {
1084 throw MakeRangeError(kInvalidCurrencyCode, currency);
1085 }
1086
1087 if (internalOptions.style === 'currency' && IS_UNDEFINED(currency)) {
1088 throw MakeTypeError(kCurrencyCode);
1089 }
1090
1091 var currencyDisplay = getOption(
1092 'currencyDisplay', 'string', ['code', 'symbol', 'name'], 'symbol');
1093 if (internalOptions.style === 'currency') {
1094 defineWEProperty(internalOptions, 'currency', %StringToUpperCase(currency));
1095 defineWEProperty(internalOptions, 'currencyDisplay', currencyDisplay);
1096 }
1097
1098 // Digit ranges.
1099 var mnid = getNumberOption(options, 'minimumIntegerDigits', 1, 21, 1);
1100 defineWEProperty(internalOptions, 'minimumIntegerDigits', mnid);
1101
1102 var mnfd = options['minimumFractionDigits'];
1103 var mxfd = options['maximumFractionDigits'];
1104 if (!IS_UNDEFINED(mnfd) || !internalOptions.style === 'currency') {
1105 mnfd = getNumberOption(options, 'minimumFractionDigits', 0, 20, 0);
1106 defineWEProperty(internalOptions, 'minimumFractionDigits', mnfd);
1107 }
1108
1109 if (!IS_UNDEFINED(mxfd) || !internalOptions.style === 'currency') {
1110 mnfd = IS_UNDEFINED(mnfd) ? 0 : mnfd;
1111 fallback_limit = (mnfd > 3) ? mnfd : 3;
1112 mxfd = getNumberOption(options, 'maximumFractionDigits', mnfd, 20, fallback_ limit);
1113 defineWEProperty(internalOptions, 'maximumFractionDigits', mxfd);
1114 }
1115
1116 var mnsd = options['minimumSignificantDigits'];
1117 var mxsd = options['maximumSignificantDigits'];
1118 if (!IS_UNDEFINED(mnsd) || !IS_UNDEFINED(mxsd)) {
1119 mnsd = getNumberOption(options, 'minimumSignificantDigits', 1, 21, 0);
1120 defineWEProperty(internalOptions, 'minimumSignificantDigits', mnsd);
1121
1122 mxsd = getNumberOption(options, 'maximumSignificantDigits', mnsd, 21, 21);
1123 defineWEProperty(internalOptions, 'maximumSignificantDigits', mxsd);
1124 }
1125
1126 // Grouping.
1127 defineWEProperty(internalOptions, 'useGrouping', getOption(
1128 'useGrouping', 'boolean', UNDEFINED, true));
1129
1130 // ICU prefers options to be passed using -u- extension key/values for
1131 // number format, so we need to build that.
1132 var extensionMap = parseExtension(locale.extension);
1133
1134 /**
1135 * Map of Unicode extensions to option properties, and their values and types,
1136 * for a number format.
1137 */
1138 var NUMBER_FORMAT_KEY_MAP = {
1139 'nu': {'property': UNDEFINED, 'type': 'string'}
1140 };
1141
1142 var extension = setOptions(options, extensionMap, NUMBER_FORMAT_KEY_MAP,
1143 getOption, internalOptions);
1144
1145 var requestedLocale = locale.locale + extension;
1146 var resolved = ObjectDefineProperties({}, {
1147 currency: {writable: true},
1148 currencyDisplay: {writable: true},
1149 locale: {writable: true},
1150 maximumFractionDigits: {writable: true},
1151 minimumFractionDigits: {writable: true},
1152 minimumIntegerDigits: {writable: true},
1153 numberingSystem: {writable: true},
1154 requestedLocale: {value: requestedLocale, writable: true},
1155 style: {value: internalOptions.style, writable: true},
1156 useGrouping: {writable: true}
1157 });
1158 if (%HasOwnProperty(internalOptions, 'minimumSignificantDigits')) {
1159 defineWEProperty(resolved, 'minimumSignificantDigits', UNDEFINED);
1160 }
1161 if (%HasOwnProperty(internalOptions, 'maximumSignificantDigits')) {
1162 defineWEProperty(resolved, 'maximumSignificantDigits', UNDEFINED);
1163 }
1164 var formatter = %CreateNumberFormat(requestedLocale,
1165 internalOptions,
1166 resolved);
1167
1168 if (internalOptions.style === 'currency') {
1169 ObjectDefineProperty(resolved, 'currencyDisplay', {value: currencyDisplay,
1170 writable: true});
1171 }
1172
1173 %MarkAsInitializedIntlObjectOfType(numberFormat, 'numberformat', formatter);
1174 ObjectDefineProperty(numberFormat, 'resolved', {value: resolved});
1175
1176 return numberFormat;
1177 }
1178
1179
1180 /**
1181 * Constructs Intl.NumberFormat object given optional locales and options
1182 * parameters.
1183 *
1184 * @constructor
1185 */
1186 %AddNamedProperty(Intl, 'NumberFormat', function() {
1187 var locales = %_Arguments(0);
1188 var options = %_Arguments(1);
1189
1190 if (!this || this === Intl) {
1191 // Constructor is called as a function.
1192 return new Intl.NumberFormat(locales, options);
1193 }
1194
1195 return initializeNumberFormat(TO_OBJECT(this), locales, options);
1196 },
1197 DONT_ENUM
1198 );
1199
1200
1201 /**
1202 * NumberFormat resolvedOptions method.
1203 */
1204 %AddNamedProperty(Intl.NumberFormat.prototype, 'resolvedOptions', function() {
1205 if (%_IsConstructCall()) {
1206 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
1207 }
1208
1209 if (!%IsInitializedIntlObjectOfType(this, 'numberformat')) {
1210 throw MakeTypeError(kResolvedOptionsCalledOnNonObject, "NumberFormat");
1211 }
1212
1213 var format = this;
1214 var locale = getOptimalLanguageTag(format.resolved.requestedLocale,
1215 format.resolved.locale);
1216
1217 var result = {
1218 locale: locale,
1219 numberingSystem: format.resolved.numberingSystem,
1220 style: format.resolved.style,
1221 useGrouping: format.resolved.useGrouping,
1222 minimumIntegerDigits: format.resolved.minimumIntegerDigits,
1223 minimumFractionDigits: format.resolved.minimumFractionDigits,
1224 maximumFractionDigits: format.resolved.maximumFractionDigits,
1225 };
1226
1227 if (result.style === 'currency') {
1228 defineWECProperty(result, 'currency', format.resolved.currency);
1229 defineWECProperty(result, 'currencyDisplay',
1230 format.resolved.currencyDisplay);
1231 }
1232
1233 if (%HasOwnProperty(format.resolved, 'minimumSignificantDigits')) {
1234 defineWECProperty(result, 'minimumSignificantDigits',
1235 format.resolved.minimumSignificantDigits);
1236 }
1237
1238 if (%HasOwnProperty(format.resolved, 'maximumSignificantDigits')) {
1239 defineWECProperty(result, 'maximumSignificantDigits',
1240 format.resolved.maximumSignificantDigits);
1241 }
1242
1243 return result;
1244 },
1245 DONT_ENUM
1246 );
1247 %FunctionSetName(Intl.NumberFormat.prototype.resolvedOptions,
1248 'resolvedOptions');
1249 %FunctionRemovePrototype(Intl.NumberFormat.prototype.resolvedOptions);
1250 %SetNativeFlag(Intl.NumberFormat.prototype.resolvedOptions);
1251
1252
1253 /**
1254 * Returns the subset of the given locale list for which this locale list
1255 * has a matching (possibly fallback) locale. Locales appear in the same
1256 * order in the returned list as in the input list.
1257 * Options are optional parameter.
1258 */
1259 %AddNamedProperty(Intl.NumberFormat, 'supportedLocalesOf', function(locales) {
1260 if (%_IsConstructCall()) {
1261 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
1262 }
1263
1264 return supportedLocalesOf('numberformat', locales, %_Arguments(1));
1265 },
1266 DONT_ENUM
1267 );
1268 %FunctionSetName(Intl.NumberFormat.supportedLocalesOf, 'supportedLocalesOf');
1269 %FunctionRemovePrototype(Intl.NumberFormat.supportedLocalesOf);
1270 %SetNativeFlag(Intl.NumberFormat.supportedLocalesOf);
1271
1272
1273 /**
1274 * Returns a String value representing the result of calling ToNumber(value)
1275 * according to the effective locale and the formatting options of this
1276 * NumberFormat.
1277 */
1278 function formatNumber(formatter, value) {
1279 // Spec treats -0 and +0 as 0.
1280 var number = TO_NUMBER(value) + 0;
1281
1282 return %InternalNumberFormat(%GetImplFromInitializedIntlObject(formatter),
1283 number);
1284 }
1285
1286
1287 /**
1288 * Returns a Number that represents string value that was passed in.
1289 */
1290 function parseNumber(formatter, value) {
1291 return %InternalNumberParse(%GetImplFromInitializedIntlObject(formatter),
1292 GlobalString(value));
1293 }
1294
1295
1296 addBoundMethod(Intl.NumberFormat, 'format', formatNumber, 1);
1297 addBoundMethod(Intl.NumberFormat, 'v8Parse', parseNumber, 1);
1298
1299 /**
1300 * Returns a string that matches LDML representation of the options object.
1301 */
1302 function toLDMLString(options) {
1303 var getOption = getGetOption(options, 'dateformat');
1304
1305 var ldmlString = '';
1306
1307 var option = getOption('weekday', 'string', ['narrow', 'short', 'long']);
1308 ldmlString += appendToLDMLString(
1309 option, {narrow: 'EEEEE', short: 'EEE', long: 'EEEE'});
1310
1311 option = getOption('era', 'string', ['narrow', 'short', 'long']);
1312 ldmlString += appendToLDMLString(
1313 option, {narrow: 'GGGGG', short: 'GGG', long: 'GGGG'});
1314
1315 option = getOption('year', 'string', ['2-digit', 'numeric']);
1316 ldmlString += appendToLDMLString(option, {'2-digit': 'yy', 'numeric': 'y'});
1317
1318 option = getOption('month', 'string',
1319 ['2-digit', 'numeric', 'narrow', 'short', 'long']);
1320 ldmlString += appendToLDMLString(option, {'2-digit': 'MM', 'numeric': 'M',
1321 'narrow': 'MMMMM', 'short': 'MMM', 'long': 'MMMM'});
1322
1323 option = getOption('day', 'string', ['2-digit', 'numeric']);
1324 ldmlString += appendToLDMLString(
1325 option, {'2-digit': 'dd', 'numeric': 'd'});
1326
1327 var hr12 = getOption('hour12', 'boolean');
1328 option = getOption('hour', 'string', ['2-digit', 'numeric']);
1329 if (IS_UNDEFINED(hr12)) {
1330 ldmlString += appendToLDMLString(option, {'2-digit': 'jj', 'numeric': 'j'});
1331 } else if (hr12 === true) {
1332 ldmlString += appendToLDMLString(option, {'2-digit': 'hh', 'numeric': 'h'});
1333 } else {
1334 ldmlString += appendToLDMLString(option, {'2-digit': 'HH', 'numeric': 'H'});
1335 }
1336
1337 option = getOption('minute', 'string', ['2-digit', 'numeric']);
1338 ldmlString += appendToLDMLString(option, {'2-digit': 'mm', 'numeric': 'm'});
1339
1340 option = getOption('second', 'string', ['2-digit', 'numeric']);
1341 ldmlString += appendToLDMLString(option, {'2-digit': 'ss', 'numeric': 's'});
1342
1343 option = getOption('timeZoneName', 'string', ['short', 'long']);
1344 ldmlString += appendToLDMLString(option, {short: 'z', long: 'zzzz'});
1345
1346 return ldmlString;
1347 }
1348
1349
1350 /**
1351 * Returns either LDML equivalent of the current option or empty string.
1352 */
1353 function appendToLDMLString(option, pairs) {
1354 if (!IS_UNDEFINED(option)) {
1355 return pairs[option];
1356 } else {
1357 return '';
1358 }
1359 }
1360
1361
1362 /**
1363 * Returns object that matches LDML representation of the date.
1364 */
1365 function fromLDMLString(ldmlString) {
1366 // First remove '' quoted text, so we lose 'Uhr' strings.
1367 ldmlString = %_CallFunction(ldmlString, GetQuotedStringRE(), '',
1368 StringReplace);
1369
1370 var options = {};
1371 var match = %_CallFunction(ldmlString, /E{3,5}/g, StringMatch);
1372 options = appendToDateTimeObject(
1373 options, 'weekday', match, {EEEEE: 'narrow', EEE: 'short', EEEE: 'long'});
1374
1375 match = %_CallFunction(ldmlString, /G{3,5}/g, StringMatch);
1376 options = appendToDateTimeObject(
1377 options, 'era', match, {GGGGG: 'narrow', GGG: 'short', GGGG: 'long'});
1378
1379 match = %_CallFunction(ldmlString, /y{1,2}/g, StringMatch);
1380 options = appendToDateTimeObject(
1381 options, 'year', match, {y: 'numeric', yy: '2-digit'});
1382
1383 match = %_CallFunction(ldmlString, /M{1,5}/g, StringMatch);
1384 options = appendToDateTimeObject(options, 'month', match, {MM: '2-digit',
1385 M: 'numeric', MMMMM: 'narrow', MMM: 'short', MMMM: 'long'});
1386
1387 // Sometimes we get L instead of M for month - standalone name.
1388 match = %_CallFunction(ldmlString, /L{1,5}/g, StringMatch);
1389 options = appendToDateTimeObject(options, 'month', match, {LL: '2-digit',
1390 L: 'numeric', LLLLL: 'narrow', LLL: 'short', LLLL: 'long'});
1391
1392 match = %_CallFunction(ldmlString, /d{1,2}/g, StringMatch);
1393 options = appendToDateTimeObject(
1394 options, 'day', match, {d: 'numeric', dd: '2-digit'});
1395
1396 match = %_CallFunction(ldmlString, /h{1,2}/g, StringMatch);
1397 if (match !== null) {
1398 options['hour12'] = true;
1399 }
1400 options = appendToDateTimeObject(
1401 options, 'hour', match, {h: 'numeric', hh: '2-digit'});
1402
1403 match = %_CallFunction(ldmlString, /H{1,2}/g, StringMatch);
1404 if (match !== null) {
1405 options['hour12'] = false;
1406 }
1407 options = appendToDateTimeObject(
1408 options, 'hour', match, {H: 'numeric', HH: '2-digit'});
1409
1410 match = %_CallFunction(ldmlString, /m{1,2}/g, StringMatch);
1411 options = appendToDateTimeObject(
1412 options, 'minute', match, {m: 'numeric', mm: '2-digit'});
1413
1414 match = %_CallFunction(ldmlString, /s{1,2}/g, StringMatch);
1415 options = appendToDateTimeObject(
1416 options, 'second', match, {s: 'numeric', ss: '2-digit'});
1417
1418 match = %_CallFunction(ldmlString, /z|zzzz/g, StringMatch);
1419 options = appendToDateTimeObject(
1420 options, 'timeZoneName', match, {z: 'short', zzzz: 'long'});
1421
1422 return options;
1423 }
1424
1425
1426 function appendToDateTimeObject(options, option, match, pairs) {
1427 if (IS_NULL(match)) {
1428 if (!%HasOwnProperty(options, option)) {
1429 defineWEProperty(options, option, UNDEFINED);
1430 }
1431 return options;
1432 }
1433
1434 var property = match[0];
1435 defineWEProperty(options, option, pairs[property]);
1436
1437 return options;
1438 }
1439
1440
1441 /**
1442 * Returns options with at least default values in it.
1443 */
1444 function toDateTimeOptions(options, required, defaults) {
1445 if (IS_UNDEFINED(options)) {
1446 options = {};
1447 } else {
1448 options = TO_OBJECT(options);
1449 }
1450
1451 var needsDefault = true;
1452 if ((required === 'date' || required === 'any') &&
1453 (!IS_UNDEFINED(options.weekday) || !IS_UNDEFINED(options.year) ||
1454 !IS_UNDEFINED(options.month) || !IS_UNDEFINED(options.day))) {
1455 needsDefault = false;
1456 }
1457
1458 if ((required === 'time' || required === 'any') &&
1459 (!IS_UNDEFINED(options.hour) || !IS_UNDEFINED(options.minute) ||
1460 !IS_UNDEFINED(options.second))) {
1461 needsDefault = false;
1462 }
1463
1464 if (needsDefault && (defaults === 'date' || defaults === 'all')) {
1465 ObjectDefineProperty(options, 'year', {value: 'numeric',
1466 writable: true,
1467 enumerable: true,
1468 configurable: true});
1469 ObjectDefineProperty(options, 'month', {value: 'numeric',
1470 writable: true,
1471 enumerable: true,
1472 configurable: true});
1473 ObjectDefineProperty(options, 'day', {value: 'numeric',
1474 writable: true,
1475 enumerable: true,
1476 configurable: true});
1477 }
1478
1479 if (needsDefault && (defaults === 'time' || defaults === 'all')) {
1480 ObjectDefineProperty(options, 'hour', {value: 'numeric',
1481 writable: true,
1482 enumerable: true,
1483 configurable: true});
1484 ObjectDefineProperty(options, 'minute', {value: 'numeric',
1485 writable: true,
1486 enumerable: true,
1487 configurable: true});
1488 ObjectDefineProperty(options, 'second', {value: 'numeric',
1489 writable: true,
1490 enumerable: true,
1491 configurable: true});
1492 }
1493
1494 return options;
1495 }
1496
1497
1498 /**
1499 * Initializes the given object so it's a valid DateTimeFormat instance.
1500 * Useful for subclassing.
1501 */
1502 function initializeDateTimeFormat(dateFormat, locales, options) {
1503
1504 if (%IsInitializedIntlObject(dateFormat)) {
1505 throw MakeTypeError(kReinitializeIntl, "DateTimeFormat");
1506 }
1507
1508 if (IS_UNDEFINED(options)) {
1509 options = {};
1510 }
1511
1512 var locale = resolveLocale('dateformat', locales, options);
1513
1514 options = toDateTimeOptions(options, 'any', 'date');
1515
1516 var getOption = getGetOption(options, 'dateformat');
1517
1518 // We implement only best fit algorithm, but still need to check
1519 // if the formatMatcher values are in range.
1520 var matcher = getOption('formatMatcher', 'string',
1521 ['basic', 'best fit'], 'best fit');
1522
1523 // Build LDML string for the skeleton that we pass to the formatter.
1524 var ldmlString = toLDMLString(options);
1525
1526 // Filter out supported extension keys so we know what to put in resolved
1527 // section later on.
1528 // We need to pass calendar and number system to the method.
1529 var tz = canonicalizeTimeZoneID(options.timeZone);
1530
1531 // ICU prefers options to be passed using -u- extension key/values, so
1532 // we need to build that.
1533 var internalOptions = {};
1534 var extensionMap = parseExtension(locale.extension);
1535
1536 /**
1537 * Map of Unicode extensions to option properties, and their values and types,
1538 * for a date/time format.
1539 */
1540 var DATETIME_FORMAT_KEY_MAP = {
1541 'ca': {'property': UNDEFINED, 'type': 'string'},
1542 'nu': {'property': UNDEFINED, 'type': 'string'}
1543 };
1544
1545 var extension = setOptions(options, extensionMap, DATETIME_FORMAT_KEY_MAP,
1546 getOption, internalOptions);
1547
1548 var requestedLocale = locale.locale + extension;
1549 var resolved = ObjectDefineProperties({}, {
1550 calendar: {writable: true},
1551 day: {writable: true},
1552 era: {writable: true},
1553 hour12: {writable: true},
1554 hour: {writable: true},
1555 locale: {writable: true},
1556 minute: {writable: true},
1557 month: {writable: true},
1558 numberingSystem: {writable: true},
1559 pattern: {writable: true},
1560 requestedLocale: {value: requestedLocale, writable: true},
1561 second: {writable: true},
1562 timeZone: {writable: true},
1563 timeZoneName: {writable: true},
1564 tz: {value: tz, writable: true},
1565 weekday: {writable: true},
1566 year: {writable: true}
1567 });
1568
1569 var formatter = %CreateDateTimeFormat(
1570 requestedLocale, {skeleton: ldmlString, timeZone: tz}, resolved);
1571
1572 if (!IS_UNDEFINED(tz) && tz !== resolved.timeZone) {
1573 throw MakeRangeError(kUnsupportedTimeZone, tz);
1574 }
1575
1576 %MarkAsInitializedIntlObjectOfType(dateFormat, 'dateformat', formatter);
1577 ObjectDefineProperty(dateFormat, 'resolved', {value: resolved});
1578
1579 return dateFormat;
1580 }
1581
1582
1583 /**
1584 * Constructs Intl.DateTimeFormat object given optional locales and options
1585 * parameters.
1586 *
1587 * @constructor
1588 */
1589 %AddNamedProperty(Intl, 'DateTimeFormat', function() {
1590 var locales = %_Arguments(0);
1591 var options = %_Arguments(1);
1592
1593 if (!this || this === Intl) {
1594 // Constructor is called as a function.
1595 return new Intl.DateTimeFormat(locales, options);
1596 }
1597
1598 return initializeDateTimeFormat(TO_OBJECT(this), locales, options);
1599 },
1600 DONT_ENUM
1601 );
1602
1603
1604 /**
1605 * DateTimeFormat resolvedOptions method.
1606 */
1607 %AddNamedProperty(Intl.DateTimeFormat.prototype, 'resolvedOptions', function() {
1608 if (%_IsConstructCall()) {
1609 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
1610 }
1611
1612 if (!%IsInitializedIntlObjectOfType(this, 'dateformat')) {
1613 throw MakeTypeError(kResolvedOptionsCalledOnNonObject, "DateTimeFormat");
1614 }
1615
1616 /**
1617 * Maps ICU calendar names into LDML type.
1618 */
1619 var ICU_CALENDAR_MAP = {
1620 'gregorian': 'gregory',
1621 'japanese': 'japanese',
1622 'buddhist': 'buddhist',
1623 'roc': 'roc',
1624 'persian': 'persian',
1625 'islamic-civil': 'islamicc',
1626 'islamic': 'islamic',
1627 'hebrew': 'hebrew',
1628 'chinese': 'chinese',
1629 'indian': 'indian',
1630 'coptic': 'coptic',
1631 'ethiopic': 'ethiopic',
1632 'ethiopic-amete-alem': 'ethioaa'
1633 };
1634
1635 var format = this;
1636 var fromPattern = fromLDMLString(format.resolved.pattern);
1637 var userCalendar = ICU_CALENDAR_MAP[format.resolved.calendar];
1638 if (IS_UNDEFINED(userCalendar)) {
1639 // Use ICU name if we don't have a match. It shouldn't happen, but
1640 // it would be too strict to throw for this.
1641 userCalendar = format.resolved.calendar;
1642 }
1643
1644 var locale = getOptimalLanguageTag(format.resolved.requestedLocale,
1645 format.resolved.locale);
1646
1647 var result = {
1648 locale: locale,
1649 numberingSystem: format.resolved.numberingSystem,
1650 calendar: userCalendar,
1651 timeZone: format.resolved.timeZone
1652 };
1653
1654 addWECPropertyIfDefined(result, 'timeZoneName', fromPattern.timeZoneName);
1655 addWECPropertyIfDefined(result, 'era', fromPattern.era);
1656 addWECPropertyIfDefined(result, 'year', fromPattern.year);
1657 addWECPropertyIfDefined(result, 'month', fromPattern.month);
1658 addWECPropertyIfDefined(result, 'day', fromPattern.day);
1659 addWECPropertyIfDefined(result, 'weekday', fromPattern.weekday);
1660 addWECPropertyIfDefined(result, 'hour12', fromPattern.hour12);
1661 addWECPropertyIfDefined(result, 'hour', fromPattern.hour);
1662 addWECPropertyIfDefined(result, 'minute', fromPattern.minute);
1663 addWECPropertyIfDefined(result, 'second', fromPattern.second);
1664
1665 return result;
1666 },
1667 DONT_ENUM
1668 );
1669 %FunctionSetName(Intl.DateTimeFormat.prototype.resolvedOptions,
1670 'resolvedOptions');
1671 %FunctionRemovePrototype(Intl.DateTimeFormat.prototype.resolvedOptions);
1672 %SetNativeFlag(Intl.DateTimeFormat.prototype.resolvedOptions);
1673
1674
1675 /**
1676 * Returns the subset of the given locale list for which this locale list
1677 * has a matching (possibly fallback) locale. Locales appear in the same
1678 * order in the returned list as in the input list.
1679 * Options are optional parameter.
1680 */
1681 %AddNamedProperty(Intl.DateTimeFormat, 'supportedLocalesOf', function(locales) {
1682 if (%_IsConstructCall()) {
1683 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
1684 }
1685
1686 return supportedLocalesOf('dateformat', locales, %_Arguments(1));
1687 },
1688 DONT_ENUM
1689 );
1690 %FunctionSetName(Intl.DateTimeFormat.supportedLocalesOf, 'supportedLocalesOf');
1691 %FunctionRemovePrototype(Intl.DateTimeFormat.supportedLocalesOf);
1692 %SetNativeFlag(Intl.DateTimeFormat.supportedLocalesOf);
1693
1694
1695 /**
1696 * Returns a String value representing the result of calling ToNumber(date)
1697 * according to the effective locale and the formatting options of this
1698 * DateTimeFormat.
1699 */
1700 function formatDate(formatter, dateValue) {
1701 var dateMs;
1702 if (IS_UNDEFINED(dateValue)) {
1703 dateMs = %DateCurrentTime();
1704 } else {
1705 dateMs = TO_NUMBER(dateValue);
1706 }
1707
1708 if (!IsFinite(dateMs)) throw MakeRangeError(kDateRange);
1709
1710 return %InternalDateFormat(%GetImplFromInitializedIntlObject(formatter),
1711 new GlobalDate(dateMs));
1712 }
1713
1714
1715 /**
1716 * Returns a Date object representing the result of calling ToString(value)
1717 * according to the effective locale and the formatting options of this
1718 * DateTimeFormat.
1719 * Returns undefined if date string cannot be parsed.
1720 */
1721 function parseDate(formatter, value) {
1722 return %InternalDateParse(%GetImplFromInitializedIntlObject(formatter),
1723 GlobalString(value));
1724 }
1725
1726
1727 // 0 because date is optional argument.
1728 addBoundMethod(Intl.DateTimeFormat, 'format', formatDate, 0);
1729 addBoundMethod(Intl.DateTimeFormat, 'v8Parse', parseDate, 1);
1730
1731
1732 /**
1733 * Returns canonical Area/Location name, or throws an exception if the zone
1734 * name is invalid IANA name.
1735 */
1736 function canonicalizeTimeZoneID(tzID) {
1737 // Skip undefined zones.
1738 if (IS_UNDEFINED(tzID)) {
1739 return tzID;
1740 }
1741
1742 // Special case handling (UTC, GMT).
1743 var upperID = %StringToUpperCase(tzID);
1744 if (upperID === 'UTC' || upperID === 'GMT' ||
1745 upperID === 'ETC/UTC' || upperID === 'ETC/GMT') {
1746 return 'UTC';
1747 }
1748
1749 // We expect only _ and / beside ASCII letters.
1750 // All inputs should conform to Area/Location from now on.
1751 var match = %_CallFunction(tzID, GetTimezoneNameCheckRE(), StringMatch);
1752 if (IS_NULL(match)) throw MakeRangeError(kExpectedLocation, tzID);
1753
1754 var result = toTitleCaseWord(match[1]) + '/' + toTitleCaseWord(match[2]);
1755 var i = 3;
1756 while (!IS_UNDEFINED(match[i]) && i < match.length) {
1757 result = result + '_' + toTitleCaseWord(match[i]);
1758 i++;
1759 }
1760
1761 return result;
1762 }
1763
1764 /**
1765 * Initializes the given object so it's a valid BreakIterator instance.
1766 * Useful for subclassing.
1767 */
1768 function initializeBreakIterator(iterator, locales, options) {
1769 if (%IsInitializedIntlObject(iterator)) {
1770 throw MakeTypeError(kReinitializeIntl, "v8BreakIterator");
1771 }
1772
1773 if (IS_UNDEFINED(options)) {
1774 options = {};
1775 }
1776
1777 var getOption = getGetOption(options, 'breakiterator');
1778
1779 var internalOptions = {};
1780
1781 defineWEProperty(internalOptions, 'type', getOption(
1782 'type', 'string', ['character', 'word', 'sentence', 'line'], 'word'));
1783
1784 var locale = resolveLocale('breakiterator', locales, options);
1785 var resolved = ObjectDefineProperties({}, {
1786 requestedLocale: {value: locale.locale, writable: true},
1787 type: {value: internalOptions.type, writable: true},
1788 locale: {writable: true}
1789 });
1790
1791 var internalIterator = %CreateBreakIterator(locale.locale,
1792 internalOptions,
1793 resolved);
1794
1795 %MarkAsInitializedIntlObjectOfType(iterator, 'breakiterator',
1796 internalIterator);
1797 ObjectDefineProperty(iterator, 'resolved', {value: resolved});
1798
1799 return iterator;
1800 }
1801
1802
1803 /**
1804 * Constructs Intl.v8BreakIterator object given optional locales and options
1805 * parameters.
1806 *
1807 * @constructor
1808 */
1809 %AddNamedProperty(Intl, 'v8BreakIterator', function() {
1810 var locales = %_Arguments(0);
1811 var options = %_Arguments(1);
1812
1813 if (!this || this === Intl) {
1814 // Constructor is called as a function.
1815 return new Intl.v8BreakIterator(locales, options);
1816 }
1817
1818 return initializeBreakIterator(TO_OBJECT(this), locales, options);
1819 },
1820 DONT_ENUM
1821 );
1822
1823
1824 /**
1825 * BreakIterator resolvedOptions method.
1826 */
1827 %AddNamedProperty(Intl.v8BreakIterator.prototype, 'resolvedOptions',
1828 function() {
1829 if (%_IsConstructCall()) {
1830 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
1831 }
1832
1833 if (!%IsInitializedIntlObjectOfType(this, 'breakiterator')) {
1834 throw MakeTypeError(kResolvedOptionsCalledOnNonObject, "v8BreakIterator");
1835 }
1836
1837 var segmenter = this;
1838 var locale = getOptimalLanguageTag(segmenter.resolved.requestedLocale,
1839 segmenter.resolved.locale);
1840
1841 return {
1842 locale: locale,
1843 type: segmenter.resolved.type
1844 };
1845 },
1846 DONT_ENUM
1847 );
1848 %FunctionSetName(Intl.v8BreakIterator.prototype.resolvedOptions,
1849 'resolvedOptions');
1850 %FunctionRemovePrototype(Intl.v8BreakIterator.prototype.resolvedOptions);
1851 %SetNativeFlag(Intl.v8BreakIterator.prototype.resolvedOptions);
1852
1853
1854 /**
1855 * Returns the subset of the given locale list for which this locale list
1856 * has a matching (possibly fallback) locale. Locales appear in the same
1857 * order in the returned list as in the input list.
1858 * Options are optional parameter.
1859 */
1860 %AddNamedProperty(Intl.v8BreakIterator, 'supportedLocalesOf',
1861 function(locales) {
1862 if (%_IsConstructCall()) {
1863 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
1864 }
1865
1866 return supportedLocalesOf('breakiterator', locales, %_Arguments(1));
1867 },
1868 DONT_ENUM
1869 );
1870 %FunctionSetName(Intl.v8BreakIterator.supportedLocalesOf, 'supportedLocalesOf');
1871 %FunctionRemovePrototype(Intl.v8BreakIterator.supportedLocalesOf);
1872 %SetNativeFlag(Intl.v8BreakIterator.supportedLocalesOf);
1873
1874
1875 /**
1876 * Adopts text to segment using the iterator. Old text, if present,
1877 * gets discarded.
1878 */
1879 function adoptText(iterator, text) {
1880 %BreakIteratorAdoptText(%GetImplFromInitializedIntlObject(iterator),
1881 GlobalString(text));
1882 }
1883
1884
1885 /**
1886 * Returns index of the first break in the string and moves current pointer.
1887 */
1888 function first(iterator) {
1889 return %BreakIteratorFirst(%GetImplFromInitializedIntlObject(iterator));
1890 }
1891
1892
1893 /**
1894 * Returns the index of the next break and moves the pointer.
1895 */
1896 function next(iterator) {
1897 return %BreakIteratorNext(%GetImplFromInitializedIntlObject(iterator));
1898 }
1899
1900
1901 /**
1902 * Returns index of the current break.
1903 */
1904 function current(iterator) {
1905 return %BreakIteratorCurrent(%GetImplFromInitializedIntlObject(iterator));
1906 }
1907
1908
1909 /**
1910 * Returns type of the current break.
1911 */
1912 function breakType(iterator) {
1913 return %BreakIteratorBreakType(%GetImplFromInitializedIntlObject(iterator));
1914 }
1915
1916
1917 addBoundMethod(Intl.v8BreakIterator, 'adoptText', adoptText, 1);
1918 addBoundMethod(Intl.v8BreakIterator, 'first', first, 0);
1919 addBoundMethod(Intl.v8BreakIterator, 'next', next, 0);
1920 addBoundMethod(Intl.v8BreakIterator, 'current', current, 0);
1921 addBoundMethod(Intl.v8BreakIterator, 'breakType', breakType, 0);
1922
1923 // Save references to Intl objects and methods we use, for added security.
1924 var savedObjects = {
1925 'collator': Intl.Collator,
1926 'numberformat': Intl.NumberFormat,
1927 'dateformatall': Intl.DateTimeFormat,
1928 'dateformatdate': Intl.DateTimeFormat,
1929 'dateformattime': Intl.DateTimeFormat
1930 };
1931
1932
1933 // Default (created with undefined locales and options parameters) collator,
1934 // number and date format instances. They'll be created as needed.
1935 var defaultObjects = {
1936 'collator': UNDEFINED,
1937 'numberformat': UNDEFINED,
1938 'dateformatall': UNDEFINED,
1939 'dateformatdate': UNDEFINED,
1940 'dateformattime': UNDEFINED,
1941 };
1942
1943
1944 /**
1945 * Returns cached or newly created instance of a given service.
1946 * We cache only default instances (where no locales or options are provided).
1947 */
1948 function cachedOrNewService(service, locales, options, defaults) {
1949 var useOptions = (IS_UNDEFINED(defaults)) ? options : defaults;
1950 if (IS_UNDEFINED(locales) && IS_UNDEFINED(options)) {
1951 if (IS_UNDEFINED(defaultObjects[service])) {
1952 defaultObjects[service] = new savedObjects[service](locales, useOptions);
1953 }
1954 return defaultObjects[service];
1955 }
1956 return new savedObjects[service](locales, useOptions);
1957 }
1958
1959
1960 function OverrideFunction(object, name, f) {
1961 %CheckIsBootstrapping();
1962 ObjectDefineProperty(object, name, { value: f,
1963 writeable: true,
1964 configurable: true,
1965 enumerable: false });
1966 %FunctionSetName(f, name);
1967 %FunctionRemovePrototype(f);
1968 %SetNativeFlag(f);
1969 }
1970
1971 /**
1972 * Compares this and that, and returns less than 0, 0 or greater than 0 value.
1973 * Overrides the built-in method.
1974 */
1975 OverrideFunction(GlobalString.prototype, 'localeCompare', function(that) {
1976 if (%_IsConstructCall()) {
1977 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
1978 }
1979
1980 if (IS_NULL_OR_UNDEFINED(this)) {
1981 throw MakeTypeError(kMethodInvokedOnNullOrUndefined);
1982 }
1983
1984 var locales = %_Arguments(1);
1985 var options = %_Arguments(2);
1986 var collator = cachedOrNewService('collator', locales, options);
1987 return compare(collator, this, that);
1988 }
1989 );
1990
1991
1992 /**
1993 * Unicode normalization. This method is called with one argument that
1994 * specifies the normalization form.
1995 * If none is specified, "NFC" is assumed.
1996 * If the form is not one of "NFC", "NFD", "NFKC", or "NFKD", then throw
1997 * a RangeError Exception.
1998 */
1999
2000 OverrideFunction(GlobalString.prototype, 'normalize', function() {
2001 if (%_IsConstructCall()) {
2002 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
2003 }
2004
2005 CHECK_OBJECT_COERCIBLE(this, "String.prototype.normalize");
2006 var s = TO_STRING(this);
2007
2008 var formArg = %_Arguments(0);
2009 var form = IS_UNDEFINED(formArg) ? 'NFC' : TO_STRING(formArg);
2010
2011 var NORMALIZATION_FORMS = ['NFC', 'NFD', 'NFKC', 'NFKD'];
2012
2013 var normalizationForm =
2014 %_CallFunction(NORMALIZATION_FORMS, form, ArrayIndexOf);
2015 if (normalizationForm === -1) {
2016 throw MakeRangeError(kNormalizationForm,
2017 %_CallFunction(NORMALIZATION_FORMS, ', ', ArrayJoin));
2018 }
2019
2020 return %StringNormalize(s, normalizationForm);
2021 }
2022 );
2023
2024
2025 /**
2026 * Formats a Number object (this) using locale and options values.
2027 * If locale or options are omitted, defaults are used.
2028 */
2029 OverrideFunction(GlobalNumber.prototype, 'toLocaleString', function() {
2030 if (%_IsConstructCall()) {
2031 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
2032 }
2033
2034 if (!(this instanceof GlobalNumber) && typeof(this) !== 'number') {
2035 throw MakeTypeError(kMethodInvokedOnWrongType, "Number");
2036 }
2037
2038 var locales = %_Arguments(0);
2039 var options = %_Arguments(1);
2040 var numberFormat = cachedOrNewService('numberformat', locales, options);
2041 return formatNumber(numberFormat, this);
2042 }
2043 );
2044
2045
2046 /**
2047 * Returns actual formatted date or fails if date parameter is invalid.
2048 */
2049 function toLocaleDateTime(date, locales, options, required, defaults, service) {
2050 if (!(date instanceof GlobalDate)) {
2051 throw MakeTypeError(kMethodInvokedOnWrongType, "Date");
2052 }
2053
2054 if (IsNaN(date)) return 'Invalid Date';
2055
2056 var internalOptions = toDateTimeOptions(options, required, defaults);
2057
2058 var dateFormat =
2059 cachedOrNewService(service, locales, options, internalOptions);
2060
2061 return formatDate(dateFormat, date);
2062 }
2063
2064
2065 /**
2066 * Formats a Date object (this) using locale and options values.
2067 * If locale or options are omitted, defaults are used - both date and time are
2068 * present in the output.
2069 */
2070 OverrideFunction(GlobalDate.prototype, 'toLocaleString', function() {
2071 if (%_IsConstructCall()) {
2072 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
2073 }
2074
2075 var locales = %_Arguments(0);
2076 var options = %_Arguments(1);
2077 return toLocaleDateTime(
2078 this, locales, options, 'any', 'all', 'dateformatall');
2079 }
2080 );
2081
2082
2083 /**
2084 * Formats a Date object (this) using locale and options values.
2085 * If locale or options are omitted, defaults are used - only date is present
2086 * in the output.
2087 */
2088 OverrideFunction(GlobalDate.prototype, 'toLocaleDateString', function() {
2089 if (%_IsConstructCall()) {
2090 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
2091 }
2092
2093 var locales = %_Arguments(0);
2094 var options = %_Arguments(1);
2095 return toLocaleDateTime(
2096 this, locales, options, 'date', 'date', 'dateformatdate');
2097 }
2098 );
2099
2100
2101 /**
2102 * Formats a Date object (this) using locale and options values.
2103 * If locale or options are omitted, defaults are used - only time is present
2104 * in the output.
2105 */
2106 OverrideFunction(GlobalDate.prototype, 'toLocaleTimeString', function() {
2107 if (%_IsConstructCall()) {
2108 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
2109 }
2110
2111 var locales = %_Arguments(0);
2112 var options = %_Arguments(1);
2113 return toLocaleDateTime(
2114 this, locales, options, 'time', 'time', 'dateformattime');
2115 }
2116 );
2117
2118 })
OLDNEW
« no previous file with comments | « src/harmony-typedarray.js ('k') | src/iterator-prototype.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698