From 34f02fd1dadae445282c8551c5b6a3a81ea5a026 Mon Sep 17 00:00:00 2001 From: Michael Salaverry Date: Wed, 9 Feb 2022 23:11:02 +0200 Subject: [PATCH] feat: use customizer to more accurately generate calendar data file feat: brand new UI to display upcoming/current calendar events feat: add holidays, shabbat torah readings, and time display to clock --- apps/hebrew_calendar/ChangeLog | 4 +- .../HebrewCalendar-Screenshot.png | Bin 0 -> 3110 bytes apps/hebrew_calendar/README.md | 15 +- apps/hebrew_calendar/app.js | 199 +++++++++-- apps/hebrew_calendar/customizer.html | 46 +++ apps/hebrew_calendar/customizer.mjs | 329 ++++++++++++++++++ apps/hebrew_calendar/hebrewDate.js | 311 ----------------- apps/hebrew_calendar/metadata.json | 16 +- 8 files changed, 569 insertions(+), 351 deletions(-) create mode 100644 apps/hebrew_calendar/HebrewCalendar-Screenshot.png create mode 100644 apps/hebrew_calendar/customizer.html create mode 100644 apps/hebrew_calendar/customizer.mjs delete mode 100644 apps/hebrew_calendar/hebrewDate.js diff --git a/apps/hebrew_calendar/ChangeLog b/apps/hebrew_calendar/ChangeLog index fdd29db66..1fb04080d 100644 --- a/apps/hebrew_calendar/ChangeLog +++ b/apps/hebrew_calendar/ChangeLog @@ -1,4 +1,6 @@ 0.01: New App! 0.02: using TS and rollup to bundle 0.03: bug fixes and support bangle 1 -0.04: removing TS \ No newline at end of file +0.04: removing TS +0.05: major overhaul; now you customize your calendar based on your location for candle lighting times +0.06: bug fixes and improvements \ No newline at end of file diff --git a/apps/hebrew_calendar/HebrewCalendar-Screenshot.png b/apps/hebrew_calendar/HebrewCalendar-Screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..f60314c997b65cbb99a8049aec51d3d17d25e162 GIT binary patch literal 3110 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.png) +Displays the current hebrew calendar date and upcoming holidays alongside a clock + +![](./HebrewCalendar-Screenshot.png) ## Usage -Open the app, and it shows a menu with the date components +Set it up as your clock in the settings ## Features -Shows the hebrew date, month, and year; alongside the gregorian date +- Shows the hebrew date, month, and year; alongside the gregorian date +- Shows when upcoming holidays start +- Shows the gregorian day of week, date, and current time ## Controls -Name the buttons and what they are used for +N/A ## Requests @@ -22,5 +25,5 @@ Michael Salaverry (github.com/barakplasma) ## Creator Michael Salaverry -with help from https://github.com/IonicaBizau/hebrew-date (MIT license) +with help from https://github.com/hebcal/hebcal-es6 (MIT license) which is used to calculate the calendar \ No newline at end of file diff --git a/apps/hebrew_calendar/app.js b/apps/hebrew_calendar/app.js index 399d124f3..3d098c60a 100644 --- a/apps/hebrew_calendar/app.js +++ b/apps/hebrew_calendar/app.js @@ -1,26 +1,181 @@ -g.clear(); +const dayInMS = 86400000; -let now = new Date(); +const DateProvider = { now: () => Date.now() }; -let today = require('hebrewDate').hebrewDate(now); +const Layout = require("Layout"); +const Locale = require("locale"); -var mainmenu = { - "": { - "title": "Hebrew Date" - }, - greg: { - // @ts-ignore - value: require('locale').date(now, 1), - }, - date: { - value: today.date, - }, - month: { - value: today.month_name, - }, - year: { - value: today.year, +let nextEndingEvent; + +function getCurrentEvents() { + const now = DateProvider.now(); + + const current = hebrewCalendar.filter( + (x) => x.startEvent <= now && x.endEvent >= now + ); + + nextEndingEvent = current.reduce((acc, ev) => { + return Math.min(acc, ev.endEvent); + }, Infinity); + + return current.map((event, i) => { + return { + type: "txt", + font: "12x20", + id: "currentEvents" + i, + label: event.desc, + pad: 2, + bgCol: g.theme.bg, + }; + }); +} + +function getUpcomingEvents() { + const now = DateProvider.now(); + + const futureEvents = hebrewCalendar.filter( + (x) => x.startEvent >= now && x.startEvent <= now + dayInMS + ); + + let warning; + let eventsLeft = hebrewCalendar.filter( + (x) => x.startEvent >= now && x.startEvent <= now + dayInMS * 14 + ).length; + + if (eventsLeft < 14) { + warning = { + startEvent: 0, + type: "txt", + font: "4x6", + id: "warning", + label: "only " + eventsLeft + " events left in calendar; update soon", + pad: 2, + bgCol: g.theme.bg, + }; } -}; -// @ts-ignore -E.showMenu(mainmenu); \ No newline at end of file + + return futureEvents + .slice(0, 2) + .map((event, i) => { + return { + startEvent: event.startEvent, + type: "txt", + font: "6x8", + id: "upcomingEvents" + 1, + label: event.desc + " at " + Locale.time(new Date(event.startEvent), 1), + pad: 2, + bgCol: g.theme.bg, + }; + }) + .concat(warning) + .sort(function (a, b) { + return a.startEvent - b.startEvent; + }); +} + +function dateTime() { + return ( + Locale.dow(new Date(), 1) + + " " + + Locale.date(new Date(), 1) + + " " + + Locale.time(new Date(), 1) + ); +} + +function makeLayout() { + return new Layout( + { + type: "v", + c: [ + { + type: "txt", + font: "6x8", + id: "title", + label: "-- Hebrew Calendar Events --", + pad: 2, + bgCol: g.theme.bg2, + }, + { + type: "txt", + font: "6x8", + id: "currently", + label: "Currently", + pad: 2, + bgCol: g.theme.bgH, + }, + ] + .concat(getCurrentEvents()) + .concat([ + { + type: "txt", + font: "6x8", + label: "Upcoming", + id: "upcoming", + pad: 2, + bgCol: g.theme.bgH, + }, + ]) + .concat(getUpcomingEvents()) + .concat([ + { + type: "txt", + font: "Vector14", + id: "time", + label: dateTime(), + pad: 2, + bgCol: undefined, + }, + ]), + }, + { lazy: true } + ); +} +let layout = makeLayout(); +// see also https://www.espruino.com/Bangle.js+Layout#updating-the-screen + +// timeout used to update every minute +let drawTimeout; + +function draw() { + layout.time.label = dateTime(); + layout.render(); + + // schedule a draw for the next minute + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function () { + drawTimeout = undefined; + draw(); + }, 60000 - (DateProvider.now() % 60000)); + console.log("updated time"); +} + +// update time and draw +g.clear(); +Bangle.loadWidgets(); +Bangle.drawWidgets(); +draw(); + +function findNextEvent() { + return hebrewCalendar.find((ev) => { + return ev.startEvent > DateProvider.now(); + }); +} + +function updateCalendar() { + layout.clear(); + layout = makeLayout(); + layout.forgetLazyState(); + layout.render(); + + let nextChange = Math.min( + findNextEvent().startEvent - DateProvider.now() + 5000, + nextEndingEvent - DateProvider.now() + 5000 + ); + setTimeout(updateCalendar, nextChange); + console.log("updated events"); +} + +updateCalendar(); + +Bangle.setUI("clock"); \ No newline at end of file diff --git a/apps/hebrew_calendar/customizer.html b/apps/hebrew_calendar/customizer.html new file mode 100644 index 000000000..bea860e53 --- /dev/null +++ b/apps/hebrew_calendar/customizer.html @@ -0,0 +1,46 @@ + + + + + + + + Hebrew Calendar Customizer + + + + +
+
+
+
Hebrew Calendar Loader
+
+
+
Your location
+
+
+ + + + +
get your latitude and longitude from plus.codes or:
+ + + +
+
+
+ +
+ + +
+ + + \ No newline at end of file diff --git a/apps/hebrew_calendar/customizer.mjs b/apps/hebrew_calendar/customizer.mjs new file mode 100644 index 000000000..06716a63b --- /dev/null +++ b/apps/hebrew_calendar/customizer.mjs @@ -0,0 +1,329 @@ +import { + HebrewCalendar, + HDate, + Location, + Zmanim, +} from "https://cdn.skypack.dev/@hebcal/core@^3?min"; + +function onload(event) { + event.preventDefault(); + const latLon = getLatLonFromForm(); + const events = generateHebCal(latLon); + const calendar = serializeEvents(events); + console.debug(calendar); + globalThis["cal"] = calendar; + loadWatch(calendar); +} + +function loadWatch(json) { + sendCustomizedApp({ + id: "hebrew_calendar", + + storage: [ + { + name: "hebrew_calendar.app.js", + url: "app.js", + // content below is same as app.js except for the first line which customizes the hebrewCalendar object used + content: ` +let hebrewCalendar = ${json}; + +const dayInMS = 86400000; + +const DateProvider = { now: () => Date.now() }; + +const Layout = require("Layout"); +const Locale = require("locale"); + +let nextEndingEvent; + +function getCurrentEvents() { + const now = DateProvider.now(); + + const current = hebrewCalendar.filter( + (x) => x.startEvent <= now && x.endEvent >= now + ); + + nextEndingEvent = current.reduce((acc, ev) => { + return Math.min(acc, ev.endEvent); + }, Infinity); + + return current.map((event, i) => { + return { + type: "txt", + font: "12x20", + id: "currentEvents" + i, + label: event.desc, + pad: 2, + bgCol: g.theme.bg, + }; + }); +} + +function getUpcomingEvents() { + const now = DateProvider.now(); + + const futureEvents = hebrewCalendar.filter( + (x) => x.startEvent >= now && x.startEvent <= now + dayInMS + ); + + let warning; + let eventsLeft = hebrewCalendar.filter( + (x) => x.startEvent >= now && x.startEvent <= now + dayInMS * 14 + ).length; + + if (eventsLeft < 14) { + warning = { + startEvent: 0, + type: "txt", + font: "4x6", + id: "warning", + label: "only " + eventsLeft + " events left in calendar; update soon", + pad: 2, + bgCol: g.theme.bg, + }; + } + + return futureEvents + .slice(0, 2) + .map((event, i) => { + return { + startEvent: event.startEvent, + type: "txt", + font: "6x8", + id: "upcomingEvents" + 1, + label: event.desc + " at " + Locale.time(new Date(event.startEvent), 1), + pad: 2, + bgCol: g.theme.bg, + }; + }) + .concat(warning) + .sort(function (a, b) { + return a.startEvent - b.startEvent; + }); +} + +function dateTime() { + return ( + Locale.dow(new Date(), 1) + + " " + + Locale.date(new Date(), 1) + + " " + + Locale.time(new Date(), 1) + ); +} + +function makeLayout() { + return new Layout( + { + type: "v", + c: [ + { + type: "txt", + font: "6x8", + id: "title", + label: "-- Hebrew Calendar Events --", + pad: 2, + bgCol: g.theme.bg2, + }, + { + type: "txt", + font: "6x8", + id: "currently", + label: "Currently", + pad: 2, + bgCol: g.theme.bgH, + }, + ] + .concat(getCurrentEvents()) + .concat([ + { + type: "txt", + font: "6x8", + label: "Upcoming", + id: "upcoming", + pad: 2, + bgCol: g.theme.bgH, + }, + ]) + .concat(getUpcomingEvents()) + .concat([ + { + type: "txt", + font: "Vector14", + id: "time", + label: dateTime(), + pad: 2, + bgCol: undefined, + }, + ]), + }, + { lazy: true } + ); +} +let layout = makeLayout(); +// see also https://www.espruino.com/Bangle.js+Layout#updating-the-screen + +// timeout used to update every minute +let drawTimeout; + +function draw() { + layout.time.label = dateTime(); + layout.render(); + + // schedule a draw for the next minute + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function () { + drawTimeout = undefined; + draw(); + }, 60000 - (DateProvider.now() % 60000)); + console.log("updated time"); +} + +// update time and draw +g.clear(); +Bangle.loadWidgets(); +Bangle.drawWidgets(); +draw(); + +function findNextEvent() { + return hebrewCalendar.find((ev) => { + return ev.startEvent > DateProvider.now(); + }); +} + +function updateCalendar() { + layout.clear(); + layout = makeLayout(); + layout.forgetLazyState(); + layout.render(); + + let nextChange = Math.min( + findNextEvent().startEvent - DateProvider.now() + 5000, + nextEndingEvent - DateProvider.now() + 5000 + ); + setTimeout(updateCalendar, nextChange); + console.log("updated events"); +} + +updateCalendar(); + +Bangle.setUI("clock"); + `, + }, + ], + }); +} + +document + .querySelector("button[type=submit]") + .addEventListener("click", onload, false); + +document.querySelector("#geoloc")?.addEventListener("click", (event) => { + event.preventDefault(); + navigator.geolocation.getCurrentPosition( + (pos) => { + const { + coords: { latitude, longitude }, + } = pos; + locationElements[0].value = latitude; + locationElements[1].value = longitude; + console.debug(pos); + }, + (err) => { + if (err.PERMISSION_DENIED) { + alert("permission required to use geolocation api; enter manually"); + } + if (err.POSITION_UNAVAILABLE) { + alert("position unavailable; enter manually"); + } + }, + { enableHighAccuracy: false } + ); +}); + +document.querySelector( + "#hDate" +).innerText = `Today is ${new Date().toLocaleDateString()} & ${new HDate().toString()}`; + +const locationElements = [ + document.querySelector("#lat"), + document.querySelector("#lon"), +]; + +function getLatLonFromForm() { + const latLon = locationElements.map((el) => el.value); + if (locationElements.every((x) => x.checkValidity())) { + return latLon; + } else { + console.debug("lat lon invalid error"); + return [0, 0]; + } +} + +function groupBy(arr, fn) { + return arr + .map(typeof fn === "function" ? fn : (val) => val[fn]) + .reduce((acc, val, i) => { + acc[val] = (acc[val] || []).concat(arr[i]); + return acc; + }, {}); +} + +function generateHebCal(latLon) { + const location = new Location( + ...latLon, + document.querySelector("#inIL").checked + ); + + const now = new Date(); + + const options = { + year: now.getFullYear(), + isHebrewYear: false, + candlelighting: true, + location, + addHebrewDates: true, + addHebrewDatesForEvents: true, + sedrot: true, + start: now, + end: new Date(now.getFullYear(), now.getMonth() + 3), + }; + + const events = HebrewCalendar.calendar(options).map((ev) => { + const { desc, eventTime, startEvent, endEvent } = ev; + + const zman = new Zmanim(ev.date, ...latLon.map(Number)); + + let output = { + desc, + startEvent: startEvent?.eventTime?.getTime() || zman.gregEve().getTime(), + endEvent: endEvent?.eventTime?.getTime() || zman.shkiah().getTime(), + }; + + if (eventTime) { + delete output.startEvent; + delete output.endEvent; + output.startEvent = eventTime.getTime(); + output.endEvent = eventTime.getTime() + 60000 * 15; + } + + return output; + }); + + // console.table(events) + + return events.sort((a, b) => { + return a.startEvent - b.startEvent; + }); +} + +function enc(data) { + return btoa(heatshrink.compress(new TextEncoder().encode(data))); +} + +function serializeEvents(events) { + // const splitByGregorianMonth = groupBy(events, (evt) => { + // return new Date(evt.startEvent).getMonth(); + // }); + return JSON.stringify(events); +} diff --git a/apps/hebrew_calendar/hebrewDate.js b/apps/hebrew_calendar/hebrewDate.js deleted file mode 100644 index da0c9cf50..000000000 --- a/apps/hebrew_calendar/hebrewDate.js +++ /dev/null @@ -1,311 +0,0 @@ -/*! - * This script was taked from this page http://www.shamash.org/help/javadate.shtml and ported to Node.js by Ionică Bizău in https://github.com/IonicaBizau/hebrew-date - * - * This script was adapted from C sources written by - * Scott E. Lee, which contain the following copyright notice: - * - * Copyright 1993-1995, Scott E. Lee, all rights reserved. - * Permission granted to use, copy, modify, distribute and sell so long as - * the above copyright and this permission statement are retained in all - * copies. THERE IS NO WARRANTY - USE AT YOUR OWN RISK. - * - * Bill Hastings - * RBI Software Systems - * bhastings@rbi.com - */ -var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; -var GREG_SDN_OFFSET = 32045, DAYS_PER_5_MONTHS = 153, DAYS_PER_4_YEARS = 1461, DAYS_PER_400_YEARS = 146097; -var HALAKIM_PER_HOUR = 1080, HALAKIM_PER_DAY = 25920, HALAKIM_PER_LUNAR_CYCLE = 29 * HALAKIM_PER_DAY + 13753, HALAKIM_PER_METONIC_CYCLE = HALAKIM_PER_LUNAR_CYCLE * (12 * 19 + 7); -var HEB_SDN_OFFSET = 347997, NEW_MOON_OF_CREATION = 31524, NOON = 18 * HALAKIM_PER_HOUR, AM3_11_20 = 9 * HALAKIM_PER_HOUR + 204, AM9_32_43 = 15 * HALAKIM_PER_HOUR + 589; -var SUN = 0, MON = 1, TUES = 2, WED = 3, THUR = 4, FRI = 5, SAT = 6; -function weekdayarr(d0, d1, d2, d3, d4, d5, d6) { - this[0] = d0; - this[1] = d1; - this[2] = d2; - this[3] = d3; - this[4] = d4; - this[5] = d5; - this[6] = d6; -} -function gregmontharr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11) { - this[0] = m0; - this[1] = m1; - this[2] = m2; - this[3] = m3; - this[4] = m4; - this[5] = m5; - this[6] = m6; - this[7] = m7; - this[8] = m8; - this[9] = m9; - this[10] = m10; - this[11] = m11; -} -function hebrewmontharr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13) { - this[0] = m0; - this[1] = m1; - this[2] = m2; - this[3] = m3; - this[4] = m4; - this[5] = m5; - this[6] = m6; - this[7] = m7; - this[8] = m8; - this[9] = m9; - this[10] = m10; - this[11] = m11; - this[12] = m12; - this[13] = m13; -} -function monthsperyeararr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18) { - this[0] = m0; - this[1] = m1; - this[2] = m2; - this[3] = m3; - this[4] = m4; - this[5] = m5; - this[6] = m6; - this[7] = m7; - this[8] = m8; - this[9] = m9; - this[10] = m10; - this[11] = m11; - this[12] = m12; - this[13] = m13; - this[14] = m14; - this[15] = m15; - this[16] = m16; - this[17] = m17; - this[18] = m18; -} -var gWeekday = new weekdayarr("Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "Satur"), gMonth = new gregmontharr("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"), hMonth = new hebrewmontharr("Tishri", "Heshvan", "Kislev", "Tevet", "Shevat", "AdarI", "AdarII", "Nisan", "Iyyar", "Sivan", "Tammuz", "Av", "Elul"), mpy = new monthsperyeararr(12, 12, 13, 12, 12, 13, 12, 13, 12, 12, 13, 12, 12, 13, 12, 12, 13, 12, 13); -/** - * hebrewDate - * Convert the Gregorian dates into Hebrew calendar dates. - * - * @name hebrewDate - * @function - * @param {Date|Number} inputDate The date object (representing the Gregorian date) or the year. - * @return {Object} An object containing: - * - * - `year`: The Hebrew year. - * - `month`: The Hebrew month. - * - `month_name`: The Hebrew month name. - * - `date`: The Hebrew date. - */ -function hebrewDate(inputDateOrYear) { - var inputMonth, inputDate; - var hebrewMonth = 0, hebrewDate = 0, hebrewYear = 0, metonicCycle = 0, metonicYear = 0, moladDay = 0, moladHalakim = 0; - function GregorianToSdn(inputYear, inputMonth, inputDay) { - var year = 0, month = 0, sdn = void 0; - // Make year a positive number - if (inputYear < 0) { - year = inputYear + 4801; - } - else { - year = inputYear + 4800; - } - // Adjust the start of the year - if (inputMonth > 2) { - month = inputMonth - 3; - } - else { - month = inputMonth + 9; - year--; - } - sdn = Math.floor(Math.floor(year / 100) * DAYS_PER_400_YEARS / 4); - sdn += Math.floor(year % 100 * DAYS_PER_4_YEARS / 4); - sdn += Math.floor((month * DAYS_PER_5_MONTHS + 2) / 5); - sdn += inputDay - GREG_SDN_OFFSET; - return sdn; - } - function SdnToHebrew(sdn) { - var tishri1 = 0, tishri1After = 0, yearLength = 0, inputDay = sdn - HEB_SDN_OFFSET; - FindTishriMolad(inputDay); - tishri1 = Tishri1(metonicYear, moladDay, moladHalakim); - if (inputDay >= tishri1) { - // It found Tishri 1 at the start of the year. - hebrewYear = metonicCycle * 19 + metonicYear + 1; - if (inputDay < tishri1 + 59) { - if (inputDay < tishri1 + 30) { - hebrewMonth = 1; - hebrewDate = inputDay - tishri1 + 1; - } - else { - hebrewMonth = 2; - hebrewDate = inputDay - tishri1 - 29; - } - return; - } - // We need the length of the year to figure this out,so find Tishri 1 of the next year. - moladHalakim += HALAKIM_PER_LUNAR_CYCLE * mpy[metonicYear]; - moladDay += Math.floor(moladHalakim / HALAKIM_PER_DAY); - moladHalakim = moladHalakim % HALAKIM_PER_DAY; - tishri1After = Tishri1((metonicYear + 1) % 19, moladDay, moladHalakim); - } - else { - // It found Tishri 1 at the end of the year. - hebrewYear = metonicCycle * 19 + metonicYear; - if (inputDay >= tishri1 - 177) { - // It is one of the last 6 months of the year. - if (inputDay > tishri1 - 30) { - hebrewMonth = 13; - hebrewDate = inputDay - tishri1 + 30; - } - else if (inputDay > tishri1 - 60) { - hebrewMonth = 12; - hebrewDate = inputDay - tishri1 + 60; - } - else if (inputDay > tishri1 - 89) { - hebrewMonth = 11; - hebrewDate = inputDay - tishri1 + 89; - } - else if (inputDay > tishri1 - 119) { - hebrewMonth = 10; - hebrewDate = inputDay - tishri1 + 119; - } - else if (inputDay > tishri1 - 148) { - hebrewMonth = 9; - hebrewDate = inputDay - tishri1 + 148; - } - else { - hebrewMonth = 8; - hebrewDate = inputDay - tishri1 + 178; - } - return; - } - else { - if (mpy[(hebrewYear - 1) % 19] == 13) { - hebrewMonth = 7; - hebrewDate = inputDay - tishri1 + 207; - if (hebrewDate > 0) - return; - hebrewMonth--; - hebrewDate += 30; - if (hebrewDate > 0) - return; - hebrewMonth--; - hebrewDate += 30; - } - else { - hebrewMonth = 6; - hebrewDate = inputDay - tishri1 + 207; - if (hebrewDate > 0) - return; - hebrewMonth--; - hebrewDate += 30; - } - if (hebrewDate > 0) - return; - hebrewMonth--; - hebrewDate += 29; - if (hebrewDate > 0) - return; - // We need the length of the year to figure this out,so find Tishri 1 of this year. - tishri1After = tishri1; - FindTishriMolad(moladDay - 365); - tishri1 = Tishri1(metonicYear, moladDay, moladHalakim); - } - } - yearLength = tishri1After - tishri1; - moladDay = inputDay - tishri1 - 29; - if (yearLength == 355 || yearLength == 385) { - // Heshvan has 30 days - if (moladDay <= 30) { - hebrewMonth = 2; - hebrewDate = moladDay; - return; - } - moladDay -= 30; - } - else { - // Heshvan has 29 days - if (moladDay <= 29) { - hebrewMonth = 2; - hebrewDate = moladDay; - return; - } - moladDay -= 29; - } - // It has to be Kislev. - hebrewMonth = 3; - hebrewDate = moladDay; - } - function FindTishriMolad(inputDay) { - // Estimate the metonic cycle number. Note that this may be an under - // estimate because there are 6939.6896 days in a metonic cycle not - // 6940,but it will never be an over estimate. The loop below will - // correct for any error in this estimate. - metonicCycle = Math.floor((inputDay + 310) / 6940); - // Calculate the time of the starting molad for this metonic cycle. - MoladOfMetonicCycle(); - // If the above was an under estimate,increment the cycle number until - // the correct one is found. For modern dates this loop is about 98.6% - // likely to not execute,even once,because the above estimate is - // really quite close. - while (moladDay < inputDay - 6940 + 310) { - metonicCycle++; - moladHalakim += HALAKIM_PER_METONIC_CYCLE; - moladDay += Math.floor(moladHalakim / HALAKIM_PER_DAY); - moladHalakim = moladHalakim % HALAKIM_PER_DAY; - } - // Find the molad of Tishri closest to this date. - for (metonicYear = 0; metonicYear < 18; metonicYear++) { - if (moladDay > inputDay - 74) - break; - moladHalakim += HALAKIM_PER_LUNAR_CYCLE * mpy[metonicYear]; - moladDay += Math.floor(moladHalakim / HALAKIM_PER_DAY); - moladHalakim = moladHalakim % HALAKIM_PER_DAY; - } - } - function MoladOfMetonicCycle() { - var r1 = void 0, r2 = void 0, d1 = void 0, d2 = void 0; - // Start with the time of the first molad after creation. - r1 = NEW_MOON_OF_CREATION; - // Calculate gMetonicCycle * HALAKIM_PER_METONIC_CYCLE. The upper 32 - // bits of the result will be in r2 and the lower 16 bits will be in r1. - r1 += metonicCycle * (HALAKIM_PER_METONIC_CYCLE & 0xFFFF); - r2 = r1 >> 16; - r2 += metonicCycle * (HALAKIM_PER_METONIC_CYCLE >> 16 & 0xFFFF); - // Calculate r2r1 / HALAKIM_PER_DAY. The remainder will be in r1,the - // upper 16 bits of the quotient will be in d2 and the lower 16 bits - // will be in d1. - d2 = Math.floor(r2 / HALAKIM_PER_DAY); - r2 -= d2 * HALAKIM_PER_DAY; - r1 = r2 << 16 | r1 & 0xFFFF; - d1 = Math.floor(r1 / HALAKIM_PER_DAY); - r1 -= d1 * HALAKIM_PER_DAY; - moladDay = d2 << 16 | d1; - moladHalakim = r1; - } - function Tishri1(metonicYear, moladDay, moladHalakim) { - var tishri1 = moladDay, dow = tishri1 % 7, leapYear = metonicYear == 2 || metonicYear == 5 || metonicYear == 7 || metonicYear == 10 || metonicYear == 13 || metonicYear == 16 || metonicYear == 18, lastWasLeapYear = metonicYear == 3 || metonicYear == 6 || metonicYear == 8 || metonicYear == 11 || metonicYear == 14 || metonicYear == 17 || metonicYear == 0; - // Apply rules 2,3 and 4 - if (moladHalakim >= NOON || !leapYear && dow == TUES && moladHalakim >= AM3_11_20 || lastWasLeapYear && dow == MON && moladHalakim >= AM9_32_43) { - tishri1++; - dow++; - if (dow == 7) - dow = 0; - } - // Apply rule 1 after the others because it can cause an additional delay of one day. - if (dow == WED || dow == FRI || dow == SUN) { - tishri1++; - } - return tishri1; - } - var inputYear = inputDateOrYear; - if ((typeof inputYear === "undefined" ? "undefined" : _typeof(inputYear)) === "object") { - inputMonth = inputDateOrYear.getMonth() + 1; - inputDate = inputDateOrYear.getDate(); - inputYear = inputDateOrYear.getFullYear(); - } - SdnToHebrew(GregorianToSdn(inputYear, inputMonth, inputDate)); - return { - year: hebrewYear, - month: hebrewMonth, - date: hebrewDate, - month_name: hMonth[hebrewMonth - 1] - }; -} - -exports.hebrewDate = hebrewDate; diff --git a/apps/hebrew_calendar/metadata.json b/apps/hebrew_calendar/metadata.json index a2b7932b6..dc10a99f2 100644 --- a/apps/hebrew_calendar/metadata.json +++ b/apps/hebrew_calendar/metadata.json @@ -2,25 +2,19 @@ "id": "hebrew_calendar", "name": "Hebrew Calendar", "shortName": "HebCal", - "version": "0.04", - "description": "lists the date according to the hebrew calendar", + "version": "0.06", + "description": "lists the date & holidays according to the hebrew calendar", "icon": "app.png", "allow_emulator": false, - "tags": "tool,locale", + "tags": "clocks,tools", + "custom": "customizer.html", "supports": [ "BANGLEJS", "BANGLEJS2" ], + "type": "clock", "readme": "README.md", "storage": [ - { - "name": "hebrew_calendar.app.js", - "url": "app.js" - }, - { - "name": "hebrewDate", - "url": "hebrewDate.js" - }, { "name": "hebrew_calendar.img", "url": "app-icon.js",