mirror of
https://github.com/kiwix/kiwix-js-pwa.git
synced 2025-09-08 03:37:12 -04:00
Preload stylesheets
Implements kiwix-js #149 (Load images after CSS), except does this to raw HTML. Not tested with serviceworker. Former-commit-id: fd9559cfb555f2ace17c504092ff61efebafa055 [formerly 27a3a40c5e42650fb6c06d4bb981fe1c0f81a18f] Former-commit-id: 4f3ac1d9c4ea4c5acb53130dc31972fdb53cc3e3
This commit is contained in:
parent
db83e2adcb
commit
46b00ad6a5
308
www/js/app.js
308
www/js/app.js
@ -29,15 +29,6 @@
|
|||||||
define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFilesystemAccess'],
|
define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFilesystemAccess'],
|
||||||
function($, zimArchiveLoader, util, uiUtil, cookies, abstractFilesystemAccess) {
|
function($, zimArchiveLoader, util, uiUtil, cookies, abstractFilesystemAccess) {
|
||||||
|
|
||||||
/*/ Disable any eval() call in jQuery : it's disabled by CSP in any packaged application
|
|
||||||
// It happens on some wiktionary archives, because there is some javascript inside the html article
|
|
||||||
// Cf http://forum.jquery.com/topic/jquery-ajax-disable-script-eval
|
|
||||||
jQuery.globalEval = function (code) {
|
|
||||||
// jQuery believes the javascript has been executed, but we did nothing
|
|
||||||
// In any case, that would have been blocked by CSP for package applications
|
|
||||||
console.log("jQuery tried to run some javascript with eval(), which is not allowed in packaged applications");
|
|
||||||
}; */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum number of articles to display in a search
|
* Maximum number of articles to display in a search
|
||||||
* @type Integer
|
* @type Integer
|
||||||
@ -772,6 +763,9 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFiles
|
|||||||
// Since late 2014, all ZIM files should use relative URLs
|
// Since late 2014, all ZIM files should use relative URLs
|
||||||
var regexpImageUrl = /^(?:\.\.\/|\/)+(I\/.*)$/;
|
var regexpImageUrl = /^(?:\.\.\/|\/)+(I\/.*)$/;
|
||||||
var regexpMetadataUrl = /^(?:\.\.\/|\/)+(-\/.*)$/;
|
var regexpMetadataUrl = /^(?:\.\.\/|\/)+(-\/.*)$/;
|
||||||
|
// This regular expression matches the href of all <link> tags containing rel="stylesheet" in raw HTML
|
||||||
|
var regexpSheetHref = /(<link\s+(?=[^>]*rel\s*=\s*["']stylesheet)[^>]*href\s*=\s*["'])([^"']+)(["'][^>]*>)/ig;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display the the given HTML article in the web page,
|
* Display the the given HTML article in the web page,
|
||||||
@ -781,160 +775,178 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFiles
|
|||||||
* @param {String} htmlArticle
|
* @param {String} htmlArticle
|
||||||
*/
|
*/
|
||||||
function displayArticleInForm(dirEntry, htmlArticle) {
|
function displayArticleInForm(dirEntry, htmlArticle) {
|
||||||
$("#readingArticle").hide();
|
|
||||||
$("#articleContent").show();
|
|
||||||
// Scroll the iframe to its top
|
|
||||||
$("#articleContent").contents().scrollTop(0);
|
|
||||||
|
|
||||||
// Display the article inside the web page.
|
// Display the article inside the web page.
|
||||||
//Fast-replace img with data-img and hide image [kiwix-js #272]
|
|
||||||
htmlArticle = htmlArticle.replace(/(<img\s+[^>]*)src(\s*=)/ig,
|
//Fast-replace img src with data-kiwixsrc and hide image [kiwix-js #272]
|
||||||
|
htmlArticle = htmlArticle.replace(/(<img\s+[^>]*\b)src(\s*=)/ig,
|
||||||
"$1style=\"display: none;\" onload=\"this.style.display='inline'\" data-kiwixsrc$2");
|
"$1style=\"display: none;\" onload=\"this.style.display='inline'\" data-kiwixsrc$2");
|
||||||
|
|
||||||
$('#articleContent').contents().find('body').html(htmlArticle);
|
//Preload stylesheets [kiwix-js @149]
|
||||||
|
//Set up blobArray of promises
|
||||||
// If the ServiceWorker is not useable, we need to fallback to parse the DOM
|
var cssArray = htmlArticle.match(regexpSheetHref);
|
||||||
// to inject math images, and replace some links with javascript calls
|
var blobArray = [];
|
||||||
if (contentInjectionMode === 'jquery') {
|
getBLOB(cssArray);
|
||||||
|
|
||||||
// Convert links into javascript calls
|
//Extract CSS URLs from given array of links
|
||||||
$('#articleContent').contents().find('body').find('a').each(function() {
|
function getBLOB(arr) {
|
||||||
// Store current link's url
|
for (var i = 0; i < arr.length; i++) {
|
||||||
var url = $(this).attr("href");
|
var linkArray = regexpSheetHref.exec(arr[i]);
|
||||||
if (url === null || url === undefined) {
|
regexpSheetHref.lastIndex = 0; //Reset start position for next loop
|
||||||
return;
|
if (regexpMetadataUrl.test(linkArray[2])) { //It's a CSS file contained in ZIM
|
||||||
|
var linkURL = uiUtil.removeUrlParameters(decodeURIComponent(linkArray[2].match(regexpMetadataUrl)[1]));
|
||||||
|
console.log("Attempting to resolve CSS link #" + i + "...");
|
||||||
|
var linkBLOB = resolveCSS(linkURL, i); //Pass link and index
|
||||||
|
} else {
|
||||||
|
blobArray[i] = linkArray[2]; //If CSS not in ZIM, store URL in blobArray
|
||||||
|
injectCSS(); //Ensure this is called even if none of CSS links are in ZIM
|
||||||
}
|
}
|
||||||
var lowerCaseUrl = url.toLowerCase();
|
}
|
||||||
var cssClass = $(this).attr("class");
|
}
|
||||||
|
|
||||||
if (cssClass === "new") {
|
function resolveCSS(title, index) {
|
||||||
// It's a link to a missing article : display a message
|
selectedArchive.getDirEntryByTitle(title).then(
|
||||||
$(this).on('click', function(e) {
|
function (dirEntry) {
|
||||||
alert("Missing article in Wikipedia");
|
selectedArchive.readBinaryFile(dirEntry, function (readableTitle, content) {
|
||||||
return false;
|
//var cssContent = util.uintToString(content);
|
||||||
});
|
var cssBlob = new Blob([content], { type: 'text/css' });
|
||||||
|
var newURL = URL.createObjectURL(cssBlob);
|
||||||
|
//return URL.createObjectURL(cssBlob);
|
||||||
|
blobArray[index] = newURL;
|
||||||
|
injectCSS();
|
||||||
|
});
|
||||||
|
}).fail(function (e) {
|
||||||
|
console.error("could not find DirEntry for CSS : " + title, e);
|
||||||
|
blobArray[index] = "Error";
|
||||||
|
injectCSS();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function injectCSS() {
|
||||||
|
if (blobArray.length === cssArray.length) { //If all promised values have been obtained
|
||||||
|
for (var i in cssArray) {
|
||||||
|
cssArray[i] = cssArray[i].replace(/(href\s*=\s*["'])([^"']+)/ig, "$1" + blobArray[i]);
|
||||||
}
|
}
|
||||||
else if (url.slice(0, 1) === "#") {
|
htmlArticle = htmlArticle.replace(regexpSheetHref, ""); //Void existing stylesheets
|
||||||
// It's an anchor link : do nothing
|
var cssArray$ = "\r\n" + cssArray.join("\r\n") + "\r\n";
|
||||||
}
|
htmlArticle = htmlArticle.replace(/\s*(<\/head>)/i, cssArray$ + "$1");
|
||||||
else if (url.substring(0, 4) === "http") {
|
injectHTML(htmlArticle); //This passes the revised HTML to the image and JS subroutine...
|
||||||
// It's an external link : open in a new tab
|
} else {
|
||||||
$(this).attr("target", "_blank");
|
console.log("Waiting for " + (cssArray.length - blobArray.length) + " out of " + cssArray.length + " to resolve...")
|
||||||
}
|
}
|
||||||
else if (url.match(regexpImageLink)
|
}
|
||||||
&& (util.endsWith(lowerCaseUrl, ".png")
|
//End of preload stylesheets code
|
||||||
|| util.endsWith(lowerCaseUrl, ".svg")
|
|
||||||
|| util.endsWith(lowerCaseUrl, ".jpg")
|
function injectHTML(htmlContent) {
|
||||||
|| util.endsWith(lowerCaseUrl, ".jpeg"))) {
|
$("#readingArticle").hide();
|
||||||
// It's a link to a file of Wikipedia : change the URL to the online version and open in a new tab
|
$("#articleContent").show();
|
||||||
var onlineWikipediaUrl = url.replace(regexpImageLink, "https://" + selectedArchive._language + ".wikipedia.org/wiki/File:$1");
|
// Scroll the iframe to its top
|
||||||
$(this).attr("href", onlineWikipediaUrl);
|
$("#articleContent").contents().scrollTop(0);
|
||||||
$(this).attr("target", "_blank");
|
$('#articleContent').contents().find('body').html(htmlContent);
|
||||||
}
|
|
||||||
else {
|
// If the ServiceWorker is not useable, we need to fallback to parse the DOM
|
||||||
// It's a link to another article
|
// to inject math images, and replace some links with javascript calls
|
||||||
// Add an onclick event to go to this article
|
if (contentInjectionMode === 'jquery') {
|
||||||
// instead of following the link
|
|
||||||
|
// Convert links into javascript calls
|
||||||
if (url.substring(0, 2) === "./") {
|
$('#articleContent').contents().find('body').find('a').each(function () {
|
||||||
url = url.substring(2);
|
// Store current link's url
|
||||||
|
var url = $(this).attr("href");
|
||||||
|
if (url === null || url === undefined) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// Remove the initial slash if it's an absolute URL
|
var lowerCaseUrl = url.toLowerCase();
|
||||||
else if (url.substring(0, 1) === "/") {
|
var cssClass = $(this).attr("class");
|
||||||
url = url.substring(1);
|
|
||||||
|
if (cssClass === "new") {
|
||||||
|
// It's a link to a missing article : display a message
|
||||||
|
$(this).on('click', function (e) {
|
||||||
|
alert("Missing article in Wikipedia");
|
||||||
|
return false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
$(this).on('click', function(e) {
|
else if (url.slice(0, 1) === "#") {
|
||||||
var decodedURL = decodeURIComponent(url);
|
// It's an anchor link : do nothing
|
||||||
pushBrowserHistoryState(decodedURL);
|
}
|
||||||
goToArticle(decodedURL);
|
else if (url.substring(0, 4) === "http") {
|
||||||
return false;
|
// It's an external link : open in a new tab
|
||||||
});
|
$(this).attr("target", "_blank");
|
||||||
}
|
}
|
||||||
});
|
else if (url.match(regexpImageLink)
|
||||||
|
&& (util.endsWith(lowerCaseUrl, ".png")
|
||||||
|
|| util.endsWith(lowerCaseUrl, ".svg")
|
||||||
|
|| util.endsWith(lowerCaseUrl, ".jpg")
|
||||||
|
|| util.endsWith(lowerCaseUrl, ".jpeg"))) {
|
||||||
|
// It's a link to a file of Wikipedia : change the URL to the online version and open in a new tab
|
||||||
|
var onlineWikipediaUrl = url.replace(regexpImageLink, "https://" + selectedArchive._language + ".wikipedia.org/wiki/File:$1");
|
||||||
|
$(this).attr("href", onlineWikipediaUrl);
|
||||||
|
$(this).attr("target", "_blank");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// It's a link to another article
|
||||||
|
// Add an onclick event to go to this article
|
||||||
|
// instead of following the link
|
||||||
|
|
||||||
// Load images
|
if (url.substring(0, 2) === "./") {
|
||||||
$('#articleContent').contents().find('body').find('img').each(function() {
|
url = url.substring(2);
|
||||||
var image = $(this);
|
}
|
||||||
// It's a standard image contained in the ZIM file
|
// Remove the initial slash if it's an absolute URL
|
||||||
// We try to find its name (from an absolute or relative URL)
|
else if (url.substring(0, 1) === "/") {
|
||||||
var imageMatch = image.attr('data-kiwixsrc').match(regexpImageUrl); //kiwix-js #272
|
url = url.substring(1);
|
||||||
if (imageMatch) {
|
}
|
||||||
var title = decodeURIComponent(imageMatch[1]);
|
$(this).on('click', function (e) {
|
||||||
selectedArchive.getDirEntryByTitle(title).then(function(dirEntry) {
|
var decodedURL = decodeURIComponent(url);
|
||||||
selectedArchive.readBinaryFile(dirEntry, function (readableTitle, content) {
|
pushBrowserHistoryState(decodedURL);
|
||||||
// TODO : use the complete MIME-type of the image (as read from the ZIM file)
|
goToArticle(decodedURL);
|
||||||
uiUtil.feedNodeWithBlob(image, 'src', content, 'image');
|
return false;
|
||||||
});
|
});
|
||||||
}).fail(function (e) {
|
}
|
||||||
console.error("could not find DirEntry for image:" + title, e);
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Load CSS content
|
// Load images
|
||||||
$('#articleContent').contents().find('link[rel=stylesheet]').each(function() {
|
$('#articleContent').contents().find('body').find('img').each(function () {
|
||||||
var link = $(this);
|
var image = $(this);
|
||||||
// We try to find its name (from an absolute or relative URL)
|
// It's a standard image contained in the ZIM file
|
||||||
var hrefMatch = link.attr("href").match(regexpMetadataUrl);
|
// We try to find its name (from an absolute or relative URL)
|
||||||
if (hrefMatch) {
|
var imageMatch = image.attr('data-kiwixsrc').match(regexpImageUrl); //kiwix-js #272
|
||||||
// It's a CSS file contained in the ZIM file
|
if (imageMatch) {
|
||||||
var title = uiUtil.removeUrlParameters(decodeURIComponent(hrefMatch[1]));
|
var title = decodeURIComponent(imageMatch[1]);
|
||||||
selectedArchive.getDirEntryByTitle(title).then(function(dirEntry) {
|
selectedArchive.getDirEntryByTitle(title).then(function (dirEntry) {
|
||||||
selectedArchive.readBinaryFile(dirEntry, function (readableTitle, content) {
|
|
||||||
var cssContent = util.uintToString(content);
|
|
||||||
// For some reason, Firefox OS does not accept the syntax <link rel="stylesheet" href="data:text/css,...">
|
|
||||||
// So we replace the tag with a <style type="text/css">...</style>
|
|
||||||
// while copying some attributes of the original tag
|
|
||||||
// Cf http://jonraasch.com/blog/javascript-style-node
|
|
||||||
var cssElement = document.createElement('style');
|
|
||||||
cssElement.type = 'text/css';
|
|
||||||
|
|
||||||
if (cssElement.styleSheet) {
|
|
||||||
cssElement.styleSheet.cssText = cssContent;
|
|
||||||
} else {
|
|
||||||
cssElement.appendChild(document.createTextNode(cssContent));
|
|
||||||
}
|
|
||||||
var mediaAttributeValue = link.attr('media');
|
|
||||||
if (mediaAttributeValue) {
|
|
||||||
cssElement.media = mediaAttributeValue;
|
|
||||||
}
|
|
||||||
var disabledAttributeValue = link.attr('media');
|
|
||||||
if (disabledAttributeValue) {
|
|
||||||
cssElement.disabled = disabledAttributeValue;
|
|
||||||
}
|
|
||||||
link.replaceWith(cssElement);
|
|
||||||
});
|
|
||||||
}).fail(function (e) {
|
|
||||||
console.error("could not find DirEntry for CSS : " + title, e);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Load Javascript content
|
|
||||||
$('#articleContent').contents().find('script').each(function() {
|
|
||||||
var script = $(this);
|
|
||||||
// We try to find its name (from an absolute or relative URL)
|
|
||||||
var srcMatch = script.attr("src").match(regexpMetadataUrl);
|
|
||||||
// TODO check that the type of the script is text/javascript or application/javascript
|
|
||||||
if (srcMatch) {
|
|
||||||
// It's a Javascript file contained in the ZIM file
|
|
||||||
var title = uiUtil.removeUrlParameters(decodeURIComponent(srcMatch[1]));
|
|
||||||
selectedArchive.getDirEntryByTitle(title).then(function(dirEntry) {
|
|
||||||
if (dirEntry === null)
|
|
||||||
console.log("Error: js file not found: " + title);
|
|
||||||
else
|
|
||||||
selectedArchive.readBinaryFile(dirEntry, function (readableTitle, content) {
|
selectedArchive.readBinaryFile(dirEntry, function (readableTitle, content) {
|
||||||
// TODO : I have to disable javascript for now
|
// TODO : use the complete MIME-type of the image (as read from the ZIM file)
|
||||||
// var jsContent = encodeURIComponent(util.uintToString(content));
|
uiUtil.feedNodeWithBlob(image, 'src', content, 'image');
|
||||||
//script.attr("src", 'data:text/javascript;charset=UTF-8,' + jsContent);
|
|
||||||
});
|
});
|
||||||
}).fail(function (e) {
|
}).fail(function (e) {
|
||||||
console.error("could not find DirEntry for javascript : " + title, e);
|
console.error("could not find DirEntry for image:" + title, e);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
// Load Javascript content
|
||||||
|
$('#articleContent').contents().find('script').each(function () {
|
||||||
|
var script = $(this);
|
||||||
|
// We try to find its name (from an absolute or relative URL)
|
||||||
|
var srcMatch = script.attr("src").match(regexpMetadataUrl);
|
||||||
|
// TODO check that the type of the script is text/javascript or application/javascript
|
||||||
|
if (srcMatch) {
|
||||||
|
// It's a Javascript file contained in the ZIM file
|
||||||
|
var title = uiUtil.removeUrlParameters(decodeURIComponent(srcMatch[1]));
|
||||||
|
selectedArchive.getDirEntryByTitle(title).then(function (dirEntry) {
|
||||||
|
if (dirEntry === null)
|
||||||
|
console.log("Error: js file not found: " + title);
|
||||||
|
else
|
||||||
|
selectedArchive.readBinaryFile(dirEntry, function (readableTitle, content) {
|
||||||
|
// TODO : I have to disable javascript for now
|
||||||
|
// var jsContent = encodeURIComponent(util.uintToString(content));
|
||||||
|
//script.attr("src", 'data:text/javascript;charset=UTF-8,' + jsContent);
|
||||||
|
});
|
||||||
|
}).fail(function (e) {
|
||||||
|
console.error("could not find DirEntry for javascript : " + title, e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user