mirror of
https://github.com/kiwix/kiwix-js.git
synced 2025-09-22 12:01:15 -04:00
Improve the way we start the ServiceWorker, and allow the user to choose between jQuery and ServiceWorker modes.
If the user chooses the jQuery mode, we have to disable the ServiceWorker. It's a first step for #136
This commit is contained in:
parent
93cc3dcdfd
commit
f9ccc51145
@ -81,6 +81,15 @@ function(util, utf8) {
|
|||||||
console.log('Init message received', event.data);
|
console.log('Init message received', event.data);
|
||||||
outgoingMessagePort = event.ports[0];
|
outgoingMessagePort = event.ports[0];
|
||||||
console.log('outgoingMessagePort initialized', outgoingMessagePort);
|
console.log('outgoingMessagePort initialized', outgoingMessagePort);
|
||||||
|
self.addEventListener('fetch', fetchEventListener);
|
||||||
|
console.log('fetchEventListener enabled');
|
||||||
|
}
|
||||||
|
if (event.data.action === 'disable') {
|
||||||
|
console.log('Disable message received');
|
||||||
|
outgoingMessagePort = null;
|
||||||
|
console.log('outgoingMessagePort deleted');
|
||||||
|
self.removeEventListener('fetch', fetchEventListener);
|
||||||
|
console.log('fetchEventListener removed');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -93,13 +102,13 @@ function(util, utf8) {
|
|||||||
|
|
||||||
var regexpContentUrl = new RegExp(/\/(.)\/(.*[^\/]+)$/);
|
var regexpContentUrl = new RegExp(/\/(.)\/(.*[^\/]+)$/);
|
||||||
var regexpDummyArticle = new RegExp(/dummyArticle\.html$/);
|
var regexpDummyArticle = new RegExp(/dummyArticle\.html$/);
|
||||||
|
|
||||||
self.addEventListener('fetch', function(event) {
|
function fetchEventListener(event) {
|
||||||
console.log('ServiceWorker handling fetch event for : ' + event.request.url);
|
console.log('ServiceWorker handling fetch event for : ' + event.request.url);
|
||||||
|
|
||||||
// TODO handle the dummy article more properly
|
// TODO handle the dummy article more properly
|
||||||
if (regexpContentUrl.test(event.request.url) && !regexpDummyArticle.test(event.request.url)) {
|
if (regexpContentUrl.test(event.request.url) && !regexpDummyArticle.test(event.request.url)) {
|
||||||
|
|
||||||
console.log('Asking app.js for a content', event.request.url);
|
console.log('Asking app.js for a content', event.request.url);
|
||||||
event.respondWith(new Promise(function(resolve, reject) {
|
event.respondWith(new Promise(function(resolve, reject) {
|
||||||
var regexpResult = regexpContentUrl.exec(event.request.url);
|
var regexpResult = regexpContentUrl.exec(event.request.url);
|
||||||
@ -163,6 +172,5 @@ function(util, utf8) {
|
|||||||
}
|
}
|
||||||
// If event.respondWith() isn't called because this wasn't a request that we want to handle,
|
// If event.respondWith() isn't called because this wasn't a request that we want to handle,
|
||||||
// then the default request/response behavior will automatically be used.
|
// then the default request/response behavior will automatically be used.
|
||||||
});
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -207,6 +207,12 @@
|
|||||||
<br /> Please select the archive you want to use : <select id="archiveList" class="form-control"></select>
|
<br /> Please select the archive you want to use : <select id="archiveList" class="form-control"></select>
|
||||||
<br /> Click <a id="btnRescanDeviceStorage">here</a> to rescan your SD Cards and internal memory
|
<br /> Click <a id="btnRescanDeviceStorage">here</a> to rescan your SD Cards and internal memory
|
||||||
</div>
|
</div>
|
||||||
|
<div id="contentInjectionModeDiv">
|
||||||
|
Content injection mode : <br/>
|
||||||
|
<input type="radio" name="contentInjectionMode" value="jquery" id="jQueryModeRadio" checked><label for="jQueryModeRadio">JQuery</label>
|
||||||
|
<br>
|
||||||
|
<input type="radio" name="contentInjectionMode" value="serviceworker" id="serviceworkerModeRadio"><label for="serviceworkerModeRadio">ServiceWorker</label>
|
||||||
|
</div>
|
||||||
<div id="serviceWorkerStatus"></div>
|
<div id="serviceWorkerStatus"></div>
|
||||||
<div id="messageChannelStatus"></div>
|
<div id="messageChannelStatus"></div>
|
||||||
<br />
|
<br />
|
||||||
|
236
www/js/app.js
236
www/js/app.js
@ -227,24 +227,7 @@ define(['jquery', 'abstractBackend', 'util', 'cookies','geometry','osabstraction
|
|||||||
$('#geolocationProgress').hide();
|
$('#geolocationProgress').hide();
|
||||||
$('#articleContent').hide();
|
$('#articleContent').hide();
|
||||||
$('#searchingForTitles').hide();
|
$('#searchingForTitles').hide();
|
||||||
// Refresh the ServiceWorker status (in case it has been unregistered)
|
refreshAPIStatus();
|
||||||
if (isServiceWorkerAvailable()) {
|
|
||||||
if (isServiceWorkerReady()) {
|
|
||||||
$('#serviceWorkerStatus').html("ServiceWorker API available, and registered");
|
|
||||||
$('#serviceWorkerStatus').removeClass("apiAvailable apiUnavailable")
|
|
||||||
.addClass("apiAvailable");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$('#serviceWorkerStatus').html("ServiceWorker API available, but not registered");
|
|
||||||
$('#serviceWorkerStatus').removeClass("apiAvailable apiUnavailable")
|
|
||||||
.addClass("apiUnavailable");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$('#serviceWorkerStatus').html("ServiceWorker API unavailable");
|
|
||||||
$('#serviceWorkerStatus').removeClass("apiAvailable apiUnavailable")
|
|
||||||
.addClass("apiUnavailable");
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
$('#btnAbout').on('click', function(e) {
|
$('#btnAbout').on('click', function(e) {
|
||||||
@ -271,6 +254,140 @@ define(['jquery', 'abstractBackend', 'util', 'cookies','geometry','osabstraction
|
|||||||
$('#searchingForTitles').hide();
|
$('#searchingForTitles').hide();
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
$('input:radio[name=contentInjectionMode]').on('change', function(e) {
|
||||||
|
// Do the necessary to enable or disable the Service Worker
|
||||||
|
setContentInjectionMode(this.value);
|
||||||
|
checkSelectedArchiveCompatibilityWithInjectionMode();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays of refreshes the API status shown to the user
|
||||||
|
*/
|
||||||
|
function refreshAPIStatus() {
|
||||||
|
if (isMessageChannelAvailable()) {
|
||||||
|
$('#messageChannelStatus').html("MessageChannel API available");
|
||||||
|
$('#messageChannelStatus').removeClass("apiAvailable apiUnavailable")
|
||||||
|
.addClass("apiAvailable");
|
||||||
|
} else {
|
||||||
|
$('#messageChannelStatus').html("MessageChannel API unavailable");
|
||||||
|
$('#messageChannelStatus').removeClass("apiAvailable apiUnavailable")
|
||||||
|
.addClass("apiUnavailable");
|
||||||
|
}
|
||||||
|
if (isServiceWorkerAvailable()) {
|
||||||
|
if (isServiceWorkerReady()) {
|
||||||
|
$('#serviceWorkerStatus').html("ServiceWorker API available, and registered");
|
||||||
|
$('#serviceWorkerStatus').removeClass("apiAvailable apiUnavailable")
|
||||||
|
.addClass("apiAvailable");
|
||||||
|
} else {
|
||||||
|
$('#serviceWorkerStatus').html("ServiceWorker API available, but not registered");
|
||||||
|
$('#serviceWorkerStatus').removeClass("apiAvailable apiUnavailable")
|
||||||
|
.addClass("apiUnavailable");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$('#serviceWorkerStatus').html("ServiceWorker API unavailable");
|
||||||
|
$('#serviceWorkerStatus').removeClass("apiAvailable apiUnavailable")
|
||||||
|
.addClass("apiUnavailable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var contentInjectionMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the given injection mode.
|
||||||
|
* This involves registering (or re-enabling) the Service Worker if necessary
|
||||||
|
* It also refreshes the API status for the user afterwards.
|
||||||
|
*
|
||||||
|
* @param {String} value The chosen content injection mode : 'jquery' or 'serviceworker'
|
||||||
|
*/
|
||||||
|
function setContentInjectionMode(value) {
|
||||||
|
if (value === 'jquery') {
|
||||||
|
if (isServiceWorkerReady()) {
|
||||||
|
// We need to disable the ServiceWorker
|
||||||
|
// Unregistering it does not seem to work as expected : the ServiceWorker
|
||||||
|
// is indeed unregistered but still active...
|
||||||
|
// So we have to disable it manually (even if it's still registered and active)
|
||||||
|
navigator.serviceWorker.controller.postMessage({'action': 'disable'});
|
||||||
|
messageChannel = null;
|
||||||
|
}
|
||||||
|
refreshAPIStatus();
|
||||||
|
} else if (value === 'serviceworker') {
|
||||||
|
if (!isServiceWorkerAvailable()) {
|
||||||
|
alert("The ServiceWorker API is not available on your device. Falling back to JQuery mode");
|
||||||
|
setContentInjectionMode('jquery');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isMessageChannelAvailable()) {
|
||||||
|
alert("The MessageChannel API is not available on your device. Falling back to JQuery mode");
|
||||||
|
setContentInjectionMode('jquery');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!messageChannel) {
|
||||||
|
// Let's create the messageChannel for the 2-way communication
|
||||||
|
// with the Service Worker
|
||||||
|
messageChannel = new MessageChannel();
|
||||||
|
messageChannel.port1.onmessage = handleMessageChannelMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isServiceWorkerReady()) {
|
||||||
|
$('#serviceWorkerStatus').html("ServiceWorker API available : trying to register it...");
|
||||||
|
navigator.serviceWorker.register('../service-worker.js').then(function (reg) {
|
||||||
|
console.log('serviceWorker registered', reg);
|
||||||
|
serviceWorkerRegistration = reg;
|
||||||
|
refreshAPIStatus();
|
||||||
|
|
||||||
|
// We need to wait for the ServiceWorker to be activated
|
||||||
|
// before sending the first init message
|
||||||
|
var serviceWorker;
|
||||||
|
if (reg.installing) {
|
||||||
|
serviceWorker = reg.installing;
|
||||||
|
} else if (reg.waiting) {
|
||||||
|
serviceWorker = reg.waiting;
|
||||||
|
} else if (reg.active) {
|
||||||
|
serviceWorker = reg.active;
|
||||||
|
}
|
||||||
|
serviceWorker.addEventListener('statechange', function(statechangeevent) {
|
||||||
|
if (statechangeevent.target.state === 'activated') {
|
||||||
|
console.log("try to post an init message to ServiceWorker");
|
||||||
|
navigator.serviceWorker.controller.postMessage({'action': 'init'}, [messageChannel.port2]);
|
||||||
|
console.log("init message sent to ServiceWorker");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, function (err) {
|
||||||
|
console.error('error while registering serviceWorker', err);
|
||||||
|
refreshAPIStatus();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log("try to re-post an init message to ServiceWorker, to re-enable it in case it was disabled");
|
||||||
|
navigator.serviceWorker.controller.postMessage({'action': 'init'}, [messageChannel.port2]);
|
||||||
|
console.log("init message sent to ServiceWorker");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$('input:radio[name=contentInjectionMode]').filter('[value="' + value + '"]').attr('checked', true);
|
||||||
|
contentInjectionMode = value;
|
||||||
|
// Save the value in a cookie, so that to be able to keep it after a reload/restart
|
||||||
|
cookies.setItem('lastContentInjectionMode', value, Infinity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the archive selected by the user is compatible
|
||||||
|
* with the injection mode, and warn the user if it's not
|
||||||
|
* @returns {Boolean} true if they're compatible
|
||||||
|
*/
|
||||||
|
function checkSelectedArchiveCompatibilityWithInjectionMode() {
|
||||||
|
if (selectedArchive.needsWikimediaCSS() && contentInjectionMode === 'serviceworker') {
|
||||||
|
alert('You seem to want to use ServiceWorker mode for an Evopedia archive : this is not supported. Please use the JQuery mode or use a ZIM file');
|
||||||
|
$("#btnConfigure").click();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// At launch, we try to set the last content injection mode (stored in a cookie)
|
||||||
|
var lastContentInjectionMode = cookies.getItem('lastContentInjectionMode');
|
||||||
|
if (lastContentInjectionMode) {
|
||||||
|
setContentInjectionMode(lastContentInjectionMode);
|
||||||
|
}
|
||||||
|
|
||||||
var serviceWorkerRegistration = null;
|
var serviceWorkerRegistration = null;
|
||||||
|
|
||||||
@ -305,42 +422,10 @@ define(['jquery', 'abstractBackend', 'util', 'cookies','geometry','osabstraction
|
|||||||
* @returns {Boolean}
|
* @returns {Boolean}
|
||||||
*/
|
*/
|
||||||
function isServiceWorkerReady() {
|
function isServiceWorkerReady() {
|
||||||
return (serviceWorkerRegistration !== null);
|
// Return true if the serviceWorkerRegistration is not null and not undefined
|
||||||
|
return (serviceWorkerRegistration);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isServiceWorkerAvailable()) {
|
|
||||||
$('#serviceWorkerStatus').html("ServiceWorker API available : trying to register it...");
|
|
||||||
navigator.serviceWorker.register('../service-worker.js').then(function(reg) {
|
|
||||||
console.log('serviceWorker registered', reg);
|
|
||||||
serviceWorkerRegistration = reg;
|
|
||||||
$('#serviceWorkerStatus').html("ServiceWorker API available, and registered");
|
|
||||||
$('#serviceWorkerStatus').removeClass("apiAvailable apiUnavailable")
|
|
||||||
.addClass("apiAvailable");
|
|
||||||
}, function(err) {
|
|
||||||
console.error('error while registering serviceWorker', err);
|
|
||||||
$('#serviceWorkerStatus').html("ServiceWorker API available, but unable to register : " + err);
|
|
||||||
$('#serviceWorkerStatus').removeClass("apiAvailable apiUnavailable")
|
|
||||||
.addClass("apiUnavailable");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.log("serviceWorker API not available");
|
|
||||||
$('#serviceWorkerStatus').html("ServiceWorker API unavailable");
|
|
||||||
$('#serviceWorkerStatus').removeClass("apiAvailable apiUnavailable")
|
|
||||||
.addClass("apiUnavailable");
|
|
||||||
}
|
|
||||||
if (isMessageChannelAvailable()) {
|
|
||||||
$('#messageChannelStatus').html("MessageChannel API available");
|
|
||||||
$('#messageChannelStatus').removeClass("apiAvailable apiUnavailable")
|
|
||||||
.addClass("apiAvailable");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$('#messageChannelStatus').html("MessageChannel API unavailable");
|
|
||||||
$('#messageChannelStatus').removeClass("apiAvailable apiUnavailable")
|
|
||||||
.addClass("apiUnavailable");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect if DeviceStorage is available
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type Array.<StorageFirefoxOS|StoragePhoneGap>
|
* @type Array.<StorageFirefoxOS|StoragePhoneGap>
|
||||||
@ -532,8 +617,10 @@ define(['jquery', 'abstractBackend', 'util', 'cookies','geometry','osabstraction
|
|||||||
}
|
}
|
||||||
selectedArchive = backend.loadArchiveFromDeviceStorage(selectedStorage, archiveDirectory);
|
selectedArchive = backend.loadArchiveFromDeviceStorage(selectedStorage, archiveDirectory);
|
||||||
cookies.setItem("lastSelectedArchive", archiveDirectory, Infinity);
|
cookies.setItem("lastSelectedArchive", archiveDirectory, Infinity);
|
||||||
// The archive is set : go back to home page to start searching
|
if (checkSelectedArchiveCompatibilityWithInjectionMode()) {
|
||||||
$("#btnHome").click();
|
// The archive is set : go back to home page to start searching
|
||||||
|
$("#btnHome").click();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -550,8 +637,10 @@ define(['jquery', 'abstractBackend', 'util', 'cookies','geometry','osabstraction
|
|||||||
*/
|
*/
|
||||||
function setLocalArchiveFromFileSelect() {
|
function setLocalArchiveFromFileSelect() {
|
||||||
selectedArchive = backend.loadArchiveFromFiles(document.getElementById('archiveFiles').files);
|
selectedArchive = backend.loadArchiveFromFiles(document.getElementById('archiveFiles').files);
|
||||||
// The archive is set : go back to home page to start searching
|
if (checkSelectedArchiveCompatibilityWithInjectionMode()) {
|
||||||
$("#btnHome").click();
|
// The archive is set : go back to home page to start searching
|
||||||
|
$("#btnHome").click();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -745,13 +834,7 @@ define(['jquery', 'abstractBackend', 'util', 'cookies','geometry','osabstraction
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMessageChannelAvailable()) {
|
var messageChannel;
|
||||||
// Let's instanciate the messageChannel where the ServiceWorker can ask for contents
|
|
||||||
// NB : note that we use the var keyword here (and not let),
|
|
||||||
// so that the scope of the variable is the whole file
|
|
||||||
var messageChannel = new MessageChannel();
|
|
||||||
messageChannel.port1.onmessage = handleMessageChannelMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function that handles a message of the messageChannel.
|
* Function that handles a message of the messageChannel.
|
||||||
@ -811,25 +894,6 @@ define(['jquery', 'abstractBackend', 'util', 'cookies','geometry','osabstraction
|
|||||||
$("#articleContent").show();
|
$("#articleContent").show();
|
||||||
// Scroll the iframe to its top
|
// Scroll the iframe to its top
|
||||||
$("#articleContent").contents().scrollTop(0);
|
$("#articleContent").contents().scrollTop(0);
|
||||||
|
|
||||||
if (isServiceWorkerReady()) {
|
|
||||||
// TODO : We do not use Service Workers on Evopedia archives, for now
|
|
||||||
// Maybe it would be worth trying to enable them in the future?
|
|
||||||
if (selectedArchive.needsWikimediaCSS()) {
|
|
||||||
// Let's unregister the ServiceWorker
|
|
||||||
serviceWorkerRegistration.unregister().then(function() {serviceWorkerRegistration = null;});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// TODO : for testing : this initialization should be done earlier,
|
|
||||||
// as soon as the ServiceWorker is ready.
|
|
||||||
// This can probably been done by listening to state change :
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker/onstatechange
|
|
||||||
console.log("try to post an init message to ServiceWorker");
|
|
||||||
console.log("messageChannel :", messageChannel);
|
|
||||||
navigator.serviceWorker.controller.postMessage({'action': 'init'}, [messageChannel.port2]);
|
|
||||||
console.log("init message sent to ServiceWorker");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply Mediawiki CSS only when it's an Evopedia archive
|
// Apply Mediawiki CSS only when it's an Evopedia archive
|
||||||
if (selectedArchive.needsWikimediaCSS() === true) {
|
if (selectedArchive.needsWikimediaCSS() === true) {
|
||||||
@ -838,17 +902,13 @@ define(['jquery', 'abstractBackend', 'util', 'cookies','geometry','osabstraction
|
|||||||
var currentPath = regexpPath.exec(currentHref)[1];
|
var currentPath = regexpPath.exec(currentHref)[1];
|
||||||
$('#articleContent').contents().find('head').append("<link rel='stylesheet' type='text/css' href='" + currentPath + "css/mediawiki-main.css' id='mediawiki-stylesheet' />");
|
$('#articleContent').contents().find('head').append("<link rel='stylesheet' type='text/css' href='" + currentPath + "css/mediawiki-main.css' id='mediawiki-stylesheet' />");
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// TODO temporary test to inject CSS inside the iframe
|
|
||||||
$('#articleContent').contents().find('head').empty();
|
|
||||||
$('#articleContent').contents().find('head').append("<link rel='stylesheet' href='data:text/css;charset=UTF-8," + encodeURIComponent("body {background: #E9E9E9;}") + "' />");
|
|
||||||
}
|
|
||||||
// Display the article inside the web page.
|
// Display the article inside the web page.
|
||||||
$('#articleContent').contents().find('body').html(htmlArticle);
|
$('#articleContent').contents().find('body').html(htmlArticle);
|
||||||
|
|
||||||
// If the ServiceWorker is not useable, we need to fallback to parse the DOM
|
// If the ServiceWorker is not useable, we need to fallback to parse the DOM
|
||||||
// to inject math images, and replace some links with javascript calls
|
// to inject math images, and replace some links with javascript calls
|
||||||
if (selectedArchive.needsWikimediaCSS() || !isServiceWorkerReady() || !isMessageChannelAvailable()) {
|
if (contentInjectionMode === 'jquery') {
|
||||||
|
|
||||||
// Convert links into javascript calls
|
// Convert links into javascript calls
|
||||||
$('#articleContent').contents().find('body').find('a').each(function() {
|
$('#articleContent').contents().find('body').find('a').each(function() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user