Repository navigation
Expand file tree
/
Copy pathsw.js
More file actions
102 lines (96 loc) · 3.39 KB
/
Copy pathsw.js
File metadata and controls
102 lines (96 loc) · 3.39 KB
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
const CACHE_VERSION = 'devanagari-v0.7.0';
// Clips are immutable per slug and heavy, so they live in their own cache
// that survives shell version bumps.
const AUDIO_CACHE = 'devanagari-audio-v2';
const SHELL = [
'./',
'./index.html',
'./russian.html',
'./hebrew.html',
'./style.css',
'./js/app.js',
'./js/courses.js',
'./js/scheduler.js',
'./js/words.js',
'./js/stats.js',
'./js/audio.js',
'./js/sound.js',
'./characters.json',
'./words.json',
'./russian.json',
'./russian-words.json',
'./hebrew.json',
'./hebrew-words.json',
'./audio/devanagari/manifest.json',
'./audio/russian/manifest.json',
'./manifest.webmanifest',
'./manifest-russian.webmanifest',
'./manifest-hebrew.webmanifest',
'./icons/icon-192.png',
'./icons/icon-512.png',
'./icons/icon-russian-192.png',
'./icons/icon-russian-512.png',
'./icons/icon-hebrew-192.png',
'./icons/icon-hebrew-512.png',
];
// Best-effort: a failed clip download never fails the install and is healed
// by the runtime caching below on the next online playback.
const AUDIO_DIRS = ['./audio/devanagari/', './audio/russian/'];
async function prefetchAudio() {
const cache = await caches.open(AUDIO_CACHE);
await Promise.allSettled(AUDIO_DIRS.map(async dir => {
const manifest = await (await fetch(`${dir}manifest.json`)).json();
await Promise.allSettled(manifest.clips.map(async slug => {
const url = `${dir}${slug}.mp3`;
if (!await cache.match(url))
await cache.add(url);
}));
}));
}
self.addEventListener('install', event => {
event.waitUntil(Promise.all([
caches.open(CACHE_VERSION).then(cache => cache.addAll(SHELL)),
prefetchAudio().catch(() => {}),
]).then(() => self.skipWaiting()));
});
self.addEventListener('activate', event => {
event.waitUntil(caches.keys()
.then(keys => Promise.all(keys
.filter(k => k !== CACHE_VERSION && k !== AUDIO_CACHE)
.map(k => caches.delete(k))))
.then(() => self.clients.claim()));
});
self.addEventListener('fetch', event => {
const request = event.request;
if (request.method !== 'GET')
return;
const url = new URL(request.url);
if (url.origin !== location.origin)
return;
if (url.pathname.endsWith('.json')) {
// Network-first: data updates should not require a cache version bump.
event.respondWith(fetch(request)
.then(response => {
const copy = response.clone();
caches.open(CACHE_VERSION).then(cache => cache.put(request, copy));
return response;
})
.catch(() => caches.match(request)));
return;
}
if (url.pathname.includes('/audio/') && url.pathname.endsWith('.mp3')) {
// Requests come from fetch() in js/audio.js (never the media element),
// so no Range handling is needed here.
event.respondWith(caches.open(AUDIO_CACHE).then(async cache => {
const hit = await cache.match(request.url);
if (hit)
return hit;
const response = await fetch(request);
if (response.ok)
cache.put(request.url, response.clone());
return response;
}));
return;
}
event.respondWith(caches.match(request).then(hit => hit || fetch(request)));
});