| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 the V8 project authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 // ECMAScript 402 API implementation. | |
| 6 | |
| 7 /** | |
| 8 * Intl object is a single object that has some named properties, | |
| 9 * all of which are constructors. | |
| 10 */ | |
| 11 (function(global, utils) { | |
| 12 | |
| 13 "use strict"; | |
| 14 | |
| 15 %CheckIsBootstrapping(); | |
| 16 | |
| 17 // ------------------------------------------------------------------- | |
| 18 // Imports | |
| 19 | |
| 20 var ArrayIndexOf; | |
| 21 var ArrayJoin; | |
| 22 var ArrayPush; | |
| 23 var IsFinite; | |
| 24 var IsNaN; | |
| 25 var GlobalBoolean = global.Boolean; | |
| 26 var GlobalDate = global.Date; | |
| 27 var GlobalNumber = global.Number; | |
| 28 var GlobalRegExp = global.RegExp; | |
| 29 var GlobalString = global.String; | |
| 30 var MathFloor; | |
| 31 var ObjectDefineProperties = utils.ImportNow("ObjectDefineProperties"); | |
| 32 var ObjectDefineProperty = utils.ImportNow("ObjectDefineProperty"); | |
| 33 var RegExpTest; | |
| 34 var StringIndexOf; | |
| 35 var StringLastIndexOf; | |
| 36 var StringMatch; | |
| 37 var StringReplace; | |
| 38 var StringSplit; | |
| 39 var StringSubstr; | |
| 40 var StringSubstring; | |
| 41 | |
| 42 utils.Import(function(from) { | |
| 43 ArrayIndexOf = from.ArrayIndexOf; | |
| 44 ArrayJoin = from.ArrayJoin; | |
| 45 ArrayPush = from.ArrayPush; | |
| 46 IsFinite = from.IsFinite; | |
| 47 IsNaN = from.IsNaN; | |
| 48 MathFloor = from.MathFloor; | |
| 49 RegExpTest = from.RegExpTest; | |
| 50 StringIndexOf = from.StringIndexOf; | |
| 51 StringLastIndexOf = from.StringLastIndexOf; | |
| 52 StringMatch = from.StringMatch; | |
| 53 StringReplace = from.StringReplace; | |
| 54 StringSplit = from.StringSplit; | |
| 55 StringSubstr = from.StringSubstr; | |
| 56 StringSubstring = from.StringSubstring; | |
| 57 }); | |
| 58 | |
| 59 // ------------------------------------------------------------------- | |
| 60 | |
| 61 var Intl = {}; | |
| 62 | |
| 63 %AddNamedProperty(global, "Intl", Intl, DONT_ENUM); | |
| 64 | |
| 65 /** | |
| 66 * Caches available locales for each service. | |
| 67 */ | |
| 68 var AVAILABLE_LOCALES = { | |
| 69 'collator': UNDEFINED, | |
| 70 'numberformat': UNDEFINED, | |
| 71 'dateformat': UNDEFINED, | |
| 72 'breakiterator': UNDEFINED | |
| 73 }; | |
| 74 | |
| 75 /** | |
| 76 * Caches default ICU locale. | |
| 77 */ | |
| 78 var DEFAULT_ICU_LOCALE = UNDEFINED; | |
| 79 | |
| 80 /** | |
| 81 * Unicode extension regular expression. | |
| 82 */ | |
| 83 var UNICODE_EXTENSION_RE = UNDEFINED; | |
| 84 | |
| 85 function GetUnicodeExtensionRE() { | |
| 86 if (IS_UNDEFINED(UNDEFINED)) { | |
| 87 UNICODE_EXTENSION_RE = new GlobalRegExp('-u(-[a-z0-9]{2,8})+', 'g'); | |
| 88 } | |
| 89 return UNICODE_EXTENSION_RE; | |
| 90 } | |
| 91 | |
| 92 /** | |
| 93 * Matches any Unicode extension. | |
| 94 */ | |
| 95 var ANY_EXTENSION_RE = UNDEFINED; | |
| 96 | |
| 97 function GetAnyExtensionRE() { | |
| 98 if (IS_UNDEFINED(ANY_EXTENSION_RE)) { | |
| 99 ANY_EXTENSION_RE = new GlobalRegExp('-[a-z0-9]{1}-.*', 'g'); | |
| 100 } | |
| 101 return ANY_EXTENSION_RE; | |
| 102 } | |
| 103 | |
| 104 /** | |
| 105 * Replace quoted text (single quote, anything but the quote and quote again). | |
| 106 */ | |
| 107 var QUOTED_STRING_RE = UNDEFINED; | |
| 108 | |
| 109 function GetQuotedStringRE() { | |
| 110 if (IS_UNDEFINED(QUOTED_STRING_RE)) { | |
| 111 QUOTED_STRING_RE = new GlobalRegExp("'[^']+'", 'g'); | |
| 112 } | |
| 113 return QUOTED_STRING_RE; | |
| 114 } | |
| 115 | |
| 116 /** | |
| 117 * Matches valid service name. | |
| 118 */ | |
| 119 var SERVICE_RE = UNDEFINED; | |
| 120 | |
| 121 function GetServiceRE() { | |
| 122 if (IS_UNDEFINED(SERVICE_RE)) { | |
| 123 SERVICE_RE = | |
| 124 new GlobalRegExp('^(collator|numberformat|dateformat|breakiterator)$'); | |
| 125 } | |
| 126 return SERVICE_RE; | |
| 127 } | |
| 128 | |
| 129 /** | |
| 130 * Validates a language tag against bcp47 spec. | |
| 131 * Actual value is assigned on first run. | |
| 132 */ | |
| 133 var LANGUAGE_TAG_RE = UNDEFINED; | |
| 134 | |
| 135 function GetLanguageTagRE() { | |
| 136 if (IS_UNDEFINED(LANGUAGE_TAG_RE)) { | |
| 137 BuildLanguageTagREs(); | |
| 138 } | |
| 139 return LANGUAGE_TAG_RE; | |
| 140 } | |
| 141 | |
| 142 /** | |
| 143 * Helps find duplicate variants in the language tag. | |
| 144 */ | |
| 145 var LANGUAGE_VARIANT_RE = UNDEFINED; | |
| 146 | |
| 147 function GetLanguageVariantRE() { | |
| 148 if (IS_UNDEFINED(LANGUAGE_VARIANT_RE)) { | |
| 149 BuildLanguageTagREs(); | |
| 150 } | |
| 151 return LANGUAGE_VARIANT_RE; | |
| 152 } | |
| 153 | |
| 154 /** | |
| 155 * Helps find duplicate singletons in the language tag. | |
| 156 */ | |
| 157 var LANGUAGE_SINGLETON_RE = UNDEFINED; | |
| 158 | |
| 159 function GetLanguageSingletonRE() { | |
| 160 if (IS_UNDEFINED(LANGUAGE_SINGLETON_RE)) { | |
| 161 BuildLanguageTagREs(); | |
| 162 } | |
| 163 return LANGUAGE_SINGLETON_RE; | |
| 164 } | |
| 165 | |
| 166 /** | |
| 167 * Matches valid IANA time zone names. | |
| 168 */ | |
| 169 var TIMEZONE_NAME_CHECK_RE = UNDEFINED; | |
| 170 | |
| 171 function GetTimezoneNameCheckRE() { | |
| 172 if (IS_UNDEFINED(TIMEZONE_NAME_CHECK_RE)) { | |
| 173 TIMEZONE_NAME_CHECK_RE = | |
| 174 new GlobalRegExp('^([A-Za-z]+)/([A-Za-z]+)(?:_([A-Za-z]+))*$'); | |
| 175 } | |
| 176 return TIMEZONE_NAME_CHECK_RE; | |
| 177 } | |
| 178 | |
| 179 /** | |
| 180 * Adds bound method to the prototype of the given object. | |
| 181 */ | |
| 182 function addBoundMethod(obj, methodName, implementation, length) { | |
| 183 %CheckIsBootstrapping(); | |
| 184 function getter() { | |
| 185 if (!%IsInitializedIntlObject(this)) { | |
| 186 throw MakeTypeError(kMethodCalledOnWrongObject, methodName); | |
| 187 } | |
| 188 var internalName = '__bound' + methodName + '__'; | |
| 189 if (IS_UNDEFINED(this[internalName])) { | |
| 190 var that = this; | |
| 191 var boundMethod; | |
| 192 if (IS_UNDEFINED(length) || length === 2) { | |
| 193 boundMethod = function(x, y) { | |
| 194 if (%_IsConstructCall()) { | |
| 195 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); | |
| 196 } | |
| 197 return implementation(that, x, y); | |
| 198 } | |
| 199 } else if (length === 1) { | |
| 200 boundMethod = function(x) { | |
| 201 if (%_IsConstructCall()) { | |
| 202 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); | |
| 203 } | |
| 204 return implementation(that, x); | |
| 205 } | |
| 206 } else { | |
| 207 boundMethod = function() { | |
| 208 if (%_IsConstructCall()) { | |
| 209 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); | |
| 210 } | |
| 211 // DateTimeFormat.format needs to be 0 arg method, but can stil | |
| 212 // receive optional dateValue param. If one was provided, pass it | |
| 213 // along. | |
| 214 if (%_ArgumentsLength() > 0) { | |
| 215 return implementation(that, %_Arguments(0)); | |
| 216 } else { | |
| 217 return implementation(that); | |
| 218 } | |
| 219 } | |
| 220 } | |
| 221 %FunctionSetName(boundMethod, internalName); | |
| 222 %FunctionRemovePrototype(boundMethod); | |
| 223 %SetNativeFlag(boundMethod); | |
| 224 this[internalName] = boundMethod; | |
| 225 } | |
| 226 return this[internalName]; | |
| 227 } | |
| 228 | |
| 229 %FunctionSetName(getter, methodName); | |
| 230 %FunctionRemovePrototype(getter); | |
| 231 %SetNativeFlag(getter); | |
| 232 | |
| 233 ObjectDefineProperty(obj.prototype, methodName, { | |
| 234 get: getter, | |
| 235 enumerable: false, | |
| 236 configurable: true | |
| 237 }); | |
| 238 } | |
| 239 | |
| 240 | |
| 241 /** | |
| 242 * Returns an intersection of locales and service supported locales. | |
| 243 * Parameter locales is treated as a priority list. | |
| 244 */ | |
| 245 function supportedLocalesOf(service, locales, options) { | |
| 246 if (IS_NULL(%_CallFunction(service, GetServiceRE(), StringMatch))) { | |
| 247 throw MakeError(kWrongServiceType, service); | |
| 248 } | |
| 249 | |
| 250 // Provide defaults if matcher was not specified. | |
| 251 if (IS_UNDEFINED(options)) { | |
| 252 options = {}; | |
| 253 } else { | |
| 254 options = TO_OBJECT(options); | |
| 255 } | |
| 256 | |
| 257 var matcher = options.localeMatcher; | |
| 258 if (!IS_UNDEFINED(matcher)) { | |
| 259 matcher = GlobalString(matcher); | |
| 260 if (matcher !== 'lookup' && matcher !== 'best fit') { | |
| 261 throw MakeRangeError(kLocaleMatcher, matcher); | |
| 262 } | |
| 263 } else { | |
| 264 matcher = 'best fit'; | |
| 265 } | |
| 266 | |
| 267 var requestedLocales = initializeLocaleList(locales); | |
| 268 | |
| 269 // Cache these, they don't ever change per service. | |
| 270 if (IS_UNDEFINED(AVAILABLE_LOCALES[service])) { | |
| 271 AVAILABLE_LOCALES[service] = getAvailableLocalesOf(service); | |
| 272 } | |
| 273 | |
| 274 // Use either best fit or lookup algorithm to match locales. | |
| 275 if (matcher === 'best fit') { | |
| 276 return initializeLocaleList(bestFitSupportedLocalesOf( | |
| 277 requestedLocales, AVAILABLE_LOCALES[service])); | |
| 278 } | |
| 279 | |
| 280 return initializeLocaleList(lookupSupportedLocalesOf( | |
| 281 requestedLocales, AVAILABLE_LOCALES[service])); | |
| 282 } | |
| 283 | |
| 284 | |
| 285 /** | |
| 286 * Returns the subset of the provided BCP 47 language priority list for which | |
| 287 * this service has a matching locale when using the BCP 47 Lookup algorithm. | |
| 288 * Locales appear in the same order in the returned list as in the input list. | |
| 289 */ | |
| 290 function lookupSupportedLocalesOf(requestedLocales, availableLocales) { | |
| 291 var matchedLocales = []; | |
| 292 for (var i = 0; i < requestedLocales.length; ++i) { | |
| 293 // Remove -u- extension. | |
| 294 var locale = %_CallFunction(requestedLocales[i], GetUnicodeExtensionRE(), | |
| 295 '', StringReplace); | |
| 296 do { | |
| 297 if (!IS_UNDEFINED(availableLocales[locale])) { | |
| 298 // Push requested locale not the resolved one. | |
| 299 %_CallFunction(matchedLocales, requestedLocales[i], ArrayPush); | |
| 300 break; | |
| 301 } | |
| 302 // Truncate locale if possible, if not break. | |
| 303 var pos = %_CallFunction(locale, '-', StringLastIndexOf); | |
| 304 if (pos === -1) { | |
| 305 break; | |
| 306 } | |
| 307 locale = %_CallFunction(locale, 0, pos, StringSubstring); | |
| 308 } while (true); | |
| 309 } | |
| 310 | |
| 311 return matchedLocales; | |
| 312 } | |
| 313 | |
| 314 | |
| 315 /** | |
| 316 * Returns the subset of the provided BCP 47 language priority list for which | |
| 317 * this service has a matching locale when using the implementation | |
| 318 * dependent algorithm. | |
| 319 * Locales appear in the same order in the returned list as in the input list. | |
| 320 */ | |
| 321 function bestFitSupportedLocalesOf(requestedLocales, availableLocales) { | |
| 322 // TODO(cira): implement better best fit algorithm. | |
| 323 return lookupSupportedLocalesOf(requestedLocales, availableLocales); | |
| 324 } | |
| 325 | |
| 326 | |
| 327 /** | |
| 328 * Returns a getOption function that extracts property value for given | |
| 329 * options object. If property is missing it returns defaultValue. If value | |
| 330 * is out of range for that property it throws RangeError. | |
| 331 */ | |
| 332 function getGetOption(options, caller) { | |
| 333 if (IS_UNDEFINED(options)) throw MakeError(kDefaultOptionsMissing, caller); | |
| 334 | |
| 335 var getOption = function getOption(property, type, values, defaultValue) { | |
| 336 if (!IS_UNDEFINED(options[property])) { | |
| 337 var value = options[property]; | |
| 338 switch (type) { | |
| 339 case 'boolean': | |
| 340 value = GlobalBoolean(value); | |
| 341 break; | |
| 342 case 'string': | |
| 343 value = GlobalString(value); | |
| 344 break; | |
| 345 case 'number': | |
| 346 value = GlobalNumber(value); | |
| 347 break; | |
| 348 default: | |
| 349 throw MakeError(kWrongValueType); | |
| 350 } | |
| 351 | |
| 352 if (!IS_UNDEFINED(values) && | |
| 353 %_CallFunction(values, value, ArrayIndexOf) === -1) { | |
| 354 throw MakeRangeError(kValueOutOfRange, value, caller, property); | |
| 355 } | |
| 356 | |
| 357 return value; | |
| 358 } | |
| 359 | |
| 360 return defaultValue; | |
| 361 } | |
| 362 | |
| 363 return getOption; | |
| 364 } | |
| 365 | |
| 366 | |
| 367 /** | |
| 368 * Compares a BCP 47 language priority list requestedLocales against the locales | |
| 369 * in availableLocales and determines the best available language to meet the | |
| 370 * request. Two algorithms are available to match the locales: the Lookup | |
| 371 * algorithm described in RFC 4647 section 3.4, and an implementation dependent | |
| 372 * best-fit algorithm. Independent of the locale matching algorithm, options | |
| 373 * specified through Unicode locale extension sequences are negotiated | |
| 374 * separately, taking the caller's relevant extension keys and locale data as | |
| 375 * well as client-provided options into consideration. Returns an object with | |
| 376 * a locale property whose value is the language tag of the selected locale, | |
| 377 * and properties for each key in relevantExtensionKeys providing the selected | |
| 378 * value for that key. | |
| 379 */ | |
| 380 function resolveLocale(service, requestedLocales, options) { | |
| 381 requestedLocales = initializeLocaleList(requestedLocales); | |
| 382 | |
| 383 var getOption = getGetOption(options, service); | |
| 384 var matcher = getOption('localeMatcher', 'string', | |
| 385 ['lookup', 'best fit'], 'best fit'); | |
| 386 var resolved; | |
| 387 if (matcher === 'lookup') { | |
| 388 resolved = lookupMatcher(service, requestedLocales); | |
| 389 } else { | |
| 390 resolved = bestFitMatcher(service, requestedLocales); | |
| 391 } | |
| 392 | |
| 393 return resolved; | |
| 394 } | |
| 395 | |
| 396 | |
| 397 /** | |
| 398 * Returns best matched supported locale and extension info using basic | |
| 399 * lookup algorithm. | |
| 400 */ | |
| 401 function lookupMatcher(service, requestedLocales) { | |
| 402 if (IS_NULL(%_CallFunction(service, GetServiceRE(), StringMatch))) { | |
| 403 throw MakeError(kWrongServiceType, service); | |
| 404 } | |
| 405 | |
| 406 // Cache these, they don't ever change per service. | |
| 407 if (IS_UNDEFINED(AVAILABLE_LOCALES[service])) { | |
| 408 AVAILABLE_LOCALES[service] = getAvailableLocalesOf(service); | |
| 409 } | |
| 410 | |
| 411 for (var i = 0; i < requestedLocales.length; ++i) { | |
| 412 // Remove all extensions. | |
| 413 var locale = %_CallFunction(requestedLocales[i], GetAnyExtensionRE(), '', | |
| 414 StringReplace); | |
| 415 do { | |
| 416 if (!IS_UNDEFINED(AVAILABLE_LOCALES[service][locale])) { | |
| 417 // Return the resolved locale and extension. | |
| 418 var extensionMatch = | |
| 419 %_CallFunction(requestedLocales[i], GetUnicodeExtensionRE(), | |
| 420 StringMatch); | |
| 421 var extension = IS_NULL(extensionMatch) ? '' : extensionMatch[0]; | |
| 422 return {'locale': locale, 'extension': extension, 'position': i}; | |
| 423 } | |
| 424 // Truncate locale if possible. | |
| 425 var pos = %_CallFunction(locale, '-', StringLastIndexOf); | |
| 426 if (pos === -1) { | |
| 427 break; | |
| 428 } | |
| 429 locale = %_CallFunction(locale, 0, pos, StringSubstring); | |
| 430 } while (true); | |
| 431 } | |
| 432 | |
| 433 // Didn't find a match, return default. | |
| 434 if (IS_UNDEFINED(DEFAULT_ICU_LOCALE)) { | |
| 435 DEFAULT_ICU_LOCALE = %GetDefaultICULocale(); | |
| 436 } | |
| 437 | |
| 438 return {'locale': DEFAULT_ICU_LOCALE, 'extension': '', 'position': -1}; | |
| 439 } | |
| 440 | |
| 441 | |
| 442 /** | |
| 443 * Returns best matched supported locale and extension info using | |
| 444 * implementation dependend algorithm. | |
| 445 */ | |
| 446 function bestFitMatcher(service, requestedLocales) { | |
| 447 // TODO(cira): implement better best fit algorithm. | |
| 448 return lookupMatcher(service, requestedLocales); | |
| 449 } | |
| 450 | |
| 451 | |
| 452 /** | |
| 453 * Parses Unicode extension into key - value map. | |
| 454 * Returns empty object if the extension string is invalid. | |
| 455 * We are not concerned with the validity of the values at this point. | |
| 456 */ | |
| 457 function parseExtension(extension) { | |
| 458 var extensionSplit = %_CallFunction(extension, '-', StringSplit); | |
| 459 | |
| 460 // Assume ['', 'u', ...] input, but don't throw. | |
| 461 if (extensionSplit.length <= 2 || | |
| 462 (extensionSplit[0] !== '' && extensionSplit[1] !== 'u')) { | |
| 463 return {}; | |
| 464 } | |
| 465 | |
| 466 // Key is {2}alphanum, value is {3,8}alphanum. | |
| 467 // Some keys may not have explicit values (booleans). | |
| 468 var extensionMap = {}; | |
| 469 var previousKey = UNDEFINED; | |
| 470 for (var i = 2; i < extensionSplit.length; ++i) { | |
| 471 var length = extensionSplit[i].length; | |
| 472 var element = extensionSplit[i]; | |
| 473 if (length === 2) { | |
| 474 extensionMap[element] = UNDEFINED; | |
| 475 previousKey = element; | |
| 476 } else if (length >= 3 && length <=8 && !IS_UNDEFINED(previousKey)) { | |
| 477 extensionMap[previousKey] = element; | |
| 478 previousKey = UNDEFINED; | |
| 479 } else { | |
| 480 // There is a value that's too long, or that doesn't have a key. | |
| 481 return {}; | |
| 482 } | |
| 483 } | |
| 484 | |
| 485 return extensionMap; | |
| 486 } | |
| 487 | |
| 488 | |
| 489 /** | |
| 490 * Populates internalOptions object with boolean key-value pairs | |
| 491 * from extensionMap and options. | |
| 492 * Returns filtered extension (number and date format constructors use | |
| 493 * Unicode extensions for passing parameters to ICU). | |
| 494 * It's used for extension-option pairs only, e.g. kn-normalization, but not | |
| 495 * for 'sensitivity' since it doesn't have extension equivalent. | |
| 496 * Extensions like nu and ca don't have options equivalent, so we place | |
| 497 * undefined in the map.property to denote that. | |
| 498 */ | |
| 499 function setOptions(inOptions, extensionMap, keyValues, getOption, outOptions) { | |
| 500 var extension = ''; | |
| 501 | |
| 502 var updateExtension = function updateExtension(key, value) { | |
| 503 return '-' + key + '-' + GlobalString(value); | |
| 504 } | |
| 505 | |
| 506 var updateProperty = function updateProperty(property, type, value) { | |
| 507 if (type === 'boolean' && (typeof value === 'string')) { | |
| 508 value = (value === 'true') ? true : false; | |
| 509 } | |
| 510 | |
| 511 if (!IS_UNDEFINED(property)) { | |
| 512 defineWEProperty(outOptions, property, value); | |
| 513 } | |
| 514 } | |
| 515 | |
| 516 for (var key in keyValues) { | |
| 517 if (%HasOwnProperty(keyValues, key)) { | |
| 518 var value = UNDEFINED; | |
| 519 var map = keyValues[key]; | |
| 520 if (!IS_UNDEFINED(map.property)) { | |
| 521 // This may return true if user specifies numeric: 'false', since | |
| 522 // Boolean('nonempty') === true. | |
| 523 value = getOption(map.property, map.type, map.values); | |
| 524 } | |
| 525 if (!IS_UNDEFINED(value)) { | |
| 526 updateProperty(map.property, map.type, value); | |
| 527 extension += updateExtension(key, value); | |
| 528 continue; | |
| 529 } | |
| 530 // User options didn't have it, check Unicode extension. | |
| 531 // Here we want to convert strings 'true', 'false' into proper Boolean | |
| 532 // values (not a user error). | |
| 533 if (%HasOwnProperty(extensionMap, key)) { | |
| 534 value = extensionMap[key]; | |
| 535 if (!IS_UNDEFINED(value)) { | |
| 536 updateProperty(map.property, map.type, value); | |
| 537 extension += updateExtension(key, value); | |
| 538 } else if (map.type === 'boolean') { | |
| 539 // Boolean keys are allowed not to have values in Unicode extension. | |
| 540 // Those default to true. | |
| 541 updateProperty(map.property, map.type, true); | |
| 542 extension += updateExtension(key, true); | |
| 543 } | |
| 544 } | |
| 545 } | |
| 546 } | |
| 547 | |
| 548 return extension === ''? '' : '-u' + extension; | |
| 549 } | |
| 550 | |
| 551 | |
| 552 /** | |
| 553 * Converts all OwnProperties into | |
| 554 * configurable: false, writable: false, enumerable: true. | |
| 555 */ | |
| 556 function freezeArray(array) { | |
| 557 var l = array.length; | |
| 558 for (var i = 0; i < l; i++) { | |
| 559 if (i in array) { | |
| 560 ObjectDefineProperty(array, i, {value: array[i], | |
| 561 configurable: false, | |
| 562 writable: false, | |
| 563 enumerable: true}); | |
| 564 } | |
| 565 } | |
| 566 | |
| 567 ObjectDefineProperty(array, 'length', {value: l, writable: false}); | |
| 568 return array; | |
| 569 } | |
| 570 | |
| 571 | |
| 572 /** | |
| 573 * It's sometimes desireable to leave user requested locale instead of ICU | |
| 574 * supported one (zh-TW is equivalent to zh-Hant-TW, so we should keep shorter | |
| 575 * one, if that was what user requested). | |
| 576 * This function returns user specified tag if its maximized form matches ICU | |
| 577 * resolved locale. If not we return ICU result. | |
| 578 */ | |
| 579 function getOptimalLanguageTag(original, resolved) { | |
| 580 // Returns Array<Object>, where each object has maximized and base properties. | |
| 581 // Maximized: zh -> zh-Hans-CN | |
| 582 // Base: zh-CN-u-ca-gregory -> zh-CN | |
| 583 // Take care of grandfathered or simple cases. | |
| 584 if (original === resolved) { | |
| 585 return original; | |
| 586 } | |
| 587 | |
| 588 var locales = %GetLanguageTagVariants([original, resolved]); | |
| 589 if (locales[0].maximized !== locales[1].maximized) { | |
| 590 return resolved; | |
| 591 } | |
| 592 | |
| 593 // Preserve extensions of resolved locale, but swap base tags with original. | |
| 594 var resolvedBase = new GlobalRegExp('^' + locales[1].base); | |
| 595 return %_CallFunction(resolved, resolvedBase, locales[0].base, StringReplace); | |
| 596 } | |
| 597 | |
| 598 | |
| 599 /** | |
| 600 * Returns an Object that contains all of supported locales for a given | |
| 601 * service. | |
| 602 * In addition to the supported locales we add xx-ZZ locale for each xx-Yyyy-ZZ | |
| 603 * that is supported. This is required by the spec. | |
| 604 */ | |
| 605 function getAvailableLocalesOf(service) { | |
| 606 var available = %AvailableLocalesOf(service); | |
| 607 | |
| 608 for (var i in available) { | |
| 609 if (%HasOwnProperty(available, i)) { | |
| 610 var parts = %_CallFunction(i, /^([a-z]{2,3})-([A-Z][a-z]{3})-([A-Z]{2})$/, | |
| 611 StringMatch); | |
| 612 if (parts !== null) { | |
| 613 // Build xx-ZZ. We don't care about the actual value, | |
| 614 // as long it's not undefined. | |
| 615 available[parts[1] + '-' + parts[3]] = null; | |
| 616 } | |
| 617 } | |
| 618 } | |
| 619 | |
| 620 return available; | |
| 621 } | |
| 622 | |
| 623 | |
| 624 /** | |
| 625 * Defines a property and sets writable and enumerable to true. | |
| 626 * Configurable is false by default. | |
| 627 */ | |
| 628 function defineWEProperty(object, property, value) { | |
| 629 ObjectDefineProperty(object, property, | |
| 630 {value: value, writable: true, enumerable: true}); | |
| 631 } | |
| 632 | |
| 633 | |
| 634 /** | |
| 635 * Adds property to an object if the value is not undefined. | |
| 636 * Sets configurable descriptor to false. | |
| 637 */ | |
| 638 function addWEPropertyIfDefined(object, property, value) { | |
| 639 if (!IS_UNDEFINED(value)) { | |
| 640 defineWEProperty(object, property, value); | |
| 641 } | |
| 642 } | |
| 643 | |
| 644 | |
| 645 /** | |
| 646 * Defines a property and sets writable, enumerable and configurable to true. | |
| 647 */ | |
| 648 function defineWECProperty(object, property, value) { | |
| 649 ObjectDefineProperty(object, property, {value: value, | |
| 650 writable: true, | |
| 651 enumerable: true, | |
| 652 configurable: true}); | |
| 653 } | |
| 654 | |
| 655 | |
| 656 /** | |
| 657 * Adds property to an object if the value is not undefined. | |
| 658 * Sets all descriptors to true. | |
| 659 */ | |
| 660 function addWECPropertyIfDefined(object, property, value) { | |
| 661 if (!IS_UNDEFINED(value)) { | |
| 662 defineWECProperty(object, property, value); | |
| 663 } | |
| 664 } | |
| 665 | |
| 666 | |
| 667 /** | |
| 668 * Returns titlecased word, aMeRricA -> America. | |
| 669 */ | |
| 670 function toTitleCaseWord(word) { | |
| 671 return %StringToUpperCase(%_CallFunction(word, 0, 1, StringSubstr)) + | |
| 672 %StringToLowerCase(%_CallFunction(word, 1, StringSubstr)); | |
| 673 } | |
| 674 | |
| 675 /** | |
| 676 * Canonicalizes the language tag, or throws in case the tag is invalid. | |
| 677 */ | |
| 678 function canonicalizeLanguageTag(localeID) { | |
| 679 // null is typeof 'object' so we have to do extra check. | |
| 680 if (typeof localeID !== 'string' && typeof localeID !== 'object' || | |
| 681 IS_NULL(localeID)) { | |
| 682 throw MakeTypeError(kLanguageID); | |
| 683 } | |
| 684 | |
| 685 var localeString = GlobalString(localeID); | |
| 686 | |
| 687 if (isValidLanguageTag(localeString) === false) { | |
| 688 throw MakeRangeError(kInvalidLanguageTag, localeString); | |
| 689 } | |
| 690 | |
| 691 // This call will strip -kn but not -kn-true extensions. | |
| 692 // ICU bug filled - http://bugs.icu-project.org/trac/ticket/9265. | |
| 693 // TODO(cira): check if -u-kn-true-kc-true-kh-true still throws after | |
| 694 // upgrade to ICU 4.9. | |
| 695 var tag = %CanonicalizeLanguageTag(localeString); | |
| 696 if (tag === 'invalid-tag') { | |
| 697 throw MakeRangeError(kInvalidLanguageTag, localeString); | |
| 698 } | |
| 699 | |
| 700 return tag; | |
| 701 } | |
| 702 | |
| 703 | |
| 704 /** | |
| 705 * Returns an array where all locales are canonicalized and duplicates removed. | |
| 706 * Throws on locales that are not well formed BCP47 tags. | |
| 707 */ | |
| 708 function initializeLocaleList(locales) { | |
| 709 var seen = []; | |
| 710 if (IS_UNDEFINED(locales)) { | |
| 711 // Constructor is called without arguments. | |
| 712 seen = []; | |
| 713 } else { | |
| 714 // We allow single string localeID. | |
| 715 if (typeof locales === 'string') { | |
| 716 %_CallFunction(seen, canonicalizeLanguageTag(locales), ArrayPush); | |
| 717 return freezeArray(seen); | |
| 718 } | |
| 719 | |
| 720 var o = TO_OBJECT(locales); | |
| 721 var len = TO_UINT32(o.length); | |
| 722 | |
| 723 for (var k = 0; k < len; k++) { | |
| 724 if (k in o) { | |
| 725 var value = o[k]; | |
| 726 | |
| 727 var tag = canonicalizeLanguageTag(value); | |
| 728 | |
| 729 if (%_CallFunction(seen, tag, ArrayIndexOf) === -1) { | |
| 730 %_CallFunction(seen, tag, ArrayPush); | |
| 731 } | |
| 732 } | |
| 733 } | |
| 734 } | |
| 735 | |
| 736 return freezeArray(seen); | |
| 737 } | |
| 738 | |
| 739 | |
| 740 /** | |
| 741 * Validates the language tag. Section 2.2.9 of the bcp47 spec | |
| 742 * defines a valid tag. | |
| 743 * | |
| 744 * ICU is too permissible and lets invalid tags, like | |
| 745 * hant-cmn-cn, through. | |
| 746 * | |
| 747 * Returns false if the language tag is invalid. | |
| 748 */ | |
| 749 function isValidLanguageTag(locale) { | |
| 750 // Check if it's well-formed, including grandfadered tags. | |
| 751 if (!%_CallFunction(GetLanguageTagRE(), locale, RegExpTest)) { | |
| 752 return false; | |
| 753 } | |
| 754 | |
| 755 // Just return if it's a x- form. It's all private. | |
| 756 if (%_CallFunction(locale, 'x-', StringIndexOf) === 0) { | |
| 757 return true; | |
| 758 } | |
| 759 | |
| 760 // Check if there are any duplicate variants or singletons (extensions). | |
| 761 | |
| 762 // Remove private use section. | |
| 763 locale = %_CallFunction(locale, /-x-/, StringSplit)[0]; | |
| 764 | |
| 765 // Skip language since it can match variant regex, so we start from 1. | |
| 766 // We are matching i-klingon here, but that's ok, since i-klingon-klingon | |
| 767 // is not valid and would fail LANGUAGE_TAG_RE test. | |
| 768 var variants = []; | |
| 769 var extensions = []; | |
| 770 var parts = %_CallFunction(locale, /-/, StringSplit); | |
| 771 for (var i = 1; i < parts.length; i++) { | |
| 772 var value = parts[i]; | |
| 773 if (%_CallFunction(GetLanguageVariantRE(), value, RegExpTest) && | |
| 774 extensions.length === 0) { | |
| 775 if (%_CallFunction(variants, value, ArrayIndexOf) === -1) { | |
| 776 %_CallFunction(variants, value, ArrayPush); | |
| 777 } else { | |
| 778 return false; | |
| 779 } | |
| 780 } | |
| 781 | |
| 782 if (%_CallFunction(GetLanguageSingletonRE(), value, RegExpTest)) { | |
| 783 if (%_CallFunction(extensions, value, ArrayIndexOf) === -1) { | |
| 784 %_CallFunction(extensions, value, ArrayPush); | |
| 785 } else { | |
| 786 return false; | |
| 787 } | |
| 788 } | |
| 789 } | |
| 790 | |
| 791 return true; | |
| 792 } | |
| 793 | |
| 794 | |
| 795 /** | |
| 796 * Builds a regular expresion that validates the language tag | |
| 797 * against bcp47 spec. | |
| 798 * Uses http://tools.ietf.org/html/bcp47, section 2.1, ABNF. | |
| 799 * Runs on load and initializes the global REs. | |
| 800 */ | |
| 801 function BuildLanguageTagREs() { | |
| 802 var alpha = '[a-zA-Z]'; | |
| 803 var digit = '[0-9]'; | |
| 804 var alphanum = '(' + alpha + '|' + digit + ')'; | |
| 805 var regular = '(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|' + | |
| 806 'zh-min|zh-min-nan|zh-xiang)'; | |
| 807 var irregular = '(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|' + | |
| 808 'i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|' + | |
| 809 'i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)'; | |
| 810 var grandfathered = '(' + irregular + '|' + regular + ')'; | |
| 811 var privateUse = '(x(-' + alphanum + '{1,8})+)'; | |
| 812 | |
| 813 var singleton = '(' + digit + '|[A-WY-Za-wy-z])'; | |
| 814 LANGUAGE_SINGLETON_RE = new GlobalRegExp('^' + singleton + '$', 'i'); | |
| 815 | |
| 816 var extension = '(' + singleton + '(-' + alphanum + '{2,8})+)'; | |
| 817 | |
| 818 var variant = '(' + alphanum + '{5,8}|(' + digit + alphanum + '{3}))'; | |
| 819 LANGUAGE_VARIANT_RE = new GlobalRegExp('^' + variant + '$', 'i'); | |
| 820 | |
| 821 var region = '(' + alpha + '{2}|' + digit + '{3})'; | |
| 822 var script = '(' + alpha + '{4})'; | |
| 823 var extLang = '(' + alpha + '{3}(-' + alpha + '{3}){0,2})'; | |
| 824 var language = '(' + alpha + '{2,3}(-' + extLang + ')?|' + alpha + '{4}|' + | |
| 825 alpha + '{5,8})'; | |
| 826 var langTag = language + '(-' + script + ')?(-' + region + ')?(-' + | |
| 827 variant + ')*(-' + extension + ')*(-' + privateUse + ')?'; | |
| 828 | |
| 829 var languageTag = | |
| 830 '^(' + langTag + '|' + privateUse + '|' + grandfathered + ')$'; | |
| 831 LANGUAGE_TAG_RE = new GlobalRegExp(languageTag, 'i'); | |
| 832 } | |
| 833 | |
| 834 /** | |
| 835 * Initializes the given object so it's a valid Collator instance. | |
| 836 * Useful for subclassing. | |
| 837 */ | |
| 838 function initializeCollator(collator, locales, options) { | |
| 839 if (%IsInitializedIntlObject(collator)) { | |
| 840 throw MakeTypeError(kReinitializeIntl, "Collator"); | |
| 841 } | |
| 842 | |
| 843 if (IS_UNDEFINED(options)) { | |
| 844 options = {}; | |
| 845 } | |
| 846 | |
| 847 var getOption = getGetOption(options, 'collator'); | |
| 848 | |
| 849 var internalOptions = {}; | |
| 850 | |
| 851 defineWEProperty(internalOptions, 'usage', getOption( | |
| 852 'usage', 'string', ['sort', 'search'], 'sort')); | |
| 853 | |
| 854 var sensitivity = getOption('sensitivity', 'string', | |
| 855 ['base', 'accent', 'case', 'variant']); | |
| 856 if (IS_UNDEFINED(sensitivity) && internalOptions.usage === 'sort') { | |
| 857 sensitivity = 'variant'; | |
| 858 } | |
| 859 defineWEProperty(internalOptions, 'sensitivity', sensitivity); | |
| 860 | |
| 861 defineWEProperty(internalOptions, 'ignorePunctuation', getOption( | |
| 862 'ignorePunctuation', 'boolean', UNDEFINED, false)); | |
| 863 | |
| 864 var locale = resolveLocale('collator', locales, options); | |
| 865 | |
| 866 // ICU can't take kb, kc... parameters through localeID, so we need to pass | |
| 867 // them as options. | |
| 868 // One exception is -co- which has to be part of the extension, but only for | |
| 869 // usage: sort, and its value can't be 'standard' or 'search'. | |
| 870 var extensionMap = parseExtension(locale.extension); | |
| 871 | |
| 872 /** | |
| 873 * Map of Unicode extensions to option properties, and their values and types, | |
| 874 * for a collator. | |
| 875 */ | |
| 876 var COLLATOR_KEY_MAP = { | |
| 877 'kn': {'property': 'numeric', 'type': 'boolean'}, | |
| 878 'kf': {'property': 'caseFirst', 'type': 'string', | |
| 879 'values': ['false', 'lower', 'upper']} | |
| 880 }; | |
| 881 | |
| 882 setOptions( | |
| 883 options, extensionMap, COLLATOR_KEY_MAP, getOption, internalOptions); | |
| 884 | |
| 885 var collation = 'default'; | |
| 886 var extension = ''; | |
| 887 if (%HasOwnProperty(extensionMap, 'co') && internalOptions.usage === 'sort') { | |
| 888 | |
| 889 /** | |
| 890 * Allowed -u-co- values. List taken from: | |
| 891 * http://unicode.org/repos/cldr/trunk/common/bcp47/collation.xml | |
| 892 */ | |
| 893 var ALLOWED_CO_VALUES = [ | |
| 894 'big5han', 'dict', 'direct', 'ducet', 'gb2312', 'phonebk', 'phonetic', | |
| 895 'pinyin', 'reformed', 'searchjl', 'stroke', 'trad', 'unihan', 'zhuyin' | |
| 896 ]; | |
| 897 | |
| 898 if (%_CallFunction(ALLOWED_CO_VALUES, extensionMap.co, ArrayIndexOf) !== | |
| 899 -1) { | |
| 900 extension = '-u-co-' + extensionMap.co; | |
| 901 // ICU can't tell us what the collation is, so save user's input. | |
| 902 collation = extensionMap.co; | |
| 903 } | |
| 904 } else if (internalOptions.usage === 'search') { | |
| 905 extension = '-u-co-search'; | |
| 906 } | |
| 907 defineWEProperty(internalOptions, 'collation', collation); | |
| 908 | |
| 909 var requestedLocale = locale.locale + extension; | |
| 910 | |
| 911 // We define all properties C++ code may produce, to prevent security | |
| 912 // problems. If malicious user decides to redefine Object.prototype.locale | |
| 913 // we can't just use plain x.locale = 'us' or in C++ Set("locale", "us"). | |
| 914 // ObjectDefineProperties will either succeed defining or throw an error. | |
| 915 var resolved = ObjectDefineProperties({}, { | |
| 916 caseFirst: {writable: true}, | |
| 917 collation: {value: internalOptions.collation, writable: true}, | |
| 918 ignorePunctuation: {writable: true}, | |
| 919 locale: {writable: true}, | |
| 920 numeric: {writable: true}, | |
| 921 requestedLocale: {value: requestedLocale, writable: true}, | |
| 922 sensitivity: {writable: true}, | |
| 923 strength: {writable: true}, | |
| 924 usage: {value: internalOptions.usage, writable: true} | |
| 925 }); | |
| 926 | |
| 927 var internalCollator = %CreateCollator(requestedLocale, | |
| 928 internalOptions, | |
| 929 resolved); | |
| 930 | |
| 931 // Writable, configurable and enumerable are set to false by default. | |
| 932 %MarkAsInitializedIntlObjectOfType(collator, 'collator', internalCollator); | |
| 933 ObjectDefineProperty(collator, 'resolved', {value: resolved}); | |
| 934 | |
| 935 return collator; | |
| 936 } | |
| 937 | |
| 938 | |
| 939 /** | |
| 940 * Constructs Intl.Collator object given optional locales and options | |
| 941 * parameters. | |
| 942 * | |
| 943 * @constructor | |
| 944 */ | |
| 945 %AddNamedProperty(Intl, 'Collator', function() { | |
| 946 var locales = %_Arguments(0); | |
| 947 var options = %_Arguments(1); | |
| 948 | |
| 949 if (!this || this === Intl) { | |
| 950 // Constructor is called as a function. | |
| 951 return new Intl.Collator(locales, options); | |
| 952 } | |
| 953 | |
| 954 return initializeCollator(TO_OBJECT(this), locales, options); | |
| 955 }, | |
| 956 DONT_ENUM | |
| 957 ); | |
| 958 | |
| 959 | |
| 960 /** | |
| 961 * Collator resolvedOptions method. | |
| 962 */ | |
| 963 %AddNamedProperty(Intl.Collator.prototype, 'resolvedOptions', function() { | |
| 964 if (%_IsConstructCall()) { | |
| 965 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); | |
| 966 } | |
| 967 | |
| 968 if (!%IsInitializedIntlObjectOfType(this, 'collator')) { | |
| 969 throw MakeTypeError(kResolvedOptionsCalledOnNonObject, "Collator"); | |
| 970 } | |
| 971 | |
| 972 var coll = this; | |
| 973 var locale = getOptimalLanguageTag(coll.resolved.requestedLocale, | |
| 974 coll.resolved.locale); | |
| 975 | |
| 976 return { | |
| 977 locale: locale, | |
| 978 usage: coll.resolved.usage, | |
| 979 sensitivity: coll.resolved.sensitivity, | |
| 980 ignorePunctuation: coll.resolved.ignorePunctuation, | |
| 981 numeric: coll.resolved.numeric, | |
| 982 caseFirst: coll.resolved.caseFirst, | |
| 983 collation: coll.resolved.collation | |
| 984 }; | |
| 985 }, | |
| 986 DONT_ENUM | |
| 987 ); | |
| 988 %FunctionSetName(Intl.Collator.prototype.resolvedOptions, 'resolvedOptions'); | |
| 989 %FunctionRemovePrototype(Intl.Collator.prototype.resolvedOptions); | |
| 990 %SetNativeFlag(Intl.Collator.prototype.resolvedOptions); | |
| 991 | |
| 992 | |
| 993 /** | |
| 994 * Returns the subset of the given locale list for which this locale list | |
| 995 * has a matching (possibly fallback) locale. Locales appear in the same | |
| 996 * order in the returned list as in the input list. | |
| 997 * Options are optional parameter. | |
| 998 */ | |
| 999 %AddNamedProperty(Intl.Collator, 'supportedLocalesOf', function(locales) { | |
| 1000 if (%_IsConstructCall()) { | |
| 1001 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); | |
| 1002 } | |
| 1003 | |
| 1004 return supportedLocalesOf('collator', locales, %_Arguments(1)); | |
| 1005 }, | |
| 1006 DONT_ENUM | |
| 1007 ); | |
| 1008 %FunctionSetName(Intl.Collator.supportedLocalesOf, 'supportedLocalesOf'); | |
| 1009 %FunctionRemovePrototype(Intl.Collator.supportedLocalesOf); | |
| 1010 %SetNativeFlag(Intl.Collator.supportedLocalesOf); | |
| 1011 | |
| 1012 | |
| 1013 /** | |
| 1014 * When the compare method is called with two arguments x and y, it returns a | |
| 1015 * Number other than NaN that represents the result of a locale-sensitive | |
| 1016 * String comparison of x with y. | |
| 1017 * The result is intended to order String values in the sort order specified | |
| 1018 * by the effective locale and collation options computed during construction | |
| 1019 * of this Collator object, and will be negative, zero, or positive, depending | |
| 1020 * on whether x comes before y in the sort order, the Strings are equal under | |
| 1021 * the sort order, or x comes after y in the sort order, respectively. | |
| 1022 */ | |
| 1023 function compare(collator, x, y) { | |
| 1024 return %InternalCompare(%GetImplFromInitializedIntlObject(collator), | |
| 1025 GlobalString(x), GlobalString(y)); | |
| 1026 }; | |
| 1027 | |
| 1028 | |
| 1029 addBoundMethod(Intl.Collator, 'compare', compare, 2); | |
| 1030 | |
| 1031 /** | |
| 1032 * Verifies that the input is a well-formed ISO 4217 currency code. | |
| 1033 * Don't uppercase to test. It could convert invalid code into a valid one. | |
| 1034 * For example \u00DFP (Eszett+P) becomes SSP. | |
| 1035 */ | |
| 1036 function isWellFormedCurrencyCode(currency) { | |
| 1037 return typeof currency == "string" && | |
| 1038 currency.length == 3 && | |
| 1039 %_CallFunction(currency, /[^A-Za-z]/, StringMatch) == null; | |
| 1040 } | |
| 1041 | |
| 1042 | |
| 1043 /** | |
| 1044 * Returns the valid digit count for a property, or throws RangeError on | |
| 1045 * a value out of the range. | |
| 1046 */ | |
| 1047 function getNumberOption(options, property, min, max, fallback) { | |
| 1048 var value = options[property]; | |
| 1049 if (!IS_UNDEFINED(value)) { | |
| 1050 value = GlobalNumber(value); | |
| 1051 if (IsNaN(value) || value < min || value > max) { | |
| 1052 throw MakeRangeError(kPropertyValueOutOfRange, property); | |
| 1053 } | |
| 1054 return MathFloor(value); | |
| 1055 } | |
| 1056 | |
| 1057 return fallback; | |
| 1058 } | |
| 1059 | |
| 1060 | |
| 1061 /** | |
| 1062 * Initializes the given object so it's a valid NumberFormat instance. | |
| 1063 * Useful for subclassing. | |
| 1064 */ | |
| 1065 function initializeNumberFormat(numberFormat, locales, options) { | |
| 1066 if (%IsInitializedIntlObject(numberFormat)) { | |
| 1067 throw MakeTypeError(kReinitializeIntl, "NumberFormat"); | |
| 1068 } | |
| 1069 | |
| 1070 if (IS_UNDEFINED(options)) { | |
| 1071 options = {}; | |
| 1072 } | |
| 1073 | |
| 1074 var getOption = getGetOption(options, 'numberformat'); | |
| 1075 | |
| 1076 var locale = resolveLocale('numberformat', locales, options); | |
| 1077 | |
| 1078 var internalOptions = {}; | |
| 1079 defineWEProperty(internalOptions, 'style', getOption( | |
| 1080 'style', 'string', ['decimal', 'percent', 'currency'], 'decimal')); | |
| 1081 | |
| 1082 var currency = getOption('currency', 'string'); | |
| 1083 if (!IS_UNDEFINED(currency) && !isWellFormedCurrencyCode(currency)) { | |
| 1084 throw MakeRangeError(kInvalidCurrencyCode, currency); | |
| 1085 } | |
| 1086 | |
| 1087 if (internalOptions.style === 'currency' && IS_UNDEFINED(currency)) { | |
| 1088 throw MakeTypeError(kCurrencyCode); | |
| 1089 } | |
| 1090 | |
| 1091 var currencyDisplay = getOption( | |
| 1092 'currencyDisplay', 'string', ['code', 'symbol', 'name'], 'symbol'); | |
| 1093 if (internalOptions.style === 'currency') { | |
| 1094 defineWEProperty(internalOptions, 'currency', %StringToUpperCase(currency)); | |
| 1095 defineWEProperty(internalOptions, 'currencyDisplay', currencyDisplay); | |
| 1096 } | |
| 1097 | |
| 1098 // Digit ranges. | |
| 1099 var mnid = getNumberOption(options, 'minimumIntegerDigits', 1, 21, 1); | |
| 1100 defineWEProperty(internalOptions, 'minimumIntegerDigits', mnid); | |
| 1101 | |
| 1102 var mnfd = options['minimumFractionDigits']; | |
| 1103 var mxfd = options['maximumFractionDigits']; | |
| 1104 if (!IS_UNDEFINED(mnfd) || !internalOptions.style === 'currency') { | |
| 1105 mnfd = getNumberOption(options, 'minimumFractionDigits', 0, 20, 0); | |
| 1106 defineWEProperty(internalOptions, 'minimumFractionDigits', mnfd); | |
| 1107 } | |
| 1108 | |
| 1109 if (!IS_UNDEFINED(mxfd) || !internalOptions.style === 'currency') { | |
| 1110 mnfd = IS_UNDEFINED(mnfd) ? 0 : mnfd; | |
| 1111 fallback_limit = (mnfd > 3) ? mnfd : 3; | |
| 1112 mxfd = getNumberOption(options, 'maximumFractionDigits', mnfd, 20, fallback_
limit); | |
| 1113 defineWEProperty(internalOptions, 'maximumFractionDigits', mxfd); | |
| 1114 } | |
| 1115 | |
| 1116 var mnsd = options['minimumSignificantDigits']; | |
| 1117 var mxsd = options['maximumSignificantDigits']; | |
| 1118 if (!IS_UNDEFINED(mnsd) || !IS_UNDEFINED(mxsd)) { | |
| 1119 mnsd = getNumberOption(options, 'minimumSignificantDigits', 1, 21, 0); | |
| 1120 defineWEProperty(internalOptions, 'minimumSignificantDigits', mnsd); | |
| 1121 | |
| 1122 mxsd = getNumberOption(options, 'maximumSignificantDigits', mnsd, 21, 21); | |
| 1123 defineWEProperty(internalOptions, 'maximumSignificantDigits', mxsd); | |
| 1124 } | |
| 1125 | |
| 1126 // Grouping. | |
| 1127 defineWEProperty(internalOptions, 'useGrouping', getOption( | |
| 1128 'useGrouping', 'boolean', UNDEFINED, true)); | |
| 1129 | |
| 1130 // ICU prefers options to be passed using -u- extension key/values for | |
| 1131 // number format, so we need to build that. | |
| 1132 var extensionMap = parseExtension(locale.extension); | |
| 1133 | |
| 1134 /** | |
| 1135 * Map of Unicode extensions to option properties, and their values and types, | |
| 1136 * for a number format. | |
| 1137 */ | |
| 1138 var NUMBER_FORMAT_KEY_MAP = { | |
| 1139 'nu': {'property': UNDEFINED, 'type': 'string'} | |
| 1140 }; | |
| 1141 | |
| 1142 var extension = setOptions(options, extensionMap, NUMBER_FORMAT_KEY_MAP, | |
| 1143 getOption, internalOptions); | |
| 1144 | |
| 1145 var requestedLocale = locale.locale + extension; | |
| 1146 var resolved = ObjectDefineProperties({}, { | |
| 1147 currency: {writable: true}, | |
| 1148 currencyDisplay: {writable: true}, | |
| 1149 locale: {writable: true}, | |
| 1150 maximumFractionDigits: {writable: true}, | |
| 1151 minimumFractionDigits: {writable: true}, | |
| 1152 minimumIntegerDigits: {writable: true}, | |
| 1153 numberingSystem: {writable: true}, | |
| 1154 requestedLocale: {value: requestedLocale, writable: true}, | |
| 1155 style: {value: internalOptions.style, writable: true}, | |
| 1156 useGrouping: {writable: true} | |
| 1157 }); | |
| 1158 if (%HasOwnProperty(internalOptions, 'minimumSignificantDigits')) { | |
| 1159 defineWEProperty(resolved, 'minimumSignificantDigits', UNDEFINED); | |
| 1160 } | |
| 1161 if (%HasOwnProperty(internalOptions, 'maximumSignificantDigits')) { | |
| 1162 defineWEProperty(resolved, 'maximumSignificantDigits', UNDEFINED); | |
| 1163 } | |
| 1164 var formatter = %CreateNumberFormat(requestedLocale, | |
| 1165 internalOptions, | |
| 1166 resolved); | |
| 1167 | |
| 1168 if (internalOptions.style === 'currency') { | |
| 1169 ObjectDefineProperty(resolved, 'currencyDisplay', {value: currencyDisplay, | |
| 1170 writable: true}); | |
| 1171 } | |
| 1172 | |
| 1173 %MarkAsInitializedIntlObjectOfType(numberFormat, 'numberformat', formatter); | |
| 1174 ObjectDefineProperty(numberFormat, 'resolved', {value: resolved}); | |
| 1175 | |
| 1176 return numberFormat; | |
| 1177 } | |
| 1178 | |
| 1179 | |
| 1180 /** | |
| 1181 * Constructs Intl.NumberFormat object given optional locales and options | |
| 1182 * parameters. | |
| 1183 * | |
| 1184 * @constructor | |
| 1185 */ | |
| 1186 %AddNamedProperty(Intl, 'NumberFormat', function() { | |
| 1187 var locales = %_Arguments(0); | |
| 1188 var options = %_Arguments(1); | |
| 1189 | |
| 1190 if (!this || this === Intl) { | |
| 1191 // Constructor is called as a function. | |
| 1192 return new Intl.NumberFormat(locales, options); | |
| 1193 } | |
| 1194 | |
| 1195 return initializeNumberFormat(TO_OBJECT(this), locales, options); | |
| 1196 }, | |
| 1197 DONT_ENUM | |
| 1198 ); | |
| 1199 | |
| 1200 | |
| 1201 /** | |
| 1202 * NumberFormat resolvedOptions method. | |
| 1203 */ | |
| 1204 %AddNamedProperty(Intl.NumberFormat.prototype, 'resolvedOptions', function() { | |
| 1205 if (%_IsConstructCall()) { | |
| 1206 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); | |
| 1207 } | |
| 1208 | |
| 1209 if (!%IsInitializedIntlObjectOfType(this, 'numberformat')) { | |
| 1210 throw MakeTypeError(kResolvedOptionsCalledOnNonObject, "NumberFormat"); | |
| 1211 } | |
| 1212 | |
| 1213 var format = this; | |
| 1214 var locale = getOptimalLanguageTag(format.resolved.requestedLocale, | |
| 1215 format.resolved.locale); | |
| 1216 | |
| 1217 var result = { | |
| 1218 locale: locale, | |
| 1219 numberingSystem: format.resolved.numberingSystem, | |
| 1220 style: format.resolved.style, | |
| 1221 useGrouping: format.resolved.useGrouping, | |
| 1222 minimumIntegerDigits: format.resolved.minimumIntegerDigits, | |
| 1223 minimumFractionDigits: format.resolved.minimumFractionDigits, | |
| 1224 maximumFractionDigits: format.resolved.maximumFractionDigits, | |
| 1225 }; | |
| 1226 | |
| 1227 if (result.style === 'currency') { | |
| 1228 defineWECProperty(result, 'currency', format.resolved.currency); | |
| 1229 defineWECProperty(result, 'currencyDisplay', | |
| 1230 format.resolved.currencyDisplay); | |
| 1231 } | |
| 1232 | |
| 1233 if (%HasOwnProperty(format.resolved, 'minimumSignificantDigits')) { | |
| 1234 defineWECProperty(result, 'minimumSignificantDigits', | |
| 1235 format.resolved.minimumSignificantDigits); | |
| 1236 } | |
| 1237 | |
| 1238 if (%HasOwnProperty(format.resolved, 'maximumSignificantDigits')) { | |
| 1239 defineWECProperty(result, 'maximumSignificantDigits', | |
| 1240 format.resolved.maximumSignificantDigits); | |
| 1241 } | |
| 1242 | |
| 1243 return result; | |
| 1244 }, | |
| 1245 DONT_ENUM | |
| 1246 ); | |
| 1247 %FunctionSetName(Intl.NumberFormat.prototype.resolvedOptions, | |
| 1248 'resolvedOptions'); | |
| 1249 %FunctionRemovePrototype(Intl.NumberFormat.prototype.resolvedOptions); | |
| 1250 %SetNativeFlag(Intl.NumberFormat.prototype.resolvedOptions); | |
| 1251 | |
| 1252 | |
| 1253 /** | |
| 1254 * Returns the subset of the given locale list for which this locale list | |
| 1255 * has a matching (possibly fallback) locale. Locales appear in the same | |
| 1256 * order in the returned list as in the input list. | |
| 1257 * Options are optional parameter. | |
| 1258 */ | |
| 1259 %AddNamedProperty(Intl.NumberFormat, 'supportedLocalesOf', function(locales) { | |
| 1260 if (%_IsConstructCall()) { | |
| 1261 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); | |
| 1262 } | |
| 1263 | |
| 1264 return supportedLocalesOf('numberformat', locales, %_Arguments(1)); | |
| 1265 }, | |
| 1266 DONT_ENUM | |
| 1267 ); | |
| 1268 %FunctionSetName(Intl.NumberFormat.supportedLocalesOf, 'supportedLocalesOf'); | |
| 1269 %FunctionRemovePrototype(Intl.NumberFormat.supportedLocalesOf); | |
| 1270 %SetNativeFlag(Intl.NumberFormat.supportedLocalesOf); | |
| 1271 | |
| 1272 | |
| 1273 /** | |
| 1274 * Returns a String value representing the result of calling ToNumber(value) | |
| 1275 * according to the effective locale and the formatting options of this | |
| 1276 * NumberFormat. | |
| 1277 */ | |
| 1278 function formatNumber(formatter, value) { | |
| 1279 // Spec treats -0 and +0 as 0. | |
| 1280 var number = TO_NUMBER(value) + 0; | |
| 1281 | |
| 1282 return %InternalNumberFormat(%GetImplFromInitializedIntlObject(formatter), | |
| 1283 number); | |
| 1284 } | |
| 1285 | |
| 1286 | |
| 1287 /** | |
| 1288 * Returns a Number that represents string value that was passed in. | |
| 1289 */ | |
| 1290 function parseNumber(formatter, value) { | |
| 1291 return %InternalNumberParse(%GetImplFromInitializedIntlObject(formatter), | |
| 1292 GlobalString(value)); | |
| 1293 } | |
| 1294 | |
| 1295 | |
| 1296 addBoundMethod(Intl.NumberFormat, 'format', formatNumber, 1); | |
| 1297 addBoundMethod(Intl.NumberFormat, 'v8Parse', parseNumber, 1); | |
| 1298 | |
| 1299 /** | |
| 1300 * Returns a string that matches LDML representation of the options object. | |
| 1301 */ | |
| 1302 function toLDMLString(options) { | |
| 1303 var getOption = getGetOption(options, 'dateformat'); | |
| 1304 | |
| 1305 var ldmlString = ''; | |
| 1306 | |
| 1307 var option = getOption('weekday', 'string', ['narrow', 'short', 'long']); | |
| 1308 ldmlString += appendToLDMLString( | |
| 1309 option, {narrow: 'EEEEE', short: 'EEE', long: 'EEEE'}); | |
| 1310 | |
| 1311 option = getOption('era', 'string', ['narrow', 'short', 'long']); | |
| 1312 ldmlString += appendToLDMLString( | |
| 1313 option, {narrow: 'GGGGG', short: 'GGG', long: 'GGGG'}); | |
| 1314 | |
| 1315 option = getOption('year', 'string', ['2-digit', 'numeric']); | |
| 1316 ldmlString += appendToLDMLString(option, {'2-digit': 'yy', 'numeric': 'y'}); | |
| 1317 | |
| 1318 option = getOption('month', 'string', | |
| 1319 ['2-digit', 'numeric', 'narrow', 'short', 'long']); | |
| 1320 ldmlString += appendToLDMLString(option, {'2-digit': 'MM', 'numeric': 'M', | |
| 1321 'narrow': 'MMMMM', 'short': 'MMM', 'long': 'MMMM'}); | |
| 1322 | |
| 1323 option = getOption('day', 'string', ['2-digit', 'numeric']); | |
| 1324 ldmlString += appendToLDMLString( | |
| 1325 option, {'2-digit': 'dd', 'numeric': 'd'}); | |
| 1326 | |
| 1327 var hr12 = getOption('hour12', 'boolean'); | |
| 1328 option = getOption('hour', 'string', ['2-digit', 'numeric']); | |
| 1329 if (IS_UNDEFINED(hr12)) { | |
| 1330 ldmlString += appendToLDMLString(option, {'2-digit': 'jj', 'numeric': 'j'}); | |
| 1331 } else if (hr12 === true) { | |
| 1332 ldmlString += appendToLDMLString(option, {'2-digit': 'hh', 'numeric': 'h'}); | |
| 1333 } else { | |
| 1334 ldmlString += appendToLDMLString(option, {'2-digit': 'HH', 'numeric': 'H'}); | |
| 1335 } | |
| 1336 | |
| 1337 option = getOption('minute', 'string', ['2-digit', 'numeric']); | |
| 1338 ldmlString += appendToLDMLString(option, {'2-digit': 'mm', 'numeric': 'm'}); | |
| 1339 | |
| 1340 option = getOption('second', 'string', ['2-digit', 'numeric']); | |
| 1341 ldmlString += appendToLDMLString(option, {'2-digit': 'ss', 'numeric': 's'}); | |
| 1342 | |
| 1343 option = getOption('timeZoneName', 'string', ['short', 'long']); | |
| 1344 ldmlString += appendToLDMLString(option, {short: 'z', long: 'zzzz'}); | |
| 1345 | |
| 1346 return ldmlString; | |
| 1347 } | |
| 1348 | |
| 1349 | |
| 1350 /** | |
| 1351 * Returns either LDML equivalent of the current option or empty string. | |
| 1352 */ | |
| 1353 function appendToLDMLString(option, pairs) { | |
| 1354 if (!IS_UNDEFINED(option)) { | |
| 1355 return pairs[option]; | |
| 1356 } else { | |
| 1357 return ''; | |
| 1358 } | |
| 1359 } | |
| 1360 | |
| 1361 | |
| 1362 /** | |
| 1363 * Returns object that matches LDML representation of the date. | |
| 1364 */ | |
| 1365 function fromLDMLString(ldmlString) { | |
| 1366 // First remove '' quoted text, so we lose 'Uhr' strings. | |
| 1367 ldmlString = %_CallFunction(ldmlString, GetQuotedStringRE(), '', | |
| 1368 StringReplace); | |
| 1369 | |
| 1370 var options = {}; | |
| 1371 var match = %_CallFunction(ldmlString, /E{3,5}/g, StringMatch); | |
| 1372 options = appendToDateTimeObject( | |
| 1373 options, 'weekday', match, {EEEEE: 'narrow', EEE: 'short', EEEE: 'long'}); | |
| 1374 | |
| 1375 match = %_CallFunction(ldmlString, /G{3,5}/g, StringMatch); | |
| 1376 options = appendToDateTimeObject( | |
| 1377 options, 'era', match, {GGGGG: 'narrow', GGG: 'short', GGGG: 'long'}); | |
| 1378 | |
| 1379 match = %_CallFunction(ldmlString, /y{1,2}/g, StringMatch); | |
| 1380 options = appendToDateTimeObject( | |
| 1381 options, 'year', match, {y: 'numeric', yy: '2-digit'}); | |
| 1382 | |
| 1383 match = %_CallFunction(ldmlString, /M{1,5}/g, StringMatch); | |
| 1384 options = appendToDateTimeObject(options, 'month', match, {MM: '2-digit', | |
| 1385 M: 'numeric', MMMMM: 'narrow', MMM: 'short', MMMM: 'long'}); | |
| 1386 | |
| 1387 // Sometimes we get L instead of M for month - standalone name. | |
| 1388 match = %_CallFunction(ldmlString, /L{1,5}/g, StringMatch); | |
| 1389 options = appendToDateTimeObject(options, 'month', match, {LL: '2-digit', | |
| 1390 L: 'numeric', LLLLL: 'narrow', LLL: 'short', LLLL: 'long'}); | |
| 1391 | |
| 1392 match = %_CallFunction(ldmlString, /d{1,2}/g, StringMatch); | |
| 1393 options = appendToDateTimeObject( | |
| 1394 options, 'day', match, {d: 'numeric', dd: '2-digit'}); | |
| 1395 | |
| 1396 match = %_CallFunction(ldmlString, /h{1,2}/g, StringMatch); | |
| 1397 if (match !== null) { | |
| 1398 options['hour12'] = true; | |
| 1399 } | |
| 1400 options = appendToDateTimeObject( | |
| 1401 options, 'hour', match, {h: 'numeric', hh: '2-digit'}); | |
| 1402 | |
| 1403 match = %_CallFunction(ldmlString, /H{1,2}/g, StringMatch); | |
| 1404 if (match !== null) { | |
| 1405 options['hour12'] = false; | |
| 1406 } | |
| 1407 options = appendToDateTimeObject( | |
| 1408 options, 'hour', match, {H: 'numeric', HH: '2-digit'}); | |
| 1409 | |
| 1410 match = %_CallFunction(ldmlString, /m{1,2}/g, StringMatch); | |
| 1411 options = appendToDateTimeObject( | |
| 1412 options, 'minute', match, {m: 'numeric', mm: '2-digit'}); | |
| 1413 | |
| 1414 match = %_CallFunction(ldmlString, /s{1,2}/g, StringMatch); | |
| 1415 options = appendToDateTimeObject( | |
| 1416 options, 'second', match, {s: 'numeric', ss: '2-digit'}); | |
| 1417 | |
| 1418 match = %_CallFunction(ldmlString, /z|zzzz/g, StringMatch); | |
| 1419 options = appendToDateTimeObject( | |
| 1420 options, 'timeZoneName', match, {z: 'short', zzzz: 'long'}); | |
| 1421 | |
| 1422 return options; | |
| 1423 } | |
| 1424 | |
| 1425 | |
| 1426 function appendToDateTimeObject(options, option, match, pairs) { | |
| 1427 if (IS_NULL(match)) { | |
| 1428 if (!%HasOwnProperty(options, option)) { | |
| 1429 defineWEProperty(options, option, UNDEFINED); | |
| 1430 } | |
| 1431 return options; | |
| 1432 } | |
| 1433 | |
| 1434 var property = match[0]; | |
| 1435 defineWEProperty(options, option, pairs[property]); | |
| 1436 | |
| 1437 return options; | |
| 1438 } | |
| 1439 | |
| 1440 | |
| 1441 /** | |
| 1442 * Returns options with at least default values in it. | |
| 1443 */ | |
| 1444 function toDateTimeOptions(options, required, defaults) { | |
| 1445 if (IS_UNDEFINED(options)) { | |
| 1446 options = {}; | |
| 1447 } else { | |
| 1448 options = TO_OBJECT(options); | |
| 1449 } | |
| 1450 | |
| 1451 var needsDefault = true; | |
| 1452 if ((required === 'date' || required === 'any') && | |
| 1453 (!IS_UNDEFINED(options.weekday) || !IS_UNDEFINED(options.year) || | |
| 1454 !IS_UNDEFINED(options.month) || !IS_UNDEFINED(options.day))) { | |
| 1455 needsDefault = false; | |
| 1456 } | |
| 1457 | |
| 1458 if ((required === 'time' || required === 'any') && | |
| 1459 (!IS_UNDEFINED(options.hour) || !IS_UNDEFINED(options.minute) || | |
| 1460 !IS_UNDEFINED(options.second))) { | |
| 1461 needsDefault = false; | |
| 1462 } | |
| 1463 | |
| 1464 if (needsDefault && (defaults === 'date' || defaults === 'all')) { | |
| 1465 ObjectDefineProperty(options, 'year', {value: 'numeric', | |
| 1466 writable: true, | |
| 1467 enumerable: true, | |
| 1468 configurable: true}); | |
| 1469 ObjectDefineProperty(options, 'month', {value: 'numeric', | |
| 1470 writable: true, | |
| 1471 enumerable: true, | |
| 1472 configurable: true}); | |
| 1473 ObjectDefineProperty(options, 'day', {value: 'numeric', | |
| 1474 writable: true, | |
| 1475 enumerable: true, | |
| 1476 configurable: true}); | |
| 1477 } | |
| 1478 | |
| 1479 if (needsDefault && (defaults === 'time' || defaults === 'all')) { | |
| 1480 ObjectDefineProperty(options, 'hour', {value: 'numeric', | |
| 1481 writable: true, | |
| 1482 enumerable: true, | |
| 1483 configurable: true}); | |
| 1484 ObjectDefineProperty(options, 'minute', {value: 'numeric', | |
| 1485 writable: true, | |
| 1486 enumerable: true, | |
| 1487 configurable: true}); | |
| 1488 ObjectDefineProperty(options, 'second', {value: 'numeric', | |
| 1489 writable: true, | |
| 1490 enumerable: true, | |
| 1491 configurable: true}); | |
| 1492 } | |
| 1493 | |
| 1494 return options; | |
| 1495 } | |
| 1496 | |
| 1497 | |
| 1498 /** | |
| 1499 * Initializes the given object so it's a valid DateTimeFormat instance. | |
| 1500 * Useful for subclassing. | |
| 1501 */ | |
| 1502 function initializeDateTimeFormat(dateFormat, locales, options) { | |
| 1503 | |
| 1504 if (%IsInitializedIntlObject(dateFormat)) { | |
| 1505 throw MakeTypeError(kReinitializeIntl, "DateTimeFormat"); | |
| 1506 } | |
| 1507 | |
| 1508 if (IS_UNDEFINED(options)) { | |
| 1509 options = {}; | |
| 1510 } | |
| 1511 | |
| 1512 var locale = resolveLocale('dateformat', locales, options); | |
| 1513 | |
| 1514 options = toDateTimeOptions(options, 'any', 'date'); | |
| 1515 | |
| 1516 var getOption = getGetOption(options, 'dateformat'); | |
| 1517 | |
| 1518 // We implement only best fit algorithm, but still need to check | |
| 1519 // if the formatMatcher values are in range. | |
| 1520 var matcher = getOption('formatMatcher', 'string', | |
| 1521 ['basic', 'best fit'], 'best fit'); | |
| 1522 | |
| 1523 // Build LDML string for the skeleton that we pass to the formatter. | |
| 1524 var ldmlString = toLDMLString(options); | |
| 1525 | |
| 1526 // Filter out supported extension keys so we know what to put in resolved | |
| 1527 // section later on. | |
| 1528 // We need to pass calendar and number system to the method. | |
| 1529 var tz = canonicalizeTimeZoneID(options.timeZone); | |
| 1530 | |
| 1531 // ICU prefers options to be passed using -u- extension key/values, so | |
| 1532 // we need to build that. | |
| 1533 var internalOptions = {}; | |
| 1534 var extensionMap = parseExtension(locale.extension); | |
| 1535 | |
| 1536 /** | |
| 1537 * Map of Unicode extensions to option properties, and their values and types, | |
| 1538 * for a date/time format. | |
| 1539 */ | |
| 1540 var DATETIME_FORMAT_KEY_MAP = { | |
| 1541 'ca': {'property': UNDEFINED, 'type': 'string'}, | |
| 1542 'nu': {'property': UNDEFINED, 'type': 'string'} | |
| 1543 }; | |
| 1544 | |
| 1545 var extension = setOptions(options, extensionMap, DATETIME_FORMAT_KEY_MAP, | |
| 1546 getOption, internalOptions); | |
| 1547 | |
| 1548 var requestedLocale = locale.locale + extension; | |
| 1549 var resolved = ObjectDefineProperties({}, { | |
| 1550 calendar: {writable: true}, | |
| 1551 day: {writable: true}, | |
| 1552 era: {writable: true}, | |
| 1553 hour12: {writable: true}, | |
| 1554 hour: {writable: true}, | |
| 1555 locale: {writable: true}, | |
| 1556 minute: {writable: true}, | |
| 1557 month: {writable: true}, | |
| 1558 numberingSystem: {writable: true}, | |
| 1559 pattern: {writable: true}, | |
| 1560 requestedLocale: {value: requestedLocale, writable: true}, | |
| 1561 second: {writable: true}, | |
| 1562 timeZone: {writable: true}, | |
| 1563 timeZoneName: {writable: true}, | |
| 1564 tz: {value: tz, writable: true}, | |
| 1565 weekday: {writable: true}, | |
| 1566 year: {writable: true} | |
| 1567 }); | |
| 1568 | |
| 1569 var formatter = %CreateDateTimeFormat( | |
| 1570 requestedLocale, {skeleton: ldmlString, timeZone: tz}, resolved); | |
| 1571 | |
| 1572 if (!IS_UNDEFINED(tz) && tz !== resolved.timeZone) { | |
| 1573 throw MakeRangeError(kUnsupportedTimeZone, tz); | |
| 1574 } | |
| 1575 | |
| 1576 %MarkAsInitializedIntlObjectOfType(dateFormat, 'dateformat', formatter); | |
| 1577 ObjectDefineProperty(dateFormat, 'resolved', {value: resolved}); | |
| 1578 | |
| 1579 return dateFormat; | |
| 1580 } | |
| 1581 | |
| 1582 | |
| 1583 /** | |
| 1584 * Constructs Intl.DateTimeFormat object given optional locales and options | |
| 1585 * parameters. | |
| 1586 * | |
| 1587 * @constructor | |
| 1588 */ | |
| 1589 %AddNamedProperty(Intl, 'DateTimeFormat', function() { | |
| 1590 var locales = %_Arguments(0); | |
| 1591 var options = %_Arguments(1); | |
| 1592 | |
| 1593 if (!this || this === Intl) { | |
| 1594 // Constructor is called as a function. | |
| 1595 return new Intl.DateTimeFormat(locales, options); | |
| 1596 } | |
| 1597 | |
| 1598 return initializeDateTimeFormat(TO_OBJECT(this), locales, options); | |
| 1599 }, | |
| 1600 DONT_ENUM | |
| 1601 ); | |
| 1602 | |
| 1603 | |
| 1604 /** | |
| 1605 * DateTimeFormat resolvedOptions method. | |
| 1606 */ | |
| 1607 %AddNamedProperty(Intl.DateTimeFormat.prototype, 'resolvedOptions', function() { | |
| 1608 if (%_IsConstructCall()) { | |
| 1609 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); | |
| 1610 } | |
| 1611 | |
| 1612 if (!%IsInitializedIntlObjectOfType(this, 'dateformat')) { | |
| 1613 throw MakeTypeError(kResolvedOptionsCalledOnNonObject, "DateTimeFormat"); | |
| 1614 } | |
| 1615 | |
| 1616 /** | |
| 1617 * Maps ICU calendar names into LDML type. | |
| 1618 */ | |
| 1619 var ICU_CALENDAR_MAP = { | |
| 1620 'gregorian': 'gregory', | |
| 1621 'japanese': 'japanese', | |
| 1622 'buddhist': 'buddhist', | |
| 1623 'roc': 'roc', | |
| 1624 'persian': 'persian', | |
| 1625 'islamic-civil': 'islamicc', | |
| 1626 'islamic': 'islamic', | |
| 1627 'hebrew': 'hebrew', | |
| 1628 'chinese': 'chinese', | |
| 1629 'indian': 'indian', | |
| 1630 'coptic': 'coptic', | |
| 1631 'ethiopic': 'ethiopic', | |
| 1632 'ethiopic-amete-alem': 'ethioaa' | |
| 1633 }; | |
| 1634 | |
| 1635 var format = this; | |
| 1636 var fromPattern = fromLDMLString(format.resolved.pattern); | |
| 1637 var userCalendar = ICU_CALENDAR_MAP[format.resolved.calendar]; | |
| 1638 if (IS_UNDEFINED(userCalendar)) { | |
| 1639 // Use ICU name if we don't have a match. It shouldn't happen, but | |
| 1640 // it would be too strict to throw for this. | |
| 1641 userCalendar = format.resolved.calendar; | |
| 1642 } | |
| 1643 | |
| 1644 var locale = getOptimalLanguageTag(format.resolved.requestedLocale, | |
| 1645 format.resolved.locale); | |
| 1646 | |
| 1647 var result = { | |
| 1648 locale: locale, | |
| 1649 numberingSystem: format.resolved.numberingSystem, | |
| 1650 calendar: userCalendar, | |
| 1651 timeZone: format.resolved.timeZone | |
| 1652 }; | |
| 1653 | |
| 1654 addWECPropertyIfDefined(result, 'timeZoneName', fromPattern.timeZoneName); | |
| 1655 addWECPropertyIfDefined(result, 'era', fromPattern.era); | |
| 1656 addWECPropertyIfDefined(result, 'year', fromPattern.year); | |
| 1657 addWECPropertyIfDefined(result, 'month', fromPattern.month); | |
| 1658 addWECPropertyIfDefined(result, 'day', fromPattern.day); | |
| 1659 addWECPropertyIfDefined(result, 'weekday', fromPattern.weekday); | |
| 1660 addWECPropertyIfDefined(result, 'hour12', fromPattern.hour12); | |
| 1661 addWECPropertyIfDefined(result, 'hour', fromPattern.hour); | |
| 1662 addWECPropertyIfDefined(result, 'minute', fromPattern.minute); | |
| 1663 addWECPropertyIfDefined(result, 'second', fromPattern.second); | |
| 1664 | |
| 1665 return result; | |
| 1666 }, | |
| 1667 DONT_ENUM | |
| 1668 ); | |
| 1669 %FunctionSetName(Intl.DateTimeFormat.prototype.resolvedOptions, | |
| 1670 'resolvedOptions'); | |
| 1671 %FunctionRemovePrototype(Intl.DateTimeFormat.prototype.resolvedOptions); | |
| 1672 %SetNativeFlag(Intl.DateTimeFormat.prototype.resolvedOptions); | |
| 1673 | |
| 1674 | |
| 1675 /** | |
| 1676 * Returns the subset of the given locale list for which this locale list | |
| 1677 * has a matching (possibly fallback) locale. Locales appear in the same | |
| 1678 * order in the returned list as in the input list. | |
| 1679 * Options are optional parameter. | |
| 1680 */ | |
| 1681 %AddNamedProperty(Intl.DateTimeFormat, 'supportedLocalesOf', function(locales) { | |
| 1682 if (%_IsConstructCall()) { | |
| 1683 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); | |
| 1684 } | |
| 1685 | |
| 1686 return supportedLocalesOf('dateformat', locales, %_Arguments(1)); | |
| 1687 }, | |
| 1688 DONT_ENUM | |
| 1689 ); | |
| 1690 %FunctionSetName(Intl.DateTimeFormat.supportedLocalesOf, 'supportedLocalesOf'); | |
| 1691 %FunctionRemovePrototype(Intl.DateTimeFormat.supportedLocalesOf); | |
| 1692 %SetNativeFlag(Intl.DateTimeFormat.supportedLocalesOf); | |
| 1693 | |
| 1694 | |
| 1695 /** | |
| 1696 * Returns a String value representing the result of calling ToNumber(date) | |
| 1697 * according to the effective locale and the formatting options of this | |
| 1698 * DateTimeFormat. | |
| 1699 */ | |
| 1700 function formatDate(formatter, dateValue) { | |
| 1701 var dateMs; | |
| 1702 if (IS_UNDEFINED(dateValue)) { | |
| 1703 dateMs = %DateCurrentTime(); | |
| 1704 } else { | |
| 1705 dateMs = TO_NUMBER(dateValue); | |
| 1706 } | |
| 1707 | |
| 1708 if (!IsFinite(dateMs)) throw MakeRangeError(kDateRange); | |
| 1709 | |
| 1710 return %InternalDateFormat(%GetImplFromInitializedIntlObject(formatter), | |
| 1711 new GlobalDate(dateMs)); | |
| 1712 } | |
| 1713 | |
| 1714 | |
| 1715 /** | |
| 1716 * Returns a Date object representing the result of calling ToString(value) | |
| 1717 * according to the effective locale and the formatting options of this | |
| 1718 * DateTimeFormat. | |
| 1719 * Returns undefined if date string cannot be parsed. | |
| 1720 */ | |
| 1721 function parseDate(formatter, value) { | |
| 1722 return %InternalDateParse(%GetImplFromInitializedIntlObject(formatter), | |
| 1723 GlobalString(value)); | |
| 1724 } | |
| 1725 | |
| 1726 | |
| 1727 // 0 because date is optional argument. | |
| 1728 addBoundMethod(Intl.DateTimeFormat, 'format', formatDate, 0); | |
| 1729 addBoundMethod(Intl.DateTimeFormat, 'v8Parse', parseDate, 1); | |
| 1730 | |
| 1731 | |
| 1732 /** | |
| 1733 * Returns canonical Area/Location name, or throws an exception if the zone | |
| 1734 * name is invalid IANA name. | |
| 1735 */ | |
| 1736 function canonicalizeTimeZoneID(tzID) { | |
| 1737 // Skip undefined zones. | |
| 1738 if (IS_UNDEFINED(tzID)) { | |
| 1739 return tzID; | |
| 1740 } | |
| 1741 | |
| 1742 // Special case handling (UTC, GMT). | |
| 1743 var upperID = %StringToUpperCase(tzID); | |
| 1744 if (upperID === 'UTC' || upperID === 'GMT' || | |
| 1745 upperID === 'ETC/UTC' || upperID === 'ETC/GMT') { | |
| 1746 return 'UTC'; | |
| 1747 } | |
| 1748 | |
| 1749 // We expect only _ and / beside ASCII letters. | |
| 1750 // All inputs should conform to Area/Location from now on. | |
| 1751 var match = %_CallFunction(tzID, GetTimezoneNameCheckRE(), StringMatch); | |
| 1752 if (IS_NULL(match)) throw MakeRangeError(kExpectedLocation, tzID); | |
| 1753 | |
| 1754 var result = toTitleCaseWord(match[1]) + '/' + toTitleCaseWord(match[2]); | |
| 1755 var i = 3; | |
| 1756 while (!IS_UNDEFINED(match[i]) && i < match.length) { | |
| 1757 result = result + '_' + toTitleCaseWord(match[i]); | |
| 1758 i++; | |
| 1759 } | |
| 1760 | |
| 1761 return result; | |
| 1762 } | |
| 1763 | |
| 1764 /** | |
| 1765 * Initializes the given object so it's a valid BreakIterator instance. | |
| 1766 * Useful for subclassing. | |
| 1767 */ | |
| 1768 function initializeBreakIterator(iterator, locales, options) { | |
| 1769 if (%IsInitializedIntlObject(iterator)) { | |
| 1770 throw MakeTypeError(kReinitializeIntl, "v8BreakIterator"); | |
| 1771 } | |
| 1772 | |
| 1773 if (IS_UNDEFINED(options)) { | |
| 1774 options = {}; | |
| 1775 } | |
| 1776 | |
| 1777 var getOption = getGetOption(options, 'breakiterator'); | |
| 1778 | |
| 1779 var internalOptions = {}; | |
| 1780 | |
| 1781 defineWEProperty(internalOptions, 'type', getOption( | |
| 1782 'type', 'string', ['character', 'word', 'sentence', 'line'], 'word')); | |
| 1783 | |
| 1784 var locale = resolveLocale('breakiterator', locales, options); | |
| 1785 var resolved = ObjectDefineProperties({}, { | |
| 1786 requestedLocale: {value: locale.locale, writable: true}, | |
| 1787 type: {value: internalOptions.type, writable: true}, | |
| 1788 locale: {writable: true} | |
| 1789 }); | |
| 1790 | |
| 1791 var internalIterator = %CreateBreakIterator(locale.locale, | |
| 1792 internalOptions, | |
| 1793 resolved); | |
| 1794 | |
| 1795 %MarkAsInitializedIntlObjectOfType(iterator, 'breakiterator', | |
| 1796 internalIterator); | |
| 1797 ObjectDefineProperty(iterator, 'resolved', {value: resolved}); | |
| 1798 | |
| 1799 return iterator; | |
| 1800 } | |
| 1801 | |
| 1802 | |
| 1803 /** | |
| 1804 * Constructs Intl.v8BreakIterator object given optional locales and options | |
| 1805 * parameters. | |
| 1806 * | |
| 1807 * @constructor | |
| 1808 */ | |
| 1809 %AddNamedProperty(Intl, 'v8BreakIterator', function() { | |
| 1810 var locales = %_Arguments(0); | |
| 1811 var options = %_Arguments(1); | |
| 1812 | |
| 1813 if (!this || this === Intl) { | |
| 1814 // Constructor is called as a function. | |
| 1815 return new Intl.v8BreakIterator(locales, options); | |
| 1816 } | |
| 1817 | |
| 1818 return initializeBreakIterator(TO_OBJECT(this), locales, options); | |
| 1819 }, | |
| 1820 DONT_ENUM | |
| 1821 ); | |
| 1822 | |
| 1823 | |
| 1824 /** | |
| 1825 * BreakIterator resolvedOptions method. | |
| 1826 */ | |
| 1827 %AddNamedProperty(Intl.v8BreakIterator.prototype, 'resolvedOptions', | |
| 1828 function() { | |
| 1829 if (%_IsConstructCall()) { | |
| 1830 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); | |
| 1831 } | |
| 1832 | |
| 1833 if (!%IsInitializedIntlObjectOfType(this, 'breakiterator')) { | |
| 1834 throw MakeTypeError(kResolvedOptionsCalledOnNonObject, "v8BreakIterator"); | |
| 1835 } | |
| 1836 | |
| 1837 var segmenter = this; | |
| 1838 var locale = getOptimalLanguageTag(segmenter.resolved.requestedLocale, | |
| 1839 segmenter.resolved.locale); | |
| 1840 | |
| 1841 return { | |
| 1842 locale: locale, | |
| 1843 type: segmenter.resolved.type | |
| 1844 }; | |
| 1845 }, | |
| 1846 DONT_ENUM | |
| 1847 ); | |
| 1848 %FunctionSetName(Intl.v8BreakIterator.prototype.resolvedOptions, | |
| 1849 'resolvedOptions'); | |
| 1850 %FunctionRemovePrototype(Intl.v8BreakIterator.prototype.resolvedOptions); | |
| 1851 %SetNativeFlag(Intl.v8BreakIterator.prototype.resolvedOptions); | |
| 1852 | |
| 1853 | |
| 1854 /** | |
| 1855 * Returns the subset of the given locale list for which this locale list | |
| 1856 * has a matching (possibly fallback) locale. Locales appear in the same | |
| 1857 * order in the returned list as in the input list. | |
| 1858 * Options are optional parameter. | |
| 1859 */ | |
| 1860 %AddNamedProperty(Intl.v8BreakIterator, 'supportedLocalesOf', | |
| 1861 function(locales) { | |
| 1862 if (%_IsConstructCall()) { | |
| 1863 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); | |
| 1864 } | |
| 1865 | |
| 1866 return supportedLocalesOf('breakiterator', locales, %_Arguments(1)); | |
| 1867 }, | |
| 1868 DONT_ENUM | |
| 1869 ); | |
| 1870 %FunctionSetName(Intl.v8BreakIterator.supportedLocalesOf, 'supportedLocalesOf'); | |
| 1871 %FunctionRemovePrototype(Intl.v8BreakIterator.supportedLocalesOf); | |
| 1872 %SetNativeFlag(Intl.v8BreakIterator.supportedLocalesOf); | |
| 1873 | |
| 1874 | |
| 1875 /** | |
| 1876 * Adopts text to segment using the iterator. Old text, if present, | |
| 1877 * gets discarded. | |
| 1878 */ | |
| 1879 function adoptText(iterator, text) { | |
| 1880 %BreakIteratorAdoptText(%GetImplFromInitializedIntlObject(iterator), | |
| 1881 GlobalString(text)); | |
| 1882 } | |
| 1883 | |
| 1884 | |
| 1885 /** | |
| 1886 * Returns index of the first break in the string and moves current pointer. | |
| 1887 */ | |
| 1888 function first(iterator) { | |
| 1889 return %BreakIteratorFirst(%GetImplFromInitializedIntlObject(iterator)); | |
| 1890 } | |
| 1891 | |
| 1892 | |
| 1893 /** | |
| 1894 * Returns the index of the next break and moves the pointer. | |
| 1895 */ | |
| 1896 function next(iterator) { | |
| 1897 return %BreakIteratorNext(%GetImplFromInitializedIntlObject(iterator)); | |
| 1898 } | |
| 1899 | |
| 1900 | |
| 1901 /** | |
| 1902 * Returns index of the current break. | |
| 1903 */ | |
| 1904 function current(iterator) { | |
| 1905 return %BreakIteratorCurrent(%GetImplFromInitializedIntlObject(iterator)); | |
| 1906 } | |
| 1907 | |
| 1908 | |
| 1909 /** | |
| 1910 * Returns type of the current break. | |
| 1911 */ | |
| 1912 function breakType(iterator) { | |
| 1913 return %BreakIteratorBreakType(%GetImplFromInitializedIntlObject(iterator)); | |
| 1914 } | |
| 1915 | |
| 1916 | |
| 1917 addBoundMethod(Intl.v8BreakIterator, 'adoptText', adoptText, 1); | |
| 1918 addBoundMethod(Intl.v8BreakIterator, 'first', first, 0); | |
| 1919 addBoundMethod(Intl.v8BreakIterator, 'next', next, 0); | |
| 1920 addBoundMethod(Intl.v8BreakIterator, 'current', current, 0); | |
| 1921 addBoundMethod(Intl.v8BreakIterator, 'breakType', breakType, 0); | |
| 1922 | |
| 1923 // Save references to Intl objects and methods we use, for added security. | |
| 1924 var savedObjects = { | |
| 1925 'collator': Intl.Collator, | |
| 1926 'numberformat': Intl.NumberFormat, | |
| 1927 'dateformatall': Intl.DateTimeFormat, | |
| 1928 'dateformatdate': Intl.DateTimeFormat, | |
| 1929 'dateformattime': Intl.DateTimeFormat | |
| 1930 }; | |
| 1931 | |
| 1932 | |
| 1933 // Default (created with undefined locales and options parameters) collator, | |
| 1934 // number and date format instances. They'll be created as needed. | |
| 1935 var defaultObjects = { | |
| 1936 'collator': UNDEFINED, | |
| 1937 'numberformat': UNDEFINED, | |
| 1938 'dateformatall': UNDEFINED, | |
| 1939 'dateformatdate': UNDEFINED, | |
| 1940 'dateformattime': UNDEFINED, | |
| 1941 }; | |
| 1942 | |
| 1943 | |
| 1944 /** | |
| 1945 * Returns cached or newly created instance of a given service. | |
| 1946 * We cache only default instances (where no locales or options are provided). | |
| 1947 */ | |
| 1948 function cachedOrNewService(service, locales, options, defaults) { | |
| 1949 var useOptions = (IS_UNDEFINED(defaults)) ? options : defaults; | |
| 1950 if (IS_UNDEFINED(locales) && IS_UNDEFINED(options)) { | |
| 1951 if (IS_UNDEFINED(defaultObjects[service])) { | |
| 1952 defaultObjects[service] = new savedObjects[service](locales, useOptions); | |
| 1953 } | |
| 1954 return defaultObjects[service]; | |
| 1955 } | |
| 1956 return new savedObjects[service](locales, useOptions); | |
| 1957 } | |
| 1958 | |
| 1959 | |
| 1960 function OverrideFunction(object, name, f) { | |
| 1961 %CheckIsBootstrapping(); | |
| 1962 ObjectDefineProperty(object, name, { value: f, | |
| 1963 writeable: true, | |
| 1964 configurable: true, | |
| 1965 enumerable: false }); | |
| 1966 %FunctionSetName(f, name); | |
| 1967 %FunctionRemovePrototype(f); | |
| 1968 %SetNativeFlag(f); | |
| 1969 } | |
| 1970 | |
| 1971 /** | |
| 1972 * Compares this and that, and returns less than 0, 0 or greater than 0 value. | |
| 1973 * Overrides the built-in method. | |
| 1974 */ | |
| 1975 OverrideFunction(GlobalString.prototype, 'localeCompare', function(that) { | |
| 1976 if (%_IsConstructCall()) { | |
| 1977 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); | |
| 1978 } | |
| 1979 | |
| 1980 if (IS_NULL_OR_UNDEFINED(this)) { | |
| 1981 throw MakeTypeError(kMethodInvokedOnNullOrUndefined); | |
| 1982 } | |
| 1983 | |
| 1984 var locales = %_Arguments(1); | |
| 1985 var options = %_Arguments(2); | |
| 1986 var collator = cachedOrNewService('collator', locales, options); | |
| 1987 return compare(collator, this, that); | |
| 1988 } | |
| 1989 ); | |
| 1990 | |
| 1991 | |
| 1992 /** | |
| 1993 * Unicode normalization. This method is called with one argument that | |
| 1994 * specifies the normalization form. | |
| 1995 * If none is specified, "NFC" is assumed. | |
| 1996 * If the form is not one of "NFC", "NFD", "NFKC", or "NFKD", then throw | |
| 1997 * a RangeError Exception. | |
| 1998 */ | |
| 1999 | |
| 2000 OverrideFunction(GlobalString.prototype, 'normalize', function() { | |
| 2001 if (%_IsConstructCall()) { | |
| 2002 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); | |
| 2003 } | |
| 2004 | |
| 2005 CHECK_OBJECT_COERCIBLE(this, "String.prototype.normalize"); | |
| 2006 var s = TO_STRING(this); | |
| 2007 | |
| 2008 var formArg = %_Arguments(0); | |
| 2009 var form = IS_UNDEFINED(formArg) ? 'NFC' : TO_STRING(formArg); | |
| 2010 | |
| 2011 var NORMALIZATION_FORMS = ['NFC', 'NFD', 'NFKC', 'NFKD']; | |
| 2012 | |
| 2013 var normalizationForm = | |
| 2014 %_CallFunction(NORMALIZATION_FORMS, form, ArrayIndexOf); | |
| 2015 if (normalizationForm === -1) { | |
| 2016 throw MakeRangeError(kNormalizationForm, | |
| 2017 %_CallFunction(NORMALIZATION_FORMS, ', ', ArrayJoin)); | |
| 2018 } | |
| 2019 | |
| 2020 return %StringNormalize(s, normalizationForm); | |
| 2021 } | |
| 2022 ); | |
| 2023 | |
| 2024 | |
| 2025 /** | |
| 2026 * Formats a Number object (this) using locale and options values. | |
| 2027 * If locale or options are omitted, defaults are used. | |
| 2028 */ | |
| 2029 OverrideFunction(GlobalNumber.prototype, 'toLocaleString', function() { | |
| 2030 if (%_IsConstructCall()) { | |
| 2031 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); | |
| 2032 } | |
| 2033 | |
| 2034 if (!(this instanceof GlobalNumber) && typeof(this) !== 'number') { | |
| 2035 throw MakeTypeError(kMethodInvokedOnWrongType, "Number"); | |
| 2036 } | |
| 2037 | |
| 2038 var locales = %_Arguments(0); | |
| 2039 var options = %_Arguments(1); | |
| 2040 var numberFormat = cachedOrNewService('numberformat', locales, options); | |
| 2041 return formatNumber(numberFormat, this); | |
| 2042 } | |
| 2043 ); | |
| 2044 | |
| 2045 | |
| 2046 /** | |
| 2047 * Returns actual formatted date or fails if date parameter is invalid. | |
| 2048 */ | |
| 2049 function toLocaleDateTime(date, locales, options, required, defaults, service) { | |
| 2050 if (!(date instanceof GlobalDate)) { | |
| 2051 throw MakeTypeError(kMethodInvokedOnWrongType, "Date"); | |
| 2052 } | |
| 2053 | |
| 2054 if (IsNaN(date)) return 'Invalid Date'; | |
| 2055 | |
| 2056 var internalOptions = toDateTimeOptions(options, required, defaults); | |
| 2057 | |
| 2058 var dateFormat = | |
| 2059 cachedOrNewService(service, locales, options, internalOptions); | |
| 2060 | |
| 2061 return formatDate(dateFormat, date); | |
| 2062 } | |
| 2063 | |
| 2064 | |
| 2065 /** | |
| 2066 * Formats a Date object (this) using locale and options values. | |
| 2067 * If locale or options are omitted, defaults are used - both date and time are | |
| 2068 * present in the output. | |
| 2069 */ | |
| 2070 OverrideFunction(GlobalDate.prototype, 'toLocaleString', function() { | |
| 2071 if (%_IsConstructCall()) { | |
| 2072 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); | |
| 2073 } | |
| 2074 | |
| 2075 var locales = %_Arguments(0); | |
| 2076 var options = %_Arguments(1); | |
| 2077 return toLocaleDateTime( | |
| 2078 this, locales, options, 'any', 'all', 'dateformatall'); | |
| 2079 } | |
| 2080 ); | |
| 2081 | |
| 2082 | |
| 2083 /** | |
| 2084 * Formats a Date object (this) using locale and options values. | |
| 2085 * If locale or options are omitted, defaults are used - only date is present | |
| 2086 * in the output. | |
| 2087 */ | |
| 2088 OverrideFunction(GlobalDate.prototype, 'toLocaleDateString', function() { | |
| 2089 if (%_IsConstructCall()) { | |
| 2090 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); | |
| 2091 } | |
| 2092 | |
| 2093 var locales = %_Arguments(0); | |
| 2094 var options = %_Arguments(1); | |
| 2095 return toLocaleDateTime( | |
| 2096 this, locales, options, 'date', 'date', 'dateformatdate'); | |
| 2097 } | |
| 2098 ); | |
| 2099 | |
| 2100 | |
| 2101 /** | |
| 2102 * Formats a Date object (this) using locale and options values. | |
| 2103 * If locale or options are omitted, defaults are used - only time is present | |
| 2104 * in the output. | |
| 2105 */ | |
| 2106 OverrideFunction(GlobalDate.prototype, 'toLocaleTimeString', function() { | |
| 2107 if (%_IsConstructCall()) { | |
| 2108 throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor); | |
| 2109 } | |
| 2110 | |
| 2111 var locales = %_Arguments(0); | |
| 2112 var options = %_Arguments(1); | |
| 2113 return toLocaleDateTime( | |
| 2114 this, locales, options, 'time', 'time', 'dateformattime'); | |
| 2115 } | |
| 2116 ); | |
| 2117 | |
| 2118 }) | |
| OLD | NEW |