mirror of
https://github.com/kiwix/kiwix-js-pwa.git
synced 2025-09-09 04:06:27 -04:00
Enable SW caching of assets
Former-commit-id: 7ad9905ccabf584b6a7b421b7f841ca8782cc4e8 [formerly 229d62536b540780695ef101c7b31393c0331189] Former-commit-id: cd9fdc887da36bf79285373e096b34a5bd28bebc
This commit is contained in:
parent
0291c86de1
commit
f8d3c09c30
215
pwabuilder-sw.js
215
pwabuilder-sw.js
@ -93,117 +93,120 @@ const regexpZIMUrlWithNamespace = /(?:^|\/)([^\/]+\/)([-ABIJMUVWX])\/(.+)/;
|
||||
self.addEventListener("fetch", function (event) {
|
||||
console.log('[SW] Service Worker ' + (event.request.method === "GET" ? 'intercepted ' : 'noted ') + event.request.url, event.request.method);
|
||||
if (event.request.method !== "GET") return;
|
||||
if (/\.zim\w{0,2}\//i.test(event.request.url) && regexpZIMUrlWithNamespace.test(event.request.url)) {
|
||||
// If the user has disabled the display of images, and the browser wants an image, respond with empty SVG
|
||||
// A URL with "?kiwix-display" query string acts as a passthrough so that the regex will not match and
|
||||
// the image will be fetched by app.js
|
||||
// DEV: If you need to hide more image types, add them to regex below and also edit equivalent regex in app.js
|
||||
if (imageDisplay !== 'all' && /(^|\/)[IJ]\/.*\.(jpe?g|png|svg|gif)($|[?#])(?!kiwix-display)/i.test(event.request.url)) {
|
||||
var svgResponse;
|
||||
if (imageDisplay === 'manual')
|
||||
svgResponse = "<svg xmlns='http://www.w3.org/2000/svg' width='1' height='1'><rect width='1' height='1' style='fill:lightblue'/></svg>";
|
||||
else
|
||||
svgResponse = "<svg xmlns='http://www.w3.org/2000/svg'/>";
|
||||
event.respondWith(new Response(svgResponse, {
|
||||
headers: {
|
||||
'Content-Type': 'image/svg+xml'
|
||||
}
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
// Let's ask app.js for that content
|
||||
event.respondWith(new Promise(function (resolve, reject) {
|
||||
var nameSpace;
|
||||
var title;
|
||||
var titleWithNameSpace;
|
||||
var regexpResult = regexpZIMUrlWithNamespace.exec(event.request.url);
|
||||
var prefix = regexpResult[1];
|
||||
nameSpace = regexpResult[2];
|
||||
title = regexpResult[3];
|
||||
event.respondWith(
|
||||
fromCache(event.request).then(
|
||||
function (response) {
|
||||
// The response was found in the cache so we respond with it and update the entry
|
||||
|
||||
// We need to remove the potential parameters in the URL
|
||||
title = removeUrlParameters(decodeURIComponent(title));
|
||||
// This is where we call the server to get the newest version of the
|
||||
// file to use the next time we show view
|
||||
// event.waitUntil(
|
||||
// fetch(event.request).then(function (response) {
|
||||
// console.log('[SW] Refreshing CACHE from server...');
|
||||
// return updateCache(event.request, response);
|
||||
// })
|
||||
// );
|
||||
|
||||
titleWithNameSpace = nameSpace + '/' + title;
|
||||
|
||||
// Let's instantiate a new messageChannel, to allow app.js to give us the content
|
||||
var messageChannel = new MessageChannel();
|
||||
messageChannel.port1.onmessage = function (event) {
|
||||
if (event.data.action === 'giveContent') {
|
||||
// Content received from app.js
|
||||
var contentLength = event.data.content ? event.data.content.byteLength : null;
|
||||
var contentType = event.data.mimetype;
|
||||
// Set the imageDisplay variable if it has been sent in the event data
|
||||
imageDisplay = typeof event.data.imageDisplay !== 'undefined' ?
|
||||
event.data.imageDisplay : imageDisplay;
|
||||
var headers = new Headers();
|
||||
if (contentLength) headers.set('Content-Length', contentLength);
|
||||
if (contentType) headers.set('Content-Type', contentType);
|
||||
// Test if the content is a video or audio file
|
||||
// See kiwix-js #519 and openzim/zimwriterfs #113 for why we test for invalid types like "mp4" or "webm" (without "video/")
|
||||
// The full list of types produced by zimwriterfs is in https://github.com/openzim/zimwriterfs/blob/master/src/tools.cpp
|
||||
if (contentLength >= 1 && /^(video|audio)|(^|\/)(mp4|webm|og[gmv]|mpeg)$/i.test(contentType)) {
|
||||
// In case of a video (at least), Chrome and Edge need these HTTP headers else seeking doesn't work
|
||||
// (even if we always send all the video content, not the requested range, until the backend supports it)
|
||||
headers.set('Accept-Ranges', 'bytes');
|
||||
headers.set('Content-Range', 'bytes 0-' + (contentLength - 1) + '/' + contentLength);
|
||||
}
|
||||
var responseInit = {
|
||||
status: 200,
|
||||
statusText: 'OK',
|
||||
headers: headers
|
||||
};
|
||||
|
||||
var httpResponse = new Response(event.data.content, responseInit);
|
||||
|
||||
// Let's send the content back from the ServiceWorker
|
||||
resolve(httpResponse);
|
||||
} else if (event.data.action === 'sendRedirect') {
|
||||
resolve(Response.redirect(prefix + event.data.redirectUrl));
|
||||
} else {
|
||||
console.error('Invalid message received from app.js for ' + titleWithNameSpace, event.data);
|
||||
reject(event.data);
|
||||
}
|
||||
};
|
||||
outgoingMessagePort.postMessage({
|
||||
'action': 'askForContent',
|
||||
'title': titleWithNameSpace
|
||||
}, [messageChannel.port2]);
|
||||
}));
|
||||
} else {
|
||||
event.respondWith(
|
||||
fromCache(event.request).then(
|
||||
function (response) {
|
||||
// The response was found in the cache so we responde with it and update the entry
|
||||
|
||||
// This is where we call the server to get the newest version of the
|
||||
// file to use the next time we show view
|
||||
event.waitUntil(
|
||||
fetch(event.request).then(function (response) {
|
||||
console.log('[SW] Refreshing CACHE from server...');
|
||||
return updateCache(event.request, response);
|
||||
})
|
||||
);
|
||||
console.log('[SW] Supplying ' + event.request.url + ' from CACHE...');
|
||||
return response;
|
||||
},
|
||||
function () {
|
||||
// The response was not found in the cache so we look for it on the server
|
||||
return fetch(event.request)
|
||||
.then(function (response) {
|
||||
// If request was success, add or update it in the cache
|
||||
event.waitUntil(updateCache(event.request, response.clone()));
|
||||
|
||||
return response;
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log("[SW] Network request failed and no cache.", error);
|
||||
console.log('[SW] Supplying ' + event.request.url + ' from CACHE...');
|
||||
return response;
|
||||
},
|
||||
function () {
|
||||
// The response was not found in the cache so we look for it on the server
|
||||
if (/\.zim\w{0,2}\//i.test(event.request.url) && regexpZIMUrlWithNamespace.test(event.request.url)) {
|
||||
// If the user has disabled the display of images, and the browser wants an image, respond with empty SVG
|
||||
// A URL with "?kiwix-display" query string acts as a passthrough so that the regex will not match and
|
||||
// the image will be fetched by app.js
|
||||
// DEV: If you need to hide more image types, add them to regex below and also edit equivalent regex in app.js
|
||||
if (imageDisplay !== 'all' && /(^|\/)[IJ]\/.*\.(jpe?g|png|svg|gif)($|[?#])(?!kiwix-display)/i.test(event.request.url)) {
|
||||
var svgResponse;
|
||||
if (imageDisplay === 'manual')
|
||||
svgResponse = "<svg xmlns='http://www.w3.org/2000/svg' width='1' height='1'><rect width='1' height='1' style='fill:lightblue'/></svg>";
|
||||
else
|
||||
svgResponse = "<svg xmlns='http://www.w3.org/2000/svg'/>";
|
||||
return new Response(svgResponse, {
|
||||
headers: {
|
||||
'Content-Type': 'image/svg+xml'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Let's ask app.js for that content
|
||||
return new Promise(function (resolve, reject) {
|
||||
var nameSpace;
|
||||
var title;
|
||||
var titleWithNameSpace;
|
||||
var regexpResult = regexpZIMUrlWithNamespace.exec(event.request.url);
|
||||
var prefix = regexpResult[1];
|
||||
nameSpace = regexpResult[2];
|
||||
title = regexpResult[3];
|
||||
|
||||
// We need to remove the potential parameters in the URL
|
||||
title = removeUrlParameters(decodeURIComponent(title));
|
||||
|
||||
titleWithNameSpace = nameSpace + '/' + title;
|
||||
|
||||
// Let's instantiate a new messageChannel, to allow app.js to give us the content
|
||||
var messageChannel = new MessageChannel();
|
||||
messageChannel.port1.onmessage = function (msgEvent) {
|
||||
if (msgEvent.data.action === 'giveContent') {
|
||||
// Content received from app.js
|
||||
var contentLength = msgEvent.data.content ? msgEvent.data.content.byteLength : null;
|
||||
var contentType = msgEvent.data.mimetype;
|
||||
// Set the imageDisplay variable if it has been sent in the event data
|
||||
imageDisplay = typeof msgEvent.data.imageDisplay !== 'undefined' ?
|
||||
msgEvent.data.imageDisplay : imageDisplay;
|
||||
var headers = new Headers();
|
||||
if (contentLength) headers.set('Content-Length', contentLength);
|
||||
if (contentType) headers.set('Content-Type', contentType);
|
||||
// Test if the content is a video or audio file
|
||||
// See kiwix-js #519 and openzim/zimwriterfs #113 for why we test for invalid types like "mp4" or "webm" (without "video/")
|
||||
// The full list of types produced by zimwriterfs is in https://github.com/openzim/zimwriterfs/blob/master/src/tools.cpp
|
||||
if (contentLength >= 1 && /^(video|audio)|(^|\/)(mp4|webm|og[gmv]|mpeg)$/i.test(contentType)) {
|
||||
// In case of a video (at least), Chrome and Edge need these HTTP headers else seeking doesn't work
|
||||
// (even if we always send all the video content, not the requested range, until the backend supports it)
|
||||
headers.set('Accept-Ranges', 'bytes');
|
||||
headers.set('Content-Range', 'bytes 0-' + (contentLength - 1) + '/' + contentLength);
|
||||
}
|
||||
var responseInit = {
|
||||
status: 200,
|
||||
statusText: 'OK',
|
||||
headers: headers
|
||||
};
|
||||
|
||||
var httpResponse = new Response(msgEvent.data.content, responseInit);
|
||||
|
||||
// Add or update css or javascript assets to the cache
|
||||
if (/(text|application)\/(css|javascript)/i.test(contentType)) {
|
||||
updateCache(event.request, httpResponse.clone());
|
||||
}
|
||||
|
||||
// Let's send the content back from the ServiceWorker
|
||||
resolve(httpResponse);
|
||||
} else if (msgEvent.data.action === 'sendRedirect') {
|
||||
resolve(Response.redirect(prefix + msgEvent.data.redirectUrl));
|
||||
} else {
|
||||
console.error('Invalid message received from app.js for ' + titleWithNameSpace, msgEvent.data);
|
||||
reject(msgEvent.data);
|
||||
}
|
||||
};
|
||||
outgoingMessagePort.postMessage({
|
||||
'action': 'askForContent',
|
||||
'title': titleWithNameSpace
|
||||
}, [messageChannel.port2]);
|
||||
});
|
||||
} else {
|
||||
return fetch(event.request).then(function (response) {
|
||||
// If request was success, add or update it in the cache
|
||||
event.waitUntil(updateCache(event.request, response.clone()));
|
||||
return response;
|
||||
}).catch(function (error) {
|
||||
console.log("[SW] Network request failed and no cache.", error);
|
||||
});
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
function fromCache(request) {
|
||||
|
@ -1221,6 +1221,7 @@ define(['jquery', 'zimArchiveLoader', 'uiUtil', 'util', 'utf8', 'images', 'cooki
|
||||
}
|
||||
|
||||
var keepAliveServiceWorkerHandle;
|
||||
var serviceWorkerRegistration = null;
|
||||
|
||||
/**
|
||||
* Send an 'init' message to the ServiceWorker with a new MessageChannel
|
||||
@ -1351,8 +1352,6 @@ define(['jquery', 'zimArchiveLoader', 'uiUtil', 'util', 'utf8', 'images', 'cooki
|
||||
setContentInjectionMode('jquery');
|
||||
}
|
||||
|
||||
var serviceWorkerRegistration = null;
|
||||
|
||||
/**
|
||||
* Tells if the ServiceWorker API is available
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker
|
||||
|
Loading…
x
Reference in New Issue
Block a user