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

Side by Side Diff: src/i18n.js

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

Powered by Google App Engine
This is Rietveld 408576698