This commit is contained in:
2026-04-06 09:26:59 +02:00
26 changed files with 619 additions and 797 deletions
-514
View File
@@ -1,514 +0,0 @@
[Sat Apr 4 13:05:02 2026] PHP 8.5.4 Development Server (http://127.0.0.1:8000) started
[Sat Apr 4 13:05:02 2026] 127.0.0.1:56847 Accepted
[Sat Apr 4 13:05:02 2026] 127.0.0.1:56847 [200]: GET /
[Sat Apr 4 13:05:02 2026] 127.0.0.1:56847 Closing
[Sat Apr 4 13:05:06 2026] 127.0.0.1:60094 Accepted
[Sat Apr 4 13:05:08 2026] 127.0.0.1:60094 [200]: GET /
[Sat Apr 4 13:05:08 2026] 127.0.0.1:60094 Closing
[Sat Apr 4 13:05:08 2026] 127.0.0.1:55506 Accepted
[Sat Apr 4 13:05:08 2026] 127.0.0.1:55506 [200]: GET /css/style.css
[Sat Apr 4 13:05:08 2026] 127.0.0.1:55506 Closing
[Sat Apr 4 13:05:08 2026] 127.0.0.1:52560 Accepted
[Sat Apr 4 13:05:08 2026] 127.0.0.1:49223 Accepted
[Sat Apr 4 13:05:08 2026] 127.0.0.1:52560 [200]: GET /images/star16x16.png
[Sat Apr 4 13:05:08 2026] 127.0.0.1:49223 [200]: GET /images/star16x16_rays.png
[Sat Apr 4 13:05:08 2026] 127.0.0.1:52560 Closing
[Sat Apr 4 13:05:08 2026] 127.0.0.1:49223 Closing
[Sat Apr 4 13:05:08 2026] 127.0.0.1:64599 Accepted
[Sat Apr 4 13:05:10 2026] 127.0.0.1:64599 [200]: GET /favicon.ico
[Sat Apr 4 13:05:10 2026] 127.0.0.1:64599 Closing
[Sat Apr 4 13:05:10 2026] 127.0.0.1:63078 Accepted
[Sat Apr 4 13:05:20 2026] 127.0.0.1:63078 [200]: GET /monatsvorhersage.php
[Sat Apr 4 13:05:20 2026] 127.0.0.1:63078 Closing
[Sat Apr 4 13:05:20 2026] 127.0.0.1:64746 Accepted
[Sat Apr 4 13:05:20 2026] 127.0.0.1:57663 Accepted
[Sat Apr 4 13:05:20 2026] 127.0.0.1:64746 [200]: GET /css/style.css
[Sat Apr 4 13:05:39 2026] 127.0.0.1:57663 [200]: GET /monatsvorhersage_old.php
[Sat Apr 4 13:05:39 2026] 127.0.0.1:57663 Closing
[Sat Apr 4 13:05:39 2026] 127.0.0.1:60792 Accepted
[Sat Apr 4 13:05:39 2026] 127.0.0.1:64746 Closing
[Sat Apr 4 13:05:39 2026] 127.0.0.1:51819 Accepted
[Sat Apr 4 13:05:39 2026] 127.0.0.1:60792 [200]: GET /js/astronomy.browser.min.js
[Sat Apr 4 13:05:39 2026] 127.0.0.1:51819 [200]: GET /css/style.css
[Sat Apr 4 13:05:39 2026] 127.0.0.1:60792 Closing
[Sat Apr 4 13:05:39 2026] 127.0.0.1:51819 Closing
[Sat Apr 4 13:05:40 2026] 127.0.0.1:55510 Accepted
[Sat Apr 4 13:05:40 2026] 127.0.0.1:55510 [200]: GET /js/astronomy.browser.min.js
[Sat Apr 4 13:05:40 2026] 127.0.0.1:55510 Closing
[Sat Apr 4 13:05:40 2026] 127.0.0.1:61999 Accepted
[Sat Apr 4 13:05:40 2026] 127.0.0.1:56212 Accepted
[Sat Apr 4 13:05:40 2026] 127.0.0.1:61999 [200]: GET /images/star16x16.png
[Sat Apr 4 13:05:40 2026] 127.0.0.1:56212 [200]: GET /images/star16x16_rays.png
[Sat Apr 4 13:05:40 2026] 127.0.0.1:61999 Closing
[Sat Apr 4 13:05:40 2026] 127.0.0.1:56212 Closing
[Sat Apr 4 13:05:40 2026] 127.0.0.1:64580 Accepted
[Sat Apr 4 13:05:40 2026] 127.0.0.1:64580 [200]: GET /images/moons/moon4geocron.png
[Sat Apr 4 13:05:40 2026] 127.0.0.1:64580 Closing
[Sat Apr 4 13:05:40 2026] 127.0.0.1:55814 Accepted
[Sat Apr 4 13:05:42 2026] 127.0.0.1:55814 [200]: GET /favicon.ico
[Sat Apr 4 13:05:42 2026] 127.0.0.1:55814 Closing
[Sat Apr 4 13:05:42 2026] 127.0.0.1:62833 Accepted
[Sat Apr 4 13:07:52 2026] 127.0.0.1:62833 Closed without sending a request; it was probably just an unused speculative preconnection
[Sat Apr 4 13:07:52 2026] 127.0.0.1:62833 Closing
[Sat Apr 4 13:07:52 2026] 127.0.0.1:51688 Accepted
[Sat Apr 4 13:08:11 2026] 127.0.0.1:51688 [200]: GET /monatsvorhersage_old.php?year=2026&month=8
[Sat Apr 4 13:08:11 2026] 127.0.0.1:51688 Closing
[Sat Apr 4 13:08:11 2026] 127.0.0.1:51110 Accepted
[Sat Apr 4 13:08:11 2026] 127.0.0.1:51110 [200]: GET /css/style.css
[Sat Apr 4 13:08:11 2026] 127.0.0.1:51110 Closing
[Sat Apr 4 13:08:12 2026] 127.0.0.1:61842 Accepted
[Sat Apr 4 13:08:12 2026] 127.0.0.1:61842 [200]: GET /js/astronomy.browser.min.js
[Sat Apr 4 13:08:12 2026] 127.0.0.1:61842 Closing
[Sat Apr 4 13:08:12 2026] 127.0.0.1:64415 Accepted
[Sat Apr 4 13:08:12 2026] 127.0.0.1:62641 Accepted
[Sat Apr 4 13:08:12 2026] 127.0.0.1:64415 [200]: GET /images/star16x16.png
[Sat Apr 4 13:08:12 2026] 127.0.0.1:62641 [200]: GET /images/star16x16_rays.png
[Sat Apr 4 13:08:12 2026] 127.0.0.1:64415 Closing
[Sat Apr 4 13:08:12 2026] 127.0.0.1:62641 Closing
[Sat Apr 4 13:08:12 2026] 127.0.0.1:63695 Accepted
[Sat Apr 4 13:08:12 2026] 127.0.0.1:63695 [200]: GET /images/moons/moon4geocron.png
[Sat Apr 4 13:08:12 2026] 127.0.0.1:63695 Closing
[Sat Apr 4 13:08:12 2026] 127.0.0.1:65119 Accepted
[Sat Apr 4 13:08:14 2026] 127.0.0.1:65119 [200]: GET /favicon.ico
[Sat Apr 4 13:08:14 2026] 127.0.0.1:65119 Closing
[Sat Apr 4 13:08:14 2026] 127.0.0.1:50310 Accepted
[Sat Apr 4 13:09:39 2026] 127.0.0.1:50310 Closed without sending a request; it was probably just an unused speculative preconnection
[Sat Apr 4 13:09:39 2026] 127.0.0.1:50310 Closing
[Sat Apr 4 13:11:39 2026] 127.0.0.1:51038 Accepted
[Sat Apr 4 13:11:59 2026] 127.0.0.1:51038 [200]: GET /monatsvorhersage_old.php?month=8&year=2026
[Sat Apr 4 13:11:59 2026] 127.0.0.1:51038 Closing
[Sat Apr 4 13:15:49 2026] 127.0.0.1:61379 Accepted
[Sat Apr 4 13:16:07 2026] 127.0.0.1:61379 [200]: GET /monatsvorhersage_old.php?year=2026&month=8
[Sat Apr 4 13:16:07 2026] 127.0.0.1:61379 Closing
[Sat Apr 4 13:16:07 2026] 127.0.0.1:57225 Accepted
[Sat Apr 4 13:16:07 2026] 127.0.0.1:57225 [200]: GET /css/style.css
[Sat Apr 4 13:16:07 2026] 127.0.0.1:57225 Closing
[Sat Apr 4 13:16:08 2026] 127.0.0.1:54279 Accepted
[Sat Apr 4 13:16:08 2026] 127.0.0.1:63941 Accepted
[Sat Apr 4 13:16:08 2026] 127.0.0.1:50404 Accepted
[Sat Apr 4 13:16:08 2026] 127.0.0.1:54279 [200]: GET /js/astronomy.browser.min.js
[Sat Apr 4 13:16:08 2026] 127.0.0.1:63941 [200]: GET /images/star16x16.png
[Sat Apr 4 13:16:08 2026] 127.0.0.1:50404 [200]: GET /images/star16x16_rays.png
[Sat Apr 4 13:16:08 2026] 127.0.0.1:54279 Closing
[Sat Apr 4 13:16:08 2026] 127.0.0.1:63941 Closing
[Sat Apr 4 13:16:08 2026] 127.0.0.1:50404 Closing
[Sat Apr 4 13:16:08 2026] 127.0.0.1:65327 Accepted
[Sat Apr 4 13:16:08 2026] 127.0.0.1:65327 [200]: GET /images/moons/moon4geocron.png
[Sat Apr 4 13:16:08 2026] 127.0.0.1:65327 Closing
[Sat Apr 4 13:16:08 2026] 127.0.0.1:59854 Accepted
[Sat Apr 4 13:16:10 2026] 127.0.0.1:59854 [200]: GET /favicon.ico
[Sat Apr 4 13:16:10 2026] 127.0.0.1:59854 Closing
[Sat Apr 4 13:16:10 2026] 127.0.0.1:56915 Accepted
[Sat Apr 4 13:17:37 2026] 127.0.0.1:56915 Closed without sending a request; it was probably just an unused speculative preconnection
[Sat Apr 4 13:17:37 2026] 127.0.0.1:56915 Closing
[Sat Apr 4 13:19:31 2026] 127.0.0.1:62466 Accepted
[Sat Apr 4 13:19:49 2026] 127.0.0.1:62466 [200]: GET /monatsvorhersage_old.php?year=2026&month=8
[Sat Apr 4 13:19:49 2026] 127.0.0.1:62466 Closing
[Sat Apr 4 13:19:49 2026] 127.0.0.1:56563 Accepted
[Sat Apr 4 13:19:49 2026] 127.0.0.1:56563 [200]: GET /css/style.css
[Sat Apr 4 13:19:49 2026] 127.0.0.1:56563 Closing
[Sat Apr 4 13:19:50 2026] 127.0.0.1:64763 Accepted
[Sat Apr 4 13:19:50 2026] 127.0.0.1:57365 Accepted
[Sat Apr 4 13:19:50 2026] 127.0.0.1:65177 Accepted
[Sat Apr 4 13:19:50 2026] 127.0.0.1:64763 [200]: GET /js/astronomy.browser.min.js
[Sat Apr 4 13:19:50 2026] 127.0.0.1:57365 [200]: GET /images/star16x16.png
[Sat Apr 4 13:19:50 2026] 127.0.0.1:65177 [200]: GET /images/star16x16_rays.png
[Sat Apr 4 13:19:50 2026] 127.0.0.1:64763 Closing
[Sat Apr 4 13:19:50 2026] 127.0.0.1:57365 Closing
[Sat Apr 4 13:19:50 2026] 127.0.0.1:65177 Closing
[Sat Apr 4 13:19:50 2026] 127.0.0.1:55468 Accepted
[Sat Apr 4 13:19:50 2026] 127.0.0.1:55468 [200]: GET /images/moons/moon4geocron.png
[Sat Apr 4 13:19:50 2026] 127.0.0.1:55468 Closing
[Sat Apr 4 13:19:50 2026] 127.0.0.1:59829 Accepted
[Sat Apr 4 13:19:52 2026] 127.0.0.1:59829 [200]: GET /favicon.ico
[Sat Apr 4 13:19:52 2026] 127.0.0.1:59829 Closing
[Sat Apr 4 13:19:52 2026] 127.0.0.1:59742 Accepted
[Sat Apr 4 13:20:29 2026] 127.0.0.1:59742 [200]: GET /monatsvorhersage_old.php?year=2026&month=4
[Sat Apr 4 13:20:29 2026] 127.0.0.1:59742 Closing
[Sat Apr 4 13:20:29 2026] 127.0.0.1:56911 Accepted
[Sat Apr 4 13:20:29 2026] 127.0.0.1:56911 [200]: GET /css/style.css
[Sat Apr 4 13:20:29 2026] 127.0.0.1:56911 Closing
[Sat Apr 4 13:20:29 2026] 127.0.0.1:56768 Accepted
[Sat Apr 4 13:20:29 2026] 127.0.0.1:55966 Accepted
[Sat Apr 4 13:20:29 2026] 127.0.0.1:63374 Accepted
[Sat Apr 4 13:20:29 2026] 127.0.0.1:56768 [200]: GET /js/astronomy.browser.min.js
[Sat Apr 4 13:20:29 2026] 127.0.0.1:55966 [200]: GET /images/star16x16.png
[Sat Apr 4 13:20:29 2026] 127.0.0.1:63374 [200]: GET /images/star16x16_rays.png
[Sat Apr 4 13:20:29 2026] 127.0.0.1:56768 Closing
[Sat Apr 4 13:20:29 2026] 127.0.0.1:55966 Closing
[Sat Apr 4 13:20:29 2026] 127.0.0.1:63374 Closing
[Sat Apr 4 13:20:29 2026] 127.0.0.1:49904 Accepted
[Sat Apr 4 13:20:29 2026] 127.0.0.1:49904 [200]: GET /images/moons/moon4geocron.png
[Sat Apr 4 13:20:29 2026] 127.0.0.1:49904 Closing
[Sat Apr 4 13:20:29 2026] 127.0.0.1:58593 Accepted
[Sat Apr 4 13:20:31 2026] 127.0.0.1:58593 [200]: GET /favicon.ico
[Sat Apr 4 13:20:31 2026] 127.0.0.1:58593 Closing
[Sat Apr 4 13:20:31 2026] 127.0.0.1:52115 Accepted
[Sat Apr 4 13:21:39 2026] 127.0.0.1:52115 Closed without sending a request; it was probably just an unused speculative preconnection
[Sat Apr 4 13:21:39 2026] 127.0.0.1:52115 Closing
[Sat Apr 4 13:23:23 2026] 127.0.0.1:64731 Accepted
[Sat Apr 4 13:23:41 2026] 127.0.0.1:64731 [200]: GET /monatsvorhersage_old.php?year=2026&month=4
[Sat Apr 4 13:23:41 2026] 127.0.0.1:64731 Closing
[Sat Apr 4 13:23:41 2026] 127.0.0.1:58328 Accepted
[Sat Apr 4 13:23:41 2026] 127.0.0.1:58328 [200]: GET /css/style.css
[Sat Apr 4 13:23:41 2026] 127.0.0.1:58328 Closing
[Sat Apr 4 13:23:41 2026] 127.0.0.1:58441 Accepted
[Sat Apr 4 13:23:41 2026] 127.0.0.1:54768 Accepted
[Sat Apr 4 13:23:41 2026] 127.0.0.1:62374 Accepted
[Sat Apr 4 13:23:41 2026] 127.0.0.1:58441 [200]: GET /js/astronomy.browser.min.js
[Sat Apr 4 13:23:41 2026] 127.0.0.1:54768 [200]: GET /images/star16x16.png
[Sat Apr 4 13:23:41 2026] 127.0.0.1:62374 [200]: GET /images/star16x16_rays.png
[Sat Apr 4 13:23:41 2026] 127.0.0.1:58441 Closing
[Sat Apr 4 13:23:41 2026] 127.0.0.1:54768 Closing
[Sat Apr 4 13:23:41 2026] 127.0.0.1:62374 Closing
[Sat Apr 4 13:23:42 2026] 127.0.0.1:63045 Accepted
[Sat Apr 4 13:23:42 2026] 127.0.0.1:63045 [200]: GET /images/moons/moon4geocron.png
[Sat Apr 4 13:23:42 2026] 127.0.0.1:63045 Closing
[Sat Apr 4 13:23:42 2026] 127.0.0.1:58545 Accepted
[Sat Apr 4 13:23:43 2026] 127.0.0.1:58545 [200]: GET /favicon.ico
[Sat Apr 4 13:23:43 2026] 127.0.0.1:58545 Closing
[Sat Apr 4 13:23:43 2026] 127.0.0.1:62655 Accepted
[Sat Apr 4 13:25:25 2026] 127.0.0.1:62655 Closed without sending a request; it was probably just an unused speculative preconnection
[Sat Apr 4 13:25:25 2026] 127.0.0.1:62655 Closing
[Sat Apr 4 13:25:56 2026] 127.0.0.1:61286 Accepted
[Sat Apr 4 13:26:14 2026] 127.0.0.1:61286 [200]: GET /monatsvorhersage_old.php?year=2026&month=4
[Sat Apr 4 13:26:14 2026] 127.0.0.1:61286 Closing
[Sat Apr 4 13:26:14 2026] 127.0.0.1:50887 Accepted
[Sat Apr 4 13:26:14 2026] 127.0.0.1:50887 [200]: GET /css/style.css
[Sat Apr 4 13:26:14 2026] 127.0.0.1:50887 Closing
[Sat Apr 4 13:26:14 2026] 127.0.0.1:51178 Accepted
[Sat Apr 4 13:26:14 2026] 127.0.0.1:62601 Accepted
[Sat Apr 4 13:26:14 2026] 127.0.0.1:50633 Accepted
[Sat Apr 4 13:26:14 2026] 127.0.0.1:51178 [200]: GET /js/astronomy.browser.min.js
[Sat Apr 4 13:26:14 2026] 127.0.0.1:62601 [200]: GET /images/star16x16.png
[Sat Apr 4 13:26:14 2026] 127.0.0.1:50633 [200]: GET /images/star16x16_rays.png
[Sat Apr 4 13:26:14 2026] 127.0.0.1:51178 Closing
[Sat Apr 4 13:26:14 2026] 127.0.0.1:62601 Closing
[Sat Apr 4 13:26:14 2026] 127.0.0.1:50633 Closing
[Sat Apr 4 13:26:15 2026] 127.0.0.1:64777 Accepted
[Sat Apr 4 13:26:15 2026] 127.0.0.1:64777 [200]: GET /images/moons/moon4geocron.png
[Sat Apr 4 13:26:15 2026] 127.0.0.1:64777 Closing
[Sat Apr 4 13:26:15 2026] 127.0.0.1:54587 Accepted
[Sat Apr 4 13:26:16 2026] 127.0.0.1:54587 [200]: GET /favicon.ico
[Sat Apr 4 13:26:16 2026] 127.0.0.1:54587 Closing
[Sat Apr 4 13:26:16 2026] 127.0.0.1:59843 Accepted
[Sat Apr 4 13:27:02 2026] 127.0.0.1:59843 [200]: GET /monatsvorhersage_old.php?year=2026&month=4
[Sat Apr 4 13:27:02 2026] 127.0.0.1:59843 Closing
[Sat Apr 4 13:27:02 2026] 127.0.0.1:60256 Accepted
[Sat Apr 4 13:27:02 2026] 127.0.0.1:60256 [200]: GET /css/style.css
[Sat Apr 4 13:27:02 2026] 127.0.0.1:60256 Closing
[Sat Apr 4 13:27:03 2026] 127.0.0.1:50709 Accepted
[Sat Apr 4 13:27:03 2026] 127.0.0.1:50709 [200]: GET /js/astronomy.browser.min.js
[Sat Apr 4 13:27:03 2026] 127.0.0.1:50709 Closing
[Sat Apr 4 13:27:03 2026] 127.0.0.1:55625 Accepted
[Sat Apr 4 13:27:03 2026] 127.0.0.1:61166 Accepted
[Sat Apr 4 13:27:03 2026] 127.0.0.1:55625 [200]: GET /images/star16x16.png
[Sat Apr 4 13:27:03 2026] 127.0.0.1:61166 [200]: GET /images/star16x16_rays.png
[Sat Apr 4 13:27:03 2026] 127.0.0.1:55625 Closing
[Sat Apr 4 13:27:03 2026] 127.0.0.1:61166 Closing
[Sat Apr 4 13:27:03 2026] 127.0.0.1:56924 Accepted
[Sat Apr 4 13:27:03 2026] 127.0.0.1:56924 [200]: GET /images/moons/moon4geocron.png
[Sat Apr 4 13:27:03 2026] 127.0.0.1:56924 Closing
[Sat Apr 4 13:27:03 2026] 127.0.0.1:58839 Accepted
[Sat Apr 4 13:27:04 2026] 127.0.0.1:58839 [200]: GET /favicon.ico
[Sat Apr 4 13:27:04 2026] 127.0.0.1:58839 Closing
[Sat Apr 4 13:27:04 2026] 127.0.0.1:59023 Accepted
[Sat Apr 4 13:28:04 2026] 127.0.0.1:59023 Closed without sending a request; it was probably just an unused speculative preconnection
[Sat Apr 4 13:28:04 2026] 127.0.0.1:59023 Closing
[Sat Apr 4 13:28:24 2026] 127.0.0.1:65445 Accepted
[Sat Apr 4 13:28:43 2026] 127.0.0.1:65445 [200]: GET /monatsvorhersage_old.php?year=2026&month=6
[Sat Apr 4 13:28:43 2026] 127.0.0.1:65445 Closing
[Sat Apr 4 13:28:43 2026] 127.0.0.1:57680 Accepted
[Sat Apr 4 13:28:43 2026] 127.0.0.1:57680 [200]: GET /css/style.css
[Sat Apr 4 13:28:43 2026] 127.0.0.1:57680 Closing
[Sat Apr 4 13:28:43 2026] 127.0.0.1:64580 Accepted
[Sat Apr 4 13:28:43 2026] 127.0.0.1:59058 Accepted
[Sat Apr 4 13:28:43 2026] 127.0.0.1:59321 Accepted
[Sat Apr 4 13:28:43 2026] 127.0.0.1:64580 [200]: GET /js/astronomy.browser.min.js
[Sat Apr 4 13:28:43 2026] 127.0.0.1:59058 [200]: GET /images/star16x16.png
[Sat Apr 4 13:28:43 2026] 127.0.0.1:59321 [200]: GET /images/star16x16_rays.png
[Sat Apr 4 13:28:43 2026] 127.0.0.1:64580 Closing
[Sat Apr 4 13:28:43 2026] 127.0.0.1:59058 Closing
[Sat Apr 4 13:28:43 2026] 127.0.0.1:59321 Closing
[Sat Apr 4 13:28:43 2026] 127.0.0.1:57091 Accepted
[Sat Apr 4 13:28:43 2026] 127.0.0.1:57091 [200]: GET /images/moons/moon4geocron.png
[Sat Apr 4 13:28:43 2026] 127.0.0.1:57091 Closing
[Sat Apr 4 13:28:44 2026] 127.0.0.1:64529 Accepted
[Sat Apr 4 13:28:45 2026] 127.0.0.1:64529 [200]: GET /favicon.ico
[Sat Apr 4 13:28:45 2026] 127.0.0.1:64529 Closing
[Sat Apr 4 13:28:45 2026] 127.0.0.1:57521 Accepted
[Sat Apr 4 13:29:45 2026] 127.0.0.1:57521 Closed without sending a request; it was probably just an unused speculative preconnection
[Sat Apr 4 13:29:45 2026] 127.0.0.1:57521 Closing
[Sat Apr 4 13:29:45 2026] 127.0.0.1:60715 Accepted
[Sat Apr 4 13:30:04 2026] 127.0.0.1:60715 [200]: GET /monatsvorhersage_old.php?year=2026&month=7
[Sat Apr 4 13:30:04 2026] 127.0.0.1:60715 Closing
[Sat Apr 4 13:30:04 2026] 127.0.0.1:63768 Accepted
[Sat Apr 4 13:30:04 2026] 127.0.0.1:63768 [200]: GET /css/style.css
[Sat Apr 4 13:30:04 2026] 127.0.0.1:63768 Closing
[Sat Apr 4 13:30:04 2026] 127.0.0.1:53424 Accepted
[Sat Apr 4 13:30:04 2026] 127.0.0.1:63669 Accepted
[Sat Apr 4 13:30:04 2026] 127.0.0.1:61253 Accepted
[Sat Apr 4 13:30:04 2026] 127.0.0.1:53424 [200]: GET /js/astronomy.browser.min.js
[Sat Apr 4 13:30:04 2026] 127.0.0.1:63669 [200]: GET /images/star16x16.png
[Sat Apr 4 13:30:04 2026] 127.0.0.1:61253 [200]: GET /images/star16x16_rays.png
[Sat Apr 4 13:30:04 2026] 127.0.0.1:53424 Closing
[Sat Apr 4 13:30:04 2026] 127.0.0.1:63669 Closing
[Sat Apr 4 13:30:04 2026] 127.0.0.1:61253 Closing
[Sat Apr 4 13:30:05 2026] 127.0.0.1:64094 Accepted
[Sat Apr 4 13:30:05 2026] 127.0.0.1:64094 [200]: GET /images/moons/moon4geocron.png
[Sat Apr 4 13:30:05 2026] 127.0.0.1:64094 Closing
[Sat Apr 4 13:30:05 2026] 127.0.0.1:51726 Accepted
[Sat Apr 4 13:30:06 2026] 127.0.0.1:51726 [200]: GET /favicon.ico
[Sat Apr 4 13:30:06 2026] 127.0.0.1:51726 Closing
[Sat Apr 4 13:30:06 2026] 127.0.0.1:52200 Accepted
[Sat Apr 4 13:30:50 2026] 127.0.0.1:52200 [200]: GET /monatsvorhersage_old.php?year=2026&month=11
[Sat Apr 4 13:30:50 2026] 127.0.0.1:52200 Closing
[Sat Apr 4 13:30:50 2026] 127.0.0.1:62487 Accepted
[Sat Apr 4 13:30:50 2026] 127.0.0.1:62487 [200]: GET /css/style.css
[Sat Apr 4 13:30:50 2026] 127.0.0.1:62487 Closing
[Sat Apr 4 13:30:50 2026] 127.0.0.1:60781 Accepted
[Sat Apr 4 13:30:50 2026] 127.0.0.1:60781 [200]: GET /js/astronomy.browser.min.js
[Sat Apr 4 13:30:50 2026] 127.0.0.1:60781 Closing
[Sat Apr 4 13:30:50 2026] 127.0.0.1:53275 Accepted
[Sat Apr 4 13:30:50 2026] 127.0.0.1:49256 Accepted
[Sat Apr 4 13:30:50 2026] 127.0.0.1:53275 [200]: GET /images/star16x16.png
[Sat Apr 4 13:30:50 2026] 127.0.0.1:49256 [200]: GET /images/star16x16_rays.png
[Sat Apr 4 13:30:50 2026] 127.0.0.1:53275 Closing
[Sat Apr 4 13:30:50 2026] 127.0.0.1:49256 Closing
[Sat Apr 4 13:30:51 2026] 127.0.0.1:63389 Accepted
[Sat Apr 4 13:30:51 2026] 127.0.0.1:63389 [200]: GET /images/moons/moon4geocron.png
[Sat Apr 4 13:30:51 2026] 127.0.0.1:63389 Closing
[Sat Apr 4 13:30:51 2026] 127.0.0.1:61832 Accepted
[Sat Apr 4 13:30:52 2026] 127.0.0.1:61832 [200]: GET /favicon.ico
[Sat Apr 4 13:30:52 2026] 127.0.0.1:61832 Closing
[Sat Apr 4 13:30:52 2026] 127.0.0.1:59473 Accepted
[Sat Apr 4 13:31:32 2026] 127.0.0.1:59473 [200]: GET /monatsvorhersage_old.php?year=2026&month=12
[Sat Apr 4 13:31:32 2026] 127.0.0.1:59473 Closing
[Sat Apr 4 13:31:32 2026] 127.0.0.1:65461 Accepted
[Sat Apr 4 13:31:32 2026] 127.0.0.1:65461 [200]: GET /css/style.css
[Sat Apr 4 13:31:32 2026] 127.0.0.1:65461 Closing
[Sat Apr 4 13:31:32 2026] 127.0.0.1:62904 Accepted
[Sat Apr 4 13:31:32 2026] 127.0.0.1:62904 [200]: GET /js/astronomy.browser.min.js
[Sat Apr 4 13:31:32 2026] 127.0.0.1:62904 Closing
[Sat Apr 4 13:31:32 2026] 127.0.0.1:54230 Accepted
[Sat Apr 4 13:31:32 2026] 127.0.0.1:56692 Accepted
[Sat Apr 4 13:31:32 2026] 127.0.0.1:54230 [200]: GET /images/star16x16.png
[Sat Apr 4 13:31:32 2026] 127.0.0.1:56692 [200]: GET /images/star16x16_rays.png
[Sat Apr 4 13:31:32 2026] 127.0.0.1:54230 Closing
[Sat Apr 4 13:31:32 2026] 127.0.0.1:56692 Closing
[Sat Apr 4 13:31:33 2026] 127.0.0.1:55075 Accepted
[Sat Apr 4 13:31:33 2026] 127.0.0.1:55075 [200]: GET /images/moons/moon4geocron.png
[Sat Apr 4 13:31:33 2026] 127.0.0.1:55075 Closing
[Sat Apr 4 13:31:33 2026] 127.0.0.1:64547 Accepted
[Sat Apr 4 13:31:34 2026] 127.0.0.1:64547 [200]: GET /favicon.ico
[Sat Apr 4 13:31:34 2026] 127.0.0.1:64547 Closing
[Sat Apr 4 13:31:34 2026] 127.0.0.1:55972 Accepted
[Sat Apr 4 13:32:38 2026] 127.0.0.1:55972 [200]: GET /monatsvorhersage_old.php?year=2026&month=6
[Sat Apr 4 13:32:38 2026] 127.0.0.1:55972 Closing
[Sat Apr 4 13:32:38 2026] 127.0.0.1:59385 Accepted
[Sat Apr 4 13:32:38 2026] 127.0.0.1:59385 [200]: GET /css/style.css
[Sat Apr 4 13:32:38 2026] 127.0.0.1:59385 Closing
[Sat Apr 4 13:32:38 2026] 127.0.0.1:57529 Accepted
[Sat Apr 4 13:32:38 2026] 127.0.0.1:57529 [200]: GET /js/astronomy.browser.min.js
[Sat Apr 4 13:32:38 2026] 127.0.0.1:57529 Closing
[Sat Apr 4 13:32:38 2026] 127.0.0.1:52680 Accepted
[Sat Apr 4 13:32:38 2026] 127.0.0.1:53823 Accepted
[Sat Apr 4 13:32:38 2026] 127.0.0.1:52680 [200]: GET /images/star16x16.png
[Sat Apr 4 13:32:38 2026] 127.0.0.1:53823 [200]: GET /images/star16x16_rays.png
[Sat Apr 4 13:32:38 2026] 127.0.0.1:52680 Closing
[Sat Apr 4 13:32:38 2026] 127.0.0.1:53823 Closing
[Sat Apr 4 13:32:38 2026] 127.0.0.1:55373 Accepted
[Sat Apr 4 13:32:38 2026] 127.0.0.1:55373 [200]: GET /images/moons/moon4geocron.png
[Sat Apr 4 13:32:38 2026] 127.0.0.1:55373 Closing
[Sat Apr 4 13:32:39 2026] 127.0.0.1:57222 Accepted
[Sat Apr 4 13:32:40 2026] 127.0.0.1:57222 [200]: GET /favicon.ico
[Sat Apr 4 13:32:40 2026] 127.0.0.1:57222 Closing
[Sat Apr 4 13:32:40 2026] 127.0.0.1:65119 Accepted
[Sat Apr 4 13:33:49 2026] 127.0.0.1:65119 Closed without sending a request; it was probably just an unused speculative preconnection
[Sat Apr 4 13:33:49 2026] 127.0.0.1:65119 Closing
[Sat Apr 4 13:38:06 2026] 127.0.0.1:58712 Accepted
[Sat Apr 4 13:38:25 2026] 127.0.0.1:58712 [200]: GET /monatsvorhersage_old.php?year=2026&month=6
[Sat Apr 4 13:38:25 2026] 127.0.0.1:58712 Closing
[Sat Apr 4 13:38:25 2026] 127.0.0.1:49291 Accepted
[Sat Apr 4 13:38:25 2026] 127.0.0.1:49291 [200]: GET /css/style.css
[Sat Apr 4 13:38:25 2026] 127.0.0.1:49291 Closing
[Sat Apr 4 13:38:25 2026] 127.0.0.1:54898 Accepted
[Sat Apr 4 13:38:25 2026] 127.0.0.1:64556 Accepted
[Sat Apr 4 13:38:25 2026] 127.0.0.1:52911 Accepted
[Sat Apr 4 13:38:25 2026] 127.0.0.1:54898 [200]: GET /js/astronomy.browser.min.js
[Sat Apr 4 13:38:25 2026] 127.0.0.1:64556 [200]: GET /images/star16x16.png
[Sat Apr 4 13:38:25 2026] 127.0.0.1:52911 [200]: GET /images/star16x16_rays.png
[Sat Apr 4 13:38:25 2026] 127.0.0.1:54898 Closing
[Sat Apr 4 13:38:25 2026] 127.0.0.1:64556 Closing
[Sat Apr 4 13:38:25 2026] 127.0.0.1:52911 Closing
[Sat Apr 4 13:38:25 2026] 127.0.0.1:52036 Accepted
[Sat Apr 4 13:38:25 2026] 127.0.0.1:52036 [200]: GET /images/moons/moon4geocron.png
[Sat Apr 4 13:38:25 2026] 127.0.0.1:52036 Closing
[Sat Apr 4 13:38:25 2026] 127.0.0.1:51944 Accepted
[Sat Apr 4 13:38:27 2026] 127.0.0.1:51944 [200]: GET /favicon.ico
[Sat Apr 4 13:38:27 2026] 127.0.0.1:51944 Closing
[Sat Apr 4 13:38:27 2026] 127.0.0.1:51201 Accepted
[Sat Apr 4 13:40:25 2026] 127.0.0.1:51201 Closed without sending a request; it was probably just an unused speculative preconnection
[Sat Apr 4 13:40:25 2026] 127.0.0.1:51201 Closing
[Sat Apr 4 13:44:22 2026] 127.0.0.1:62240 Accepted
[Sat Apr 4 13:44:45 2026] 127.0.0.1:62240 [200]: GET /monatsvorhersage_old.php?year=2026&month=6
[Sat Apr 4 13:44:45 2026] 127.0.0.1:62240 Closing
[Sat Apr 4 13:44:45 2026] 127.0.0.1:56366 Accepted
[Sat Apr 4 13:44:45 2026] 127.0.0.1:56366 [200]: GET /css/style.css
[Sat Apr 4 13:44:45 2026] 127.0.0.1:56366 Closing
[Sat Apr 4 13:44:46 2026] 127.0.0.1:61203 Accepted
[Sat Apr 4 13:44:46 2026] 127.0.0.1:60789 Accepted
[Sat Apr 4 13:44:46 2026] 127.0.0.1:58216 Accepted
[Sat Apr 4 13:44:46 2026] 127.0.0.1:61203 [200]: GET /js/astronomy.browser.min.js
[Sat Apr 4 13:44:46 2026] 127.0.0.1:60789 [200]: GET /images/star16x16.png
[Sat Apr 4 13:44:46 2026] 127.0.0.1:58216 [200]: GET /images/star16x16_rays.png
[Sat Apr 4 13:44:46 2026] 127.0.0.1:61203 Closing
[Sat Apr 4 13:44:46 2026] 127.0.0.1:60789 Closing
[Sat Apr 4 13:44:46 2026] 127.0.0.1:58216 Closing
[Sat Apr 4 13:44:46 2026] 127.0.0.1:51748 Accepted
[Sat Apr 4 13:44:46 2026] 127.0.0.1:51748 [200]: GET /images/moons/moon4geocron.png
[Sat Apr 4 13:44:46 2026] 127.0.0.1:51748 Closing
[Sat Apr 4 13:44:46 2026] 127.0.0.1:58822 Accepted
[Sat Apr 4 13:44:48 2026] 127.0.0.1:58822 [200]: GET /favicon.ico
[Sat Apr 4 13:44:48 2026] 127.0.0.1:58822 Closing
[Sat Apr 4 13:44:48 2026] 127.0.0.1:59663 Accepted
[Sat Apr 4 13:45:47 2026] 127.0.0.1:59663 [200]: GET /monatsvorhersage_old.php?year=2026&month=3
[Sat Apr 4 13:45:47 2026] 127.0.0.1:59663 Closing
[Sat Apr 4 13:45:47 2026] 127.0.0.1:58444 Accepted
[Sat Apr 4 13:45:47 2026] 127.0.0.1:58444 [200]: GET /css/style.css
[Sat Apr 4 13:45:47 2026] 127.0.0.1:58444 Closing
[Sat Apr 4 13:45:47 2026] 127.0.0.1:54748 Accepted
[Sat Apr 4 13:45:47 2026] 127.0.0.1:50160 Accepted
[Sat Apr 4 13:45:47 2026] 127.0.0.1:63963 Accepted
[Sat Apr 4 13:45:47 2026] 127.0.0.1:54748 [200]: GET /js/astronomy.browser.min.js
[Sat Apr 4 13:45:47 2026] 127.0.0.1:50160 [200]: GET /images/star16x16.png
[Sat Apr 4 13:45:47 2026] 127.0.0.1:63963 [200]: GET /images/star16x16_rays.png
[Sat Apr 4 13:45:47 2026] 127.0.0.1:54748 Closing
[Sat Apr 4 13:45:47 2026] 127.0.0.1:50160 Closing
[Sat Apr 4 13:45:47 2026] 127.0.0.1:63963 Closing
[Sat Apr 4 13:45:47 2026] 127.0.0.1:49669 Accepted
[Sat Apr 4 13:45:47 2026] 127.0.0.1:49669 [200]: GET /images/moons/moon4geocron.png
[Sat Apr 4 13:45:47 2026] 127.0.0.1:49669 Closing
[Sat Apr 4 13:45:48 2026] 127.0.0.1:62598 Accepted
[Sat Apr 4 13:45:49 2026] 127.0.0.1:62598 [200]: GET /favicon.ico
[Sat Apr 4 13:45:49 2026] 127.0.0.1:62598 Closing
[Sat Apr 4 13:45:49 2026] 127.0.0.1:64872 Accepted
[Sat Apr 4 13:47:10 2026] 127.0.0.1:64872 [200]: GET /monatsvorhersage_old.php?year=2026&month=3
[Sat Apr 4 13:47:10 2026] 127.0.0.1:64872 Closing
[Sat Apr 4 13:47:10 2026] 127.0.0.1:52271 Accepted
[Sat Apr 4 13:47:10 2026] 127.0.0.1:52271 [200]: GET /css/style.css
[Sat Apr 4 13:47:10 2026] 127.0.0.1:52271 Closing
[Sat Apr 4 13:47:10 2026] 127.0.0.1:60145 Accepted
[Sat Apr 4 13:47:10 2026] 127.0.0.1:60145 [200]: GET /js/astronomy.browser.min.js
[Sat Apr 4 13:47:10 2026] 127.0.0.1:60145 Closing
[Sat Apr 4 13:47:10 2026] 127.0.0.1:64268 Accepted
[Sat Apr 4 13:47:10 2026] 127.0.0.1:54962 Accepted
[Sat Apr 4 13:47:10 2026] 127.0.0.1:64268 [200]: GET /images/star16x16.png
[Sat Apr 4 13:47:10 2026] 127.0.0.1:54962 [200]: GET /images/star16x16_rays.png
[Sat Apr 4 13:47:10 2026] 127.0.0.1:64268 Closing
[Sat Apr 4 13:47:10 2026] 127.0.0.1:54962 Closing
[Sat Apr 4 13:47:10 2026] 127.0.0.1:60276 Accepted
[Sat Apr 4 13:47:10 2026] 127.0.0.1:60276 [200]: GET /images/moons/moon4geocron.png
[Sat Apr 4 13:47:10 2026] 127.0.0.1:60276 Closing
[Sat Apr 4 13:47:11 2026] 127.0.0.1:57233 Accepted
[Sat Apr 4 13:47:12 2026] 127.0.0.1:57233 [200]: GET /favicon.ico
[Sat Apr 4 13:47:12 2026] 127.0.0.1:57233 Closing
[Sat Apr 4 13:47:12 2026] 127.0.0.1:57496 Accepted
[Sat Apr 4 13:48:32 2026] 127.0.0.1:57496 Closed without sending a request; it was probably just an unused speculative preconnection
[Sat Apr 4 13:48:32 2026] 127.0.0.1:57496 Closing
[Sat Apr 4 13:52:34 2026] 127.0.0.1:59903 Accepted
[Sat Apr 4 13:53:02 2026] 127.0.0.1:59903 [200]: GET /monatsvorhersage_old.php?year=2026&month=3
[Sat Apr 4 13:53:02 2026] 127.0.0.1:59903 Closing
[Sat Apr 4 13:53:02 2026] 127.0.0.1:57051 Accepted
[Sat Apr 4 13:53:02 2026] 127.0.0.1:57051 [200]: GET /css/style.css
[Sat Apr 4 13:53:02 2026] 127.0.0.1:57051 Closing
[Sat Apr 4 13:53:02 2026] 127.0.0.1:55642 Accepted
[Sat Apr 4 13:53:02 2026] 127.0.0.1:55642 [200]: GET /js/astronomy.browser.min.js
[Sat Apr 4 13:53:02 2026] 127.0.0.1:55642 Closing
[Sat Apr 4 13:53:02 2026] 127.0.0.1:58427 Accepted
[Sat Apr 4 13:53:02 2026] 127.0.0.1:55928 Accepted
[Sat Apr 4 13:53:02 2026] 127.0.0.1:58427 [200]: GET /images/star16x16.png
[Sat Apr 4 13:53:02 2026] 127.0.0.1:55928 [200]: GET /images/star16x16_rays.png
[Sat Apr 4 13:53:02 2026] 127.0.0.1:58427 Closing
[Sat Apr 4 13:53:02 2026] 127.0.0.1:55928 Closing
[Sat Apr 4 13:53:03 2026] 127.0.0.1:61870 Accepted
[Sat Apr 4 13:53:03 2026] 127.0.0.1:61870 [200]: GET /images/moons/moon4geocron.png
[Sat Apr 4 13:53:03 2026] 127.0.0.1:61870 Closing
[Sat Apr 4 13:53:03 2026] 127.0.0.1:54335 Accepted
[Sat Apr 4 13:53:04 2026] 127.0.0.1:54335 [200]: GET /favicon.ico
[Sat Apr 4 13:53:04 2026] 127.0.0.1:54335 Closing
[Sat Apr 4 13:53:04 2026] 127.0.0.1:62912 Accepted
[Sat Apr 4 13:54:05 2026] 127.0.0.1:62912 Closed without sending a request; it was probably just an unused speculative preconnection
[Sat Apr 4 13:54:05 2026] 127.0.0.1:62912 Closing
[Sat Apr 4 13:54:06 2026] 127.0.0.1:49467 Accepted
[Sat Apr 4 13:54:33 2026] 127.0.0.1:49467 [200]: GET /monatsvorhersage_old.php?year=2026&month=3
[Sat Apr 4 13:54:33 2026] 127.0.0.1:49467 Closing
[Sat Apr 4 13:54:33 2026] 127.0.0.1:54734 Accepted
[Sat Apr 4 13:54:33 2026] 127.0.0.1:54734 [200]: GET /css/style.css
[Sat Apr 4 13:54:34 2026] 127.0.0.1:54734 Closing
[Sat Apr 4 13:54:34 2026] 127.0.0.1:60887 Accepted
[Sat Apr 4 13:54:34 2026] 127.0.0.1:51648 Accepted
[Sat Apr 4 13:54:34 2026] 127.0.0.1:58048 Accepted
[Sat Apr 4 13:54:34 2026] 127.0.0.1:60887 [200]: GET /js/astronomy.browser.min.js
[Sat Apr 4 13:54:34 2026] 127.0.0.1:51648 [200]: GET /images/star16x16.png
[Sat Apr 4 13:54:34 2026] 127.0.0.1:58048 [200]: GET /images/star16x16_rays.png
[Sat Apr 4 13:54:34 2026] 127.0.0.1:60887 Closing
[Sat Apr 4 13:54:34 2026] 127.0.0.1:51648 Closing
[Sat Apr 4 13:54:34 2026] 127.0.0.1:58048 Closing
[Sat Apr 4 13:54:34 2026] 127.0.0.1:64802 Accepted
[Sat Apr 4 13:54:34 2026] 127.0.0.1:64802 [200]: GET /images/moons/moon4geocron.png
[Sat Apr 4 13:54:34 2026] 127.0.0.1:64802 Closing
[Sat Apr 4 13:54:34 2026] 127.0.0.1:63820 Accepted
[Sat Apr 4 13:54:36 2026] 127.0.0.1:63820 [200]: GET /favicon.ico
[Sat Apr 4 13:54:36 2026] 127.0.0.1:63820 Closing
[Sat Apr 4 13:54:36 2026] 127.0.0.1:52071 Accepted
[Sat Apr 4 13:55:40 2026] 127.0.0.1:52071 Closed without sending a request; it was probably just an unused speculative preconnection
[Sat Apr 4 13:55:40 2026] 127.0.0.1:52071 Closing
[Sat Apr 4 13:57:33 2026] 127.0.0.1:51514 Accepted
[Sat Apr 4 13:58:01 2026] 127.0.0.1:51514 [200]: GET /monatsvorhersage_old.php?year=2026&month=3
[Sat Apr 4 13:58:01 2026] 127.0.0.1:51514 Closing
[Sat Apr 4 13:58:01 2026] 127.0.0.1:64035 Accepted
[Sat Apr 4 13:58:01 2026] 127.0.0.1:64035 [200]: GET /css/style.css
[Sat Apr 4 13:58:01 2026] 127.0.0.1:64035 Closing
[Sat Apr 4 13:58:02 2026] 127.0.0.1:62594 Accepted
[Sat Apr 4 13:58:02 2026] 127.0.0.1:62594 [200]: GET /js/astronomy.browser.min.js
[Sat Apr 4 13:58:02 2026] 127.0.0.1:62594 Closing
[Sat Apr 4 13:58:02 2026] 127.0.0.1:57161 Accepted
[Sat Apr 4 13:58:02 2026] 127.0.0.1:64207 Accepted
[Sat Apr 4 13:58:02 2026] 127.0.0.1:57161 [200]: GET /images/star16x16.png
[Sat Apr 4 13:58:02 2026] 127.0.0.1:64207 [200]: GET /images/star16x16_rays.png
[Sat Apr 4 13:58:02 2026] 127.0.0.1:57161 Closing
[Sat Apr 4 13:58:02 2026] 127.0.0.1:64207 Closing
[Sat Apr 4 13:58:02 2026] 127.0.0.1:62295 Accepted
[Sat Apr 4 13:58:02 2026] 127.0.0.1:62295 [200]: GET /images/moons/moon4geocron.png
[Sat Apr 4 13:58:02 2026] 127.0.0.1:62295 Closing
[Sat Apr 4 13:58:02 2026] 127.0.0.1:58421 Accepted
[Sat Apr 4 13:58:03 2026] 127.0.0.1:58421 [200]: GET /favicon.ico
[Sat Apr 4 13:58:03 2026] 127.0.0.1:58421 Closing
[Sat Apr 4 13:58:03 2026] 127.0.0.1:64749 Accepted
[Sat Apr 4 13:59:29 2026] 127.0.0.1:64749 Closed without sending a request; it was probably just an unused speculative preconnection
[Sat Apr 4 13:59:29 2026] 127.0.0.1:64749 Closing
[Sat Apr 4 14:00:30 2026] 127.0.0.1:59949 Accepted
[Sat Apr 4 14:00:58 2026] 127.0.0.1:59949 [200]: GET /monatsvorhersage_old.php?year=2026&month=3
[Sat Apr 4 14:00:58 2026] 127.0.0.1:59949 Closing
[Sat Apr 4 14:00:58 2026] 127.0.0.1:58547 Accepted
[Sat Apr 4 14:00:58 2026] 127.0.0.1:58547 [200]: GET /css/style.css
[Sat Apr 4 14:00:58 2026] 127.0.0.1:58547 Closing
[Sat Apr 4 14:00:59 2026] 127.0.0.1:52032 Accepted
[Sat Apr 4 14:00:59 2026] 127.0.0.1:59328 Accepted
[Sat Apr 4 14:00:59 2026] 127.0.0.1:61499 Accepted
[Sat Apr 4 14:00:59 2026] 127.0.0.1:52032 [200]: GET /js/astronomy.browser.min.js
[Sat Apr 4 14:00:59 2026] 127.0.0.1:59328 [200]: GET /images/star16x16.png
[Sat Apr 4 14:00:59 2026] 127.0.0.1:61499 [200]: GET /images/star16x16_rays.png
[Sat Apr 4 14:00:59 2026] 127.0.0.1:52032 Closing
[Sat Apr 4 14:00:59 2026] 127.0.0.1:59328 Closing
[Sat Apr 4 14:00:59 2026] 127.0.0.1:61499 Closing
[Sat Apr 4 14:00:59 2026] 127.0.0.1:52843 Accepted
[Sat Apr 4 14:00:59 2026] 127.0.0.1:52843 [200]: GET /images/moons/moon4geocron.png
[Sat Apr 4 14:00:59 2026] 127.0.0.1:52843 Closing
[Sat Apr 4 14:00:59 2026] 127.0.0.1:63369 Accepted
[Sat Apr 4 14:01:01 2026] 127.0.0.1:63369 [200]: GET /favicon.ico
[Sat Apr 4 14:01:01 2026] 127.0.0.1:63369 Closing
[Sat Apr 4 14:01:01 2026] 127.0.0.1:61071 Accepted
[Sat Apr 4 14:02:40 2026] 127.0.0.1:61071 Closed without sending a request; it was probably just an unused speculative preconnection
[Sat Apr 4 14:02:40 2026] 127.0.0.1:61071 Closing
+6 -1
View File
@@ -1,3 +1,8 @@
<<<<<<< HEAD
php-server.log
php-server.err.log
php-server-error.log
php-server-error.log
=======
.codex-temp/
php-server-error.log
>>>>>>> fc3264cc2fa76249c40e422de2381eb7c6920c28
+4 -4
View File
@@ -1,13 +1,13 @@
# Dokumentation
Hier liegen die internen Anleitungen und technischen Hilfen fuer das Projekt.
Hier liegen die internen Anleitungen und technischen Hilfen für das Projekt.
## Verfuegbar
## Verfügbar
- [`docs/README_PROJEKT.md`](README_PROJEKT.md)
Grundsaetzliche Erklaerung der Website, ihrer Bereiche und Datenfluesse
Grundsätzliche Erklärung der Website, ihrer Bereiche und Datenflüsse
- [`docs/README_LOCAL_WEBSERVER.md`](README_LOCAL_WEBSERVER.md)
Lokale Startanleitung fuer den PHP-Webserver auf den bekannten Arbeitsplaetzen
Lokale Startanleitung für den PHP-Webserver auf den bekannten Arbeitsplätzen
- [`docs/README_RECHTESYSTEM.md`](README_RECHTESYSTEM.md)
Beschreibung des aktuellen Rollen- und Seitenrechtesystems
+2 -2
View File
@@ -1,6 +1,6 @@
# Lokaler PHP-Webserver
Diese Anleitung enthaelt die lokalen Startbefehle fuer mehrere Rechner.
Diese Anleitung enthält die lokalen Startbefehle für mehrere Rechner.
## ARBEITSPLATZTM
@@ -80,4 +80,4 @@ Ctrl + C
## Hinweis
Der Webserver nutzt den eingebauten PHP-Entwicklungsserver und ist fuer lokale Entwicklung gedacht.
Der Webserver nutzt den eingebauten PHP-Entwicklungsserver und ist für lokale Entwicklung gedacht.
+32 -20
View File
@@ -1,6 +1,6 @@
# Projektueberblick
# Projektüberblick
Diese Datei beschreibt die Website grundsaetzlich: Worum es geht, welche Bereiche es gibt und wie die wichtigsten Bausteine zusammenspielen.
Diese Datei beschreibt die Website grundsätzlich: Worum es geht, welche Bereiche es gibt und wie die wichtigsten Bausteine zusammenspielen.
## Zweck der Website
@@ -8,16 +8,16 @@ Diese Datei beschreibt die Website grundsaetzlich: Worum es geht, welche Bereich
Die Seite verbindet:
- oeffentliche Astronomie-Werkzeuge
- persoenliche Benutzerfunktionen
- Import- und Auswerteprozesse fuer astronomische Daten
- Telegram-Anbindung fuer Benachrichtigungen und Tests
- öffentliche Astronomie-Werkzeuge
- persönliche Benutzerfunktionen
- Import- und Auswerteprozesse für astronomische Daten
- Telegram-Anbindung für Benachrichtigungen und Tests
## Hauptbereiche
Die Website besteht im Kern aus vier Bereichen.
## 1. Oeffentliche Astronomie-Seiten
## 1. Öffentliche Astronomie-Seiten
Unter [`public/`](../public) liegen die eigentlichen Seiten der Website.
@@ -26,7 +26,7 @@ Typische Inhalte sind:
- Mond und Mondphasen
- Planetensichtbarkeit
- Sternenhimmel
- Meteorstroeme
- Meteorströme
- Finsternisse und Bedeckungen
- Geocron und Geocron 3D
- Vorhersage-Seiten wie Monats- und Wochenvorhersage
@@ -48,7 +48,7 @@ Wichtige Konto-Seiten:
- [`public/settings.php`](../public/settings.php)
- [`public/my_favorites.php`](../public/my_favorites.php)
Benutzer koennen dort unter anderem:
Benutzer können dort unter anderem:
- ihr Konto verwalten
- Telegram verbinden
@@ -67,9 +67,9 @@ Aktuell gibt es:
Wichtig:
- `admin` und `master` haben Vollzugriff auf die geschuetzten Seiten
- `admin` und `master` haben Vollzugriff auf die geschützten Seiten
- normale Benutzer erhalten gezielte Seitenfreigaben
- das Header-Menue passt sich an die Seitenrechte an
- das Header-Menü passt sich an die Seitenrechte an
Die Details dazu stehen in:
@@ -87,17 +87,17 @@ Wichtige Skripte liegen unter:
Beispiele:
- [`scripts/import/import_tcrb.php`](../scripts/import/import_tcrb.php)
Importiert AAVSO-WebObs-Daten fuer T CrB
Importiert AAVSO-WebObs-Daten für T CrB
- [`scripts/import/import_celestrak_active.php`](../scripts/import/import_celestrak_active.php)
Importiert Satellitendaten
- [`scripts/telegram/telegram_cron.php`](../scripts/telegram/telegram_cron.php)
Fuehrt Telegram-Aufgaben zeitgesteuert aus
Führt Telegram-Aufgaben zeitgesteuert aus
## T CrB
Ein aktueller Spezialbereich ist `T CrB`.
Dazu gehoeren:
Dazu gehören:
- Import der AAVSO-Daten
- Speicherung in der Datenbank
@@ -114,10 +114,10 @@ Wichtige Dateien:
Die Website besitzt eine Telegram-Anbindung.
Damit koennen Benutzer:
Damit können Benutzer:
- ihr Konto mit Telegram verknuepfen
- spaeter Nachrichten oder Diagramme erhalten
- ihr Konto mit Telegram verknüpfen
- später Nachrichten oder Diagramme erhalten
Wichtige Bausteine:
@@ -134,7 +134,7 @@ Vereinfacht sieht der Ablauf so aus:
1. Import-Skripte holen astronomische Daten aus externen Quellen
2. Die Daten werden in der Datenbank gespeichert
3. Seiten unter `public/` lesen diese Daten aus und stellen sie dar
4. Cron-Skripte koennen daraus Telegram-Nachrichten oder andere Folgeaktionen erzeugen
4. Cron-Skripte können daraus Telegram-Nachrichten oder andere Folgeaktionen erzeugen
## Technischer Aufbau
@@ -151,16 +151,28 @@ Wichtige Verzeichnisse:
- [`docs/`](.)
Projektdokumentation
## Fuer neue Arbeit an der Seite wichtig
## Für neue Arbeit an der Seite wichtig
Wenn neue Funktionen dazukommen, sollte man immer an diese Punkte denken:
- braucht die Seite einen Eintrag im Rechtesystem
- soll sie im Header-Menue erscheinen
- soll sie im Header-Menü erscheinen
- braucht sie Datenimport oder Cron
- braucht sie Telegram-Anbindung
- braucht sie Dokumentation in `docs`
## Schreibweise
Für deutsche Ausgabetexte in UI, Meldungen, Dialogen und Dokumentation sollen normale Umlaute und `ß` verwendet werden.
Für technische Bezeichner soll das nicht automatisch gelten, insbesondere bei:
- Dateinamen
- URLs und Pfaden
- Funktionsnamen
- Variablennamen
- Datenbankspalten, Keys und anderen Schnittstellen
## Passende Zusatzdokumente
- [`docs/README_LOCAL_WEBSERVER.md`](README_LOCAL_WEBSERVER.md)
+34 -34
View File
@@ -1,22 +1,22 @@
# Rechtesystem
Diese Anleitung beschreibt das aktuelle Rechtesystem fuer die Seiten unter `public/`.
Diese Anleitung beschreibt das aktuelle Rechtesystem für die Seiten unter `public/`.
## Grundidee
Aktuell gilt:
- `admin` und `master` haben Vollzugriff auf alle Seiten unter `public/`
- normale Benutzer sehen standardmaessig keine geschuetzten Seiten
- einzelne Seiten koennen pro Benutzer durch einen `master` freigegeben werden
- das Header-Menue passt sich an die hinterlegten Seitenrechte an
- normale Benutzer sehen standardmäßig keine geschützten Seiten
- einzelne Seiten können pro Benutzer durch einen `master` freigegeben werden
- das Header-Menü passt sich an die hinterlegten Seitenrechte an
## Rollen
Es gibt zurzeit diese Rollen:
- `user`
Normale Benutzerrolle ohne automatischen Zugriff auf die geschuetzten Seiten
Normale Benutzerrolle ohne automatischen Zugriff auf die geschützten Seiten
- `admin`
Vollzugriff auf alle Seiten unter `public/`
- `master`
@@ -29,15 +29,15 @@ Die Rollen-Helfer liegen in [`public/auth_helpers.php`](../public/auth_helpers.p
Die zentrale Rechtebasis besteht aus drei Bausteinen:
- [`public/auth_helpers.php`](../public/auth_helpers.php)
Enthaelt Rollen-Helfer, Allowlist und die Liste der freigebbaren Seiten
Enthält Rollen-Helfer, Allowlist und die Liste der freigebbaren Seiten
- [`public/header.php`](../public/header.php)
Prueft beim Aufruf einer Seite den Zugriff und baut das Menue passend zu den Rechten
Prüft beim Aufruf einer Seite den Zugriff und baut das Menü passend zu den Rechten
- [`public/admin_users.php`](../public/admin_users.php)
Enthaelt die Benutzerverwaltung und die Seitenmatrix fuer `master`
Enthält die Benutzerverwaltung und die Seitenmatrix für `master`
## Datenbank
Fuer die Seitenfreigaben wird diese Tabelle verwendet:
Für die Seitenfreigaben wird diese Tabelle verwendet:
- `app_user_page_permissions`
@@ -54,10 +54,10 @@ Wichtige Spalten:
Wichtig:
- Nicht nur der Dateiname zaehlt.
- Nicht nur der Dateiname zählt.
- Unterordner-Seiten werden mit vollem relativen Pfad gespeichert.
## Oeffentliche Ausnahmen
## Öffentliche Ausnahmen
Einige Seiten bleiben ohne Seitenfreigabe erreichbar. Diese stehen in der Allowlist in [`public/auth_helpers.php`](../public/auth_helpers.php):
@@ -65,37 +65,37 @@ Einige Seiten bleiben ohne Seitenfreigabe erreichbar. Diese stehen in der Allowl
- `register.php`
- `logout.php`
## Wie der Zugriff geprueft wird
## Wie der Zugriff geprüft wird
Beim Laden einer Seite mit gemeinsamem Header passiert Folgendes:
1. `header.php` ermittelt den aktuellen `page_key`
2. Wenn der Benutzer `admin` oder `master` ist, ist der Zugriff erlaubt
3. Wenn die Seite in der Allowlist steht, ist der Zugriff erlaubt
4. Sonst wird in `app_user_page_permissions` geprueft, ob der Benutzer genau diese Seite sehen darf
4. Sonst wird in `app_user_page_permissions` geprüft, ob der Benutzer genau diese Seite sehen darf
5. Wenn nein, kommt `403 Zugriff verweigert`
Wichtig:
- Eine einzelne Seitenfreigabe macht den Benutzer nicht zu `admin`
- dadurch bleibt das Menue auf seine wirklich erlaubten Seiten beschraenkt
- dadurch bleibt das Menü auf seine wirklich erlaubten Seiten beschränkt
## Wie das Menue funktioniert
## Wie das Menü funktioniert
Das Header-Menue wird ebenfalls in [`public/header.php`](../public/header.php) aufgebaut.
Das Header-Menü wird ebenfalls in [`public/header.php`](../public/header.php) aufgebaut.
Dabei gilt:
- ausgeloggte Besucher sehen nur `Anmelden` und `Registrieren`
- `admin` und `master` sehen das volle Menue
- normale Benutzer sehen nur die Menuepunkte, deren `href` in ihren Seitenfreigaben vorhanden ist
- auch der Kontobereich unten im Menue wird entsprechend gefiltert
- `admin` und `master` sehen das volle Menü
- normale Benutzer sehen nur die Menüpunkte, deren `href` in ihren Seitenfreigaben vorhanden ist
- auch der Kontobereich unten im Menü wird entsprechend gefiltert
Damit das funktioniert, muessen Menuelink und `page_key` exakt zusammenpassen.
Damit das funktioniert, müssen Menülink und `page_key` exakt zusammenpassen.
Beispiel:
- Menuelink: `geocron/index.php`
- Menülink: `geocron/index.php`
- Datenbankeintrag: `geocron/index.php`
## Wie ein Master Seiten freigibt
@@ -105,8 +105,8 @@ Die Freigaben werden in [`public/admin_users.php`](../public/admin_users.php) ve
Ablauf:
1. Als `master` anmelden
2. `Benutzerverwaltung` oeffnen
3. Im Bereich `Seitenfreigaben` den gewuenschten Benutzer waehlen
2. `Benutzerverwaltung` öffnen
3. Im Bereich `Seitenfreigaben` den gewünschten Benutzer wählen
4. Haken bei den erlaubten Seiten setzen
5. Speichern
@@ -121,20 +121,20 @@ Die Liste der freigebaren Seiten steht zentral in [`public/auth_helpers.php`](..
- `manageablePublicPages()`
Nur Seiten, die dort eingetragen sind, koennen:
Nur Seiten, die dort eingetragen sind, können:
- in der Matrix erscheinen
- einzeln fuer Benutzer freigegeben werden
- sauber im Menue gefiltert werden
- einzeln für Benutzer freigegeben werden
- sauber im Menü gefiltert werden
## Neue Seite ins Rechtesystem aufnehmen
Wenn eine neue Seite unter `public/` dazukommt, sind in der Regel diese Schritte noetig:
Wenn eine neue Seite unter `public/` dazukommt, sind in der Regel diese Schritte nötig:
1. Seite anlegen
2. Seite so bauen, dass sie den gemeinsamen Header nutzt
3. In `manageablePublicPages()` eintragen
4. Falls noetig als Menuepunkt in [`public/header.php`](../public/header.php) eintragen
4. Falls nötig als Menüpunkt in [`public/header.php`](../public/header.php) eintragen
5. Darauf achten, dass `href` und `page_key` exakt gleich sind
Beispiel:
@@ -143,13 +143,13 @@ Beispiel:
'beispielseite.php' => 'Beispielseite',
```
Oder fuer Unterordner:
Oder für Unterordner:
```php
'tools/beispiel.php' => 'Beispielseite',
```
## Wichtige Regel fuer Unterordner
## Wichtige Regel für Unterordner
Bei Unterordnern niemals nur `index.php` verwenden.
@@ -161,13 +161,13 @@ Falsch:
- `index.php`
Die Seitenerkennung dafuer macht `currentPublicPageKey()` in [`public/auth_helpers.php`](../public/auth_helpers.php).
Die Seitenerkennung dafür macht `currentPublicPageKey()` in [`public/auth_helpers.php`](../public/auth_helpers.php).
## Aktueller Stand
Der aktuelle Zustand ist bewusst streng:
- standardmaessig nur `admin` oder `master`
- normale Benutzer nur ueber explizite Seitenfreigaben
- standardmäßig nur `admin` oder `master`
- normale Benutzer nur über explizite Seitenfreigaben
Spaeter kann entschieden werden, welche Seiten wieder standardmaessig fuer normale Benutzer sichtbar sein sollen.
Später kann entschieden werden, welche Seiten wieder standardmäßig für normale Benutzer sichtbar sein sollen.
+15 -15
View File
@@ -85,7 +85,7 @@ function sendActivationNotification(string $username, string $email, ?string $di
'',
'Benutzername: ' . $username,
'',
'Viele Gruesse',
'Viele Grüße',
'Astronomiemuseum der Sternwarte Sonneberg',
];
@@ -180,7 +180,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
if ($targetUser && $targetUserId === $currentUserId && in_array($action, ['deactivate_user', 'change_role', 'delete_user'], true)) {
$errors[] = 'Das eigene Administrationskonto kann hier nicht deaktiviert, geloescht oder umgestellt werden.';
$errors[] = 'Das eigene Administrationskonto kann hier nicht deaktiviert, gelöscht oder umgestellt werden.';
}
$currentUserIsMaster = isMasterRole((string) ($currentUser['role'] ?? ''));
@@ -206,7 +206,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
!$currentUserIsMaster &&
$targetUserIsMaster
) {
$errors[] = 'Nur ein Master-Benutzer darf die Rolle master vergeben oder aendern.';
$errors[] = 'Nur ein Master-Benutzer darf die Rolle master vergeben oder ändern.';
}
if (
@@ -214,7 +214,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$requestedRole === 'master' &&
$masterCount > 0
) {
$errors[] = 'Nur ein Master-Benutzer darf die Rolle master vergeben oder aendern.';
$errors[] = 'Nur ein Master-Benutzer darf die Rolle master vergeben oder ändern.';
}
if (
@@ -242,7 +242,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
(int)$targetUser['is_active'] === 1 &&
$activeAdminCount <= 1
) {
$errors[] = 'Der letzte aktive privilegierte Benutzer kann nicht geloescht werden.';
$errors[] = 'Der letzte aktive privilegierte Benutzer kann nicht gelöscht werden.';
}
}
@@ -309,7 +309,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$stmtDelete->execute([
':id' => $targetUserId,
]);
$_SESSION['admin_users_flash_success'] = 'Benutzerkonto wurde geloescht. Zugehoerige Standorte und Satelliten wurden automatisch entfernt.';
$_SESSION['admin_users_flash_success'] = 'Benutzerkonto wurde gelöscht. Zugehörige Standorte und Satelliten wurden automatisch entfernt.';
redirectToAdminUsers();
}
@@ -442,8 +442,8 @@ if ($currentUserIsMaster) {
<div class="admin-users-layout">
<section class="card admin-users-summary-card">
<h2>Uebersicht</h2>
<p class="hint">Hier kannst du Benutzerkonten freischalten, deaktivieren, loeschen und zwischen <strong>user</strong>, <strong>admin</strong> und <strong>master</strong> wechseln.</p>
<h2>Übersicht</h2>
<p class="hint">Hier kannst du Benutzerkonten freischalten, deaktivieren, löschen und zwischen <strong>user</strong>, <strong>admin</strong> und <strong>master</strong> wechseln.</p>
<?php if ($successMessage !== ''): ?>
<p class="msg-success"><?= h($successMessage) ?></p>
@@ -558,7 +558,7 @@ if ($currentUserIsMaster) {
<input type="hidden" name="csrf_token" value="<?= h($_SESSION['admin_users_csrf']) ?>">
<input type="hidden" name="action" value="delete_user">
<input type="hidden" name="user_id" value="<?= (int)$userRow['id'] ?>">
<button type="submit" class="btn btn-danger btn-sm"<?= $isSelf ? ' disabled' : '' ?>>Benutzer loeschen</button>
<button type="submit" class="btn btn-danger btn-sm"<?= $isSelf ? ' disabled' : '' ?>>Benutzer löschen</button>
</form>
</div>
</td>
@@ -572,7 +572,7 @@ if ($currentUserIsMaster) {
<?php if ($currentUserIsMaster): ?>
<section class="card admin-users-table-card">
<h2>Seitenfreigaben</h2>
<p class="hint">Nur Master-Benutzer duerfen hier festlegen, welche Seiten normale Nutzer im Menue sehen und direkt aufrufen duerfen.</p>
<p class="hint">Nur Master-Benutzer dürfen hier festlegen, welche Seiten normale Nutzer im Menü sehen und direkt aufrufen dürfen.</p>
<div class="admin-users-table-wrap">
<table class="data-table admin-users-table">
<thead>
@@ -616,7 +616,7 @@ if ($currentUserIsMaster) {
<?php endforeach; ?>
<td>
<?php if ($isPrivilegedUser): ?>
<span class="hint">Vollzugriff ueber Rolle</span>
<span class="hint">Vollzugriff über Rolle</span>
<?php else: ?>
<button type="submit" class="btn btn-secondary btn-sm" form="<?= h($permissionFormId) ?>">Freigaben speichern</button>
<?php endif; ?>
@@ -643,11 +643,11 @@ if ($currentUserIsMaster) {
<dialog class="admin-dialog" id="deleteUserDialog">
<form method="dialog" class="admin-dialog-card">
<h3>Benutzer loeschen?</h3>
<p id="deleteUserDialogText">Soll dieser Benutzer wirklich geloescht werden?</p>
<h3>Benutzer löschen?</h3>
<p id="deleteUserDialogText">Soll dieser Benutzer wirklich gelöscht werden?</p>
<div class="admin-dialog-actions">
<button type="button" class="btn btn-secondary" data-delete-choice="no">Abbrechen</button>
<button type="button" class="btn btn-danger" data-delete-choice="yes">Loeschen</button>
<button type="button" class="btn btn-danger" data-delete-choice="yes">Löschen</button>
</div>
</form>
</dialog>
@@ -713,7 +713,7 @@ if ($currentUserIsMaster) {
pendingDeleteForm = form;
const username = form.getAttribute('data-username') || 'dieser Benutzer';
deleteDialogText.textContent = 'Benutzer "' + username + '" wirklich loeschen? Standorte und Satelliten dieses Kontos werden ebenfalls entfernt.';
deleteDialogText.textContent = 'Benutzer "' + username + '" wirklich löschen? Standorte und Satelliten dieses Kontos werden ebenfalls entfernt.';
deleteDialog.showModal();
});
});
+2 -2
View File
@@ -63,12 +63,12 @@ function manageablePublicPages(): array
'jupitersystem.php' => 'Jupitersystem',
'tagbogen.php' => 'Tagbogen',
'sternenhimmel.php' => 'Sternenhimmel',
'meteorshowers.php' => 'Meteorstroeme',
'meteorshowers.php' => 'Meteorströme',
'geocron/index.php' => 'Geocron',
'geocron3d.php' => 'Geocron 3D',
'mondfinsternis.php' => 'Mondfinsternis',
'sonnenfinsternis.php' => 'Sonnenfinsternis',
'bedeckung.php' => 'Bedeckungsphaenomene',
'bedeckung.php' => 'Bedeckungsphänomene',
'sternbedeckungen.php' => 'Mond-Stern-Bedeckungen',
'monatsvorhersage.php' => 'Monatsvorhersage',
'wochenvorhersage.php' => 'Wochenvorhersage',
+43 -43
View File
@@ -53,15 +53,15 @@ $bodyClass = 'occ-page';
</div>
<div class="info-card">
<div class="ic-name" id="exoName">HD 209458 b</div>
<div class="ic-tag" id="exoTag">HEISSER JUPITER · 1999</div>
<div class="ic-tag" id="exoTag">HEISSER JUPITER · 1999</div>
<div class="ic-desc" id="exoDesc">Erster Exoplanet mit direkt nachgewiesenem Transit.</div>
</div>
</div>
<div class="ctrl-block">
<div class="ctrl-label">Orbitparameter</div>
<div class="param-row">
<div class="param-head"><span class="param-name">INKLINATION</span><span class="param-val" id="vIncl">86.6°</span></div>
<input type="range" class="sl" id="sIncl" data-tip="Inklination" data-tip-text="Neigung der Planetenbahn zur Sichtlinie. Bei 90° liegt die Bahn exakt vor dem Stern (maximaler Transit). Je kleiner, desto kürzer oder gar kein Transit." min="45" max="90" step="0.1" value="86.6">
<div class="param-head"><span class="param-name">INKLINATION</span><span class="param-val" id="vIncl">86.6°</span></div>
<input type="range" class="sl" id="sIncl" data-tip="Inklination" data-tip-text="Neigung der Planetenbahn zur Sichtlinie. Bei 90° liegt die Bahn exakt vor dem Stern (maximaler Transit). Je kleiner, desto kürzer oder gar kein Transit." min="45" max="90" step="0.1" value="86.6">
</div>
<div class="param-row">
<div class="param-head"><span class="param-name">ABSTAND (a/R*)</span><span class="param-val" id="vDist">8.8</span></div>
@@ -76,7 +76,7 @@ $bodyClass = 'occ-page';
</div>
<div class="param-row">
<div class="param-head"><span class="param-name">ALBEDO</span><span class="param-val" id="vAlb">0.04</span></div>
<input type="range" class="sl" id="sAlb" data-tip="Albedo" data-tip-text="Rückstrahlvermögen des Planeten (0 = schwarz, 1 = perfekter Spiegel). Hohe Albedo erzeugt einen kleinen Helligkeitsbuckel vor dem Transit (Phasenkurve)." min="0" max="1" step="0.01" value="0.04">
<input type="range" class="sl" id="sAlb" data-tip="Albedo" data-tip-text="Rückstrahlvermögen des Planeten (0 = schwarz, 1 = perfekter Spiegel). Hohe Albedo erzeugt einen kleinen Helligkeitsbuckel vor dem Transit (Phasenkurve)." min="0" max="1" step="0.01" value="0.04">
</div>
</div>
</div>
@@ -94,7 +94,7 @@ $bodyClass = 'occ-page';
</div>
<div class="info-card">
<div class="ic-name" id="multiName">TRAPPIST-1</div>
<div class="ic-tag" id="multiTag">ULTRAKUEHLER M-ZWERG · 7 PLANETEN</div>
<div class="ic-tag" id="multiTag">ULTRAKÜHLER M-ZWERG · 7 PLANETEN</div>
<div class="ic-desc" id="multiDesc">Das bekannteste Mehrplanetensystem.</div>
</div>
<div class="planet-legend" id="planetLegend"></div>
@@ -103,7 +103,7 @@ $bodyClass = 'occ-page';
<div class="ctrl-label">Zeitfenster</div>
<div class="param-row">
<div class="param-head"><span class="param-name">ZEITFENSTER (TAGE)</span><span class="param-val" id="vTimeWin">25</span></div>
<input type="range" class="sl" id="sTimeWin" data-tip="Zeitfenster" data-tip-text="Dargestellter Zeitraum der Lichtkurve in Tagen. Längere Fenster zeigen mehr Transitevents mehrerer Planeten." min="5" max="200" step="1" value="25">
<input type="range" class="sl" id="sTimeWin" data-tip="Zeitfenster" data-tip-text="Dargestellter Zeitraum der Lichtkurve in Tagen. Längere Fenster zeigen mehr Transitevents mehrerer Planeten." min="5" max="200" step="1" value="25">
</div>
</div>
</div>
@@ -114,23 +114,23 @@ $bodyClass = 'occ-page';
<div class="ctrl-label">Doppelsternsystem</div>
<div class="sel-wrap">
<select class="sel" id="binSelect">
<option value="algol">Algol (β Persei)</option>
<option value="algol">Algol (β Persei)</option>
<option value="wuma">W Ursae Majoris</option>
<option value="betalyr">Beta Lyrae (β Lyr)</option>
<option value="betalyr">Beta Lyrae (β Lyr)</option>
<option value="custom_bin">Benutzerdefiniert</option>
</select>
</div>
<div class="info-card">
<div class="ic-name" id="binName">Algol</div>
<div class="ic-tag" id="binTag">β PERSEI · P=2.87 TAGE</div>
<div class="ic-tag" id="binTag">β PERSEI · P=2.87 TAGE</div>
<div class="ic-desc" id="binDesc">Der "Daemonenstern" - bekanntester Bedeckungsveraenderlicher.</div>
</div>
</div>
<div class="ctrl-block">
<div class="ctrl-label">Systemparameter</div>
<div class="param-row">
<div class="param-head"><span class="param-name">INKLINATION</span><span class="param-val" id="vBinIncl">81.0°</span></div>
<input type="range" class="sl" id="sBinIncl" data-tip="Inklination" data-tip-text="Neigung der Doppelsternbahn. Bei 90° bedeckt jeder Stern den anderen vollständig (flaches Minimum). Je kleiner, desto flacher die Bedeckung." min="45" max="90" step="0.1" value="81.0">
<div class="param-head"><span class="param-name">INKLINATION</span><span class="param-val" id="vBinIncl">81.0°</span></div>
<input type="range" class="sl" id="sBinIncl" data-tip="Inklination" data-tip-text="Neigung der Doppelsternbahn. Bei 90° bedeckt jeder Stern den anderen vollständig (flaches Minimum). Je kleiner, desto flacher die Bedeckung." min="45" max="90" step="0.1" value="81.0">
</div>
<div class="param-row">
<div class="param-head"><span class="param-name">ABSTAND (a/R1)</span><span class="param-val" id="vBinDist">5.3</span></div>
@@ -152,7 +152,7 @@ $bodyClass = 'occ-page';
<div class="ctrl-label">Kenngroessen</div>
<div class="stats-grid">
<div class="stat-item" data-tip="Primaeres Minimum" data-tip-text="Tiefster Helligkeitsabfall in Prozent. Beim Planeten: (Rp/R*)^2. Beim Doppelstern: Bedeckung des helleren Sterns."><div class="stat-lbl">Prim. Min.</div><div class="stat-val" id="sDepth">--</div></div>
<div class="stat-item" data-tip="Aktueller Flux" data-tip-text="Normierte Helligkeit des Systems zum aktuellen Zeitpunkt (1.0 = volle Helligkeit). Leuchtet grün, wenn gerade ein Transit oder eine Bedeckung stattfindet."><div class="stat-lbl">Flux jetzt</div><div class="stat-val" id="sFlux">1.000</div></div>
<div class="stat-item" data-tip="Aktueller Flux" data-tip-text="Normierte Helligkeit des Systems zum aktuellen Zeitpunkt (1.0 = volle Helligkeit). Leuchtet grün, wenn gerade ein Transit oder eine Bedeckung stattfindet."><div class="stat-lbl">Flux jetzt</div><div class="stat-val" id="sFlux">1.000</div></div>
<div class="stat-item" id="stat-item-3" data-tip="Impact-Parameter b" data-tip-text="Einzelplanet: Minimaler Abstand Planetenmitte-Sternmitte in Sternradien. b=0 -> zentraler Transit, b&lt;1 -> Transit moeglich, b&gt;1 -> kein Transit. Doppelstern: Sekundaeres Minimum. Mehrplaneten: Anzahl der Planeten."><div class="stat-lbl" id="statLbl3">Impact b</div><div class="stat-val" id="sStat3">--</div></div>
<div class="stat-item" id="stat-item-4" data-tip="Weitere Kenngroesse" data-tip-text="Einzelplanet: Ob ein Transit geometrisch moeglich ist (b &lt; 1+Rp/R*). Doppelstern: Umlaufzeit in Tagen. Mehrplaneten: Kuerzeste Periode im System."><div class="stat-lbl" id="statLbl4">Transit</div><div class="stat-val" id="sStat4">--</div></div>
</div>
@@ -207,7 +207,7 @@ $bodyClass = 'occ-page';
<div class="modal-head">
<div>
<div class="modal-head-title">HILFE &amp; PARAMETER</div>
<div class="modal-head-sub">Bedeckungsphänomene · Astronomiemuseum der Sternwarte Sonneberg</div>
<div class="modal-head-sub">Bedeckungsphänomene · Astronomiemuseum der Sternwarte Sonneberg</div>
</div>
<button class="modal-close" id="helpClose">&times;</button>
</div>
@@ -230,7 +230,7 @@ $bodyClass = 'occ-page';
<div class="help-section">
<div class="help-section-title">Planetenparameter</div>
<div class="help-row"><div class="help-key">Radius Rp/R*</div><div class="help-val">Planetenradius relativ zum Sternradius. Die Transit-Tiefe ist direkt proportional zum Flaechenverhaeltnis: Delta F ~= (Rp/R*)^2. Ein Planet mit 10% Sternradius erzeugt 1% Helligkeitsabfall.</div></div>
<div class="help-row"><div class="help-key">Albedo</div><div class="help-val">Rückstrahlvermögen (0 = absolut schwarz, 1 = perfekter Spiegel). Hohe Albedo erzeugt vor dem Transit einen schwachen Helligkeitsbuckel (reflektierte Phasenkurve) und während der Sekundärbedeckung ein Mini-Minimum.</div></div>
<div class="help-row"><div class="help-key">Albedo</div><div class="help-val">Rückstrahlvermögen (0 = absolut schwarz, 1 = perfekter Spiegel). Hohe Albedo erzeugt vor dem Transit einen schwachen Helligkeitsbuckel (reflektierte Phasenkurve) und während der Sekundärbedeckung ein Mini-Minimum.</div></div>
</div>
<div class="help-section">
<div class="help-section-title">Kenngroessen</div>
@@ -243,7 +243,7 @@ $bodyClass = 'occ-page';
<div class="help-tab-content" id="htab-multi">
<div class="help-section">
<div class="help-section-title">Mehrplanetensysteme</div>
<div class="help-row"><div class="help-key">TRAPPIST-1</div><div class="help-val">Sieben felsige Planeten um einen ultrakühlen M-Zwergstern, 39 Lichtjahre entfernt. Alle wurden durch Transits entdeckt. Planeten e, f und g liegen in der habitablen Zone.</div></div>
<div class="help-row"><div class="help-key">TRAPPIST-1</div><div class="help-val">Sieben felsige Planeten um einen ultrakühlen M-Zwergstern, 39 Lichtjahre entfernt. Alle wurden durch Transits entdeckt. Planeten e, f und g liegen in der habitablen Zone.</div></div>
<div class="help-row"><div class="help-key">Kepler-11</div><div class="help-val">Sechs Planeten in sehr engen Orbits - alle dichter am Stern als die Venus. 2010 als erstes bekanntes Sechs-Planeten-System entdeckt.</div></div>
<div class="help-row"><div class="help-key">Sonnensystem</div><div class="help-val">Wie wuerde unser System von aussen aussehen? Alle inneren Planeten auf maximale Transitinklination (90 Grad). Man sieht: Merkur transitet haeufig, Mars sehr selten.</div></div>
</div>
@@ -265,7 +265,7 @@ $bodyClass = 'occ-page';
<div class="help-section-title">Parameter</div>
<div class="help-row"><div class="help-key">R2/R1</div><div class="help-val">Radius des Sekundaersterns relativ zum Primaerstern. Beeinflusst die Tiefe beider Minima und die Transitdauer.</div></div>
<div class="help-row"><div class="help-key">L2/L1</div><div class="help-val">Flaechenhelligkeit des Sekundaersterns. Bei 1.0 gleich hell -> gleich tiefe Minima. Bei kleinen Werten (z.B. Algol: 0.017) ist das sekundaere Minimum kaum sichtbar.</div></div>
<div class="help-row"><div class="help-key">Prim. / Sek. Min.</div><div class="help-val">Tiefe des primären (Sekundärstern bedeckt Primärstern) und sekundären Minimums (umgekehrt) in Prozent des Gesamtlichts.</div></div>
<div class="help-row"><div class="help-key">Prim. / Sek. Min.</div><div class="help-val">Tiefe des primären (Sekundärstern bedeckt Primärstern) und sekundären Minimums (umgekehrt) in Prozent des Gesamtlichts.</div></div>
</div>
</div>
@@ -274,13 +274,13 @@ $bodyClass = 'occ-page';
<div class="help-section">
<div class="help-section-title">Lichtkurve</div>
<div class="help-row"><div class="help-key">X-Achse</div><div class="help-val">Orbitphase (0-360 Grad) beim Einzel- und Doppelstern-Modus. Zeitachse in Tagen im Mehrplaneten-Modus.</div></div>
<div class="help-row"><div class="help-key">Y-Achse</div><div class="help-val">Normierter Flux (1.0 = volle Helligkeit). Werte unter 1.0 zeigen Bedeckungen, Werte über 1.0 reflektiertes Planetenlicht.</div></div>
<div class="help-row"><div class="help-key">Y-Achse</div><div class="help-val">Normierter Flux (1.0 = volle Helligkeit). Werte unter 1.0 zeigen Bedeckungen, Werte über 1.0 reflektiertes Planetenlicht.</div></div>
<div class="help-row"><div class="help-key">Weisser Punkt</div><div class="help-val">Aktuelle Position in der Lichtkurve. Durch Klicken/Ziehen in der Lichtkurve kann die Phase direkt eingestellt werden.</div></div>
<div class="help-row"><div class="help-key">Gestrichelte Linie</div><div class="help-val">Referenzlinie bei Flux = 1.0 (unbedeckter Stern).</div></div>
</div>
<div class="help-section">
<div class="help-section-title">Visualisierung</div>
<div class="help-row"><div class="help-key">3D-Ansicht</div><div class="help-val">Zeigt das System aus Beobachterperspektive. Die gestrichelte Ellipse ist die projizierte Bahn. Der grüne Badge leuchtet auf, wenn ein Transit/eine Bedeckung aktiv ist.</div></div>
<div class="help-row"><div class="help-key">3D-Ansicht</div><div class="help-val">Zeigt das System aus Beobachterperspektive. Die gestrichelte Ellipse ist die projizierte Bahn. Der grüne Badge leuchtet auf, wenn ein Transit/eine Bedeckung aktiv ist.</div></div>
<div class="help-row"><div class="help-key">Geschwindigkeit</div><div class="help-val">0.5x bis 5x beschleunigt oder verlangsamt die Animation. Nuetzlich um schnelle Transitevents zu beobachten oder ruhig zu studieren.</div></div>
</div>
</div>
@@ -297,21 +297,21 @@ const PC=['#70e0a0','#6090e0','#e09040','#e06070','#a070e0','#40c0e0','#e0c040']
const DB_S={
custom: {name:'Benutzerdefiniert',tag:'FREIE PARAMETER',desc:'Stelle alle Parameter manuell ein.',incl:85,dist:8,rr:.12,alb:.1},
hd209458b: {name:'HD 209458 b',tag:'HEISSER JUPITER · 1999 · "OSIRIS"',desc:'Erster Exoplanet mit direkt nachgewiesenem Transit. Wegen seiner expandierenden Atmosphäre auch "Osiris" genannt.',incl:86.6,dist:8.76,rr:.1209,alb:.04},
wasp12b: {name:'WASP-12 b',tag:'HEISSER JUPITER · 2008',desc:'Umkreist seinen Stern in nur 1,09 Tagen - und wird dabei langsam durch Gezeitenkraefte zerrissen.',incl:83.4,dist:3.09,rr:.1175,alb:.06},
hatp32b: {name:'HAT-P-32 b',tag:'HEISSER JUPITER · 2011',desc:'Stark aufgeblähter Riesenplanet mit einer der tiefsten bekannten Transit-Lichtkurven.',incl:88.9,dist:4.12,rr:.150,alb:.08},
kepler7b: {name:'Kepler-7 b',tag:'HEISSER JUPITER · KEPLER · 2010',desc:'Rekord-Albedo von 0.35 - hoechste bekannter Exoplaneten. Helle Wolkenbaender reflektieren das Sternlicht.',incl:85.7,dist:7.32,rr:.0880,alb:.35},
wasp96b: {name:'WASP-96 b',tag:'GASRIESE · JWST-ERSTES LICHT 2022',desc:'Erster Exoplanet dessen Atmosphäre vom James Webb Space Telescope vollständig spektroskopiert wurde.',incl:85.1,dist:9.33,rr:.1148,alb:.10},
'55cnce': {name:'55 Cancri e',tag:'SUPER-ERDE · 2004 · LAVA-WELT',desc:'Eine Welt mit möglicherweise geschmolzener Oberfläche und Lava-Ozeanen. Umkreist seinen Stern in nur 17.7 Stunden.',incl:83.0,dist:3.52,rr:.0182,alb:.30},
trappist1b:{name:'TRAPPIST-1 b',tag:'FELSIGER PLANET · 2016',desc:'Innerster der sieben Planeten des TRAPPIST-1-Systems. Fuer Leben zu heiss, aber wichtiger Vergleichsplanet.',incl:89.6,dist:20.9,rr:.0834,alb:.10},
hd209458b: {name:'HD 209458 b',tag:'HEISSER JUPITER · 1999 · "OSIRIS"',desc:'Erster Exoplanet mit direkt nachgewiesenem Transit. Wegen seiner expandierenden Atmosphäre auch "Osiris" genannt.',incl:86.6,dist:8.76,rr:.1209,alb:.04},
wasp12b: {name:'WASP-12 b',tag:'HEISSER JUPITER · 2008',desc:'Umkreist seinen Stern in nur 1,09 Tagen - und wird dabei langsam durch Gezeitenkräfte zerrissen.',incl:83.4,dist:3.09,rr:.1175,alb:.06},
hatp32b: {name:'HAT-P-32 b',tag:'HEISSER JUPITER · 2011',desc:'Stark aufgeblähter Riesenplanet mit einer der tiefsten bekannten Transit-Lichtkurven.',incl:88.9,dist:4.12,rr:.150,alb:.08},
kepler7b: {name:'Kepler-7 b',tag:'HEISSER JUPITER · KEPLER · 2010',desc:'Rekord-Albedo von 0.35 - höchste bekannter Exoplaneten. Helle Wolkenbänder reflektieren das Sternlicht.',incl:85.7,dist:7.32,rr:.0880,alb:.35},
wasp96b: {name:'WASP-96 b',tag:'GASRIESE · JWST-ERSTES LICHT 2022',desc:'Erster Exoplanet dessen Atmosphäre vom James Webb Space Telescope vollständig spektroskopiert wurde.',incl:85.1,dist:9.33,rr:.1148,alb:.10},
'55cnce': {name:'55 Cancri e',tag:'SUPER-ERDE · 2004 · LAVA-WELT',desc:'Eine Welt mit möglicherweise geschmolzener Oberfläche und Lava-Ozeanen. Umkreist seinen Stern in nur 17.7 Stunden.',incl:83.0,dist:3.52,rr:.0182,alb:.30},
trappist1b:{name:'TRAPPIST-1 b',tag:'FELSIGER PLANET · 2016',desc:'Innerster der sieben Planeten des TRAPPIST-1-Systems. Für Leben zu heiß, aber wichtiger Vergleichsplanet.',incl:89.6,dist:20.9,rr:.0834,alb:.10},
};
const DB_M={
trappist1:{name:'TRAPPIST-1',tag:'ULTRAKUEHLER M-ZWERG · 7 PLANETEN · 2017',desc:'Das bekannteste Mehrplanetensystem mit sieben felsigen Welten, darunter drei in der habitablen Zone. Alle Planeten wurden durch Transits entdeckt.',timeWindow:25,
trappist1:{name:'TRAPPIST-1',tag:'ULTRAKÜHLER M-ZWERG · 7 PLANETEN · 2017',desc:'Das bekannteste Mehrplanetensystem mit sieben felsigen Welten, darunter drei in der habitablen Zone. Alle Planeten wurden durch Transits entdeckt.',timeWindow:25,
planets:[{name:'b',rr:.0834,dist:20.9,incl:89.56,period:1.511},{name:'c',rr:.0839,dist:28.5,incl:89.70,period:2.422},{name:'d',rr:.0617,dist:39.6,incl:89.80,period:4.050},{name:'e',rr:.0730,dist:51.5,incl:89.86,period:6.101},{name:'f',rr:.0780,dist:68.4,incl:89.68,period:9.207},{name:'g',rr:.0880,dist:83.2,incl:89.50,period:12.354},{name:'h',rr:.0550,dist:109,incl:89.76,period:18.767}]},
kepler11:{name:'Kepler-11',tag:'G-ZWERG · 6 PLANETEN · KEPLER · 2010',desc:'Ein kompaktes Sechs-Planeten-System. Alle sechs Planeten wurden gleichzeitig von Kepler entdeckt - damals eine Sensation.',timeWindow:50,
kepler11:{name:'Kepler-11',tag:'G-ZWERG · 6 PLANETEN · KEPLER · 2010',desc:'Ein kompaktes Sechs-Planeten-System. Alle sechs Planeten wurden gleichzeitig von Kepler entdeckt - damals eine Sensation.',timeWindow:50,
planets:[{name:'b',rr:.0191,dist:13.3,incl:88.5,period:10.304},{name:'c',rr:.0262,dist:16.8,incl:88.6,period:13.024},{name:'d',rr:.0280,dist:22.6,incl:89.3,period:22.684},{name:'e',rr:.0360,dist:26.4,incl:88.8,period:31.996},{name:'f',rr:.0264,dist:33.1,incl:89.4,period:46.689},{name:'g',rr:.0358,dist:53.8,incl:89.0,period:118.38}]},
solar:{name:'Sonnensystem',tag:'G2V-STERN · INNERE PLANETEN · TRANSIT-PERSPEKTIVE',desc:'Wie wuerde unser Sonnensystem von aussen als Transitsystem erscheinen? Alle inneren Planeten auf maximale Transit-Inklination.',timeWindow:120,
solar:{name:'Sonnensystem',tag:'G2V-STERN · INNERE PLANETEN · TRANSIT-PERSPEKTIVE',desc:'Wie würde unser Sonnensystem von außen als Transitsystem erscheinen? Alle inneren Planeten auf maximale Transit-Inklination.',timeWindow:120,
planets:[{name:'Merkur',rr:.00351,dist:83.2,incl:90.0,period:87.97},{name:'Venus',rr:.00870,dist:155.5,incl:90.0,period:224.7},{name:'Erde',rr:.00916,dist:215.1,incl:90.0,period:365.25},{name:'Mars',rr:.00487,dist:327.7,incl:90.0,period:686.97}]},
};
@@ -403,14 +403,14 @@ function recompute(){
document.getElementById('statLbl4').textContent='Transit';
document.getElementById('stat-item-3').dataset.tip='Impact-Parameter b';
document.getElementById('stat-item-3').dataset.tipText='Minimaler Abstand Planetenmitte-Sternmitte in Sternradien. b=0 -> zentraler Transit, b<1 -> Transit moeglich, b>1 -> kein Transit. Minimum 45 Grad.';
document.getElementById('stat-item-4').dataset.tip='Transit möglich?';
document.getElementById('stat-item-4').dataset.tip='Transit möglich?';
document.getElementById('stat-item-4').dataset.tipText='Ob ein Transit geometrisch moeglich ist. Abhaengig vom Impact-Parameter b und dem Planetenradius: Transit wenn b < 1 + Rp/R*.';
const el=document.getElementById('sStat4');
const ok=b<1+S.rr;el.textContent=ok?'JA':'NEIN';el.style.color=ok?'#70e0a0':'#e07070';
}else if(S.mode==='binary'){
document.getElementById('statLbl3').textContent='Sek. Min.';
document.getElementById('stat-item-3').dataset.tip='Sekundäres Minimum';
document.getElementById('stat-item-3').dataset.tipText='Tiefe des zweiten Helligkeitseinbruchs, wenn der schwächere Stern hinter dem helleren verschwindet. Bei Algol kaum sichtbar (0.017), bei W UMa fast gleich tief wie das primäre Minimum.';
document.getElementById('stat-item-3').dataset.tip='Sekundäres Minimum';
document.getElementById('stat-item-3').dataset.tipText='Tiefe des zweiten Helligkeitseinbruchs, wenn der schwächere Stern hinter dem helleren verschwindet. Bei Algol kaum sichtbar (0.017), bei W UMa fast gleich tief wie das primäre Minimum.';
document.getElementById('stat-item-4').dataset.tip='Umlaufzeit';
document.getElementById('stat-item-4').dataset.tipText='Periode des Doppelsternsystems in Tagen. W UMa: 0.33 d (8 Stunden!), Algol: 2.87 d, Beta Lyrae: 12.94 d.';
// find secondary minimum: deepest point at least N/4 away from primary minimum
@@ -431,9 +431,9 @@ function recompute(){
const sys=DB_M[S.multiPreset];
document.getElementById('statLbl3').textContent='Planeten';
document.getElementById('stat-item-3').dataset.tip='Anzahl Planeten';
document.getElementById('stat-item-3').dataset.tipText='Anzahl der Planeten im ausgewählten System. TRAPPIST-1: 7 Planeten, Kepler-11: 6 Planeten, Sonnensystem: 4 innere Planeten.';
document.getElementById('stat-item-4').dataset.tip='Kürzeste Periode';
document.getElementById('stat-item-4').dataset.tipText='Umlaufzeit des innersten Planeten in Tagen. Planeten mit kurzer Periode transiten häufiger und sind daher leichter zu entdecken.';
document.getElementById('stat-item-3').dataset.tipText='Anzahl der Planeten im ausgewählten System. TRAPPIST-1: 7 Planeten, Kepler-11: 6 Planeten, Sonnensystem: 4 innere Planeten.';
document.getElementById('stat-item-4').dataset.tip='Kürzeste Periode';
document.getElementById('stat-item-4').dataset.tipText='Umlaufzeit des innersten Planeten in Tagen. Planeten mit kurzer Periode transiten häufiger und sind daher leichter zu entdecken.';
document.getElementById('sStat3').textContent=sys.planets.length;
document.getElementById('statLbl4').textContent='P-min (d)';
document.getElementById('sStat4').textContent=sys.planets[0].period.toFixed(2);
@@ -795,7 +795,7 @@ function drawLC(){
if(S.mode==='multi'){
const tw=S.timeWindow;[0,.17,.33,.5,.67,.83,1].forEach(f=>{lCx.fillText((f*tw).toFixed(0)+'d',ml+f*pw,mt+ph+18);});
}else{
[0,60,120,180,240,300,360].forEach(deg=>{lCx.fillText(deg+'°',ml+(deg/360)*pw,mt+ph+18);});
[0,60,120,180,240,300,360].forEach(deg=>{lCx.fillText(deg+'°',ml+(deg/360)*pw,mt+ph+18);});
}
// y labels
lCx.textAlign='right';
@@ -832,14 +832,14 @@ function applySingle(key){
updSL();recompute();
}
function updSL(){
document.getElementById('vIncl').textContent=S.incl.toFixed(1)+'°';
document.getElementById('vIncl').textContent=S.incl.toFixed(1)+'°';
document.getElementById('vDist').textContent=S.dist.toFixed(1);
document.getElementById('vRad').textContent=S.rr.toFixed(3);
document.getElementById('vAlb').textContent=S.alb.toFixed(2);
}
function markCustS(){const p=DB_S.custom;document.getElementById('exoSelect').value='custom';document.getElementById('exoName').textContent=p.name;document.getElementById('exoTag').textContent=p.tag;document.getElementById('exoDesc').textContent=p.desc;}
document.getElementById('exoSelect').addEventListener('change',e=>applySingle(e.target.value));
[['sIncl','incl','vIncl',v=>v.toFixed(1)+'°'],['sDist','dist','vDist',v=>v.toFixed(1)],['sRad','rr','vRad',v=>v.toFixed(3)],['sAlb','alb','vAlb',v=>v.toFixed(2)]].forEach(([id,key,lbl,fmt])=>{
[["sIncl","incl","vIncl",v=>v.toFixed(1)+"°"],['sDist','dist','vDist',v=>v.toFixed(1)],['sRad','rr','vRad',v=>v.toFixed(3)],['sAlb','alb','vAlb',v=>v.toFixed(2)]].forEach(([id,key,lbl,fmt])=>{
document.getElementById(id).addEventListener('input',function(){S[key]=parseFloat(this.value);document.getElementById(lbl).textContent=fmt(S[key]);markCustS();recompute();});
});
@@ -866,14 +866,14 @@ function applyBinary(key){
updBL();recompute();
}
function updBL(){
document.getElementById('vBinIncl').textContent=S.binIncl.toFixed(1)+'°';
document.getElementById('vBinIncl').textContent=S.binIncl.toFixed(1)+'°';
document.getElementById('vBinDist').textContent=S.binDist.toFixed(1);
document.getElementById('vBinR2').textContent=S.binR2.toFixed(2);
document.getElementById('vBinSB').textContent=S.binSB.toFixed(3);
}
function markCustB(){const b=DB_B.custom_bin;document.getElementById('binSelect').value='custom_bin';document.getElementById('binName').textContent=b.name;document.getElementById('binTag').textContent=b.tag;document.getElementById('binDesc').textContent=b.desc;}
document.getElementById('binSelect').addEventListener('change',e=>applyBinary(e.target.value));
[['sBinIncl','binIncl','vBinIncl',v=>v.toFixed(1)+'°'],['sBinDist','binDist','vBinDist',v=>v.toFixed(1)],['sBinR2','binR2','vBinR2',v=>v.toFixed(2)],['sBinSB','binSB','vBinSB',v=>v.toFixed(3)]].forEach(([id,key,lbl,fmt])=>{
[["sBinIncl","binIncl","vBinIncl",v=>v.toFixed(1)+"°"],['sBinDist','binDist','vBinDist',v=>v.toFixed(1)],['sBinR2','binR2','vBinR2',v=>v.toFixed(2)],['sBinSB','binSB','vBinSB',v=>v.toFixed(3)]].forEach(([id,key,lbl,fmt])=>{
document.getElementById(id).addEventListener('input',function(){S[key]=parseFloat(this.value);document.getElementById(lbl).textContent=fmt(S[key]);markCustB();recompute();});
});
@@ -957,13 +957,13 @@ window.addEventListener('mousemove', e => {
if (S.mode === 'binary') {
S.binIncl = Math.max(45, Math.min(90, S.vizDragStartIncl + delta));
document.getElementById('sBinIncl').value = S.binIncl;
document.getElementById('vBinIncl').textContent = S.binIncl.toFixed(1) + '°';
document.getElementById('vBinIncl').textContent = S.binIncl.toFixed(1) + '°';
markCustB(); recompute();
} else {
const minIncl = S.mode === 'single' ? 60 : 60;
S.incl = Math.max(minIncl, Math.min(90, S.vizDragStartIncl + delta));
document.getElementById('sIncl').value = S.incl;
document.getElementById('vIncl').textContent = S.incl.toFixed(1) + '°';
document.getElementById('vIncl').textContent = S.incl.toFixed(1) + '°';
if (S.mode === 'single') { markCustS(); }
recompute();
}
@@ -986,12 +986,12 @@ vCv.addEventListener('touchmove', e => {
if (S.mode === 'binary') {
S.binIncl = Math.max(45, Math.min(90, S.vizDragStartIncl + delta));
document.getElementById('sBinIncl').value = S.binIncl;
document.getElementById('vBinIncl').textContent = S.binIncl.toFixed(1) + '°';
document.getElementById('vBinIncl').textContent = S.binIncl.toFixed(1) + '°';
markCustB(); recompute();
} else {
S.incl = Math.max(45, Math.min(90, S.vizDragStartIncl + delta));
document.getElementById('sIncl').value = S.incl;
document.getElementById('vIncl').textContent = S.incl.toFixed(1) + '°';
document.getElementById('vIncl').textContent = S.incl.toFixed(1) + '°';
if (S.mode === 'single') markCustS();
recompute();
}
+8
View File
@@ -1301,6 +1301,12 @@ footer a:hover { color: var(--gold); }
line-height: 1.4;
}
.favorites-loading-hint-error {
border-color: rgba(200, 60, 60, 0.35);
background: rgba(200, 60, 60, 0.12);
color: #f08080;
}
.favorites-loading-form.is-loading .btn {
opacity: 0.75;
cursor: wait;
@@ -1439,6 +1445,8 @@ footer a:hover { color: var(--gold); }
border: none;
padding: 0;
background: transparent;
margin: auto;
max-width: min(92vw, 560px);
}
.admin-dialog::backdrop {
+5 -5
View File
@@ -2,7 +2,7 @@
require_once __DIR__ . '/auth_helpers.php';
$title = isset($pageTitle) && is_string($pageTitle) && $pageTitle !== ''
? $pageTitle
: 'Skyview & AstroTools — Astronomiemuseum der Sternwarte Sonneberg';
: 'Skyview & AstroTools - Astronomiemuseum der Sternwarte Sonneberg';
$bodyClassAttr = '';
if (isset($bodyClass) && is_string($bodyClass) && trim($bodyClass) !== '') {
@@ -83,7 +83,7 @@ if (!in_array($publicScriptName, $publicAccessAllowlist, true) && !$canAccessCur
<main class="container" style="padding-top: 4rem; padding-bottom: 4rem;">
<section class="card">
<h1>Zugriff verweigert</h1>
<p>Diese Seite ist aktuell nur fuer Benutzer mit der Rolle <strong>admin</strong> oder <strong>master</strong> freigegeben.</p>
<p>Diese Seite ist aktuell nur für Benutzer mit der Rolle <strong>admin</strong> oder <strong>master</strong> freigegeben.</p>
<p><a class="btn btn-primary" href="<?= htmlspecialchars($publicBasePath . 'logout.php', ENT_QUOTES, 'UTF-8') ?>">Abmelden</a></p>
</section>
</main>
@@ -125,7 +125,8 @@ $menuGroups = [
['href' => 'jupitersystem.php', 'label' => 'Jupitersystem'],
['href' => 'tagbogen.php', 'label' => 'Tagbogen'],
['href' => 'sternenhimmel.php', 'label' => 'Sternenhimmel'],
['href' => 'meteorshowers.php', 'label' => 'Meteorstroeme'],
['href' => 'satellitenhimmel.php', 'label' => 'Satellitenhimmel'],
['href' => 'meteorshowers.php', 'label' => 'Meteorströme'],
['href' => 'geocron/index.php', 'label' => 'Geocron'],
['href' => 'geocron3d.php', 'label' => 'Geocron 3D'],
],
@@ -149,7 +150,6 @@ $adminLinks = [
$accountLinks = [
['href' => 'settings.php', 'label' => 'Einstellungen'],
['href' => 'my_favorites.php', 'label' => 'Favoriten'],
['href' => 'satellitenhimmel.php', 'label' => 'Satellitenhimmel'],
];
if ($loggedIn && !$hasFullAdminAccess && $pdoHeader !== null) {
@@ -288,7 +288,7 @@ if ($loggedIn && !$hasFullAdminAccess && $pdoHeader !== null) {
</a>
<?php endif; ?>
<div class="header-menu">
<button type="button" class="menu-toggle" id="menuToggle" aria-expanded="false" aria-controls="headerMenuPanel" aria-label="Menue oeffnen">
<button type="button" class="menu-toggle" id="menuToggle" aria-expanded="false" aria-controls="headerMenuPanel" aria-label="Menü öffnen">
<span></span>
<span></span>
<span></span>
+1 -1
View File
@@ -88,7 +88,7 @@ function findMinimum(bodyName, startMs, endMs) {
function classifyVisibility(sunAltDeg) {
if (sunAltDeg > -5) return { key: 'day', label: 'Tag' };
if (sunAltDeg >= -10) return { key: 'twilight', label: 'Daemmerung' };
if (sunAltDeg >= -10) return { key: 'twilight', label: 'Dämmerung' };
return { key: 'night', label: 'Nacht' };
}
+4 -3
View File
@@ -1145,9 +1145,8 @@ async function initJupiterSystem() {
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x03050c, 0.00015);
camera = new THREE.PerspectiveCamera(45, 1, 0.1, 1000);
camera = new THREE.PerspectiveCamera(45, 1, 0.1, 2000);
textureLoader = new THREE.TextureLoader();
moonGroups = new Map();
moonShadowCasters = new Map();
@@ -1161,7 +1160,7 @@ async function initJupiterSystem() {
cameraTarget = new THREE.Vector3(0, 0, 0);
rotationEqjToEcl = AstronomyEngine.Rotation_EQJ_ECL();
const ambientLight = new THREE.AmbientLight(0x334466, 0.8);
const ambientLight = new THREE.AmbientLight(0x334466, 0.2);
scene.add(ambientLight);
const hemiLight = new THREE.HemisphereLight(0x445577, 0x111a2e, 0.6);
@@ -1180,6 +1179,8 @@ async function initJupiterSystem() {
sunLight.shadow.camera.bottom = -40;
sunLight.shadow.camera.updateProjectionMatrix();
sunLight.shadow.radius = 0.5;
sunLight.shadow.bias = 0.0008;
sunLight.shadow.normalBias = 0.02;
scene.add(sunLight);
scene.add(sunLight.target); // Pflicht: sonst ignoriert Three.js die Shadow-Camera-Ausrichtung
+15 -15
View File
@@ -4,12 +4,12 @@ declare(strict_types=1);
session_start();
$loggedIn = isset($_SESSION['user_id']);
$pageTitle = 'Meteorstroeme - AstroTools';
$pageTitle = 'Meteorströme - AstroTools';
$bodyClass = 'admin-page meteorshowers-page';
$publicBasePath = './';
$headerIntroPre = 'Beobachtung';
$headerIntroTitle = 'Meteorstroeme';
$headerIntroSub = 'Liste der importierten Meteorstroeme aus der Datenbank mit Angaben in solarer Laenge.';
$headerIntroTitle = 'Meteorströme';
$headerIntroSub = 'Liste der importierten Meteorströme aus der Datenbank mit Angaben in solarer Länge.';
$config = require __DIR__ . '/../config/database.php';
@@ -314,10 +314,10 @@ require __DIR__ . '/header.php';
<?php
$statsCards = [
['label' => 'Importierte Meteorstroeme', 'value' => (string) count($allShowers)],
['label' => 'Importierte Meteorströme', 'value' => (string) count($allShowers)],
['label' => 'Davon aktuell aktiv', 'value' => (string) $activeCount],
['label' => 'Hoechste ZHR', 'value' => $topZhr !== null ? (string) $topZhr : '-'],
['label' => 'Aktuelle solare Laenge', 'value' => number_format($todaySolarLongitude, 2, ',', '.') . '°'],
['label' => 'Höchste ZHR', 'value' => $topZhr !== null ? (string) $topZhr : '-'],
['label' => 'Aktuelle solare Länge', 'value' => number_format($todaySolarLongitude, 2, ',', '.') . '°'],
['label' => 'Datumsumrechnung', 'value' => (string) $displayYear],
];
?>
@@ -343,29 +343,29 @@ $statsCards = [
id="q"
name="q"
value="<?= h($searchQuery) ?>"
placeholder="Code, Bezeichnung oder Mutterkoerper"
placeholder="Code, Bezeichnung oder Mutterkörper"
>
</div>
<div class="form-group">
<label for="mode">Anzeige</label>
<select id="mode" name="mode">
<option value="all" <?= $showMode === 'all' ? 'selected' : '' ?>>Alle Meteorstroeme</option>
<option value="all" <?= $showMode === 'all' ? 'selected' : '' ?>>Alle Meteorströme</option>
<option value="active" <?= $showMode === 'active' ? 'selected' : '' ?>>Nur aktuell aktive</option>
</select>
</div>
</div>
<div class="input-row">
<button type="submit" class="btn btn-primary">Anzeigen</button>
<a href="meteorshowers.php" class="btn btn-secondary">Zuruecksetzen</a>
<a href="meteorshowers.php" class="btn btn-secondary">Zurücksetzen</a>
</div>
</form>
</section>
<section class="card">
<h2>Liste der Meteorstroeme</h2>
<p class="hint">Die Gradangaben in Aktivitaet und Maximum sind Werte der solaren Laenge. Zusaetzlich wird dafuer ein Kalenderdatum im Jahr <?= h((string) $displayYear) ?> angezeigt.</p>
<h2>Liste der Meteorströme</h2>
<p class="hint">Die Gradangaben in Aktivität und Maximum sind Werte der solaren Länge. Zusätzlich wird dafür ein Kalenderdatum im Jahr <?= h((string) $displayYear) ?> angezeigt.</p>
<?php if ($showers === []): ?>
<p class="hint">Keine Meteorstroeme fuer diese Auswahl gefunden.</p>
<p class="hint">Keine Meteorströme für diese Auswahl gefunden.</p>
<?php else: ?>
<div class="table-wrap">
<table class="data-table">
@@ -376,11 +376,11 @@ $statsCards = [
<th>IAU</th>
<th>Bezeichnung</th>
<th>ZHR</th>
<th>Aktivitaet (solare Laenge)</th>
<th>Maximum (solare Laenge)</th>
<th>Aktivität (solare Länge)</th>
<th>Maximum (solare Länge)</th>
<th>Radiant</th>
<th>v<sub>g</sub></th>
<th>Mutterkoerper</th>
<th>Mutterkörper</th>
</tr>
</thead>
<tbody>
+3 -3
View File
@@ -38,7 +38,7 @@ function runPythonApi(string $scriptPath, string $action, array $arguments): arr
$decoded = json_decode($joined, true);
if (!is_array($decoded)) {
$lastError = $joined !== '' ? 'Python-Ausgabe war kein gueltiges JSON.' : 'Python lieferte keine Ausgabe zurueck.';
$lastError = $joined !== '' ? 'Python-Ausgabe war kein gültiges JSON.' : 'Python lieferte keine Ausgabe zurück.';
continue;
}
@@ -71,7 +71,7 @@ if ($isAjax) {
if ($utcIso === '') {
echo json_encode([
'ok' => false,
'error' => 'utc_iso wurde nicht uebergeben.',
'error' => 'utc_iso wurde nicht übergeben.',
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
exit;
}
@@ -1071,7 +1071,7 @@ async function exportMoonPng() {
const size = getMoonExportSize();
const transparentBackground = useTransparentMoonExport();
if (!size) {
updateMoonExportStatus('Bitte eine gueltige Bildgroesse eingeben.', 'error');
updateMoonExportStatus('Bitte eine gültige Bildgröße eingeben.', 'error');
return;
}
+281 -31
View File
@@ -123,6 +123,53 @@ function countUserRows(PDO $pdo, string $tableName, int $userId): int
return (int) $stmtCount->fetchColumn();
}
function validateLocationForm(array $input, array &$errors): array
{
$name = trim((string) ($input['name'] ?? ''));
$latitude = trim((string) ($input['latitude'] ?? ''));
$longitude = trim((string) ($input['longitude'] ?? ''));
$elevation = trim((string) ($input['elevation'] ?? ''));
$timezone = trim((string) ($input['timezone'] ?? 'Europe/Berlin'));
if ($name === '') {
$errors[] = 'Bitte einen Namen für den Standort eingeben.';
}
if ($latitude === '' || !is_numeric($latitude)) {
$errors[] = 'Bitte einen gültigen Breitengrad eingeben.';
} else {
$lat = (float) $latitude;
if ($lat < -90 || $lat > 90) {
$errors[] = 'Der Breitengrad muss zwischen -90 und 90 liegen.';
}
}
if ($longitude === '' || !is_numeric($longitude)) {
$errors[] = 'Bitte einen gültigen Längengrad eingeben.';
} else {
$lon = (float) $longitude;
if ($lon < -180 || $lon > 180) {
$errors[] = 'Der Längengrad muss zwischen -180 und 180 liegen.';
}
}
if ($elevation !== '' && !is_numeric($elevation)) {
$errors[] = 'Die Höhe muss numerisch sein.';
}
if (!validTimezone($timezone)) {
$errors[] = 'Bitte eine gültige IANA-Zeitzone angeben, z. B. Europe/Berlin.';
}
return [
'name' => $name,
'latitude' => $latitude,
'longitude' => $longitude,
'elevation' => $elevation,
'timezone' => $timezone,
];
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
@@ -130,7 +177,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$satelliteId = (int) ($_POST['satellite_id'] ?? 0);
if ($satelliteId <= 0) {
$errors[] = 'Ungueltiger Satellit.';
$errors[] = 'Ungültiger Satellit.';
}
if (empty($errors)) {
@@ -178,7 +225,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$satelliteId = (int) ($_POST['satellite_id'] ?? 0);
if ($satelliteId <= 0) {
$errors[] = 'Ungueltiger Satellit.';
$errors[] = 'Ungültiger Satellit.';
}
if (empty($errors)) {
@@ -203,7 +250,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (empty($errors)) {
$satelliteCount = countUserRows($pdo, 'app_user_satellites', $userId);
if ($satelliteCount <= 1) {
$errors[] = 'Der letzte Satellit kann nicht geloescht werden.';
$errors[] = 'Der letzte Satellit kann nicht gelöscht werden.';
}
}
@@ -272,7 +319,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$satelliteId = (int) ($_POST['satellite_id'] ?? 0);
if ($satelliteId <= 0) {
$errors[] = 'Ungueltiger Satellit.';
$errors[] = 'Ungültiger Satellit.';
}
if (empty($errors)) {
@@ -314,7 +361,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$minorplanetId = (int) ($_POST['minorplanet_id'] ?? 0);
if ($minorplanetId <= 0) {
$errors[] = 'Ungueltiger Kleinplanet.';
$errors[] = 'Ungültiger Kleinplanet.';
}
if (empty($errors)) {
@@ -362,7 +409,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$minorplanetId = (int) ($_POST['minorplanet_id'] ?? 0);
if ($minorplanetId <= 0) {
$errors[] = 'Ungueltiger Kleinplanet.';
$errors[] = 'Ungültiger Kleinplanet.';
}
if (empty($errors)) {
@@ -384,7 +431,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$minorplanetId = (int) ($_POST['minorplanet_id'] ?? 0);
if ($minorplanetId <= 0) {
$errors[] = 'Ungueltiger Kleinplanet.';
$errors[] = 'Ungültiger Kleinplanet.';
}
if (empty($errors)) {
@@ -405,11 +452,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$isDefault = isset($_POST['is_default']) ? 1 : 0;
if ($name === '') {
$errors[] = 'Bitte einen Namen fuer den Standort eingeben.';
$errors[] = 'Bitte einen Namen für den Standort eingeben.';
}
if ($latitude === '' || !is_numeric($latitude)) {
$errors[] = 'Bitte einen gueltigen Breitengrad eingeben.';
$errors[] = 'Bitte einen gültigen Breitengrad eingeben.';
} else {
$lat = (float) $latitude;
if ($lat < -90 || $lat > 90) {
@@ -418,11 +465,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
if ($longitude === '' || !is_numeric($longitude)) {
$errors[] = 'Bitte einen gueltigen Laengengrad eingeben.';
$errors[] = 'Bitte einen gültigen Längengrad eingeben.';
} else {
$lon = (float) $longitude;
if ($lon < -180 || $lon > 180) {
$errors[] = 'Der Laengengrad muss zwischen -180 und 180 liegen.';
$errors[] = 'Der Längengrad muss zwischen -180 und 180 liegen.';
}
}
@@ -431,7 +478,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
if (!validTimezone($timezone)) {
$errors[] = 'Bitte eine gueltige IANA-Zeitzone angeben, z. B. Europe/Berlin.';
$errors[] = 'Bitte eine gültige IANA-Zeitzone angeben, z. B. Europe/Berlin.';
}
if (empty($errors)) {
@@ -494,11 +541,90 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
}
if ($action === 'update_location') {
$locationId = (int) ($_POST['location_id'] ?? 0);
$locationForm = validateLocationForm($_POST, $errors);
$setDefault = isset($_POST['is_default']) ? 1 : 0;
if ($locationId <= 0) {
$errors[] = 'Ungültiger Standort.';
}
$existingLocation = null;
if (empty($errors)) {
$stmtLocation = $pdo->prepare("
SELECT `id`, `is_default`
FROM `app_user_locations`
WHERE `id` = :id
AND `user_id` = :user_id
LIMIT 1
");
$stmtLocation->execute([
':id' => $locationId,
':user_id' => $userId,
]);
$existingLocation = $stmtLocation->fetch();
if (!$existingLocation) {
$errors[] = 'Der Standort wurde nicht gefunden.';
}
}
if (empty($errors)) {
$pdo->beginTransaction();
try {
if ($setDefault === 1) {
$stmtReset = $pdo->prepare("
UPDATE `app_user_locations`
SET `is_default` = 0
WHERE `user_id` = :user_id
");
$stmtReset->execute([
':user_id' => $userId,
]);
}
$stmtUpdate = $pdo->prepare("
UPDATE `app_user_locations`
SET
`name` = :name,
`latitude` = :latitude,
`longitude` = :longitude,
`elevation` = :elevation,
`timezone` = :timezone,
`is_default` = :is_default,
`updated_at` = NOW()
WHERE `id` = :id
AND `user_id` = :user_id
");
$stmtUpdate->execute([
':id' => $locationId,
':user_id' => $userId,
':name' => $locationForm['name'],
':latitude' => (float) $locationForm['latitude'],
':longitude' => (float) $locationForm['longitude'],
':elevation' => $locationForm['elevation'] !== '' ? (float) $locationForm['elevation'] : null,
':timezone' => $locationForm['timezone'],
':is_default' => $setDefault === 1 ? 1 : (int) ($existingLocation['is_default'] ?? 0),
]);
$pdo->commit();
$successMessage = 'Der Standort wurde aktualisiert.';
} catch (Throwable $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
$errors[] = 'Der Standort konnte nicht aktualisiert werden.';
}
}
}
if ($action === 'set_default_location') {
$locationId = (int) ($_POST['location_id'] ?? 0);
if ($locationId <= 0) {
$errors[] = 'Ungueltiger Standort.';
$errors[] = 'Ungültiger Standort.';
}
if (empty($errors)) {
@@ -558,7 +684,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$locationId = (int) ($_POST['location_id'] ?? 0);
if ($locationId <= 0) {
$errors[] = 'Ungueltiger Standort.';
$errors[] = 'Ungültiger Standort.';
}
if (empty($errors)) {
@@ -583,7 +709,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (empty($errors)) {
$locationCount = countUserRows($pdo, 'app_user_locations', $userId);
if ($locationCount <= 1) {
$errors[] = 'Der letzte Standort kann nicht geloescht werden.';
$errors[] = 'Der letzte Standort kann nicht gelöscht werden.';
}
}
@@ -638,12 +764,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
$pdo->commit();
$successMessage = 'Der Standort wurde geloescht.';
$successMessage = 'Der Standort wurde gelöscht.';
} catch (Throwable $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
$errors[] = 'Der Standort konnte nicht geloescht werden.';
$errors[] = 'Der Standort konnte nicht gelöscht werden.';
}
}
}
@@ -857,7 +983,7 @@ $loggedIn = true;
<tr>
<th>Name</th>
<th>Breitengrad</th>
<th>Laengengrad</th>
<th>Längengrad</th>
<th>Hoehe</th>
<th>Zeitzone</th>
<th>Standard</th>
@@ -874,6 +1000,17 @@ $loggedIn = true;
<td><?= h($location['timezone']) ?></td>
<td><?= (int) $location['is_default'] === 1 ? '*' : '-' ?></td>
<td class="action-cell">
<button
type="button"
class="btn btn-sm btn-primary js-edit-location"
data-location-id="<?= (int) $location['id'] ?>"
data-location-name="<?= h((string) $location['name']) ?>"
data-location-latitude="<?= h((string) $location['latitude']) ?>"
data-location-longitude="<?= h((string) $location['longitude']) ?>"
data-location-elevation="<?= h($location['elevation'] !== null ? (string) $location['elevation'] : '') ?>"
data-location-timezone="<?= h((string) $location['timezone']) ?>"
data-location-default="<?= (int) $location['is_default'] === 1 ? '1' : '0' ?>"
>Bearbeiten</button>
<?php if ((int) $location['is_default'] !== 1): ?>
<form method="post" action="" class="favorites-loading-form" data-loading-message="Standard-Standort wird gesetzt...">
<input type="hidden" name="action" value="set_default_location">
@@ -882,10 +1019,10 @@ $loggedIn = true;
</form>
<?php endif; ?>
<?php if (count($locations) > 1): ?>
<form method="post" action="" class="favorites-loading-form" data-loading-message="Standort wird entfernt..." onsubmit="return confirm('Standort wirklich loeschen?');">
<form method="post" action="" class="favorites-loading-form" data-loading-message="Standort wird entfernt..." onsubmit="return confirm('Standort wirklich löschen?');">
<input type="hidden" name="action" value="delete_location">
<input type="hidden" name="location_id" value="<?= (int) $location['id'] ?>">
<button type="submit" class="btn btn-sm btn-danger">Loeschen</button>
<button type="submit" class="btn btn-sm btn-danger">Löschen</button>
</form>
<?php else: ?>
<span class="hint">Letzter Standort</span>
@@ -911,12 +1048,25 @@ $loggedIn = true;
<div id="map" class="map"></div>
<form method="post" action="" class="form-stack mt-xl favorites-loading-form" data-loading-message="Standort wird gespeichert...">
<form method="post" action="" id="add-location-form" class="form-stack mt-xl favorites-loading-form" data-loading-message="Standort wird gespeichert...">
<input type="hidden" name="action" value="add_location">
<div class="grid-2 gap-sm">
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" name="name" required value="<?= h($_POST['name'] ?? '') ?>">
<input
type="text"
id="name"
name="name"
required
aria-describedby="add-location-name-error"
value="<?= h($_POST['name'] ?? '') ?>"
>
<p
id="add-location-name-error"
class="favorites-loading-hint favorites-loading-hint-error"
data-validation-hint="name"
<?= (($_POST['action'] ?? '') === 'add_location' && trim((string) ($_POST['name'] ?? '')) === '') ? '' : 'hidden' ?>
>Bitte einen Namen für den Standort eingeben.</p>
</div>
<div class="form-group">
<label for="timezone">Zeitzone (IANA)</label>
@@ -927,7 +1077,7 @@ $loggedIn = true;
<input type="text" id="latitude" name="latitude" required value="<?= h($_POST['latitude'] ?? ($defaultLocation['latitude'] ?? '')) ?>">
</div>
<div class="form-group">
<label for="longitude">Laengengrad</label>
<label for="longitude">Längengrad</label>
<input type="text" id="longitude" name="longitude" required value="<?= h($_POST['longitude'] ?? ($defaultLocation['longitude'] ?? '')) ?>">
</div>
<div class="form-group">
@@ -940,11 +1090,50 @@ $loggedIn = true;
Als Standard-Standort speichern
</label>
<button type="submit" class="btn btn-primary">Standort speichern</button>
<p class="favorites-loading-hint" hidden>Standort wird gespeichert...</p>
<p class="favorites-loading-hint" data-loading-hint hidden>Standort wird gespeichert...</p>
</form>
</div>
</div>
<dialog class="admin-dialog" id="editLocationDialog">
<form method="post" action="" class="admin-dialog-card form-stack favorites-loading-form" data-loading-message="Standort wird aktualisiert...">
<input type="hidden" name="action" value="update_location">
<input type="hidden" name="location_id" id="edit-location-id" value="">
<h3>Standort bearbeiten</h3>
<div class="grid-2 gap-sm">
<div class="form-group">
<label for="edit-location-name">Name</label>
<input type="text" id="edit-location-name" name="name" required>
</div>
<div class="form-group">
<label for="edit-location-timezone">Zeitzone (IANA)</label>
<input type="text" id="edit-location-timezone" name="timezone" required>
</div>
<div class="form-group">
<label for="edit-location-latitude">Breitengrad</label>
<input type="text" id="edit-location-latitude" name="latitude" required>
</div>
<div class="form-group">
<label for="edit-location-longitude">Längengrad</label>
<input type="text" id="edit-location-longitude" name="longitude" required>
</div>
<div class="form-group">
<label for="edit-location-elevation">Höhe (optional, Meter)</label>
<input type="text" id="edit-location-elevation" name="elevation">
</div>
</div>
<label class="form-checkbox">
<input type="checkbox" id="edit-location-default" name="is_default" value="1">
Als Standard-Standort speichern
</label>
<div class="admin-dialog-actions">
<button type="button" class="btn btn-secondary" id="editLocationCancel">Abbrechen</button>
<button type="submit" class="btn btn-primary">Änderungen speichern</button>
</div>
<p class="favorites-loading-hint" data-loading-hint hidden>Standort wird aktualisiert...</p>
</form>
</dialog>
<div class="grid-2">
<div class="card">
<h2>Satelliten suchen</h2>
@@ -959,7 +1148,7 @@ $loggedIn = true;
>
<button type="submit" class="btn btn-primary">Suchen</button>
</div>
<p class="favorites-loading-hint" hidden>Satelliten werden gesucht. Das kann kurz dauern...</p>
<p class="favorites-loading-hint" data-loading-hint hidden>Satelliten werden gesucht. Das kann kurz dauern...</p>
</form>
<?php if ($satelliteSearchQuery !== ''): ?>
@@ -1014,7 +1203,7 @@ $loggedIn = true;
>
<button type="submit" class="btn btn-primary">Suchen</button>
</div>
<p class="favorites-loading-hint" hidden>Kleinplaneten werden gesucht. Das kann etwas dauern...</p>
<p class="favorites-loading-hint" data-loading-hint hidden>Kleinplaneten werden gesucht. Das kann etwas dauern...</p>
</form>
<?php if ($minorplanetSearchQuery !== ''): ?>
@@ -1061,7 +1250,7 @@ $loggedIn = true;
<div class="card">
<h2>Meine Satelliten</h2>
<?php if (empty($trackedSatellites)): ?>
<p class="hint">Du hast noch keine Satelliten ausgewaehlt.</p>
<p class="hint">Du hast noch keine Satelliten ausgewählt.</p>
<?php else: ?>
<table class="data-table">
<thead>
@@ -1112,7 +1301,7 @@ $loggedIn = true;
<div class="card">
<h2>Meine Kleinplaneten</h2>
<?php if (empty($trackedMinorplanets)): ?>
<p class="hint">Du hast noch keine Kleinplaneten ausgewaehlt.</p>
<p class="hint">Du hast noch keine Kleinplaneten ausgewählt.</p>
<?php else: ?>
<table class="data-table">
<thead>
@@ -1221,7 +1410,7 @@ $loggedIn = true;
const lon = parseFloat(lonInput.value);
if (!Number.isFinite(lat) || !Number.isFinite(lon)) {
alert('Bitte zuerst einen gueltigen Standort waehlen.');
alert('Bitte zuerst einen gültigen Standort wählen.');
return;
}
@@ -1336,13 +1525,40 @@ $loggedIn = true;
})();
document.querySelectorAll('.favorites-loading-form').forEach((form) => {
form.addEventListener('submit', () => {
form.addEventListener('submit', (event) => {
if (form.id === 'add-location-form') {
const nameField = form.querySelector('#name');
const nameError = document.getElementById('add-location-name-error');
if (nameField) {
const trimmedName = nameField.value.trim();
if (trimmedName === '') {
event.preventDefault();
nameField.value = '';
nameField.focus();
if (nameError) {
nameError.textContent = 'Bitte einen Namen für den Standort eingeben.';
nameError.hidden = false;
}
return;
}
if (nameError) {
nameError.hidden = true;
}
}
}
const message = form.dataset.loadingMessage || 'Bitte warten...';
let hint = form.querySelector('.favorites-loading-hint');
let hint = form.querySelector('[data-loading-hint]');
if (!hint) {
hint = document.createElement('p');
hint.className = 'favorites-loading-hint';
hint.dataset.loadingHint = 'true';
form.appendChild(hint);
}
@@ -1361,6 +1577,40 @@ document.querySelectorAll('.favorites-loading-form').forEach((form) => {
});
});
});
(function () {
const dialog = document.getElementById('editLocationDialog');
const cancelButton = document.getElementById('editLocationCancel');
if (!dialog || typeof dialog.showModal !== 'function') {
return;
}
const idInput = document.getElementById('edit-location-id');
const nameInput = document.getElementById('edit-location-name');
const latitudeInput = document.getElementById('edit-location-latitude');
const longitudeInput = document.getElementById('edit-location-longitude');
const elevationInput = document.getElementById('edit-location-elevation');
const timezoneInput = document.getElementById('edit-location-timezone');
const defaultInput = document.getElementById('edit-location-default');
document.querySelectorAll('.js-edit-location').forEach((button) => {
button.addEventListener('click', () => {
idInput.value = button.dataset.locationId || '';
nameInput.value = button.dataset.locationName || '';
latitudeInput.value = button.dataset.locationLatitude || '';
longitudeInput.value = button.dataset.locationLongitude || '';
elevationInput.value = button.dataset.locationElevation || '';
timezoneInput.value = button.dataset.locationTimezone || 'Europe/Berlin';
defaultInput.checked = button.dataset.locationDefault === '1';
dialog.showModal();
});
});
cancelButton?.addEventListener('click', () => {
dialog.close();
});
})();
</script>
<?php require __DIR__ . '/footer.php'; ?>
+4 -4
View File
@@ -32,13 +32,13 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
<h2>Kein Standard-Standort gesetzt</h2>
<p class="hint">
Diese Seite verwendet den im Header geladenen Standard-Standort aus der Datenbank.
Bitte waehle in den <a href="settings.php">Einstellungen</a> einen Standard-Standort aus.
Bitte wähle in den <a href="settings.php">Einstellungen</a> einen Standard-Standort aus.
</p>
</section>
<?php else: ?>
<section class="card planetvis-card">
<p class="planetvis-intro">
Sichtbarkeit der Planeten und des Mondes ueber 24 Stunden fuer deinen Standard-Standort aus der Datenbank.
Sichtbarkeit der Planeten und des Mondes über 24 Stunden für deinen Standard-Standort aus der Datenbank.
</p>
<div class="date-section">
@@ -533,7 +533,7 @@ function renderChart(localDate) {
document.getElementById('tt-body').innerHTML =
`Uhrzeit: ${hStr(h)}<br>Aufgang: ${rStr}<br>Untergang: ${sStr}<br>` +
(def.type === 'moon' ? `Beleuchtung: ${Math.round(def.illum * 100)}%<br>` : `Magnitude: ${mag}<br>`) +
`Status: ${above ? '<span class="tt-up">ueber Horizont</span>' : '<span class="tt-down">unter Horizont</span>'}`;
`Status: ${above ? '<span class="tt-up">über Horizont</span>' : '<span class="tt-down">unter Horizont</span>'}`;
tooltip.style.left = (e.clientX + 16) + 'px';
tooltip.style.top = (e.clientY - 14) + 'px';
tooltip.classList.add('vis');
@@ -551,7 +551,7 @@ function renderChart(localDate) {
infoDiv.innerHTML = `
<div class="info-card"><strong>Standort</strong><br>${SITE_LOCATION.name}<br>${LAT.toFixed(3)}deg N · ${LNG.toFixed(3)}deg O · ${ELEV} m</div>
<div class="info-card"><strong>Sonne (${fmtDate(localDate)})</strong><br>Aufgang: ${hF(tw.sunRise)}<br>Untergang: ${hF(tw.sunSet)}</div>
<div class="info-card"><strong>Daemmerung</strong><br>Buergerlich: ${hF(tw.civDawn)} / ${hF(tw.civDusk)}<br>Nautisch: ${hF(tw.nautDawn)} / ${hF(tw.nautDusk)}<br>Astronomisch: ${hF(tw.astroDawn)} / ${hF(tw.astroDusk)}</div>
<div class="info-card"><strong>Dämmerung</strong><br>Bürgerlich: ${hF(tw.civDawn)} / ${hF(tw.civDusk)}<br>Nautisch: ${hF(tw.nautDawn)} / ${hF(tw.nautDusk)}<br>Astronomisch: ${hF(tw.astroDawn)} / ${hF(tw.astroDusk)}</div>
<div class="info-card"><strong>Mond (${fmtDate(localDate)})</strong><br>${Math.round(moonInfo.illum * 100)}% ${moonInfo.waning ? 'abnehmend' : 'zunehmend'}<br>Phase: ${moonInfo.phase.toFixed(1)}deg</div>
<div class="info-card"><strong>Zeitzone</strong><br>${tzH === 2 ? 'MESZ (UTC+2)' : 'MEZ (UTC+1)'}<br>${SITE_LOCATION.timezone}</div>
`;
+1 -1
View File
@@ -3121,7 +3121,7 @@ def classify_visibility(sun_alt_deg: float) -> dict:
if sun_alt_deg > -5:
return {"key": "day", "label": "Tag"}
if sun_alt_deg >= -10:
return {"key": "twilight", "label": "Daemmerung"}
return {"key": "twilight", "label": "Dämmerung"}
return {"key": "night", "label": "Nacht"}
+84 -41
View File
@@ -39,6 +39,54 @@ function h(?string $value): string
return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
}
function getPythonCandidates(): array
{
if (PHP_OS_FAMILY === 'Windows') {
return ['python', 'py'];
}
return ['/usr/bin/python3', '/usr/bin/python', 'python3', 'python'];
}
function runPythonCommand(string $scriptPath, array $args): array
{
putenv('PYTHONUTF8=1');
putenv('PYTHONIOENCODING=utf-8');
$escapedArguments = array_map('escapeshellarg', array_merge([$scriptPath], $args));
$lastError = 'Python konnte nicht gestartet werden.';
foreach (getPythonCandidates() as $pythonBinary) {
$command = $pythonBinary . ' ' . implode(' ', $escapedArguments) . ' 2>&1';
$output = [];
$resultCode = 0;
exec($command, $output, $resultCode);
$joined = trim(implode("\n", $output));
$decoded = json_decode($joined, true);
if (is_array($decoded)) {
return [
'ok' => (bool) ($decoded['ok'] ?? false),
'data' => $decoded,
'raw' => $joined,
'command' => $command,
];
}
if ($joined !== '') {
$lastError = $joined;
} elseif ($resultCode !== 0) {
$lastError = 'Fehlercode ' . $resultCode . ' bei ' . $pythonBinary;
}
}
return [
'ok' => false,
'error' => $lastError,
];
}
// ── Satelliten aus DB laden ──────────────────────────────────────────────────
function tleChecksum(string $line): int
@@ -291,34 +339,29 @@ if (!empty($satellitePayload) && !empty($defaultLocation['latitude'])) {
$windowStart = $dayStart->setTimezone(new DateTimeZone('UTC'))->format(DateTime::ATOM);
$windowEnd = $dayEnd->setTimezone(new DateTimeZone('UTC'))->format(DateTime::ATOM);
$scriptPath = realpath(__DIR__ . '/py/api.py');
$satellitesJson = json_encode($satellitePayload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$args = [
'/usr/bin/python',
escapeshellarg($scriptPath),
escapeshellarg('satellite_passes'),
escapeshellarg((string)$lat),
escapeshellarg((string)$lon),
escapeshellarg((string)$elev),
escapeshellarg($tz),
escapeshellarg($windowStart),
escapeshellarg($windowEnd),
escapeshellarg($satellitesJson),
];
$command = implode(' ', $args) . ' 2>&1';
$output = [];
$code = 0;
exec($command, $output, $code);
$joined = implode('', $output);
$decoded = json_decode($joined, true);
if (is_array($decoded) && ($decoded['ok'] ?? false)) {
$pythonResult = $decoded;
$scriptPath = realpath(__DIR__ . '/py/api.py');
if ($scriptPath === false) {
$pythonError = 'Das Python-Skript für den Satellitenhimmel wurde nicht gefunden.';
} else {
$pythonError = $joined ?: 'Leere Antwort von Python.';
$satellitesJson = json_encode($satellitePayload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$satellitesPayload = base64_encode((string)$satellitesJson);
$pythonCall = runPythonCommand($scriptPath, [
'satellite_passes',
(string)$lat,
(string)$lon,
(string)$elev,
$tz,
$windowStart,
$windowEnd,
$satellitesPayload,
]);
if ($pythonCall['ok'] ?? false) {
$pythonResult = $pythonCall['data'];
} else {
$pythonError = $pythonCall['raw'] ?? $pythonCall['error'] ?? 'Leere Antwort von Python.';
}
}
}
?>
@@ -340,8 +383,8 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
<div class="satpass-tool">
<section class="card satpass-card">
<p class="satpass-intro">
Berechnet fuer den heutigen Tag den naechsten Ueberflug deiner ausgewaehlten Satelliten
und zeichnet deren Bahn auf einer Zenitkarte fuer deinen Standard-Standort.
Berechnet für den heutigen Tag den nächsten Überflug deiner ausgewählten Satelliten
und zeichnet deren Bahn auf einer Zenitkarte für deinen Standard-Standort.
</p>
</section>
@@ -349,14 +392,14 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
<section class="card satpass-card">
<h2>Kein Standard-Standort gesetzt</h2>
<p class="hint">
Bitte waehle in den <a href="settings.php">Einstellungen</a> einen Standard-Standort aus.
Bitte wähle in den <a href="settings.php">Einstellungen</a> einen Standard-Standort aus.
</p>
</section>
<?php elseif (empty($satellitePayload)): ?>
<section class="card satpass-card">
<h2>Keine Satelliten ausgewaehlt</h2>
<h2>Keine Satelliten ausgewählt</h2>
<p class="hint">
Es sind noch keine ausgewaehlten Satelliten mit aktuellem TLE vorhanden.
Es sind noch keine ausgewählten Satelliten mit aktuellem TLE vorhanden.
Bitte fuege in <a href="my_favorites.php">Favoriten</a> Satelliten hinzu.
</p>
</section>
@@ -392,13 +435,13 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
<div class="form-group">
<label for="satSelect">Satellit</label>
<select id="satSelect" class="satpass-select">
<option value="">– Satellit waehlen –</option>
<option value="">– Satellit wählen –</option>
</select>
</div>
<div class="form-group">
<label for="passSelect">Ueberflug</label>
<label for="passSelect">Überflug</label>
<select id="passSelect" class="satpass-select" disabled>
<option value="">– zuerst Satellit waehlen –</option>
<option value="">– zuerst Satellit wählen –</option>
</select>
</div>
<div id="satpassPassInfo" class="satpass-pass-info"></div>
@@ -653,15 +696,15 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
}
function buildObservationSummary(satName, pass, visibility) {
if (visibility.sky.key === 'day') return `${satName} ist bei diesem Ueberflug tagsueber nicht zu sehen.`;
if (visibility.sky.key === 'day') return `${satName} ist bei diesem Überflug tagsüber nicht zu sehen.`;
const visiblePhase = getVisiblePhase(pass);
const pathText = buildPathDescription(visiblePhase.visiblePath);
const shadowText = pass.shadowEntry
? ` Sie tritt um ${formatTime(pass.shadowEntry.time)} Uhr in den Erdschatten ein.` : '';
const magText = pass.peakMag !== null
? ` Ihre maximale Helligkeit betraegt ${formatMag(pass.peakMag)} mag.` : '';
return `Die Rahmenbedingungen fuer die Beobachtung von ${satName} am ${formatDateLocal(pass.aosTime)} (${formatWeekdayShort(pass.aosTime)}) um ${formatTime(pass.aosTime)} Uhr sind ${visibility.label}. `
+ `Der Ueberflug ist ${formatDurationLong(visiblePhase.visibleDurationMs)} lang und bis ${formatTime(visiblePhase.visibleEndTime)} Uhr zu sehen. `
return `Die Rahmenbedingungen für die Beobachtung von ${satName} am ${formatDateLocal(pass.aosTime)} (${formatWeekdayShort(pass.aosTime)}) um ${formatTime(pass.aosTime)} Uhr sind ${visibility.label}. `
+ `Der Überflug ist ${formatDurationLong(visiblePhase.visibleDurationMs)} lang und bis ${formatTime(visiblePhase.visibleEndTime)} Uhr zu sehen. `
+ `${pathText} Die maximale Horizonthoehe betraegt ${pass.peakElevation.toFixed(0)}&deg;.`
+ `${shadowText}${magText}`;
}
@@ -910,7 +953,7 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
timeZone: SITE_LOCATION.timezone, day:'2-digit', month:'2-digit', year:'numeric'
}).format(new Date(WINDOW_END)) : '';
satSelectEl.innerHTML = '<option value="">– Satellit waehlen –</option>';
satSelectEl.innerHTML = '<option value="">– Satellit wählen –</option>';
PYTHON_RESULTS.forEach((sat, satIdx) => {
const upcomingCount = sat.passes.filter(p => p.losTime > now).length;
const opt = document.createElement('option');
@@ -925,13 +968,13 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
passInfoEl.innerHTML = '';
if (isNaN(satIdx)) {
passSelectEl.disabled = true;
passSelectEl.innerHTML = '<option value="">– zuerst Satellit waehlen –</option>';
passSelectEl.innerHTML = '<option value="">– zuerst Satellit wählen –</option>';
drawSky([], null, false);
return;
}
const sat = PYTHON_RESULTS[satIdx];
passSelectEl.disabled = false;
passSelectEl.innerHTML = '<option value="">– Ueberflug waehlen –</option>';
passSelectEl.innerHTML = '<option value="">– Überflug wählen –</option>';
sat.passes.forEach((p, passIdx) => {
const isPast = p.losTime <= now;
const opt = document.createElement('option');
+7 -7
View File
@@ -80,7 +80,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$email = trim($_POST['email'] ?? '');
if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = 'Bitte eine gueltige E-Mail-Adresse eingeben.';
$errors[] = 'Bitte eine gültige E-Mail-Adresse eingeben.';
}
if ($displayName !== '' && mb_strlen($displayName) > 100) {
@@ -163,7 +163,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
':id' => $userId,
]);
$successMessage = 'Die Telegram-Verknuepfung wurde entfernt.';
$successMessage = 'Die Telegram-Verknüpfung wurde entfernt.';
}
if ($action === 'update_tcrb_telegram_notifications') {
@@ -217,7 +217,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
if ($newPassword !== $newPasswordRepeat) {
$errors[] = 'Die neuen Passwoerter stimmen nicht ueberein.';
$errors[] = 'Die neuen Passwörter stimmen nicht überein.';
}
if (empty($errors)) {
@@ -311,7 +311,7 @@ $telegramDeepLink = $telegramLinkIsActive && $telegramBotUsername !== ''
<h2>Telegram verbinden</h2>
<div class="form-stack">
<p class="hint">
Spaetere Bot-Nachrichten funktionieren nur mit einer verknuepften Chat-ID. Der Telegram-Username allein reicht Telegram fuer aktive Nachrichten an einen Nutzer nicht aus.
Spätere Bot-Nachrichten funktionieren nur mit einer verknüpften Chat-ID. Der Telegram-Username allein reicht Telegram für aktive Nachrichten an einen Nutzer nicht aus.
</p>
<table class="data-table mb-lg">
<tr><td>Status</td><td><?= $telegramHasConnection ? 'Verbunden' : 'Nicht verbunden' ?></td></tr>
@@ -336,9 +336,9 @@ $telegramDeepLink = $telegramLinkIsActive && $telegramBotUsername !== ''
</form>
<?php if ($telegramHasConnection): ?>
<form method="post" action="" onsubmit="return confirm('Telegram-Verknuepfung wirklich entfernen?');">
<form method="post" action="" onsubmit="return confirm('Telegram-Verknüpfung wirklich entfernen?');">
<input type="hidden" name="action" value="disconnect_telegram">
<button type="submit" class="btn btn-danger">Verknuepfung loesen</button>
<button type="submit" class="btn btn-danger">Verknüpfung lösen</button>
</form>
<?php endif; ?>
</div>
@@ -351,7 +351,7 @@ $telegramDeepLink = $telegramLinkIsActive && $telegramBotUsername !== ''
</div>
<?php endif; ?>
<p class="hint">
Verbindung starten: <a href="<?= h($publicBasePath . 'telegram/connect.php') ?>">Telegram-Verknuepfung oeffnen</a>
Verbindung starten: <a href="<?= h($publicBasePath . 'telegram/connect.php') ?>">Telegram-Verknüpfung öffnen</a>
<?php if ($telegramDeepLink !== null): ?>
oder direkt im Bot:
<a href="<?= h($telegramDeepLink) ?>" target="_blank" rel="noopener">@<?= h($telegramBotUsername) ?></a>
-1
View File
@@ -1246,7 +1246,6 @@ async function initSolarSystem() {
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x03050c, 0.0018);
camera = new THREE.PerspectiveCamera(45, 1, 0.1, 2000);
planetMeshes = new Map();
+8 -8
View File
@@ -46,7 +46,7 @@ function runPythonApi(string $scriptPath, string $action, array $arguments): arr
$escapedArguments[] = escapeshellarg((string)$argument);
}
$lastError = 'Python konnte nicht ausgefuehrt werden.';
$lastError = 'Python konnte nicht ausgeführt werden.';
$debugOutput = [];
foreach ($pythonCandidates as $pythonBinary) {
@@ -65,7 +65,7 @@ function runPythonApi(string $scriptPath, string $action, array $arguments): arr
$decoded = json_decode($joined, true);
if (!is_array($decoded)) {
$lastError = $joined !== '' ? 'Python-Ausgabe war kein gueltiges JSON.' : 'Python lieferte keine Ausgabe zurueck.';
$lastError = $joined !== '' ? 'Python-Ausgabe war kein gültiges JSON.' : 'Python lieferte keine Ausgabe zurück.';
continue;
}
@@ -332,7 +332,7 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
<h2>Kein Standard-Standort gesetzt</h2>
<p class="hint">
Fuer die Berechnung wird der Standard-Standort aus der Datenbank verwendet.
Bitte waehle in den <a href="settings.php">Einstellungen</a> einen Standard-Standort aus.
Bitte wähle in den <a href="settings.php">Einstellungen</a> einen Standard-Standort aus.
</p>
</section>
<?php elseif ($pageError !== null): ?>
@@ -371,12 +371,12 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
<button type="button" class="btn btn-secondary btn-sm" id="occultWeekBtn">7 Tage</button>
<button type="button" class="btn btn-secondary btn-sm" id="occultMonthBtn">Monat</button>
</form>
<span class="hint">Die Berechnung laeuft im Hintergrund. Hoehere Grenzgroessen brauchen deutlich laenger.</span>
<span class="hint">Die Berechnung läuft im Hintergrund. Hoehere Grenzgrössen brauchen deutlich länger.</span>
</div>
</div>
<div class="occult-status occult-status-hidden" id="occultSubmitStatus" hidden>
<span class="occult-spinner occult-spinner-visible" aria-hidden="true"></span>
<span>Berechnung laeuft... Die serverseitige Python-Auswertung kann je nach Grenzgroesse etwas dauern.</span>
<span>Berechnung laeuft... Die Auswertung kann je nach Grenzgrösse etwas dauern.</span>
</div>
</section>
@@ -384,7 +384,7 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
<section class="card occult-card">
<h2>Bereit zur Berechnung</h2>
<p class="hint">
Die Seite bleibt jetzt waehrend der Berechnung sichtbar. Waehle Datum und Grenzgroesse und starte die
Wähle Datum und Grenzgröße und starte die
Auswertung mit dem Button.
</p>
</section>
@@ -399,7 +399,7 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
<div class="occult-detail-head-controls">
<div class="occult-detail-meta" id="occultDetailMeta">Feld 5 x 5 Grad, Mond im Zentrum</div>
<div class="occult-map-size-row">
<label for="occultMapSizeSelect" class="occult-select-label">Kartengroesse</label>
<label for="occultMapSizeSelect" class="occult-select-label">Kartengrösse</label>
<select id="occultMapSizeSelect" class="satpass-select occult-select">
<option value="5" selected>5 x 5 Grad</option>
<option value="10">10 x 10 Grad</option>
@@ -432,7 +432,7 @@ if (!empty($currentLocation) && is_array($currentLocation)) {
<div class="occult-submit-progress-bar" aria-hidden="true">
<div class="occult-submit-progress-fill" id="occultSubmitProgressFill"></div>
</div>
<div class="occult-submit-progress-note">Geschaetzter Fortschritt waehrend der laufenden Serverberechnung.</div>
<div class="occult-submit-progress-note">Geschätzter Fortschritt während der laufenden Serverberechnung.</div>
</div>
</div>
</div>
-21
View File
@@ -717,27 +717,6 @@ function drawArc(){
ctx.clip();
if(!sd.polarNight){
// Unter-Horizont gestrichelt (normal day) - nur Punkte wo alt<0
if(!sd.polarDay){
const sub=[];
for(let i=0;i<=80;i++){
const h=sd.riseUTC-3+i*6/80;
const{altitude,azimuth}=sunAltAz(loc.lat,loc.lon,hourToDate(currentDate,h));
if(altitude<0) sub.push(proj(altitude,azimuth));
else if(sub.length>0) { sub.push(null); } // Unterbrecher
}
// Zeichne mit Unterbrechungen
ctx.beginPath();
let inPath=false;
sub.forEach(p=>{
if(!p){inPath=false;return;}
if(!inPath){ctx.moveTo(p.x,p.y);inPath=true;}
else ctx.lineTo(p.x,p.y);
});
ctx.strokeStyle='rgba(200,168,75,.15)';ctx.lineWidth=1.5*dpr;
ctx.setLineDash([4*dpr,8*dpr]);ctx.stroke();ctx.setLineDash([]);
}
// Äußerer Gluehschein
ctx.beginPath();
drawWrappedPath(ctx,pts,W,sd.polarDay);
+15 -12
View File
@@ -185,9 +185,12 @@ PS
$body = @file_get_contents($url, false, $context);
$statusCode = 0;
$responseHeaders = function_exists('http_get_last_response_headers')
? http_get_last_response_headers()
: ($http_response_header ?? null);
if (isset($http_response_header) && is_array($http_response_header)) {
foreach ($http_response_header as $headerLine) {
if (is_array($responseHeaders)) {
foreach ($responseHeaders as $headerLine) {
if (preg_match('/^HTTP\/\S+\s+(\d{3})\b/', $headerLine, $matches) === 1) {
$statusCode = (int) $matches[1];
break;
@@ -233,7 +236,7 @@ function sendTelegramMessage(string $botToken, string $chatId, string $messageTe
if (!is_array($decoded)) {
return [
'ok' => false,
'description' => 'Telegram hat keine gueltige JSON-Antwort geliefert.',
'description' => 'Telegram hat keine gültige JSON-Antwort geliefert.',
];
}
@@ -304,7 +307,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
if ($action !== 'send_broadcast') {
$errors[] = 'Die angeforderte Aktion ist ungueltig.';
$errors[] = 'Die angeforderte Aktion ist ungültig.';
}
if ($messageText === '') {
@@ -348,7 +351,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
} elseif ($sendMode === 'selected') {
if (empty($selectedRecipientIds)) {
$errors[] = 'Bitte mindestens einen Empfaenger auswaehlen.';
$errors[] = 'Bitte mindestens einen Empfänger auswählen.';
} else {
foreach ($selectedRecipientIds as $recipientId) {
if (isset($recipientIndex[$recipientId])) {
@@ -357,13 +360,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
if (empty($targetRecipients)) {
$errors[] = 'Die ausgewaehlten Empfaenger konnten nicht geladen werden.';
$errors[] = 'Die ausgewählten Empfänger konnten nicht geladen werden.';
}
}
} elseif ($sendMode === 'all') {
$targetRecipients = $recipients;
} else {
$errors[] = 'Der Versandmodus ist ungueltig.';
$errors[] = 'Der Versandmodus ist ungültig.';
}
}
@@ -419,7 +422,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
</p>
<table class="data-table mb-lg">
<tr><td>Verknuepfte Master-Konten</td><td><?= count($recipients) ?></td></tr>
<tr><td>Verknüpfte Master-Konten</td><td><?= count($recipients) ?></td></tr>
<tr><td>Bot</td><td><?= h(($telegramConfig['bot_username'] ?? '') !== '' ? '@' . (string) $telegramConfig['bot_username'] : 'nicht konfiguriert') ?></td></tr>
</table>
@@ -428,14 +431,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<input type="hidden" name="csrf_token" value="<?= h($_SESSION['telegram_broadcast_csrf']) ?>">
<div class="form-group">
<label>Empfaenger</label>
<label>Empfänger</label>
<label>
<input type="radio" name="send_mode" value="self" <?= $sendMode === 'self' ? 'checked' : '' ?>>
Nur an mich senden
</label>
<label>
<input type="radio" name="send_mode" value="selected" <?= $sendMode === 'selected' ? 'checked' : '' ?>>
An ausgewaehlte Nutzer senden
An ausgewählte Nutzer senden
</label>
<label>
<input type="radio" name="send_mode" value="all" <?= $sendMode === 'all' ? 'checked' : '' ?>>
@@ -444,7 +447,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
</div>
<div class="form-group">
<label for="recipient_ids">Auswahl fuer "ausgewaehlte Nutzer"</label>
<label for="recipient_ids">Auswahl für "ausgewählte Nutzer"</label>
<select id="recipient_ids" name="recipient_ids[]" multiple size="8">
<?php foreach ($recipients as $recipient): ?>
<?php $recipientId = (int) $recipient['id']; ?>
@@ -473,7 +476,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
</div>
<div class="card">
<h2>Empfaenger</h2>
<h2>Empfänger</h2>
<?php if (empty($recipients)): ?>
<p class="hint">Noch keine verknuepften Master-Konten vorhanden.</p>
<?php else: ?>
+5 -5
View File
@@ -36,7 +36,7 @@ $publicBasePath = '../';
$pageTitle = 'Telegram verbinden - AstroTools';
$headerIntroPre = 'Konto';
$headerIntroTitle = 'Telegram verbinden';
$headerIntroSub = 'Bot-Verknuepfung fuer spaetere Benachrichtigungen.';
$headerIntroSub = 'Bot-Verknüpfung für spätere Benachrichtigungen.';
function h(?string $value): string
{
@@ -107,7 +107,7 @@ $deepLink = $botUsername !== ''
<?php require __DIR__ . '/../header.php'; ?>
<div class="card">
<h2>Telegram-Verknuepfung</h2>
<h2>Telegram-Verknüpfung</h2>
<p class="hint">
Diese Verbindung merkt sich die Chat-ID deines Telegram-Kontos, damit spaeter ein Bot dir Inhalte direkt schicken kann.
</p>
@@ -122,11 +122,11 @@ $deepLink = $botUsername !== ''
<?php if ($deepLink !== null): ?>
<p>
<a class="btn btn-primary" href="<?= h($deepLink) ?>" target="_blank" rel="noopener">Bot in Telegram oeffnen</a>
<a class="btn btn-primary" href="<?= h($deepLink) ?>" target="_blank" rel="noopener">Bot in Telegram öffnen</a>
</p>
<div class="telegram-qr-block">
<div class="telegram-qr-image" id="telegramConnectQrLarge" data-telegram-link="<?= h($deepLink) ?>"></div>
<p class="hint">QR-Code mit dem Handy scannen. Telegram oeffnet dann direkt den richtigen Bot mit deinem Verknuepfungslink.</p>
<p class="hint">QR-Code mit dem Handy scannen. Telegram öffnet dann direkt den richtigen Bot mit deinem Verknüpfungslink.</p>
</div>
<?php else: ?>
<p class="hint">
@@ -144,7 +144,7 @@ $deepLink = $botUsername !== ''
</div>
<p class="hint">
Nach erfolgreicher Verknuepfung kannst du zurueck zu <a href="<?= h($publicBasePath . 'settings.php') ?>">Einstellungen</a>.
Nach erfolgreicher Verknüpfung kannst du zurück zu <a href="<?= h($publicBasePath . 'settings.php') ?>">Einstellungen</a>.
</p>
</div>
+40 -4
View File
@@ -40,7 +40,7 @@ foreach (['host', 'dbname', 'user', 'pass'] as $requiredKey) {
}
}
$options = parseCliArguments($argv);
$options = parseCliArguments(getCliArguments());
if (($options['help'] ?? false) === true) {
writeUsage();
@@ -341,6 +341,19 @@ function parseCliArguments(array $argv): array
return $options;
}
function getCliArguments(): array
{
if (isset($GLOBALS['argv']) && is_array($GLOBALS['argv'])) {
return $GLOBALS['argv'];
}
if (isset($_SERVER['argv']) && is_array($_SERVER['argv'])) {
return $_SERVER['argv'];
}
return [];
}
function writeUsage(): void
{
$usageLines = [
@@ -516,12 +529,10 @@ PS
$response = curl_exec($ch);
if ($response === false) {
$error = curl_error($ch);
curl_close($ch);
throw new RuntimeException('Download fehlgeschlagen: ' . $error);
}
$httpCode = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($httpCode < 200 || $httpCode >= 300) {
throw new RuntimeException("Download fehlgeschlagen. HTTP-Status: {$httpCode}");
@@ -699,6 +710,31 @@ function jdToUtcString(float $jd): string
function writeMessage(string $message, bool $isError = false): void
{
$prefix = '[' . date('Y-m-d H:i:s') . '] ';
$stream = $isError ? STDERR : STDOUT;
$stream = openOutputStream($isError);
fwrite($stream, $prefix . $message . PHP_EOL);
}
/**
* `STDOUT` und `STDERR` sind in manchen CGI-/Web-Kontexten nicht definiert.
* Daher greifen wir auf die entsprechenden php:// Streams zurueck.
*
* @return resource
*/
function openOutputStream(bool $isError)
{
if ($isError && defined('STDERR')) {
return STDERR;
}
if (!$isError && defined('STDOUT')) {
return STDOUT;
}
$stream = fopen($isError ? 'php://stderr' : 'php://stdout', 'wb');
if ($stream === false) {
throw new RuntimeException('Ausgabestream konnte nicht geoeffnet werden.');
}
return $stream;
}