76 lines
3.1 KiB
JavaScript
76 lines
3.1 KiB
JavaScript
/*
|
|
* Offline copy of the public calculator (field staff often have weak signal).
|
|
* - The calculator page: network first (fresh parameters), stored copy when
|
|
* offline or slow. Responses marked no-store (admin views) are never kept.
|
|
* - Versioned assets (assets/...?v=): stored copy first.
|
|
* - calc.php, admin/ and everything else: network only, never stored.
|
|
*/
|
|
'use strict';
|
|
var CACHE = 'serubi-v1';
|
|
var PAGE_TIMEOUT_MS = 4000;
|
|
var SCOPE = self.registration.scope; // e.g. https://host/ or http://localhost/calcserubi-prod/
|
|
var PAGE_KEY = SCOPE; // one stored copy of the calculator, whatever the query string
|
|
|
|
self.addEventListener('install', function () { self.skipWaiting(); });
|
|
|
|
self.addEventListener('activate', function (event) {
|
|
event.waitUntil(caches.keys().then(function (keys) {
|
|
return Promise.all(keys.filter(function (k) { return k !== CACHE; }).map(function (k) { return caches.delete(k); }));
|
|
}).then(function () { return self.clients.claim(); }));
|
|
});
|
|
|
|
function isCalculatorPage(url) {
|
|
var rel = url.href.slice(SCOPE.length).split('?')[0];
|
|
return rel === '' || rel === 'index.php';
|
|
}
|
|
|
|
function storable(res) {
|
|
return res && res.ok && res.type === 'basic' && !/no-store/i.test(res.headers.get('Cache-Control') || '');
|
|
}
|
|
|
|
function pageNetworkFirst(request) {
|
|
return caches.open(CACHE).then(function (cache) {
|
|
var network = fetch(request).then(function (res) {
|
|
if (storable(res)) cache.put(PAGE_KEY, res.clone());
|
|
return res;
|
|
});
|
|
network.catch(function () { /* handled below */ });
|
|
var timeout = new Promise(function (resolve) { setTimeout(resolve, PAGE_TIMEOUT_MS); });
|
|
// slow network: answer from the stored copy after the timeout, if there is one
|
|
return Promise.race([network, timeout.then(function () { return cache.match(PAGE_KEY).then(function (c) { return c || network; }); })])
|
|
.catch(function () { return cache.match(PAGE_KEY).then(function (c) { if (c) return c; throw new Error('offline'); }); });
|
|
});
|
|
}
|
|
|
|
function assetCacheFirst(request) {
|
|
return caches.open(CACHE).then(function (cache) {
|
|
return cache.match(request).then(function (hit) {
|
|
if (hit) return hit;
|
|
return fetch(request).then(function (res) {
|
|
if (storable(res)) {
|
|
// keep only the newest version (?v=) of each asset
|
|
var path = new URL(request.url).pathname;
|
|
cache.keys().then(function (keys) {
|
|
keys.forEach(function (k) { if (new URL(k.url).pathname === path && k.url !== request.url) cache.delete(k); });
|
|
});
|
|
cache.put(request, res.clone());
|
|
}
|
|
return res;
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
self.addEventListener('fetch', function (event) {
|
|
var req = event.request;
|
|
if (req.method !== 'GET') return;
|
|
var url = new URL(req.url);
|
|
if (url.origin !== self.location.origin || req.url.indexOf(SCOPE) !== 0) return;
|
|
if (req.mode === 'navigate' && isCalculatorPage(url)) {
|
|
event.respondWith(pageNetworkFirst(req));
|
|
} else if (url.pathname.indexOf('/assets/') !== -1) {
|
|
event.respondWith(assetCacheFirst(req));
|
|
}
|
|
// anything else (calc.php, admin/, ...) goes straight to the network
|
|
});
|