/* * Der Bildbetrachter. * * Ohne dieses Skript ist ein vergrößerbares Bild schlicht ein Verweis auf die große Fassung: ein * Klick öffnet sie, zurück führt die Zurück-Taste. Das ist die Grundlage, und sie funktioniert * immer. Dieses Skript fängt den Klick ab und zeigt das Bild stattdessen über der Seite - mit * Blättern, wenn es zu einer Galerie gehört. * * Benutzt wird : der Browser bringt damit das Fangen des Fokus, das Schließen mit Escape * und die Auszeichnung als Dialog schon mit. Eine nachgebaute Fassung aus
und Tastatur- * abfragen wäre mehr Code und weniger zugänglich. */ (() => { const zoomable = [...document.querySelectorAll('a[data-zoom]')]; if (0 === zoomable.length) { return; } const dialog = document.createElement('dialog'); dialog.className = 'zoom-dialog'; dialog.innerHTML = `

`; document.body.append(dialog); const bild = dialog.querySelector('.zoom-image'); const beschriftung = dialog.querySelector('.zoom-caption'); const zaehler = dialog.querySelector('.zoom-counter'); const zurueck = dialog.querySelector('.zoom-prev'); const vor = dialog.querySelector('.zoom-next'); /** Die Bilder, zwischen denen geblättert wird: die der Galerie, sonst nur dieses eine. */ const gruppeVon = (link) => { const galerie = link.closest('[data-zoom-group]'); return galerie ? [...galerie.querySelectorAll('a[data-zoom]')] : [link]; }; let gruppe = []; let stelle = 0; const zeige = (index) => { stelle = (index + gruppe.length) % gruppe.length; const link = gruppe[stelle]; const inneres = link.querySelector('img'); bild.src = link.href; bild.alt = inneres ? inneres.alt : ''; const text = link.dataset.caption || ''; beschriftung.textContent = text; beschriftung.hidden = '' === text; const mehrere = gruppe.length > 1; zurueck.hidden = !mehrere; vor.hidden = !mehrere; zaehler.hidden = !mehrere; zaehler.textContent = mehrere ? `${stelle + 1} von ${gruppe.length}` : ''; }; for (const link of zoomable) { link.addEventListener('click', event => { // Mit gedrückter Zusatztaste oder mittlerer Maustaste bleibt es ein normaler Verweis: // wer ein Bild in einem neuen Reiter öffnen will, soll das können. if (event.metaKey || event.ctrlKey || event.shiftKey || 1 === event.button) { return; } event.preventDefault(); gruppe = gruppeVon(link); zeige(gruppe.indexOf(link)); dialog.showModal(); }); } zurueck.addEventListener('click', () => zeige(stelle - 1)); vor.addEventListener('click', () => zeige(stelle + 1)); dialog.querySelector('.zoom-close').addEventListener('click', () => dialog.close()); // Ein Klick neben das Bild schließt - erwartet, und der Dialog selbst ist die Fläche daneben. dialog.addEventListener('click', event => { if (event.target === dialog) { dialog.close(); } }); dialog.addEventListener('keydown', event => { if (1 === gruppe.length) { return; } if ('ArrowLeft' === event.key) { event.preventDefault(); zeige(stelle - 1); } if ('ArrowRight' === event.key) { event.preventDefault(); zeige(stelle + 1); } }); })(); /* * Der PDF-Betrachter - in derselben Datei wie der Bildbetrachter, damit eine Seite mit beidem * nicht zwei Skripte laedt. * * Ohne dieses Skript ist ein Link auf ein PDF ein gewöhnlicher Link: der Browser öffnet die * Datei. Das Skript zeigt sie stattdessen in einem Rahmen über der Seite, mit dem Betrachter, * den der Browser ohnehin mitbringt - kein eigener Nachbau, der ein Megabyte wiegt. * * Wo der Browser keinen solchen Betrachter hat oder ihn im Rahmen nur halb zeigt, bleibt es beim * neuen Tab: Chrome auf Android hat keinen, und Safari auf dem iPhone zeigt im Rahmen nur die * erste Seite. Ein Link mit „In neuem Tab öffnen“ bleibt ohnehin, was er ist. */ (() => { const PDF = /^\/media\/[a-f0-9]{32}\.pdf$/; const links = [...document.querySelectorAll('a[href]')].filter(link => { const url = new URL(link.href, location.href); return url.origin === location.origin && PDF.test(url.pathname) && '_blank' !== link.target; }); if (0 === links.length) { return; } const eingebettet = false !== navigator.pdfViewerEnabled && !matchMedia('(pointer: coarse)').matches; let dialog = null; let rahmen = null; let titel = null; let laden = null; const baue = () => { dialog = document.createElement('dialog'); dialog.className = 'pdf-dialog'; dialog.setAttribute('aria-labelledby', 'pdf-dialog-title'); dialog.innerHTML = ` `; document.body.append(dialog); rahmen = dialog.querySelector('.pdf-frame'); titel = dialog.querySelector('.pdf-title'); laden = [dialog.querySelector('.pdf-download'), dialog.querySelector('.pdf-open')]; dialog.querySelector('.pdf-close').addEventListener('click', () => dialog.close()); dialog.addEventListener('click', event => { if (event.target === dialog) { dialog.close(); } }); // Der Rahmen wird geleert, sonst läuft ein eingebettetes Video oder Audio im PDF weiter // und das nächste Öffnen zeigt kurz das vorige Dokument. dialog.addEventListener('close', () => rahmen.removeAttribute('src')); }; for (const link of links) { link.addEventListener('click', event => { if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || 1 === event.button) { return; } event.preventDefault(); if (!eingebettet) { window.open(link.href, '_blank', 'noopener'); return; } if (null === dialog) { baue(); } const name = (link.getAttribute('aria-label') || link.textContent || '').trim(); titel.textContent = name || 'Dokument'; rahmen.title = name ? `PDF: ${name}` : 'PDF-Dokument'; for (const aktion of laden) { aktion.href = link.href; } rahmen.src = link.href; dialog.showModal(); }); } })();