Reader: Fußnoten-Popover erkennt jetzt mehr EPUB-Konventionen (SW v31)
Die Erkennung von Fußnoten-Links war auf class="footnote" + gleiche-Datei-
Anker (href startsWith '#') hartkodiert. Andere Editionen markieren Fußnoten
anders — z.B. die e-artnow-Ausgabe von Marx' "Das Kapital" (zeno.org-Konvertierung)
nutzt gar keine Klasse und verlinkt mit einem <sup> als Kind (statt Vorfahre)
des <a> auf eine ANDERE Kapiteldatei ("../Text/Karl0045.html#F537") — dadurch
griff weder die Klassen- noch die Hash-Prüfung, das Popover blieb tot.
_looksLikeFootnoteLink() erkennt jetzt zusätzlich epub:type="noteref" (EPUB3-
Standard), jede Klasse mit "note"/"footnote"/"fn" im Namen, und als Fallback
jeden Link mit Fragment-Anker, der irgendwo mit einem <sup> verschachtelt ist
(in beide Richtungen) — der in EPUBs praktisch universelle Marker für
Fußnotenziffern. Die Fragment-Extraktion nimmt jetzt alles nach dem letzten
'#' statt nur bei href.startsWith('#'), da das Buch als eine zusammenhängende
DOM gerendert wird und getElementById dateiübergreifend funktioniert.
Verifiziert per Playwright: Marx-Edition zeigt jetzt beim Hover 1326 erkannte
Fußnoten-Links mit korrektem Popover-Inhalt; Žižeks "Weniger als nichts"
(vorher funktionierend, class="footnote"-basiert) weiterhin unverändert
funktionsfähig — keine Regression.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
42bc090a28
commit
76068f6ed8
2 changed files with 23 additions and 3 deletions
|
|
@ -2725,6 +2725,22 @@ function _injectImageBlobs(html, chapterDir, imageMap) {
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EPUBs mark footnote/endnote reference links very inconsistently across
|
||||||
|
// editions/tools — some use class="footnote" (same-file '#frag' href), some
|
||||||
|
// use the EPUB3 epub:type="noteref" attribute, and some (e.g. older
|
||||||
|
// Calibre/zeno.org conversions) use neither and just wrap a plain link to
|
||||||
|
// another chapter file's fragment in a <sup> — in either direction
|
||||||
|
// (<sup><a>…</a></sup> or <a><sup>…</sup></a>). Recognize all of these
|
||||||
|
// rather than only the single hardcoded class name.
|
||||||
|
function _looksLikeFootnoteLink(el, href) {
|
||||||
|
if (!href.includes('#')) return false;
|
||||||
|
const cls = (el.getAttribute('class') || '').toLowerCase();
|
||||||
|
if (/\bnote|\bfootnote|\bfn\b/.test(cls)) return true;
|
||||||
|
const epubType = (el.getAttribute('epub:type') || '').toLowerCase();
|
||||||
|
if (epubType.includes('noteref')) return true;
|
||||||
|
return !!(el.closest('sup') || el.querySelector('sup'));
|
||||||
|
}
|
||||||
|
|
||||||
function sanitizeEpubHtml(html) {
|
function sanitizeEpubHtml(html) {
|
||||||
const doc = new DOMParser().parseFromString(html, 'text/html');
|
const doc = new DOMParser().parseFromString(html, 'text/html');
|
||||||
|
|
||||||
|
|
@ -2745,8 +2761,12 @@ function sanitizeEpubHtml(html) {
|
||||||
if (href.startsWith('http://') || href.startsWith('https://')) {
|
if (href.startsWith('http://') || href.startsWith('https://')) {
|
||||||
el.setAttribute('target', '_blank');
|
el.setAttribute('target', '_blank');
|
||||||
el.setAttribute('rel', 'noopener noreferrer');
|
el.setAttribute('rel', 'noopener noreferrer');
|
||||||
} else if (el.classList.contains('footnote') && href.startsWith('#')) {
|
} else if (_looksLikeFootnoteLink(el, href)) {
|
||||||
el.dataset.footnoteRef = href.slice(1);
|
// The whole book is one concatenated DOM, so a fragment id resolves
|
||||||
|
// via getElementById regardless of which chapter file it originally
|
||||||
|
// pointed into — strip any "../Text/other.html" path prefix and keep
|
||||||
|
// just the id after the last '#'.
|
||||||
|
el.dataset.footnoteRef = href.slice(href.indexOf('#') + 1);
|
||||||
el.removeAttribute('href');
|
el.removeAttribute('href');
|
||||||
el.style.cursor = 'pointer';
|
el.style.cursor = 'pointer';
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
* diora service worker — caches the app shell for offline use.
|
* diora service worker — caches the app shell for offline use.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const CACHE = 'diora-v30';
|
const CACHE = 'diora-v31';
|
||||||
const PODCAST_CACHE = 'diora-podcast-v1';
|
const PODCAST_CACHE = 'diora-podcast-v1';
|
||||||
const SHELL = [
|
const SHELL = [
|
||||||
'/static/css/app.css',
|
'/static/css/app.css',
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue