@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/index.mjs CHANGED
@@ -1,349 +1,4 @@
1
- /*! @hebcal/core v5.0.0 */
2
- const GERESH = '׳';
3
- const GERSHAYIM = '״';
4
- const heb2num = {
5
- 'א': 1,
6
- 'ב': 2,
7
- 'ג': 3,
8
- 'ד': 4,
9
- 'ה': 5,
10
- 'ו': 6,
11
- 'ז': 7,
12
- 'ח': 8,
13
- 'ט': 9,
14
- 'י': 10,
15
- 'כ': 20,
16
- 'ל': 30,
17
- 'מ': 40,
18
- 'נ': 50,
19
- 'ס': 60,
20
- 'ע': 70,
21
- 'פ': 80,
22
- 'צ': 90,
23
- 'ק': 100,
24
- 'ר': 200,
25
- 'ש': 300,
26
- 'ת': 400
27
- };
28
- const num2heb = new Map();
29
- Object.keys(heb2num).forEach(key => {
30
- const val = heb2num[key];
31
- num2heb.set(val, key);
32
- });
33
-
34
- /**
35
- * @private
36
- * @param {number} num
37
- * @return {number[]}
38
- */
39
- function num2digits(num) {
40
- const digits = [];
41
- while (num > 0) {
42
- if (num === 15 || num === 16) {
43
- digits.push(9);
44
- digits.push(num - 9);
45
- break;
46
- }
47
- let incr = 100;
48
- let i;
49
- for (i = 400; i > num; i -= incr) {
50
- if (i === incr) {
51
- incr = incr / 10;
52
- }
53
- }
54
- digits.push(i);
55
- num -= i;
56
- }
57
- return digits;
58
- }
59
-
60
- /**
61
- * Converts a numerical value to a string of Hebrew letters.
62
- *
63
- * When specifying years of the Hebrew calendar in the present millennium,
64
- * we omit the thousands (which is presently 5 [ה]).
65
- * @example
66
- * gematriya(5774) // 'תשע״ד' - cropped to 774
67
- * gematriya(25) // 'כ״ה'
68
- * gematriya(60) // 'ס׳'
69
- * gematriya(3761) // 'ג׳תשס״א'
70
- * gematriya(1123) // 'א׳קכ״ג'
71
- * @param {number} number
72
- * @return {string}
73
- */
74
- function gematriya(number) {
75
- const num = parseInt(number, 10);
76
- if (!num) {
77
- throw new TypeError(`invalid parameter to gematriya ${number}`);
78
- }
79
- let str = '';
80
- const thousands = Math.floor(num / 1000);
81
- if (thousands > 0 && thousands !== 5) {
82
- const tdigits = num2digits(thousands);
83
- for (const tdig of tdigits) {
84
- str += num2heb.get(tdig);
85
- }
86
- str += GERESH;
87
- }
88
- const digits = num2digits(num % 1000);
89
- if (digits.length == 1) {
90
- return str + num2heb.get(digits[0]) + GERESH;
91
- }
92
- for (let i = 0; i < digits.length; i++) {
93
- if (i + 1 === digits.length) {
94
- str += GERSHAYIM;
95
- }
96
- str += num2heb.get(digits[i]);
97
- }
98
- return str;
99
- }
100
-
101
- /**
102
- * Converts a string of Hebrew letters to a numerical value.
103
- *
104
- * Only considers the value of Hebrew letters `א` through `ת`.
105
- * Ignores final Hebrew letters such as `ך` (kaf sofit) or `ם` (mem sofit)
106
- * and vowels (nekudot).
107
- *
108
- * @param {string} str
109
- * @return {number}
110
- */
111
- function gematriyaStrToNum(str) {
112
- let num = 0;
113
- const gereshIdx = str.indexOf(GERESH);
114
- if (gereshIdx !== -1 && gereshIdx !== str.length - 1) {
115
- const thousands = str.substring(0, gereshIdx);
116
- num += gematriyaStrToNum(thousands) * 1000;
117
- str = str.substring(gereshIdx);
118
- }
119
- for (const ch of str) {
120
- const n = heb2num[ch];
121
- if (typeof n === 'number') {
122
- num += n;
123
- }
124
- }
125
- return num;
126
- }
127
-
128
- const noopLocale = {
129
- headers: {
130
- 'plural-forms': 'nplurals=2; plural=(n!=1);'
131
- },
132
- contexts: {
133
- '': {}
134
- }
135
- };
136
- const alias = {
137
- 'h': 'he',
138
- 'a': 'ashkenazi',
139
- 's': 'en',
140
- '': 'en'
141
- };
142
-
143
- /** @private */
144
- const locales = new Map();
145
- /** @private */
146
- let activeLocale = null;
147
- /** @private */
148
- let activeName = null;
149
-
150
- /**
151
- * A locale in Hebcal is used for translations/transliterations of
152
- * holidays. `@hebcal/core` supports four locales by default
153
- * * `en` - default, Sephardic transliterations (e.g. "Shabbat")
154
- * * `ashkenazi` - Ashkenazi transliterations (e.g. "Shabbos")
155
- * * `he` - Hebrew (e.g. "שַׁבָּת")
156
- * * `he-x-NoNikud` - Hebrew without nikud (e.g. "שבת")
157
- */
158
- class Locale {
159
- /**
160
- * Returns translation only if `locale` offers a non-empty translation for `id`.
161
- * Otherwise, returns `undefined`.
162
- * @param {string} id Message ID to translate
163
- * @param {string} [locale] Optional locale name (i.e: `'he'`, `'fr'`). Defaults to active locale.
164
- * @return {string}
165
- */
166
- static lookupTranslation(id, locale) {
167
- const locale0 = locale === null || locale === void 0 ? void 0 : locale.toLowerCase();
168
- const loc = typeof locale == 'string' && locales.get(locale0) || activeLocale;
169
- const array = loc[id];
170
- if (array && array.length && array[0].length) {
171
- return array[0];
172
- }
173
- return undefined;
174
- }
175
-
176
- /**
177
- * By default, if no translation was found, returns `id`.
178
- * @param {string} id Message ID to translate
179
- * @param {string} [locale] Optional locale name (i.e: `'he'`, `'fr'`). Defaults to active locale.
180
- * @return {string}
181
- */
182
- static gettext(id, locale) {
183
- const text = this.lookupTranslation(id, locale);
184
- if (typeof text == 'undefined') {
185
- return id;
186
- }
187
- return text;
188
- }
189
-
190
- /**
191
- * Register locale translations.
192
- * @param {string} locale Locale name (i.e.: `'he'`, `'fr'`)
193
- * @param {LocaleData} data parsed data from a `.po` file.
194
- */
195
- static addLocale(locale, data) {
196
- if (typeof locale !== 'string') {
197
- throw new TypeError(`Invalid locale name: ${locale}`);
198
- }
199
- if (typeof data.contexts !== 'object' || typeof data.contexts[''] !== 'object') {
200
- throw new TypeError(`Locale '${locale}' invalid compact format`);
201
- }
202
- locales.set(locale.toLowerCase(), data.contexts['']);
203
- }
204
-
205
- /**
206
- * Adds a translation to `locale`, replacing any previous translation.
207
- * @param {string} locale Locale name (i.e: `'he'`, `'fr'`).
208
- * @param {string} id Message ID to translate
209
- * @param {string} translation Translation text
210
- */
211
- static addTranslation(locale, id, translation) {
212
- if (typeof locale !== 'string') {
213
- throw new TypeError(`Invalid locale name: ${locale}`);
214
- }
215
- const locale0 = locale.toLowerCase();
216
- const loc = locales.get(locale0);
217
- if (!loc) {
218
- throw new TypeError(`Unknown locale: ${locale}`);
219
- }
220
- if (typeof id !== 'string' || id.length === 0) {
221
- throw new TypeError(`Invalid id: ${id}`);
222
- }
223
- const isArray = Array.isArray(translation);
224
- if (isArray) {
225
- const t0 = translation[0];
226
- if (typeof t0 !== 'string' || t0.length === 0) {
227
- throw new TypeError(`Invalid translation array: ${translation}`);
228
- }
229
- } else if (typeof translation !== 'string') {
230
- throw new TypeError(`Invalid translation: ${translation}`);
231
- }
232
- loc[id] = isArray ? translation : [translation];
233
- }
234
- /**
235
- * Adds multiple translations to `locale`, replacing any previous translations.
236
- * @param {string} locale Locale name (i.e: `'he'`, `'fr'`).
237
- * @param {LocaleData} data parsed data from a `.po` file.
238
- */
239
- static addTranslations(locale, data) {
240
- if (typeof locale !== 'string') {
241
- throw new TypeError(`Invalid locale name: ${locale}`);
242
- }
243
- const locale0 = locale.toLowerCase();
244
- const loc = locales.get(locale0);
245
- if (!loc) {
246
- throw new TypeError(`Unknown locale: ${locale}`);
247
- }
248
- if (typeof data.contexts !== 'object' || typeof data.contexts[''] !== 'object') {
249
- throw new TypeError(`Locale '${locale}' invalid compact format`);
250
- }
251
- const ctx = data.contexts[''];
252
- Object.keys(ctx).forEach(id => {
253
- loc[id] = ctx[id];
254
- });
255
- }
256
- /**
257
- * Activates a locale. Throws an error if the locale has not been previously added.
258
- * After setting the locale to be used, all strings marked for translations
259
- * will be represented by the corresponding translation in the specified locale.
260
- * @param {string} locale Locale name (i.e: `'he'`, `'fr'`)
261
- * @return {LocaleData}
262
- */
263
- static useLocale(locale) {
264
- const locale0 = locale.toLowerCase();
265
- const obj = locales.get(locale0);
266
- if (!obj) {
267
- throw new RangeError(`Locale '${locale}' not found`);
268
- }
269
- activeName = alias[locale0] || locale0;
270
- activeLocale = obj;
271
- return activeLocale;
272
- }
273
-
274
- /**
275
- * Returns the name of the active locale (i.e. 'he', 'ashkenazi', 'fr')
276
- * @return {string}
277
- */
278
- static getLocaleName() {
279
- return activeName;
280
- }
281
-
282
- /**
283
- * Returns the names of registered locales
284
- * @return {string[]}
285
- */
286
- static getLocaleNames() {
287
- const keys = Array.from(locales.keys());
288
- return keys.sort((a, b) => a.localeCompare(b));
289
- }
290
-
291
- /**
292
- * @param {number} n
293
- * @param {string} [locale] Optional locale name (i.e: `'he'`, `'fr'`). Defaults to active locale.
294
- * @return {string}
295
- */
296
- static ordinal(n, locale) {
297
- const locale1 = locale === null || locale === void 0 ? void 0 : locale.toLowerCase();
298
- const locale0 = locale1 || activeName;
299
- if (!locale0) {
300
- return this.getEnOrdinal(n);
301
- }
302
- switch (locale0) {
303
- case 'en':
304
- case 's':
305
- case 'a':
306
- case 'ashkenazi':
307
- case 'ashkenazi_litvish':
308
- case 'ashkenazi_poylish':
309
- case 'ashkenazi_standard':
310
- return this.getEnOrdinal(n);
311
- case 'es':
312
- return n + 'º';
313
- case 'h':
314
- case 'he':
315
- case 'he-x-nonikud':
316
- return String(n);
317
- default:
318
- return n + '.';
319
- }
320
- }
321
-
322
- /**
323
- * @private
324
- * @param {number} n
325
- * @return {string}
326
- */
327
- static getEnOrdinal(n) {
328
- const s = ['th', 'st', 'nd', 'rd'];
329
- const v = n % 100;
330
- return n + (s[(v - 20) % 10] || s[v] || s[0]);
331
- }
332
-
333
- /**
334
- * Removes nekudot from Hebrew string
335
- * @param {string} str
336
- * @return {string}
337
- */
338
- static hebrewStripNikkud(str) {
339
- return str.replace(/[\u0590-\u05bd]/g, '').replace(/[\u05bf-\u05c7]/g, '');
340
- }
341
- }
342
- Locale.addLocale('en', noopLocale);
343
- Locale.addLocale('s', noopLocale);
344
- Locale.addLocale('', noopLocale);
345
- Locale.useLocale('en');
346
-
1
+ /*! @hebcal/core v5.0.2 */
347
2
  /** @private */
348
3
  const lengths = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
349
4
  /** @private */
@@ -937,76 +592,413 @@ function toSimpleHebrewDate(obj) {
937
592
  else {
938
593
  throw new TypeError(`Argument not a Date: ${obj}`);
939
594
  }
940
- }
941
- function getYahrzeitHD(hyear, date) {
942
- let hDeath = toSimpleHebrewDate(date);
943
- if (hyear <= hDeath.yy) {
944
- // Hebrew year ${hyear} occurs on or before original date in ${hDeath.yy}
945
- return undefined;
595
+ }
596
+ function getYahrzeitHD(hyear, date) {
597
+ let hDeath = toSimpleHebrewDate(date);
598
+ if (hyear <= hDeath.yy) {
599
+ // Hebrew year ${hyear} occurs on or before original date in ${hDeath.yy}
600
+ return undefined;
601
+ }
602
+ if (hDeath.mm == CHESHVAN && hDeath.dd == 30 && !longCheshvan(hDeath.yy + 1)) {
603
+ // If it's Heshvan 30 it depends on the first anniversary;
604
+ // if that was not Heshvan 30, use the day before Kislev 1.
605
+ hDeath = abs2hebrew(hebrew2abs(hyear, KISLEV$1, 1) - 1);
606
+ }
607
+ else if (hDeath.mm == KISLEV$1 && hDeath.dd == 30 && shortKislev(hDeath.yy + 1)) {
608
+ // If it's Kislev 30 it depends on the first anniversary;
609
+ // if that was not Kislev 30, use the day before Teveth 1.
610
+ hDeath = abs2hebrew(hebrew2abs(hyear, TEVET$1, 1) - 1);
611
+ }
612
+ else if (hDeath.mm == ADAR_II) {
613
+ // If it's Adar II, use the same day in last month of year (Adar or Adar II).
614
+ hDeath.mm = monthsInYear(hyear);
615
+ }
616
+ else if (hDeath.mm == ADAR_I$1 && hDeath.dd == 30 && !isLeapYear(hyear)) {
617
+ // If it's the 30th in Adar I and year is not a leap year
618
+ // (so Adar has only 29 days), use the last day in Shevat.
619
+ hDeath.dd = 30;
620
+ hDeath.mm = SHVAT;
621
+ }
622
+ // In all other cases, use the normal anniversary of the date of death.
623
+ // advance day to rosh chodesh if needed
624
+ if (hDeath.mm == CHESHVAN && hDeath.dd == 30 && !longCheshvan(hyear)) {
625
+ hDeath.mm = KISLEV$1;
626
+ hDeath.dd = 1;
627
+ }
628
+ else if (hDeath.mm == KISLEV$1 && hDeath.dd == 30 && shortKislev(hyear)) {
629
+ hDeath.mm = TEVET$1;
630
+ hDeath.dd = 1;
631
+ }
632
+ hDeath.yy = hyear;
633
+ return hDeath;
634
+ }
635
+ function getBirthdayHD(hyear, date) {
636
+ const orig = toSimpleHebrewDate(date);
637
+ const origYear = orig.yy;
638
+ if (hyear === origYear) {
639
+ return orig;
640
+ }
641
+ else if (hyear < origYear) {
642
+ // Hebrew year ${hyear} occurs on or before original date in ${origYear}
643
+ return undefined;
644
+ }
645
+ const isOrigLeap = isLeapYear(origYear);
646
+ let month = orig.mm;
647
+ let day = orig.dd;
648
+ if ((month == ADAR_I$1 && !isOrigLeap) || (month == ADAR_II && isOrigLeap)) {
649
+ month = monthsInYear(hyear);
650
+ }
651
+ else if (month == CHESHVAN && day == 30 && !longCheshvan(hyear)) {
652
+ month = KISLEV$1;
653
+ day = 1;
654
+ }
655
+ else if (month == KISLEV$1 && day == 30 && shortKislev(hyear)) {
656
+ month = TEVET$1;
657
+ day = 1;
658
+ }
659
+ else if (month == ADAR_I$1 && day == 30 && isOrigLeap && !isLeapYear(hyear)) {
660
+ month = NISAN$3;
661
+ day = 1;
662
+ }
663
+ return { yy: hyear, mm: month, dd: day };
664
+ }
665
+
666
+ const GERESH = '׳';
667
+ const GERSHAYIM = '״';
668
+ const alefbet = {
669
+ 'א': 1,
670
+ 'ב': 2,
671
+ 'ג': 3,
672
+ 'ד': 4,
673
+ 'ה': 5,
674
+ 'ו': 6,
675
+ 'ז': 7,
676
+ 'ח': 8,
677
+ 'ט': 9,
678
+ 'י': 10,
679
+ 'כ': 20,
680
+ 'ל': 30,
681
+ 'מ': 40,
682
+ 'נ': 50,
683
+ 'ס': 60,
684
+ 'ע': 70,
685
+ 'פ': 80,
686
+ 'צ': 90,
687
+ 'ק': 100,
688
+ 'ר': 200,
689
+ 'ש': 300,
690
+ 'ת': 400,
691
+ };
692
+ const heb2num = new Map();
693
+ const num2heb = new Map();
694
+ for (const [key, val] of Object.entries(alefbet)) {
695
+ heb2num.set(key, val);
696
+ num2heb.set(val, key);
697
+ }
698
+ function num2digits(num) {
699
+ const digits = [];
700
+ while (num > 0) {
701
+ if (num === 15 || num === 16) {
702
+ digits.push(9);
703
+ digits.push(num - 9);
704
+ break;
705
+ }
706
+ let incr = 100;
707
+ let i;
708
+ for (i = 400; i > num; i -= incr) {
709
+ if (i === incr) {
710
+ incr = incr / 10;
711
+ }
712
+ }
713
+ digits.push(i);
714
+ num -= i;
715
+ }
716
+ return digits;
717
+ }
718
+ /**
719
+ * Converts a numerical value to a string of Hebrew letters.
720
+ *
721
+ * When specifying years of the Hebrew calendar in the present millennium,
722
+ * we omit the thousands (which is presently 5 [ה]).
723
+ * @example
724
+ * gematriya(5774) // 'תשע״ד' - cropped to 774
725
+ * gematriya(25) // 'כ״ה'
726
+ * gematriya(60) // 'ס׳'
727
+ * gematriya(3761) // 'ג׳תשס״א'
728
+ * gematriya(1123) // 'א׳קכ״ג'
729
+ * @param {number} num
730
+ * @return {string}
731
+ */
732
+ function gematriya(num) {
733
+ const num0 = num;
734
+ const num1 = parseInt(num0, 10);
735
+ if (!num1) {
736
+ throw new TypeError(`invalid parameter to gematriya ${num}`);
737
+ }
738
+ let str = '';
739
+ const thousands = Math.floor(num1 / 1000);
740
+ if (thousands > 0 && thousands !== 5) {
741
+ const tdigits = num2digits(thousands);
742
+ for (const tdig of tdigits) {
743
+ str += num2heb.get(tdig);
744
+ }
745
+ str += GERESH;
746
+ }
747
+ const digits = num2digits(num1 % 1000);
748
+ if (digits.length == 1) {
749
+ return str + num2heb.get(digits[0]) + GERESH;
750
+ }
751
+ for (let i = 0; i < digits.length; i++) {
752
+ if (i + 1 === digits.length) {
753
+ str += GERSHAYIM;
754
+ }
755
+ str += num2heb.get(digits[i]);
756
+ }
757
+ return str;
758
+ }
759
+ /**
760
+ * Converts a string of Hebrew letters to a numerical value.
761
+ *
762
+ * Only considers the value of Hebrew letters `א` through `ת`.
763
+ * Ignores final Hebrew letters such as `ך` (kaf sofit) or `ם` (mem sofit)
764
+ * and vowels (nekudot).
765
+ *
766
+ * @param {string} str
767
+ * @return {number}
768
+ */
769
+ function gematriyaStrToNum(str) {
770
+ let num = 0;
771
+ const gereshIdx = str.indexOf(GERESH);
772
+ if (gereshIdx !== -1 && gereshIdx !== str.length - 1) {
773
+ const thousands = str.substring(0, gereshIdx);
774
+ num += gematriyaStrToNum(thousands) * 1000;
775
+ str = str.substring(gereshIdx);
776
+ }
777
+ for (const ch of str) {
778
+ const n = heb2num.get(ch);
779
+ if (typeof n === 'number') {
780
+ num += n;
781
+ }
782
+ }
783
+ return num;
784
+ }
785
+
786
+ const noopLocale = {
787
+ headers: {
788
+ 'plural-forms': 'nplurals=2; plural=(n!=1);'
789
+ },
790
+ contexts: {
791
+ '': {}
792
+ }
793
+ };
794
+ const alias = {
795
+ 'h': 'he',
796
+ 'a': 'ashkenazi',
797
+ 's': 'en',
798
+ '': 'en'
799
+ };
800
+
801
+ /** @private */
802
+ const locales = new Map();
803
+ /** @private */
804
+ let activeLocale = null;
805
+ /** @private */
806
+ let activeName = null;
807
+
808
+ /**
809
+ * A locale in Hebcal is used for translations/transliterations of
810
+ * holidays. `@hebcal/core` supports four locales by default
811
+ * * `en` - default, Sephardic transliterations (e.g. "Shabbat")
812
+ * * `ashkenazi` - Ashkenazi transliterations (e.g. "Shabbos")
813
+ * * `he` - Hebrew (e.g. "שַׁבָּת")
814
+ * * `he-x-NoNikud` - Hebrew without nikud (e.g. "שבת")
815
+ */
816
+ class Locale {
817
+ /**
818
+ * Returns translation only if `locale` offers a non-empty translation for `id`.
819
+ * Otherwise, returns `undefined`.
820
+ * @param {string} id Message ID to translate
821
+ * @param {string} [locale] Optional locale name (i.e: `'he'`, `'fr'`). Defaults to active locale.
822
+ * @return {string}
823
+ */
824
+ static lookupTranslation(id, locale) {
825
+ const locale0 = locale === null || locale === void 0 ? void 0 : locale.toLowerCase();
826
+ const loc = typeof locale == 'string' && locales.get(locale0) || activeLocale;
827
+ const array = loc[id];
828
+ if (array && array.length && array[0].length) {
829
+ return array[0];
830
+ }
831
+ return undefined;
832
+ }
833
+
834
+ /**
835
+ * By default, if no translation was found, returns `id`.
836
+ * @param {string} id Message ID to translate
837
+ * @param {string} [locale] Optional locale name (i.e: `'he'`, `'fr'`). Defaults to active locale.
838
+ * @return {string}
839
+ */
840
+ static gettext(id, locale) {
841
+ const text = this.lookupTranslation(id, locale);
842
+ if (typeof text == 'undefined') {
843
+ return id;
946
844
  }
947
- if (hDeath.mm == CHESHVAN && hDeath.dd == 30 && !longCheshvan(hDeath.yy + 1)) {
948
- // If it's Heshvan 30 it depends on the first anniversary;
949
- // if that was not Heshvan 30, use the day before Kislev 1.
950
- hDeath = abs2hebrew(hebrew2abs(hyear, KISLEV$1, 1) - 1);
845
+ return text;
846
+ }
847
+
848
+ /**
849
+ * Register locale translations.
850
+ * @param {string} locale Locale name (i.e.: `'he'`, `'fr'`)
851
+ * @param {LocaleData} data parsed data from a `.po` file.
852
+ */
853
+ static addLocale(locale, data) {
854
+ if (typeof locale !== 'string') {
855
+ throw new TypeError(`Invalid locale name: ${locale}`);
951
856
  }
952
- else if (hDeath.mm == KISLEV$1 && hDeath.dd == 30 && shortKislev(hDeath.yy + 1)) {
953
- // If it's Kislev 30 it depends on the first anniversary;
954
- // if that was not Kislev 30, use the day before Teveth 1.
955
- hDeath = abs2hebrew(hebrew2abs(hyear, TEVET$1, 1) - 1);
857
+ if (typeof data.contexts !== 'object' || typeof data.contexts[''] !== 'object') {
858
+ throw new TypeError(`Locale '${locale}' invalid compact format`);
956
859
  }
957
- else if (hDeath.mm == ADAR_II) {
958
- // If it's Adar II, use the same day in last month of year (Adar or Adar II).
959
- hDeath.mm = monthsInYear(hyear);
860
+ locales.set(locale.toLowerCase(), data.contexts['']);
861
+ }
862
+
863
+ /**
864
+ * Adds a translation to `locale`, replacing any previous translation.
865
+ * @param {string} locale Locale name (i.e: `'he'`, `'fr'`).
866
+ * @param {string} id Message ID to translate
867
+ * @param {string} translation Translation text
868
+ */
869
+ static addTranslation(locale, id, translation) {
870
+ if (typeof locale !== 'string') {
871
+ throw new TypeError(`Invalid locale name: ${locale}`);
960
872
  }
961
- else if (hDeath.mm == ADAR_I$1 && hDeath.dd == 30 && !isLeapYear(hyear)) {
962
- // If it's the 30th in Adar I and year is not a leap year
963
- // (so Adar has only 29 days), use the last day in Shevat.
964
- hDeath.dd = 30;
965
- hDeath.mm = SHVAT;
873
+ const locale0 = locale.toLowerCase();
874
+ const loc = locales.get(locale0);
875
+ if (!loc) {
876
+ throw new TypeError(`Unknown locale: ${locale}`);
966
877
  }
967
- // In all other cases, use the normal anniversary of the date of death.
968
- // advance day to rosh chodesh if needed
969
- if (hDeath.mm == CHESHVAN && hDeath.dd == 30 && !longCheshvan(hyear)) {
970
- hDeath.mm = KISLEV$1;
971
- hDeath.dd = 1;
878
+ if (typeof id !== 'string' || id.length === 0) {
879
+ throw new TypeError(`Invalid id: ${id}`);
972
880
  }
973
- else if (hDeath.mm == KISLEV$1 && hDeath.dd == 30 && shortKislev(hyear)) {
974
- hDeath.mm = TEVET$1;
975
- hDeath.dd = 1;
881
+ const isArray = Array.isArray(translation);
882
+ if (isArray) {
883
+ const t0 = translation[0];
884
+ if (typeof t0 !== 'string' || t0.length === 0) {
885
+ throw new TypeError(`Invalid translation array: ${translation}`);
886
+ }
887
+ } else if (typeof translation !== 'string') {
888
+ throw new TypeError(`Invalid translation: ${translation}`);
976
889
  }
977
- hDeath.yy = hyear;
978
- return hDeath;
979
- }
980
- function getBirthdayHD(hyear, date) {
981
- const orig = toSimpleHebrewDate(date);
982
- const origYear = orig.yy;
983
- if (hyear === origYear) {
984
- return orig;
890
+ loc[id] = isArray ? translation : [translation];
891
+ }
892
+ /**
893
+ * Adds multiple translations to `locale`, replacing any previous translations.
894
+ * @param {string} locale Locale name (i.e: `'he'`, `'fr'`).
895
+ * @param {LocaleData} data parsed data from a `.po` file.
896
+ */
897
+ static addTranslations(locale, data) {
898
+ if (typeof locale !== 'string') {
899
+ throw new TypeError(`Invalid locale name: ${locale}`);
985
900
  }
986
- else if (hyear < origYear) {
987
- // Hebrew year ${hyear} occurs on or before original date in ${origYear}
988
- return undefined;
901
+ const locale0 = locale.toLowerCase();
902
+ const loc = locales.get(locale0);
903
+ if (!loc) {
904
+ throw new TypeError(`Unknown locale: ${locale}`);
989
905
  }
990
- const isOrigLeap = isLeapYear(origYear);
991
- let month = orig.mm;
992
- let day = orig.dd;
993
- if ((month == ADAR_I$1 && !isOrigLeap) || (month == ADAR_II && isOrigLeap)) {
994
- month = monthsInYear(hyear);
906
+ if (typeof data.contexts !== 'object' || typeof data.contexts[''] !== 'object') {
907
+ throw new TypeError(`Locale '${locale}' invalid compact format`);
995
908
  }
996
- else if (month == CHESHVAN && day == 30 && !longCheshvan(hyear)) {
997
- month = KISLEV$1;
998
- day = 1;
909
+ const ctx = data.contexts[''];
910
+ Object.assign(loc, ctx);
911
+ }
912
+ /**
913
+ * Activates a locale. Throws an error if the locale has not been previously added.
914
+ * After setting the locale to be used, all strings marked for translations
915
+ * will be represented by the corresponding translation in the specified locale.
916
+ * @param {string} locale Locale name (i.e: `'he'`, `'fr'`)
917
+ * @return {LocaleData}
918
+ */
919
+ static useLocale(locale) {
920
+ const locale0 = locale.toLowerCase();
921
+ const obj = locales.get(locale0);
922
+ if (!obj) {
923
+ throw new RangeError(`Locale '${locale}' not found`);
999
924
  }
1000
- else if (month == KISLEV$1 && day == 30 && shortKislev(hyear)) {
1001
- month = TEVET$1;
1002
- day = 1;
925
+ activeName = alias[locale0] || locale0;
926
+ activeLocale = obj;
927
+ return activeLocale;
928
+ }
929
+
930
+ /**
931
+ * Returns the name of the active locale (i.e. 'he', 'ashkenazi', 'fr')
932
+ * @return {string}
933
+ */
934
+ static getLocaleName() {
935
+ return activeName;
936
+ }
937
+
938
+ /**
939
+ * Returns the names of registered locales
940
+ * @return {string[]}
941
+ */
942
+ static getLocaleNames() {
943
+ const keys = Array.from(locales.keys());
944
+ return keys.sort((a, b) => a.localeCompare(b));
945
+ }
946
+
947
+ /**
948
+ * @param {number} n
949
+ * @param {string} [locale] Optional locale name (i.e: `'he'`, `'fr'`). Defaults to active locale.
950
+ * @return {string}
951
+ */
952
+ static ordinal(n, locale) {
953
+ const locale1 = locale === null || locale === void 0 ? void 0 : locale.toLowerCase();
954
+ const locale0 = locale1 || activeName;
955
+ if (!locale0) {
956
+ return this.getEnOrdinal(n);
1003
957
  }
1004
- else if (month == ADAR_I$1 && day == 30 && isOrigLeap && !isLeapYear(hyear)) {
1005
- month = NISAN$3;
1006
- day = 1;
958
+ switch (locale0) {
959
+ case 'en':
960
+ case 's':
961
+ case 'a':
962
+ case 'ashkenazi':
963
+ case 'ashkenazi_litvish':
964
+ case 'ashkenazi_poylish':
965
+ case 'ashkenazi_standard':
966
+ return this.getEnOrdinal(n);
967
+ case 'es':
968
+ return n + 'º';
969
+ case 'h':
970
+ case 'he':
971
+ case 'he-x-nonikud':
972
+ return String(n);
973
+ default:
974
+ return n + '.';
1007
975
  }
1008
- return { yy: hyear, mm: month, dd: day };
976
+ }
977
+
978
+ /**
979
+ * @private
980
+ * @param {number} n
981
+ * @return {string}
982
+ */
983
+ static getEnOrdinal(n) {
984
+ const s = ['th', 'st', 'nd', 'rd'];
985
+ const v = n % 100;
986
+ return n + (s[(v - 20) % 10] || s[v] || s[0]);
987
+ }
988
+
989
+ /**
990
+ * Removes nekudot from Hebrew string
991
+ * @param {string} str
992
+ * @return {string}
993
+ */
994
+ static hebrewStripNikkud(str) {
995
+ return str.replace(/[\u0590-\u05bd]/g, '').replace(/[\u05bf-\u05c7]/g, '');
996
+ }
1009
997
  }
998
+ Locale.addLocale('en', noopLocale);
999
+ Locale.addLocale('s', noopLocale);
1000
+ Locale.addLocale('', noopLocale);
1001
+ Locale.useLocale('en');
1010
1002
 
1011
1003
  /**
1012
1004
  * @private
@@ -1835,7 +1827,7 @@ class Event {
1835
1827
  this.desc = desc;
1836
1828
  this.mask = +mask;
1837
1829
  if (typeof attrs === 'object' && attrs !== null) {
1838
- Object.keys(attrs).forEach(k => this[k] = attrs[k]);
1830
+ Object.assign(this, attrs);
1839
1831
  }
1840
1832
  }
1841
1833
  /**
@@ -3428,10 +3420,10 @@ class Location extends GeoLocation {
3428
3420
  return true;
3429
3421
  }
3430
3422
  }
3431
- classicCities0.forEach(city => {
3423
+ for (const city of classicCities0) {
3432
3424
  const location = new Location(city[2], city[3], city[1] == 'IL', city[4], city[0], city[1], undefined, city[5]);
3433
3425
  Location.addLocation(location.getName(), location);
3434
- });
3426
+ }
3435
3427
 
3436
3428
  const _formatters = new Map();
3437
3429
 
@@ -5610,40 +5602,7 @@ const MINOR_HOLIDAY$1 = flags.MINOR_HOLIDAY;
5610
5602
  const EREV$1 = flags.EREV;
5611
5603
  // const CHOL_HAMOED = flags.CHOL_HAMOED;
5612
5604
 
5613
- /**
5614
- * Avoid dependency on ES6 Map object
5615
- * @private
5616
- */
5617
- class SimpleMap {
5618
- /**
5619
- * @param {string} key
5620
- * @return {boolean}
5621
- */
5622
- has(key) {
5623
- return typeof this[key] !== 'undefined';
5624
- }
5625
- /**
5626
- * @param {string} key
5627
- * @return {any}
5628
- */
5629
- get(key) {
5630
- return this[key];
5631
- }
5632
- /**
5633
- * @param {string} key
5634
- * @param {any} val
5635
- */
5636
- set(key, val) {
5637
- this[key] = val;
5638
- }
5639
- /**
5640
- * @return {string[]}
5641
- */
5642
- keys() {
5643
- return Object.keys(this);
5644
- }
5645
- }
5646
- const sedraCache = new SimpleMap();
5605
+ const sedraCache = new Map();
5647
5606
 
5648
5607
  /**
5649
5608
  * @private
@@ -5687,13 +5646,12 @@ function getHolidaysForYear_(year) {
5687
5646
  }
5688
5647
  const RH = new HDate(1, TISHREI$1, year);
5689
5648
  const pesach = new HDate(15, NISAN$1, year);
5690
- const h = new SimpleMap();
5649
+ const map = new Map();
5691
5650
  // eslint-disable-next-line require-jsdoc
5692
5651
  function add(...events) {
5693
- // for (const ev of events) {
5694
- events.forEach(ev => {
5652
+ for (const ev of events) {
5695
5653
  const key = ev.date.toString();
5696
- const arr = h.get(key);
5654
+ const arr = map.get(key);
5697
5655
  if (typeof arr === 'object') {
5698
5656
  if (arr[0].getFlags() & flags.EREV) {
5699
5657
  arr.unshift(ev);
@@ -5701,17 +5659,17 @@ function getHolidaysForYear_(year) {
5701
5659
  arr.push(ev);
5702
5660
  }
5703
5661
  } else {
5704
- h.set(key, [ev]);
5662
+ map.set(key, [ev]);
5705
5663
  }
5706
- });
5664
+ }
5707
5665
  }
5708
- staticHolidays.forEach(h => {
5666
+ for (const h of staticHolidays) {
5709
5667
  const hd = new HDate(h.dd, h.mm, year);
5710
5668
  const ev = new HolidayEvent(hd, h.desc, h.flags);
5711
5669
  if (h.emoji) ev.emoji = h.emoji;
5712
5670
  if (h.chmDay) ev.cholHaMoedDay = h.chmDay;
5713
5671
  add(ev);
5714
- });
5672
+ }
5715
5673
 
5716
5674
  // standard holidays that don't shift based on year
5717
5675
  add(new RoshHashanaEvent(RH, year, CHAG | LIGHT_CANDLES_TZEIS$1));
@@ -5763,7 +5721,7 @@ function getHolidaysForYear_(year) {
5763
5721
  if (yomHaZikaronDt) {
5764
5722
  add(new HolidayEvent(yomHaZikaronDt, 'Yom HaZikaron', MODERN_HOLIDAY$1, emojiIsraelFlag), new HolidayEvent(yomHaZikaronDt.next(), 'Yom HaAtzma\'ut', MODERN_HOLIDAY$1, emojiIsraelFlag));
5765
5723
  }
5766
- staticModernHolidays.forEach(h => {
5724
+ for (const h of staticModernHolidays) {
5767
5725
  if (year >= h.firstYear) {
5768
5726
  let hd = new HDate(h.dd, h.mm, year);
5769
5727
  const dow = hd.getDay();
@@ -5781,7 +5739,7 @@ function getHolidaysForYear_(year) {
5781
5739
  }
5782
5740
  add(ev);
5783
5741
  }
5784
- });
5742
+ }
5785
5743
  let tamuz17 = new HDate(17, TAMUZ, year);
5786
5744
  let tamuz17attrs;
5787
5745
  if (tamuz17.getDay() == SAT$1) {
@@ -5852,8 +5810,8 @@ function getHolidaysForYear_(year) {
5852
5810
  emoji: '☀️'
5853
5811
  }));
5854
5812
  }
5855
- yearCache.set(year, h);
5856
- return h;
5813
+ yearCache.set(year, map);
5814
+ return map;
5857
5815
  }
5858
5816
 
5859
5817
  /**
@@ -5920,7 +5878,7 @@ class DailyLearning {
5920
5878
  }
5921
5879
  }
5922
5880
 
5923
- const version="5.0.0";
5881
+ const version="5.0.2";
5924
5882
 
5925
5883
  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};
5926
5884
 
@@ -5933,9 +5891,9 @@ Locale.addLocale('he', poHe);
5933
5891
  Locale.addLocale('h', poHe);
5934
5892
  const heStrs = poHe.contexts[''];
5935
5893
  const heNoNikud = {};
5936
- Object.keys(heStrs).forEach(key => {
5937
- heNoNikud[key] = [Locale.hebrewStripNikkud(heStrs[key][0])];
5938
- });
5894
+ for (const [key, val] of Object.entries(heStrs)) {
5895
+ heNoNikud[key] = [Locale.hebrewStripNikkud(val[0])];
5896
+ }
5939
5897
  const poHeNoNikud = {
5940
5898
  headers: poHe.headers,
5941
5899
  contexts: {
@@ -6216,24 +6174,12 @@ const RECOGNIZED_OPTIONS = {
6216
6174
  * @param {CalOptions} options
6217
6175
  */
6218
6176
  function warnUnrecognizedOptions(options) {
6219
- Object.keys(options).forEach(k => {
6177
+ for (const k of Object.keys(options)) {
6220
6178
  if (typeof RECOGNIZED_OPTIONS[k] === 'undefined' && !unrecognizedAlreadyWarned.has(k)) {
6221
6179
  console.warn(`Ignoring unrecognized HebrewCalendar option: ${k}`);
6222
6180
  unrecognizedAlreadyWarned.add(k);
6223
6181
  }
6224
- });
6225
- }
6226
-
6227
- /**
6228
- * A bit like Object.assign(), but just a shallow copy
6229
- * @private
6230
- * @param {any} target
6231
- * @param {any} source
6232
- * @return {any}
6233
- */
6234
- function shallowCopy(target, source) {
6235
- Object.keys(source).forEach(k => target[k] = source[k]);
6236
- return target;
6182
+ }
6237
6183
  }
6238
6184
  const israelCityOffset = {
6239
6185
  'Jerusalem': 40,
@@ -6594,6 +6540,7 @@ function observedInDiaspora(ev) {
6594
6540
  return ev.observedInDiaspora();
6595
6541
  }
6596
6542
  const yearArrayCache = new Map();
6543
+ const holidaysOnDate = new Map();
6597
6544
 
6598
6545
  /**
6599
6546
  * HebrewCalendar is the main interface to the `@hebcal/core` library.
@@ -6711,7 +6658,7 @@ class HebrewCalendar {
6711
6658
  * @return {Event[]}
6712
6659
  */
6713
6660
  static calendar(options = {}) {
6714
- options = shallowCopy({}, options); // so we can modify freely
6661
+ options = Object.assign({}, options); // so we can modify freely
6715
6662
  checkCandleOptions(options);
6716
6663
  const location = options.location = options.location || defaultLocation;
6717
6664
  const il = options.il = options.il || location.il || false;
@@ -6760,9 +6707,9 @@ class HebrewCalendar {
6760
6707
  const dow = hd.getDay();
6761
6708
  let candlesEv;
6762
6709
  const ev = holidaysYear.get(hd.toString()) || [];
6763
- ev.forEach(e => {
6710
+ for (const e of ev) {
6764
6711
  candlesEv = appendHolidayAndRelated(evts, e, options, candlesEv, dow);
6765
- });
6712
+ }
6766
6713
  if (options.sedrot && dow === SAT) {
6767
6714
  const parsha0 = sedra.lookup(abs);
6768
6715
  if (!parsha0.chag) {
@@ -6771,8 +6718,7 @@ class HebrewCalendar {
6771
6718
  }
6772
6719
  const dailyLearning = options.dailyLearning;
6773
6720
  if (typeof dailyLearning === 'object') {
6774
- Object.keys(dailyLearning).forEach(key => {
6775
- const val = dailyLearning[key];
6721
+ for (const [key, val] of Object.entries(dailyLearning)) {
6776
6722
  if (val) {
6777
6723
  const name = key === 'yerushalmi' ? val === 2 ? 'yerushalmi-schottenstein' : 'yerushalmi-vilna' : key;
6778
6724
  const learningEv = DailyLearning.lookup(name, hd);
@@ -6780,7 +6726,7 @@ class HebrewCalendar {
6780
6726
  evts.push(learningEv);
6781
6727
  }
6782
6728
  }
6783
- });
6729
+ }
6784
6730
  }
6785
6731
  if (options.omer && abs >= beginOmer && abs <= endOmer) {
6786
6732
  const omer = abs - beginOmer + 1;
@@ -6942,20 +6888,29 @@ class HebrewCalendar {
6942
6888
  }
6943
6889
 
6944
6890
  /**
6945
- * Returns an array of Events on this date (or undefined if no events)
6891
+ * Returns an array of Events on this date (or `undefined` if no events)
6946
6892
  * @param {HDate|Date|number} date Hebrew Date, Gregorian date, or absolute R.D. day number
6947
6893
  * @param {boolean} [il] use the Israeli schedule for holidays
6948
6894
  * @return {Event[]}
6949
6895
  */
6950
6896
  static getHolidaysOnDate(date, il) {
6951
6897
  const hd = HDate.isHDate(date) ? date : new HDate(date);
6898
+ const hdStr = hd.toString();
6899
+ const cacheKey = hdStr + '/' + (typeof il === 'undefined' ? 2 : il ? 1 : 0);
6900
+ if (holidaysOnDate.has(cacheKey)) {
6901
+ return holidaysOnDate.get(cacheKey);
6902
+ }
6952
6903
  const yearMap = getHolidaysForYear_(hd.getFullYear());
6953
- const events = yearMap.get(hd.toString());
6904
+ const events = yearMap.get(hdStr);
6905
+ // if il isn't a boolean return both diaspora + IL for day
6954
6906
  if (typeof il === 'undefined' || typeof events === 'undefined') {
6907
+ holidaysOnDate.set(cacheKey, events);
6955
6908
  return events;
6956
6909
  }
6957
6910
  const myFilter = il ? observedInIsrael : observedInDiaspora;
6958
- return events.filter(myFilter);
6911
+ const filtered = events.filter(myFilter);
6912
+ holidaysOnDate.set(cacheKey, filtered);
6913
+ return filtered;
6959
6914
  }
6960
6915
 
6961
6916
  /**