Refactoring Schritt 2: app.js nach Bereichen in static/js/app/ aufgeteilt; sw v106
32 klassische Skripte in der alten Reihenfolge (01-core … 99-init), ohne
Änderung am Code darin: geschnitten nur zwischen Anweisungen, je Datei
'use strict' wie vorher für die ganze Datei. Geprüft (acorn): kein Code, der
beim Laden läuft, braucht einen Namen aus einer späteren Datei. Globale
Funktionen und let/const bleiben global, die App-Schichten (native.js,
native-eink.js, …) umhüllen sie wie bisher. Template und sw.js-SHELL listen
die Dateien; diora-android build_web nimmt static/js rekursiv.
Getestet: 14 Browser-Tests (e2e), Desktop-App (Buch, Lesestand, Leiste im
Reader, Hell/Dunkel).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:30:43 +02:00
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
// Reader Settings
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
// Typography defaults. Phones keep the long-standing ones; a wide screen
|
|
|
|
|
|
// (desktop browser, diora desktop) starts bigger and wider.
|
|
|
|
|
|
const _READER_TYPE_DEFAULTS_NARROW = { fontSize: 16, lineHeight: 1.8, maxWidth: 65, fontWeight: null };
|
|
|
|
|
|
const _READER_TYPE_DEFAULTS_WIDE = { fontSize: 20, lineHeight: 1.6, maxWidth: 90, fontWeight: 375 };
|
|
|
|
|
|
const _READER_WIDTH_MAX = 140; // ch, the width slider's upper end ("Full" stays separate)
|
|
|
|
|
|
|
|
|
|
|
|
function _readerTypeDefaults() {
|
|
|
|
|
|
return window.matchMedia('(min-width: 900px)').matches
|
|
|
|
|
|
? _READER_TYPE_DEFAULTS_WIDE : _READER_TYPE_DEFAULTS_NARROW;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Nachrichten und Blogs im Buch-Reader, Doppelklick für Vollbild, Hörbücher wie die Bücher-Bibliothek
65-article.js (DioraArticle): Artikel öffnen im selben Reader wie Bücher – Schrift, Farben,
Seiten/Doppelseite mit Fußnoten am Seitenfuß, Suche, Vorlesen, Vollbild. Lesezeichen,
Markierungen und Notizspalte bleiben Büchern vorbehalten. Merken, Projekt und Original
stehen in der Kopfzeile (Apps: im Lesemenü). feeds.js gibt sein eigenes Overlay auf;
Fortschritt wie bisher (bis zum unteren Bildschirmrand gelesen, ab 90 % gelesen).
Einstellungen der Artikel gelten für alle Artikel zusammen.
62-reader.js: Doppelklick (Maus) auf den Text schaltet Vollbild an/aus, die Leisten gehen mit.
audiobooks.js: die Hörbücher sind gebaut wie die Bücher – Kopf mit Suche, Sortierung und +,
Chips Angefangen/Ungehört/Gehört und Ordner, „Weiterhören“ wie „Weiterlesen“, Kacheln aus
denselben Bausteinen (53-library.js gibt seine Styles auch an #audiobook-shelf), Cover quadratisch.
63-pages.js: bei nur einem Kapitel zeigt die Fußzeile nur die Seiten.
sw CACHE v158
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 20:15:00 +01:00
|
|
|
|
// Where the settings shown are kept: the book's id, or 'article' (all articles, 65-article.js).
|
|
|
|
|
|
let _readerSettingsKey = null;
|
|
|
|
|
|
|
Refactoring Schritt 2: app.js nach Bereichen in static/js/app/ aufgeteilt; sw v106
32 klassische Skripte in der alten Reihenfolge (01-core … 99-init), ohne
Änderung am Code darin: geschnitten nur zwischen Anweisungen, je Datei
'use strict' wie vorher für die ganze Datei. Geprüft (acorn): kein Code, der
beim Laden läuft, braucht einen Namen aus einer späteren Datei. Globale
Funktionen und let/const bleiben global, die App-Schichten (native.js,
native-eink.js, …) umhüllen sie wie bisher. Template und sw.js-SHELL listen
die Dateien; diora-android build_web nimmt static/js rekursiv.
Getestet: 14 Browser-Tests (e2e), Desktop-App (Buch, Lesestand, Leiste im
Reader, Hell/Dunkel).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:30:43 +02:00
|
|
|
|
function loadReaderSettings(bookId) {
|
Nachrichten und Blogs im Buch-Reader, Doppelklick für Vollbild, Hörbücher wie die Bücher-Bibliothek
65-article.js (DioraArticle): Artikel öffnen im selben Reader wie Bücher – Schrift, Farben,
Seiten/Doppelseite mit Fußnoten am Seitenfuß, Suche, Vorlesen, Vollbild. Lesezeichen,
Markierungen und Notizspalte bleiben Büchern vorbehalten. Merken, Projekt und Original
stehen in der Kopfzeile (Apps: im Lesemenü). feeds.js gibt sein eigenes Overlay auf;
Fortschritt wie bisher (bis zum unteren Bildschirmrand gelesen, ab 90 % gelesen).
Einstellungen der Artikel gelten für alle Artikel zusammen.
62-reader.js: Doppelklick (Maus) auf den Text schaltet Vollbild an/aus, die Leisten gehen mit.
audiobooks.js: die Hörbücher sind gebaut wie die Bücher – Kopf mit Suche, Sortierung und +,
Chips Angefangen/Ungehört/Gehört und Ordner, „Weiterhören“ wie „Weiterlesen“, Kacheln aus
denselben Bausteinen (53-library.js gibt seine Styles auch an #audiobook-shelf), Cover quadratisch.
63-pages.js: bei nur einem Kapitel zeigt die Fußzeile nur die Seiten.
sw CACHE v158
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 20:15:00 +01:00
|
|
|
|
_readerSettingsKey = bookId;
|
Refactoring Schritt 2: app.js nach Bereichen in static/js/app/ aufgeteilt; sw v106
32 klassische Skripte in der alten Reihenfolge (01-core … 99-init), ohne
Änderung am Code darin: geschnitten nur zwischen Anweisungen, je Datei
'use strict' wie vorher für die ganze Datei. Geprüft (acorn): kein Code, der
beim Laden läuft, braucht einen Namen aus einer späteren Datei. Globale
Funktionen und let/const bleiben global, die App-Schichten (native.js,
native-eink.js, …) umhüllen sie wie bisher. Template und sw.js-SHELL listen
die Dateien; diora-android build_web nimmt static/js rekursiv.
Getestet: 14 Browser-Tests (e2e), Desktop-App (Buch, Lesestand, Leiste im
Reader, Hell/Dunkel).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:30:43 +02:00
|
|
|
|
// Reset to defaults, then apply per-book overrides
|
|
|
|
|
|
Object.assign(readerSettings, { theme: 'app', fontFamily: 'serif', noBold: false,
|
|
|
|
|
|
pdfZoom: 100, pdfInverted: false, pdfPaginated: false, pdfSpread: false },
|
|
|
|
|
|
_readerTypeDefaults());
|
|
|
|
|
|
try {
|
|
|
|
|
|
const saved = JSON.parse(localStorage.getItem(`diora_reader_settings_${bookId}`) || '{}');
|
|
|
|
|
|
// Settings saved before v2 hold every value, changed or not. A value equal
|
|
|
|
|
|
// to the old default was never chosen, so it gives way to today's default.
|
|
|
|
|
|
if (!saved.v) {
|
|
|
|
|
|
for (const [key, old] of Object.entries(_READER_TYPE_DEFAULTS_NARROW)) {
|
|
|
|
|
|
if (saved[key] === old) delete saved[key];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// Before v3 the reader's theme was always stored, "dark" by default: that now means "Wie die App".
|
|
|
|
|
|
if ((saved.v || 0) < 3 && saved.theme === 'dark') delete saved.theme;
|
|
|
|
|
|
delete saved.v;
|
|
|
|
|
|
Object.assign(readerSettings, saved);
|
|
|
|
|
|
if (saved.pdfPaginated === undefined) {
|
|
|
|
|
|
readerSettings.pdfPaginated = window.innerWidth < 768;
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function saveReaderSettings() {
|
Nachrichten und Blogs im Buch-Reader, Doppelklick für Vollbild, Hörbücher wie die Bücher-Bibliothek
65-article.js (DioraArticle): Artikel öffnen im selben Reader wie Bücher – Schrift, Farben,
Seiten/Doppelseite mit Fußnoten am Seitenfuß, Suche, Vorlesen, Vollbild. Lesezeichen,
Markierungen und Notizspalte bleiben Büchern vorbehalten. Merken, Projekt und Original
stehen in der Kopfzeile (Apps: im Lesemenü). feeds.js gibt sein eigenes Overlay auf;
Fortschritt wie bisher (bis zum unteren Bildschirmrand gelesen, ab 90 % gelesen).
Einstellungen der Artikel gelten für alle Artikel zusammen.
62-reader.js: Doppelklick (Maus) auf den Text schaltet Vollbild an/aus, die Leisten gehen mit.
audiobooks.js: die Hörbücher sind gebaut wie die Bücher – Kopf mit Suche, Sortierung und +,
Chips Angefangen/Ungehört/Gehört und Ordner, „Weiterhören“ wie „Weiterlesen“, Kacheln aus
denselben Bausteinen (53-library.js gibt seine Styles auch an #audiobook-shelf), Cover quadratisch.
63-pages.js: bei nur einem Kapitel zeigt die Fußzeile nur die Seiten.
sw CACHE v158
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 20:15:00 +01:00
|
|
|
|
const key = currentBookId || _readerSettingsKey;
|
|
|
|
|
|
if (key) {
|
|
|
|
|
|
localStorage.setItem(`diora_reader_settings_${key}`, JSON.stringify({ ...readerSettings, v: 3 }));
|
Refactoring Schritt 2: app.js nach Bereichen in static/js/app/ aufgeteilt; sw v106
32 klassische Skripte in der alten Reihenfolge (01-core … 99-init), ohne
Änderung am Code darin: geschnitten nur zwischen Anweisungen, je Datei
'use strict' wie vorher für die ganze Datei. Geprüft (acorn): kein Code, der
beim Laden läuft, braucht einen Namen aus einer späteren Datei. Globale
Funktionen und let/const bleiben global, die App-Schichten (native.js,
native-eink.js, …) umhüllen sie wie bisher. Template und sw.js-SHELL listen
die Dateien; diora-android build_web nimmt static/js rekursiv.
Getestet: 14 Browser-Tests (e2e), Desktop-App (Buch, Lesestand, Leiste im
Reader, Hell/Dunkel).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:30:43 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function applyReaderSettings(isPdf) {
|
|
|
|
|
|
const overlay = $('reader-overlay');
|
|
|
|
|
|
const contentEl = $('reader-content');
|
|
|
|
|
|
if (!overlay || !contentEl) return;
|
|
|
|
|
|
|
|
|
|
|
|
if (!isPdf) {
|
|
|
|
|
|
contentEl.style.fontSize = readerSettings.fontSize + 'px';
|
|
|
|
|
|
contentEl.style.lineHeight = readerSettings.lineHeight;
|
|
|
|
|
|
contentEl.style.setProperty('--reader-max-width', readerSettings.maxWidth + 'ch');
|
|
|
|
|
|
const fontMap = {
|
|
|
|
|
|
serif: "'Source Serif 4', Georgia, 'Times New Roman', serif",
|
|
|
|
|
|
sans: 'system-ui, -apple-system, sans-serif',
|
|
|
|
|
|
humanist: 'Verdana, Geneva, sans-serif',
|
|
|
|
|
|
mono: "'Courier New', monospace",
|
|
|
|
|
|
};
|
|
|
|
|
|
contentEl.style.fontFamily = fontMap[readerSettings.fontFamily] || fontMap.serif;
|
|
|
|
|
|
contentEl.classList.toggle('reader-no-bold', !!readerSettings.noBold);
|
|
|
|
|
|
// Weight: null keeps the stylesheet's. --reader-weight lets old WebViews
|
|
|
|
|
|
// without variable-font support (which drop weights like 425) pick a
|
|
|
|
|
|
// static cut instead.
|
|
|
|
|
|
contentEl.style.fontWeight = '';
|
|
|
|
|
|
if (readerSettings.fontWeight) {
|
|
|
|
|
|
contentEl.style.fontWeight = String(readerSettings.fontWeight);
|
|
|
|
|
|
contentEl.style.setProperty('--reader-weight', String(readerSettings.fontWeight));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
contentEl.style.removeProperty('--reader-weight');
|
|
|
|
|
|
}
|
|
|
|
|
|
if (_currentPositionAnchor && currentBookId) {
|
|
|
|
|
|
_suppressScrollJumpDetect();
|
|
|
|
|
|
requestAnimationFrame(() => restoreFromAnchor($('reader-content'), _currentPositionAnchor));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Theme
|
|
|
|
|
|
overlay.classList.remove('reader-theme-sepia', 'reader-theme-bright');
|
|
|
|
|
|
const readerTheme = readerSettings.theme === 'app'
|
|
|
|
|
|
? (document.documentElement.getAttribute('data-theme') === 'light' ? 'bright' : 'dark')
|
|
|
|
|
|
: readerSettings.theme;
|
|
|
|
|
|
if (readerTheme === 'sepia') overlay.classList.add('reader-theme-sepia');
|
|
|
|
|
|
else if (readerTheme === 'bright') overlay.classList.add('reader-theme-bright');
|
|
|
|
|
|
|
|
|
|
|
|
// PDF invert
|
|
|
|
|
|
if (isPdf && readerSettings.pdfInverted) overlay.classList.add('pdf-inverted');
|
|
|
|
|
|
else overlay.classList.remove('pdf-inverted');
|
|
|
|
|
|
|
|
|
|
|
|
// Annotations (marker mode + margin panel) are EPUB-only for now — PDFs have no text layer yet
|
|
|
|
|
|
const marginBtn = $('reader-margin-btn');
|
|
|
|
|
|
if (marginBtn) marginBtn.style.display = isPdf ? 'none' : '';
|
|
|
|
|
|
if (isPdf && readerAnnotationsMarginOpen) toggleAnnotationsMargin();
|
|
|
|
|
|
document.querySelectorAll('.marker-mode-btn').forEach(b => { b.style.display = isPdf ? 'none' : ''; });
|
|
|
|
|
|
if (isPdf && markerModeActive) toggleMarkerMode();
|
2026-10-14 19:43:03 +02:00
|
|
|
|
// Line length or font changed: the column's place next to an open margin too.
|
|
|
|
|
|
if (readerAnnotationsMarginOpen) _centerReaderColumn();
|
Refactoring Schritt 2: app.js nach Bereichen in static/js/app/ aufgeteilt; sw v106
32 klassische Skripte in der alten Reihenfolge (01-core … 99-init), ohne
Änderung am Code darin: geschnitten nur zwischen Anweisungen, je Datei
'use strict' wie vorher für die ganze Datei. Geprüft (acorn): kein Code, der
beim Laden läuft, braucht einen Namen aus einer späteren Datei. Globale
Funktionen und let/const bleiben global, die App-Schichten (native.js,
native-eink.js, …) umhüllen sie wie bisher. Template und sw.js-SHELL listen
die Dateien; diora-android build_web nimmt static/js rekursiv.
Getestet: 14 Browser-Tests (e2e), Desktop-App (Buch, Lesestand, Leiste im
Reader, Hell/Dunkel).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:30:43 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Font weight slider: the variable Source Serif 4 takes any weight, the
|
|
|
|
|
|
// system fonts only their fixed cuts (multiples of 100).
|
|
|
|
|
|
function _readerWeightStep() {
|
|
|
|
|
|
return readerSettings.fontFamily === 'serif' ? 25 : 100;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function _readerWeight() {
|
|
|
|
|
|
const step = _readerWeightStep();
|
|
|
|
|
|
const w = readerSettings.fontWeight
|
|
|
|
|
|
|| parseInt(getComputedStyle($('reader-content')).fontWeight, 10) || 400;
|
|
|
|
|
|
return Math.round(w / step) * step;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Lesen in Seiten auch im Browser und auf dem Desktop, mit Fußnoten unten auf der Seite und Doppelseite
63-pages.js (DioraPages): die Seitenansicht des E-Reader-Modus der Android-Apps, jetzt für alle –
Seitenumbrüche vor angeschnittenen Zeilen, ein Kapitel auf einmal, Fußnoten am Seitenfuß wie im
gedruckten Buch, Blättern per Tippen/Klick am Rand, Wischen, Pfeil-/Bild-/Leertaste und Mausrad.
Neu: Doppelseite (zwei Seiten nebeneinander ab 900 px Breite). Einstellung „Scrollen | Seiten“
(+ „Fußnoten unten“, „Doppelseite“) im Schrift-&-Layout-Panel, je Gerät.
Fußnoten: Erkennung und Auflösung aus diora-android native-ui.js übernommen (40-epub.js,
78-footnotes.js) – die Notiz wird im richtigen Kapitel gesucht (IDs wiederholen sich je Datei),
auch das Popover zeigt jetzt genau die Notiz. Vorbereitete Bücher werden einmal neu aufbereitet.
Behoben gegenüber der App-Fassung: ein Verweis in der ersten Zeile einer Seite (hochgestellt)
zählte zur Vorseite, und ein Verweis, der samt Notiz nicht mehr passte, blieb ohne Notiz stehen –
jetzt wandert er mit ihr auf die nächste Seite. Schnelles Blättern löst „Weit gescrollt“ nicht mehr aus.
sw CACHE v157
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 19:41:27 +01:00
|
|
|
|
// "Scrollen | Seiten" and, in pages, whether the notes stand at the page's foot
|
|
|
|
|
|
// and whether two pages lie side by side.
|
|
|
|
|
|
// The apps' e-reader mode always reads in pages: no choice there.
|
|
|
|
|
|
function _readerModeButtons() {
|
|
|
|
|
|
if (!window.DioraPages || DioraPages.isForced()) return '';
|
|
|
|
|
|
const pages = DioraPages.isOn();
|
|
|
|
|
|
return `
|
|
|
|
|
|
<button class="btn btn-sm ${pages ? '' : 'active'}" data-rs-mode="scroll">Scrollen</button>
|
|
|
|
|
|
<button class="btn btn-sm ${pages ? 'active' : ''}" data-rs-mode="pages">Seiten</button>
|
|
|
|
|
|
<button class="btn btn-sm ${DioraPages.pageNotes() ? 'active' : ''}" id="rs-page-notes" style="${pages ? '' : 'display:none'}"
|
|
|
|
|
|
title="Die Fußnoten stehen unten auf der Seite, auf der auf sie verwiesen wird">Fußnoten unten</button>
|
|
|
|
|
|
<button class="btn btn-sm ${DioraPages.spread() ? 'active' : ''}" id="rs-page-spread" style="${pages ? '' : 'display:none'}"
|
|
|
|
|
|
title="Zwei Seiten nebeneinander wie ein aufgeschlagenes Buch — ab einer Breite von 900 Pixeln">Doppelseite</button>`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Refactoring Schritt 2: app.js nach Bereichen in static/js/app/ aufgeteilt; sw v106
32 klassische Skripte in der alten Reihenfolge (01-core … 99-init), ohne
Änderung am Code darin: geschnitten nur zwischen Anweisungen, je Datei
'use strict' wie vorher für die ganze Datei. Geprüft (acorn): kein Code, der
beim Laden läuft, braucht einen Namen aus einer späteren Datei. Globale
Funktionen und let/const bleiben global, die App-Schichten (native.js,
native-eink.js, …) umhüllen sie wie bisher. Template und sw.js-SHELL listen
die Dateien; diora-android build_web nimmt static/js rekursiv.
Getestet: 14 Browser-Tests (e2e), Desktop-App (Buch, Lesestand, Leiste im
Reader, Hell/Dunkel).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:30:43 +02:00
|
|
|
|
function toggleSettingsPanel() {
|
|
|
|
|
|
const overlay = $('reader-overlay');
|
|
|
|
|
|
const contentEl = $('reader-content');
|
|
|
|
|
|
if (!overlay || !contentEl) return;
|
|
|
|
|
|
|
|
|
|
|
|
const existing = document.getElementById('reader-settings-panel');
|
|
|
|
|
|
if (existing) {
|
|
|
|
|
|
existing.remove();
|
|
|
|
|
|
readerSettingsPanelOpen = false;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
readerSettingsPanelOpen = true;
|
|
|
|
|
|
const isPdf = !!currentPdfDoc;
|
|
|
|
|
|
|
|
|
|
|
|
const panel = document.createElement('div');
|
|
|
|
|
|
panel.id = 'reader-settings-panel';
|
|
|
|
|
|
panel.className = 'reader-settings-panel';
|
|
|
|
|
|
|
|
|
|
|
|
if (!isPdf) {
|
|
|
|
|
|
panel.innerHTML = `
|
|
|
|
|
|
<label>Schrift <button class="btn btn-sm" id="rs-font-minus">−</button> <input type="range" id="rs-font" min="12" max="24" step="1" value="${readerSettings.fontSize}"> <button class="btn btn-sm" id="rs-font-plus">+</button> <span id="rs-font-val">${readerSettings.fontSize}px</span></label>
|
|
|
|
|
|
<label>Zeilen <button class="btn btn-sm" id="rs-line-minus">−</button> <input type="range" id="rs-line" min="12" max="30" step="1" value="${Math.round(readerSettings.lineHeight * 10)}"> <button class="btn btn-sm" id="rs-line-plus">+</button> <span id="rs-line-val">${readerSettings.lineHeight}</span></label>
|
|
|
|
|
|
<label>Breite <button class="btn btn-sm" id="rs-width-minus">−</button> <input type="range" id="rs-width" min="40" max="${_READER_WIDTH_MAX}" step="5" value="${Math.min(readerSettings.maxWidth, _READER_WIDTH_MAX)}"> <button class="btn btn-sm" id="rs-width-plus">+</button> <span id="rs-width-val">${readerSettings.maxWidth}ch</span></label>
|
|
|
|
|
|
<button class="btn btn-sm" id="rs-width-full">Volle Breite</button>
|
|
|
|
|
|
<label>Stärke <button class="btn btn-sm" id="rs-weight-minus">−</button> <input type="range" id="rs-weight" min="200" max="800" step="${_readerWeightStep()}" value="${_readerWeight()}"> <button class="btn btn-sm" id="rs-weight-plus">+</button> <span id="rs-weight-val">${_readerWeight()}</span></label>
|
|
|
|
|
|
<button class="btn btn-sm ${readerSettings.fontFamily === 'serif' ? 'active' : ''}" data-rs-font="serif">Serif</button>
|
|
|
|
|
|
<button class="btn btn-sm ${readerSettings.fontFamily === 'sans' ? 'active' : ''}" data-rs-font="sans">Sans</button>
|
|
|
|
|
|
<button class="btn btn-sm ${readerSettings.fontFamily === 'humanist' ? 'active' : ''}" data-rs-font="humanist">Verdana</button>
|
|
|
|
|
|
<button class="btn btn-sm ${readerSettings.fontFamily === 'mono' ? 'active' : ''}" data-rs-font="mono">Mono</button>
|
|
|
|
|
|
<button class="btn btn-sm ${readerSettings.noBold ? 'active' : ''}" id="rs-no-bold">Kein Fett</button>
|
|
|
|
|
|
<button class="btn btn-sm ${readerSettings.theme === 'app' ? 'active' : ''}" data-rs-theme="app">Wie die App</button>
|
|
|
|
|
|
<button class="btn btn-sm ${readerSettings.theme === 'dark' ? 'active' : ''}" data-rs-theme="dark">Dunkel</button>
|
|
|
|
|
|
<button class="btn btn-sm ${readerSettings.theme === 'sepia' ? 'active' : ''}" data-rs-theme="sepia">Sepia</button>
|
|
|
|
|
|
<button class="btn btn-sm ${readerSettings.theme === 'bright' ? 'active' : ''}" data-rs-theme="bright">Hell</button>
|
Lesen in Seiten auch im Browser und auf dem Desktop, mit Fußnoten unten auf der Seite und Doppelseite
63-pages.js (DioraPages): die Seitenansicht des E-Reader-Modus der Android-Apps, jetzt für alle –
Seitenumbrüche vor angeschnittenen Zeilen, ein Kapitel auf einmal, Fußnoten am Seitenfuß wie im
gedruckten Buch, Blättern per Tippen/Klick am Rand, Wischen, Pfeil-/Bild-/Leertaste und Mausrad.
Neu: Doppelseite (zwei Seiten nebeneinander ab 900 px Breite). Einstellung „Scrollen | Seiten“
(+ „Fußnoten unten“, „Doppelseite“) im Schrift-&-Layout-Panel, je Gerät.
Fußnoten: Erkennung und Auflösung aus diora-android native-ui.js übernommen (40-epub.js,
78-footnotes.js) – die Notiz wird im richtigen Kapitel gesucht (IDs wiederholen sich je Datei),
auch das Popover zeigt jetzt genau die Notiz. Vorbereitete Bücher werden einmal neu aufbereitet.
Behoben gegenüber der App-Fassung: ein Verweis in der ersten Zeile einer Seite (hochgestellt)
zählte zur Vorseite, und ein Verweis, der samt Notiz nicht mehr passte, blieb ohne Notiz stehen –
jetzt wandert er mit ihr auf die nächste Seite. Schnelles Blättern löst „Weit gescrollt“ nicht mehr aus.
sw CACHE v157
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 19:41:27 +01:00
|
|
|
|
${_readerModeButtons()}
|
Refactoring Schritt 2: app.js nach Bereichen in static/js/app/ aufgeteilt; sw v106
32 klassische Skripte in der alten Reihenfolge (01-core … 99-init), ohne
Änderung am Code darin: geschnitten nur zwischen Anweisungen, je Datei
'use strict' wie vorher für die ganze Datei. Geprüft (acorn): kein Code, der
beim Laden läuft, braucht einen Namen aus einer späteren Datei. Globale
Funktionen und let/const bleiben global, die App-Schichten (native.js,
native-eink.js, …) umhüllen sie wie bisher. Template und sw.js-SHELL listen
die Dateien; diora-android build_web nimmt static/js rekursiv.
Getestet: 14 Browser-Tests (e2e), Desktop-App (Buch, Lesestand, Leiste im
Reader, Hell/Dunkel).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:30:43 +02:00
|
|
|
|
`;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
panel.innerHTML = `
|
|
|
|
|
|
<label>Zoom <button class="btn btn-sm" id="rs-zoom-minus">−</button> <input type="range" id="rs-zoom" min="50" max="200" step="10" value="${readerSettings.pdfZoom}"> <button class="btn btn-sm" id="rs-zoom-plus">+</button> <span id="rs-zoom-val">${readerSettings.pdfZoom}%</span></label>
|
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet)
- 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird
abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die
gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker
(_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts-
verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel;
ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite.
- 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in
Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen
Seite (eine fast leere Seite wird nicht unendlich groß); nächste und
vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten
nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die
Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn'
gemeldet (E-Ink-Refresh).
- pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf,
Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab).
- Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten
immer „Seite 1“).
- Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu,
wenn er ruht.
- openBook hängt seine Listener über _readerListen an; closeReader nimmt
sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und
ein zweites Buch geben das alte Dokument frei; der entschlüsselte
PDF-Puffer wird nicht mehr gehalten.
- Invertieren dreht die Farbtöne zurück (Fotos keine Negative).
- e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten,
Outline, Links und einer fast leeren Seite.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
|
|
|
|
<button class="btn btn-sm ${readerSettings.pdfPaginated ? '' : 'active'}" data-rs-pdfmode="scroll">Scrollen</button>
|
|
|
|
|
|
<button class="btn btn-sm ${readerSettings.pdfPaginated ? 'active' : ''}" data-rs-pdfmode="pages">Seiten</button>
|
Refactoring Schritt 2: app.js nach Bereichen in static/js/app/ aufgeteilt; sw v106
32 klassische Skripte in der alten Reihenfolge (01-core … 99-init), ohne
Änderung am Code darin: geschnitten nur zwischen Anweisungen, je Datei
'use strict' wie vorher für die ganze Datei. Geprüft (acorn): kein Code, der
beim Laden läuft, braucht einen Namen aus einer späteren Datei. Globale
Funktionen und let/const bleiben global, die App-Schichten (native.js,
native-eink.js, …) umhüllen sie wie bisher. Template und sw.js-SHELL listen
die Dateien; diora-android build_web nimmt static/js rekursiv.
Getestet: 14 Browser-Tests (e2e), Desktop-App (Buch, Lesestand, Leiste im
Reader, Hell/Dunkel).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:30:43 +02:00
|
|
|
|
<button class="btn btn-sm ${readerSettings.pdfInverted ? 'active' : ''}" id="rs-invert">Invertieren</button>
|
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet)
- 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird
abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die
gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker
(_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts-
verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel;
ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite.
- 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in
Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen
Seite (eine fast leere Seite wird nicht unendlich groß); nächste und
vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten
nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die
Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn'
gemeldet (E-Ink-Refresh).
- pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf,
Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab).
- Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten
immer „Seite 1“).
- Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu,
wenn er ruht.
- openBook hängt seine Listener über _readerListen an; closeReader nimmt
sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und
ein zweites Buch geben das alte Dokument frei; der entschlüsselte
PDF-Puffer wird nicht mehr gehalten.
- Invertieren dreht die Farbtöne zurück (Fotos keine Negative).
- e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten,
Outline, Links und einer fast leeren Seite.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
|
|
|
|
<button class="btn btn-sm ${readerSettings.pdfSpread ? 'active' : ''}" id="rs-spread"${readerSettings.pdfPaginated ? ' style="display:none"' : ''}>Doppelseite</button>
|
Refactoring Schritt 2: app.js nach Bereichen in static/js/app/ aufgeteilt; sw v106
32 klassische Skripte in der alten Reihenfolge (01-core … 99-init), ohne
Änderung am Code darin: geschnitten nur zwischen Anweisungen, je Datei
'use strict' wie vorher für die ganze Datei. Geprüft (acorn): kein Code, der
beim Laden läuft, braucht einen Namen aus einer späteren Datei. Globale
Funktionen und let/const bleiben global, die App-Schichten (native.js,
native-eink.js, …) umhüllen sie wie bisher. Template und sw.js-SHELL listen
die Dateien; diora-android build_web nimmt static/js rekursiv.
Getestet: 14 Browser-Tests (e2e), Desktop-App (Buch, Lesestand, Leiste im
Reader, Hell/Dunkel).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:30:43 +02:00
|
|
|
|
`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
overlay.insertBefore(panel, $('reader-body-row'));
|
|
|
|
|
|
|
|
|
|
|
|
if (!isPdf) {
|
|
|
|
|
|
const fontRange = panel.querySelector('#rs-font');
|
|
|
|
|
|
const fontVal = panel.querySelector('#rs-font-val');
|
|
|
|
|
|
fontRange.addEventListener('input', () => {
|
|
|
|
|
|
readerSettings.fontSize = parseInt(fontRange.value, 10);
|
|
|
|
|
|
fontVal.textContent = readerSettings.fontSize + 'px';
|
|
|
|
|
|
applyReaderSettings(false);
|
|
|
|
|
|
saveReaderSettings();
|
|
|
|
|
|
});
|
|
|
|
|
|
panel.querySelector('#rs-font-minus').addEventListener('click', () => {
|
|
|
|
|
|
fontRange.value = Math.max(+fontRange.min, parseInt(fontRange.value, 10) - 1);
|
|
|
|
|
|
fontRange.dispatchEvent(new Event('input'));
|
|
|
|
|
|
});
|
|
|
|
|
|
panel.querySelector('#rs-font-plus').addEventListener('click', () => {
|
|
|
|
|
|
fontRange.value = Math.min(+fontRange.max, parseInt(fontRange.value, 10) + 1);
|
|
|
|
|
|
fontRange.dispatchEvent(new Event('input'));
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const lineRange = panel.querySelector('#rs-line');
|
|
|
|
|
|
const lineVal = panel.querySelector('#rs-line-val');
|
|
|
|
|
|
lineRange.addEventListener('input', () => {
|
|
|
|
|
|
readerSettings.lineHeight = (parseInt(lineRange.value, 10) / 10).toFixed(1);
|
|
|
|
|
|
lineVal.textContent = readerSettings.lineHeight;
|
|
|
|
|
|
applyReaderSettings(false);
|
|
|
|
|
|
saveReaderSettings();
|
|
|
|
|
|
});
|
|
|
|
|
|
panel.querySelector('#rs-line-minus').addEventListener('click', () => {
|
|
|
|
|
|
lineRange.value = Math.max(+lineRange.min, parseInt(lineRange.value, 10) - 1);
|
|
|
|
|
|
lineRange.dispatchEvent(new Event('input'));
|
|
|
|
|
|
});
|
|
|
|
|
|
panel.querySelector('#rs-line-plus').addEventListener('click', () => {
|
|
|
|
|
|
lineRange.value = Math.min(+lineRange.max, parseInt(lineRange.value, 10) + 1);
|
|
|
|
|
|
lineRange.dispatchEvent(new Event('input'));
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const widthRange = panel.querySelector('#rs-width');
|
|
|
|
|
|
const widthVal = panel.querySelector('#rs-width-val');
|
|
|
|
|
|
widthRange.addEventListener('input', () => {
|
|
|
|
|
|
readerSettings.maxWidth = parseInt(widthRange.value, 10);
|
|
|
|
|
|
widthVal.textContent = readerSettings.maxWidth + 'ch';
|
|
|
|
|
|
applyReaderSettings(false);
|
|
|
|
|
|
saveReaderSettings();
|
|
|
|
|
|
});
|
|
|
|
|
|
panel.querySelector('#rs-width-minus').addEventListener('click', () => {
|
|
|
|
|
|
widthRange.value = Math.max(+widthRange.min, parseInt(widthRange.value, 10) - 5);
|
|
|
|
|
|
widthRange.dispatchEvent(new Event('input'));
|
|
|
|
|
|
});
|
|
|
|
|
|
panel.querySelector('#rs-width-plus').addEventListener('click', () => {
|
|
|
|
|
|
widthRange.value = Math.min(+widthRange.max, parseInt(widthRange.value, 10) + 5);
|
|
|
|
|
|
widthRange.dispatchEvent(new Event('input'));
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
panel.querySelector('#rs-width-full').addEventListener('click', () => {
|
|
|
|
|
|
readerSettings.maxWidth = 999;
|
|
|
|
|
|
widthRange.value = 90;
|
|
|
|
|
|
widthVal.textContent = 'full';
|
|
|
|
|
|
applyReaderSettings(false);
|
|
|
|
|
|
saveReaderSettings();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const weightRange = panel.querySelector('#rs-weight');
|
|
|
|
|
|
const weightVal = panel.querySelector('#rs-weight-val');
|
|
|
|
|
|
weightRange.addEventListener('input', () => {
|
|
|
|
|
|
readerSettings.fontWeight = parseInt(weightRange.value, 10);
|
|
|
|
|
|
weightVal.textContent = readerSettings.fontWeight;
|
|
|
|
|
|
applyReaderSettings(false);
|
|
|
|
|
|
saveReaderSettings();
|
|
|
|
|
|
});
|
|
|
|
|
|
panel.querySelector('#rs-weight-minus').addEventListener('click', () => {
|
|
|
|
|
|
weightRange.value = Math.max(+weightRange.min, parseInt(weightRange.value, 10) - +weightRange.step);
|
|
|
|
|
|
weightRange.dispatchEvent(new Event('input'));
|
|
|
|
|
|
});
|
|
|
|
|
|
panel.querySelector('#rs-weight-plus').addEventListener('click', () => {
|
|
|
|
|
|
weightRange.value = Math.min(+weightRange.max, parseInt(weightRange.value, 10) + +weightRange.step);
|
|
|
|
|
|
weightRange.dispatchEvent(new Event('input'));
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
panel.querySelectorAll('[data-rs-font]').forEach(btn => {
|
|
|
|
|
|
btn.addEventListener('click', () => {
|
|
|
|
|
|
readerSettings.fontFamily = btn.dataset.rsFont;
|
|
|
|
|
|
panel.querySelectorAll('[data-rs-font]').forEach(b => b.classList.toggle('active', b === btn));
|
|
|
|
|
|
// The system fonts only have cuts at multiples of 100.
|
|
|
|
|
|
weightRange.step = _readerWeightStep();
|
|
|
|
|
|
if (readerSettings.fontWeight) {
|
|
|
|
|
|
readerSettings.fontWeight = Math.round(readerSettings.fontWeight / weightRange.step) * weightRange.step;
|
|
|
|
|
|
}
|
|
|
|
|
|
applyReaderSettings(false);
|
|
|
|
|
|
saveReaderSettings();
|
|
|
|
|
|
weightRange.value = _readerWeight();
|
|
|
|
|
|
weightVal.textContent = _readerWeight();
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
panel.querySelector('#rs-no-bold').addEventListener('click', function () {
|
|
|
|
|
|
readerSettings.noBold = !readerSettings.noBold;
|
|
|
|
|
|
this.classList.toggle('active', readerSettings.noBold);
|
|
|
|
|
|
applyReaderSettings(false);
|
|
|
|
|
|
saveReaderSettings();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
panel.querySelectorAll('[data-rs-theme]').forEach(btn => {
|
|
|
|
|
|
btn.addEventListener('click', () => {
|
|
|
|
|
|
readerSettings.theme = btn.dataset.rsTheme;
|
|
|
|
|
|
panel.querySelectorAll('[data-rs-theme]').forEach(b => b.classList.toggle('active', b === btn));
|
|
|
|
|
|
applyReaderSettings(false);
|
|
|
|
|
|
saveReaderSettings();
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
Lesen in Seiten auch im Browser und auf dem Desktop, mit Fußnoten unten auf der Seite und Doppelseite
63-pages.js (DioraPages): die Seitenansicht des E-Reader-Modus der Android-Apps, jetzt für alle –
Seitenumbrüche vor angeschnittenen Zeilen, ein Kapitel auf einmal, Fußnoten am Seitenfuß wie im
gedruckten Buch, Blättern per Tippen/Klick am Rand, Wischen, Pfeil-/Bild-/Leertaste und Mausrad.
Neu: Doppelseite (zwei Seiten nebeneinander ab 900 px Breite). Einstellung „Scrollen | Seiten“
(+ „Fußnoten unten“, „Doppelseite“) im Schrift-&-Layout-Panel, je Gerät.
Fußnoten: Erkennung und Auflösung aus diora-android native-ui.js übernommen (40-epub.js,
78-footnotes.js) – die Notiz wird im richtigen Kapitel gesucht (IDs wiederholen sich je Datei),
auch das Popover zeigt jetzt genau die Notiz. Vorbereitete Bücher werden einmal neu aufbereitet.
Behoben gegenüber der App-Fassung: ein Verweis in der ersten Zeile einer Seite (hochgestellt)
zählte zur Vorseite, und ein Verweis, der samt Notiz nicht mehr passte, blieb ohne Notiz stehen –
jetzt wandert er mit ihr auf die nächste Seite. Schnelles Blättern löst „Weit gescrollt“ nicht mehr aus.
sw CACHE v157
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 19:41:27 +01:00
|
|
|
|
|
|
|
|
|
|
// Scrolling or pages (63-pages.js): per device, for every book.
|
|
|
|
|
|
const syncMode = () => {
|
|
|
|
|
|
const pages = DioraPages.isOn();
|
|
|
|
|
|
panel.querySelectorAll('[data-rs-mode]').forEach(b => b.classList.toggle('active', (b.dataset.rsMode === 'pages') === pages));
|
|
|
|
|
|
const notes = panel.querySelector('#rs-page-notes');
|
|
|
|
|
|
if (notes) {
|
|
|
|
|
|
notes.style.display = pages ? '' : 'none';
|
|
|
|
|
|
notes.classList.toggle('active', DioraPages.pageNotes());
|
|
|
|
|
|
}
|
|
|
|
|
|
const spread = panel.querySelector('#rs-page-spread');
|
|
|
|
|
|
if (spread) {
|
|
|
|
|
|
spread.style.display = pages ? '' : 'none';
|
|
|
|
|
|
spread.classList.toggle('active', DioraPages.spread());
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
panel.querySelectorAll('[data-rs-mode]').forEach(btn => {
|
|
|
|
|
|
btn.addEventListener('click', () => { DioraPages.setOn(btn.dataset.rsMode === 'pages'); syncMode(); });
|
|
|
|
|
|
});
|
|
|
|
|
|
panel.querySelector('#rs-page-notes')?.addEventListener('click', () => { DioraPages.togglePageNotes(); syncMode(); });
|
|
|
|
|
|
panel.querySelector('#rs-page-spread')?.addEventListener('click', () => { DioraPages.setSpread(!DioraPages.spread()); syncMode(); });
|
Refactoring Schritt 2: app.js nach Bereichen in static/js/app/ aufgeteilt; sw v106
32 klassische Skripte in der alten Reihenfolge (01-core … 99-init), ohne
Änderung am Code darin: geschnitten nur zwischen Anweisungen, je Datei
'use strict' wie vorher für die ganze Datei. Geprüft (acorn): kein Code, der
beim Laden läuft, braucht einen Namen aus einer späteren Datei. Globale
Funktionen und let/const bleiben global, die App-Schichten (native.js,
native-eink.js, …) umhüllen sie wie bisher. Template und sw.js-SHELL listen
die Dateien; diora-android build_web nimmt static/js rekursiv.
Getestet: 14 Browser-Tests (e2e), Desktop-App (Buch, Lesestand, Leiste im
Reader, Hell/Dunkel).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:30:43 +02:00
|
|
|
|
} else {
|
|
|
|
|
|
const zoomRange = panel.querySelector('#rs-zoom');
|
|
|
|
|
|
|
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet)
- 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird
abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die
gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker
(_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts-
verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel;
ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite.
- 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in
Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen
Seite (eine fast leere Seite wird nicht unendlich groß); nächste und
vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten
nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die
Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn'
gemeldet (E-Ink-Refresh).
- pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf,
Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab).
- Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten
immer „Seite 1“).
- Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu,
wenn er ruht.
- openBook hängt seine Listener über _readerListen an; closeReader nimmt
sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und
ein zweites Buch geben das alte Dokument frei; der entschlüsselte
PDF-Puffer wird nicht mehr gehalten.
- Invertieren dreht die Farbtöne zurück (Fotos keine Negative).
- e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten,
Outline, Links und einer fast leeren Seite.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
|
|
|
|
// While the slider moves only the number follows; the pages are drawn anew once it rests.
|
|
|
|
|
|
let zoomTimer = 0;
|
|
|
|
|
|
zoomRange.addEventListener('input', () => {
|
|
|
|
|
|
const z = parseInt(zoomRange.value, 10);
|
|
|
|
|
|
const val = panel.querySelector('#rs-zoom-val');
|
|
|
|
|
|
if (val) val.textContent = z + '%';
|
|
|
|
|
|
clearTimeout(zoomTimer);
|
|
|
|
|
|
zoomTimer = setTimeout(() => applyPdfZoom(z), 250);
|
|
|
|
|
|
});
|
Refactoring Schritt 2: app.js nach Bereichen in static/js/app/ aufgeteilt; sw v106
32 klassische Skripte in der alten Reihenfolge (01-core … 99-init), ohne
Änderung am Code darin: geschnitten nur zwischen Anweisungen, je Datei
'use strict' wie vorher für die ganze Datei. Geprüft (acorn): kein Code, der
beim Laden läuft, braucht einen Namen aus einer späteren Datei. Globale
Funktionen und let/const bleiben global, die App-Schichten (native.js,
native-eink.js, …) umhüllen sie wie bisher. Template und sw.js-SHELL listen
die Dateien; diora-android build_web nimmt static/js rekursiv.
Getestet: 14 Browser-Tests (e2e), Desktop-App (Buch, Lesestand, Leiste im
Reader, Hell/Dunkel).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:30:43 +02:00
|
|
|
|
panel.querySelector('#rs-zoom-minus').addEventListener('click', () =>
|
|
|
|
|
|
applyPdfZoom(Math.max(50, readerSettings.pdfZoom - 10)));
|
|
|
|
|
|
panel.querySelector('#rs-zoom-plus').addEventListener('click', () =>
|
|
|
|
|
|
applyPdfZoom(Math.min(200, readerSettings.pdfZoom + 10)));
|
|
|
|
|
|
|
|
|
|
|
|
panel.querySelector('#rs-invert').addEventListener('click', function () {
|
|
|
|
|
|
readerSettings.pdfInverted = !readerSettings.pdfInverted;
|
|
|
|
|
|
this.classList.toggle('active', readerSettings.pdfInverted);
|
|
|
|
|
|
applyReaderSettings(true);
|
|
|
|
|
|
saveReaderSettings();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet)
- 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird
abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die
gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker
(_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts-
verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel;
ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite.
- 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in
Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen
Seite (eine fast leere Seite wird nicht unendlich groß); nächste und
vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten
nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die
Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn'
gemeldet (E-Ink-Refresh).
- pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf,
Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab).
- Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten
immer „Seite 1“).
- Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu,
wenn er ruht.
- openBook hängt seine Listener über _readerListen an; closeReader nimmt
sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und
ein zweites Buch geben das alte Dokument frei; der entschlüsselte
PDF-Puffer wird nicht mehr gehalten.
- Invertieren dreht die Farbtöne zurück (Fotos keine Negative).
- e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten,
Outline, Links und einer fast leeren Seite.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
|
|
|
|
// Scrolling or page by page (phones and e-readers start with pages).
|
|
|
|
|
|
panel.querySelectorAll('[data-rs-pdfmode]').forEach(btn => {
|
|
|
|
|
|
btn.addEventListener('click', () => {
|
|
|
|
|
|
const pages = btn.dataset.rsPdfmode === 'pages';
|
|
|
|
|
|
if (pages === !!readerSettings.pdfPaginated || !currentPdfDoc) return;
|
|
|
|
|
|
const contentEl = $('reader-content');
|
|
|
|
|
|
const page = pages ? _pdfTopPage(contentEl) : pdfCurrentPage;
|
|
|
|
|
|
readerSettings.pdfPaginated = pages;
|
|
|
|
|
|
saveReaderSettings();
|
|
|
|
|
|
panel.querySelectorAll('[data-rs-pdfmode]').forEach(b => b.classList.toggle('active', b === btn));
|
|
|
|
|
|
const spread = panel.querySelector('#rs-spread');
|
|
|
|
|
|
if (spread) spread.style.display = pages ? 'none' : '';
|
|
|
|
|
|
if (pages) {
|
|
|
|
|
|
pdfCurrentPage = page;
|
|
|
|
|
|
// Spreads are a scrolling thing: one page at a time here.
|
|
|
|
|
|
if (readerSettings.pdfSpread) reRenderPdf(page).then(() => enterPdfPaginatedMode());
|
|
|
|
|
|
else enterPdfPaginatedMode();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
exitPdfPaginatedMode(page);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
Refactoring Schritt 2: app.js nach Bereichen in static/js/app/ aufgeteilt; sw v106
32 klassische Skripte in der alten Reihenfolge (01-core … 99-init), ohne
Änderung am Code darin: geschnitten nur zwischen Anweisungen, je Datei
'use strict' wie vorher für die ganze Datei. Geprüft (acorn): kein Code, der
beim Laden läuft, braucht einen Namen aus einer späteren Datei. Globale
Funktionen und let/const bleiben global, die App-Schichten (native.js,
native-eink.js, …) umhüllen sie wie bisher. Template und sw.js-SHELL listen
die Dateien; diora-android build_web nimmt static/js rekursiv.
Getestet: 14 Browser-Tests (e2e), Desktop-App (Buch, Lesestand, Leiste im
Reader, Hell/Dunkel).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:30:43 +02:00
|
|
|
|
panel.querySelector('#rs-spread').addEventListener('click', function () {
|
|
|
|
|
|
readerSettings.pdfSpread = !readerSettings.pdfSpread;
|
|
|
|
|
|
this.classList.toggle('active', readerSettings.pdfSpread);
|
|
|
|
|
|
saveReaderSettings();
|
|
|
|
|
|
reRenderPdf();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function applyPdfZoom(newZoom) {
|
|
|
|
|
|
readerSettings.pdfZoom = newZoom;
|
|
|
|
|
|
const zoomRange = document.getElementById('rs-zoom');
|
|
|
|
|
|
const zoomVal = document.getElementById('rs-zoom-val');
|
|
|
|
|
|
if (zoomRange) zoomRange.value = newZoom;
|
|
|
|
|
|
if (zoomVal) zoomVal.textContent = newZoom + '%';
|
|
|
|
|
|
saveReaderSettings();
|
|
|
|
|
|
if (readerSettings.pdfPaginated) {
|
|
|
|
|
|
pdfSmartZoomPage(pdfCurrentPage);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
await reRenderPdf();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet)
- 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird
abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die
gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker
(_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts-
verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel;
ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite.
- 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in
Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen
Seite (eine fast leere Seite wird nicht unendlich groß); nächste und
vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten
nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die
Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn'
gemeldet (E-Ink-Refresh).
- pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf,
Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab).
- Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten
immer „Seite 1“).
- Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu,
wenn er ruht.
- openBook hängt seine Listener über _readerListen an; closeReader nimmt
sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und
ein zweites Buch geben das alte Dokument frei; der entschlüsselte
PDF-Puffer wird nicht mehr gehalten.
- Invertieren dreht die Farbtöne zurück (Fotos keine Negative).
- e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten,
Outline, Links und einer fast leeren Seite.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
|
|
|
|
// The page at the top of the view (scrolling) — 58-pdf.js keeps the pages in order.
|
Refactoring Schritt 2: app.js nach Bereichen in static/js/app/ aufgeteilt; sw v106
32 klassische Skripte in der alten Reihenfolge (01-core … 99-init), ohne
Änderung am Code darin: geschnitten nur zwischen Anweisungen, je Datei
'use strict' wie vorher für die ganze Datei. Geprüft (acorn): kein Code, der
beim Laden läuft, braucht einen Namen aus einer späteren Datei. Globale
Funktionen und let/const bleiben global, die App-Schichten (native.js,
native-eink.js, …) umhüllen sie wie bisher. Template und sw.js-SHELL listen
die Dateien; diora-android build_web nimmt static/js rekursiv.
Getestet: 14 Browser-Tests (e2e), Desktop-App (Buch, Lesestand, Leiste im
Reader, Hell/Dunkel).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:30:43 +02:00
|
|
|
|
function _currentScrollPage(contentEl) {
|
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet)
- 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird
abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die
gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker
(_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts-
verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel;
ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite.
- 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in
Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen
Seite (eine fast leere Seite wird nicht unendlich groß); nächste und
vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten
nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die
Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn'
gemeldet (E-Ink-Refresh).
- pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf,
Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab).
- Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten
immer „Seite 1“).
- Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu,
wenn er ruht.
- openBook hängt seine Listener über _readerListen an; closeReader nimmt
sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und
ein zweites Buch geben das alte Dokument frei; der entschlüsselte
PDF-Puffer wird nicht mehr gehalten.
- Invertieren dreht die Farbtöne zurück (Fotos keine Negative).
- e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten,
Outline, Links und einer fast leeren Seite.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
|
|
|
|
return _pdfTopPage(contentEl);
|
Refactoring Schritt 2: app.js nach Bereichen in static/js/app/ aufgeteilt; sw v106
32 klassische Skripte in der alten Reihenfolge (01-core … 99-init), ohne
Änderung am Code darin: geschnitten nur zwischen Anweisungen, je Datei
'use strict' wie vorher für die ganze Datei. Geprüft (acorn): kein Code, der
beim Laden läuft, braucht einen Namen aus einer späteren Datei. Globale
Funktionen und let/const bleiben global, die App-Schichten (native.js,
native-eink.js, …) umhüllen sie wie bisher. Template und sw.js-SHELL listen
die Dateien; diora-android build_web nimmt static/js rekursiv.
Getestet: 14 Browser-Tests (e2e), Desktop-App (Buch, Lesestand, Leiste im
Reader, Hell/Dunkel).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:30:43 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet)
- 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird
abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die
gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker
(_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts-
verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel;
ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite.
- 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in
Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen
Seite (eine fast leere Seite wird nicht unendlich groß); nächste und
vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten
nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die
Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn'
gemeldet (E-Ink-Refresh).
- pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf,
Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab).
- Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten
immer „Seite 1“).
- Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu,
wenn er ruht.
- openBook hängt seine Listener über _readerListen an; closeReader nimmt
sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und
ein zweites Buch geben das alte Dokument frei; der entschlüsselte
PDF-Puffer wird nicht mehr gehalten.
- Invertieren dreht die Farbtöne zurück (Fotos keine Negative).
- e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten,
Outline, Links und einer fast leeren Seite.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
|
|
|
|
/** Lay the open PDF out again (zoom, spreads, another width), at [pivot] or the page shown now. */
|
|
|
|
|
|
async function reRenderPdf(pivot) {
|
|
|
|
|
|
if (!currentPdfDoc) return;
|
Refactoring Schritt 2: app.js nach Bereichen in static/js/app/ aufgeteilt; sw v106
32 klassische Skripte in der alten Reihenfolge (01-core … 99-init), ohne
Änderung am Code darin: geschnitten nur zwischen Anweisungen, je Datei
'use strict' wie vorher für die ganze Datei. Geprüft (acorn): kein Code, der
beim Laden läuft, braucht einen Namen aus einer späteren Datei. Globale
Funktionen und let/const bleiben global, die App-Schichten (native.js,
native-eink.js, …) umhüllen sie wie bisher. Template und sw.js-SHELL listen
die Dateien; diora-android build_web nimmt static/js rekursiv.
Getestet: 14 Browser-Tests (e2e), Desktop-App (Buch, Lesestand, Leiste im
Reader, Hell/Dunkel).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:30:43 +02:00
|
|
|
|
const contentEl = $('reader-content');
|
|
|
|
|
|
if (!contentEl) return;
|
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet)
- 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird
abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die
gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker
(_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts-
verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel;
ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite.
- 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in
Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen
Seite (eine fast leere Seite wird nicht unendlich groß); nächste und
vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten
nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die
Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn'
gemeldet (E-Ink-Refresh).
- pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf,
Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab).
- Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten
immer „Seite 1“).
- Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu,
wenn er ruht.
- openBook hängt seine Listener über _readerListen an; closeReader nimmt
sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und
ein zweites Buch geben das alte Dokument frei; der entschlüsselte
PDF-Puffer wird nicht mehr gehalten.
- Invertieren dreht die Farbtöne zurück (Fotos keine Negative).
- e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten,
Outline, Links und einer fast leeren Seite.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
|
|
|
|
if (!pivot) pivot = readerSettings.pdfPaginated ? pdfCurrentPage : _pdfTopPage(contentEl);
|
|
|
|
|
|
await renderPdf(null, contentEl, undefined, pivot);
|
Refactoring Schritt 2: app.js nach Bereichen in static/js/app/ aufgeteilt; sw v106
32 klassische Skripte in der alten Reihenfolge (01-core … 99-init), ohne
Änderung am Code darin: geschnitten nur zwischen Anweisungen, je Datei
'use strict' wie vorher für die ganze Datei. Geprüft (acorn): kein Code, der
beim Laden läuft, braucht einen Namen aus einer späteren Datei. Globale
Funktionen und let/const bleiben global, die App-Schichten (native.js,
native-eink.js, …) umhüllen sie wie bisher. Template und sw.js-SHELL listen
die Dateien; diora-android build_web nimmt static/js rekursiv.
Getestet: 14 Browser-Tests (e2e), Desktop-App (Buch, Lesestand, Leiste im
Reader, Hell/Dunkel).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:30:43 +02:00
|
|
|
|
if (readerSettings.pdfPaginated) enterPdfPaginatedMode();
|
|
|
|
|
|
}
|