Skip to content

Instantly share code, notes, and snippets.

@brandonrozek
Last active November 7, 2023 09:10
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save brandonrozek/0cf038df40a913fda655 to your computer and use it in GitHub Desktop.
Save brandonrozek/0cf038df40a913fda655 to your computer and use it in GitHub Desktop.
A basic serviceworker implementation for offline website viewing
// Licensed under a CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
// http://creativecommons.org/publicdomain/zero/1.0/
var version = 'v2.0.24:';
var offlineFundamentals = [
'/',
'/offline/'
];
//Add core website files to cache during serviceworker installation
var updateStaticCache = function() {
return caches.open(version + 'fundamentals').then(function(cache) {
return Promise.all(offlineFundamentals.map(function(value) {
var request = new Request(value);
var url = new URL(request.url);
if (url.origin != location.origin) {
request = new Request(value, {mode: 'no-cors'});
}
return fetch(request).then(function(response) {
var cachedCopy = response.clone();
return cache.put(request, cachedCopy);
});
}))
})
};
//Clear caches with a different version number
var clearOldCaches = function() {
return caches.keys().then(function(keys) {
return Promise.all(
keys
.filter(function (key) {
return key.indexOf(version) != 0;
})
.map(function (key) {
return caches.delete(key);
})
);
})
}
/*
trims the cache
If cache has more than maxItems then it removes the excess items starting from the beginning
*/
var trimCache = function (cacheName, maxItems) {
caches.open(cacheName)
.then(function (cache) {
cache.keys()
.then(function (keys) {
if (keys.length > maxItems) {
cache.delete(keys[0])
.then(trimCache(cacheName, maxItems));
}
});
});
};
//When the service worker is first added to a computer
self.addEventListener("install", function(event) {
event.waitUntil(updateStaticCache()
.then(function() {
return self.skipWaiting();
})
);
})
self.addEventListener("message", function(event) {
var data = event.data;
//Send this command whenever many files are downloaded (ex: a page load)
if (data.command == "trimCache") {
trimCache(version + "pages", 25);
trimCache(version + "images", 10);
trimCache(version + "assets", 30);
}
});
//Service worker handles networking
self.addEventListener("fetch", function(event) {
//Fetch from network and cache
var fetchFromNetwork = function(response) {
var cacheCopy = response.clone();
if (event.request.headers.get('Accept').indexOf('text/html') != -1) {
caches.open(version + 'pages').then(function(cache) {
cache.put(event.request, cacheCopy);
});
} else if (event.request.headers.get('Accept').indexOf('image') != -1) {
caches.open(version + 'images').then(function(cache) {
cache.put(event.request, cacheCopy);
});
} else {
caches.open(version + 'assets').then(function add(cache) {
cache.put(event.request, cacheCopy);
});
}
return response;
}
//Fetch from network failed
var fallback = function() {
if (event.request.headers.get('Accept').indexOf('text/html') != -1) {
return caches.match(event.request).then(function (response) {
return response || caches.match('/offline/');
})
} else if (event.request.headers.get('Accept').indexOf('image') != -1) {
return new Response('<svg width="400" height="300" 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' }});
}
}
//This service worker won't touch non-get requests
if (event.request.method != 'GET') {
return;
}
//For HTML requests, look for file in network, then cache if network fails.
if (event.request.headers.get('Accept').indexOf('text/html') != -1) {
event.respondWith(fetch(event.request).then(fetchFromNetwork, fallback));
return;
}
//For non-HTML requests, look for file in cache, then network if no cache exists.
event.respondWith(
caches.match(event.request).then(function(cached) {
return cached || fetch(event.request).then(fetchFromNetwork, fallback);
})
)
});
//After the install event
self.addEventListener("activate", function(event) {
event.waitUntil(clearOldCaches()
.then(function() {
return self.clients.claim();
})
);
});
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('https://brandonrozek.com/serviceworker.js', {scope: '/'});
window.addEventListener("load", function() {
if (navigator.serviceWorker.controller != null) {
navigator.serviceWorker.controller.postMessage({"command":"trimCache"});
}
});
}
@MKDan
Copy link

MKDan commented Oct 22, 2018

How this differs from https://gist.github.com/flavioso16/fa0bcb969bf432f55cba1b255d441e3a ---------> Is this just a minification? And the above is the full code?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment