File size: 5,111 Bytes
1e40c2a |
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 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
'use strict';
const OFFLINE_DATA_FILE = 'offline.json',
CACHE_NAME_PREFIX = 'c3offline',
BROADCASTCHANNEL_NAME = 'offline',
CONSOLE_PREFIX = '[SW] ',
LAZYLOAD_KEYNAME = '',
broadcastChannel =
'undefined' == typeof BroadcastChannel ? null : new BroadcastChannel('offline');
function PostBroadcastMessage(a) {
broadcastChannel && setTimeout(() => broadcastChannel.postMessage(a), 3e3);
}
function Broadcast(a) {
PostBroadcastMessage({ type: a });
}
function BroadcastDownloadingUpdate(a) {
PostBroadcastMessage({ type: 'downloading-update', version: a });
}
function BroadcastUpdateReady(a) {
PostBroadcastMessage({ type: 'update-ready', version: a });
}
function IsUrlInLazyLoadList(a, b) {
if (!b) return !1;
try {
for (const c of b) if (new RegExp(c).test(a)) return !0;
} catch (a) {
console.error('[SW] Error matching in lazy-load list: ', a);
}
return !1;
}
function WriteLazyLoadListToStorage(a) {
return 'undefined' == typeof localforage
? Promise.resolve()
: localforage.setItem(LAZYLOAD_KEYNAME, a);
}
function ReadLazyLoadListFromStorage() {
return 'undefined' == typeof localforage
? Promise.resolve([])
: localforage.getItem(LAZYLOAD_KEYNAME);
}
function GetCacheBaseName() {
return 'c3offline-' + self.registration.scope;
}
function GetCacheVersionName(a) {
return GetCacheBaseName() + '-v' + a;
}
async function GetAvailableCacheNames() {
const a = await caches.keys(),
b = GetCacheBaseName();
return a.filter((a) => a.startsWith(b));
}
async function IsUpdatePending() {
const a = await GetAvailableCacheNames();
return 2 <= a.length;
}
async function GetMainPageUrl() {
const a = await clients.matchAll({ includeUncontrolled: !0, type: 'window' });
for (const b of a) {
let a = b.url;
if (
(a.startsWith(self.registration.scope) && (a = a.substring(self.registration.scope.length)),
a && '/' !== a)
)
return a.startsWith('?') && (a = '/' + a), a;
}
return '';
}
function fetchWithBypass(a, b) {
return (
'string' == typeof a && (a = new Request(a)),
b
? fetch(a.url, {
headers: a.headers,
mode: a.mode,
credentials: a.credentials,
redirect: a.redirect,
cache: 'no-store'
})
: fetch(a)
);
}
async function CreateCacheFromFileList(a, b, c) {
const d = await Promise.all(b.map((a) => fetchWithBypass(a, c)));
let e = !0;
for (const f of d)
f.ok ||
((e = !1),
console.error("[SW] Error fetching '" + f.url + "' (" + f.status + ' ' + f.statusText + ')'));
if (!e) throw new Error('not all resources were fetched successfully');
const f = await caches.open(a);
try {
return await Promise.all(d.map((a, c) => f.put(b[c], a)));
} catch (b) {
throw (console.error('[SW] Error writing cache entries: ', b), caches.delete(a), b);
}
}
async function UpdateCheck(a) {
try {
const b = await fetchWithBypass(OFFLINE_DATA_FILE, !0);
if (!b.ok) throw new Error('offline.json responded with ' + b.status + ' ' + b.statusText);
const c = await b.json(),
d = c.version,
e = c.fileList,
f = c.lazyLoad,
g = GetCacheVersionName(d),
h = await caches.has(g);
if (h) {
const a = await IsUpdatePending();
return void (a
? (console.log('[SW] Update pending'), Broadcast('update-pending'))
: (console.log('[SW] Up to date'), Broadcast('up-to-date')));
}
const i = await GetMainPageUrl();
e.unshift('./'),
i && -1 === e.indexOf(i) && e.unshift(i),
console.log('[SW] Caching ' + e.length + ' files for offline use'),
a ? Broadcast('downloading') : BroadcastDownloadingUpdate(d),
f && (await WriteLazyLoadListToStorage(f)),
await CreateCacheFromFileList(g, e, !a);
const j = await IsUpdatePending();
j
? (console.log('[SW] All resources saved, update ready'), BroadcastUpdateReady(d))
: (console.log('[SW] All resources saved, offline support ready'),
Broadcast('offline-ready'));
} catch (a) {
console.warn('[SW] Update check failed: ', a);
}
}
self.addEventListener('install', (a) => {
a.waitUntil(UpdateCheck(!0).catch(() => null));
});
async function GetCacheNameToUse(a, b) {
if (1 === a.length || !b) return a[0];
const c = await clients.matchAll();
if (1 < c.length) return a[0];
const d = a[a.length - 1];
return (
console.log('[SW] Updating to new version'),
await Promise.all(a.slice(0, -1).map((a) => caches.delete(a))),
d
);
}
async function HandleFetch(a, b) {
const c = await GetAvailableCacheNames();
if (!c.length) return fetch(a.request);
const d = await GetCacheNameToUse(c, b),
e = await caches.open(d),
f = await e.match(a.request);
if (f) return f;
const g = await Promise.all([fetch(a.request), ReadLazyLoadListFromStorage()]),
h = g[0],
i = g[1];
if (IsUrlInLazyLoadList(a.request.url, i))
try {
await e.put(a.request, h.clone());
} catch (b) {
console.warn("[SW] Error caching '" + a.request.url + "': ", b);
}
return h;
}
self.addEventListener('fetch', (a) => {
if (new URL(a.request.url).origin === location.origin) {
const b = 'navigate' === a.request.mode,
c = HandleFetch(a, b);
b && a.waitUntil(c.then(() => UpdateCheck(!1))), a.respondWith(c);
}
});
|