1const cacheName = 'dokuwiki PWA cache';
2
3self.addEventListener('install', function (e) {
4    idbKeyval.del('lastSync');
5});
6
7self.addEventListener('message', function (e) {
8
9    // console.log("[ServiceWorker] Received Message:");
10    // console.log(e.data.type);
11
12    switch (e.data.type) {
13        case 'updatePages':
14            cachePages(e, e.data.pages);
15            break;
16        case 'getLastUpdate':
17            idbKeyval.get('lastSync').then((value) => e.source.postMessage(
18                {
19                    type: 'lastUpdate',
20                    ts: value,
21                }
22            ));
23            break;
24        case 'getHashVersion':
25            e.source.postMessage(
26                {
27                    type: 'swHashVersion',
28                    hash: swHashVersion, // injected when serviceworker is constructed in PHP
29                }
30            )
31    }
32
33});
34
35function cachePages(e, data) {
36    idbKeyval.set('lastSync', Math.floor(Date.now()/1000));
37    e.waitUntil(
38        caches.open(cacheName).then(function (cache) {
39
40            // if (r) {
41            //     const lmTimeString = r.headers.get('Last-Modified');
42            //     const ts = (new Date( lmTimeString )).getTime();
43            //     console.log('we have a cache for ' + e.request.url + ' from ', lmTimeString, ts);
44            // }
45            return Promise.all(data.map(function (pageData) {
46                return cache.add(pageData.link)
47            }));
48        })
49    );
50}
51
52self.addEventListener('activate', function (e) {
53    console.log('[ServiceWorker] Activate');
54});
55
56const CACHED_DESTINATIONS = [
57    'document',
58    'style',
59    'script',
60    'image',
61    'font',
62];
63
64const OFFLINE_FIRST_DESTINATIONS = [
65    'script',
66    'style',
67    'font',
68];
69
70self.addEventListener('fetch', function (e) {
71    if (e.request.method !== 'GET') {
72        return;
73    }
74
75    if (!CACHED_DESTINATIONS.includes(e.request.destination)) {
76        // only cache important modes
77        return;
78    }
79
80    if (OFFLINE_FIRST_DESTINATIONS.includes(e.request.destination)) {
81        e.respondWith(cacheFirst(e.request));
82        return;
83    }
84
85    e.respondWith(networkFirst(e.request));
86});
87
88function networkFirst(request) {
89    return fromNetwork(request, 400).then(function (response) {
90        if (response.headers.has('X-DWPLUGIN-PWAOFFLINE-ACT') &&
91            response.headers.get('X-DWPLUGIN-PWAOFFLINE-ACT') !== 'show') {
92            // don't cache modes other than show
93            return response;
94        }
95        return caches.open(cacheName).then(function (cache) {
96            cache.put(request, response.clone());
97            return response;
98        });
99    }).catch(function () {
100        return fromCache(request);
101    })
102}
103
104function cacheFirst(request) {
105    return caches.match(request).then(function (cacheResponse) {
106        return cacheResponse || fetch(request).then(function (response) {
107                return caches.open(cacheName).then(function (cache) {
108                    cache.put(request, response.clone());
109                    return response;
110                });
111            });
112    })
113}
114
115function fromNetwork(request, timeout) {
116    return new Promise(function (fulfill, reject) {
117        const timeoutId = setTimeout(reject, timeout);
118        fetch(request).then(function (response) {
119
120            if (response.status >= 500) {
121                reject();
122            }
123            clearTimeout(timeoutId);
124            fulfill(response);
125        }, reject);
126    });
127}
128
129function fromCache(request) {
130    if (!CACHED_DESTINATIONS.includes(request.destination)) {
131        return Promise.reject('no-match');
132    }
133
134    return caches.open(cacheName).then(function (cache) {
135        return cache.match(request).then(function (matching) {
136            if (matching) {
137                return matching;
138            }
139            if (request.destination === 'document') {
140                return new Response('Page not available. Please go back.', {
141                    headers: {
142                        'Content-Type': 'text/plain'
143                    }
144                });
145            }
146            return Promise.reject('no-match');
147        });
148    });
149}
150