UNPKG

hebcal-fixed

Version:

An easy to use, richly-featured, perpetual Jewish Calendar API

1,042 lines (894 loc) 28.5 kB
/* Hebcal - A Jewish Calendar Generator Copyright (C) 1994-2004 Danny Sadinoff Portions Copyright (c) 2002 Michael J. Radwin. All Rights Reserved. https://github.com/hebcal/hebcal-js This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Danny Sadinoff can be reached at danny@sadinoff.com Michael Radwin has made significant contributions as a result of maintaining hebcal.com. The JavaScript code was completely rewritten in 2014 by Eyal Schachter. */ var c = require('./common'), HDate = require('./hdate'), holidays = require('./holidays'), Sedra = require('./sedra'), dafyomi = require('./dafyomi'), cities = require('./cities'), greg = require('./greg'), EventEmitter = require('events').EventEmitter, gematriya = require('gematriya'); // for minifying optimizations var defProp = Object.defineProperty, TE = TypeError, find = 'find', strings = 'strings', getYearObject = 'getYearObject', map = 'map', getDay = 'getDay', getMonth = 'getMonth', getFullYear = 'getFullYear', isLeapYear = 'isLeapYear', length = 'length', next = 'next', prev = 'prev', months = c.months, TISHREI = months.TISHREI, NISAN = months.NISAN, HebcalProto = Hebcal.prototype, MonthProto = Month.prototype, GregYearProto = GregYear.prototype, GregMonthProto = GregMonth.prototype, HDateProto = HDate.prototype; function getset(g, s) { return { enumerable: true, configurable: true, get: g, set: s }; } function extend(base, into) { for (var i in into) { base[i] = into[i]; } return base; } // Main Hebcal function function Hebcal(year, month) { var me = this; // whenever this is done, it is for optimizations. if (!year) { year = (new HDate())[getFullYear](); // this year; } if (typeof year !== 'number') { throw new TE('year to Hebcal() is not a number'); } me.year = year; if (month) { if (typeof month == 'string') { month = c.monthFromName(month); } if (typeof month == 'number') { month = [month]; } if (Array.isArray(month)) { me.months = month[map](function(i){ var m = new Month(i, year); defProp(m, '__year', { configurable: true, writable: true, value: me }); return m; }); me.holidays = holidays.year(year); } else { throw new TE('month to Hebcal is not a valid type'); } } else { return new Hebcal(year, c.range(1, c.MONTH_CNT(year))); } me[length] = c.daysInYear(year); defProp(me, 'il', getset(function() { return me[getMonth](1).il; }, function(il) { me.months.forEach(function(m){ m.il = il; }); })); defProp(me, 'lat', getset(function() { return me[getMonth](1).lat; }, function(lat) { me.months.forEach(function(m){ m.lat = lat; }); })); defProp(me, 'long', getset(function() { return me[getMonth](1).long; }, function(lon) { me.months.forEach(function(m){ m.long = lon; }); })); } HebcalProto[isLeapYear] = HDateProto[isLeapYear]; HebcalProto.setCity = function(city) { this.months.forEach(function(m){ m.setCity(city); }); return this; }; HebcalProto.setLocation = function(lat, lon) { this.months.forEach(function(m){ m.setLocation(lat, lon); }); return this; }; HebcalProto[next] = function() { return new Hebcal(this.year + 1); }; HebcalProto[prev] = function() { return new Hebcal(this.year - 1); }; HebcalProto[getMonth] = function(month) { var months = this.months; month = c.monthNum(month); if (month > this.months[length]) { return this[next]()[getMonth](month - months[length]); } return months[month > 0 ? month - 1 : months[length] + month]; }; HebcalProto[getDay] = function(day) { var me = this; if (day > me[length]) { return null; } if (day < 0) { return me[getDay](me[length] - day); } var rosh = me[find](29, months.ELUL)[0].abs() + 1 - me[find](1, NISAN)[0].abs(); // number of days between Nisan and Tishrei if (day <= rosh) { return me[getMonth](NISAN)[getDay](day); } return me[getMonth](TISHREI)[getDay](day - rosh); }; HebcalProto.days = function() { return [].concat.apply([], this.months[map](function(m){ return m.days; })); }; HebcalProto[map] = function() { return [][map].apply(this.days(), arguments); }; HebcalProto.filter = function() { return [].filter.apply(this.days(), arguments); }; HebcalProto.addHoliday = function(holiday) { if (!(holiday instanceof holidays.Event)) { throw new TE('non-Event passed to addHoliday()'); } this.holidays.add(holiday); return this; }; HebcalProto.findParsha = function(parsha, o) { var langs = o ? [o] : ['s','a','h']; // FIXME: abstract this away somewhere var days = this.filter(function(d){ return Math.max.apply(null, langs.map(function(l){ return d.getSedra(l).indexOf(parsha) + 1; })); }); return days[days[length] - 1]; }; HebcalProto.findSedra = HebcalProto.findParsha; HebcalProto[find] = function find_f(day, month) { var me = this; if (arguments[length] === 1) { if (typeof day == 'string') { return find_f[strings].call(me, day); } else if (Array.isArray(day)) { return [].concat.apply([], day[map](function(d){ return me[find][Array.isArray(d) ? 'apply' : 'call'](me, d); })); } else if (day instanceof HDate) { return me[find](day.getDate(), day[getMonth]()); } else if (day instanceof Date) { return me[find](new HDate(day)); } } else if (arguments[length] == 2) { if (month instanceof Month) { return month[find](day); } else if (Array.isArray(month)) { return [].concat.apply([], month[map](function(m){ return me[find](day, m); })); } else if (typeof month == 'string') { return me[find](day, c.monthNum(month)); } else if (typeof month == 'number') { return me[find](day, me[getMonth](month)); } } return []; }; HebcalProto[find][strings] = function strings(str) { var split = str.split(/\s+/), func = strings[str.replace(/\s/g, '_').toLowerCase()]; if (!split[length]) { return []; } else if (func) { return func.call(this); } try { return this[find](new HDate(str)); } catch(e) { return split[length] - 1 ? this[find].apply(this, split) : []; } }; HebcalProto[find][strings].rosh_chodesh = function() { return this[find]('Rosh Chodesh', c.range(1, this.months[length])); }; HebcalProto[find][strings].holidays = function() { return [].concat.apply([], this.holidays[map](function(h){ return this[find](h.date); }, this)); }; HebcalProto[find][strings].omer = function() { return this[find](c.range(15+1, 15+49), NISAN); }; HebcalProto[find][strings].today = function() { return this[find](new HDate()); }; HebcalProto[find][strings].yesterday = function() { return [this[find]('today')[0][prev]()]; }; HebcalProto[find][strings].tomorrow = function() { return [this[find]('today')[0][next]()]; }; HebcalProto[find][strings].pesach = function() { return this[find](c.range(15, 15+8-this.il), NISAN); }; HebcalProto[find][strings].sukkot = function() { return this[find](c.range(15, 15+9-this.il), TISHREI); }; HebcalProto[find][strings].succot = HebcalProto[find][strings].succos = HebcalProto[find][strings].sukkos = HebcalProto[find][strings].sukkot; HebcalProto[find][strings].shavuot = function() { return this[find](c.range(6, 7-this.il), months.SIVAN); }; HebcalProto[find][strings].shavuos = HebcalProto[find][strings].shavuot; HebcalProto[find][strings].rosh_hashana = function() { return this[find](c.range(1, 2), TISHREI); }; HebcalProto[find][strings].rosh_hashanah = HebcalProto[find][strings].rosh_hashana; // Hebcal properties Hebcal.addZeman = HDate.addZeman; Hebcal.cities = cities; Hebcal.range = c.range; Hebcal.gematriya = gematriya; Hebcal.holidays = c.filter(holidays, ['masks', 'Event']); // not year(), atzmaut() Hebcal.parshiot = Sedra.parshiot; Hebcal.LANGUAGE = c.LANG; Hebcal[map] = c[map]; Hebcal.filter = c.filter; defProp(Hebcal, 'defaultLocation', getset(function(){ return HDate.defaultLocation; }, function(loc){ Hebcal.events.emit('locationChange', HDate.defaultLocation); HDate.defaultLocation = loc; })); defProp(Hebcal, 'defaultCity', getset(function(){ return HDate.defaultCity; }, function(city){ Hebcal.defaultLocation = cities.getCity(city).slice(0, 2); // call the event })); defProp(Hebcal, 'candleLighting', getset(function(){ return holidays.Event.candleLighting; }, function(mins){ holidays.Event.candleLighting = mins; })); defProp(Hebcal, 'havdalah', getset(function(){ return holidays.Event.havdalah; }, function(mins){ holidays.Event.havdalah = mins; })); // Months function Month(month, year) { var me = this; month = c.monthNum(month); if (typeof month != 'number') { throw new TE('month to Hebcal.Month is not a valid type'); } if (typeof year != 'number') { throw new TE('year to Hebcal.Month is not a number'); } me.month = month; me.year = year; me.days = c.range(1, c.daysInMonth(month, year))[map](function(i){ var d = new HDate(i, month, year); defProp(d, '__month', { configurable: true, writable: true, value: me }); return d; }); me[length] = me.days[length]; me.holidays = c.filter(holidays.year(year), function(h){ return h[0].date[getMonth]() == month; }); defProp(me, 'il', getset(function(){ return me[getDay](1).il; }, function(il){ me.days.forEach(function(d){ d.il = il; }); })); defProp(me, 'lat', getset(function(){ return me[getDay](1).lat; }, function(lat){ me.days.forEach(function(d){ d.lat = lat; }); })); defProp(me, 'long', getset(function(){ return me[getDay](1).long; }, function(lon){ me.days.forEach(function(d){ d.long = lon; }); })); return me; }; Hebcal.Month = Month; MonthProto[isLeapYear] = HDateProto[isLeapYear]; MonthProto[prev] = function() { var me = this, year = me[getYearObject](); if (me.month === 1) { // Nisan return year[getMonth](-1); } else if (me.month === TISHREI) { return year[prev]()[getMonth](months.ELUL); } else { return year[getMonth](me.month - 1); } }; MonthProto[next] = function() { var me = this, year = me[getYearObject](); if (me.month === c.MONTH_CNT(me.year)) { // Adar return year[getMonth](1); } else if (me.month === months.ELUL) { return year[next]()[getMonth](TISHREI); } else { return year[getMonth](me.month + 1); } }; MonthProto[getDay] = function(day) { var days = this.days; day = c.dayYearNum(day); if (day > days[length]) { return this[next]()[getDay](day - days[length]); } return days[day > 0 ? day - 1 : days[length] + day]; }; MonthProto[getYearObject] = function() { return this.__year || new Hebcal(this.year); }; MonthProto.getName = function(o) { return c.LANG(c.monthNames[+this[isLeapYear]()][this.month], o); }; MonthProto.rosh_chodesh = function() { var prevMonth = this[prev](); return prevMonth[length] == 30 ? [prevMonth[getDay](-1), this[getDay](1)] : [this[getDay](1)]; }; MonthProto.setCity = function(city) { this.days.forEach(function(d){ d.setCity(city); }); return this; }; MonthProto.setLocation = function(lat, lon) { this.days.forEach(function(d){ d.setLocation(lat, lon); }); return this; }; MonthProto[map] = function() { return [][map].apply(this.days, arguments); }; MonthProto.molad = function() { var retMolad = {}, year, m_elapsed, p_elapsed, h_elapsed, parts, m_adj, toInt = parseInt; m_adj = this.month - 7; year = this.year - 1; if (m_adj < 0) { m_adj += c.MONTH_CNT(year + 1); } m_elapsed = toInt(m_adj + 235 * (year / 19)/* + 12 * (year % 19) + (((year % 19) * 7) + 1) / 19*/); p_elapsed = toInt(204 + (793 * (m_elapsed % 1080))); h_elapsed = toInt(5 + (12 * m_elapsed) + 793 * (m_elapsed / 1080)/* + p_elapsed / 1080*/ - 6); parts = toInt((p_elapsed % 1080) + 1080 * (h_elapsed % 24)); retMolad.doy = new HDate(toInt(1 + 29 * m_elapsed + h_elapsed / 24)).getDay(); retMolad.hour = Math.round(h_elapsed % 24); var chalakim = toInt(parts % 1080); retMolad.minutes = toInt(chalakim / 18); retMolad.chalakim = chalakim % 18; var day = this.prev().find.strings.shabbat_mevarchim._calc.call(this)[0].onOrAfter(retMolad.doy).greg(); day.setHours(retMolad.hour); day.setMinutes(retMolad.minutes); day.setSeconds(retMolad.chalakim * 3.33); retMolad.day = day; return retMolad; }; MonthProto[find] = function find_f(day) { var me = this; if (typeof day == 'number') { return [me[getDay](day)]; } else if (typeof day == 'string') { return find_f[strings].call(me, day); } else if (Array.isArray(day)) { return [].concat.apply([], day[map](function(d){ return me[find](d); })); } else if (day instanceof HDate && day[getFullYear]() == me.year && day[getMonth]() == me.month) { return me[find](day.getDate()); } else if (day instanceof Date) { return me[find](new HDate(day)); } return []; }; MonthProto[find][strings] = function strings(str) { var func = strings[str.replace(/\s/g, '_').toLowerCase()]; if (func) { return func.call(this); } try { return this[find](new HDate(str)); } catch(e) { var num = c.dayYearNum(str); return num ? this[find](num) : []; } }; MonthProto[find][strings].rosh_chodesh = function() { return this.rosh_chodesh(); }; MonthProto[find][strings].shabbat_mevarchim = function sm() { return this.month === months.ELUL ? [] : // No birchat hachodesh in Elul sm._calc.call(this); }; MonthProto[find][strings].shabbat_mevarchim._calc = function() { return this[find](this[getDay](29).onOrBefore(c.days.SAT)); }; MonthProto[find][strings].shabbos_mevarchim = MonthProto[find][strings].shabbos_mevorchim = MonthProto[find][strings].shabbat_mevarchim; // HDate days Hebcal.HDate = HDate; HDateProto.getMonthObject = function() { return this.__month || new Month(this[getMonth](), this[getFullYear]()); }; HDateProto[getYearObject] = function() { return this.getMonthObject()[getYearObject](); }; (function(){ var orig = {}; // slightly less overhead when using unaffiliated HDate()s [prev, next].forEach(function(func){ orig[func] = HDateProto[func]; HDateProto[func] = function() { var day = orig[func].call(this); if (!this.__month) { return day; } return this[getYearObject]()[find](day)[0]; }; }); })(); HDateProto.getSedra = (function(){ var __cache = {}; return function(o) { var sedraYear = __cache[this[getFullYear]()]; if (!sedraYear || (sedraYear.il != this.il)) { sedraYear = __cache[this[getFullYear]()] = new Sedra(this[getFullYear](), this.il); } return sedraYear.get(this)[map](function(p){ return c.LANG(p, o); }); } })(); HDateProto.getParsha = HDateProto.getSedra; HDateProto.holidays = function(all) { var me = this, days = me[getYearObject]().holidays[me]; return days ? days.filter(function(h){ return all ? true : !h.routine() && h.is(me); })[map](function(h){ h.date.setLocation(me); return h; }) : []; }; ['candleLighting', 'havdalah'].forEach(function(prop){ HDateProto[prop] = function(){ var me = this, hd = me.holidays(true).filter(function(h){ return h.is(me); }); if (hd.length) { hd = c.filter(hd.map(function(h){ return h[prop](); }), true); } return hd.length ? new Date(Math.max.apply(null, hd)) : null; }; }); HDateProto.omer = function() { var me = this, greg = me.greg().getTime(), year = me[getFullYear](); if (greg > new HDate(15, NISAN, year).greg().getTime() && greg < new HDate( 6, months.SIVAN, year).greg().getTime()) { return me.abs() - new HDate(16, NISAN, year).abs() + 1; } return 0; }; HDateProto.dafyomi = function(o) { return dafyomi.dafname(dafyomi.dafyomi(this.greg()), o); }; HDateProto.tachanun = (function() { var NONE = tachanun.NONE = 0, MINCHA = tachanun.MINCHA = 1, SHACHARIT = tachanun.SHACHARIT = 2, ALL_CONGS = tachanun.ALL_CONGS = 4; var __cache = { all: {}, some: {}, yes_prev: {}, il: {} }; function tachanun() { var checkNext = !arguments[0], me = this; var year = me[getYearObject](), y = year.year; function mapAbs(arr) { return arr[map](function(d){ return d.abs(); }); } var all, some, yes_prev; if (__cache.il[y] === me.il) { all = __cache.all[y]; some = __cache.some[y]; yes_prev = __cache.yes_prev[y]; } else { all = __cache.all[y] = mapAbs(year[find]('Rosh Chodesh').concat( year[find](c.range(1, c.daysInMonth(NISAN, y)), NISAN), // all of Nisan year[find](15 + 33, NISAN), // Lag Baomer year[find](c.range(1, 8 - me.il), months.SIVAN), // Rosh Chodesh Sivan thru Isru Chag year[find]([9, 15], months.AV), // Tisha B'av and Tu B'av year[find](-1, months.ELUL), // Erev Rosh Hashanah year[find]([1, 2], TISHREI), // Rosh Hashanah year[find](c.range(9, 24 - me.il), TISHREI), // Erev Yom Kippur thru Isru Chag year[find](c.range(25, 33), months.KISLEV), // Chanukah year[find](15, months.SHVAT), // Tu B'shvat year[find]([14, 15], year[isLeapYear]() ? [months.ADAR_I, months.ADAR_II] : months.ADAR_I) // Purim/Shushan Purim + Katan )); some = __cache.some[y] = mapAbs([].concat( // Don't care if it overlaps days in all, because all takes precedence year[find](c.range(1, 13), months.SIVAN), // Until 14 Sivan year[find](c.range(20, 31), TISHREI), // Until after Rosh Chodesh Cheshvan year[find](14, months.IYYAR), // Pesach Sheini holidays.atzmaut(y)[1].date || [], // Yom HaAtzma'ut, which changes based on day of week y >= 5727 ? year[find](29, months.IYYAR) : [] // Yom Yerushalayim )); yes_prev = __cache.yes_prev[y] = mapAbs([].concat( // tachanun is said on the previous day at mincha year[find](-1, months.ELUL), // Erev Rosh Hashanah year[find](9, months.TISHREI), // Erev Yom Kippur year[find](14, months.IYYAR) // Pesach Sheini )); __cache.il[y] = me.il; } all = all.indexOf(me.abs()) > -1; some = some.indexOf(me.abs()) > -1; yes_prev = yes_prev.indexOf(me.abs()+1) > -1; if (all) { return NONE; } var ret = (!some && ALL_CONGS) | (me[getDay]() != 6 && SHACHARIT); if (checkNext && !yes_prev) { ret |= ((me[next]().tachanun(true) & SHACHARIT) && MINCHA); } else { ret |= (me[getDay]() != 5 && MINCHA); } return ret == ALL_CONGS ? NONE : ret; } return tachanun; })(); HDateProto.tachanun_uf = function(){ var ret = this.tachanun(); return { shacharit: !!(ret & this.tachanun.SHACHARIT), mincha: !!(ret & this.tachanun.MINCHA), all_congs: !!(ret & this.tachanun.ALL_CONGS) }; }; HDateProto.hallel = (function() { var NONE = hallel.NONE = 0, HALF = hallel.HALF = 1, WHOLE = hallel.WHOLE = 2; var __cache = { whole: {}, half: {}, il: {} }; function hallel() { var me = this, year = me[getYearObject](), y = year.year; var whole = __cache.il[y] == me.il && __cache.whole[y] || (__cache.whole[y] = [].concat( year[find](c.range(25, 33), months.KISLEV), // Chanukah year[find]([15, me.il ? null : 16], NISAN), // First day(s) of Pesach year[find]('Shavuot'), year[find]('Sukkot'), holidays.atzmaut(y)[1].date || [], // Yom HaAtzma'ut, which changes based on day of week y >= 5727 ? year[find](29, months.IYYAR) : [] // Yom Yerushalayim )[map](function(d){ return d.abs(); })); var half = __cache.il[y] == me.il && __cache.half[y] || (__cache.half[y] = [].concat( year[find]('Rosh Chodesh').filter(function(rc){return rc[getMonth]() != TISHREI}), // Rosh Chodesh, but not Rosh Hashanah year[find](c.range(17 - me.il, 23 - me.il), NISAN) // Last six days of Pesach )[map](function(d){ return d.abs(); })); __cache.il[y] = me.il; return (whole.indexOf(me.abs()) > -1 && WHOLE) || (half.indexOf(me.abs()) > -1 && HALF) || NONE; } return hallel; })(); // Events (function(events){ var refreshInterval, refresh, today = new HDate(); defProp(events, 'refreshInterval', getset(function(){ return refreshInterval; }, function(ms){ if (refresh) { refresh = clearInterval(refresh); } refreshInterval = ms; if (ms) { refresh = setInterval(checkTimes, ms); if (refresh.unref) { refresh.unref(); // don't keep the process open } } })); events.beforeZeman = 1000 * 60 * 10; // 10 minutes function checkTimes() { var now = new HDate(); if (!today.isSameDate(now)) { events.emit('dayChange'); today = now; } function close(obj, compare) { return c.filter(c[map](obj, function(time){ return time - nowGreg; }), function(time) { return time > 0 && time - compare < 0; }); } var nowGreg = new Date(), almostTime = close(now.getZemanim(), events.beforeZeman), customTimes = close(events.customs, events.refreshInterval); for (var zeman in almostTime) { events.emit('almostZeman', zeman, almostTime[zeman]); if (almostTime[zeman] < events.refreshInterval) { events.emit('atZeman', zeman); } } for (var custom in customTimes) { events.emit('custom', custom); } } checkTimes(); events.refreshInterval = 1000 * 60 * 5; // 5 minutes // set the interval events.customs = {}; })(Hebcal.events = new EventEmitter()); // Gregorian years function GregYear(year, month) { var me = this; if (!year) { year = (new Date)[getFullYear](); } if (typeof year === 'string') { var d = new Date(year); month = year.indexOf(' ') + 1 || year.indexOf('-') + 1 || year.indexOf('/') + 1 ? d[getMonth]() + 1 : c.range(1, 12); // Check if a month was passed in the string. Can't just check for default January, because a real January might have been passed. return new GregYear(d[getFullYear](), month); } if (typeof year !== 'number') { throw new TE('year to Hebcal.GregYear() is not a number'); } me.year = year; if (month) { if (typeof month === 'string') { // month name month = greg.lookupMonthName(month); } if (typeof month === 'number') { month = [month]; } if (Array.isArray(month)) { me.months = month[map](function(i){ var m = new GregMonth(i, year); defProp(m, '__year', { configurable: true, writable: true, value: me }); return m; }); } else { throw new TE('month to Hebcal.GregYear() is not a valid type'); } } else { return new GregYear(year, c.range(1, 12)); } me.hebyears = [].concat.apply([], me.months[map](function(m){ return m.hebmonths[map](function(hm){ return hm.year; }); })).filter(function(val, i, arr){ return arr.indexOf(val) === i; // keep unique values only }); me.holidays = c.filter(holidays.year(me.hebyears[0]), function(h){ return h[0].date.greg()[getFullYear]() === year && me.months.filter(function(m){ // don't keep ones that are out of bounds return m.month === h[0].date.greg()[getMonth]() + 1; })[length]; }); if (me.hebyears[1]) { extend(me.holidays, c.filter(holidays.year(me.hebyears[1]), function(h){ return h[0].date.greg()[getFullYear]() === year && me.months.filter(function(m){ // don't keep ones that are out of bounds return m.month === h[0].date.greg()[getMonth]() + 1; })[length]; })); } me[length] = 365 + greg.LEAP(year); defProp(me, 'il', getset(function() { return me[getMonth](1).il; }, function(il) { me.months.forEach(function(m){ m.il = il; }); })); defProp(me, 'lat', getset(function() { return me[getMonth](1).lat; }, function(lat) { me.months.forEach(function(m){ m.lat = lat; }); })); defProp(me, 'long', getset(function() { return me[getMonth](1).long; }, function(lon) { me.months.forEach(function(m){ m.long = lon; }); })); return me; }; Hebcal.GregYear = GregYear; GregYearProto[isLeapYear] = function() { return this[length] == 366; }; GregYearProto.setCity = HebcalProto.setCity; GregYearProto.setLocation = HebcalProto.setLocation; GregYearProto[next] = function() { return new GregYear(this.year + 1); }; GregYearProto[prev] = function() { return new GregYear(this.year - 1); }; GregYearProto[getMonth] = function(month) { var months = this.months month = typeof month == 'number' ? month : greg.lookupMonthNum(month); if (month > months[length]) { return this[next]()[getMonth](month - months[length]); } return months[month > 0 ? month - 1 : months[length] + month]; }; extend(GregYearProto, { days: HebcalProto.days, map: HebcalProto[map], filter: HebcalProto.filter, addHoliday: HebcalProto.addHoliday, }); /*GregYearProto.days = HebcalProto.days; GregYearProto[map] = HebcalProto[map]; GregYearProto.filter = HebcalProto.filter; GregYearProto.addHoliday = HebcalProto.addHoliday;*/ function GregMonth(month, year) { var me = this; if (typeof month == 'string') { month = greg.lookupMonthNum(month); } if (typeof month != 'number') { throw new TE('month to Hebcal.GregMonth is not a valid type'); } if (typeof year != 'number') { throw new TE('year to Hebcal.GregMonth is not a number'); } me.year = year; me.month = month; me.days = c.range(1, greg.daysInMonth(month, year))[map](function(i){ var d = new HDate(new Date(year, month - 1, i)); defProp(d, '__gregmonth', { configurable: true, writable: true, value: me }); return d; }); me[length] = me.days[length]; me.hebmonths = [ {month: me[getDay]( 1)[getMonth](), year: me[getDay]( 1)[getFullYear]()}, {month: me[getDay](-1)[getMonth](), year: me[getDay](-1)[getFullYear]()} ].filter(function(val, i, arr){ return i === 0 || val.month != arr[0].month; }); defProp(me, 'il', getset(function(){ return me[getDay](1).il; }, function(il){ me.days.forEach(function(d){ d.il = il; }); })); defProp(me, 'lat', getset(function(){ return me[getDay](1).lat; }, function(lat){ me.days.forEach(function(d){ d.lat = lat; }); })); defProp(me, 'long', getset(function(){ return me[getDay](1).long; }, function(lon){ me.days.forEach(function(d){ d.long = lon; }); })); return me; }; Hebcal.GregMonth = GregMonth; GregMonthProto[isLeapYear] = function() { return greg.LEAP(this.year); }; GregMonthProto[prev] = function() { if (this.month === 1) { return this[getYearObject]()[prev]()[getMonth](-1); } else { return this[getYearObject]()[getMonth](this.month - 1); } }; GregMonthProto[next] = function() { return this[getYearObject]()[getMonth](this.month + 1); }; GregMonthProto[getDay] = function(day) { if (day > this.days[length]) { return this[next]()[getDay](day - this.days[length]); } return this.days[day > 0 ? day - 1 : this.days[length] + day]; }; GregMonthProto[getYearObject] = function() { return this.__year || new GregYear(this.year); }; GregMonthProto.getName = function() { return greg.monthNames[this.month]; }; GregMonthProto.setCity = MonthProto.setCity; GregMonthProto.setLocation = MonthProto.setLocation; GregMonthProto[map] = MonthProto[map]; HDateProto.getGregMonthObject = function() { return this.__gregmonth || new GregMonth(this.greg()[getMonth]() + 1, this.greg()[getFullYear]()); }; HDateProto.getGregYearObject = function() { return this.getGregMonthObject()[getYearObject](); }; module.exports = Hebcal;