-
Notifications
You must be signed in to change notification settings - Fork 19
/
service-worker.js
117 lines (102 loc) · 4 KB
/
service-worker.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
'use strict';
// Get a random cache key every time this script starts --
// cached files under this key gets updated every time after the page loads,
// and everytime service worker updates.
const backgroundUpdateCacheKey = Math.random().toString(36).substr(2, 8);
// Cached files saved under this key only gets re-installed everytime
// service worker updates.
const persistCacheKey = 'persist';
// List of assets to cache converts to full URLs.
// The fetch event for the last URL will invoke cache refresh.
const backgroundUpdateFiles = [
'./lib/bopomofo_encoder.js',
'./lib/client.js',
'./lib/worker.js',
'./lib/jszhuyin.js',
'./lib/jszhuyin_data_pack.js',
'./lib/jszhuyin_server.js',
'./lib/storage.js',
'./lib/data_loader.js',
'./lib/web.js',
'./assets/index.js?_=1',
'./',
'./data/database.data'
].map(relativeURL => (new URL(relativeURL, self.location)).href);
// List of assets to persist in cache, converts to full URLs.
const persistFiles = [
'//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css',
'//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-theme.min.css'
].map(relativeURL => (new URL(relativeURL, self.location)).href);
const allFiles = [].concat(persistFiles, backgroundUpdateFiles);
self.oninstall = evt => {
// Attempt to install two caches atomically when the service worker is
// being installed.
// addAll() is atomic by design and we will rely on it.
// We however cannot make sure the two caches are in sync given there isn't
// an API for us to make sure of that.
evt.waitUntil(
self.caches.keys()
.then(keys => Promise.all(keys.map(key => self.caches.delete(key))))
.then(() => Promise.all([
self.caches.open(backgroundUpdateCacheKey)
.then(cache => cache.addAll(backgroundUpdateFiles)),
self.caches.open(persistCacheKey)
.then(cache => cache.addAll(persistFiles))])));
};
function Deferred() {
this.promise = new Promise((res, rej) => {
this.resolve = res;
this.reject = rej;
});
}
// This deferred object will keep the service worker alive for the entire page
// load. Also, given the lifetime of one service worker invocation is governed
// here, we don't need to use async-waituntil-polyfill here.
var deferredCacheRefresh = new Deferred();
var requestsToRecache = new Set();
self.onfetch = evt => {
if (allFiles.indexOf(evt.request.url) === -1) {
// A unlisted file is being requested.
return;
}
evt.waitUntil(deferredCacheRefresh.promise);
// We will be using cache-then-network strategy for better experience,
// given the database takes time to download.
let responsePromise = self.caches.match(evt.request)
.then((cachedResponse) => {
if (cachedResponse) {
if (backgroundUpdateFiles.indexOf(evt.request.url) !== -1) {
requestsToRecache.add(evt.request.url);
startCacheRefresh(evt.request.url);
}
return cachedResponse;
}
// Unlisted file being requested.
return fetch(evt.request);
});
evt.respondWith(responsePromise);
};
function startCacheRefresh(url) {
// We will wait for the current cache to be used fully before the new caching
// process.
if (requestsToRecache.size !== backgroundUpdateFiles.length &&
url !== backgroundUpdateFiles[backgroundUpdateFiles.length - 1]) {
return;
}
requestsToRecache.clear();
// Open a new named cache, have it fetched fully,
// and remove the entries in the old cache.
// Given that there isn't a defined versioning here, we can't detect
// the actual file changes here. Thus, we will never ask user to reload to
// get the new version.
Promise.resolve()
.then(() => self.caches.open(backgroundUpdateCacheKey))
.then(cache => cache.addAll(backgroundUpdateFiles))
.then(() => self.caches.keys())
.then(keys => Promise.all(
keys
.filter(key =>
(key !== backgroundUpdateCacheKey) && (key !== persistCacheKey))
.map(key => self.caches.delete(key))))
.then(() => deferredCacheRefresh.resolve());
}