@hebcal/core 5.0.0 → 5.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bundle.js CHANGED
@@ -1,457 +1,112 @@
1
- /*! @hebcal/core v5.0.0 */
1
+ /*! @hebcal/core v5.0.2 */
2
2
  var hebcal = (function (exports) {
3
3
  'use strict';
4
4
 
5
- const GERESH = '׳';
6
- const GERSHAYIM = '״';
7
- const heb2num = {
8
- 'א': 1,
9
- 'ב': 2,
10
- 'ג': 3,
11
- 'ד': 4,
12
- 'ה': 5,
13
- 'ו': 6,
14
- 'ז': 7,
15
- 'ח': 8,
16
- 'ט': 9,
17
- 'י': 10,
18
- 'כ': 20,
19
- 'ל': 30,
20
- 'מ': 40,
21
- 'נ': 50,
22
- 'ס': 60,
23
- 'ע': 70,
24
- 'פ': 80,
25
- 'צ': 90,
26
- 'ק': 100,
27
- 'ר': 200,
28
- 'ש': 300,
29
- 'ת': 400
30
- };
31
- const num2heb = new Map();
32
- Object.keys(heb2num).forEach(key => {
33
- const val = heb2num[key];
34
- num2heb.set(val, key);
35
- });
36
-
5
+ /** @private */
6
+ const lengths = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
7
+ /** @private */
8
+ const monthLengths = [lengths, lengths.slice()];
9
+ monthLengths[1][2] = 29;
37
10
  /**
38
11
  * @private
39
- * @param {number} num
40
- * @return {number[]}
41
12
  */
42
- function num2digits(num) {
43
- const digits = [];
44
- while (num > 0) {
45
- if (num === 15 || num === 16) {
46
- digits.push(9);
47
- digits.push(num - 9);
48
- break;
49
- }
50
- let incr = 100;
51
- let i;
52
- for (i = 400; i > num; i -= incr) {
53
- if (i === incr) {
54
- incr = incr / 10;
55
- }
56
- }
57
- digits.push(i);
58
- num -= i;
59
- }
60
- return digits;
13
+ function mod$1(x, y) {
14
+ return x - y * Math.floor(x / y);
61
15
  }
62
-
63
16
  /**
64
- * Converts a numerical value to a string of Hebrew letters.
65
- *
66
- * When specifying years of the Hebrew calendar in the present millennium,
67
- * we omit the thousands (which is presently 5 [ה]).
68
- * @example
69
- * gematriya(5774) // 'תשע״ד' - cropped to 774
70
- * gematriya(25) // 'כ״ה'
71
- * gematriya(60) // 'ס׳'
72
- * gematriya(3761) // 'ג׳תשס״א'
73
- * gematriya(1123) // 'א׳קכ״ג'
74
- * @param {number} number
75
- * @return {string}
17
+ * @private
76
18
  */
77
- function gematriya(number) {
78
- const num = parseInt(number, 10);
79
- if (!num) {
80
- throw new TypeError(`invalid parameter to gematriya ${number}`);
81
- }
82
- let str = '';
83
- const thousands = Math.floor(num / 1000);
84
- if (thousands > 0 && thousands !== 5) {
85
- const tdigits = num2digits(thousands);
86
- for (const tdig of tdigits) {
87
- str += num2heb.get(tdig);
88
- }
89
- str += GERESH;
90
- }
91
- const digits = num2digits(num % 1000);
92
- if (digits.length == 1) {
93
- return str + num2heb.get(digits[0]) + GERESH;
94
- }
95
- for (let i = 0; i < digits.length; i++) {
96
- if (i + 1 === digits.length) {
97
- str += GERSHAYIM;
98
- }
99
- str += num2heb.get(digits[i]);
100
- }
101
- return str;
19
+ function quotient(x, y) {
20
+ return Math.floor(x / y);
102
21
  }
103
-
22
+ /*
23
+ const ABS_14SEP1752 = 639797;
24
+ const ABS_2SEP1752 = 639785;
25
+ */
104
26
  /**
105
- * Converts a string of Hebrew letters to a numerical value.
106
- *
107
- * Only considers the value of Hebrew letters `א` through `ת`.
108
- * Ignores final Hebrew letters such as `ך` (kaf sofit) or `ם` (mem sofit)
109
- * and vowels (nekudot).
110
- *
111
- * @param {string} str
112
- * @return {number}
27
+ * Gregorian date helper functions.
113
28
  */
114
- function gematriyaStrToNum(str) {
115
- let num = 0;
116
- const gereshIdx = str.indexOf(GERESH);
117
- if (gereshIdx !== -1 && gereshIdx !== str.length - 1) {
118
- const thousands = str.substring(0, gereshIdx);
119
- num += gematriyaStrToNum(thousands) * 1000;
120
- str = str.substring(gereshIdx);
29
+ exports.greg = void 0;
30
+ (function (greg) {
31
+ /**
32
+ * Long names of the Gregorian months (1='January', 12='December')
33
+ * @readonly
34
+ * @type {string[]}
35
+ */
36
+ greg.monthNames = ['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
37
+ /**
38
+ * Returns true if the Gregorian year is a leap year
39
+ * @param {number} year Gregorian year
40
+ * @return {boolean}
41
+ */
42
+ function isLeapYear(year) {
43
+ return !(year % 4) && (!!(year % 100) || !(year % 400));
121
44
  }
122
- for (const ch of str) {
123
- const n = heb2num[ch];
124
- if (typeof n === 'number') {
125
- num += n;
126
- }
45
+ greg.isLeapYear = isLeapYear;
46
+ /**
47
+ * Number of days in the Gregorian month for given year
48
+ * @param {number} month Gregorian month (1=January, 12=December)
49
+ * @param {number} year Gregorian year
50
+ * @return {number}
51
+ */
52
+ function daysInMonth(month, year) {
53
+ // 1 based months
54
+ return monthLengths[+isLeapYear(year)][month];
127
55
  }
128
- return num;
129
- }
130
-
131
- const noopLocale = {
132
- headers: {
133
- 'plural-forms': 'nplurals=2; plural=(n!=1);'
134
- },
135
- contexts: {
136
- '': {}
56
+ greg.daysInMonth = daysInMonth;
57
+ /**
58
+ * Returns true if the object is a Javascript Date
59
+ * @param {Object} obj
60
+ * @return {boolean}
61
+ */
62
+ function isDate(obj) {
63
+ return typeof obj === 'object' && Date.prototype.isPrototypeOf(obj);
137
64
  }
138
- };
139
- const alias = {
140
- 'h': 'he',
141
- 'a': 'ashkenazi',
142
- 's': 'en',
143
- '': 'en'
144
- };
145
-
146
- /** @private */
147
- const locales = new Map();
148
- /** @private */
149
- let activeLocale = null;
150
- /** @private */
151
- let activeName = null;
152
-
153
- /**
154
- * A locale in Hebcal is used for translations/transliterations of
155
- * holidays. `@hebcal/core` supports four locales by default
156
- * * `en` - default, Sephardic transliterations (e.g. "Shabbat")
157
- * * `ashkenazi` - Ashkenazi transliterations (e.g. "Shabbos")
158
- * * `he` - Hebrew (e.g. "שַׁבָּת")
159
- * * `he-x-NoNikud` - Hebrew without nikud (e.g. "שבת")
160
- */
161
- class Locale {
65
+ greg.isDate = isDate;
162
66
  /**
163
- * Returns translation only if `locale` offers a non-empty translation for `id`.
164
- * Otherwise, returns `undefined`.
165
- * @param {string} id Message ID to translate
166
- * @param {string} [locale] Optional locale name (i.e: `'he'`, `'fr'`). Defaults to active locale.
167
- * @return {string}
67
+ * @private
68
+ * @param abs - R.D. number of days
168
69
  */
169
- static lookupTranslation(id, locale) {
170
- const locale0 = locale === null || locale === void 0 ? void 0 : locale.toLowerCase();
171
- const loc = typeof locale == 'string' && locales.get(locale0) || activeLocale;
172
- const array = loc[id];
173
- if (array && array.length && array[0].length) {
174
- return array[0];
175
- }
176
- return undefined;
70
+ function yearFromFixed(abs) {
71
+ const l0 = abs - 1;
72
+ const n400 = quotient(l0, 146097);
73
+ const d1 = mod$1(l0, 146097);
74
+ const n100 = quotient(d1, 36524);
75
+ const d2 = mod$1(d1, 36524);
76
+ const n4 = quotient(d2, 1461);
77
+ const d3 = mod$1(d2, 1461);
78
+ const n1 = quotient(d3, 365);
79
+ const year = 400 * n400 + 100 * n100 + 4 * n4 + n1;
80
+ return n100 != 4 && n1 != 4 ? year + 1 : year;
177
81
  }
178
-
179
82
  /**
180
- * By default, if no translation was found, returns `id`.
181
- * @param {string} id Message ID to translate
182
- * @param {string} [locale] Optional locale name (i.e: `'he'`, `'fr'`). Defaults to active locale.
183
- * @return {string}
83
+ * @private
84
+ * @param year
85
+ * @param month (1-12)
86
+ * @param day (1-31)
184
87
  */
185
- static gettext(id, locale) {
186
- const text = this.lookupTranslation(id, locale);
187
- if (typeof text == 'undefined') {
188
- return id;
189
- }
190
- return text;
88
+ function toFixed(year, month, day) {
89
+ const py = year - 1;
90
+ return 365 * py + quotient(py, 4) - quotient(py, 100) + quotient(py, 400) + quotient(367 * month - 362, 12) + (month <= 2 ? 0 : isLeapYear(year) ? -1 : -2) + day;
191
91
  }
192
-
193
92
  /**
194
- * Register locale translations.
195
- * @param {string} locale Locale name (i.e.: `'he'`, `'fr'`)
196
- * @param {LocaleData} data parsed data from a `.po` file.
93
+ * Converts Gregorian date to absolute R.D. (Rata Die) days
94
+ * @param {Date} date Gregorian date
95
+ * @return {number}
197
96
  */
198
- static addLocale(locale, data) {
199
- if (typeof locale !== 'string') {
200
- throw new TypeError(`Invalid locale name: ${locale}`);
97
+ function greg2abs(date) {
98
+ if (!isDate(date)) {
99
+ throw new TypeError(`Argument not a Date: ${date}`);
201
100
  }
202
- if (typeof data.contexts !== 'object' || typeof data.contexts[''] !== 'object') {
203
- throw new TypeError(`Locale '${locale}' invalid compact format`);
101
+ const abs = toFixed(date.getFullYear(), date.getMonth() + 1, date.getDate());
102
+ /*
103
+ if (abs < ABS_14SEP1752 && abs > ABS_2SEP1752) {
104
+ throw new RangeError(`Invalid Date: ${date}`);
204
105
  }
205
- locales.set(locale.toLowerCase(), data.contexts['']);
106
+ */
107
+ return abs;
206
108
  }
207
-
208
- /**
209
- * Adds a translation to `locale`, replacing any previous translation.
210
- * @param {string} locale Locale name (i.e: `'he'`, `'fr'`).
211
- * @param {string} id Message ID to translate
212
- * @param {string} translation Translation text
213
- */
214
- static addTranslation(locale, id, translation) {
215
- if (typeof locale !== 'string') {
216
- throw new TypeError(`Invalid locale name: ${locale}`);
217
- }
218
- const locale0 = locale.toLowerCase();
219
- const loc = locales.get(locale0);
220
- if (!loc) {
221
- throw new TypeError(`Unknown locale: ${locale}`);
222
- }
223
- if (typeof id !== 'string' || id.length === 0) {
224
- throw new TypeError(`Invalid id: ${id}`);
225
- }
226
- const isArray = Array.isArray(translation);
227
- if (isArray) {
228
- const t0 = translation[0];
229
- if (typeof t0 !== 'string' || t0.length === 0) {
230
- throw new TypeError(`Invalid translation array: ${translation}`);
231
- }
232
- } else if (typeof translation !== 'string') {
233
- throw new TypeError(`Invalid translation: ${translation}`);
234
- }
235
- loc[id] = isArray ? translation : [translation];
236
- }
237
- /**
238
- * Adds multiple translations to `locale`, replacing any previous translations.
239
- * @param {string} locale Locale name (i.e: `'he'`, `'fr'`).
240
- * @param {LocaleData} data parsed data from a `.po` file.
241
- */
242
- static addTranslations(locale, data) {
243
- if (typeof locale !== 'string') {
244
- throw new TypeError(`Invalid locale name: ${locale}`);
245
- }
246
- const locale0 = locale.toLowerCase();
247
- const loc = locales.get(locale0);
248
- if (!loc) {
249
- throw new TypeError(`Unknown locale: ${locale}`);
250
- }
251
- if (typeof data.contexts !== 'object' || typeof data.contexts[''] !== 'object') {
252
- throw new TypeError(`Locale '${locale}' invalid compact format`);
253
- }
254
- const ctx = data.contexts[''];
255
- Object.keys(ctx).forEach(id => {
256
- loc[id] = ctx[id];
257
- });
258
- }
259
- /**
260
- * Activates a locale. Throws an error if the locale has not been previously added.
261
- * After setting the locale to be used, all strings marked for translations
262
- * will be represented by the corresponding translation in the specified locale.
263
- * @param {string} locale Locale name (i.e: `'he'`, `'fr'`)
264
- * @return {LocaleData}
265
- */
266
- static useLocale(locale) {
267
- const locale0 = locale.toLowerCase();
268
- const obj = locales.get(locale0);
269
- if (!obj) {
270
- throw new RangeError(`Locale '${locale}' not found`);
271
- }
272
- activeName = alias[locale0] || locale0;
273
- activeLocale = obj;
274
- return activeLocale;
275
- }
276
-
277
- /**
278
- * Returns the name of the active locale (i.e. 'he', 'ashkenazi', 'fr')
279
- * @return {string}
280
- */
281
- static getLocaleName() {
282
- return activeName;
283
- }
284
-
285
- /**
286
- * Returns the names of registered locales
287
- * @return {string[]}
288
- */
289
- static getLocaleNames() {
290
- const keys = Array.from(locales.keys());
291
- return keys.sort((a, b) => a.localeCompare(b));
292
- }
293
-
294
- /**
295
- * @param {number} n
296
- * @param {string} [locale] Optional locale name (i.e: `'he'`, `'fr'`). Defaults to active locale.
297
- * @return {string}
298
- */
299
- static ordinal(n, locale) {
300
- const locale1 = locale === null || locale === void 0 ? void 0 : locale.toLowerCase();
301
- const locale0 = locale1 || activeName;
302
- if (!locale0) {
303
- return this.getEnOrdinal(n);
304
- }
305
- switch (locale0) {
306
- case 'en':
307
- case 's':
308
- case 'a':
309
- case 'ashkenazi':
310
- case 'ashkenazi_litvish':
311
- case 'ashkenazi_poylish':
312
- case 'ashkenazi_standard':
313
- return this.getEnOrdinal(n);
314
- case 'es':
315
- return n + 'º';
316
- case 'h':
317
- case 'he':
318
- case 'he-x-nonikud':
319
- return String(n);
320
- default:
321
- return n + '.';
322
- }
323
- }
324
-
325
- /**
326
- * @private
327
- * @param {number} n
328
- * @return {string}
329
- */
330
- static getEnOrdinal(n) {
331
- const s = ['th', 'st', 'nd', 'rd'];
332
- const v = n % 100;
333
- return n + (s[(v - 20) % 10] || s[v] || s[0]);
334
- }
335
-
336
- /**
337
- * Removes nekudot from Hebrew string
338
- * @param {string} str
339
- * @return {string}
340
- */
341
- static hebrewStripNikkud(str) {
342
- return str.replace(/[\u0590-\u05bd]/g, '').replace(/[\u05bf-\u05c7]/g, '');
343
- }
344
- }
345
- Locale.addLocale('en', noopLocale);
346
- Locale.addLocale('s', noopLocale);
347
- Locale.addLocale('', noopLocale);
348
- Locale.useLocale('en');
349
-
350
- /** @private */
351
- const lengths = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
352
- /** @private */
353
- const monthLengths = [lengths, lengths.slice()];
354
- monthLengths[1][2] = 29;
355
- /**
356
- * @private
357
- */
358
- function mod$1(x, y) {
359
- return x - y * Math.floor(x / y);
360
- }
361
- /**
362
- * @private
363
- */
364
- function quotient(x, y) {
365
- return Math.floor(x / y);
366
- }
367
- /*
368
- const ABS_14SEP1752 = 639797;
369
- const ABS_2SEP1752 = 639785;
370
- */
371
- /**
372
- * Gregorian date helper functions.
373
- */
374
- exports.greg = void 0;
375
- (function (greg) {
376
- /**
377
- * Long names of the Gregorian months (1='January', 12='December')
378
- * @readonly
379
- * @type {string[]}
380
- */
381
- greg.monthNames = ['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
382
- /**
383
- * Returns true if the Gregorian year is a leap year
384
- * @param {number} year Gregorian year
385
- * @return {boolean}
386
- */
387
- function isLeapYear(year) {
388
- return !(year % 4) && (!!(year % 100) || !(year % 400));
389
- }
390
- greg.isLeapYear = isLeapYear;
391
- /**
392
- * Number of days in the Gregorian month for given year
393
- * @param {number} month Gregorian month (1=January, 12=December)
394
- * @param {number} year Gregorian year
395
- * @return {number}
396
- */
397
- function daysInMonth(month, year) {
398
- // 1 based months
399
- return monthLengths[+isLeapYear(year)][month];
400
- }
401
- greg.daysInMonth = daysInMonth;
402
- /**
403
- * Returns true if the object is a Javascript Date
404
- * @param {Object} obj
405
- * @return {boolean}
406
- */
407
- function isDate(obj) {
408
- return typeof obj === 'object' && Date.prototype.isPrototypeOf(obj);
409
- }
410
- greg.isDate = isDate;
411
- /**
412
- * @private
413
- * @param abs - R.D. number of days
414
- */
415
- function yearFromFixed(abs) {
416
- const l0 = abs - 1;
417
- const n400 = quotient(l0, 146097);
418
- const d1 = mod$1(l0, 146097);
419
- const n100 = quotient(d1, 36524);
420
- const d2 = mod$1(d1, 36524);
421
- const n4 = quotient(d2, 1461);
422
- const d3 = mod$1(d2, 1461);
423
- const n1 = quotient(d3, 365);
424
- const year = 400 * n400 + 100 * n100 + 4 * n4 + n1;
425
- return n100 != 4 && n1 != 4 ? year + 1 : year;
426
- }
427
- /**
428
- * @private
429
- * @param year
430
- * @param month (1-12)
431
- * @param day (1-31)
432
- */
433
- function toFixed(year, month, day) {
434
- const py = year - 1;
435
- return 365 * py + quotient(py, 4) - quotient(py, 100) + quotient(py, 400) + quotient(367 * month - 362, 12) + (month <= 2 ? 0 : isLeapYear(year) ? -1 : -2) + day;
436
- }
437
- /**
438
- * Converts Gregorian date to absolute R.D. (Rata Die) days
439
- * @param {Date} date Gregorian date
440
- * @return {number}
441
- */
442
- function greg2abs(date) {
443
- if (!isDate(date)) {
444
- throw new TypeError(`Argument not a Date: ${date}`);
445
- }
446
- const abs = toFixed(date.getFullYear(), date.getMonth() + 1, date.getDate());
447
- /*
448
- if (abs < ABS_14SEP1752 && abs > ABS_2SEP1752) {
449
- throw new RangeError(`Invalid Date: ${date}`);
450
- }
451
- */
452
- return abs;
453
- }
454
- greg.greg2abs = greg2abs;
109
+ greg.greg2abs = greg2abs;
455
110
  /**
456
111
  * Converts from Rata Die (R.D. number) to Gregorian date.
457
112
  * See the footnote on page 384 of ``Calendrical Calculations, Part II:
@@ -850,106 +505,443 @@ function monthFromName(monthName) {
850
505
  case 'ש':
851
506
  return months.TISHREI;
852
507
  }
853
- break;
508
+ break;
509
+ }
510
+ throw new RangeError(`Unable to parse month name: ${monthName}`);
511
+ }
512
+
513
+ const NISAN$3 = months.NISAN;
514
+ const CHESHVAN = months.CHESHVAN;
515
+ const KISLEV$1 = months.KISLEV;
516
+ const TEVET$1 = months.TEVET;
517
+ const SHVAT = months.SHVAT;
518
+ const ADAR_I$1 = months.ADAR_I;
519
+ const ADAR_II = months.ADAR_II;
520
+ /**
521
+ * Returns true if the object is a Javascript Date
522
+ * @private
523
+ * @param {Object} obj
524
+ */
525
+ function isSimpleHebrewDate(obj) {
526
+ return typeof obj === 'object' && obj !== null && typeof obj.yy === 'number' && typeof obj.mm === 'number' && typeof obj.dd === 'number';
527
+ }
528
+ /**
529
+ * @private
530
+ */
531
+ function toSimpleHebrewDate(obj) {
532
+ if (isSimpleHebrewDate(obj)) {
533
+ return obj;
534
+ } else if (typeof obj === 'number') {
535
+ return abs2hebrew(obj);
536
+ } else if (exports.greg.isDate(obj)) {
537
+ const abs = exports.greg.greg2abs(obj);
538
+ return abs2hebrew(abs);
539
+ } else {
540
+ throw new TypeError(`Argument not a Date: ${obj}`);
541
+ }
542
+ }
543
+ function getYahrzeitHD(hyear, date) {
544
+ let hDeath = toSimpleHebrewDate(date);
545
+ if (hyear <= hDeath.yy) {
546
+ // Hebrew year ${hyear} occurs on or before original date in ${hDeath.yy}
547
+ return undefined;
548
+ }
549
+ if (hDeath.mm == CHESHVAN && hDeath.dd == 30 && !longCheshvan(hDeath.yy + 1)) {
550
+ // If it's Heshvan 30 it depends on the first anniversary;
551
+ // if that was not Heshvan 30, use the day before Kislev 1.
552
+ hDeath = abs2hebrew(hebrew2abs(hyear, KISLEV$1, 1) - 1);
553
+ } else if (hDeath.mm == KISLEV$1 && hDeath.dd == 30 && shortKislev(hDeath.yy + 1)) {
554
+ // If it's Kislev 30 it depends on the first anniversary;
555
+ // if that was not Kislev 30, use the day before Teveth 1.
556
+ hDeath = abs2hebrew(hebrew2abs(hyear, TEVET$1, 1) - 1);
557
+ } else if (hDeath.mm == ADAR_II) {
558
+ // If it's Adar II, use the same day in last month of year (Adar or Adar II).
559
+ hDeath.mm = monthsInYear(hyear);
560
+ } else if (hDeath.mm == ADAR_I$1 && hDeath.dd == 30 && !isLeapYear(hyear)) {
561
+ // If it's the 30th in Adar I and year is not a leap year
562
+ // (so Adar has only 29 days), use the last day in Shevat.
563
+ hDeath.dd = 30;
564
+ hDeath.mm = SHVAT;
565
+ }
566
+ // In all other cases, use the normal anniversary of the date of death.
567
+ // advance day to rosh chodesh if needed
568
+ if (hDeath.mm == CHESHVAN && hDeath.dd == 30 && !longCheshvan(hyear)) {
569
+ hDeath.mm = KISLEV$1;
570
+ hDeath.dd = 1;
571
+ } else if (hDeath.mm == KISLEV$1 && hDeath.dd == 30 && shortKislev(hyear)) {
572
+ hDeath.mm = TEVET$1;
573
+ hDeath.dd = 1;
574
+ }
575
+ hDeath.yy = hyear;
576
+ return hDeath;
577
+ }
578
+ function getBirthdayHD(hyear, date) {
579
+ const orig = toSimpleHebrewDate(date);
580
+ const origYear = orig.yy;
581
+ if (hyear === origYear) {
582
+ return orig;
583
+ } else if (hyear < origYear) {
584
+ // Hebrew year ${hyear} occurs on or before original date in ${origYear}
585
+ return undefined;
586
+ }
587
+ const isOrigLeap = isLeapYear(origYear);
588
+ let month = orig.mm;
589
+ let day = orig.dd;
590
+ if (month == ADAR_I$1 && !isOrigLeap || month == ADAR_II && isOrigLeap) {
591
+ month = monthsInYear(hyear);
592
+ } else if (month == CHESHVAN && day == 30 && !longCheshvan(hyear)) {
593
+ month = KISLEV$1;
594
+ day = 1;
595
+ } else if (month == KISLEV$1 && day == 30 && shortKislev(hyear)) {
596
+ month = TEVET$1;
597
+ day = 1;
598
+ } else if (month == ADAR_I$1 && day == 30 && isOrigLeap && !isLeapYear(hyear)) {
599
+ month = NISAN$3;
600
+ day = 1;
601
+ }
602
+ return {
603
+ yy: hyear,
604
+ mm: month,
605
+ dd: day
606
+ };
607
+ }
608
+
609
+ const GERESH = '׳';
610
+ const GERSHAYIM = '״';
611
+ const alefbet = {
612
+ 'א': 1,
613
+ 'ב': 2,
614
+ 'ג': 3,
615
+ 'ד': 4,
616
+ 'ה': 5,
617
+ 'ו': 6,
618
+ 'ז': 7,
619
+ 'ח': 8,
620
+ 'ט': 9,
621
+ 'י': 10,
622
+ 'כ': 20,
623
+ 'ל': 30,
624
+ 'מ': 40,
625
+ 'נ': 50,
626
+ 'ס': 60,
627
+ 'ע': 70,
628
+ 'פ': 80,
629
+ 'צ': 90,
630
+ 'ק': 100,
631
+ 'ר': 200,
632
+ 'ש': 300,
633
+ 'ת': 400
634
+ };
635
+ const heb2num = new Map();
636
+ const num2heb = new Map();
637
+ for (const [key, val] of Object.entries(alefbet)) {
638
+ heb2num.set(key, val);
639
+ num2heb.set(val, key);
640
+ }
641
+ function num2digits(num) {
642
+ const digits = [];
643
+ while (num > 0) {
644
+ if (num === 15 || num === 16) {
645
+ digits.push(9);
646
+ digits.push(num - 9);
647
+ break;
648
+ }
649
+ let incr = 100;
650
+ let i;
651
+ for (i = 400; i > num; i -= incr) {
652
+ if (i === incr) {
653
+ incr = incr / 10;
654
+ }
655
+ }
656
+ digits.push(i);
657
+ num -= i;
658
+ }
659
+ return digits;
660
+ }
661
+ /**
662
+ * Converts a numerical value to a string of Hebrew letters.
663
+ *
664
+ * When specifying years of the Hebrew calendar in the present millennium,
665
+ * we omit the thousands (which is presently 5 [ה]).
666
+ * @example
667
+ * gematriya(5774) // 'תשע״ד' - cropped to 774
668
+ * gematriya(25) // 'כ״ה'
669
+ * gematriya(60) // 'ס׳'
670
+ * gematriya(3761) // 'ג׳תשס״א'
671
+ * gematriya(1123) // 'א׳קכ״ג'
672
+ * @param {number} num
673
+ * @return {string}
674
+ */
675
+ function gematriya(num) {
676
+ const num0 = num;
677
+ const num1 = parseInt(num0, 10);
678
+ if (!num1) {
679
+ throw new TypeError(`invalid parameter to gematriya ${num}`);
680
+ }
681
+ let str = '';
682
+ const thousands = Math.floor(num1 / 1000);
683
+ if (thousands > 0 && thousands !== 5) {
684
+ const tdigits = num2digits(thousands);
685
+ for (const tdig of tdigits) {
686
+ str += num2heb.get(tdig);
687
+ }
688
+ str += GERESH;
689
+ }
690
+ const digits = num2digits(num1 % 1000);
691
+ if (digits.length == 1) {
692
+ return str + num2heb.get(digits[0]) + GERESH;
693
+ }
694
+ for (let i = 0; i < digits.length; i++) {
695
+ if (i + 1 === digits.length) {
696
+ str += GERSHAYIM;
697
+ }
698
+ str += num2heb.get(digits[i]);
699
+ }
700
+ return str;
701
+ }
702
+ /**
703
+ * Converts a string of Hebrew letters to a numerical value.
704
+ *
705
+ * Only considers the value of Hebrew letters `א` through `ת`.
706
+ * Ignores final Hebrew letters such as `ך` (kaf sofit) or `ם` (mem sofit)
707
+ * and vowels (nekudot).
708
+ *
709
+ * @param {string} str
710
+ * @return {number}
711
+ */
712
+ function gematriyaStrToNum(str) {
713
+ let num = 0;
714
+ const gereshIdx = str.indexOf(GERESH);
715
+ if (gereshIdx !== -1 && gereshIdx !== str.length - 1) {
716
+ const thousands = str.substring(0, gereshIdx);
717
+ num += gematriyaStrToNum(thousands) * 1000;
718
+ str = str.substring(gereshIdx);
719
+ }
720
+ for (const ch of str) {
721
+ const n = heb2num.get(ch);
722
+ if (typeof n === 'number') {
723
+ num += n;
724
+ }
725
+ }
726
+ return num;
727
+ }
728
+
729
+ const noopLocale = {
730
+ headers: {
731
+ 'plural-forms': 'nplurals=2; plural=(n!=1);'
732
+ },
733
+ contexts: {
734
+ '': {}
735
+ }
736
+ };
737
+ const alias = {
738
+ 'h': 'he',
739
+ 'a': 'ashkenazi',
740
+ 's': 'en',
741
+ '': 'en'
742
+ };
743
+
744
+ /** @private */
745
+ const locales = new Map();
746
+ /** @private */
747
+ let activeLocale = null;
748
+ /** @private */
749
+ let activeName = null;
750
+
751
+ /**
752
+ * A locale in Hebcal is used for translations/transliterations of
753
+ * holidays. `@hebcal/core` supports four locales by default
754
+ * * `en` - default, Sephardic transliterations (e.g. "Shabbat")
755
+ * * `ashkenazi` - Ashkenazi transliterations (e.g. "Shabbos")
756
+ * * `he` - Hebrew (e.g. "שַׁבָּת")
757
+ * * `he-x-NoNikud` - Hebrew without nikud (e.g. "שבת")
758
+ */
759
+ class Locale {
760
+ /**
761
+ * Returns translation only if `locale` offers a non-empty translation for `id`.
762
+ * Otherwise, returns `undefined`.
763
+ * @param {string} id Message ID to translate
764
+ * @param {string} [locale] Optional locale name (i.e: `'he'`, `'fr'`). Defaults to active locale.
765
+ * @return {string}
766
+ */
767
+ static lookupTranslation(id, locale) {
768
+ const locale0 = locale === null || locale === void 0 ? void 0 : locale.toLowerCase();
769
+ const loc = typeof locale == 'string' && locales.get(locale0) || activeLocale;
770
+ const array = loc[id];
771
+ if (array && array.length && array[0].length) {
772
+ return array[0];
773
+ }
774
+ return undefined;
775
+ }
776
+
777
+ /**
778
+ * By default, if no translation was found, returns `id`.
779
+ * @param {string} id Message ID to translate
780
+ * @param {string} [locale] Optional locale name (i.e: `'he'`, `'fr'`). Defaults to active locale.
781
+ * @return {string}
782
+ */
783
+ static gettext(id, locale) {
784
+ const text = this.lookupTranslation(id, locale);
785
+ if (typeof text == 'undefined') {
786
+ return id;
787
+ }
788
+ return text;
789
+ }
790
+
791
+ /**
792
+ * Register locale translations.
793
+ * @param {string} locale Locale name (i.e.: `'he'`, `'fr'`)
794
+ * @param {LocaleData} data parsed data from a `.po` file.
795
+ */
796
+ static addLocale(locale, data) {
797
+ if (typeof locale !== 'string') {
798
+ throw new TypeError(`Invalid locale name: ${locale}`);
799
+ }
800
+ if (typeof data.contexts !== 'object' || typeof data.contexts[''] !== 'object') {
801
+ throw new TypeError(`Locale '${locale}' invalid compact format`);
802
+ }
803
+ locales.set(locale.toLowerCase(), data.contexts['']);
804
+ }
805
+
806
+ /**
807
+ * Adds a translation to `locale`, replacing any previous translation.
808
+ * @param {string} locale Locale name (i.e: `'he'`, `'fr'`).
809
+ * @param {string} id Message ID to translate
810
+ * @param {string} translation Translation text
811
+ */
812
+ static addTranslation(locale, id, translation) {
813
+ if (typeof locale !== 'string') {
814
+ throw new TypeError(`Invalid locale name: ${locale}`);
815
+ }
816
+ const locale0 = locale.toLowerCase();
817
+ const loc = locales.get(locale0);
818
+ if (!loc) {
819
+ throw new TypeError(`Unknown locale: ${locale}`);
820
+ }
821
+ if (typeof id !== 'string' || id.length === 0) {
822
+ throw new TypeError(`Invalid id: ${id}`);
823
+ }
824
+ const isArray = Array.isArray(translation);
825
+ if (isArray) {
826
+ const t0 = translation[0];
827
+ if (typeof t0 !== 'string' || t0.length === 0) {
828
+ throw new TypeError(`Invalid translation array: ${translation}`);
829
+ }
830
+ } else if (typeof translation !== 'string') {
831
+ throw new TypeError(`Invalid translation: ${translation}`);
832
+ }
833
+ loc[id] = isArray ? translation : [translation];
854
834
  }
855
- throw new RangeError(`Unable to parse month name: ${monthName}`);
856
- }
857
-
858
- const NISAN$3 = months.NISAN;
859
- const CHESHVAN = months.CHESHVAN;
860
- const KISLEV$1 = months.KISLEV;
861
- const TEVET$1 = months.TEVET;
862
- const SHVAT = months.SHVAT;
863
- const ADAR_I$1 = months.ADAR_I;
864
- const ADAR_II = months.ADAR_II;
865
- /**
866
- * Returns true if the object is a Javascript Date
867
- * @private
868
- * @param {Object} obj
869
- */
870
- function isSimpleHebrewDate(obj) {
871
- return typeof obj === 'object' && obj !== null && typeof obj.yy === 'number' && typeof obj.mm === 'number' && typeof obj.dd === 'number';
872
- }
873
- /**
874
- * @private
875
- */
876
- function toSimpleHebrewDate(obj) {
877
- if (isSimpleHebrewDate(obj)) {
878
- return obj;
879
- } else if (typeof obj === 'number') {
880
- return abs2hebrew(obj);
881
- } else if (exports.greg.isDate(obj)) {
882
- const abs = exports.greg.greg2abs(obj);
883
- return abs2hebrew(abs);
884
- } else {
885
- throw new TypeError(`Argument not a Date: ${obj}`);
835
+ /**
836
+ * Adds multiple translations to `locale`, replacing any previous translations.
837
+ * @param {string} locale Locale name (i.e: `'he'`, `'fr'`).
838
+ * @param {LocaleData} data parsed data from a `.po` file.
839
+ */
840
+ static addTranslations(locale, data) {
841
+ if (typeof locale !== 'string') {
842
+ throw new TypeError(`Invalid locale name: ${locale}`);
843
+ }
844
+ const locale0 = locale.toLowerCase();
845
+ const loc = locales.get(locale0);
846
+ if (!loc) {
847
+ throw new TypeError(`Unknown locale: ${locale}`);
848
+ }
849
+ if (typeof data.contexts !== 'object' || typeof data.contexts[''] !== 'object') {
850
+ throw new TypeError(`Locale '${locale}' invalid compact format`);
851
+ }
852
+ const ctx = data.contexts[''];
853
+ Object.assign(loc, ctx);
886
854
  }
887
- }
888
- function getYahrzeitHD(hyear, date) {
889
- let hDeath = toSimpleHebrewDate(date);
890
- if (hyear <= hDeath.yy) {
891
- // Hebrew year ${hyear} occurs on or before original date in ${hDeath.yy}
892
- return undefined;
855
+ /**
856
+ * Activates a locale. Throws an error if the locale has not been previously added.
857
+ * After setting the locale to be used, all strings marked for translations
858
+ * will be represented by the corresponding translation in the specified locale.
859
+ * @param {string} locale Locale name (i.e: `'he'`, `'fr'`)
860
+ * @return {LocaleData}
861
+ */
862
+ static useLocale(locale) {
863
+ const locale0 = locale.toLowerCase();
864
+ const obj = locales.get(locale0);
865
+ if (!obj) {
866
+ throw new RangeError(`Locale '${locale}' not found`);
867
+ }
868
+ activeName = alias[locale0] || locale0;
869
+ activeLocale = obj;
870
+ return activeLocale;
893
871
  }
894
- if (hDeath.mm == CHESHVAN && hDeath.dd == 30 && !longCheshvan(hDeath.yy + 1)) {
895
- // If it's Heshvan 30 it depends on the first anniversary;
896
- // if that was not Heshvan 30, use the day before Kislev 1.
897
- hDeath = abs2hebrew(hebrew2abs(hyear, KISLEV$1, 1) - 1);
898
- } else if (hDeath.mm == KISLEV$1 && hDeath.dd == 30 && shortKislev(hDeath.yy + 1)) {
899
- // If it's Kislev 30 it depends on the first anniversary;
900
- // if that was not Kislev 30, use the day before Teveth 1.
901
- hDeath = abs2hebrew(hebrew2abs(hyear, TEVET$1, 1) - 1);
902
- } else if (hDeath.mm == ADAR_II) {
903
- // If it's Adar II, use the same day in last month of year (Adar or Adar II).
904
- hDeath.mm = monthsInYear(hyear);
905
- } else if (hDeath.mm == ADAR_I$1 && hDeath.dd == 30 && !isLeapYear(hyear)) {
906
- // If it's the 30th in Adar I and year is not a leap year
907
- // (so Adar has only 29 days), use the last day in Shevat.
908
- hDeath.dd = 30;
909
- hDeath.mm = SHVAT;
872
+
873
+ /**
874
+ * Returns the name of the active locale (i.e. 'he', 'ashkenazi', 'fr')
875
+ * @return {string}
876
+ */
877
+ static getLocaleName() {
878
+ return activeName;
910
879
  }
911
- // In all other cases, use the normal anniversary of the date of death.
912
- // advance day to rosh chodesh if needed
913
- if (hDeath.mm == CHESHVAN && hDeath.dd == 30 && !longCheshvan(hyear)) {
914
- hDeath.mm = KISLEV$1;
915
- hDeath.dd = 1;
916
- } else if (hDeath.mm == KISLEV$1 && hDeath.dd == 30 && shortKislev(hyear)) {
917
- hDeath.mm = TEVET$1;
918
- hDeath.dd = 1;
880
+
881
+ /**
882
+ * Returns the names of registered locales
883
+ * @return {string[]}
884
+ */
885
+ static getLocaleNames() {
886
+ const keys = Array.from(locales.keys());
887
+ return keys.sort((a, b) => a.localeCompare(b));
919
888
  }
920
- hDeath.yy = hyear;
921
- return hDeath;
922
- }
923
- function getBirthdayHD(hyear, date) {
924
- const orig = toSimpleHebrewDate(date);
925
- const origYear = orig.yy;
926
- if (hyear === origYear) {
927
- return orig;
928
- } else if (hyear < origYear) {
929
- // Hebrew year ${hyear} occurs on or before original date in ${origYear}
930
- return undefined;
889
+
890
+ /**
891
+ * @param {number} n
892
+ * @param {string} [locale] Optional locale name (i.e: `'he'`, `'fr'`). Defaults to active locale.
893
+ * @return {string}
894
+ */
895
+ static ordinal(n, locale) {
896
+ const locale1 = locale === null || locale === void 0 ? void 0 : locale.toLowerCase();
897
+ const locale0 = locale1 || activeName;
898
+ if (!locale0) {
899
+ return this.getEnOrdinal(n);
900
+ }
901
+ switch (locale0) {
902
+ case 'en':
903
+ case 's':
904
+ case 'a':
905
+ case 'ashkenazi':
906
+ case 'ashkenazi_litvish':
907
+ case 'ashkenazi_poylish':
908
+ case 'ashkenazi_standard':
909
+ return this.getEnOrdinal(n);
910
+ case 'es':
911
+ return n + 'º';
912
+ case 'h':
913
+ case 'he':
914
+ case 'he-x-nonikud':
915
+ return String(n);
916
+ default:
917
+ return n + '.';
918
+ }
931
919
  }
932
- const isOrigLeap = isLeapYear(origYear);
933
- let month = orig.mm;
934
- let day = orig.dd;
935
- if (month == ADAR_I$1 && !isOrigLeap || month == ADAR_II && isOrigLeap) {
936
- month = monthsInYear(hyear);
937
- } else if (month == CHESHVAN && day == 30 && !longCheshvan(hyear)) {
938
- month = KISLEV$1;
939
- day = 1;
940
- } else if (month == KISLEV$1 && day == 30 && shortKislev(hyear)) {
941
- month = TEVET$1;
942
- day = 1;
943
- } else if (month == ADAR_I$1 && day == 30 && isOrigLeap && !isLeapYear(hyear)) {
944
- month = NISAN$3;
945
- day = 1;
920
+
921
+ /**
922
+ * @private
923
+ * @param {number} n
924
+ * @return {string}
925
+ */
926
+ static getEnOrdinal(n) {
927
+ const s = ['th', 'st', 'nd', 'rd'];
928
+ const v = n % 100;
929
+ return n + (s[(v - 20) % 10] || s[v] || s[0]);
930
+ }
931
+
932
+ /**
933
+ * Removes nekudot from Hebrew string
934
+ * @param {string} str
935
+ * @return {string}
936
+ */
937
+ static hebrewStripNikkud(str) {
938
+ return str.replace(/[\u0590-\u05bd]/g, '').replace(/[\u05bf-\u05c7]/g, '');
946
939
  }
947
- return {
948
- yy: hyear,
949
- mm: month,
950
- dd: day
951
- };
952
940
  }
941
+ Locale.addLocale('en', noopLocale);
942
+ Locale.addLocale('s', noopLocale);
943
+ Locale.addLocale('', noopLocale);
944
+ Locale.useLocale('en');
953
945
 
954
946
  /**
955
947
  * @private
@@ -1784,7 +1776,7 @@ class Event {
1784
1776
  this.desc = desc;
1785
1777
  this.mask = +mask;
1786
1778
  if (typeof attrs === 'object' && attrs !== null) {
1787
- Object.keys(attrs).forEach(k => this[k] = attrs[k]);
1779
+ Object.assign(this, attrs);
1788
1780
  }
1789
1781
  }
1790
1782
  /**
@@ -6574,10 +6566,10 @@ class Location extends GeoLocation {
6574
6566
  return true;
6575
6567
  }
6576
6568
  }
6577
- classicCities0.forEach(city => {
6569
+ for (const city of classicCities0) {
6578
6570
  const location = new Location(city[2], city[3], city[1] == 'IL', city[4], city[0], city[1], undefined, city[5]);
6579
6571
  Location.addLocation(location.getName(), location);
6580
- });
6572
+ }
6581
6573
 
6582
6574
  const _formatters = new Map();
6583
6575
 
@@ -9490,40 +9482,7 @@ const MINOR_HOLIDAY$1 = flags.MINOR_HOLIDAY;
9490
9482
  const EREV$1 = flags.EREV;
9491
9483
  // const CHOL_HAMOED = flags.CHOL_HAMOED;
9492
9484
 
9493
- /**
9494
- * Avoid dependency on ES6 Map object
9495
- * @private
9496
- */
9497
- class SimpleMap {
9498
- /**
9499
- * @param {string} key
9500
- * @return {boolean}
9501
- */
9502
- has(key) {
9503
- return typeof this[key] !== 'undefined';
9504
- }
9505
- /**
9506
- * @param {string} key
9507
- * @return {any}
9508
- */
9509
- get(key) {
9510
- return this[key];
9511
- }
9512
- /**
9513
- * @param {string} key
9514
- * @param {any} val
9515
- */
9516
- set(key, val) {
9517
- this[key] = val;
9518
- }
9519
- /**
9520
- * @return {string[]}
9521
- */
9522
- keys() {
9523
- return Object.keys(this);
9524
- }
9525
- }
9526
- const sedraCache = new SimpleMap();
9485
+ const sedraCache = new Map();
9527
9486
 
9528
9487
  /**
9529
9488
  * @private
@@ -9567,16 +9526,15 @@ function getHolidaysForYear_(year) {
9567
9526
  }
9568
9527
  const RH = new HDate(1, TISHREI$1, year);
9569
9528
  const pesach = new HDate(15, NISAN$1, year);
9570
- const h = new SimpleMap();
9529
+ const map = new Map();
9571
9530
  // eslint-disable-next-line require-jsdoc
9572
9531
  function add() {
9573
9532
  for (var _len = arguments.length, events = new Array(_len), _key = 0; _key < _len; _key++) {
9574
9533
  events[_key] = arguments[_key];
9575
9534
  }
9576
- // for (const ev of events) {
9577
- events.forEach(ev => {
9535
+ for (const ev of events) {
9578
9536
  const key = ev.date.toString();
9579
- const arr = h.get(key);
9537
+ const arr = map.get(key);
9580
9538
  if (typeof arr === 'object') {
9581
9539
  if (arr[0].getFlags() & flags.EREV) {
9582
9540
  arr.unshift(ev);
@@ -9584,17 +9542,17 @@ function getHolidaysForYear_(year) {
9584
9542
  arr.push(ev);
9585
9543
  }
9586
9544
  } else {
9587
- h.set(key, [ev]);
9545
+ map.set(key, [ev]);
9588
9546
  }
9589
- });
9547
+ }
9590
9548
  }
9591
- staticHolidays.forEach(h => {
9549
+ for (const h of staticHolidays) {
9592
9550
  const hd = new HDate(h.dd, h.mm, year);
9593
9551
  const ev = new HolidayEvent(hd, h.desc, h.flags);
9594
9552
  if (h.emoji) ev.emoji = h.emoji;
9595
9553
  if (h.chmDay) ev.cholHaMoedDay = h.chmDay;
9596
9554
  add(ev);
9597
- });
9555
+ }
9598
9556
 
9599
9557
  // standard holidays that don't shift based on year
9600
9558
  add(new RoshHashanaEvent(RH, year, CHAG | LIGHT_CANDLES_TZEIS$1));
@@ -9646,7 +9604,7 @@ function getHolidaysForYear_(year) {
9646
9604
  if (yomHaZikaronDt) {
9647
9605
  add(new HolidayEvent(yomHaZikaronDt, 'Yom HaZikaron', MODERN_HOLIDAY$1, emojiIsraelFlag), new HolidayEvent(yomHaZikaronDt.next(), 'Yom HaAtzma\'ut', MODERN_HOLIDAY$1, emojiIsraelFlag));
9648
9606
  }
9649
- staticModernHolidays.forEach(h => {
9607
+ for (const h of staticModernHolidays) {
9650
9608
  if (year >= h.firstYear) {
9651
9609
  let hd = new HDate(h.dd, h.mm, year);
9652
9610
  const dow = hd.getDay();
@@ -9664,7 +9622,7 @@ function getHolidaysForYear_(year) {
9664
9622
  }
9665
9623
  add(ev);
9666
9624
  }
9667
- });
9625
+ }
9668
9626
  let tamuz17 = new HDate(17, TAMUZ, year);
9669
9627
  let tamuz17attrs;
9670
9628
  if (tamuz17.getDay() == SAT$1) {
@@ -9735,8 +9693,8 @@ function getHolidaysForYear_(year) {
9735
9693
  emoji: '☀️'
9736
9694
  }));
9737
9695
  }
9738
- yearCache.set(year, h);
9739
- return h;
9696
+ yearCache.set(year, map);
9697
+ return map;
9740
9698
  }
9741
9699
 
9742
9700
  /**
@@ -9803,7 +9761,7 @@ class DailyLearning {
9803
9761
  }
9804
9762
  }
9805
9763
 
9806
- const version="5.0.0";
9764
+ const version="5.0.2";
9807
9765
 
9808
9766
  const headers$1={"plural-forms":"nplurals=2; plural=(n > 1);"};const contexts$1={"":{Shabbat:["Shabbos"],"Achrei Mot":["Achrei Mos"],Bechukotai:["Bechukosai"],"Beha'alotcha":["Beha'aloscha"],Bereshit:["Bereshis"],Chukat:["Chukas"],"Erev Shavuot":["Erev Shavuos"],"Erev Sukkot":["Erev Sukkos"],"Ki Tavo":["Ki Savo"],"Ki Teitzei":["Ki Seitzei"],"Ki Tisa":["Ki Sisa"],Matot:["Matos"],"Purim Katan":["Purim Koton"],"Shabbat Chazon":["Shabbos Chazon"],"Shabbat HaChodesh":["Shabbos HaChodesh"],"Shabbat HaGadol":["Shabbos HaGadol"],"Shabbat Nachamu":["Shabbos Nachamu"],"Shabbat Parah":["Shabbos Parah"],"Shabbat Shekalim":["Shabbos Shekalim"],"Shabbat Shuva":["Shabbos Shuvah"],"Shabbat Zachor":["Shabbos Zachor"],Shavuot:["Shavuos"],"Shavuot I":["Shavuos I"],"Shavuot II":["Shavuos II"],Shemot:["Shemos"],"Shmini Atzeret":["Shmini Atzeres"],"Simchat Torah":["Simchas Torah"],Sukkot:["Sukkos"],"Sukkot I":["Sukkos I"],"Sukkot II":["Sukkos II"],"Sukkot II (CH''M)":["Sukkos II (CH''M)"],"Sukkot III (CH''M)":["Sukkos III (CH''M)"],"Sukkot IV (CH''M)":["Sukkos IV (CH''M)"],"Sukkot V (CH''M)":["Sukkos V (CH''M)"],"Sukkot VI (CH''M)":["Sukkos VI (CH''M)"],"Sukkot VII (Hoshana Raba)":["Sukkos VII (Hoshana Raba)"],"Ta'anit Bechorot":["Ta'anis Bechoros"],"Ta'anit Esther":["Ta'anis Esther"],Toldot:["Toldos"],Vaetchanan:["Vaeschanan"],Yitro:["Yisro"],"Vezot Haberakhah":["Vezos Haberakhah"],Parashat:["Parshas"],"Leil Selichot":["Leil Selichos"],"Shabbat Mevarchim Chodesh":["Shabbos Mevorchim Chodesh"],"Shabbat Shirah":["Shabbos Shirah"],Tevet:["Teves"],"Asara B'Tevet":["Asara B'Teves"],"Alot HaShachar":["Alos HaShachar"],"Kriat Shema, sof zeman":["Krias Shema, sof zman"],"Tefilah, sof zeman":["Tefilah, sof zman"],"Kriat Shema, sof zeman (MGA)":["Krias Shema, sof zman (MGA)"],"Tefilah, sof zeman (MGA)":["Tefilah, sof zman (MGA)"],"Chatzot HaLailah":["Chatzos HaLailah"],"Chatzot hayom":["Chatzos"],"Tzeit HaKochavim":["Tzeis HaKochavim"],"Birkat Hachamah":["Birkas Hachamah"],"Shushan Purim Katan":["Shushan Purim Koton"]}};var poAshkenazi = {headers:headers$1,contexts:contexts$1};
9809
9767
 
@@ -9816,9 +9774,9 @@ Locale.addLocale('he', poHe);
9816
9774
  Locale.addLocale('h', poHe);
9817
9775
  const heStrs = poHe.contexts[''];
9818
9776
  const heNoNikud = {};
9819
- Object.keys(heStrs).forEach(key => {
9820
- heNoNikud[key] = [Locale.hebrewStripNikkud(heStrs[key][0])];
9821
- });
9777
+ for (const [key, val] of Object.entries(heStrs)) {
9778
+ heNoNikud[key] = [Locale.hebrewStripNikkud(val[0])];
9779
+ }
9822
9780
  const poHeNoNikud = {
9823
9781
  headers: poHe.headers,
9824
9782
  contexts: {
@@ -10611,24 +10569,12 @@ const RECOGNIZED_OPTIONS = {
10611
10569
  * @param {CalOptions} options
10612
10570
  */
10613
10571
  function warnUnrecognizedOptions(options) {
10614
- Object.keys(options).forEach(k => {
10572
+ for (const k of Object.keys(options)) {
10615
10573
  if (typeof RECOGNIZED_OPTIONS[k] === 'undefined' && !unrecognizedAlreadyWarned.has(k)) {
10616
10574
  console.warn(`Ignoring unrecognized HebrewCalendar option: ${k}`);
10617
10575
  unrecognizedAlreadyWarned.add(k);
10618
10576
  }
10619
- });
10620
- }
10621
-
10622
- /**
10623
- * A bit like Object.assign(), but just a shallow copy
10624
- * @private
10625
- * @param {any} target
10626
- * @param {any} source
10627
- * @return {any}
10628
- */
10629
- function shallowCopy(target, source) {
10630
- Object.keys(source).forEach(k => target[k] = source[k]);
10631
- return target;
10577
+ }
10632
10578
  }
10633
10579
  const israelCityOffset = {
10634
10580
  'Jerusalem': 40,
@@ -10989,6 +10935,7 @@ function observedInDiaspora(ev) {
10989
10935
  return ev.observedInDiaspora();
10990
10936
  }
10991
10937
  const yearArrayCache = new Map();
10938
+ const holidaysOnDate = new Map();
10992
10939
 
10993
10940
  /**
10994
10941
  * HebrewCalendar is the main interface to the `@hebcal/core` library.
@@ -11107,7 +11054,7 @@ class HebrewCalendar {
11107
11054
  */
11108
11055
  static calendar() {
11109
11056
  let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
11110
- options = shallowCopy({}, options); // so we can modify freely
11057
+ options = Object.assign({}, options); // so we can modify freely
11111
11058
  checkCandleOptions(options);
11112
11059
  const location = options.location = options.location || defaultLocation;
11113
11060
  const il = options.il = options.il || location.il || false;
@@ -11156,9 +11103,9 @@ class HebrewCalendar {
11156
11103
  const dow = hd.getDay();
11157
11104
  let candlesEv;
11158
11105
  const ev = holidaysYear.get(hd.toString()) || [];
11159
- ev.forEach(e => {
11106
+ for (const e of ev) {
11160
11107
  candlesEv = appendHolidayAndRelated(evts, e, options, candlesEv, dow);
11161
- });
11108
+ }
11162
11109
  if (options.sedrot && dow === SAT) {
11163
11110
  const parsha0 = sedra.lookup(abs);
11164
11111
  if (!parsha0.chag) {
@@ -11167,8 +11114,7 @@ class HebrewCalendar {
11167
11114
  }
11168
11115
  const dailyLearning = options.dailyLearning;
11169
11116
  if (typeof dailyLearning === 'object') {
11170
- Object.keys(dailyLearning).forEach(key => {
11171
- const val = dailyLearning[key];
11117
+ for (const [key, val] of Object.entries(dailyLearning)) {
11172
11118
  if (val) {
11173
11119
  const name = key === 'yerushalmi' ? val === 2 ? 'yerushalmi-schottenstein' : 'yerushalmi-vilna' : key;
11174
11120
  const learningEv = DailyLearning.lookup(name, hd);
@@ -11176,7 +11122,7 @@ class HebrewCalendar {
11176
11122
  evts.push(learningEv);
11177
11123
  }
11178
11124
  }
11179
- });
11125
+ }
11180
11126
  }
11181
11127
  if (options.omer && abs >= beginOmer && abs <= endOmer) {
11182
11128
  const omer = abs - beginOmer + 1;
@@ -11338,20 +11284,29 @@ class HebrewCalendar {
11338
11284
  }
11339
11285
 
11340
11286
  /**
11341
- * Returns an array of Events on this date (or undefined if no events)
11287
+ * Returns an array of Events on this date (or `undefined` if no events)
11342
11288
  * @param {HDate|Date|number} date Hebrew Date, Gregorian date, or absolute R.D. day number
11343
11289
  * @param {boolean} [il] use the Israeli schedule for holidays
11344
11290
  * @return {Event[]}
11345
11291
  */
11346
11292
  static getHolidaysOnDate(date, il) {
11347
11293
  const hd = HDate.isHDate(date) ? date : new HDate(date);
11294
+ const hdStr = hd.toString();
11295
+ const cacheKey = hdStr + '/' + (typeof il === 'undefined' ? 2 : il ? 1 : 0);
11296
+ if (holidaysOnDate.has(cacheKey)) {
11297
+ return holidaysOnDate.get(cacheKey);
11298
+ }
11348
11299
  const yearMap = getHolidaysForYear_(hd.getFullYear());
11349
- const events = yearMap.get(hd.toString());
11300
+ const events = yearMap.get(hdStr);
11301
+ // if il isn't a boolean return both diaspora + IL for day
11350
11302
  if (typeof il === 'undefined' || typeof events === 'undefined') {
11303
+ holidaysOnDate.set(cacheKey, events);
11351
11304
  return events;
11352
11305
  }
11353
11306
  const myFilter = il ? observedInIsrael : observedInDiaspora;
11354
- return events.filter(myFilter);
11307
+ const filtered = events.filter(myFilter);
11308
+ holidaysOnDate.set(cacheKey, filtered);
11309
+ return filtered;
11355
11310
  }
11356
11311
 
11357
11312
  /**