/* Spiegel im Blaugrau-Eingang — Anzeige-Logik.
*
* Die Seite liegt statisch auf mirror.bbox.at und holt sich alles aus zwei
* kleinen Dateien, die ein Cronjob auf derselben Box schreibt:
* data/mirror.json Bitcoin, Wetter Wien, "Heute vor X Jahren"
* data/nachricht.txt Team-Nachricht (leer = keine)
* Dazu data/geburtstage.json, das mit dem Deploy mitkommt.
*
* Bewusst KEINE direkten API-Aufrufe mehr aus dem Browser: haengt eine Quelle
* oder drosselt sie, bleibt am Spiegel der letzte gute Stand stehen.
*
* Emma Stark, 28.07.2026
*/
var wochentageDE = ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'];
var monateDE = ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli',
'August', 'September', 'Oktober', 'November', 'Dezember'];
var geburtstage = []; // [{tag, monat, name}]
var historie = []; // [{jahre, jahr, text}]
var historieIndex = 0;
var nachrichtAktiv = false;
var feiertage = {}; // {"2026-12-25": {name, icon}}
$(document).ready(function () {
uhrStellen();
setInterval(uhrStellen, 10000);
ladeGeburtstage();
ladeDaten();
setInterval(ladeDaten, 300000); // Cron schreibt alle 5 Minuten
ladeNachricht();
setInterval(ladeNachricht, 60000); // Nachricht soll schnell ankommen
// Historie langsam durchwechseln — der Spiegel wird mehrmals taeglich
// im Vorbeigehen gelesen, ein einziger Fakt pro Stunde waere fad.
setInterval(naechsteHistorie, 45000);
// Nach einem Deploy von selbst die neue Fassung holen — siehe pruefeVersion().
merkeVersion();
setInterval(pruefeVersion, 120000);
// Sicherheitsnetz gegen Speicherlecks im Dauerbetrieb. Selten, weil ein
// Reload den Bildschirm kurz aufblitzen laesst; Inhalte und Tageswechsel
// aktualisieren sich ohnehin ohne Neuladen.
setTimeout(function () { location.reload(); }, 21600000); // 6 Stunden
});
/* ---------- Uhrzeit + Datum ---------- */
var heutigerTag = null;
function uhrStellen() {
var d = new Date();
// Um Mitternacht rechnen sich die drei Tage und die Geburtstage neu —
// sonst stuende bis zum naechsten Reload der Vortag da.
var datumsKennung = d.toDateString();
if (heutigerTag !== null && heutigerTag !== datumsKennung) {
zeigeTage();
pruefeHeutigenGeburtstag();
}
heutigerTag = datumsKennung;
// Ohne Jahreszahl — kurz gehalten, damit die Zeile nicht ausufert.
$('#tag').html(wochentageDE[d.getDay()] + ', ' + d.getDate() + '. ' + monateDE[d.getMonth()]);
$('#uhrzeit').html(('0' + d.getHours()).slice(-2) + ':' + ('0' + d.getMinutes()).slice(-2));
$('#today').show();
}
/* ---------- Geburtstage ---------- */
function ladeGeburtstage() {
$.getJSON('data/geburtstage.json', function (d) {
geburtstage = d || [];
zeigeTage();
pruefeHeutigenGeburtstag();
});
}
// Wer hat an diesem Datum Geburtstag? Liefert die Namen als Array.
function geburtstagsNamen(datum) {
var tag = datum.getDate(), monat = datum.getMonth() + 1;
var treffer = [];
for (var i = 0; i < geburtstage.length; i++) {
if (geburtstage[i].tag === tag && geburtstage[i].monat === monat) {
treffer.push(geburtstage[i].name);
}
}
return treffer;
}
// Die naechsten drei Tage unten: Wochentag, darunter ggf. Torte + Name.
function zeigeTage() {
for (var i = 1; i <= 3; i++) {
var d = new Date();
d.setDate(d.getDate() + i);
$('.day' + i + ' h1').html(wochentageDE[d.getDay()]);
var namen = geburtstagsNamen(d);
var html = '';
for (var n = 0; n < namen.length; n++) {
html += "
" + namen[n] + "
";
}
$('.day' + i + ' .eintrag').html(html);
var f = feiertage[datumsSchluessel(d)];
var zeile = $('.day' + i + ' .feiertag');
if (f) {
zeile.attr('class', 'feiertag f-' + f.icon).html(f.name).show();
} else {
zeile.hide().empty();
}
}
$('#kalender').show();
streifenAusmessen();
}
// "2026-12-25" — lokal gerechnet, nicht ueber toISOString (das waere UTC und
// wuerde abends auf den Folgetag springen).
function datumsSchluessel(d) {
return d.getFullYear() + '-' +
('0' + (d.getMonth() + 1)).slice(-2) + '-' +
('0' + d.getDate()).slice(-2);
}
// Der Mittelstreifen bekommt genau den Platz, der zwischen Bitcoin-Block und
// Tagesvorschau uebrig ist. Noetig, weil die Tagesspalten je nach Inhalt
// unterschiedlich hoch sind (Vorhersage + evtl. Feiertag + evtl. Geburtstag) —
// mit fester Position waere der Streifen sonst irgendwann ueberlappt.
function streifenAusmessen() {
var untenFrei = $('#kalender').outerHeight() + 30;
var obenFrei = $('#btc').position().top + $('#btc').outerHeight() + 30;
$('#mitte').css({ top: obenFrei + 'px', bottom: untenFrei + 'px' });
}
// Am Tag selbst: Vollbild, das sich periodisch fuer 15 Sekunden einblendet.
// Dauerhaft waere es zu viel — man sieht sonst nie mehr Uhrzeit oder Kurs.
//
// Erst aufraeumen, dann neu setzen: die Funktion laeuft auch beim Tageswechsel
// um Mitternacht. Ohne das Loeschen liefe der Timer von gestern weiter (das
// Vollbild kaeme dann doppelt so oft) und der Name von gestern stuende bis zum
// naechsten Reload weiter oben links.
var geburtstagsTimer = null;
function pruefeHeutigenGeburtstag() {
if (geburtstagsTimer !== null) {
clearInterval(geburtstagsTimer);
geburtstagsTimer = null;
}
$('#todayevents').empty();
$('#geburtstag').hide();
var namen = geburtstagsNamen(new Date());
if (!namen.length) return;
$('#todayevents').html("
" + namen.join(', ') + "
");
$('#geburtstag .gb-name').html(namen.join(' & '));
zeigeGeburtstagsVollbild();
geburtstagsTimer = setInterval(zeigeGeburtstagsVollbild, 120000); // alle 2 Minuten
}
function zeigeGeburtstagsVollbild() {
$('#geburtstag').fadeIn(600);
setTimeout(function () { $('#geburtstag').fadeOut(600); }, 15000);
}
/* ---------- Bitcoin + Historie (aus data/mirror.json) ---------- */
function ladeDaten() {
$.getJSON('data/mirror.json?t=' + new Date().getTime(), function (d) {
if (!d) return;
if (d.btc) zeigeBitcoin(d.btc);
if (d.wetter) zeigeWetter(d.wetter);
if (d.feiertage) {
feiertage = d.feiertage;
zeigeTage();
}
if (d.heute_vor && d.heute_vor.length) {
historie = d.heute_vor;
if (historieIndex >= historie.length) historieIndex = 0;
zeigeHistorie();
}
});
}
function zeigeBitcoin(btc) {
var rauf = btc.aenderung >= 0;
$('.bitcoin .bcvalue').html(zahl(btc.eur) + ' €');
$('.bitcoin .usdvalue').html(zahl(btc.usd) + ' $');
$('.bitcoin .bcchange').html((rauf ? '▲' : '▼') + ' ' +
Math.abs(btc.aenderung).toFixed(1).replace('.', ',') + ' %');
// up/down am HTML-Container — von dort faerben sich Prozentwert UND Graph.
// NICHT am