Created
February 7, 2019 15:34
-
-
Save ryanirelan/d024e7d91f9f671b69ec77d070238cb0 to your computer and use it in GitHub Desktop.
Service worker for pre-caching pages for offline use. From CraftQuest livestream: https://craftquest.io/livestreams/what-are-service-workers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// borrowed mightily from Fillament Group - https://www.filamentgroup.com/lab/modernizing-delivery.html | |
/*jshint esversion: 6 */ | |
/*jshint strict:false */ | |
/*global self:true */ | |
/*global caches:true */ | |
/*global Response:true */ | |
/*global Request:true */ | |
(function(){ | |
'use strict'; | |
// Constants for the cache size | |
const maxPagesCache = 30; | |
const maxStaticAssetsCache = 100; | |
// borrowing heavily from adactio's sw patterns here... Thanks JK! | |
const version = 'craftquest1.0::'; | |
const staticCacheName = version + 'static'; | |
const pagesCacheName = version + 'pages'; | |
const imagesCacheName = version + 'images'; | |
const offlinePages = [ | |
'/', | |
]; | |
const staticAssets = [ | |
'', | |
]; | |
function stashInCache(cacheName, request, response) { | |
if (request.url.indexOf('http') === 0) { | |
caches.open(cacheName) | |
.then( cache => cache.put(request, response) ); | |
} | |
} | |
function updateStaticCache() { | |
//try to fetch static top level pages - can be done after install. | |
caches.open(staticCacheName) | |
.then( cache => { | |
// These items must be cached for the Service Worker to complete installation | |
return cache.addAll(offlinePages.map(url => new Request(url, {credentials: 'same-origin'}))); | |
}); | |
return caches.open(staticCacheName) | |
.then( cache => { | |
// These items must be cached for the Service Worker to complete installation | |
return cache.addAll(staticAssets.map(url => new Request(url, {credentials: 'same-origin'}))); | |
}); | |
} | |
// Limit the number of items in a specified cache. | |
function trimCache(cacheName, maxItems) { | |
caches.open(cacheName) | |
.then( cache => { | |
cache.keys() | |
.then(keys => { | |
if (keys.length > maxItems) { | |
cache.delete(keys[0]) | |
.then(trimCache(cacheName, maxItems)); | |
} | |
}); | |
}); | |
} | |
// Remove caches whose name is no longer valid | |
function clearOldCaches() { | |
return caches.keys() | |
.then( keys => { | |
return Promise.all(keys | |
.filter(key => key.indexOf(version) !== 0) | |
.map(key => caches.delete(key)) | |
); | |
}); | |
} | |
self.addEventListener('install', event => { | |
event.waitUntil(updateStaticCache() | |
.then( () => self.skipWaiting() ) | |
); | |
}); | |
self.addEventListener('activate', event => { | |
event.waitUntil(clearOldCaches() | |
.then( () => self.clients.claim() ) | |
); | |
}); | |
self.addEventListener('message', event => { | |
if (event.data.command === 'trimCaches') { | |
trimCache(pagesCacheName, maxPagesCache); | |
trimCache(imagesCacheName, maxStaticAssetsCache); | |
} | |
}); | |
self.addEventListener('fetch', event => { | |
let request = event.request; | |
let url = new URL(request.url); | |
// Ignore requests to some directories | |
if ( request.url.indexOf('google-analytics') !== -1 ) { | |
return; | |
} | |
// Ignore non-GET requests | |
if (request.method !== 'GET') { | |
return; | |
} | |
// For HTML requests, try the network first, fall back to the cache, finally the offline page | |
if (request.headers.get('Accept').indexOf('text/html') !== -1) { | |
event.respondWith( | |
fetch(request) | |
.then( response => { | |
// NETWORK | |
// Stash a copy of this page in the pages cache | |
let copy = response.clone(); | |
if (offlinePages.includes(url.pathname) || offlinePages.includes(url.pathname + '/')) { | |
stashInCache(staticCacheName, request, copy); | |
} else { | |
stashInCache(pagesCacheName, request, copy); | |
} | |
return response; | |
}) | |
.catch( () => { | |
// CACHE or FALLBACK | |
return caches.match(request) | |
.then( response => response || caches.match('/offline.html') ); | |
}) | |
); | |
return; | |
} | |
// For non-HTML requests, look in the cache first, fall back to the network | |
event.respondWith( | |
caches.match(request) | |
.then(response => { | |
// CACHE | |
return response || fetch(request) | |
.then( response => { | |
// NETWORK | |
// If the request is for an image, stash a copy of this image in the images cache | |
if (request.headers.get('Accept').indexOf('image') !== -1) { | |
let copy = response.clone(); | |
stashInCache(imagesCacheName, request, copy); | |
} | |
return response; | |
}) | |
.catch( () => { | |
// OFFLINE | |
// If the request is for an image, show an offline placeholder | |
if (request.headers.get('Accept').indexOf('image') !== -1) { | |
return new Response('<svg role="img" aria-labelledby="offline-title" viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg"><title id="offline-title">Offline</title><g fill="none" fill-rule="evenodd"><path fill="#D8D8D8" d="M0 0h400v300H0z"/><text fill="#9B9B9B" font-family="Helvetica Neue,Arial,Helvetica,sans-serif" font-size="72" font-weight="bold"><tspan x="93" y="172">offline</tspan></text></g></svg>', {headers: {'Content-Type': 'image/svg+xml'}}); | |
} | |
}); | |
}) | |
); | |
}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment