diff options
Diffstat (limited to 'kuchenstand.js')
| -rw-r--r-- | kuchenstand.js | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/kuchenstand.js b/kuchenstand.js new file mode 100644 index 0000000..a3b8949 --- /dev/null +++ b/kuchenstand.js @@ -0,0 +1,202 @@ +/** + * + * @licstart The following is the entire license notice for the + * JavaScript code in this page. + * + * Copyright (C) 2025-2026 Florian Fischer + * + * + * The JavaScript code in this page is free software: you can + * redistribute it and/or modify it under the terms of the GNU + * General Public License (GNU GPL) as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. The code is distributed WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU GPL for more details. + * + * As additional permission under GNU GPL version 3 section 7, you + * may distribute non-source (e.g., minimized or compacted) forms of + * that code without the copy of the GNU GPL normally required by + * section 4, provided you include this license notice and a URL + * through which recipients can access the Corresponding Source. + * + * @licend The above is the entire license notice + * for the JavaScript code in this page. + * + */ + +// Observer der auf 'resize' events unseres Kuchens reagiert und den zweiten +// Kuchen und die Streak-Anzeige gegebenfalls anpasst. +let kuchenResizeObserver; + +// Berechne die Streaks, wenn die Seite komplett geladen wurde. +window.onload = function() { + berechneStreaks(); + const params = new URLSearchParams(location.search); + // Ist in der URL die Klasse per Search-Parameter ?klasse=... spezifiziert? + const klasse = params.get('klasse'); + if (klasse) { + wechselKlasse(klasse); + } +} + +var streaks; + +// Berechne den Streak für alle Klassen. +// Die globale Variable daten.streakEreignisse muss definiert sein und ein Iterable +// aller Ereignisse, die für den Streak relevant sind, enthalten. +// Ereignisse ohne positives Delta beenden einen Streak. +function berechneStreaks() { + // Es gibt keine Streak-Ereignisse + if (!daten.streakEreignisse) { + return; + } + + // Variable um den laufenden Streak per Klasse zu bestimmen + streaks = Object.fromEntries(daten.klassen.map(klasse => [klasse.bezeichner, 0])); + + for (let ereignis of daten.streakEreignisse) { + const klasse = `${ereignis.Jahrgang}${ereignis.Bezeichner}`; + + // Das Streak Ereignis bringt keine Punkte und unterbricht damit den Streak. + if (ereignis.Delta == 0) { + streaks[klasse] = 0; + // Erhöhe den Streak. + } else { + streaks[klasse] = streaks[klasse] + 1; + } + } +} + + +// Wechsel zur Ansicht der Klasse. +function wechselKlasse(klasse) { + zeichne(klasse); + zeichneStreak(klasse); + listeStreakEreignisse(klasse); +} + +// Zeichne die Kuchen Graphik für die aktuelle Klasse. +function zeichne(klasse) { + // Wir haben keine Daten zum Zeichnen + if (!streaks) { + return; + } + + const kuchenNode = document.getElementById("kuchen"); + // Entferne alle bestehenden Kind-Objekte vom Kuchen-Element. + while (kuchenNode.firstChild) { + kuchenNode.removeChild(kuchenNode.firstChild); + } + + const kuchenHintergrund = document.createElement("IMG"); + kuchenHintergrund.src = "img/kuchen.png" + kuchenHintergrund.classList.add("kuchen-hintergrund"); + // Füge das Hintergrundbild dem DOM hinzu + kuchenNode.appendChild(kuchenHintergrund); + + const kuchenVordergrund = document.createElement("IMG"); + kuchenVordergrund.src = "img/kuchen.png" + kuchenVordergrund.classList.add("kuchen-vordergrund"); + + // Passe die Größe des Kuchens anhand des Streaks an + const streak = streaks[klasse]; + const proportion = streak/streak_anzahl * 100; + kuchenVordergrund.style.maxWidth = `${proportion}%`; + kuchenVordergrund.style.maxHeight = `${proportion}%`; + + kuchenNode.appendChild(kuchenVordergrund); + + // Erzeuge ein Observer der auf Größenveränderungen des Kuchens reagiert. + let streakNode = document.getElementById("streak"); + kuchenResizeObserver = new ResizeObserver((entries) => { + for (const entry of entries) { + // Positioniere den kleinen Kuchen und die Streak-Anzeige passend zur + // echten Anzeigegröße. + kuchenVordergrund.style.top = `-${entry.contentRect.height/2 + kuchenVordergrund.offsetHeight/2}px`; + kuchenVordergrund.style.left = `${entry.contentRect.width/2 - kuchenVordergrund.offsetWidth/2}px`; + streakNode.style.top = `-${kuchenVordergrund.offsetHeight}px` + } + }); + + kuchenResizeObserver.observe(kuchenHintergrund); +} + +// Zeichne den Streak für die aktuelle Klasse. +function zeichneStreak(klasse) { + // Es gibt keine Streak-Ereignisse + if (!streaks) { + return; + } + + const streak = streaks[klasse]; + + let streakNode = document.getElementById("streak"); + // Schreibe den aktuellen Streak in das Streak span-Element + streakNode.innerHTML = `${streak}/${streak_anzahl}`; +} + +// Erzeuge eine Liste aller Ereignisse der Klasse. +// Die globale Variable daten.streakEreignisse muss definiert sein und ein Iterable aller +// Ereignisse, die angezeigt werden sollen, enthalten. +function listeStreakEreignisse(klasse) { + let liste = document.getElementById("ereignisse"); + while (liste.firstChild) { + liste.removeChild(liste.firstChild); + } + + // Erzeuge ein Mapping von Klassenbezeichner auf die entsprechende Klasse + let klassen = {}; + daten.klassen.forEach(klasse => { klassen[klasse.bezeichner] = klasse }); + + for (let ereignis of daten.streakEreignisse) { + let klassenBezeichner = `${ereignis.Jahrgang}${ereignis.Bezeichner}` + + // Das Ereignis ist nicht für diese Klasse + if (klassenBezeichner !== klasse) { + continue; + } + + // Erzeuge ein Listenelement, das alle Infos des Ereignisses enthält + let ereignisNode = document.createElement("li"); + ereignisNode.classList.add("ereignis"); + + // Setze den Hintergrund des Ereignisses auf 40% der Klassenfarbe + const farbe = klassen[klassenBezeichner].farbe; + if (klasse) { + ereignisNode.style.backgroundColor = `${farbe}40`; + } + + // Span, das die Meta-Informationen (Klasse, Datum) eines Ereignisses enthält + let ereignisUrsache = document.createElement("span"); + ereignisUrsache.innerHTML = `<b>${klassenBezeichner}</b>` + if (ereignis.Datum) { + ereignisUrsache.innerHTML += ` <small>${ereignis.Datum}</small>` + } + ereignisUrsache.classList.add("ereignis-klasse"); + ereignisNode.appendChild(ereignisUrsache); + + // Die Veränderung des Punktestands durch das Ereignis + let ereignisDelta = document.createElement("span"); + ereignisDelta.innerHTML = `${ereignis.Delta}`; + ereignisDelta.classList.add("ereignis-delta"); + // Ergänze positive und negative CSS Klassen + if (ereignis.Delta > 0) { + ereignisDelta.classList.add("positiv"); + } else { + ereignisDelta.classList.add("negativ"); + } + ereignisNode.appendChild(ereignisDelta); + + // Die Beschreibung des Ereignisses + let ereignisBeschreibung = document.createElement("span"); + ereignisBeschreibung.innerHTML = `<i>${ereignis.Beschreibung}</i>`; + ereignisBeschreibung.classList.add("ereignis-beschreibung"); + ereignisNode.appendChild(ereignisBeschreibung); + + // Füge das neue Ereignis-Listenelement der Ereignis-Liste hinzu. + // Jedes neue Element wird als erstes Kind eingefügt, damit + // neuere Ereignisse weiter oben in der Liste stehen + liste.insertBefore(ereignisNode, liste.firstChild); + } +} |
