hebcal-fixed
Version:
An easy to use, richly-featured, perpetual Jewish Calendar API
382 lines (347 loc) • 9.85 kB
JavaScript
/*
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 gematriya = require('gematriya');
var charCodeAt = 'charCodeAt';
var months = exports.months = {
NISAN : 1,
IYYAR : 2,
SIVAN : 3,
TAMUZ : 4,
AV : 5,
ELUL : 6,
TISHREI : 7,
CHESHVAN: 8,
KISLEV : 9,
TEVET : 10,
SHVAT : 11,
ADAR_I : 12,
ADAR_II : 13
};
var monthNames = [
["", 0, ""],
["Nisan", 0, "ניסן"],
["Iyyar", 0, "אייר"],
["Sivan", 0, "סיון"],
["Tamuz", 0, "תמוז"],
["Av", 0, "אב"],
["Elul", 0, "אלול"],
["Tishrei", 0, "תשרי"],
["Cheshvan", 0, "חשון"],
["Kislev", 0, "כסלו"],
["Tevet", 0, "טבת"],
["Sh'vat", 0, "שבט"]
];
exports.monthNames = [
monthNames.concat([["Adar", 0, "אדר"],["Nisan", 0, "ניסן"]]),
monthNames.concat([["Adar 1", 0, "אדר א'"],["Adar 2", 0, "אדר ב'"],["Nisan", 0, "ניסן"]])
];
exports.days = {
SUN: 0,
MON: 1,
TUE: 2,
WED: 3,
THU: 4,
FRI: 5,
SAT: 6
};
exports.LANG = function(str, opts){
return opts == 'h' && str[2] || (opts == 'a' && str[1] || str[0]);
};
function LEAP(x) {
return (1 + x * 7) % 19 < 7;
}
exports.LEAP = LEAP;
exports.MONTH_CNT = function(x) {
return 12 + LEAP(x); // boolean is cast to 1 or 0
};
exports.daysInMonth = function(month, year) {
return 30 - (month == months.IYYAR ||
month == months.TAMUZ ||
month == months.ELUL ||
month == months.TEVET ||
month == months.ADAR_II ||
(month == months.ADAR_I && !LEAP(year)) ||
(month == months.CHESHVAN && !lngChesh(year)) ||
(month == months.KISLEV && shrtKis(year)));
};
exports.monthNum = function(month) {
return typeof month === 'number' ? month :
month[charCodeAt](0) >= 1488 && month[charCodeAt](0) <= 1514 && /('|")/.test(month) ? gematriya(month) :
month[charCodeAt](0) >= 48 && month[charCodeAt](0) <= 57 /* number */ ? parseInt(month, 10) : monthFromName(month);
};
exports.dayYearNum = function(str) {
return typeof str === 'number' ? str :
str[charCodeAt](0) >= 1488 && str[charCodeAt](0) <= 1514 ? gematriya(str, true) : parseInt(str, 10);
};
/* Days from sunday prior to start of Hebrew calendar to mean
conjunction of Tishrei in Hebrew YEAR
*/
function hebElapsedDays(hYear){
// borrowed from original JS
var m_elapsed = 235 * Math.floor((hYear - 1) / 19) +
12 * ((hYear - 1) % 19) +
Math.floor(((((hYear - 1) % 19) * 7) + 1) / 19);
var p_elapsed = 204 + (793 * (m_elapsed % 1080));
var h_elapsed = 5 + (12 * m_elapsed) +
793 * Math.floor(m_elapsed / 1080) +
Math.floor(p_elapsed / 1080);
var parts = (p_elapsed % 1080) + 1080 * (h_elapsed % 24);
var day = 1 + 29 * m_elapsed + Math.floor(h_elapsed / 24);
var alt_day = day + ((parts >= 19440) ||
((2 == (day % 7)) && (parts >= 9924) && !(LEAP (hYear))) ||
((1 == (day % 7)) && (parts >= 16789) && LEAP (hYear - 1)));
return alt_day + ((alt_day % 7) === 0 ||
(alt_day % 7) == 3 ||
(alt_day % 7) == 5);
}
exports.hebElapsedDays = hebElapsedDays;
/* Number of days in the hebrew YEAR */
function daysInYear(year)
{
return hebElapsedDays(year + 1) - hebElapsedDays(year);
}
exports.daysInYear = daysInYear;
/* true if Cheshvan is long in Hebrew YEAR */
function lngChesh(year) {
return (daysInYear(year) % 10) == 5;
}
exports.lngChesh = lngChesh;
/* true if Kislev is short in Hebrew YEAR */
function shrtKis(year) {
return (daysInYear(year) % 10) == 3;
}
exports.shrtKis = shrtKis;
function monthFromName(c) {
/*
the Hebrew months are unique to their second letter
N Nisan (November?)
I Iyyar
E Elul
C Cheshvan
K Kislev
1 1Adar
2 2Adar
Si Sh Sivan, Shvat
Ta Ti Te Tamuz, Tishrei, Tevet
Av Ad Av, Adar
אב אד אי אל אב אדר אייר אלול
ח חשון
ט טבת
כ כסלב
נ ניסן
ס סיון
ש שבט
תמ תש תמוז תשרי
*/
switch (c.toLowerCase()[0]) {
case 'n':
case 'נ':
return (c.toLowerCase()[1] == 'o') ? /* this catches "november" */
0 : months.NISAN;
case 'i':
return months.IYYAR;
case 'e':
return months.ELUL;
case 'c':
case 'ח':
return months.CHESHVAN;
case 'k':
case 'כ':
return months.KISLEV;
case 's':
switch (c.toLowerCase()[1]) {
case 'i':
return months.SIVAN;
case 'h':
return months.SHVAT;
default:
return 0;
}
case 't':
switch (c.toLowerCase()[1]) {
case 'a':
return months.TAMUZ;
case 'i':
return months.TISHREI;
case 'e':
return months.TEVET;
}
break;
case 'a':
switch (c.toLowerCase()[1]) {
case 'v':
return months.AV;
case 'd':
if (/(1|[^i]i|a|א)$/i.test(c)) {
return months.ADAR_I;
}
return months.ADAR_II; // else assume sheini
}
break;
case 'ס':
return months.SIVAN;
case 'ט':
return months.TEVET;
case 'ש':
return months.SHVAT;
case 'א':
switch (c.toLowerCase()[1]) {
case 'ב':
return months.AV;
case 'ד':
if (/(1|[^i]i|a|א)$/i.test(c)) {
return months.ADAR_I;
}
return months.ADAR_II; // else assume sheini
case 'י':
return months.IYYAR;
case 'ל':
return months.ELUL;
}
break;
case 'ת':
switch (c.toLowerCase()[1]) {
case 'מ':
return months.TAMUZ;
case 'ש':
return months.TISHREI;
}
break;
}
return 0;
};
exports.monthFromName = monthFromName;
/* Note: Applying this function to d+6 gives us the DAYNAME on or after an
* absolute day d. Similarly, applying it to d+3 gives the DAYNAME nearest to
* absolute date d, applying it to d-1 gives the DAYNAME previous to absolute
* date d, and applying it to d+7 gives the DAYNAME following absolute date d.
**/
exports.dayOnOrBefore = function(day_of_week, absdate) {
return absdate - ((absdate - day_of_week) % 7);
};
exports.map = function(self, fun, thisp) {
// originally written for http://github.com/Scimonster/localbrowse
if (self === null || typeof fun != 'function') {
throw new TypeError();
}
var t = Object(self);
var res = {};
for (var i in t) {
if (t.hasOwnProperty(i)) {
res[i] = fun.call(thisp, t[i], i, t);
}
}
if (Array.isArray(self) || typeof self == 'string') { // came as an array, return an array
var arr = [];
for (i in res) {
arr[Number(i)] = res[i];
}
res = filter(arr, true); // for...in isn't guaranteed to give any meaningful order
if (typeof self == 'string') {
res = res.join('');
}
}
return res;
};
function filter(self, fun, thisp) {
if (self === null) {
throw new TypeError('self is null');
}
switch (typeof fun) {
case 'function':
break; // do nothing
case 'string':
case 'number':
return self[fun]; // str/num is just the property
case 'boolean':
// boolean shortcuts to filter only truthy/falsy values
if (fun) {
fun = function (v) {
return v;
};
} else {
fun = function (v) {
return !v;
};
}
break;
case 'object':
var funOrig = fun; // save it
if (fun instanceof RegExp) { // test the val against the regex
fun = function (v) {
return funOrig.test(v);
};
break;
} else if (Array.isArray(fun)) { // keep these keys
fun = function (v, k) {
return funOrig.indexOf(k) > -1;
};
break;
}
default:
throw new TypeError('fun is not a supported type');
}
var res = {};
var t = Object(self);
for (var i in t) {
if (t.hasOwnProperty(i)) {
var val = t[i]; // in case fun mutates it
if (fun.call(thisp, val, i, t)) {
// define property on res in the same manner as it was originally defined
var props = Object.getOwnPropertyDescriptor(t, i);
props.value = val;
Object.defineProperty(res, i, props);
}
}
}
if (Array.isArray(self) || typeof self == 'string') { // came as an array, return an array
var arr = [];
for (i in res) {
arr[Number(i)] = res[i];
}
res = arr.filter(function (v) {
return v;
}); // for...in isn't guaranteed to give any meaningful order
// can't use c.filter(arr,true) here because that would infitely recurse
if (typeof self == 'string') {
res = res.join('');
}
}
return res;
}
exports.filter = filter;
exports.range = function(start, end, step) {
step = step || 1;
if (step < 0) {
step = 0 - step;
}
var arr = [], i = start;
if (start < end) {
for (; i <= end; i += step) {
arr.push(i);
}
} else {
for (; i >= end; i -= step) {
arr.push(i);
}
}
return arr;
};