Intl.js 175 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. "use strict";
  6. // Core intl lib
  7. (function (EngineInterface, InitType) {
  8. var platform = EngineInterface.Intl;
  9. // allow unit tests to disable caching behavior for testing convenience but have this always `true` in real scenarios
  10. platform.useCaches = true;
  11. // determine what backing library we are using
  12. // making these vars in JS allows us to more change how we
  13. // determine the backing library
  14. const isPlatformUsingICU = !platform.winglob;
  15. const isPlatformUsingWinGlob = platform.winglob;
  16. // constants
  17. const NOT_FOUND = "NOT_FOUND";
  18. // Built-Ins
  19. var setPrototype = platform.builtInSetPrototype;
  20. var getArrayLength = platform.builtInGetArrayLength;
  21. var callInstanceFunc = platform.builtInCallInstanceFunction;
  22. // Helper for our extensive usage of null-prototyped objects
  23. const bare = (obj = {}) => setPrototype(obj, null);
  24. // REVIEW(jahorto): IntlCache replaces past use of raw objects and JS Maps to cache arbitrary data for a given locale
  25. // We use a raw object rather than a Map because we don't need any features specific to Maps
  26. // If the cache gets too big (arbitrarily, > 25 keys is "too big" by default), we delete the entire internal object and start from scratch
  27. // TODO(jahorto): Experiment with the performance benefit of using an LRU or random-delete cache here.
  28. class IntlCache {
  29. constructor(n = 25) {
  30. this.n = n;
  31. this._cache = _.create();
  32. }
  33. get(key) {
  34. return platform.useCaches ? this._cache[key] : undefined;
  35. }
  36. set(key, value) {
  37. if (!platform.useCaches) {
  38. return;
  39. }
  40. if (_.keys(this._cache).length > this.n && this._cache[key] === undefined) {
  41. this._cache = _.create();
  42. }
  43. this._cache[key] = value;
  44. }
  45. }
  46. var Boolean = platform.Boolean;
  47. var Object = platform.Object;
  48. var RegExp = platform.RegExp;
  49. var Number = platform.Number;
  50. var String = platform.String;
  51. var Date = platform.Date;
  52. var Error = platform.Error;
  53. var Map = platform.Map;
  54. var RaiseAssert = platform.raiseAssert;
  55. var Math = setPrototype({
  56. abs: platform.builtInMathAbs,
  57. floor: platform.builtInMathFloor,
  58. max: platform.builtInMathMax,
  59. pow: platform.builtInMathPow
  60. }, null);
  61. var ObjectGetPrototypeOf = platform.builtInJavascriptObjectEntryGetPrototypeOf;
  62. var ObjectIsExtensible = platform.builtInJavascriptObjectEntryIsExtensible;
  63. var ObjectGetOwnPropertyNames = platform.builtInJavascriptObjectEntryGetOwnPropertyNames;
  64. var ObjectInstanceHasOwnProperty = platform.builtInJavascriptObjectEntryHasOwnProperty;
  65. // Because we don't keep track of the attributes object, and neither does the internals of Object.defineProperty;
  66. // We don't need to restore it's prototype.
  67. var _objectDefineProperty = platform.builtInJavascriptObjectEntryDefineProperty;
  68. var ObjectDefineProperty = function (obj, prop, attributes) {
  69. _objectDefineProperty(obj, prop, setPrototype(attributes, null));
  70. };
  71. var ArrayInstanceForEach = platform.builtInJavascriptArrayEntryForEach;
  72. var ArrayInstanceIndexOf = platform.builtInJavascriptArrayEntryIndexOf;
  73. var ArrayInstancePush = platform.builtInJavascriptArrayEntryPush;
  74. var ArrayInstanceJoin = platform.builtInJavascriptArrayEntryJoin;
  75. var FunctionInstanceBind = platform.builtInJavascriptFunctionEntryBind;
  76. var DateInstanceGetDate = platform.builtInJavascriptDateEntryGetDate;
  77. var DateNow = platform.builtInJavascriptDateEntryNow;
  78. var StringInstanceReplace = platform.builtInJavascriptStringEntryReplace;
  79. var StringInstanceToLowerCase = platform.builtInJavascriptStringEntryToLowerCase;
  80. var StringInstanceToUpperCase = platform.builtInJavascriptStringEntryToUpperCase;
  81. var ObjectPrototype = platform.Object_prototype;
  82. var isFinite = platform.builtInGlobalObjectEntryIsFinite;
  83. var isNaN = platform.builtInGlobalObjectEntryIsNaN;
  84. // Keep this "enum" in sync with IntlEngineInterfaceExtensionObject::EntryIntl_RegisterBuiltInFunction
  85. const IntlBuiltInFunctionID = setPrototype({
  86. MIN: 0,
  87. DateToLocaleString: 0,
  88. DateToLocaleDateString: 1,
  89. DateToLocaleTimeString: 2,
  90. NumberToLocaleString: 3,
  91. StringLocaleCompare: 4,
  92. MAX: 5
  93. }, null);
  94. const _ = {
  95. toUpperCase(str) { return callInstanceFunc(StringInstanceToUpperCase, str); },
  96. toLowerCase(str) { return callInstanceFunc(StringInstanceToLowerCase, str); },
  97. replace(str, pattern, replacement) { return callInstanceFunc(StringInstanceReplace, str, pattern, replacement); },
  98. split(str, pattern) { return callInstanceFunc(platform.builtInJavascriptStringEntrySplit, str, pattern); },
  99. substring(str, start, end) { return callInstanceFunc(platform.builtInJavascriptStringEntrySubstring, str, start, end); },
  100. stringIndexOf(str, el, from) { return callInstanceFunc(platform.builtInJavascriptStringEntryIndexOf, str, el, from); },
  101. match(str, re) { return platform.builtInRegexMatch(str, re); },
  102. repeat(str, count) { return callInstanceFunc(platform.builtInJavascriptStringEntryRepeat, str, count); },
  103. forEach(array, func) { return callInstanceFunc(ArrayInstanceForEach, array, func); },
  104. push(array, ...els) { return callInstanceFunc(ArrayInstancePush, array, ...els); },
  105. join(array, sep) { return callInstanceFunc(ArrayInstanceJoin, array, sep); },
  106. arrayIndexOf(array, el, from) { return callInstanceFunc(ArrayInstanceIndexOf, array, el, from); },
  107. map(array, func) { return callInstanceFunc(platform.builtInJavascriptArrayEntryMap, array, func); },
  108. reduce(array, func, init) { return callInstanceFunc(platform.builtInJavascriptArrayEntryReduce, array, func, init); },
  109. slice(array, start, end) { return callInstanceFunc(platform.builtInJavascriptArrayEntrySlice, array, start, end); },
  110. concat(array, ...els) { return callInstanceFunc(platform.builtInJavascriptArrayEntryConcat, array, ...els); },
  111. filter(array, func) { return callInstanceFunc(platform.builtInJavascriptArrayEntryFilter, array, func); },
  112. unique(array) { return _.filter(array, (v, i) => _.arrayIndexOf(array, v) === i); },
  113. keys: platform.builtInJavascriptObjectEntryKeys,
  114. hasOwnProperty(o, prop) { return callInstanceFunc(platform.builtInJavascriptObjectEntryHasOwnProperty, o, prop); },
  115. // If we don't set the descriptor's prototype to null, defining properties with `value`s can fail of Object.prototype.get is defined
  116. defineProperty(o, prop, desc) {
  117. _.setPrototypeOf(desc, null);
  118. platform.builtInJavascriptObjectEntryDefineProperty(o, prop, desc);
  119. },
  120. isExtensible: platform.builtInJavascriptObjectEntryIsExtensible,
  121. create(proto = null) { return platform.builtInJavascriptObjectCreate(proto); },
  122. setPrototypeOf(target, proto = null) { return platform.builtInSetPrototype(target, proto); },
  123. abs: platform.builtInMathAbs,
  124. // Make _.floor more like ECMA262 #sec-mathematical-operations' floor by normalizing -0
  125. floor(x) { return x === 0 ? 0 : platform.builtInMathFloor(x) },
  126. max: platform.builtInMathMax,
  127. pow: platform.builtInMathPow,
  128. isFinite: platform.builtInGlobalObjectEntryIsFinite,
  129. isNaN: platform.builtInGlobalObjectEntryIsNaN,
  130. getDate(date) { return callInstanceFunc(platform.builtInJavascriptDateEntryGetDate, date); },
  131. bind(func, that) { return callInstanceFunc(platform.builtInJavascriptFunctionEntryBind, func, that); },
  132. apply(func, that, args) { return callInstanceFunc(platform.builtInJavascriptFunctionEntryApply, func, that, args); },
  133. };
  134. const raise = {
  135. rangeError() { return arguments.length === 3 ? platform.raiseOptionValueOutOfRange_3(...arguments) : platform.raiseOptionValueOutOfRange(); },
  136. assert(test, err) { return test ? undefined : platform.raiseAssert(err || new Error("Assert failed")); }
  137. };
  138. // Keep these "enums" in sync with lib/Runtime/PlatformAgnostic/Intl.h
  139. const CollatorSensitivity = bare({
  140. base: 0,
  141. accent: 1,
  142. case: 2,
  143. variant: 3,
  144. DEFAULT: 3
  145. });
  146. const CollatorCaseFirst = bare({
  147. upper: 0,
  148. lower: 1,
  149. false: 2,
  150. DEFAULT: 2
  151. });
  152. const NumberFormatStyle = bare({
  153. DEFAULT: 0, // "decimal" is the default
  154. DECIMAL: 0, // Intl.NumberFormat(locale, { style: "decimal" }); // aka in our code as "number"
  155. PERCENT: 1, // Intl.NumberFormat(locale, { style: "percent" });
  156. CURRENCY: 2, // Intl.NumberFormat(locale, { style: "currency", ... });
  157. });
  158. const NumberFormatCurrencyDisplay = bare({
  159. DEFAULT: 0, // "symbol" is the default
  160. SYMBOL: 0, // Intl.NumberFormat(locale, { style: "currency", currencyDisplay: "symbol" }); // e.g. "$" or "US$" depeding on locale
  161. CODE: 1, // Intl.NumberFormat(locale, { style: "currency", currencyDisplay: "code" }); // e.g. "USD"
  162. NAME: 2, // Intl.NumberFormat(locale, { style: "currency", currencyDisplay: "name" }); // e.g. "US dollar"
  163. });
  164. const toEnum = function (enumObject, key) {
  165. if (!key || typeof key !== "string") {
  166. return enumObject.DEFAULT;
  167. } else {
  168. return enumObject[key];
  169. }
  170. }
  171. // When this file was originally written, it assumed Windows Globalization semantics.
  172. // Throughout the transition to ICU, we tried to share as much code as possible between WinGlob and ICU.
  173. // However, because ICU has different semantics and our ICU-based implementation tries to match a newer
  174. // version of the Intl spec, we have decided that the code sharing was causing more harm than good.
  175. // Thus, while we support both ICU and WinGlob, we have decided to duplicate a substantial amount of code.
  176. // The indentation of the below if block is intentionally incorrect so as to minimize diff.
  177. if (isPlatformUsingICU) {
  178. let __defaultLocale = undefined;
  179. const GetDefaultLocale = function () {
  180. if (__defaultLocale && platform.useCaches) {
  181. return __defaultLocale;
  182. }
  183. const locale = platform.getDefaultLocale();
  184. if (!locale) {
  185. // if the system locale is undefined/null/empty string, we have to
  186. // do something or else we will crash
  187. __defaultLocale = "en";
  188. } else {
  189. __defaultLocale = locale;
  190. }
  191. return __defaultLocale;
  192. };
  193. // A helper function that is meant to rethrow SOE and OOM exceptions allowing them to propagate.
  194. var throwExIfOOMOrSOE = function (ex) {
  195. if (ex.number === -2146828260 || ex.number === -2146828281) {
  196. throw ex;
  197. }
  198. };
  199. var tagPublicFunction = function (name, f) {
  200. return platform.tagPublicLibraryCode(f, name);
  201. };
  202. const createPublicMethod = function (name, f) {
  203. return platform.tagPublicLibraryCode(f, name, false);
  204. }
  205. const OrdinaryCreateFromConstructor = function (constructor, intrinsicDefaultProto) {
  206. let proto = constructor.prototype;
  207. if (typeof proto !== "object") {
  208. proto = intrinsicDefaultProto;
  209. }
  210. return _.create(proto);
  211. };
  212. /**
  213. * Determines the best possible locale available in the system
  214. *
  215. * ECMA-402: #sec-bestavailablelocale
  216. *
  217. * @param {Function} isAvailableLocale A function that takes a locale and returns if the locale is supported
  218. * @param {String} locale the locale (including its fallbacks) that will be searched for
  219. * @returns {String} the given locale or one of its fallbacks, or undefined
  220. */
  221. const BestAvailableLocale = function (isAvailableLocale, locale) {
  222. if (locale === undefined) {
  223. return undefined;
  224. }
  225. let candidate = locale;
  226. const hyphen = "-";
  227. while (true) {
  228. if (isAvailableLocale(candidate)) {
  229. return candidate;
  230. }
  231. let pos = -1;
  232. for (let i = candidate.length - 1; i >= 0; i--) {
  233. if (candidate[i] === hyphen) {
  234. pos = i;
  235. break;
  236. }
  237. }
  238. if (pos === -1) {
  239. return undefined;
  240. } else if (pos >= 2 && candidate[pos - 2] === hyphen) {
  241. // This is spec code likely intended to skip over singletons,
  242. // such that if we just searched for "en-a-value",
  243. // pos would initially truncate the candidate to "en-a", which
  244. // is not a valid language tag.
  245. // See https://tools.ietf.org/html/rfc5646#section-4.4.2
  246. pos -= 2;
  247. }
  248. candidate = _.substring(candidate, 0, pos);
  249. }
  250. };
  251. /**
  252. * Returns an array of acceptable values for a given key in a given locale. It is expected that
  253. * locale is one that has already been validated by platform.is*LocaleAvailable and key is limited
  254. * to the [[RelevantExtensionKeys]] of Collator, NumberFormat, and DateTimeFormat.
  255. *
  256. * ECMA402: #sec-internal-slots ([[SortLocaleData]], [[SearchLocaleData]], and [[LocaleData]])
  257. *
  258. * @param {String} key a unicode extension key like "co", "ca", etc
  259. * @param {String} locale the locale for which to get the given key's data
  260. * @returns {String[]}
  261. */
  262. const getKeyLocaleData = function (key, locale) {
  263. // NOTE: keep this enum in sync with `enum class LocaleDataKind` in IntlEngineInterfaceExtensionObject.cpp
  264. const LocaleDataKind = {
  265. co: 0,
  266. kf: 1,
  267. kn: 2,
  268. ca: 3,
  269. nu: 4,
  270. hc: 5,
  271. };
  272. const keyLocaleData = platform.getLocaleData(LocaleDataKind[key], locale);
  273. return keyLocaleData;
  274. };
  275. /**
  276. * Determines which locale (or fallback) to use of an array of locales.
  277. *
  278. * ECMA-402: #sec-lookupmatcher
  279. *
  280. * @param {Function} isAvailableLocale A function that takes a locale and returns if the locale is supported
  281. * @param {String[]} requestedLocales An array of requested locales
  282. */
  283. const LookupMatcher = function (isAvailableLocale, requestedLocales) {
  284. const result = _.create();
  285. for (let i = 0; i < requestedLocales.length; ++i) {
  286. const parsedLangtag = parseLangtag(requestedLocales[i]);
  287. if (parsedLangtag === null) {
  288. continue;
  289. }
  290. const availableLocale = BestAvailableLocale(isAvailableLocale, parsedLangtag.base);
  291. if (availableLocale !== undefined) {
  292. result.locale = availableLocale;
  293. if (requestedLocales[i] !== parsedLangtag.base) {
  294. result.extension = parsedLangtag.unicodeExtension;
  295. }
  296. return result;
  297. }
  298. }
  299. result.locale = GetDefaultLocale();
  300. return result;
  301. };
  302. const BestFitMatcher = LookupMatcher;
  303. /**
  304. * Determine a value for a given key in the given extension string
  305. *
  306. * ECMA-402: #sec-unicodeextensionvalue
  307. *
  308. * @param {String} extension the full unicode extension, such as "-u-co-phonebk-kf-true"
  309. * @param {String} key the specific key we are looking for in the extension, such as "co"
  310. */
  311. const UnicodeExtensionValue = function (extension, key) {
  312. raise.assert(key.length === 2);
  313. const size = extension.length;
  314. // search for the key-value pair
  315. let pos = _.stringIndexOf(extension, `-${key}-`);
  316. if (pos !== -1) {
  317. const start = pos + 4;
  318. let end = start;
  319. let k = start;
  320. let done = false;
  321. while (!done) {
  322. const e = _.stringIndexOf(extension, "-", k);
  323. const len = e === -1 ? size - k : e - k;
  324. if (len === 2) {
  325. done = true;
  326. } else if (e === -1) {
  327. end = size;
  328. done = true;
  329. } else {
  330. end = e;
  331. k = e + 1;
  332. }
  333. }
  334. return _.substring(extension, start, end);
  335. }
  336. // search for the key with no associated value
  337. pos = _.stringIndexOf(extension, `-${key}`);
  338. if (pos !== -1 && pos + 3 === size) {
  339. return "";
  340. } else {
  341. return undefined;
  342. }
  343. };
  344. /**
  345. * Resolves a locale by finding which base locale or fallback is available on the system,
  346. * then determines which provided unicode options are available for that locale.
  347. *
  348. * ECMA-402: #sec-resolvelocale
  349. *
  350. * @param {Function} isAvailableLocale A function that takes a locale and returns if the locale is supported
  351. * @param {String[]} requestedLocales The result of calling CanonicalizeLocaleList on the user-requested locale array
  352. * @param {Object} options An object containing a lookupMatcher value and any value given by the user's option object,
  353. * mapped to the correct unicode extension key
  354. * @param {String[]} relevantExtensionKeys An array of unicode extension keys that we care about for the current lookup
  355. */
  356. const ResolveLocale = function (isAvailableLocale, requestedLocales, options, relevantExtensionKeys) {
  357. const matcher = options.lookupMatcher;
  358. let r;
  359. if (matcher === "lookup") {
  360. r = LookupMatcher(isAvailableLocale, requestedLocales);
  361. } else {
  362. r = BestFitMatcher(isAvailableLocale, requestedLocales);
  363. }
  364. let foundLocale = r.locale;
  365. const result = bare({ dataLocale: foundLocale });
  366. let supportedExtension = "-u";
  367. _.forEach(relevantExtensionKeys, function (key) {
  368. const keyLocaleData = getKeyLocaleData(key, foundLocale);
  369. let value = keyLocaleData[0];
  370. let supportedExtensionAddition = "";
  371. if (r.extension) {
  372. const requestedValue = UnicodeExtensionValue(r.extension, key);
  373. if (requestedValue !== undefined) {
  374. if (requestedValue !== "") {
  375. if (_.arrayIndexOf(keyLocaleData, requestedValue) !== -1) {
  376. value = requestedValue;
  377. supportedExtensionAddition = `-${key}-${value}`;
  378. }
  379. } else if (_.arrayIndexOf(keyLocaleData, "true") !== -1) {
  380. value = "true";
  381. }
  382. }
  383. }
  384. if (_.hasOwnProperty(options, key)) {
  385. const optionsValue = options[key];
  386. if (_.arrayIndexOf(keyLocaleData, optionsValue) !== -1) {
  387. if (optionsValue !== value) {
  388. value = optionsValue;
  389. supportedExtensionAddition = "";
  390. }
  391. }
  392. }
  393. result[key] = value;
  394. supportedExtension += supportedExtensionAddition;
  395. });
  396. if (supportedExtension.length > 2) {
  397. const privateIndex = _.stringIndexOf(foundLocale, "-x-");
  398. if (privateIndex === -1) {
  399. foundLocale += supportedExtension;
  400. } else {
  401. const preExtension = _.substring(foundLocale, 0, privateIndex);
  402. const postExtension = _.substring(foundLocale, privateIndex);
  403. foundLocale = preExtension + supportedExtension + postExtension;
  404. }
  405. foundLocale = platform.normalizeLanguageTag(foundLocale);
  406. }
  407. result.locale = foundLocale;
  408. return result;
  409. };
  410. var Internal = bare({
  411. ToObject(o) {
  412. if (o === null) {
  413. platform.raiseNeedObject();
  414. }
  415. return o !== undefined ? Object(o) : undefined;
  416. },
  417. ToString(s) {
  418. return s !== undefined ? String(s) : undefined;
  419. },
  420. ToNumber(n) {
  421. return n !== undefined ? Number(n) : NaN;
  422. },
  423. ToLogicalBoolean(v) {
  424. return v !== undefined ? Boolean(v) : undefined;
  425. },
  426. ToInteger(n) {
  427. const number = Number(n);
  428. if (isNaN(number)) {
  429. return 0;
  430. } else if (number === 0 || !isFinite(number)) {
  431. return number;
  432. }
  433. const ret = _.floor(_.abs(number));
  434. if (number < 0) {
  435. return -ret
  436. } else {
  437. return ret;
  438. }
  439. },
  440. ToLength(n) {
  441. const len = Internal.ToInteger(n);
  442. if (len <= 0) {
  443. return 0;
  444. }
  445. const max = _.pow(2, 53) - 1;
  446. return max < len ? max : len;
  447. }
  448. });
  449. // Internal ops implemented in JS:
  450. function GetOption(options, property, type, values, fallback) {
  451. let value = options[property];
  452. if (value !== undefined) {
  453. if (type == "boolean") {
  454. value = Internal.ToLogicalBoolean(value);
  455. }
  456. if (type == "string") {
  457. value = Internal.ToString(value);
  458. }
  459. if (type == "number") {
  460. value = Internal.ToNumber(value);
  461. }
  462. if (values !== undefined && _.arrayIndexOf(values, value) == -1) {
  463. platform.raiseOptionValueOutOfRange_3(String(value), String(property), `['${_.join(values, "', '")}']`);
  464. }
  465. return value;
  466. }
  467. return fallback;
  468. }
  469. /**
  470. * Extracts the value of the property named property from the provided options object,
  471. * converts it to a Number value, checks whether it is in the allowed range,
  472. * and fills in a fallback value if necessary.
  473. *
  474. * NOTE: this has known differences compared to the spec GetNumberOption in order to
  475. * support more verbose errors. It is more similar to DefaultNumberOption
  476. *
  477. * ECMA402: #sec-defaultnumberoption
  478. *
  479. * @param {Object} options user-provided options object
  480. * @param {String} property the property we are trying to get off of `options`
  481. * @param {Number} minimum minimum allowable value for options[property]
  482. * @param {Number} maximum maximum allowable value for options[property]
  483. * @param {Number} fallback return value if options[property] is undefined or invalid
  484. * @returns {Number}
  485. */
  486. const GetNumberOption = function (options, property, minimum, maximum, fallback) {
  487. let value = options[property];
  488. if (value !== undefined) {
  489. value = Internal.ToNumber(value);
  490. if (_.isNaN(value) || value < minimum || value > maximum) {
  491. platform.raiseOptionValueOutOfRange_3(String(value), property, `[${minimum} - ${maximum}]`);
  492. }
  493. return _.floor(value);
  494. }
  495. return fallback;
  496. };
  497. let CURRENCY_CODE_RE;
  498. function InitializeCurrencyRegExp() {
  499. CURRENCY_CODE_RE = /^[A-Z]{3}$/i;
  500. }
  501. /**
  502. * Returns an object representing the language, script, region, extension, and base of a language tag,
  503. * or null if the language tag isn't valid.
  504. *
  505. * @param {String} langtag a candidate BCP47 langtag
  506. */
  507. const parseLangtag = (function () {
  508. // Language Tag Syntax as described in RFC 5646 #section-2.1
  509. // Note: All language tags are comprised only of ASCII characters (makes our job easy here)
  510. // Note: Language tags in canonical form have case conventions, but language tags are case-insensitive for our purposes
  511. // Note: The ABNF syntax used in RFC 5646 #section-2.1 uses the following numeric quantifier conventions:
  512. // - (Parentheses) are used for grouping
  513. // - PRODUCTION => exactly 1 of PRODUCTION /PRODUCTION/
  514. // - [PRODUCTION] => 0 or 1 of PRODUCTION /(PRODUCTION)?/
  515. // - #PRODUCTION => exactly # of PRODUCTION /(PRODUCTION){#}/
  516. // - a*bPRODUCTION (where a and b are optional)
  517. // - *PRODUCTION => any number of PRODUCTION /(PRODUCTION)*/
  518. // - 1*PRODUCTION => 1 or more of PRODUCTION /(PRODUCTION)+/
  519. // - #*PRODUCTION => # or more of PRODUCTION /(PRODUCTION){#,}/
  520. // - *#PRODUCTION => 0 to # (inclusive) of PRODUCTION /(PRODUCTION){,#}/ or /(PRODUCTION){0,#}/
  521. // - a*bPRODUCTION => a to b (inclusive) of PRODUCTION /(PRODUCTION){a,b}/
  522. const ALPHA = "[A-Z]";
  523. const DIGIT = "[0-9]";
  524. const alphanum = `(?:${ALPHA}|${DIGIT})`;
  525. const regular = "\\b(?:art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang)\\b";
  526. const irregular = "\\b(?:en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo" +
  527. "|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)\\b";
  528. const grandfathered = `\\b(?:${regular}|${irregular})\\b`;
  529. const privateuse = `\\b(?:x(?:-${alphanum}{1,8}\\b)+)\\b`; // privateuse = "x" 1*("-" (1*8alphanum))
  530. const singleton = `\\b(?:${DIGIT}|[A-WY-Z])\\b`; // singleton ~= alphanum except for 'x' ; (paraphrased)
  531. const extension = `\\b(?:${singleton}(?:-${alphanum}{2,8})+)\\b`; // extension = singleton 1*("-" (2*8alphanum))
  532. const variant = `\\b(?:${alphanum}{5,8}|${DIGIT}${alphanum}{3})\\b`; // variant = 5*8alphanum / (DIGIT 3alphanum)
  533. const region = `\\b(?:${ALPHA}{2}|${DIGIT}{3})\\b`; // region = 2ALPHA / 3DIGIT
  534. const script = `\\b(?:${ALPHA}{4})\\b`; // script = 4ALPHA
  535. const extlang = `\\b(?:${ALPHA}{3}\\b(?:-${ALPHA}{3}){0,2})\\b`; // extlang = 3ALPHA *2("-" 3ALPHA)
  536. const language = '\\b(?:' + // language =
  537. `${ALPHA}{2,3}` + // 2*3ALPHA ; shortest ISO 639 code
  538. `\\b(?:-${extlang})?` + // ["-" extlang] ; sometimes followed by extended language subtags
  539. // `|${ALPHA}{4}` + // / 4ALPHA ; or reserved for future use
  540. // `|${ALPHA}{5,8}` + // / 5*8ALPHA ; or registered language subtag
  541. `|${ALPHA}{4,8}` + // ~/ 4*8ALPHA ; (paraphrased: combined previous two lines)
  542. ')\\b';
  543. // Use matching groups only when needed
  544. const LANG_TAG_BASE = `\\b(${language})\\b` + // langtag = language
  545. `\\b(?:-(${script}))?\\b` + // ["-" script]
  546. `\\b(?:-(${region}))?\\b` ; // ["-" region]
  547. const LANG_TAG_EXT = `\\b((?:-${variant})*)\\b` + // *("-" variant)
  548. `\\b((?:-${extension})*)\\b` + // *("-" extension)
  549. `\\b(?:-${privateuse})?\\b` ; // ["-" privateuse]
  550. const langtag = `\\b${LANG_TAG_BASE}\\b${LANG_TAG_EXT}\\b`;
  551. const LANG_TAG = `\\b(?:${langtag}|${privateuse}|${grandfathered})\\b`; // Language-Tag = ...
  552. // Use ^ and $ to enforce that the entire input string is a langtag
  553. const LANG_TAG_BASE_RE = new RegExp(`^${LANG_TAG_BASE}$`, 'i'); // [1] language; [2] script; [3] region;
  554. const LANG_TAG_EXT_RE = new RegExp(`^${LANG_TAG_EXT}$`, 'i'); // [1] variants; [2] extensions;
  555. const LANG_TAG_RE = new RegExp(`^${LANG_TAG}$`, 'i'); // [1] language; [2] script; [3] region; [4] variants; [5] extensions;
  556. const parsedLangtagCache = new IntlCache();
  557. return function (langtag) {
  558. const cached = parsedLangtagCache.get(langtag);
  559. if (cached) {
  560. return cached;
  561. }
  562. const parts = _.match(langtag, LANG_TAG_RE);
  563. if (!parts) {
  564. return null;
  565. }
  566. const ret = _.create();
  567. ret.language = parts[1];
  568. ret.base = parts[1];
  569. if (parts[2]) {
  570. ret.script = parts[2];
  571. ret.base += "-" + parts[2];
  572. }
  573. if (parts[3]) {
  574. ret.region = parts[3];
  575. ret.base += "-" + parts[3];
  576. }
  577. if (parts[4]) {
  578. ret.variants = parts[4];
  579. }
  580. if (parts[5]) {
  581. ret.extensions = parts[5];
  582. // parse the extension to find the unicode (-u) extension
  583. const extensionParts = _.split(parts[5], "-");
  584. for (let i = 0; i < extensionParts.length; ++i) {
  585. if (extensionParts[i] !== "u") {
  586. continue;
  587. }
  588. let k;
  589. for (k = i + 1; k < extensionParts.length && extensionParts[k].length > 1; k++) {
  590. // do nothing, we just want k to equal the index of the next element whose length is 1
  591. // or to equal the length of extensionParts
  592. // We could have done this with Array.prototype.findIndex too
  593. }
  594. if (k > i + 1) {
  595. // this creates u-(keys and values)*, which is good enough for the UnicodeExtensionValue,
  596. // which is the only place that this return value is intended to be used
  597. ret.unicodeExtension = _.join(_.slice(extensionParts, i, k), "-");
  598. }
  599. // if we have gotten this far, we have found -u-{values}, so we can break
  600. break;
  601. }
  602. }
  603. parsedLangtagCache.set(langtag, ret);
  604. return ret;
  605. };
  606. })();
  607. const IsWellFormedCurrencyCode = function (code) {
  608. code = Internal.ToString(code);
  609. if (!CURRENCY_CODE_RE) {
  610. InitializeCurrencyRegExp();
  611. }
  612. return platform.builtInRegexMatch(code, CURRENCY_CODE_RE) !== null;
  613. }
  614. /**
  615. * Returns true if locale can be generated by RFC5646 section 2.1 and does not contain
  616. * duplicate variant or singleton subtags.
  617. *
  618. * Note that ICU does not implement this correctly for our usage because it is
  619. * extremely permissive about what it will allow -- completely invalid language tags can
  620. * pass through a round of uloc_forLanguageTag/uloc_toLanguageTag or uloc_canonicalize
  621. * even if they are completely bogus.
  622. *
  623. * ECMA402: #sec-isstructurallyvalidlanguagetag
  624. *
  625. * @param {String} locale The locale to check
  626. * @returns {Boolean}
  627. */
  628. const IsStructurallyValidLanguageTag = function (locale) {
  629. const parsed = parseLangtag(locale);
  630. if (parsed === null) {
  631. return false;
  632. }
  633. // check duplicate variants
  634. if (parsed.variants) {
  635. const variants = _.split(parsed.variants, "-");
  636. const uniqueVariants = _.unique(variants);
  637. if (variants.length !== uniqueVariants.length) {
  638. return false;
  639. }
  640. }
  641. if (parsed.extensions) {
  642. const extensionParts = _.split(parsed.extensions, "-");
  643. const singletons = _.map(_.filter(extensionParts, (element) => element.length === 1), (element) => _.toLowerCase(element));
  644. const uniqueSingletons = _.unique(singletons);
  645. return singletons.length === uniqueSingletons.length;
  646. }
  647. return true;
  648. };
  649. /**
  650. * Given a locale or list of locales, returns a corresponding list where each locale
  651. * is guaranteed to be "canonical" (proper capitalization, order, etc.).
  652. *
  653. * ECMA402: #sec-canonicalizelocalelist
  654. *
  655. * @param {String|String[]} locales the user-provided locales to be canonicalized
  656. */
  657. const CanonicalizeLocaleList = function (locales) {
  658. if (typeof locales === "undefined") {
  659. return [];
  660. }
  661. const seen = [];
  662. const O = typeof locales === "string" ? [locales] : Internal.ToObject(locales);
  663. const len = Internal.ToLength(O.length);
  664. let k = 0;
  665. while (k < len) {
  666. const Pk = Internal.ToString(k);
  667. if (Pk in O) {
  668. const kValue = O[Pk];
  669. if ((typeof kValue !== "string" && typeof kValue !== "object") || kValue === null) {
  670. platform.raiseNeedObjectOrString("locale");
  671. }
  672. const tag = Internal.ToString(kValue);
  673. if (!IsStructurallyValidLanguageTag(tag)) {
  674. platform.raiseLocaleNotWellFormed(tag);
  675. }
  676. const canonicalizedTag = platform.normalizeLanguageTag(tag);
  677. if (canonicalizedTag === undefined) {
  678. // See comment in platform.normalizeLanguageTag about when this happens
  679. platform.raiseLocaleNotWellFormed(tag);
  680. } else if (_.arrayIndexOf(seen, canonicalizedTag) === -1) {
  681. _.push(seen, canonicalizedTag);
  682. }
  683. }
  684. k += 1;
  685. }
  686. return seen;
  687. };
  688. /**
  689. * Returns the subset of requestedLocales that has a matching locale according to BestAvailableLocale.
  690. *
  691. * ECMA402: #sec-lookupsupportedlocales
  692. *
  693. * @param {Function} isAvailableLocale A function that takes a locale and returns if the locale is supported
  694. * @param {String|String[]} requestedLocales
  695. */
  696. const LookupSupportedLocales = function (isAvailableLocale, requestedLocales) {
  697. const subset = [];
  698. _.forEach(requestedLocales, function (locale) {
  699. const noExtensionsLocale = parseLangtag(locale).base;
  700. if (BestAvailableLocale(isAvailableLocale, noExtensionsLocale) !== undefined) {
  701. _.push(subset, locale);
  702. }
  703. });
  704. return subset;
  705. };
  706. const BestFitSupportedLocales = LookupSupportedLocales;
  707. /**
  708. * Applies digit options used for number formatting onto the given intlObj
  709. *
  710. * This function is used by both NumberFormat and PluralRules, despite being defined
  711. * as a NumberFormat abstract operation
  712. *
  713. * ECMA 402: #sec-setnfdigitoptions
  714. *
  715. * @param {Object} intlObj The state object of either a NumberFormat or PluralRules on which to set the resolved number options
  716. * @param {Object} options The option object to pull min/max sigfigs, fraction digits, and integer digits
  717. * @param {Number} mnfdDefault The default minimumFractionDigits
  718. * @param {Number} mxfdDefault The default maximumFractionDigits
  719. */
  720. const SetNumberFormatDigitOptions = function (intlObj, options, mnfdDefault, mxfdDefault) {
  721. const mnid = GetNumberOption(options, "minimumIntegerDigits", 1, 21, 1);
  722. const mnfd = GetNumberOption(options, "minimumFractionDigits", 0, 20, mnfdDefault);
  723. const mxfdActualDefault = _.max(mnfd, mxfdDefault);
  724. const mxfd = GetNumberOption(options, "maximumFractionDigits", mnfd, 20, mxfdActualDefault);
  725. intlObj.minimumIntegerDigits = mnid;
  726. intlObj.minimumFractionDigits = mnfd;
  727. intlObj.maximumFractionDigits = mxfd;
  728. let mnsd = options.minimumSignificantDigits;
  729. let mxsd = options.maximumSignificantDigits;
  730. if (mnsd !== undefined || mxsd !== undefined) {
  731. // don't read options.minimumSignificantDigits below in order to pass
  732. // test262/test/intl402/NumberFormat/significant-digits-options-get-sequence.js
  733. mnsd = GetNumberOption({ minimumSignificantDigits: mnsd }, "minimumSignificantDigits", 1, 21, 1);
  734. mxsd = GetNumberOption({ maximumSignificantDigits: mxsd }, "maximumSignificantDigits", mnsd, 21, 21);
  735. intlObj.minimumSignificantDigits = mnsd;
  736. intlObj.maximumSignificantDigits = mxsd;
  737. }
  738. };
  739. /**
  740. * Returns the subset of requestedLocales that has a matching locale, according to
  741. * options.localeMatcher and isAvailableLocale.
  742. *
  743. * ECMA402: #sec-supportedlocales
  744. *
  745. * @param {Function} isAvailableLocale A function that takes a locale and returns if the locale is supported
  746. * @param {String|String[]} requestedLocales
  747. * @param {Object} options
  748. */
  749. const SupportedLocales = function (isAvailableLocale, requestedLocales, options) {
  750. const matcher = options === undefined
  751. ? "best fit"
  752. : GetOption(Internal.ToObject(options), "localeMatcher", "string", ["best fit", "lookup"], "best fit");
  753. const supportedLocales = matcher === "best fit"
  754. ? BestFitSupportedLocales(isAvailableLocale, requestedLocales)
  755. : LookupSupportedLocales(isAvailableLocale, requestedLocales);
  756. for (let i = 0; i < supportedLocales.length; i++) {
  757. _.defineProperty(supportedLocales, Internal.ToString(i), { configurable: false, writable: false });
  758. }
  759. // test262 supportedLocalesOf-returned-array-elements-are-frozen.js:
  760. // Property length of object returned by SupportedLocales should not be writable
  761. _.defineProperty(supportedLocales, "length", {
  762. writable: false,
  763. configurable: false,
  764. enumerable: false,
  765. });
  766. return supportedLocales;
  767. };
  768. if (InitType === "Intl") {
  769. const getCanonicalLocales = createPublicMethod("Intl.getCanonicalLocales", function getCanonicalLocales(locales) {
  770. return CanonicalizeLocaleList(locales);
  771. });
  772. _.defineProperty(Intl, "getCanonicalLocales", {
  773. value: getCanonicalLocales,
  774. writable: true,
  775. enumerable: false,
  776. configurable: true
  777. });
  778. }
  779. /**
  780. * Creates an object to be returned out of resolvedOptions() methods that avoids being tainted by Object.prototype
  781. *
  782. * @param {String[]} props The list of properties to extract from hiddenObject and add to the final resolved options
  783. * @param {Object} hiddenObject The hiddenObject of the calling constructor that contains values for each prop in props
  784. * @param {Function} func An optional custom function(prop, resolved) run for each prop; it should return true when
  785. * it handles a property itself. If it does not return true, the default logic will be used.
  786. */
  787. const createResolvedOptions = function (props, hiddenObject, func = null) {
  788. const resolved = _.create();
  789. _.forEach(props, function (prop) {
  790. if (func !== null && func(prop, resolved) === true) {
  791. // the callback returned true, which means this property was handled and we can go to the next one
  792. return;
  793. }
  794. if (typeof hiddenObject[prop] !== "undefined") {
  795. resolved[prop] = hiddenObject[prop];
  796. }
  797. });
  798. return _.setPrototypeOf(resolved, platform.Object_prototype);
  799. };
  800. // Intl.Collator, String.prototype.localeCompare
  801. const Collator = (function () {
  802. if (InitType !== "Intl" && InitType !== "String") {
  803. return;
  804. }
  805. const InitializeCollator = function (collator, locales, options) {
  806. const requestedLocales = CanonicalizeLocaleList(locales);
  807. options = options === undefined ? _.create() : Internal.ToObject(options);
  808. collator.usage = GetOption(options, "usage", "string", ["sort", "search"], "sort");
  809. // TODO: determine the difference between sort and search locale data
  810. // const collatorLocaleData = collator.usage === "sort" ? localeData : localeData;
  811. const opt = _.create();
  812. opt.matcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit");
  813. let kn = GetOption(options, "numeric", "boolean", undefined, undefined);
  814. opt.kn = kn === undefined ? kn : Internal.ToString(kn);
  815. opt.kf = GetOption(options, "caseFirst", "string", ["upper", "lower", "false"], undefined);
  816. const r = ResolveLocale(platform.isCollatorLocaleAvailable, requestedLocales, opt, ["co", "kn", "kf"]);
  817. collator.locale = r.locale;
  818. collator.collation = r.co === null ? "default" : r.co;
  819. collator.numeric = r.kn === "true";
  820. collator.caseFirst = r.kf;
  821. collator.caseFirstEnum = toEnum(CollatorCaseFirst, collator.caseFirst);
  822. collator.sensitivity = GetOption(options, "sensitivity", "string", ["base", "accent", "case", "variant"], "variant");
  823. collator.sensitivityEnum = toEnum(CollatorSensitivity, collator.sensitivity);
  824. collator.ignorePunctuation = GetOption(options, "ignorePunctuation", "boolean", undefined, false);
  825. collator.initializedCollator = true;
  826. return collator;
  827. };
  828. let localeCompareStateCache;
  829. // Make arguments undefined to ensure that localeCompare.length === 1
  830. platform.registerBuiltInFunction(createPublicMethod("String.prototype.localeCompare", function localeCompare(that, locales = undefined, options = undefined) {
  831. if (this === undefined || this === null) {
  832. platform.raiseThis_NullOrUndefined("String.prototype.localeCompare");
  833. }
  834. const thisStr = String(this);
  835. const thatStr = String(that);
  836. // Performance optimization to cache the state object and UCollator when the default arguments are provided
  837. // TODO(jahorto): investigate caching when locales and/or options are provided
  838. let stateObject;
  839. if (locales === undefined && options === undefined) {
  840. if (localeCompareStateCache === undefined) {
  841. localeCompareStateCache = _.create();
  842. InitializeCollator(localeCompareStateCache, undefined, undefined);
  843. }
  844. stateObject = localeCompareStateCache;
  845. } else {
  846. stateObject = _.create();
  847. InitializeCollator(stateObject, locales, options);
  848. }
  849. return platform.localeCompare(thisStr, thatStr, stateObject, /* forStringPrototypeLocaleCompare */ true);
  850. }), IntlBuiltInFunctionID.StringLocaleCompare);
  851. // If we were only initializing Intl for String.prototype, don't initialize Intl.Collator
  852. if (InitType === "String") {
  853. return;
  854. }
  855. const CollatorPrototype = {};
  856. const Collator = tagPublicFunction("Intl.Collator", function Collator(locales = undefined, options = undefined) {
  857. const newTarget = new.target === undefined ? Collator : new.target;
  858. const collator = OrdinaryCreateFromConstructor(newTarget, CollatorPrototype);
  859. // Use the hidden object to store data
  860. let hiddenObject = platform.getHiddenObject(collator);
  861. if (hiddenObject === undefined) {
  862. hiddenObject = _.create();
  863. platform.setHiddenObject(collator, hiddenObject);
  864. }
  865. InitializeCollator(hiddenObject, locales, options);
  866. // Add the bound compare
  867. hiddenObject.boundCompare = _.bind(compare, collator);
  868. delete hiddenObject.boundCompare.name;
  869. return collator;
  870. });
  871. const compare = createPublicMethod("Intl.Collator.prototype.compare", function compare(x, y) {
  872. if (typeof this !== "object") {
  873. platform.raiseNeedObjectOfType("Collator.prototype.compare", "Collator");
  874. }
  875. const hiddenObject = platform.getHiddenObject(this);
  876. if (hiddenObject === undefined || !hiddenObject.initializedCollator) {
  877. platform.raiseNeedObjectOfType("Collator.prototype.compare", "Collator");
  878. }
  879. return platform.localeCompare(String(x), String(y), hiddenObject, /* forStringPrototypeLocaleCompare */ false);
  880. });
  881. const supportedLocalesOf = createPublicMethod("Intl.Collator.supportedLocalesOf", function supportedLocalesOf(locales, options = undefined) {
  882. return SupportedLocales(platform.isCollatorLocaleAvailable, CanonicalizeLocaleList(locales), options);
  883. });
  884. _.defineProperty(Collator, "supportedLocalesOf", {
  885. value: supportedLocalesOf,
  886. writable: true,
  887. enumerable: false,
  888. configurable: true,
  889. });
  890. _.defineProperty(Collator, "prototype", {
  891. value: CollatorPrototype,
  892. writable: false,
  893. enumerable: false,
  894. configurable: false
  895. });
  896. _.defineProperty(CollatorPrototype, "constructor", {
  897. value: Collator,
  898. writable: true,
  899. enumerable: false,
  900. configurable: true
  901. });
  902. _.defineProperty(CollatorPrototype, "resolvedOptions", {
  903. value: createPublicMethod("Intl.Collator.prototype.resolvedOptions", function resolvedOptions() {
  904. if (typeof this !== "object") {
  905. platform.raiseNeedObjectOfType("Collator.prototype.resolvedOptions", "Collator");
  906. }
  907. const hiddenObject = platform.getHiddenObject(this);
  908. if (hiddenObject === undefined || !hiddenObject.initializedCollator) {
  909. platform.raiseNeedObjectOfType("Collator.prototype.resolvedOptions", "Collator");
  910. }
  911. const options = [
  912. "locale",
  913. "usage",
  914. "sensitivity",
  915. "ignorePunctuation",
  916. "collation",
  917. "numeric",
  918. "caseFirst",
  919. ];
  920. return createResolvedOptions(options, hiddenObject);
  921. }),
  922. writable: true,
  923. enumerable: false,
  924. configurable: true
  925. });
  926. // test262's test\intl402\Collator\prototype\compare\name.js checks the name of the descriptor's getter function
  927. const getCompare = createPublicMethod("get compare", function () {
  928. if (typeof this !== "object") {
  929. platform.raiseNeedObjectOfType("Collator.prototype.compare", "Collator");
  930. }
  931. const hiddenObject = platform.getHiddenObject(this);
  932. if (hiddenObject === undefined || !hiddenObject.initializedCollator) {
  933. platform.raiseNeedObjectOfType("Collator.prototype.compare", "Collator");
  934. }
  935. return hiddenObject.boundCompare;
  936. });
  937. _.defineProperty(getCompare, "name", {
  938. value: "get compare",
  939. writable: false,
  940. enumerable: false,
  941. configurable: true,
  942. });
  943. _.defineProperty(CollatorPrototype, "compare", {
  944. get: getCompare,
  945. enumerable: false,
  946. configurable: true
  947. });
  948. return Collator;
  949. })();
  950. // Intl.NumberFormat, Number.prototype.toLocaleString
  951. var NumberFormat = (function () {
  952. if (InitType !== "Intl" && InitType !== "Number") {
  953. return;
  954. }
  955. const InitializeNumberFormat = function (nf, locales, options) {
  956. const requestedLocales = CanonicalizeLocaleList(locales);
  957. options = options === undefined ? _.create() : Internal.ToObject(options);
  958. const opt = _.create();
  959. opt.localeMatcher = GetOption(options, "localeMatcher", "string", ["best fit", "lookup"], "best fit");
  960. const r = ResolveLocale(platform.isNFLocaleAvailable, requestedLocales, opt, ["nu"]);
  961. nf.locale = r.locale;
  962. nf.numberingSystem = r.nu;
  963. const style = GetOption(options, "style", "string", ["decimal", "percent", "currency"], "decimal");
  964. nf.style = style;
  965. nf.formatterToUse = toEnum(NumberFormatStyle, _.toUpperCase(style));
  966. const useCurrency = style === "currency";
  967. let currency = GetOption(options, "currency", "string", undefined, undefined);
  968. if (currency !== undefined && !IsWellFormedCurrencyCode(currency)) {
  969. platform.raiseInvalidCurrencyCode(currency);
  970. } else if (currency === undefined && useCurrency) {
  971. platform.raiseMissingCurrencyCode();
  972. }
  973. let cDigits = 0;
  974. if (useCurrency) {
  975. currency = _.toUpperCase(currency);
  976. nf.currency = currency;
  977. cDigits = platform.currencyDigits(currency);
  978. }
  979. let currencyDisplay = GetOption(options, "currencyDisplay", "string", ["code", "symbol", "name"], "symbol");
  980. if (useCurrency) {
  981. nf.currencyDisplay = currencyDisplay
  982. nf.currencyDisplayToUse = toEnum(NumberFormatCurrencyDisplay, _.toUpperCase(currencyDisplay));
  983. }
  984. let mnfdDefault, mxfdDefault;
  985. if (useCurrency) {
  986. mnfdDefault = cDigits;
  987. mxfdDefault = cDigits;
  988. } else {
  989. mnfdDefault = 0;
  990. if (style === "percent") {
  991. mxfdDefault = 0;
  992. } else {
  993. mxfdDefault = 3;
  994. }
  995. }
  996. SetNumberFormatDigitOptions(nf, options, mnfdDefault, mxfdDefault);
  997. nf.useGrouping = GetOption(options, "useGrouping", "boolean", undefined, true);
  998. nf.initializedNumberFormat = true;
  999. // Cache api instance and update numbering system on the object
  1000. platform.cacheNumberFormat(nf);
  1001. return nf;
  1002. };
  1003. platform.registerBuiltInFunction(createPublicMethod("Number.prototype.toLocaleString", function toLocaleString() {
  1004. if (typeof this !== "number" && !(this instanceof Number)) {
  1005. platform.raiseNeedObjectOfType("Number.prototype.toLocaleString", "Number");
  1006. }
  1007. const stateObject = _.create();
  1008. InitializeNumberFormat(stateObject, arguments[0], arguments[1]);
  1009. const n = Internal.ToNumber(this);
  1010. return platform.formatNumber(n, stateObject, /* toParts */ false, /* forNumberPrototypeToLocaleString */ true);
  1011. }), IntlBuiltInFunctionID.NumberToLocaleString);
  1012. if (InitType === "Number") {
  1013. return;
  1014. }
  1015. const NumberFormatPrototype = {};
  1016. const NumberFormat = tagPublicFunction("Intl.NumberFormat", function NumberFormat(locales = undefined, options = undefined) {
  1017. const newTarget = new.target === undefined ? NumberFormat : new.target;
  1018. const numberFormat = OrdinaryCreateFromConstructor(newTarget, NumberFormatPrototype);
  1019. let hiddenObject = platform.getHiddenObject(numberFormat);
  1020. if (hiddenObject === undefined) {
  1021. hiddenObject = _.create();
  1022. platform.setHiddenObject(numberFormat, hiddenObject);
  1023. }
  1024. InitializeNumberFormat(hiddenObject, locales, options);
  1025. if (new.target === undefined && this instanceof NumberFormat) {
  1026. _.defineProperty(this, platform.FallbackSymbol, {
  1027. value: numberFormat,
  1028. writable: false,
  1029. enumerable: false,
  1030. configurable: false
  1031. });
  1032. return this;
  1033. }
  1034. return numberFormat;
  1035. });
  1036. // format should always be bound to a valid NumberFormat's hiddenObject by getFormat()
  1037. const format = createPublicMethod("Intl.NumberFormat.prototype.format", function format(n) {
  1038. n = Internal.ToNumber(n);
  1039. if (!this || !this.initializedNumberFormat) {
  1040. platform.raiseNeedObjectOfType("NumberFormat.prototype.format", "NumberFormat");
  1041. }
  1042. return platform.formatNumber(n, this, /* toParts */ false, /* forNumberPrototypeToLocaleString */ false);
  1043. });
  1044. const formatToParts = createPublicMethod("Intl.NumberFormat.prototype.formatToParts", function formatToParts(n) {
  1045. n = Internal.ToNumber(n);
  1046. if (typeof this !== "object") {
  1047. platform.raiseNeedObjectOfType("NumberFormat.prototype.formatToParts", "NumberFormat");
  1048. }
  1049. const hiddenObject = platform.getHiddenObject(this);
  1050. if (hiddenObject === undefined || !hiddenObject.initializedNumberFormat) {
  1051. platform.raiseNeedObjectOfType("NumberFormat.prototype.formatToParts", "NumberFormat");
  1052. }
  1053. return platform.formatNumber(n, hiddenObject, /* toParts */ true, /* forNumberPrototypeToLocaleString */ false);
  1054. });
  1055. const supportedLocalesOf = createPublicMethod("Intl.NumberFormat.supportedLocalesOf", function supportedLocalesOf(locales, options = undefined) {
  1056. return SupportedLocales(platform.isNFLocaleAvailable, CanonicalizeLocaleList(locales), options);
  1057. });
  1058. _.defineProperty(NumberFormat, "supportedLocalesOf", {
  1059. value: supportedLocalesOf,
  1060. writable: true,
  1061. enumerable: false,
  1062. configurable: true,
  1063. });
  1064. _.defineProperty(NumberFormat, "prototype", {
  1065. value: NumberFormatPrototype,
  1066. writable: false,
  1067. enumerable: false,
  1068. configurable: false
  1069. });
  1070. _.defineProperty(NumberFormatPrototype, "constructor", {
  1071. value: NumberFormat,
  1072. writable: true,
  1073. enumerable: false,
  1074. configurable: true,
  1075. });
  1076. const UnwrapNumberFormat = function (nf) {
  1077. let hiddenObject = platform.getHiddenObject(nf);
  1078. if ((!hiddenObject || !hiddenObject.initializedNumberFormat) && nf instanceof NumberFormat) {
  1079. nf = nf[platform.FallbackSymbol];
  1080. }
  1081. if (typeof nf !== "object") {
  1082. platform.raiseNeedObjectOfType("NumberFormat.prototype.format", "NumberFormat");
  1083. }
  1084. hiddenObject = platform.getHiddenObject(nf);
  1085. if (!hiddenObject.initializedNumberFormat) {
  1086. platform.raiseNeedObjectOfType("NumberFormat.prototype.format", "NumberFormat");
  1087. }
  1088. return hiddenObject;
  1089. };
  1090. _.defineProperty(NumberFormatPrototype, "resolvedOptions", {
  1091. value: createPublicMethod("Intl.NumberFormat.prototype.resolvedOptions", function resolvedOptions() {
  1092. if (typeof this !== "object") {
  1093. platform.raiseNeedObjectOfType("NumberFormat.prototype.format", "NumberFormat");
  1094. }
  1095. const hiddenObject = UnwrapNumberFormat(this);
  1096. const options = ["locale", "numberingSystem", "style", "currency", "currencyDisplay", "minimumIntegerDigits",
  1097. "minimumFractionDigits", "maximumFractionDigits", "minimumSignificantDigits", "maximumSignificantDigits",
  1098. "useGrouping"];
  1099. return createResolvedOptions(options, hiddenObject);
  1100. }),
  1101. writable: true,
  1102. enumerable: false,
  1103. configurable: true,
  1104. });
  1105. // test262's test\intl402\NumberFormat\prototype\format\name.js checks the name of the descriptor's getter function
  1106. const getFormat = createPublicMethod("get format", function () {
  1107. if (typeof this !== "object") {
  1108. platform.raiseNeedObjectOfType("NumberFormat.prototype.format", "NumberFormat");
  1109. }
  1110. const hiddenObject = UnwrapNumberFormat(this);
  1111. if (hiddenObject.boundFormat === undefined) {
  1112. hiddenObject.boundFormat = _.bind(format, hiddenObject);
  1113. delete hiddenObject.boundFormat.name;
  1114. }
  1115. return hiddenObject.boundFormat;
  1116. });
  1117. _.defineProperty(getFormat, "name", {
  1118. value: "get format",
  1119. writable: false,
  1120. enumerable: false,
  1121. configurable: true,
  1122. });
  1123. _.defineProperty(NumberFormatPrototype, "format", {
  1124. get: getFormat,
  1125. enumerable: false,
  1126. configurable: true,
  1127. });
  1128. _.defineProperty(NumberFormatPrototype, "formatToParts", {
  1129. value: formatToParts,
  1130. enumerable: false,
  1131. configurable: true,
  1132. writable: true,
  1133. });
  1134. return NumberFormat;
  1135. })();
  1136. // Intl.DateTimeFormat, Date.prototype.toLocaleString, Date.prototype.toLocaleDateString, Date.prototype.toLocaleTimeString
  1137. var DateTimeFormat = (function () {
  1138. if (InitType !== "Intl" && InitType !== "Date") {
  1139. return;
  1140. }
  1141. const narrowShortLong = ["narrow", "short", "long"];
  1142. const twoDigitNumeric = ["2-digit", "numeric"];
  1143. const allOptionValues = _.concat(twoDigitNumeric, narrowShortLong);
  1144. const dateTimeComponents = [
  1145. ["weekday", narrowShortLong],
  1146. ["era", narrowShortLong],
  1147. ["year", twoDigitNumeric],
  1148. ["month", allOptionValues], // month has every option available to it
  1149. ["day", twoDigitNumeric],
  1150. ["hour", twoDigitNumeric],
  1151. ["minute", twoDigitNumeric],
  1152. ["second", twoDigitNumeric],
  1153. ["timeZoneName", _.slice(narrowShortLong, 1)] // timeZoneName only allows "short" and "long"
  1154. ];
  1155. /**
  1156. * Given a user-provided options object, getPatternForOptions generates a LDML/ICU pattern and then
  1157. * sets the pattern and all of the relevant options implemented by the pattern on the provided dtf before returning.
  1158. *
  1159. * @param {Object} dtf the DateTimeFormat internal object
  1160. * @param {Object} options the options object originally given by the user
  1161. */
  1162. const getPatternForOptions = (function () {
  1163. // symbols come from the Unicode LDML: http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
  1164. const symbolForOption = {
  1165. weekday: "E",
  1166. era: "G",
  1167. year: "y",
  1168. month: "M",
  1169. day: "d",
  1170. // for hour, we have some special handling
  1171. hour: "j", hour12: "h", hour24: "H",
  1172. minute: "m",
  1173. second: "s",
  1174. timeZoneName: "z",
  1175. };
  1176. // NOTE - keep this up to date with the map in PlatformAgnostic::Intl::GetDateTimePartKind and the UDateFormatField enum
  1177. const optionForSymbol = {
  1178. E: "weekday", c: "weekday", e: "weekday",
  1179. G: "era",
  1180. y: "year", u: "year", U: "year",
  1181. M: "month", L: "month",
  1182. d: "day",
  1183. h: "hour", H: "hour", K: "hour", k: "hour",
  1184. m: "minute",
  1185. s: "second",
  1186. z: "timeZoneName", Z: "timeZoneName", v: "timeZoneName", V: "timeZoneName", O: "timeZoneName", X: "timeZoneName", x: "timeZoneName",
  1187. };
  1188. // lengths here are how many times a symbol is repeated in a skeleton for a given option
  1189. // the Intl spec recommends that Intl "short" -> CLDR "abbreviated" and Intl "long" -> CLDR "wide"
  1190. const symbolLengthForOption = {
  1191. numeric: 1,
  1192. "2-digit": 2,
  1193. short: 3,
  1194. long: 4,
  1195. narrow: 5,
  1196. };
  1197. const optionForSymbolLength = {
  1198. 1: "numeric",
  1199. 2: "2-digit",
  1200. 3: "short",
  1201. 4: "long",
  1202. 5: "narrow",
  1203. };
  1204. // for fixing up the hour pattern later
  1205. const patternForHourCycle = {
  1206. h12: "h",
  1207. h23: "H",
  1208. h11: "K",
  1209. h24: "k",
  1210. };
  1211. const hourCycleForPattern = {
  1212. h: "h12",
  1213. H: "h23",
  1214. K: "h11",
  1215. k: "h24",
  1216. };
  1217. // take the hour12 option by name so that we dont call the getter for options.hour12 twice
  1218. return function (dtf, options, hour12) {
  1219. const resolvedOptions = _.reduce(dateTimeComponents, function (resolved, component) {
  1220. const prop = component[0];
  1221. const value = GetOption(options, prop, "string", component[1], undefined);
  1222. if (value !== undefined) {
  1223. resolved[prop] = value;
  1224. }
  1225. return resolved;
  1226. }, _.create());
  1227. const hc = dtf.hourCycle;
  1228. // Build up a skeleton by repeating skeleton keys (like "G", "y", etc) for a count corresponding to the intl option value.
  1229. const skeleton = _.reduce(_.keys(resolvedOptions), function (skeleton, optionKey) {
  1230. let optionValue = resolvedOptions[optionKey];
  1231. if (optionKey === "hour") {
  1232. // hour12/hourCycle resolution in the spec has multiple issues:
  1233. // hourCycle and -hc can be out of sync: https://github.com/tc39/ecma402/issues/195
  1234. // hour12 has precedence over a more specific option in hourCycle/hc
  1235. // hour12 can force a locale that prefers h23 and h12 to use h11 or h24, according to the spec
  1236. // We temporarily work around these similarly to firefox and implement custom hourCycle/hour12 resolution.
  1237. // TODO(jahorto): follow up with Intl spec about these issues
  1238. if (hour12 === true || (hour12 === undefined && (hc === "h11" || hc === "h12"))) {
  1239. optionKey = "hour12";
  1240. } else if (hour12 === false || (hour12 === undefined && (hc === "h23" || hc === "h24"))) {
  1241. optionKey = "hour24";
  1242. }
  1243. }
  1244. return skeleton + _.repeat(symbolForOption[optionKey], symbolLengthForOption[optionValue]);
  1245. }, "");
  1246. let pattern = platform.getPatternForSkeleton(dtf.locale, skeleton);
  1247. // getPatternForSkeleton (udatpg_getBestPattern) can ignore, add, and modify fields compared to the markers we gave in the skeleton.
  1248. // Most importantly, udatpg_getBestPattern will determine the most-preferred hour field for a locale and time type (12 or 24).
  1249. // Scan the generated pattern to extract the resolved fields, and fix up the hour field if the user requested an explicit hour cycle
  1250. let inLiteral = false;
  1251. let i = 0;
  1252. while (i < pattern.length) {
  1253. let cur = pattern[i];
  1254. const isQuote = cur === "'";
  1255. if (inLiteral) {
  1256. if (isQuote) {
  1257. inLiteral = false;
  1258. }
  1259. ++i;
  1260. continue;
  1261. } else if (isQuote) {
  1262. inLiteral = true;
  1263. ++i;
  1264. continue;
  1265. } else if (cur === " ") {
  1266. ++i;
  1267. continue;
  1268. }
  1269. // we are not in a format literal, so we are in a symbolic section of the pattern
  1270. // now, we can force the correct hour pattern and set the internal slots correctly
  1271. if (cur === "h" || cur === "H" || cur === "K" || cur === "k") {
  1272. if (hc && hour12 === undefined) {
  1273. // if we have found an hour-like symbol and the user wanted a specific hour cycle,
  1274. // replace it and all such proceding contiguous symbols with the symbol corresponding
  1275. // to the user-requested hour cycle, if they are different
  1276. const replacement = patternForHourCycle[hc];
  1277. if (replacement !== cur) {
  1278. if (pattern[i + 1] === cur) {
  1279. // 2-digit hour
  1280. pattern = _.substring(pattern, 0, i) + replacement + replacement + _.substring(pattern, i + 2);
  1281. } else {
  1282. // numeric hour
  1283. pattern = _.substring(pattern, 0, i) + replacement + _.substring(pattern, i + 1);
  1284. }
  1285. // we have modified pattern[i] so we need to update cur
  1286. cur = pattern[i];
  1287. }
  1288. } else {
  1289. // if we have found an hour-like symbol and the user didnt request an hour cycle,
  1290. // set the internal hourCycle property from the resolved pattern
  1291. dtf.hourCycle = hourCycleForPattern[cur];
  1292. }
  1293. }
  1294. let k = i + 1;
  1295. while (k < pattern.length && pattern[k] === cur) {
  1296. ++k;
  1297. }
  1298. const resolvedKey = optionForSymbol[cur];
  1299. const resolvedValue = optionForSymbolLength[k - i];
  1300. dtf[resolvedKey] = resolvedValue;
  1301. i = k;
  1302. }
  1303. dtf.pattern = pattern;
  1304. };
  1305. })();
  1306. /**
  1307. * Initializes the dateTimeFormat argument with the given locales and options.
  1308. *
  1309. * ECMA-402: #sec-initializedatetimeformat
  1310. *
  1311. * @param {Object} dateTimeFormat the state object representing a DateTimeFormat instance or toLocale*String call
  1312. * @param {String|String[]} locales a user-provided list of locales
  1313. * @param {Object} options a user-provided options object
  1314. */
  1315. const InitializeDateTimeFormat = function (dateTimeFormat, locales, options) {
  1316. const requestedLocales = CanonicalizeLocaleList(locales);
  1317. options = ToDateTimeOptions(options, "any", "date");
  1318. const opt = _.create();
  1319. opt.localeMatcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit");
  1320. // Providing undefined for the `values` argument allows { hour12: "asd" } to become hour12 = true,
  1321. // which is apparently a feature of the spec, rather than a bug.
  1322. const hour12 = GetOption(options, "hour12", "boolean", undefined, undefined);
  1323. let hourCycle = GetOption(options, "hourCycle", "string", ["h11", "h12", "h23", "h24"], undefined);
  1324. if (hour12 !== undefined) {
  1325. hourCycle = null;
  1326. }
  1327. opt.hc = hourCycle;
  1328. const r = ResolveLocale(platform.isDTFLocaleAvailable, requestedLocales, opt, ["nu", "ca", "hc"]);
  1329. dateTimeFormat.locale = r.locale;
  1330. dateTimeFormat.calendar = r.ca;
  1331. dateTimeFormat.hourCycle = r.hc;
  1332. dateTimeFormat.numberingSystem = r.nu;
  1333. const localeWithoutSubtags = r.dataLocale;
  1334. let tz = options.timeZone;
  1335. if (tz === undefined) {
  1336. tz = platform.getDefaultTimeZone();
  1337. } else {
  1338. tz = Internal.ToString(tz);
  1339. }
  1340. // make tz uppercase here, as its easier to do now than in platform (even though the uppercase operation
  1341. // is supposed to be done in #sec-isvalidtimezonename)
  1342. const canonicalTZ = platform.validateAndCanonicalizeTimeZone(tz);
  1343. if (canonicalTZ === undefined || canonicalTZ === "Etc/Unknown") {
  1344. raise.rangeError(tz, "timeZone", "IANA Zone or Link name (Area/Location)");
  1345. } else if (canonicalTZ === "Etc/UTC" || canonicalTZ === "Etc/GMT") {
  1346. tz = "UTC";
  1347. } else {
  1348. tz = canonicalTZ;
  1349. }
  1350. dateTimeFormat.timeZone = tz;
  1351. // get the formatMatcher for validation only
  1352. GetOption(options, "formatMatcher", "string", ["basic", "best fit"], "best fit");
  1353. // this call replaces most of the spec code related to hour12/hourCycle and format negotiation/handling
  1354. getPatternForOptions(dateTimeFormat, options, hour12);
  1355. dateTimeFormat.initializedDateTimeFormat = true;
  1356. return dateTimeFormat;
  1357. };
  1358. /**
  1359. * Modifies the options argument to have correct default values
  1360. *
  1361. * ECMA-402: #sec-todatetimeoptions
  1362. *
  1363. * @param {Object} options user-provided options object passed as second argument to Intl.DateTimeFormat/toLocale*String
  1364. * @param {String} required which kind of options must be provided for the call (one of "date", "time", or "any")
  1365. * @param {String} defaults which kind of options will be set to a default value (one of "date", "time", or "all")
  1366. * @returns {Object} modified options object
  1367. */
  1368. const ToDateTimeOptions = function (options, required, defaults) {
  1369. options = options === undefined ? null : Internal.ToObject(options);
  1370. options = _.create(options);
  1371. let needDefaults = true;
  1372. if (required === "date" || required === "any") {
  1373. _.forEach(["weekday", "year", "month", "day"], function (prop) {
  1374. const value = options[prop];
  1375. if (value !== undefined) {
  1376. needDefaults = false;
  1377. }
  1378. });
  1379. }
  1380. if (required === "time" || required === "any") {
  1381. _.forEach(["hour", "minute", "second"], function (prop) {
  1382. const value = options[prop];
  1383. if (value !== undefined) {
  1384. needDefaults = false;
  1385. }
  1386. });
  1387. }
  1388. if (needDefaults === true && (defaults === "date" || defaults === "all")) {
  1389. _.forEach(["year", "month", "day"], function (prop) {
  1390. _.defineProperty(options, prop, {
  1391. value: "numeric",
  1392. writable: true,
  1393. enumerable: true,
  1394. configurable: true,
  1395. });
  1396. })
  1397. }
  1398. if (needDefaults === true && (defaults === "time" || defaults === "all")) {
  1399. _.forEach(["hour", "minute", "second"], function (prop) {
  1400. _.defineProperty(options, prop, {
  1401. value: "numeric",
  1402. writable: true,
  1403. enumerable: true,
  1404. configurable: true,
  1405. });
  1406. })
  1407. }
  1408. return options;
  1409. };
  1410. const FormatDateTimeToParts = function (dtf, x) {
  1411. if (_.isNaN(x) || !_.isFinite(x)) {
  1412. platform.raiseInvalidDate();
  1413. }
  1414. return platform.formatDateTime(dtf, x, /* toParts */ true, /* forDatePrototypeToLocaleString */ false);
  1415. };
  1416. // caches for objects constructed with default parameters for each method
  1417. const __DateInstanceToLocaleStringDefaultCache = [undefined, undefined, undefined];
  1418. const __DateInstanceToLocaleStringDefaultCacheSlot = bare({
  1419. toLocaleString: 0,
  1420. toLocaleDateString: 1,
  1421. toLocaleTimeString: 2
  1422. });
  1423. function DateInstanceToLocaleStringImplementation(name, option1, option2, cacheSlot, locales, options) {
  1424. if (typeof this !== 'object' || !(this instanceof Date)) {
  1425. platform.raiseNeedObjectOfType(name, "Date");
  1426. }
  1427. const value = _.getDate(new Date(this));
  1428. if (_.isNaN(value) || !_.isFinite(value)) {
  1429. return "Invalid Date";
  1430. }
  1431. let stateObject = undefined;
  1432. if (platform.useCaches && locales === undefined && options === undefined) {
  1433. // All default parameters (locales and options): this is the most valuable case to cache.
  1434. if (__DateInstanceToLocaleStringDefaultCache[cacheSlot]) {
  1435. // retrieve cached value
  1436. stateObject = __DateInstanceToLocaleStringDefaultCache[cacheSlot];
  1437. } else {
  1438. // populate cache
  1439. stateObject = _.create();
  1440. InitializeDateTimeFormat(stateObject, undefined, ToDateTimeOptions(undefined, option1, option2));
  1441. __DateInstanceToLocaleStringDefaultCache[cacheSlot] = stateObject;
  1442. }
  1443. }
  1444. if (!stateObject) {
  1445. stateObject = _.create();
  1446. InitializeDateTimeFormat(stateObject, locales, ToDateTimeOptions(options, option1, option2));
  1447. }
  1448. return platform.formatDateTime(stateObject, Internal.ToNumber(this), /* toParts */ false, /* forDatePrototypeToLocaleString */ true);
  1449. }
  1450. // Note: createPublicMethod (platform.tagPublicLibraryCode) messes with declared name of the FunctionBody so that
  1451. // the functions called appear correctly in the debugger and stack traces. Thus, we we cannot call createPublicMethod in a loop.
  1452. // Each entry point needs to have its own unique FunctionBody (which is a function as defined in the source code);
  1453. // this is why we have seemingly repeated ourselves below, instead of having one function and calling it multiple times with
  1454. // different parameters.
  1455. //
  1456. // The following invocations of `platform.registerBuiltInFunction(createPublicMethod(name, entryPoint))` are enclosed in IIFEs.
  1457. // The IIFEs are used to group all of the meaningful differences between each entry point into the arguments to the IIFE.
  1458. // The exception to this are the different entryPoint names which are only significant for debugging (and cannot be passed in
  1459. // as arguments, as the name is intrinsic to the function declaration).
  1460. //
  1461. // The `date_toLocale*String_entryPoint` function names are placeholder names that will never be seen from user code.
  1462. // The function name property and FunctionBody declared name are overwritten by `createPublicMethod`.
  1463. // The fact that they are declared with unique names is helpful for debugging.
  1464. // The functions *must not* be declared as anonymous functions (must be declared with a name);
  1465. // converting from an unnnamed function to a named function is not readily supported by the platform code and
  1466. // this has caused us to hit assertions in debug builds in the past.
  1467. //
  1468. // The entryPoint functions will be called as `Date.prototype.toLocale*String` and thus their `this` parameters will be a Date.
  1469. // `DateInstanceToLocaleStringImplementation` is not on `Date.prototype`, so we must propagate `this` into the call by using
  1470. // `DateInstanceToLocaleStringImplementation.call(this, ...)`.
  1471. (function (name, option1, option2, cacheSlot, platformFunctionID) {
  1472. platform.registerBuiltInFunction(createPublicMethod(name, function date_toLocaleString_entryPoint(locales = undefined, options = undefined) {
  1473. return DateInstanceToLocaleStringImplementation.call(this, name, option1, option2, cacheSlot, locales, options);
  1474. }), platformFunctionID);
  1475. })("Date.prototype.toLocaleString", "any", "all", __DateInstanceToLocaleStringDefaultCacheSlot.toLocaleString, IntlBuiltInFunctionID.DateToLocaleString);
  1476. (function (name, option1, option2, cacheSlot, platformFunctionID) {
  1477. platform.registerBuiltInFunction(createPublicMethod(name, function date_toLocaleDateString_entryPoint(locales = undefined, options = undefined) {
  1478. return DateInstanceToLocaleStringImplementation.call(this, name, option1, option2, cacheSlot, locales, options);
  1479. }), platformFunctionID);
  1480. })("Date.prototype.toLocaleDateString", "date", "date", __DateInstanceToLocaleStringDefaultCacheSlot.toLocaleDateString, IntlBuiltInFunctionID.DateToLocaleDateString);
  1481. (function (name, option1, option2, cacheSlot, platformFunctionID) {
  1482. platform.registerBuiltInFunction(createPublicMethod(name, function date_toLocaleTimeString_entryPoint(locales = undefined, options = undefined) {
  1483. return DateInstanceToLocaleStringImplementation.call(this, name, option1, option2, cacheSlot, locales, options);
  1484. }), platformFunctionID);
  1485. })("Date.prototype.toLocaleTimeString", "time", "time", __DateInstanceToLocaleStringDefaultCacheSlot.toLocaleTimeString, IntlBuiltInFunctionID.DateToLocaleTimeString);
  1486. // if we were only initializing Date, dont bother initializing Intl.DateTimeFormat
  1487. if (InitType !== "Intl") {
  1488. return;
  1489. }
  1490. const DateTimeFormatPrototype = {};
  1491. /**
  1492. * The Intl.DateTimeFormat constructor
  1493. *
  1494. * ECMA-402: #sec-intl.datetimeformat
  1495. *
  1496. * @param {String|String[]} locales
  1497. * @param {Object} options
  1498. */
  1499. const DateTimeFormat = tagPublicFunction("Intl.DateTimeFormat", function DateTimeFormat(locales = undefined, options = undefined) {
  1500. const newTarget = new.target === undefined ? DateTimeFormat : new.target;
  1501. const dateTimeFormat = OrdinaryCreateFromConstructor(newTarget, DateTimeFormatPrototype);
  1502. let hiddenObject = platform.getHiddenObject(dateTimeFormat);
  1503. if (hiddenObject === undefined) {
  1504. hiddenObject = _.create();
  1505. platform.setHiddenObject(dateTimeFormat, hiddenObject);
  1506. }
  1507. InitializeDateTimeFormat(hiddenObject, locales, options);
  1508. if (new.target === undefined && this instanceof DateTimeFormat) {
  1509. _.defineProperty(this, platform.FallbackSymbol, {
  1510. value: dateTimeFormat,
  1511. writable: false,
  1512. enumerable: false,
  1513. configurable: false
  1514. });
  1515. return this;
  1516. }
  1517. return dateTimeFormat;
  1518. });
  1519. const UnwrapDateTimeFormat = function (dtf) {
  1520. let hiddenObject = platform.getHiddenObject(dtf);
  1521. if ((!hiddenObject || !hiddenObject.initializedDateTimeFormat) && dtf instanceof DateTimeFormat) {
  1522. dtf = dtf[platform.FallbackSymbol];
  1523. }
  1524. if (typeof dtf !== "object") {
  1525. platform.raiseNeedObjectOfType("DateTimeFormat.prototype.format", "DateTimeFormat");
  1526. }
  1527. hiddenObject = platform.getHiddenObject(dtf);
  1528. if (!hiddenObject.initializedDateTimeFormat) {
  1529. platform.raiseNeedObjectOfType("DateTimeFormat.prototype.format", "DateTimeFormat");
  1530. }
  1531. return hiddenObject;
  1532. };
  1533. // format should always be bound to a valid DateTimeFormat's hiddenObject by getFormat()
  1534. const format = createPublicMethod("Intl.DateTimeFormat.prototype.format", function format(date) {
  1535. if (!this || !this.initializedDateTimeFormat) {
  1536. platform.raiseNeedObjectOfType("DateTimeFormat.prototype.format", "DateTimeFormat");
  1537. }
  1538. let x;
  1539. if (date === undefined) {
  1540. x = platform.builtInJavascriptDateEntryNow();
  1541. } else {
  1542. x = Internal.ToNumber(date);
  1543. if (_.isNaN(x) || !_.isFinite(x)) {
  1544. platform.raiseInvalidDate();
  1545. }
  1546. }
  1547. return platform.formatDateTime(this, x, /* toParts */ false, /* forDatePrototypeToLocaleString */ false);
  1548. });
  1549. const formatToParts = createPublicMethod("Intl.DateTimeFormat.prototype.formatToParts", function formatToParts(date) {
  1550. if (typeof this !== "object") {
  1551. platform.raiseNeedObjectOfType("DateTimeFormat.prototype.formatToParts", "DateTimeFormat");
  1552. }
  1553. const hiddenObject = platform.getHiddenObject(this);
  1554. if (hiddenObject === undefined || !hiddenObject.initializedDateTimeFormat) {
  1555. platform.raiseNeedObjectOfType("DateTimeFormat.prototype.formatToParts", "DateTimeFormat");
  1556. }
  1557. let x;
  1558. if (date === undefined) {
  1559. x = platform.builtInJavascriptDateEntryNow();
  1560. } else {
  1561. x = Internal.ToNumber(date);
  1562. if (_.isNaN(x) || !_.isFinite(x)) {
  1563. platform.raiseInvalidDate();
  1564. }
  1565. }
  1566. return platform.formatDateTime(hiddenObject, x, /* toParts */ true, /* forDatePrototypeToLocaleString */ false);
  1567. });
  1568. _.defineProperty(DateTimeFormat, "prototype", {
  1569. value: DateTimeFormatPrototype,
  1570. writable: false,
  1571. enumerable: false,
  1572. configurable: false
  1573. });
  1574. _.defineProperty(DateTimeFormatPrototype, "constructor", {
  1575. value: DateTimeFormat,
  1576. writable: true,
  1577. enumerable: false,
  1578. configurable: true
  1579. });
  1580. // test262's test\intl402\DateTimeFormat\prototype\format\name.js checks the name of the descriptor's getter function
  1581. const getFormat = createPublicMethod("get format", function () {
  1582. if (typeof this !== "object") {
  1583. platform.raiseNeedObjectOfType("DateTimeFormat.prototype.format", "DateTimeFormat");
  1584. }
  1585. const hiddenObject = UnwrapDateTimeFormat(this);
  1586. if (hiddenObject.boundFormat === undefined) {
  1587. hiddenObject.boundFormat = _.bind(format, hiddenObject);
  1588. delete hiddenObject.boundFormat.name;
  1589. }
  1590. return hiddenObject.boundFormat;
  1591. });
  1592. _.defineProperty(getFormat, "name", {
  1593. value: "get format",
  1594. writable: false,
  1595. enumerable: false,
  1596. configurable: true,
  1597. });
  1598. _.defineProperty(DateTimeFormatPrototype, "format", {
  1599. get: getFormat,
  1600. enumerable: false,
  1601. configurable: true,
  1602. });
  1603. _.defineProperty(DateTimeFormatPrototype, "formatToParts", {
  1604. value: formatToParts,
  1605. enumerable: false,
  1606. configurable: true,
  1607. writable: true,
  1608. });
  1609. _.defineProperty(DateTimeFormatPrototype, "resolvedOptions", {
  1610. value: createPublicMethod("Intl.DateTimeFormat.prototype.resolvedOptions", function resolvedOptions() {
  1611. if (typeof this !== "object") {
  1612. platform.raiseNeedObjectOfType("DateTimeFormat.prototype.format", "DateTimeFormat");
  1613. }
  1614. const hiddenObject = UnwrapDateTimeFormat(this);
  1615. const options = [
  1616. "locale",
  1617. "calendar",
  1618. "numberingSystem",
  1619. "timeZone",
  1620. "hourCycle",
  1621. "weekday",
  1622. "era",
  1623. "year",
  1624. "month",
  1625. "day",
  1626. "hour",
  1627. "minute",
  1628. "second",
  1629. "timeZoneName",
  1630. ];
  1631. return createResolvedOptions(options, hiddenObject, function (prop, resolved) {
  1632. if (prop === "hourCycle") {
  1633. const hc = hiddenObject.hourCycle;
  1634. if (hiddenObject.hour !== undefined && hc !== null) {
  1635. resolved.hourCycle = hc;
  1636. resolved.hour12 = hc === "h11" || hc === "h12";
  1637. }
  1638. return true;
  1639. }
  1640. });
  1641. }),
  1642. writable: true,
  1643. enumerable: false,
  1644. configurable: true,
  1645. });
  1646. const supportedLocalesOf = createPublicMethod("Intl.DateTimeFormat.supportedLocalesOf", function supportedLocalesOf(locales, options = undefined) {
  1647. return SupportedLocales(platform.isDTFLocaleAvailable, CanonicalizeLocaleList(locales), options);
  1648. });
  1649. _.defineProperty(DateTimeFormat, "supportedLocalesOf", {
  1650. value: supportedLocalesOf,
  1651. writable: true,
  1652. enumerable: false,
  1653. configurable: true,
  1654. });
  1655. return DateTimeFormat;
  1656. })();
  1657. const PluralRules = (function() {
  1658. if (InitType !== "Intl") {
  1659. return;
  1660. }
  1661. /**
  1662. * Initializes the given pluralRules object
  1663. *
  1664. * ECMA 402: #sec-initializepluralrules
  1665. *
  1666. * @param {Object} pluralRules
  1667. * @param {String|String[]} locales
  1668. * @param {Object} options
  1669. */
  1670. const InitializePluralRules = function (pluralRules, locales, options) {
  1671. const requestedLocales = CanonicalizeLocaleList(locales);
  1672. options = options === undefined ? _.create() : Internal.ToObject(options);
  1673. const opt = _.create();
  1674. opt.matcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit");
  1675. pluralRules.type = GetOption(options, "type", "string", ["cardinal", "ordinal"], "cardinal");
  1676. SetNumberFormatDigitOptions(pluralRules, options, 0, 3);
  1677. // %PluralRules%.[[RelevantExtensionKeys]] = [] (#sec-intl.pluralrules-internal-slots)
  1678. const r = ResolveLocale(platform.isPRLocaleAvailable, requestedLocales, opt, []);
  1679. pluralRules.locale = r.locale;
  1680. pluralRules.pluralCategories = platform.pluralRulesKeywords(pluralRules);
  1681. pluralRules.initializedPluralRules = true;
  1682. return pluralRules;
  1683. };
  1684. /**
  1685. * Returns a String value representing the plural form of n according to
  1686. * the effective locale and the options of pluralRules
  1687. *
  1688. * ECMA 402: #sec-resolveplural
  1689. *
  1690. * @param {Object} pluralRules
  1691. * @param {Number} n
  1692. */
  1693. const ResolvePlural = function (pluralRules, n) {
  1694. if (!_.isFinite(n)) {
  1695. return "other";
  1696. }
  1697. return platform.pluralRulesSelect(pluralRules, n);
  1698. };
  1699. const PluralRulesPrototype = {};
  1700. // params are explicitly `= undefined` to make PluralRules.length === 0
  1701. const PluralRules = tagPublicFunction("Intl.PluralRules", function PluralRules(locales = undefined, options = undefined) {
  1702. if (new.target === undefined) {
  1703. platform.raiseNeedObjectOfType("Intl.PluralRules", "PluralRules");
  1704. }
  1705. const pluralRules = OrdinaryCreateFromConstructor(new.target, PluralRulesPrototype);
  1706. const stateObject = _.create();
  1707. platform.setHiddenObject(pluralRules, stateObject);
  1708. InitializePluralRules(stateObject, locales, options);
  1709. return pluralRules;
  1710. });
  1711. // ECMA 402: #sec-intl.pluralrules.prototype
  1712. _.defineProperty(PluralRules, "prototype", {
  1713. value: PluralRulesPrototype,
  1714. writable: false,
  1715. enumerable: false,
  1716. configurable: false,
  1717. });
  1718. _.defineProperty(PluralRulesPrototype, "constructor", {
  1719. value: PluralRules,
  1720. writable: true,
  1721. enumerable: false,
  1722. configurable: true
  1723. });
  1724. const supportedLocalesOf = createPublicMethod("Intl.PluralRules.supportedLocalesOf", function supportedLocalesOf(locales, options = undefined) {
  1725. return SupportedLocales(platform.isPRLocaleAvailable, CanonicalizeLocaleList(locales), options);
  1726. });
  1727. _.defineProperty(PluralRules, "supportedLocalesOf", {
  1728. value: supportedLocalesOf,
  1729. writable: true,
  1730. enumerable: false,
  1731. configurable: true,
  1732. });
  1733. // ECMA 402: #sec-intl.pluralrules.prototype.select
  1734. const select = createPublicMethod("Intl.PluralRules.prototype.select", function select(value) {
  1735. const pr = platform.getHiddenObject(this);
  1736. if (!pr || !pr.initializedPluralRules) {
  1737. platform.raiseNeedObjectOfType("Intl.PluralRules.prototype.select", "PluralRules");
  1738. }
  1739. const n = Internal.ToNumber(value);
  1740. return ResolvePlural(pr, n);
  1741. });
  1742. _.defineProperty(PluralRulesPrototype, "select", {
  1743. value: select,
  1744. enumerable: false,
  1745. configurable: true,
  1746. writable: true,
  1747. });
  1748. const resolvedOptions = createPublicMethod("Intl.PluralRules.prototype.resolvedOptions", function resolvedOptions() {
  1749. const pr = platform.getHiddenObject(this);
  1750. if (!pr || !pr.initializedPluralRules) {
  1751. platform.raiseNeedObjectOfType("Intl.PluralRules.prototype.select", "PluralRules");
  1752. }
  1753. return createResolvedOptions([
  1754. "locale",
  1755. "type",
  1756. "minimumIntegerDigits",
  1757. "minimumFractionDigits",
  1758. "maximumFractionDigits",
  1759. "minimumSignificantDigits",
  1760. "maximumSignificantDigits",
  1761. "pluralCategories"
  1762. ], pr, (prop, resolved) => {
  1763. if (prop === "pluralCategories") {
  1764. // https://github.com/tc39/ecma402/issues/224: create a copy of the pluralCategories array
  1765. resolved.pluralCategories = _.slice(pr.pluralCategories, 0);
  1766. return true;
  1767. }
  1768. });
  1769. });
  1770. _.defineProperty(PluralRulesPrototype, "resolvedOptions", {
  1771. value: resolvedOptions,
  1772. enumerable: false,
  1773. configurable: true,
  1774. writable: true,
  1775. });
  1776. return PluralRules;
  1777. })();
  1778. // Initialize Intl properties only if needed
  1779. if (InitType === "Intl") {
  1780. _.defineProperty(Intl, "Collator", { value: Collator, writable: true, enumerable: false, configurable: true });
  1781. _.defineProperty(Intl, "NumberFormat", { value: NumberFormat, writable: true, enumerable: false, configurable: true });
  1782. _.defineProperty(Intl, "DateTimeFormat", { value: DateTimeFormat, writable: true, enumerable: false, configurable: true });
  1783. _.defineProperty(Intl, "PluralRules", { value: PluralRules, writable: true, enumerable: false, configurable: true });
  1784. }
  1785. }
  1786. /**
  1787. *
  1788. *
  1789. *
  1790. *
  1791. *
  1792. *
  1793. * END ICU, BEGIN WINGLOB
  1794. *
  1795. *
  1796. *
  1797. *
  1798. *
  1799. *
  1800. */
  1801. else {
  1802. if (platform.localeLookupCache === undefined) {
  1803. platform.localeLookupCache = new platform.Map();
  1804. }
  1805. if (platform.localeBestFitCache === undefined) {
  1806. platform.localeBestFitCache = new platform.Map();
  1807. }
  1808. let __defaultLocale = undefined;
  1809. const GetDefaultLocale = function () {
  1810. if (__defaultLocale && platform.useCaches) {
  1811. return __defaultLocale;
  1812. }
  1813. const locale = platform.getDefaultLocale();
  1814. if (!locale) {
  1815. // if the system locale is undefined/null/empty string, we have to
  1816. // do something or else we will crash
  1817. __defaultLocale = "en";
  1818. } else {
  1819. __defaultLocale = locale;
  1820. }
  1821. return __defaultLocale;
  1822. };
  1823. let CreateDateTimeFormat = function (dateTimeFormat, condition) {
  1824. let retVal = platform.createDateTimeFormat(dateTimeFormat, condition);
  1825. if (retVal === null) {
  1826. // TODO (doilij): remove this fallback when implemented under ICU
  1827. dateTimeFormat.__numberingSystem = "";
  1828. dateTimeFormat.__patternStrings = [
  1829. "{month.a}{day.b}{hour.c}{minute.d}{second.e}",
  1830. "" // another entry for fun
  1831. ]
  1832. }
  1833. // no return value
  1834. };
  1835. let IsWellFormedLanguageTag = function (langTag) {
  1836. let retVal = platform.isWellFormedLanguageTag(langTag);
  1837. if (retVal === null) {
  1838. if (!LANG_TAG_RE) {
  1839. InitializeLangTagREs();
  1840. }
  1841. let match = platform.builtInRegexMatch(langTag, LANG_TAG_RE);
  1842. return !!match;
  1843. } else {
  1844. return retVal;
  1845. }
  1846. };
  1847. var forEachIfPresent = function (obj, length, func) {
  1848. let current = 0;
  1849. while (current < length) {
  1850. if (current in obj) {
  1851. func(obj[current]);
  1852. }
  1853. current++;
  1854. }
  1855. };
  1856. // A helper function that is meant to rethrow SOE and OOM exceptions allowing them to propagate.
  1857. var throwExIfOOMOrSOE = function (ex) {
  1858. if (ex.number === -2146828260 || ex.number === -2146828281) {
  1859. throw ex;
  1860. }
  1861. };
  1862. var tagPublicFunction = function (name, f) {
  1863. return platform.tagPublicLibraryCode(f, name);
  1864. };
  1865. var resolveLocaleBestFit = function (locale, defaultLocale) {
  1866. var resolvedLocale = platform.localeBestFitCache.get(locale);
  1867. if (resolvedLocale === undefined) {
  1868. resolvedLocale = platform.resolveLocaleBestFit(locale);
  1869. if (resolvedLocale === null) {
  1870. if (!LANG_TAG_BASE_RE) {
  1871. InitializeLangTagREs();
  1872. }
  1873. let match = platform.builtInRegexMatch(locale, LANG_TAG_BASE_RE);
  1874. resolvedLocale = match[1] + (match[2] ? ('-' + match[2]) : '') + (match[3] ? ('-' + match[3]) : '');
  1875. }
  1876. // If resolvedLocale is undefined, cache that we got undefined
  1877. // so we don't try to resolve for `locale` in future.
  1878. platform.localeBestFitCache.set(locale, resolvedLocale === undefined ? NOT_FOUND : resolvedLocale);
  1879. } else if (resolvedLocale === NOT_FOUND) {
  1880. resolvedLocale = undefined;
  1881. }
  1882. if (defaultLocale === locale) {
  1883. return resolvedLocale;
  1884. } else if (defaultLocale === resolvedLocale) {
  1885. return undefined;
  1886. } else {
  1887. return resolvedLocale;
  1888. }
  1889. }
  1890. var resolveLocaleLookup = function (localeWithoutSubtags) {
  1891. let resolvedLocale = platform.localeLookupCache.get(localeWithoutSubtags);
  1892. if (resolvedLocale === undefined) {
  1893. resolvedLocale = platform.resolveLocaleLookup(localeWithoutSubtags);
  1894. if (resolvedLocale === null) {
  1895. if (!LANG_TAG_BASE_RE) {
  1896. InitializeLangTagREs();
  1897. }
  1898. let match = platform.builtInRegexMatch(localeWithoutSubtags, LANG_TAG_BASE_RE);
  1899. // match: [1] language; [2] script; [3] region (e.g. en-Latn-US)
  1900. resolvedLocale = match[1]
  1901. + (match[2] ? ('-' + match[2]) : '')
  1902. + (match[3] ? ('-' + match[3]) : '');
  1903. }
  1904. // If resolvedLocale is undefined, cache that we got undefined
  1905. // so we don't try to resolve for `locale` in future.
  1906. platform.localeLookupCache.set(localeWithoutSubtags, resolvedLocale === undefined ? NOT_FOUND : resolvedLocale);
  1907. } else if (resolvedLocale === NOT_FOUND) {
  1908. resolvedLocale = undefined;
  1909. }
  1910. return resolvedLocale;
  1911. }
  1912. var getExtensionSubtags = function (locale) {
  1913. if (!LANG_TAG_EXT_RE) {
  1914. InitializeLangTagREs();
  1915. }
  1916. const match = platform.builtInRegexMatch(locale, LANG_TAG_EXT_RE);
  1917. if (!match) {
  1918. return undefined;
  1919. }
  1920. // Note: extensions are /((${extension})-)*/ and are made up of \\b(?:${singleton}(?:-${alphanum}{2,8})+)\\b
  1921. // where the ${alphanum}{2,8} fields are of the form `${key}-${value}`.
  1922. // TODO (doilij): return an array of `${key}-${value}` pairs
  1923. // REVIEW (doilij): leading - might mean we need to filter: // ss.match(rr)[4].split('-').filter((x)=>!!x)
  1924. // In that case:
  1925. // TODO StringInstanceSplit
  1926. // TODO ArrayInstanceFilter
  1927. // let extSubtags = ArrayInstanceFilter(extensionsString.split('-'), (x)=>!!x);
  1928. const extSubtags = match[0].split('-').filter((x) => !!x);
  1929. // REVIEW (doilij): performance (testing for str[0]==='-' and using the string after that or updating the regex might be faster)
  1930. return extSubtags;
  1931. }
  1932. var resolveLocaleHelper = function (locale, fitter, extensionFilter, defaultLocale) {
  1933. var subTags = platform.getExtensions(locale);
  1934. if (subTags === null) {
  1935. // platform.getExtensions returns null to indicate fallback to JS implementation
  1936. subTags = getExtensionSubtags(locale);
  1937. }
  1938. if (subTags) {
  1939. callInstanceFunc(ArrayInstanceForEach, subTags, function (subTag) {
  1940. locale = callInstanceFunc(StringInstanceReplace, locale, "-" + subTag, "");
  1941. });
  1942. }
  1943. // Instead of using replace, we will match two groups, one capturing, one not. The non capturing group just strips away -u if present.
  1944. // We are substituting for the function replace; which will only make a change if /-u$/ was found (-u at the end of the line)
  1945. // And because match will return null if we don't match entire sequence, we are using the two groups stated above.
  1946. locale = platform.builtInRegexMatch(locale, /(.*?)(?:-u)?$/)[1];
  1947. var resolved = fitter(locale, defaultLocale);
  1948. if (extensionFilter !== undefined) { // Filter to expected sub-tags
  1949. var filtered = [];
  1950. callInstanceFunc(ArrayInstanceForEach, subTags, (function (subTag) {
  1951. var parts = platform.builtInRegexMatch(subTag, /([^-]*)-?(.*)?/); // [0] entire thing; [1] key; [2] value
  1952. var key = parts[1];
  1953. if (callInstanceFunc(ArrayInstanceIndexOf, extensionFilter, key) !== -1) {
  1954. callInstanceFunc(ArrayInstancePush, filtered, subTag);
  1955. }
  1956. }));
  1957. subTags = filtered;
  1958. }
  1959. // As long as we are using the JS version of getExtensions on ICU, "u" will be considered an extension
  1960. // of a locale like "de-u-co-phonebk"
  1961. // Thus, we can't add the -u- ourselves here
  1962. const withoutSubTags = resolved;
  1963. if (resolved) {
  1964. if (subTags && getArrayLength(subTags) > 0) {
  1965. if (isPlatformUsingICU) {
  1966. resolved += "-";
  1967. } else {
  1968. resolved += "-u-";
  1969. }
  1970. }
  1971. resolved += callInstanceFunc(ArrayInstanceJoin, subTags, "-");
  1972. } else {
  1973. resolved = undefined;
  1974. }
  1975. return setPrototype({
  1976. locale: resolved,
  1977. subTags: subTags,
  1978. localeWithoutSubtags: withoutSubTags
  1979. }, null);
  1980. }
  1981. var resolveLocales = function (givenLocales, matcher, extensionFilter, defaultLocaleFunc) {
  1982. var fitter = matcher === "lookup" ? resolveLocaleLookup : resolveLocaleBestFit;
  1983. var length = getArrayLength(givenLocales);
  1984. var defaultLocale = defaultLocaleFunc();
  1985. length = length !== undefined ? length : 0;
  1986. for (var i = 0; i < length; i++) {
  1987. var resolved = resolveLocaleHelper(givenLocales[i], fitter, extensionFilter, defaultLocale);
  1988. if (resolved.locale !== undefined) {
  1989. return resolved;
  1990. }
  1991. }
  1992. return resolveLocaleHelper(defaultLocale, fitter, undefined, defaultLocale);
  1993. }
  1994. // get just the language-script-region from the default locale
  1995. let __strippedDefaultLocale = undefined;
  1996. var strippedDefaultLocale = function () {
  1997. if (__strippedDefaultLocale) {
  1998. return __strippedDefaultLocale;
  1999. }
  2000. if (isPlatformUsingICU) {
  2001. if (!LANG_TAG_BASE_RE) {
  2002. InitializeLangTagREs();
  2003. }
  2004. const def = GetDefaultLocale();
  2005. const match = platform.builtInRegexMatch(def, LANG_TAG_BASE_RE);
  2006. if (match) {
  2007. // strip extensions by matching only the base
  2008. __strippedDefaultLocale = match[0];
  2009. } else {
  2010. __strippedDefaultLocale = def;
  2011. }
  2012. } else {
  2013. // the only thing to strip off of a WinGlob locale is the collation,
  2014. // which comes after the underscore
  2015. __strippedDefaultLocale = platform.builtInRegexMatch(GetDefaultLocale(), /([^_]*).*/)[1];
  2016. }
  2017. return __strippedDefaultLocale;
  2018. };
  2019. var Internal = (function () {
  2020. return setPrototype({
  2021. ToObject: function (o) {
  2022. if (o === null) {
  2023. platform.raiseNeedObject();
  2024. }
  2025. return o !== undefined ? Object(o) : undefined;
  2026. },
  2027. ToString: function (s) {
  2028. return s !== undefined ? String(s) : undefined;
  2029. },
  2030. ToNumber: function (n) {
  2031. return n === undefined ? NaN : Number(n);
  2032. },
  2033. ToLogicalBoolean: function (v) {
  2034. return v !== undefined ? Boolean(v) : undefined;
  2035. },
  2036. ToUint32: function (n) {
  2037. var num = Number(n),
  2038. ret = 0;
  2039. if (!isNaN(num) && isFinite(num)) {
  2040. ret = Math.abs(num % Math.pow(2, 32));
  2041. }
  2042. return ret;
  2043. },
  2044. HasProperty: function (o, p) {
  2045. // Walk the prototype chain
  2046. while (o) {
  2047. if (callInstanceFunc(ObjectInstanceHasOwnProperty, o, p)) {
  2048. return true;
  2049. }
  2050. o = ObjectGetPrototypeOf(o);
  2051. }
  2052. }
  2053. }, null)
  2054. })();
  2055. // Internal ops implemented in JS:
  2056. function GetOption(options, property, type, values, fallback) {
  2057. let value = options[property];
  2058. if (value !== undefined) {
  2059. if (type == "boolean") {
  2060. value = Internal.ToLogicalBoolean(value);
  2061. }
  2062. if (type == "string") {
  2063. value = Internal.ToString(value);
  2064. }
  2065. if (type == "number") {
  2066. value = Internal.ToNumber(value);
  2067. }
  2068. if (values !== undefined && callInstanceFunc(ArrayInstanceIndexOf, values, value) == -1) {
  2069. platform.raiseOptionValueOutOfRange_3(String(value), String(property), "['" + callInstanceFunc(ArrayInstanceJoin, values, "', '") + "']");
  2070. }
  2071. return value;
  2072. }
  2073. return fallback;
  2074. }
  2075. function GetNumberOption(options, property, minimum, maximum, fallback) {
  2076. const rawValue = options[property];
  2077. if (typeof rawValue !== 'undefined') {
  2078. const formattedValue = Internal.ToNumber(rawValue);
  2079. if (isNaN(formattedValue) || formattedValue < minimum || formattedValue > maximum) {
  2080. platform.raiseOptionValueOutOfRange_3(String(rawValue), String(property), "[" + minimum + " - " + maximum + "]");
  2081. }
  2082. return Math.floor(formattedValue);
  2083. } else {
  2084. return fallback;
  2085. }
  2086. }
  2087. let CURRENCY_CODE_RE;
  2088. function InitializeCurrencyRegExp() {
  2089. CURRENCY_CODE_RE = /^[A-Z]{3}$/i;
  2090. }
  2091. let LANG_TAG_BASE_RE; // language[-script[-region]]
  2092. let LANG_TAG_EXT_RE; // extension part (variant, extension, privateuse)
  2093. let LANG_TAG_RE; // full syntax of language tags (including privateuse and grandfathered)
  2094. function InitializeLangTagREs() {
  2095. // Language Tag Syntax as described in RFC 5646 #section-2.1
  2096. // Note: All language tags are comprised only of ASCII characters (makes our job easy here)
  2097. // Note: Language tags in canonical form have case conventions, but language tags are case-insensitive for our purposes
  2098. // Note: The ABNF syntax used in RFC 5646 #section-2.1 uses the following numeric quantifier conventions:
  2099. // - (Parentheses) are used for grouping
  2100. // - PRODUCTION => exactly 1 of PRODUCTION /PRODUCTION/
  2101. // - [PRODUCTION] => 0 or 1 of PRODUCTION /(PRODUCTION)?/
  2102. // - #PRODUCTION => exactly # of PRODUCTION /(PRODUCTION){#}/
  2103. // - a*bPRODUCTION (where a and b are optional)
  2104. // - *PRODUCTION => any number of PRODUCTION /(PRODUCTION)*/
  2105. // - 1*PRODUCTION => 1 or more of PRODUCTION /(PRODUCTION)+/
  2106. // - #*PRODUCTION => # or more of PRODUCTION /(PRODUCTION){#,}/
  2107. // - *#PRODUCTION => 0 to # (inclusive) of PRODUCTION /(PRODUCTION){,#}/ or /(PRODUCTION){0,#}/
  2108. // - a*bPRODUCTION => a to b (inclusive) of PRODUCTION /(PRODUCTION){a,b}/
  2109. const ALPHA = "[A-Z]";
  2110. const DIGIT = "[0-9]";
  2111. const alphanum = `(?:${ALPHA}|${DIGIT})`;
  2112. const regular = "\\b(?:art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang)\\b";
  2113. const irregular = "\\b(?:en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo" +
  2114. "|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)\\b";
  2115. const grandfathered = `\\b(?:${regular}|${irregular})\\b`;
  2116. const privateuse = `\\b(?:x(?:-${alphanum}{1,8}\\b)+)\\b`; // privateuse = "x" 1*("-" (1*8alphanum))
  2117. const singleton = `\\b(?:${DIGIT}|[A-WY-Z])\\b`; // singleton ~= alphanum except for 'x' ; (paraphrased)
  2118. const extension = `\\b(?:${singleton}(?:-${alphanum}{2,8})+)\\b`; // extension = singleton 1*("-" (2*8alphanum))
  2119. const variant = `\\b(?:${alphanum}{5,8}|${DIGIT}${alphanum}{3})\\b`; // variant = 5*8alphanum / (DIGIT 3alphanum)
  2120. const region = `\\b(?:${ALPHA}{2}|${DIGIT}{3})\\b`; // region = 2ALPHA / 3DIGIT
  2121. const script = `\\b(?:${ALPHA}{4})\\b`; // script = 4ALPHA
  2122. const extlang = `\\b(?:${ALPHA}{3}\\b(?:-${ALPHA}{3}){0,2})\\b`; // extlang = 3ALPHA *2("-" 3ALPHA)
  2123. const language = '\\b(?:' + // language =
  2124. `${ALPHA}{2,3}` + // 2*3ALPHA ; shortest ISO 639 code
  2125. `\\b(?:-${extlang})?` + // ["-" extlang] ; sometimes followed by extended language subtags
  2126. // `|${ALPHA}{4}` + // / 4ALPHA ; or reserved for future use
  2127. // `|${ALPHA}{5,8}` + // / 5*8ALPHA ; or registered language subtag
  2128. `|${ALPHA}{4,8}` + // ~/ 4*8ALPHA ; (paraphrased: combined previous two lines)
  2129. ')\\b';
  2130. // below: ${language}, ${script}, and ${region} are wrapped in parens because matching groups are useful for replacement
  2131. const LANG_TAG_BASE = `\\b(${language})\\b` + // langtag = language
  2132. `\\b(?:-(${script}))?\\b` + // ["-" script]
  2133. `\\b(?:-(${region}))?\\b` ; // ["-" region]
  2134. const LANG_TAG_EXT = `\\b(?:-${variant})*\\b` + // *("-" variant)
  2135. `\\b((?:-${extension})*)\\b` + // *("-" extension)
  2136. `\\b(?:-${privateuse})?\\b` ; // ["-" privateuse]
  2137. const langtag = `\\b${LANG_TAG_BASE}\\b${LANG_TAG_EXT}\\b`;
  2138. const LANG_TAG = `\\b(?:${langtag}|${privateuse}|${grandfathered})\\b`; // Language-Tag = ...
  2139. LANG_TAG_BASE_RE = new RegExp(LANG_TAG_BASE, 'i'); // [1] language; [2] script; [3] region
  2140. LANG_TAG_EXT_RE = new RegExp(LANG_TAG_EXT, 'i'); // [1] extensions /((${extension})-)*/
  2141. LANG_TAG_RE = new RegExp(LANG_TAG, 'i'); // [1] language; [2] script; [3] region; [4] extensions
  2142. }
  2143. function IsWellFormedCurrencyCode(code) {
  2144. code = Internal.ToString(code);
  2145. if (!CURRENCY_CODE_RE) {
  2146. InitializeCurrencyRegExp();
  2147. }
  2148. return platform.builtInRegexMatch(code, CURRENCY_CODE_RE) !== null;
  2149. }
  2150. // Make sure locales is an array, remove duplicate locales, make sure each locale is valid, and canonicalize each.
  2151. function CanonicalizeLocaleList(locales) {
  2152. if (typeof locales === 'undefined') {
  2153. return [];
  2154. }
  2155. if (typeof locales === 'string') {
  2156. locales = [locales];
  2157. }
  2158. locales = Internal.ToObject(locales);
  2159. const length = Internal.ToUint32(locales.length);
  2160. // TODO: Use sets here to prevent duplicates
  2161. let seen = [];
  2162. forEachIfPresent(locales, length, function (locale) {
  2163. if ((typeof locale !== 'string' && typeof locale !== 'object') || locale === null) {
  2164. platform.raiseNeedObjectOrString("Locale");
  2165. }
  2166. let tag = Internal.ToString(locale);
  2167. if (!IsWellFormedLanguageTag(tag)) {
  2168. platform.raiseLocaleNotWellFormed(String(tag));
  2169. }
  2170. tag = platform.normalizeLanguageTag(tag);
  2171. if (tag !== undefined && callInstanceFunc(ArrayInstanceIndexOf, seen, tag) === -1) {
  2172. callInstanceFunc(ArrayInstancePush, seen, tag);
  2173. }
  2174. });
  2175. return seen;
  2176. }
  2177. function LookupSupportedLocales(requestedLocales, fitter, defaultLocale) {
  2178. var subset = [];
  2179. var count = 0;
  2180. callInstanceFunc(ArrayInstanceForEach, requestedLocales, function (locale) {
  2181. try {
  2182. var resolved = resolveLocaleHelper(locale, fitter, undefined, defaultLocale);
  2183. if (resolved.locale) {
  2184. ObjectDefineProperty(subset, count, { value: resolved.locale, writable: false, configurable: false, enumerable: true });
  2185. count = count + 1;
  2186. }
  2187. } catch (ex) {
  2188. throwExIfOOMOrSOE(ex);
  2189. // Expecting an error (other than OOM or SOE), same as fitter returning undefined
  2190. }
  2191. });
  2192. ObjectDefineProperty(subset, "length", { value: count, writable: false, configurable: false });
  2193. return subset;
  2194. }
  2195. var supportedLocalesOfWrapper = function (that, functionName, locales, options) {
  2196. if (that === null || that === undefined) {
  2197. platform.raiseNotAConstructor(functionName);
  2198. }
  2199. var hiddenObj = platform.getHiddenObject(that);
  2200. if (!hiddenObj || hiddenObj.isValid !== "Valid") {
  2201. platform.raiseNotAConstructor(functionName);
  2202. }
  2203. return supportedLocalesOf(locales, options);
  2204. }
  2205. var canonicalizeLocaleListWrapper = function (that, functionName, locales) {
  2206. if (that === null || that === undefined) {
  2207. platform.raiseNotAConstructor(functionName);
  2208. }
  2209. var hiddenObj = platform.getHiddenObject(that);
  2210. if (!hiddenObj || hiddenObj.isValid !== "Valid") {
  2211. platform.raiseNotAConstructor(functionName);
  2212. }
  2213. return CanonicalizeLocaleList(locales);
  2214. }
  2215. // Shared among all the constructors
  2216. var supportedLocalesOf = function (locales, options) {
  2217. var matcher;
  2218. locales = CanonicalizeLocaleList(locales);
  2219. if (typeof options !== 'undefined') {
  2220. matcher = options.localeMatcher;
  2221. if (typeof matcher !== 'undefined') {
  2222. matcher = Internal.ToString(matcher);
  2223. if (matcher !== 'lookup' && matcher !== 'best fit') {
  2224. platform.raiseOptionValueOutOfRange_3(String(matcher), "localeMatcher", "['best fit', 'lookup']");
  2225. }
  2226. }
  2227. }
  2228. if (typeof matcher === 'undefined' || matcher === 'best fit') {
  2229. return LookupSupportedLocales(locales, resolveLocaleBestFit, platform.normalizeLanguageTag(strippedDefaultLocale()));
  2230. } else {
  2231. return LookupSupportedLocales(locales, resolveLocaleLookup, strippedDefaultLocale());
  2232. }
  2233. };
  2234. const intlStaticMethodThisArg = setPrototype({}, null);
  2235. platform.setHiddenObject(intlStaticMethodThisArg, setPrototype({ isValid: "Valid" }, null));
  2236. // We wrap these functions so that we can define the correct name for this function for each Intl constructor,
  2237. // which allows us to display the correct error message for each Intl type.
  2238. const collator_supportedLocalesOf_name = "Intl.Collator.supportedLocalesOf";
  2239. const collator_supportedLocalesOf = callInstanceFunc(FunctionInstanceBind, tagPublicFunction(collator_supportedLocalesOf_name,
  2240. function collator_supportedLocalesOf_dummyName(locales, options = undefined) {
  2241. return supportedLocalesOfWrapper(this, collator_supportedLocalesOf_name, locales, options);
  2242. }), intlStaticMethodThisArg);
  2243. const numberFormat_supportedLocalesOf_name = "Intl.NumberFormat.supportedLocalesOf";
  2244. const numberFormat_supportedLocalesOf = callInstanceFunc(FunctionInstanceBind, tagPublicFunction(numberFormat_supportedLocalesOf_name,
  2245. function numberFormat_supportedLocalesOf_dummyName(locales, options = undefined) {
  2246. return supportedLocalesOfWrapper(this, numberFormat_supportedLocalesOf_name, locales, options);
  2247. }), intlStaticMethodThisArg);
  2248. const dateTimeFormat_supportedLocalesOf_name = "Intl.DateTimeFormat.supportedLocalesOf";
  2249. const dateTimeFormat_supportedLocalesOf = callInstanceFunc(FunctionInstanceBind, tagPublicFunction(dateTimeFormat_supportedLocalesOf_name,
  2250. function dateTimeFormat_supportedLocalesOf_dummyName(locales, options = undefined) {
  2251. return supportedLocalesOfWrapper(this, dateTimeFormat_supportedLocalesOf_name, locales, options);
  2252. }), intlStaticMethodThisArg);
  2253. const getCanonicalLocales_name = "Intl.getCanonicalLocales";
  2254. const getCanonicalLocales = callInstanceFunc(FunctionInstanceBind, tagPublicFunction(getCanonicalLocales_name,
  2255. function getCanonicalLocales_dummyName(locales) {
  2256. return canonicalizeLocaleListWrapper(this, getCanonicalLocales_name, locales);
  2257. }), intlStaticMethodThisArg);
  2258. // TODO: Bound functions get the "bound" prefix by default, so we need to remove it.
  2259. // When https://github.com/Microsoft/ChakraCore/issues/637 is fixed and we have a way
  2260. // to make built-in functions non-constructible, we can remove the call to
  2261. // Function.prototype.bind (i.e. FunctionInstanceBind) and just rely on tagging instead of setting the "name" manually.
  2262. ObjectDefineProperty(collator_supportedLocalesOf, 'name', { value: 'supportedLocalesOf' });
  2263. ObjectDefineProperty(numberFormat_supportedLocalesOf, 'name', { value: 'supportedLocalesOf' });
  2264. ObjectDefineProperty(dateTimeFormat_supportedLocalesOf, 'name', { value: 'supportedLocalesOf' });
  2265. ObjectDefineProperty(getCanonicalLocales, 'name', { value: 'getCanonicalLocales' });
  2266. // If an empty string is encountered for the value of the property; that means that is by default.
  2267. // So in the case of zh-TW; "default" and "stroke" are the same.
  2268. // This list was discussed with AnBorod, AnGlass and SureshJa.
  2269. var localesAcceptingCollationValues = setPrototype({
  2270. "es-ES": setPrototype({ "trad": "tradnl" }, null),
  2271. "lv-LV": setPrototype({ "trad": "tradnl" }, null),
  2272. "de-DE": setPrototype({ "phonebk": "phoneb" }, null),
  2273. "ja-JP": setPrototype({ "unihan": "radstr" }, null),
  2274. // We believe "pronun" means "pronunciation"
  2275. "zh-TW": setPrototype({ "phonetic": "pronun", "unihan": "radstr", "stroke": "" }, null),
  2276. "zh-HK": setPrototype({ "unihan": "radstr", "stroke": "" }, null),
  2277. "zh-MO": setPrototype({ "unihan": "radstr", "stroke": "" }, null),
  2278. "zh-CN": setPrototype({ "stroke": "stroke", "pinyin": "" }, null),
  2279. "zh-SG": setPrototype({ "stroke": "stroke", "pinyin": "" }, null)
  2280. // The following locales are supported by Windows; however, no BCP47 equivalent collation values were found for these.
  2281. // In future releases; this list (plus most of the Collator implementation) will be changed/removed as the platform support is expected to change.
  2282. // "hu-HU": ["technl"],
  2283. // "ka-GE": ["modern"],
  2284. // "x-IV": ["mathan"]
  2285. }, null);
  2286. // reverses the keys and values in each locale's sub-object in localesAcceptingCollationValues
  2287. // localesAcceptingCollationValues[locale][key] = value -> reverseLocalesAcceptingCollationValues[locale][value] = key
  2288. var reverseLocalesAcceptingCollationValues = (function () {
  2289. const toReturn = setPrototype({}, null);
  2290. callInstanceFunc(ArrayInstanceForEach, ObjectGetOwnPropertyNames(localesAcceptingCollationValues), function (locale) {
  2291. const collationValuesForLocale = localesAcceptingCollationValues[locale];
  2292. const reversedCollationValues = setPrototype({}, null);
  2293. callInstanceFunc(ArrayInstanceForEach, ObjectGetOwnPropertyNames(collationValuesForLocale), function (collation) {
  2294. const windowsTag = collationValuesForLocale[collation];
  2295. if (windowsTag !== "") {
  2296. reversedCollationValues[windowsTag] = collation;
  2297. }
  2298. });
  2299. toReturn[locale] = reversedCollationValues;
  2300. });
  2301. return toReturn;
  2302. }());
  2303. // mappedDefaultLocale will get the default locale and update any deprecated
  2304. // collation/sort order values it may use
  2305. let __mappedDefaultLocale = undefined;
  2306. var mappedDefaultLocale = function () {
  2307. if (__mappedDefaultLocale && platform.useCaches) {
  2308. return __mappedDefaultLocale;
  2309. }
  2310. let locale = undefined;
  2311. let collation = undefined;
  2312. if (isPlatformUsingICU) {
  2313. // ICU's getDefaultLocale() will return a valid BCP-47/RFC 5646 langtag
  2314. locale = GetDefaultLocale();
  2315. const match = platform.builtInRegexMatch(locale, /-u(?:-[^\-][^\-]?-[^\-]+)*-co-([^\-]+).*/);
  2316. if (match) {
  2317. // if the system default locale had a collation, strip it for now
  2318. // we will add the collation back later in this function
  2319. collation = match[1];
  2320. locale = callInstanceFunc(StringInstanceReplace, locale, `-co-${collation}`, "");
  2321. }
  2322. } else {
  2323. // Windows' getDefaultLocale() will return a RFC4646 langtag
  2324. const parts = platform.builtInRegexMatch(GetDefaultLocale(), /([^_]*)_?(.+)?/);
  2325. locale = parts[1];
  2326. collation = parts[2];
  2327. }
  2328. if (collation === undefined) {
  2329. __mappedDefaultLocale = locale;
  2330. return __mappedDefaultLocale;
  2331. }
  2332. // we stripped the -co-collation or _collation above, so this function adds it back
  2333. const createLocaleCollationString = function (finalLocale, finalCollation) {
  2334. if (isPlatformUsingICU) {
  2335. return `${finalLocale}-co-${finalCollation}`;
  2336. } else {
  2337. return `${finalLocale}-u-co-${finalCollation}`;
  2338. }
  2339. };
  2340. const collationMapForLocale = reverseLocalesAcceptingCollationValues[locale];
  2341. if (collationMapForLocale === undefined) {
  2342. // Assume the system wouldn't give us back a bad collation value
  2343. __mappedDefaultLocale = createLocaleCollationString(locale, collation);
  2344. return __mappedDefaultLocale;
  2345. }
  2346. const mappedCollation = collationMapForLocale[collation];
  2347. if (mappedCollation !== undefined) {
  2348. __mappedDefaultLocale = createLocaleCollationString(locale, mappedCollation);
  2349. } else {
  2350. __mappedDefaultLocale = createLocaleCollationString(locale, collation);
  2351. }
  2352. return __mappedDefaultLocale;
  2353. };
  2354. // Intl.Collator, String.prototype.localeCompare
  2355. var Collator = (function () {
  2356. if (InitType === 'Intl' || InitType === 'String') {
  2357. function InitializeCollator(collator, localeList, options) {
  2358. if (typeof collator != "object") {
  2359. platform.raiseNeedObject();
  2360. }
  2361. if (callInstanceFunc(ObjectInstanceHasOwnProperty, collator, '__initializedIntlObject') && collator.__initializedIntlObject) {
  2362. platform.raiseObjectIsAlreadyInitialized("Collator", "Collator");
  2363. }
  2364. collator.__initializedIntlObject = true;
  2365. // Extract options
  2366. if (typeof options === 'undefined') {
  2367. options = setPrototype({}, null);
  2368. } else {
  2369. options = Internal.ToObject(options);
  2370. }
  2371. var matcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit");
  2372. var usage = GetOption(options, "usage", "string", ["sort", "search"], "sort");
  2373. var sensitivity = GetOption(options, "sensitivity", "string", ["base", "accent", "case", "variant"], undefined);
  2374. var ignorePunctuation = GetOption(options, "ignorePunctuation", "boolean", undefined, false);
  2375. var caseFirst = GetOption(options, "caseFirst", "string", ["upper", "lower", "false"], undefined);
  2376. var numeric = GetOption(options, "numeric", "boolean", [true, false], undefined);
  2377. // Deal with the locales and extensions
  2378. localeList = CanonicalizeLocaleList(localeList);
  2379. var resolvedLocaleInfo = resolveLocales(localeList, matcher, undefined, mappedDefaultLocale);
  2380. var collation = "default";
  2381. var resolvedLocaleLookup = resolveLocaleLookup(resolvedLocaleInfo.localeWithoutSubtags);
  2382. var collationAugmentedLocale = resolvedLocaleLookup;
  2383. if (resolvedLocaleInfo.subTags) {
  2384. callInstanceFunc(ArrayInstanceForEach, resolvedLocaleInfo.subTags, function (subTag) {
  2385. var parts = platform.builtInRegexMatch(subTag, /([^-]*)-?(.*)?/); // [0] entire thing; [1] key; [2] value
  2386. var key = parts[1];
  2387. var value = parts[2] === "" ? undefined : parts[2];
  2388. if (key === "kf" && caseFirst === undefined) {
  2389. caseFirst = GetOption(setPrototype({ caseFirst: value }, null), "caseFirst", "string", ["upper", "lower", "false"], undefined);
  2390. } else if (key === "kn" && numeric === undefined) {
  2391. if (value !== undefined) {
  2392. numeric = Internal.ToLogicalBoolean(callInstanceFunc(StringInstanceToLowerCase, value) === "true");
  2393. } else {
  2394. numeric = true;
  2395. }
  2396. } else if (key === "co" && value !== undefined && value !== "default" && value !== "search" && value !== "sort" && value !== "standard") {
  2397. // Ignore these collation values as they shouldn't have any impact
  2398. collation = value;
  2399. }
  2400. });
  2401. }
  2402. if (collation !== "default") {
  2403. var accepedCollationForLocale = localesAcceptingCollationValues[collationAugmentedLocale];
  2404. var windowsCollation = "";
  2405. if (accepedCollationForLocale !== undefined && (windowsCollation = accepedCollationForLocale[collation]) !== undefined) {
  2406. if (windowsCollation !== "") {
  2407. collationAugmentedLocale = collationAugmentedLocale + "_" + windowsCollation;
  2408. }
  2409. }
  2410. else {
  2411. collation = "default";
  2412. }
  2413. }
  2414. // Correct options if need be.
  2415. if (caseFirst === undefined) {
  2416. try {
  2417. var num = platform.compareString('A', 'a', resolvedLocaleLookup, undefined, undefined, undefined, undefined);
  2418. } catch (e) {
  2419. // Rethrow OOM or SOE
  2420. throwExIfOOMOrSOE(e);
  2421. // Otherwise, Generic message to cover the exception throw from the CompareStringEx api.
  2422. // The platform's exception is also generic and in most if not all cases specifies that "a" argument is invalid.
  2423. // We have no other information from the platform on the cause of the exception.
  2424. platform.raiseOptionValueOutOfRange();
  2425. }
  2426. if (num === 0) {
  2427. caseFirst = 'false';
  2428. } else if (num === -1) {
  2429. caseFirst = 'upper';
  2430. } else {
  2431. caseFirst = 'lower';
  2432. }
  2433. }
  2434. if (sensitivity === undefined) {
  2435. sensitivity = "variant";
  2436. }
  2437. if (numeric === undefined) {
  2438. numeric = false;
  2439. }
  2440. // Set the options on the object
  2441. collator.__matcher = matcher;
  2442. collator.__locale = resolvedLocaleInfo.localeWithoutSubtags;
  2443. collator.__localeForCompare = collationAugmentedLocale;
  2444. collator.__usage = usage;
  2445. collator.__sensitivity = sensitivity;
  2446. collator.__ignorePunctuation = ignorePunctuation;
  2447. collator.__caseFirst = caseFirst;
  2448. collator.__numeric = numeric;
  2449. collator.__collation = collation;
  2450. collator.__initializedCollator = true;
  2451. }
  2452. platform.registerBuiltInFunction(tagPublicFunction("String.prototype.localeCompare", function () {
  2453. var that = arguments[0];
  2454. if (this === undefined || this === null) {
  2455. platform.raiseThis_NullOrUndefined("String.prototype.localeCompare");
  2456. }
  2457. else if (that === null) {
  2458. platform.raiseNeedObject();
  2459. }
  2460. // ToString must be called on this/that argument before we do any other operation, as other operations in InitializeCollator may also be observable
  2461. var thisArg = String(this);
  2462. var that = String(that);
  2463. var stateObject = setPrototype({}, null);
  2464. InitializeCollator(stateObject, arguments[1], arguments[2]);
  2465. return Number(platform.compareString(
  2466. thisArg,
  2467. that,
  2468. stateObject.__localeForCompare,
  2469. toEnum(CollatorSensitivity, stateObject.__sensitivity),
  2470. stateObject.__ignorePunctuation,
  2471. stateObject.__numeric,
  2472. toEnum(CollatorCaseFirst, stateObject.__caseFirst)
  2473. ));
  2474. }), IntlBuiltInFunctionID.StringLocaleCompare);
  2475. if (InitType === 'Intl') {
  2476. function Collator(locales = undefined, options = undefined) {
  2477. if (this === Intl || this === undefined) {
  2478. return new Collator(locales, options);
  2479. }
  2480. let obj = Internal.ToObject(this);
  2481. if (!ObjectIsExtensible(obj)) {
  2482. platform.raiseObjectIsNonExtensible("Collator");
  2483. }
  2484. // Use the hidden object to store data
  2485. let hiddenObject = platform.getHiddenObject(obj);
  2486. if (hiddenObject === undefined) {
  2487. hiddenObject = setPrototype({}, null);
  2488. platform.setHiddenObject(obj, hiddenObject);
  2489. }
  2490. InitializeCollator(hiddenObject, locales, options);
  2491. // Add the bound compare
  2492. hiddenObject.__boundCompare = callInstanceFunc(FunctionInstanceBind, compare, obj);
  2493. delete hiddenObject.__boundCompare.name;
  2494. return obj;
  2495. }
  2496. tagPublicFunction("Intl.Collator", Collator);
  2497. function compare(a, b) {
  2498. if (typeof this !== 'object') {
  2499. platform.raiseNeedObjectOfType("Collator.prototype.compare", "Collator");
  2500. }
  2501. var hiddenObject = platform.getHiddenObject(this);
  2502. if (hiddenObject === undefined || !hiddenObject.__initializedCollator) {
  2503. platform.raiseNeedObjectOfType("Collator.prototype.compare", "Collator");
  2504. }
  2505. a = String(a);
  2506. b = String(b);
  2507. return Number(platform.compareString(
  2508. a,
  2509. b,
  2510. hiddenObject.__localeForCompare,
  2511. toEnum(CollatorSensitivity, hiddenObject.__sensitivity),
  2512. hiddenObject.__ignorePunctuation,
  2513. hiddenObject.__numeric,
  2514. toEnum(CollatorCaseFirst, hiddenObject.__caseFirst)
  2515. ));
  2516. }
  2517. tagPublicFunction("Intl.Collator.prototype.compare", compare);
  2518. ObjectDefineProperty(Collator, 'supportedLocalesOf', { value: collator_supportedLocalesOf, writable: true, configurable: true });
  2519. ObjectDefineProperty(Collator, 'prototype', { value: new Collator(), writable: false, enumerable: false, configurable: false });
  2520. setPrototype(Collator.prototype, Object.prototype);
  2521. ObjectDefineProperty(Collator.prototype, 'constructor', { value: Collator, writable: true, enumerable: false, configurable: true });
  2522. ObjectDefineProperty(Collator.prototype, 'resolvedOptions', {
  2523. value: function resolvedOptions() {
  2524. if (typeof this !== 'object') {
  2525. platform.raiseNeedObjectOfType("Collator.prototype.resolvedOptions", "Collator");
  2526. }
  2527. var hiddenObject = platform.getHiddenObject(this);
  2528. if (hiddenObject === undefined || !hiddenObject.__initializedCollator) {
  2529. platform.raiseNeedObjectOfType("Collator.prototype.resolvedOptions", "Collator");
  2530. }
  2531. return {
  2532. locale: hiddenObject.__locale,
  2533. usage: hiddenObject.__usage,
  2534. sensitivity: hiddenObject.__sensitivity,
  2535. ignorePunctuation: hiddenObject.__ignorePunctuation,
  2536. collation: hiddenObject.__collation, // "co" unicode extension
  2537. numeric: hiddenObject.__numeric, // "ka" unicode extension TODO: Determine if this is supported (doesn't have to be)
  2538. caseFirst: hiddenObject.__caseFirst // "kf" unicode extension TODO: Determine if this is supported (doesn't have to be)
  2539. }
  2540. }, writable: true, enumerable: false, configurable: true
  2541. });
  2542. ObjectDefineProperty(Collator.prototype, 'compare', {
  2543. get: tagPublicFunction('get compare', function () {
  2544. if (typeof this !== 'object') {
  2545. platform.raiseNeedObjectOfType("Collator.prototype.compare", "Collator");
  2546. }
  2547. var hiddenObject = platform.getHiddenObject(this);
  2548. if (hiddenObject === undefined || !hiddenObject.__initializedCollator) {
  2549. platform.raiseNeedObjectOfType("Collator.prototype.compare", "Collator");
  2550. }
  2551. return hiddenObject.__boundCompare;
  2552. }), enumerable: false, configurable: true
  2553. });
  2554. return Collator;
  2555. }
  2556. }
  2557. // 'Init.Collator' not defined if reached here. Return 'undefined'
  2558. return undefined;
  2559. })();
  2560. // Intl.NumberFormat, Number.prototype.toLocaleString
  2561. var NumberFormat = (function () {
  2562. if (InitType === 'Intl' || InitType === 'Number') {
  2563. function InitializeNumberFormat(numberFormat, localeList, options) {
  2564. if (typeof numberFormat != "object") {
  2565. platform.raiseNeedObject();
  2566. }
  2567. if (callInstanceFunc(ObjectInstanceHasOwnProperty, numberFormat, '__initializedIntlObject') && numberFormat.__initializedIntlObject) {
  2568. platform.raiseObjectIsAlreadyInitialized("NumberFormat", "NumberFormat");
  2569. }
  2570. numberFormat.__initializedIntlObject = true;
  2571. // Extract options
  2572. if (typeof options === 'undefined') {
  2573. options = setPrototype({}, null);
  2574. } else {
  2575. options = Internal.ToObject(options);
  2576. }
  2577. var matcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit");
  2578. var style = GetOption(options, "style", "string", ["decimal", "percent", "currency"], "decimal");
  2579. var formatterToUse = NumberFormatStyle.DECIMAL; // DEFAULT
  2580. if (style === 'percent') {
  2581. formatterToUse = NumberFormatStyle.PERCENT;
  2582. } else if (style === 'currency') {
  2583. formatterToUse = NumberFormatStyle.CURRENCY;
  2584. }
  2585. var currency = GetOption(options, "currency", "string", undefined, undefined);
  2586. var currencyDisplay = GetOption(options, 'currencyDisplay', 'string', ['code', 'symbol', 'name'], 'symbol');
  2587. var currencyDigits = undefined;
  2588. var minimumIntegerDigits = GetNumberOption(options, 'minimumIntegerDigits', 1, 21, 1);
  2589. var minimumFractionDigits = undefined;
  2590. var maximumFractionDigits = undefined;
  2591. var maximumFractionDigitsDefault = undefined;
  2592. var minimumSignificantDigits = options.minimumSignificantDigits;
  2593. var maximumSignificantDigits = options.maximumSignificantDigits;
  2594. if (typeof minimumSignificantDigits !== 'undefined' || typeof maximumSignificantDigits !== 'undefined') {
  2595. minimumSignificantDigits = GetNumberOption(options, 'minimumSignificantDigits', 1, 21, 1);
  2596. maximumSignificantDigits = GetNumberOption(options, 'maximumSignificantDigits', minimumSignificantDigits, 21, 21);
  2597. }
  2598. var useGrouping = GetOption(options, 'useGrouping', 'boolean', undefined, true);
  2599. // Deal with the locales and extensions
  2600. localeList = CanonicalizeLocaleList(localeList);
  2601. var resolvedLocaleInfo = resolveLocales(localeList, matcher, ["nu"], strippedDefaultLocale);
  2602. // Correct the options if necessary
  2603. if (typeof currency !== 'undefined' && !IsWellFormedCurrencyCode(currency)) {
  2604. platform.raiseInvalidCurrencyCode(String(currency));
  2605. }
  2606. if (style === "currency") {
  2607. if (typeof currency === 'undefined') {
  2608. platform.raiseMissingCurrencyCode();
  2609. }
  2610. currency = callInstanceFunc(StringInstanceToUpperCase, currency);
  2611. try {
  2612. currencyDigits = platform.currencyDigits(currency);
  2613. } catch (e) {
  2614. throwExIfOOMOrSOE(e);
  2615. platform.raiseInvalidCurrencyCode(String(currency));
  2616. }
  2617. minimumFractionDigits = GetNumberOption(options, 'minimumFractionDigits', 0, 20, currencyDigits);
  2618. maximumFractionDigitsDefault = Math.max(currencyDigits, minimumFractionDigits);
  2619. } else {
  2620. currency = undefined;
  2621. currencyDisplay = undefined;
  2622. minimumFractionDigits = GetNumberOption(options, 'minimumFractionDigits', 0, 20, 0);
  2623. if (style === "percent") {
  2624. maximumFractionDigitsDefault = Math.max(minimumFractionDigits, 0);
  2625. } else {
  2626. maximumFractionDigitsDefault = Math.max(minimumFractionDigits, 3)
  2627. }
  2628. }
  2629. maximumFractionDigits = GetNumberOption(options, 'maximumFractionDigits', minimumFractionDigits, 20, maximumFractionDigitsDefault);
  2630. // Set the options on the object
  2631. numberFormat.__localeMatcher = matcher;
  2632. numberFormat.__locale = resolvedLocaleInfo.locale;
  2633. numberFormat.__style = style;
  2634. if (currency !== undefined) {
  2635. numberFormat.__currency = currency;
  2636. }
  2637. if (currencyDisplay !== undefined) {
  2638. numberFormat.__currencyDisplay = currencyDisplay;
  2639. numberFormat.__currencyDisplayToUse = NumberFormatCurrencyDisplay.DEFAULT;
  2640. if (currencyDisplay === "symbol") {
  2641. numberFormat.__currencyDisplayToUse = NumberFormatCurrencyDisplay.SYMBOL;
  2642. } else if (currencyDisplay === "code") {
  2643. numberFormat.__currencyDisplayToUse = NumberFormatCurrencyDisplay.CODE;
  2644. } else if (currencyDisplay === "name") {
  2645. numberFormat.__currencyDisplayToUse = NumberFormatCurrencyDisplay.NAME;
  2646. }
  2647. }
  2648. numberFormat.__minimumIntegerDigits = minimumIntegerDigits;
  2649. numberFormat.__minimumFractionDigits = minimumFractionDigits;
  2650. numberFormat.__maximumFractionDigits = maximumFractionDigits;
  2651. if (maximumSignificantDigits !== undefined) {
  2652. numberFormat.__minimumSignificantDigits = minimumSignificantDigits;
  2653. numberFormat.__maximumSignificantDigits = maximumSignificantDigits;
  2654. }
  2655. numberFormat.__formatterToUse = formatterToUse;
  2656. numberFormat.__useGrouping = useGrouping;
  2657. try {
  2658. // Cache api instance and update numbering system on the object
  2659. platform.cacheNumberFormat(numberFormat);
  2660. } catch (e) {
  2661. throwExIfOOMOrSOE(e);
  2662. // Generic message to cover the exception throw from the platform.
  2663. // The platform's exception is also generic and in most if not all cases specifies that "a" argument is invalid.
  2664. // We have no other information from the platform on the cause of the exception.
  2665. platform.raiseOptionValueOutOfRange();
  2666. }
  2667. if (!numberFormat.__numberingSystem) {
  2668. numberFormat.__numberingSystem = "latn"; // assume Latin numerals by default
  2669. }
  2670. numberFormat.__numberingSystem = callInstanceFunc(StringInstanceToLowerCase, numberFormat.__numberingSystem);
  2671. numberFormat.__initializedNumberFormat = true;
  2672. }
  2673. platform.registerBuiltInFunction(tagPublicFunction("Number.prototype.toLocaleString", function () {
  2674. if ((typeof this) !== 'number' && !(this instanceof Number)) {
  2675. platform.raiseNeedObjectOfType("Number.prototype.toLocaleString", "Number");
  2676. }
  2677. var stateObject = setPrototype({}, null);
  2678. InitializeNumberFormat(stateObject, arguments[0], arguments[1]);
  2679. var n = Internal.ToNumber(this);
  2680. return String(platform.formatNumber(n, stateObject));
  2681. }), IntlBuiltInFunctionID.NumberToLocaleString);
  2682. if (InitType === 'Intl') {
  2683. function NumberFormat(locales = undefined, options = undefined) {
  2684. if (this === Intl || this === undefined) {
  2685. return new NumberFormat(locales, options);
  2686. }
  2687. let obj = Internal.ToObject(this);
  2688. if (!ObjectIsExtensible(obj)) {
  2689. platform.raiseObjectIsNonExtensible("NumberFormat");
  2690. }
  2691. // Use the hidden object to store data
  2692. let hiddenObject = platform.getHiddenObject(obj);
  2693. if (hiddenObject === undefined) {
  2694. hiddenObject = setPrototype({}, null);
  2695. platform.setHiddenObject(obj, hiddenObject);
  2696. }
  2697. InitializeNumberFormat(hiddenObject, locales, options);
  2698. hiddenObject.__boundFormat = callInstanceFunc(FunctionInstanceBind, format, obj)
  2699. delete hiddenObject.__boundFormat.name;
  2700. return obj;
  2701. }
  2702. tagPublicFunction("Intl.NumberFormat", NumberFormat);
  2703. function format(n) {
  2704. n = Internal.ToNumber(n);
  2705. if (typeof this !== 'object') {
  2706. platform.raiseNeedObjectOfType("NumberFormat.prototype.format", "NumberFormat");
  2707. }
  2708. var hiddenObject = platform.getHiddenObject(this);
  2709. if (hiddenObject === undefined || !hiddenObject.__initializedNumberFormat) {
  2710. platform.raiseNeedObjectOfType("NumberFormat.prototype.format", "NumberFormat");
  2711. }
  2712. return String(platform.formatNumber(n, hiddenObject));
  2713. }
  2714. tagPublicFunction("Intl.NumberFormat.prototype.format", format);
  2715. ObjectDefineProperty(NumberFormat, 'supportedLocalesOf', { value: numberFormat_supportedLocalesOf, writable: true, configurable: true });
  2716. var options = ['locale', 'numberingSystem', 'style', 'currency', 'currencyDisplay', 'minimumIntegerDigits',
  2717. 'minimumFractionDigits', 'maximumFractionDigits', 'minimumSignificantDigits', 'maximumSignificantDigits',
  2718. 'useGrouping'];
  2719. ObjectDefineProperty(NumberFormat, 'prototype', { value: new NumberFormat(), writable: false, enumerable: false, configurable: false });
  2720. setPrototype(NumberFormat.prototype, Object.prototype);
  2721. ObjectDefineProperty(NumberFormat.prototype, 'constructor', { value: NumberFormat, writable: true, enumerable: false, configurable: true });
  2722. ObjectDefineProperty(NumberFormat.prototype, 'resolvedOptions', {
  2723. value: function resolvedOptions() {
  2724. if (typeof this !== 'object') {
  2725. platform.raiseNeedObjectOfType("NumberFormat.prototype.resolvedOptions", "NumberFormat");
  2726. }
  2727. var hiddenObject = platform.getHiddenObject(this);
  2728. if (hiddenObject === undefined || !hiddenObject.__initializedNumberFormat) {
  2729. platform.raiseNeedObjectOfType("NumberFormat.prototype.resolvedOptions", "NumberFormat");
  2730. }
  2731. var resolvedOptions = setPrototype({}, null);
  2732. callInstanceFunc(ArrayInstanceForEach, options, function (option) {
  2733. if (typeof hiddenObject['__' + option] !== 'undefined') {
  2734. resolvedOptions[option] = hiddenObject['__' + option];
  2735. }
  2736. });
  2737. return setPrototype(resolvedOptions, {});
  2738. }, writable: true, enumerable: false, configurable: true
  2739. });
  2740. ObjectDefineProperty(NumberFormat.prototype, 'format', {
  2741. get: tagPublicFunction('get format', function () {
  2742. if (typeof this !== 'object') {
  2743. platform.raiseNeedObjectOfType("NumberFormat.prototype.format", "NumberFormat");
  2744. }
  2745. var hiddenObject = platform.getHiddenObject(this);
  2746. if (hiddenObject === undefined || !hiddenObject.__initializedNumberFormat) {
  2747. platform.raiseNeedObjectOfType("NumberFormat.prototype.format", "NumberFormat");
  2748. }
  2749. return hiddenObject.__boundFormat;
  2750. }), enumerable: false, configurable: true
  2751. });
  2752. return NumberFormat;
  2753. }
  2754. }
  2755. // 'Init.NumberFormat' not defined if reached here. Return 'undefined'
  2756. return undefined;
  2757. })();
  2758. // Intl.DateTimeFormat, Date.prototype.toLocaleString, Date.prototype.toLocaleDateString, Date.prototype.toLocaleTimeString
  2759. var DateTimeFormat = (function () {
  2760. if (InitType === 'Intl' || InitType === 'Date') {
  2761. function ToDateTimeOptions(options, required, defaults) {
  2762. if (options === undefined) {
  2763. options = setPrototype({}, null);
  2764. } else {
  2765. options = Internal.ToObject(options);
  2766. }
  2767. var needDefaults = true;
  2768. if (required === "date" || required === "any") {
  2769. if (options.weekday !== undefined || options.year !== undefined || options.month !== undefined || options.day !== undefined) {
  2770. needDefaults = false;
  2771. }
  2772. }
  2773. if (required === "time" || required === "any") {
  2774. if (options.hour !== undefined || options.minute !== undefined || options.second !== undefined) {
  2775. needDefaults = false;
  2776. }
  2777. }
  2778. if (needDefaults && (defaults === "date" || defaults === "all")) {
  2779. ObjectDefineProperty(options, "year", {
  2780. value: "numeric", writable: true,
  2781. enumerable: true, configurable: true
  2782. });
  2783. ObjectDefineProperty(options, "month", {
  2784. value: "numeric", writable: true,
  2785. enumerable: true, configurable: true
  2786. });
  2787. ObjectDefineProperty(options, "day", {
  2788. value: "numeric", writable: true,
  2789. enumerable: true, configurable: true
  2790. });
  2791. }
  2792. if (needDefaults && (defaults === "time" || defaults === "all")) {
  2793. ObjectDefineProperty(options, "hour", {
  2794. value: "numeric", writable: true,
  2795. enumerable: true, configurable: true
  2796. });
  2797. ObjectDefineProperty(options, "minute", {
  2798. value: "numeric", writable: true,
  2799. enumerable: true, configurable: true
  2800. });
  2801. ObjectDefineProperty(options, "second", {
  2802. value: "numeric", writable: true,
  2803. enumerable: true, configurable: true
  2804. });
  2805. }
  2806. return options;
  2807. }
  2808. // Currently you cannot format date pieces and time pieces together, so this builds up a format template for each separately.
  2809. function EcmaOptionsToWindowsTemplate(options) {
  2810. var template = [];
  2811. if (options.weekday) {
  2812. if (options.weekday === 'narrow' || options.weekday === 'short') {
  2813. callInstanceFunc(ArrayInstancePush, template, 'dayofweek.abbreviated');
  2814. } else if (options.weekday === 'long') {
  2815. callInstanceFunc(ArrayInstancePush, template, 'dayofweek.full');
  2816. }
  2817. }
  2818. // TODO: Era not supported
  2819. if (options.year) {
  2820. if (options.year === '2-digit') {
  2821. callInstanceFunc(ArrayInstancePush, template, 'year.abbreviated');
  2822. } else if (options.year === 'numeric') {
  2823. callInstanceFunc(ArrayInstancePush, template, 'year.full');
  2824. }
  2825. }
  2826. if (options.month) {
  2827. if (options.month === '2-digit' || options.month === 'numeric') {
  2828. callInstanceFunc(ArrayInstancePush, template, 'month.numeric')
  2829. } else if (options.month === 'short' || options.month === 'narrow') {
  2830. callInstanceFunc(ArrayInstancePush, template, 'month.abbreviated');
  2831. } else if (options.month === 'long') {
  2832. callInstanceFunc(ArrayInstancePush, template, 'month.full');
  2833. }
  2834. }
  2835. if (options.day) {
  2836. callInstanceFunc(ArrayInstancePush, template, 'day');
  2837. }
  2838. if (options.timeZoneName) {
  2839. if (options.timeZoneName === "short") {
  2840. callInstanceFunc(ArrayInstancePush, template, 'timezone.abbreviated');
  2841. } else if (options.timeZoneName === "long") {
  2842. callInstanceFunc(ArrayInstancePush, template, 'timezone.full');
  2843. }
  2844. }
  2845. callInstanceFunc(ArrayInstanceForEach, ['hour', 'minute', 'second'], function (opt) {
  2846. if (options[opt]) {
  2847. callInstanceFunc(ArrayInstancePush, template, opt);
  2848. }
  2849. });
  2850. // TODO: Timezone Name not supported.
  2851. return getArrayLength(template) > 0 ? callInstanceFunc(ArrayInstanceJoin, template, ' ') : undefined;
  2852. }
  2853. var WindowsToEcmaCalendarMap = {
  2854. 'GregorianCalendar': 'gregory',
  2855. 'HebrewCalendar': 'hebrew',
  2856. 'HijriCalendar': 'islamic',
  2857. 'JapaneseCalendar': 'japanese',
  2858. 'JulianCalendar': 'julian',
  2859. 'KoreanCalendar': 'korean',
  2860. 'UmAlQuraCalendar': 'islamic-civil',
  2861. 'ThaiCalendar': 'thai',
  2862. 'TaiwanCalendar': 'taiwan'
  2863. };
  2864. function WindowsToEcmaCalendar(calendar) {
  2865. if (typeof calendar === 'undefined') {
  2866. return '';
  2867. }
  2868. return WindowsToEcmaCalendarMap[calendar] || 'gregory';
  2869. }
  2870. // Certain formats have similar patterns on both ecma and windows; will use helper methods for them
  2871. function correctWeekdayEraMonthPattern(patternString, userValue, searchParam) {
  2872. // parts[1] is either dayofweek.solo, dayofweek, era or month; parts[2] is either abbreviated or full
  2873. var parts = platform.builtInRegexMatch(patternString, RegExp("{(" + searchParam + "(?:\\.solo)?)\\.([a-z]*)(?:\\([0-9]\\))?}"));
  2874. // If this happens that means windows removed the specific pattern (which isn't expected; but better be safe)
  2875. if (parts === null) {
  2876. RaiseAssert(new Error("Error when correcting windows returned weekday/Era/Month pattern; regex returned null. \nInput was: '" + patternString + "'\nRegex: '" + "{(" + searchParam + "(\\.solo)?)\\.([a-z]*)(\\([0-9]\\))?}'"));
  2877. return patternString;
  2878. }
  2879. if (parts[2] !== "full" && userValue === "long") {
  2880. return callInstanceFunc(StringInstanceReplace, patternString, parts[0], "{" + parts[1] + "." + "full" + "}");
  2881. } else if (userValue !== "long") {
  2882. return callInstanceFunc(StringInstanceReplace, patternString, parts[0], "{" + parts[1] + "." + (userValue === "short" ? "abbreviated" : "abbreviated(1)") + "}");
  2883. }
  2884. return patternString;
  2885. }
  2886. function correctDayHourMinuteSecondMonthPattern(patternString, userValue, searchParam) {
  2887. // parts[1] is either month, day, hour, minute, or second
  2888. // REVIEW (doilij) is it even possible to have a '.solo' (i.e. /(?:\\.solo)?/ ) in the above cases?
  2889. var parts = platform.builtInRegexMatch(patternString, RegExp("{(" + searchParam + ")(?:\\.solo)?\\.([a-z]*)(?:\\([0-9]\\))?}"));
  2890. if (parts === null) {
  2891. RaiseAssert(new Error("Error when correcting windows returned day/hour/minute/second/month pattern; regex returned null. \nInput was: '" + patternString + "'\nRegex: '" + "{(" + searchParam + "(\\.solo)?)\\.([a-z]*)(\\([0-9]\\))?}'"));
  2892. return patternString;
  2893. }
  2894. // Only correct the 2 digit; unless part[2] isn't integer
  2895. if (userValue === "2-digit") {
  2896. return callInstanceFunc(StringInstanceReplace, patternString, parts[0], "{" + parts[1] + ".integer(2)}");
  2897. } else if (parts[2] !== "integer") {
  2898. return callInstanceFunc(StringInstanceReplace, patternString, parts[0], "{" + parts[1] + ".integer}");
  2899. }
  2900. return patternString;
  2901. }
  2902. // Perhaps the level of validation that we have might not be required for this method
  2903. function updatePatternStrings(patternString, dateTimeFormat) {
  2904. if (dateTimeFormat.__weekday !== undefined) {
  2905. patternString = correctWeekdayEraMonthPattern(patternString, dateTimeFormat.__weekday, "dayofweek");
  2906. }
  2907. if (dateTimeFormat.__era !== undefined) {
  2908. // This is commented because not all options are supported for locales that do have era;
  2909. // In addition, we can't force era to be part of a locale using templates.
  2910. // patternString = correctWeekdayEraMonthPattern(patternString, dateTimeFormat.__era, "era", 2);
  2911. }
  2912. if (dateTimeFormat.__year === "2-digit") {
  2913. var parts = platform.builtInRegexMatch(patternString, /\{year\.[a-z]*(\([0-9]\))?\}/);
  2914. if (parts === null) {
  2915. RaiseAssert(new Error("Error when correcting windows returned year; regex returned null"));
  2916. } else {
  2917. patternString = callInstanceFunc(StringInstanceReplace, patternString, parts[0], "{year.abbreviated(2)}");
  2918. }
  2919. } else if (dateTimeFormat.__year === "full") {
  2920. var parts = platform.builtInRegexMatch(patternString, /\{year\.[a-z]*(\([0-9]\))?\}/);
  2921. if (parts === null) {
  2922. RaiseAssert(new Error("Error when correcting windows returned year; regex returned null"));
  2923. } else {
  2924. patternString = callInstanceFunc(StringInstanceReplace, patternString, parts[0], "{year.full}");
  2925. }
  2926. }
  2927. // Month partially overlaps with weekday/month; unless it's 2-digit or numeric in which case it overlaps with day/hour/minute/second
  2928. if (dateTimeFormat.__month !== undefined && dateTimeFormat.__month !== "2-digit" && dateTimeFormat.__month !== "numeric") {
  2929. patternString = correctWeekdayEraMonthPattern(patternString, dateTimeFormat.__month, "month");
  2930. } else if (dateTimeFormat.__month !== undefined) {
  2931. patternString = correctDayHourMinuteSecondMonthPattern(patternString, dateTimeFormat.__month, "month");
  2932. }
  2933. if (dateTimeFormat.__day !== undefined) {
  2934. patternString = correctDayHourMinuteSecondMonthPattern(patternString, dateTimeFormat.__day, "day");
  2935. }
  2936. if (dateTimeFormat.__hour !== undefined) {
  2937. patternString = correctDayHourMinuteSecondMonthPattern(patternString, dateTimeFormat.__hour, "hour");
  2938. }
  2939. if (dateTimeFormat.__minute !== undefined) {
  2940. patternString = correctDayHourMinuteSecondMonthPattern(patternString, dateTimeFormat.__minute, "minute");
  2941. }
  2942. if (dateTimeFormat.__second !== undefined) {
  2943. patternString = correctDayHourMinuteSecondMonthPattern(patternString, dateTimeFormat.__second, "second");
  2944. }
  2945. if (dateTimeFormat.__timeZoneName !== undefined) {
  2946. patternString = correctWeekdayEraMonthPattern(patternString, dateTimeFormat.__timeZoneName, "timezone");
  2947. }
  2948. return patternString;
  2949. }
  2950. function InitializeDateTimeFormat(dateTimeFormat, localeList, options) {
  2951. if (typeof dateTimeFormat != "object") {
  2952. platform.raiseNeedObject();
  2953. }
  2954. if (callInstanceFunc(ObjectInstanceHasOwnProperty, dateTimeFormat, '__initializedIntlObject') && dateTimeFormat.__initializedIntlObject) {
  2955. platform.raiseObjectIsAlreadyInitialized("DateTimeFormat", "DateTimeFormat");
  2956. }
  2957. dateTimeFormat.__initializedIntlObject = true;
  2958. // Extract the options
  2959. options = ToDateTimeOptions(options, "any", "date");
  2960. var matcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit");
  2961. var timeZone = GetOption(options, "timeZone", "string", undefined, undefined);
  2962. if (timeZone !== undefined) {
  2963. timeZone = platform.validateAndCanonicalizeTimeZone(timeZone);
  2964. } else {
  2965. timeZone = platform.getDefaultTimeZone();
  2966. }
  2967. if (timeZone === undefined) {
  2968. platform.raiseOptionValueOutOfRange();
  2969. }
  2970. // Format options
  2971. var weekday = GetOption(options, "weekday", "string", ['narrow', 'short', 'long'], undefined);
  2972. var era = GetOption(options, "era", "string", ['narrow', 'short', 'long'], undefined);
  2973. var year = GetOption(options, "year", "string", ['2-digit', 'numeric'], undefined);
  2974. var month = GetOption(options, "month", "string", ['2-digit', 'numeric', 'narrow', 'short', 'long'], undefined);
  2975. var day = GetOption(options, "day", "string", ['2-digit', 'numeric'], undefined);
  2976. var hour = GetOption(options, "hour", "string", ['2-digit', 'numeric'], undefined);
  2977. var minute = GetOption(options, "minute", "string", ['2-digit', 'numeric'], undefined);
  2978. var second = GetOption(options, "second", "string", ['2-digit', 'numeric'], undefined);
  2979. var timeZoneName = GetOption(options, "timeZoneName", "string", ['short', 'long'], undefined);
  2980. var hour12 = hour ? GetOption(options, "hour12", "boolean", undefined, undefined) : undefined;
  2981. var formatMatcher = GetOption(options, "formatMatcher", "string", ["basic", "best fit"], "best fit");
  2982. var windowsClock = hour12 !== undefined ? (hour12 ? "12HourClock" : "24HourClock") : undefined;
  2983. var templateString = EcmaOptionsToWindowsTemplate(setPrototype({
  2984. weekday: weekday,
  2985. era: era,
  2986. year: year,
  2987. month: month,
  2988. day: day,
  2989. hour: hour,
  2990. minute: minute,
  2991. second: second,
  2992. timeZoneName: timeZoneName
  2993. }, null));
  2994. // Deal with the locale
  2995. localeList = CanonicalizeLocaleList(localeList);
  2996. var resolvedLocaleInfo = resolveLocales(localeList, matcher, ["nu", "ca"], strippedDefaultLocale);
  2997. // Assign the options
  2998. dateTimeFormat.__matcher = matcher;
  2999. dateTimeFormat.__timeZone = timeZone;
  3000. dateTimeFormat.__locale = resolvedLocaleInfo.locale;
  3001. // Format options
  3002. dateTimeFormat.__weekday = weekday;
  3003. dateTimeFormat.__era = era;
  3004. dateTimeFormat.__year = year;
  3005. dateTimeFormat.__month = month;
  3006. dateTimeFormat.__day = day;
  3007. dateTimeFormat.__hour = hour;
  3008. dateTimeFormat.__minute = minute;
  3009. dateTimeFormat.__second = second;
  3010. dateTimeFormat.__timeZoneName = timeZoneName;
  3011. dateTimeFormat.__hour12 = hour12;
  3012. dateTimeFormat.__formatMatcher = formatMatcher;
  3013. dateTimeFormat.__windowsClock = windowsClock;
  3014. dateTimeFormat.__templateString = templateString;
  3015. /*
  3016. * NOTE:
  3017. * Pattern string's are position-sensitive; while templates are not.
  3018. * If we specify {hour.integer(2)}:{minute.integer(2)} pattern string; we will always format as HH:mm.
  3019. * On the other hand, template strings don't give as fine granularity for options; and the platform decides how long month.abbreviated should be.
  3020. * Therefore, we have to create using template strings; and then change the .abbreivated/.integer values to have correct digits count if necessary.
  3021. * Thus, this results in this redundant looking code to create dateTimeFormat twice.
  3022. */
  3023. var errorThrown = false;
  3024. try {
  3025. // Create the DateTimeFormatter to extract pattern strings
  3026. CreateDateTimeFormat(dateTimeFormat, false);
  3027. } catch (e) {
  3028. // Rethrow SOE or OOM
  3029. throwExIfOOMOrSOE(e);
  3030. // We won't throw for the first exception, but assume the template strings were rejected.
  3031. // Instead, we will try to fall back to default template strings.
  3032. var defaultOptions = ToDateTimeOptions(options, "none", "all");
  3033. dateTimeFormat.__templateString = EcmaOptionsToWindowsTemplate(defaultOptions, null);
  3034. errorThrown = true;
  3035. }
  3036. if (!errorThrown) {
  3037. // Update the pattern strings
  3038. dateTimeFormat.__templateString = updatePatternStrings(dateTimeFormat.__patternStrings[0], dateTimeFormat);
  3039. }
  3040. try {
  3041. // Cache the date time formatter
  3042. CreateDateTimeFormat(dateTimeFormat, true);
  3043. } catch (e) {
  3044. // Rethrow SOE or OOM
  3045. throwExIfOOMOrSOE(e);
  3046. // Otherwise, Generic message to cover the exception throw from the platform.
  3047. // The platform's exception is also generic and in most if not all cases specifies that "a" argument is invalid.
  3048. // We have no other information from the platform on the cause of the exception.
  3049. platform.raiseOptionValueOutOfRange();
  3050. }
  3051. // Correct the api's updated
  3052. dateTimeFormat.__calendar = WindowsToEcmaCalendar(dateTimeFormat.__windowsCalendar);
  3053. dateTimeFormat.__numberingSystem = callInstanceFunc(StringInstanceToLowerCase, dateTimeFormat.__numberingSystem);
  3054. if (dateTimeFormat.__hour !== undefined) {
  3055. dateTimeFormat.__hour12 = dateTimeFormat.__windowsClock === "12HourClock";
  3056. }
  3057. dateTimeFormat.__initializedDateTimeFormat = true;
  3058. }
  3059. // caches for objects constructed with default parameters for each method
  3060. let __DateInstanceToLocaleStringDefaultCache = [undefined, undefined, undefined];
  3061. const __DateInstanceToLocaleStringDefaultCacheSlot = setPrototype({
  3062. toLocaleString: 0,
  3063. toLocaleDateString: 1,
  3064. toLocaleTimeString: 2
  3065. }, null);
  3066. function DateInstanceToLocaleStringImplementation(name, option1, option2, cacheSlot, locales, options) {
  3067. if (typeof this !== 'object' || !(this instanceof Date)) {
  3068. platform.raiseNeedObjectOfType(name, "Date");
  3069. }
  3070. let value = callInstanceFunc(DateInstanceGetDate, new Date(this));
  3071. if (isNaN(value) || !isFinite(value)) {
  3072. return "Invalid Date";
  3073. }
  3074. let stateObject = undefined;
  3075. if (platform.useCaches && locales === undefined && options === undefined) {
  3076. // All default parameters (locales and options): this is the most valuable case to cache.
  3077. if (__DateInstanceToLocaleStringDefaultCache[cacheSlot]) {
  3078. // retrieve cached value
  3079. stateObject = __DateInstanceToLocaleStringDefaultCache[cacheSlot];
  3080. } else {
  3081. // populate cache
  3082. stateObject = setPrototype({}, null);
  3083. InitializeDateTimeFormat(stateObject, undefined, ToDateTimeOptions(undefined, option1, option2));
  3084. __DateInstanceToLocaleStringDefaultCache[cacheSlot] = stateObject;
  3085. }
  3086. }
  3087. if (!stateObject) {
  3088. stateObject = setPrototype({}, null);
  3089. InitializeDateTimeFormat(stateObject, locales, ToDateTimeOptions(options, option1, option2));
  3090. }
  3091. return String(platform.formatDateTime(Internal.ToNumber(this), stateObject));
  3092. }
  3093. // Note: tagPublicFunction (platform.tagPublicLibraryCode) messes with declared name of the FunctionBody so that
  3094. // the functions called appear correctly in the debugger and stack traces. Thus, we we cannot call tagPublicFunction in a loop.
  3095. // Each entry point needs to have its own unique FunctionBody (which is a function as defined in the source code);
  3096. // this is why we have seemingly repeated ourselves below, instead of having one function and calling it multiple times with
  3097. // different parameters.
  3098. //
  3099. // The following invocations of `platform.registerBuiltInFunction(tagPublicFunction(name, entryPoint))` are enclosed in IIFEs.
  3100. // The IIFEs are used to group all of the meaningful differences between each entry point into the arguments to the IIFE.
  3101. // The exception to this are the different entryPoint names which are only significant for debugging (and cannot be passed in
  3102. // as arguments, as the name is intrinsic to the function declaration).
  3103. //
  3104. // The `date_toLocale*String_entryPoint` function names are placeholder names that will never be seen from user code.
  3105. // The function name property and FunctionBody declared name are overwritten by `tagPublicFunction`.
  3106. // The fact that they are declared with unique names is helpful for debugging.
  3107. // The functions *must not* be declared as anonymous functions (must be declared with a name);
  3108. // converting from an unnnamed function to a named function is not readily supported by the platform code and
  3109. // this has caused us to hit assertions in debug builds in the past.
  3110. //
  3111. // See invocations of `tagPublicFunction` on the `supportedLocalesOf` entry points for a similar pattern.
  3112. //
  3113. // The entryPoint functions will be called as `Date.prototype.toLocale*String` and thus their `this` parameters will be a Date.
  3114. // `DateInstanceToLocaleStringImplementation` is not on `Date.prototype`, so we must propagate `this` into the call by using
  3115. // `DateInstanceToLocaleStringImplementation.call(this, ...)`.
  3116. (function (name, option1, option2, cacheSlot, platformFunctionID) {
  3117. platform.registerBuiltInFunction(tagPublicFunction(name, function date_toLocaleString_entryPoint(locales = undefined, options = undefined) {
  3118. return DateInstanceToLocaleStringImplementation.call(this, name, option1, option2, cacheSlot, locales, options);
  3119. }), platformFunctionID);
  3120. })("Date.prototype.toLocaleString", "any", "all", __DateInstanceToLocaleStringDefaultCacheSlot.toLocaleString, IntlBuiltInFunctionID.DateToLocaleString);
  3121. (function (name, option1, option2, cacheSlot, platformFunctionID) {
  3122. platform.registerBuiltInFunction(tagPublicFunction(name, function date_toLocaleDateString_entryPoint(locales = undefined, options = undefined) {
  3123. return DateInstanceToLocaleStringImplementation.call(this, name, option1, option2, cacheSlot, locales, options);
  3124. }), platformFunctionID);
  3125. })("Date.prototype.toLocaleDateString", "date", "date", __DateInstanceToLocaleStringDefaultCacheSlot.toLocaleDateString, IntlBuiltInFunctionID.DateToLocaleDateString);
  3126. (function (name, option1, option2, cacheSlot, platformFunctionID) {
  3127. platform.registerBuiltInFunction(tagPublicFunction(name, function date_toLocaleTimeString_entryPoint(locales = undefined, options = undefined) {
  3128. return DateInstanceToLocaleStringImplementation.call(this, name, option1, option2, cacheSlot, locales, options);
  3129. }), platformFunctionID);
  3130. })("Date.prototype.toLocaleTimeString", "time", "time", __DateInstanceToLocaleStringDefaultCacheSlot.toLocaleTimeString, IntlBuiltInFunctionID.DateToLocaleTimeString);
  3131. if (InitType === 'Intl') {
  3132. function DateTimeFormat(locales = undefined, options = undefined) {
  3133. if (this === Intl || this === undefined) {
  3134. return new DateTimeFormat(locales, options);
  3135. }
  3136. let obj = Internal.ToObject(this);
  3137. if (!ObjectIsExtensible(obj)) {
  3138. platform.raiseObjectIsNonExtensible("DateTimeFormat");
  3139. }
  3140. // Use the hidden object to store data
  3141. let hiddenObject = platform.getHiddenObject(obj);
  3142. if (hiddenObject === undefined) {
  3143. hiddenObject = setPrototype({}, null);
  3144. platform.setHiddenObject(obj, hiddenObject);
  3145. }
  3146. InitializeDateTimeFormat(hiddenObject, locales, options);
  3147. hiddenObject.__boundFormat = callInstanceFunc(FunctionInstanceBind, format, obj);
  3148. delete hiddenObject.__boundFormat.name;
  3149. return obj;
  3150. }
  3151. tagPublicFunction("Intl.DateTimeFormat", DateTimeFormat);
  3152. function format(date) {
  3153. if (typeof this !== 'object') {
  3154. platform.raiseNeedObjectOfType("DateTimeFormat.prototype.format", "DateTimeFormat");
  3155. }
  3156. let hiddenObject = platform.getHiddenObject(this);
  3157. if (hiddenObject === undefined || !hiddenObject.__initializedDateTimeFormat) {
  3158. platform.raiseNeedObjectOfType("DateTimeFormat.prototype.format", "DateTimeFormat");
  3159. }
  3160. if (date !== undefined && (isNaN(date) || !isFinite(date))) {
  3161. platform.raiseInvalidDate();
  3162. }
  3163. let dateValue = undefined;
  3164. if (date !== undefined) {
  3165. dateValue = Internal.ToNumber(date)
  3166. } else {
  3167. dateValue = DateNow();
  3168. }
  3169. return String(platform.formatDateTime(dateValue, hiddenObject));
  3170. }
  3171. tagPublicFunction("Intl.DateTimeFormat.prototype.format", format);
  3172. DateTimeFormat.__relevantExtensionKeys = ['ca', 'nu'];
  3173. ObjectDefineProperty(DateTimeFormat, 'prototype', { value: new DateTimeFormat(), writable: false, enumerable: false, configurable: false });
  3174. setPrototype(DateTimeFormat.prototype, Object.prototype);
  3175. ObjectDefineProperty(DateTimeFormat.prototype, 'constructor', { value: DateTimeFormat, writable: true, enumerable: false, configurable: true });
  3176. ObjectDefineProperty(DateTimeFormat.prototype, 'format', {
  3177. get: tagPublicFunction('get format', function () {
  3178. if (typeof this !== 'object') {
  3179. platform.raiseNeedObjectOfType("DateTimeFormat.prototype.format", "DateTimeFormat");
  3180. }
  3181. var hiddenObject = platform.getHiddenObject(this);
  3182. if (hiddenObject === undefined || !hiddenObject.__initializedDateTimeFormat) {
  3183. platform.raiseNeedObjectOfType("DateTimeFormat.prototype.format", "DateTimeFormat");
  3184. }
  3185. return hiddenObject.__boundFormat;
  3186. }), enumerable: false, configurable: true
  3187. });
  3188. ObjectDefineProperty(DateTimeFormat.prototype, 'resolvedOptions', {
  3189. value: function resolvedOptions() {
  3190. if (typeof this !== 'object') {
  3191. platform.raiseNeedObjectOfType("DateTimeFormat.prototype.resolvedOptions", "DateTimeFormat");
  3192. }
  3193. var hiddenObject = platform.getHiddenObject(this);
  3194. if (hiddenObject === undefined || !hiddenObject.__initializedDateTimeFormat) {
  3195. platform.raiseNeedObjectOfType("DateTimeFormat.prototype.resolvedOptions", "DateTimeFormat");
  3196. }
  3197. var temp = setPrototype({
  3198. locale: hiddenObject.__locale,
  3199. calendar: hiddenObject.__calendar, // ca unicode extension
  3200. numberingSystem: hiddenObject.__numberingSystem, // nu unicode extension
  3201. timeZone: hiddenObject.__timeZone,
  3202. hour12: hiddenObject.__hour12,
  3203. weekday: hiddenObject.__weekday,
  3204. era: hiddenObject.__era,
  3205. year: hiddenObject.__year,
  3206. month: hiddenObject.__month,
  3207. day: hiddenObject.__day,
  3208. hour: hiddenObject.__hour,
  3209. minute: hiddenObject.__minute,
  3210. second: hiddenObject.__second,
  3211. timeZoneName: hiddenObject.__timeZoneName
  3212. }, null)
  3213. var options = setPrototype({}, null);
  3214. callInstanceFunc(ArrayInstanceForEach, ObjectGetOwnPropertyNames(temp), function (prop) {
  3215. if ((temp[prop] !== undefined || prop === 'timeZone') && callInstanceFunc(ObjectInstanceHasOwnProperty, hiddenObject, "__" + prop)) {
  3216. options[prop] = temp[prop];
  3217. }
  3218. }, hiddenObject);
  3219. return setPrototype(options, Object.prototype);
  3220. }, writable: true, enumerable: false, configurable: true
  3221. });
  3222. ObjectDefineProperty(DateTimeFormat, 'supportedLocalesOf', { value: dateTimeFormat_supportedLocalesOf, writable: true, configurable: true });
  3223. return DateTimeFormat;
  3224. }
  3225. }
  3226. // 'Init.DateTimeFormat' not defined if reached here. Return 'undefined'
  3227. return undefined;
  3228. })();
  3229. // Initialize Intl properties only if needed
  3230. if (InitType === 'Intl') {
  3231. ObjectDefineProperty(Intl, "Collator", { value: Collator, writable: true, enumerable: false, configurable: true });
  3232. ObjectDefineProperty(Intl, "NumberFormat", { value: NumberFormat, writable: true, enumerable: false, configurable: true });
  3233. ObjectDefineProperty(Intl, "DateTimeFormat", { value: DateTimeFormat, writable: true, enumerable: false, configurable: true });
  3234. ObjectDefineProperty(Intl, "getCanonicalLocales", { value: getCanonicalLocales, writable: true, enumerable: false, configurable: true });
  3235. }
  3236. } // END WINGLOB
  3237. });