1b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch// Copyright 2013 the V8 project authors. All rights reserved.
2b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch// found in the LICENSE file.
4b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
5b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch// ECMAScript 402 API implementation.
6b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
7b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
8b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Intl object is a single object that has some named properties,
9b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * all of which are constructors.
10b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
11014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch(function(global, utils) {
12b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
13014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch"use strict";
14b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
15014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch%CheckIsBootstrapping();
16014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
17014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch// -------------------------------------------------------------------
18014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch// Imports
19014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
20014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochvar ArrayJoin;
21014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochvar ArrayPush;
22014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochvar GlobalDate = global.Date;
2362ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdochvar GlobalIntl = global.Intl;
2462ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdochvar GlobalIntlDateTimeFormat = GlobalIntl.DateTimeFormat;
2562ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdochvar GlobalIntlNumberFormat = GlobalIntl.NumberFormat;
2662ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdochvar GlobalIntlCollator = GlobalIntl.Collator;
2762ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdochvar GlobalIntlv8BreakIterator = GlobalIntl.v8BreakIterator;
28014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochvar GlobalNumber = global.Number;
29014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochvar GlobalRegExp = global.RegExp;
30014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochvar GlobalString = global.String;
3162ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdochvar IntlFallbackSymbol = utils.ImportNow("intl_fallback_symbol");
323b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochvar InstallFunctions = utils.InstallFunctions;
333b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochvar InstallGetter = utils.InstallGetter;
34bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdochvar InternalArray = utils.InternalArray;
353b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochvar ObjectHasOwnProperty = utils.ImportNow("ObjectHasOwnProperty");
363b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochvar OverrideFunction = utils.OverrideFunction;
37014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochvar patternSymbol = utils.ImportNow("intl_pattern_symbol");
38014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochvar resolvedSymbol = utils.ImportNow("intl_resolved_symbol");
393b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochvar SetFunctionName = utils.SetFunctionName;
40f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdochvar StringSubstr = GlobalString.prototype.substr;
41f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdochvar StringSubstring = GlobalString.prototype.substring;
42014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
43014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochutils.Import(function(from) {
44014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  ArrayJoin = from.ArrayJoin;
45014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  ArrayPush = from.ArrayPush;
46bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch});
47bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch
483b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch// Utilities for definitions
493b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
503b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdochfunction InstallFunction(object, name, func) {
513b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  InstallFunctions(object, DONT_ENUM, [name, func]);
523b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch}
533b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
543b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
553b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch/**
563b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch * Adds bound method to the prototype of the given object.
573b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch */
5862ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdochfunction AddBoundMethod(obj, methodName, implementation, length, typename,
5962ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch                        compat) {
603b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  %CheckIsBootstrapping();
613b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  var internalName = %CreatePrivateSymbol(methodName);
62f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  // Making getter an anonymous function will cause
63f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  // %DefineGetterPropertyUnchecked to properly set the "name"
64f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  // property on each JSFunction instance created here, rather
65f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  // than (as utils.InstallGetter would) on the SharedFunctionInfo
66f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  // associated with all functions returned from AddBoundMethod.
67f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  var getter = ANONYMOUS_FUNCTION(function() {
6862ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch    var receiver = Unwrap(this, typename, obj, methodName, compat);
6962ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch    if (IS_UNDEFINED(receiver[internalName])) {
703b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      var boundMethod;
713b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      if (IS_UNDEFINED(length) || length === 2) {
72c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch        boundMethod =
7362ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch          ANONYMOUS_FUNCTION((fst, snd) => implementation(receiver, fst, snd));
743b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      } else if (length === 1) {
7562ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch        boundMethod = ANONYMOUS_FUNCTION(fst => implementation(receiver, fst));
763b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      } else {
77f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        boundMethod = ANONYMOUS_FUNCTION((...args) => {
78f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch          // DateTimeFormat.format needs to be 0 arg method, but can still
79f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch          // receive an optional dateValue param. If one was provided, pass it
803b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch          // along.
813b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch          if (args.length > 0) {
8262ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch            return implementation(receiver, args[0]);
833b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch          } else {
8462ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch            return implementation(receiver);
853b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch          }
86f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        });
873b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      }
883b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      %SetNativeFlag(boundMethod);
8962ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch      receiver[internalName] = boundMethod;
903b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    }
9162ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch    return receiver[internalName];
92f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  });
933b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
94f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  %FunctionRemovePrototype(getter);
95f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  %DefineGetterPropertyUnchecked(obj.prototype, methodName, getter, DONT_ENUM);
96f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  %SetNativeFlag(getter);
973b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch}
983b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch
9962ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdochfunction IntlConstruct(receiver, constructor, create, newTarget, args,
10062ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch                       compat) {
10162ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  var locales = args[0];
10262ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  var options = args[1];
103b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
10462ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  if (IS_UNDEFINED(newTarget)) {
10562ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch    if (compat && receiver instanceof constructor) {
10662ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch      let success = %object_define_property(receiver, IntlFallbackSymbol,
10762ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch                           { value: new constructor(locales, options) });
10862ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch      if (!success) {
10962ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch        throw %make_type_error(kReinitializeIntl, constructor);
11062ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch      }
11162ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch      return receiver;
11262ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch    }
11362ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch
11462ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch    return new constructor(locales, options);
11562ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  }
11662ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch
11762ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  return create(locales, options);
11862ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch}
119b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
12062ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch
12162ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch
12262ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdochfunction Unwrap(receiver, typename, constructor, method, compat) {
12362ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  if (!%IsInitializedIntlObjectOfType(receiver, typename)) {
12462ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch    if (compat && receiver instanceof constructor) {
12562ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch      let fallback = receiver[IntlFallbackSymbol];
12662ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch      if (%IsInitializedIntlObjectOfType(fallback, typename)) {
12762ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch        return fallback;
12862ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch      }
12962ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch    }
13062ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch    throw %make_type_error(kIncompatibleMethodReceiver, method, receiver);
13162ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  }
13262ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  return receiver;
13362ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch}
13462ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch
13562ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch
13662ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch// -------------------------------------------------------------------
137b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
138b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
139b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Caches available locales for each service.
140b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
141b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochvar AVAILABLE_LOCALES = {
142014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  'collator': UNDEFINED,
143014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  'numberformat': UNDEFINED,
144014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  'dateformat': UNDEFINED,
145014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  'breakiterator': UNDEFINED
146b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch};
147b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
148b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
149b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Caches default ICU locale.
150b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
151014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochvar DEFAULT_ICU_LOCALE = UNDEFINED;
152b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
153bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdochfunction GetDefaultICULocaleJS() {
154bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  if (IS_UNDEFINED(DEFAULT_ICU_LOCALE)) {
155bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch    DEFAULT_ICU_LOCALE = %GetDefaultICULocale();
15662ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch    // Check that this is a valid default, otherwise fall back to "und"
15762ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch    for (let service in AVAILABLE_LOCALES) {
15862ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch      if (IS_UNDEFINED(getAvailableLocalesOf(service)[DEFAULT_ICU_LOCALE])) {
15962ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch        DEFAULT_ICU_LOCALE = "und";
16062ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch        break;
16162ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch      }
16262ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch    }
163bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  }
164bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  return DEFAULT_ICU_LOCALE;
165bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch}
166bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch
167b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
168b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Unicode extension regular expression.
169b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
170014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochvar UNICODE_EXTENSION_RE = UNDEFINED;
171b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
172b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction GetUnicodeExtensionRE() {
173014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (IS_UNDEFINED(UNDEFINED)) {
174014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    UNICODE_EXTENSION_RE = new GlobalRegExp('-u(-[a-z0-9]{2,8})+', 'g');
175b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
176b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return UNICODE_EXTENSION_RE;
177b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
178b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
179b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
180b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Matches any Unicode extension.
181b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
182014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochvar ANY_EXTENSION_RE = UNDEFINED;
183b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
184b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction GetAnyExtensionRE() {
185014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (IS_UNDEFINED(ANY_EXTENSION_RE)) {
186014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    ANY_EXTENSION_RE = new GlobalRegExp('-[a-z0-9]{1}-.*', 'g');
187b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
188b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return ANY_EXTENSION_RE;
189b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
190b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
191b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
192b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Replace quoted text (single quote, anything but the quote and quote again).
193b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
194014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochvar QUOTED_STRING_RE = UNDEFINED;
195b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
196b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction GetQuotedStringRE() {
197014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (IS_UNDEFINED(QUOTED_STRING_RE)) {
198014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    QUOTED_STRING_RE = new GlobalRegExp("'[^']+'", 'g');
199b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
200b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return QUOTED_STRING_RE;
201b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
202b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
203b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
204b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Matches valid service name.
205b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
206014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochvar SERVICE_RE = UNDEFINED;
207b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
208b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction GetServiceRE() {
209014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (IS_UNDEFINED(SERVICE_RE)) {
210b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    SERVICE_RE =
211014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        new GlobalRegExp('^(collator|numberformat|dateformat|breakiterator)$');
212b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
213b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return SERVICE_RE;
214b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
215b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
216b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
217b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Validates a language tag against bcp47 spec.
218b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Actual value is assigned on first run.
219b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
220014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochvar LANGUAGE_TAG_RE = UNDEFINED;
221b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
222b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction GetLanguageTagRE() {
223014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (IS_UNDEFINED(LANGUAGE_TAG_RE)) {
224b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    BuildLanguageTagREs();
225b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
226b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return LANGUAGE_TAG_RE;
227b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
228b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
229b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
230b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Helps find duplicate variants in the language tag.
231b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
232014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochvar LANGUAGE_VARIANT_RE = UNDEFINED;
233b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
234b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction GetLanguageVariantRE() {
235014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (IS_UNDEFINED(LANGUAGE_VARIANT_RE)) {
236b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    BuildLanguageTagREs();
237b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
238b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return LANGUAGE_VARIANT_RE;
239b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
240b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
241b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
242b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Helps find duplicate singletons in the language tag.
243b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
244014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochvar LANGUAGE_SINGLETON_RE = UNDEFINED;
245b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
246b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction GetLanguageSingletonRE() {
247014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (IS_UNDEFINED(LANGUAGE_SINGLETON_RE)) {
248b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    BuildLanguageTagREs();
249b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
250b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return LANGUAGE_SINGLETON_RE;
251b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
252b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
253b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
254b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Matches valid IANA time zone names.
255b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
256014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochvar TIMEZONE_NAME_CHECK_RE = UNDEFINED;
257b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
258b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction GetTimezoneNameCheckRE() {
259014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (IS_UNDEFINED(TIMEZONE_NAME_CHECK_RE)) {
260014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    TIMEZONE_NAME_CHECK_RE = new GlobalRegExp(
261014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        '^([A-Za-z]+)/([A-Za-z_-]+)((?:\/[A-Za-z_-]+)+)*$');
262b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
263b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return TIMEZONE_NAME_CHECK_RE;
264b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
265b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
266b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
267014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * Matches valid location parts of IANA time zone names.
268b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
269014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochvar TIMEZONE_NAME_LOCATION_PART_RE = UNDEFINED;
270b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
271014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochfunction GetTimezoneNameLocationPartRE() {
272014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (IS_UNDEFINED(TIMEZONE_NAME_LOCATION_PART_RE)) {
273014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    TIMEZONE_NAME_LOCATION_PART_RE =
274014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        new GlobalRegExp('^([A-Za-z]+)((?:[_-][A-Za-z]+)+)*$');
275014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  }
276014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  return TIMEZONE_NAME_LOCATION_PART_RE;
277014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch}
278b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
279b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
280b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
281b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns an intersection of locales and service supported locales.
282b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Parameter locales is treated as a priority list.
283b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
284b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction supportedLocalesOf(service, locales, options) {
285c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch  if (IS_NULL(%regexp_internal_match(GetServiceRE(), service))) {
286f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    throw %make_error(kWrongServiceType, service);
287b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
288b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
289b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Provide defaults if matcher was not specified.
290014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (IS_UNDEFINED(options)) {
291b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    options = {};
292b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  } else {
293014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    options = TO_OBJECT(options);
294b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
295b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
296b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var matcher = options.localeMatcher;
297014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (!IS_UNDEFINED(matcher)) {
29813e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch    matcher = TO_STRING(matcher);
299b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if (matcher !== 'lookup' && matcher !== 'best fit') {
300f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      throw %make_range_error(kLocaleMatcher, matcher);
301b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
302b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  } else {
303b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    matcher = 'best fit';
304b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
305b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
306b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var requestedLocales = initializeLocaleList(locales);
307b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
30862ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  var availableLocales = getAvailableLocalesOf(service);
309b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
310b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Use either best fit or lookup algorithm to match locales.
311b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  if (matcher === 'best fit') {
312b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return initializeLocaleList(bestFitSupportedLocalesOf(
31362ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch        requestedLocales, availableLocales));
314b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
315b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
316b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return initializeLocaleList(lookupSupportedLocalesOf(
31762ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch      requestedLocales, availableLocales));
318b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
319b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
320b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
321b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
322b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns the subset of the provided BCP 47 language priority list for which
323b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * this service has a matching locale when using the BCP 47 Lookup algorithm.
324b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Locales appear in the same order in the returned list as in the input list.
325b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
326b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction lookupSupportedLocalesOf(requestedLocales, availableLocales) {
327bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  var matchedLocales = new InternalArray();
328b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  for (var i = 0; i < requestedLocales.length; ++i) {
329b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    // Remove -u- extension.
330c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch    var locale = %RegExpInternalReplace(
3313b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch        GetUnicodeExtensionRE(), requestedLocales[i], '');
332b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    do {
333014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      if (!IS_UNDEFINED(availableLocales[locale])) {
334b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        // Push requested locale not the resolved one.
335014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        %_Call(ArrayPush, matchedLocales, requestedLocales[i]);
336b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        break;
337b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      }
338b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      // Truncate locale if possible, if not break.
339f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdoch      var pos = %StringLastIndexOf(locale, '-');
340b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      if (pos === -1) {
341b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        break;
342b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      }
343014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      locale = %_Call(StringSubstring, locale, 0, pos);
344b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    } while (true);
345b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
346b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
347b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return matchedLocales;
348b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
349b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
350b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
351b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
352b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns the subset of the provided BCP 47 language priority list for which
353b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * this service has a matching locale when using the implementation
354b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * dependent algorithm.
355b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Locales appear in the same order in the returned list as in the input list.
356b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
357b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction bestFitSupportedLocalesOf(requestedLocales, availableLocales) {
358b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // TODO(cira): implement better best fit algorithm.
359b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return lookupSupportedLocalesOf(requestedLocales, availableLocales);
360b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
361b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
362b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
363b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
364b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns a getOption function that extracts property value for given
365b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * options object. If property is missing it returns defaultValue. If value
366b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * is out of range for that property it throws RangeError.
367b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
368b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction getGetOption(options, caller) {
369f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  if (IS_UNDEFINED(options)) throw %make_error(kDefaultOptionsMissing, caller);
370b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
371b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var getOption = function getOption(property, type, values, defaultValue) {
372014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    if (!IS_UNDEFINED(options[property])) {
373b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      var value = options[property];
374b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      switch (type) {
375b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        case 'boolean':
37613e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch          value = TO_BOOLEAN(value);
377b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          break;
378b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        case 'string':
37913e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch          value = TO_STRING(value);
380b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          break;
381b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        case 'number':
38213e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch          value = TO_NUMBER(value);
383b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          break;
384b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        default:
385f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch          throw %make_error(kWrongValueType);
386b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      }
387014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
388f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      if (!IS_UNDEFINED(values) && %ArrayIndexOf(values, value, 0) === -1) {
389f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        throw %make_range_error(kValueOutOfRange, value, caller, property);
390b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      }
391b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
392b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      return value;
393b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
394b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
395b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return defaultValue;
396b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
397b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
398b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return getOption;
399b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
400b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
401b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
402b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
403f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch * Ecma 402 9.2.5
404f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch * TODO(jshin): relevantExtensionKeys and localeData need to be taken into
405f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch * account per spec.
406b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Compares a BCP 47 language priority list requestedLocales against the locales
407b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * in availableLocales and determines the best available language to meet the
408b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * request. Two algorithms are available to match the locales: the Lookup
409b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * algorithm described in RFC 4647 section 3.4, and an implementation dependent
410b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * best-fit algorithm. Independent of the locale matching algorithm, options
411b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * specified through Unicode locale extension sequences are negotiated
412b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * separately, taking the caller's relevant extension keys and locale data as
413b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * well as client-provided options into consideration. Returns an object with
414b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * a locale property whose value is the language tag of the selected locale,
415b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * and properties for each key in relevantExtensionKeys providing the selected
416b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * value for that key.
417b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
418b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction resolveLocale(service, requestedLocales, options) {
419b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  requestedLocales = initializeLocaleList(requestedLocales);
420b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
421b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var getOption = getGetOption(options, service);
422b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var matcher = getOption('localeMatcher', 'string',
423b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                          ['lookup', 'best fit'], 'best fit');
424b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var resolved;
425b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  if (matcher === 'lookup') {
426b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    resolved = lookupMatcher(service, requestedLocales);
427b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  } else {
428b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    resolved = bestFitMatcher(service, requestedLocales);
429b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
430b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
431b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return resolved;
432b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
433b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
434b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
435b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
436b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns best matched supported locale and extension info using basic
437b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * lookup algorithm.
438b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
439b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction lookupMatcher(service, requestedLocales) {
440c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch  if (IS_NULL(%regexp_internal_match(GetServiceRE(), service))) {
441f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    throw %make_error(kWrongServiceType, service);
442b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
443b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
44462ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  var availableLocales = getAvailableLocalesOf(service);
445b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
446b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  for (var i = 0; i < requestedLocales.length; ++i) {
447b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    // Remove all extensions.
448c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch    var locale = %RegExpInternalReplace(
4493b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch        GetAnyExtensionRE(), requestedLocales[i], '');
450b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    do {
45162ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch      if (!IS_UNDEFINED(availableLocales[locale])) {
452b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        // Return the resolved locale and extension.
453c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch        var extensionMatch = %regexp_internal_match(
4543b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch            GetUnicodeExtensionRE(), requestedLocales[i]);
455b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        var extension = IS_NULL(extensionMatch) ? '' : extensionMatch[0];
456b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        return {'locale': locale, 'extension': extension, 'position': i};
457b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      }
458b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      // Truncate locale if possible.
459f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdoch      var pos = %StringLastIndexOf(locale, '-');
460b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      if (pos === -1) {
461b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        break;
462b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      }
463014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      locale = %_Call(StringSubstring, locale, 0, pos);
464b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    } while (true);
465b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
466b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
467b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Didn't find a match, return default.
468bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  return {'locale': GetDefaultICULocaleJS(), 'extension': '', 'position': -1};
469b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
470b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
471b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
472b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
473b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns best matched supported locale and extension info using
474b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * implementation dependend algorithm.
475b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
476b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction bestFitMatcher(service, requestedLocales) {
477b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // TODO(cira): implement better best fit algorithm.
478b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return lookupMatcher(service, requestedLocales);
479b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
480b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
481b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
482b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
483b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Parses Unicode extension into key - value map.
484b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns empty object if the extension string is invalid.
485b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * We are not concerned with the validity of the values at this point.
486f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch * 'attribute' in RFC 6047 is not supported. Keys without explicit
487f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch * values are assigned UNDEFINED.
488f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch * TODO(jshin): Fix the handling of 'attribute' (in RFC 6047, but none
489f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch * has been defined so that it's not used) and boolean keys without
490f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch * an explicit value.
491b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
492b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction parseExtension(extension) {
493f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  var extensionSplit = %StringSplit(extension, '-', kMaxUint32);
494b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
495b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Assume ['', 'u', ...] input, but don't throw.
496b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  if (extensionSplit.length <= 2 ||
497b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      (extensionSplit[0] !== '' && extensionSplit[1] !== 'u')) {
498b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return {};
499b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
500b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
501b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Key is {2}alphanum, value is {3,8}alphanum.
502b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Some keys may not have explicit values (booleans).
503b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var extensionMap = {};
504f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  var key = UNDEFINED;
505f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  var value = UNDEFINED;
506b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  for (var i = 2; i < extensionSplit.length; ++i) {
507b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    var length = extensionSplit[i].length;
508b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    var element = extensionSplit[i];
509b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if (length === 2) {
510f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      if (!IS_UNDEFINED(key)) {
511f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        if (!(key in extensionMap)) {
512f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch          extensionMap[key] = value;
513f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        }
514f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        value = UNDEFINED;
515f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      }
516f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      key = element;
517f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    } else if (length >= 3 && length <= 8 && !IS_UNDEFINED(key)) {
518f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      if (IS_UNDEFINED(value)) {
519f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        value = element;
520f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      } else {
521f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        value = value + "-" + element;
522f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      }
523b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    } else {
524b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      // There is a value that's too long, or that doesn't have a key.
525b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      return {};
526b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
527b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
528f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  if (!IS_UNDEFINED(key) && !(key in extensionMap)) {
529f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    extensionMap[key] = value;
530f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  }
531b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
532b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return extensionMap;
533b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
534b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
535b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
536b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
537b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Populates internalOptions object with boolean key-value pairs
538b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * from extensionMap and options.
539b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns filtered extension (number and date format constructors use
540b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Unicode extensions for passing parameters to ICU).
541b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * It's used for extension-option pairs only, e.g. kn-normalization, but not
542b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * for 'sensitivity' since it doesn't have extension equivalent.
543b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Extensions like nu and ca don't have options equivalent, so we place
544b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * undefined in the map.property to denote that.
545b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
546b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction setOptions(inOptions, extensionMap, keyValues, getOption, outOptions) {
547b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var extension = '';
548b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
549b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var updateExtension = function updateExtension(key, value) {
55013e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch    return '-' + key + '-' + TO_STRING(value);
551b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
552b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
553b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var updateProperty = function updateProperty(property, type, value) {
554b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if (type === 'boolean' && (typeof value === 'string')) {
555b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      value = (value === 'true') ? true : false;
556b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
557b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
558014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    if (!IS_UNDEFINED(property)) {
559b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      defineWEProperty(outOptions, property, value);
560b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
561b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
562b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
563b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  for (var key in keyValues) {
5643b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    if (HAS_OWN_PROPERTY(keyValues, key)) {
565014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      var value = UNDEFINED;
566b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      var map = keyValues[key];
567014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      if (!IS_UNDEFINED(map.property)) {
568b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        // This may return true if user specifies numeric: 'false', since
569b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        // Boolean('nonempty') === true.
570b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        value = getOption(map.property, map.type, map.values);
571b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      }
572014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      if (!IS_UNDEFINED(value)) {
573b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        updateProperty(map.property, map.type, value);
574b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        extension += updateExtension(key, value);
575b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        continue;
576b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      }
577b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      // User options didn't have it, check Unicode extension.
578b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      // Here we want to convert strings 'true', 'false' into proper Boolean
579b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      // values (not a user error).
5803b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      if (HAS_OWN_PROPERTY(extensionMap, key)) {
581b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        value = extensionMap[key];
582014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        if (!IS_UNDEFINED(value)) {
583b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          updateProperty(map.property, map.type, value);
584b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          extension += updateExtension(key, value);
585b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        } else if (map.type === 'boolean') {
586b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          // Boolean keys are allowed not to have values in Unicode extension.
587b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          // Those default to true.
588b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          updateProperty(map.property, map.type, true);
589b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          extension += updateExtension(key, true);
590b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        }
591b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      }
592b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
593b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
594b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
595b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return extension === ''? '' : '-u' + extension;
596b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
597b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
598b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
599b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
600bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch * Given an array-like, outputs an Array with the numbered
601bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch * properties copied over and defined
602b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * configurable: false, writable: false, enumerable: true.
603f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch * When |expandable| is true, the result array can be expanded.
604b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
605bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdochfunction freezeArray(input) {
606bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  var array = [];
607bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  var l = input.length;
608014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  for (var i = 0; i < l; i++) {
609bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch    if (i in input) {
610bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch      %object_define_property(array, i, {value: input[i],
611bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch                                         configurable: false,
612bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch                                         writable: false,
613bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch                                         enumerable: true});
614014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    }
615014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  }
616b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
617bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  %object_define_property(array, 'length', {value: l, writable: false});
618b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return array;
619b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
620b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
621f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch/* Make JS array[] out of InternalArray */
622f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochfunction makeArray(input) {
623f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  var array = [];
624f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  %MoveArrayContents(input, array);
625f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  return array;
626f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch}
627b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
628b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
629b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * It's sometimes desireable to leave user requested locale instead of ICU
630b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * supported one (zh-TW is equivalent to zh-Hant-TW, so we should keep shorter
631b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * one, if that was what user requested).
632b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * This function returns user specified tag if its maximized form matches ICU
633b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * resolved locale. If not we return ICU result.
634b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
635b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction getOptimalLanguageTag(original, resolved) {
636b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Returns Array<Object>, where each object has maximized and base properties.
637b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Maximized: zh -> zh-Hans-CN
638b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Base: zh-CN-u-ca-gregory -> zh-CN
639b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Take care of grandfathered or simple cases.
640b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  if (original === resolved) {
641b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return original;
642b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
643b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
644b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var locales = %GetLanguageTagVariants([original, resolved]);
645b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  if (locales[0].maximized !== locales[1].maximized) {
646b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return resolved;
647b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
648b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
649b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Preserve extensions of resolved locale, but swap base tags with original.
6503b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  var resolvedBase = new GlobalRegExp('^' + locales[1].base, 'g');
651c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch  return %RegExpInternalReplace(resolvedBase, resolved, locales[0].base);
652b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
653b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
654b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
655b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
656b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns an Object that contains all of supported locales for a given
657b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * service.
658b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * In addition to the supported locales we add xx-ZZ locale for each xx-Yyyy-ZZ
659b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * that is supported. This is required by the spec.
660b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
661b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction getAvailableLocalesOf(service) {
66262ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  // Cache these, they don't ever change per service.
66362ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  if (!IS_UNDEFINED(AVAILABLE_LOCALES[service])) {
66462ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch    return AVAILABLE_LOCALES[service];
66562ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  }
66662ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch
667b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var available = %AvailableLocalesOf(service);
668b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
669b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  for (var i in available) {
6703b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    if (HAS_OWN_PROPERTY(available, i)) {
671c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch      var parts = %regexp_internal_match(
6723b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch          /^([a-z]{2,3})-([A-Z][a-z]{3})-([A-Z]{2})$/, i);
6733b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch      if (!IS_NULL(parts)) {
674b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        // Build xx-ZZ. We don't care about the actual value,
675b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        // as long it's not undefined.
676b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        available[parts[1] + '-' + parts[3]] = null;
677b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      }
678b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
679b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
680b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
68162ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  AVAILABLE_LOCALES[service] = available;
68262ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch
683b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return available;
684b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
685b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
686b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
687b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
688b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Defines a property and sets writable and enumerable to true.
689b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Configurable is false by default.
690b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
691b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction defineWEProperty(object, property, value) {
692bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  %object_define_property(object, property,
693bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch                          {value: value, writable: true, enumerable: true});
694b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
695b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
696b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
697b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
698b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Adds property to an object if the value is not undefined.
699b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Sets configurable descriptor to false.
700b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
701b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction addWEPropertyIfDefined(object, property, value) {
702014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (!IS_UNDEFINED(value)) {
703b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    defineWEProperty(object, property, value);
704b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
705b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
706b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
707b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
708b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
709b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Defines a property and sets writable, enumerable and configurable to true.
710b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
711b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction defineWECProperty(object, property, value) {
712bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  %object_define_property(object, property, {value: value,
713bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch                                             writable: true,
714bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch                                             enumerable: true,
715bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch                                             configurable: true});
716b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
717b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
718b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
719b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
720b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Adds property to an object if the value is not undefined.
721b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Sets all descriptors to true.
722b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
723b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction addWECPropertyIfDefined(object, property, value) {
724014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (!IS_UNDEFINED(value)) {
725b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    defineWECProperty(object, property, value);
726b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
727b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
728b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
729b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
730b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
731b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns titlecased word, aMeRricA -> America.
732b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
733b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction toTitleCaseWord(word) {
73462ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  return %StringToUpperCaseI18N(%_Call(StringSubstr, word, 0, 1)) +
73562ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch         %StringToLowerCaseI18N(%_Call(StringSubstr, word, 1));
736014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch}
737014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
738014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch/**
739014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * Returns titlecased location, bueNos_airES -> Buenos_Aires
740014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * or ho_cHi_minH -> Ho_Chi_Minh. It is locale-agnostic and only
741014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * deals with ASCII only characters.
742014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * 'of', 'au' and 'es' are special-cased and lowercased.
743014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch */
744014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochfunction toTitleCaseTimezoneLocation(location) {
745c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch  var match = %regexp_internal_match(GetTimezoneNameLocationPartRE(), location)
746f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  if (IS_NULL(match)) throw %make_range_error(kExpectedLocation, location);
747014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
748014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  var result = toTitleCaseWord(match[1]);
749014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (!IS_UNDEFINED(match[2]) && 2 < match.length) {
750014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    // The first character is a separator, '_' or '-'.
751014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    // None of IANA zone names has both '_' and '-'.
752014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    var separator = %_Call(StringSubstring, match[2], 0, 1);
753f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    var parts = %StringSplit(match[2], separator, kMaxUint32);
754014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    for (var i = 1; i < parts.length; i++) {
755014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      var part = parts[i]
75662ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch      var lowercasedPart = %StringToLowerCaseI18N(part);
757014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      result = result + separator +
758014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          ((lowercasedPart !== 'es' &&
759014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch            lowercasedPart !== 'of' && lowercasedPart !== 'au') ?
760014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          toTitleCaseWord(part) : lowercasedPart);
761014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    }
762014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  }
763014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  return result;
764b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
765b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
766b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
767b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Canonicalizes the language tag, or throws in case the tag is invalid.
768f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch * ECMA 402 9.2.1 steps 7.c ii ~ v.
769b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
770b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction canonicalizeLanguageTag(localeID) {
771b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // null is typeof 'object' so we have to do extra check.
772bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  if ((!IS_STRING(localeID) && !IS_RECEIVER(localeID)) ||
773b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      IS_NULL(localeID)) {
774f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    throw %make_type_error(kLanguageID);
775b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
776b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
777bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  // Optimize for the most common case; a language code alone in
778bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  // the canonical form/lowercase (e.g. "en", "fil").
779bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  if (IS_STRING(localeID) &&
780c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch      !IS_NULL(%regexp_internal_match(/^[a-z]{2,3}$/, localeID))) {
781bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch    return localeID;
782bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  }
783bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch
78413e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch  var localeString = TO_STRING(localeID);
785b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
786f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  if (isStructuallyValidLanguageTag(localeString) === false) {
787f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    throw %make_range_error(kInvalidLanguageTag, localeString);
788b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
789b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
790f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  // ECMA 402 6.2.3
791b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var tag = %CanonicalizeLanguageTag(localeString);
792f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  // TODO(jshin): This should not happen because the structual validity
793f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  // is already checked. If that's the case, remove this.
794b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  if (tag === 'invalid-tag') {
795f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    throw %make_range_error(kInvalidLanguageTag, localeString);
796b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
797b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
798b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return tag;
799b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
800b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
801b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
802b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
803f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch * Returns an InternalArray where all locales are canonicalized and duplicates
804f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch * removed.
805b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Throws on locales that are not well formed BCP47 tags.
806f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch * ECMA 402 8.2.1 steps 1 (ECMA 402 9.2.1) and 2.
807b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
808f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochfunction canonicalizeLocaleList(locales) {
809bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  var seen = new InternalArray();
810bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  if (!IS_UNDEFINED(locales)) {
811b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    // We allow single string localeID.
812b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if (typeof locales === 'string') {
813014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      %_Call(ArrayPush, seen, canonicalizeLanguageTag(locales));
814f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      return seen;
815b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
816b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
817014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    var o = TO_OBJECT(locales);
818f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    var len = TO_LENGTH(o.length);
819b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
820b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    for (var k = 0; k < len; k++) {
821b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      if (k in o) {
822b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        var value = o[k];
823b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
824b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        var tag = canonicalizeLanguageTag(value);
825b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
826f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        if (%ArrayIndexOf(seen, tag, 0) === -1) {
827014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          %_Call(ArrayPush, seen, tag);
828b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        }
829b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      }
830b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
831b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
832b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
833f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  return seen;
834b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
835b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
836f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochfunction initializeLocaleList(locales) {
837f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  return freezeArray(canonicalizeLocaleList(locales));
838f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch}
839b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
840b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
841f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch * Check the structual Validity of the language tag per ECMA 402 6.2.2:
842f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch *   - Well-formed per RFC 5646 2.1
843f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch *   - There are no duplicate variant subtags
844f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch *   - There are no duplicate singletion (extension) subtags
845f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch *
846f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch * One extra-check is done (from RFC 5646 2.2.9): the tag is compared
847f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch * against the list of grandfathered tags. However, subtags for
848f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch * primary/extended language, script, region, variant are not checked
849f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch * against the IANA language subtag registry.
850b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch *
851b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * ICU is too permissible and lets invalid tags, like
852b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * hant-cmn-cn, through.
853b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch *
854b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns false if the language tag is invalid.
855b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
856f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochfunction isStructuallyValidLanguageTag(locale) {
857b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Check if it's well-formed, including grandfadered tags.
858c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch  if (IS_NULL(%regexp_internal_match(GetLanguageTagRE(), locale))) {
859b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return false;
860b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
861b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
86262ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  locale = %StringToLowerCaseI18N(locale);
86362ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch
864b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Just return if it's a x- form. It's all private.
865c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch  if (%StringIndexOf(locale, 'x-', 0) === 0) {
866b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return true;
867b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
868b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
869b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Check if there are any duplicate variants or singletons (extensions).
870b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
871b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Remove private use section.
872f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  locale = %StringSplit(locale, '-x-', kMaxUint32)[0];
873b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
874b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Skip language since it can match variant regex, so we start from 1.
875b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // We are matching i-klingon here, but that's ok, since i-klingon-klingon
876b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // is not valid and would fail LANGUAGE_TAG_RE test.
877bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  var variants = new InternalArray();
878bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  var extensions = new InternalArray();
879f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  var parts = %StringSplit(locale, '-', kMaxUint32);
880b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  for (var i = 1; i < parts.length; i++) {
881b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    var value = parts[i];
882c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch    if (!IS_NULL(%regexp_internal_match(GetLanguageVariantRE(), value)) &&
883014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        extensions.length === 0) {
884f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      if (%ArrayIndexOf(variants, value, 0) === -1) {
885014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        %_Call(ArrayPush, variants, value);
886b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      } else {
887b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        return false;
888b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      }
889b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
890b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
891c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch    if (!IS_NULL(%regexp_internal_match(GetLanguageSingletonRE(), value))) {
892f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      if (%ArrayIndexOf(extensions, value, 0) === -1) {
893014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        %_Call(ArrayPush, extensions, value);
894b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      } else {
895b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        return false;
896b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      }
897b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
898b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
899b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
900b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return true;
901b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch }
902b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
903b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
904b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
905b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Builds a regular expresion that validates the language tag
906b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * against bcp47 spec.
907b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Uses http://tools.ietf.org/html/bcp47, section 2.1, ABNF.
908b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Runs on load and initializes the global REs.
909b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
910b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction BuildLanguageTagREs() {
911b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var alpha = '[a-zA-Z]';
912b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var digit = '[0-9]';
913b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var alphanum = '(' + alpha + '|' + digit + ')';
914b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var regular = '(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|' +
915b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                'zh-min|zh-min-nan|zh-xiang)';
916b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var irregular = '(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|' +
917b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                  'i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|' +
918b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                  'i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)';
919b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var grandfathered = '(' + irregular + '|' + regular + ')';
920b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var privateUse = '(x(-' + alphanum + '{1,8})+)';
921b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
922b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var singleton = '(' + digit + '|[A-WY-Za-wy-z])';
923014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  LANGUAGE_SINGLETON_RE = new GlobalRegExp('^' + singleton + '$', 'i');
924b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
925b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var extension = '(' + singleton + '(-' + alphanum + '{2,8})+)';
926b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
927b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var variant = '(' + alphanum + '{5,8}|(' + digit + alphanum + '{3}))';
928014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  LANGUAGE_VARIANT_RE = new GlobalRegExp('^' + variant + '$', 'i');
929b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
930b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var region = '(' + alpha + '{2}|' + digit + '{3})';
931b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var script = '(' + alpha + '{4})';
932b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var extLang = '(' + alpha + '{3}(-' + alpha + '{3}){0,2})';
933b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var language = '(' + alpha + '{2,3}(-' + extLang + ')?|' + alpha + '{4}|' +
934b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                 alpha + '{5,8})';
935b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var langTag = language + '(-' + script + ')?(-' + region + ')?(-' +
936b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                variant + ')*(-' + extension + ')*(-' + privateUse + ')?';
937b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
938b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var languageTag =
939b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      '^(' + langTag + '|' + privateUse + '|' + grandfathered + ')$';
940014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  LANGUAGE_TAG_RE = new GlobalRegExp(languageTag, 'i');
941b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
942b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
943014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochvar resolvedAccessor = {
944014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  get() {
945014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    %IncrementUseCounter(kIntlResolved);
946014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    return this[resolvedSymbol];
947014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  },
948014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  set(value) {
949014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    this[resolvedSymbol] = value;
950014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  }
951014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch};
952014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
953f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch// ECMA 402 section 8.2.1
95462ed631aa0ff23db68a47fd423efa9c019ff2c9eBen MurdochInstallFunction(GlobalIntl, 'getCanonicalLocales', function(locales) {
955f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    return makeArray(canonicalizeLocaleList(locales));
956f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  }
957f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch);
958f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
959b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
960b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Initializes the given object so it's a valid Collator instance.
961b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Useful for subclassing.
962b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
96362ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdochfunction CreateCollator(locales, options) {
964014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (IS_UNDEFINED(options)) {
965b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    options = {};
966b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
967b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
968b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var getOption = getGetOption(options, 'collator');
969b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
970b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var internalOptions = {};
971b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
972b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  defineWEProperty(internalOptions, 'usage', getOption(
973b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    'usage', 'string', ['sort', 'search'], 'sort'));
974b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
975b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var sensitivity = getOption('sensitivity', 'string',
976b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                              ['base', 'accent', 'case', 'variant']);
977014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (IS_UNDEFINED(sensitivity) && internalOptions.usage === 'sort') {
978b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    sensitivity = 'variant';
979b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
980b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  defineWEProperty(internalOptions, 'sensitivity', sensitivity);
981b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
982b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  defineWEProperty(internalOptions, 'ignorePunctuation', getOption(
983014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    'ignorePunctuation', 'boolean', UNDEFINED, false));
984b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
985b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var locale = resolveLocale('collator', locales, options);
986b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
987f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  // TODO(jshin): ICU now can take kb, kc, etc. Switch over to using ICU
988f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  // directly. See Collator::InitializeCollator and
989f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  // Collator::CreateICUCollator in src/i18n.cc
990b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // ICU can't take kb, kc... parameters through localeID, so we need to pass
991b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // them as options.
992b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // One exception is -co- which has to be part of the extension, but only for
993b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // usage: sort, and its value can't be 'standard' or 'search'.
994b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var extensionMap = parseExtension(locale.extension);
995014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
996014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  /**
997014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch   * Map of Unicode extensions to option properties, and their values and types,
998014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch   * for a collator.
999014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch   */
1000014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  var COLLATOR_KEY_MAP = {
1001014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    'kn': {'property': 'numeric', 'type': 'boolean'},
1002014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    'kf': {'property': 'caseFirst', 'type': 'string',
1003014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch           'values': ['false', 'lower', 'upper']}
1004014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  };
1005014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
1006b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  setOptions(
1007b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      options, extensionMap, COLLATOR_KEY_MAP, getOption, internalOptions);
1008b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1009b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var collation = 'default';
1010b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var extension = '';
10113b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  if (HAS_OWN_PROPERTY(extensionMap, 'co') && internalOptions.usage === 'sort') {
1012014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
1013014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    /**
1014014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch     * Allowed -u-co- values. List taken from:
1015014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch     * http://unicode.org/repos/cldr/trunk/common/bcp47/collation.xml
1016014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch     */
1017014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    var ALLOWED_CO_VALUES = [
1018014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      'big5han', 'dict', 'direct', 'ducet', 'gb2312', 'phonebk', 'phonetic',
1019014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      'pinyin', 'reformed', 'searchjl', 'stroke', 'trad', 'unihan', 'zhuyin'
1020014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    ];
1021014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
1022f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    if (%ArrayIndexOf(ALLOWED_CO_VALUES, extensionMap.co, 0) !== -1) {
1023b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      extension = '-u-co-' + extensionMap.co;
1024b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      // ICU can't tell us what the collation is, so save user's input.
1025b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      collation = extensionMap.co;
1026b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
1027b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  } else if (internalOptions.usage === 'search') {
1028b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    extension = '-u-co-search';
1029b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1030b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  defineWEProperty(internalOptions, 'collation', collation);
1031b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1032b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var requestedLocale = locale.locale + extension;
1033b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1034b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // We define all properties C++ code may produce, to prevent security
1035b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // problems. If malicious user decides to redefine Object.prototype.locale
1036b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // we can't just use plain x.locale = 'us' or in C++ Set("locale", "us").
1037bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  // %object_define_properties will either succeed defining or throw an error.
1038bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  var resolved = %object_define_properties({}, {
1039b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    caseFirst: {writable: true},
1040b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    collation: {value: internalOptions.collation, writable: true},
1041b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    ignorePunctuation: {writable: true},
1042b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    locale: {writable: true},
1043b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    numeric: {writable: true},
1044b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    requestedLocale: {value: requestedLocale, writable: true},
1045b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    sensitivity: {writable: true},
1046b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    strength: {writable: true},
1047b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    usage: {value: internalOptions.usage, writable: true}
1048b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  });
1049b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
105062ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  var collator = %CreateCollator(requestedLocale, internalOptions, resolved);
1051b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
105262ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  %MarkAsInitializedIntlObjectOfType(collator, 'collator');
1053014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  collator[resolvedSymbol] = resolved;
1054b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1055b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return collator;
1056b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
1057b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1058b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1059b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1060b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Constructs Intl.Collator object given optional locales and options
1061b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * parameters.
1062b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch *
1063b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @constructor
1064b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
106562ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdochfunction CollatorConstructor() {
106662ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  return IntlConstruct(this, GlobalIntlCollator, CreateCollator, new.target,
106762ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch                       arguments);
106862ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch}
106962ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch%SetCode(GlobalIntlCollator, CollatorConstructor);
1070b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1071b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1072b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1073b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Collator resolvedOptions method.
1074b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
107562ed631aa0ff23db68a47fd423efa9c019ff2c9eBen MurdochInstallFunction(GlobalIntlCollator.prototype, 'resolvedOptions', function() {
107662ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch    var coll = Unwrap(this, 'collator', GlobalIntlCollator, 'resolvedOptions',
107762ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch                      false);
1078014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    var locale = getOptimalLanguageTag(coll[resolvedSymbol].requestedLocale,
1079014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                                       coll[resolvedSymbol].locale);
1080b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1081b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return {
1082b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      locale: locale,
1083014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      usage: coll[resolvedSymbol].usage,
1084014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      sensitivity: coll[resolvedSymbol].sensitivity,
1085014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      ignorePunctuation: coll[resolvedSymbol].ignorePunctuation,
1086014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      numeric: coll[resolvedSymbol].numeric,
1087014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      caseFirst: coll[resolvedSymbol].caseFirst,
1088014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      collation: coll[resolvedSymbol].collation
1089b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    };
10903b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  }
1091b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch);
1092b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1093b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1094b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1095b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns the subset of the given locale list for which this locale list
1096b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * has a matching (possibly fallback) locale. Locales appear in the same
1097b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * order in the returned list as in the input list.
1098b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Options are optional parameter.
1099b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
110062ed631aa0ff23db68a47fd423efa9c019ff2c9eBen MurdochInstallFunction(GlobalIntlCollator, 'supportedLocalesOf', function(locales) {
1101109988c7ccb6f3fd1a58574fa3dfb88beaef6632Ben Murdoch    return supportedLocalesOf('collator', locales, arguments[1]);
11023b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  }
1103b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch);
1104b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1105b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1106b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1107b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * When the compare method is called with two arguments x and y, it returns a
1108b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Number other than NaN that represents the result of a locale-sensitive
1109b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * String comparison of x with y.
1110b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * The result is intended to order String values in the sort order specified
1111b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * by the effective locale and collation options computed during construction
1112b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * of this Collator object, and will be negative, zero, or positive, depending
1113b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * on whether x comes before y in the sort order, the Strings are equal under
1114b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * the sort order, or x comes after y in the sort order, respectively.
1115b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
1116b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction compare(collator, x, y) {
111762ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  return %InternalCompare(collator, TO_STRING(x), TO_STRING(y));
1118b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch};
1119b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1120b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
112162ed631aa0ff23db68a47fd423efa9c019ff2c9eBen MurdochAddBoundMethod(GlobalIntlCollator, 'compare', compare, 2, 'collator', false);
1122b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1123b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1124b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Verifies that the input is a well-formed ISO 4217 currency code.
1125b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Don't uppercase to test. It could convert invalid code into a valid one.
1126b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * For example \u00DFP (Eszett+P) becomes SSP.
1127b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
1128b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction isWellFormedCurrencyCode(currency) {
112962ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  return typeof currency === "string" && currency.length === 3 &&
1130c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch      IS_NULL(%regexp_internal_match(/[^A-Za-z]/, currency));
1131b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
1132b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1133b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1134b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1135b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns the valid digit count for a property, or throws RangeError on
1136b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * a value out of the range.
1137b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
1138b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction getNumberOption(options, property, min, max, fallback) {
1139b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var value = options[property];
1140014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (!IS_UNDEFINED(value)) {
114113e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch    value = TO_NUMBER(value);
114213e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch    if (NUMBER_IS_NAN(value) || value < min || value > max) {
1143f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      throw %make_range_error(kPropertyValueOutOfRange, property);
1144b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
11453b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    return %math_floor(value);
1146b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1147b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1148b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return fallback;
1149b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
1150b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1151014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochvar patternAccessor = {
1152014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  get() {
1153014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    %IncrementUseCounter(kIntlPattern);
1154014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    return this[patternSymbol];
1155014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  },
1156014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  set(value) {
1157014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    this[patternSymbol] = value;
1158014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  }
1159014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch};
1160b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1161b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1162b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Initializes the given object so it's a valid NumberFormat instance.
1163b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Useful for subclassing.
1164b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
116562ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdochfunction CreateNumberFormat(locales, options) {
1166014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (IS_UNDEFINED(options)) {
1167b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    options = {};
1168b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1169b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1170b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var getOption = getGetOption(options, 'numberformat');
1171b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1172b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var locale = resolveLocale('numberformat', locales, options);
1173b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1174b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var internalOptions = {};
1175b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  defineWEProperty(internalOptions, 'style', getOption(
1176b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    'style', 'string', ['decimal', 'percent', 'currency'], 'decimal'));
1177b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1178b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var currency = getOption('currency', 'string');
1179014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (!IS_UNDEFINED(currency) && !isWellFormedCurrencyCode(currency)) {
1180f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    throw %make_range_error(kInvalidCurrencyCode, currency);
1181b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1182b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1183014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (internalOptions.style === 'currency' && IS_UNDEFINED(currency)) {
1184f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    throw %make_type_error(kCurrencyCode);
1185b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1186b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1187b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var currencyDisplay = getOption(
1188b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      'currencyDisplay', 'string', ['code', 'symbol', 'name'], 'symbol');
1189b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  if (internalOptions.style === 'currency') {
119062ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch    defineWEProperty(internalOptions, 'currency', %StringToUpperCaseI18N(currency));
1191b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    defineWEProperty(internalOptions, 'currencyDisplay', currencyDisplay);
1192b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1193b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1194b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Digit ranges.
1195b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var mnid = getNumberOption(options, 'minimumIntegerDigits', 1, 21, 1);
1196b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  defineWEProperty(internalOptions, 'minimumIntegerDigits', mnid);
1197b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1198014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  var mnfd = options['minimumFractionDigits'];
1199014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  var mxfd = options['maximumFractionDigits'];
1200014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (!IS_UNDEFINED(mnfd) || internalOptions.style !== 'currency') {
1201014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    mnfd = getNumberOption(options, 'minimumFractionDigits', 0, 20, 0);
1202014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    defineWEProperty(internalOptions, 'minimumFractionDigits', mnfd);
1203014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  }
1204b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1205014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (!IS_UNDEFINED(mxfd) || internalOptions.style !== 'currency') {
1206014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    var min_mxfd = internalOptions.style === 'percent' ? 0 : 3;
1207014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    mnfd = IS_UNDEFINED(mnfd) ? 0 : mnfd;
1208014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    var fallback_limit = (mnfd > min_mxfd) ? mnfd : min_mxfd;
1209014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    mxfd = getNumberOption(options, 'maximumFractionDigits', mnfd, 20, fallback_limit);
1210014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    defineWEProperty(internalOptions, 'maximumFractionDigits', mxfd);
1211014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  }
1212b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1213b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var mnsd = options['minimumSignificantDigits'];
1214b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var mxsd = options['maximumSignificantDigits'];
1215014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (!IS_UNDEFINED(mnsd) || !IS_UNDEFINED(mxsd)) {
121662ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch    mnsd = getNumberOption(options, 'minimumSignificantDigits', 1, 21, 1);
1217b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    defineWEProperty(internalOptions, 'minimumSignificantDigits', mnsd);
1218b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1219b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    mxsd = getNumberOption(options, 'maximumSignificantDigits', mnsd, 21, 21);
1220b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    defineWEProperty(internalOptions, 'maximumSignificantDigits', mxsd);
1221b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1222b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1223b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Grouping.
1224b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  defineWEProperty(internalOptions, 'useGrouping', getOption(
1225014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    'useGrouping', 'boolean', UNDEFINED, true));
1226b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1227b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // ICU prefers options to be passed using -u- extension key/values for
1228b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // number format, so we need to build that.
1229b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var extensionMap = parseExtension(locale.extension);
1230014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
1231014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  /**
1232014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch   * Map of Unicode extensions to option properties, and their values and types,
1233014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch   * for a number format.
1234014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch   */
1235014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  var NUMBER_FORMAT_KEY_MAP = {
1236014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    'nu': {'property': UNDEFINED, 'type': 'string'}
1237014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  };
1238014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
1239b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var extension = setOptions(options, extensionMap, NUMBER_FORMAT_KEY_MAP,
1240b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                             getOption, internalOptions);
1241b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1242b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var requestedLocale = locale.locale + extension;
1243bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  var resolved = %object_define_properties({}, {
1244b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    currency: {writable: true},
1245b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    currencyDisplay: {writable: true},
1246b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    locale: {writable: true},
1247b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    maximumFractionDigits: {writable: true},
1248b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    minimumFractionDigits: {writable: true},
1249b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    minimumIntegerDigits: {writable: true},
1250b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    numberingSystem: {writable: true},
1251b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    requestedLocale: {value: requestedLocale, writable: true},
1252b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    style: {value: internalOptions.style, writable: true},
1253b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    useGrouping: {writable: true}
1254b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  });
12553b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  if (HAS_OWN_PROPERTY(internalOptions, 'minimumSignificantDigits')) {
1256014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    defineWEProperty(resolved, 'minimumSignificantDigits', UNDEFINED);
1257b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
12583b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  if (HAS_OWN_PROPERTY(internalOptions, 'maximumSignificantDigits')) {
1259014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    defineWEProperty(resolved, 'maximumSignificantDigits', UNDEFINED);
1260b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
126162ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  var numberFormat = %CreateNumberFormat(requestedLocale, internalOptions,
126262ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch                                         resolved);
1263b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1264b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  if (internalOptions.style === 'currency') {
1265bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch    %object_define_property(resolved, 'currencyDisplay',
1266bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch        {value: currencyDisplay, writable: true});
1267b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1268b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
126962ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  %MarkAsInitializedIntlObjectOfType(numberFormat, 'numberformat');
1270014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  numberFormat[resolvedSymbol] = resolved;
1271b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1272b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return numberFormat;
1273b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
1274b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1275b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1276b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1277b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Constructs Intl.NumberFormat object given optional locales and options
1278b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * parameters.
1279b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch *
1280b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @constructor
1281b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
128262ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdochfunction NumberFormatConstructor() {
128362ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  return IntlConstruct(this, GlobalIntlNumberFormat, CreateNumberFormat,
128462ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch                       new.target, arguments, true);
128562ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch}
128662ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch%SetCode(GlobalIntlNumberFormat, NumberFormatConstructor);
1287b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1288b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1289b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1290b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * NumberFormat resolvedOptions method.
1291b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
129262ed631aa0ff23db68a47fd423efa9c019ff2c9eBen MurdochInstallFunction(GlobalIntlNumberFormat.prototype, 'resolvedOptions',
129362ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  function() {
129462ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch    var format = Unwrap(this, 'numberformat', GlobalIntlNumberFormat,
129562ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch                        'resolvedOptions', true);
1296014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    var locale = getOptimalLanguageTag(format[resolvedSymbol].requestedLocale,
1297014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                                       format[resolvedSymbol].locale);
1298b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1299b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    var result = {
1300b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      locale: locale,
1301014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      numberingSystem: format[resolvedSymbol].numberingSystem,
1302014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      style: format[resolvedSymbol].style,
1303014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      useGrouping: format[resolvedSymbol].useGrouping,
1304014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      minimumIntegerDigits: format[resolvedSymbol].minimumIntegerDigits,
1305014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      minimumFractionDigits: format[resolvedSymbol].minimumFractionDigits,
1306014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      maximumFractionDigits: format[resolvedSymbol].maximumFractionDigits,
1307b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    };
1308b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1309b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if (result.style === 'currency') {
1310014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      defineWECProperty(result, 'currency', format[resolvedSymbol].currency);
1311b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      defineWECProperty(result, 'currencyDisplay',
1312014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                        format[resolvedSymbol].currencyDisplay);
1313b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
1314b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
13153b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    if (HAS_OWN_PROPERTY(format[resolvedSymbol], 'minimumSignificantDigits')) {
1316b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      defineWECProperty(result, 'minimumSignificantDigits',
1317014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                        format[resolvedSymbol].minimumSignificantDigits);
1318b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
1319b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
13203b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    if (HAS_OWN_PROPERTY(format[resolvedSymbol], 'maximumSignificantDigits')) {
1321b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      defineWECProperty(result, 'maximumSignificantDigits',
1322014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                        format[resolvedSymbol].maximumSignificantDigits);
1323b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
1324b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1325b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return result;
13263b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  }
1327b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch);
1328b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1329b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1330b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1331b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns the subset of the given locale list for which this locale list
1332b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * has a matching (possibly fallback) locale. Locales appear in the same
1333b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * order in the returned list as in the input list.
1334b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Options are optional parameter.
1335b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
133662ed631aa0ff23db68a47fd423efa9c019ff2c9eBen MurdochInstallFunction(GlobalIntlNumberFormat, 'supportedLocalesOf',
133762ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  function(locales) {
1338109988c7ccb6f3fd1a58574fa3dfb88beaef6632Ben Murdoch    return supportedLocalesOf('numberformat', locales, arguments[1]);
13393b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  }
1340b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch);
1341b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1342b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1343b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1344b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns a String value representing the result of calling ToNumber(value)
1345b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * according to the effective locale and the formatting options of this
1346b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * NumberFormat.
1347b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
1348b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction formatNumber(formatter, value) {
1349b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Spec treats -0 and +0 as 0.
1350014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  var number = TO_NUMBER(value) + 0;
1351b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
135262ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  return %InternalNumberFormat(formatter, number);
1353b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
1354b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1355b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
135662ed631aa0ff23db68a47fd423efa9c019ff2c9eBen MurdochAddBoundMethod(GlobalIntlNumberFormat, 'format', formatNumber, 1,
135762ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch               'numberformat', true);
1358b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1359b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1360b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns a string that matches LDML representation of the options object.
1361b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
1362b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction toLDMLString(options) {
1363b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var getOption = getGetOption(options, 'dateformat');
1364b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1365b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var ldmlString = '';
1366b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1367b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var option = getOption('weekday', 'string', ['narrow', 'short', 'long']);
1368b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  ldmlString += appendToLDMLString(
1369b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      option, {narrow: 'EEEEE', short: 'EEE', long: 'EEEE'});
1370b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1371b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  option = getOption('era', 'string', ['narrow', 'short', 'long']);
1372b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  ldmlString += appendToLDMLString(
1373b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      option, {narrow: 'GGGGG', short: 'GGG', long: 'GGGG'});
1374b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1375b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  option = getOption('year', 'string', ['2-digit', 'numeric']);
1376b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  ldmlString += appendToLDMLString(option, {'2-digit': 'yy', 'numeric': 'y'});
1377b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1378b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  option = getOption('month', 'string',
1379b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                     ['2-digit', 'numeric', 'narrow', 'short', 'long']);
1380b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  ldmlString += appendToLDMLString(option, {'2-digit': 'MM', 'numeric': 'M',
1381b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch          'narrow': 'MMMMM', 'short': 'MMM', 'long': 'MMMM'});
1382b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1383b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  option = getOption('day', 'string', ['2-digit', 'numeric']);
1384b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  ldmlString += appendToLDMLString(
1385b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      option, {'2-digit': 'dd', 'numeric': 'd'});
1386b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1387b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var hr12 = getOption('hour12', 'boolean');
1388b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  option = getOption('hour', 'string', ['2-digit', 'numeric']);
1389014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (IS_UNDEFINED(hr12)) {
1390b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    ldmlString += appendToLDMLString(option, {'2-digit': 'jj', 'numeric': 'j'});
1391b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  } else if (hr12 === true) {
1392b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    ldmlString += appendToLDMLString(option, {'2-digit': 'hh', 'numeric': 'h'});
1393b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  } else {
1394b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    ldmlString += appendToLDMLString(option, {'2-digit': 'HH', 'numeric': 'H'});
1395b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1396b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1397b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  option = getOption('minute', 'string', ['2-digit', 'numeric']);
1398b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  ldmlString += appendToLDMLString(option, {'2-digit': 'mm', 'numeric': 'm'});
1399b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1400b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  option = getOption('second', 'string', ['2-digit', 'numeric']);
1401b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  ldmlString += appendToLDMLString(option, {'2-digit': 'ss', 'numeric': 's'});
1402b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1403b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  option = getOption('timeZoneName', 'string', ['short', 'long']);
1404b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  ldmlString += appendToLDMLString(option, {short: 'z', long: 'zzzz'});
1405b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1406b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return ldmlString;
1407b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
1408b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1409b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1410b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1411b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns either LDML equivalent of the current option or empty string.
1412b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
1413b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction appendToLDMLString(option, pairs) {
1414014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (!IS_UNDEFINED(option)) {
1415b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return pairs[option];
1416b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  } else {
1417b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return '';
1418b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1419b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
1420b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1421b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1422b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1423b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns object that matches LDML representation of the date.
1424b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
1425b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction fromLDMLString(ldmlString) {
1426b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // First remove '' quoted text, so we lose 'Uhr' strings.
1427c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch  ldmlString = %RegExpInternalReplace(GetQuotedStringRE(), ldmlString, '');
1428b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1429b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var options = {};
1430c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch  var match = %regexp_internal_match(/E{3,5}/, ldmlString);
1431b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  options = appendToDateTimeObject(
1432b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      options, 'weekday', match, {EEEEE: 'narrow', EEE: 'short', EEEE: 'long'});
1433b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1434c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch  match = %regexp_internal_match(/G{3,5}/, ldmlString);
1435b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  options = appendToDateTimeObject(
1436b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      options, 'era', match, {GGGGG: 'narrow', GGG: 'short', GGGG: 'long'});
1437b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1438c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch  match = %regexp_internal_match(/y{1,2}/, ldmlString);
1439b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  options = appendToDateTimeObject(
1440b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      options, 'year', match, {y: 'numeric', yy: '2-digit'});
1441b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1442c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch  match = %regexp_internal_match(/M{1,5}/, ldmlString);
1443b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  options = appendToDateTimeObject(options, 'month', match, {MM: '2-digit',
1444b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      M: 'numeric', MMMMM: 'narrow', MMM: 'short', MMMM: 'long'});
1445b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1446b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Sometimes we get L instead of M for month - standalone name.
1447c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch  match = %regexp_internal_match(/L{1,5}/, ldmlString);
1448b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  options = appendToDateTimeObject(options, 'month', match, {LL: '2-digit',
1449b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      L: 'numeric', LLLLL: 'narrow', LLL: 'short', LLLL: 'long'});
1450b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1451c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch  match = %regexp_internal_match(/d{1,2}/, ldmlString);
1452b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  options = appendToDateTimeObject(
1453b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      options, 'day', match, {d: 'numeric', dd: '2-digit'});
1454b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1455c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch  match = %regexp_internal_match(/h{1,2}/, ldmlString);
1456b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  if (match !== null) {
1457b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    options['hour12'] = true;
1458b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1459b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  options = appendToDateTimeObject(
1460b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      options, 'hour', match, {h: 'numeric', hh: '2-digit'});
1461b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1462c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch  match = %regexp_internal_match(/H{1,2}/, ldmlString);
1463b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  if (match !== null) {
1464b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    options['hour12'] = false;
1465b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1466b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  options = appendToDateTimeObject(
1467b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      options, 'hour', match, {H: 'numeric', HH: '2-digit'});
1468b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1469c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch  match = %regexp_internal_match(/m{1,2}/, ldmlString);
1470b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  options = appendToDateTimeObject(
1471b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      options, 'minute', match, {m: 'numeric', mm: '2-digit'});
1472b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1473c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch  match = %regexp_internal_match(/s{1,2}/, ldmlString);
1474b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  options = appendToDateTimeObject(
1475b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      options, 'second', match, {s: 'numeric', ss: '2-digit'});
1476b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1477c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch  match = %regexp_internal_match(/z|zzzz/, ldmlString);
1478b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  options = appendToDateTimeObject(
1479b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      options, 'timeZoneName', match, {z: 'short', zzzz: 'long'});
1480b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1481b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return options;
1482b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
1483b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1484b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1485b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction appendToDateTimeObject(options, option, match, pairs) {
1486b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  if (IS_NULL(match)) {
14873b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch    if (!HAS_OWN_PROPERTY(options, option)) {
1488014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      defineWEProperty(options, option, UNDEFINED);
1489b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
1490b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return options;
1491b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1492b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1493b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var property = match[0];
1494b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  defineWEProperty(options, option, pairs[property]);
1495b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1496b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return options;
1497b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
1498b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1499b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1500b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1501b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns options with at least default values in it.
1502b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
1503b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction toDateTimeOptions(options, required, defaults) {
1504014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (IS_UNDEFINED(options)) {
1505b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    options = {};
1506b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  } else {
1507014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    options = TO_OBJECT(options);
1508b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1509b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
151062ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  options = %object_create(options);
151162ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch
1512b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var needsDefault = true;
1513b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  if ((required === 'date' || required === 'any') &&
1514014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      (!IS_UNDEFINED(options.weekday) || !IS_UNDEFINED(options.year) ||
1515014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch       !IS_UNDEFINED(options.month) || !IS_UNDEFINED(options.day))) {
1516b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    needsDefault = false;
1517b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1518b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1519b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  if ((required === 'time' || required === 'any') &&
1520014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      (!IS_UNDEFINED(options.hour) || !IS_UNDEFINED(options.minute) ||
1521014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch       !IS_UNDEFINED(options.second))) {
1522b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    needsDefault = false;
1523b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1524b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1525b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  if (needsDefault && (defaults === 'date' || defaults === 'all')) {
1526bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch    %object_define_property(options, 'year', {value: 'numeric',
1527bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch                                              writable: true,
1528bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch                                              enumerable: true,
1529bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch                                              configurable: true});
1530bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch    %object_define_property(options, 'month', {value: 'numeric',
1531bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch                                               writable: true,
1532bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch                                               enumerable: true,
1533bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch                                               configurable: true});
1534bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch    %object_define_property(options, 'day', {value: 'numeric',
1535014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                                             writable: true,
1536014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                                             enumerable: true,
1537014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                                             configurable: true});
1538b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1539b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1540bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  if (needsDefault && (defaults === 'time' || defaults === 'all')) {
1541bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch    %object_define_property(options, 'hour', {value: 'numeric',
1542bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch                                              writable: true,
1543bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch                                              enumerable: true,
1544bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch                                              configurable: true});
1545bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch    %object_define_property(options, 'minute', {value: 'numeric',
1546bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch                                                writable: true,
1547bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch                                                enumerable: true,
1548bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch                                                configurable: true});
1549bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch    %object_define_property(options, 'second', {value: 'numeric',
1550bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch                                                writable: true,
1551bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch                                                enumerable: true,
1552bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch                                                configurable: true});
1553bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  }
1554bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch
1555b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return options;
1556b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
1557b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1558b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1559b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1560b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Initializes the given object so it's a valid DateTimeFormat instance.
1561b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Useful for subclassing.
1562b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
156362ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdochfunction CreateDateTimeFormat(locales, options) {
1564014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (IS_UNDEFINED(options)) {
1565b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    options = {};
1566b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1567b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1568b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var locale = resolveLocale('dateformat', locales, options);
1569b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1570b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  options = toDateTimeOptions(options, 'any', 'date');
1571b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1572b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var getOption = getGetOption(options, 'dateformat');
1573b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1574b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // We implement only best fit algorithm, but still need to check
1575b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // if the formatMatcher values are in range.
1576b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var matcher = getOption('formatMatcher', 'string',
1577b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                          ['basic', 'best fit'], 'best fit');
1578b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1579b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Build LDML string for the skeleton that we pass to the formatter.
1580b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var ldmlString = toLDMLString(options);
1581b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1582b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Filter out supported extension keys so we know what to put in resolved
1583b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // section later on.
1584b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // We need to pass calendar and number system to the method.
1585b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var tz = canonicalizeTimeZoneID(options.timeZone);
1586b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1587b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // ICU prefers options to be passed using -u- extension key/values, so
1588b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // we need to build that.
1589b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var internalOptions = {};
1590b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var extensionMap = parseExtension(locale.extension);
1591014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
1592014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  /**
1593014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch   * Map of Unicode extensions to option properties, and their values and types,
1594014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch   * for a date/time format.
1595014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch   */
1596014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  var DATETIME_FORMAT_KEY_MAP = {
1597014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    'ca': {'property': UNDEFINED, 'type': 'string'},
1598014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    'nu': {'property': UNDEFINED, 'type': 'string'}
1599014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  };
1600014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
1601b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var extension = setOptions(options, extensionMap, DATETIME_FORMAT_KEY_MAP,
1602b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch                             getOption, internalOptions);
1603b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1604b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var requestedLocale = locale.locale + extension;
1605bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  var resolved = %object_define_properties({}, {
1606b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    calendar: {writable: true},
1607b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    day: {writable: true},
1608b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    era: {writable: true},
1609b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    hour12: {writable: true},
1610b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    hour: {writable: true},
1611b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    locale: {writable: true},
1612b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    minute: {writable: true},
1613b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    month: {writable: true},
1614b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    numberingSystem: {writable: true},
1615014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    [patternSymbol]: {writable: true},
1616b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    requestedLocale: {value: requestedLocale, writable: true},
1617b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    second: {writable: true},
1618b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    timeZone: {writable: true},
1619b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    timeZoneName: {writable: true},
1620b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    tz: {value: tz, writable: true},
1621b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    weekday: {writable: true},
1622b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    year: {writable: true}
1623b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  });
1624b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
162562ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  var dateFormat = %CreateDateTimeFormat(
1626b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    requestedLocale, {skeleton: ldmlString, timeZone: tz}, resolved);
1627b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1628014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (resolved.timeZone === "Etc/Unknown") {
1629f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    throw %make_range_error(kUnsupportedTimeZone, tz);
1630b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1631b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
163262ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  %MarkAsInitializedIntlObjectOfType(dateFormat, 'dateformat');
1633014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  dateFormat[resolvedSymbol] = resolved;
1634b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1635b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return dateFormat;
1636b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
1637b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1638b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1639b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1640b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Constructs Intl.DateTimeFormat object given optional locales and options
1641b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * parameters.
1642b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch *
1643b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @constructor
1644b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
164562ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdochfunction DateTimeFormatConstructor() {
164662ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  return IntlConstruct(this, GlobalIntlDateTimeFormat, CreateDateTimeFormat,
164762ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch                       new.target, arguments, true);
164862ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch}
164962ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch%SetCode(GlobalIntlDateTimeFormat, DateTimeFormatConstructor);
1650b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1651b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1652b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1653b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * DateTimeFormat resolvedOptions method.
1654b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
165562ed631aa0ff23db68a47fd423efa9c019ff2c9eBen MurdochInstallFunction(GlobalIntlDateTimeFormat.prototype, 'resolvedOptions',
165662ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  function() {
165762ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch    var format = Unwrap(this, 'dateformat', GlobalIntlDateTimeFormat,
165862ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch                        'resolvedOptions', true);
1659b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1660014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    /**
1661f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch     * Maps ICU calendar names to LDML/BCP47 types for key 'ca'.
1662f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch     * See typeMap section in third_party/icu/source/data/misc/keyTypeData.txt
1663f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch     * and
1664f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch     * http://www.unicode.org/repos/cldr/tags/latest/common/bcp47/calendar.xml
1665014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch     */
1666014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    var ICU_CALENDAR_MAP = {
1667014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      'gregorian': 'gregory',
1668014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      'ethiopic-amete-alem': 'ethioaa'
1669014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    };
1670014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
1671014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    var fromPattern = fromLDMLString(format[resolvedSymbol][patternSymbol]);
1672014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    var userCalendar = ICU_CALENDAR_MAP[format[resolvedSymbol].calendar];
1673014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    if (IS_UNDEFINED(userCalendar)) {
1674f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      // No match means that ICU's legacy name is identical to LDML/BCP type.
1675014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      userCalendar = format[resolvedSymbol].calendar;
1676b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
1677b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1678014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    var locale = getOptimalLanguageTag(format[resolvedSymbol].requestedLocale,
1679014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                                       format[resolvedSymbol].locale);
1680b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1681b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    var result = {
1682b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      locale: locale,
1683014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      numberingSystem: format[resolvedSymbol].numberingSystem,
1684b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      calendar: userCalendar,
1685014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      timeZone: format[resolvedSymbol].timeZone
1686b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    };
1687b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1688b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    addWECPropertyIfDefined(result, 'timeZoneName', fromPattern.timeZoneName);
1689b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    addWECPropertyIfDefined(result, 'era', fromPattern.era);
1690b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    addWECPropertyIfDefined(result, 'year', fromPattern.year);
1691b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    addWECPropertyIfDefined(result, 'month', fromPattern.month);
1692b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    addWECPropertyIfDefined(result, 'day', fromPattern.day);
1693b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    addWECPropertyIfDefined(result, 'weekday', fromPattern.weekday);
1694b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    addWECPropertyIfDefined(result, 'hour12', fromPattern.hour12);
1695b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    addWECPropertyIfDefined(result, 'hour', fromPattern.hour);
1696b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    addWECPropertyIfDefined(result, 'minute', fromPattern.minute);
1697b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    addWECPropertyIfDefined(result, 'second', fromPattern.second);
1698b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1699b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return result;
17003b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  }
1701b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch);
1702b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1703b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1704b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1705b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns the subset of the given locale list for which this locale list
1706b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * has a matching (possibly fallback) locale. Locales appear in the same
1707b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * order in the returned list as in the input list.
1708b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Options are optional parameter.
1709b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
171062ed631aa0ff23db68a47fd423efa9c019ff2c9eBen MurdochInstallFunction(GlobalIntlDateTimeFormat, 'supportedLocalesOf',
171162ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  function(locales) {
1712109988c7ccb6f3fd1a58574fa3dfb88beaef6632Ben Murdoch    return supportedLocalesOf('dateformat', locales, arguments[1]);
17133b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  }
1714b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch);
1715b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1716b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1717b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1718b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns a String value representing the result of calling ToNumber(date)
1719b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * according to the effective locale and the formatting options of this
1720b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * DateTimeFormat.
1721b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
1722b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction formatDate(formatter, dateValue) {
1723b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var dateMs;
1724014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (IS_UNDEFINED(dateValue)) {
1725014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    dateMs = %DateCurrentTime();
1726b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  } else {
1727014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    dateMs = TO_NUMBER(dateValue);
1728b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1729b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1730f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  if (!NUMBER_IS_FINITE(dateMs)) throw %make_range_error(kDateRange);
1731b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
173262ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  return %InternalDateFormat(formatter, new GlobalDate(dateMs));
1733b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
1734b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1735f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdochfunction FormatDateToParts(dateValue) {
1736f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdoch  CHECK_OBJECT_COERCIBLE(this, "Intl.DateTimeFormat.prototype.formatToParts");
1737f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdoch  if (!IS_OBJECT(this)) {
1738f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdoch    throw %make_type_error(kCalledOnNonObject, this);
1739f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdoch  }
174062ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  if (!%IsInitializedIntlObjectOfType(this, 'dateformat')) {
174162ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch    throw %make_type_error(kIncompatibleMethodReceiver,
174262ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch                          'Intl.DateTimeFormat.prototype.formatToParts',
174362ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch                          this);
174462ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  }
1745f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdoch  var dateMs;
1746f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdoch  if (IS_UNDEFINED(dateValue)) {
1747f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdoch    dateMs = %DateCurrentTime();
1748f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdoch  } else {
1749f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdoch    dateMs = TO_NUMBER(dateValue);
1750f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdoch  }
1751f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdoch
1752f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdoch  if (!NUMBER_IS_FINITE(dateMs)) throw %make_range_error(kDateRange);
1753b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
175462ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  return %InternalDateFormatToParts(this, new GlobalDate(dateMs));
1755b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
1756b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1757f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdoch%FunctionSetLength(FormatDateToParts, 0);
1758f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdoch
1759b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1760b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch// 0 because date is optional argument.
176162ed631aa0ff23db68a47fd423efa9c019ff2c9eBen MurdochAddBoundMethod(GlobalIntlDateTimeFormat, 'format', formatDate, 0, 'dateformat',
176262ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch               true);
1763b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1764b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1765b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1766014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * Returns canonical Area/Location(/Location) name, or throws an exception
1767014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * if the zone name is invalid IANA name.
1768b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
1769b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction canonicalizeTimeZoneID(tzID) {
1770b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Skip undefined zones.
1771014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (IS_UNDEFINED(tzID)) {
1772b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return tzID;
1773b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1774b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1775f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  // Convert zone name to string.
1776f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  tzID = TO_STRING(tzID);
1777f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
1778b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  // Special case handling (UTC, GMT).
177962ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  var upperID = %StringToUpperCaseI18N(tzID);
1780b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  if (upperID === 'UTC' || upperID === 'GMT' ||
1781b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      upperID === 'ETC/UTC' || upperID === 'ETC/GMT') {
1782b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return 'UTC';
1783b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1784b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1785014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  // TODO(jshin): Add support for Etc/GMT[+-]([1-9]|1[0-2])
1786014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
1787014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  // We expect only _, '-' and / beside ASCII letters.
1788014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  // All inputs should conform to Area/Location(/Location)* from now on.
1789c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch  var match = %regexp_internal_match(GetTimezoneNameCheckRE(), tzID);
1790f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  if (IS_NULL(match)) throw %make_range_error(kExpectedTimezoneID, tzID);
1791b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1792014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  var result = toTitleCaseTimezoneLocation(match[1]) + '/' +
1793014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch               toTitleCaseTimezoneLocation(match[2]);
1794014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
1795014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (!IS_UNDEFINED(match[3]) && 3 < match.length) {
1796f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    var locations = %StringSplit(match[3], '/', kMaxUint32);
1797014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    // The 1st element is empty. Starts with i=1.
1798014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    for (var i = 1; i < locations.length; i++) {
1799014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      result = result + '/' + toTitleCaseTimezoneLocation(locations[i]);
1800014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    }
1801b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1802b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1803b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return result;
1804b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
1805b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1806b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1807b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Initializes the given object so it's a valid BreakIterator instance.
1808b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Useful for subclassing.
1809b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
181062ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdochfunction CreateBreakIterator(locales, options) {
1811014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (IS_UNDEFINED(options)) {
1812b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    options = {};
1813b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1814b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1815b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var getOption = getGetOption(options, 'breakiterator');
1816b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1817b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var internalOptions = {};
1818b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1819b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  defineWEProperty(internalOptions, 'type', getOption(
1820b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    'type', 'string', ['character', 'word', 'sentence', 'line'], 'word'));
1821b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1822b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var locale = resolveLocale('breakiterator', locales, options);
1823bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  var resolved = %object_define_properties({}, {
1824b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    requestedLocale: {value: locale.locale, writable: true},
1825b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    type: {value: internalOptions.type, writable: true},
1826b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    locale: {writable: true}
1827b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  });
1828b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
182962ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  var iterator = %CreateBreakIterator(locale.locale, internalOptions, resolved);
1830b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
183162ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  %MarkAsInitializedIntlObjectOfType(iterator, 'breakiterator');
1832014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  iterator[resolvedSymbol] = resolved;
1833b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1834b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return iterator;
1835b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
1836b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1837b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1838b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1839b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Constructs Intl.v8BreakIterator object given optional locales and options
1840b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * parameters.
1841b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch *
1842b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @constructor
1843b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
184462ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdochfunction v8BreakIteratorConstructor() {
184562ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  return IntlConstruct(this, GlobalIntlv8BreakIterator, CreateBreakIterator,
184662ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch                       new.target, arguments);
184762ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch}
184862ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch%SetCode(GlobalIntlv8BreakIterator, v8BreakIteratorConstructor);
1849b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1850b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1851b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1852b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * BreakIterator resolvedOptions method.
1853b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
185462ed631aa0ff23db68a47fd423efa9c019ff2c9eBen MurdochInstallFunction(GlobalIntlv8BreakIterator.prototype, 'resolvedOptions',
1855b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  function() {
1856014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    if (!IS_UNDEFINED(new.target)) {
1857f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      throw %make_type_error(kOrdinaryFunctionCalledAsConstructor);
1858b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
1859b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
186062ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch    var segmenter = Unwrap(this, 'breakiterator', GlobalIntlv8BreakIterator,
186162ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch                           'resolvedOptions', false);
1862b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1863014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    var locale =
1864014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch        getOptimalLanguageTag(segmenter[resolvedSymbol].requestedLocale,
1865014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch                              segmenter[resolvedSymbol].locale);
1866b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1867b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return {
1868b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      locale: locale,
1869014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch      type: segmenter[resolvedSymbol].type
1870b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    };
18713b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  }
1872b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch);
1873b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1874b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1875b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1876b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns the subset of the given locale list for which this locale list
1877b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * has a matching (possibly fallback) locale. Locales appear in the same
1878b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * order in the returned list as in the input list.
1879b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Options are optional parameter.
1880b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
188162ed631aa0ff23db68a47fd423efa9c019ff2c9eBen MurdochInstallFunction(GlobalIntlv8BreakIterator, 'supportedLocalesOf',
1882b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  function(locales) {
1883014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    if (!IS_UNDEFINED(new.target)) {
1884f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      throw %make_type_error(kOrdinaryFunctionCalledAsConstructor);
1885b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
1886b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1887109988c7ccb6f3fd1a58574fa3dfb88beaef6632Ben Murdoch    return supportedLocalesOf('breakiterator', locales, arguments[1]);
18883b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch  }
1889b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch);
1890b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1891b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1892b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1893b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Adopts text to segment using the iterator. Old text, if present,
1894b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * gets discarded.
1895b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
1896b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction adoptText(iterator, text) {
189762ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  %BreakIteratorAdoptText(iterator, TO_STRING(text));
1898b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
1899b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1900b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1901b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1902b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns index of the first break in the string and moves current pointer.
1903b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
1904b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction first(iterator) {
190562ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  return %BreakIteratorFirst(iterator);
1906b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
1907b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1908b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1909b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1910b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns the index of the next break and moves the pointer.
1911b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
1912b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction next(iterator) {
191362ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  return %BreakIteratorNext(iterator);
1914b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
1915b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1916b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1917b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1918b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns index of the current break.
1919b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
1920b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction current(iterator) {
192162ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  return %BreakIteratorCurrent(iterator);
1922b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
1923b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1924b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1925b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1926b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns type of the current break.
1927b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
1928b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction breakType(iterator) {
192962ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  return %BreakIteratorBreakType(iterator);
1930b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
1931b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1932b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
193362ed631aa0ff23db68a47fd423efa9c019ff2c9eBen MurdochAddBoundMethod(GlobalIntlv8BreakIterator, 'adoptText', adoptText, 1,
193462ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch               'breakiterator');
193562ed631aa0ff23db68a47fd423efa9c019ff2c9eBen MurdochAddBoundMethod(GlobalIntlv8BreakIterator, 'first', first, 0, 'breakiterator');
193662ed631aa0ff23db68a47fd423efa9c019ff2c9eBen MurdochAddBoundMethod(GlobalIntlv8BreakIterator, 'next', next, 0, 'breakiterator');
193762ed631aa0ff23db68a47fd423efa9c019ff2c9eBen MurdochAddBoundMethod(GlobalIntlv8BreakIterator, 'current', current, 0,
1938bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch               'breakiterator');
193962ed631aa0ff23db68a47fd423efa9c019ff2c9eBen MurdochAddBoundMethod(GlobalIntlv8BreakIterator, 'breakType', breakType, 0,
1940bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch               'breakiterator');
1941b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1942b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch// Save references to Intl objects and methods we use, for added security.
1943b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochvar savedObjects = {
194462ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  'collator': GlobalIntlCollator,
194562ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  'numberformat': GlobalIntlNumberFormat,
194662ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  'dateformatall': GlobalIntlDateTimeFormat,
194762ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  'dateformatdate': GlobalIntlDateTimeFormat,
194862ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  'dateformattime': GlobalIntlDateTimeFormat
1949b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch};
1950b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1951b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1952b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch// Default (created with undefined locales and options parameters) collator,
1953b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch// number and date format instances. They'll be created as needed.
1954b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochvar defaultObjects = {
1955014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  'collator': UNDEFINED,
1956014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  'numberformat': UNDEFINED,
1957014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  'dateformatall': UNDEFINED,
1958014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  'dateformatdate': UNDEFINED,
1959014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  'dateformattime': UNDEFINED,
1960b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch};
1961b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
196213e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdochfunction clearDefaultObjects() {
196313e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch  defaultObjects['dateformatall'] = UNDEFINED;
196413e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch  defaultObjects['dateformatdate'] = UNDEFINED;
196513e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch  defaultObjects['dateformattime'] = UNDEFINED;
196613e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch}
196713e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch
196813e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdochvar date_cache_version = 0;
196913e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch
197013e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdochfunction checkDateCacheCurrent() {
197113e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch  var new_date_cache_version = %DateCacheVersion();
197213e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch  if (new_date_cache_version == date_cache_version) {
197313e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch    return;
197413e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch  }
197513e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch  date_cache_version = new_date_cache_version;
197613e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch
197713e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch  clearDefaultObjects();
197813e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch}
1979b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1980b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
1981b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns cached or newly created instance of a given service.
1982b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * We cache only default instances (where no locales or options are provided).
1983b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
1984b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction cachedOrNewService(service, locales, options, defaults) {
1985014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  var useOptions = (IS_UNDEFINED(defaults)) ? options : defaults;
1986014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (IS_UNDEFINED(locales) && IS_UNDEFINED(options)) {
198713e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch    checkDateCacheCurrent();
1988014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    if (IS_UNDEFINED(defaultObjects[service])) {
1989b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      defaultObjects[service] = new savedObjects[service](locales, useOptions);
1990b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
1991b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return defaultObjects[service];
1992b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
1993b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return new savedObjects[service](locales, useOptions);
1994b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
1995b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
1996bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdochfunction LocaleConvertCase(s, locales, isToUpper) {
1997bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  // ECMA 402 section 13.1.2 steps 1 through 12.
1998bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  var language;
1999bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  // Optimize for the most common two cases. initializeLocaleList() can handle
2000bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  // them as well, but it's rather slow accounting for over 60% of
2001bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  // toLocale{U,L}Case() and about 40% of toLocale{U,L}Case("<locale>").
2002bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  if (IS_UNDEFINED(locales)) {
2003bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch    language = GetDefaultICULocaleJS();
2004bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  } else if (IS_STRING(locales)) {
2005bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch    language = canonicalizeLanguageTag(locales);
2006bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  } else {
2007bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch    var locales = initializeLocaleList(locales);
2008bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch    language = locales.length > 0 ? locales[0] : GetDefaultICULocaleJS();
2009bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  }
2010bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch
2011bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  // StringSplit is slower than this.
2012c8c1d9e03f4babd16833b0f8ccf6aab5fa6e8c7aBen Murdoch  var pos = %StringIndexOf(language, '-', 0);
201362ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  if (pos !== -1) {
2014bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch    language = %_Call(StringSubstring, language, 0, pos);
2015bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  }
2016bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch
201762ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  return %StringLocaleConvertCase(s, isToUpper, language);
2018bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch}
2019bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch
2020b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
2021b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Compares this and that, and returns less than 0, 0 or greater than 0 value.
2022b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Overrides the built-in method.
2023b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
2024014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben MurdochOverrideFunction(GlobalString.prototype, 'localeCompare', function(that) {
2025b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if (IS_NULL_OR_UNDEFINED(this)) {
2026f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      throw %make_type_error(kMethodInvokedOnNullOrUndefined);
2027b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
2028b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
2029109988c7ccb6f3fd1a58574fa3dfb88beaef6632Ben Murdoch    var locales = arguments[1];
2030109988c7ccb6f3fd1a58574fa3dfb88beaef6632Ben Murdoch    var options = arguments[2];
2031b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    var collator = cachedOrNewService('collator', locales, options);
2032b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return compare(collator, this, that);
2033014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  }
2034014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch);
2035b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
2036b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
2037b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
2038b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Unicode normalization. This method is called with one argument that
2039b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * specifies the normalization form.
2040b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * If none is specified, "NFC" is assumed.
2041b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * If the form is not one of "NFC", "NFD", "NFKC", or "NFKD", then throw
2042b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * a RangeError Exception.
2043b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
2044014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
2045014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben MurdochOverrideFunction(GlobalString.prototype, 'normalize', function() {
2046b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    CHECK_OBJECT_COERCIBLE(this, "String.prototype.normalize");
2047014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    var s = TO_STRING(this);
2048014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch
2049109988c7ccb6f3fd1a58574fa3dfb88beaef6632Ben Murdoch    var formArg = arguments[0];
2050014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    var form = IS_UNDEFINED(formArg) ? 'NFC' : TO_STRING(formArg);
2051b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
2052014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    var NORMALIZATION_FORMS = ['NFC', 'NFD', 'NFKC', 'NFKD'];
2053b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
2054f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    var normalizationForm = %ArrayIndexOf(NORMALIZATION_FORMS, form, 0);
2055b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    if (normalizationForm === -1) {
2056f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      throw %make_range_error(kNormalizationForm,
2057014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch          %_Call(ArrayJoin, NORMALIZATION_FORMS, ', '));
2058b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
2059b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
2060014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    return %StringNormalize(s, normalizationForm);
2061014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  }
2062014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch);
2063b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
206462ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch// TODO(littledan): Rewrite these two functions as C++ builtins
2065bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdochfunction ToLowerCaseI18N() {
2066bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLowerCase");
206762ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  return %StringToLowerCaseI18N(TO_STRING(this));
2068bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch}
2069bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch
2070bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdochfunction ToUpperCaseI18N() {
2071bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  CHECK_OBJECT_COERCIBLE(this, "String.prototype.toUpperCase");
207262ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  return %StringToUpperCaseI18N(TO_STRING(this));
2073bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch}
2074bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch
2075bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdochfunction ToLocaleLowerCaseI18N(locales) {
2076bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleLowerCase");
2077bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  return LocaleConvertCase(TO_STRING(this), locales, false);
2078bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch}
2079bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch
2080bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch%FunctionSetLength(ToLocaleLowerCaseI18N, 0);
2081bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch
2082bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdochfunction ToLocaleUpperCaseI18N(locales) {
2083bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleUpperCase");
2084bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch  return LocaleConvertCase(TO_STRING(this), locales, true);
2085bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch}
2086bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch
2087bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch%FunctionSetLength(ToLocaleUpperCaseI18N, 0);
2088bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch
2089b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
2090b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
2091b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Formats a Number object (this) using locale and options values.
2092b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * If locale or options are omitted, defaults are used.
2093b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
2094014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben MurdochOverrideFunction(GlobalNumber.prototype, 'toLocaleString', function() {
2095014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch    if (!(this instanceof GlobalNumber) && typeof(this) !== 'number') {
2096f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      throw %make_type_error(kMethodInvokedOnWrongType, "Number");
2097b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    }
2098b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
2099109988c7ccb6f3fd1a58574fa3dfb88beaef6632Ben Murdoch    var locales = arguments[0];
2100109988c7ccb6f3fd1a58574fa3dfb88beaef6632Ben Murdoch    var options = arguments[1];
2101b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    var numberFormat = cachedOrNewService('numberformat', locales, options);
2102b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return formatNumber(numberFormat, this);
2103014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  }
2104014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch);
2105b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
2106b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
2107b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
2108b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns actual formatted date or fails if date parameter is invalid.
2109b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
2110b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochfunction toLocaleDateTime(date, locales, options, required, defaults, service) {
2111014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  if (!(date instanceof GlobalDate)) {
2112f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    throw %make_type_error(kMethodInvokedOnWrongType, "Date");
2113b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  }
2114b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
2115f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdoch  var dateValue = TO_NUMBER(date);
2116f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdoch  if (NUMBER_IS_NAN(dateValue)) return 'Invalid Date';
2117b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
2118b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var internalOptions = toDateTimeOptions(options, required, defaults);
2119b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
2120b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  var dateFormat =
2121b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch      cachedOrNewService(service, locales, options, internalOptions);
2122b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
2123b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch  return formatDate(dateFormat, date);
2124b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch}
2125b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
2126b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
2127b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
2128b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Formats a Date object (this) using locale and options values.
2129b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * If locale or options are omitted, defaults are used - both date and time are
2130b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * present in the output.
2131b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
2132014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben MurdochOverrideFunction(GlobalDate.prototype, 'toLocaleString', function() {
2133109988c7ccb6f3fd1a58574fa3dfb88beaef6632Ben Murdoch    var locales = arguments[0];
2134109988c7ccb6f3fd1a58574fa3dfb88beaef6632Ben Murdoch    var options = arguments[1];
2135b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return toLocaleDateTime(
2136b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        this, locales, options, 'any', 'all', 'dateformatall');
2137014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  }
2138014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch);
2139b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
2140b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
2141b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
2142b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Formats a Date object (this) using locale and options values.
2143b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * If locale or options are omitted, defaults are used - only date is present
2144b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * in the output.
2145b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
2146014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben MurdochOverrideFunction(GlobalDate.prototype, 'toLocaleDateString', function() {
2147109988c7ccb6f3fd1a58574fa3dfb88beaef6632Ben Murdoch    var locales = arguments[0];
2148109988c7ccb6f3fd1a58574fa3dfb88beaef6632Ben Murdoch    var options = arguments[1];
2149b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return toLocaleDateTime(
2150b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        this, locales, options, 'date', 'date', 'dateformatdate');
2151014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  }
2152014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch);
2153b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
2154b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
2155b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/**
2156b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Formats a Date object (this) using locale and options values.
2157b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * If locale or options are omitted, defaults are used - only time is present
2158b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * in the output.
2159b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */
2160014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben MurdochOverrideFunction(GlobalDate.prototype, 'toLocaleTimeString', function() {
2161109988c7ccb6f3fd1a58574fa3dfb88beaef6632Ben Murdoch    var locales = arguments[0];
2162109988c7ccb6f3fd1a58574fa3dfb88beaef6632Ben Murdoch    var options = arguments[1];
2163b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch    return toLocaleDateTime(
2164b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch        this, locales, options, 'time', 'time', 'dateformattime');
2165014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch  }
2166014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch);
2167b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch
2168f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdoch%FunctionRemovePrototype(FormatDateToParts);
216962ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch%FunctionRemovePrototype(ToLowerCaseI18N);
217062ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch%FunctionRemovePrototype(ToUpperCaseI18N);
217162ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch%FunctionRemovePrototype(ToLocaleLowerCaseI18N);
217262ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch%FunctionRemovePrototype(ToLocaleUpperCaseI18N);
217362ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch
217462ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdochutils.SetFunctionName(FormatDateToParts, "formatToParts");
217562ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdochutils.SetFunctionName(ToLowerCaseI18N, "toLowerCase");
217662ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdochutils.SetFunctionName(ToUpperCaseI18N, "toUpperCase");
217762ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdochutils.SetFunctionName(ToLocaleLowerCaseI18N, "toLocaleLowerCase");
217862ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdochutils.SetFunctionName(ToLocaleUpperCaseI18N, "toLocaleUpperCase");
2179f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdoch
2180bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdochutils.Export(function(to) {
2181f3b273f5e6ffd2f6ba1c18a27a17db41dfb113c3Ben Murdoch  to.FormatDateToParts = FormatDateToParts;
218262ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  to.ToLowerCaseI18N = ToLowerCaseI18N;
218362ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  to.ToUpperCaseI18N = ToUpperCaseI18N;
218462ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  to.ToLocaleLowerCaseI18N = ToLocaleLowerCaseI18N;
218562ed631aa0ff23db68a47fd423efa9c019ff2c9eBen Murdoch  to.ToLocaleUpperCaseI18N = ToLocaleUpperCaseI18N;
2186bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch});
2187bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch
2188014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch})
2189