befund.js
| 1 | // Befund: Körperschema stempeln, Tabellenzeilen ergänzen, Warnung bei ungespeicherten Änderungen. |
| 2 | // Ohne JavaScript funktioniert alles außer dem Stempeln (Formular, Tabellen per Speichern). |
| 3 | (() => { |
| 4 | "use strict"; |
| 5 | document.documentElement.classList.add("js"); |
| 6 | const form = document.getElementById("befund-form"); |
| 7 | if (!form) return; |
| 8 | |
| 9 | let dirty = false; |
| 10 | form.addEventListener("input", () => { dirty = true; }); |
| 11 | form.addEventListener("submit", () => { dirty = false; }); |
| 12 | window.addEventListener("beforeunload", (e) => { if (dirty) { e.preventDefault(); e.returnValue = ""; } }); |
| 13 | document.addEventListener("keydown", (e) => { |
| 14 | if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "s") { e.preventDefault(); dirty = false; form.requestSubmit(); } |
| 15 | }); |
| 16 | |
| 17 | // ---- Tabellen: weitere Zeile ---- |
| 18 | form.querySelectorAll("button.addrow").forEach((btn) => btn.addEventListener("click", () => { |
| 19 | const key = btn.dataset.table; |
| 20 | const tbody = form.querySelector(`table[data-table="${key}"] tbody`); |
| 21 | const count = form.querySelector(`input[name="t.${key}.n"]`); |
| 22 | if (!tbody || !count || !tbody.lastElementChild) return; |
| 23 | const n = parseInt(count.value, 10) || 0; |
| 24 | const tr = tbody.lastElementChild.cloneNode(true); |
| 25 | tr.querySelectorAll("input,select").forEach((el) => { |
| 26 | el.name = el.name.replace(/^(t\.[^.]+\.)\d+(\.)/, `$1${n}$2`); |
| 27 | if (el.tagName === "SELECT") el.selectedIndex = 0; else el.value = ""; |
| 28 | }); |
| 29 | tbody.appendChild(tr); |
| 30 | count.value = n + 1; |
| 31 | tr.querySelector("input,select")?.focus(); |
| 32 | })); |
| 33 | |
| 34 | // ---- Körperschema ---- |
| 35 | const hidden = document.getElementById("stamps"); |
| 36 | if (!hidden) return; |
| 37 | let stamps = []; |
| 38 | try { stamps = JSON.parse(hidden.value || "[]") || []; } catch { stamps = []; } |
| 39 | const symButtons = [...document.querySelectorAll(".body-tools .sym")]; |
| 40 | const chars = Object.fromEntries(symButtons.map((b) => [b.dataset.sym, b.textContent])); |
| 41 | let sym = symButtons.find((b) => b.classList.contains("active"))?.dataset.sym || "pain"; |
| 42 | symButtons.forEach((b) => b.addEventListener("click", () => { |
| 43 | symButtons.forEach((x) => x.classList.remove("active")); |
| 44 | b.classList.add("active"); |
| 45 | sym = b.dataset.sym; |
| 46 | })); |
| 47 | const color = document.getElementById("stamp-color"); |
| 48 | const size = document.getElementById("stamp-size"); |
| 49 | const SIZES = [0, 7, 11, 16]; // gleich wie befund.StampSize |
| 50 | const NS = "http://www.w3.org/2000/svg"; |
| 51 | |
| 52 | const draw = (svg, s) => { |
| 53 | const t = document.createElementNS(NS, "text"); |
| 54 | t.setAttribute("x", (s.x * +svg.dataset.w).toFixed(1)); |
| 55 | t.setAttribute("y", (s.y * +svg.dataset.h).toFixed(1)); |
| 56 | t.setAttribute("font-size", SIZES[s.z] || 11); |
| 57 | t.setAttribute("fill", s.c); |
| 58 | t.setAttribute("text-anchor", "middle"); |
| 59 | t.setAttribute("dominant-baseline", "central"); |
| 60 | t.textContent = chars[s.s] || "?"; |
| 61 | svg.appendChild(t); |
| 62 | }; |
| 63 | const save = () => { hidden.value = JSON.stringify(stamps); dirty = true; }; |
| 64 | |
| 65 | document.querySelectorAll("svg[data-view]").forEach((svg) => svg.addEventListener("pointerdown", (e) => { |
| 66 | e.preventDefault(); |
| 67 | const r = svg.getBoundingClientRect(); |
| 68 | const s = { |
| 69 | v: +svg.dataset.view, |
| 70 | x: +Math.min(1, Math.max(0, (e.clientX - r.left) / r.width)).toFixed(4), |
| 71 | y: +Math.min(1, Math.max(0, (e.clientY - r.top) / r.height)).toFixed(4), |
| 72 | s: sym, c: color?.value || "#d32f2f", z: +(size?.value || 2), |
| 73 | }; |
| 74 | stamps.push(s); |
| 75 | draw(svg, s); |
| 76 | save(); |
| 77 | })); |
| 78 | document.getElementById("stamp-undo")?.addEventListener("click", () => { |
| 79 | const s = stamps.pop(); |
| 80 | if (!s) return; |
| 81 | document.querySelector(`svg[data-view="${s.v}"]`)?.lastElementChild?.remove(); |
| 82 | save(); |
| 83 | }); |
| 84 | })(); |
| 85 |