diff --git a/moonphase@techi-freki/CHANGELOG.md b/moonphase@techi-freki/CHANGELOG.md new file mode 100644 index 00000000000..9b3a8e597a3 --- /dev/null +++ b/moonphase@techi-freki/CHANGELOG.md @@ -0,0 +1,72 @@ +# Changelog + +## v1.0.5 +- Fixes issue [#6015](https://github.com/linuxmint/cinnamon-spices-applets/issues/6015) + +## v1.0.4 ([#6020](https://github.com/linuxmint/cinnamon-spices-applets/pull/6020)) +- Updates Russian translation + +## v1.0.3 ([#5997](https://github.com/linuxmint/cinnamon-spices-applets/pull/5997)) +- Updates French translations + +## v1.0.2 ([#5983](https://github.com/linuxmint/cinnamon-spices-applets/pull/5983)) +- Updates Portuguese translations + +## v1.0.1 ([#5982](https://github.com/linuxmint/cinnamon-spices-applets/pull/5982)) +- Updates Spanish translations + +## v1.0.0 ([#5961](https://github.com/linuxmint/cinnamon-spices-applets/pull/5961)) +- Feature add: Added geolocation features +- Feature add: Added moon rise, set, and transit times on left click +- Feature add: Added current moon phase information on left click +- Swaps out [SunCalc](https://github.com/mourner/suncalc) for [SunCalc3](https://github.com/hypnos3/suncalc3) for geolocation features +- Heavily refactors and cleans up code base +- Cleans up versioning (major/minor/patch) +- Adds LICENSE for icons +- Updates .pot file for new translations + +## v0.2.12 ([#5819](https://github.com/linuxmint/cinnamon-spices-applets/pull/5819)) +- Adds Dutch translations + +## v0.2.11 ([#5628](https://github.com/linuxmint/cinnamon-spices-applets/pull/5628)) +- Fixes location bug ([#5610](https://github.com/linuxmint/cinnamon-spices-applets/issues/5610)) + +## v0.2.10 ([#5306](https://github.com/linuxmint/cinnamon-spices-applets/pull/5306)) +- Adds Danish translations + +## v0.2.9 ([#5226](https://github.com/linuxmint/cinnamon-spices-applets/pull/5226)) +- Adds Hungarian translations + +## v0.2.8 ([#5223](https://github.com/linuxmint/cinnamon-spices-applets/pull/5223)) +- Fixes day 28 bug ([#5222](https://github.com/linuxmint/cinnamon-spices-applets/issues/5222)) + +## v0.2.7 ([#5087](https://github.com/linuxmint/cinnamon-spices-applets/issues/5087)) +- Bug fix ([#5072](https://github.com/linuxmint/cinnamon-spices-applets/issues/5072)) + +## v0.2.6 ([#5086](https://github.com/linuxmint/cinnamon-spices-applets/pull/5086)) +- Updating version + +## v0.2.5 ([#5085](https://github.com/linuxmint/cinnamon-spices-applets/pull/5085)) +- Adds Russian translations + +## v0.2.4 ([5080](https://github.com/linuxmint/cinnamon-spices-applets/pull/5080)) +- Reviews Italian translations + +## v0.2.3 ([#5078](https://github.com/linuxmint/cinnamon-spices-applets/pull/5078)) +- Adds Italian translations + +## v0.2.2 ([#5073](https://github.com/linuxmint/cinnamon-spices-applets/pull/5073)) +- Adds Spanish translations + +## v0.2.1 ([#5071](https://github.com/linuxmint/cinnamon-spices-applets/pull/5071)) +- Updates .pot file and French translations + +## v0.2.0 ([#5058](https://github.com/linuxmint/cinnamon-spices-applets/pull/5058)) +- Feature add: Adds percentage display to label and tooltip + +## v0.1.0 ([#5054](https://github.com/linuxmint/cinnamon-spices-applets/pull/5054)) +- Feature add: Translations +- Adds French translations + +## v0.0.1 ([#5052](https://github.com/linuxmint/cinnamon-spices-applets/pull/5052)) +- Initial release \ No newline at end of file diff --git a/moonphase@techi-freki/README.md b/moonphase@techi-freki/README.md index 35630502016..89955a9fae9 100644 --- a/moonphase@techi-freki/README.md +++ b/moonphase@techi-freki/README.md @@ -1,7 +1,9 @@ # Moon Phase Cinnamon Applet -A Cinnamon applet that displays the current moon phase. +A Cinnamon applet that displays up-to-date moon related information. ## Installation -Move extract and move moonphase@techi-freki to the ~./local/share/cinnamon/applets directory. +Extract and move moonphase@techi-freki to the ~/.local/share/cinnamon/applets directory. + +Review Changelog for latest updates \ No newline at end of file diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/icons/LICENSE b/moonphase@techi-freki/files/moonphase@techi-freki/icons/LICENSE new file mode 100644 index 00000000000..b22be90b3d9 --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/icons/LICENSE @@ -0,0 +1,8 @@ +Notice: Icons used are named "weather-icons" available from here: https://github.com/erikflowers/weather-icons + +Licence: OFL Licence +https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL + +The warning-outline-svgrepo-com icon is under the MIT license and is available here: https://www.svgrepo.com/svg/326459/warning-outline + +no-moonrise-symbolic and no-moonset-symbolic where created by Techi-Freki and released under the OFL license \ No newline at end of file diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-down-left-symbolic.svg b/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-down-left-symbolic.svg new file mode 100644 index 00000000000..85db8547343 --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-down-left-symbolic.svg @@ -0,0 +1,39 @@ + + + + + + diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-down-right-symbolic.svg b/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-down-right-symbolic.svg new file mode 100644 index 00000000000..8885a5b0d60 --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-down-right-symbolic.svg @@ -0,0 +1,39 @@ + + + + + + diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-down-symbolic.svg b/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-down-symbolic.svg new file mode 100644 index 00000000000..613c7695514 --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-down-symbolic.svg @@ -0,0 +1,39 @@ + + + + + + diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-left-symbolic.svg b/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-left-symbolic.svg new file mode 100644 index 00000000000..f4e99b43ff3 --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-left-symbolic.svg @@ -0,0 +1,39 @@ + + + + + + diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-right-symbolic.svg b/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-right-symbolic.svg new file mode 100644 index 00000000000..f63af484ea2 --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-right-symbolic.svg @@ -0,0 +1,18 @@ + + + + + + diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-up-left-symbolic.svg b/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-up-left-symbolic.svg new file mode 100644 index 00000000000..0d6c76a3392 --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-up-left-symbolic.svg @@ -0,0 +1,39 @@ + + + + + + diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-up-right-symbolic.svg b/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-up-right-symbolic.svg new file mode 100644 index 00000000000..1f3b7d53661 --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-up-right-symbolic.svg @@ -0,0 +1,39 @@ + + + + + + diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-up-symbolic.svg b/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-up-symbolic.svg new file mode 100644 index 00000000000..668a15255a6 --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/icons/direction-up-symbolic.svg @@ -0,0 +1,9 @@ + + + + + diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/icons/night-clear-symbolic.svg b/moonphase@techi-freki/files/moonphase@techi-freki/icons/night-clear-symbolic.svg new file mode 100644 index 00000000000..cb456ce9703 --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/icons/night-clear-symbolic.svg @@ -0,0 +1,47 @@ + +image/svg+xml + + diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/icons/no-moonrise-symbolic.svg b/moonphase@techi-freki/files/moonphase@techi-freki/icons/no-moonrise-symbolic.svg new file mode 100644 index 00000000000..038dbc2b845 --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/icons/no-moonrise-symbolic.svg @@ -0,0 +1,47 @@ + + + + + + diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/icons/no-moonset-symbolic.svg b/moonphase@techi-freki/files/moonphase@techi-freki/icons/no-moonset-symbolic.svg new file mode 100644 index 00000000000..4a7388200bc --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/icons/no-moonset-symbolic.svg @@ -0,0 +1,58 @@ + + + + + + diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/icons/warning-outline-svgrepo-com.svg b/moonphase@techi-freki/files/moonphase@techi-freki/icons/warning-outline-svgrepo-com.svg new file mode 100644 index 00000000000..7944bbfa8e8 --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/icons/warning-outline-svgrepo-com.svg @@ -0,0 +1,2 @@ + +ionicons-v5-r \ No newline at end of file diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/js/calc.js b/moonphase@techi-freki/files/moonphase@techi-freki/js/calc.js new file mode 100644 index 00000000000..81d8a2f36bb --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/js/calc.js @@ -0,0 +1,33 @@ +const SunCalc = require('./lib/suncalc3.js'); + +class Calculator { + constructor(lat, lon, date = new Date()) { + this.moonDate = date; + this.observerLat = Number(lat); + this.observerLon = Number(lon); + this.moonData = SunCalc.getMoonData(this.moonDate, this.observerLat, this.observerLon); + this.times = SunCalc.getMoonTimes(this.moonDate, this.observerLat, this.observerLon); + this.transit = SunCalc.moonTransit(this.times.rise, this.times.set, this.observerLat, this.observerLon).main; + } + + getMoonIllumination() { + return this.moonData.illumination; + } + + getRiseSetTimes() { + const rise = isNaN(this.times.rise) ? null : this.times.rise; + const set = isNaN(this.times.set) ? null : this.times.set; + + return { + rise, + set, + alwaysUp: this.times.alwaysUp, + alwaysDown: this.times.alwaysDown, + highestPosition: this.times.highest, + transit: this.transit, + riseAzimuth: SunCalc.getMoonPosition(this.times.rise, this.observerLat, this.observerLon).azimuthDegrees, + transitAzimuth: SunCalc.getMoonPosition(this.transit, this.observerLat, this.observerLon).azimuthDegrees, + setAzimuth: SunCalc.getMoonPosition(this.times.set, this.observerLat, this.observerLon).azimuthDegrees + } + } +} \ No newline at end of file diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/js/compass.js b/moonphase@techi-freki/files/moonphase@techi-freki/js/compass.js new file mode 100644 index 00000000000..3fde24958a1 --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/js/compass.js @@ -0,0 +1,39 @@ +const TOLERANCE_MET = 'tolerance met'; +const { Direction } = require('./js/direction'); +const { Translator } = require('./js/translator'); + +class Compass { + constructor(app) { + this.app = app; + this.translator = new Translator(this.app.metadata.uuid); + this.compassDegrees = [ + new Direction(22.5, this.translator.translate('North'), this.app.moon.iconSet.directionUp), + new Direction(67.5, this.translator.translate('North East'), this.app.moon.iconSet.directionUpRight), + new Direction(112.5, this.translator.translate('East'), this.app.moon.iconSet.directionRight), + new Direction(157.5, this.translator.translate('South East'), this.app.moon.iconSet.directionDownRight), + new Direction(202.5, this.translator.translate('South'), this.app.moon.iconSet.directionDown), + new Direction(247.5, this.translator.translate('South West'), this.app.moon.iconSet.directionDownLeft), + new Direction(292.5, this.translator.translate('West'), this.app.moon.iconSet.directionLeft), + new Direction(337.5, this.translator.translate('North West'), this.app.moon.iconSet.directionUpLeft) + ]; + } + + getCardinalDirection(degree) { + try { + this.compassDegrees.forEach((direction) => { + const fixedDegree = Math.floor(degree * 100); + const fixedTolerance = Math.floor(direction.tolerance * 100); + + if (fixedDegree <= fixedTolerance) { + this.direction = direction; + throw new Error(TOLERANCE_MET); + } + }); + } catch (e) { + if (e.message !== TOLERANCE_MET) { + throw e; + } + } + return this.direction; + } +} \ No newline at end of file diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/js/config.js b/moonphase@techi-freki/files/moonphase@techi-freki/js/config.js new file mode 100644 index 00000000000..62c26f1f3e3 --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/js/config.js @@ -0,0 +1,54 @@ +const { Moon } = require('./js/moon'); + +class Config { + constructor(app) { + this.app = app; + } + + bindSettings() { + this.app.settings.bind('useAltIcons', 'useAltIcons', this.updateSettings.bind(this.app)); + + this.app.settings.bind('showTooltip', 'showTooltip', this.updateSettings.bind(this.app)); + this.app.settings.bind('showPhaseTooltip', 'showPhaseTooltip', this.updateSettings.bind(this.app)); + this.app.settings.bind('showPercentageTooltip', 'showPercentageTooltip', this.updateSettings.bind(this.app)); + + this.app.settings.bind('showPhaseLabel', 'showPhaseLabel', this.updateSettings.bind(this.app)); + this.app.settings.bind('showNameLabel', 'showNameLabel', this.updateSettings.bind(this.app)); + this.app.settings.bind('showPercentageLabel', 'showPercentageLabel', this.updateSettings.bind(this.app)); + + this.app.settings.bind('enableGeolocation', 'enableGeolocation', this.updateSettings.bind(this.app)); + this.app.settings.bind('latitude', 'latitude', this.updateSettings.bind(this.app)); + this.app.settings.bind('longitude', 'longitude', this.updateSettings.bind(this.app)); + + this.app.settings.bind('enablePopup', 'enablePopup', this.updateSettings.bind(this.app)); + this.app.settings.bind('showCurrentPhaseInfo', 'showCurrentPhaseInfo', this.updateSettings.bind(this.app)); + this.app.settings.bind('showRiseSet', 'showRiseSet', this.updateSettings.bind(this.app)); + + this.app.settings.bind('updateInterval', 'updateInterval', this.updateSettings.bind(this.app)); + } + + updateSettings() { + this.app.useAltIcons = this.app.settings.getValue('useAltIcons'); + + this.app.showTooltip = this.app.settings.getValue('showTooltip'); + this.app.showPhaseTooltip = this.app.settings.getValue('showPhaseTooltip'); + this.app.showPercentageTooltip = this.app.settings.getValue('showPercentageTooltip'); + + this.app.showPhaseLabel = this.app.settings.getValue('showPhaseLabel'); + this.app.showNameLabel = this.app.settings.getValue('showNameLabel'); + this.app.showPercentageLabel = this.app.settings.getValue('showPercentageLabel'); + + this.app.enableGeolocation = this.app.settings.getValue('enableGeolocation'); + this.app.latitude = this.app.settings.getValue('latitude'); + this.app.longitude = this.app.settings.getValue('longitude'); + + this.app.enablePopup = this.app.settings.getValue('enablePopup'); + this.app.showCurrentPhaseInfo = this.app.settings.getValue('showCurrentPhaseInfo'); + this.app.showRiseSet = this.app.settings.getValue('showRiseSet'); + + this.app.updateInterval = this.app.settings.getValue('updateInterval'); + + this.app.buildPopupMenu(); + this.app.updateApplet(); + } +} \ No newline at end of file diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/js/direction.js b/moonphase@techi-freki/files/moonphase@techi-freki/js/direction.js new file mode 100644 index 00000000000..695cb98fff8 --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/js/direction.js @@ -0,0 +1,7 @@ +class Direction { + constructor(tolerance, directionName, iconName) { + this.tolerance = tolerance; + this.name = directionName; + this.icon_name = iconName; + } +} \ No newline at end of file diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/js/iconSet.js b/moonphase@techi-freki/files/moonphase@techi-freki/js/iconSet.js index 229cd0b5d2a..5b7d05abfa5 100644 --- a/moonphase@techi-freki/files/moonphase@techi-freki/js/iconSet.js +++ b/moonphase@techi-freki/files/moonphase@techi-freki/js/iconSet.js @@ -58,11 +58,23 @@ class IconSet { this.waningCrescent5 = waningCrescent5; this.waningCrescent6 = waningCrescent6; this.moonRise = `moonrise-symbolic`; + this.noMoonRise = `no-moonrise-symbolic`; this.moonSet = `moonset-symbolic`; + this.noMoonSet = `no-moonset-symbolic`; + this.nightClear = `night-clear-symbolic`; + this.directionUp = `direction-up-symbolic`; + this.directionUpRight = `direction-up-right-symbolic`; + this.directionRight = `direction-right-symbolic`; + this.directionDownRight = `direction-down-right-symbolic`; + this.directionDown = `direction-down-symbolic`; + this.directionDownLeft = `direction-down-left-symbolic`; + this.directionLeft = `direction-left-symbolic`; + this.directionUpLeft = `direction-up-left-symbolic`; + this.warning = `warning-outline-svgrepo-com.svg`; this.lunarEclipse = `lunar-eclipse-symbolic`; } - getSet() { + getPhaseIcons() { return [ this.newMoon, this.waxingCrescent1, diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/js/menu.js b/moonphase@techi-freki/files/moonphase@techi-freki/js/menu.js new file mode 100644 index 00000000000..70b90c531a4 --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/js/menu.js @@ -0,0 +1,23 @@ +const { HeaderUi } = require('./js/ui/headerUi'); +const { CurrentPhaseUi } = require('./js/ui/currentPhaseUi'); +const { RiseSetUi } = require('./js/ui/riseSetUi'); + +class Menu { + constructor(app) { + this.app = app; + } + + buildMenu() { + const headerUi = new HeaderUi(this.app); + const currentPhaseUi = new CurrentPhaseUi(this.app); + const riseSetUi = new RiseSetUi(this.app); + + headerUi.rebuild(); + currentPhaseUi.rebuild(); + riseSetUi.rebuild(); + + this.app.menu.addActor(headerUi.actor); + this.app.menu.addActor(currentPhaseUi.actor); + this.app.menu.addActor(riseSetUi.actor); + } +} \ No newline at end of file diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/js/moon.js b/moonphase@techi-freki/files/moonphase@techi-freki/js/moon.js index 091a61eb5e1..b9fb156ceaa 100644 --- a/moonphase@techi-freki/files/moonphase@techi-freki/js/moon.js +++ b/moonphase@techi-freki/files/moonphase@techi-freki/js/moon.js @@ -1,59 +1,33 @@ -const SunCalc = require('./lib/suncalc'); const { DefaultIconSet, AltIconSet } = require('./js/iconSet'); - -const UUID = "moonphase@techi-freki"; -const { get_home_dir } = imports.gi.GLib; -const Gettext = imports.gettext; -Gettext.bindtextdomain(UUID, get_home_dir() + "/.local/share/locale") +const { Calculator } = require('./js/calc'); +const { Translator } = require('./js/translator'); class Moon { - constructor(currentDate, useAltIcons, showNameLabel, showPercentageLabel, showNameTooltip, showPercentageTooltip) { - this.currentDate = currentDate; - this.age = this._getAge(); - this.currentPhaseIcon = this._getCurrentPhaseIcon(useAltIcons); - this.currentPhaseName = this._getCurrentPhaseName(showNameLabel, showPercentageLabel); - this.currentTooltip = this._getCurrentPhaseName(showNameTooltip, showPercentageTooltip); - } - // translation - _(str) { - let translated = Gettext.dgettext(UUID, str); - if (translated !== str) - return translated; - return str; + constructor(app) { + this.app = app; + this.calc = new Calculator(app.latitude, app.longitude); + this.translator = new Translator(this.app.metadata.uuid); + this.illumination = this.calc.getMoonIllumination(); + this.age = Math.trunc(this.illumination.phaseValue * 28); + this.iconSet = app.useAltIcons ? new AltIconSet() : new DefaultIconSet(); + this.riseSetTimes = this.calc.getRiseSetTimes(); + this.currentPhaseIcon = this.iconSet.getPhaseIcons()[this.age]; + this.currentPhaseName = this._getCurrentPhaseName(); } - _getAge() { - return SunCalc.getMoonIllumination(this.currentDate).phase; - } - _getCurrentPhaseIcon(useAltIcons = false) { - const iconSet = useAltIcons ? - new AltIconSet().getSet() : - new DefaultIconSet().getSet(); - - const age = Math.trunc(this.age * 28); - - return iconSet[age]; - } - _getCurrentPhaseName(showName = true, showPercentage = true) { + _getCurrentPhaseName() { let name = ""; - const age = Math.trunc(this.age * 28) / 28; - - // method to convert to percentage without rounding to keep precision - const toPercentage = (n, fixed) => `${n * 100}`.match(new RegExp(`^-?\\d+(?:\.\\d{0,${fixed}})?`))[0] + "%"; - const percent = toPercentage(SunCalc.getMoonIllumination(this.currentDate).fraction, 2); + const age = this.age / 28; - if (age === 0) name = this._("New Moon"); - else if (age < 0.25) name = this._("Waxing Crescent"); - else if (age === 0.25) name = this._("First Quarter"); - else if (age < 0.5) name = this._("Waxing Gibbous"); - else if (age === 0.5) name = this._("Full Moon"); - else if (age < 0.75) name = this._("Waning Gibbous"); - else if (age === 0.75) name = this._("Last Quarter"); - else if (age <= 1) name = this._("Waning Crescent") - else name = this._("New Moon"); + if (age === 0) name = this.translator.translate('New Moon'); + else if (age < 0.25) name = this.translator.translate('Waxing Crescent'); + else if (age === 0.25) name = this.translator.translate('First Quarter'); + else if (age < 0.5) name = this.translator.translate('Waxing Gibbous'); + else if (age === 0.5) name = this.translator.translate('Full Moon'); + else if (age < 0.75) name = this.translator.translate('Waning Gibbous'); + else if (age === 0.75) name = this.translator.translate('Last Quarter'); + else if (age <= 1) name = this.translator.translate('Waning Crescent'); + else name = this.translator.translate('New Moon'); - if (showName && showPercentage) return name + " (" + percent + ")"; - if (showName) return name; - if (showPercentage) return percent; - return _("Moon Phase"); + return name; } -} +} \ No newline at end of file diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/js/moonPhase.js b/moonphase@techi-freki/files/moonphase@techi-freki/js/moonPhase.js index c49880efca2..8018c617c62 100644 --- a/moonphase@techi-freki/files/moonphase@techi-freki/js/moonPhase.js +++ b/moonphase@techi-freki/files/moonphase@techi-freki/js/moonPhase.js @@ -1,8 +1,11 @@ const Applet = imports.ui.applet; +const { PopupMenuManager } = imports.ui.popupMenu; const Mainloop = imports.mainloop; const Settings = imports.ui.settings; const Lang = imports.lang; const { Moon } = require('./js/moon'); +const { Menu } = require('./js/menu'); +const { Config } = require('./js/config'); class MoonPhase extends Applet.TextIconApplet { constructor(metadata, orientation, panel_height, instance_id) { @@ -10,62 +13,82 @@ class MoonPhase extends Applet.TextIconApplet { // setting defaults this.metadata = metadata; - this.settings = new Settings.AppletSettings(this, metadata.uuid, instance_id); + this.settings = new Settings.AppletSettings(this, this.metadata.uuid, instance_id); + this.orientation = orientation; + this.config = new Config(this); + this.config.bindSettings(); + this.moon = new Moon(this); - this.settings.bind('useAltIcons', 'useAltIcons', this._onSettingsChanged.bind(this)); - this.settings.bind('showTooltip', 'showTooltip', this._onSettingsChanged.bind(this)); - this.settings.bind('showNameTooltip', 'showNameTooltip', this._onSettingsChanged.bind(this)); - this.settings.bind('showPercentageTooltip', 'showPercentageTooltip', this._onSettingsChanged.bind(this)); - this.settings.bind('showPhaseLabel', 'showPhaseLabel', this._onSettingsChanged.bind(this)); - this.settings.bind('showNameLabel', 'showNameLabel', this._onSettingsChanged.bind(this)); - this.settings.bind('showPercentageLabel', 'showPercentageLabel', this._onSettingsChanged.bind(this)); - - this.settings.bind('updateInterval', 'updateInterval', this._onSettingsChanged.bind(this)); - - this._updateApplet(); + this.updateApplet(); } on_applet_removed_from_panel() { if (this.updateLoopId) { Mainloop.source_remove(this.updateLoopId); } + this.settings.finalize(); } - _onSettingsChanged(value) { - this.useAltIcons = this.settings.getValue('useAltIcons'); - this.showTooltip = this.settings.getValue('showTooltip'); - this.showNameTooltip = this.settings.getValue('showNameTooltip'); - this.showPercentageTooltip = this.settings.getValue('showPercentageTooltip'); - this.showPhaseLabel = this.settings.getValue('showPhaseLabel'); - this.showNameLabel = this.settings.getValue('showNameLabel'); - this.showPercentageLabel = this.settings.getValue('showPercentageLabel'); + on_applet_clicked() { + if (!this.enablePopup) return; + if (!this.showCurrentPhaseInfo && !this.showRiseSet) return; - this.updateInterval = this.settings.getValue('updateInterval'); + if (this.popupOpen) { + this.menu.toggle(); + this.popupOpen = false; + } else { + this.menuManager = new PopupMenuManager(this); + this.menu = new Applet.AppletPopupMenu(this, this.orientation); + this.menuManager.addMenu(this.menu); + this.buildPopupMenu(); + this.menu.toggle(); + this.popupOpen = true; + } + } - this._updateApplet(); + buildPopupMenu() { + const menu = new Menu(this); + menu.buildMenu(); } - _updateApplet() { + updateApplet() { if (this.updateLoopId) { Mainloop.source_remove(this.updateLoopId); } - const moon = new Moon(new Date(), this.useAltIcons, this.showNameLabel, this.showPercentageLabel, this.showNameTooltip, this.showPercentageTooltip); - - this.set_applet_icon_symbolic_name(moon.currentPhaseIcon); + this.moon = new Moon(this); + const phaseLabel = this._createPhaseLabel(this.showNameLabel, this.showPercentageLabel); + const phaseTooltip = this._createPhaseTooltip(this.showPhaseTooltip, this.showPercentageTooltip); + this.set_applet_icon_symbolic_name(this.moon.currentPhaseIcon); if (this.showTooltip) { - this.set_applet_tooltip(moon.currentTooltip); + this.set_applet_tooltip(phaseTooltip); } else { this.set_applet_tooltip(''); } if (this.showPhaseLabel) { - this.set_applet_label(moon.currentPhaseName); + this.set_applet_label(phaseLabel); } else { this.set_applet_label(''); } - this.updateLoopId = Mainloop.timeout_add((this.updateInterval * 1000), Lang.bind(this, this._updateApplet)); + this.updateLoopId = Mainloop.timeout_add((this.updateInterval * 1000), Lang.bind(this, this.updateApplet)); + } + + _createPhaseLabel(showNameLabel, showPercentageLabel) { + return this._createAppletDisplay(showNameLabel, showPercentageLabel); + } + + _createPhaseTooltip(showPhaseTooltip, showPercentageTooltip) { + return this._createAppletDisplay(showPhaseTooltip, showPercentageTooltip); + } + + _createAppletDisplay(showName, showPercentage) { + const percent = Math.floor(this.moon.illumination.fraction * 100 * 100) / 100; + if (showName && showPercentage) return `${ this.moon.currentPhaseName } (${ percent }%)`; + if (showName) return `${ this.moon.currentPhaseName }`; + if (showPercentage) return `${ percent }%`; + return this.metadata.name; } -} +} \ No newline at end of file diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/js/translator.js b/moonphase@techi-freki/files/moonphase@techi-freki/js/translator.js new file mode 100644 index 00000000000..10fe89b045f --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/js/translator.js @@ -0,0 +1,15 @@ +const { get_home_dir } = imports.gi.GLib; +const GetText = imports.gettext; + +class Translator { + constructor(uuid) { + this.uuid = uuid; + } + + translate(str) { + GetText.bindtextdomain(this.uuid, `${get_home_dir()}/.local/share/locale`); + let translated = GetText.dgettext(this.uuid, str); + if (translated !== str) return translated; + return str; + } +} \ No newline at end of file diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/currentPhaseUi.js b/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/currentPhaseUi.js new file mode 100644 index 00000000000..6ccd1b8af4d --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/currentPhaseUi.js @@ -0,0 +1,32 @@ +const { paddingTop5Strict, margin5, textAlignLeft } = require('./js/ui/styles'); +const { Align, BoxLayout } = imports.gi.St; +const { ActorAlign } = imports.gi.Clutter; +const { UiElement } = require('./js/ui/elements/uiElement'); +const { IconTextElementGenerator } = require('./js/ui/elements/iconTextElementGenerator'); +const { Translator } = require('./js/translator'); + +class CurrentPhaseUi extends UiElement { + constructor (app) { + super(app); + this.actor = new BoxLayout({ + style_class: margin5, + x_align: ActorAlign.CENTER, + y_align: Align.MIDDLE + }); + this.elementGenerator = new IconTextElementGenerator(); + this.translator = new Translator(this.app.metadata.uuid); + } + + create() { + if (!this.app.showCurrentPhaseInfo) return; + const illumLabel = this.elementGenerator.generateLabel(`${ Math.floor(this.app.moon.illumination.fraction * 100 * 100) / 100 }% ` + this.translator.translate('illumination')); + const illumLayout = this.elementGenerator.generateLayout([illumLabel], false); + + const phaseLabel = this.elementGenerator.generateLabel(this.app.moon.currentPhaseName); + const dateLabel = this.elementGenerator.generateLabel(new Date().toLocaleDateString(), paddingTop5Strict); + const timeLabel = this.elementGenerator.generateLabel(new Date().toLocaleTimeString()); + const infoLayout = this.elementGenerator.generateLayout([phaseLabel, illumLayout, dateLabel, timeLabel], true, `${ margin5 };${ textAlignLeft }`); + + this.actor.add_actor(this.elementGenerator.generateElement(this.app.moon.currentPhaseIcon, 80, [infoLayout], margin5)); + } +} \ No newline at end of file diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/elements/iconTextElementGenerator.js b/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/elements/iconTextElementGenerator.js new file mode 100644 index 00000000000..f9397be9cb2 --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/elements/iconTextElementGenerator.js @@ -0,0 +1,42 @@ +const { BoxLayout, Icon, IconType, Label } = imports.gi.St; + +class IconTextElementGenerator { + constructor() {} + + generateElement(iconName, iconSize, layouts, rootStyleClass = '', iconStyleClass = '') { + const icon = this.generateIcon(iconName, iconSize); + const root = new BoxLayout({ style_class: rootStyleClass }); + const iconParent = new BoxLayout({ style_class: iconStyleClass }); + + iconParent.add(icon); + root.add(iconParent); + + layouts.forEach((layout) => { + root.add(layout); + }); + + return root; + } + + generateLabel(text, styleClass = '') { + return new Label({ text: text, style_class: styleClass }); + } + + generateIcon(iconName, iconSize) { + return new Icon({ + icon_name: iconName, + icon_size: iconSize, + icon_type: IconType.SYMBOLIC + }); + } + + generateLayout(labels, vertical, styleClass = '') { + const parent = new BoxLayout({ vertical: vertical, style_class: styleClass }); + + labels.forEach((label) => { + parent.add(label); + }); + + return parent; + } +} \ No newline at end of file diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/elements/noRiseSetElement.js b/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/elements/noRiseSetElement.js new file mode 100644 index 00000000000..772fcae51fa --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/elements/noRiseSetElement.js @@ -0,0 +1,63 @@ +const { margin5 } = require('./js/ui/styles'); +const { IconTextElementGenerator } = require('./js/ui/elements/iconTextElementGenerator'); +const { UiElement } = require('./js/ui/elements/uiElement'); +const { RiseSetElement } = require('./js/ui/elements/riseSetElement'); +const { Calculator } = require('./js/calc'); +const { Translator } = require('./js/translator'); + +class NoRiseSetElement extends UiElement { + constructor(app) { + super(app); + const calc = new Calculator(this.app.latitude, this.app.longitude, this._addDays(new Date(), 1)); + + this.elementGenerator = new IconTextElementGenerator(); + this.label = ''; + this.iconName = ''; + this.iconSize = 0; + this.moonRise = true; + this.tomorrowsRiseSet = calc.getRiseSetTimes(); + this.translator = new Translator(app.metadata.uuid); + } + + create() { + const icon = this.elementGenerator.generateIcon(this.iconName, this.iconSize); + const label = this.elementGenerator.generateLabel(this.label, margin5); + const layout = this.elementGenerator.generateLayout([icon, label], true, margin5); + const rootLayout = this.elementGenerator.generateLayout([ + layout, + this.moonRise + ? this._createRiseElement() + : this._createSetElement() + ], false, margin5); + + this.actor.add_actor(rootLayout); + } + + _addDays(date, days) { + return new Date(date).setDate(date.getDate() + days); + } + + _createRiseElement() { + const riseElement = new RiseSetElement(this.app); + riseElement.iconName = this.app.moon.iconSet.moonRise; + riseElement.iconSize = 64; + riseElement.header = this.translator.translate('Next Moonrise'); + riseElement.dateObject = this.tomorrowsRiseSet.rise; + riseElement.angle = this.tomorrowsRiseSet.riseAzimuth; + riseElement.create(); + + return riseElement.actor; + } + + _createSetElement() { + const setElement = new RiseSetElement(this.app); + setElement.iconName = this.app.moon.iconSet.moonSet; + setElement.iconSize = 64; + setElement.header = this.translator.translate('Next Moonset'); + setElement.dateObject = this.tomorrowsRiseSet.set; + setElement.angle = this.tomorrowsRiseSet.setAzimuth; + setElement.create(); + + return setElement.actor; + } +} \ No newline at end of file diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/elements/riseSetElement.js b/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/elements/riseSetElement.js new file mode 100644 index 00000000000..b4989770592 --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/elements/riseSetElement.js @@ -0,0 +1,36 @@ +const { marginBottom5, margin5 } = require('./js/ui/styles'); +const { UiElement } = require('./js/ui/elements/uiElement'); +const { IconTextElementGenerator } = require('./js/ui/elements/iconTextElementGenerator'); +const { Compass } = require('./js/compass'); + +class RiseSetElement extends UiElement { + constructor(app) { + super(app); + this.compass = new Compass(this.app); + this.elementGenerator = new IconTextElementGenerator(); + this.iconName = null; + this.iconSize = 0; + this.header = null; + this.dateObject = null; + this.angle = 0; + this.vertical = false; + } + + create() { + const direction = this.compass.getCardinalDirection(this.angle); + + const headerLabel = this.elementGenerator.generateLabel(this.header, marginBottom5); + const dateLabel = this.elementGenerator.generateLabel(this.dateObject.toLocaleDateString()); + const timeLabel = this.elementGenerator.generateLabel(this.dateObject.toLocaleTimeString(), marginBottom5); + const datetimeLayout = this.elementGenerator.generateLayout([headerLabel, dateLabel, timeLabel], true); + + const directionNameLabel = this.elementGenerator.generateLabel(direction.name); + const directionArrowIcon = this.elementGenerator.generateIcon(direction.icon_name, 18); + const directionAngleLabel = this.elementGenerator.generateLabel(`(${Math.floor(this.angle)}°)`); + const directionLayout = this.elementGenerator.generateLayout([directionNameLabel, directionArrowIcon, directionAngleLabel], false); + + const infoLayout = this.elementGenerator.generateLayout([datetimeLayout, directionLayout], true); + + this.actor.add_actor(this.elementGenerator.generateElement(this.iconName, this.iconSize, [infoLayout], margin5, margin5)); + } +} \ No newline at end of file diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/elements/uiElement.js b/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/elements/uiElement.js new file mode 100644 index 00000000000..fcc6632374e --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/elements/uiElement.js @@ -0,0 +1,21 @@ +const { BoxLayout } = imports.gi.St; + +class UiElement { + constructor(app) { + this.app = app; + this.actor = new BoxLayout(); + } + + destroy() { + this.actor.destroy_all_children(); + } + + create() { + throw new Error('Must implement create abstract method'); + } + + rebuild() { + this.destroy(); + this.create(); + } +} \ No newline at end of file diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/headerUi.js b/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/headerUi.js new file mode 100644 index 00000000000..6ad3607e389 --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/headerUi.js @@ -0,0 +1,23 @@ +const { marginBottom5 } = require('./js/ui/styles'); +const { BoxLayout } = imports.gi.St; +const { ActorAlign } = imports.gi.Clutter; +const { UiElement } = require('./js/ui/elements/uiElement'); +const { IconTextElementGenerator } = require('./js/ui/elements/iconTextElementGenerator'); +const { Translator } = require('./js/translator'); + +class HeaderUi extends UiElement { + constructor(app) { + super(app); + this.actor = new BoxLayout({ + x_align: ActorAlign.CENTER, + style_class: marginBottom5 + }); + this.elementGenerator = new IconTextElementGenerator(); + this.translator = new Translator(this.app.metadata.uuid); + } + + create() { + const headerLabel = this.elementGenerator.generateLabel(`${ this.translator.translate(this.app.metadata.name) } v${ this.app.metadata.version }`); + this.actor.add_actor(headerLabel); + } +} \ No newline at end of file diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/riseSetUi.js b/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/riseSetUi.js new file mode 100644 index 00000000000..ae379a3a368 --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/riseSetUi.js @@ -0,0 +1,101 @@ +const { Translator } = require('./js/translator'); +const { IconTextElementGenerator } = require('./js/ui/elements/iconTextElementGenerator'); +const { margin5 } = require('./js/ui/styles'); +const { UiElement } = require('./js/ui/elements/uiElement'); +const { RiseSetElement } = require('./js/ui/elements/riseSetElement'); +const { NoRiseSetElement } = require('./js/ui/elements/noRiseSetElement'); +const { Align, BoxLayout, Label } = imports.gi.St; +const { ActorAlign } = imports.gi.Clutter + +class RiseSetUi extends UiElement { + constructor(app) { + super(app); + this.actor = new BoxLayout({ + style_class: margin5, + x_align: ActorAlign.CENTER, + y_align: Align.MIDDLE + }); + this.alwaysUp = this.app.moon.riseSetTimes.alwaysUp; + this.alwaysDown = this.app.moon.riseSetTimes.alwaysDown; + this.elementGenerator = new IconTextElementGenerator(); + this.translator = new Translator(this.app.metadata.uuid); + } + + create() { + if (!this.app.showRiseSet) return; + if (this.alwaysUp || this.alwaysDown) { + this._createNoRiseSetLayout(); + } else { + this._createStandardLayout(); + } + } + + _createNoRiseSetLayout() { + if (this.alwaysUp) { + this._createNoSetLayout(); + } else if (this.alwaysDown) { + this._createNoRiseLayout(); + } + } + + _createNoRiseLayout() { + const noRiseElement = new NoRiseSetElement(this.app); + noRiseElement.iconName = this.app.moon.iconSet.noMoonRise; + noRiseElement.iconSize = 64; + noRiseElement.label = this.translator.translate('The moon is always down and will not rise today'); + noRiseElement.create(); + + this.actor.add_actor(noRiseElement.actor); + } + + _createNoSetLayout() { + const noSetElement = new NoRiseSetElement(this.app); + noSetElement.iconName = this.app.moon.iconSet.noMoonSet; + noSetElement.iconSize = 64; + noSetElement.label = this.translator.translate('The moon is always up and will not set today'); + noSetElement.moonRise = false; + noSetElement.create(); + + this.actor.add_actor(noSetElement.actor); + } + + _createStandardLayout() { + const riseElement = new RiseSetElement(this.app); + riseElement.iconName = this.app.moon.iconSet.moonRise; + riseElement.iconSize = 64; + riseElement.header = this.translator.translate('Moonrise'); + riseElement.dateObject = this.app.moon.riseSetTimes.rise; + riseElement.angle = this.app.moon.riseSetTimes.riseAzimuth; + + const transitElement = new RiseSetElement(this.app); + transitElement.iconName = this.app.moon.iconSet.nightClear; + transitElement.iconSize = 48; + transitElement.header = this.translator.translate('Lunar Noon'); + transitElement.dateObject = this.app.moon.riseSetTimes.transit; + transitElement.angle = this.app.moon.riseSetTimes.transitAzimuth; + + const setElement = new RiseSetElement(this.app); + setElement.iconName = this.app.moon.iconSet.moonSet; + setElement.iconSize = 64; + setElement.header = this.translator.translate('Moonset'); + setElement.dateObject = this.app.moon.riseSetTimes.set; + setElement.angle = this.app.moon.riseSetTimes.setAzimuth; + + setElement.create(); + transitElement.create(); + riseElement.create(); + + const elements = [riseElement, transitElement, setElement]; + const orderedElements = this._orderElementsByDateAsc(elements); + + orderedElements.forEach((element) => { + this.actor.add_actor(element.actor); + }); + } + + _orderElementsByDateAsc(elements) { + return elements.sort((a, b) => { + return a.dateObject - b.dateObject; + }); + } +} \ No newline at end of file diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/styles.js b/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/styles.js new file mode 100644 index 00000000000..fac1174faa0 --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/js/ui/styles.js @@ -0,0 +1,20 @@ +// padding styles +const paddingTop3 = 'padding-top-3'; +const paddingTop5Strict = 'padding-top-5-strict'; +const padding15 = 'padding-15'; + +// margin styles +const margin5 = 'margin-5'; +const marginBottom5 = 'margin-bottom-5'; +const marginBottom15 = 'margin-bottom-15'; +const marginTop5 = 'margin-top-5'; + +// font styles +const font20 = 'font-20'; +const font15 = 'font-15'; +const font10 = 'font-10'; + +// alignment styles +const alignLeft = 'align-left'; +const alignCenter = 'align-center'; +const alignRight = 'align-right'; \ No newline at end of file diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/lib/suncalc.js b/moonphase@techi-freki/files/moonphase@techi-freki/lib/suncalc.js deleted file mode 100644 index c9ca56d205e..00000000000 --- a/moonphase@techi-freki/files/moonphase@techi-freki/lib/suncalc.js +++ /dev/null @@ -1,317 +0,0 @@ -/* - (c) 2011-2015, Vladimir Agafonkin - SunCalc is a JavaScript library for calculating sun/moon position and light phases. - https://github.com/mourner/suncalc -*/ - -(function () { 'use strict'; - -// shortcuts for easier to read formulas - -var PI = Math.PI, - sin = Math.sin, - cos = Math.cos, - tan = Math.tan, - asin = Math.asin, - atan = Math.atan2, - acos = Math.acos, - rad = PI / 180; - -// sun calculations are based on http://aa.quae.nl/en/reken/zonpositie.html formulas - - -// date/time constants and conversions - -var dayMs = 1000 * 60 * 60 * 24, - J1970 = 2440588, - J2000 = 2451545; - -function toJulian(date) { return date.valueOf() / dayMs - 0.5 + J1970; } -function fromJulian(j) { return new Date((j + 0.5 - J1970) * dayMs); } -function toDays(date) { return toJulian(date) - J2000; } - - -// general calculations for position - -var e = rad * 23.4397; // obliquity of the Earth - -function rightAscension(l, b) { return atan(sin(l) * cos(e) - tan(b) * sin(e), cos(l)); } -function declination(l, b) { return asin(sin(b) * cos(e) + cos(b) * sin(e) * sin(l)); } - -function azimuth(H, phi, dec) { return atan(sin(H), cos(H) * sin(phi) - tan(dec) * cos(phi)); } -function altitude(H, phi, dec) { return asin(sin(phi) * sin(dec) + cos(phi) * cos(dec) * cos(H)); } - -function siderealTime(d, lw) { return rad * (280.16 + 360.9856235 * d) - lw; } - -function astroRefraction(h) { - if (h < 0) // the following formula works for positive altitudes only. - h = 0; // if h = -0.08901179 a div/0 would occur. - - // formula 16.4 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. - // 1.02 / tan(h + 10.26 / (h + 5.10)) h in degrees, result in arc minutes -> converted to rad: - return 0.0002967 / Math.tan(h + 0.00312536 / (h + 0.08901179)); -} - -// general sun calculations - -function solarMeanAnomaly(d) { return rad * (357.5291 + 0.98560028 * d); } - -function eclipticLongitude(M) { - - var C = rad * (1.9148 * sin(M) + 0.02 * sin(2 * M) + 0.0003 * sin(3 * M)), // equation of center - P = rad * 102.9372; // perihelion of the Earth - - return M + C + P + PI; -} - -function sunCoords(d) { - - var M = solarMeanAnomaly(d), - L = eclipticLongitude(M); - - return { - dec: declination(L, 0), - ra: rightAscension(L, 0) - }; -} - - -var SunCalc = {}; - - -// calculates sun position for a given date and latitude/longitude - -SunCalc.getPosition = function (date, lat, lng) { - - var lw = rad * -lng, - phi = rad * lat, - d = toDays(date), - - c = sunCoords(d), - H = siderealTime(d, lw) - c.ra; - - return { - azimuth: azimuth(H, phi, c.dec), - altitude: altitude(H, phi, c.dec) - }; -}; - - -// sun times configuration (angle, morning name, evening name) - -var times = SunCalc.times = [ - [-0.833, 'sunrise', 'sunset' ], - [ -0.3, 'sunriseEnd', 'sunsetStart' ], - [ -6, 'dawn', 'dusk' ], - [ -12, 'nauticalDawn', 'nauticalDusk'], - [ -18, 'nightEnd', 'night' ], - [ 6, 'goldenHourEnd', 'goldenHour' ] -]; - -// adds a custom time to the times config - -SunCalc.addTime = function (angle, riseName, setName) { - times.push([angle, riseName, setName]); -}; - - -// calculations for sun times - -var J0 = 0.0009; - -function julianCycle(d, lw) { return Math.round(d - J0 - lw / (2 * PI)); } - -function approxTransit(Ht, lw, n) { return J0 + (Ht + lw) / (2 * PI) + n; } -function solarTransitJ(ds, M, L) { return J2000 + ds + 0.0053 * sin(M) - 0.0069 * sin(2 * L); } - -function hourAngle(h, phi, d) { return acos((sin(h) - sin(phi) * sin(d)) / (cos(phi) * cos(d))); } -function observerAngle(height) { return -2.076 * Math.sqrt(height) / 60; } - -// returns set time for the given sun altitude -function getSetJ(h, lw, phi, dec, n, M, L) { - - var w = hourAngle(h, phi, dec), - a = approxTransit(w, lw, n); - return solarTransitJ(a, M, L); -} - - -// calculates sun times for a given date, latitude/longitude, and, optionally, -// the observer height (in meters) relative to the horizon - -SunCalc.getTimes = function (date, lat, lng, height) { - - height = height || 0; - - var lw = rad * -lng, - phi = rad * lat, - - dh = observerAngle(height), - - d = toDays(date), - n = julianCycle(d, lw), - ds = approxTransit(0, lw, n), - - M = solarMeanAnomaly(ds), - L = eclipticLongitude(M), - dec = declination(L, 0), - - Jnoon = solarTransitJ(ds, M, L), - - i, len, time, h0, Jset, Jrise; - - - var result = { - solarNoon: fromJulian(Jnoon), - nadir: fromJulian(Jnoon - 0.5) - }; - - for (i = 0, len = times.length; i < len; i += 1) { - time = times[i]; - h0 = (time[0] + dh) * rad; - - Jset = getSetJ(h0, lw, phi, dec, n, M, L); - Jrise = Jnoon - (Jset - Jnoon); - - result[time[1]] = fromJulian(Jrise); - result[time[2]] = fromJulian(Jset); - } - - return result; -}; - - -// moon calculations, based on http://aa.quae.nl/en/reken/hemelpositie.html formulas - -function moonCoords(d) { // geocentric ecliptic coordinates of the moon - - var L = rad * (218.316 + 13.176396 * d), // ecliptic longitude - M = rad * (134.963 + 13.064993 * d), // mean anomaly - F = rad * (93.272 + 13.229350 * d), // mean distance - - l = L + rad * 6.289 * sin(M), // longitude - b = rad * 5.128 * sin(F), // latitude - dt = 385001 - 20905 * cos(M); // distance to the moon in km - - return { - ra: rightAscension(l, b), - dec: declination(l, b), - dist: dt - }; -} - -SunCalc.getMoonPosition = function (date, lat, lng) { - - var lw = rad * -lng, - phi = rad * lat, - d = toDays(date), - - c = moonCoords(d), - H = siderealTime(d, lw) - c.ra, - h = altitude(H, phi, c.dec), - // formula 14.1 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. - pa = atan(sin(H), tan(phi) * cos(c.dec) - sin(c.dec) * cos(H)); - - h = h + astroRefraction(h); // altitude correction for refraction - - return { - azimuth: azimuth(H, phi, c.dec), - altitude: h, - distance: c.dist, - parallacticAngle: pa - }; -}; - - -// calculations for illumination parameters of the moon, -// based on http://idlastro.gsfc.nasa.gov/ftp/pro/astro/mphase.pro formulas and -// Chapter 48 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. - -SunCalc.getMoonIllumination = function (date) { - - var d = toDays(date || new Date()), - s = sunCoords(d), - m = moonCoords(d), - - sdist = 149598000, // distance from Earth to Sun in km - - phi = acos(sin(s.dec) * sin(m.dec) + cos(s.dec) * cos(m.dec) * cos(s.ra - m.ra)), - inc = atan(sdist * sin(phi), m.dist - sdist * cos(phi)), - angle = atan(cos(s.dec) * sin(s.ra - m.ra), sin(s.dec) * cos(m.dec) - - cos(s.dec) * sin(m.dec) * cos(s.ra - m.ra)); - - return { - fraction: (1 + cos(inc)) / 2, - phase: 0.5 + 0.5 * inc * (angle < 0 ? -1 : 1) / Math.PI, - angle: angle - }; -}; - - -function hoursLater(date, h) { - return new Date(date.valueOf() + h * dayMs / 24); -} - -// calculations for moon rise/set times are based on http://www.stargazing.net/kepler/moonrise.html article - -SunCalc.getMoonTimes = function (date, lat, lng, inUTC) { - var t = new Date(date); - if (inUTC) t.setUTCHours(0, 0, 0, 0); - else t.setHours(0, 0, 0, 0); - - var hc = 0.133 * rad, - h0 = SunCalc.getMoonPosition(t, lat, lng).altitude - hc, - h1, h2, rise, set, a, b, xe, ye, d, roots, x1, x2, dx; - - // go in 2-hour chunks, each time seeing if a 3-point quadratic curve crosses zero (which means rise or set) - for (var i = 1; i <= 24; i += 2) { - h1 = SunCalc.getMoonPosition(hoursLater(t, i), lat, lng).altitude - hc; - h2 = SunCalc.getMoonPosition(hoursLater(t, i + 1), lat, lng).altitude - hc; - - a = (h0 + h2) / 2 - h1; - b = (h2 - h0) / 2; - xe = -b / (2 * a); - ye = (a * xe + b) * xe + h1; - d = b * b - 4 * a * h1; - roots = 0; - - if (d >= 0) { - dx = Math.sqrt(d) / (Math.abs(a) * 2); - x1 = xe - dx; - x2 = xe + dx; - if (Math.abs(x1) <= 1) roots++; - if (Math.abs(x2) <= 1) roots++; - if (x1 < -1) x1 = x2; - } - - if (roots === 1) { - if (h0 < 0) rise = i + x1; - else set = i + x1; - - } else if (roots === 2) { - rise = i + (ye < 0 ? x2 : x1); - set = i + (ye < 0 ? x1 : x2); - } - - if (rise && set) break; - - h0 = h2; - } - - var result = {}; - - if (rise) result.rise = hoursLater(t, rise); - if (set) result.set = hoursLater(t, set); - - if (!rise && !set) result[ye > 0 ? 'alwaysUp' : 'alwaysDown'] = true; - - return result; -}; - - -// export as Node module / AMD module / browser variable -if (typeof exports === 'object' && typeof module !== 'undefined') module.exports = SunCalc; -else if (typeof define === 'function' && define.amd) define(SunCalc); -else window.SunCalc = SunCalc; - -}()); diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/lib/suncalc3.js b/moonphase@techi-freki/files/moonphase@techi-freki/lib/suncalc3.js new file mode 100644 index 00000000000..6ccf03e8ff3 --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/lib/suncalc3.js @@ -0,0 +1,1241 @@ +// @ts-check +/* + (c) 2011-2015, Vladimir Agafonkin + SunCalc is a JavaScript library for calculating sun/moon position and light phases. + https://github.com/mourner/suncalc + + Reworked and enhanced by Robert Gester + Additional Copyright (c) 2022 Robert Gester + https://github.com/hypnos3/suncalc3 +*/ + +/** + * @typedef {Object} ISunTimeDef + * @property {string} name - The Name of the time + * @property {Date} value - Date object with the calculated sun-time + * @property {number} ts - The time as timestamp + * @property {number} pos - The position of the sun on the time + * @property {number} [elevation] - Angle of the sun on the time (except for solarNoon / nadir) + * @property {number} julian - The time as Julian calendar + * @property {boolean} valid - indicates if the time is valid or not + * @property {boolean} [deprecated] - indicates if the time is a deprecated time name + * @property {string} [nameOrg] - if it is a deprecated name, the original property name + * @property {number} [posOrg] - if it is a deprecated name, the original position + */ + +/** + * @typedef {Object} ISunTimeSingle + * @property {ISunTimeDef} rise - sun-time for sun rise + * @property {ISunTimeDef} set - sun-time for sun set + * @property {string} [error] - string of an error message if an error occurs + */ + +/** + * @typedef {Object} ISunTimeList + * @property {ISunTimeDef} solarNoon - The sun-time for the solar noon (sun is in the highest position) + * @property {ISunTimeDef} nadir - The sun-time for nadir (darkest moment of the night, sun is in the lowest position) + * @property {ISunTimeDef} goldenHourDawnStart - The sun-time for morning golden hour (soft light, best time for photography) + * @property {ISunTimeDef} goldenHourDawnEnd - The sun-time for morning golden hour (soft light, best time for photography) + * @property {ISunTimeDef} goldenHourDuskStart - The sun-time for evening golden hour starts + * @property {ISunTimeDef} goldenHourDuskEnd - The sun-time for evening golden hour starts + * @property {ISunTimeDef} sunriseStart - The sun-time for sunrise starts (top edge of the sun appears on the horizon) + * @property {ISunTimeDef} sunriseEnd - The sun-time for sunrise ends (bottom edge of the sun touches the horizon) + * @property {ISunTimeDef} sunsetStart - The sun-time for sunset starts (bottom edge of the sun touches the horizon) + * @property {ISunTimeDef} sunsetEnd - The sun-time for sunset ends (sun disappears below the horizon, evening civil twilight starts) + * @property {ISunTimeDef} blueHourDawnStart - The sun-time for blue Hour start (time for special photography photos starts) + * @property {ISunTimeDef} blueHourDawnEnd - The sun-time for blue Hour end (time for special photography photos end) + * @property {ISunTimeDef} blueHourDuskStart - The sun-time for blue Hour start (time for special photography photos starts) + * @property {ISunTimeDef} blueHourDuskEnd - The sun-time for blue Hour end (time for special photography photos end) + * @property {ISunTimeDef} civilDawn - The sun-time for dawn (morning nautical twilight ends, morning civil twilight starts) + * @property {ISunTimeDef} civilDusk - The sun-time for dusk (evening nautical twilight starts) + * @property {ISunTimeDef} nauticalDawn - The sun-time for nautical dawn (morning nautical twilight starts) + * @property {ISunTimeDef} nauticalDusk - The sun-time for nautical dusk end (evening astronomical twilight starts) + * @property {ISunTimeDef} amateurDawn - The sun-time for amateur astronomical dawn (sun at 12° before sunrise) + * @property {ISunTimeDef} amateurDusk - The sun-time for amateur astronomical dusk (sun at 12° after sunrise) + * @property {ISunTimeDef} astronomicalDawn - The sun-time for night ends (morning astronomical twilight starts) + * @property {ISunTimeDef} astronomicalDusk - The sun-time for night starts (dark enough for astronomical observations) + * @property {ISunTimeDef} [dawn] - Deprecated: alternate for civilDawn + * @property {ISunTimeDef} [dusk] - Deprecated: alternate for civilDusk + * @property {ISunTimeDef} [nightEnd] - Deprecated: alternate for astronomicalDawn + * @property {ISunTimeDef} [night] - Deprecated: alternate for astronomicalDusk + * @property {ISunTimeDef} [nightStart] - Deprecated: alternate for astronomicalDusk + * @property {ISunTimeDef} [goldenHour] - Deprecated: alternate for goldenHourDuskStart + * @property {ISunTimeDef} [sunset] - Deprecated: alternate for sunsetEnd + * @property {ISunTimeDef} [sunrise] - Deprecated: alternate for sunriseStart + * @property {ISunTimeDef} [goldenHourEnd] - Deprecated: alternate for goldenHourDawnEnd + * @property {ISunTimeDef} [goldenHourStart] - Deprecated: alternate for goldenHourDuskStart + */ + +/** + * @typedef ISunTimeNames + * @type {Object} + * @property {number} angle - angle of the sun position in degrees + * @property {string} riseName - name of sun rise (morning name) + * @property {string} setName - name of sun set (evening name) + * @property {number} [risePos] - (optional) position at rise + * @property {number} [setPos] - (optional) position at set + */ + + +/** + * @typedef {Object} ISunCoordinates + * @property {number} dec - The declination of the sun + * @property {number} ra - The right ascension of the sun + */ + +/** + * @typedef {Object} ISunPosition + * @property {number} azimuth - The azimuth above the horizon of the sun in radians + * @property {number} altitude - The altitude of the sun in radians + * @property {number} zenith - The zenith of the sun in radians + * @property {number} azimuthDegrees - The azimuth of the sun in decimal degree + * @property {number} altitudeDegrees - The altitude of the sun in decimal degree + * @property {number} zenithDegrees - The zenith of the sun in decimal degree + * @property {number} declination - The declination of the sun + */ + +/** + * @typedef {Object} IMoonPosition + * @property {number} azimuth - The moon azimuth in radians + * @property {number} altitude - The moon altitude above the horizon in radians + * @property {number} azimuthDegrees - The moon azimuth in degree + * @property {number} altitudeDegrees - The moon altitude above the horizon in degree + * @property {number} distance - The distance of the moon to the earth in kilometers + * @property {number} parallacticAngle - The parallactic angle of the moon + * @property {number} parallacticAngleDegrees - The parallactic angle of the moon in degree + */ + + +/** + * @typedef {Object} IDateObj + * @property {string} date - The Date as a ISO String YYYY-MM-TTTHH:MM:SS.mmmmZ + * @property {number} value - The Date as the milliseconds since 1.1.1970 0:00 UTC + */ + +/** + * @typedef {Object} IPhaseObj + * @property {number} from - The phase start + * @property {number} to - The phase end + * @property {('newMoon'|'waxingCrescentMoon'|'firstQuarterMoon'|'waxingGibbousMoon'|'fullMoon'|'waningGibbousMoon'|'thirdQuarterMoon'|'waningCrescentMoon')} id - id of the phase + * @property {string} emoji - unicode symbol of the phase + * @property {string} name - name of the phase + * @property {string} id - phase name + * @property {number} weight - weight of the phase + * @property {string} css - a css value of the phase + * @property {string} [nameAlt] - an alernate name (not used by this library) + * @property {string} [tag] - additional tag (not used by this library) + */ + +/** + * @typedef {Object} IMoonIlluminationNext + * @property {string} date - The Date as a ISO String YYYY-MM-TTTHH:MM:SS.mmmmZ of the next phase + * @property {number} value - The Date as the milliseconds since 1.1.1970 0:00 UTC of the next phase + * @property {string} type - The name of the next phase [newMoon, fullMoon, firstQuarter, thirdQuarter] + * @property {IDateObj} newMoon - Date of the next new moon + * @property {IDateObj} fullMoon - Date of the next full moon + * @property {IDateObj} firstQuarter - Date of the next first quater of the moon + * @property {IDateObj} thirdQuarter - Date of the next third/last quater of the moon + */ + +/** + * @typedef {Object} IMoonIllumination + * @property {number} fraction - illuminated fraction of the moon; varies from `0.0` (new moon) to `1.0` (full moon) + * @property {IPhaseObj} phase - moon phase as object + * @property {number} phaseValue - The phase of the moon in the current cycle; varies from `0.0` to `1.0` + * @property {number} angle - The midpoint angle in radians of the illuminated limb of the moon reckoned eastward from the north point of the disk; + * @property {IMoonIlluminationNext} next - object containing information about the next phases of the moon + * @remarks the moon is waxing if the angle is negative, and waning if positive + */ + +/** + * @typedef {Object} IMoonDataInst + * @property {number} zenithAngle - The zenith angle of the moon + * @property {IMoonIllumination} illumination - object containing information about the next phases of the moon + * + * @typedef {IMoonPosition & IMoonDataInst} IMoonData + */ + +/** + * @typedef {Object} IMoonTimes + * @property {Date|NaN} rise - a Date object if the moon is rising on the given Date, otherwise NaN + * @property {Date|NaN} set - a Date object if the moon is setting on the given Date, otherwise NaN + * @property {boolean} alwaysUp - is true if the moon never rises/sets and is always _above_ the horizon during the day + * @property {boolean} alwaysDown - is true if the moon is always _below_ the horizon + * @property {Date} [highest] - Date of the highest position, only avalílable if set and rise is not NaN + */ + +(function () { + 'use strict'; + // sun calculations are based on http://aa.quae.nl/en/reken/zonpositie.html formulas + + // shortcuts for easier to read formulas + const sin = Math.sin; + const cos = Math.cos; + const tan = Math.tan; + const asin = Math.asin; + const atan = Math.atan2; + const acos = Math.acos; + const rad = Math.PI / 180; + const degr = 180 / Math.PI; + + // date/time constants and conversions + const dayMs = 86400000; // 1000 * 60 * 60 * 24; + const J1970 = 2440587.5; + const J2000 = 2451545; + + const lunarDaysMs = 2551442778; // The duration in days of a lunar cycle is 29.53058770576 + const firstNewMoon2000 = 947178840000; // first newMoon in the year 2000 2000-01-06 18:14 + + /** + * convert date from Julian calendar + * @param {number} j - day number in Julian calendar to convert + * @return {number} result date as timestamp + */ + function fromJulianDay(j) { + return (j - J1970) * dayMs; + } + + /** + * get number of days for a dateValue since 2000 + * @param {number} dateValue date as timestamp to get days + * @return {number} count of days + */ + function toDays(dateValue) { + return ((dateValue / dayMs) + J1970) - J2000; + } + + // general calculations for position + + const e = rad * 23.4397; // obliquity of the Earth + + /** + * get right ascension + * @param {number} l + * @param {number} b + * @returns {number} + */ + function rightAscension(l, b) { + return atan(sin(l) * cos(e) - tan(b) * sin(e), cos(l)); + } + + /** + * get declination + * @param {number} l + * @param {number} b + * @returns {number} + */ + function declination(l, b) { + return asin(sin(b) * cos(e) + cos(b) * sin(e) * sin(l)); + } + + /** + * get azimuth + * @param {number} H - siderealTime + * @param {number} phi - PI constant + * @param {number} dec - The declination of the sun + * @returns {number} azimuth in rad + */ + function azimuthCalc(H, phi, dec) { + return atan(sin(H), cos(H) * sin(phi) - tan(dec) * cos(phi)) + Math.PI; + } + + /** + * get altitude + * @param {number} H - siderealTime + * @param {number} phi - PI constant + * @param {number} dec - The declination of the sun + * @returns {number} + */ + function altitudeCalc(H, phi, dec) { + return asin(sin(phi) * sin(dec) + cos(phi) * cos(dec) * cos(H)); + } + + /** + * side real time + * @param {number} d + * @param {number} lw + * @returns {number} + */ + function siderealTime(d, lw) { + return rad * (280.16 + 360.9856235 * d) - lw; + } + + /** + * get astro refraction + * @param {number} h + * @returns {number} + */ + function astroRefraction(h) { + if (h < 0) { // the following formula works for positive altitudes only. + h = 0; + } // if h = -0.08901179 a div/0 would occur. + + // formula 16.4 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. + // 1.02 / tan(h + 10.26 / (h + 5.10)) h in degrees, result in arc minutes -> converted to rad: + return 0.0002967 / Math.tan(h + 0.00312536 / (h + 0.08901179)); + } + // general sun calculations + /** + * get solar mean anomaly + * @param {number} d + * @returns {number} + */ + function solarMeanAnomaly(d) { + return rad * (357.5291 + 0.98560028 * d); + } + + /** + * ecliptic longitude + * @param {number} M + * @returns {number} + */ + function eclipticLongitude(M) { + const C = rad * (1.9148 * sin(M) + 0.02 * sin(2 * M) + 0.0003 * sin(3 * M)); + // equation of center + const P = rad * 102.9372; // perihelion of the Earth + return M + C + P + Math.PI; + } + + /** + * sun coordinates + * @param {number} d days in Julian calendar + * @returns {ISunCoordinates} + */ + function sunCoords(d) { + const M = solarMeanAnomaly(d); + const L = eclipticLongitude(M); + + return { + dec: declination(L, 0), + ra: rightAscension(L, 0) + }; + } + + const SunCalc = {}; + + /** + * calculates sun position for a given date and latitude/longitude + * @param {number|Date} dateValue Date object or timestamp for calculating sun-position + * @param {number} lat latitude for calculating sun-position + * @param {number} lng longitude for calculating sun-position + * @return {ISunPosition} result object of sun-position + */ + SunCalc.getPosition = function (dateValue, lat, lng) { + // console.log(`getPosition dateValue=${dateValue} lat=${lat}, lng=${lng}`); + if (isNaN(lat)) { + throw new Error('latitude missing'); + } + if (isNaN(lng)) { + throw new Error('longitude missing'); + } + if (dateValue instanceof Date) { + dateValue = dateValue.valueOf(); + } + const lw = rad * -lng; + const phi = rad * lat; + const d = toDays(dateValue); + const c = sunCoords(d); + const H = siderealTime(d, lw) - c.ra; + const azimuth = azimuthCalc(H, phi, c.dec); + const altitude = altitudeCalc(H, phi, c.dec); + // console.log(`getPosition date=${date}, M=${H}, L=${H}, c=${JSON.stringify(c)}, d=${d}, lw=${lw}, phi=${phi}`); + + return { + azimuth, + altitude, + zenith: (90*Math.PI/180) - altitude, + azimuthDegrees: degr * azimuth, + altitudeDegrees: degr * altitude, + zenithDegrees: 90 - (degr * altitude), + declination: c.dec + }; + }; + + /** sun times configuration + * @type {Array.} + */ + const sunTimes = SunCalc.times = [ + { angle: 6, riseName: 'goldenHourDawnEnd', setName: 'goldenHourDuskStart'}, // GOLDEN_HOUR_2 + { angle: -0.3, riseName: 'sunriseEnd', setName: 'sunsetStart'}, // SUNRISE_END + { angle: -0.833, riseName: 'sunriseStart', setName: 'sunsetEnd'}, // SUNRISE + { angle: -1, riseName: 'goldenHourDawnStart', setName: 'goldenHourDuskEnd'}, // GOLDEN_HOUR_1 + { angle: -4, riseName: 'blueHourDawnEnd', setName: 'blueHourDuskStart'}, // BLUE_HOUR + { angle: -6, riseName: 'civilDawn', setName: 'civilDusk'}, // DAWN + { angle: -8, riseName: 'blueHourDawnStart', setName: 'blueHourDuskEnd'}, // BLUE_HOUR + { angle: -12, riseName: 'nauticalDawn', setName: 'nauticalDusk'}, // NAUTIC_DAWN + { angle: -15, riseName: 'amateurDawn', setName: 'amateurDusk'}, + { angle: -18, riseName: 'astronomicalDawn', setName: 'astronomicalDusk'} // ASTRO_DAWN + ]; + + /** alternate time names for backward compatibility + * @type {Array.<[string, string]>} + */ + const suntimesDeprecated = SunCalc.timesDeprecated = [ + ['dawn', 'civilDawn'], + ['dusk', 'civilDusk'], + ['nightEnd', 'astronomicalDawn'], + ['night', 'astronomicalDusk'], + ['nightStart', 'astronomicalDusk'], + ['goldenHour', 'goldenHourDuskStart'], + ['sunrise', 'sunriseStart'], + ['sunset', 'sunsetEnd'], + ['goldenHourEnd', 'goldenHourDawnEnd'], + ['goldenHourStart', 'goldenHourDuskStart'] + ]; + + /** adds a custom time to the times config + * @param {number} angleAltitude - angle of Altitude/elevation above the horizont of the sun in degrees + * @param {string} riseName - name of sun rise (morning name) + * @param {string} setName - name of sun set (evening name) + * @param {number} [risePos] - (optional) position at rise (morning) + * @param {number} [setPos] - (optional) position at set (evening) + * @param {boolean} [degree=true] defines if the elevationAngle is in degree not in radians + * @return {Boolean} true if new time could be added, false if not (parameter missing; riseName or setName already existing) + */ + SunCalc.addTime = function (angleAltitude, riseName, setName, risePos, setPos, degree) { + let isValid = (typeof riseName === 'string') && (riseName.length > 0) && + (typeof setName === 'string') && (setName.length > 0) && + (typeof angleAltitude === 'number'); + if (isValid) { + const EXP = /^(?![0-9])[a-zA-Z0-9$_]+$/; + // check for invalid names + for (let i=0; i= 0; i--) { + if (suntimesDeprecated[i][0] === riseName || suntimesDeprecated[i][0] === setName) { + suntimesDeprecated.splice(i, 1); + } + } + return true; + } + } + return false; + }; + + /** + * add an alternate name for a sun time + * @param {string} alternameName - alternate or deprecated time name + * @param {string} originalName - original time name from SunCalc.times array + * @return {Boolean} true if could be added, false if not (parameter missing; originalName does not exists; alternameName already existis) + */ + SunCalc.addDeprecatedTimeName = function (alternameName, originalName) { + let isValid = (typeof alternameName === 'string') && (alternameName.length > 0) && + (typeof originalName === 'string') && (originalName.length > 0); + if (isValid) { + let hasOrg = false; + const EXP = /^(?![0-9])[a-zA-Z0-9$_]+$/; + // check for invalid names + for (let i=0; i 200) { + // let nazi = this.getPosition(dateVal, lat, lng).azimuth; + const d = toDays(dateVal); + const c = sunCoords(d); + const H = siderealTime(d, lw) - c.ra; + const nazim = azimuthCalc(H, phi, c.dec); + + addval /= 2; + if (nazim < nazimuth) { + dateVal += addval; + } else { + dateVal -= addval; + } + } + return new Date(Math.floor(dateVal)); + }; + + // calculation for solar time based on https://www.pveducation.org/pvcdrom/properties-of-sunlight/solar-time + + /** + * Calculaes the solar time of the given date in the given latitude and UTC offset. + * @param {number|Date} dateValue Date object or timestamp for calculating solar time + * @param {number} lng longitude for calculating sun-time + * @param {number} utcOffset offset to the utc time + * @returns {Date} Returns the solar time of the given date in the given latitude and UTC offset. + */ + SunCalc.getSolarTime = function (dateValue, lng, utcOffset) { + // @ts-ignore + const date = new Date(dateValue); + // calculate the day of year + const start = new Date(date.getFullYear(), 0, 0); + const diff = (date.getTime() - start.getTime()) + ((start.getTimezoneOffset() - date.getTimezoneOffset()) * 60 * 1000); + const dayOfYear = Math.floor(diff / dayMs); + + const b = 360 / 365 * (dayOfYear - 81) * rad; + const equationOfTime = 9.87 * sin(2 * b) - 7.53 * cos(b) - 1.5 * sin(b); + const localSolarTimeMeridian = 15 * utcOffset; + const timeCorrection = equationOfTime + 4 * (lng - localSolarTimeMeridian); + const localSolarTime = date.getHours() + timeCorrection / 60 + date.getMinutes() / 60; + + const solarDate = new Date(0, 0); + solarDate.setMinutes(+localSolarTime * 60); + return solarDate; + }; + + // moon calculations, based on http://aa.quae.nl/en/reken/hemelpositie.html formulas + + /** + * calculate the geocentric ecliptic coordinates of the moon + * @param {number} d number of days + */ + function moonCoords(d) { + const L = rad * (218.316 + 13.176396 * d); // ecliptic longitude + const M = rad * (134.963 + 13.064993 * d); // mean anomaly + const F = rad * (93.272 + 13.229350 * d); // mean distance + const l = L + rad * 6.289 * sin(M); // longitude + const b = rad * 5.128 * sin(F); // latitude + const dt = 385001 - 20905 * cos(M); // distance to the moon in km + + return { + ra: rightAscension(l, b), + dec: declination(l, b), + dist: dt + }; + } + + /** + * calculates moon position for a given date and latitude/longitude + * @param {number|Date} dateValue Date object or timestamp for calculating moon-position + * @param {number} lat latitude for calculating moon-position + * @param {number} lng longitude for calculating moon-position + * @return {IMoonPosition} result object of moon-position + */ + SunCalc.getMoonPosition = function (dateValue, lat, lng) { + // console.log(`getMoonPosition dateValue=${dateValue} lat=${lat}, lng=${lng}`); + if (isNaN(lat)) { + throw new Error('latitude missing'); + } + if (isNaN(lng)) { + throw new Error('longitude missing'); + } + if (dateValue instanceof Date) { + dateValue = dateValue.valueOf(); + } + const lw = rad * -lng; + const phi = rad * lat; + const d = toDays(dateValue); + const c = moonCoords(d); + const H = siderealTime(d, lw) - c.ra; + let altitude = altitudeCalc(H, phi, c.dec); + altitude += astroRefraction(altitude); // altitude correction for refraction + + // formula 14.1 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. + const pa = atan(sin(H), tan(phi) * cos(c.dec) - sin(c.dec) * cos(H)); + + const azimuth = azimuthCalc(H, phi, c.dec); + + return { + azimuth, + altitude, + azimuthDegrees: degr * azimuth, + altitudeDegrees: degr * altitude, + distance: c.dist, + parallacticAngle: pa, + parallacticAngleDegrees: degr * pa + }; + }; + + const fractionOfTheMoonCycle = SunCalc.moonCycles = [{ + from: 0, + to: 0.033863193308711, + id: 'newMoon', + emoji: '🌚', + code: ':new_moon_with_face:', + name: 'New Moon', + weight: 1, + css: 'wi-moon-new' + }, + { + from: 0.033863193308711, + to: 0.216136806691289, + id: 'waxingCrescentMoon', + emoji: '🌒', + code: ':waxing_crescent_moon:', + name: 'Waxing Crescent', + weight: 6.3825, + css: 'wi-moon-wax-cres' + }, + { + from: 0.216136806691289, + to: 0.283863193308711, + id: 'firstQuarterMoon', + emoji: '🌓', + code: ':first_quarter_moon:', + name: 'First Quarter', + weight: 1, + css: 'wi-moon-first-quart' + }, + { + from: 0.283863193308711, + to: 0.466136806691289, + id: 'waxingGibbousMoon', + emoji: '🌔', + code: ':waxing_gibbous_moon:', + name: 'Waxing Gibbous', + weight: 6.3825, + css: 'wi-moon-wax-gibb' + }, + { + from: 0.466136806691289, + to: 0.533863193308711, + id: 'fullMoon', + emoji: '🌝', + code: ':full_moon_with_face:', + name: 'Full Moon', + weight: 1, + css: 'wi-moon-full' + }, + { + from: 0.533863193308711, + to: 0.716136806691289, + id: 'waningGibbousMoon', + emoji: '🌖', + code: ':waning_gibbous_moon:', + name: 'Waning Gibbous', + weight: 6.3825, + css: 'wi-moon-wan-gibb' + }, + { + from: 0.716136806691289, + to: 0.783863193308711, + id: 'thirdQuarterMoon', + emoji: '🌗', + code: ':last_quarter_moon:', + name: 'third Quarter', + weight: 1, + css: 'wi-moon-third-quart' + }, + { + from: 0.783863193308711, + to: 0.966136806691289, + id: 'waningCrescentMoon', + emoji: '🌘', + code: ':waning_crescent_moon:', + name: 'Waning Crescent', + weight: 6.3825, + css: 'wi-moon-wan-cres' + }, + { + from: 0.966136806691289, + to: 1, + id: 'newMoon', + emoji: '🌚', + code: ':new_moon_with_face:', + name: 'New Moon', + weight: 1, + css: 'wi-moon-new' + }]; + + /** + * calculations for illumination parameters of the moon, + * based on http://idlastro.gsfc.nasa.gov/ftp/pro/astro/mphase.pro formulas and + * Chapter 48 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. + * @param {number|Date} dateValue Date object or timestamp for calculating moon-illumination + * @return {IMoonIllumination} result object of moon-illumination + */ + SunCalc.getMoonIllumination = function (dateValue) { + // console.log(`getMoonIllumination dateValue=${dateValue}`); + if (dateValue instanceof Date) { + dateValue = dateValue.valueOf(); + } + const d = toDays(dateValue); + const s = sunCoords(d); + const m = moonCoords(d); + const sdist = 149598000; // distance from Earth to Sun in km + const phi = acos(sin(s.dec) * sin(m.dec) + cos(s.dec) * cos(m.dec) * cos(s.ra - m.ra)); + const inc = atan(sdist * sin(phi), m.dist - sdist * cos(phi)); + const angle = atan(cos(s.dec) * sin(s.ra - m.ra), sin(s.dec) * cos(m.dec) - + cos(s.dec) * sin(m.dec) * cos(s.ra - m.ra)); + const phaseValue = 0.5 + 0.5 * inc * (angle < 0 ? -1 : 1) / Math.PI; + + // calculates the difference in ms between the sirst fullMoon 2000 and given Date + const diffBase = dateValue - firstNewMoon2000; + // Calculate modulus to drop completed cycles + let cycleModMs = diffBase % lunarDaysMs; + // If negative number (date before new moon 2000) add lunarDaysMs + if ( cycleModMs < 0 ) { cycleModMs += lunarDaysMs; } + const nextNewMoon = (lunarDaysMs - cycleModMs) + dateValue; + let nextFullMoon = ((lunarDaysMs/2) - cycleModMs) + dateValue; + if (nextFullMoon < dateValue) { nextFullMoon += lunarDaysMs; } + const quater = (lunarDaysMs/4); + let nextFirstQuarter = (quater - cycleModMs) + dateValue; + if (nextFirstQuarter < dateValue) { nextFirstQuarter += lunarDaysMs; } + let nextThirdQuarter = (lunarDaysMs - quater - cycleModMs) + dateValue; + if (nextThirdQuarter < dateValue) { nextThirdQuarter += lunarDaysMs; } + // Calculate the fraction of the moon cycle + // const currentfrac = cycleModMs / lunarDaysMs; + const next = Math.min(nextNewMoon, nextFirstQuarter, nextFullMoon, nextThirdQuarter); + let phase; + + for (let index = 0; index < fractionOfTheMoonCycle.length; index++) { + const element = fractionOfTheMoonCycle[index]; + if ( (phaseValue >= element.from) && (phaseValue <= element.to) ) { + phase = element; + break; + } + } + + return { + fraction: (1 + cos(inc)) / 2, + // fraction2: cycleModMs / lunarDaysMs, + // @ts-ignore + phase, + phaseValue, + angle, + next : { + value: next, + date: (new Date(next)).toISOString(), + type: (next === nextNewMoon) ? 'newMoon' : ((next === nextFirstQuarter) ? 'firstQuarter' : ((next === nextFullMoon) ? 'fullMoon' : 'thirdQuarter')), + newMoon: { + value: nextNewMoon, + date: (new Date(nextNewMoon)).toISOString() + }, + fullMoon: { + value: nextFullMoon, + date: (new Date(nextFullMoon)).toISOString() + }, + firstQuarter: { + value: nextFirstQuarter, + date: (new Date(nextFirstQuarter)).toISOString() + }, + thirdQuarter: { + value: nextThirdQuarter, + date: (new Date(nextThirdQuarter)).toISOString() + } + } + }; + }; + + /** + * calculations moon position and illumination for a given date and latitude/longitude of the moon, + * @param {number|Date} dateValue Date object or timestamp for calculating moon-illumination + * @param {number} lat latitude for calculating moon-position + * @param {number} lng longitude for calculating moon-position + * @return {IMoonData} result object of moon-illumination + */ + SunCalc.getMoonData = function (dateValue, lat, lng) { + const pos = SunCalc.getMoonPosition(dateValue, lat, lng); + const illum = SunCalc.getMoonIllumination(dateValue); + return Object.assign({ + illumination : illum, + zenithAngle : illum.angle - pos.parallacticAngle + }, pos); + }; + + /** + * add hours to a date + * @param {number} dateValue timestamp to add hours + * @param {number} h - hours to add + * @returns {number} new timestamp with added hours + */ + function hoursLater(dateValue, h) { + return dateValue + h * dayMs / 24; + } + + /** + * calculations for moon rise/set times are based on http://www.stargazing.net/kepler/moonrise.html article + * @param {number|Date} dateValue Date object or timestamp for calculating moon-times + * @param {number} lat latitude for calculating moon-times + * @param {number} lng longitude for calculating moon-times + * @param {boolean} [inUTC] defines if the calculation should be in utc or local time (default is local) + * @return {IMoonTimes} result object of sunTime + */ + SunCalc.getMoonTimes = function (dateValue, lat, lng, inUTC) { + if (isNaN(lat)) { + throw new Error('latitude missing'); + } + if (isNaN(lng)) { + throw new Error('longitude missing'); + } + const t = new Date(dateValue); + if (inUTC) { + t.setUTCHours(0, 0, 0, 0); + } else { + t.setHours(0, 0, 0, 0); + } + dateValue = t.valueOf(); + // console.log(`getMoonTimes lat=${lat} lng=${lng} dateValue=${dateValue} t=${t}`); + + const hc = 0.133 * rad; + let h0 = SunCalc.getMoonPosition(dateValue, lat, lng).altitude - hc; + let rise; let set; let ye; let d; let roots; let x1; let x2; let dx; + + // go in 2-hour chunks, each time seeing if a 3-point quadratic curve crosses zero (which means rise or set) + for (let i = 1; i <= 26; i += 2) { + const h1 = SunCalc.getMoonPosition(hoursLater(dateValue, i), lat, lng).altitude - hc; + const h2 = SunCalc.getMoonPosition(hoursLater(dateValue, i + 1), lat, lng).altitude - hc; + + const a = (h0 + h2) / 2 - h1; + const b = (h2 - h0) / 2; + const xe = -b / (2 * a); + ye = (a * xe + b) * xe + h1; + d = b * b - 4 * a * h1; + roots = 0; + + if (d >= 0) { + dx = Math.sqrt(d) / (Math.abs(a) * 2); + x1 = xe - dx; + x2 = xe + dx; + if (Math.abs(x1) <= 1) { + roots++; + } + + if (Math.abs(x2) <= 1) { + roots++; + } + + if (x1 < -1) { + x1 = x2; + } + } + + if (roots === 1) { + if (h0 < 0) { + rise = i + x1; + } else { + set = i + x1; + } + } else if (roots === 2) { + rise = i + (ye < 0 ? x2 : x1); + set = i + (ye < 0 ? x1 : x2); + } + + if (rise && set) { + break; + } + + h0 = h2; + } + + const result = {}; + if (rise) { + result.rise = new Date(hoursLater(dateValue, rise)); + } else { + result.rise = NaN; + } + + if (set) { + result.set = new Date(hoursLater(dateValue, set)); + } else { + result.set = NaN; + } + + if (!rise && !set) { + if (ye > 0) { + result.alwaysUp = true; + result.alwaysDown = false; + } else { + result.alwaysUp = false; + result.alwaysDown = true; + } + } else if (rise && set) { + result.alwaysUp = false; + result.alwaysDown = false; + result.highest = new Date(hoursLater(dateValue, Math.min(rise, set) + (Math.abs(set - rise) / 2))); + } else { + result.alwaysUp = false; + result.alwaysDown = false; + } + return result; + }; + + /** + * calc moon transit + * @param {number} rize timestamp for rise + * @param {number} set timestamp for set time + * @returns {Date} new moon transit + */ + function calcMoonTransit(rize, set) { + if (rize > set) { + return new Date(set + (rize - set) / 2); + } + return new Date(rize + (set - rize) / 2); + } + + /** + * calculated the moon transit + * @param {number|Date} rise rise time as Date object or timestamp for calculating moon-transit + * @param {number|Date} set set time as Date object or timestamp for calculating moon-transit + * @param {number} lat latitude for calculating moon-times + * @param {number} lng longitude for calculating moon-times + * @returns {{main: (Date|null), invert: (Date|null)}} + */ + SunCalc.moonTransit = function (rise, set, lat, lng) { + /** @type {Date|null} */ let main = null; + /** @type {Date|null} */ let invert = null; + const riseDate = new Date(rise); + const setDate = new Date(set); + const riseValue = riseDate.getTime(); + const setValue = setDate.getTime(); + const day = setDate.getDate(); + let tempTransitBefore; + let tempTransitAfter; + + if (rise && set) { + if (rise < set) { + main = calcMoonTransit(riseValue, setValue); + } else { + invert = calcMoonTransit(riseValue, setValue); + } + } + + if (rise) { + tempTransitAfter = calcMoonTransit(riseValue, SunCalc.getMoonTimes(new Date(riseDate).setDate(day + 1), lat, lng).set.valueOf()); + if (tempTransitAfter.getDate() === day) { + if (main) { + invert = tempTransitAfter; + } else { + main = tempTransitAfter; + } + } + } + + if (set) { + tempTransitBefore = calcMoonTransit(setValue, SunCalc.getMoonTimes(new Date(setDate).setDate(day - 1), lat, lng).rise.valueOf()); + if (tempTransitBefore.getDate() === day) { + main = tempTransitBefore; + } + } + return { + main, + invert + }; + }; + + // export as Node module / AMD module / browser variable + if (typeof exports === 'object' && typeof module !== 'undefined') { + module.exports = SunCalc; + // @ts-ignore + } else if (typeof define === 'function' && define.amd) { + // @ts-ignore + define(SunCalc); + } else { + // @ts-ignore + window.SunCalc = SunCalc; + } + +})(); \ No newline at end of file diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/metadata.json b/moonphase@techi-freki/files/moonphase@techi-freki/metadata.json index 840eff50352..53b75cd27ff 100644 --- a/moonphase@techi-freki/files/moonphase@techi-freki/metadata.json +++ b/moonphase@techi-freki/files/moonphase@techi-freki/metadata.json @@ -1,9 +1,8 @@ { "uuid": "moonphase@techi-freki", "name": "Moon Phase", - "description": "A Cinnamon applet that displays the current moon phase.", - "version": "0.0.6", + "description": "A Cinnamon applet that displays up-to-date moon related information.", + "version": "1.0.0", "max-instances": 1, - "author": "Techi Freki", - "email": "techifreki@proton.me" + "author": "Techi Freki" } diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/po/moonphase@techi-freki.pot b/moonphase@techi-freki/files/moonphase@techi-freki/po/moonphase@techi-freki.pot index 5bd878c05ed..97d169a348f 100644 --- a/moonphase@techi-freki/files/moonphase@techi-freki/po/moonphase@techi-freki.pot +++ b/moonphase@techi-freki/files/moonphase@techi-freki/po/moonphase@techi-freki.pot @@ -1,7 +1,7 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. +# Translation file for moonphase@techi-freki +# Copyright (C) 2023 Techi-Freki +# This file is distributed under the same license as the moonphase@techi-freki package. +# D. Bryan Combs (Techi-Freki), 2023 # #, fuzzy msgid "" @@ -17,45 +17,108 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: js/moon.js:41 js/moon.js:49 +#: js/moon.js:22 js/moon.js:30 msgid "New Moon" msgstr "" -#: js/moon.js:42 +#: js/moon.js:23 msgid "Waxing Crescent" msgstr "" -#: js/moon.js:43 +#: js/moon.js:24 msgid "First Quarter" msgstr "" -#: js/moon.js:44 +#: js/moon.js:25 msgid "Waxing Gibbous" msgstr "" -#: js/moon.js:45 +#: js/moon.js:26 msgid "Full Moon" msgstr "" -#: js/moon.js:46 +#: js/moon.js:27 msgid "Waning Gibbous" msgstr "" -#: js/moon.js:47 +#: js/moon.js:28 msgid "Last Quarter" msgstr "" -#: js/moon.js:48 +#: js/moon.js:29 msgid "Waning Crescent" msgstr "" +#: js/ui/elements/noRiseSetElement.js:40 +msgid "Next Moonrise" +msgstr "" + +#: js/ui/elements/noRiseSetElement.js:52 +msgid "Next Moonset" +msgstr "" + +#: js/ui/elements/noRiseSetElement.js:22 +msgid "illumination" +msgstr "" + +#: js/ui/elements/riseSetUi.js:45 +msgid "The moon is always down and will not rise today" +msgstr "" + +#: js/ui/elements/riseSetUi.js:55 +msgid "The moon is always up and will not set today" +msgstr "" + +#: js/ui/elements/riseSetUi.js:66 +msgid "Moonrise" +msgstr "" + +#: js/ui/elements/riseSetUi.js:73 +msgid "Lunar Noon" +msgstr "" + +#: js/ui/elements/riseSetUi.js:80 +msgid "Moonset" +msgstr "" + +#: js/ui/elements/compass.js:10 +msgid "North" +msgstr "" + +#: js/ui/elements/compass.js:11 +msgid "North East" +msgstr "" + +#: js/ui/elements/compass.js:12 +msgid "East" +msgstr "" + +#: js/ui/elements/compass.js:13 +msgid "South East" +msgstr "" + +#: js/ui/elements/compass.js:14 +msgid "South" +msgstr "" + +#: js/ui/elements/compass.js:15 +msgid "South West" +msgstr "" + +#: js/ui/elements/compass.js:16 +msgid "West" +msgstr "" + +#: js/ui/elements/compass.js:17 +msgid "North West" +msgstr "" + #. metadata.json->name -#: js/moon.js:54 msgid "Moon Phase" msgstr "" #. metadata.json->description -msgid "A Cinnamon applet that displays the current moon phase." +msgid "A Cinnamon applet that displays up-to-date moon related information." msgstr "" #. settings-schema.json->page-settings->title @@ -70,8 +133,12 @@ msgstr "" msgid "Update Settings" msgstr "" +#. settings-schema.json->section-geolocation->title +msgid "Geolocation Settings" +msgstr "" + #. settings-schema.json->showPhaseLabel->description -msgid "Display the moon phase as a string" +msgid "Display the applet label" msgstr "" #. settings-schema.json->showNameLabel->description @@ -105,3 +172,27 @@ msgstr "" #. settings-schema.json->updateInterval->description msgid "Update interval" msgstr "" + +#. settings-schema.json->enableGeolocation->description +msgid "Enable geolocation features" +msgstr "" + +#. settings-schema.json->latitude->description +msgid "Latitude" +msgstr "" + +#. settings-schema.json->longitude->description +msgid "Longitude" +msgstr "" + +#. settings-schema.json->enablePopup->description +msgid "Enable popup menu" +msgstr "" + +#. settings-schema.json->showCurrentPhaseInfo->description +msgid "Display the current phase information" +msgstr "" + +#. settings-schema.json->showRiseSet->description +msgid "Display the moon rise, transit, and set times" +msgstr "" \ No newline at end of file diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/settings-schema.json b/moonphase@techi-freki/files/moonphase@techi-freki/settings-schema.json index 66da57a9633..7a9dbd1924d 100644 --- a/moonphase@techi-freki/files/moonphase@techi-freki/settings-schema.json +++ b/moonphase@techi-freki/files/moonphase@techi-freki/settings-schema.json @@ -9,25 +9,39 @@ "type": "page", "title": "Settings", "sections": [ - "section-panel", - "section-menu" + "section-general", + "section-geolocation", + "section-update" ] }, - "section-panel": { + "section-geolocation": { + "type": "section", + "title": "Geolocation Settings", + "keys": [ + "enableGeolocation", + "latitude", + "longitude", + "enablePopup", + "showCurrentPhaseInfo", + "showRiseSet" + ] + }, + + "section-general": { "type": "section", "title": "General Settings", "keys": [ "useAltIcons", "showTooltip", - "showNameTooltip", + "showPhaseTooltip", "showPercentageTooltip", "showPhaseLabel", "showNameLabel", "showPercentageLabel" ] }, - "section-menu": { + "section-update": { "type": "section", "title": "Update Settings", "keys": [ @@ -36,10 +50,45 @@ } }, + "enableGeolocation": { + "type": "checkbox", + "default": false, + "description": "Enable geolocation features" + }, + "latitude": { + "type": "entry", + "default": "40.730610", + "description": "Latitude", + "dependency": "enableGeolocation" + }, + "longitude": { + "type": "entry", + "default": "-73.935242", + "description": "Longitude", + "dependency": "enableGeolocation" + }, + "enablePopup": { + "type": "checkbox", + "default": false, + "description": "Enable popup menu", + "dependency": "enableGeolocation" + }, + "showCurrentPhaseInfo": { + "type": "checkbox", + "default": false, + "description": "Display the current phase information", + "dependency": "enableGeolocation" + }, + "showRiseSet": { + "type": "checkbox", + "default": false, + "description": "Display the moon rise, transit, and set times", + "dependency": "enableGeolocation" + }, "showPhaseLabel": { "type": "checkbox", "default": false, - "description": "Display the moon phase as a string" + "description": "Display the applet label" }, "showNameLabel": { "type": "checkbox", @@ -58,7 +107,7 @@ "default": true, "description": "Display a tooltip on hover" }, - "showNameTooltip": { + "showPhaseTooltip": { "type": "checkbox", "default": true, "description": "Display the name of the moon phase in the tooltip", diff --git a/moonphase@techi-freki/files/moonphase@techi-freki/stylesheet.css b/moonphase@techi-freki/files/moonphase@techi-freki/stylesheet.css new file mode 100644 index 00000000000..ec3fad02ce6 --- /dev/null +++ b/moonphase@techi-freki/files/moonphase@techi-freki/stylesheet.css @@ -0,0 +1,52 @@ +.padding-top-3 { + margin: 5px; + padding-top: 3px; +} + +.padding-top-5-strict { + padding-top: 5px; +} + +.padding-15 { + padding: 15px; +} + +.margin-5 { + margin: 5px; +} + +.margin-bottom-5 { + margin-bottom: 5px; +} + +.margin-bottom-15 { + margin-bottom: 15px; +} + +.margin-top-5 { + margin-top: 5px; +} + +.font-20 { + font-size: 20px; +} + +.font-15 { + font-size: 15px; +} + +.font-10 { + font-size: 10px; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-right { + text-align: right; +} \ No newline at end of file diff --git a/moonphase@techi-freki/screenshot.png b/moonphase@techi-freki/screenshot.png index 48f98cbb897..43c77151cea 100644 Binary files a/moonphase@techi-freki/screenshot.png and b/moonphase@techi-freki/screenshot.png differ diff --git a/moonphase@techi-freki/screenshot_0.png b/moonphase@techi-freki/screenshot_0.png deleted file mode 100644 index 2653ede8a6d..00000000000 Binary files a/moonphase@techi-freki/screenshot_0.png and /dev/null differ diff --git a/moonphase@techi-freki/screenshot_1.png b/moonphase@techi-freki/screenshot_1.png deleted file mode 100644 index 6280814123f..00000000000 Binary files a/moonphase@techi-freki/screenshot_1.png and /dev/null differ diff --git a/moonphase@techi-freki/screenshot_2.png b/moonphase@techi-freki/screenshot_2.png deleted file mode 100644 index dfc66df0ee3..00000000000 Binary files a/moonphase@techi-freki/screenshot_2.png and /dev/null differ