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>
67 lines
2.4 KiB
JavaScript
67 lines
2.4 KiB
JavaScript
'use strict';
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Tabs
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const TOP_TABS = ['home', 'radio', 'focus', 'podcasts', 'books', 'feeds', 'ambient'];
|
|
const RADIO_SUB_TABS = ['search', 'saved', 'history'];
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// URL hash routing — makes tabs deep-linkable/shareable and lets the browser
|
|
// back/forward buttons move between them. Deliberately hash-based rather
|
|
// than real paths: /podcasts/ and /books/ etc. are already taken by the
|
|
// JSON API, so this avoids touching Django's urls.py at all.
|
|
// ---------------------------------------------------------------------------
|
|
let _routingFromHash = false;
|
|
let _currentTopTab = 'radio';
|
|
|
|
function _syncHash() {
|
|
if (_routingFromHash) return;
|
|
let hash = _currentTopTab;
|
|
if (_currentTopTab === 'radio') {
|
|
hash += '/' + (localStorage.getItem('diora_active_radio_tab') || 'saved');
|
|
}
|
|
if (location.hash.slice(1) !== hash) history.pushState(null, '', '#' + hash);
|
|
}
|
|
|
|
function _routeFromHash() {
|
|
const [tab, sub] = (location.hash || '').replace(/^#/, '').split('/').filter(Boolean);
|
|
if (!TOP_TABS.includes(tab)) return;
|
|
_routingFromHash = true;
|
|
try {
|
|
showTab(tab);
|
|
if (tab === 'radio' && RADIO_SUB_TABS.includes(sub)) showRadioTab(sub);
|
|
} finally {
|
|
_routingFromHash = false;
|
|
}
|
|
}
|
|
|
|
window.addEventListener('popstate', _routeFromHash);
|
|
window.addEventListener('hashchange', _routeFromHash);
|
|
|
|
function showOfflineBanner(offline) {
|
|
document.body.classList.toggle('app-offline', offline);
|
|
const uploadArea = $('book-upload-area');
|
|
if (uploadArea) uploadArea.style.display = offline ? 'none' : '';
|
|
}
|
|
|
|
function showTab(name) {
|
|
TOP_TABS.forEach(p => {
|
|
const panel = $(`tab-${p}`);
|
|
if (panel) panel.style.display = (p === name) ? '' : 'none';
|
|
});
|
|
|
|
document.querySelectorAll('#tabs .tab-btn').forEach((btn, i) => {
|
|
btn.classList.toggle('active', TOP_TABS[i] === name);
|
|
});
|
|
|
|
localStorage.setItem('diora_active_tab', name);
|
|
_currentTopTab = name;
|
|
_syncHash();
|
|
|
|
if (name === 'books') loadBookList();
|
|
if (name === 'ambient') _ambientLoad();
|
|
if (name === 'home' && window.__dioraHomeShow) window.__dioraHomeShow(); // static/js/home.js
|
|
if (name === 'feeds' && window.__dioraFeedsShow) window.__dioraFeedsShow(); // static/js/feeds.js
|
|
}
|