js/src/builtin/Intl.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 /* Portions Copyright Norbert Lindenberg 2011-2012. */
     7 /*global JSMSG_INTL_OBJECT_NOT_INITED: false, JSMSG_INVALID_LOCALES_ELEMENT: false,
     8          JSMSG_INVALID_LANGUAGE_TAG: false, JSMSG_INVALID_LOCALE_MATCHER: false,
     9          JSMSG_INVALID_OPTION_VALUE: false, JSMSG_INVALID_DIGITS_VALUE: false,
    10          JSMSG_INTL_OBJECT_REINITED: false, JSMSG_INVALID_CURRENCY_CODE: false,
    11          JSMSG_UNDEFINED_CURRENCY: false, JSMSG_INVALID_TIME_ZONE: false,
    12          JSMSG_DATE_NOT_FINITE: false,
    13          intl_Collator_availableLocales: false,
    14          intl_availableCollations: false,
    15          intl_CompareStrings: false,
    16          intl_NumberFormat_availableLocales: false,
    17          intl_numberingSystem: false,
    18          intl_FormatNumber: false,
    19          intl_DateTimeFormat_availableLocales: false,
    20          intl_availableCalendars: false,
    21          intl_patternForSkeleton: false,
    22          intl_FormatDateTime: false,
    23 */
    25 /*
    26  * The Intl module specified by standard ECMA-402,
    27  * ECMAScript Internationalization API Specification.
    28  */
    31 /********** Locales, Time Zones, and Currencies **********/
    34 /**
    35  * Convert s to upper case, but limited to characters a-z.
    36  *
    37  * Spec: ECMAScript Internationalization API Specification, 6.1.
    38  */
    39 function toASCIIUpperCase(s) {
    40     assert(typeof s === "string", "toASCIIUpperCase");
    42     // String.prototype.toUpperCase may map non-ASCII characters into ASCII,
    43     // so go character by character (actually code unit by code unit, but
    44     // since we only care about ASCII characters here, that's OK).
    45     var result = "";
    46     for (var i = 0; i < s.length; i++) {
    47         var c = s[i];
    48         if ("a" <= c && c <= "z")
    49             c = callFunction(std_String_toUpperCase, c);
    50         result += c;
    51     }
    52     return result;
    53 }
    56 /**
    57  * Regular expression matching a "Unicode locale extension sequence", which the
    58  * specification defines as: "any substring of a language tag that starts with
    59  * a separator '-' and the singleton 'u' and includes the maximum sequence of
    60  * following non-singleton subtags and their preceding '-' separators."
    61  *
    62  * Alternatively, this may be defined as: the components of a language tag that
    63  * match the extension production in RFC 5646, where the singleton component is
    64  * "u".
    65  *
    66  * Spec: ECMAScript Internationalization API Specification, 6.2.1.
    67  */
    68 var unicodeLocaleExtensionSequence = "-u(-[a-z0-9]{2,8})+";
    69 var unicodeLocaleExtensionSequenceRE = new RegExp(unicodeLocaleExtensionSequence);
    72 /**
    73  * Removes Unicode locale extension sequences from the given language tag.
    74  */
    75 function removeUnicodeExtensions(locale) {
    76     // Don't use std_String_replace directly with a regular expression,
    77     // as that would set RegExp statics.
    78     var extensions;
    79     while ((extensions = regexp_exec_no_statics(unicodeLocaleExtensionSequenceRE, locale)) !== null) {
    80         locale = callFunction(std_String_replace, locale, extensions[0], "");
    81         unicodeLocaleExtensionSequenceRE.lastIndex = 0;
    82     }
    83     return locale;
    84 }
    87 /**
    88  * Regular expression defining BCP 47 language tags.
    89  *
    90  * Spec: RFC 5646 section 2.1.
    91  */
    92 var languageTagRE = (function () {
    93     // RFC 5234 section B.1
    94     // ALPHA          =  %x41-5A / %x61-7A   ; A-Z / a-z
    95     var ALPHA = "[a-zA-Z]";
    96     // DIGIT          =  %x30-39
    97     //                        ; 0-9
    98     var DIGIT = "[0-9]";
   100     // RFC 5646 section 2.1
   101     // alphanum      = (ALPHA / DIGIT)     ; letters and numbers
   102     var alphanum = "(?:" + ALPHA + "|" + DIGIT + ")";
   103     // regular       = "art-lojban"        ; these tags match the 'langtag'
   104     //               / "cel-gaulish"       ; production, but their subtags
   105     //               / "no-bok"            ; are not extended language
   106     //               / "no-nyn"            ; or variant subtags: their meaning
   107     //               / "zh-guoyu"          ; is defined by their registration
   108     //               / "zh-hakka"          ; and all of these are deprecated
   109     //               / "zh-min"            ; in favor of a more modern
   110     //               / "zh-min-nan"        ; subtag or sequence of subtags
   111     //               / "zh-xiang"
   112     var regular = "(?:art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang)";
   113     // irregular     = "en-GB-oed"         ; irregular tags do not match
   114     //                / "i-ami"             ; the 'langtag' production and
   115     //                / "i-bnn"             ; would not otherwise be
   116     //                / "i-default"         ; considered 'well-formed'
   117     //                / "i-enochian"        ; These tags are all valid,
   118     //                / "i-hak"             ; but most are deprecated
   119     //                / "i-klingon"         ; in favor of more modern
   120     //                / "i-lux"             ; subtags or subtag
   121     //                / "i-mingo"           ; combination
   122     //                / "i-navajo"
   123     //                / "i-pwn"
   124     //                / "i-tao"
   125     //                / "i-tay"
   126     //                / "i-tsu"
   127     //                / "sgn-BE-FR"
   128     //                / "sgn-BE-NL"
   129     //                / "sgn-CH-DE"
   130     var irregular = "(?:en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)";
   131     // grandfathered = irregular           ; non-redundant tags registered
   132     //               / regular             ; during the RFC 3066 era
   133     var grandfathered = "(?:" + irregular + "|" + regular + ")";
   134     // privateuse    = "x" 1*("-" (1*8alphanum))
   135     var privateuse = "(?:x(?:-[a-z0-9]{1,8})+)";
   136     // singleton     = DIGIT               ; 0 - 9
   137     //               / %x41-57             ; A - W
   138     //               / %x59-5A             ; Y - Z
   139     //               / %x61-77             ; a - w
   140     //               / %x79-7A             ; y - z
   141     var singleton = "(?:" + DIGIT + "|[A-WY-Za-wy-z])";
   142     // extension     = singleton 1*("-" (2*8alphanum))
   143     var extension = "(?:" + singleton + "(?:-" + alphanum + "{2,8})+)";
   144     // variant       = 5*8alphanum         ; registered variants
   145     //               / (DIGIT 3alphanum)
   146     var variant = "(?:" + alphanum + "{5,8}|(?:" + DIGIT + alphanum + "{3}))";
   147     // region        = 2ALPHA              ; ISO 3166-1 code
   148     //               / 3DIGIT              ; UN M.49 code
   149     var region = "(?:" + ALPHA + "{2}|" + DIGIT + "{3})";
   150     // script        = 4ALPHA              ; ISO 15924 code
   151     var script = "(?:" + ALPHA + "{4})";
   152     // extlang       = 3ALPHA              ; selected ISO 639 codes
   153     //                 *2("-" 3ALPHA)      ; permanently reserved
   154     var extlang = "(?:" + ALPHA + "{3}(?:-" + ALPHA + "{3}){0,2})";
   155     // language      = 2*3ALPHA            ; shortest ISO 639 code
   156     //                 ["-" extlang]       ; sometimes followed by
   157     //                                     ; extended language subtags
   158     //               / 4ALPHA              ; or reserved for future use
   159     //               / 5*8ALPHA            ; or registered language subtag
   160     var language = "(?:" + ALPHA + "{2,3}(?:-" + extlang + ")?|" + ALPHA + "{4}|" + ALPHA + "{5,8})";
   161     // langtag       = language
   162     //                 ["-" script]
   163     //                 ["-" region]
   164     //                 *("-" variant)
   165     //                 *("-" extension)
   166     //                 ["-" privateuse]
   167     var langtag = language + "(?:-" + script + ")?(?:-" + region + ")?(?:-" +
   168                   variant + ")*(?:-" + extension + ")*(?:-" + privateuse + ")?";
   169     // Language-Tag  = langtag             ; normal language tags
   170     //               / privateuse          ; private use tag
   171     //               / grandfathered       ; grandfathered tags
   172     var languageTag = "^(?:" + langtag + "|" + privateuse + "|" + grandfathered + ")$";
   174     // Language tags are case insensitive (RFC 5646 section 2.1.1).
   175     return new RegExp(languageTag, "i");
   176 }());
   179 var duplicateVariantRE = (function () {
   180     // RFC 5234 section B.1
   181     // ALPHA          =  %x41-5A / %x61-7A   ; A-Z / a-z
   182     var ALPHA = "[a-zA-Z]";
   183     // DIGIT          =  %x30-39
   184     //                        ; 0-9
   185     var DIGIT = "[0-9]";
   187     // RFC 5646 section 2.1
   188     // alphanum      = (ALPHA / DIGIT)     ; letters and numbers
   189     var alphanum = "(?:" + ALPHA + "|" + DIGIT + ")";
   190     // variant       = 5*8alphanum         ; registered variants
   191     //               / (DIGIT 3alphanum)
   192     var variant = "(?:" + alphanum + "{5,8}|(?:" + DIGIT + alphanum + "{3}))";
   194     // Match a langtag that contains a duplicate variant.
   195     var duplicateVariant =
   196         // Match everything in a langtag prior to any variants, and maybe some
   197         // of the variants as well (which makes this pattern inefficient but
   198         // not wrong, for our purposes);
   199         "(?:" + alphanum + "{2,8}-)+" +
   200         // a variant, parenthesised so that we can refer back to it later;
   201         "(" + variant + ")-" +
   202         // zero or more subtags at least two characters long (thus stopping
   203         // before extension and privateuse components);
   204         "(?:" + alphanum + "{2,8}-)*" +
   205         // and the same variant again
   206         "\\1" +
   207         // ...but not followed by any characters that would turn it into a
   208         // different subtag.
   209         "(?!" + alphanum + ")";
   211     // Language tags are case insensitive (RFC 5646 section 2.1.1), but for
   212     // this regular expression that's covered by having its character classes
   213     // list both upper- and lower-case characters.
   214     return new RegExp(duplicateVariant);
   215 }());
   218 var duplicateSingletonRE = (function () {
   219     // RFC 5234 section B.1
   220     // ALPHA          =  %x41-5A / %x61-7A   ; A-Z / a-z
   221     var ALPHA = "[a-zA-Z]";
   222     // DIGIT          =  %x30-39
   223     //                        ; 0-9
   224     var DIGIT = "[0-9]";
   226     // RFC 5646 section 2.1
   227     // alphanum      = (ALPHA / DIGIT)     ; letters and numbers
   228     var alphanum = "(?:" + ALPHA + "|" + DIGIT + ")";
   229     // singleton     = DIGIT               ; 0 - 9
   230     //               / %x41-57             ; A - W
   231     //               / %x59-5A             ; Y - Z
   232     //               / %x61-77             ; a - w
   233     //               / %x79-7A             ; y - z
   234     var singleton = "(?:" + DIGIT + "|[A-WY-Za-wy-z])";
   236     // Match a langtag that contains a duplicate singleton.
   237     var duplicateSingleton =
   238         // Match a singleton subtag, parenthesised so that we can refer back to
   239         // it later;
   240         "-(" + singleton + ")-" +
   241         // then zero or more subtags;
   242         "(?:" + alphanum + "+-)*" +
   243         // and the same singleton again
   244         "\\1" +
   245         // ...but not followed by any characters that would turn it into a
   246         // different subtag.
   247         "(?!" + alphanum + ")";
   249     // Language tags are case insensitive (RFC 5646 section 2.1.1), but for
   250     // this regular expression that's covered by having its character classes
   251     // list both upper- and lower-case characters.
   252     return new RegExp(duplicateSingleton);
   253 }());
   256 /**
   257  * Verifies that the given string is a well-formed BCP 47 language tag
   258  * with no duplicate variant or singleton subtags.
   259  *
   260  * Spec: ECMAScript Internationalization API Specification, 6.2.2.
   261  */
   262 function IsStructurallyValidLanguageTag(locale) {
   263     assert(typeof locale === "string", "IsStructurallyValidLanguageTag");
   264     if (!regexp_test_no_statics(languageTagRE, locale))
   265         return false;
   267     // Before checking for duplicate variant or singleton subtags with
   268     // regular expressions, we have to get private use subtag sequences
   269     // out of the picture.
   270     if (callFunction(std_String_startsWith, locale, "x-"))
   271         return true;
   272     var pos = callFunction(std_String_indexOf, locale, "-x-");
   273     if (pos !== -1)
   274         locale = callFunction(std_String_substring, locale, 0, pos);
   276     // Check for duplicate variant or singleton subtags.
   277     return !regexp_test_no_statics(duplicateVariantRE, locale) &&
   278            !regexp_test_no_statics(duplicateSingletonRE, locale);
   279 }
   282 /**
   283  * Canonicalizes the given structurally valid BCP 47 language tag, including
   284  * regularized case of subtags. For example, the language tag
   285  * Zh-NAN-haNS-bu-variant2-Variant1-u-ca-chinese-t-Zh-laTN-x-PRIVATE, where
   286  *
   287  *     Zh             ; 2*3ALPHA
   288  *     -NAN           ; ["-" extlang]
   289  *     -haNS          ; ["-" script]
   290  *     -bu            ; ["-" region]
   291  *     -variant2      ; *("-" variant)
   292  *     -Variant1
   293  *     -u-ca-chinese  ; *("-" extension)
   294  *     -t-Zh-laTN
   295  *     -x-PRIVATE     ; ["-" privateuse]
   296  *
   297  * becomes nan-Hans-mm-variant2-variant1-t-zh-latn-u-ca-chinese-x-private
   298  *
   299  * Spec: ECMAScript Internationalization API Specification, 6.2.3.
   300  * Spec: RFC 5646, section 4.5.
   301  */
   302 function CanonicalizeLanguageTag(locale) {
   303     assert(IsStructurallyValidLanguageTag(locale), "CanonicalizeLanguageTag");
   305     // The input
   306     // "Zh-NAN-haNS-bu-variant2-Variant1-u-ca-chinese-t-Zh-laTN-x-PRIVATE"
   307     // will be used throughout this method to illustrate how it works.
   309     // Language tags are compared and processed case-insensitively, so
   310     // technically it's not necessary to adjust case. But for easier processing,
   311     // and because the canonical form for most subtags is lower case, we start
   312     // with lower case for all.
   313     // "Zh-NAN-haNS-bu-variant2-Variant1-u-ca-chinese-t-Zh-laTN-x-PRIVATE" ->
   314     // "zh-nan-hans-bu-variant2-variant1-u-ca-chinese-t-zh-latn-x-private"
   315     locale = callFunction(std_String_toLowerCase, locale);
   317     // Handle mappings for complete tags.
   318     if (callFunction(std_Object_hasOwnProperty, langTagMappings, locale))
   319         return langTagMappings[locale];
   321     var subtags = callFunction(std_String_split, locale, "-");
   322     var i = 0;
   324     // Handle the standard part: All subtags before the first singleton or "x".
   325     // "zh-nan-hans-bu-variant2-variant1"
   326     while (i < subtags.length) {
   327         var subtag = subtags[i];
   329         // If we reach the start of an extension sequence or private use part,
   330         // we're done with this loop. We have to check for i > 0 because for
   331         // irregular language tags, such as i-klingon, the single-character
   332         // subtag "i" is not the start of an extension sequence.
   333         // In the example, we break at "u".
   334         if (subtag.length === 1 && (i > 0 || subtag === "x"))
   335             break;
   337         if (subtag.length === 4) {
   338             // 4-character subtags are script codes; their first character
   339             // needs to be capitalized. "hans" -> "Hans"
   340             subtag = callFunction(std_String_toUpperCase, subtag[0]) +
   341                      callFunction(std_String_substring, subtag, 1);
   342         } else if (i !== 0 && subtag.length === 2) {
   343             // 2-character subtags that are not in initial position are region
   344             // codes; they need to be upper case. "bu" -> "BU"
   345             subtag = callFunction(std_String_toUpperCase, subtag);
   346         }
   347         if (callFunction(std_Object_hasOwnProperty, langSubtagMappings, subtag)) {
   348             // Replace deprecated subtags with their preferred values.
   349             // "BU" -> "MM"
   350             // This has to come after we capitalize region codes because
   351             // otherwise some language and region codes could be confused.
   352             // For example, "in" is an obsolete language code for Indonesian,
   353             // but "IN" is the country code for India.
   354             // Note that the script generating langSubtagMappings makes sure
   355             // that no regular subtag mapping will replace an extlang code.
   356             subtag = langSubtagMappings[subtag];
   357         } else if (callFunction(std_Object_hasOwnProperty, extlangMappings, subtag)) {
   358             // Replace deprecated extlang subtags with their preferred values,
   359             // and remove the preceding subtag if it's a redundant prefix.
   360             // "zh-nan" -> "nan"
   361             // Note that the script generating extlangMappings makes sure that
   362             // no extlang mapping will replace a normal language code.
   363             subtag = extlangMappings[subtag].preferred;
   364             if (i === 1 && extlangMappings[subtag].prefix === subtags[0]) {
   365                 callFunction(std_Array_shift, subtags);
   366                 i--;
   367             }
   368         }
   369         subtags[i] = subtag;
   370         i++;
   371     }
   372     var normal = callFunction(std_Array_join, callFunction(std_Array_slice, subtags, 0, i), "-");
   374     // Extension sequences are sorted by their singleton characters.
   375     // "u-ca-chinese-t-zh-latn" -> "t-zh-latn-u-ca-chinese"
   376     var extensions = new List();
   377     while (i < subtags.length && subtags[i] !== "x") {
   378         var extensionStart = i;
   379         i++;
   380         while (i < subtags.length && subtags[i].length > 1)
   381             i++;
   382         var extension = callFunction(std_Array_join, callFunction(std_Array_slice, subtags, extensionStart, i), "-");
   383         extensions.push(extension);
   384     }
   385     extensions.sort();
   387     // Private use sequences are left as is. "x-private"
   388     var privateUse = "";
   389     if (i < subtags.length)
   390         privateUse = callFunction(std_Array_join, callFunction(std_Array_slice, subtags, i), "-");
   392     // Put everything back together.
   393     var canonical = normal;
   394     if (extensions.length > 0)
   395         canonical += "-" + extensions.join("-");
   396     if (privateUse.length > 0) {
   397         // Be careful of a Language-Tag that is entirely privateuse.
   398         if (canonical.length > 0)
   399             canonical += "-" + privateUse;
   400         else
   401             canonical = privateUse;
   402     }
   404     return canonical;
   405 }
   408 // mappings from some commonly used old-style language tags to current flavors
   409 // with script codes
   410 var oldStyleLanguageTagMappings = {
   411     "pa-PK": "pa-Arab-PK",
   412     "zh-CN": "zh-Hans-CN",
   413     "zh-HK": "zh-Hant-HK",
   414     "zh-SG": "zh-Hans-SG",
   415     "zh-TW": "zh-Hant-TW"
   416 };
   419 /**
   420  * Returns the BCP 47 language tag for the host environment's current locale.
   421  *
   422  * Spec: ECMAScript Internationalization API Specification, 6.2.4.
   423  */
   424 function DefaultLocale() {
   425     // The locale of last resort is used if none of the available locales
   426     // satisfies a request. "en-GB" is used based on the assumptions that
   427     // English is the most common second language, that both en-GB and en-US
   428     // are normally available in an implementation, and that en-GB is more
   429     // representative of the English used in other locales.
   430     var localeOfLastResort = "en-GB";
   432     var locale = RuntimeDefaultLocale();
   433     if (!IsStructurallyValidLanguageTag(locale))
   434         return localeOfLastResort;
   436     locale = CanonicalizeLanguageTag(locale);
   437     if (callFunction(std_Object_hasOwnProperty, oldStyleLanguageTagMappings, locale))
   438         locale = oldStyleLanguageTagMappings[locale];
   440     if (!(collatorInternalProperties.availableLocales()[locale] &&
   441           numberFormatInternalProperties.availableLocales()[locale] &&
   442           dateTimeFormatInternalProperties.availableLocales()[locale]))
   443     {
   444         locale = localeOfLastResort;
   445     }
   446     return locale;
   447 }
   450 /**
   451  * Verifies that the given string is a well-formed ISO 4217 currency code.
   452  *
   453  * Spec: ECMAScript Internationalization API Specification, 6.3.1.
   454  */
   455 function IsWellFormedCurrencyCode(currency) {
   456     var c = ToString(currency);
   457     var normalized = toASCIIUpperCase(c);
   458     if (normalized.length !== 3)
   459         return false;
   460     return !regexp_test_no_statics(/[^A-Z]/, normalized);
   461 }
   464 /********** Locale and Parameter Negotiation **********/
   467 /**
   468  * Add old-style language tags without script code for locales that in current
   469  * usage would include a script subtag. Returns the availableLocales argument
   470  * provided.
   471  *
   472  * Spec: ECMAScript Internationalization API Specification, 9.1.
   473  */
   474 function addOldStyleLanguageTags(availableLocales) {
   475     var oldStyleLocales = std_Object_getOwnPropertyNames(oldStyleLanguageTagMappings);
   476     for (var i = 0; i < oldStyleLocales.length; i++) {
   477         var oldStyleLocale = oldStyleLocales[i];
   478         if (availableLocales[oldStyleLanguageTagMappings[oldStyleLocale]])
   479             availableLocales[oldStyleLocale] = true;
   480     }
   481     return availableLocales;
   482 }
   485 /**
   486  * Canonicalizes a locale list.
   487  *
   488  * Spec: ECMAScript Internationalization API Specification, 9.2.1.
   489  */
   490 function CanonicalizeLocaleList(locales) {
   491     if (locales === undefined)
   492         return new List();
   493     var seen = new List();
   494     if (typeof locales === "string")
   495         locales = [locales];
   496     var O = ToObject(locales);
   497     var len = TO_UINT32(O.length);
   498     var k = 0;
   499     while (k < len) {
   500         // Don't call ToString(k) - SpiderMonkey is faster with integers.
   501         var kPresent = HasProperty(O, k);
   502         if (kPresent) {
   503             var kValue = O[k];
   504             if (!(typeof kValue === "string" || IsObject(kValue)))
   505                 ThrowError(JSMSG_INVALID_LOCALES_ELEMENT);
   506             var tag = ToString(kValue);
   507             if (!IsStructurallyValidLanguageTag(tag))
   508                 ThrowError(JSMSG_INVALID_LANGUAGE_TAG, tag);
   509             tag = CanonicalizeLanguageTag(tag);
   510             if (seen.indexOf(tag) === -1)
   511                 seen.push(tag);
   512         }
   513         k++;
   514     }
   515     return seen;
   516 }
   519 /**
   520  * Compares a BCP 47 language tag against the locales in availableLocales
   521  * and returns the best available match. Uses the fallback
   522  * mechanism of RFC 4647, section 3.4.
   523  *
   524  * Spec: ECMAScript Internationalization API Specification, 9.2.2.
   525  * Spec: RFC 4647, section 3.4.
   526  */
   527 function BestAvailableLocale(availableLocales, locale) {
   528     assert(IsStructurallyValidLanguageTag(locale), "invalid BestAvailableLocale locale structure");
   529     assert(locale === CanonicalizeLanguageTag(locale), "non-canonical BestAvailableLocale locale");
   530     assert(callFunction(std_String_indexOf, locale, "-u-") === -1, "locale shouldn't contain -u-");
   532     var candidate = locale;
   533     while (true) {
   534         if (availableLocales[candidate])
   535             return candidate;
   536         var pos = callFunction(std_String_lastIndexOf, candidate, "-");
   537         if (pos === -1)
   538             return undefined;
   539         if (pos >= 2 && candidate[pos - 2] === "-")
   540             pos -= 2;
   541         candidate = callFunction(std_String_substring, candidate, 0, pos);
   542     }
   543 }
   546 /**
   547  * Compares a BCP 47 language priority list against the set of locales in
   548  * availableLocales and determines the best available language to meet the
   549  * request. Options specified through Unicode extension subsequences are
   550  * ignored in the lookup, but information about such subsequences is returned
   551  * separately.
   552  *
   553  * This variant is based on the Lookup algorithm of RFC 4647 section 3.4.
   554  *
   555  * Spec: ECMAScript Internationalization API Specification, 9.2.3.
   556  * Spec: RFC 4647, section 3.4.
   557  */
   558 function LookupMatcher(availableLocales, requestedLocales) {
   559     var i = 0;
   560     var len = requestedLocales.length;
   561     var availableLocale;
   562     var locale, noExtensionsLocale;
   563     while (i < len && availableLocale === undefined) {
   564         locale = requestedLocales[i];
   565         noExtensionsLocale = removeUnicodeExtensions(locale);
   566         availableLocale = BestAvailableLocale(availableLocales, noExtensionsLocale);
   567         i++;
   568     }
   570     var result = new Record();
   571     if (availableLocale !== undefined) {
   572         result.locale = availableLocale;
   573         if (locale !== noExtensionsLocale) {
   574             var extensionMatch = regexp_exec_no_statics(unicodeLocaleExtensionSequenceRE, locale);
   575             var extension = extensionMatch[0];
   576             var extensionIndex = extensionMatch.index;
   577             result.extension = extension;
   578             result.extensionIndex = extensionIndex;
   579         }
   580     } else {
   581         result.locale = DefaultLocale();
   582     }
   583     return result;
   584 }
   587 /**
   588  * Compares a BCP 47 language priority list against the set of locales in
   589  * availableLocales and determines the best available language to meet the
   590  * request. Options specified through Unicode extension subsequences are
   591  * ignored in the lookup, but information about such subsequences is returned
   592  * separately.
   593  *
   594  * Spec: ECMAScript Internationalization API Specification, 9.2.4.
   595  */
   596 function BestFitMatcher(availableLocales, requestedLocales) {
   597     // this implementation doesn't have anything better
   598     return LookupMatcher(availableLocales, requestedLocales);
   599 }
   602 /**
   603  * Compares a BCP 47 language priority list against availableLocales and
   604  * determines the best available language to meet the request. Options specified
   605  * through Unicode extension subsequences are negotiated separately, taking the
   606  * caller's relevant extensions and locale data as well as client-provided
   607  * options into consideration.
   608  *
   609  * Spec: ECMAScript Internationalization API Specification, 9.2.5.
   610  */
   611 function ResolveLocale(availableLocales, requestedLocales, options, relevantExtensionKeys, localeData) {
   612     /*jshint laxbreak: true */
   614     // Steps 1-3.
   615     var matcher = options.localeMatcher;
   616     var r = (matcher === "lookup")
   617             ? LookupMatcher(availableLocales, requestedLocales)
   618             : BestFitMatcher(availableLocales, requestedLocales);
   620     // Step 4.
   621     var foundLocale = r.locale;
   623     // Step 5.a.
   624     var extension = r.extension;
   625     var extensionIndex, extensionSubtags, extensionSubtagsLength;
   627     // Step 5.
   628     if (extension !== undefined) {
   629         // Step 5.b.
   630         extensionIndex = r.extensionIndex;
   632         // Steps 5.d-e.
   633         extensionSubtags = callFunction(std_String_split, extension, "-");
   634         extensionSubtagsLength = extensionSubtags.length;
   635     }
   637     // Steps 6-7.
   638     var result = new Record();
   639     result.dataLocale = foundLocale;
   641     // Step 8.
   642     var supportedExtension = "-u";
   644     // Steps 9-11.
   645     var i = 0;
   646     var len = relevantExtensionKeys.length;
   647     while (i < len) {
   648         // Steps 11.a-c.
   649         var key = relevantExtensionKeys[i];
   651         // In this implementation, localeData is a function, not an object.
   652         var foundLocaleData = localeData(foundLocale);
   653         var keyLocaleData = foundLocaleData[key];
   655         // Locale data provides default value.
   656         // Step 11.d.
   657         var value = keyLocaleData[0];
   659         // Locale tag may override.
   661         // Step 11.e.
   662         var supportedExtensionAddition = "";
   664         // Step 11.f is implemented by Utilities.js.
   666         var valuePos;
   668         // Step 11.g.
   669         if (extensionSubtags !== undefined) {
   670             // Step 11.g.i.
   671             var keyPos = callFunction(std_Array_indexOf, extensionSubtags, key);
   673             // Step 11.g.ii.
   674             if (keyPos !== -1) {
   675                 // Step 11.g.ii.1.
   676                 if (keyPos + 1 < extensionSubtagsLength &&
   677                     extensionSubtags[keyPos + 1].length > 2)
   678                 {
   679                     // Step 11.g.ii.1.a.
   680                     var requestedValue = extensionSubtags[keyPos + 1];
   682                     // Step 11.g.ii.1.b.
   683                     valuePos = callFunction(std_Array_indexOf, keyLocaleData, requestedValue);
   685                     // Step 11.g.ii.1.c.
   686                     if (valuePos !== -1) {
   687                         value = requestedValue;
   688                         supportedExtensionAddition = "-" + key + "-" + value;
   689                     }
   690                 } else {
   691                     // Step 11.g.ii.2.
   693                     // According to the LDML spec, if there's no type value,
   694                     // and true is an allowed value, it's used.
   696                     // Step 11.g.ii.2.a.
   697                     valuePos = callFunction(std_Array_indexOf, keyLocaleData, "true");
   699                     // Step 11.g.ii.2.b.
   700                     if (valuePos !== -1)
   701                         value = "true";
   702                 }
   703             }
   704         }
   706         // Options override all.
   708         // Step 11.h.i.
   709         var optionsValue = options[key];
   711         // Step 11.h, 11.h.ii.
   712         if (optionsValue !== undefined &&
   713             callFunction(std_Array_indexOf, keyLocaleData, optionsValue) !== -1)
   714         {
   715             // Step 11.h.ii.1.
   716             if (optionsValue !== value) {
   717                 value = optionsValue;
   718                 supportedExtensionAddition = "";
   719             }
   720         }
   722         // Steps 11.i-k.
   723         result[key] = value;
   724         supportedExtension += supportedExtensionAddition;
   725         i++;
   726     }
   728     // Step 12.
   729     if (supportedExtension.length > 2) {
   730         var preExtension = callFunction(std_String_substring, foundLocale, 0, extensionIndex);
   731         var postExtension = callFunction(std_String_substring, foundLocale, extensionIndex);
   732         foundLocale = preExtension + supportedExtension + postExtension;
   733     }
   735     // Steps 13-14.
   736     result.locale = foundLocale;
   737     return result;
   738 }
   741 /**
   742  * Returns the subset of requestedLocales for which availableLocales has a
   743  * matching (possibly fallback) locale. Locales appear in the same order in the
   744  * returned list as in the input list.
   745  *
   746  * Spec: ECMAScript Internationalization API Specification, 9.2.6.
   747  */
   748 function LookupSupportedLocales(availableLocales, requestedLocales) {
   749     // Steps 1-2.
   750     var len = requestedLocales.length;
   751     var subset = new List();
   753     // Steps 3-4.
   754     var k = 0;
   755     while (k < len) {
   756         // Steps 4.a-b.
   757         var locale = requestedLocales[k];
   758         var noExtensionsLocale = removeUnicodeExtensions(locale);
   760         // Step 4.c-d.
   761         var availableLocale = BestAvailableLocale(availableLocales, noExtensionsLocale);
   762         if (availableLocale !== undefined)
   763             subset.push(locale);
   765         // Step 4.e.
   766         k++;
   767     }
   769     // Steps 5-6.
   770     return subset.slice(0);
   771 }
   774 /**
   775  * Returns the subset of requestedLocales for which availableLocales has a
   776  * matching (possibly fallback) locale. Locales appear in the same order in the
   777  * returned list as in the input list.
   778  *
   779  * Spec: ECMAScript Internationalization API Specification, 9.2.7.
   780  */
   781 function BestFitSupportedLocales(availableLocales, requestedLocales) {
   782     // don't have anything better
   783     return LookupSupportedLocales(availableLocales, requestedLocales);
   784 }
   787 /**
   788  * Returns the subset of requestedLocales for which availableLocales has a
   789  * matching (possibly fallback) locale. Locales appear in the same order in the
   790  * returned list as in the input list.
   791  *
   792  * Spec: ECMAScript Internationalization API Specification, 9.2.8.
   793  */
   794 function SupportedLocales(availableLocales, requestedLocales, options) {
   795     /*jshint laxbreak: true */
   797     // Step 1.
   798     var matcher;
   799     if (options !== undefined) {
   800         // Steps 1.a-b.
   801         options = ToObject(options);
   802         matcher = options.localeMatcher;
   804         // Step 1.c.
   805         if (matcher !== undefined) {
   806             matcher = ToString(matcher);
   807             if (matcher !== "lookup" && matcher !== "best fit")
   808                 ThrowError(JSMSG_INVALID_LOCALE_MATCHER, matcher);
   809         }
   810     }
   812     // Steps 2-3.
   813     var subset = (matcher === undefined || matcher === "best fit")
   814                  ? BestFitSupportedLocales(availableLocales, requestedLocales)
   815                  : LookupSupportedLocales(availableLocales, requestedLocales);
   817     // Step 4.
   818     for (var i = 0; i < subset.length; i++) {
   819         _DefineValueProperty(subset, i, subset[i],
   820                              ATTR_ENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE);
   821     }
   822     _DefineValueProperty(subset, "length", subset.length,
   823                          ATTR_NONENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE);
   825     // Step 5.
   826     return subset;
   827 }
   830 /**
   831  * Extracts a property value from the provided options object, converts it to
   832  * the required type, checks whether it is one of a list of allowed values,
   833  * and fills in a fallback value if necessary.
   834  *
   835  * Spec: ECMAScript Internationalization API Specification, 9.2.9.
   836  */
   837 function GetOption(options, property, type, values, fallback) {
   838     // Step 1.
   839     var value = options[property];
   841     // Step 2.
   842     if (value !== undefined) {
   843         // Steps 2.a-c.
   844         if (type === "boolean")
   845             value = ToBoolean(value);
   846         else if (type === "string")
   847             value = ToString(value);
   848         else
   849             assert(false, "GetOption");
   851         // Step 2.d.
   852         if (values !== undefined && callFunction(std_Array_indexOf, values, value) === -1)
   853             ThrowError(JSMSG_INVALID_OPTION_VALUE, property, value);
   855         // Step 2.e.
   856         return value;
   857     }
   859     // Step 3.
   860     return fallback;
   861 }
   863 /**
   864  * Extracts a property value from the provided options object, converts it to a
   865  * Number value, checks whether it is in the allowed range, and fills in a
   866  * fallback value if necessary.
   867  *
   868  * Spec: ECMAScript Internationalization API Specification, 9.2.10.
   869  */
   870 function GetNumberOption(options, property, minimum, maximum, fallback) {
   871     assert(typeof minimum === "number", "GetNumberOption");
   872     assert(typeof maximum === "number", "GetNumberOption");
   873     assert(fallback === undefined || (fallback >= minimum && fallback <= maximum), "GetNumberOption");
   875     // Step 1.
   876     var value = options[property];
   878     // Step 2.
   879     if (value !== undefined) {
   880         value = ToNumber(value);
   881         if (std_isNaN(value) || value < minimum || value > maximum)
   882             ThrowError(JSMSG_INVALID_DIGITS_VALUE, value);
   883         return std_Math_floor(value);
   884     }
   886     // Step 3.
   887     return fallback;
   888 }
   891 /********** Property access for Intl objects **********/
   894 /**
   895  * Set a normal public property p of o to value v, but use Object.defineProperty
   896  * to avoid interference from setters on Object.prototype.
   897  */
   898 function defineProperty(o, p, v) {
   899     _DefineValueProperty(o, p, v, ATTR_ENUMERABLE | ATTR_CONFIGURABLE | ATTR_WRITABLE);
   900 }
   903 /**
   904  * Weak map used to track the initialize-as-Intl status (and, if an object has
   905  * been so initialized, the Intl-specific internal properties) of all objects.
   906  * Presence of an object as a key within this map indicates that the object has
   907  * its [[initializedIntlObject]] internal property set to true.  The associated
   908  * value is an object whose structure is documented in |initializeIntlObject|
   909  * below.
   910  *
   911  * Ideally we'd be using private symbols for internal properties, but
   912  * SpiderMonkey doesn't have those yet.
   913  */
   914 var internalsMap = new WeakMap();
   917 /**
   918  * Set the [[initializedIntlObject]] internal property of |obj| to true.
   919  */
   920 function initializeIntlObject(obj) {
   921     assert(IsObject(obj), "Non-object passed to initializeIntlObject");
   923     // Intl-initialized objects are weird.  They have [[initializedIntlObject]]
   924     // set on them, but they don't *necessarily* have any other properties.
   926     var internals = std_Object_create(null);
   928     // The meaning of an internals object for an object |obj| is as follows.
   929     //
   930     // If the .type is "partial", |obj| has [[initializedIntlObject]] set but
   931     // nothing else.  No other property of |internals| can be used.  (This
   932     // occurs when InitializeCollator or similar marks an object as
   933     // [[initializedIntlObject]] but fails before marking it as the appropriate
   934     // more-specific type ["Collator", "DateTimeFormat", "NumberFormat"].)
   935     //
   936     // Otherwise, the .type indicates the type of Intl object that |obj| is:
   937     // "Collator", "DateTimeFormat", or "NumberFormat" (likely with more coming
   938     // in future Intl specs).  In these cases |obj| *conceptually* also has
   939     // [[initializedCollator]] or similar set, and all the other properties
   940     // implied by that.
   941     //
   942     // If |internals| doesn't have a "partial" .type, two additional properties
   943     // have meaning.  The .lazyData property stores information needed to
   944     // compute -- without observable side effects -- the actual internal Intl
   945     // properties of |obj|.  If it is non-null, then the actual internal
   946     // properties haven't been computed, and .lazyData must be processed by
   947     // |setInternalProperties| before internal Intl property values are
   948     // available.  If it is null, then the .internalProps property contains an
   949     // object whose properties are the internal Intl properties of |obj|.
   951     internals.type = "partial";
   952     internals.lazyData = null;
   953     internals.internalProps = null;
   955     callFunction(std_WeakMap_set, internalsMap, obj, internals);
   956     return internals;
   957 }
   960 /**
   961  * Mark |internals| as having the given type and lazy data.
   962  */
   963 function setLazyData(internals, type, lazyData)
   964 {
   965     assert(internals.type === "partial", "can't set lazy data for anything but a newborn");
   966     assert(type === "Collator" || type === "DateTimeFormat" || type == "NumberFormat", "bad type");
   967     assert(IsObject(lazyData), "non-object lazy data");
   969     // Set in reverse order so that the .type change is a barrier.
   970     internals.lazyData = lazyData;
   971     internals.type = type;
   972 }
   975 /**
   976  * Set the internal properties object for an |internals| object previously
   977  * associated with lazy data.
   978  */
   979 function setInternalProperties(internals, internalProps)
   980 {
   981     assert(internals.type !== "partial", "newborn internals can't have computed internals");
   982     assert(IsObject(internals.lazyData), "lazy data must exist already");
   983     assert(IsObject(internalProps), "internalProps argument should be an object");
   985     // Set in reverse order so that the .lazyData nulling is a barrier.
   986     internals.internalProps = internalProps;
   987     internals.lazyData = null;
   988 }
   991 /**
   992  * Get the existing internal properties out of a non-newborn |internals|, or
   993  * null if none have been computed.
   994  */
   995 function maybeInternalProperties(internals)
   996 {
   997     assert(IsObject(internals), "non-object passed to maybeInternalProperties");
   998     assert(internals.type !== "partial", "maybeInternalProperties must only be used on completely-initialized internals objects");
   999     var lazyData = internals.lazyData;
  1000     if (lazyData)
  1001         return null;
  1002     assert(IsObject(internals.internalProps), "missing lazy data and computed internals");
  1003     return internals.internalProps;
  1007 /**
  1008  * Return whether |obj| has an[[initializedIntlObject]] property set to true.
  1009  */
  1010 function isInitializedIntlObject(obj) {
  1011 #ifdef DEBUG
  1012     var internals = callFunction(std_WeakMap_get, internalsMap, obj);
  1013     if (IsObject(internals)) {
  1014         assert(callFunction(std_Object_hasOwnProperty, internals, "type"), "missing type");
  1015         var type = internals.type;
  1016         assert(type === "partial" || type === "Collator" || type === "DateTimeFormat" || type === "NumberFormat", "unexpected type");
  1017         assert(callFunction(std_Object_hasOwnProperty, internals, "lazyData"), "missing lazyData");
  1018         assert(callFunction(std_Object_hasOwnProperty, internals, "internalProps"), "missing internalProps");
  1019     } else {
  1020         assert(internals === undefined, "bad mapping for |obj|");
  1022 #endif
  1023     return callFunction(std_WeakMap_has, internalsMap, obj);
  1027 /**
  1028  * Check that |obj| meets the requirements for "this Collator object", "this
  1029  * NumberFormat object", or "this DateTimeFormat object" as used in the method
  1030  * with the given name.  Throw a TypeError if |obj| doesn't meet these
  1031  * requirements.  But if it does, return |obj|'s internals object (*not* the
  1032  * object holding its internal properties!), associated with it by
  1033  * |internalsMap|, with structure specified above.
  1035  * Spec: ECMAScript Internationalization API Specification, 10.3.
  1036  * Spec: ECMAScript Internationalization API Specification, 11.3.
  1037  * Spec: ECMAScript Internationalization API Specification, 12.3.
  1038  */
  1039 function getIntlObjectInternals(obj, className, methodName) {
  1040     assert(typeof className === "string", "bad className for getIntlObjectInternals");
  1042     var internals = callFunction(std_WeakMap_get, internalsMap, obj);
  1043     assert(internals === undefined || isInitializedIntlObject(obj), "bad mapping in internalsMap");
  1045     if (internals === undefined || internals.type !== className)
  1046         ThrowError(JSMSG_INTL_OBJECT_NOT_INITED, className, methodName, className);
  1048     return internals;
  1052 /**
  1053  * Get the internal properties of known-Intl object |obj|.  For use only by
  1054  * C++ code that knows what it's doing!
  1055  */
  1056 function getInternals(obj)
  1058     assert(isInitializedIntlObject(obj), "for use only on guaranteed Intl objects");
  1060     var internals = callFunction(std_WeakMap_get, internalsMap, obj);
  1062     assert(internals.type !== "partial", "must have been successfully initialized");
  1063     var lazyData = internals.lazyData;
  1064     if (!lazyData)
  1065         return internals.internalProps;
  1067     var internalProps;
  1068     var type = internals.type;
  1069     if (type === "Collator")
  1070         internalProps = resolveCollatorInternals(lazyData)
  1071     else if (type === "DateTimeFormat")
  1072         internalProps = resolveDateTimeFormatInternals(lazyData)
  1073     else
  1074         internalProps = resolveNumberFormatInternals(lazyData);
  1075     setInternalProperties(internals, internalProps);
  1076     return internalProps;
  1080 /********** Intl.Collator **********/
  1083 /**
  1084  * Mapping from Unicode extension keys for collation to options properties,
  1085  * their types and permissible values.
  1087  * Spec: ECMAScript Internationalization API Specification, 10.1.1.
  1088  */
  1089 var collatorKeyMappings = {
  1090     kn: {property: "numeric", type: "boolean"},
  1091     kf: {property: "caseFirst", type: "string", values: ["upper", "lower", "false"]}
  1092 };
  1095 /**
  1096  * Compute an internal properties object from |lazyCollatorData|.
  1097  */
  1098 function resolveCollatorInternals(lazyCollatorData)
  1100     assert(IsObject(lazyCollatorData), "lazy data not an object?");
  1102     var internalProps = std_Object_create(null);
  1104     // Step 7.
  1105     internalProps.usage = lazyCollatorData.usage;
  1107     // Step 8.
  1108     var Collator = collatorInternalProperties;
  1110     // Step 9.
  1111     var collatorIsSorting = lazyCollatorData.usage === "sort";
  1112     var localeData = collatorIsSorting
  1113                      ? Collator.sortLocaleData
  1114                      : Collator.searchLocaleData;
  1116     // Compute effective locale.
  1117     // Step 14.
  1118     var relevantExtensionKeys = Collator.relevantExtensionKeys;
  1120     // Step 15.
  1121     var r = ResolveLocale(Collator.availableLocales(),
  1122                           lazyCollatorData.requestedLocales,
  1123                           lazyCollatorData.opt,
  1124                           relevantExtensionKeys,
  1125                           localeData);
  1127     // Step 16.
  1128     internalProps.locale = r.locale;
  1130     // Steps 17-19.
  1131     var key, property, value, mapping;
  1132     var i = 0, len = relevantExtensionKeys.length;
  1133     while (i < len) {
  1134         // Step 19.a.
  1135         key = relevantExtensionKeys[i];
  1136         if (key === "co") {
  1137             // Step 19.b.
  1138             property = "collation";
  1139             value = r.co === null ? "default" : r.co;
  1140         } else {
  1141             // Step 19.c.
  1142             mapping = collatorKeyMappings[key];
  1143             property = mapping.property;
  1144             value = r[key];
  1145             if (mapping.type === "boolean")
  1146                 value = value === "true";
  1149         // Step 19.d.
  1150         internalProps[property] = value;
  1152         // Step 19.e.
  1153         i++;
  1156     // Compute remaining collation options.
  1157     // Steps 21-22.
  1158     var s = lazyCollatorData.rawSensitivity;
  1159     if (s === undefined) {
  1160         if (collatorIsSorting) {
  1161             // Step 21.a.
  1162             s = "variant";
  1163         } else {
  1164             // Step 21.b.
  1165             var dataLocale = r.dataLocale;
  1166             var dataLocaleData = localeData(dataLocale);
  1167             s = dataLocaleData.sensitivity;
  1170     internalProps.sensitivity = s;
  1172     // Step 24.
  1173     internalProps.ignorePunctuation = lazyCollatorData.ignorePunctuation;
  1175     // Step 25.
  1176     internalProps.boundFormat = undefined;
  1178     // The caller is responsible for associating |internalProps| with the right
  1179     // object using |setInternalProperties|.
  1180     return internalProps;
  1184 /**
  1185  * Returns an object containing the Collator internal properties of |obj|, or
  1186  * throws a TypeError if |obj| isn't Collator-initialized.
  1187  */
  1188 function getCollatorInternals(obj, methodName) {
  1189     var internals = getIntlObjectInternals(obj, "Collator", methodName);
  1190     assert(internals.type === "Collator", "bad type escaped getIntlObjectInternals");
  1192     // If internal properties have already been computed, use them.
  1193     var internalProps = maybeInternalProperties(internals);
  1194     if (internalProps)
  1195         return internalProps;
  1197     // Otherwise it's time to fully create them.
  1198     internalProps = resolveCollatorInternals(internals.lazyData);
  1199     setInternalProperties(internals, internalProps);
  1200     return internalProps;
  1204 /**
  1205  * Initializes an object as a Collator.
  1207  * This method is complicated a moderate bit by its implementing initialization
  1208  * as a *lazy* concept.  Everything that must happen now, does -- but we defer
  1209  * all the work we can until the object is actually used as a Collator.  This
  1210  * later work occurs in |resolveCollatorInternals|; steps not noted here occur
  1211  * there.
  1213  * Spec: ECMAScript Internationalization API Specification, 10.1.1.
  1214  */
  1215 function InitializeCollator(collator, locales, options) {
  1216     assert(IsObject(collator), "InitializeCollator");
  1218     // Step 1.
  1219     if (isInitializedIntlObject(collator))
  1220         ThrowError(JSMSG_INTL_OBJECT_REINITED);
  1222     // Step 2.
  1223     var internals = initializeIntlObject(collator);
  1225     // Lazy Collator data has the following structure:
  1226     //
  1227     //   {
  1228     //     requestedLocales: List of locales,
  1229     //     usage: "sort" / "search",
  1230     //     opt: // opt object computed in InitializeCollator
  1231     //       {
  1232     //         localeMatcher: "lookup" / "best fit",
  1233     //         kn: true / false / undefined,
  1234     //         kf: "upper" / "lower" / "false" / undefined
  1235     //       }
  1236     //     rawSensitivity: "base" / "accent" / "case" / "variant" / undefined,
  1237     //     ignorePunctuation: true / false
  1238     //   }
  1239     //
  1240     // Note that lazy data is only installed as a final step of initialization,
  1241     // so every Collator lazy data object has *all* these properties, never a
  1242     // subset of them.
  1243     var lazyCollatorData = std_Object_create(null);
  1245     // Step 3.
  1246     var requestedLocales = CanonicalizeLocaleList(locales);
  1247     lazyCollatorData.requestedLocales = requestedLocales;
  1249     // Steps 4-5.
  1250     //
  1251     // If we ever need more speed here at startup, we should try to detect the
  1252     // case where |options === undefined| and Object.prototype hasn't been
  1253     // mucked with.  (|options| is fully consumed in this method, so it's not a
  1254     // concern that Object.prototype might be touched between now and when
  1255     // |resolveCollatorInternals| is called.)  For now, just keep it simple.
  1256     if (options === undefined)
  1257         options = {};
  1258     else
  1259         options = ToObject(options);
  1261     // Compute options that impact interpretation of locale.
  1262     // Step 6.
  1263     var u = GetOption(options, "usage", "string", ["sort", "search"], "sort");
  1264     lazyCollatorData.usage = u;
  1266     // Step 10.
  1267     var opt = new Record();
  1268     lazyCollatorData.opt = opt;
  1270     // Steps 11-12.
  1271     var matcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit");
  1272     opt.localeMatcher = matcher;
  1274     // Step 13, unrolled.
  1275     var numericValue = GetOption(options, "numeric", "boolean", undefined, undefined);
  1276     if (numericValue !== undefined)
  1277         numericValue = callFunction(std_Boolean_toString, numericValue);
  1278     opt.kn = numericValue;
  1280     var caseFirstValue = GetOption(options, "caseFirst", "string", ["upper", "lower", "false"], undefined);
  1281     opt.kf = caseFirstValue;
  1283     // Compute remaining collation options.
  1284     // Step 20.
  1285     var s = GetOption(options, "sensitivity", "string",
  1286                       ["base", "accent", "case", "variant"], undefined);
  1287     lazyCollatorData.rawSensitivity = s;
  1289     // Step 23.
  1290     var ip = GetOption(options, "ignorePunctuation", "boolean", undefined, false);
  1291     lazyCollatorData.ignorePunctuation = ip;
  1293     // Step 26.
  1294     //
  1295     // We've done everything that must be done now: mark the lazy data as fully
  1296     // computed and install it.
  1297     setLazyData(internals, "Collator", lazyCollatorData);
  1301 /**
  1302  * Returns the subset of the given locale list for which this locale list has a
  1303  * matching (possibly fallback) locale. Locales appear in the same order in the
  1304  * returned list as in the input list.
  1306  * Spec: ECMAScript Internationalization API Specification, 10.2.2.
  1307  */
  1308 function Intl_Collator_supportedLocalesOf(locales /*, options*/) {
  1309     var options = arguments.length > 1 ? arguments[1] : undefined;
  1311     var availableLocales = collatorInternalProperties.availableLocales();
  1312     var requestedLocales = CanonicalizeLocaleList(locales);
  1313     return SupportedLocales(availableLocales, requestedLocales, options);
  1317 /**
  1318  * Collator internal properties.
  1320  * Spec: ECMAScript Internationalization API Specification, 9.1 and 10.2.3.
  1321  */
  1322 var collatorInternalProperties = {
  1323     sortLocaleData: collatorSortLocaleData,
  1324     searchLocaleData: collatorSearchLocaleData,
  1325     _availableLocales: null,
  1326     availableLocales: function()
  1328         var locales = this._availableLocales;
  1329         if (locales)
  1330             return locales;
  1331         return (this._availableLocales =
  1332           addOldStyleLanguageTags(intl_Collator_availableLocales()));
  1333     },
  1334     relevantExtensionKeys: ["co", "kn"]
  1335 };
  1338 function collatorSortLocaleData(locale) {
  1339     var collations = intl_availableCollations(locale);
  1340     callFunction(std_Array_unshift, collations, null);
  1341     return {
  1342         co: collations,
  1343         kn: ["false", "true"]
  1344     };
  1348 function collatorSearchLocaleData(locale) {
  1349     return {
  1350         co: [null],
  1351         kn: ["false", "true"],
  1352         // In theory the default sensitivity is locale dependent;
  1353         // in reality the CLDR/ICU default strength is always tertiary.
  1354         sensitivity: "variant"
  1355     };
  1359 /**
  1360  * Function to be bound and returned by Intl.Collator.prototype.format.
  1362  * Spec: ECMAScript Internationalization API Specification, 12.3.2.
  1363  */
  1364 function collatorCompareToBind(x, y) {
  1365     // Steps 1.a.i-ii implemented by ECMAScript declaration binding instantiation,
  1366     // ES5.1 10.5, step 4.d.ii.
  1368     // Step 1.a.iii-v.
  1369     var X = ToString(x);
  1370     var Y = ToString(y);
  1371     return intl_CompareStrings(this, X, Y);
  1375 /**
  1376  * Returns a function bound to this Collator that compares x (converted to a
  1377  * String value) and y (converted to a String value),
  1378  * and returns a number less than 0 if x < y, 0 if x = y, or a number greater
  1379  * than 0 if x > y according to the sort order for the locale and collation
  1380  * options of this Collator object.
  1382  * Spec: ECMAScript Internationalization API Specification, 10.3.2.
  1383  */
  1384 function Intl_Collator_compare_get() {
  1385     // Check "this Collator object" per introduction of section 10.3.
  1386     var internals = getCollatorInternals(this, "compare");
  1388     // Step 1.
  1389     if (internals.boundCompare === undefined) {
  1390         // Step 1.a.
  1391         var F = collatorCompareToBind;
  1393         // Step 1.b-d.
  1394         var bc = callFunction(std_Function_bind, F, this);
  1395         internals.boundCompare = bc;
  1398     // Step 2.
  1399     return internals.boundCompare;
  1403 /**
  1404  * Returns the resolved options for a Collator object.
  1406  * Spec: ECMAScript Internationalization API Specification, 10.3.3 and 10.4.
  1407  */
  1408 function Intl_Collator_resolvedOptions() {
  1409     // Check "this Collator object" per introduction of section 10.3.
  1410     var internals = getCollatorInternals(this, "resolvedOptions");
  1412     var result = {
  1413         locale: internals.locale,
  1414         usage: internals.usage,
  1415         sensitivity: internals.sensitivity,
  1416         ignorePunctuation: internals.ignorePunctuation
  1417     };
  1419     var relevantExtensionKeys = collatorInternalProperties.relevantExtensionKeys;
  1420     for (var i = 0; i < relevantExtensionKeys.length; i++) {
  1421         var key = relevantExtensionKeys[i];
  1422         var property = (key === "co") ? "collation" : collatorKeyMappings[key].property;
  1423         defineProperty(result, property, internals[property]);
  1425     return result;
  1429 /********** Intl.NumberFormat **********/
  1432 /**
  1433  * NumberFormat internal properties.
  1435  * Spec: ECMAScript Internationalization API Specification, 9.1 and 11.2.3.
  1436  */
  1437 var numberFormatInternalProperties = {
  1438     localeData: numberFormatLocaleData,
  1439     _availableLocales: null,
  1440     availableLocales: function()
  1442         var locales = this._availableLocales;
  1443         if (locales)
  1444             return locales;
  1445         return (this._availableLocales =
  1446           addOldStyleLanguageTags(intl_NumberFormat_availableLocales()));
  1447     },
  1448     relevantExtensionKeys: ["nu"]
  1449 };
  1452 /**
  1453  * Compute an internal properties object from |lazyNumberFormatData|.
  1454  */
  1455 function resolveNumberFormatInternals(lazyNumberFormatData) {
  1456     assert(IsObject(lazyNumberFormatData), "lazy data not an object?");
  1458     var internalProps = std_Object_create(null);
  1460     // Step 3.
  1461     var requestedLocales = lazyNumberFormatData.requestedLocales;
  1463     // Compute options that impact interpretation of locale.
  1464     // Step 6.
  1465     var opt = lazyNumberFormatData.opt;
  1467     // Compute effective locale.
  1468     // Step 9.
  1469     var NumberFormat = numberFormatInternalProperties;
  1471     // Step 10.
  1472     var localeData = NumberFormat.localeData;
  1474     // Step 11.
  1475     var r = ResolveLocale(NumberFormat.availableLocales(),
  1476                           lazyNumberFormatData.requestedLocales,
  1477                           lazyNumberFormatData.opt,
  1478                           NumberFormat.relevantExtensionKeys,
  1479                           localeData);
  1481     // Steps 12-13.  (Step 14 is not relevant to our implementation.)
  1482     internalProps.locale = r.locale;
  1483     internalProps.numberingSystem = r.nu;
  1485     // Compute formatting options.
  1486     // Step 16.
  1487     var s = lazyNumberFormatData.style;
  1488     internalProps.style = s;
  1490     // Steps 20, 22.
  1491     if (s === "currency") {
  1492         internalProps.currency = lazyNumberFormatData.currency;
  1493         internalProps.currencyDisplay = lazyNumberFormatData.currencyDisplay;
  1496     // Step 24.
  1497     internalProps.minimumIntegerDigits = lazyNumberFormatData.minimumIntegerDigits;
  1499     // Steps 27.
  1500     internalProps.minimumFractionDigits = lazyNumberFormatData.minimumFractionDigits;
  1502     // Step 30.
  1503     internalProps.maximumFractionDigits = lazyNumberFormatData.maximumFractionDigits;
  1505     // Step 33.
  1506     if ("minimumSignificantDigits" in lazyNumberFormatData) {
  1507         // Note: Intl.NumberFormat.prototype.resolvedOptions() exposes the
  1508         // actual presence (versus undefined-ness) of these properties.
  1509         assert("maximumSignificantDigits" in lazyNumberFormatData, "min/max sig digits mismatch");
  1510         internalProps.minimumSignificantDigits = lazyNumberFormatData.minimumSignificantDigits;
  1511         internalProps.maximumSignificantDigits = lazyNumberFormatData.maximumSignificantDigits;
  1514     // Step 35.
  1515     internalProps.useGrouping = lazyNumberFormatData.useGrouping;
  1517     // Step 42.
  1518     internalProps.boundFormat = undefined;
  1520     // The caller is responsible for associating |internalProps| with the right
  1521     // object using |setInternalProperties|.
  1522     return internalProps;
  1526 /**
  1527  * Returns an object containing the NumberFormat internal properties of |obj|,
  1528  * or throws a TypeError if |obj| isn't NumberFormat-initialized.
  1529  */
  1530 function getNumberFormatInternals(obj, methodName) {
  1531     var internals = getIntlObjectInternals(obj, "NumberFormat", methodName);
  1532     assert(internals.type === "NumberFormat", "bad type escaped getIntlObjectInternals");
  1534     // If internal properties have already been computed, use them.
  1535     var internalProps = maybeInternalProperties(internals);
  1536     if (internalProps)
  1537         return internalProps;
  1539     // Otherwise it's time to fully create them.
  1540     internalProps = resolveNumberFormatInternals(internals.lazyData);
  1541     setInternalProperties(internals, internalProps);
  1542     return internalProps;
  1546 /**
  1547  * Initializes an object as a NumberFormat.
  1549  * This method is complicated a moderate bit by its implementing initialization
  1550  * as a *lazy* concept.  Everything that must happen now, does -- but we defer
  1551  * all the work we can until the object is actually used as a NumberFormat.
  1552  * This later work occurs in |resolveNumberFormatInternals|; steps not noted
  1553  * here occur there.
  1555  * Spec: ECMAScript Internationalization API Specification, 11.1.1.
  1556  */
  1557 function InitializeNumberFormat(numberFormat, locales, options) {
  1558     assert(IsObject(numberFormat), "InitializeNumberFormat");
  1560     // Step 1.
  1561     if (isInitializedIntlObject(numberFormat))
  1562         ThrowError(JSMSG_INTL_OBJECT_REINITED);
  1564     // Step 2.
  1565     var internals = initializeIntlObject(numberFormat);
  1567     // Lazy NumberFormat data has the following structure:
  1568     //
  1569     //   {
  1570     //     requestedLocales: List of locales,
  1571     //     style: "decimal" / "percent" / "currency",
  1572     //
  1573     //     // fields present only if style === "currency":
  1574     //     currency: a well-formed currency code (IsWellFormedCurrencyCode),
  1575     //     currencyDisplay: "code" / "symbol" / "name",
  1576     //
  1577     //     opt: // opt object computed in InitializeNumberFormat
  1578     //       {
  1579     //         localeMatcher: "lookup" / "best fit",
  1580     //       }
  1581     //
  1582     //     minimumIntegerDigits: integer ∈ [1, 21],
  1583     //     minimumFractionDigits: integer ∈ [0, 20],
  1584     //     maximumFractionDigits: integer ∈ [0, 20],
  1585     //
  1586     //     // optional
  1587     //     minimumSignificantDigits: integer ∈ [1, 21],
  1588     //     maximumSignificantDigits: integer ∈ [1, 21],
  1589     //
  1590     //     useGrouping: true / false,
  1591     //   }
  1592     //
  1593     // Note that lazy data is only installed as a final step of initialization,
  1594     // so every Collator lazy data object has *all* these properties, never a
  1595     // subset of them.
  1596     var lazyNumberFormatData = std_Object_create(null);
  1598     // Step 3.
  1599     var requestedLocales = CanonicalizeLocaleList(locales);
  1600     lazyNumberFormatData.requestedLocales = requestedLocales;
  1602     // Steps 4-5.
  1603     //
  1604     // If we ever need more speed here at startup, we should try to detect the
  1605     // case where |options === undefined| and Object.prototype hasn't been
  1606     // mucked with.  (|options| is fully consumed in this method, so it's not a
  1607     // concern that Object.prototype might be touched between now and when
  1608     // |resolveNumberFormatInternals| is called.)  For now just keep it simple.
  1609     if (options === undefined)
  1610         options = {};
  1611     else
  1612         options = ToObject(options);
  1614     // Compute options that impact interpretation of locale.
  1615     // Step 6.
  1616     var opt = new Record();
  1617     lazyNumberFormatData.opt = opt;
  1619     // Steps 7-8.
  1620     var matcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit");
  1621     opt.localeMatcher = matcher;
  1623     // Compute formatting options.
  1624     // Step 15.
  1625     var s = GetOption(options, "style", "string", ["decimal", "percent", "currency"], "decimal");
  1626     lazyNumberFormatData.style = s;
  1628     // Steps 17-20.
  1629     var c = GetOption(options, "currency", "string", undefined, undefined);
  1630     if (c !== undefined && !IsWellFormedCurrencyCode(c))
  1631         ThrowError(JSMSG_INVALID_CURRENCY_CODE, c);
  1632     var cDigits;
  1633     if (s === "currency") {
  1634         if (c === undefined)
  1635             ThrowError(JSMSG_UNDEFINED_CURRENCY);
  1637         // Steps 20.a-c.
  1638         c = toASCIIUpperCase(c);
  1639         lazyNumberFormatData.currency = c;
  1640         cDigits = CurrencyDigits(c);
  1643     // Step 21.
  1644     var cd = GetOption(options, "currencyDisplay", "string", ["code", "symbol", "name"], "symbol");
  1645     if (s === "currency")
  1646         lazyNumberFormatData.currencyDisplay = cd;
  1648     // Step 23.
  1649     var mnid = GetNumberOption(options, "minimumIntegerDigits", 1, 21, 1);
  1650     lazyNumberFormatData.minimumIntegerDigits = mnid;
  1652     // Steps 25-26.
  1653     var mnfdDefault = (s === "currency") ? cDigits : 0;
  1654     var mnfd = GetNumberOption(options, "minimumFractionDigits", 0, 20, mnfdDefault);
  1655     lazyNumberFormatData.minimumFractionDigits = mnfd;
  1657     // Steps 28-29.
  1658     var mxfdDefault;
  1659     if (s === "currency")
  1660         mxfdDefault = std_Math_max(mnfd, cDigits);
  1661     else if (s === "percent")
  1662         mxfdDefault = std_Math_max(mnfd, 0);
  1663     else
  1664         mxfdDefault = std_Math_max(mnfd, 3);
  1665     var mxfd = GetNumberOption(options, "maximumFractionDigits", mnfd, 20, mxfdDefault);
  1666     lazyNumberFormatData.maximumFractionDigits = mxfd;
  1668     // Steps 31-32.
  1669     var mnsd = options.minimumSignificantDigits;
  1670     var mxsd = options.maximumSignificantDigits;
  1672     // Step 33.
  1673     if (mnsd !== undefined || mxsd !== undefined) {
  1674         mnsd = GetNumberOption(options, "minimumSignificantDigits", 1, 21, 1);
  1675         mxsd = GetNumberOption(options, "maximumSignificantDigits", mnsd, 21, 21);
  1676         lazyNumberFormatData.minimumSignificantDigits = mnsd;
  1677         lazyNumberFormatData.maximumSignificantDigits = mxsd;
  1680     // Step 34.
  1681     var g = GetOption(options, "useGrouping", "boolean", undefined, true);
  1682     lazyNumberFormatData.useGrouping = g;
  1684     // Step 43.
  1685     //
  1686     // We've done everything that must be done now: mark the lazy data as fully
  1687     // computed and install it.
  1688     setLazyData(internals, "NumberFormat", lazyNumberFormatData);
  1692 /**
  1693  * Mapping from currency codes to the number of decimal digits used for them.
  1694  * Default is 2 digits.
  1696  * Spec: ISO 4217 Currency and Funds Code List.
  1697  * http://www.currency-iso.org/en/home/tables/table-a1.html
  1698  */
  1699 var currencyDigits = {
  1700     BHD: 3,
  1701     BIF: 0,
  1702     BYR: 0,
  1703     CLF: 0,
  1704     CLP: 0,
  1705     DJF: 0,
  1706     IQD: 3,
  1707     GNF: 0,
  1708     ISK: 0,
  1709     JOD: 3,
  1710     JPY: 0,
  1711     KMF: 0,
  1712     KRW: 0,
  1713     KWD: 3,
  1714     LYD: 3,
  1715     OMR: 3,
  1716     PYG: 0,
  1717     RWF: 0,
  1718     TND: 3,
  1719     UGX: 0,
  1720     UYI: 0,
  1721     VND: 0,
  1722     VUV: 0,
  1723     XAF: 0,
  1724     XOF: 0,
  1725     XPF: 0
  1726 };
  1729 /**
  1730  * Returns the number of decimal digits to be used for the given currency.
  1732  * Spec: ECMAScript Internationalization API Specification, 11.1.1.
  1733  */
  1734 function CurrencyDigits(currency) {
  1735     assert(typeof currency === "string", "CurrencyDigits");
  1736     assert(regexp_test_no_statics(/^[A-Z]{3}$/, currency), "CurrencyDigits");
  1738     if (callFunction(std_Object_hasOwnProperty, currencyDigits, currency))
  1739         return currencyDigits[currency];
  1740     return 2;
  1744 /**
  1745  * Returns the subset of the given locale list for which this locale list has a
  1746  * matching (possibly fallback) locale. Locales appear in the same order in the
  1747  * returned list as in the input list.
  1749  * Spec: ECMAScript Internationalization API Specification, 11.2.2.
  1750  */
  1751 function Intl_NumberFormat_supportedLocalesOf(locales /*, options*/) {
  1752     var options = arguments.length > 1 ? arguments[1] : undefined;
  1754     var availableLocales = numberFormatInternalProperties.availableLocales();
  1755     var requestedLocales = CanonicalizeLocaleList(locales);
  1756     return SupportedLocales(availableLocales, requestedLocales, options);
  1760 function getNumberingSystems(locale) {
  1761     // ICU doesn't have an API to determine the set of numbering systems
  1762     // supported for a locale; it generally pretends that any numbering system
  1763     // can be used with any locale. Supporting a decimal numbering system
  1764     // (where only the digits are replaced) is easy, so we offer them all here.
  1765     // Algorithmic numbering systems are typically tied to one locale, so for
  1766     // lack of information we don't offer them. To increase chances that
  1767     // other software will process output correctly, we further restrict to
  1768     // those decimal numbering systems explicitly listed in table 2 of
  1769     // the ECMAScript Internationalization API Specification, 11.3.2, which
  1770     // in turn are those with full specifications in version 21 of Unicode
  1771     // Technical Standard #35 using digits that were defined in Unicode 5.0,
  1772     // the Unicode version supported in Windows Vista.
  1773     // The one thing we can find out from ICU is the default numbering system
  1774     // for a locale.
  1775     var defaultNumberingSystem = intl_numberingSystem(locale);
  1776     return [
  1777         defaultNumberingSystem,
  1778         "arab", "arabext", "bali", "beng", "deva",
  1779         "fullwide", "gujr", "guru", "hanidec", "khmr",
  1780         "knda", "laoo", "latn", "limb", "mlym",
  1781         "mong", "mymr", "orya", "tamldec", "telu",
  1782         "thai", "tibt"
  1783     ];
  1787 function numberFormatLocaleData(locale) {
  1788     return {
  1789         nu: getNumberingSystems(locale)
  1790     };
  1794 /**
  1795  * Function to be bound and returned by Intl.NumberFormat.prototype.format.
  1797  * Spec: ECMAScript Internationalization API Specification, 11.3.2.
  1798  */
  1799 function numberFormatFormatToBind(value) {
  1800     // Steps 1.a.i implemented by ECMAScript declaration binding instantiation,
  1801     // ES5.1 10.5, step 4.d.ii.
  1803     // Step 1.a.ii-iii.
  1804     var x = ToNumber(value);
  1805     return intl_FormatNumber(this, x);
  1809 /**
  1810  * Returns a function bound to this NumberFormat that returns a String value
  1811  * representing the result of calling ToNumber(value) according to the
  1812  * effective locale and the formatting options of this NumberFormat.
  1814  * Spec: ECMAScript Internationalization API Specification, 11.3.2.
  1815  */
  1816 function Intl_NumberFormat_format_get() {
  1817     // Check "this NumberFormat object" per introduction of section 11.3.
  1818     var internals = getNumberFormatInternals(this, "format");
  1820     // Step 1.
  1821     if (internals.boundFormat === undefined) {
  1822         // Step 1.a.
  1823         var F = numberFormatFormatToBind;
  1825         // Step 1.b-d.
  1826         var bf = callFunction(std_Function_bind, F, this);
  1827         internals.boundFormat = bf;
  1829     // Step 2.
  1830     return internals.boundFormat;
  1834 /**
  1835  * Returns the resolved options for a NumberFormat object.
  1837  * Spec: ECMAScript Internationalization API Specification, 11.3.3 and 11.4.
  1838  */
  1839 function Intl_NumberFormat_resolvedOptions() {
  1840     // Check "this NumberFormat object" per introduction of section 11.3.
  1841     var internals = getNumberFormatInternals(this, "resolvedOptions");
  1843     var result = {
  1844         locale: internals.locale,
  1845         numberingSystem: internals.numberingSystem,
  1846         style: internals.style,
  1847         minimumIntegerDigits: internals.minimumIntegerDigits,
  1848         minimumFractionDigits: internals.minimumFractionDigits,
  1849         maximumFractionDigits: internals.maximumFractionDigits,
  1850         useGrouping: internals.useGrouping
  1851     };
  1852     var optionalProperties = [
  1853         "currency",
  1854         "currencyDisplay",
  1855         "minimumSignificantDigits",
  1856         "maximumSignificantDigits"
  1857     ];
  1858     for (var i = 0; i < optionalProperties.length; i++) {
  1859         var p = optionalProperties[i];
  1860         if (callFunction(std_Object_hasOwnProperty, internals, p))
  1861             defineProperty(result, p, internals[p]);
  1863     return result;
  1867 /********** Intl.DateTimeFormat **********/
  1870 /**
  1871  * Compute an internal properties object from |lazyDateTimeFormatData|.
  1872  */
  1873 function resolveDateTimeFormatInternals(lazyDateTimeFormatData) {
  1874     assert(IsObject(lazyDateTimeFormatData), "lazy data not an object?");
  1876     // Lazy DateTimeFormat data has the following structure:
  1877     //
  1878     //   {
  1879     //     requestedLocales: List of locales,
  1880     //
  1881     //     localeOpt: // *first* opt computed in InitializeDateTimeFormat
  1882     //       {
  1883     //         localeMatcher: "lookup" / "best fit",
  1884     //
  1885     //         hour12: true / false,  // optional
  1886     //       }
  1887     //
  1888     //     timeZone: undefined / "UTC",
  1889     //
  1890     //     formatOpt: // *second* opt computed in InitializeDateTimeFormat
  1891     //       {
  1892     //         // all the properties/values listed in Table 3
  1893     //         // (weekday, era, year, month, day, &c.)
  1894     //       }
  1895     //
  1896     //     formatMatcher: "basic" / "best fit",
  1897     //   }
  1898     //
  1899     // Note that lazy data is only installed as a final step of initialization,
  1900     // so every DateTimeFormat lazy data object has *all* these properties,
  1901     // never a subset of them.
  1903     var internalProps = std_Object_create(null);
  1905     // Compute effective locale.
  1906     // Step 8.
  1907     var DateTimeFormat = dateTimeFormatInternalProperties;
  1909     // Step 9.
  1910     var localeData = DateTimeFormat.localeData;
  1912     // Step 10.
  1913     var r = ResolveLocale(DateTimeFormat.availableLocales(),
  1914                           lazyDateTimeFormatData.requestedLocales,
  1915                           lazyDateTimeFormatData.localeOpt,
  1916                           DateTimeFormat.relevantExtensionKeys,
  1917                           localeData);
  1919     // Steps 11-13.
  1920     internalProps.locale = r.locale;
  1921     internalProps.calendar = r.ca;
  1922     internalProps.numberingSystem = r.nu;
  1924     // Compute formatting options.
  1925     // Step 14.
  1926     var dataLocale = r.dataLocale;
  1928     // Steps 15-17.
  1929     internalProps.timeZone = lazyDateTimeFormatData.timeZone;
  1931     // Step 18.
  1932     var formatOpt = lazyDateTimeFormatData.formatOpt;
  1934     // Steps 27-28, more or less - see comment after this function.
  1935     var pattern = toBestICUPattern(dataLocale, formatOpt);
  1937     // Step 29.
  1938     internalProps.pattern = pattern;
  1940     // Step 30.
  1941     internalProps.boundFormat = undefined;
  1943     // The caller is responsible for associating |internalProps| with the right
  1944     // object using |setInternalProperties|.
  1945     return internalProps;
  1949 /**
  1950  * Returns an object containing the DateTimeFormat internal properties of |obj|,
  1951  * or throws a TypeError if |obj| isn't DateTimeFormat-initialized.
  1952  */
  1953 function getDateTimeFormatInternals(obj, methodName) {
  1954     var internals = getIntlObjectInternals(obj, "DateTimeFormat", methodName);
  1955     assert(internals.type === "DateTimeFormat", "bad type escaped getIntlObjectInternals");
  1957     // If internal properties have already been computed, use them.
  1958     var internalProps = maybeInternalProperties(internals);
  1959     if (internalProps)
  1960         return internalProps;
  1962     // Otherwise it's time to fully create them.
  1963     internalProps = resolveDateTimeFormatInternals(internals.lazyData);
  1964     setInternalProperties(internals, internalProps);
  1965     return internalProps;
  1968 /**
  1969  * Components of date and time formats and their values.
  1971  * Spec: ECMAScript Internationalization API Specification, 12.1.1.
  1972  */
  1973 var dateTimeComponentValues = {
  1974     weekday: ["narrow", "short", "long"],
  1975     era: ["narrow", "short", "long"],
  1976     year: ["2-digit", "numeric"],
  1977     month: ["2-digit", "numeric", "narrow", "short", "long"],
  1978     day: ["2-digit", "numeric"],
  1979     hour: ["2-digit", "numeric"],
  1980     minute: ["2-digit", "numeric"],
  1981     second: ["2-digit", "numeric"],
  1982     timeZoneName: ["short", "long"]
  1983 };
  1986 var dateTimeComponents = std_Object_getOwnPropertyNames(dateTimeComponentValues);
  1989 /**
  1990  * Initializes an object as a DateTimeFormat.
  1992  * This method is complicated a moderate bit by its implementing initialization
  1993  * as a *lazy* concept.  Everything that must happen now, does -- but we defer
  1994  * all the work we can until the object is actually used as a DateTimeFormat.
  1995  * This later work occurs in |resolveDateTimeFormatInternals|; steps not noted
  1996  * here occur there.
  1998  * Spec: ECMAScript Internationalization API Specification, 12.1.1.
  1999  */
  2000 function InitializeDateTimeFormat(dateTimeFormat, locales, options) {
  2001     assert(IsObject(dateTimeFormat), "InitializeDateTimeFormat");
  2003     // Step 1.
  2004     if (isInitializedIntlObject(dateTimeFormat))
  2005         ThrowError(JSMSG_INTL_OBJECT_REINITED);
  2007     // Step 2.
  2008     var internals = initializeIntlObject(dateTimeFormat);
  2010     // Lazy DateTimeFormat data has the following structure:
  2011     //
  2012     //   {
  2013     //     requestedLocales: List of locales,
  2014     //
  2015     //     localeOpt: // *first* opt computed in InitializeDateTimeFormat
  2016     //       {
  2017     //         localeMatcher: "lookup" / "best fit",
  2018     //       }
  2019     //
  2020     //     timeZone: undefined / "UTC",
  2021     //
  2022     //     formatOpt: // *second* opt computed in InitializeDateTimeFormat
  2023     //       {
  2024     //         // all the properties/values listed in Table 3
  2025     //         // (weekday, era, year, month, day, &c.)
  2026     //
  2027     //         hour12: true / false  // optional
  2028     //       }
  2029     //
  2030     //     formatMatcher: "basic" / "best fit",
  2031     //   }
  2032     //
  2033     // Note that lazy data is only installed as a final step of initialization,
  2034     // so every DateTimeFormat lazy data object has *all* these properties,
  2035     // never a subset of them.
  2036     var lazyDateTimeFormatData = std_Object_create(null);
  2038     // Step 3.
  2039     var requestedLocales = CanonicalizeLocaleList(locales);
  2040     lazyDateTimeFormatData.requestedLocales = requestedLocales;
  2042     // Step 4.
  2043     options = ToDateTimeOptions(options, "any", "date");
  2045     // Compute options that impact interpretation of locale.
  2046     // Step 5.
  2047     var localeOpt = new Record();
  2048     lazyDateTimeFormatData.localeOpt = localeOpt;
  2050     // Steps 6-7.
  2051     var localeMatcher =
  2052         GetOption(options, "localeMatcher", "string", ["lookup", "best fit"],
  2053                   "best fit");
  2054     localeOpt.localeMatcher = localeMatcher;
  2056     // Steps 15-17.
  2057     var tz = options.timeZone;
  2058     if (tz !== undefined) {
  2059         tz = toASCIIUpperCase(ToString(tz));
  2060         if (tz !== "UTC")
  2061             ThrowError(JSMSG_INVALID_TIME_ZONE, tz);
  2063     lazyDateTimeFormatData.timeZone = tz;
  2065     // Step 18.
  2066     var formatOpt = new Record();
  2067     lazyDateTimeFormatData.formatOpt = formatOpt;
  2069     // Step 19.
  2070     var i, prop;
  2071     for (i = 0; i < dateTimeComponents.length; i++) {
  2072         prop = dateTimeComponents[i];
  2073         var value = GetOption(options, prop, "string", dateTimeComponentValues[prop], undefined);
  2074         formatOpt[prop] = value;
  2077     // Steps 20-21 provided by ICU - see comment after this function.
  2079     // Step 22.
  2080     //
  2081     // For some reason (ICU not exposing enough interface?) we drop the
  2082     // requested format matcher on the floor after this.  In any case, even if
  2083     // doing so is justified, we have to do this work here in case it triggers
  2084     // getters or similar.
  2085     var formatMatcher =
  2086         GetOption(options, "formatMatcher", "string", ["basic", "best fit"],
  2087                   "best fit");
  2089     // Steps 23-25 provided by ICU, more or less - see comment after this function.
  2091     // Step 26.
  2092     var hr12  = GetOption(options, "hour12", "boolean", undefined, undefined);
  2094     // Pass hr12 on to ICU.
  2095     if (hr12 !== undefined)
  2096         formatOpt.hour12 = hr12;
  2098     // Step 31.
  2099     //
  2100     // We've done everything that must be done now: mark the lazy data as fully
  2101     // computed and install it.
  2102     setLazyData(internals, "DateTimeFormat", lazyDateTimeFormatData);
  2106 // Intl.DateTimeFormat and ICU skeletons and patterns
  2107 // ==================================================
  2108 //
  2109 // Different locales have different ways to display dates using the same
  2110 // basic components. For example, en-US might use "Sept. 24, 2012" while
  2111 // fr-FR might use "24 Sept. 2012". The intent of Intl.DateTimeFormat is to
  2112 // permit production of a format for the locale that best matches the
  2113 // set of date-time components and their desired representation as specified
  2114 // by the API client.
  2115 //
  2116 // ICU supports specification of date and time formats in three ways:
  2117 //
  2118 // 1) A style is just one of the identifiers FULL, LONG, MEDIUM, or SHORT.
  2119 //    The date-time components included in each style and their representation
  2120 //    are defined by ICU using CLDR locale data (CLDR is the Unicode
  2121 //    Consortium's Common Locale Data Repository).
  2122 //
  2123 // 2) A skeleton is a string specifying which date-time components to include,
  2124 //    and which representations to use for them. For example, "yyyyMMMMdd"
  2125 //    specifies a year with at least four digits, a full month name, and a
  2126 //    two-digit day. It does not specify in which order the components appear,
  2127 //    how they are separated, the localized strings for textual components
  2128 //    (such as weekday or month), whether the month is in format or
  2129 //    stand-alone form¹, or the numbering system used for numeric components.
  2130 //    All that information is filled in by ICU using CLDR locale data.
  2131 //    ¹ The format form is the one used in formatted strings that include a
  2132 //    day; the stand-alone form is used when not including days, e.g., in
  2133 //    calendar headers. The two forms differ at least in some Slavic languages,
  2134 //    e.g. Russian: "22 марта 2013 г." vs. "Март 2013".
  2135 //
  2136 // 3) A pattern is a string specifying which date-time components to include,
  2137 //    in which order, with which separators, in which grammatical case. For
  2138 //    example, "EEEE, d MMMM y" specifies the full localized weekday name,
  2139 //    followed by comma and space, followed by the day, followed by space,
  2140 //    followed by the full month name in format form, followed by space,
  2141 //    followed by the full year. It
  2142 //    still does not specify localized strings for textual components and the
  2143 //    numbering system - these are determined by ICU using CLDR locale data or
  2144 //    possibly API parameters.
  2145 //
  2146 // All actual formatting in ICU is done with patterns; styles and skeletons
  2147 // have to be mapped to patterns before processing.
  2148 //
  2149 // The options of DateTimeFormat most closely correspond to ICU skeletons. This
  2150 // implementation therefore, in the toBestICUPattern function, converts
  2151 // DateTimeFormat options to ICU skeletons, and then lets ICU map skeletons to
  2152 // actual ICU patterns. The pattern may not directly correspond to what the
  2153 // skeleton requests, as the mapper (UDateTimePatternGenerator) is constrained
  2154 // by the available locale data for the locale. The resulting ICU pattern is
  2155 // kept as the DateTimeFormat's [[pattern]] internal property and passed to ICU
  2156 // in the format method.
  2157 //
  2158 // An ICU pattern represents the information of the following DateTimeFormat
  2159 // internal properties described in the specification, which therefore don't
  2160 // exist separately in the implementation:
  2161 // - [[weekday]], [[era]], [[year]], [[month]], [[day]], [[hour]], [[minute]],
  2162 //   [[second]], [[timeZoneName]]
  2163 // - [[hour12]]
  2164 // - [[hourNo0]]
  2165 // When needed for the resolvedOptions method, the resolveICUPattern function
  2166 // maps the instance's ICU pattern back to the specified properties of the
  2167 // object returned by resolvedOptions.
  2168 //
  2169 // ICU date-time skeletons and patterns aren't fully documented in the ICU
  2170 // documentation (see http://bugs.icu-project.org/trac/ticket/9627). The best
  2171 // documentation at this point is in UTR 35:
  2172 // http://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
  2175 /**
  2176  * Returns an ICU pattern string for the given locale and representing the
  2177  * specified options as closely as possible given available locale data.
  2178  */
  2179 function toBestICUPattern(locale, options) {
  2180     // Create an ICU skeleton representing the specified options. See
  2181     // http://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
  2182     var skeleton = "";
  2183     switch (options.weekday) {
  2184     case "narrow":
  2185         skeleton += "EEEEE";
  2186         break;
  2187     case "short":
  2188         skeleton += "E";
  2189         break;
  2190     case "long":
  2191         skeleton += "EEEE";
  2193     switch (options.era) {
  2194     case "narrow":
  2195         skeleton += "GGGGG";
  2196         break;
  2197     case "short":
  2198         skeleton += "G";
  2199         break;
  2200     case "long":
  2201         skeleton += "GGGG";
  2202         break;
  2204     switch (options.year) {
  2205     case "2-digit":
  2206         skeleton += "yy";
  2207         break;
  2208     case "numeric":
  2209         skeleton += "y";
  2210         break;
  2212     switch (options.month) {
  2213     case "2-digit":
  2214         skeleton += "MM";
  2215         break;
  2216     case "numeric":
  2217         skeleton += "M";
  2218         break;
  2219     case "narrow":
  2220         skeleton += "MMMMM";
  2221         break;
  2222     case "short":
  2223         skeleton += "MMM";
  2224         break;
  2225     case "long":
  2226         skeleton += "MMMM";
  2227         break;
  2229     switch (options.day) {
  2230     case "2-digit":
  2231         skeleton += "dd";
  2232         break;
  2233     case "numeric":
  2234         skeleton += "d";
  2235         break;
  2237     var hourSkeletonChar = "j";
  2238     if (options.hour12 !== undefined) {
  2239         if (options.hour12)
  2240             hourSkeletonChar = "h";
  2241         else
  2242             hourSkeletonChar = "H";
  2244     switch (options.hour) {
  2245     case "2-digit":
  2246         skeleton += hourSkeletonChar + hourSkeletonChar;
  2247         break;
  2248     case "numeric":
  2249         skeleton += hourSkeletonChar;
  2250         break;
  2252     switch (options.minute) {
  2253     case "2-digit":
  2254         skeleton += "mm";
  2255         break;
  2256     case "numeric":
  2257         skeleton += "m";
  2258         break;
  2260     switch (options.second) {
  2261     case "2-digit":
  2262         skeleton += "ss";
  2263         break;
  2264     case "numeric":
  2265         skeleton += "s";
  2266         break;
  2268     switch (options.timeZoneName) {
  2269     case "short":
  2270         skeleton += "z";
  2271         break;
  2272     case "long":
  2273         skeleton += "zzzz";
  2274         break;
  2277     // Let ICU convert the ICU skeleton to an ICU pattern for the given locale.
  2278     return intl_patternForSkeleton(locale, skeleton);
  2282 /**
  2283  * Returns a new options object that includes the provided options (if any)
  2284  * and fills in default components if required components are not defined.
  2285  * Required can be "date", "time", or "any".
  2286  * Defaults can be "date", "time", or "all".
  2288  * Spec: ECMAScript Internationalization API Specification, 12.1.1.
  2289  */
  2290 function ToDateTimeOptions(options, required, defaults) {
  2291     assert(typeof required === "string", "ToDateTimeOptions");
  2292     assert(typeof defaults === "string", "ToDateTimeOptions");
  2294     // Steps 1-3.
  2295     if (options === undefined)
  2296         options = null;
  2297     else
  2298         options = ToObject(options);
  2299     options = std_Object_create(options);
  2301     // Step 4.
  2302     var needDefaults = true;
  2304     // Step 5.
  2305     if ((required === "date" || required === "any") &&
  2306         (options.weekday !== undefined || options.year !== undefined ||
  2307          options.month !== undefined || options.day !== undefined))
  2309         needDefaults = false;
  2312     // Step 6.
  2313     if ((required === "time" || required === "any") &&
  2314         (options.hour !== undefined || options.minute !== undefined ||
  2315          options.second !== undefined))
  2317         needDefaults = false;
  2320     // Step 7.
  2321     if (needDefaults && (defaults === "date" || defaults === "all")) {
  2322         // The specification says to call [[DefineOwnProperty]] with false for
  2323         // the Throw parameter, while Object.defineProperty uses true. For the
  2324         // calls here, the difference doesn't matter because we're adding
  2325         // properties to a new object.
  2326         defineProperty(options, "year", "numeric");
  2327         defineProperty(options, "month", "numeric");
  2328         defineProperty(options, "day", "numeric");
  2331     // Step 8.
  2332     if (needDefaults && (defaults === "time" || defaults === "all")) {
  2333         // See comment for step 7.
  2334         defineProperty(options, "hour", "numeric");
  2335         defineProperty(options, "minute", "numeric");
  2336         defineProperty(options, "second", "numeric");
  2339     // Step 9.
  2340     return options;
  2344 /**
  2345  * Compares the date and time components requested by options with the available
  2346  * date and time formats in formats, and selects the best match according
  2347  * to a specified basic matching algorithm.
  2349  * Spec: ECMAScript Internationalization API Specification, 12.1.1.
  2350  */
  2351 function BasicFormatMatcher(options, formats) {
  2352     // Steps 1-6.
  2353     var removalPenalty = 120,
  2354         additionPenalty = 20,
  2355         longLessPenalty = 8,
  2356         longMorePenalty = 6,
  2357         shortLessPenalty = 6,
  2358         shortMorePenalty = 3;
  2360     // Table 3.
  2361     var properties = ["weekday", "era", "year", "month", "day",
  2362         "hour", "minute", "second", "timeZoneName"];
  2364     // Step 11.c.vi.1.
  2365     var values = ["2-digit", "numeric", "narrow", "short", "long"];
  2367     // Steps 7-8.
  2368     var bestScore = -Infinity;
  2369     var bestFormat;
  2371     // Steps 9-11.
  2372     var i = 0;
  2373     var len = formats.length;
  2374     while (i < len) {
  2375         // Steps 11.a-b.
  2376         var format = formats[i];
  2377         var score = 0;
  2379         // Step 11.c.
  2380         var formatProp;
  2381         for (var j = 0; j < properties.length; j++) {
  2382             var property = properties[j];
  2384             // Step 11.c.i.
  2385             var optionsProp = options[property];
  2386             // Step missing from spec.
  2387             // https://bugs.ecmascript.org/show_bug.cgi?id=1254
  2388             formatProp = undefined;
  2390             // Steps 11.c.ii-iii.
  2391             if (callFunction(std_Object_hasOwnProperty, format, property))
  2392                 formatProp = format[property];
  2394             if (optionsProp === undefined && formatProp !== undefined) {
  2395                 // Step 11.c.iv.
  2396                 score -= additionPenalty;
  2397             } else if (optionsProp !== undefined && formatProp === undefined) {
  2398                 // Step 11.c.v.
  2399                 score -= removalPenalty;
  2400             } else {
  2401                 // Step 11.c.vi.
  2402                 var optionsPropIndex = callFunction(std_Array_indexOf, values, optionsProp);
  2403                 var formatPropIndex = callFunction(std_Array_indexOf, values, formatProp);
  2404                 var delta = std_Math_max(std_Math_min(formatPropIndex - optionsPropIndex, 2), -2);
  2405                 if (delta === 2)
  2406                     score -= longMorePenalty;
  2407                 else if (delta === 1)
  2408                     score -= shortMorePenalty;
  2409                 else if (delta === -1)
  2410                     score -= shortLessPenalty;
  2411                 else if (delta === -2)
  2412                     score -= longLessPenalty;
  2416         // Step 11.d.
  2417         if (score > bestScore) {
  2418             bestScore = score;
  2419             bestFormat = format;
  2422         // Step 11.e.
  2423         i++;
  2426     // Step 12.
  2427     return bestFormat;
  2431 /**
  2432  * Compares the date and time components requested by options with the available
  2433  * date and time formats in formats, and selects the best match according
  2434  * to an unspecified best-fit matching algorithm.
  2436  * Spec: ECMAScript Internationalization API Specification, 12.1.1.
  2437  */
  2438 function BestFitFormatMatcher(options, formats) {
  2439     // this implementation doesn't have anything better
  2440     return BasicFormatMatcher(options, formats);
  2444 /**
  2445  * Returns the subset of the given locale list for which this locale list has a
  2446  * matching (possibly fallback) locale. Locales appear in the same order in the
  2447  * returned list as in the input list.
  2449  * Spec: ECMAScript Internationalization API Specification, 12.2.2.
  2450  */
  2451 function Intl_DateTimeFormat_supportedLocalesOf(locales /*, options*/) {
  2452     var options = arguments.length > 1 ? arguments[1] : undefined;
  2454     var availableLocales = dateTimeFormatInternalProperties.availableLocales();
  2455     var requestedLocales = CanonicalizeLocaleList(locales);
  2456     return SupportedLocales(availableLocales, requestedLocales, options);
  2460 /**
  2461  * DateTimeFormat internal properties.
  2463  * Spec: ECMAScript Internationalization API Specification, 9.1 and 12.2.3.
  2464  */
  2465 var dateTimeFormatInternalProperties = {
  2466     localeData: dateTimeFormatLocaleData,
  2467     _availableLocales: null,
  2468     availableLocales: function()
  2470         var locales = this._availableLocales;
  2471         if (locales)
  2472             return locales;
  2473         return (this._availableLocales =
  2474           addOldStyleLanguageTags(intl_DateTimeFormat_availableLocales()));
  2475     },
  2476     relevantExtensionKeys: ["ca", "nu"]
  2477 };
  2480 function dateTimeFormatLocaleData(locale) {
  2481     return {
  2482         ca: intl_availableCalendars(locale),
  2483         nu: getNumberingSystems(locale)
  2484     };
  2488 /**
  2489  * Function to be bound and returned by Intl.DateTimeFormat.prototype.format.
  2491  * Spec: ECMAScript Internationalization API Specification, 12.3.2.
  2492  */
  2493 function dateTimeFormatFormatToBind() {
  2494     // Steps 1.a.i-ii
  2495     var date = arguments.length > 0 ? arguments[0] : undefined;
  2496     var x = (date === undefined) ? std_Date_now() : ToNumber(date);
  2498     // Step 1.a.iii.
  2499     return intl_FormatDateTime(this, x);
  2503 /**
  2504  * Returns a function bound to this DateTimeFormat that returns a String value
  2505  * representing the result of calling ToNumber(date) according to the
  2506  * effective locale and the formatting options of this DateTimeFormat.
  2508  * Spec: ECMAScript Internationalization API Specification, 12.3.2.
  2509  */
  2510 function Intl_DateTimeFormat_format_get() {
  2511     // Check "this DateTimeFormat object" per introduction of section 12.3.
  2512     var internals = getDateTimeFormatInternals(this, "format");
  2514     // Step 1.
  2515     if (internals.boundFormat === undefined) {
  2516         // Step 1.a.
  2517         var F = dateTimeFormatFormatToBind;
  2519         // Step 1.b-d.
  2520         var bf = callFunction(std_Function_bind, F, this);
  2521         internals.boundFormat = bf;
  2524     // Step 2.
  2525     return internals.boundFormat;
  2529 /**
  2530  * Returns the resolved options for a DateTimeFormat object.
  2532  * Spec: ECMAScript Internationalization API Specification, 12.3.3 and 12.4.
  2533  */
  2534 function Intl_DateTimeFormat_resolvedOptions() {
  2535     // Check "this DateTimeFormat object" per introduction of section 12.3.
  2536     var internals = getDateTimeFormatInternals(this, "resolvedOptions");
  2538     var result = {
  2539         locale: internals.locale,
  2540         calendar: internals.calendar,
  2541         numberingSystem: internals.numberingSystem,
  2542         timeZone: internals.timeZone
  2543     };
  2544     resolveICUPattern(internals.pattern, result);
  2545     return result;
  2549 // Table mapping ICU pattern characters back to the corresponding date-time
  2550 // components of DateTimeFormat. See
  2551 // http://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
  2552 var icuPatternCharToComponent = {
  2553     E: "weekday",
  2554     G: "era",
  2555     y: "year",
  2556     M: "month",
  2557     L: "month",
  2558     d: "day",
  2559     h: "hour",
  2560     H: "hour",
  2561     k: "hour",
  2562     K: "hour",
  2563     m: "minute",
  2564     s: "second",
  2565     z: "timeZoneName",
  2566     v: "timeZoneName",
  2567     V: "timeZoneName"
  2568 };
  2571 /**
  2572  * Maps an ICU pattern string to a corresponding set of date-time components
  2573  * and their values, and adds properties for these components to the result
  2574  * object, which will be returned by the resolvedOptions method. For the
  2575  * interpretation of ICU pattern characters, see
  2576  * http://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
  2577  */
  2578 function resolveICUPattern(pattern, result) {
  2579     assert(IsObject(result), "resolveICUPattern");
  2580     var i = 0;
  2581     while (i < pattern.length) {
  2582         var c = pattern[i++];
  2583         if (c === "'") {
  2584             while (i < pattern.length && pattern[i] !== "'")
  2585                 i++;
  2586             i++;
  2587         } else {
  2588             var count = 1;
  2589             while (i < pattern.length && pattern[i] === c) {
  2590                 i++;
  2591                 count++;
  2593             var value;
  2594             switch (c) {
  2595             // "text" cases
  2596             case "G":
  2597             case "E":
  2598             case "z":
  2599             case "v":
  2600             case "V":
  2601                 if (count <= 3)
  2602                     value = "short";
  2603                 else if (count === 4)
  2604                     value = "long";
  2605                 else
  2606                     value = "narrow";
  2607                 break;
  2608             // "number" cases
  2609             case "y":
  2610             case "d":
  2611             case "h":
  2612             case "H":
  2613             case "m":
  2614             case "s":
  2615             case "k":
  2616             case "K":
  2617                 if (count === 2)
  2618                     value = "2-digit";
  2619                 else
  2620                     value = "numeric";
  2621                 break;
  2622             // "text & number" cases
  2623             case "M":
  2624             case "L":
  2625                 if (count === 1)
  2626                     value = "numeric";
  2627                 else if (count === 2)
  2628                     value = "2-digit";
  2629                 else if (count === 3)
  2630                     value = "short";
  2631                 else if (count === 4)
  2632                     value = "long";
  2633                 else
  2634                     value = "narrow";
  2635                 break;
  2636             default:
  2637                 // skip other pattern characters and literal text
  2639             if (callFunction(std_Object_hasOwnProperty, icuPatternCharToComponent, c))
  2640                 defineProperty(result, icuPatternCharToComponent[c], value);
  2641             if (c === "h" || c === "K")
  2642                 defineProperty(result, "hour12", true);
  2643             else if (c === "H" || c === "k")
  2644                 defineProperty(result, "hour12", false);

mercurial