2022-11-22 22:24:06 -05:00
|
|
|
let version = 'v1::';
|
|
|
|
let cacheName = 'website';
|
2024-10-20 12:36:35 -04:00
|
|
|
let offlinePage = '/offline/';
|
2022-11-22 22:24:06 -05:00
|
|
|
let offlineFundamentals = [offlinePage, '/'];
|
|
|
|
let maxItems = 100;
|
|
|
|
|
|
|
|
|
2024-10-20 12:05:59 -04:00
|
|
|
function addFundamentals() {
|
|
|
|
return caches.open(version + cacheName)
|
|
|
|
.then(cache => cache.addAll(offlineFundamentals))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cache the page(s) that initiate the service worker
|
|
|
|
function cacheClients() {
|
|
|
|
return clients.matchAll({
|
|
|
|
includeUncontrolled: true
|
|
|
|
})
|
|
|
|
.then(allClients => allClients.map(client => client.url))
|
2024-10-20 12:13:26 -04:00
|
|
|
.then(pages => Promise.all([pages, caches.open(cacheName)]))
|
2024-10-20 12:05:59 -04:00
|
|
|
.then(([pages, cache]) => cache.addAll(pages))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove caches whose name is no longer valid
|
|
|
|
function clearInvalidatedCaches() {
|
|
|
|
return caches.keys()
|
|
|
|
.then( keys => {
|
|
|
|
return Promise.all(keys
|
|
|
|
.filter(key => !key.includes(version))
|
|
|
|
.map(key => caches.delete(key))
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
2024-10-20 10:56:57 -04:00
|
|
|
|
2024-10-20 12:05:59 -04:00
|
|
|
function trimCache(name, maxItems) {
|
|
|
|
return caches.open(name)
|
2024-10-20 12:13:26 -04:00
|
|
|
.then(cache => Promise.all([cache, cache.keys()]))
|
2024-10-20 12:05:59 -04:00
|
|
|
// Make sure offlineFundamentals don't get deleted
|
2024-10-20 12:18:24 -04:00
|
|
|
.then(([cache, keys]) => [
|
|
|
|
cache,
|
|
|
|
keys.filter(key => !offlineFundamentals.includes(key)),
|
|
|
|
keys.length - maxItems
|
|
|
|
])
|
|
|
|
.then(([cache, possibleDelete, numToDelete]) => {
|
2024-10-20 12:05:59 -04:00
|
|
|
// Trim cache until we are of the right size
|
|
|
|
deleteInProgress = []
|
2024-10-20 12:18:24 -04:00
|
|
|
for (let i = 0; i < numToDelete; i++) {
|
2024-10-20 10:56:57 -04:00
|
|
|
// Keep track of each delete
|
2024-10-20 12:05:59 -04:00
|
|
|
deleteInProgress.push(cache.delete(possibleDelete[i]));
|
|
|
|
}
|
2024-10-20 10:56:57 -04:00
|
|
|
|
2024-10-20 12:05:59 -04:00
|
|
|
// Return when everything is resolved
|
|
|
|
return Promise.all(deleteInProgress);
|
2024-10-20 10:56:57 -04:00
|
|
|
})
|
2022-11-22 22:24:06 -05:00
|
|
|
}
|
|
|
|
|
2024-10-20 12:05:59 -04:00
|
|
|
self.addEventListener('install', function (event) {
|
|
|
|
// Cache offline fundamentals
|
|
|
|
event.waitUntil(
|
|
|
|
addFundamentals()
|
|
|
|
.then(() => cacheClients())
|
|
|
|
.then(() => skipWaiting())
|
|
|
|
)
|
|
|
|
});
|
|
|
|
|
2022-11-22 22:24:06 -05:00
|
|
|
// Listens for trimCache command which occurs on page load
|
|
|
|
self.addEventListener('message', event => {
|
2024-10-19 21:52:15 -04:00
|
|
|
if (event.data.command == 'trimCache') {
|
|
|
|
trimCache(version + cacheName, maxItems);
|
|
|
|
}
|
|
|
|
});
|
2022-11-22 22:24:06 -05:00
|
|
|
|
|
|
|
// If the version changes, invalidate the cache
|
|
|
|
self.addEventListener('activate', function (event) {
|
2024-10-19 21:52:15 -04:00
|
|
|
event.waitUntil(
|
2024-10-20 12:05:59 -04:00
|
|
|
clearInvalidatedCaches()
|
|
|
|
.then(() => clients.claim())
|
2024-10-19 21:52:15 -04:00
|
|
|
);
|
2022-11-22 22:24:06 -05:00
|
|
|
});
|
|
|
|
|
2024-10-20 12:05:59 -04:00
|
|
|
if (registration.navigationPreload) {
|
|
|
|
addEventListener('activate', event => {
|
|
|
|
event.waitUntil(
|
|
|
|
registration.navigationPreload.enable()
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-11-22 22:24:06 -05:00
|
|
|
// Listen for request events
|
|
|
|
self.addEventListener('fetch', function (event) {
|
2024-10-20 12:05:59 -04:00
|
|
|
const request = event.request;
|
2022-11-22 22:24:06 -05:00
|
|
|
|
2024-10-20 10:56:57 -04:00
|
|
|
let isRequestType = function(name) {
|
|
|
|
return request.headers
|
|
|
|
.get('Accept')
|
|
|
|
.includes(name);
|
|
|
|
}
|
2022-11-22 22:24:06 -05:00
|
|
|
|
2024-10-20 12:36:35 -04:00
|
|
|
// Always fetch non-GET requests from the network
|
|
|
|
if (request.method !== 'GET') {
|
|
|
|
// Present offline page when failed to
|
|
|
|
// fetch a HTML page
|
|
|
|
if (isRequestType('text/html')) {
|
|
|
|
event.respondWith(
|
|
|
|
fetch(request)
|
|
|
|
.catch(() => caches.match(offlinePage))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-11-22 22:24:06 -05:00
|
|
|
// Network-first Approach
|
|
|
|
event.respondWith(
|
2024-10-19 21:52:15 -04:00
|
|
|
// Attepmt to grab the latest copy from the network
|
2024-10-20 12:05:59 -04:00
|
|
|
Promise.resolve(event.preloadResponse)
|
|
|
|
.then(preloadResponse => preloadResponse || fetch(request))
|
|
|
|
.then(response => {
|
2022-11-22 22:24:06 -05:00
|
|
|
// If successful, create a copy of the response
|
2024-10-19 21:52:15 -04:00
|
|
|
// and save it to the cache
|
|
|
|
// Note: Ignore badges
|
2024-10-20 10:56:57 -04:00
|
|
|
if (!request.url.includes("/badges")) {
|
2024-10-19 21:52:15 -04:00
|
|
|
let cacheCopy = response.clone();
|
2024-10-20 12:05:59 -04:00
|
|
|
event.waitUntil(
|
|
|
|
caches.open(version + cacheName)
|
|
|
|
.then(cache => cache.put(request, cacheCopy))
|
|
|
|
);
|
2024-10-19 21:52:15 -04:00
|
|
|
}
|
2022-11-22 22:24:06 -05:00
|
|
|
return response;
|
|
|
|
})
|
2024-10-20 12:05:59 -04:00
|
|
|
// Check the cache
|
|
|
|
.catch(error =>
|
|
|
|
caches.match(request)
|
|
|
|
// Show offline page for HTML pages if cache miss
|
|
|
|
.then(response => isRequestType('text/html')? response || caches.match(offlinePage) : response)
|
|
|
|
)
|
2022-11-22 22:24:06 -05:00
|
|
|
);
|
|
|
|
});
|