Intl.js 51 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477
  1. define([ "./impl/Record", "./impl/calendars", "./impl/common", "./locales!",
  2. "requirejs-text/text!./cldr/supplemental/currencyData.json",
  3. "requirejs-text/text!./cldr/supplemental/timeData.json",
  4. "requirejs-text/text!./cldr/supplemental/numberingSystems.json" ],
  5. function (Record, calendars, common, preloads,
  6. currencyDataJson, timeDataJson, numberingSystemsJson) {
  7. /**
  8. * JavaScript implementation of internationalization APIs ("Intl") as defined by ECMA standard 402
  9. * version 1.0, available for download at http://www.ecma-international.org/ecma-402/1.0/ECMA-402.pdf
  10. *
  11. * @constructor
  12. */
  13. /*jshint maxcomplexity: 25*/
  14. var Intl = {};
  15. var currencyData = JSON.parse(currencyDataJson);
  16. var timeData = JSON.parse(timeDataJson).supplemental.timeData;
  17. var numberingSystems = JSON.parse(numberingSystemsJson).supplemental.numberingSystems;
  18. var availableNumberingSystems = [ "latn" ];
  19. for (var ns in numberingSystems) {
  20. if (numberingSystems[ns]._type === "numeric" && ns !== "latn") {
  21. availableNumberingSystems.push(ns);
  22. }
  23. }
  24. /**
  25. * InitializeNumberFormat abstract operation as defined in ECMA-402 Section 11.1.1.1
  26. *
  27. * @param {Object} numberFormat The object to be initialized as a NumberFormat object
  28. * @param {*} locales The requested locale or locales for formatting
  29. * @param {Object} options Number formatting options
  30. * @private
  31. */
  32. function _initializeNumberFormat(numberFormat, locales, options) {
  33. if (numberFormat.hasOwnProperty("initializedIntlObject") && numberFormat.initializedIntlObject) {
  34. throw new TypeError("NumberFormat is already initialized.");
  35. }
  36. numberFormat.initializedIntlObject = true;
  37. var requestedLocales = common.CanonicalizeLocaleList(locales);
  38. if (options === undefined) {
  39. options = {};
  40. } else {
  41. options = Object(options);
  42. }
  43. var opt = new Record();
  44. var matcher = common
  45. .GetOption(options, "localeMatcher", "string", [ "lookup", "best fit" ], "best fit");
  46. opt.set("localeMatcher", matcher);
  47. var r = common.ResolveLocale(NumberFormat.availableLocales, requestedLocales, opt,
  48. NumberFormat.relevantExtensionKeys, NumberFormat.localeData);
  49. numberFormat.locale = r.locale;
  50. numberFormat.dataLocale = r.dataLocale;
  51. numberFormat.numberingSystem = r.nu;
  52. var s = common.GetOption(options, "style", "string", [ "decimal", "percent", "currency" ], "decimal");
  53. numberFormat.style = s;
  54. var c = common.GetOption(options, "currency", "string");
  55. if (c !== undefined && !common.IsWellFormedCurrencyCode(c)) {
  56. throw new RangeError("Invalid currency code " + c);
  57. }
  58. if (s === "currency" && c === undefined) {
  59. throw new TypeError("No currency code specified.");
  60. }
  61. var cDigits = 2;
  62. if (s === "currency") {
  63. c = common._toUpperCaseIdentifier(c);
  64. numberFormat.currency = c;
  65. numberFormat.currencySymbol = c;
  66. numberFormat.currencyDisplayName = c;
  67. if (currencyData.supplemental.currencyData.fractions[c]) {
  68. cDigits = currencyData.supplemental.currencyData.fractions[c]._digits;
  69. }
  70. }
  71. var cd = common.GetOption(options, "currencyDisplay", "string", [ "code", "symbol", "name" ], "symbol");
  72. if (s === "currency") {
  73. numberFormat.currencyDisplay = cd;
  74. if (cd === "symbol" || cd === "name") {
  75. var curr = preloads[r.dataLocale].currencies.main[r.dataLocale].numbers.currencies;
  76. if (curr[numberFormat.currency]) {
  77. numberFormat.currencySymbol = curr[numberFormat.currency].symbol;
  78. numberFormat.currencyDisplayName = curr[numberFormat.currency].displayName;
  79. }
  80. }
  81. }
  82. var mnid = common.GetNumberOption(options, "minimumIntegerDigits", 1, 21, 1);
  83. numberFormat.minimumIntegerDigits = mnid;
  84. var mnfdDefault;
  85. if (s === "currency") {
  86. mnfdDefault = cDigits;
  87. } else {
  88. mnfdDefault = 0;
  89. }
  90. var mnfd = common.GetNumberOption(options, "minimumFractionDigits", 0, 20, mnfdDefault);
  91. numberFormat.minimumFractionDigits = mnfd;
  92. var mxfdDefault;
  93. if (s === "currency") {
  94. mxfdDefault = Math.max(mnfd, cDigits);
  95. } else if (s === "percent") {
  96. mxfdDefault = Math.max(mnfd, 0);
  97. } else {
  98. mxfdDefault = Math.max(mnfd, 3);
  99. }
  100. var mxfd = common.GetNumberOption(options, "maximumFractionDigits", mnfd, 20, mxfdDefault);
  101. numberFormat.maximumFractionDigits = mxfd;
  102. var mnsd = options.minimumSignificantDigits;
  103. var mxsd = options.maximumSignificantDigits;
  104. if (mnsd !== undefined || mxsd !== undefined) {
  105. mnsd = common.GetNumberOption(options, "minimumSignificantDigits", 1, 21, 1);
  106. mxsd = common.GetNumberOption(options, "maximumSignificantDigits", mnsd, 21, 1);
  107. numberFormat.minimumSignificantDigits = mnsd;
  108. numberFormat.maximumSignificantDigits = mxsd;
  109. }
  110. var g = common.GetOption(options, "useGrouping", "boolean", undefined, true);
  111. numberFormat.useGrouping = g;
  112. var numb = preloads[r.dataLocale].numbers.main[r.dataLocale].numbers;
  113. if (r.locale === r.dataLocale) {
  114. numberFormat.numberingSystem = numb.defaultNumberingSystem;
  115. }
  116. var numberInfo = _getNumberInfo(numb, numberFormat.numberingSystem);
  117. var stylePatterns = numberInfo.patterns[s];
  118. numberFormat.positivePattern = stylePatterns.positivePattern;
  119. numberFormat.negativePattern = stylePatterns.negativePattern;
  120. /*
  121. * The CLDR number format pattern is necessary in order to do localized grouping properly, for example
  122. * #,##,##0.00 grouping in India.
  123. */
  124. numberFormat.cldrPattern = stylePatterns.cldrPattern;
  125. numberFormat.symbols = numberInfo.symbols;
  126. numberFormat.boundFormat = undefined;
  127. numberFormat.initializedNumberFormat = true;
  128. }
  129. /**
  130. * Utility function to insert grouping separators into the proper locations in a string of digits based on
  131. * the CLDR pattern string.
  132. *
  133. * @param {String} n The string representing the number to be formatted
  134. * @param {String} pattern The number formatting pattern (from CLDR)
  135. * @returns {String} The formatted string
  136. * @private
  137. */
  138. function doGrouping(n, pattern) {
  139. var numExp = /[0-9#.,]+/;
  140. var number = numExp.exec(pattern)[0];
  141. var dPos = number.lastIndexOf(".");
  142. if (dPos !== -1) {
  143. number = number.substring(0, dPos);
  144. }
  145. var groupings = number.split(",");
  146. groupings.reverse();
  147. groupings.pop();
  148. var currentGrouping = groupings.shift();
  149. var ungroupedDigits = /^\d+/.exec(n);
  150. while (ungroupedDigits && ungroupedDigits[0].length > currentGrouping.length) {
  151. var digitsLeft = ungroupedDigits[0].length - currentGrouping.length;
  152. n = n.substr(0, digitsLeft) + "," + n.substring(digitsLeft);
  153. if (groupings.length > 0) {
  154. currentGrouping = groupings.shift();
  155. }
  156. ungroupedDigits = /^\d+/.exec(n);
  157. }
  158. return n;
  159. }
  160. /**
  161. * Utility function to convert a string in scientific notation to a corresponding string of digits
  162. *
  163. * @param {String} x The string to be converted
  164. * @returns {String} The corresponding string of digits
  165. * @private
  166. */
  167. function _toDigitString(x) {
  168. var m = x;
  169. var negative = false;
  170. if (m.charAt(0) === "-") {
  171. negative = true;
  172. m = m.substring(1);
  173. }
  174. var parts = m.split("e");
  175. var mantissa = parts[0];
  176. var exponent = Number(parts[1]);
  177. if (exponent > 0) {
  178. m = mantissa.substr(0, 1) + mantissa.substr(2); // Get just the digits
  179. if (m.length - 1 < exponent) { // Need to add zeroes.
  180. var e = exponent + 1 - m.length;
  181. while (e > 0) {
  182. m = m + "0";
  183. e--;
  184. }
  185. } else if (m.length - 1 > exponent) {
  186. m = m.substr(0, exponent + 1) + "." + m.substr(exponent + 1);
  187. }
  188. } else if (exponent < 0) {
  189. var digits = mantissa.substr(0, 1) + mantissa.substr(2); // Get just the digits
  190. m = "0.";
  191. for (var i = exponent; i < -1; i++) {
  192. m += "0";
  193. }
  194. m += digits;
  195. }
  196. if (negative) {
  197. m = "-" + m;
  198. }
  199. return m;
  200. }
  201. /**
  202. * ToRawPrecision abstract operation as defined in ECMA-402 Section 11.3.2
  203. *
  204. * @param {Number} x The number being formatted
  205. * @param {Number} minPrecision The minimum precision
  206. * @param {Number} maxPrecision The maximum precision
  207. * @returns {String} The string representing the formatted number
  208. * @private
  209. */
  210. function _toRawPrecision(x, minPrecision, maxPrecision) {
  211. var p = maxPrecision;
  212. var e;
  213. var m = "";
  214. var target;
  215. if (x === 0) {
  216. for (var i = 0; i < p; i++) {
  217. m += "0";
  218. }
  219. e = 0;
  220. } else {
  221. target = Math.pow(10, p - 1);
  222. if (x < target) {
  223. e = p - 1;
  224. while (x < target) {
  225. target /= 10;
  226. e--;
  227. }
  228. } else {
  229. target = Math.pow(10, p);
  230. e = p - 1;
  231. while (x >= target) {
  232. target *= 10;
  233. e++;
  234. }
  235. }
  236. m = x.toString();
  237. if (/e/.test(m)) {
  238. m = _toDigitString(m);
  239. }
  240. if (!/\./.test(m)) {
  241. m += ".";
  242. }
  243. for (i = 0; i < p; i++) {
  244. m += "0";
  245. }
  246. var placesToMove = p - 1 - e;
  247. var mi = m.indexOf(".");
  248. if (placesToMove > 0) {
  249. m = m.substr(0, mi) + m.substr(mi + 1, placesToMove) + "." + m.substr(mi + 1 + placesToMove, 1);
  250. }
  251. if (placesToMove < 0) {
  252. m = m.substr(0, p) + "." + m.substr(p, 1);
  253. }
  254. m = Math.round(m).toString();
  255. }
  256. if (e >= p) {
  257. for (i = 0; i < e - p + 1; i++) {
  258. m += "0";
  259. }
  260. return m;
  261. }
  262. if (e === p - 1) {
  263. return m;
  264. }
  265. if (e >= 0) {
  266. m = m.substr(0, e + 1) + "." + m.substr(e + 1, p - (e + 1));
  267. } else {
  268. var prefix = "0.";
  269. for (i = 0; i < -(e + 1); i++) {
  270. prefix += "0";
  271. }
  272. m = prefix + m;
  273. }
  274. if (/\./.test(m) && maxPrecision > minPrecision) {
  275. var cut = maxPrecision - minPrecision;
  276. while (cut > 0 && /0$/.test(m)) {
  277. m = m.substr(0, m.length - 1);
  278. cut--;
  279. }
  280. if (/\.$/.test(m)) {
  281. m = m.substr(0, m.length - 1);
  282. }
  283. }
  284. return m;
  285. }
  286. /**
  287. * ToRawFixed abstract operation as defined in ECMA-402 Section 11.3.2
  288. *
  289. * @param {Number} x The number being formatted
  290. * @param {Number} minInteger The minimum number of integer digits
  291. * @param {Number} minFraction The minimum number of fractional digits
  292. * @param {Number} maxFraction The maximum number of fractional digits
  293. * @returns {String} The string representing the formatted number
  294. * @private
  295. */
  296. function _toRawFixed(x, minInteger, minFraction, maxFraction) {
  297. var m;
  298. /*
  299. * if x < 10^21, then we can use the standard built in function. Otherwise, Number.toFixed() is going to
  300. * give us back a value in scientific notation, and we have to convert it back to a series of digits.
  301. */
  302. if (Math.abs(x) < Math.pow(10, 21)) {
  303. m = x.toFixed(maxFraction).toString();
  304. } else {
  305. m = _toDigitString(x.toString());
  306. if (maxFraction > 0) {
  307. m += ".";
  308. for (var i = 0; i < maxFraction; i++) {
  309. m += "0";
  310. }
  311. }
  312. }
  313. var cut = maxFraction - minFraction;
  314. while (cut > 0 && /0$/.test(m)) {
  315. m = m.replace(/0$/, "");
  316. cut--;
  317. }
  318. if (/\.$/.test(m)) {
  319. m = m.replace(/\.$/, "");
  320. }
  321. var dPos = m.indexOf(".");
  322. var int = dPos > 0 ? dPos : m.length;
  323. while (int < minInteger) {
  324. m = "0" + m;
  325. int++;
  326. }
  327. return m;
  328. }
  329. /**
  330. * FormatNumber abstract operation as defined in ECMA-402 Section 11.3.2
  331. *
  332. * @param {Object} numberFormat The number format object to use for formatting
  333. * @param {Number} x The number being formatted
  334. * @returns {String} The string representing the formatted number
  335. * @private
  336. */
  337. function _formatNumber(numberFormat, x) {
  338. var negative = (x < 0);
  339. var n;
  340. if (!isFinite(x)) {
  341. if (isNaN(x)) {
  342. n = numberFormat.symbols.nan;
  343. } else {
  344. n = numberFormat.symbols.infinity;
  345. }
  346. } else {
  347. if (negative) {
  348. x = -x;
  349. }
  350. if (numberFormat.style === "percent") {
  351. x *= 100;
  352. }
  353. if (numberFormat.minimumSignificantDigits !== undefined
  354. && numberFormat.maximumSignificantDigits !== undefined) {
  355. n = _toRawPrecision(x, numberFormat.minimumSignificantDigits,
  356. numberFormat.maximumSignificantDigits);
  357. } else {
  358. n = _toRawFixed(x, numberFormat.minimumIntegerDigits, numberFormat.minimumFractionDigits,
  359. numberFormat.maximumFractionDigits);
  360. }
  361. if (numberFormat.useGrouping) {
  362. n = doGrouping(n, numberFormat.cldrPattern);
  363. }
  364. if (numberFormat.numberingSystem !== undefined && numberFormat.numberingSystem !== "latn") {
  365. var alldigits = /\d/g;
  366. n = n.replace(alldigits, function (m) {
  367. return numberingSystems[numberFormat.numberingSystem]._digits.charAt(m);
  368. });
  369. }
  370. n = n.replace(/[.,]/g, function (m) {
  371. if (m === ".") {
  372. return numberFormat.symbols.decimal ? numberFormat.symbols.decimal : m;
  373. }
  374. return numberFormat.symbols.group ? numberFormat.symbols.group : m;
  375. });
  376. }
  377. var result = numberFormat.positivePattern;
  378. if (negative) {
  379. result = numberFormat.negativePattern;
  380. }
  381. if (result) {
  382. result = result.replace("-", numberFormat.symbols.minusSign);
  383. result = result.replace("%", numberFormat.symbols.percentSign);
  384. result = result.replace("{number}", n);
  385. if (numberFormat.style === "currency") {
  386. var currency = numberFormat.currency;
  387. var cd = currency;
  388. if (numberFormat.currencyDisplay === "symbol") {
  389. cd = numberFormat.currencySymbol;
  390. } else if (numberFormat.currencyDisplay === "name") {
  391. cd = numberFormat.currencyDisplayName;
  392. }
  393. result = result.replace("{currency}", cd);
  394. }
  395. }
  396. return result;
  397. }
  398. /**
  399. * Utility function to retrive necessary number fields from the CLDR data
  400. *
  401. * @param {Object} numbers The JSON object containing numbers data from CLDR
  402. * @param {String} numberingSystem The numbering system being used
  403. * @returns {Object} An object containing the number symbols and formatting patterns
  404. * @private
  405. */
  406. function _getNumberInfo(numbers, numberingSystem) {
  407. var result = {};
  408. result.symbols = {};
  409. var numberExp = /[0-9#.,]+/;
  410. var key = "symbols-numberSystem-" + numberingSystem;
  411. var altkey = "symbols-numberSystem-latn";
  412. var cldrSymbols = numbers[key] ? numbers[key] : numbers[altkey];
  413. result.symbols = cldrSymbols;
  414. result.patterns = {};
  415. var styles = [ "decimal", "percent", "currency" ];
  416. for (var s in styles) {
  417. var style = styles[s];
  418. key = style + "Formats-numberSystem-" + numberingSystem;
  419. altkey = style + "Formats-numberSystem-latn";
  420. var cldrPattern = numbers[key] ? numbers[key].standard : numbers[altkey].standard;
  421. var patterns = cldrPattern.split(";");
  422. var positivePattern, negativePattern;
  423. positivePattern = patterns[0];
  424. if (patterns[length] === 2) {
  425. negativePattern = patterns[1];
  426. } else {
  427. negativePattern = "-" + positivePattern;
  428. }
  429. positivePattern = positivePattern.replace(numberExp, "{number}").replace(/\u00A4/, "{currency}");
  430. negativePattern = negativePattern.replace(numberExp, "{number}").replace(/\u00A4/, "{currency}");
  431. result.patterns[style] = {
  432. "cldrPattern" : cldrPattern,
  433. "positivePattern" : positivePattern,
  434. "negativePattern" : negativePattern
  435. };
  436. }
  437. return result;
  438. }
  439. /**
  440. * ToDateTimeOptions abstract operation as defined in ECMA-402 Section 12.1.1.1
  441. *
  442. * @param {Object} options The number format object to use for formatting
  443. * @param {String} required String indicating which options are required
  444. * @param {String} defaults String indicating which options can use defaults
  445. * @returns {Object} The corresponding date/time options
  446. * @private
  447. */
  448. function _toDateTimeOptions(options, required, defaults) {
  449. if (options === undefined) {
  450. options = null;
  451. } else {
  452. options = Object(options);
  453. }
  454. options = Object.create(options);
  455. var weekdayFields = [ "weekday", "year", "month", "day" ];
  456. var dateFields = [ "year", "month", "day" ];
  457. var timeFields = [ "hour", "minute", "second" ];
  458. var needDefaults = true;
  459. if (required === "date" || required === "any") {
  460. weekdayFields.forEach(function (field) {
  461. if (options[field] !== undefined) {
  462. needDefaults = false;
  463. }
  464. });
  465. }
  466. if (required === "time" || required === "any") {
  467. timeFields.forEach(function (field) {
  468. if (options[field] !== undefined) {
  469. needDefaults = false;
  470. }
  471. });
  472. }
  473. if (needDefaults && (defaults === "date" || defaults === "all")) {
  474. dateFields.forEach(function (field) {
  475. Object.defineProperty(options, field, {
  476. value : "numeric",
  477. writable : true,
  478. configurable : true,
  479. enumerable : true
  480. });
  481. });
  482. }
  483. if (needDefaults && (defaults === "time" || defaults === "all")) {
  484. timeFields.forEach(function (field) {
  485. Object.defineProperty(options, field, {
  486. value : "numeric",
  487. writable : true,
  488. configurable : true,
  489. enumerable : true
  490. });
  491. });
  492. }
  493. return options;
  494. }
  495. /**
  496. * BasicFormatMatcher abstract operation as defined in ECMA-402 Section 12.1.1.1
  497. *
  498. * @param {Object} options The requested options (i.e. fields) to be included in the date/time format
  499. * @param {Object []} formats An array of the available date/time formats
  500. * @returns {Object} The date/time format that best matches the requested options
  501. * @private
  502. */
  503. function _basicFormatMatcher(options, formats) {
  504. var removalPenalty = 120;
  505. var additionPenalty = 20;
  506. var longLessPenalty = 8;
  507. var longMorePenalty = 6;
  508. var shortLessPenalty = 6;
  509. var shortMorePenalty = 3;
  510. var bestScore = Number.NEGATIVE_INFINITY;
  511. var bestFormat;
  512. var i = 0;
  513. var len = formats.length;
  514. while (i < len) {
  515. var format = formats[i.toString()];
  516. var score = 0;
  517. var dateTimeProperties = [ "weekday", "era", "year", "month", "day", "hour", "minute", "second",
  518. "timeZoneName" ];
  519. dateTimeProperties.forEach(function (property) {
  520. var optionsProp = options[property];
  521. var formatProp;
  522. var formatPropDesc = Object.getOwnPropertyDescriptor(format, property);
  523. if (formatPropDesc !== undefined) {
  524. formatProp = format[property];
  525. }
  526. if (optionsProp === undefined && formatProp !== undefined) {
  527. score -= additionPenalty;
  528. } else if (optionsProp !== undefined && formatProp === undefined) {
  529. score -= removalPenalty;
  530. } else {
  531. var values = [ "2-digit", "numeric", "narrow", "short", "long" ];
  532. var optionsPropIndex = values.indexOf(optionsProp);
  533. var formatPropIndex = values.indexOf(formatProp);
  534. var delta = Math.max(Math.min(formatPropIndex - optionsPropIndex, 2), -2);
  535. if (delta === 2) {
  536. score -= longMorePenalty;
  537. } else if (delta === 1) {
  538. score -= shortMorePenalty;
  539. } else if (delta === -1) {
  540. score -= shortLessPenalty;
  541. } else if (delta === -2) {
  542. score -= longLessPenalty;
  543. }
  544. }
  545. });
  546. if (score > bestScore) {
  547. bestScore = score;
  548. bestFormat = format;
  549. }
  550. i++;
  551. }
  552. return bestFormat;
  553. }
  554. /**
  555. * BestFitFormat abstract operation as defined in ECMA-402 Section 12.1.1.1
  556. * ECMA-402 allows this algorithm to be implementation defined. For now we are using
  557. * the same algorithm as for BasicFormatMatcher.
  558. *
  559. * @param {Object} options The requested options (i.e. fields) to be included in the date/time format
  560. * @param {Object []} formats An array of the available date/time formats
  561. * @returns {Object} The date/time format that best matches the requested options
  562. * @private
  563. */
  564. function _bestFitFormatMatcher(options, formats) {
  565. return _basicFormatMatcher(options, formats);
  566. }
  567. /**
  568. * InitializeDateTimeFormat abstract operation as defined in ECMA-402 Section 12.1.1.1
  569. *
  570. * @param {Object} dateTimeFormat The object to be initialized as a DateTimeFormat object
  571. * @param {*} locales The requested locale or locales for formatting
  572. * @param {Object} options Date/time formatting options
  573. * @private
  574. */
  575. function _initializeDateTimeFormat(dateTimeFormat, locales, options) {
  576. var dateTimeProperties = [ "weekday", "era", "year", "month", "day", "hour", "minute", "second",
  577. "timeZoneName" ];
  578. if (dateTimeFormat.hasOwnProperty("initializedIntlObject") && dateTimeFormat.initializedIntlObject) {
  579. throw new TypeError("DateTimeFormat is already initialized.");
  580. }
  581. dateTimeFormat.initializedIntlObject = true;
  582. var requestedLocales = common.CanonicalizeLocaleList(locales);
  583. options = _toDateTimeOptions(options, "any", "date");
  584. var opt = new Record();
  585. var matcher = common
  586. .GetOption(options, "localeMatcher", "string", [ "lookup", "best fit" ], "best fit");
  587. opt.set("localeMatcher", matcher);
  588. var r = common.ResolveLocale(DateTimeFormat.availableLocales, requestedLocales, opt,
  589. DateTimeFormat.relevantExtensionKeys, DateTimeFormat.localeData);
  590. dateTimeFormat.locale = r.locale;
  591. dateTimeFormat.numberingSystem = r.nu;
  592. dateTimeFormat.calendar = r.ca;
  593. dateTimeFormat.dataLocale = r.dataLocale;
  594. var tz = options.timeZone;
  595. if (tz !== undefined) {
  596. tz = tz.toString();
  597. tz = common._toUpperCaseIdentifier(tz);
  598. if (tz !== "UTC") {
  599. throw new RangeError("Timezones other than UTC are not supported");
  600. }
  601. }
  602. dateTimeFormat.timeZone = tz;
  603. opt = new Record();
  604. dateTimeProperties.forEach(function (prop) {
  605. var value = common
  606. .GetOption(options, prop, "string", _validDateTimePropertyValues(prop), undefined);
  607. opt.set(prop, value);
  608. });
  609. /*
  610. * Steps 20-21: Here we deviate slightly from the strict definition as defined in ECMA 402. Instead of
  611. * having all the formats predefined (i.e. hard-coded) in the locale data object up front, and accessing
  612. * them here, we instead wait until we know which locale we are interested in, and load the formats from
  613. * the JSON data. This saves us having to convert CLDR date formats to ECMA 402's format for a bunch of
  614. * locales that we aren't really using.
  615. */
  616. var cldrCalendar = dateTimeFormat.calendar.replace("gregory", "gregorian");
  617. dateTimeFormat.calData =
  618. preloads[r.dataLocale]["ca-" + cldrCalendar].main[r.dataLocale].dates.calendars[cldrCalendar];
  619. var formats = _convertAvailableDateTimeFormats(dateTimeFormat.calData.dateTimeFormats);
  620. matcher = common.GetOption(options, "formatMatcher", "string", [ "basic", "best fit" ], "best fit");
  621. var bestFormat = matcher === "basic" ? _basicFormatMatcher(opt, formats) : _bestFitFormatMatcher(opt,
  622. formats);
  623. dateTimeProperties.forEach(function (prop) {
  624. var pDesc = Object.getOwnPropertyDescriptor(bestFormat, prop);
  625. if (pDesc !== undefined) {
  626. dateTimeFormat[prop] = bestFormat[prop];
  627. }
  628. });
  629. var pattern;
  630. var hr12 = common.GetOption(options, "hour12", "boolean", undefined, undefined);
  631. if (dateTimeFormat.hour !== undefined) {
  632. if (hr12 === undefined) {
  633. hr12 = DateTimeFormat.localeData[dateTimeFormat.dataLocale]
  634. && DateTimeFormat.localeData[dateTimeFormat.dataLocale].hour12;
  635. }
  636. dateTimeFormat.hour12 = hr12;
  637. if (hr12) {
  638. var hourNo0 = DateTimeFormat.localeData[dateTimeFormat.dataLocale]
  639. && DateTimeFormat.localeData[dateTimeFormat.dataLocale].hourNo0;
  640. dateTimeFormat.hourNo0 = hourNo0;
  641. dateTimeFormat.hour = bestFormat.hour12;
  642. pattern = bestFormat.pattern12;
  643. } else {
  644. pattern = bestFormat.pattern;
  645. }
  646. } else {
  647. pattern = bestFormat.pattern;
  648. }
  649. dateTimeFormat.pattern = pattern;
  650. dateTimeFormat.boundFormat = undefined;
  651. dateTimeFormat.initializedDateTimeFormat = true;
  652. }
  653. /**
  654. * FormatDateTime abstract operation as defined in ECMA-402 Section 12.3.2
  655. *
  656. * @param {Object} dateTimeFormat The date/time format object to use for formatting
  657. * @param {Number} x The value of the date being formatted, as would be received from
  658. * the getTime() method of the Date object
  659. * @returns {String} The string representing the formatted number
  660. * @private
  661. */
  662. function _formatDateTime(dateTimeFormat, x) {
  663. var dateTimeProperties = [ "weekday", "era", "year", "month", "day", "hour", "minute", "second",
  664. "timeZoneName" ];
  665. if (!isFinite(x)) {
  666. throw new RangeError("Attempting to format an invalid date/time.");
  667. }
  668. var locale = dateTimeFormat.locale;
  669. var nf = {};
  670. _initializeNumberFormat(nf, locale, {
  671. useGrouping : false
  672. });
  673. var nf2 = {};
  674. _initializeNumberFormat(nf2, locale, {
  675. minimumIntegerDigits : 2,
  676. useGrouping : false
  677. });
  678. var tm = calendars.toLocalTime(x, dateTimeFormat.calendar, dateTimeFormat.timeZone);
  679. var pm = false;
  680. var result = dateTimeFormat.pattern;
  681. dateTimeProperties.forEach(function (prop) {
  682. var p = prop;
  683. var f = dateTimeFormat[p];
  684. var v = tm[p];
  685. var fv;
  686. if (p === "month") {
  687. v++;
  688. }
  689. if (p === "hour" && dateTimeFormat.hour12) {
  690. v = v % 12;
  691. pm = (v !== tm[p]);
  692. if (v === 0 && dateTimeFormat.hourNo0) {
  693. v = 12;
  694. }
  695. }
  696. if (f === "numeric") {
  697. fv = _formatNumber(nf, v);
  698. } else if (f === "2-digit") {
  699. fv = _formatNumber(nf2, v);
  700. if (fv.length > 2) {
  701. fv = fv.substr(-2);
  702. }
  703. } else {
  704. var standalone = (p === "month" && dateTimeFormat.standaloneMonth);
  705. fv = _getCalendarField(dateTimeFormat.calendar, dateTimeFormat.calData, tm.year, standalone, p,
  706. f, v);
  707. }
  708. if (result) {
  709. result = result.replace("{" + p + "}", fv);
  710. }
  711. });
  712. if (dateTimeFormat.hour12) {
  713. var ampm = pm ? "pm" : "am";
  714. var fv = _getCalendarField(dateTimeFormat.calendar, dateTimeFormat.calData, tm.year, false,
  715. "dayperiod", "short", ampm);
  716. if (result) {
  717. result = result.replace("{ampm}", fv);
  718. }
  719. }
  720. return result;
  721. }
  722. /**
  723. * Utility function to retrive necessary date/time fields from the CLDR data
  724. *
  725. * @param {String} calType The type of calendar in use (i.e. "hebrew", "japanese", etc. )
  726. * @param {Object} calData The JSON object containing calendar data from CLDR
  727. * @param {Number} year The year number
  728. * @param {Boolean} standalone TRUE indicates a stand-alone month name,
  729. * which is spelled differently in some languages.
  730. * @param {String} property The type of field being requested (i.e. "weekday", "month", etc.)
  731. * @param {String} format The length of the field being requested (i.e. "narrow", "short", "long")
  732. * @param {Number} value Indicates which month, day, etc. is being requested (zero based)
  733. * @returns {String} A string containing the requested calendar field
  734. * @private
  735. */
  736. function _getCalendarField(calType, calData, year, standalone, property, format, value) {
  737. var result = null;
  738. switch (property) {
  739. case "weekday":
  740. var cldrWeekdayKeys = [ "sun", "mon", "tue", "wed", "thu", "fri", "sat" ];
  741. var weekdayKey = cldrWeekdayKeys[value];
  742. switch (format) {
  743. case "narrow":
  744. result = calData.days.format.narrow[weekdayKey];
  745. break;
  746. case "short":
  747. result = calData.days.format.abbreviated[weekdayKey];
  748. break;
  749. case "long":
  750. result = calData.days.format.wide[weekdayKey];
  751. break;
  752. }
  753. break;
  754. case "era":
  755. switch (format) {
  756. case "narrow":
  757. result = calData.eras.eraNarrow[value];
  758. break;
  759. case "short":
  760. result = calData.eras.eraAbbr[value];
  761. break;
  762. case "long":
  763. result = calData.eras.eraNames[value];
  764. break;
  765. }
  766. break;
  767. case "month":
  768. var monthValue = value;
  769. /*
  770. * Because of leap month in the Hebrew calendar, there isn't a 1-1 correlation between
  771. * month number and the resource name in CLDR, so we have to adjust accordingly.
  772. */
  773. if (calType === "hebrew") {
  774. monthValue = calendars.hebrewMonthResource(year, value);
  775. }
  776. switch (format) {
  777. case "narrow":
  778. result = standalone ? calData.months["stand-alone"].narrow[monthValue]
  779. : calData.months.format.narrow[monthValue];
  780. break;
  781. case "short":
  782. result = standalone ? calData.months["stand-alone"].abbreviated[monthValue]
  783. : calData.months.format.abbreviated[monthValue];
  784. break;
  785. case "long":
  786. result = standalone ? calData.months["stand-alone"].wide[monthValue]
  787. : calData.months.format.wide[monthValue];
  788. break;
  789. }
  790. break;
  791. case "dayperiod":
  792. switch (format) {
  793. case "narrow":
  794. result = calData.dayPeriods.format.narrow[value];
  795. break;
  796. case "short":
  797. result = calData.dayPeriods.format.abbreviated[value];
  798. break;
  799. case "long":
  800. result = calData.dayPeriods.format.wide[value];
  801. break;
  802. }
  803. break;
  804. case "timeZoneName":
  805. if (value === "UTC") {
  806. result = "UTC";
  807. }
  808. result = "local";
  809. break;
  810. }
  811. return result;
  812. }
  813. /**
  814. * Utility function to convert the availableFormats from a CLDR JSON object into an array of available
  815. * formats as defined by ECMA 402. For definition of fields, in CLDR, refer to
  816. * http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
  817. *
  818. * @param {String} format The date/time format pattern as from CLDR
  819. * @returns {String} A string containing the corresponding date/time pattern in ECMA-402 format
  820. * @private
  821. */
  822. function _ToIntlDateTimeFormat(format) {
  823. var dateFields = /G{1,5}|y{1,4}|[ML]{1,5}|E{1,5}|d{1,2}|a|[Hh]{1,2}|m{1,2}|s{1,2}/g;
  824. var result = new Record();
  825. var pieces = format.split("'");
  826. for (var x = 0; x < pieces.length; x += 2) { // Don't do replacements for fields that are quoted
  827. /*jshint maxcomplexity: 36*/
  828. pieces[x] = pieces[x].replace(dateFields, function (field) {
  829. switch (field) {
  830. case "GGGGG":
  831. result.set("era", "narrow");
  832. return "{era}";
  833. case "GGGG":
  834. result.set("era", "long");
  835. return "{era}";
  836. case "GGG":
  837. case "GG":
  838. case "G":
  839. result.set("era", "short");
  840. return "{era}";
  841. case "yy":
  842. result.set("year", "2-digit");
  843. return "{year}";
  844. case "y":
  845. case "yyy":
  846. case "yyyy":
  847. result.set("year", "numeric");
  848. return "{year}";
  849. case "LLLLL":
  850. result.set("standaloneMonth", true);
  851. result.set("month", "narrow");
  852. return "{month}";
  853. case "MMMMM":
  854. result.set("month", "narrow");
  855. return "{month}";
  856. case "LLLL":
  857. result.set("standaloneMonth", true);
  858. result.set("month", "long");
  859. return "{month}";
  860. case "MMMM":
  861. result.set("month", "long");
  862. return "{month}";
  863. case "LLL":
  864. result.set("standaloneMonth", true);
  865. result.set("month", "short");
  866. return "{month}";
  867. case "MMM":
  868. result.set("month", "short");
  869. return "{month}";
  870. case "LL":
  871. case "MM":
  872. result.set("month", "2-digit");
  873. return "{month}";
  874. case "L":
  875. case "M":
  876. result.set("month", "numeric");
  877. return "{month}";
  878. case "EEEEE":
  879. result.set("weekday", "narrow");
  880. return "{weekday}";
  881. case "EEEE":
  882. result.set("weekday", "long");
  883. return "{weekday}";
  884. case "EEE":
  885. case "EE":
  886. case "E":
  887. result.set("weekday", "short");
  888. return "{weekday}";
  889. case "dd":
  890. result.set("day", "2-digit");
  891. return "{day}";
  892. case "d":
  893. result.set("day", "numeric");
  894. return "{day}";
  895. case "a":
  896. return "{ampm}";
  897. case "hh":
  898. result.set("hour12", "2-digit");
  899. result.set("hour", "2-digit");
  900. return "{hour}";
  901. case "HH":
  902. result.set("hour", "2-digit");
  903. return "{hour}";
  904. case "h":
  905. result.set("hour12", "numeric");
  906. result.set("hour", "numeric");
  907. return "{hour}";
  908. case "H":
  909. result.set("hour", "numeric");
  910. return "{hour}";
  911. case "mm":
  912. result.set("minute", "2-digit");
  913. return "{minute}";
  914. case "m":
  915. result.set("minute", "numeric");
  916. return "{minute}";
  917. case "ss":
  918. result.set("second", "2-digit");
  919. return "{second}";
  920. case "s":
  921. result.set("second", "numeric");
  922. return "{second}";
  923. default:
  924. return field;
  925. }
  926. });
  927. /*jshint maxcomplexity: 10*/
  928. }
  929. result.set("pattern", pieces.join(""));
  930. return result;
  931. }
  932. /**
  933. * Utility function to convert the availableFormats from a CLDR JSON
  934. * object into an array of available formats as defined by ECMA 402. For
  935. * a definition of fields in CLDR, refer to
  936. * http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
  937. *
  938. * @param {Object} dateTimeFormats The CLDR JSON object containing date/time information
  939. * @returns {String[]} A array of strings, each corrresponding to one of the date/time
  940. * patterns (in ECMA-402 format) that can be used to format dates or times
  941. * @private
  942. */
  943. function _convertAvailableDateTimeFormats(dateTimeFormats) {
  944. var availableFormats = dateTimeFormats.availableFormats;
  945. var result = [];
  946. var usableFormatSkeletons = /^G{0,5}y{0,4}M{0,5}E{0,5}d{0,2}H{0,2}m{0,2}s{0,2}$/;
  947. var abbrMonthSkeleton = /(^|[^M])M{3}([^M]|$)/;
  948. for (var format in availableFormats) {
  949. var format12 = availableFormats[format.replace("H", "h")];
  950. if (usableFormatSkeletons.test(format) && format12 !== undefined) {
  951. var outputFormat = _ToIntlDateTimeFormat(availableFormats[format]);
  952. if (/H/.test(format)) {
  953. var outputFormat12 = _ToIntlDateTimeFormat(format12);
  954. outputFormat.set("hour12", outputFormat12.hour12);
  955. outputFormat.set("pattern12", outputFormat12.pattern);
  956. }
  957. result.push(outputFormat);
  958. // Flexible date format logic - If the locale contains just a MMM pattern (abbreviated month)
  959. // and not a corresponding MMMM pattern (long month), then we can infer
  960. // an appropriate MMMM pattern by just replacing the MMM with MMMM.
  961. if (abbrMonthSkeleton.test(format) && !availableFormats[format.replace("MMM", "MMMM")]) {
  962. var constructedFormatPattern = availableFormats[format]
  963. .replace("MMM", "MMMM").replace("LLL", "LLLL");
  964. var constructedFormat = _ToIntlDateTimeFormat(constructedFormatPattern);
  965. result.push(constructedFormat);
  966. }
  967. }
  968. }
  969. // ECMA402 requires us to have a full date format that includes
  970. // weekday, year, month
  971. // day, hour, minute, and second. Since CLDR doesn't
  972. // specifically have that combination,
  973. // we have to piece it together using three pieces:
  974. // a). The yMMMMEd or yMMMEd format from available formats
  975. // b). The "full" date/time format combiner.
  976. // c). The Hms format in locales using 24-hour clock, or the hms
  977. // format in locales using a 12-hour clock.
  978. var combinedDateFormat = dateTimeFormats.full || "{1} {0}";
  979. combinedDateFormat = combinedDateFormat.replace("{1}",
  980. availableFormats.yMMMMEd || availableFormats.yMMMEd ||
  981. availableFormats.yyyyMMMMEd || availableFormats.yyyyMMMEd ||
  982. availableFormats.GyMMMMEd || availableFormats.GyMMMEd);
  983. var combinedDateTimeFormat24 = combinedDateFormat.replace("{0}", availableFormats.Hms);
  984. var combinedDateTimeFormat12 = combinedDateFormat.replace("{0}", availableFormats.hms);
  985. outputFormat = _ToIntlDateTimeFormat(combinedDateTimeFormat24);
  986. outputFormat12 = _ToIntlDateTimeFormat(combinedDateTimeFormat12);
  987. outputFormat.set("hour12", outputFormat12.hour12);
  988. outputFormat.set("pattern12", outputFormat12.pattern);
  989. result.push(outputFormat);
  990. // Flexible date format logic - If the locale contains just a MMM pattern (abbreviated month)
  991. // and not a corresponding MMMM pattern (long month), then we can infer
  992. // an appropriate MMMM pattern by just replacing the MMM with MMMM.
  993. if (abbrMonthSkeleton.test(combinedDateTimeFormat24)) {
  994. var longFormatPattern24 = combinedDateTimeFormat24
  995. .replace("MMM", "MMMM").replace("LLL", "LLLL");
  996. var longFormatPattern12 = combinedDateTimeFormat12
  997. .replace("MMM", "MMMM").replace("LLL", "LLLL");
  998. var longFormat = _ToIntlDateTimeFormat(longFormatPattern24);
  999. var longFormat12 = _ToIntlDateTimeFormat(longFormatPattern12);
  1000. longFormat.set("hour12", longFormat12.hour12);
  1001. longFormat.set("pattern12", longFormat12.pattern);
  1002. result.push(longFormat);
  1003. }
  1004. return result;
  1005. }
  1006. /**
  1007. * Utility function to return the valid values for a date/time field, according to table 3 in ECMA 402
  1008. * section 12.1.1.1
  1009. *
  1010. * @param {String} prop The requested date/time field
  1011. * @returns {String[]} A array of strings, each corrresponding one of the valid date/time property values
  1012. * @private
  1013. */
  1014. function _validDateTimePropertyValues(prop) {
  1015. if (prop === "weekday" || prop === "era") {
  1016. return [ "narrow", "short", "long" ];
  1017. }
  1018. if (prop === "year" || prop === "day" || prop === "hour" || prop === "minute" || prop === "second") {
  1019. return [ "2-digit", "numeric" ];
  1020. }
  1021. if (prop === "month") {
  1022. return [ "2-digit", "numeric", "narrow", "short", "long" ];
  1023. }
  1024. if (prop === "timeZoneName") {
  1025. return [ "short", "long" ];
  1026. }
  1027. }
  1028. /**
  1029. * Internal properties of NumberFormat, as defined in ECMA 402 Section 11.2.3
  1030. */
  1031. var NumberFormat = {};
  1032. NumberFormat.availableLocales = Object.keys(preloads);
  1033. NumberFormat.relevantExtensionKeys = [ "nu" ];
  1034. NumberFormat.localeData = {};
  1035. NumberFormat.availableLocales.forEach(function (loc) {
  1036. NumberFormat.localeData[loc] = {
  1037. "nu" : availableNumberingSystems
  1038. };
  1039. });
  1040. Object.freeze(NumberFormat);
  1041. /**
  1042. * Internal properties of DateTimeFormat, as defined in ECMA 402 Section 12.2.3
  1043. */
  1044. var DateTimeFormat = {};
  1045. DateTimeFormat.availableLocales = Object.keys(preloads);
  1046. DateTimeFormat.relevantExtensionKeys = [ "ca", "nu" ];
  1047. DateTimeFormat.localeData = {};
  1048. DateTimeFormat.availableLocales.forEach(function (loc) {
  1049. var region = common._getRegion(loc);
  1050. var hour12 = false;
  1051. var hourNo0 = false;
  1052. hour12 = timeData[region] && (/h|K/.test(timeData[region]._preferred));
  1053. hourNo0 = timeData[region] && (/h|k/.test(timeData[region]._preferred));
  1054. DateTimeFormat.localeData[loc] = {
  1055. "nu" : availableNumberingSystems,
  1056. "ca" : common._getSupportedCalendars(common._getRegion(loc)),
  1057. "hour12" : hour12,
  1058. "hourNo0" : hourNo0
  1059. };
  1060. });
  1061. Object.freeze(DateTimeFormat);
  1062. /**
  1063. * Placeholder for Intl.Collator constructor as defined by EMCA 402 Section 10.1.
  1064. * Intl.Collator is not supported by this package.
  1065. * @constructor
  1066. */
  1067. Intl.Collator = function () {
  1068. throw new TypeError("Intl.Collator is not supported.");
  1069. };
  1070. /**
  1071. * Intl.NumberFormat constructor as defined by EMCA 402 Section 11.1.
  1072. * @constructor
  1073. */
  1074. Intl.NumberFormat = function () {
  1075. this.prototype = Intl.NumberFormat.prototype;
  1076. this.extensible = true;
  1077. // ECMA 402 Section 11.1.3.1
  1078. var locales;
  1079. var options;
  1080. if (arguments.length > 0) {
  1081. locales = arguments[0];
  1082. }
  1083. if (arguments.length > 1) {
  1084. options = arguments[1];
  1085. }
  1086. _initializeNumberFormat(this, locales, options);
  1087. };
  1088. // ECMA 402 Section 7
  1089. Object.defineProperty(Intl, "NumberFormat", {
  1090. writable : true,
  1091. configurable : true,
  1092. enumerable : false
  1093. });
  1094. /**
  1095. * Intl.NumberFormat.call as defined in ECMA-402 Section 11.1.2.1
  1096. *
  1097. * @param {Object} thisObject The NumberFormat object. If undefined,
  1098. * a new NumberFormat object will be created.
  1099. * @param {*} locales The requested locale or locales for formatting
  1100. * @param {Object} options Number formatting options
  1101. * @returns
  1102. */
  1103. Intl.NumberFormat.call = function (thisObject, locales, options) {
  1104. if (thisObject === Intl || thisObject === undefined) {
  1105. return new Intl.NumberFormat(locales, options);
  1106. }
  1107. var obj = Object(thisObject);
  1108. if (!Object.isExtensible(obj)) {
  1109. throw new TypeError("Intl.NumberFormat.call: object is not extensible");
  1110. }
  1111. _initializeNumberFormat(obj, locales, options);
  1112. return obj;
  1113. };
  1114. /**
  1115. * Intl.NumberFormat.supportedLocalesOf as defined in ECMA-402 Section 11.2.2
  1116. *
  1117. * @param {*} locales The requested locale or locales for formatting
  1118. * @param {Object} options Locale lookup options
  1119. * @returns {String[]} An array of supported locales that matches the request
  1120. */
  1121. Object.defineProperty(Intl.NumberFormat, "supportedLocalesOf", {
  1122. value : function (locales) {
  1123. var availableLocales = NumberFormat.availableLocales;
  1124. var requestedLocales = common.CanonicalizeLocaleList(locales);
  1125. var options;
  1126. if (arguments.length > 1) {
  1127. options = arguments[1];
  1128. }
  1129. return common.SupportedLocales(availableLocales, requestedLocales, options);
  1130. },
  1131. writable : true,
  1132. enumerable : false,
  1133. configurable : true
  1134. });
  1135. Intl.NumberFormat.prototype = Intl.NumberFormat.call({});
  1136. // ECMA 402 Section 11.2.1
  1137. Object.defineProperty(Intl.NumberFormat, "prototype", {
  1138. writable : false,
  1139. enumerable : false,
  1140. configurable : false
  1141. });
  1142. // ECMA 402 Section 11.3.1
  1143. Object.defineProperty(Intl.NumberFormat.prototype, "constructor", {
  1144. value : Intl.NumberFormat,
  1145. writable : true,
  1146. configurable : true,
  1147. enumerable : false
  1148. });
  1149. /**
  1150. * Intl.NumberFormat.prototype.format as defined in ECMA-402 Section 11.3.2
  1151. * @param {Number} value The number to format
  1152. */
  1153. Object.defineProperty(Intl.NumberFormat.prototype, "format", {
  1154. get : function () {
  1155. if (this !== Object(this) || !this.initializedNumberFormat) {
  1156. throw new TypeError(
  1157. "Intl.NumberFormat format getter: 'this' is not a valid Intl.NumberFormat instance");
  1158. }
  1159. if (this.boundFormat === undefined) {
  1160. var F = function (value) {
  1161. var x = Number(value);
  1162. return _formatNumber(this, x);
  1163. };
  1164. var bf = F.bind(this);
  1165. this.boundFormat = bf;
  1166. }
  1167. return this.boundFormat;
  1168. },
  1169. configurable : true
  1170. });
  1171. /**
  1172. * Intl.NumberFormat.resolvedOptions as defined in ECMA-402 Section 11.3.3
  1173. *
  1174. * @returns {Object} An object containing information about the options associated with a NumberFormat
  1175. */
  1176. Object.defineProperty(Intl.NumberFormat.prototype, "resolvedOptions", {
  1177. value : function () {
  1178. if (this !== Object(this) || !this.initializedNumberFormat) {
  1179. throw new TypeError(
  1180. "Intl.NumberFormat.resolvedOptions: 'this' is not a valid Intl.NumberFormat instance");
  1181. }
  1182. var fields = [ "locale", "numberingSystem", "style", "currency", "currencyDisplay",
  1183. "minimumIntegerDigits", "minimumFractionDigits", "maximumFractionDigits",
  1184. "minimumSignificantDigits", "maximumSignificantDigits", "useGrouping" ];
  1185. var result = new Record();
  1186. for (var f in fields) {
  1187. if (this.hasOwnProperty(fields[f])) {
  1188. result.set(fields[f], this[fields[f]]);
  1189. }
  1190. }
  1191. return result;
  1192. },
  1193. writable : true,
  1194. enumerable : false,
  1195. configurable : true
  1196. });
  1197. /**
  1198. * Intl.DateTimeFormat constructor as defined by EMCA 402 Section 12.1.
  1199. * @constructor
  1200. */
  1201. Intl.DateTimeFormat = function () {
  1202. this.prototype = Intl.DateTimeFormat.prototype;
  1203. this.extensible = true;
  1204. // ECMA 402 Section 12.1.3.1
  1205. var locales;
  1206. var options;
  1207. if (arguments.length > 0) {
  1208. locales = arguments[0];
  1209. }
  1210. if (arguments.length > 1) {
  1211. options = arguments[1];
  1212. }
  1213. _initializeDateTimeFormat(this, locales, options);
  1214. };
  1215. // ECMA 402 Section 7
  1216. Object.defineProperty(Intl, "DateTimeFormat", {
  1217. writable : true,
  1218. configurable : true,
  1219. enumerable : false
  1220. });
  1221. /**
  1222. * Intl.DateTimeFormat.call as defined in ECMA-402 Section 12.1.2.1
  1223. *
  1224. * @param {Object} thisObject The DateTimeFormat object. If undefined,
  1225. * a new DateTimeFormat object will be created.
  1226. * @param {*} locales The requested locale or locales for formatting
  1227. * @param {Object} options Date/time formatting options
  1228. * @returns
  1229. */
  1230. Intl.DateTimeFormat.call = function (thisObject, locales, options) {
  1231. if (thisObject === Intl || thisObject === undefined) {
  1232. return new Intl.DateTimeFormat(locales, options);
  1233. }
  1234. var obj = Object(thisObject);
  1235. if (!Object.isExtensible(obj)) {
  1236. throw new TypeError("Intl.DateTimeFormat.call: object is not extensible");
  1237. }
  1238. _initializeDateTimeFormat(obj, locales, options);
  1239. return obj;
  1240. };
  1241. /**
  1242. * Intl.DateTimeFormat.supportedLocalesOf as defined in ECMA-402 Section 12.2.2
  1243. *
  1244. * @param {*} locales The requested locale or locales for formatting
  1245. * @param {Object} options Locale lookup options
  1246. * @returns {String[]} An array of supported locales that matches the request
  1247. */
  1248. Object.defineProperty(Intl.DateTimeFormat, "supportedLocalesOf", {
  1249. value : function (locales) {
  1250. var availableLocales = DateTimeFormat.availableLocales;
  1251. var requestedLocales = common.CanonicalizeLocaleList(locales);
  1252. var options;
  1253. if (arguments.length > 1) {
  1254. options = arguments[1];
  1255. }
  1256. return common.SupportedLocales(availableLocales, requestedLocales, options);
  1257. },
  1258. writable : true,
  1259. enumerable : false,
  1260. configurable : true
  1261. });
  1262. Intl.DateTimeFormat.prototype = Intl.DateTimeFormat.call({});
  1263. // ECMA 402 Section 12.2.1
  1264. Object.defineProperty(Intl.DateTimeFormat, "prototype", {
  1265. writable : false,
  1266. enumerable : false,
  1267. configurable : false
  1268. });
  1269. // ECMA 402 Section 12.3.1
  1270. Object.defineProperty(Intl.DateTimeFormat.prototype, "constructor", {
  1271. value : Intl.DateTimeFormat,
  1272. writable : true,
  1273. configurable : true,
  1274. enumerable : false
  1275. });
  1276. /**
  1277. * @param {Date} date The date to format
  1278. * Intl.DateTimeFormat.prototype.format as defined in ECMA-402 Section 12.3.2
  1279. */
  1280. Object.defineProperty(Intl.DateTimeFormat.prototype, "format", {
  1281. get : function () {
  1282. if (this !== Object(this) || !this.initializedDateTimeFormat) {
  1283. var msg = "DateTimeFormat format getter: 'this' is not a valid Intl.DateTimeFormat instance";
  1284. throw new TypeError(msg);
  1285. }
  1286. if (this.boundFormat === undefined) {
  1287. var F = function () {
  1288. var date;
  1289. if (arguments.length > 0) {
  1290. date = arguments[0];
  1291. }
  1292. var x;
  1293. if (date === undefined) {
  1294. x = Date.now();
  1295. } else {
  1296. x = Number(date);
  1297. }
  1298. return _formatDateTime(this, x);
  1299. };
  1300. var bf = F.bind(this);
  1301. this.boundFormat = bf;
  1302. }
  1303. return this.boundFormat;
  1304. },
  1305. configurable : true
  1306. });
  1307. /**
  1308. * Intl.DateTimeFormat.resolvedOptions as defined in ECMA-402 Section 12.3.3
  1309. *
  1310. * @returns {Object} An object containing information about the options associated with a DateTimeFormat
  1311. */
  1312. Object.defineProperty(Intl.DateTimeFormat.prototype, "resolvedOptions", {
  1313. value : function () {
  1314. if (this !== Object(this) || !this.initializedDateTimeFormat) {
  1315. var msg = "DateTimeFormat.resolvedOptions: 'this' is not a valid Intl.DateTimeFormat instance";
  1316. throw new TypeError(msg);
  1317. }
  1318. var fields = [ "locale", "calendar", "numberingSystem", "timeZone", "hour12", "weekday", "era",
  1319. "year", "month", "day", "hour", "minute", "second", "timeZoneName" ];
  1320. var result = new Record();
  1321. for (var f in fields) {
  1322. if (this.hasOwnProperty(fields[f])) {
  1323. result.set(fields[f], this[fields[f]]);
  1324. }
  1325. }
  1326. return result;
  1327. },
  1328. writable : true,
  1329. enumerable : false,
  1330. configurable : true
  1331. });
  1332. /**
  1333. * Number.prototype.toLocaleString as defined in ECMA-402 Section 13.2.1
  1334. *
  1335. * @param {*} locales The requested locale or locales for formatting
  1336. * @param {Object} options Number formatting options
  1337. * @returns {String} String representing the formatted number
  1338. */
  1339. Number.prototype.toLocaleString = function () {
  1340. if (!(this instanceof Number)) {
  1341. throw new TypeError("not a valid Number");
  1342. }
  1343. var x = Number(this);
  1344. var locales;
  1345. var options;
  1346. if (arguments.length > 0) {
  1347. locales = arguments[0];
  1348. }
  1349. if (arguments.length > 1) {
  1350. options = arguments[1];
  1351. }
  1352. var numberFormat = {};
  1353. _initializeNumberFormat(numberFormat, locales, options);
  1354. return _formatNumber(numberFormat, x);
  1355. };
  1356. /**
  1357. * Date.prototype.toLocaleString as defined in ECMA-402 Section 13.3.1
  1358. *
  1359. * @param {*} locales The requested locale or locales for formatting
  1360. * @param {Object} options Date/time formatting options
  1361. * @returns {String} String representing the formatted date/time
  1362. */
  1363. Date.prototype.toLocaleString = function () {
  1364. if (!(this instanceof Date)) {
  1365. throw new TypeError("not a valid Date");
  1366. }
  1367. var x = this.getTime();
  1368. if (isNaN(x)) {
  1369. return "Invalid Date";
  1370. }
  1371. var locales;
  1372. var options;
  1373. if (arguments.length > 0) {
  1374. locales = arguments[0];
  1375. }
  1376. if (arguments.length > 1) {
  1377. options = arguments[1];
  1378. }
  1379. options = _toDateTimeOptions(options, "any", "all");
  1380. var dateTimeFormat = {};
  1381. _initializeDateTimeFormat(dateTimeFormat, locales, options);
  1382. return _formatDateTime(dateTimeFormat, x);
  1383. };
  1384. /**
  1385. * Date.prototype.toLocaleDateString as defined in ECMA-402 Section 13.3.2
  1386. *
  1387. * @param {*} locales The requested locale or locales for formatting
  1388. * @param {Object} options Date formatting options
  1389. * @returns {String} String representing the formatted date
  1390. */
  1391. Date.prototype.toLocaleDateString = function () {
  1392. if (!(this instanceof Date)) {
  1393. throw new TypeError("not a valid Date");
  1394. }
  1395. var x = this.getTime();
  1396. if (isNaN(x)) {
  1397. return "Invalid Date";
  1398. }
  1399. var locales;
  1400. var options;
  1401. if (arguments.length > 0) {
  1402. locales = arguments[0];
  1403. }
  1404. if (arguments.length > 1) {
  1405. options = arguments[1];
  1406. }
  1407. options = _toDateTimeOptions(options, "date", "date");
  1408. var dateTimeFormat = {};
  1409. _initializeDateTimeFormat(dateTimeFormat, locales, options);
  1410. return _formatDateTime(dateTimeFormat, x);
  1411. };
  1412. /**
  1413. * Date.prototype.toLocaleTimeString as defined in ECMA-402 Section 13.3.3
  1414. *
  1415. * @param {*} locales The requested locale or locales for formatting
  1416. * @param {Object} options Time formatting options
  1417. * @returns {String} String representing the formatted time
  1418. */
  1419. Date.prototype.toLocaleTimeString = function () {
  1420. if (!(this instanceof Date)) {
  1421. throw new TypeError("not a valid Date");
  1422. }
  1423. var x = this.getTime();
  1424. if (isNaN(x)) {
  1425. return "Invalid Date";
  1426. }
  1427. var locales;
  1428. var options;
  1429. if (arguments.length > 0) {
  1430. locales = arguments[0];
  1431. }
  1432. if (arguments.length > 1) {
  1433. options = arguments[1];
  1434. }
  1435. options = _toDateTimeOptions(options, "time", "time");
  1436. var dateTimeFormat = {};
  1437. _initializeDateTimeFormat(dateTimeFormat, locales, options);
  1438. return _formatDateTime(dateTimeFormat, x);
  1439. };
  1440. return Intl;
  1441. });