diff --git a/www/js/app.js b/www/js/app.js index bcbdf7de..84d6174c 100644 --- a/www/js/app.js +++ b/www/js/app.js @@ -529,6 +529,7 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFiles + " storages found with getDeviceStorages instead of 1"); } } + resetCssCache(); selectedArchive = zimArchiveLoader.loadArchiveFromDeviceStorage(selectedStorage, archiveDirectory, function (archive) { cookies.setItem("lastSelectedArchive", archiveDirectory, Infinity); // The archive is set : go back to home page to start searching @@ -537,6 +538,16 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFiles } } + + /** + * Resets the CSS Cache (used only in jQuery mode) + */ + function resetCssCache() { + // Reset the cssCache. Must be done when archive changes. + if (cssCache) { + cssCache = new Map(); + } + } /** * Displays the zone to select files from the archive @@ -547,6 +558,7 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFiles } function setLocalArchiveFromFileList(files) { + resetCssCache(); selectedArchive = zimArchiveLoader.loadArchiveFromFiles(files, function (archive) { // The archive is set : go back to home page to start searching $("#btnHome").click(); @@ -792,6 +804,10 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFiles // Since late 2014, all ZIM files should use relative URLs var regexpImageUrl = /^(?:\.\.\/|\/)+(I\/.*)$/; var regexpMetadataUrl = /^(?:\.\.\/|\/)+(-\/.*)$/; + + // Cache for CSS styles contained in ZIM. + // It significantly speeds up subsequent page display. See kiwix-js issue #335 + var cssCache = new Map(); /** * Display the the given HTML article in the web page, @@ -895,34 +911,23 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFiles if (hrefMatch) { // It's a CSS file contained in the ZIM file var title = uiUtil.removeUrlParameters(decodeURIComponent(hrefMatch[1])); - selectedArchive.getDirEntryByTitle(title).then(function(dirEntry) { - selectedArchive.readBinaryFile(dirEntry, function (fileDirEntry, content) { - var cssContent = util.uintToString(content); - // For some reason, Firefox OS does not accept the syntax - // So we replace the tag with a - // 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); + if (cssCache && cssCache.has(title)) { + var cssContent = cssCache.get(title); + uiUtil.replaceCSSLinkWithInlineCSS(link, cssContent); + } else { + selectedArchive.getDirEntryByTitle(title) + .then(function (dirEntry) { + return selectedArchive.readBinaryFile(dirEntry, + function (fileDirEntry, content) { + var fullUrl = fileDirEntry.namespace + "/" + fileDirEntry.url; + var contentString = util.uintToString(content); + if (cssCache) cssCache.set(fullUrl, contentString); + uiUtil.replaceCSSLinkWithInlineCSS(link, contentString); + }); + }).fail(function (e) { + console.error("could not find DirEntry for CSS : " + title, e); }); - }).fail(function (e) { - console.error("could not find DirEntry for CSS : " + title, e); - }); + } } }); diff --git a/www/js/lib/uiUtil.js b/www/js/lib/uiUtil.js index 6a3e48a4..fc9a3593 100644 --- a/www/js/lib/uiUtil.js +++ b/www/js/lib/uiUtil.js @@ -42,6 +42,36 @@ define([], function() { }); jQueryNode.attr(nodeAttribute, url); } + + /** + * Replace the given CSS link (from the DOM) with an inline CSS of the given content + * + * Due to CSP, Firefox OS does not accept syntax with href="data:text/css..." or href="blob:..." + * So we replace the tag with a + * while copying some attributes of the original tag + * Cf http://jonraasch.com/blog/javascript-style-node + * + * @param {Element} link from the DOM + * @param {String} cssContent + */ + function replaceCSSLinkWithInlineCSS (link, cssContent) { + 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('disabled'); + if (disabledAttributeValue) { + cssElement.disabled = disabledAttributeValue; + } + link.replaceWith(cssElement); + } var regexpRemoveUrlParameters = new RegExp(/([^\?]+)\?.*$/); @@ -58,6 +88,7 @@ define([], function() { */ return { feedNodeWithBlob: feedNodeWithBlob, + replaceCSSLinkWithInlineCSS: replaceCSSLinkWithInlineCSS, removeUrlParameters: removeUrlParameters }; });