1'use strict'; 2/** 3 * 934tel â DocFrame: eingefrorene Dokumente (PDF) SICHER in ein <iframe> laden. 4 * 5 * Warum es diesen Helfer gibt (Vorfall 2026-09-29, Rechnung F-000418 zeigte das PDF einer 6 * anderen Rechnung): Wird die `src` eines BESTEHENDEN iframes per Skript geändert, legt der 7 * Browser dafür einen eigenen Eintrag im Seitenverlauf an. âZurückâ navigiert dann NUR das 8 * iframe auf das vorherige Dokument, während die Seite selbst beim aktuellen Beleg bleibt â 9 * Kopf, Positionen und Vorschau laufen auseinander. Auf Seiten ohne Hash-Routing 10 * (rechnungen.html) sind die iframe-Navigationen sogar die EINZIGEN Verlaufseinträge. 11 * 12 * Regeln, die dieser Helfer durchsetzt: 13 * 1. NIE `.src` auf einem vorhandenen iframe setzen. Je Dokument ein FRISCHES Element, 14 * `src` VOR dem Einfügen â die Erst-Navigation ersetzt about:blank und erzeugt keinen 15 * Verlaufseintrag (Chromium + Firefox verifiziert). 16 * 2. Jede Navigation des Rahmens, die nicht von uns stammt (Zurück/Vorwärts, Reload durch 17 * den Browser), wird am `load`-Ereignis erkannt (URL des Rahmens â erwartete URL oder 18 * zweiter load ohne Auftrag) â Rahmen sofort neu aufbauen. Nach 3 Versuchen leer lassen 19 * statt Falsches zu zeigen. 20 * 3. Kommt die Seite aus dem Back-Forward-Cache zurück (`pageshow`, persisted), werden alle 21 * Rahmen neu aufgebaut. 22 * 4. `data-doc` am Rahmen trägt den Beleg-Schlüssel â Prüfbarkeit im DOM. 23 * 24 * API: DocFrame.set(frameId, url, docKey) â neues iframe-Element (oder null) 25 * DocFrame.clear(frameId) â leerer Rahmen (beim Verlassen der Detailansicht) 26 * DocFrame.current(frameId) â docKey des aktuell geladenen Dokuments 27 */ 28(function () { 29 var STATE = {}; // frameId -> { url, key, loads, rebuilds } 30 var MAX_REBUILDS = 3; 31 32 function pathOf(href) { 33 try { var u = new URL(href, location.href); return u.pathname + u.search; } catch (e) { return null; } 34 } 35 36 function cloneShell(old, key) { 37 var f = document.createElement('iframe'); 38 for (var i = 0; i < old.attributes.length; i++) { 39 var a = old.attributes[i]; 40 if (a.name === 'src' || a.name === 'srcdoc' || a.name === 'data-doc') continue; 41 f.setAttribute(a.name, a.value); 42 } 43 if (key) f.setAttribute('data-doc', key); else f.removeAttribute('data-doc'); 44 return f; 45 } 46 47 function onLoad(frameId, f, st) { 48 if (STATE[frameId] !== st || f !== document.getElementById(frameId)) return; // veraltet 49 st.loads++; 50 var actual = null; 51 try { actual = f.contentWindow.location.href; } catch (e) { actual = null; } 52 if (actual === 'about:blank') return; // Leer-Dokument, kein Inhalt 53 var ok = actual == null ? (st.loads === 1) : (pathOf(actual) === pathOf(st.url)); 54 if (ok && st.loads === 1) return; // der eine erwartete load 55 // Fremdnavigation (Zurück/Vorwärts im Rahmen) oder unerwarteter Reload â neu aufbauen. 56 if (st.rebuilds >= MAX_REBUILDS) { blank(frameId, 'Dokument konnte nicht sicher angezeigt werden. Bitte Seite neu laden.'); return; } 57 var rebuilds = st.rebuilds + 1; 58 set(frameId, st.url, st.key, rebuilds); 59 } 60 61 function set(frameId, url, key, rebuilds) { 62 var old = document.getElementById(frameId); if (!old) return null; 63 key = key || url; 64 var st = { url: url, key: key, loads: 0, rebuilds: rebuilds || 0 }; 65 STATE[frameId] = st; 66 var f = cloneShell(old, key); 67 f.addEventListener('load', function () { onLoad(frameId, f, st); }); 68 f.src = url; // VOR dem Einfügen: Erst-Navigation = Ersetzen, kein Verlaufseintrag 69 old.parentNode.replaceChild(f, old); 70 return f; 71 } 72 73 function blank(frameId, message) { 74 var old = document.getElementById(frameId); if (!old) return null; 75 delete STATE[frameId]; 76 var f = cloneShell(old, ''); 77 if (message) f.setAttribute('srcdoc', '<!doctype html><p style="font-family:sans-serif;color:#b00020;padding:1rem;">' + String(message).replace(/[<>&]/g, '') + '</p>'); 78 old.parentNode.replaceChild(f, old); 79 return f; 80 } 81 82 function clear(frameId) { return blank(frameId, ''); } 83 function current(frameId) { return STATE[frameId] ? STATE[frameId].key : null; } 84 85 window.addEventListener('pageshow', function (ev) { 86 if (!ev.persisted) return;
87 Object.keys(STATE).forEach(function (id) { var st = STATE[id]; set(id, st.url, st.key, 0); }); 88 }); 89 90 window.DocFrame = { set: set, clear: clear, current: current }; 91})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.