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 +}