187 lines
4.6 KiB
JavaScript
187 lines
4.6 KiB
JavaScript
// Generated by render_books.py -- do not edit by hand.
|
|
//
|
|
// STRATEGY decides what a same-origin GET does:
|
|
//
|
|
// network-first ask the network, fall back to the cache when it fails.
|
|
// Offline, fetch rejects immediately, so the fallback is
|
|
// effectively instant -- the only cost is one round trip
|
|
// while online, and you never see a stale page.
|
|
// cache-first answer from the cache the moment there is a hit. Fastest
|
|
// possible load, but a rebuild needs two reloads to appear.
|
|
//
|
|
// Either way every response that arrives is written back to the cache, so the
|
|
// offline copy stays current, and a new build changes VERSION, dropping the
|
|
// caches belonging to older builds.
|
|
const STRATEGY = 'network-first';
|
|
const NETWORK_TIMEOUT_MS = 3000;
|
|
const VERSION = 'c5d29c3cdf15';
|
|
const CACHE = 'bible-' + VERSION;
|
|
const ASSETS = [
|
|
"./",
|
|
"./1CH.html",
|
|
"./1CO.html",
|
|
"./1ES.html",
|
|
"./1JN.html",
|
|
"./1KI.html",
|
|
"./1MA.html",
|
|
"./1PE.html",
|
|
"./1SA.html",
|
|
"./1TH.html",
|
|
"./1TI.html",
|
|
"./2CH.html",
|
|
"./2CO.html",
|
|
"./2ES.html",
|
|
"./2JN.html",
|
|
"./2KI.html",
|
|
"./2MA.html",
|
|
"./2PE.html",
|
|
"./2SA.html",
|
|
"./2TH.html",
|
|
"./2TI.html",
|
|
"./3JN.html",
|
|
"./3MA.html",
|
|
"./4MA.html",
|
|
"./ACT.html",
|
|
"./AMO.html",
|
|
"./BAR.html",
|
|
"./COL.html",
|
|
"./DAG.html",
|
|
"./DAN.html",
|
|
"./DEU.html",
|
|
"./ECC.html",
|
|
"./EPH.html",
|
|
"./ESG.html",
|
|
"./EST.html",
|
|
"./EXO.html",
|
|
"./EZK.html",
|
|
"./EZR.html",
|
|
"./FRT.html",
|
|
"./GAL.html",
|
|
"./GEN.html",
|
|
"./GLO.html",
|
|
"./HAB.html",
|
|
"./HAG.html",
|
|
"./HEB.html",
|
|
"./HOS.html",
|
|
"./ISA.html",
|
|
"./JAS.html",
|
|
"./JDG.html",
|
|
"./JDT.html",
|
|
"./JER.html",
|
|
"./JHN.html",
|
|
"./JOB.html",
|
|
"./JOL.html",
|
|
"./JON.html",
|
|
"./JOS.html",
|
|
"./JUD.html",
|
|
"./LAM.html",
|
|
"./LEV.html",
|
|
"./LUK.html",
|
|
"./MAL.html",
|
|
"./MAN.html",
|
|
"./MAT.html",
|
|
"./MIC.html",
|
|
"./MRK.html",
|
|
"./NAM.html",
|
|
"./NEH.html",
|
|
"./NUM.html",
|
|
"./OBA.html",
|
|
"./PHM.html",
|
|
"./PHP.html",
|
|
"./PRO.html",
|
|
"./PS2.html",
|
|
"./PSA.html",
|
|
"./REV.html",
|
|
"./ROM.html",
|
|
"./RUT.html",
|
|
"./SIR.html",
|
|
"./SNG.html",
|
|
"./TIT.html",
|
|
"./TOB.html",
|
|
"./WIS.html",
|
|
"./ZEC.html",
|
|
"./ZEP.html",
|
|
"./app.js",
|
|
"./bible.css",
|
|
"./icons/apple-touch-icon.png",
|
|
"./icons/icon-192.png",
|
|
"./icons/icon-512.png",
|
|
"./index.html",
|
|
"./manifest.webmanifest"
|
|
];
|
|
|
|
self.addEventListener('install', (event) => {
|
|
event.waitUntil((async () => {
|
|
const cache = await caches.open(CACHE);
|
|
// Added one at a time rather than addAll(): addAll is atomic, so a single
|
|
// failed asset would leave the app with no offline copy at all.
|
|
let failed = 0;
|
|
await Promise.all(ASSETS.map((url) =>
|
|
cache.add(new Request(url, { cache: 'reload' })).catch(() => { failed++; })));
|
|
if (failed) console.warn('precache: ' + failed + ' of ' + ASSETS.length + ' failed');
|
|
await self.skipWaiting();
|
|
})());
|
|
});
|
|
|
|
self.addEventListener('activate', (event) => {
|
|
event.waitUntil((async () => {
|
|
const keys = await caches.keys();
|
|
await Promise.all(keys
|
|
.filter((k) => k.startsWith('bible-') && k !== CACHE)
|
|
.map((k) => caches.delete(k)));
|
|
await self.clients.claim();
|
|
})());
|
|
});
|
|
|
|
self.addEventListener('fetch', (event) => {
|
|
const req = event.request;
|
|
if (req.method !== 'GET') return;
|
|
if (new URL(req.url).origin !== location.origin) return;
|
|
event.respondWith(respond(event));
|
|
});
|
|
|
|
// Aborted rather than raced, so a hung request is actually torn down instead
|
|
// of leaking while the cache answers.
|
|
async function fromNetwork(req) {
|
|
const controller = new AbortController();
|
|
const timer = setTimeout(() => controller.abort(), NETWORK_TIMEOUT_MS);
|
|
try {
|
|
return await fetch(req, { signal: controller.signal });
|
|
} finally {
|
|
clearTimeout(timer);
|
|
}
|
|
}
|
|
|
|
function cached(req) {
|
|
return caches.match(req, { ignoreSearch: true });
|
|
}
|
|
|
|
async function respond(event) {
|
|
const req = event.request;
|
|
|
|
if (STRATEGY === 'cache-first') {
|
|
const hit = await cached(req);
|
|
if (hit) return hit;
|
|
}
|
|
|
|
try {
|
|
const res = await fromNetwork(req);
|
|
if (res && res.ok && res.type === 'basic') {
|
|
const copy = res.clone();
|
|
// Not awaited: the response should not wait on the cache write.
|
|
event.waitUntil(caches.open(CACHE).then((c) => c.put(req, copy)));
|
|
}
|
|
return res;
|
|
} catch (err) {
|
|
const hit = await cached(req);
|
|
if (hit) return hit;
|
|
// Offline and never cached. Navigations get the index rather than the
|
|
// browser's error page; anything else genuinely fails.
|
|
if (req.mode === 'navigate') {
|
|
const index = await cached('./index.html');
|
|
if (index) return index;
|
|
}
|
|
throw err;
|
|
}
|
|
}
|