Files
Alcea 2422d31874 Add files via upload
Update to render fullscreen
2026-08-02 04:24:10 +02:00

130 lines
4.4 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* eslint-env serviceworker */
importScripts('jszip.min.js');
const ZIP_URL = 'alceawis.de.zip';
const files = new Map(); // <virtual-path, Uint8Array>
let zipReadyResolve;
const zipReady = new Promise(r => (zipReadyResolve = r));
function swLog(...a) {
const msg = '[ZIPFS SW] ' + a.join(' ');
console.log(msg);
self.clients.matchAll().then(clients =>
clients.forEach(c => c.postMessage({ type: 'SW_LOG', text: msg }))
);
}
/* ---------- install ---------- */
self.addEventListener('install', () => {
swLog('Installing…');
self.skipWaiting();
});
/* ---------- activate --------- */
self.addEventListener('activate', event => {
event.waitUntil((async () => {
swLog('Activating; claiming clients…');
await self.clients.claim();
try {
swLog('Fetching ZIP:', ZIP_URL);
const res = await fetch(ZIP_URL, { mode: 'cors' });
if (!res.ok) throw new Error(`ZIP fetch failed (${res.status})`);
const buf = await res.arrayBuffer();
const zip = await JSZip.loadAsync(buf);
/* 1. find the first index.html and mark its folder as ROOT -------- */
let rootDir = ''; // e.g. "alceawis.de"
for (const entry of Object.values(zip.files)) {
if (entry.dir) continue;
const name = entry.name.replace(/\\/g, '/'); // normalise
if (name.endsWith('/index.html')) {
rootDir = name.slice(0, -'/index.html'.length);
swLog('Root directory detected:', rootDir);
break; // FIRST match only
}
}
/* 2. cache every file, stripping the rootDir prefix --------------- */
let count = 0;
for (const entry of Object.values(zip.files)) {
if (entry.dir) continue;
const raw = entry.name.replace(/\\/g, '/');
const stripped = rootDir && raw.startsWith(rootDir + '/')
? raw.slice(rootDir.length + 1)
: raw;
const vPath = '/' + stripped; // e.g. "/css/style.css"
const content = await entry.async('uint8array');
files.set(vPath, content);
swLog('→', vPath, `(${content.length} bytes)`);
count++;
}
swLog(`Unzipped ${count} files`);
} catch (err) {
swLog('ZIP load failed:', err);
} finally {
zipReadyResolve(); // always resolve so fetches dont hang
}
})());
});
/* ---------- fetch ------------ */
const SHELL_PAGE = '/zipfs-sw-worker.html'; // never routed into the ZIP fs
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
if (url.origin !== self.location.origin) return; // let CORS requests pass
let path = url.pathname;
/* Never intercept the demo shell page — always go to network for it */
if (path === SHELL_PAGE) return;
/* Friendly aliases → /index.html */
if (path === '/' || path === '/root' || path === '/home' || path === '/index')
path = '/index.html';
/* Directory paths → index.html */
if (path.endsWith('/')) path += 'index.html';
event.respondWith((async () => {
await zipReady;
const hit = files.get(path);
if (hit) {
swLog('Serving', path, 'from ZIP');
return new Response(hit, { headers: { 'Content-Type': mime(path) } });
}
/* Not in ZIP try network */
swLog('Not cached, fetching network:', path);
try {
return await fetch(event.request);
} catch (err) {
swLog('Network fetch failed:', err);
return new Response('<h1>Offline & not cached</h1>', {
status: 503,
headers: { 'Content-Type': 'text/html' }
});
}
})());
});
/* ---------- helpers ---------- */
function mime(p) {
return p.endsWith('.html') ? 'text/html' :
p.endsWith('.js') ? 'application/javascript':
p.endsWith('.css') ? 'text/css' :
p.endsWith('.json') ? 'application/json' :
p.endsWith('.png') ? 'image/png' :
p.endsWith('.jpg')||p.endsWith('.jpeg') ? 'image/jpeg' :
p.endsWith('.svg') ? 'image/svg+xml' :
'application/octet-stream';
}
self.addEventListener('error', e => swLog('Error:', e.message));
self.addEventListener('unhandledrejection', e => swLog('Unhandled rejection:', e.reason));