diff --git a/css/solar.css b/css/solar.css
index b0fde4c..bd45463 100644
--- a/css/solar.css
+++ b/css/solar.css
@@ -2974,8 +2974,11 @@ input[type='range']::-webkit-slider-runnable-track {
background: var(--ton-flaeche, #23282c);
border-color: rgba(var(--zl-farbe-rgb), .3);
}
-/* Am rechten Rand endet das Etikett am Balkenende statt dahinter. */
-.zl-fenster.links { justify-content: flex-end; }
+/* Reicht der Platz bis zum rechten Rand nicht, wird der Name gekuerzt. */
+.zl-etikett.gekappt {
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
.zl-lauf {
margin-left: .25rem;
color: var(--ton-gut, #4caf7d);
diff --git a/js/solar/zeitleiste.js b/js/solar/zeitleiste.js
index 71505a2..9fbdf5f 100644
--- a/js/solar/zeitleiste.js
+++ b/js/solar/zeitleiste.js
@@ -113,13 +113,26 @@ function zlStapeln(eintraege, breite) {
const lang = zlBreite(etikett) + 26;
let links = px(von) - 8;
let rechts = a.art === "fenster" ? Math.max(px(bis), px(von) + lang) : px(von) + lang;
- // Am rechten Rand steht das Etikett links vom Punkt bzw. endet am
- // Balkenende - sonst ragte es aus der Spur und der Rahmen bekaeme einen
- // Scrollbalken fuer ein paar Buchstaben.
- const nachLinks = px(von) + lang > breite - 4;
- if (nachLinks && a.art !== "fenster") { links = px(von) - lang; rechts = px(von) + 8; }
- if (nachLinks && a.art === "fenster") { links = Math.min(px(von), px(bis) - lang) - 8; rechts = px(bis); }
- return { a, links, rechts, nachLinks, etikett };
+ const ueber = px(von) + lang > breite - 4;
+ // Ein Punkt am rechten Rand trägt sein Etikett links von sich - das
+ // liest sich natürlich, die Marke bleibt, wo die Zeit ist.
+ const nachLinks = ueber && a.art !== "fenster";
+ if (nachLinks) { links = px(von) - lang; rechts = px(von) + 8; }
+ /*
+ * Ein Fenster-Etikett beginnt dagegen nie vor seinem Balken - sonst sähe
+ * "15:00–18:31" aus, als finge es um halb elf an. Reicht der Platz nicht,
+ * wird der Name gekürzt (voll steht er im Hinweis); nur wenn nicht einmal
+ * die Uhrzeit Platz hat, rückt es so wenig wie nötig nach links.
+ */
+ let kappe = null, versatz = 0;
+ if (ueber && a.art === "fenster") {
+ const rest = breite - 4 - px(von);
+ kappe = Math.max(96, rest);
+ versatz = Math.min(0, rest - kappe);
+ links = px(von) + versatz - 8;
+ rechts = Math.max(px(bis), px(von) + versatz + kappe);
+ }
+ return { a, links, rechts, nachLinks, kappe, versatz, etikett };
}).sort((x, y) => x.links - y.links);
/*
@@ -322,7 +335,9 @@ function zlEintrag(e, d, prozent) {
const titel = zlText(zlTitel(a, zustand));
const lauf = zustand !== "gelaufen" ? ""
: ' ' + (a.art === "fenster" ? '' + zlText(a.laeufe[a.laeufe.length - 1]) + "" : "");
- const etikett = '' + zlText(zlEtikettZeit(a)) + "" + zlText(a.name) + lauf + "";
+ const kappe = e.kappe ? ' style="max-width:' + Math.round(e.kappe) + "px" + (e.versatz ? ";margin-left:" + Math.round(e.versatz) + "px" : "") + '"' : "";
+ const etikett = '"
+ + zlText(zlEtikettZeit(a)) + "" + zlText(a.name) + lauf + "";
if (a.art === "fenster") {
// Läufe und die früheste Marke als Punkte auf dem Balken.
@@ -334,7 +349,7 @@ function zlEintrag(e, d, prozent) {
const m = zlMinuteAus(t);
marken += '';
});
- return '
'
+ '' + marken + "" + etikett + "
";
@@ -365,29 +380,47 @@ function zlKettenZeichnen(ziel) {
svg.setAttribute("width", basis.width);
svg.setAttribute("height", basis.height);
/*
- * Ankerpunkt eines Eintrags: beim Punkt die Marke, beim Fenster die
- * Laufmarke, sonst der Balkenanfang. Die Linie beginnt am Auslöser rechts
- * vom Anker und endet am Nachfolger links davon - so kommt sie an und
- * läuft nicht quer durch den Kreis.
+ * Die Linie verlässt den Auslöser lotrecht nach unten und biegt mit einer
+ * kleinen Rundung von links in den Nachfolger ein.
+ *
+ * Start: beim Punkt unter der Marke; beim Fenster unter der Laufmarke,
+ * sonst unter dem Balkenanfang. Ziel: beim Punkt links an der Marke, beim
+ * Fenster am Balkenanfang. Liegt der Nachfolger links vom Auslöser (etwa
+ * weil er früher beginnen darf), kommt die Linie von oben an.
*/
- const anker = el => {
+ const rect = el => {
+ const r = el.getBoundingClientRect();
+ return { l: r.left - basis.left, r: r.right - basis.left, t: r.top - basis.top, b: r.bottom - basis.top };
+ };
+ const start = el => {
const marke = el.querySelector(".zl-marke-innen.gelaufen") || el.querySelector(".zl-marke:not(.zl-marke-innen)");
- const r = (marke || el.querySelector(".zl-balken") || el).getBoundingClientRect();
- return marke
- ? { x: r.left + r.width / 2 - basis.left, y: r.top + r.height / 2 - basis.top, r: r.width / 2 }
- : { x: r.left - basis.left, y: r.top + r.height / 2 - basis.top, r: 0 };
+ if (marke) { const m = rect(marke); return { x: (m.l + m.r) / 2, y: m.b }; }
+ const b = rect(el.querySelector(".zl-balken") || el), ding = rect(el);
+ return { x: b.l + 6, y: ding.b - 2 };
+ };
+ const ankunft = el => {
+ const marke = el.querySelector(".zl-marke:not(.zl-marke-innen)");
+ const m = rect(marke || el.querySelector(".zl-balken") || el);
+ return { x: m.l, y: (m.t + m.b) / 2, oben: m.t, mitte: (m.l + m.r) / 2 };
};
let pfade = "";
raster.querySelectorAll(".zl-ding[data-kette]").forEach(kind => {
const vater = raster.querySelector('.zl-ding[data-id="' + kind.dataset.kette + '"]');
if (!vater) return;
- const v = anker(vater), k = anker(kind);
- const x1 = v.x + (v.r ? v.r * 0.7 : 0), y1 = v.y + (v.r ? v.r * 0.7 : 0);
- const x2 = k.x - (k.r ? k.r + 1 : 0), y2 = k.y;
- // Erst senkrecht nach unten, dann waagerecht hinein: so trifft der Bogen
- // den Nachfolger von links, auch wenn der fast senkrecht darunter liegt.
- const d = "M" + x1 + " " + y1 + " C" + x1 + " " + (y1 + (y2 - y1) * 0.8) + " "
- + (x2 - Math.max(14, (x2 - x1) * 0.5)) + " " + y2 + " " + x2 + " " + y2;
+ const s = start(vater), z = ankunft(kind);
+ const x1 = Math.round(s.x) + 0.5, y1 = Math.round(s.y);
+ let d, x2, y2;
+ if (z.x - x1 >= 10) {
+ // lotrecht hinunter, Rundung, waagerecht hinein
+ x2 = Math.round(z.x) - 1; y2 = Math.round(z.y);
+ const rund = Math.min(10, (y2 - y1) / 2, (x2 - x1) / 2);
+ d = "M" + x1 + " " + y1 + " V" + (y2 - rund) + " Q" + x1 + " " + y2 + " " + (x1 + rund) + " " + y2 + " H" + x2;
+ } else {
+ // Nachfolger liegt links oder genau darunter: von oben anfahren
+ x2 = Math.round(z.mitte) + 0.5; y2 = Math.round(z.oben) - 1;
+ const mitte = y1 + (y2 - y1) / 2;
+ d = "M" + x1 + " " + y1 + " V" + mitte + " H" + x2 + " V" + y2;
+ }
const paar = ' data-von="' + kind.dataset.kette + '" data-nach="' + kind.dataset.id + '"';
pfade += ''
+ ''