diff --git a/package-lock.json b/package-lock.json index 949b598..181de0a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@hebcal/leyning", - "version": "5.1.0", + "version": "6.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@hebcal/leyning", - "version": "5.1.0", + "version": "6.0.1", "license": "BSD-2-Clause", "dependencies": { "@hebcal/core": "^3.42.2" @@ -23,7 +23,7 @@ "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^13.3.0", "ava": "^4.3.1", - "core-js": "^3.24.1", + "core-js": "^3.25.0", "eslint": "^8.22.0", "eslint-config-google": "^0.14.0", "jsdoc": "^3.6.11", @@ -2908,9 +2908,9 @@ } }, "node_modules/core-js": { - "version": "3.24.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.24.1.tgz", - "integrity": "sha512-0QTBSYSUZ6Gq21utGzkfITDylE8jWC9Ne1D2MrhvlsZBI1x39OdDIVbzSqtgMndIy6BlHxBXpMGqzZmnztg2rg==", + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.25.0.tgz", + "integrity": "sha512-CVU1xvJEfJGhyCpBrzzzU1kjCfgsGUxhEvwUV2e/cOedYWHdmluamx+knDnmhqALddMG16fZvIqvs9aijsHHaA==", "dev": true, "hasInstallScript": true, "funding": { @@ -8655,9 +8655,9 @@ "dev": true }, "core-js": { - "version": "3.24.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.24.1.tgz", - "integrity": "sha512-0QTBSYSUZ6Gq21utGzkfITDylE8jWC9Ne1D2MrhvlsZBI1x39OdDIVbzSqtgMndIy6BlHxBXpMGqzZmnztg2rg==", + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.25.0.tgz", + "integrity": "sha512-CVU1xvJEfJGhyCpBrzzzU1kjCfgsGUxhEvwUV2e/cOedYWHdmluamx+knDnmhqALddMG16fZvIqvs9aijsHHaA==", "dev": true }, "core-js-compat": { diff --git a/package.json b/package.json index 02e790e..9a63fea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@hebcal/leyning", - "version": "6.0.0", + "version": "6.0.1", "author": "Michael J. Radwin (https://github.com/mjradwin)", "keywords": [ "hebcal", @@ -60,7 +60,7 @@ "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^13.3.0", "ava": "^4.3.1", - "core-js": "^3.24.1", + "core-js": "^3.25.0", "eslint": "^8.22.0", "eslint-config-google": "^0.14.0", "jsdoc": "^3.6.11", diff --git a/src/getLeyningForParshaHaShavua.spec.js b/src/getLeyningForParshaHaShavua.spec.js index 0b53bd4..7cbe51b 100644 --- a/src/getLeyningForParshaHaShavua.spec.js +++ b/src/getLeyningForParshaHaShavua.spec.js @@ -31,8 +31,7 @@ function formatAliyah(aliyot, num) { return formatAliyahWithBook(aliyot.fullkriyah[num]); } -test('getLeyningForParshaHaShavua', (t) => { - /** @type {HebrewCalendar.Options} */ +test('2026', (t) => { const options = {year: 2026, isHebrewYear: false, sedrot: true, noHolidays: true}; const events = HebrewCalendar.calendar(options); for (const ev of events) { @@ -71,8 +70,7 @@ test('getLeyningForParshaHaShavua', (t) => { t.is(a.haftara, 'I Samuel 20:18-42'); break; case 'Parashat Vayeshev': - t.is(a.reason.haftara, 'Shabbat Chanukah'); - t.is(a.reason.M, 'Chanukah Day 1'); + t.is(a.reason.haftara, 'Chanukah Day 1 (on Shabbat)'); t.is(a.haftara, 'Zechariah 2:14-4:7'); t.is(formatAliyah(a, 'M'), 'Numbers 7:1-7:17'); break; @@ -100,14 +98,14 @@ test('getLeyningForParshaHaShavua', (t) => { '6': {k: 'Genesis', b: '43:16', e: '43:29', v: 14}, '7': {k: 'Genesis', b: '43:30', e: '44:17', v: 22}, 'M': {p: 35, k: 'Numbers', b: '7:54', e: '8:4', v: 40, - reason: 'Chanukah Day 8'}, + reason: 'Chanukah Day 8 (on Shabbat)'}, }, haft: { k: 'I Kings', b: '7:40', e: '7:50', v: 11, - reason: 'Shabbat Chanukah II', + reason: 'Chanukah Day 8 (on Shabbat)', }, haftara: 'I Kings 7:40-50', haftaraNumV: 11, @@ -117,29 +115,37 @@ test('getLeyningForParshaHaShavua', (t) => { '3': {k: 'Genesis', b: '41:8', e: '41:14', v: 7}, }, reason: { - haftara: 'Shabbat Chanukah II', - M: 'Chanukah Day 8', + haftara: 'Chanukah Day 8 (on Shabbat)', + M: 'Chanukah Day 8 (on Shabbat)', }, }; - t.deepEqual(a, expected, 'Shabbat Chanukah II'); + t.deepEqual(a, expected, 'Chanukah Day 8 (on Shabbat)'); break; } } +}); - options.year = 2020; - options.month = 12; - let events2 = HebrewCalendar.calendar(options); - const vayeshev = events2.find((e) => e.getDesc() == 'Parashat Vayeshev'); - let a = getLeyningForParshaHaShavua(vayeshev); - t.is(a.reason.haftara, 'Shabbat Chanukah'); - t.is(a.reason['M'], 'Chanukah Day 2'); +test('2020-12', (t) => { + const events = HebrewCalendar.calendar({year: 2020, month: 12, sedrot: true, noHolidays: true}); + const vayeshev = events.find((e) => e.getDesc() == 'Parashat Vayeshev'); + const a = getLeyningForParshaHaShavua(vayeshev); + t.is(a.reason.haftara, 'Chanukah Day 2 (on Shabbat)'); + t.is(a.reason['M'], 'Chanukah Day 2 (on Shabbat)'); t.is(a.haftara, 'Zechariah 2:14-4:7'); - t.is(formatAliyah(a, 'M'), 'Numbers 7:18-7:29'); + const expected = { + p: 35, + k: 'Numbers', + b: '7:18', + e: '7:23', + v: 6, + reason: 'Chanukah Day 2 (on Shabbat)', + }; + t.deepEqual(a.fullkriyah.M, expected); +}); - options.year = 2021; - options.month = 12; - events2 = HebrewCalendar.calendar(options); - const miketz = events2.find((e) => e.getDesc() == 'Parashat Miketz'); +test('2021-12', (t) => { + const events = HebrewCalendar.calendar({year: 2021, month: 12, sedrot: true, noHolidays: true}); + const miketz = events.find((e) => e.getDesc() == 'Parashat Miketz'); const expected = { name: { en: 'Miketz', @@ -187,14 +193,14 @@ test('getLeyningForParshaHaShavua', (t) => { 'M': 'Shabbat Rosh Chodesh Chanukah', }, }; - a = getLeyningForParshaHaShavua(miketz); + const a = getLeyningForParshaHaShavua(miketz); t.deepEqual(a, expected, 'Shabbat Rosh Chodesh Chanukah'); +}); - options.year = 2019; - options.month = 4; - events2 = HebrewCalendar.calendar(options); - const tazria = events2.find((e) => e.getDesc() == 'Parashat Tazria'); - a = getLeyningForParshaHaShavua(tazria); +test('2019-04', (t) => { + const events = HebrewCalendar.calendar({year: 2019, month: 4, sedrot: true, noHolidays: true}); + const tazria = events.find((e) => e.getDesc() == 'Parashat Tazria'); + const a = getLeyningForParshaHaShavua(tazria); t.is(a.reason.haftara, 'Shabbat HaChodesh (on Rosh Chodesh)'); t.is(a.reason['7'], 'Shabbat HaChodesh (on Rosh Chodesh)'); t.is(a.reason['M'], 'Shabbat HaChodesh (on Rosh Chodesh)'); diff --git a/src/getLeyningKeyForEvent.js b/src/getLeyningKeyForEvent.js index 4601fab..1ca86a5 100644 --- a/src/getLeyningKeyForEvent.js +++ b/src/getLeyningKeyForEvent.js @@ -59,6 +59,8 @@ export function getLeyningKeyForEvent(ev, il = false) { return 'Shabbat Rosh Chodesh Chanukah'; } else if (isRoshChodesh && ev.chanukahDay == 7) { return `Chanukah Day 7 (on Rosh Chodesh)`; + } else if (isShabbat) { + return `Chanukah Day ${ev.chanukahDay} (on Shabbat)`; } else { return `Chanukah Day ${ev.chanukahDay}`; } diff --git a/src/getLeyningKeyForEvent.spec.js b/src/getLeyningKeyForEvent.spec.js index 4ae2d47..d749ff8 100644 --- a/src/getLeyningKeyForEvent.spec.js +++ b/src/getLeyningKeyForEvent.spec.js @@ -31,7 +31,7 @@ test('getLeyningKeyForEvent', (t) => { 'Rosh Chodesh Kislev': 'Rosh Chodesh Kislev', 'Chanukah: 1 Candle': undefined, 'Chanukah: 2 Candles': 'Chanukah Day 1', - 'Chanukah: 3 Candles': 'Chanukah Day 2', + 'Chanukah: 3 Candles': 'Chanukah Day 2 (on Shabbat)', 'Chanukah: 4 Candles': 'Chanukah Day 3', 'Chanukah: 5 Candles': 'Chanukah Day 4', 'Chanukah: 6 Candles': 'Chanukah Day 5', diff --git a/src/holiday-readings.json b/src/holiday-readings.json index 647bae1..173b66a 100644 --- a/src/holiday-readings.json +++ b/src/holiday-readings.json @@ -354,10 +354,6 @@ "6":{"p":54,"k":5,"b":"33:27","e":"34:12"}, "7":{"p":1,"k":1,"b":"1:1","e":"2:3"}, "M":{"p":41,"k":4,"b":"29:35","e":"30:1"}}}, -"Shabbat Chanukah":{ - "haft":{"k":"Zechariah","b":"2:14","e":"4:7"}}, -"Shabbat Chanukah II":{ - "haft":{"k":"I Kings","b":"7:40","e":"7:50"}}, "Shabbat Rosh Chodesh Chanukah":{ "haft":{"k":"Zechariah","b":"2:14","e":"4:7"}, "fullkriyah": @@ -371,6 +367,9 @@ "alt":{"1":{"p":35,"k":4,"b":"7:1","e":"7:3"}, "2":{"p":35,"k":4,"b":"7:4","e":"7:11"}, "3":{"p":35,"k":4,"b":"7:12","e":"7:17"}}}, +"Chanukah Day 1 (on Shabbat)":{ + "haft":{"k":"Zechariah","b":"2:14","e":"4:7"}, + "fullkriyah":{"M":{"p":35,"k":4,"b":"7:1","e":"7:17"}}}, "Chanukah Day 2":{ "fullkriyah": {"1":{"p":35,"k":4,"b":"7:18","e":"7:20"}, @@ -379,6 +378,9 @@ "alt":{"1":{"p":35,"k":4,"b":"7:18","e":"7:20"}, "2":{"p":35,"k":4,"b":"7:21","e":"7:23"}, "3":{"p":35,"k":4,"b":"7:18","e":"7:23"}}}, +"Chanukah Day 2 (on Shabbat)":{ + "haft":{"k":"Zechariah","b":"2:14","e":"4:7"}, + "fullkriyah":{"M":{"p":35,"k":4,"b":"7:18","e":"7:23"}}}, "Chanukah Day 3":{ "fullkriyah": {"1":{"p":35,"k":4,"b":"7:24","e":"7:26"}, @@ -387,6 +389,9 @@ "alt":{"1":{"p":35,"k":4,"b":"7:24","e":"7:26"}, "2":{"p":35,"k":4,"b":"7:27","e":"7:29"}, "3":{"p":35,"k":4,"b":"7:24","e":"7:29"}}}, +"Chanukah Day 3 (on Shabbat)":{ + "haft":{"k":"Zechariah","b":"2:14","e":"4:7"}, + "fullkriyah":{"M":{"p":35,"k":4,"b":"7:24","e":"7:29"}}}, "Chanukah Day 4":{ "fullkriyah": {"1":{"p":35,"k":4,"b":"7:30","e":"7:32"}, @@ -395,6 +400,9 @@ "alt":{"1":{"p":35,"k":4,"b":"7:30","e":"7:32"}, "2":{"p":35,"k":4,"b":"7:33","e":"7:35"}, "3":{"p":35,"k":4,"b":"7:30","e":"7:35"}}}, +"Chanukah Day 4 (on Shabbat)":{ + "haft":{"k":"Zechariah","b":"2:14","e":"4:7"}, + "fullkriyah":{"M":{"p":35,"k":4,"b":"7:30","e":"7:35"}}}, "Chanukah Day 5":{ "fullkriyah": {"1":{"p":35,"k":4,"b":"7:36","e":"7:38"}, @@ -403,6 +411,9 @@ "alt":{"1":{"p":35,"k":4,"b":"7:36","e":"7:38"}, "2":{"p":35,"k":4,"b":"7:39","e":"7:41"}, "3":{"p":35,"k":4,"b":"7:36","e":"7:41"}}}, +"Chanukah Day 5 (on Shabbat)":{ + "haft":{"k":"Zechariah","b":"2:14","e":"4:7"}, + "fullkriyah":{"M":{"p":35,"k":4,"b":"7:36","e":"7:41"}}}, "Chanukah Day 6":{ "fullkriyah": {"1":{"p":41,"k":4,"b":"28:1","e":"28:5"}, @@ -423,11 +434,17 @@ "2":{"p":41,"k":4,"b":"28:6","e":"28:10"}, "3":{"p":41,"k":4,"b":"28:11","e":"28:15"}, "4":{"p":35,"k":4,"b":"7:48","e":"7:53"}}}, +"Chanukah Day 7 (on Shabbat)":{ + "haft":{"k":"Zechariah","b":"2:14","e":"4:7"}, + "fullkriyah":{"M":{"p":35,"k":4,"b":"7:48","e":"7:53"}}}, "Chanukah Day 8":{ "fullkriyah": {"1":{"p":35,"k":4,"b":"7:54","e":"7:56"}, "2":{"p":35,"k":4,"b":"7:57","e":"7:59"}, "3":{"p":35,"k":4,"b":"7:60","e":"8:4"}}}, +"Chanukah Day 8 (on Shabbat)":{ + "haft":{"k":"I Kings","b":"7:40","e":"7:50"}, + "fullkriyah":{"M":{"p":35,"k":4,"b":"7:54","e":"8:4"}}}, "Purim":{ "fullkriyah": {"1":{"p":16,"k":2,"b":"17:8","e":"17:10"}, diff --git a/src/holiday.spec.js b/src/holiday.spec.js index cae52b2..3f3d61b 100644 --- a/src/holiday.spec.js +++ b/src/holiday.spec.js @@ -113,8 +113,6 @@ test('getLeyningForHoliday', (t) => { t.is(getLeyningForHoliday(sukkot2).fullkriyah['4'].p, 41); const shminiAtzeret = events.find((e) => e.getDesc() == 'Shmini Atzeret'); t.is(getLeyningForHoliday(shminiAtzeret).fullkriyah['7'].p, 47); - const chanukah3 = events.find((e) => e.getDesc() == 'Chanukah: 3 Candles'); - t.is(getLeyningForHoliday(chanukah3).fullkriyah['3'].e, '7:29'); const tevet17 = events.find((e) => e.getDesc() == 'Asara B\'Tevet'); t.is(getLeyningForHoliday(tevet17).fullkriyah['3'].e, '34:10'); const pesach5 = events.find((e) => e.getDesc() == 'Pesach V (CH\'\'M)'); @@ -125,6 +123,21 @@ test('getLeyningForHoliday', (t) => { t.is(getLeyningForHoliday(av9).haftara, 'Jeremiah 8:13-9:23'); }); +test('getLeyningForHoliday-Chanukah', (t) => { + const options = {year: 5757, isHebrewYear: true, il: true}; + const events = HebrewCalendar.calendar(options); + const chanukah3 = events.find((e) => e.getDesc() == 'Chanukah: 3 Candles'); + const reading = getLeyningForHoliday(chanukah3); + const expected = { + name: {en: 'Chanukah Day 2 (on Shabbat)', he: undefined}, + fullkriyah: {M: {p: 35, k: 'Numbers', b: '7:18', e: '7:23', v: 6}}, + haft: {k: 'Zechariah', b: '2:14', e: '4:7', v: 21}, + haftara: 'Zechariah 2:14-4:7', + haftaraNumV: 21, + }; + t.deepEqual(reading, expected); +}); + test('getLeyningForHoliday-RoshChodesh', (t) => { const ev = new Event(new HDate(1, months.SIVAN, 5782), 'Rosh Chodesh Sivan', flags.ROSH_CHODESH); diff --git a/src/leyning.js b/src/leyning.js index 194e435..557d113 100644 --- a/src/leyning.js +++ b/src/leyning.js @@ -132,18 +132,12 @@ function getHaftaraKey(parsha) { /** * @private - * @param {Event} ev the Hebcal event associated with this leyning - * @return {any} + * @param {string[]} parsha + * @param {HDate} hd + * @return {Object|boolean} */ -function getSpecialHaftara(ev) { - if (!ev instanceof Event) { - throw new TypeError(`Bad event argument: ${ev}`); - } else if (ev.getFlags() != flags.PARSHA_HASHAVUA) { - throw new TypeError(`Event must be parsha hashavua: ${ev.getDesc()}`); - } - const parsha = ev.parsha; +function getSpecialHaftara(parsha, hd) { const name = parshaToString(parsha); // untranslated - const hd = ev.getDate(); const day = hd.getDate(); if (name === 'Pinchas') { const month = hd.getMonth(); @@ -260,7 +254,7 @@ export function getLeyningForParshaHaShavua(ev, il=false) { result.summary = makeSummaryFromParts(parts); result.summaryParts = parts; } - const specialHaftara2 = getSpecialHaftara(ev); + const specialHaftara2 = getSpecialHaftara(parsha, hd); if (specialHaftara2) { result.haft = cloneHaftara(specialHaftara2.haft); updateHaftaraSummary = true; diff --git a/src/specialReadings.js b/src/specialReadings.js index 24d31c4..c92b2d8 100644 --- a/src/specialReadings.js +++ b/src/specialReadings.js @@ -97,22 +97,7 @@ export function specialReadings(hd, il, aliyot, reason) { // console.log(hd.greg().toLocaleDateString(), name, ev.getDesc(), key); const special = lookupFestival(key); if (special) { - const shabbatChanukah = getChanukahShabbatKey(ev, key); - if (shabbatChanukah) { - // only for Shabbat Chanukah I or Shabbat Chanukah II. Note - // this section doesn't apply to Shabbat Rosh Chodesh Chanukah; that - // case gets handled below with the mergeAliyotWithSpecial() logic - haft = cloneHaftara(lookupFestival(shabbatChanukah).haft); - specialHaft = true; - reason.haftara = shabbatChanukah; - // Aliyot 1-3 from regular daily reading becomes Maftir - const maftir = aliyot['M'] = clone(special.fullkriyah['1']); - maftir.e = special.fullkriyah['3'].e; - calculateNumVerses(maftir); - reason.M = key; - } else { - handleSpecial(special, key); - } + handleSpecial(special, key); } }); if (!haft) {