1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/tests/supporting/testIntl.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1167 @@ 1.4 +// Copyright 2011-2012 Norbert Lindenberg. All rights reserved. 1.5 +// Copyright 2012-2013 Mozilla Corporation. All rights reserved. 1.6 +// This code is governed by the BSD license found in the LICENSE file. 1.7 + 1.8 +/** 1.9 + * This file contains shared functions for the tests in the conformance test 1.10 + * suite for the ECMAScript Internationalization API. 1.11 + * @author Norbert Lindenberg 1.12 + */ 1.13 + 1.14 + 1.15 +/** 1.16 + * @description Calls the provided function for every service constructor in 1.17 + * the Intl object, until f returns a falsy value. It returns the result of the 1.18 + * last call to f, mapped to a boolean. 1.19 + * @param {Function} f the function to call for each service constructor in 1.20 + * the Intl object. 1.21 + * @param {Function} Constructor the constructor object to test with. 1.22 + * @result {Boolean} whether the test succeeded. 1.23 + */ 1.24 +function testWithIntlConstructors(f) { 1.25 + var constructors = ["Collator", "NumberFormat", "DateTimeFormat"]; 1.26 + return constructors.every(function (constructor) { 1.27 + var Constructor = Intl[constructor]; 1.28 + var result; 1.29 + try { 1.30 + result = f(Constructor); 1.31 + } catch (e) { 1.32 + e.message += " (Testing with " + constructor + ".)"; 1.33 + throw e; 1.34 + } 1.35 + return result; 1.36 + }); 1.37 +} 1.38 + 1.39 + 1.40 +/** 1.41 + * Returns the name of the given constructor object, which must be one of 1.42 + * Intl.Collator, Intl.NumberFormat, or Intl.DateTimeFormat. 1.43 + * @param {object} Constructor a constructor 1.44 + * @return {string} the name of the constructor 1.45 + */ 1.46 +function getConstructorName(Constructor) { 1.47 + switch (Constructor) { 1.48 + case Intl.Collator: 1.49 + return "Collator"; 1.50 + case Intl.NumberFormat: 1.51 + return "NumberFormat"; 1.52 + case Intl.DateTimeFormat: 1.53 + return "DateTimeFormat"; 1.54 + default: 1.55 + $ERROR("test internal error: unknown Constructor"); 1.56 + } 1.57 +} 1.58 + 1.59 + 1.60 +/** 1.61 + * Taints a named data property of the given object by installing 1.62 + * a setter that throws an exception. 1.63 + * @param {object} obj the object whose data property to taint 1.64 + * @param {string} property the property to taint 1.65 + */ 1.66 +function taintDataProperty(obj, property) { 1.67 + Object.defineProperty(obj, property, { 1.68 + set: function(value) { 1.69 + $ERROR("Client code can adversely affect behavior: setter for " + property + "."); 1.70 + }, 1.71 + enumerable: false, 1.72 + configurable: true 1.73 + }); 1.74 +} 1.75 + 1.76 + 1.77 +/** 1.78 + * Taints a named method of the given object by replacing it with a function 1.79 + * that throws an exception. 1.80 + * @param {object} obj the object whose method to taint 1.81 + * @param {string} property the name of the method to taint 1.82 + */ 1.83 +function taintMethod(obj, property) { 1.84 + Object.defineProperty(obj, property, { 1.85 + value: function() { 1.86 + $ERROR("Client code can adversely affect behavior: method " + property + "."); 1.87 + }, 1.88 + writable: true, 1.89 + enumerable: false, 1.90 + configurable: true 1.91 + }); 1.92 +} 1.93 + 1.94 + 1.95 +/** 1.96 + * Taints the given properties (and similarly named properties) by installing 1.97 + * setters on Object.prototype that throw exceptions. 1.98 + * @param {Array} properties an array of property names to taint 1.99 + */ 1.100 +function taintProperties(properties) { 1.101 + properties.forEach(function (property) { 1.102 + var adaptedProperties = [property, "__" + property, "_" + property, property + "_", property + "__"]; 1.103 + adaptedProperties.forEach(function (property) { 1.104 + taintDataProperty(Object.prototype, property); 1.105 + }); 1.106 + }); 1.107 +} 1.108 + 1.109 + 1.110 +/** 1.111 + * Taints the Array object by creating a setter for the property "0" and 1.112 + * replacing some key methods with functions that throw exceptions. 1.113 + */ 1.114 +function taintArray() { 1.115 + taintDataProperty(Array.prototype, "0"); 1.116 + taintMethod(Array.prototype, "indexOf"); 1.117 + taintMethod(Array.prototype, "join"); 1.118 + taintMethod(Array.prototype, "push"); 1.119 + taintMethod(Array.prototype, "slice"); 1.120 + taintMethod(Array.prototype, "sort"); 1.121 +} 1.122 + 1.123 + 1.124 +// auxiliary data for getLocaleSupportInfo 1.125 +var languages = ["zh", "es", "en", "hi", "ur", "ar", "ja", "pa"]; 1.126 +var scripts = ["Latn", "Hans", "Deva", "Arab", "Jpan", "Hant"]; 1.127 +var countries = ["CN", "IN", "US", "PK", "JP", "TW", "HK", "SG"]; 1.128 +var localeSupportInfo = {}; 1.129 + 1.130 + 1.131 +/** 1.132 + * Gets locale support info for the given constructor object, which must be one 1.133 + * of Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat. 1.134 + * @param {object} Constructor the constructor for which to get locale support info 1.135 + * @return {object} locale support info with the following properties: 1.136 + * supported: array of fully supported language tags 1.137 + * byFallback: array of language tags that are supported through fallbacks 1.138 + * unsupported: array of unsupported language tags 1.139 + */ 1.140 +function getLocaleSupportInfo(Constructor) { 1.141 + var constructorName = getConstructorName(Constructor); 1.142 + if (localeSupportInfo[constructorName] !== undefined) { 1.143 + return localeSupportInfo[constructorName]; 1.144 + } 1.145 + 1.146 + var allTags = []; 1.147 + var i, j, k; 1.148 + var language, script, country; 1.149 + for (i = 0; i < languages.length; i++) { 1.150 + language = languages[i]; 1.151 + allTags.push(language); 1.152 + for (j = 0; j < scripts.length; j++) { 1.153 + script = scripts[j]; 1.154 + allTags.push(language + "-" + script); 1.155 + for (k = 0; k < countries.length; k++) { 1.156 + country = countries[k]; 1.157 + allTags.push(language + "-" + script + "-" + country); 1.158 + } 1.159 + } 1.160 + for (k = 0; k < countries.length; k++) { 1.161 + country = countries[k]; 1.162 + allTags.push(language + "-" + country); 1.163 + } 1.164 + } 1.165 + 1.166 + var supported = []; 1.167 + var byFallback = []; 1.168 + var unsupported = []; 1.169 + for (i = 0; i < allTags.length; i++) { 1.170 + var request = allTags[i]; 1.171 + var result = new Constructor([request], {localeMatcher: "lookup"}).resolvedOptions().locale; 1.172 + if (request === result) { 1.173 + supported.push(request); 1.174 + } else if (request.indexOf(result) === 0) { 1.175 + byFallback.push(request); 1.176 + } else { 1.177 + unsupported.push(request); 1.178 + } 1.179 + } 1.180 + 1.181 + localeSupportInfo[constructorName] = { 1.182 + supported: supported, 1.183 + byFallback: byFallback, 1.184 + unsupported: unsupported 1.185 + }; 1.186 + 1.187 + return localeSupportInfo[constructorName]; 1.188 +} 1.189 + 1.190 + 1.191 +/** 1.192 + * @description Tests whether locale is a String value representing a 1.193 + * structurally valid and canonicalized BCP 47 language tag, as defined in 1.194 + * sections 6.2.2 and 6.2.3 of the ECMAScript Internationalization API 1.195 + * Specification. 1.196 + * @param {String} locale the string to be tested. 1.197 + * @result {Boolean} whether the test succeeded. 1.198 + */ 1.199 +function isCanonicalizedStructurallyValidLanguageTag(locale) { 1.200 + 1.201 + /** 1.202 + * Regular expression defining BCP 47 language tags. 1.203 + * 1.204 + * Spec: RFC 5646 section 2.1. 1.205 + */ 1.206 + var alpha = "[a-zA-Z]", 1.207 + digit = "[0-9]", 1.208 + alphanum = "(" + alpha + "|" + digit + ")", 1.209 + regular = "(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang)", 1.210 + 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.211 + grandfathered = "(" + irregular + "|" + regular + ")", 1.212 + privateuse = "(x(-[a-z0-9]{1,8})+)", 1.213 + singleton = "(" + digit + "|[A-WY-Za-wy-z])", 1.214 + extension = "(" + singleton + "(-" + alphanum + "{2,8})+)", 1.215 + variant = "(" + alphanum + "{5,8}|(" + digit + alphanum + "{3}))", 1.216 + region = "(" + alpha + "{2}|" + digit + "{3})", 1.217 + script = "(" + alpha + "{4})", 1.218 + extlang = "(" + alpha + "{3}(-" + alpha + "{3}){0,2})", 1.219 + language = "(" + alpha + "{2,3}(-" + extlang + ")?|" + alpha + "{4}|" + alpha + "{5,8})", 1.220 + langtag = language + "(-" + script + ")?(-" + region + ")?(-" + variant + ")*(-" + extension + ")*(-" + privateuse + ")?", 1.221 + languageTag = "^(" + langtag + "|" + privateuse + "|" + grandfathered + ")$", 1.222 + languageTagRE = new RegExp(languageTag, "i"); 1.223 + var duplicateSingleton = "-" + singleton + "-(.*-)?\\1(?!" + alphanum + ")", 1.224 + duplicateSingletonRE = new RegExp(duplicateSingleton, "i"), 1.225 + duplicateVariant = "(" + alphanum + "{2,8}-)+" + variant + "-(" + alphanum + "{2,8}-)*\\3(?!" + alphanum + ")", 1.226 + duplicateVariantRE = new RegExp(duplicateVariant, "i"); 1.227 + 1.228 + 1.229 + /** 1.230 + * Verifies that the given string is a well-formed BCP 47 language tag 1.231 + * with no duplicate variant or singleton subtags. 1.232 + * 1.233 + * Spec: ECMAScript Internationalization API Specification, draft, 6.2.2. 1.234 + */ 1.235 + function isStructurallyValidLanguageTag(locale) { 1.236 + if (!languageTagRE.test(locale)) { 1.237 + return false; 1.238 + } 1.239 + locale = locale.split(/-x-/)[0]; 1.240 + return !duplicateSingletonRE.test(locale) && !duplicateVariantRE.test(locale); 1.241 + } 1.242 + 1.243 + 1.244 + /** 1.245 + * Mappings from complete tags to preferred values. 1.246 + * 1.247 + * Spec: IANA Language Subtag Registry. 1.248 + */ 1.249 + var __tagMappings = { 1.250 + // property names must be in lower case; values in canonical form 1.251 + 1.252 + // grandfathered tags from IANA language subtag registry, file date 2011-08-25 1.253 + "art-lojban": "jbo", 1.254 + "cel-gaulish": "cel-gaulish", 1.255 + "en-gb-oed": "en-GB-oed", 1.256 + "i-ami": "ami", 1.257 + "i-bnn": "bnn", 1.258 + "i-default": "i-default", 1.259 + "i-enochian": "i-enochian", 1.260 + "i-hak": "hak", 1.261 + "i-klingon": "tlh", 1.262 + "i-lux": "lb", 1.263 + "i-mingo": "i-mingo", 1.264 + "i-navajo": "nv", 1.265 + "i-pwn": "pwn", 1.266 + "i-tao": "tao", 1.267 + "i-tay": "tay", 1.268 + "i-tsu": "tsu", 1.269 + "no-bok": "nb", 1.270 + "no-nyn": "nn", 1.271 + "sgn-be-fr": "sfb", 1.272 + "sgn-be-nl": "vgt", 1.273 + "sgn-ch-de": "sgg", 1.274 + "zh-guoyu": "cmn", 1.275 + "zh-hakka": "hak", 1.276 + "zh-min": "zh-min", 1.277 + "zh-min-nan": "nan", 1.278 + "zh-xiang": "hsn", 1.279 + // deprecated redundant tags from IANA language subtag registry, file date 2011-08-25 1.280 + "sgn-br": "bzs", 1.281 + "sgn-co": "csn", 1.282 + "sgn-de": "gsg", 1.283 + "sgn-dk": "dsl", 1.284 + "sgn-es": "ssp", 1.285 + "sgn-fr": "fsl", 1.286 + "sgn-gb": "bfi", 1.287 + "sgn-gr": "gss", 1.288 + "sgn-ie": "isg", 1.289 + "sgn-it": "ise", 1.290 + "sgn-jp": "jsl", 1.291 + "sgn-mx": "mfs", 1.292 + "sgn-ni": "ncs", 1.293 + "sgn-nl": "dse", 1.294 + "sgn-no": "nsl", 1.295 + "sgn-pt": "psr", 1.296 + "sgn-se": "swl", 1.297 + "sgn-us": "ase", 1.298 + "sgn-za": "sfs", 1.299 + "zh-cmn": "cmn", 1.300 + "zh-cmn-hans": "cmn-Hans", 1.301 + "zh-cmn-hant": "cmn-Hant", 1.302 + "zh-gan": "gan", 1.303 + "zh-wuu": "wuu", 1.304 + "zh-yue": "yue", 1.305 + // deprecated variant with prefix from IANA language subtag registry, file date 2011-08-25 1.306 + "ja-latn-hepburn-heploc": "ja-Latn-alalc97" 1.307 + }; 1.308 + 1.309 + 1.310 + /** 1.311 + * Mappings from non-extlang subtags to preferred values. 1.312 + * 1.313 + * Spec: IANA Language Subtag Registry. 1.314 + */ 1.315 + var __subtagMappings = { 1.316 + // property names and values must be in canonical case 1.317 + // language subtags with Preferred-Value mappings from IANA language subtag registry, file date 2011-08-25 1.318 + "in": "id", 1.319 + "iw": "he", 1.320 + "ji": "yi", 1.321 + "jw": "jv", 1.322 + "mo": "ro", 1.323 + "ayx": "nun", 1.324 + "cjr": "mom", 1.325 + "cmk": "xch", 1.326 + "drh": "khk", 1.327 + "drw": "prs", 1.328 + "gav": "dev", 1.329 + "mst": "mry", 1.330 + "myt": "mry", 1.331 + "tie": "ras", 1.332 + "tkk": "twm", 1.333 + "tnf": "prs", 1.334 + // region subtags with Preferred-Value mappings from IANA language subtag registry, file date 2011-08-25 1.335 + "BU": "MM", 1.336 + "DD": "DE", 1.337 + "FX": "FR", 1.338 + "TP": "TL", 1.339 + "YD": "YE", 1.340 + "ZR": "CD" 1.341 + }; 1.342 + 1.343 + 1.344 + /** 1.345 + * Mappings from extlang subtags to preferred values. 1.346 + * 1.347 + * Spec: IANA Language Subtag Registry. 1.348 + */ 1.349 + var __extlangMappings = { 1.350 + // extlang subtags with Preferred-Value mappings from IANA language subtag registry, file date 2011-08-25 1.351 + // values are arrays with [0] the replacement value, [1] (if present) the prefix to be removed 1.352 + "aao": ["aao", "ar"], 1.353 + "abh": ["abh", "ar"], 1.354 + "abv": ["abv", "ar"], 1.355 + "acm": ["acm", "ar"], 1.356 + "acq": ["acq", "ar"], 1.357 + "acw": ["acw", "ar"], 1.358 + "acx": ["acx", "ar"], 1.359 + "acy": ["acy", "ar"], 1.360 + "adf": ["adf", "ar"], 1.361 + "ads": ["ads", "sgn"], 1.362 + "aeb": ["aeb", "ar"], 1.363 + "aec": ["aec", "ar"], 1.364 + "aed": ["aed", "sgn"], 1.365 + "aen": ["aen", "sgn"], 1.366 + "afb": ["afb", "ar"], 1.367 + "afg": ["afg", "sgn"], 1.368 + "ajp": ["ajp", "ar"], 1.369 + "apc": ["apc", "ar"], 1.370 + "apd": ["apd", "ar"], 1.371 + "arb": ["arb", "ar"], 1.372 + "arq": ["arq", "ar"], 1.373 + "ars": ["ars", "ar"], 1.374 + "ary": ["ary", "ar"], 1.375 + "arz": ["arz", "ar"], 1.376 + "ase": ["ase", "sgn"], 1.377 + "asf": ["asf", "sgn"], 1.378 + "asp": ["asp", "sgn"], 1.379 + "asq": ["asq", "sgn"], 1.380 + "asw": ["asw", "sgn"], 1.381 + "auz": ["auz", "ar"], 1.382 + "avl": ["avl", "ar"], 1.383 + "ayh": ["ayh", "ar"], 1.384 + "ayl": ["ayl", "ar"], 1.385 + "ayn": ["ayn", "ar"], 1.386 + "ayp": ["ayp", "ar"], 1.387 + "bbz": ["bbz", "ar"], 1.388 + "bfi": ["bfi", "sgn"], 1.389 + "bfk": ["bfk", "sgn"], 1.390 + "bjn": ["bjn", "ms"], 1.391 + "bog": ["bog", "sgn"], 1.392 + "bqn": ["bqn", "sgn"], 1.393 + "bqy": ["bqy", "sgn"], 1.394 + "btj": ["btj", "ms"], 1.395 + "bve": ["bve", "ms"], 1.396 + "bvl": ["bvl", "sgn"], 1.397 + "bvu": ["bvu", "ms"], 1.398 + "bzs": ["bzs", "sgn"], 1.399 + "cdo": ["cdo", "zh"], 1.400 + "cds": ["cds", "sgn"], 1.401 + "cjy": ["cjy", "zh"], 1.402 + "cmn": ["cmn", "zh"], 1.403 + "coa": ["coa", "ms"], 1.404 + "cpx": ["cpx", "zh"], 1.405 + "csc": ["csc", "sgn"], 1.406 + "csd": ["csd", "sgn"], 1.407 + "cse": ["cse", "sgn"], 1.408 + "csf": ["csf", "sgn"], 1.409 + "csg": ["csg", "sgn"], 1.410 + "csl": ["csl", "sgn"], 1.411 + "csn": ["csn", "sgn"], 1.412 + "csq": ["csq", "sgn"], 1.413 + "csr": ["csr", "sgn"], 1.414 + "czh": ["czh", "zh"], 1.415 + "czo": ["czo", "zh"], 1.416 + "doq": ["doq", "sgn"], 1.417 + "dse": ["dse", "sgn"], 1.418 + "dsl": ["dsl", "sgn"], 1.419 + "dup": ["dup", "ms"], 1.420 + "ecs": ["ecs", "sgn"], 1.421 + "esl": ["esl", "sgn"], 1.422 + "esn": ["esn", "sgn"], 1.423 + "eso": ["eso", "sgn"], 1.424 + "eth": ["eth", "sgn"], 1.425 + "fcs": ["fcs", "sgn"], 1.426 + "fse": ["fse", "sgn"], 1.427 + "fsl": ["fsl", "sgn"], 1.428 + "fss": ["fss", "sgn"], 1.429 + "gan": ["gan", "zh"], 1.430 + "gom": ["gom", "kok"], 1.431 + "gse": ["gse", "sgn"], 1.432 + "gsg": ["gsg", "sgn"], 1.433 + "gsm": ["gsm", "sgn"], 1.434 + "gss": ["gss", "sgn"], 1.435 + "gus": ["gus", "sgn"], 1.436 + "hab": ["hab", "sgn"], 1.437 + "haf": ["haf", "sgn"], 1.438 + "hak": ["hak", "zh"], 1.439 + "hds": ["hds", "sgn"], 1.440 + "hji": ["hji", "ms"], 1.441 + "hks": ["hks", "sgn"], 1.442 + "hos": ["hos", "sgn"], 1.443 + "hps": ["hps", "sgn"], 1.444 + "hsh": ["hsh", "sgn"], 1.445 + "hsl": ["hsl", "sgn"], 1.446 + "hsn": ["hsn", "zh"], 1.447 + "icl": ["icl", "sgn"], 1.448 + "ils": ["ils", "sgn"], 1.449 + "inl": ["inl", "sgn"], 1.450 + "ins": ["ins", "sgn"], 1.451 + "ise": ["ise", "sgn"], 1.452 + "isg": ["isg", "sgn"], 1.453 + "isr": ["isr", "sgn"], 1.454 + "jak": ["jak", "ms"], 1.455 + "jax": ["jax", "ms"], 1.456 + "jcs": ["jcs", "sgn"], 1.457 + "jhs": ["jhs", "sgn"], 1.458 + "jls": ["jls", "sgn"], 1.459 + "jos": ["jos", "sgn"], 1.460 + "jsl": ["jsl", "sgn"], 1.461 + "jus": ["jus", "sgn"], 1.462 + "kgi": ["kgi", "sgn"], 1.463 + "knn": ["knn", "kok"], 1.464 + "kvb": ["kvb", "ms"], 1.465 + "kvk": ["kvk", "sgn"], 1.466 + "kvr": ["kvr", "ms"], 1.467 + "kxd": ["kxd", "ms"], 1.468 + "lbs": ["lbs", "sgn"], 1.469 + "lce": ["lce", "ms"], 1.470 + "lcf": ["lcf", "ms"], 1.471 + "liw": ["liw", "ms"], 1.472 + "lls": ["lls", "sgn"], 1.473 + "lsg": ["lsg", "sgn"], 1.474 + "lsl": ["lsl", "sgn"], 1.475 + "lso": ["lso", "sgn"], 1.476 + "lsp": ["lsp", "sgn"], 1.477 + "lst": ["lst", "sgn"], 1.478 + "lsy": ["lsy", "sgn"], 1.479 + "ltg": ["ltg", "lv"], 1.480 + "lvs": ["lvs", "lv"], 1.481 + "lzh": ["lzh", "zh"], 1.482 + "max": ["max", "ms"], 1.483 + "mdl": ["mdl", "sgn"], 1.484 + "meo": ["meo", "ms"], 1.485 + "mfa": ["mfa", "ms"], 1.486 + "mfb": ["mfb", "ms"], 1.487 + "mfs": ["mfs", "sgn"], 1.488 + "min": ["min", "ms"], 1.489 + "mnp": ["mnp", "zh"], 1.490 + "mqg": ["mqg", "ms"], 1.491 + "mre": ["mre", "sgn"], 1.492 + "msd": ["msd", "sgn"], 1.493 + "msi": ["msi", "ms"], 1.494 + "msr": ["msr", "sgn"], 1.495 + "mui": ["mui", "ms"], 1.496 + "mzc": ["mzc", "sgn"], 1.497 + "mzg": ["mzg", "sgn"], 1.498 + "mzy": ["mzy", "sgn"], 1.499 + "nan": ["nan", "zh"], 1.500 + "nbs": ["nbs", "sgn"], 1.501 + "ncs": ["ncs", "sgn"], 1.502 + "nsi": ["nsi", "sgn"], 1.503 + "nsl": ["nsl", "sgn"], 1.504 + "nsp": ["nsp", "sgn"], 1.505 + "nsr": ["nsr", "sgn"], 1.506 + "nzs": ["nzs", "sgn"], 1.507 + "okl": ["okl", "sgn"], 1.508 + "orn": ["orn", "ms"], 1.509 + "ors": ["ors", "ms"], 1.510 + "pel": ["pel", "ms"], 1.511 + "pga": ["pga", "ar"], 1.512 + "pks": ["pks", "sgn"], 1.513 + "prl": ["prl", "sgn"], 1.514 + "prz": ["prz", "sgn"], 1.515 + "psc": ["psc", "sgn"], 1.516 + "psd": ["psd", "sgn"], 1.517 + "pse": ["pse", "ms"], 1.518 + "psg": ["psg", "sgn"], 1.519 + "psl": ["psl", "sgn"], 1.520 + "pso": ["pso", "sgn"], 1.521 + "psp": ["psp", "sgn"], 1.522 + "psr": ["psr", "sgn"], 1.523 + "pys": ["pys", "sgn"], 1.524 + "rms": ["rms", "sgn"], 1.525 + "rsi": ["rsi", "sgn"], 1.526 + "rsl": ["rsl", "sgn"], 1.527 + "sdl": ["sdl", "sgn"], 1.528 + "sfb": ["sfb", "sgn"], 1.529 + "sfs": ["sfs", "sgn"], 1.530 + "sgg": ["sgg", "sgn"], 1.531 + "sgx": ["sgx", "sgn"], 1.532 + "shu": ["shu", "ar"], 1.533 + "slf": ["slf", "sgn"], 1.534 + "sls": ["sls", "sgn"], 1.535 + "sqs": ["sqs", "sgn"], 1.536 + "ssh": ["ssh", "ar"], 1.537 + "ssp": ["ssp", "sgn"], 1.538 + "ssr": ["ssr", "sgn"], 1.539 + "svk": ["svk", "sgn"], 1.540 + "swc": ["swc", "sw"], 1.541 + "swh": ["swh", "sw"], 1.542 + "swl": ["swl", "sgn"], 1.543 + "syy": ["syy", "sgn"], 1.544 + "tmw": ["tmw", "ms"], 1.545 + "tse": ["tse", "sgn"], 1.546 + "tsm": ["tsm", "sgn"], 1.547 + "tsq": ["tsq", "sgn"], 1.548 + "tss": ["tss", "sgn"], 1.549 + "tsy": ["tsy", "sgn"], 1.550 + "tza": ["tza", "sgn"], 1.551 + "ugn": ["ugn", "sgn"], 1.552 + "ugy": ["ugy", "sgn"], 1.553 + "ukl": ["ukl", "sgn"], 1.554 + "uks": ["uks", "sgn"], 1.555 + "urk": ["urk", "ms"], 1.556 + "uzn": ["uzn", "uz"], 1.557 + "uzs": ["uzs", "uz"], 1.558 + "vgt": ["vgt", "sgn"], 1.559 + "vkk": ["vkk", "ms"], 1.560 + "vkt": ["vkt", "ms"], 1.561 + "vsi": ["vsi", "sgn"], 1.562 + "vsl": ["vsl", "sgn"], 1.563 + "vsv": ["vsv", "sgn"], 1.564 + "wuu": ["wuu", "zh"], 1.565 + "xki": ["xki", "sgn"], 1.566 + "xml": ["xml", "sgn"], 1.567 + "xmm": ["xmm", "ms"], 1.568 + "xms": ["xms", "sgn"], 1.569 + "yds": ["yds", "sgn"], 1.570 + "ysl": ["ysl", "sgn"], 1.571 + "yue": ["yue", "zh"], 1.572 + "zib": ["zib", "sgn"], 1.573 + "zlm": ["zlm", "ms"], 1.574 + "zmi": ["zmi", "ms"], 1.575 + "zsl": ["zsl", "sgn"], 1.576 + "zsm": ["zsm", "ms"] 1.577 + }; 1.578 + 1.579 + 1.580 + /** 1.581 + * Canonicalizes the given well-formed BCP 47 language tag, including regularized case of subtags. 1.582 + * 1.583 + * Spec: ECMAScript Internationalization API Specification, draft, 6.2.3. 1.584 + * Spec: RFC 5646, section 4.5. 1.585 + */ 1.586 + function canonicalizeLanguageTag(locale) { 1.587 + 1.588 + // start with lower case for easier processing, and because most subtags will need to be lower case anyway 1.589 + locale = locale.toLowerCase(); 1.590 + 1.591 + // handle mappings for complete tags 1.592 + if (__tagMappings.hasOwnProperty(locale)) { 1.593 + return __tagMappings[locale]; 1.594 + } 1.595 + 1.596 + var subtags = locale.split("-"); 1.597 + var i = 0; 1.598 + 1.599 + // handle standard part: all subtags before first singleton or "x" 1.600 + while (i < subtags.length) { 1.601 + var subtag = subtags[i]; 1.602 + if (subtag.length === 1 && (i > 0 || subtag === "x")) { 1.603 + break; 1.604 + } else if (i !== 0 && subtag.length === 2) { 1.605 + subtag = subtag.toUpperCase(); 1.606 + } else if (subtag.length === 4) { 1.607 + subtag = subtag[0].toUpperCase() + subtag.substring(1).toLowerCase(); 1.608 + } 1.609 + if (__subtagMappings.hasOwnProperty(subtag)) { 1.610 + subtag = __subtagMappings[subtag]; 1.611 + } else if (__extlangMappings.hasOwnProperty(subtag)) { 1.612 + subtag = __extlangMappings[subtag][0]; 1.613 + if (i === 1 && __extlangMappings[subtag][1] === subtags[0]) { 1.614 + subtags.shift(); 1.615 + i--; 1.616 + } 1.617 + } 1.618 + subtags[i] = subtag; 1.619 + i++; 1.620 + } 1.621 + var normal = subtags.slice(0, i).join("-"); 1.622 + 1.623 + // handle extensions 1.624 + var extensions = []; 1.625 + while (i < subtags.length && subtags[i] !== "x") { 1.626 + var extensionStart = i; 1.627 + i++; 1.628 + while (i < subtags.length && subtags[i].length > 1) { 1.629 + i++; 1.630 + } 1.631 + var extension = subtags.slice(extensionStart, i).join("-"); 1.632 + extensions.push(extension); 1.633 + } 1.634 + extensions.sort(); 1.635 + 1.636 + // handle private use 1.637 + var privateUse; 1.638 + if (i < subtags.length) { 1.639 + privateUse = subtags.slice(i).join("-"); 1.640 + } 1.641 + 1.642 + // put everything back together 1.643 + var canonical = normal; 1.644 + if (extensions.length > 0) { 1.645 + canonical += "-" + extensions.join("-"); 1.646 + } 1.647 + if (privateUse !== undefined) { 1.648 + if (canonical.length > 0) { 1.649 + canonical += "-" + privateUse; 1.650 + } else { 1.651 + canonical = privateUse; 1.652 + } 1.653 + } 1.654 + 1.655 + return canonical; 1.656 + } 1.657 + 1.658 + return typeof locale === "string" && isStructurallyValidLanguageTag(locale) && 1.659 + canonicalizeLanguageTag(locale) === locale; 1.660 +} 1.661 + 1.662 + 1.663 +/** 1.664 + * Tests whether the named options property is correctly handled by the given constructor. 1.665 + * @param {object} Constructor the constructor to test. 1.666 + * @param {string} property the name of the options property to test. 1.667 + * @param {string} type the type that values of the property are expected to have 1.668 + * @param {Array} [values] an array of allowed values for the property. Not needed for boolean. 1.669 + * @param {any} fallback the fallback value that the property assumes if not provided. 1.670 + * @param {object} testOptions additional options: 1.671 + * @param {boolean} isOptional whether support for this property is optional for implementations. 1.672 + * @param {boolean} noReturn whether the resulting value of the property is not returned. 1.673 + * @param {boolean} isILD whether the resulting value of the property is implementation and locale dependent. 1.674 + * @param {object} extra additional option to pass along, properties are value -> {option: value}. 1.675 + * @return {boolean} whether the test succeeded. 1.676 + */ 1.677 +function testOption(Constructor, property, type, values, fallback, testOptions) { 1.678 + var isOptional = testOptions !== undefined && testOptions.isOptional === true; 1.679 + var noReturn = testOptions !== undefined && testOptions.noReturn === true; 1.680 + var isILD = testOptions !== undefined && testOptions.isILD === true; 1.681 + 1.682 + function addExtraOptions(options, value, testOptions) { 1.683 + if (testOptions !== undefined && testOptions.extra !== undefined) { 1.684 + var extra; 1.685 + if (value !== undefined && testOptions.extra[value] !== undefined) { 1.686 + extra = testOptions.extra[value]; 1.687 + } else if (testOptions.extra.any !== undefined) { 1.688 + extra = testOptions.extra.any; 1.689 + } 1.690 + if (extra !== undefined) { 1.691 + Object.getOwnPropertyNames(extra).forEach(function (prop) { 1.692 + options[prop] = extra[prop]; 1.693 + }); 1.694 + } 1.695 + } 1.696 + } 1.697 + 1.698 + var testValues, options, obj, expected, actual, error; 1.699 + 1.700 + // test that the specified values are accepted. Also add values that convert to specified values. 1.701 + if (type === "boolean") { 1.702 + if (values === undefined) { 1.703 + values = [true, false]; 1.704 + } 1.705 + testValues = values.slice(0); 1.706 + testValues.push(888); 1.707 + testValues.push(0); 1.708 + } else if (type === "string") { 1.709 + testValues = values.slice(0); 1.710 + testValues.push({toString: function () { return values[0]; }}); 1.711 + } 1.712 + testValues.forEach(function (value) { 1.713 + options = {}; 1.714 + options[property] = value; 1.715 + addExtraOptions(options, value, testOptions); 1.716 + obj = new Constructor(undefined, options); 1.717 + if (noReturn) { 1.718 + if (obj.resolvedOptions().hasOwnProperty(property)) { 1.719 + $ERROR("Option property " + property + " is returned, but shouldn't be."); 1.720 + } 1.721 + } else { 1.722 + actual = obj.resolvedOptions()[property]; 1.723 + if (isILD) { 1.724 + if (actual !== undefined && values.indexOf(actual) === -1) { 1.725 + $ERROR("Invalid value " + actual + " returned for property " + property + "."); 1.726 + } 1.727 + } else { 1.728 + if (type === "boolean") { 1.729 + expected = Boolean(value); 1.730 + } else if (type === "string") { 1.731 + expected = String(value); 1.732 + } 1.733 + if (actual !== expected && !(isOptional && actual === undefined)) { 1.734 + $ERROR("Option value " + value + " for property " + property + 1.735 + " was not accepted; got " + actual + " instead."); 1.736 + } 1.737 + } 1.738 + } 1.739 + }); 1.740 + 1.741 + // test that invalid values are rejected 1.742 + if (type === "string") { 1.743 + var invalidValues = ["invalidValue", -1, null]; 1.744 + // assume that we won't have values in caseless scripts 1.745 + if (values[0].toUpperCase() !== values[0]) { 1.746 + invalidValues.push(values[0].toUpperCase()); 1.747 + } else { 1.748 + invalidValues.push(values[0].toLowerCase()); 1.749 + } 1.750 + invalidValues.forEach(function (value) { 1.751 + options = {}; 1.752 + options[property] = value; 1.753 + addExtraOptions(options, value, testOptions); 1.754 + error = undefined; 1.755 + try { 1.756 + obj = new Constructor(undefined, options); 1.757 + } catch (e) { 1.758 + error = e; 1.759 + } 1.760 + if (error === undefined) { 1.761 + $ERROR("Invalid option value " + value + " for property " + property + " was not rejected."); 1.762 + } else if (error.name !== "RangeError") { 1.763 + $ERROR("Invalid option value " + value + " for property " + property + " was rejected with wrong error " + error.name + "."); 1.764 + } 1.765 + }); 1.766 + } 1.767 + 1.768 + // test that fallback value or another valid value is used if no options value is provided 1.769 + if (!noReturn) { 1.770 + options = {}; 1.771 + addExtraOptions(options, undefined, testOptions); 1.772 + obj = new Constructor(undefined, options); 1.773 + actual = obj.resolvedOptions()[property]; 1.774 + if (!(isOptional && actual === undefined)) { 1.775 + if (fallback !== undefined) { 1.776 + if (actual !== fallback) { 1.777 + $ERROR("Option fallback value " + fallback + " for property " + property + 1.778 + " was not used; got " + actual + " instead."); 1.779 + } 1.780 + } else { 1.781 + if (values.indexOf(actual) === -1 && !(isILD && actual === undefined)) { 1.782 + $ERROR("Invalid value " + actual + " returned for property " + property + "."); 1.783 + } 1.784 + } 1.785 + } 1.786 + } 1.787 + 1.788 + return true; 1.789 +} 1.790 + 1.791 + 1.792 +/** 1.793 + * Tests whether the named property of the given object has a valid value 1.794 + * and the default attributes of the properties of an object literal. 1.795 + * @param {Object} obj the object to be tested. 1.796 + * @param {string} property the name of the property 1.797 + * @param {Function|Array} valid either a function that tests value for validity and returns a boolean, 1.798 + * an array of valid values. 1.799 + * @exception if the property has an invalid value. 1.800 + */ 1.801 +function testProperty(obj, property, valid) { 1.802 + var desc = Object.getOwnPropertyDescriptor(obj, property); 1.803 + if (!desc.writable) { 1.804 + $ERROR("Property " + property + " must be writable."); 1.805 + } 1.806 + if (!desc.enumerable) { 1.807 + $ERROR("Property " + property + " must be enumerable."); 1.808 + } 1.809 + if (!desc.configurable) { 1.810 + $ERROR("Property " + property + " must be configurable."); 1.811 + } 1.812 + var value = desc.value; 1.813 + var isValid = (typeof valid === "function") ? valid(value) : (valid.indexOf(value) !== -1); 1.814 + if (!isValid) { 1.815 + $ERROR("Property value " + value + " is not allowed for property " + property + "."); 1.816 + } 1.817 +} 1.818 + 1.819 + 1.820 +/** 1.821 + * Tests whether the named property of the given object, if present at all, has a valid value 1.822 + * and the default attributes of the properties of an object literal. 1.823 + * @param {Object} obj the object to be tested. 1.824 + * @param {string} property the name of the property 1.825 + * @param {Function|Array} valid either a function that tests value for validity and returns a boolean, 1.826 + * an array of valid values. 1.827 + * @exception if the property is present and has an invalid value. 1.828 + */ 1.829 +function mayHaveProperty(obj, property, valid) { 1.830 + if (obj.hasOwnProperty(property)) { 1.831 + testProperty(obj, property, valid); 1.832 + } 1.833 +} 1.834 + 1.835 + 1.836 +/** 1.837 + * Tests whether the given object has the named property with a valid value 1.838 + * and the default attributes of the properties of an object literal. 1.839 + * @param {Object} obj the object to be tested. 1.840 + * @param {string} property the name of the property 1.841 + * @param {Function|Array} valid either a function that tests value for validity and returns a boolean, 1.842 + * an array of valid values. 1.843 + * @exception if the property is missing or has an invalid value. 1.844 + */ 1.845 +function mustHaveProperty(obj, property, valid) { 1.846 + if (!obj.hasOwnProperty(property)) { 1.847 + $ERROR("Object is missing property " + property + "."); 1.848 + } 1.849 + testProperty(obj, property, valid); 1.850 +} 1.851 + 1.852 + 1.853 +/** 1.854 + * Tests whether the given object does not have the named property. 1.855 + * @param {Object} obj the object to be tested. 1.856 + * @param {string} property the name of the property 1.857 + * @exception if the property is present. 1.858 + */ 1.859 +function mustNotHaveProperty(obj, property) { 1.860 + if (obj.hasOwnProperty(property)) { 1.861 + $ERROR("Object has property it mustn't have: " + property + "."); 1.862 + } 1.863 +} 1.864 + 1.865 + 1.866 +/** 1.867 + * Properties of the RegExp constructor that may be affected by use of regular 1.868 + * expressions, and the default values of these properties. Properties are from 1.869 + * https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Deprecated_and_obsolete_features#RegExp_Properties 1.870 + */ 1.871 +var regExpProperties = ["$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", 1.872 + "$_", "$*", "$&", "$+", "$`", "$'", 1.873 + "input", "lastMatch", "lastParen", "leftContext", "rightContext" 1.874 +]; 1.875 + 1.876 +var regExpPropertiesDefaultValues = (function () { 1.877 + var values = Object.create(null); 1.878 + regExpProperties.forEach(function (property) { 1.879 + values[property] = RegExp[property]; 1.880 + }); 1.881 + return values; 1.882 +}()); 1.883 + 1.884 + 1.885 +/** 1.886 + * Tests that executing the provided function (which may use regular expressions 1.887 + * in its implementation) does not create or modify unwanted properties on the 1.888 + * RegExp constructor. 1.889 + */ 1.890 +function testForUnwantedRegExpChanges(testFunc) { 1.891 + regExpProperties.forEach(function (property) { 1.892 + RegExp[property] = regExpPropertiesDefaultValues[property]; 1.893 + }); 1.894 + testFunc(); 1.895 + regExpProperties.forEach(function (property) { 1.896 + if (RegExp[property] !== regExpPropertiesDefaultValues[property]) { 1.897 + $ERROR("RegExp has unexpected property " + property + " with value " + 1.898 + RegExp[property] + "."); 1.899 + } 1.900 + }); 1.901 +} 1.902 + 1.903 + 1.904 +/** 1.905 + * Tests whether name is a valid BCP 47 numbering system name 1.906 + * and not excluded from use in the ECMAScript Internationalization API. 1.907 + * @param {string} name the name to be tested. 1.908 + * @return {boolean} whether name is a valid BCP 47 numbering system name and 1.909 + * allowed for use in the ECMAScript Internationalization API. 1.910 + */ 1.911 + 1.912 +function isValidNumberingSystem(name) { 1.913 + 1.914 + // source: CLDR file common/bcp47/number.xml; version CLDR 21. 1.915 + var numberingSystems = [ 1.916 + "arab", 1.917 + "arabext", 1.918 + "armn", 1.919 + "armnlow", 1.920 + "bali", 1.921 + "beng", 1.922 + "brah", 1.923 + "cakm", 1.924 + "cham", 1.925 + "deva", 1.926 + "ethi", 1.927 + "finance", 1.928 + "fullwide", 1.929 + "geor", 1.930 + "grek", 1.931 + "greklow", 1.932 + "gujr", 1.933 + "guru", 1.934 + "hanidec", 1.935 + "hans", 1.936 + "hansfin", 1.937 + "hant", 1.938 + "hantfin", 1.939 + "hebr", 1.940 + "java", 1.941 + "jpan", 1.942 + "jpanfin", 1.943 + "kali", 1.944 + "khmr", 1.945 + "knda", 1.946 + "osma", 1.947 + "lana", 1.948 + "lanatham", 1.949 + "laoo", 1.950 + "latn", 1.951 + "lepc", 1.952 + "limb", 1.953 + "mlym", 1.954 + "mong", 1.955 + "mtei", 1.956 + "mymr", 1.957 + "mymrshan", 1.958 + "native", 1.959 + "nkoo", 1.960 + "olck", 1.961 + "orya", 1.962 + "roman", 1.963 + "romanlow", 1.964 + "saur", 1.965 + "shrd", 1.966 + "sora", 1.967 + "sund", 1.968 + "talu", 1.969 + "takr", 1.970 + "taml", 1.971 + "tamldec", 1.972 + "telu", 1.973 + "thai", 1.974 + "tibt", 1.975 + "traditio", 1.976 + "vaii" 1.977 + ]; 1.978 + 1.979 + var excluded = [ 1.980 + "finance", 1.981 + "native", 1.982 + "traditio" 1.983 + ]; 1.984 + 1.985 + 1.986 + return numberingSystems.indexOf(name) !== -1 && excluded.indexOf(name) === -1; 1.987 +} 1.988 + 1.989 + 1.990 +/** 1.991 + * Provides the digits of numbering systems with simple digit mappings, 1.992 + * as specified in 11.3.2. 1.993 + */ 1.994 + 1.995 +var numberingSystemDigits = { 1.996 + arab: "٠١٢٣٤٥٦٧٨٩", 1.997 + arabext: "۰۱۲۳۴۵۶۷۸۹", 1.998 + beng: "০১২৩৪৫৬৭৮৯", 1.999 + deva: "०१२३४५६७८९", 1.1000 + fullwide: "0123456789", 1.1001 + gujr: "૦૧૨૩૪૫૬૭૮૯", 1.1002 + guru: "੦੧੨੩੪੫੬੭੮੯", 1.1003 + hanidec: "〇一二三四五六七八九", 1.1004 + khmr: "០១២៣៤៥៦៧៨៩", 1.1005 + knda: "೦೧೨೩೪೫೬೭೮೯", 1.1006 + laoo: "໐໑໒໓໔໕໖໗໘໙", 1.1007 + latn: "0123456789", 1.1008 + mlym: "൦൧൨൩൪൫൬൭൮൯", 1.1009 + mong: "᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙", 1.1010 + mymr: "၀၁၂၃၄၅၆၇၈၉", 1.1011 + orya: "୦୧୨୩୪୫୬୭୮୯", 1.1012 + tamldec: "௦௧௨௩௪௫௬௭௮௯", 1.1013 + telu: "౦౧౨౩౪౫౬౭౮౯", 1.1014 + thai: "๐๑๒๓๔๕๖๗๘๙", 1.1015 + tibt: "༠༡༢༣༤༥༦༧༨༩" 1.1016 +}; 1.1017 + 1.1018 + 1.1019 +/** 1.1020 + * Tests that number formatting is handled correctly. The function checks that the 1.1021 + * digit sequences in formatted output are as specified, converted to the 1.1022 + * selected numbering system, and embedded in consistent localized patterns. 1.1023 + * @param {Array} locales the locales to be tested. 1.1024 + * @param {Array} numberingSystems the numbering systems to be tested. 1.1025 + * @param {Object} options the options to pass to Intl.NumberFormat. Options 1.1026 + * must include {useGrouping: false}, and must cause 1.1 to be formatted 1.1027 + * pre- and post-decimal digits. 1.1028 + * @param {Object} testData maps input data (in ES5 9.3.1 format) to expected output strings 1.1029 + * in unlocalized format with Western digits. 1.1030 + */ 1.1031 + 1.1032 +function testNumberFormat(locales, numberingSystems, options, testData) { 1.1033 + locales.forEach(function (locale) { 1.1034 + numberingSystems.forEach(function (numbering) { 1.1035 + var digits = numberingSystemDigits[numbering]; 1.1036 + var format = new Intl.NumberFormat([locale + "-u-nu-" + numbering], options); 1.1037 + 1.1038 + function getPatternParts(positive) { 1.1039 + var n = positive ? 1.1 : -1.1; 1.1040 + var formatted = format.format(n); 1.1041 + var oneoneRE = "([^" + digits + "]*)[" + digits + "]+([^" + digits + "]+)[" + digits + "]+([^" + digits + "]*)"; 1.1042 + var match = formatted.match(new RegExp(oneoneRE)); 1.1043 + if (match === null) { 1.1044 + $ERROR("Unexpected formatted " + n + " for " + 1.1045 + format.resolvedOptions().locale + " and options " + 1.1046 + JSON.stringify(options) + ": " + formatted); 1.1047 + } 1.1048 + return match; 1.1049 + } 1.1050 + 1.1051 + function toNumbering(raw) { 1.1052 + return raw.replace(/[0-9]/g, function (digit) { 1.1053 + return digits[digit.charCodeAt(0) - "0".charCodeAt(0)]; 1.1054 + }); 1.1055 + } 1.1056 + 1.1057 + function buildExpected(raw, patternParts) { 1.1058 + var period = raw.indexOf("."); 1.1059 + if (period === -1) { 1.1060 + return patternParts[1] + toNumbering(raw) + patternParts[3]; 1.1061 + } else { 1.1062 + return patternParts[1] + 1.1063 + toNumbering(raw.substring(0, period)) + 1.1064 + patternParts[2] + 1.1065 + toNumbering(raw.substring(period + 1)) + 1.1066 + patternParts[3]; 1.1067 + } 1.1068 + } 1.1069 + 1.1070 + if (format.resolvedOptions().numberingSystem === numbering) { 1.1071 + // figure out prefixes, infixes, suffixes for positive and negative values 1.1072 + var posPatternParts = getPatternParts(true); 1.1073 + var negPatternParts = getPatternParts(false); 1.1074 + 1.1075 + Object.getOwnPropertyNames(testData).forEach(function (input) { 1.1076 + var rawExpected = testData[input]; 1.1077 + var patternParts; 1.1078 + if (rawExpected[0] === "-") { 1.1079 + patternParts = negPatternParts; 1.1080 + rawExpected = rawExpected.substring(1); 1.1081 + } else { 1.1082 + patternParts = posPatternParts; 1.1083 + } 1.1084 + var expected = buildExpected(rawExpected, patternParts); 1.1085 + var actual = format.format(input); 1.1086 + if (actual !== expected) { 1.1087 + $ERROR("Formatted value for " + input + ", " + 1.1088 + format.resolvedOptions().locale + " and options " + 1.1089 + JSON.stringify(options) + " is " + actual + "; expected " + expected + "."); 1.1090 + } 1.1091 + }); 1.1092 + } 1.1093 + }); 1.1094 + }); 1.1095 +} 1.1096 + 1.1097 + 1.1098 +/** 1.1099 + * Return the components of date-time formats. 1.1100 + * @return {Array} an array with all date-time components. 1.1101 + */ 1.1102 + 1.1103 +function getDateTimeComponents() { 1.1104 + return ["weekday", "era", "year", "month", "day", "hour", "minute", "second", "timeZoneName"]; 1.1105 +} 1.1106 + 1.1107 + 1.1108 +/** 1.1109 + * Return the valid values for the given date-time component, as specified 1.1110 + * by the table in section 12.1.1. 1.1111 + * @param {string} component a date-time component. 1.1112 + * @return {Array} an array with the valid values for the component. 1.1113 + */ 1.1114 + 1.1115 +function getDateTimeComponentValues(component) { 1.1116 + 1.1117 + var components = { 1.1118 + weekday: ["narrow", "short", "long"], 1.1119 + era: ["narrow", "short", "long"], 1.1120 + year: ["2-digit", "numeric"], 1.1121 + month: ["2-digit", "numeric", "narrow", "short", "long"], 1.1122 + day: ["2-digit", "numeric"], 1.1123 + hour: ["2-digit", "numeric"], 1.1124 + minute: ["2-digit", "numeric"], 1.1125 + second: ["2-digit", "numeric"], 1.1126 + timeZoneName: ["short", "long"] 1.1127 + }; 1.1128 + 1.1129 + var result = components[component]; 1.1130 + if (result === undefined) { 1.1131 + $ERROR("Internal error: No values defined for date-time component " + component + "."); 1.1132 + } 1.1133 + return result; 1.1134 +} 1.1135 + 1.1136 + 1.1137 +/** 1.1138 + * Tests that the given value is valid for the given date-time component. 1.1139 + * @param {string} component a date-time component. 1.1140 + * @param {string} value the value to be tested. 1.1141 + * @return {boolean} true if the test succeeds. 1.1142 + * @exception if the test fails. 1.1143 + */ 1.1144 + 1.1145 +function testValidDateTimeComponentValue(component, value) { 1.1146 + if (getDateTimeComponentValues(component).indexOf(value) === -1) { 1.1147 + $ERROR("Invalid value " + value + " for date-time component " + component + "."); 1.1148 + } 1.1149 + return true; 1.1150 +} 1.1151 + 1.1152 + 1.1153 +/** 1.1154 + * Verifies that the actual array matches the expected one in length, elements, 1.1155 + * and element order. 1.1156 + * @param {Array} expected the expected array. 1.1157 + * @param {Array} actual the actual array. 1.1158 + * @return {boolean} true if the test succeeds. 1.1159 + * @exception if the test fails. 1.1160 + */ 1.1161 +function testArraysAreSame(expected, actual) { 1.1162 + for (i = 0; i < Math.max(actual.length, expected.length); i++) { 1.1163 + if (actual[i] !== expected[i]) { 1.1164 + $ERROR("Result array element at index " + i + " should be \"" + 1.1165 + expected[i] + "\" but is \"" + actual[i] + "\"."); 1.1166 + } 1.1167 + } 1.1168 + return true; 1.1169 +} 1.1170 +