@hebcal/leyning 6.2.2 → 6.2.4

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,4 +1,4 @@
1
- /*! @hebcal/leyning v6.2.2 */
1
+ /*! @hebcal/leyning v6.2.4 */
2
2
  import { flags, months, HDate, HebrewCalendar, Event, Locale, ParshaEvent } from '@hebcal/core';
3
3
 
4
4
  const Genesis=[0,31,25,24,26,32,22,24,22,29,32,32,20,18,24,21,16,27,33,38,18,34,24,20,67,34,35,46,22,35,43,54,33,20,31,29,43,36,30,23,23,57,38,34,34,28,34,31,22,33,26];const Exodus=[0,22,25,22,31,23,30,29,28,35,29,10,51,22,31,27,36,16,27,25,26,37,30,33,18,40,37,21,43,46,38,18,35,23,35,35,38,29,31,43,38];const Leviticus=[0,17,16,17,35,26,23,38,36,24,20,47,8,59,57,33,34,16,30,37,27,24,33,44,23,55,46,34];const Numbers=[0,54,34,51,49,31,27,89,26,23,36,35,16,33,45,41,35,28,32,22,29,35,41,30,25,19,65,23,31,39,17,54,42,56,29,34,13];const Deuteronomy=[0,46,37,29,49,30,25,26,20,29,22,32,31,19,29,23,22,20,22,21,20,23,29,26,22,19,19,26,69,28,20,30,52,29,12];const Joshua=[0,18,24,17,24,15,27,26,35,27,43,23,24,33,15,63,10,18,28,51,9,45,34,16,33];const Judges=[0,36,23,31,24,31,40,25,35,57,18,40,15,25,20,20,31,13,31,30,48,25];const Ruth=[0,22,23,18,22];const Isaiah=[0,31,22,26,6,30,13,25,23,20,34,16,6,22,32,9,14,14,7,25,6,17,25,18,23,12,21,13,29,24,33,9,20,24,17,10,22,38,22,8,31,29,25,28,28,25,13,15,22,26,11,23,15,12,17,13,12,21,14,21,22,11,12,19,11,25,24];const Jeremiah=[0,19,37,25,31,31,30,34,23,25,25,23,17,27,22,21,21,27,23,15,18,14,30,40,10,38,24,22,17,32,24,40,44,26,22,19,32,21,28,18,16,18,22,13,30,5,28,7,47,39,46,64,34];const Lamentations=[0,22,22,66,22,22];const Baruch=[0,22,35,38,37,9,72];const Ezekiel=[0,28,10,27,17,17,14,27,18,11,22,25,28,23,23,8,63,24,32,14,44,37,31,49,27,17,21,36,26,21,26,18,32,33,31,15,38,28,23,29,49,26,20,27,31,25,24,23,35];const Daniel=[0,21,49,100,34,30,29,28,27,27,21,45,13,64,42];const Hosea=[0,9,25,5,19,15,11,16,14,17,15,11,15,15,10];const Joel=[0,20,27,5,21];const Amos=[0,15,16,15,13,27,14,17,14,15];const Obadiah=[0,21];const Jonah=[0,16,11,10,11];const Micah=[0,16,13,12,14,14,16,20];const Nachum=[0,14,14,19];const Habakkuk=[0,17,20,19];const Zephaniah=[0,18,15,20];const Haggai=[0,15,23];const Zechariah=[0,17,17,10,14,11,15,14,23,17,12,17,14,9,21];const Malachi=[0,14,17,24];var numverses = {Genesis:Genesis,Exodus:Exodus,Leviticus:Leviticus,Numbers:Numbers,Deuteronomy:Deuteronomy,Joshua:Joshua,Judges:Judges,Ruth:Ruth,"I Samuel":[0,28,36,21,22,12,21,17,22,27,27,15,25,23,52,35,23,58,30,24,42,16,23,28,23,43,25,12,25,11,31,13],"II Samuel":[0,27,32,39,12,25,23,29,18,13,19,27,31,39,33,37,23,29,32,44,26,22,51,39,25],"I Kings":[0,53,46,28,20,32,38,51,66,28,29,43,33,34,31,34,34,24,46,21,43,29,54],"II Kings":[0,18,25,27,44,27,33,20,29,37,36,20,22,25,29,38,20,41,37,37,21,26,20,37,20,30],Isaiah:Isaiah,Jeremiah:Jeremiah,Lamentations:Lamentations,Baruch:Baruch,Ezekiel:Ezekiel,Daniel:Daniel,Hosea:Hosea,Joel:Joel,Amos:Amos,Obadiah:Obadiah,Jonah:Jonah,Micah:Micah,Nachum:Nachum,Habakkuk:Habakkuk,Zephaniah:Zephaniah,Haggai:Haggai,Zechariah:Zechariah,Malachi:Malachi};
@@ -8,23 +8,21 @@ const Genesis=[0,31,25,24,26,32,22,24,22,29,32,32,20,18,24,21,16,27,33,38,18,34,
8
8
  * @readonly
9
9
  * @const {string[]}
10
10
  */
11
-
12
11
  const BOOK = ['', 'Genesis', 'Exodus', 'Leviticus', 'Numbers', 'Deuteronomy'];
12
+
13
13
  /**
14
14
  * Formats parsha as a string
15
15
  * @param {string[]} parsha
16
16
  * @return {string}
17
17
  */
18
-
19
18
  function parshaToString(parsha) {
20
19
  let s = parsha[0];
21
-
22
20
  if (parsha.length == 2) {
23
21
  s += '-' + parsha[1];
24
22
  }
25
-
26
23
  return s;
27
24
  }
25
+
28
26
  /**
29
27
  * Represents an aliyah
30
28
  * @typedef {Object} Aliyah
@@ -40,10 +38,10 @@ function parshaToString(parsha) {
40
38
  * @param {any} src
41
39
  * @return {any}
42
40
  */
43
-
44
41
  function clone(src) {
45
42
  return JSON.parse(JSON.stringify(src));
46
43
  }
44
+
47
45
  /**
48
46
  * Calculates the number of verses in an aliyah or haftara based on
49
47
  * the `b` (begin verse), `e` (end verse) and `k` (book).
@@ -51,98 +49,85 @@ function clone(src) {
51
49
  * @param {Aliyah} aliyah
52
50
  * @return {number}
53
51
  */
54
-
55
52
  function calculateNumVerses(aliyah) {
56
53
  if (aliyah.v) {
57
54
  return aliyah.v;
58
55
  }
59
-
60
56
  const chapVerseBegin = aliyah.b.split(':');
61
57
  const chapVerseEnd = aliyah.e.split(':');
62
58
  const c1 = parseInt(chapVerseBegin[0], 10);
63
59
  const c2 = parseInt(chapVerseEnd[0], 10);
64
60
  const v1 = parseInt(chapVerseBegin[1], 10);
65
61
  const v2 = parseInt(chapVerseEnd[1], 10);
66
-
67
62
  if (c1 === c2) {
68
63
  aliyah.v = v2 - v1 + 1;
69
64
  } else if (typeof aliyah.k === 'string') {
70
65
  const numv = numverses[aliyah.k];
71
-
72
66
  if (typeof numv !== 'object' || !numv.length) {
73
67
  throw new ReferenceError(`Can't find numverses for ${aliyah.k}`);
74
68
  }
75
-
76
69
  let total = numv[c1] - v1 + 1;
77
-
78
70
  for (let chap = c1 + 1; chap < c2; chap++) {
79
71
  total += numv[chap];
80
72
  }
81
-
82
73
  total += v2;
83
74
  aliyah.v = total;
84
75
  }
85
-
86
76
  return aliyah.v;
87
77
  }
78
+
88
79
  /**
89
80
  * Formats an aliyah object like "Numbers 28:9-28:15"
90
81
  * @param {Aliyah} a aliyah
91
82
  * @return {string}
92
83
  */
93
-
94
84
  function formatAliyahWithBook(a) {
95
85
  return `${a.k} ${a.b}-${a.e}`;
96
86
  }
87
+
97
88
  /**
98
89
  * Formats an aliyah object like "Numbers 28:9-15"
99
90
  * @param {Aliyah} aliyah
100
91
  * @param {boolean} showBook
101
92
  * @return {string}
102
93
  */
103
-
104
94
  function formatAliyahShort(aliyah, showBook) {
105
95
  const begin = aliyah.b;
106
96
  const end0 = aliyah.e;
107
97
  const prefix = showBook ? aliyah.k + ' ' : '';
108
-
109
98
  if (begin === end0) {
110
99
  return `${prefix}${begin}`;
111
100
  }
112
-
113
101
  const cv1 = begin.split(':');
114
102
  const cv2 = end0.split(':');
115
103
  const end = cv1[0] === cv2[0] ? cv2[1] : end0;
116
104
  return `${prefix}${begin}-${end}`;
117
105
  }
106
+
118
107
  /**
119
108
  * Returns the total number of verses in an array of Aliyah (or haftarah) objects
120
109
  * @param {Aliyah|Aliyah[]} aliyot
121
110
  * @return {number}
122
111
  */
123
-
124
112
  function sumVerses(aliyot) {
125
113
  return Array.isArray(aliyot) ? aliyot.reduce((prev, cur) => prev + cur.v, 0) : aliyot.v;
126
114
  }
115
+
127
116
  /**
128
117
  * @private
129
118
  * @param {Aliyah|Aliyah[]} haft
130
119
  * @return {Aliyah|Aliyah[]}
131
120
  */
132
-
133
121
  function cloneHaftara(haft) {
134
122
  if (!haft) {
135
123
  return haft;
136
124
  }
137
-
138
125
  const dest = clone(haft);
139
-
140
126
  if (Array.isArray(dest)) {
141
127
  dest.map(calculateNumVerses);
142
128
  } else {
143
129
  calculateNumVerses(dest);
144
130
  }
145
-
146
131
  return dest;
147
132
  }
148
133
 
@@ -152,39 +137,33 @@ function cloneHaftara(haft) {
152
137
  * @param {string} b
153
138
  * @return {boolean}
154
139
  */
155
-
156
140
  function isChapVerseBefore(a, b) {
157
141
  const cv1 = a.split(':').map(x => +x);
158
142
  const cv2 = b.split(':').map(x => +x);
159
143
  return cv1[0] * 100 + cv1[1] < cv2[0] * 100 + cv2[1];
160
144
  }
145
+
161
146
  /**
162
147
  * Summarizes an `AliyotMap` by collapsing all adjacent aliyot.
163
148
  * Finds any non-overlapping parts (e.g. special 7th aliyah or maftir)
164
149
  * @param {Object<string,Aliyah>} aliyot
165
150
  * @return {Aliyah[]}
166
151
  */
167
-
168
-
169
152
  function makeLeyningParts(aliyot) {
170
153
  const nums = Object.keys(aliyot).filter(x => x.length === 1);
171
154
  let start = aliyot[nums[0]];
172
155
  let end = start;
173
156
  const parts = [];
174
-
175
157
  for (let i = 0; i < nums.length; i++) {
176
158
  const num = nums[i];
177
159
  const aliyah = aliyot[num];
178
-
179
160
  if (i === nums.length - 1 && aliyah.k === end.k && aliyah.e === end.e) {
180
161
  // short-circuit when final aliyah is within the previous (e.g. M inside of 7)
181
162
  continue;
182
163
  }
183
-
184
164
  const prevEndChap = +end.e.split(':')[0];
185
165
  const curStartChap = +aliyah.b.split(':')[0];
186
166
  const sameOrNextChap = curStartChap === prevEndChap || curStartChap === prevEndChap + 1;
187
-
188
167
  if (i !== 0 && (aliyah.k !== start.k || isChapVerseBefore(aliyah.b, start.e) || !sameOrNextChap)) {
189
168
  parts.push({
190
169
  k: start.k,
@@ -193,10 +172,8 @@ function makeLeyningParts(aliyot) {
193
172
  });
194
173
  start = aliyah;
195
174
  }
196
-
197
175
  end = aliyah;
198
176
  }
199
-
200
177
  parts.push({
201
178
  k: start.k,
202
179
  b: start.b,
@@ -204,6 +181,7 @@ function makeLeyningParts(aliyot) {
204
181
  });
205
182
  return parts;
206
183
  }
184
+
207
185
  /**
208
186
  * Returns a string representation of the leyning parts.
209
187
  * Separate verse ranges read from the same book are separated
@@ -213,36 +191,30 @@ function makeLeyningParts(aliyot) {
213
191
  * @param {Aliyah|Aliyah[]} parts
214
192
  * @return {string}
215
193
  */
216
-
217
194
  function makeSummaryFromParts(parts) {
218
195
  if (!Array.isArray(parts)) {
219
196
  parts = [parts];
220
197
  }
221
-
222
198
  let prev = parts[0];
223
199
  let summary = formatAliyahShort(prev, true);
224
-
225
200
  for (let i = 1; i < parts.length; i++) {
226
201
  const part = parts[i];
227
-
228
202
  if (part.k === prev.k) {
229
203
  summary += ', ';
230
204
  } else {
231
205
  summary += `; ${part.k} `;
232
206
  }
233
-
234
207
  summary += formatAliyahShort(part, false);
235
208
  prev = part;
236
209
  }
237
-
238
210
  return summary;
239
211
  }
212
+
240
213
  /**
241
214
  * Makes a summary of the leyning, like "Genesis 6:9-11:32"
242
215
  * @param {Object<string,Aliyah>} aliyot
243
216
  * @return {string}
244
217
  */
245
-
246
218
  function makeLeyningSummary(aliyot) {
247
219
  const parts = makeLeyningParts(aliyot);
248
220
  return makeSummaryFromParts(parts);
@@ -255,35 +227,28 @@ const Purim={fullkriyah:{"1":{p:16,k:2,b:"17:8",e:"17:10"},"2":{p:16,k:2,b:"17:1
255
227
  * @param {string} holiday
256
228
  * @return {boolean}
257
229
  */
258
-
259
230
  function hasFestival(holiday) {
260
231
  return typeof festivals[holiday] === 'object';
261
232
  }
233
+
262
234
  /**
263
235
  * Returns the raw metadata for festival reading for `holiday`
264
236
  * @param {string} holiday
265
237
  * @return {any}
266
238
  */
267
-
268
239
  function lookupFestival(holiday) {
269
240
  let src = festivals[holiday];
270
-
271
241
  if (typeof src === 'undefined') {
272
242
  return undefined;
273
243
  }
274
-
275
244
  if (src.alias) {
276
245
  const tmp = festivals[src.key];
277
-
278
246
  if (typeof tmp === 'undefined') {
279
247
  throw new Error(`Leyning alias ${holiday} => ${src.key} not found`);
280
248
  }
281
-
282
249
  src = tmp;
283
250
  }
284
-
285
251
  const result = src.fullkriyah ? clone(src) : src;
286
-
287
252
  if (result.fullkriyah) {
288
253
  Object.values(result.fullkriyah).forEach(aliyah => {
289
254
  if (typeof aliyah.k === 'number') {
@@ -291,32 +256,27 @@ function lookupFestival(holiday) {
291
256
  }
292
257
  });
293
258
  }
294
-
295
259
  return result;
296
260
  }
297
261
 
298
- const HOLIDAY_IGNORE_MASK = flags.DAF_YOMI | flags.OMER_COUNT | flags.SHABBAT_MEVARCHIM | flags.MOLAD | flags.USER_EVENT | flags.HEBREW_DATE | flags.MISHNA_YOMI | flags.MODERN_HOLIDAY;
262
+ const HOLIDAY_IGNORE_MASK = flags.DAF_YOMI | flags.OMER_COUNT | flags.SHABBAT_MEVARCHIM | flags.MOLAD | flags.USER_EVENT | flags.HEBREW_DATE | flags.MISHNA_YOMI | flags.MODERN_HOLIDAY | flags.YERUSHALMI_YOMI;
263
+
299
264
  /**
300
265
  * Based on the event date, type and title, finds the relevant leyning key
301
266
  * @param {Event} ev event
302
267
  * @param {boolean} [il] true if Israel holiday scheme
303
268
  * @return {string} key to look up in holiday-reading.json
304
269
  */
305
-
306
270
  function getLeyningKeyForEvent(ev, il = false) {
307
271
  const mask = ev.getFlags();
308
-
309
272
  if (mask & HOLIDAY_IGNORE_MASK) {
310
273
  return undefined;
311
- } // Skip all Erevs except for Simchat Torah
312
-
313
-
274
+ }
275
+ // Skip all Erevs except for Simchat Torah
314
276
  const desc = ev.getDesc();
315
-
316
277
  if (mask & flags.EREV && desc !== 'Erev Simchat Torah') {
317
278
  return undefined;
318
279
  }
319
-
320
280
  const hd = ev.getDate();
321
281
  const day = hd.getDate();
322
282
  const dow = hd.abs() % 7;
@@ -325,15 +285,12 @@ function getLeyningKeyForEvent(ev, il = false) {
325
285
  const isRoshChodesh = day == 1 || day == 30;
326
286
  const holiday = ev.basename();
327
287
  const isPesach = holiday === 'Pesach';
328
-
329
288
  if (il && isPesach) {
330
289
  if (isShabbat) {
331
290
  return day === 15 || day === 21 ? desc + ' (on Shabbat)' : 'Pesach Shabbat Chol ha-Moed';
332
291
  }
333
-
334
292
  return desc;
335
293
  }
336
-
337
294
  if (day == 1 && month === months.TISHREI) {
338
295
  return isShabbat ? 'Rosh Hashana I (on Shabbat)' : 'Rosh Hashana I';
339
296
  } else if (ev.cholHaMoedDay) {
@@ -342,12 +299,10 @@ function getLeyningKeyForEvent(ev, il = false) {
342
299
  return holiday + ' Shabbat Chol ha-Moed';
343
300
  } else if (desc == 'Sukkot VII (Hoshana Raba)') {
344
301
  return 'Sukkot Final Day (Hoshana Raba)';
345
- } // If Shabbat falls on the third day of Chol ha-Moed Pesach,
302
+ }
303
+ // If Shabbat falls on the third day of Chol ha-Moed Pesach,
346
304
  // the readings for the third, fourth, and fifth days are moved ahead
347
-
348
-
349
305
  let cholHaMoedDay = ev.cholHaMoedDay;
350
-
351
306
  if (isPesach && cholHaMoedDay >= 3) {
352
307
  if (dow == 0 && cholHaMoedDay == 4) {
353
308
  cholHaMoedDay = 3;
@@ -355,7 +310,6 @@ function getLeyningKeyForEvent(ev, il = false) {
355
310
  cholHaMoedDay = 4;
356
311
  }
357
312
  }
358
-
359
313
  return `${holiday} Chol ha-Moed Day ${cholHaMoedDay}`;
360
314
  } else if (ev.chanukahDay) {
361
315
  if (isShabbat && isRoshChodesh) {
@@ -368,54 +322,45 @@ function getLeyningKeyForEvent(ev, il = false) {
368
322
  return `Chanukah Day ${ev.chanukahDay}`;
369
323
  }
370
324
  }
371
-
372
325
  if (isRoshChodesh && (desc == 'Shabbat HaChodesh' || desc == 'Shabbat Shekalim')) {
373
326
  return desc + ' (on Rosh Chodesh)';
374
327
  }
375
-
376
328
  if (desc == 'Shavuot') {
377
329
  return 'Shavuot I';
378
330
  } else if (il && desc == 'Shmini Atzeret') {
379
331
  return 'Simchat Torah';
380
332
  }
381
-
333
+ if (desc === 'Chag HaBanot') {
334
+ return undefined;
335
+ }
382
336
  if (isShabbat && 'Shabbat' != desc.substring(0, 7)) {
383
337
  if (isRoshChodesh) {
384
338
  if (desc === 'Rosh Chodesh Tevet') {
385
339
  return 'Shabbat Rosh Chodesh Chanukah';
386
340
  }
387
-
388
341
  return 'Shabbat Rosh Chodesh';
389
342
  }
390
-
391
343
  const desc2 = desc + ' (on Shabbat)';
392
-
393
344
  if (hasFestival(desc2)) {
394
345
  return desc2;
395
346
  }
396
347
  }
397
-
398
348
  if (hasFestival(desc)) {
399
349
  return desc;
400
350
  }
401
-
402
351
  if (isShabbat) {
403
352
  const tommorow = hd.next().getDate();
404
-
405
353
  if (tommorow === 30 || tommorow === 1) {
406
354
  return 'Shabbat Machar Chodesh';
407
355
  }
408
356
  }
409
-
410
357
  if (desc === 'Rosh Hashana LaBehemot') {
411
358
  return undefined;
412
359
  }
413
-
414
360
  if (desc === 'Rosh Chodesh Tevet') {
415
361
  if (isShabbat) {
416
362
  return 'Shabbat Rosh Chodesh Chanukah';
417
363
  }
418
-
419
364
  if (day === 30 || HDate.shortKislev(hd.getFullYear())) {
420
365
  return 'Chanukah Day 6'; // 30th of Kislev or 1st of Tevet
421
366
  } else {
@@ -426,11 +371,9 @@ function getLeyningKeyForEvent(ev, il = false) {
426
371
  if (isRoshChodesh) {
427
372
  return desc;
428
373
  }
429
-
430
374
  if (desc === 'Tish\'a B\'Av (observed)') {
431
375
  return 'Tish\'a B\'Av';
432
376
  }
433
-
434
377
  return undefined;
435
378
  }
436
379
 
@@ -438,45 +381,40 @@ function getLeyningKeyForEvent(ev, il = false) {
438
381
  * @private
439
382
  * @param {Object<string,Aliyah>} aliyot
440
383
  */
441
-
442
384
  function aliyotCombine67(aliyot) {
443
385
  const a6 = clone(aliyot['6']);
444
386
  const a7 = aliyot['7'];
445
-
446
387
  if (a6.k !== a7.k) {
447
388
  throw new Error('Impossible to combine aliyot 6 & 7: ' + JSON.stringify(aliyot));
448
389
  }
449
-
450
390
  delete aliyot['7'];
451
391
  aliyot['6'] = {
452
392
  k: a6.k,
453
393
  b: a6.b,
454
394
  e: a7.e
455
395
  };
456
-
457
396
  if (a6.v && a7.v) {
458
397
  aliyot['6'].v = a6.v + a7.v;
459
398
  }
460
399
  }
400
+
461
401
  /**
462
402
  * @private
463
403
  * @param {Object<string,Aliyah>} aliyot
464
404
  * @param {Object<string,Aliyah>} special
465
405
  */
466
-
467
-
468
406
  function mergeAliyotWithSpecial(aliyot, special) {
469
407
  if (special['7']) {
470
408
  aliyotCombine67(aliyot);
471
409
  aliyot['7'] = clone(special['7']);
472
410
  calculateNumVerses(aliyot['7']);
473
411
  }
474
-
475
412
  if (special['M']) {
476
413
  aliyot['M'] = clone(special['M']);
477
414
  calculateNumVerses(aliyot['M']);
478
415
  }
479
416
  }
417
+
480
418
  /**
481
419
  * Determines if the regular parashat haShavua coincides with an event that requires
482
420
  * a special maftir or Haftara (for example Shabbat HaGadol, Shabbat Chanukah, Rosh
@@ -495,38 +433,32 @@ function mergeAliyotWithSpecial(aliyot, special) {
495
433
  * @param {boolean} [wantSeph]
496
434
  * @return {Aliyah | Aliyah[]}
497
435
  */
498
-
499
-
500
436
  function specialReadings(hd, il, aliyot, reason, parsha, wantSeph = false) {
501
437
  let haft;
502
438
  let seph;
503
439
  let specialHaft = false;
504
- const parshaName = parsha ? parshaToString(parsha) : null; // eslint-disable-next-line require-jsdoc
440
+ const parshaName = parsha ? parshaToString(parsha) : null;
505
441
 
442
+ // eslint-disable-next-line require-jsdoc
506
443
  function handleSpecial(key) {
507
444
  const special = lookupFestival(key);
508
-
509
445
  if (!special) {
510
446
  return;
511
447
  }
512
-
513
448
  if (special.haft && !specialHaft) {
514
449
  haft = cloneHaftara(special.haft);
515
450
  reason.haftara = key;
516
451
  specialHaft = true;
517
-
518
452
  if (special.seph) {
519
453
  seph = cloneHaftara(special.seph);
520
454
  reason.sephardic = key;
521
455
  }
522
456
  }
523
-
524
457
  if (special.fullkriyah) {
525
458
  mergeAliyotWithSpecial(aliyot, special.fullkriyah);
526
459
  Object.keys(special.fullkriyah).map(k => reason[k] = key);
527
460
  }
528
461
  }
529
-
530
462
  const events0 = HebrewCalendar.getHolidaysOnDate(hd, il) || [];
531
463
  const events = events0.filter(ev => !(ev.getFlags() & flags.ROSH_CHODESH));
532
464
  events.forEach(ev => {
@@ -534,16 +466,13 @@ function specialReadings(hd, il, aliyot, reason, parsha, wantSeph = false) {
534
466
  handleSpecial(`Shabbat Shuva (with ${parshaName})`);
535
467
  } else {
536
468
  const key = getLeyningKeyForEvent(ev, il);
537
-
538
469
  if (key) {
539
470
  handleSpecial(key);
540
471
  }
541
472
  }
542
473
  });
543
-
544
474
  if (!haft) {
545
475
  const day = hd.getDate();
546
-
547
476
  if (parshaName === 'Pinchas' && day > 17) {
548
477
  // Pinchas is always read in Tamuz (never another month)
549
478
  // either on the 14, 16, 17 (in Israel), 19, 21, 23, 24
@@ -553,13 +482,11 @@ function specialReadings(hd, il, aliyot, reason, parsha, wantSeph = false) {
553
482
  handleSpecial(key);
554
483
  } else {
555
484
  const tommorow = hd.next().getDate();
556
-
557
485
  if (tommorow === 30 || tommorow === 1) {
558
486
  handleSpecial('Shabbat Machar Chodesh');
559
487
  }
560
488
  }
561
489
  }
562
-
563
490
  if (wantSeph && haft) {
564
491
  return {
565
492
  haft,
@@ -618,7 +545,6 @@ const Bereshit={num:1,hebrew:"בְּרֵאשִׁית",book:1,haft:{k:"Isaiah",b:
618
545
  * @param {boolean} [il] true if Israel holiday scheme
619
546
  * @return {Leyning} map of aliyot
620
547
  */
621
-
622
548
  function getLeyningForHoliday(ev, il = false) {
623
549
  if (typeof ev !== 'object' || !(ev instanceof Event)) {
624
550
  throw new TypeError(`Bad event argument: ${ev}`);
@@ -627,11 +553,11 @@ function getLeyningForHoliday(ev, il = false) {
627
553
  } else if (ev.getFlags() & HOLIDAY_IGNORE_MASK) {
628
554
  return undefined;
629
555
  }
630
-
631
556
  const key = getLeyningKeyForEvent(ev, il);
632
557
  const leyning = getLeyningForHolidayKey(key, ev.cholHaMoedDay);
633
558
  return leyning;
634
559
  }
560
+
635
561
  /**
636
562
  * Looks up leyning for a given holiday key. Key should be an
637
563
  * (untranslated) string used in holiday-readings.json. Returns some
@@ -640,62 +566,50 @@ function getLeyningForHoliday(ev, il = false) {
640
566
  * @param {number} [cholHaMoedDay]
641
567
  * @return {Leyning} map of aliyot
642
568
  */
643
-
644
569
  function getLeyningForHolidayKey(key, cholHaMoedDay) {
645
570
  if (typeof key !== 'string') {
646
571
  return undefined;
647
572
  }
648
-
649
573
  const src = lookupFestival(key);
650
-
651
574
  if (typeof src === 'undefined') {
652
575
  return undefined;
653
576
  }
654
-
655
577
  const leyning = {
656
578
  name: {
657
579
  en: key,
658
580
  he: Locale.lookupTranslation(key, 'he')
659
581
  }
660
582
  };
661
-
662
583
  if (src.fullkriyah) {
663
584
  leyning.fullkriyah = clone(src.fullkriyah);
664
-
665
585
  if (key === 'Sukkot Shabbat Chol ha-Moed' && cholHaMoedDay) {
666
586
  leyning.fullkriyah['M'] = leyning.fullkriyah[`M-day${cholHaMoedDay}`];
667
-
668
587
  for (let day = 1; day <= 5; day++) {
669
588
  delete leyning.fullkriyah[`M-day${day}`];
670
589
  }
671
590
  }
672
-
673
591
  if (typeof leyning.fullkriyah['1'] === 'object') {
674
592
  const parts = makeLeyningParts(leyning.fullkriyah);
675
593
  leyning.summary = makeSummaryFromParts(parts);
676
-
677
594
  if (parts.length > 1) {
678
595
  leyning.summaryParts = parts;
679
596
  }
680
597
  }
681
-
682
598
  Object.values(leyning.fullkriyah).map(aliyah => calculateNumVerses(aliyah));
683
599
  }
684
-
685
600
  if (src.haft) {
686
601
  const haft = leyning.haft = cloneHaftara(src.haft);
687
602
  leyning.haftara = makeSummaryFromParts(haft);
688
603
  leyning.haftaraNumV = sumVerses(haft);
689
604
  }
690
-
691
605
  if (src.seph) {
692
606
  const seph = leyning.seph = cloneHaftara(src.seph);
693
607
  leyning.sephardic = makeSummaryFromParts(seph);
694
608
  leyning.sephardicNumV = sumVerses(seph);
695
609
  }
696
-
697
610
  return leyning;
698
611
  }
612
+
699
613
  /**
700
614
  * on doubled parshiot, read only the second Haftarah
701
615
  * except for Nitzavim-Vayelech
@@ -703,7 +617,6 @@ function getLeyningForHolidayKey(key, cholHaMoedDay) {
703
617
  * @param {string[]} parsha
704
618
  * @return {string}
705
619
  */
706
-
707
620
  function getHaftaraKey(parsha) {
708
621
  if (parsha.length == 1 || parsha[0] == 'Nitzavim') {
709
622
  return parsha[0];
@@ -711,20 +624,17 @@ function getHaftaraKey(parsha) {
711
624
  return parsha[1];
712
625
  }
713
626
  }
627
+
714
628
  /**
715
629
  * Looks up regular leyning for a weekly parsha with no special readings
716
630
  * @param {string|string[]} parsha untranslated name like 'Pinchas' or ['Pinchas'] or ['Matot','Masei']
717
631
  * @return {Leyning} map of aliyot
718
632
  */
719
-
720
-
721
633
  function getLeyningForParsha(parsha) {
722
634
  const isParshaString = typeof parsha === 'string';
723
-
724
635
  if (!isParshaString && (!Array.isArray(parsha) || parsha.length !== 1 && parsha.length !== 2)) {
725
636
  throw new TypeError(`Bad parsha argument: ${parsha}`);
726
637
  }
727
-
728
638
  const name = isParshaString ? parsha : parshaToString(parsha);
729
639
  const raw = parshiyotObj[name];
730
640
  const fullkriyah = {};
@@ -743,7 +653,6 @@ function getLeyningForParsha(parsha) {
743
653
  const parts = makeLeyningParts(fullkriyah);
744
654
  const summary = makeSummaryFromParts(parts);
745
655
  /** @type {Leyning} */
746
-
747
656
  const result = {
748
657
  name: {
749
658
  en: name,
@@ -754,28 +663,22 @@ function getLeyningForParsha(parsha) {
754
663
  summary,
755
664
  fullkriyah: fullkriyah
756
665
  };
757
-
758
666
  if (parts.length > 1) {
759
667
  result.summaryParts = parts;
760
668
  }
761
-
762
669
  const hkey = getHaftaraKey(parshaNameArray);
763
670
  const haft0 = parshiyotObj[hkey].haft;
764
-
765
671
  if (haft0) {
766
672
  const haft = result.haft = cloneHaftara(haft0);
767
673
  result.haftara = makeSummaryFromParts(haft);
768
674
  result.haftaraNumV = sumVerses(haft);
769
675
  }
770
-
771
676
  if (raw.seph) {
772
677
  const seph = result.seph = cloneHaftara(raw.seph);
773
678
  result.sephardic = makeSummaryFromParts(seph);
774
679
  result.sephardicNumV = sumVerses(seph);
775
680
  }
776
-
777
681
  const weekdaySrc = raw.weekday || parshiyotObj[parshaNameArray[0]].weekday;
778
-
779
682
  if (weekdaySrc) {
780
683
  const weekday = result.weekday = {};
781
684
  ['1', '2', '3'].forEach(num => {
@@ -789,9 +692,9 @@ function getLeyningForParsha(parsha) {
789
692
  weekday[num] = aliyah;
790
693
  });
791
694
  }
792
-
793
695
  return result;
794
696
  }
697
+
795
698
  /**
796
699
  * Looks up leyning for a regular Shabbat parsha, including any special
797
700
  * maftir or Haftara.
@@ -799,42 +702,35 @@ function getLeyningForParsha(parsha) {
799
702
  * @param {boolean} [il] in Israel
800
703
  * @return {Leyning} map of aliyot
801
704
  */
802
-
803
705
  function getLeyningForParshaHaShavua(ev, il = false) {
804
706
  if (!ev instanceof Event) {
805
707
  throw new TypeError(`Bad event argument: ${ev}`);
806
708
  } else if (ev.getFlags() != flags.PARSHA_HASHAVUA) {
807
709
  throw new TypeError(`Event must be parsha hashavua: ${ev.getDesc()}`);
808
- } // first, collect the default aliyot and haftara
809
-
810
-
710
+ }
711
+ // first, collect the default aliyot and haftara
811
712
  const parsha = ev.parsha;
812
713
  const result = getLeyningForParsha(parsha);
813
714
  const reason = {};
814
- const hd = ev.getDate(); // Now, check for special maftir or haftara on same date
815
-
715
+ const hd = ev.getDate();
716
+ // Now, check for special maftir or haftara on same date
816
717
  const specialHaftara = specialReadings(hd, il, result.fullkriyah, reason, parsha, true);
817
-
818
718
  if (specialHaftara) {
819
719
  const haft = result.haft = cloneHaftara(specialHaftara.haft);
820
720
  result.haftara = makeSummaryFromParts(haft);
821
721
  result.haftaraNumV = sumVerses(haft);
822
-
823
722
  if (specialHaftara.seph) {
824
723
  const seph = result.seph = cloneHaftara(specialHaftara.seph);
825
724
  result.sephardic = makeSummaryFromParts(seph);
826
725
  result.sephardicNumV = sumVerses(seph);
827
726
  }
828
727
  }
829
-
830
728
  if (reason['7'] || reason['M']) {
831
729
  const parts = makeLeyningParts(result.fullkriyah);
832
730
  result.summary = makeSummaryFromParts(parts);
833
731
  result.summaryParts = parts;
834
732
  }
835
-
836
733
  const reasons = Object.keys(reason);
837
-
838
734
  if (reasons.length !== 0) {
839
735
  result.reason = reason;
840
736
  reasons.forEach(num => {
@@ -844,18 +740,17 @@ function getLeyningForParshaHaShavua(ev, il = false) {
844
740
  hafts.forEach(haft => haft.reason = reason[num]);
845
741
  } else {
846
742
  const aliyah = result.fullkriyah[num];
847
-
848
743
  if (typeof aliyah === 'object') {
849
744
  aliyah.reason = reason[num];
850
745
  }
851
746
  }
852
747
  });
853
- } // to avoid confusion, don't include Saturday mincha reading here
854
-
855
-
748
+ }
749
+ // to avoid confusion, don't include Saturday mincha reading here
856
750
  delete result.weekday;
857
751
  return result;
858
752
  }
753
+
859
754
  /**
860
755
  * Looks up leyning for a regular Shabbat, Monday/Thursday weekday or holiday.
861
756
  *
@@ -873,28 +768,22 @@ function getLeyningForParshaHaShavua(ev, il = false) {
873
768
  * @param {boolean} il in Israel
874
769
  * @return {Leyning} map of aliyot
875
770
  */
876
-
877
771
  function getLeyningOnDate(hdate, il) {
878
772
  const dow = hdate.getDay();
879
773
  const hyear = hdate.getFullYear();
880
774
  const sedra = HebrewCalendar.getSedra(hyear, il);
881
775
  const parsha = sedra.lookup(hdate);
882
-
883
776
  if (dow === 6 && !parsha.chag) {
884
777
  const parshaEvent = new ParshaEvent(hdate, parsha.parsha, il);
885
778
  return getLeyningForParshaHaShavua(parshaEvent, il);
886
779
  }
887
-
888
780
  const events = HebrewCalendar.getHolidaysOnDate(hdate, il) || [];
889
-
890
781
  for (let i = 0; i < events.length; i++) {
891
782
  const reading = getLeyningForHoliday(events[i], il);
892
-
893
783
  if (reading) {
894
784
  return reading;
895
785
  }
896
786
  }
897
-
898
787
  if (dow === 1 || dow === 4) {
899
788
  const saturday = hdate.onOrAfter(6);
900
789
  const parsha2 = findParshaHaShavua(saturday);
@@ -906,44 +795,38 @@ function getLeyningOnDate(hdate, il) {
906
795
  weekday: reading.weekday
907
796
  };
908
797
  return result;
909
- } // no reading today: it's not Shabbat, Mon/Thu, or a Torah-reading holiday
910
-
911
-
798
+ }
799
+ // no reading today: it's not Shabbat, Mon/Thu, or a Torah-reading holiday
912
800
  return undefined;
913
801
  }
802
+
914
803
  /**
915
804
  * @private
916
805
  * @param {HDate} saturday
917
806
  * @param {boolean} il
918
807
  * @return {Leyning}
919
808
  */
920
-
921
809
  function findParshaHaShavua(saturday, il) {
922
810
  const hyear = saturday.getFullYear();
923
811
  const sedra = HebrewCalendar.getSedra(hyear, il);
924
812
  const parsha = sedra.lookup(saturday);
925
-
926
813
  if (!parsha.chag) {
927
814
  return parsha;
928
- } // Search for next regular parsha, which could even spill into next year
929
-
930
-
815
+ }
816
+ // Search for next regular parsha, which could even spill into next year
931
817
  const endOfYear = new HDate(1, months.TISHREI, hyear + 1).abs() - 1;
932
818
  const endAbs = endOfYear + 30;
933
-
934
819
  for (let sat2 = saturday.abs() + 7; sat2 <= endAbs; sat2 += 7) {
935
820
  const sedra2 = sat2 > endOfYear ? HebrewCalendar.getSedra(hyear + 1, il) : sedra;
936
821
  const parsha2 = sedra2.lookup(sat2);
937
-
938
822
  if (!parsha2.chag) {
939
823
  return parsha2;
940
824
  }
941
825
  }
942
826
  /* NOTREACHED */
943
-
944
-
945
827
  return null;
946
828
  }
829
+
947
830
  /**
948
831
  * Parsha metadata
949
832
  * @typedef {Object} ParshaMeta
@@ -962,22 +845,16 @@ function findParshaHaShavua(saturday, il) {
962
845
  * @param {string|string[]} parsha
963
846
  * @return {ParshaMeta}
964
847
  */
965
-
966
-
967
848
  function lookupParsha(parsha) {
968
849
  const isParshaString = typeof parsha === 'string';
969
-
970
850
  if (!isParshaString && (!Array.isArray(parsha) || parsha.length !== 1 && parsha.length !== 2)) {
971
851
  throw new TypeError(`Bad parsha argument: ${parsha}`);
972
852
  }
973
-
974
853
  const name = isParshaString ? parsha : parshaToString(parsha);
975
854
  const raw = parshiyotObj[name];
976
-
977
855
  if (typeof raw !== 'object') {
978
856
  throw new TypeError(`Bad parsha argument: ${parsha}`);
979
857
  }
980
-
981
858
  return raw;
982
859
  }
983
860
 
@@ -986,23 +863,22 @@ const fmt = new Intl.DateTimeFormat('en-US', {
986
863
  month: 'short',
987
864
  day: '2-digit'
988
865
  });
866
+
989
867
  /**
990
868
  * @private
991
869
  * @param {Date} dt
992
870
  * @return {string}
993
871
  */
994
-
995
872
  function fmtDate(dt) {
996
873
  const s = fmt.format(dt).split(' ');
997
874
  return s[1].substring(0, 2) + '-' + s[0] + '-' + s[2];
998
875
  }
876
+
999
877
  /**
1000
878
  * @private
1001
879
  * @param {Event[]} events
1002
880
  * @return {Object<string,boolean>}
1003
881
  */
1004
-
1005
-
1006
882
  function getParshaDates(events) {
1007
883
  const parshaEvents = events.filter(ev => ev.getFlags() === flags.PARSHA_HASHAVUA);
1008
884
  const parshaDates = parshaEvents.reduce((set, ev) => {
@@ -1011,13 +887,13 @@ function getParshaDates(events) {
1011
887
  }, {});
1012
888
  return parshaDates;
1013
889
  }
890
+
1014
891
  /**
1015
892
  * @private
1016
893
  * @param {fs.WriteStream} stream
1017
894
  * @param {number} hyear
1018
895
  * @param {boolean} il
1019
896
  */
1020
-
1021
897
  function writeFullKriyahCsv(stream, hyear, il) {
1022
898
  const events0 = HebrewCalendar.calendar({
1023
899
  year: hyear,
@@ -1034,68 +910,61 @@ function writeFullKriyahCsv(stream, hyear, il) {
1034
910
  }
1035
911
  });
1036
912
  }
913
+
1037
914
  /**
1038
915
  * @private
1039
916
  * @param {Event} ev
1040
917
  * @return {boolean}
1041
918
  */
1042
-
1043
919
  function ignore(ev) {
1044
920
  const mask = ev.getFlags();
1045
-
1046
921
  if (mask === flags.SPECIAL_SHABBAT) {
1047
922
  return true;
1048
923
  }
1049
-
1050
924
  if (mask !== flags.ROSH_CHODESH) {
1051
925
  return false;
1052
926
  }
1053
-
1054
927
  return ev.getDate().getDay() === 6;
1055
928
  }
929
+
1056
930
  /**
1057
931
  * @private
1058
932
  * @param {fs.WriteStream} stream
1059
933
  * @param {Event} ev
1060
934
  * @param {boolean} il
1061
935
  */
1062
-
1063
-
1064
936
  function writeFullKriyahEvent(stream, ev, il) {
1065
937
  if (ignore(ev)) {
1066
938
  return;
1067
939
  }
1068
-
1069
940
  const mask = ev.getFlags();
1070
941
  const isParsha = mask === flags.PARSHA_HASHAVUA;
1071
942
  const reading = isParsha ? getLeyningForParshaHaShavua(ev, il) : getLeyningForHoliday(ev, il);
1072
-
1073
943
  if (reading) {
1074
944
  writeCsvLines(stream, ev, reading, il, isParsha);
1075
-
1076
945
  if (!isParsha) {
1077
946
  writeHolidayMincha(stream, ev, il);
1078
947
  }
1079
948
  }
1080
949
  }
950
+
1081
951
  /**
1082
952
  * @private
1083
953
  * @param {fs.WriteStream} stream
1084
954
  * @param {Event} ev
1085
955
  * @param {boolean} il
1086
956
  */
1087
-
1088
957
  function writeHolidayMincha(stream, ev, il) {
1089
958
  const desc = ev.getDesc();
1090
959
  const minchaDesc1 = desc + ' (Mincha)';
1091
960
  const readingMincha1 = getLeyningForHolidayKey(minchaDesc1);
1092
961
  const readingMincha = readingMincha1 || getLeyningForHolidayKey(getLeyningKeyForEvent(ev, il) + ' (Mincha)');
1093
-
1094
962
  if (readingMincha) {
1095
963
  const minchaEv = new Event(ev.getDate(), minchaDesc1, flags.USER_EVENT);
1096
964
  writeCsvLines(stream, minchaEv, readingMincha, il, false);
1097
965
  }
1098
966
  }
967
+
1099
968
  /**
1100
969
  * Formats reading for CSV
1101
970
  * @param {fs.WriteStream} stream
@@ -1104,75 +973,59 @@ function writeHolidayMincha(stream, ev, il) {
1104
973
  * @param {boolean} il
1105
974
  * @param {boolean} isParsha
1106
975
  */
1107
-
1108
976
  function writeCsvLines(stream, ev, reading, il, isParsha) {
1109
977
  const parsha = isParsha ? ev.basename() : getLeyningKeyForEvent(ev, il) || ev.render();
1110
978
  const date = fmtDate(ev.getDate().greg());
1111
979
  const lines = getFullKriyahLines(reading);
1112
980
  lines.forEach(s => {
1113
981
  const code = s[0].charCodeAt(0);
1114
-
1115
982
  if (code < 48 || code > 57) {
1116
983
  s[0] = `"${s[0]}"`;
1117
984
  }
1118
-
1119
985
  stream.write(`${date},"${parsha}",${s[0]},"${s[1]}",${s[2]}\r\n`);
1120
986
  });
1121
987
  stream.write('\r\n');
1122
988
  }
989
+
1123
990
  /**
1124
991
  * @private
1125
992
  * @param {any} reading
1126
993
  * @return {any[]}
1127
994
  */
1128
-
1129
995
  function getFullKriyahLines(reading) {
1130
996
  const lines = [];
1131
-
1132
997
  if (reading.fullkriyah) {
1133
998
  Object.keys(reading.fullkriyah).forEach(num => {
1134
999
  const a = reading.fullkriyah[num];
1135
-
1136
1000
  if (typeof a !== 'undefined') {
1137
1001
  var _reading$reason;
1138
-
1139
1002
  const k = num == 'M' ? 'maf' : num;
1140
1003
  let aliyah = formatAliyahWithBook(a);
1141
-
1142
1004
  if ((_reading$reason = reading.reason) !== null && _reading$reason !== void 0 && _reading$reason[num]) {
1143
1005
  aliyah += ' | ' + reading.reason[num];
1144
1006
  }
1145
-
1146
1007
  lines.push([k, aliyah, a.v || '']);
1147
1008
  }
1148
1009
  });
1149
1010
  }
1150
-
1151
1011
  let specialHaftara = false;
1152
-
1153
1012
  if (reading.haftara) {
1154
1013
  var _reading$reason2;
1155
-
1156
1014
  let haftara = reading.haftara.replace(/,/g, ';');
1157
-
1158
1015
  if ((_reading$reason2 = reading.reason) !== null && _reading$reason2 !== void 0 && _reading$reason2.haftara) {
1159
1016
  specialHaftara = true;
1160
1017
  haftara += ' | ' + reading.reason.haftara;
1161
1018
  }
1162
-
1163
1019
  lines.push(['Haftara', haftara, reading.haftaraNumV || '']);
1164
1020
  }
1165
-
1166
1021
  if (reading.sephardic && !specialHaftara) {
1167
1022
  const sephardic = reading.sephardic.replace(/,/g, ';');
1168
1023
  lines.push(['Haftara for Sephardim', sephardic, reading.sephardicNumV || '']);
1169
1024
  }
1170
-
1171
1025
  if (reading.triHaftara) {
1172
1026
  const haftara = reading.triHaftara.replace(/,/g, ';');
1173
1027
  lines.push(['Alternate Haftara', haftara, reading.triHaftaraNumV || '']);
1174
1028
  }
1175
-
1176
1029
  return lines;
1177
1030
  }
1178
1031