-
-
Aufgaben
-
-
-
- | # |
- Bearbeiter |
- Aufgabe |
- Zieldatum |
- Priorität |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
+ function isEnabled() { return !!SCROLL_FLAGS.aufgaben; }
+
+ /* ===========================
+ Höhe der Scroll-Container
+ - Voller Viewport ab Oberkante
+ =========================== */
+ function sizeScrollContainers() {
+ const node = document.querySelector('#aufgaben-scroll');
+ if (!node) return;
+ const rect = node.getBoundingClientRect();
+ const bottomMargin = 16;
+ const minH = 120;
+ const available = window.innerHeight - rect.top - bottomMargin;
+ node.style.maxHeight = Math.max(minH, available) + 'px';
+ }
+
+ /* ===========================
+ Scroll-/Interaktionsstatus
+ =========================== */
+ const scrollState = new WeakMap(); // { userActive:boolean, autoTimer:number|null, loopHeight:number }
+
+ function ensureState(el) {
+ if (!scrollState.get(el)) scrollState.set(el, { userActive: false, autoTimer: null, loopHeight: 0 });
+ return scrollState.get(el);
+ }
+
+ function attachScrollGuards($scroller) {
+ const el = $scroller.get(0);
+ if (!el || el.__guardsBound) return;
+ if (!isEnabled()) return;
+ const state = ensureState(el);
+
+ const markActive = () => {
+ state.userActive = true;
+ clearTimeout(state._quietT);
+ state._quietT = setTimeout(() => { state.userActive = false; }, 800);
+ stopAutoScroll($scroller);
+ };
+
+ $scroller.on('wheel touchstart touchmove keydown mousedown mouseenter', markActive);
+ $scroller.on('mouseleave', () => setTimeout(() => maybeStartAutoScroll($scroller), 600));
+ el.__guardsBound = true;
+ }
+
+ /* ===========================
+ Nahtloser Loop (Clone)
+ =========================== */
+ function buildSeamlessLoop($table, $scroller) {
+ $table.find('tbody.__loopClone').remove();
+
+ const scEl = $scroller.get(0);
+ const st = ensureState(scEl);
+ st.loopHeight = 0;
+
+ if (!isEnabled()) return;
+
+ const $main = $table.find('tbody').first();
+ if ($main.length === 0 || $main.children().length === 0) return;
+
+ const needScroll = $main.get(0).offsetHeight > scEl.clientHeight + 1;
+ if (!needScroll) return;
+
+ const $clone = $main.clone(false, false).addClass('__loopClone').attr('aria-hidden', 'true');
+ $table.append($clone);
+ st.loopHeight = $main.get(0).offsetHeight;
+ }
+
+ /* ===========================
+ Auto-Scroll
+ =========================== */
+ function startAutoScroll($scroller, speedPx = 1, stepMs = 40) {
+ const el = $scroller.get(0);
+ if (!isEnabled()) return;
+
+ const st = ensureState(el);
+ if (st.autoTimer) return;
+ if (st.loopHeight <= 0) return;
+
+ const tick = () => {
+ if (st.userActive) { stopAutoScroll($scroller); return; }
+ el.scrollTop += speedPx;
+ if (el.scrollTop >= st.loopHeight) el.scrollTop -= st.loopHeight;
+ };
+
+ st.autoTimer = setInterval(tick, stepMs);
+ }
+
+ function stopAutoScroll($scroller) {
+ const el = $scroller.get(0);
+ const st = ensureState(el);
+ if (st.autoTimer) {
+ clearInterval(st.autoTimer);
+ st.autoTimer = null;
+ }
+ }
+
+ function maybeStartAutoScroll($scroller) {
+ const el = $scroller.get(0);
+ if (!isEnabled()) { stopAutoScroll($scroller); return; }
+ const st = ensureState(el);
+ if (st.userActive) return;
+ if (st.loopHeight > 0) startAutoScroll($scroller, 1, 80);
+ else stopAutoScroll($scroller);
+ }
+
+ /* ===========================
+ Smart AJAX Reload
+ =========================== */
+ function smartLoad($scroller, $table, $target, url, intervalMs) {
+ const scEl = $scroller.get(0);
+ const st = ensureState(scEl);
+
+ const oldLoop = st.loopHeight > 0 ? st.loopHeight : Math.max(1, scEl.scrollHeight - scEl.clientHeight);
+ const posInLoop = st.loopHeight > 0 ? (scEl.scrollTop % oldLoop) : scEl.scrollTop;
+ const posRatio = Math.min(1, posInLoop / oldLoop);
+
+ $target.load(url, function () {
+ buildSeamlessLoop($table, $scroller);
+
+ if (st.loopHeight > 0 && isEnabled()) {
+ const newPos = Math.floor(posRatio * st.loopHeight);
+ if (Math.abs(scEl.scrollTop - newPos) > 1) {
+ requestAnimationFrame(() => { scEl.scrollTop = newPos; });
+ }
+ stopAutoScroll($scroller);
+ startAutoScroll($scroller, 1, 80);
+ } else {
+ if (scEl.scrollTop !== 0) requestAnimationFrame(() => { scEl.scrollTop = 0; });
+ stopAutoScroll($scroller);
+ }
+
+ setTimeout(() => smartLoad($scroller, $table, $target, url, intervalMs), intervalMs);
+ });
+ }
+
+ /* ===========================
+ Initialisierung
+ =========================== */
+ $(document).ready(function () {
+ sizeScrollContainers();
+ $(window).on('resize', function () {
+ sizeScrollContainers();
+ const $scroller = $('#aufgaben-scroll');
+ const $table = $('#aufgaben-table');
+ buildSeamlessLoop($table, $scroller);
+ maybeStartAutoScroll($scroller);
+ });
+
+ // Guards nur aktivieren, wenn Scrolling erlaubt
+ if (isEnabled()) attachScrollGuards($('#aufgaben-scroll'));
+
+ // Erstladen
+ (function init() {
+ const $scroller = $('#aufgaben-scroll');
+ const $table = $('#aufgaben-table');
+ const $target = $('#AufgabenTableHolder');
+ const url = 'sources/getAufgabenTable.php';
+ const interval = 30000;
+
+ $target.load(url, function () {
+ buildSeamlessLoop($table, $scroller);
+ maybeStartAutoScroll($scroller);
+ setTimeout(() => smartLoad($scroller, $table, $target, url, interval), interval);
+ });
+ })();
+ });
+
+
-
-
\ No newline at end of file
+?>
diff --git a/PackAufgabenMonitor.php b/PackAufgabenMonitor.php
index 79663b9..9ac584c 100644
--- a/PackAufgabenMonitor.php
+++ b/PackAufgabenMonitor.php
@@ -23,15 +23,71 @@ $Epi = new Epirent();
-
+
+
+
+
+
+
+
+
+
+
+
+
+
Konfiguration
+
+ - Dashboard / Konfiguration
+
+
+
+
= h($message) ?>
+
+
+
= h($err) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+