js/src/builtin/Intl.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/builtin/Intl.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2647 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +/* Portions Copyright Norbert Lindenberg 2011-2012. */
     1.9 +
    1.10 +/*global JSMSG_INTL_OBJECT_NOT_INITED: false, JSMSG_INVALID_LOCALES_ELEMENT: false,
    1.11 +         JSMSG_INVALID_LANGUAGE_TAG: false, JSMSG_INVALID_LOCALE_MATCHER: false,
    1.12 +         JSMSG_INVALID_OPTION_VALUE: false, JSMSG_INVALID_DIGITS_VALUE: false,
    1.13 +         JSMSG_INTL_OBJECT_REINITED: false, JSMSG_INVALID_CURRENCY_CODE: false,
    1.14 +         JSMSG_UNDEFINED_CURRENCY: false, JSMSG_INVALID_TIME_ZONE: false,
    1.15 +         JSMSG_DATE_NOT_FINITE: false,
    1.16 +         intl_Collator_availableLocales: false,
    1.17 +         intl_availableCollations: false,
    1.18 +         intl_CompareStrings: false,
    1.19 +         intl_NumberFormat_availableLocales: false,
    1.20 +         intl_numberingSystem: false,
    1.21 +         intl_FormatNumber: false,
    1.22 +         intl_DateTimeFormat_availableLocales: false,
    1.23 +         intl_availableCalendars: false,
    1.24 +         intl_patternForSkeleton: false,
    1.25 +         intl_FormatDateTime: false,
    1.26 +*/
    1.27 +
    1.28 +/*
    1.29 + * The Intl module specified by standard ECMA-402,
    1.30 + * ECMAScript Internationalization API Specification.
    1.31 + */
    1.32 +
    1.33 +
    1.34 +/********** Locales, Time Zones, and Currencies **********/
    1.35 +
    1.36 +
    1.37 +/**
    1.38 + * Convert s to upper case, but limited to characters a-z.
    1.39 + *
    1.40 + * Spec: ECMAScript Internationalization API Specification, 6.1.
    1.41 + */
    1.42 +function toASCIIUpperCase(s) {
    1.43 +    assert(typeof s === "string", "toASCIIUpperCase");
    1.44 +
    1.45 +    // String.prototype.toUpperCase may map non-ASCII characters into ASCII,
    1.46 +    // so go character by character (actually code unit by code unit, but
    1.47 +    // since we only care about ASCII characters here, that's OK).
    1.48 +    var result = "";
    1.49 +    for (var i = 0; i < s.length; i++) {
    1.50 +        var c = s[i];
    1.51 +        if ("a" <= c && c <= "z")
    1.52 +            c = callFunction(std_String_toUpperCase, c);
    1.53 +        result += c;
    1.54 +    }
    1.55 +    return result;
    1.56 +}
    1.57 +
    1.58 +
    1.59 +/**
    1.60 + * Regular expression matching a "Unicode locale extension sequence", which the
    1.61 + * specification defines as: "any substring of a language tag that starts with
    1.62 + * a separator '-' and the singleton 'u' and includes the maximum sequence of
    1.63 + * following non-singleton subtags and their preceding '-' separators."
    1.64 + *
    1.65 + * Alternatively, this may be defined as: the components of a language tag that
    1.66 + * match the extension production in RFC 5646, where the singleton component is
    1.67 + * "u".
    1.68 + *
    1.69 + * Spec: ECMAScript Internationalization API Specification, 6.2.1.
    1.70 + */
    1.71 +var unicodeLocaleExtensionSequence = "-u(-[a-z0-9]{2,8})+";
    1.72 +var unicodeLocaleExtensionSequenceRE = new RegExp(unicodeLocaleExtensionSequence);
    1.73 +
    1.74 +
    1.75 +/**
    1.76 + * Removes Unicode locale extension sequences from the given language tag.
    1.77 + */
    1.78 +function removeUnicodeExtensions(locale) {
    1.79 +    // Don't use std_String_replace directly with a regular expression,
    1.80 +    // as that would set RegExp statics.
    1.81 +    var extensions;
    1.82 +    while ((extensions = regexp_exec_no_statics(unicodeLocaleExtensionSequenceRE, locale)) !== null) {
    1.83 +        locale = callFunction(std_String_replace, locale, extensions[0], "");
    1.84 +        unicodeLocaleExtensionSequenceRE.lastIndex = 0;
    1.85 +    }
    1.86 +    return locale;
    1.87 +}
    1.88 +
    1.89 +
    1.90 +/**
    1.91 + * Regular expression defining BCP 47 language tags.
    1.92 + *
    1.93 + * Spec: RFC 5646 section 2.1.
    1.94 + */
    1.95 +var languageTagRE = (function () {
    1.96 +    // RFC 5234 section B.1
    1.97 +    // ALPHA          =  %x41-5A / %x61-7A   ; A-Z / a-z
    1.98 +    var ALPHA = "[a-zA-Z]";
    1.99 +    // DIGIT          =  %x30-39
   1.100 +    //                        ; 0-9
   1.101 +    var DIGIT = "[0-9]";
   1.102 +
   1.103 +    // RFC 5646 section 2.1
   1.104 +    // alphanum      = (ALPHA / DIGIT)     ; letters and numbers
   1.105 +    var alphanum = "(?:" + ALPHA + "|" + DIGIT + ")";
   1.106 +    // regular       = "art-lojban"        ; these tags match the 'langtag'
   1.107 +    //               / "cel-gaulish"       ; production, but their subtags
   1.108 +    //               / "no-bok"            ; are not extended language
   1.109 +    //               / "no-nyn"            ; or variant subtags: their meaning
   1.110 +    //               / "zh-guoyu"          ; is defined by their registration
   1.111 +    //               / "zh-hakka"          ; and all of these are deprecated
   1.112 +    //               / "zh-min"            ; in favor of a more modern
   1.113 +    //               / "zh-min-nan"        ; subtag or sequence of subtags
   1.114 +    //               / "zh-xiang"
   1.115 +    var regular = "(?:art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang)";
   1.116 +    // irregular     = "en-GB-oed"         ; irregular tags do not match
   1.117 +    //                / "i-ami"             ; the 'langtag' production and
   1.118 +    //                / "i-bnn"             ; would not otherwise be
   1.119 +    //                / "i-default"         ; considered 'well-formed'
   1.120 +    //                / "i-enochian"        ; These tags are all valid,
   1.121 +    //                / "i-hak"             ; but most are deprecated
   1.122 +    //                / "i-klingon"         ; in favor of more modern
   1.123 +    //                / "i-lux"             ; subtags or subtag
   1.124 +    //                / "i-mingo"           ; combination
   1.125 +    //                / "i-navajo"
   1.126 +    //                / "i-pwn"
   1.127 +    //                / "i-tao"
   1.128 +    //                / "i-tay"
   1.129 +    //                / "i-tsu"
   1.130 +    //                / "sgn-BE-FR"
   1.131 +    //                / "sgn-BE-NL"
   1.132 +    //                / "sgn-CH-DE"
   1.133 +    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)";
   1.134 +    // grandfathered = irregular           ; non-redundant tags registered
   1.135 +    //               / regular             ; during the RFC 3066 era
   1.136 +    var grandfathered = "(?:" + irregular + "|" + regular + ")";
   1.137 +    // privateuse    = "x" 1*("-" (1*8alphanum))
   1.138 +    var privateuse = "(?:x(?:-[a-z0-9]{1,8})+)";
   1.139 +    // singleton     = DIGIT               ; 0 - 9
   1.140 +    //               / %x41-57             ; A - W
   1.141 +    //               / %x59-5A             ; Y - Z
   1.142 +    //               / %x61-77             ; a - w
   1.143 +    //               / %x79-7A             ; y - z
   1.144 +    var singleton = "(?:" + DIGIT + "|[A-WY-Za-wy-z])";
   1.145 +    // extension     = singleton 1*("-" (2*8alphanum))
   1.146 +    var extension = "(?:" + singleton + "(?:-" + alphanum + "{2,8})+)";
   1.147 +    // variant       = 5*8alphanum         ; registered variants
   1.148 +    //               / (DIGIT 3alphanum)
   1.149 +    var variant = "(?:" + alphanum + "{5,8}|(?:" + DIGIT + alphanum + "{3}))";
   1.150 +    // region        = 2ALPHA              ; ISO 3166-1 code
   1.151 +    //               / 3DIGIT              ; UN M.49 code
   1.152 +    var region = "(?:" + ALPHA + "{2}|" + DIGIT + "{3})";
   1.153 +    // script        = 4ALPHA              ; ISO 15924 code
   1.154 +    var script = "(?:" + ALPHA + "{4})";
   1.155 +    // extlang       = 3ALPHA              ; selected ISO 639 codes
   1.156 +    //                 *2("-" 3ALPHA)      ; permanently reserved
   1.157 +    var extlang = "(?:" + ALPHA + "{3}(?:-" + ALPHA + "{3}){0,2})";
   1.158 +    // language      = 2*3ALPHA            ; shortest ISO 639 code
   1.159 +    //                 ["-" extlang]       ; sometimes followed by
   1.160 +    //                                     ; extended language subtags
   1.161 +    //               / 4ALPHA              ; or reserved for future use
   1.162 +    //               / 5*8ALPHA            ; or registered language subtag
   1.163 +    var language = "(?:" + ALPHA + "{2,3}(?:-" + extlang + ")?|" + ALPHA + "{4}|" + ALPHA + "{5,8})";
   1.164 +    // langtag       = language
   1.165 +    //                 ["-" script]
   1.166 +    //                 ["-" region]
   1.167 +    //                 *("-" variant)
   1.168 +    //                 *("-" extension)
   1.169 +    //                 ["-" privateuse]
   1.170 +    var langtag = language + "(?:-" + script + ")?(?:-" + region + ")?(?:-" +
   1.171 +                  variant + ")*(?:-" + extension + ")*(?:-" + privateuse + ")?";
   1.172 +    // Language-Tag  = langtag             ; normal language tags
   1.173 +    //               / privateuse          ; private use tag
   1.174 +    //               / grandfathered       ; grandfathered tags
   1.175 +    var languageTag = "^(?:" + langtag + "|" + privateuse + "|" + grandfathered + ")$";
   1.176 +
   1.177 +    // Language tags are case insensitive (RFC 5646 section 2.1.1).
   1.178 +    return new RegExp(languageTag, "i");
   1.179 +}());
   1.180 +
   1.181 +
   1.182 +var duplicateVariantRE = (function () {
   1.183 +    // RFC 5234 section B.1
   1.184 +    // ALPHA          =  %x41-5A / %x61-7A   ; A-Z / a-z
   1.185 +    var ALPHA = "[a-zA-Z]";
   1.186 +    // DIGIT          =  %x30-39
   1.187 +    //                        ; 0-9
   1.188 +    var DIGIT = "[0-9]";
   1.189 +
   1.190 +    // RFC 5646 section 2.1
   1.191 +    // alphanum      = (ALPHA / DIGIT)     ; letters and numbers
   1.192 +    var alphanum = "(?:" + ALPHA + "|" + DIGIT + ")";
   1.193 +    // variant       = 5*8alphanum         ; registered variants
   1.194 +    //               / (DIGIT 3alphanum)
   1.195 +    var variant = "(?:" + alphanum + "{5,8}|(?:" + DIGIT + alphanum + "{3}))";
   1.196 +
   1.197 +    // Match a langtag that contains a duplicate variant.
   1.198 +    var duplicateVariant =
   1.199 +        // Match everything in a langtag prior to any variants, and maybe some
   1.200 +        // of the variants as well (which makes this pattern inefficient but
   1.201 +        // not wrong, for our purposes);
   1.202 +        "(?:" + alphanum + "{2,8}-)+" +
   1.203 +        // a variant, parenthesised so that we can refer back to it later;
   1.204 +        "(" + variant + ")-" +
   1.205 +        // zero or more subtags at least two characters long (thus stopping
   1.206 +        // before extension and privateuse components);
   1.207 +        "(?:" + alphanum + "{2,8}-)*" +
   1.208 +        // and the same variant again
   1.209 +        "\\1" +
   1.210 +        // ...but not followed by any characters that would turn it into a
   1.211 +        // different subtag.
   1.212 +        "(?!" + alphanum + ")";
   1.213 +
   1.214 +    // Language tags are case insensitive (RFC 5646 section 2.1.1), but for
   1.215 +    // this regular expression that's covered by having its character classes
   1.216 +    // list both upper- and lower-case characters.
   1.217 +    return new RegExp(duplicateVariant);
   1.218 +}());
   1.219 +
   1.220 +
   1.221 +var duplicateSingletonRE = (function () {
   1.222 +    // RFC 5234 section B.1
   1.223 +    // ALPHA          =  %x41-5A / %x61-7A   ; A-Z / a-z
   1.224 +    var ALPHA = "[a-zA-Z]";
   1.225 +    // DIGIT          =  %x30-39
   1.226 +    //                        ; 0-9
   1.227 +    var DIGIT = "[0-9]";
   1.228 +
   1.229 +    // RFC 5646 section 2.1
   1.230 +    // alphanum      = (ALPHA / DIGIT)     ; letters and numbers
   1.231 +    var alphanum = "(?:" + ALPHA + "|" + DIGIT + ")";
   1.232 +    // singleton     = DIGIT               ; 0 - 9
   1.233 +    //               / %x41-57             ; A - W
   1.234 +    //               / %x59-5A             ; Y - Z
   1.235 +    //               / %x61-77             ; a - w
   1.236 +    //               / %x79-7A             ; y - z
   1.237 +    var singleton = "(?:" + DIGIT + "|[A-WY-Za-wy-z])";
   1.238 +
   1.239 +    // Match a langtag that contains a duplicate singleton.
   1.240 +    var duplicateSingleton =
   1.241 +        // Match a singleton subtag, parenthesised so that we can refer back to
   1.242 +        // it later;
   1.243 +        "-(" + singleton + ")-" +
   1.244 +        // then zero or more subtags;
   1.245 +        "(?:" + alphanum + "+-)*" +
   1.246 +        // and the same singleton again
   1.247 +        "\\1" +
   1.248 +        // ...but not followed by any characters that would turn it into a
   1.249 +        // different subtag.
   1.250 +        "(?!" + alphanum + ")";
   1.251 +
   1.252 +    // Language tags are case insensitive (RFC 5646 section 2.1.1), but for
   1.253 +    // this regular expression that's covered by having its character classes
   1.254 +    // list both upper- and lower-case characters.
   1.255 +    return new RegExp(duplicateSingleton);
   1.256 +}());
   1.257 +
   1.258 +
   1.259 +/**
   1.260 + * Verifies that the given string is a well-formed BCP 47 language tag
   1.261 + * with no duplicate variant or singleton subtags.
   1.262 + *
   1.263 + * Spec: ECMAScript Internationalization API Specification, 6.2.2.
   1.264 + */
   1.265 +function IsStructurallyValidLanguageTag(locale) {
   1.266 +    assert(typeof locale === "string", "IsStructurallyValidLanguageTag");
   1.267 +    if (!regexp_test_no_statics(languageTagRE, locale))
   1.268 +        return false;
   1.269 +
   1.270 +    // Before checking for duplicate variant or singleton subtags with
   1.271 +    // regular expressions, we have to get private use subtag sequences
   1.272 +    // out of the picture.
   1.273 +    if (callFunction(std_String_startsWith, locale, "x-"))
   1.274 +        return true;
   1.275 +    var pos = callFunction(std_String_indexOf, locale, "-x-");
   1.276 +    if (pos !== -1)
   1.277 +        locale = callFunction(std_String_substring, locale, 0, pos);
   1.278 +
   1.279 +    // Check for duplicate variant or singleton subtags.
   1.280 +    return !regexp_test_no_statics(duplicateVariantRE, locale) &&
   1.281 +           !regexp_test_no_statics(duplicateSingletonRE, locale);
   1.282 +}
   1.283 +
   1.284 +
   1.285 +/**
   1.286 + * Canonicalizes the given structurally valid BCP 47 language tag, including
   1.287 + * regularized case of subtags. For example, the language tag
   1.288 + * Zh-NAN-haNS-bu-variant2-Variant1-u-ca-chinese-t-Zh-laTN-x-PRIVATE, where
   1.289 + *
   1.290 + *     Zh             ; 2*3ALPHA
   1.291 + *     -NAN           ; ["-" extlang]
   1.292 + *     -haNS          ; ["-" script]
   1.293 + *     -bu            ; ["-" region]
   1.294 + *     -variant2      ; *("-" variant)
   1.295 + *     -Variant1
   1.296 + *     -u-ca-chinese  ; *("-" extension)
   1.297 + *     -t-Zh-laTN
   1.298 + *     -x-PRIVATE     ; ["-" privateuse]
   1.299 + *
   1.300 + * becomes nan-Hans-mm-variant2-variant1-t-zh-latn-u-ca-chinese-x-private
   1.301 + *
   1.302 + * Spec: ECMAScript Internationalization API Specification, 6.2.3.
   1.303 + * Spec: RFC 5646, section 4.5.
   1.304 + */
   1.305 +function CanonicalizeLanguageTag(locale) {
   1.306 +    assert(IsStructurallyValidLanguageTag(locale), "CanonicalizeLanguageTag");
   1.307 +
   1.308 +    // The input
   1.309 +    // "Zh-NAN-haNS-bu-variant2-Variant1-u-ca-chinese-t-Zh-laTN-x-PRIVATE"
   1.310 +    // will be used throughout this method to illustrate how it works.
   1.311 +
   1.312 +    // Language tags are compared and processed case-insensitively, so
   1.313 +    // technically it's not necessary to adjust case. But for easier processing,
   1.314 +    // and because the canonical form for most subtags is lower case, we start
   1.315 +    // with lower case for all.
   1.316 +    // "Zh-NAN-haNS-bu-variant2-Variant1-u-ca-chinese-t-Zh-laTN-x-PRIVATE" ->
   1.317 +    // "zh-nan-hans-bu-variant2-variant1-u-ca-chinese-t-zh-latn-x-private"
   1.318 +    locale = callFunction(std_String_toLowerCase, locale);
   1.319 +
   1.320 +    // Handle mappings for complete tags.
   1.321 +    if (callFunction(std_Object_hasOwnProperty, langTagMappings, locale))
   1.322 +        return langTagMappings[locale];
   1.323 +
   1.324 +    var subtags = callFunction(std_String_split, locale, "-");
   1.325 +    var i = 0;
   1.326 +
   1.327 +    // Handle the standard part: All subtags before the first singleton or "x".
   1.328 +    // "zh-nan-hans-bu-variant2-variant1"
   1.329 +    while (i < subtags.length) {
   1.330 +        var subtag = subtags[i];
   1.331 +
   1.332 +        // If we reach the start of an extension sequence or private use part,
   1.333 +        // we're done with this loop. We have to check for i > 0 because for
   1.334 +        // irregular language tags, such as i-klingon, the single-character
   1.335 +        // subtag "i" is not the start of an extension sequence.
   1.336 +        // In the example, we break at "u".
   1.337 +        if (subtag.length === 1 && (i > 0 || subtag === "x"))
   1.338 +            break;
   1.339 +
   1.340 +        if (subtag.length === 4) {
   1.341 +            // 4-character subtags are script codes; their first character
   1.342 +            // needs to be capitalized. "hans" -> "Hans"
   1.343 +            subtag = callFunction(std_String_toUpperCase, subtag[0]) +
   1.344 +                     callFunction(std_String_substring, subtag, 1);
   1.345 +        } else if (i !== 0 && subtag.length === 2) {
   1.346 +            // 2-character subtags that are not in initial position are region
   1.347 +            // codes; they need to be upper case. "bu" -> "BU"
   1.348 +            subtag = callFunction(std_String_toUpperCase, subtag);
   1.349 +        }
   1.350 +        if (callFunction(std_Object_hasOwnProperty, langSubtagMappings, subtag)) {
   1.351 +            // Replace deprecated subtags with their preferred values.
   1.352 +            // "BU" -> "MM"
   1.353 +            // This has to come after we capitalize region codes because
   1.354 +            // otherwise some language and region codes could be confused.
   1.355 +            // For example, "in" is an obsolete language code for Indonesian,
   1.356 +            // but "IN" is the country code for India.
   1.357 +            // Note that the script generating langSubtagMappings makes sure
   1.358 +            // that no regular subtag mapping will replace an extlang code.
   1.359 +            subtag = langSubtagMappings[subtag];
   1.360 +        } else if (callFunction(std_Object_hasOwnProperty, extlangMappings, subtag)) {
   1.361 +            // Replace deprecated extlang subtags with their preferred values,
   1.362 +            // and remove the preceding subtag if it's a redundant prefix.
   1.363 +            // "zh-nan" -> "nan"
   1.364 +            // Note that the script generating extlangMappings makes sure that
   1.365 +            // no extlang mapping will replace a normal language code.
   1.366 +            subtag = extlangMappings[subtag].preferred;
   1.367 +            if (i === 1 && extlangMappings[subtag].prefix === subtags[0]) {
   1.368 +                callFunction(std_Array_shift, subtags);
   1.369 +                i--;
   1.370 +            }
   1.371 +        }
   1.372 +        subtags[i] = subtag;
   1.373 +        i++;
   1.374 +    }
   1.375 +    var normal = callFunction(std_Array_join, callFunction(std_Array_slice, subtags, 0, i), "-");
   1.376 +
   1.377 +    // Extension sequences are sorted by their singleton characters.
   1.378 +    // "u-ca-chinese-t-zh-latn" -> "t-zh-latn-u-ca-chinese"
   1.379 +    var extensions = new List();
   1.380 +    while (i < subtags.length && subtags[i] !== "x") {
   1.381 +        var extensionStart = i;
   1.382 +        i++;
   1.383 +        while (i < subtags.length && subtags[i].length > 1)
   1.384 +            i++;
   1.385 +        var extension = callFunction(std_Array_join, callFunction(std_Array_slice, subtags, extensionStart, i), "-");
   1.386 +        extensions.push(extension);
   1.387 +    }
   1.388 +    extensions.sort();
   1.389 +
   1.390 +    // Private use sequences are left as is. "x-private"
   1.391 +    var privateUse = "";
   1.392 +    if (i < subtags.length)
   1.393 +        privateUse = callFunction(std_Array_join, callFunction(std_Array_slice, subtags, i), "-");
   1.394 +
   1.395 +    // Put everything back together.
   1.396 +    var canonical = normal;
   1.397 +    if (extensions.length > 0)
   1.398 +        canonical += "-" + extensions.join("-");
   1.399 +    if (privateUse.length > 0) {
   1.400 +        // Be careful of a Language-Tag that is entirely privateuse.
   1.401 +        if (canonical.length > 0)
   1.402 +            canonical += "-" + privateUse;
   1.403 +        else
   1.404 +            canonical = privateUse;
   1.405 +    }
   1.406 +
   1.407 +    return canonical;
   1.408 +}
   1.409 +
   1.410 +
   1.411 +// mappings from some commonly used old-style language tags to current flavors
   1.412 +// with script codes
   1.413 +var oldStyleLanguageTagMappings = {
   1.414 +    "pa-PK": "pa-Arab-PK",
   1.415 +    "zh-CN": "zh-Hans-CN",
   1.416 +    "zh-HK": "zh-Hant-HK",
   1.417 +    "zh-SG": "zh-Hans-SG",
   1.418 +    "zh-TW": "zh-Hant-TW"
   1.419 +};
   1.420 +
   1.421 +
   1.422 +/**
   1.423 + * Returns the BCP 47 language tag for the host environment's current locale.
   1.424 + *
   1.425 + * Spec: ECMAScript Internationalization API Specification, 6.2.4.
   1.426 + */
   1.427 +function DefaultLocale() {
   1.428 +    // The locale of last resort is used if none of the available locales
   1.429 +    // satisfies a request. "en-GB" is used based on the assumptions that
   1.430 +    // English is the most common second language, that both en-GB and en-US
   1.431 +    // are normally available in an implementation, and that en-GB is more
   1.432 +    // representative of the English used in other locales.
   1.433 +    var localeOfLastResort = "en-GB";
   1.434 +
   1.435 +    var locale = RuntimeDefaultLocale();
   1.436 +    if (!IsStructurallyValidLanguageTag(locale))
   1.437 +        return localeOfLastResort;
   1.438 +
   1.439 +    locale = CanonicalizeLanguageTag(locale);
   1.440 +    if (callFunction(std_Object_hasOwnProperty, oldStyleLanguageTagMappings, locale))
   1.441 +        locale = oldStyleLanguageTagMappings[locale];
   1.442 +
   1.443 +    if (!(collatorInternalProperties.availableLocales()[locale] &&
   1.444 +          numberFormatInternalProperties.availableLocales()[locale] &&
   1.445 +          dateTimeFormatInternalProperties.availableLocales()[locale]))
   1.446 +    {
   1.447 +        locale = localeOfLastResort;
   1.448 +    }
   1.449 +    return locale;
   1.450 +}
   1.451 +
   1.452 +
   1.453 +/**
   1.454 + * Verifies that the given string is a well-formed ISO 4217 currency code.
   1.455 + *
   1.456 + * Spec: ECMAScript Internationalization API Specification, 6.3.1.
   1.457 + */
   1.458 +function IsWellFormedCurrencyCode(currency) {
   1.459 +    var c = ToString(currency);
   1.460 +    var normalized = toASCIIUpperCase(c);
   1.461 +    if (normalized.length !== 3)
   1.462 +        return false;
   1.463 +    return !regexp_test_no_statics(/[^A-Z]/, normalized);
   1.464 +}
   1.465 +
   1.466 +
   1.467 +/********** Locale and Parameter Negotiation **********/
   1.468 +
   1.469 +
   1.470 +/**
   1.471 + * Add old-style language tags without script code for locales that in current
   1.472 + * usage would include a script subtag. Returns the availableLocales argument
   1.473 + * provided.
   1.474 + *
   1.475 + * Spec: ECMAScript Internationalization API Specification, 9.1.
   1.476 + */
   1.477 +function addOldStyleLanguageTags(availableLocales) {
   1.478 +    var oldStyleLocales = std_Object_getOwnPropertyNames(oldStyleLanguageTagMappings);
   1.479 +    for (var i = 0; i < oldStyleLocales.length; i++) {
   1.480 +        var oldStyleLocale = oldStyleLocales[i];
   1.481 +        if (availableLocales[oldStyleLanguageTagMappings[oldStyleLocale]])
   1.482 +            availableLocales[oldStyleLocale] = true;
   1.483 +    }
   1.484 +    return availableLocales;
   1.485 +}
   1.486 +
   1.487 +
   1.488 +/**
   1.489 + * Canonicalizes a locale list.
   1.490 + *
   1.491 + * Spec: ECMAScript Internationalization API Specification, 9.2.1.
   1.492 + */
   1.493 +function CanonicalizeLocaleList(locales) {
   1.494 +    if (locales === undefined)
   1.495 +        return new List();
   1.496 +    var seen = new List();
   1.497 +    if (typeof locales === "string")
   1.498 +        locales = [locales];
   1.499 +    var O = ToObject(locales);
   1.500 +    var len = TO_UINT32(O.length);
   1.501 +    var k = 0;
   1.502 +    while (k < len) {
   1.503 +        // Don't call ToString(k) - SpiderMonkey is faster with integers.
   1.504 +        var kPresent = HasProperty(O, k);
   1.505 +        if (kPresent) {
   1.506 +            var kValue = O[k];
   1.507 +            if (!(typeof kValue === "string" || IsObject(kValue)))
   1.508 +                ThrowError(JSMSG_INVALID_LOCALES_ELEMENT);
   1.509 +            var tag = ToString(kValue);
   1.510 +            if (!IsStructurallyValidLanguageTag(tag))
   1.511 +                ThrowError(JSMSG_INVALID_LANGUAGE_TAG, tag);
   1.512 +            tag = CanonicalizeLanguageTag(tag);
   1.513 +            if (seen.indexOf(tag) === -1)
   1.514 +                seen.push(tag);
   1.515 +        }
   1.516 +        k++;
   1.517 +    }
   1.518 +    return seen;
   1.519 +}
   1.520 +
   1.521 +
   1.522 +/**
   1.523 + * Compares a BCP 47 language tag against the locales in availableLocales
   1.524 + * and returns the best available match. Uses the fallback
   1.525 + * mechanism of RFC 4647, section 3.4.
   1.526 + *
   1.527 + * Spec: ECMAScript Internationalization API Specification, 9.2.2.
   1.528 + * Spec: RFC 4647, section 3.4.
   1.529 + */
   1.530 +function BestAvailableLocale(availableLocales, locale) {
   1.531 +    assert(IsStructurallyValidLanguageTag(locale), "invalid BestAvailableLocale locale structure");
   1.532 +    assert(locale === CanonicalizeLanguageTag(locale), "non-canonical BestAvailableLocale locale");
   1.533 +    assert(callFunction(std_String_indexOf, locale, "-u-") === -1, "locale shouldn't contain -u-");
   1.534 +
   1.535 +    var candidate = locale;
   1.536 +    while (true) {
   1.537 +        if (availableLocales[candidate])
   1.538 +            return candidate;
   1.539 +        var pos = callFunction(std_String_lastIndexOf, candidate, "-");
   1.540 +        if (pos === -1)
   1.541 +            return undefined;
   1.542 +        if (pos >= 2 && candidate[pos - 2] === "-")
   1.543 +            pos -= 2;
   1.544 +        candidate = callFunction(std_String_substring, candidate, 0, pos);
   1.545 +    }
   1.546 +}
   1.547 +
   1.548 +
   1.549 +/**
   1.550 + * Compares a BCP 47 language priority list against the set of locales in
   1.551 + * availableLocales and determines the best available language to meet the
   1.552 + * request. Options specified through Unicode extension subsequences are
   1.553 + * ignored in the lookup, but information about such subsequences is returned
   1.554 + * separately.
   1.555 + *
   1.556 + * This variant is based on the Lookup algorithm of RFC 4647 section 3.4.
   1.557 + *
   1.558 + * Spec: ECMAScript Internationalization API Specification, 9.2.3.
   1.559 + * Spec: RFC 4647, section 3.4.
   1.560 + */
   1.561 +function LookupMatcher(availableLocales, requestedLocales) {
   1.562 +    var i = 0;
   1.563 +    var len = requestedLocales.length;
   1.564 +    var availableLocale;
   1.565 +    var locale, noExtensionsLocale;
   1.566 +    while (i < len && availableLocale === undefined) {
   1.567 +        locale = requestedLocales[i];
   1.568 +        noExtensionsLocale = removeUnicodeExtensions(locale);
   1.569 +        availableLocale = BestAvailableLocale(availableLocales, noExtensionsLocale);
   1.570 +        i++;
   1.571 +    }
   1.572 +
   1.573 +    var result = new Record();
   1.574 +    if (availableLocale !== undefined) {
   1.575 +        result.locale = availableLocale;
   1.576 +        if (locale !== noExtensionsLocale) {
   1.577 +            var extensionMatch = regexp_exec_no_statics(unicodeLocaleExtensionSequenceRE, locale);
   1.578 +            var extension = extensionMatch[0];
   1.579 +            var extensionIndex = extensionMatch.index;
   1.580 +            result.extension = extension;
   1.581 +            result.extensionIndex = extensionIndex;
   1.582 +        }
   1.583 +    } else {
   1.584 +        result.locale = DefaultLocale();
   1.585 +    }
   1.586 +    return result;
   1.587 +}
   1.588 +
   1.589 +
   1.590 +/**
   1.591 + * Compares a BCP 47 language priority list against the set of locales in
   1.592 + * availableLocales and determines the best available language to meet the
   1.593 + * request. Options specified through Unicode extension subsequences are
   1.594 + * ignored in the lookup, but information about such subsequences is returned
   1.595 + * separately.
   1.596 + *
   1.597 + * Spec: ECMAScript Internationalization API Specification, 9.2.4.
   1.598 + */
   1.599 +function BestFitMatcher(availableLocales, requestedLocales) {
   1.600 +    // this implementation doesn't have anything better
   1.601 +    return LookupMatcher(availableLocales, requestedLocales);
   1.602 +}
   1.603 +
   1.604 +
   1.605 +/**
   1.606 + * Compares a BCP 47 language priority list against availableLocales and
   1.607 + * determines the best available language to meet the request. Options specified
   1.608 + * through Unicode extension subsequences are negotiated separately, taking the
   1.609 + * caller's relevant extensions and locale data as well as client-provided
   1.610 + * options into consideration.
   1.611 + *
   1.612 + * Spec: ECMAScript Internationalization API Specification, 9.2.5.
   1.613 + */
   1.614 +function ResolveLocale(availableLocales, requestedLocales, options, relevantExtensionKeys, localeData) {
   1.615 +    /*jshint laxbreak: true */
   1.616 +
   1.617 +    // Steps 1-3.
   1.618 +    var matcher = options.localeMatcher;
   1.619 +    var r = (matcher === "lookup")
   1.620 +            ? LookupMatcher(availableLocales, requestedLocales)
   1.621 +            : BestFitMatcher(availableLocales, requestedLocales);
   1.622 +
   1.623 +    // Step 4.
   1.624 +    var foundLocale = r.locale;
   1.625 +
   1.626 +    // Step 5.a.
   1.627 +    var extension = r.extension;
   1.628 +    var extensionIndex, extensionSubtags, extensionSubtagsLength;
   1.629 +
   1.630 +    // Step 5.
   1.631 +    if (extension !== undefined) {
   1.632 +        // Step 5.b.
   1.633 +        extensionIndex = r.extensionIndex;
   1.634 +
   1.635 +        // Steps 5.d-e.
   1.636 +        extensionSubtags = callFunction(std_String_split, extension, "-");
   1.637 +        extensionSubtagsLength = extensionSubtags.length;
   1.638 +    }
   1.639 +
   1.640 +    // Steps 6-7.
   1.641 +    var result = new Record();
   1.642 +    result.dataLocale = foundLocale;
   1.643 +
   1.644 +    // Step 8.
   1.645 +    var supportedExtension = "-u";
   1.646 +
   1.647 +    // Steps 9-11.
   1.648 +    var i = 0;
   1.649 +    var len = relevantExtensionKeys.length;
   1.650 +    while (i < len) {
   1.651 +        // Steps 11.a-c.
   1.652 +        var key = relevantExtensionKeys[i];
   1.653 +
   1.654 +        // In this implementation, localeData is a function, not an object.
   1.655 +        var foundLocaleData = localeData(foundLocale);
   1.656 +        var keyLocaleData = foundLocaleData[key];
   1.657 +
   1.658 +        // Locale data provides default value.
   1.659 +        // Step 11.d.
   1.660 +        var value = keyLocaleData[0];
   1.661 +
   1.662 +        // Locale tag may override.
   1.663 +
   1.664 +        // Step 11.e.
   1.665 +        var supportedExtensionAddition = "";
   1.666 +
   1.667 +        // Step 11.f is implemented by Utilities.js.
   1.668 +
   1.669 +        var valuePos;
   1.670 +
   1.671 +        // Step 11.g.
   1.672 +        if (extensionSubtags !== undefined) {
   1.673 +            // Step 11.g.i.
   1.674 +            var keyPos = callFunction(std_Array_indexOf, extensionSubtags, key);
   1.675 +
   1.676 +            // Step 11.g.ii.
   1.677 +            if (keyPos !== -1) {
   1.678 +                // Step 11.g.ii.1.
   1.679 +                if (keyPos + 1 < extensionSubtagsLength &&
   1.680 +                    extensionSubtags[keyPos + 1].length > 2)
   1.681 +                {
   1.682 +                    // Step 11.g.ii.1.a.
   1.683 +                    var requestedValue = extensionSubtags[keyPos + 1];
   1.684 +
   1.685 +                    // Step 11.g.ii.1.b.
   1.686 +                    valuePos = callFunction(std_Array_indexOf, keyLocaleData, requestedValue);
   1.687 +
   1.688 +                    // Step 11.g.ii.1.c.
   1.689 +                    if (valuePos !== -1) {
   1.690 +                        value = requestedValue;
   1.691 +                        supportedExtensionAddition = "-" + key + "-" + value;
   1.692 +                    }
   1.693 +                } else {
   1.694 +                    // Step 11.g.ii.2.
   1.695 +
   1.696 +                    // According to the LDML spec, if there's no type value,
   1.697 +                    // and true is an allowed value, it's used.
   1.698 +
   1.699 +                    // Step 11.g.ii.2.a.
   1.700 +                    valuePos = callFunction(std_Array_indexOf, keyLocaleData, "true");
   1.701 +
   1.702 +                    // Step 11.g.ii.2.b.
   1.703 +                    if (valuePos !== -1)
   1.704 +                        value = "true";
   1.705 +                }
   1.706 +            }
   1.707 +        }
   1.708 +
   1.709 +        // Options override all.
   1.710 +
   1.711 +        // Step 11.h.i.
   1.712 +        var optionsValue = options[key];
   1.713 +
   1.714 +        // Step 11.h, 11.h.ii.
   1.715 +        if (optionsValue !== undefined &&
   1.716 +            callFunction(std_Array_indexOf, keyLocaleData, optionsValue) !== -1)
   1.717 +        {
   1.718 +            // Step 11.h.ii.1.
   1.719 +            if (optionsValue !== value) {
   1.720 +                value = optionsValue;
   1.721 +                supportedExtensionAddition = "";
   1.722 +            }
   1.723 +        }
   1.724 +
   1.725 +        // Steps 11.i-k.
   1.726 +        result[key] = value;
   1.727 +        supportedExtension += supportedExtensionAddition;
   1.728 +        i++;
   1.729 +    }
   1.730 +
   1.731 +    // Step 12.
   1.732 +    if (supportedExtension.length > 2) {
   1.733 +        var preExtension = callFunction(std_String_substring, foundLocale, 0, extensionIndex);
   1.734 +        var postExtension = callFunction(std_String_substring, foundLocale, extensionIndex);
   1.735 +        foundLocale = preExtension + supportedExtension + postExtension;
   1.736 +    }
   1.737 +
   1.738 +    // Steps 13-14.
   1.739 +    result.locale = foundLocale;
   1.740 +    return result;
   1.741 +}
   1.742 +
   1.743 +
   1.744 +/**
   1.745 + * Returns the subset of requestedLocales for which availableLocales has a
   1.746 + * matching (possibly fallback) locale. Locales appear in the same order in the
   1.747 + * returned list as in the input list.
   1.748 + *
   1.749 + * Spec: ECMAScript Internationalization API Specification, 9.2.6.
   1.750 + */
   1.751 +function LookupSupportedLocales(availableLocales, requestedLocales) {
   1.752 +    // Steps 1-2.
   1.753 +    var len = requestedLocales.length;
   1.754 +    var subset = new List();
   1.755 +
   1.756 +    // Steps 3-4.
   1.757 +    var k = 0;
   1.758 +    while (k < len) {
   1.759 +        // Steps 4.a-b.
   1.760 +        var locale = requestedLocales[k];
   1.761 +        var noExtensionsLocale = removeUnicodeExtensions(locale);
   1.762 +
   1.763 +        // Step 4.c-d.
   1.764 +        var availableLocale = BestAvailableLocale(availableLocales, noExtensionsLocale);
   1.765 +        if (availableLocale !== undefined)
   1.766 +            subset.push(locale);
   1.767 +
   1.768 +        // Step 4.e.
   1.769 +        k++;
   1.770 +    }
   1.771 +
   1.772 +    // Steps 5-6.
   1.773 +    return subset.slice(0);
   1.774 +}
   1.775 +
   1.776 +
   1.777 +/**
   1.778 + * Returns the subset of requestedLocales for which availableLocales has a
   1.779 + * matching (possibly fallback) locale. Locales appear in the same order in the
   1.780 + * returned list as in the input list.
   1.781 + *
   1.782 + * Spec: ECMAScript Internationalization API Specification, 9.2.7.
   1.783 + */
   1.784 +function BestFitSupportedLocales(availableLocales, requestedLocales) {
   1.785 +    // don't have anything better
   1.786 +    return LookupSupportedLocales(availableLocales, requestedLocales);
   1.787 +}
   1.788 +
   1.789 +
   1.790 +/**
   1.791 + * Returns the subset of requestedLocales for which availableLocales has a
   1.792 + * matching (possibly fallback) locale. Locales appear in the same order in the
   1.793 + * returned list as in the input list.
   1.794 + *
   1.795 + * Spec: ECMAScript Internationalization API Specification, 9.2.8.
   1.796 + */
   1.797 +function SupportedLocales(availableLocales, requestedLocales, options) {
   1.798 +    /*jshint laxbreak: true */
   1.799 +
   1.800 +    // Step 1.
   1.801 +    var matcher;
   1.802 +    if (options !== undefined) {
   1.803 +        // Steps 1.a-b.
   1.804 +        options = ToObject(options);
   1.805 +        matcher = options.localeMatcher;
   1.806 +
   1.807 +        // Step 1.c.
   1.808 +        if (matcher !== undefined) {
   1.809 +            matcher = ToString(matcher);
   1.810 +            if (matcher !== "lookup" && matcher !== "best fit")
   1.811 +                ThrowError(JSMSG_INVALID_LOCALE_MATCHER, matcher);
   1.812 +        }
   1.813 +    }
   1.814 +
   1.815 +    // Steps 2-3.
   1.816 +    var subset = (matcher === undefined || matcher === "best fit")
   1.817 +                 ? BestFitSupportedLocales(availableLocales, requestedLocales)
   1.818 +                 : LookupSupportedLocales(availableLocales, requestedLocales);
   1.819 +
   1.820 +    // Step 4.
   1.821 +    for (var i = 0; i < subset.length; i++) {
   1.822 +        _DefineValueProperty(subset, i, subset[i],
   1.823 +                             ATTR_ENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE);
   1.824 +    }
   1.825 +    _DefineValueProperty(subset, "length", subset.length,
   1.826 +                         ATTR_NONENUMERABLE | ATTR_NONCONFIGURABLE | ATTR_NONWRITABLE);
   1.827 +
   1.828 +    // Step 5.
   1.829 +    return subset;
   1.830 +}
   1.831 +
   1.832 +
   1.833 +/**
   1.834 + * Extracts a property value from the provided options object, converts it to
   1.835 + * the required type, checks whether it is one of a list of allowed values,
   1.836 + * and fills in a fallback value if necessary.
   1.837 + *
   1.838 + * Spec: ECMAScript Internationalization API Specification, 9.2.9.
   1.839 + */
   1.840 +function GetOption(options, property, type, values, fallback) {
   1.841 +    // Step 1.
   1.842 +    var value = options[property];
   1.843 +
   1.844 +    // Step 2.
   1.845 +    if (value !== undefined) {
   1.846 +        // Steps 2.a-c.
   1.847 +        if (type === "boolean")
   1.848 +            value = ToBoolean(value);
   1.849 +        else if (type === "string")
   1.850 +            value = ToString(value);
   1.851 +        else
   1.852 +            assert(false, "GetOption");
   1.853 +
   1.854 +        // Step 2.d.
   1.855 +        if (values !== undefined && callFunction(std_Array_indexOf, values, value) === -1)
   1.856 +            ThrowError(JSMSG_INVALID_OPTION_VALUE, property, value);
   1.857 +
   1.858 +        // Step 2.e.
   1.859 +        return value;
   1.860 +    }
   1.861 +
   1.862 +    // Step 3.
   1.863 +    return fallback;
   1.864 +}
   1.865 +
   1.866 +/**
   1.867 + * Extracts a property value from the provided options object, converts it to a
   1.868 + * Number value, checks whether it is in the allowed range, and fills in a
   1.869 + * fallback value if necessary.
   1.870 + *
   1.871 + * Spec: ECMAScript Internationalization API Specification, 9.2.10.
   1.872 + */
   1.873 +function GetNumberOption(options, property, minimum, maximum, fallback) {
   1.874 +    assert(typeof minimum === "number", "GetNumberOption");
   1.875 +    assert(typeof maximum === "number", "GetNumberOption");
   1.876 +    assert(fallback === undefined || (fallback >= minimum && fallback <= maximum), "GetNumberOption");
   1.877 +
   1.878 +    // Step 1.
   1.879 +    var value = options[property];
   1.880 +
   1.881 +    // Step 2.
   1.882 +    if (value !== undefined) {
   1.883 +        value = ToNumber(value);
   1.884 +        if (std_isNaN(value) || value < minimum || value > maximum)
   1.885 +            ThrowError(JSMSG_INVALID_DIGITS_VALUE, value);
   1.886 +        return std_Math_floor(value);
   1.887 +    }
   1.888 +
   1.889 +    // Step 3.
   1.890 +    return fallback;
   1.891 +}
   1.892 +
   1.893 +
   1.894 +/********** Property access for Intl objects **********/
   1.895 +
   1.896 +
   1.897 +/**
   1.898 + * Set a normal public property p of o to value v, but use Object.defineProperty
   1.899 + * to avoid interference from setters on Object.prototype.
   1.900 + */
   1.901 +function defineProperty(o, p, v) {
   1.902 +    _DefineValueProperty(o, p, v, ATTR_ENUMERABLE | ATTR_CONFIGURABLE | ATTR_WRITABLE);
   1.903 +}
   1.904 +
   1.905 +
   1.906 +/**
   1.907 + * Weak map used to track the initialize-as-Intl status (and, if an object has
   1.908 + * been so initialized, the Intl-specific internal properties) of all objects.
   1.909 + * Presence of an object as a key within this map indicates that the object has
   1.910 + * its [[initializedIntlObject]] internal property set to true.  The associated
   1.911 + * value is an object whose structure is documented in |initializeIntlObject|
   1.912 + * below.
   1.913 + *
   1.914 + * Ideally we'd be using private symbols for internal properties, but
   1.915 + * SpiderMonkey doesn't have those yet.
   1.916 + */
   1.917 +var internalsMap = new WeakMap();
   1.918 +
   1.919 +
   1.920 +/**
   1.921 + * Set the [[initializedIntlObject]] internal property of |obj| to true.
   1.922 + */
   1.923 +function initializeIntlObject(obj) {
   1.924 +    assert(IsObject(obj), "Non-object passed to initializeIntlObject");
   1.925 +
   1.926 +    // Intl-initialized objects are weird.  They have [[initializedIntlObject]]
   1.927 +    // set on them, but they don't *necessarily* have any other properties.
   1.928 +
   1.929 +    var internals = std_Object_create(null);
   1.930 +
   1.931 +    // The meaning of an internals object for an object |obj| is as follows.
   1.932 +    //
   1.933 +    // If the .type is "partial", |obj| has [[initializedIntlObject]] set but
   1.934 +    // nothing else.  No other property of |internals| can be used.  (This
   1.935 +    // occurs when InitializeCollator or similar marks an object as
   1.936 +    // [[initializedIntlObject]] but fails before marking it as the appropriate
   1.937 +    // more-specific type ["Collator", "DateTimeFormat", "NumberFormat"].)
   1.938 +    //
   1.939 +    // Otherwise, the .type indicates the type of Intl object that |obj| is:
   1.940 +    // "Collator", "DateTimeFormat", or "NumberFormat" (likely with more coming
   1.941 +    // in future Intl specs).  In these cases |obj| *conceptually* also has
   1.942 +    // [[initializedCollator]] or similar set, and all the other properties
   1.943 +    // implied by that.
   1.944 +    //
   1.945 +    // If |internals| doesn't have a "partial" .type, two additional properties
   1.946 +    // have meaning.  The .lazyData property stores information needed to
   1.947 +    // compute -- without observable side effects -- the actual internal Intl
   1.948 +    // properties of |obj|.  If it is non-null, then the actual internal
   1.949 +    // properties haven't been computed, and .lazyData must be processed by
   1.950 +    // |setInternalProperties| before internal Intl property values are
   1.951 +    // available.  If it is null, then the .internalProps property contains an
   1.952 +    // object whose properties are the internal Intl properties of |obj|.
   1.953 +
   1.954 +    internals.type = "partial";
   1.955 +    internals.lazyData = null;
   1.956 +    internals.internalProps = null;
   1.957 +
   1.958 +    callFunction(std_WeakMap_set, internalsMap, obj, internals);
   1.959 +    return internals;
   1.960 +}
   1.961 +
   1.962 +
   1.963 +/**
   1.964 + * Mark |internals| as having the given type and lazy data.
   1.965 + */
   1.966 +function setLazyData(internals, type, lazyData)
   1.967 +{
   1.968 +    assert(internals.type === "partial", "can't set lazy data for anything but a newborn");
   1.969 +    assert(type === "Collator" || type === "DateTimeFormat" || type == "NumberFormat", "bad type");
   1.970 +    assert(IsObject(lazyData), "non-object lazy data");
   1.971 +
   1.972 +    // Set in reverse order so that the .type change is a barrier.
   1.973 +    internals.lazyData = lazyData;
   1.974 +    internals.type = type;
   1.975 +}
   1.976 +
   1.977 +
   1.978 +/**
   1.979 + * Set the internal properties object for an |internals| object previously
   1.980 + * associated with lazy data.
   1.981 + */
   1.982 +function setInternalProperties(internals, internalProps)
   1.983 +{
   1.984 +    assert(internals.type !== "partial", "newborn internals can't have computed internals");
   1.985 +    assert(IsObject(internals.lazyData), "lazy data must exist already");
   1.986 +    assert(IsObject(internalProps), "internalProps argument should be an object");
   1.987 +
   1.988 +    // Set in reverse order so that the .lazyData nulling is a barrier.
   1.989 +    internals.internalProps = internalProps;
   1.990 +    internals.lazyData = null;
   1.991 +}
   1.992 +
   1.993 +
   1.994 +/**
   1.995 + * Get the existing internal properties out of a non-newborn |internals|, or
   1.996 + * null if none have been computed.
   1.997 + */
   1.998 +function maybeInternalProperties(internals)
   1.999 +{
  1.1000 +    assert(IsObject(internals), "non-object passed to maybeInternalProperties");
  1.1001 +    assert(internals.type !== "partial", "maybeInternalProperties must only be used on completely-initialized internals objects");
  1.1002 +    var lazyData = internals.lazyData;
  1.1003 +    if (lazyData)
  1.1004 +        return null;
  1.1005 +    assert(IsObject(internals.internalProps), "missing lazy data and computed internals");
  1.1006 +    return internals.internalProps;
  1.1007 +}
  1.1008 +
  1.1009 +
  1.1010 +/**
  1.1011 + * Return whether |obj| has an[[initializedIntlObject]] property set to true.
  1.1012 + */
  1.1013 +function isInitializedIntlObject(obj) {
  1.1014 +#ifdef DEBUG
  1.1015 +    var internals = callFunction(std_WeakMap_get, internalsMap, obj);
  1.1016 +    if (IsObject(internals)) {
  1.1017 +        assert(callFunction(std_Object_hasOwnProperty, internals, "type"), "missing type");
  1.1018 +        var type = internals.type;
  1.1019 +        assert(type === "partial" || type === "Collator" || type === "DateTimeFormat" || type === "NumberFormat", "unexpected type");
  1.1020 +        assert(callFunction(std_Object_hasOwnProperty, internals, "lazyData"), "missing lazyData");
  1.1021 +        assert(callFunction(std_Object_hasOwnProperty, internals, "internalProps"), "missing internalProps");
  1.1022 +    } else {
  1.1023 +        assert(internals === undefined, "bad mapping for |obj|");
  1.1024 +    }
  1.1025 +#endif
  1.1026 +    return callFunction(std_WeakMap_has, internalsMap, obj);
  1.1027 +}
  1.1028 +
  1.1029 +
  1.1030 +/**
  1.1031 + * Check that |obj| meets the requirements for "this Collator object", "this
  1.1032 + * NumberFormat object", or "this DateTimeFormat object" as used in the method
  1.1033 + * with the given name.  Throw a TypeError if |obj| doesn't meet these
  1.1034 + * requirements.  But if it does, return |obj|'s internals object (*not* the
  1.1035 + * object holding its internal properties!), associated with it by
  1.1036 + * |internalsMap|, with structure specified above.
  1.1037 + *
  1.1038 + * Spec: ECMAScript Internationalization API Specification, 10.3.
  1.1039 + * Spec: ECMAScript Internationalization API Specification, 11.3.
  1.1040 + * Spec: ECMAScript Internationalization API Specification, 12.3.
  1.1041 + */
  1.1042 +function getIntlObjectInternals(obj, className, methodName) {
  1.1043 +    assert(typeof className === "string", "bad className for getIntlObjectInternals");
  1.1044 +
  1.1045 +    var internals = callFunction(std_WeakMap_get, internalsMap, obj);
  1.1046 +    assert(internals === undefined || isInitializedIntlObject(obj), "bad mapping in internalsMap");
  1.1047 +
  1.1048 +    if (internals === undefined || internals.type !== className)
  1.1049 +        ThrowError(JSMSG_INTL_OBJECT_NOT_INITED, className, methodName, className);
  1.1050 +
  1.1051 +    return internals;
  1.1052 +}
  1.1053 +
  1.1054 +
  1.1055 +/**
  1.1056 + * Get the internal properties of known-Intl object |obj|.  For use only by
  1.1057 + * C++ code that knows what it's doing!
  1.1058 + */
  1.1059 +function getInternals(obj)
  1.1060 +{
  1.1061 +    assert(isInitializedIntlObject(obj), "for use only on guaranteed Intl objects");
  1.1062 +
  1.1063 +    var internals = callFunction(std_WeakMap_get, internalsMap, obj);
  1.1064 +
  1.1065 +    assert(internals.type !== "partial", "must have been successfully initialized");
  1.1066 +    var lazyData = internals.lazyData;
  1.1067 +    if (!lazyData)
  1.1068 +        return internals.internalProps;
  1.1069 +
  1.1070 +    var internalProps;
  1.1071 +    var type = internals.type;
  1.1072 +    if (type === "Collator")
  1.1073 +        internalProps = resolveCollatorInternals(lazyData)
  1.1074 +    else if (type === "DateTimeFormat")
  1.1075 +        internalProps = resolveDateTimeFormatInternals(lazyData)
  1.1076 +    else
  1.1077 +        internalProps = resolveNumberFormatInternals(lazyData);
  1.1078 +    setInternalProperties(internals, internalProps);
  1.1079 +    return internalProps;
  1.1080 +}
  1.1081 +
  1.1082 +
  1.1083 +/********** Intl.Collator **********/
  1.1084 +
  1.1085 +
  1.1086 +/**
  1.1087 + * Mapping from Unicode extension keys for collation to options properties,
  1.1088 + * their types and permissible values.
  1.1089 + *
  1.1090 + * Spec: ECMAScript Internationalization API Specification, 10.1.1.
  1.1091 + */
  1.1092 +var collatorKeyMappings = {
  1.1093 +    kn: {property: "numeric", type: "boolean"},
  1.1094 +    kf: {property: "caseFirst", type: "string", values: ["upper", "lower", "false"]}
  1.1095 +};
  1.1096 +
  1.1097 +
  1.1098 +/**
  1.1099 + * Compute an internal properties object from |lazyCollatorData|.
  1.1100 + */
  1.1101 +function resolveCollatorInternals(lazyCollatorData)
  1.1102 +{
  1.1103 +    assert(IsObject(lazyCollatorData), "lazy data not an object?");
  1.1104 +
  1.1105 +    var internalProps = std_Object_create(null);
  1.1106 +
  1.1107 +    // Step 7.
  1.1108 +    internalProps.usage = lazyCollatorData.usage;
  1.1109 +
  1.1110 +    // Step 8.
  1.1111 +    var Collator = collatorInternalProperties;
  1.1112 +
  1.1113 +    // Step 9.
  1.1114 +    var collatorIsSorting = lazyCollatorData.usage === "sort";
  1.1115 +    var localeData = collatorIsSorting
  1.1116 +                     ? Collator.sortLocaleData
  1.1117 +                     : Collator.searchLocaleData;
  1.1118 +
  1.1119 +    // Compute effective locale.
  1.1120 +    // Step 14.
  1.1121 +    var relevantExtensionKeys = Collator.relevantExtensionKeys;
  1.1122 +
  1.1123 +    // Step 15.
  1.1124 +    var r = ResolveLocale(Collator.availableLocales(),
  1.1125 +                          lazyCollatorData.requestedLocales,
  1.1126 +                          lazyCollatorData.opt,
  1.1127 +                          relevantExtensionKeys,
  1.1128 +                          localeData);
  1.1129 +
  1.1130 +    // Step 16.
  1.1131 +    internalProps.locale = r.locale;
  1.1132 +
  1.1133 +    // Steps 17-19.
  1.1134 +    var key, property, value, mapping;
  1.1135 +    var i = 0, len = relevantExtensionKeys.length;
  1.1136 +    while (i < len) {
  1.1137 +        // Step 19.a.
  1.1138 +        key = relevantExtensionKeys[i];
  1.1139 +        if (key === "co") {
  1.1140 +            // Step 19.b.
  1.1141 +            property = "collation";
  1.1142 +            value = r.co === null ? "default" : r.co;
  1.1143 +        } else {
  1.1144 +            // Step 19.c.
  1.1145 +            mapping = collatorKeyMappings[key];
  1.1146 +            property = mapping.property;
  1.1147 +            value = r[key];
  1.1148 +            if (mapping.type === "boolean")
  1.1149 +                value = value === "true";
  1.1150 +        }
  1.1151 +
  1.1152 +        // Step 19.d.
  1.1153 +        internalProps[property] = value;
  1.1154 +
  1.1155 +        // Step 19.e.
  1.1156 +        i++;
  1.1157 +    }
  1.1158 +
  1.1159 +    // Compute remaining collation options.
  1.1160 +    // Steps 21-22.
  1.1161 +    var s = lazyCollatorData.rawSensitivity;
  1.1162 +    if (s === undefined) {
  1.1163 +        if (collatorIsSorting) {
  1.1164 +            // Step 21.a.
  1.1165 +            s = "variant";
  1.1166 +        } else {
  1.1167 +            // Step 21.b.
  1.1168 +            var dataLocale = r.dataLocale;
  1.1169 +            var dataLocaleData = localeData(dataLocale);
  1.1170 +            s = dataLocaleData.sensitivity;
  1.1171 +        }
  1.1172 +    }
  1.1173 +    internalProps.sensitivity = s;
  1.1174 +
  1.1175 +    // Step 24.
  1.1176 +    internalProps.ignorePunctuation = lazyCollatorData.ignorePunctuation;
  1.1177 +
  1.1178 +    // Step 25.
  1.1179 +    internalProps.boundFormat = undefined;
  1.1180 +
  1.1181 +    // The caller is responsible for associating |internalProps| with the right
  1.1182 +    // object using |setInternalProperties|.
  1.1183 +    return internalProps;
  1.1184 +}
  1.1185 +
  1.1186 +
  1.1187 +/**
  1.1188 + * Returns an object containing the Collator internal properties of |obj|, or
  1.1189 + * throws a TypeError if |obj| isn't Collator-initialized.
  1.1190 + */
  1.1191 +function getCollatorInternals(obj, methodName) {
  1.1192 +    var internals = getIntlObjectInternals(obj, "Collator", methodName);
  1.1193 +    assert(internals.type === "Collator", "bad type escaped getIntlObjectInternals");
  1.1194 +
  1.1195 +    // If internal properties have already been computed, use them.
  1.1196 +    var internalProps = maybeInternalProperties(internals);
  1.1197 +    if (internalProps)
  1.1198 +        return internalProps;
  1.1199 +
  1.1200 +    // Otherwise it's time to fully create them.
  1.1201 +    internalProps = resolveCollatorInternals(internals.lazyData);
  1.1202 +    setInternalProperties(internals, internalProps);
  1.1203 +    return internalProps;
  1.1204 +}
  1.1205 +
  1.1206 +
  1.1207 +/**
  1.1208 + * Initializes an object as a Collator.
  1.1209 + *
  1.1210 + * This method is complicated a moderate bit by its implementing initialization
  1.1211 + * as a *lazy* concept.  Everything that must happen now, does -- but we defer
  1.1212 + * all the work we can until the object is actually used as a Collator.  This
  1.1213 + * later work occurs in |resolveCollatorInternals|; steps not noted here occur
  1.1214 + * there.
  1.1215 + *
  1.1216 + * Spec: ECMAScript Internationalization API Specification, 10.1.1.
  1.1217 + */
  1.1218 +function InitializeCollator(collator, locales, options) {
  1.1219 +    assert(IsObject(collator), "InitializeCollator");
  1.1220 +
  1.1221 +    // Step 1.
  1.1222 +    if (isInitializedIntlObject(collator))
  1.1223 +        ThrowError(JSMSG_INTL_OBJECT_REINITED);
  1.1224 +
  1.1225 +    // Step 2.
  1.1226 +    var internals = initializeIntlObject(collator);
  1.1227 +
  1.1228 +    // Lazy Collator data has the following structure:
  1.1229 +    //
  1.1230 +    //   {
  1.1231 +    //     requestedLocales: List of locales,
  1.1232 +    //     usage: "sort" / "search",
  1.1233 +    //     opt: // opt object computed in InitializeCollator
  1.1234 +    //       {
  1.1235 +    //         localeMatcher: "lookup" / "best fit",
  1.1236 +    //         kn: true / false / undefined,
  1.1237 +    //         kf: "upper" / "lower" / "false" / undefined
  1.1238 +    //       }
  1.1239 +    //     rawSensitivity: "base" / "accent" / "case" / "variant" / undefined,
  1.1240 +    //     ignorePunctuation: true / false
  1.1241 +    //   }
  1.1242 +    //
  1.1243 +    // Note that lazy data is only installed as a final step of initialization,
  1.1244 +    // so every Collator lazy data object has *all* these properties, never a
  1.1245 +    // subset of them.
  1.1246 +    var lazyCollatorData = std_Object_create(null);
  1.1247 +
  1.1248 +    // Step 3.
  1.1249 +    var requestedLocales = CanonicalizeLocaleList(locales);
  1.1250 +    lazyCollatorData.requestedLocales = requestedLocales;
  1.1251 +
  1.1252 +    // Steps 4-5.
  1.1253 +    //
  1.1254 +    // If we ever need more speed here at startup, we should try to detect the
  1.1255 +    // case where |options === undefined| and Object.prototype hasn't been
  1.1256 +    // mucked with.  (|options| is fully consumed in this method, so it's not a
  1.1257 +    // concern that Object.prototype might be touched between now and when
  1.1258 +    // |resolveCollatorInternals| is called.)  For now, just keep it simple.
  1.1259 +    if (options === undefined)
  1.1260 +        options = {};
  1.1261 +    else
  1.1262 +        options = ToObject(options);
  1.1263 +
  1.1264 +    // Compute options that impact interpretation of locale.
  1.1265 +    // Step 6.
  1.1266 +    var u = GetOption(options, "usage", "string", ["sort", "search"], "sort");
  1.1267 +    lazyCollatorData.usage = u;
  1.1268 +
  1.1269 +    // Step 10.
  1.1270 +    var opt = new Record();
  1.1271 +    lazyCollatorData.opt = opt;
  1.1272 +
  1.1273 +    // Steps 11-12.
  1.1274 +    var matcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit");
  1.1275 +    opt.localeMatcher = matcher;
  1.1276 +
  1.1277 +    // Step 13, unrolled.
  1.1278 +    var numericValue = GetOption(options, "numeric", "boolean", undefined, undefined);
  1.1279 +    if (numericValue !== undefined)
  1.1280 +        numericValue = callFunction(std_Boolean_toString, numericValue);
  1.1281 +    opt.kn = numericValue;
  1.1282 +
  1.1283 +    var caseFirstValue = GetOption(options, "caseFirst", "string", ["upper", "lower", "false"], undefined);
  1.1284 +    opt.kf = caseFirstValue;
  1.1285 +
  1.1286 +    // Compute remaining collation options.
  1.1287 +    // Step 20.
  1.1288 +    var s = GetOption(options, "sensitivity", "string",
  1.1289 +                      ["base", "accent", "case", "variant"], undefined);
  1.1290 +    lazyCollatorData.rawSensitivity = s;
  1.1291 +
  1.1292 +    // Step 23.
  1.1293 +    var ip = GetOption(options, "ignorePunctuation", "boolean", undefined, false);
  1.1294 +    lazyCollatorData.ignorePunctuation = ip;
  1.1295 +
  1.1296 +    // Step 26.
  1.1297 +    //
  1.1298 +    // We've done everything that must be done now: mark the lazy data as fully
  1.1299 +    // computed and install it.
  1.1300 +    setLazyData(internals, "Collator", lazyCollatorData);
  1.1301 +}
  1.1302 +
  1.1303 +
  1.1304 +/**
  1.1305 + * Returns the subset of the given locale list for which this locale list has a
  1.1306 + * matching (possibly fallback) locale. Locales appear in the same order in the
  1.1307 + * returned list as in the input list.
  1.1308 + *
  1.1309 + * Spec: ECMAScript Internationalization API Specification, 10.2.2.
  1.1310 + */
  1.1311 +function Intl_Collator_supportedLocalesOf(locales /*, options*/) {
  1.1312 +    var options = arguments.length > 1 ? arguments[1] : undefined;
  1.1313 +
  1.1314 +    var availableLocales = collatorInternalProperties.availableLocales();
  1.1315 +    var requestedLocales = CanonicalizeLocaleList(locales);
  1.1316 +    return SupportedLocales(availableLocales, requestedLocales, options);
  1.1317 +}
  1.1318 +
  1.1319 +
  1.1320 +/**
  1.1321 + * Collator internal properties.
  1.1322 + *
  1.1323 + * Spec: ECMAScript Internationalization API Specification, 9.1 and 10.2.3.
  1.1324 + */
  1.1325 +var collatorInternalProperties = {
  1.1326 +    sortLocaleData: collatorSortLocaleData,
  1.1327 +    searchLocaleData: collatorSearchLocaleData,
  1.1328 +    _availableLocales: null,
  1.1329 +    availableLocales: function()
  1.1330 +    {
  1.1331 +        var locales = this._availableLocales;
  1.1332 +        if (locales)
  1.1333 +            return locales;
  1.1334 +        return (this._availableLocales =
  1.1335 +          addOldStyleLanguageTags(intl_Collator_availableLocales()));
  1.1336 +    },
  1.1337 +    relevantExtensionKeys: ["co", "kn"]
  1.1338 +};
  1.1339 +
  1.1340 +
  1.1341 +function collatorSortLocaleData(locale) {
  1.1342 +    var collations = intl_availableCollations(locale);
  1.1343 +    callFunction(std_Array_unshift, collations, null);
  1.1344 +    return {
  1.1345 +        co: collations,
  1.1346 +        kn: ["false", "true"]
  1.1347 +    };
  1.1348 +}
  1.1349 +
  1.1350 +
  1.1351 +function collatorSearchLocaleData(locale) {
  1.1352 +    return {
  1.1353 +        co: [null],
  1.1354 +        kn: ["false", "true"],
  1.1355 +        // In theory the default sensitivity is locale dependent;
  1.1356 +        // in reality the CLDR/ICU default strength is always tertiary.
  1.1357 +        sensitivity: "variant"
  1.1358 +    };
  1.1359 +}
  1.1360 +
  1.1361 +
  1.1362 +/**
  1.1363 + * Function to be bound and returned by Intl.Collator.prototype.format.
  1.1364 + *
  1.1365 + * Spec: ECMAScript Internationalization API Specification, 12.3.2.
  1.1366 + */
  1.1367 +function collatorCompareToBind(x, y) {
  1.1368 +    // Steps 1.a.i-ii implemented by ECMAScript declaration binding instantiation,
  1.1369 +    // ES5.1 10.5, step 4.d.ii.
  1.1370 +
  1.1371 +    // Step 1.a.iii-v.
  1.1372 +    var X = ToString(x);
  1.1373 +    var Y = ToString(y);
  1.1374 +    return intl_CompareStrings(this, X, Y);
  1.1375 +}
  1.1376 +
  1.1377 +
  1.1378 +/**
  1.1379 + * Returns a function bound to this Collator that compares x (converted to a
  1.1380 + * String value) and y (converted to a String value),
  1.1381 + * and returns a number less than 0 if x < y, 0 if x = y, or a number greater
  1.1382 + * than 0 if x > y according to the sort order for the locale and collation
  1.1383 + * options of this Collator object.
  1.1384 + *
  1.1385 + * Spec: ECMAScript Internationalization API Specification, 10.3.2.
  1.1386 + */
  1.1387 +function Intl_Collator_compare_get() {
  1.1388 +    // Check "this Collator object" per introduction of section 10.3.
  1.1389 +    var internals = getCollatorInternals(this, "compare");
  1.1390 +
  1.1391 +    // Step 1.
  1.1392 +    if (internals.boundCompare === undefined) {
  1.1393 +        // Step 1.a.
  1.1394 +        var F = collatorCompareToBind;
  1.1395 +
  1.1396 +        // Step 1.b-d.
  1.1397 +        var bc = callFunction(std_Function_bind, F, this);
  1.1398 +        internals.boundCompare = bc;
  1.1399 +    }
  1.1400 +
  1.1401 +    // Step 2.
  1.1402 +    return internals.boundCompare;
  1.1403 +}
  1.1404 +
  1.1405 +
  1.1406 +/**
  1.1407 + * Returns the resolved options for a Collator object.
  1.1408 + *
  1.1409 + * Spec: ECMAScript Internationalization API Specification, 10.3.3 and 10.4.
  1.1410 + */
  1.1411 +function Intl_Collator_resolvedOptions() {
  1.1412 +    // Check "this Collator object" per introduction of section 10.3.
  1.1413 +    var internals = getCollatorInternals(this, "resolvedOptions");
  1.1414 +
  1.1415 +    var result = {
  1.1416 +        locale: internals.locale,
  1.1417 +        usage: internals.usage,
  1.1418 +        sensitivity: internals.sensitivity,
  1.1419 +        ignorePunctuation: internals.ignorePunctuation
  1.1420 +    };
  1.1421 +
  1.1422 +    var relevantExtensionKeys = collatorInternalProperties.relevantExtensionKeys;
  1.1423 +    for (var i = 0; i < relevantExtensionKeys.length; i++) {
  1.1424 +        var key = relevantExtensionKeys[i];
  1.1425 +        var property = (key === "co") ? "collation" : collatorKeyMappings[key].property;
  1.1426 +        defineProperty(result, property, internals[property]);
  1.1427 +    }
  1.1428 +    return result;
  1.1429 +}
  1.1430 +
  1.1431 +
  1.1432 +/********** Intl.NumberFormat **********/
  1.1433 +
  1.1434 +
  1.1435 +/**
  1.1436 + * NumberFormat internal properties.
  1.1437 + *
  1.1438 + * Spec: ECMAScript Internationalization API Specification, 9.1 and 11.2.3.
  1.1439 + */
  1.1440 +var numberFormatInternalProperties = {
  1.1441 +    localeData: numberFormatLocaleData,
  1.1442 +    _availableLocales: null,
  1.1443 +    availableLocales: function()
  1.1444 +    {
  1.1445 +        var locales = this._availableLocales;
  1.1446 +        if (locales)
  1.1447 +            return locales;
  1.1448 +        return (this._availableLocales =
  1.1449 +          addOldStyleLanguageTags(intl_NumberFormat_availableLocales()));
  1.1450 +    },
  1.1451 +    relevantExtensionKeys: ["nu"]
  1.1452 +};
  1.1453 +
  1.1454 +
  1.1455 +/**
  1.1456 + * Compute an internal properties object from |lazyNumberFormatData|.
  1.1457 + */
  1.1458 +function resolveNumberFormatInternals(lazyNumberFormatData) {
  1.1459 +    assert(IsObject(lazyNumberFormatData), "lazy data not an object?");
  1.1460 +
  1.1461 +    var internalProps = std_Object_create(null);
  1.1462 +
  1.1463 +    // Step 3.
  1.1464 +    var requestedLocales = lazyNumberFormatData.requestedLocales;
  1.1465 +
  1.1466 +    // Compute options that impact interpretation of locale.
  1.1467 +    // Step 6.
  1.1468 +    var opt = lazyNumberFormatData.opt;
  1.1469 +
  1.1470 +    // Compute effective locale.
  1.1471 +    // Step 9.
  1.1472 +    var NumberFormat = numberFormatInternalProperties;
  1.1473 +
  1.1474 +    // Step 10.
  1.1475 +    var localeData = NumberFormat.localeData;
  1.1476 +
  1.1477 +    // Step 11.
  1.1478 +    var r = ResolveLocale(NumberFormat.availableLocales(),
  1.1479 +                          lazyNumberFormatData.requestedLocales,
  1.1480 +                          lazyNumberFormatData.opt,
  1.1481 +                          NumberFormat.relevantExtensionKeys,
  1.1482 +                          localeData);
  1.1483 +
  1.1484 +    // Steps 12-13.  (Step 14 is not relevant to our implementation.)
  1.1485 +    internalProps.locale = r.locale;
  1.1486 +    internalProps.numberingSystem = r.nu;
  1.1487 +
  1.1488 +    // Compute formatting options.
  1.1489 +    // Step 16.
  1.1490 +    var s = lazyNumberFormatData.style;
  1.1491 +    internalProps.style = s;
  1.1492 +
  1.1493 +    // Steps 20, 22.
  1.1494 +    if (s === "currency") {
  1.1495 +        internalProps.currency = lazyNumberFormatData.currency;
  1.1496 +        internalProps.currencyDisplay = lazyNumberFormatData.currencyDisplay;
  1.1497 +    }
  1.1498 +
  1.1499 +    // Step 24.
  1.1500 +    internalProps.minimumIntegerDigits = lazyNumberFormatData.minimumIntegerDigits;
  1.1501 +
  1.1502 +    // Steps 27.
  1.1503 +    internalProps.minimumFractionDigits = lazyNumberFormatData.minimumFractionDigits;
  1.1504 +
  1.1505 +    // Step 30.
  1.1506 +    internalProps.maximumFractionDigits = lazyNumberFormatData.maximumFractionDigits;
  1.1507 +
  1.1508 +    // Step 33.
  1.1509 +    if ("minimumSignificantDigits" in lazyNumberFormatData) {
  1.1510 +        // Note: Intl.NumberFormat.prototype.resolvedOptions() exposes the
  1.1511 +        // actual presence (versus undefined-ness) of these properties.
  1.1512 +        assert("maximumSignificantDigits" in lazyNumberFormatData, "min/max sig digits mismatch");
  1.1513 +        internalProps.minimumSignificantDigits = lazyNumberFormatData.minimumSignificantDigits;
  1.1514 +        internalProps.maximumSignificantDigits = lazyNumberFormatData.maximumSignificantDigits;
  1.1515 +    }
  1.1516 +
  1.1517 +    // Step 35.
  1.1518 +    internalProps.useGrouping = lazyNumberFormatData.useGrouping;
  1.1519 +
  1.1520 +    // Step 42.
  1.1521 +    internalProps.boundFormat = undefined;
  1.1522 +
  1.1523 +    // The caller is responsible for associating |internalProps| with the right
  1.1524 +    // object using |setInternalProperties|.
  1.1525 +    return internalProps;
  1.1526 +}
  1.1527 +
  1.1528 +
  1.1529 +/**
  1.1530 + * Returns an object containing the NumberFormat internal properties of |obj|,
  1.1531 + * or throws a TypeError if |obj| isn't NumberFormat-initialized.
  1.1532 + */
  1.1533 +function getNumberFormatInternals(obj, methodName) {
  1.1534 +    var internals = getIntlObjectInternals(obj, "NumberFormat", methodName);
  1.1535 +    assert(internals.type === "NumberFormat", "bad type escaped getIntlObjectInternals");
  1.1536 +
  1.1537 +    // If internal properties have already been computed, use them.
  1.1538 +    var internalProps = maybeInternalProperties(internals);
  1.1539 +    if (internalProps)
  1.1540 +        return internalProps;
  1.1541 +
  1.1542 +    // Otherwise it's time to fully create them.
  1.1543 +    internalProps = resolveNumberFormatInternals(internals.lazyData);
  1.1544 +    setInternalProperties(internals, internalProps);
  1.1545 +    return internalProps;
  1.1546 +}
  1.1547 +
  1.1548 +
  1.1549 +/**
  1.1550 + * Initializes an object as a NumberFormat.
  1.1551 + *
  1.1552 + * This method is complicated a moderate bit by its implementing initialization
  1.1553 + * as a *lazy* concept.  Everything that must happen now, does -- but we defer
  1.1554 + * all the work we can until the object is actually used as a NumberFormat.
  1.1555 + * This later work occurs in |resolveNumberFormatInternals|; steps not noted
  1.1556 + * here occur there.
  1.1557 + *
  1.1558 + * Spec: ECMAScript Internationalization API Specification, 11.1.1.
  1.1559 + */
  1.1560 +function InitializeNumberFormat(numberFormat, locales, options) {
  1.1561 +    assert(IsObject(numberFormat), "InitializeNumberFormat");
  1.1562 +
  1.1563 +    // Step 1.
  1.1564 +    if (isInitializedIntlObject(numberFormat))
  1.1565 +        ThrowError(JSMSG_INTL_OBJECT_REINITED);
  1.1566 +
  1.1567 +    // Step 2.
  1.1568 +    var internals = initializeIntlObject(numberFormat);
  1.1569 +
  1.1570 +    // Lazy NumberFormat data has the following structure:
  1.1571 +    //
  1.1572 +    //   {
  1.1573 +    //     requestedLocales: List of locales,
  1.1574 +    //     style: "decimal" / "percent" / "currency",
  1.1575 +    //
  1.1576 +    //     // fields present only if style === "currency":
  1.1577 +    //     currency: a well-formed currency code (IsWellFormedCurrencyCode),
  1.1578 +    //     currencyDisplay: "code" / "symbol" / "name",
  1.1579 +    //
  1.1580 +    //     opt: // opt object computed in InitializeNumberFormat
  1.1581 +    //       {
  1.1582 +    //         localeMatcher: "lookup" / "best fit",
  1.1583 +    //       }
  1.1584 +    //
  1.1585 +    //     minimumIntegerDigits: integer ∈ [1, 21],
  1.1586 +    //     minimumFractionDigits: integer ∈ [0, 20],
  1.1587 +    //     maximumFractionDigits: integer ∈ [0, 20],
  1.1588 +    //
  1.1589 +    //     // optional
  1.1590 +    //     minimumSignificantDigits: integer ∈ [1, 21],
  1.1591 +    //     maximumSignificantDigits: integer ∈ [1, 21],
  1.1592 +    //
  1.1593 +    //     useGrouping: true / false,
  1.1594 +    //   }
  1.1595 +    //
  1.1596 +    // Note that lazy data is only installed as a final step of initialization,
  1.1597 +    // so every Collator lazy data object has *all* these properties, never a
  1.1598 +    // subset of them.
  1.1599 +    var lazyNumberFormatData = std_Object_create(null);
  1.1600 +
  1.1601 +    // Step 3.
  1.1602 +    var requestedLocales = CanonicalizeLocaleList(locales);
  1.1603 +    lazyNumberFormatData.requestedLocales = requestedLocales;
  1.1604 +
  1.1605 +    // Steps 4-5.
  1.1606 +    //
  1.1607 +    // If we ever need more speed here at startup, we should try to detect the
  1.1608 +    // case where |options === undefined| and Object.prototype hasn't been
  1.1609 +    // mucked with.  (|options| is fully consumed in this method, so it's not a
  1.1610 +    // concern that Object.prototype might be touched between now and when
  1.1611 +    // |resolveNumberFormatInternals| is called.)  For now just keep it simple.
  1.1612 +    if (options === undefined)
  1.1613 +        options = {};
  1.1614 +    else
  1.1615 +        options = ToObject(options);
  1.1616 +
  1.1617 +    // Compute options that impact interpretation of locale.
  1.1618 +    // Step 6.
  1.1619 +    var opt = new Record();
  1.1620 +    lazyNumberFormatData.opt = opt;
  1.1621 +
  1.1622 +    // Steps 7-8.
  1.1623 +    var matcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit");
  1.1624 +    opt.localeMatcher = matcher;
  1.1625 +
  1.1626 +    // Compute formatting options.
  1.1627 +    // Step 15.
  1.1628 +    var s = GetOption(options, "style", "string", ["decimal", "percent", "currency"], "decimal");
  1.1629 +    lazyNumberFormatData.style = s;
  1.1630 +
  1.1631 +    // Steps 17-20.
  1.1632 +    var c = GetOption(options, "currency", "string", undefined, undefined);
  1.1633 +    if (c !== undefined && !IsWellFormedCurrencyCode(c))
  1.1634 +        ThrowError(JSMSG_INVALID_CURRENCY_CODE, c);
  1.1635 +    var cDigits;
  1.1636 +    if (s === "currency") {
  1.1637 +        if (c === undefined)
  1.1638 +            ThrowError(JSMSG_UNDEFINED_CURRENCY);
  1.1639 +
  1.1640 +        // Steps 20.a-c.
  1.1641 +        c = toASCIIUpperCase(c);
  1.1642 +        lazyNumberFormatData.currency = c;
  1.1643 +        cDigits = CurrencyDigits(c);
  1.1644 +    }
  1.1645 +
  1.1646 +    // Step 21.
  1.1647 +    var cd = GetOption(options, "currencyDisplay", "string", ["code", "symbol", "name"], "symbol");
  1.1648 +    if (s === "currency")
  1.1649 +        lazyNumberFormatData.currencyDisplay = cd;
  1.1650 +
  1.1651 +    // Step 23.
  1.1652 +    var mnid = GetNumberOption(options, "minimumIntegerDigits", 1, 21, 1);
  1.1653 +    lazyNumberFormatData.minimumIntegerDigits = mnid;
  1.1654 +
  1.1655 +    // Steps 25-26.
  1.1656 +    var mnfdDefault = (s === "currency") ? cDigits : 0;
  1.1657 +    var mnfd = GetNumberOption(options, "minimumFractionDigits", 0, 20, mnfdDefault);
  1.1658 +    lazyNumberFormatData.minimumFractionDigits = mnfd;
  1.1659 +
  1.1660 +    // Steps 28-29.
  1.1661 +    var mxfdDefault;
  1.1662 +    if (s === "currency")
  1.1663 +        mxfdDefault = std_Math_max(mnfd, cDigits);
  1.1664 +    else if (s === "percent")
  1.1665 +        mxfdDefault = std_Math_max(mnfd, 0);
  1.1666 +    else
  1.1667 +        mxfdDefault = std_Math_max(mnfd, 3);
  1.1668 +    var mxfd = GetNumberOption(options, "maximumFractionDigits", mnfd, 20, mxfdDefault);
  1.1669 +    lazyNumberFormatData.maximumFractionDigits = mxfd;
  1.1670 +
  1.1671 +    // Steps 31-32.
  1.1672 +    var mnsd = options.minimumSignificantDigits;
  1.1673 +    var mxsd = options.maximumSignificantDigits;
  1.1674 +
  1.1675 +    // Step 33.
  1.1676 +    if (mnsd !== undefined || mxsd !== undefined) {
  1.1677 +        mnsd = GetNumberOption(options, "minimumSignificantDigits", 1, 21, 1);
  1.1678 +        mxsd = GetNumberOption(options, "maximumSignificantDigits", mnsd, 21, 21);
  1.1679 +        lazyNumberFormatData.minimumSignificantDigits = mnsd;
  1.1680 +        lazyNumberFormatData.maximumSignificantDigits = mxsd;
  1.1681 +    }
  1.1682 +
  1.1683 +    // Step 34.
  1.1684 +    var g = GetOption(options, "useGrouping", "boolean", undefined, true);
  1.1685 +    lazyNumberFormatData.useGrouping = g;
  1.1686 +
  1.1687 +    // Step 43.
  1.1688 +    //
  1.1689 +    // We've done everything that must be done now: mark the lazy data as fully
  1.1690 +    // computed and install it.
  1.1691 +    setLazyData(internals, "NumberFormat", lazyNumberFormatData);
  1.1692 +}
  1.1693 +
  1.1694 +
  1.1695 +/**
  1.1696 + * Mapping from currency codes to the number of decimal digits used for them.
  1.1697 + * Default is 2 digits.
  1.1698 + *
  1.1699 + * Spec: ISO 4217 Currency and Funds Code List.
  1.1700 + * http://www.currency-iso.org/en/home/tables/table-a1.html
  1.1701 + */
  1.1702 +var currencyDigits = {
  1.1703 +    BHD: 3,
  1.1704 +    BIF: 0,
  1.1705 +    BYR: 0,
  1.1706 +    CLF: 0,
  1.1707 +    CLP: 0,
  1.1708 +    DJF: 0,
  1.1709 +    IQD: 3,
  1.1710 +    GNF: 0,
  1.1711 +    ISK: 0,
  1.1712 +    JOD: 3,
  1.1713 +    JPY: 0,
  1.1714 +    KMF: 0,
  1.1715 +    KRW: 0,
  1.1716 +    KWD: 3,
  1.1717 +    LYD: 3,
  1.1718 +    OMR: 3,
  1.1719 +    PYG: 0,
  1.1720 +    RWF: 0,
  1.1721 +    TND: 3,
  1.1722 +    UGX: 0,
  1.1723 +    UYI: 0,
  1.1724 +    VND: 0,
  1.1725 +    VUV: 0,
  1.1726 +    XAF: 0,
  1.1727 +    XOF: 0,
  1.1728 +    XPF: 0
  1.1729 +};
  1.1730 +
  1.1731 +
  1.1732 +/**
  1.1733 + * Returns the number of decimal digits to be used for the given currency.
  1.1734 + *
  1.1735 + * Spec: ECMAScript Internationalization API Specification, 11.1.1.
  1.1736 + */
  1.1737 +function CurrencyDigits(currency) {
  1.1738 +    assert(typeof currency === "string", "CurrencyDigits");
  1.1739 +    assert(regexp_test_no_statics(/^[A-Z]{3}$/, currency), "CurrencyDigits");
  1.1740 +
  1.1741 +    if (callFunction(std_Object_hasOwnProperty, currencyDigits, currency))
  1.1742 +        return currencyDigits[currency];
  1.1743 +    return 2;
  1.1744 +}
  1.1745 +
  1.1746 +
  1.1747 +/**
  1.1748 + * Returns the subset of the given locale list for which this locale list has a
  1.1749 + * matching (possibly fallback) locale. Locales appear in the same order in the
  1.1750 + * returned list as in the input list.
  1.1751 + *
  1.1752 + * Spec: ECMAScript Internationalization API Specification, 11.2.2.
  1.1753 + */
  1.1754 +function Intl_NumberFormat_supportedLocalesOf(locales /*, options*/) {
  1.1755 +    var options = arguments.length > 1 ? arguments[1] : undefined;
  1.1756 +
  1.1757 +    var availableLocales = numberFormatInternalProperties.availableLocales();
  1.1758 +    var requestedLocales = CanonicalizeLocaleList(locales);
  1.1759 +    return SupportedLocales(availableLocales, requestedLocales, options);
  1.1760 +}
  1.1761 +
  1.1762 +
  1.1763 +function getNumberingSystems(locale) {
  1.1764 +    // ICU doesn't have an API to determine the set of numbering systems
  1.1765 +    // supported for a locale; it generally pretends that any numbering system
  1.1766 +    // can be used with any locale. Supporting a decimal numbering system
  1.1767 +    // (where only the digits are replaced) is easy, so we offer them all here.
  1.1768 +    // Algorithmic numbering systems are typically tied to one locale, so for
  1.1769 +    // lack of information we don't offer them. To increase chances that
  1.1770 +    // other software will process output correctly, we further restrict to
  1.1771 +    // those decimal numbering systems explicitly listed in table 2 of
  1.1772 +    // the ECMAScript Internationalization API Specification, 11.3.2, which
  1.1773 +    // in turn are those with full specifications in version 21 of Unicode
  1.1774 +    // Technical Standard #35 using digits that were defined in Unicode 5.0,
  1.1775 +    // the Unicode version supported in Windows Vista.
  1.1776 +    // The one thing we can find out from ICU is the default numbering system
  1.1777 +    // for a locale.
  1.1778 +    var defaultNumberingSystem = intl_numberingSystem(locale);
  1.1779 +    return [
  1.1780 +        defaultNumberingSystem,
  1.1781 +        "arab", "arabext", "bali", "beng", "deva",
  1.1782 +        "fullwide", "gujr", "guru", "hanidec", "khmr",
  1.1783 +        "knda", "laoo", "latn", "limb", "mlym",
  1.1784 +        "mong", "mymr", "orya", "tamldec", "telu",
  1.1785 +        "thai", "tibt"
  1.1786 +    ];
  1.1787 +}
  1.1788 +
  1.1789 +
  1.1790 +function numberFormatLocaleData(locale) {
  1.1791 +    return {
  1.1792 +        nu: getNumberingSystems(locale)
  1.1793 +    };
  1.1794 +}
  1.1795 +
  1.1796 +
  1.1797 +/**
  1.1798 + * Function to be bound and returned by Intl.NumberFormat.prototype.format.
  1.1799 + *
  1.1800 + * Spec: ECMAScript Internationalization API Specification, 11.3.2.
  1.1801 + */
  1.1802 +function numberFormatFormatToBind(value) {
  1.1803 +    // Steps 1.a.i implemented by ECMAScript declaration binding instantiation,
  1.1804 +    // ES5.1 10.5, step 4.d.ii.
  1.1805 +
  1.1806 +    // Step 1.a.ii-iii.
  1.1807 +    var x = ToNumber(value);
  1.1808 +    return intl_FormatNumber(this, x);
  1.1809 +}
  1.1810 +
  1.1811 +
  1.1812 +/**
  1.1813 + * Returns a function bound to this NumberFormat that returns a String value
  1.1814 + * representing the result of calling ToNumber(value) according to the
  1.1815 + * effective locale and the formatting options of this NumberFormat.
  1.1816 + *
  1.1817 + * Spec: ECMAScript Internationalization API Specification, 11.3.2.
  1.1818 + */
  1.1819 +function Intl_NumberFormat_format_get() {
  1.1820 +    // Check "this NumberFormat object" per introduction of section 11.3.
  1.1821 +    var internals = getNumberFormatInternals(this, "format");
  1.1822 +
  1.1823 +    // Step 1.
  1.1824 +    if (internals.boundFormat === undefined) {
  1.1825 +        // Step 1.a.
  1.1826 +        var F = numberFormatFormatToBind;
  1.1827 +
  1.1828 +        // Step 1.b-d.
  1.1829 +        var bf = callFunction(std_Function_bind, F, this);
  1.1830 +        internals.boundFormat = bf;
  1.1831 +    }
  1.1832 +    // Step 2.
  1.1833 +    return internals.boundFormat;
  1.1834 +}
  1.1835 +
  1.1836 +
  1.1837 +/**
  1.1838 + * Returns the resolved options for a NumberFormat object.
  1.1839 + *
  1.1840 + * Spec: ECMAScript Internationalization API Specification, 11.3.3 and 11.4.
  1.1841 + */
  1.1842 +function Intl_NumberFormat_resolvedOptions() {
  1.1843 +    // Check "this NumberFormat object" per introduction of section 11.3.
  1.1844 +    var internals = getNumberFormatInternals(this, "resolvedOptions");
  1.1845 +
  1.1846 +    var result = {
  1.1847 +        locale: internals.locale,
  1.1848 +        numberingSystem: internals.numberingSystem,
  1.1849 +        style: internals.style,
  1.1850 +        minimumIntegerDigits: internals.minimumIntegerDigits,
  1.1851 +        minimumFractionDigits: internals.minimumFractionDigits,
  1.1852 +        maximumFractionDigits: internals.maximumFractionDigits,
  1.1853 +        useGrouping: internals.useGrouping
  1.1854 +    };
  1.1855 +    var optionalProperties = [
  1.1856 +        "currency",
  1.1857 +        "currencyDisplay",
  1.1858 +        "minimumSignificantDigits",
  1.1859 +        "maximumSignificantDigits"
  1.1860 +    ];
  1.1861 +    for (var i = 0; i < optionalProperties.length; i++) {
  1.1862 +        var p = optionalProperties[i];
  1.1863 +        if (callFunction(std_Object_hasOwnProperty, internals, p))
  1.1864 +            defineProperty(result, p, internals[p]);
  1.1865 +    }
  1.1866 +    return result;
  1.1867 +}
  1.1868 +
  1.1869 +
  1.1870 +/********** Intl.DateTimeFormat **********/
  1.1871 +
  1.1872 +
  1.1873 +/**
  1.1874 + * Compute an internal properties object from |lazyDateTimeFormatData|.
  1.1875 + */
  1.1876 +function resolveDateTimeFormatInternals(lazyDateTimeFormatData) {
  1.1877 +    assert(IsObject(lazyDateTimeFormatData), "lazy data not an object?");
  1.1878 +
  1.1879 +    // Lazy DateTimeFormat data has the following structure:
  1.1880 +    //
  1.1881 +    //   {
  1.1882 +    //     requestedLocales: List of locales,
  1.1883 +    //
  1.1884 +    //     localeOpt: // *first* opt computed in InitializeDateTimeFormat
  1.1885 +    //       {
  1.1886 +    //         localeMatcher: "lookup" / "best fit",
  1.1887 +    //
  1.1888 +    //         hour12: true / false,  // optional
  1.1889 +    //       }
  1.1890 +    //
  1.1891 +    //     timeZone: undefined / "UTC",
  1.1892 +    //
  1.1893 +    //     formatOpt: // *second* opt computed in InitializeDateTimeFormat
  1.1894 +    //       {
  1.1895 +    //         // all the properties/values listed in Table 3
  1.1896 +    //         // (weekday, era, year, month, day, &c.)
  1.1897 +    //       }
  1.1898 +    //
  1.1899 +    //     formatMatcher: "basic" / "best fit",
  1.1900 +    //   }
  1.1901 +    //
  1.1902 +    // Note that lazy data is only installed as a final step of initialization,
  1.1903 +    // so every DateTimeFormat lazy data object has *all* these properties,
  1.1904 +    // never a subset of them.
  1.1905 +
  1.1906 +    var internalProps = std_Object_create(null);
  1.1907 +
  1.1908 +    // Compute effective locale.
  1.1909 +    // Step 8.
  1.1910 +    var DateTimeFormat = dateTimeFormatInternalProperties;
  1.1911 +
  1.1912 +    // Step 9.
  1.1913 +    var localeData = DateTimeFormat.localeData;
  1.1914 +
  1.1915 +    // Step 10.
  1.1916 +    var r = ResolveLocale(DateTimeFormat.availableLocales(),
  1.1917 +                          lazyDateTimeFormatData.requestedLocales,
  1.1918 +                          lazyDateTimeFormatData.localeOpt,
  1.1919 +                          DateTimeFormat.relevantExtensionKeys,
  1.1920 +                          localeData);
  1.1921 +
  1.1922 +    // Steps 11-13.
  1.1923 +    internalProps.locale = r.locale;
  1.1924 +    internalProps.calendar = r.ca;
  1.1925 +    internalProps.numberingSystem = r.nu;
  1.1926 +
  1.1927 +    // Compute formatting options.
  1.1928 +    // Step 14.
  1.1929 +    var dataLocale = r.dataLocale;
  1.1930 +
  1.1931 +    // Steps 15-17.
  1.1932 +    internalProps.timeZone = lazyDateTimeFormatData.timeZone;
  1.1933 +
  1.1934 +    // Step 18.
  1.1935 +    var formatOpt = lazyDateTimeFormatData.formatOpt;
  1.1936 +
  1.1937 +    // Steps 27-28, more or less - see comment after this function.
  1.1938 +    var pattern = toBestICUPattern(dataLocale, formatOpt);
  1.1939 +
  1.1940 +    // Step 29.
  1.1941 +    internalProps.pattern = pattern;
  1.1942 +
  1.1943 +    // Step 30.
  1.1944 +    internalProps.boundFormat = undefined;
  1.1945 +
  1.1946 +    // The caller is responsible for associating |internalProps| with the right
  1.1947 +    // object using |setInternalProperties|.
  1.1948 +    return internalProps;
  1.1949 +}
  1.1950 +
  1.1951 +
  1.1952 +/**
  1.1953 + * Returns an object containing the DateTimeFormat internal properties of |obj|,
  1.1954 + * or throws a TypeError if |obj| isn't DateTimeFormat-initialized.
  1.1955 + */
  1.1956 +function getDateTimeFormatInternals(obj, methodName) {
  1.1957 +    var internals = getIntlObjectInternals(obj, "DateTimeFormat", methodName);
  1.1958 +    assert(internals.type === "DateTimeFormat", "bad type escaped getIntlObjectInternals");
  1.1959 +
  1.1960 +    // If internal properties have already been computed, use them.
  1.1961 +    var internalProps = maybeInternalProperties(internals);
  1.1962 +    if (internalProps)
  1.1963 +        return internalProps;
  1.1964 +
  1.1965 +    // Otherwise it's time to fully create them.
  1.1966 +    internalProps = resolveDateTimeFormatInternals(internals.lazyData);
  1.1967 +    setInternalProperties(internals, internalProps);
  1.1968 +    return internalProps;
  1.1969 +}
  1.1970 +
  1.1971 +/**
  1.1972 + * Components of date and time formats and their values.
  1.1973 + *
  1.1974 + * Spec: ECMAScript Internationalization API Specification, 12.1.1.
  1.1975 + */
  1.1976 +var dateTimeComponentValues = {
  1.1977 +    weekday: ["narrow", "short", "long"],
  1.1978 +    era: ["narrow", "short", "long"],
  1.1979 +    year: ["2-digit", "numeric"],
  1.1980 +    month: ["2-digit", "numeric", "narrow", "short", "long"],
  1.1981 +    day: ["2-digit", "numeric"],
  1.1982 +    hour: ["2-digit", "numeric"],
  1.1983 +    minute: ["2-digit", "numeric"],
  1.1984 +    second: ["2-digit", "numeric"],
  1.1985 +    timeZoneName: ["short", "long"]
  1.1986 +};
  1.1987 +
  1.1988 +
  1.1989 +var dateTimeComponents = std_Object_getOwnPropertyNames(dateTimeComponentValues);
  1.1990 +
  1.1991 +
  1.1992 +/**
  1.1993 + * Initializes an object as a DateTimeFormat.
  1.1994 + *
  1.1995 + * This method is complicated a moderate bit by its implementing initialization
  1.1996 + * as a *lazy* concept.  Everything that must happen now, does -- but we defer
  1.1997 + * all the work we can until the object is actually used as a DateTimeFormat.
  1.1998 + * This later work occurs in |resolveDateTimeFormatInternals|; steps not noted
  1.1999 + * here occur there.
  1.2000 + *
  1.2001 + * Spec: ECMAScript Internationalization API Specification, 12.1.1.
  1.2002 + */
  1.2003 +function InitializeDateTimeFormat(dateTimeFormat, locales, options) {
  1.2004 +    assert(IsObject(dateTimeFormat), "InitializeDateTimeFormat");
  1.2005 +
  1.2006 +    // Step 1.
  1.2007 +    if (isInitializedIntlObject(dateTimeFormat))
  1.2008 +        ThrowError(JSMSG_INTL_OBJECT_REINITED);
  1.2009 +
  1.2010 +    // Step 2.
  1.2011 +    var internals = initializeIntlObject(dateTimeFormat);
  1.2012 +
  1.2013 +    // Lazy DateTimeFormat data has the following structure:
  1.2014 +    //
  1.2015 +    //   {
  1.2016 +    //     requestedLocales: List of locales,
  1.2017 +    //
  1.2018 +    //     localeOpt: // *first* opt computed in InitializeDateTimeFormat
  1.2019 +    //       {
  1.2020 +    //         localeMatcher: "lookup" / "best fit",
  1.2021 +    //       }
  1.2022 +    //
  1.2023 +    //     timeZone: undefined / "UTC",
  1.2024 +    //
  1.2025 +    //     formatOpt: // *second* opt computed in InitializeDateTimeFormat
  1.2026 +    //       {
  1.2027 +    //         // all the properties/values listed in Table 3
  1.2028 +    //         // (weekday, era, year, month, day, &c.)
  1.2029 +    //
  1.2030 +    //         hour12: true / false  // optional
  1.2031 +    //       }
  1.2032 +    //
  1.2033 +    //     formatMatcher: "basic" / "best fit",
  1.2034 +    //   }
  1.2035 +    //
  1.2036 +    // Note that lazy data is only installed as a final step of initialization,
  1.2037 +    // so every DateTimeFormat lazy data object has *all* these properties,
  1.2038 +    // never a subset of them.
  1.2039 +    var lazyDateTimeFormatData = std_Object_create(null);
  1.2040 +
  1.2041 +    // Step 3.
  1.2042 +    var requestedLocales = CanonicalizeLocaleList(locales);
  1.2043 +    lazyDateTimeFormatData.requestedLocales = requestedLocales;
  1.2044 +
  1.2045 +    // Step 4.
  1.2046 +    options = ToDateTimeOptions(options, "any", "date");
  1.2047 +
  1.2048 +    // Compute options that impact interpretation of locale.
  1.2049 +    // Step 5.
  1.2050 +    var localeOpt = new Record();
  1.2051 +    lazyDateTimeFormatData.localeOpt = localeOpt;
  1.2052 +
  1.2053 +    // Steps 6-7.
  1.2054 +    var localeMatcher =
  1.2055 +        GetOption(options, "localeMatcher", "string", ["lookup", "best fit"],
  1.2056 +                  "best fit");
  1.2057 +    localeOpt.localeMatcher = localeMatcher;
  1.2058 +
  1.2059 +    // Steps 15-17.
  1.2060 +    var tz = options.timeZone;
  1.2061 +    if (tz !== undefined) {
  1.2062 +        tz = toASCIIUpperCase(ToString(tz));
  1.2063 +        if (tz !== "UTC")
  1.2064 +            ThrowError(JSMSG_INVALID_TIME_ZONE, tz);
  1.2065 +    }
  1.2066 +    lazyDateTimeFormatData.timeZone = tz;
  1.2067 +
  1.2068 +    // Step 18.
  1.2069 +    var formatOpt = new Record();
  1.2070 +    lazyDateTimeFormatData.formatOpt = formatOpt;
  1.2071 +
  1.2072 +    // Step 19.
  1.2073 +    var i, prop;
  1.2074 +    for (i = 0; i < dateTimeComponents.length; i++) {
  1.2075 +        prop = dateTimeComponents[i];
  1.2076 +        var value = GetOption(options, prop, "string", dateTimeComponentValues[prop], undefined);
  1.2077 +        formatOpt[prop] = value;
  1.2078 +    }
  1.2079 +
  1.2080 +    // Steps 20-21 provided by ICU - see comment after this function.
  1.2081 +
  1.2082 +    // Step 22.
  1.2083 +    //
  1.2084 +    // For some reason (ICU not exposing enough interface?) we drop the
  1.2085 +    // requested format matcher on the floor after this.  In any case, even if
  1.2086 +    // doing so is justified, we have to do this work here in case it triggers
  1.2087 +    // getters or similar.
  1.2088 +    var formatMatcher =
  1.2089 +        GetOption(options, "formatMatcher", "string", ["basic", "best fit"],
  1.2090 +                  "best fit");
  1.2091 +
  1.2092 +    // Steps 23-25 provided by ICU, more or less - see comment after this function.
  1.2093 +
  1.2094 +    // Step 26.
  1.2095 +    var hr12  = GetOption(options, "hour12", "boolean", undefined, undefined);
  1.2096 +
  1.2097 +    // Pass hr12 on to ICU.
  1.2098 +    if (hr12 !== undefined)
  1.2099 +        formatOpt.hour12 = hr12;
  1.2100 +
  1.2101 +    // Step 31.
  1.2102 +    //
  1.2103 +    // We've done everything that must be done now: mark the lazy data as fully
  1.2104 +    // computed and install it.
  1.2105 +    setLazyData(internals, "DateTimeFormat", lazyDateTimeFormatData);
  1.2106 +}
  1.2107 +
  1.2108 +
  1.2109 +// Intl.DateTimeFormat and ICU skeletons and patterns
  1.2110 +// ==================================================
  1.2111 +//
  1.2112 +// Different locales have different ways to display dates using the same
  1.2113 +// basic components. For example, en-US might use "Sept. 24, 2012" while
  1.2114 +// fr-FR might use "24 Sept. 2012". The intent of Intl.DateTimeFormat is to
  1.2115 +// permit production of a format for the locale that best matches the
  1.2116 +// set of date-time components and their desired representation as specified
  1.2117 +// by the API client.
  1.2118 +//
  1.2119 +// ICU supports specification of date and time formats in three ways:
  1.2120 +//
  1.2121 +// 1) A style is just one of the identifiers FULL, LONG, MEDIUM, or SHORT.
  1.2122 +//    The date-time components included in each style and their representation
  1.2123 +//    are defined by ICU using CLDR locale data (CLDR is the Unicode
  1.2124 +//    Consortium's Common Locale Data Repository).
  1.2125 +//
  1.2126 +// 2) A skeleton is a string specifying which date-time components to include,
  1.2127 +//    and which representations to use for them. For example, "yyyyMMMMdd"
  1.2128 +//    specifies a year with at least four digits, a full month name, and a
  1.2129 +//    two-digit day. It does not specify in which order the components appear,
  1.2130 +//    how they are separated, the localized strings for textual components
  1.2131 +//    (such as weekday or month), whether the month is in format or
  1.2132 +//    stand-alone form¹, or the numbering system used for numeric components.
  1.2133 +//    All that information is filled in by ICU using CLDR locale data.
  1.2134 +//    ¹ The format form is the one used in formatted strings that include a
  1.2135 +//    day; the stand-alone form is used when not including days, e.g., in
  1.2136 +//    calendar headers. The two forms differ at least in some Slavic languages,
  1.2137 +//    e.g. Russian: "22 марта 2013 г." vs. "Март 2013".
  1.2138 +//
  1.2139 +// 3) A pattern is a string specifying which date-time components to include,
  1.2140 +//    in which order, with which separators, in which grammatical case. For
  1.2141 +//    example, "EEEE, d MMMM y" specifies the full localized weekday name,
  1.2142 +//    followed by comma and space, followed by the day, followed by space,
  1.2143 +//    followed by the full month name in format form, followed by space,
  1.2144 +//    followed by the full year. It
  1.2145 +//    still does not specify localized strings for textual components and the
  1.2146 +//    numbering system - these are determined by ICU using CLDR locale data or
  1.2147 +//    possibly API parameters.
  1.2148 +//
  1.2149 +// All actual formatting in ICU is done with patterns; styles and skeletons
  1.2150 +// have to be mapped to patterns before processing.
  1.2151 +//
  1.2152 +// The options of DateTimeFormat most closely correspond to ICU skeletons. This
  1.2153 +// implementation therefore, in the toBestICUPattern function, converts
  1.2154 +// DateTimeFormat options to ICU skeletons, and then lets ICU map skeletons to
  1.2155 +// actual ICU patterns. The pattern may not directly correspond to what the
  1.2156 +// skeleton requests, as the mapper (UDateTimePatternGenerator) is constrained
  1.2157 +// by the available locale data for the locale. The resulting ICU pattern is
  1.2158 +// kept as the DateTimeFormat's [[pattern]] internal property and passed to ICU
  1.2159 +// in the format method.
  1.2160 +//
  1.2161 +// An ICU pattern represents the information of the following DateTimeFormat
  1.2162 +// internal properties described in the specification, which therefore don't
  1.2163 +// exist separately in the implementation:
  1.2164 +// - [[weekday]], [[era]], [[year]], [[month]], [[day]], [[hour]], [[minute]],
  1.2165 +//   [[second]], [[timeZoneName]]
  1.2166 +// - [[hour12]]
  1.2167 +// - [[hourNo0]]
  1.2168 +// When needed for the resolvedOptions method, the resolveICUPattern function
  1.2169 +// maps the instance's ICU pattern back to the specified properties of the
  1.2170 +// object returned by resolvedOptions.
  1.2171 +//
  1.2172 +// ICU date-time skeletons and patterns aren't fully documented in the ICU
  1.2173 +// documentation (see http://bugs.icu-project.org/trac/ticket/9627). The best
  1.2174 +// documentation at this point is in UTR 35:
  1.2175 +// http://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
  1.2176 +
  1.2177 +
  1.2178 +/**
  1.2179 + * Returns an ICU pattern string for the given locale and representing the
  1.2180 + * specified options as closely as possible given available locale data.
  1.2181 + */
  1.2182 +function toBestICUPattern(locale, options) {
  1.2183 +    // Create an ICU skeleton representing the specified options. See
  1.2184 +    // http://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
  1.2185 +    var skeleton = "";
  1.2186 +    switch (options.weekday) {
  1.2187 +    case "narrow":
  1.2188 +        skeleton += "EEEEE";
  1.2189 +        break;
  1.2190 +    case "short":
  1.2191 +        skeleton += "E";
  1.2192 +        break;
  1.2193 +    case "long":
  1.2194 +        skeleton += "EEEE";
  1.2195 +    }
  1.2196 +    switch (options.era) {
  1.2197 +    case "narrow":
  1.2198 +        skeleton += "GGGGG";
  1.2199 +        break;
  1.2200 +    case "short":
  1.2201 +        skeleton += "G";
  1.2202 +        break;
  1.2203 +    case "long":
  1.2204 +        skeleton += "GGGG";
  1.2205 +        break;
  1.2206 +    }
  1.2207 +    switch (options.year) {
  1.2208 +    case "2-digit":
  1.2209 +        skeleton += "yy";
  1.2210 +        break;
  1.2211 +    case "numeric":
  1.2212 +        skeleton += "y";
  1.2213 +        break;
  1.2214 +    }
  1.2215 +    switch (options.month) {
  1.2216 +    case "2-digit":
  1.2217 +        skeleton += "MM";
  1.2218 +        break;
  1.2219 +    case "numeric":
  1.2220 +        skeleton += "M";
  1.2221 +        break;
  1.2222 +    case "narrow":
  1.2223 +        skeleton += "MMMMM";
  1.2224 +        break;
  1.2225 +    case "short":
  1.2226 +        skeleton += "MMM";
  1.2227 +        break;
  1.2228 +    case "long":
  1.2229 +        skeleton += "MMMM";
  1.2230 +        break;
  1.2231 +    }
  1.2232 +    switch (options.day) {
  1.2233 +    case "2-digit":
  1.2234 +        skeleton += "dd";
  1.2235 +        break;
  1.2236 +    case "numeric":
  1.2237 +        skeleton += "d";
  1.2238 +        break;
  1.2239 +    }
  1.2240 +    var hourSkeletonChar = "j";
  1.2241 +    if (options.hour12 !== undefined) {
  1.2242 +        if (options.hour12)
  1.2243 +            hourSkeletonChar = "h";
  1.2244 +        else
  1.2245 +            hourSkeletonChar = "H";
  1.2246 +    }
  1.2247 +    switch (options.hour) {
  1.2248 +    case "2-digit":
  1.2249 +        skeleton += hourSkeletonChar + hourSkeletonChar;
  1.2250 +        break;
  1.2251 +    case "numeric":
  1.2252 +        skeleton += hourSkeletonChar;
  1.2253 +        break;
  1.2254 +    }
  1.2255 +    switch (options.minute) {
  1.2256 +    case "2-digit":
  1.2257 +        skeleton += "mm";
  1.2258 +        break;
  1.2259 +    case "numeric":
  1.2260 +        skeleton += "m";
  1.2261 +        break;
  1.2262 +    }
  1.2263 +    switch (options.second) {
  1.2264 +    case "2-digit":
  1.2265 +        skeleton += "ss";
  1.2266 +        break;
  1.2267 +    case "numeric":
  1.2268 +        skeleton += "s";
  1.2269 +        break;
  1.2270 +    }
  1.2271 +    switch (options.timeZoneName) {
  1.2272 +    case "short":
  1.2273 +        skeleton += "z";
  1.2274 +        break;
  1.2275 +    case "long":
  1.2276 +        skeleton += "zzzz";
  1.2277 +        break;
  1.2278 +    }
  1.2279 +
  1.2280 +    // Let ICU convert the ICU skeleton to an ICU pattern for the given locale.
  1.2281 +    return intl_patternForSkeleton(locale, skeleton);
  1.2282 +}
  1.2283 +
  1.2284 +
  1.2285 +/**
  1.2286 + * Returns a new options object that includes the provided options (if any)
  1.2287 + * and fills in default components if required components are not defined.
  1.2288 + * Required can be "date", "time", or "any".
  1.2289 + * Defaults can be "date", "time", or "all".
  1.2290 + *
  1.2291 + * Spec: ECMAScript Internationalization API Specification, 12.1.1.
  1.2292 + */
  1.2293 +function ToDateTimeOptions(options, required, defaults) {
  1.2294 +    assert(typeof required === "string", "ToDateTimeOptions");
  1.2295 +    assert(typeof defaults === "string", "ToDateTimeOptions");
  1.2296 +
  1.2297 +    // Steps 1-3.
  1.2298 +    if (options === undefined)
  1.2299 +        options = null;
  1.2300 +    else
  1.2301 +        options = ToObject(options);
  1.2302 +    options = std_Object_create(options);
  1.2303 +
  1.2304 +    // Step 4.
  1.2305 +    var needDefaults = true;
  1.2306 +
  1.2307 +    // Step 5.
  1.2308 +    if ((required === "date" || required === "any") &&
  1.2309 +        (options.weekday !== undefined || options.year !== undefined ||
  1.2310 +         options.month !== undefined || options.day !== undefined))
  1.2311 +    {
  1.2312 +        needDefaults = false;
  1.2313 +    }
  1.2314 +
  1.2315 +    // Step 6.
  1.2316 +    if ((required === "time" || required === "any") &&
  1.2317 +        (options.hour !== undefined || options.minute !== undefined ||
  1.2318 +         options.second !== undefined))
  1.2319 +    {
  1.2320 +        needDefaults = false;
  1.2321 +    }
  1.2322 +
  1.2323 +    // Step 7.
  1.2324 +    if (needDefaults && (defaults === "date" || defaults === "all")) {
  1.2325 +        // The specification says to call [[DefineOwnProperty]] with false for
  1.2326 +        // the Throw parameter, while Object.defineProperty uses true. For the
  1.2327 +        // calls here, the difference doesn't matter because we're adding
  1.2328 +        // properties to a new object.
  1.2329 +        defineProperty(options, "year", "numeric");
  1.2330 +        defineProperty(options, "month", "numeric");
  1.2331 +        defineProperty(options, "day", "numeric");
  1.2332 +    }
  1.2333 +
  1.2334 +    // Step 8.
  1.2335 +    if (needDefaults && (defaults === "time" || defaults === "all")) {
  1.2336 +        // See comment for step 7.
  1.2337 +        defineProperty(options, "hour", "numeric");
  1.2338 +        defineProperty(options, "minute", "numeric");
  1.2339 +        defineProperty(options, "second", "numeric");
  1.2340 +    }
  1.2341 +
  1.2342 +    // Step 9.
  1.2343 +    return options;
  1.2344 +}
  1.2345 +
  1.2346 +
  1.2347 +/**
  1.2348 + * Compares the date and time components requested by options with the available
  1.2349 + * date and time formats in formats, and selects the best match according
  1.2350 + * to a specified basic matching algorithm.
  1.2351 + *
  1.2352 + * Spec: ECMAScript Internationalization API Specification, 12.1.1.
  1.2353 + */
  1.2354 +function BasicFormatMatcher(options, formats) {
  1.2355 +    // Steps 1-6.
  1.2356 +    var removalPenalty = 120,
  1.2357 +        additionPenalty = 20,
  1.2358 +        longLessPenalty = 8,
  1.2359 +        longMorePenalty = 6,
  1.2360 +        shortLessPenalty = 6,
  1.2361 +        shortMorePenalty = 3;
  1.2362 +
  1.2363 +    // Table 3.
  1.2364 +    var properties = ["weekday", "era", "year", "month", "day",
  1.2365 +        "hour", "minute", "second", "timeZoneName"];
  1.2366 +
  1.2367 +    // Step 11.c.vi.1.
  1.2368 +    var values = ["2-digit", "numeric", "narrow", "short", "long"];
  1.2369 +
  1.2370 +    // Steps 7-8.
  1.2371 +    var bestScore = -Infinity;
  1.2372 +    var bestFormat;
  1.2373 +
  1.2374 +    // Steps 9-11.
  1.2375 +    var i = 0;
  1.2376 +    var len = formats.length;
  1.2377 +    while (i < len) {
  1.2378 +        // Steps 11.a-b.
  1.2379 +        var format = formats[i];
  1.2380 +        var score = 0;
  1.2381 +
  1.2382 +        // Step 11.c.
  1.2383 +        var formatProp;
  1.2384 +        for (var j = 0; j < properties.length; j++) {
  1.2385 +            var property = properties[j];
  1.2386 +
  1.2387 +            // Step 11.c.i.
  1.2388 +            var optionsProp = options[property];
  1.2389 +            // Step missing from spec.
  1.2390 +            // https://bugs.ecmascript.org/show_bug.cgi?id=1254
  1.2391 +            formatProp = undefined;
  1.2392 +
  1.2393 +            // Steps 11.c.ii-iii.
  1.2394 +            if (callFunction(std_Object_hasOwnProperty, format, property))
  1.2395 +                formatProp = format[property];
  1.2396 +
  1.2397 +            if (optionsProp === undefined && formatProp !== undefined) {
  1.2398 +                // Step 11.c.iv.
  1.2399 +                score -= additionPenalty;
  1.2400 +            } else if (optionsProp !== undefined && formatProp === undefined) {
  1.2401 +                // Step 11.c.v.
  1.2402 +                score -= removalPenalty;
  1.2403 +            } else {
  1.2404 +                // Step 11.c.vi.
  1.2405 +                var optionsPropIndex = callFunction(std_Array_indexOf, values, optionsProp);
  1.2406 +                var formatPropIndex = callFunction(std_Array_indexOf, values, formatProp);
  1.2407 +                var delta = std_Math_max(std_Math_min(formatPropIndex - optionsPropIndex, 2), -2);
  1.2408 +                if (delta === 2)
  1.2409 +                    score -= longMorePenalty;
  1.2410 +                else if (delta === 1)
  1.2411 +                    score -= shortMorePenalty;
  1.2412 +                else if (delta === -1)
  1.2413 +                    score -= shortLessPenalty;
  1.2414 +                else if (delta === -2)
  1.2415 +                    score -= longLessPenalty;
  1.2416 +            }
  1.2417 +        }
  1.2418 +
  1.2419 +        // Step 11.d.
  1.2420 +        if (score > bestScore) {
  1.2421 +            bestScore = score;
  1.2422 +            bestFormat = format;
  1.2423 +        }
  1.2424 +
  1.2425 +        // Step 11.e.
  1.2426 +        i++;
  1.2427 +    }
  1.2428 +
  1.2429 +    // Step 12.
  1.2430 +    return bestFormat;
  1.2431 +}
  1.2432 +
  1.2433 +
  1.2434 +/**
  1.2435 + * Compares the date and time components requested by options with the available
  1.2436 + * date and time formats in formats, and selects the best match according
  1.2437 + * to an unspecified best-fit matching algorithm.
  1.2438 + *
  1.2439 + * Spec: ECMAScript Internationalization API Specification, 12.1.1.
  1.2440 + */
  1.2441 +function BestFitFormatMatcher(options, formats) {
  1.2442 +    // this implementation doesn't have anything better
  1.2443 +    return BasicFormatMatcher(options, formats);
  1.2444 +}
  1.2445 +
  1.2446 +
  1.2447 +/**
  1.2448 + * Returns the subset of the given locale list for which this locale list has a
  1.2449 + * matching (possibly fallback) locale. Locales appear in the same order in the
  1.2450 + * returned list as in the input list.
  1.2451 + *
  1.2452 + * Spec: ECMAScript Internationalization API Specification, 12.2.2.
  1.2453 + */
  1.2454 +function Intl_DateTimeFormat_supportedLocalesOf(locales /*, options*/) {
  1.2455 +    var options = arguments.length > 1 ? arguments[1] : undefined;
  1.2456 +
  1.2457 +    var availableLocales = dateTimeFormatInternalProperties.availableLocales();
  1.2458 +    var requestedLocales = CanonicalizeLocaleList(locales);
  1.2459 +    return SupportedLocales(availableLocales, requestedLocales, options);
  1.2460 +}
  1.2461 +
  1.2462 +
  1.2463 +/**
  1.2464 + * DateTimeFormat internal properties.
  1.2465 + *
  1.2466 + * Spec: ECMAScript Internationalization API Specification, 9.1 and 12.2.3.
  1.2467 + */
  1.2468 +var dateTimeFormatInternalProperties = {
  1.2469 +    localeData: dateTimeFormatLocaleData,
  1.2470 +    _availableLocales: null,
  1.2471 +    availableLocales: function()
  1.2472 +    {
  1.2473 +        var locales = this._availableLocales;
  1.2474 +        if (locales)
  1.2475 +            return locales;
  1.2476 +        return (this._availableLocales =
  1.2477 +          addOldStyleLanguageTags(intl_DateTimeFormat_availableLocales()));
  1.2478 +    },
  1.2479 +    relevantExtensionKeys: ["ca", "nu"]
  1.2480 +};
  1.2481 +
  1.2482 +
  1.2483 +function dateTimeFormatLocaleData(locale) {
  1.2484 +    return {
  1.2485 +        ca: intl_availableCalendars(locale),
  1.2486 +        nu: getNumberingSystems(locale)
  1.2487 +    };
  1.2488 +}
  1.2489 +
  1.2490 +
  1.2491 +/**
  1.2492 + * Function to be bound and returned by Intl.DateTimeFormat.prototype.format.
  1.2493 + *
  1.2494 + * Spec: ECMAScript Internationalization API Specification, 12.3.2.
  1.2495 + */
  1.2496 +function dateTimeFormatFormatToBind() {
  1.2497 +    // Steps 1.a.i-ii
  1.2498 +    var date = arguments.length > 0 ? arguments[0] : undefined;
  1.2499 +    var x = (date === undefined) ? std_Date_now() : ToNumber(date);
  1.2500 +
  1.2501 +    // Step 1.a.iii.
  1.2502 +    return intl_FormatDateTime(this, x);
  1.2503 +}
  1.2504 +
  1.2505 +
  1.2506 +/**
  1.2507 + * Returns a function bound to this DateTimeFormat that returns a String value
  1.2508 + * representing the result of calling ToNumber(date) according to the
  1.2509 + * effective locale and the formatting options of this DateTimeFormat.
  1.2510 + *
  1.2511 + * Spec: ECMAScript Internationalization API Specification, 12.3.2.
  1.2512 + */
  1.2513 +function Intl_DateTimeFormat_format_get() {
  1.2514 +    // Check "this DateTimeFormat object" per introduction of section 12.3.
  1.2515 +    var internals = getDateTimeFormatInternals(this, "format");
  1.2516 +
  1.2517 +    // Step 1.
  1.2518 +    if (internals.boundFormat === undefined) {
  1.2519 +        // Step 1.a.
  1.2520 +        var F = dateTimeFormatFormatToBind;
  1.2521 +
  1.2522 +        // Step 1.b-d.
  1.2523 +        var bf = callFunction(std_Function_bind, F, this);
  1.2524 +        internals.boundFormat = bf;
  1.2525 +    }
  1.2526 +
  1.2527 +    // Step 2.
  1.2528 +    return internals.boundFormat;
  1.2529 +}
  1.2530 +
  1.2531 +
  1.2532 +/**
  1.2533 + * Returns the resolved options for a DateTimeFormat object.
  1.2534 + *
  1.2535 + * Spec: ECMAScript Internationalization API Specification, 12.3.3 and 12.4.
  1.2536 + */
  1.2537 +function Intl_DateTimeFormat_resolvedOptions() {
  1.2538 +    // Check "this DateTimeFormat object" per introduction of section 12.3.
  1.2539 +    var internals = getDateTimeFormatInternals(this, "resolvedOptions");
  1.2540 +
  1.2541 +    var result = {
  1.2542 +        locale: internals.locale,
  1.2543 +        calendar: internals.calendar,
  1.2544 +        numberingSystem: internals.numberingSystem,
  1.2545 +        timeZone: internals.timeZone
  1.2546 +    };
  1.2547 +    resolveICUPattern(internals.pattern, result);
  1.2548 +    return result;
  1.2549 +}
  1.2550 +
  1.2551 +
  1.2552 +// Table mapping ICU pattern characters back to the corresponding date-time
  1.2553 +// components of DateTimeFormat. See
  1.2554 +// http://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
  1.2555 +var icuPatternCharToComponent = {
  1.2556 +    E: "weekday",
  1.2557 +    G: "era",
  1.2558 +    y: "year",
  1.2559 +    M: "month",
  1.2560 +    L: "month",
  1.2561 +    d: "day",
  1.2562 +    h: "hour",
  1.2563 +    H: "hour",
  1.2564 +    k: "hour",
  1.2565 +    K: "hour",
  1.2566 +    m: "minute",
  1.2567 +    s: "second",
  1.2568 +    z: "timeZoneName",
  1.2569 +    v: "timeZoneName",
  1.2570 +    V: "timeZoneName"
  1.2571 +};
  1.2572 +
  1.2573 +
  1.2574 +/**
  1.2575 + * Maps an ICU pattern string to a corresponding set of date-time components
  1.2576 + * and their values, and adds properties for these components to the result
  1.2577 + * object, which will be returned by the resolvedOptions method. For the
  1.2578 + * interpretation of ICU pattern characters, see
  1.2579 + * http://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
  1.2580 + */
  1.2581 +function resolveICUPattern(pattern, result) {
  1.2582 +    assert(IsObject(result), "resolveICUPattern");
  1.2583 +    var i = 0;
  1.2584 +    while (i < pattern.length) {
  1.2585 +        var c = pattern[i++];
  1.2586 +        if (c === "'") {
  1.2587 +            while (i < pattern.length && pattern[i] !== "'")
  1.2588 +                i++;
  1.2589 +            i++;
  1.2590 +        } else {
  1.2591 +            var count = 1;
  1.2592 +            while (i < pattern.length && pattern[i] === c) {
  1.2593 +                i++;
  1.2594 +                count++;
  1.2595 +            }
  1.2596 +            var value;
  1.2597 +            switch (c) {
  1.2598 +            // "text" cases
  1.2599 +            case "G":
  1.2600 +            case "E":
  1.2601 +            case "z":
  1.2602 +            case "v":
  1.2603 +            case "V":
  1.2604 +                if (count <= 3)
  1.2605 +                    value = "short";
  1.2606 +                else if (count === 4)
  1.2607 +                    value = "long";
  1.2608 +                else
  1.2609 +                    value = "narrow";
  1.2610 +                break;
  1.2611 +            // "number" cases
  1.2612 +            case "y":
  1.2613 +            case "d":
  1.2614 +            case "h":
  1.2615 +            case "H":
  1.2616 +            case "m":
  1.2617 +            case "s":
  1.2618 +            case "k":
  1.2619 +            case "K":
  1.2620 +                if (count === 2)
  1.2621 +                    value = "2-digit";
  1.2622 +                else
  1.2623 +                    value = "numeric";
  1.2624 +                break;
  1.2625 +            // "text & number" cases
  1.2626 +            case "M":
  1.2627 +            case "L":
  1.2628 +                if (count === 1)
  1.2629 +                    value = "numeric";
  1.2630 +                else if (count === 2)
  1.2631 +                    value = "2-digit";
  1.2632 +                else if (count === 3)
  1.2633 +                    value = "short";
  1.2634 +                else if (count === 4)
  1.2635 +                    value = "long";
  1.2636 +                else
  1.2637 +                    value = "narrow";
  1.2638 +                break;
  1.2639 +            default:
  1.2640 +                // skip other pattern characters and literal text
  1.2641 +            }
  1.2642 +            if (callFunction(std_Object_hasOwnProperty, icuPatternCharToComponent, c))
  1.2643 +                defineProperty(result, icuPatternCharToComponent[c], value);
  1.2644 +            if (c === "h" || c === "K")
  1.2645 +                defineProperty(result, "hour12", true);
  1.2646 +            else if (c === "H" || c === "k")
  1.2647 +                defineProperty(result, "hour12", false);
  1.2648 +        }
  1.2649 +    }
  1.2650 +}

mercurial