Tag-Zeitachse: gestauchte Skala - Nacht 00-06 und Abend 21-24 je 1 Block, Tag 06-21 stuendlich

Regler UND Zeitachse/Balken nutzen dieselbe Stunde-Einheit-Abbildung (_hourToUnit/_unitToHour,
17 gleich breite Bloecke). Nacht-/Abend-Bloecke dunkel ohne interne Linien; Tag stuendlich
beschriftet + Trennlinien. Auto-Default rundet auf Block-Grenzen. Marker an den Block-Grenzen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-23 22:23:34 +02:00
parent 1e1c1238bd
commit 9a1ee0fae1
+55 -33
View File
@@ -2105,6 +2105,21 @@ function _vHM(ms){
var s = new Date(ms).toLocaleString('en-GB', {timeZone:'Europe/Vienna', hour:'2-digit', minute:'2-digit', hour12:false});
var p = s.split(':'); return { h: parseInt(p[0],10)||0, m: parseInt(p[1],10)||0 };
}
// Gestauchte Tages-Skala: Nacht 0006 und Abend 2124 je NUR EIN Block, dazwischen
// (0621) stündlich. 17 Blöcke → „Einheiten" 0..17. Stunde ⇄ Einheit.
function _hourToUnit(h){
h = Math.max(0, Math.min(24, h));
if (h <= 6) return h/6; // 0006 → Einheit 0..1
if (h <= 21) return 1 + (h-6); // 0621 → Einheit 1..16 (1 pro Stunde)
return 16 + (h-21)/3; // 2124 → Einheit 16..17
}
function _unitToHour(u){
u = Math.max(0, Math.min(17, u));
if (u <= 1) return u*6; // 0→00, 1→06
if (u <= 16) return 6 + (u-1); // 1→06 … 16→21
return 21 + (u-16)*3; // 16→21, 17→24
}
var TS_STOPS = [0,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,24]; // Einheit-Index → Stunde
function _tsInjectCss(){
if (document.getElementById('ts-css')) return;
var s = document.createElement('style'); s.id = 'ts-css';
@@ -2128,21 +2143,22 @@ function _tsInjectCss(){
document.head.appendChild(s);
}
// Schieberegler-Interaktion (global, für inline-Handler).
// Regler arbeitet in EINHEITEN 0..17 (gestauchte Skala). from/to = Einheit-Index.
function _tsClamp(){
var fe=document.getElementById('tsFrom'), te=document.getElementById('tsTo');
if(!fe||!te) return null;
var f=+fe.value, t=+te.value;
if(f>=t){ if(f>=24){f=23;} t=Math.min(24,f+1); if(+te.value<t) te.value=t; if(+fe.value>f) fe.value=f; }
if(f>=t){ if(f>=17){f=16;} t=Math.min(17,f+1); if(+te.value<t) te.value=t; if(+fe.value>f) fe.value=f; }
return {f:f,t:t};
}
function _tsVisual(){
var v=_tsClamp(); if(!v) return;
var r=document.getElementById('tsRange'); if(r){ r.style.left=(v.f/24*100)+'%'; r.style.width=((v.t-v.f)/24*100)+'%'; }
var l=document.getElementById('tsLbl'); if(l) l.textContent=_pad2(v.f)+':00 '+_pad2(v.t)+':00';
var r=document.getElementById('tsRange'); if(r){ r.style.left=(v.f/17*100)+'%'; r.style.width=((v.t-v.f)/17*100)+'%'; }
var l=document.getElementById('tsLbl'); if(l) l.textContent=_pad2(_unitToHour(v.f))+':00 '+_pad2(_unitToHour(v.t))+':00';
}
function _tsCommit(){
var v=_tsClamp(); if(!v) return;
_liveWin.from=v.f; _liveWin.to=v.t;
_liveWin.from=v.f; _liveWin.to=v.t; // Einheiten
renderLiveSummary();
}
function _tsReset(){ _liveWin.from=null; _liveWin.to=null; renderLiveSummary(); }
@@ -2239,15 +2255,25 @@ async function renderLiveSummary() {
var _lo=_vHM(minMs), _hi=_vHM(maxMs);
var autoFromH = Math.max(0, _lo.h);
var autoToH = Math.min(24, _hi.h + ((_hi.m>0)?1:0));
if (autoToH <= autoFromH) autoToH = Math.min(24, autoFromH+1);
// Effektives Fenster: Schieberegler-Auswahl (falls gesetzt) ODER Auto.
var fromH = (_liveWin.from!=null) ? _liveWin.from : autoFromH;
var toH = (_liveWin.to !=null) ? _liveWin.to : autoToH;
if (toH <= fromH) toH = Math.min(24, fromH+1);
// Auto-Grenzen als EINHEITEN der gestauchten Skala (auf Block-Grenzen gerundet).
var autoFromU = Math.floor(_hourToUnit(autoFromH));
var autoToU = Math.ceil(_hourToUnit(autoToH));
if (autoToU <= autoFromU) autoToU = Math.min(17, autoFromU+1);
// Effektives Fenster in Einheiten: Regler-Auswahl ODER Auto.
var fromU = (_liveWin.from!=null) ? _liveWin.from : autoFromU;
var toU = (_liveWin.to !=null) ? _liveWin.to : autoToU;
if (toU <= fromU) toU = Math.min(17, fromU+1);
var fromH = _unitToHour(fromU), toH = _unitToHour(toU);
var winStart = dayBase + fromH*3600*1000;
var winEnd = dayBase + toH*3600*1000;
var winSpanMs = winEnd - winStart;
function _msToPct(ms) { return ((ms - winStart) / winSpanMs) * 100; }
// Position auf der GESTAUCHTEN Skala (Nacht/Abend gestaucht, Tag stündlich) —
// nicht linear in der Zeit.
function _msToPct(ms){
var h = (ms - dayBase)/3600000;
if (toU === fromU) return 0;
return ((_hourToUnit(h) - fromU) / (toU - fromU)) * 100;
}
function _fmtHM(ms) {
return new Date(ms).toLocaleTimeString('de-AT', {timeZone:'Europe/Vienna', hour:'2-digit', minute:'2-digit'});
}
@@ -2262,15 +2288,15 @@ async function renderLiveSummary() {
// (erste bis letzte Aktivität). onchange committet + rendert neu, oninput nur Visual.
_tsInjectCss();
var _isAuto = (_liveWin.from==null && _liveWin.to==null);
// Hintergrund: dunklere Nachtzeiten (0001 und 2024, um Mitternacht = ein Bereich)
// + 23 stündliche Trennlinien.
var _bg = '<span class="ts-night" style="left:0;width:'+(1/24*100)+'%"></span>'
+ '<span class="ts-night" style="left:'+(20/24*100)+'%;width:'+(4/24*100)+'%"></span>';
// Stunden-Trennlinien NUR im Tag-Bereich (219); die Nacht-Bänder (0001, 2024)
// bleiben glatt dunkel (zusammengefasst, keine Linien).
for (var _d=2; _d<20; _d++){ _bg += '<span class="ts-div" style="left:'+(_d/24*100)+'%"></span>'; }
// Stündliche Beschriftung 024.
var _ticks=''; for (var _h=0;_h<=24;_h++){ _ticks += '<span style="left:'+(_h/24*100)+'%">'+_pad2(_h)+'</span>'; }
// Gestauchte Skala (17 gleich breite Blöcke): Nacht 0006 = Block 0, Tag 0621
// stündlich = Blöcke 115, Abend 2124 = Block 16. Dunklere Nacht-/Abend-Blöcke
// OHNE interne Linien; Stunden-Trennlinien nur im Tag.
var _u2p = function(u){ return (u/17*100); };
var _bg = '<span class="ts-night" style="left:0;width:'+_u2p(1)+'%"></span>' // 0006
+ '<span class="ts-night" style="left:'+_u2p(16)+'%;width:'+(_u2p(17)-_u2p(16))+'%"></span>'; // 2124
for (var _d=2; _d<16; _d++){ _bg += '<span class="ts-div" style="left:'+_u2p(_d)+'%"></span>'; } // Tag-Stundenlinien
// Beschriftung an jeder Block-Grenze: 00,06,07,…,21,24.
var _ticks=''; for (var _u=0;_u<=17;_u++){ _ticks += '<span style="left:'+_u2p(_u)+'%">'+_pad2(_unitToHour(_u))+'</span>'; }
html += '<div class="ts-wrap">'
+ '<div class="ts-head">🕒 Zeitraum <b id="tsLbl">'+_pad2(fromH)+':00 '+_pad2(toH)+':00</b>'
+ (_isAuto ? '<span style="color:#8a8a8a;font-size:.66rem">Auto: erste letzte Aktivität</span>'
@@ -2278,25 +2304,21 @@ async function renderLiveSummary() {
+ '</div>'
+ '<div class="ts-slider">'
+ '<div class="ts-bg">'+_bg+'</div>'
+ '<div class="ts-range" id="tsRange" style="left:'+(fromH/24*100)+'%;width:'+((toH-fromH)/24*100)+'%"></div>'
+ '<input type="range" id="tsFrom" min="0" max="24" step="1" value="'+fromH+'" aria-label="Von" oninput="_tsVisual()" onchange="_tsCommit()">'
+ '<input type="range" id="tsTo" min="0" max="24" step="1" value="'+toH+'" aria-label="Bis" oninput="_tsVisual()" onchange="_tsCommit()">'
+ '<div class="ts-range" id="tsRange" style="left:'+_u2p(fromU)+'%;width:'+(_u2p(toU)-_u2p(fromU))+'%"></div>'
+ '<input type="range" id="tsFrom" min="0" max="17" step="1" value="'+fromU+'" aria-label="Von" oninput="_tsVisual()" onchange="_tsCommit()">'
+ '<input type="range" id="tsTo" min="0" max="17" step="1" value="'+toU+'" aria-label="Bis" oninput="_tsVisual()" onchange="_tsCommit()">'
+ '</div>'
+ '<div class="ts-ticks">'+_ticks+'</div>'
+ '</div>';
// Marker an sauberen Uhrzeiten INNERHALB des Fensters — Schrittweite passt sich
// der Spanne an (kurze Stunde = 15-min-Takt, langer Tag = Stunden-Takt), damit
// die Achse weder leer noch überladen wirkt. Erster Marker = erste saubere Zeit
// ab winStart (nicht winStart selbst, das liegt ja mitten im 15-min-Raster).
var stepMs = winSpanMs <= 2*3600*1000 ? 15*60*1000
: winSpanMs <= 5*3600*1000 ? 30*60*1000
: 60*60*1000;
// Marker an den Block-Grenzen der gestauchten Skala (Stopp-Stunden 0,6,7,…,21,24),
// soweit sie im gewählten Fenster liegen. So verdichtet sich nichts in den
// gestauchten Nacht-/Abend-Blöcken.
var scaleMarks = [];
for (var t = Math.ceil(winStart / stepMs) * stepMs; t <= winEnd; t += stepMs) {
var d = new Date(t);
scaleMarks.push({ ms: t, label: _fmtHM(t), isHour: d.getMinutes() === 0 });
}
TS_STOPS.forEach(function(hh){
if (hh < fromH - 0.001 || hh > toH + 0.001) return;
scaleMarks.push({ ms: dayBase + hh*3600*1000, label: _pad2(hh)+':00', isHour: true });
});
html += '<div style="display:grid;grid-template-columns:140px 1fr;gap:.5rem;font-size:.72rem;align-items:start">';
html += '<div></div><div style="position:relative;height:20px;border-bottom:1.5px solid rgba(0,0,0,.1);margin-bottom:.3rem">';