Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding browser for zim library #1127

Merged
merged 24 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
53ccd0f
[PROTOTYPE] zim library base
Rishabhg71 Sep 20, 2023
feb0cf8
[ADD] Iframe for library
Rishabhg71 Sep 20, 2023
7dc7c88
[ADD] app theme to library iframe
Rishabhg71 Sep 21, 2023
e656f98
[FIX] alignment of buttons
Rishabhg71 Sep 21, 2023
eddd6d1
[FIX] CORS issue for download.library.com
Rishabhg71 Sep 21, 2023
29a6dcd
[FIX] downloads from all mirrors
Rishabhg71 Sep 21, 2023
b0a3620
[ADD] i18n for library button
Rishabhg71 Sep 22, 2023
c6ed333
[FIX] allow all mirrors urls `*`
Rishabhg71 Sep 23, 2023
04b441b
[ADD] proper jsdocs for params
Rishabhg71 Sep 25, 2023
2e1d097
[ADD] IE support for iframe
Rishabhg71 Sep 25, 2023
507d0d9
[ADD] Fallback url old browsers
Rishabhg71 Sep 25, 2023
d253aae
[REFACTOR] updated jsdoc for `params.decompressorAPI`
Rishabhg71 Sep 25, 2023
c515f64
[REFACTOR] changes requested by @jaifroid
Rishabhg71 Sep 26, 2023
ea12093
[FIX] Download not starting due to CSP
Rishabhg71 Sep 30, 2023
b218fda
[FIX] absolute url to relative url for iframe
Rishabhg71 Sep 30, 2023
2729c0f
[ADD] script to check for `eval` support
Rishabhg71 Oct 3, 2023
279a1c7
[PROTO] Using frame communication with parent
Rishabhg71 Oct 4, 2023
1aa0d95
support for chrome extension and IE
Rishabhg71 Oct 5, 2023
de8b510
[ADD] download.kiwix load as text content
Rishabhg71 Oct 7, 2023
758bf38
[REVERT,ADD] open urls in new tab
Rishabhg71 Oct 9, 2023
a0305f3
[ADD] popup before opening link
Rishabhg71 Oct 9, 2023
fddddd6
[CLEANUP] minor changes
Rishabhg71 Oct 9, 2023
bd1059f
Update app.js
Rishabhg71 Oct 9, 2023
08b6a26
[REFACTOR] changes requested by `@jaifroid`
Rishabhg71 Oct 9, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions i18n/en.jsonp.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions i18n/es.jsonp.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions i18n/fr.jsonp.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions service-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ const precacheFiles = [
'www/img/Icon_External_Link.png',
'www/index.html',
'www/article.html',
'www/library.html',
'www/main.html',
'www/js/app.js',
'www/js/init.js',
Expand Down
4 changes: 4 additions & 0 deletions www/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ input[type="file"] {
display: none;
}

iframe {
border: 0;
}

.custom-file-upload {
border: 2px solid darkgray;
display: inline-block;
Expand Down
19 changes: 13 additions & 6 deletions www/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,9 @@ <h3 id="other" data-i18n="about-other-platforms">Other platforms/versions</h3>
<br/>
<br/>
</div>

<div id='library' style="display: none; color: white;">
<iframe width="100%" id="libraryContent" src="./library.html"></iframe>
</div>
<div id="configuration" style="display: none;">
<div class="container">
<h2 data-i18n="configure-title">Configuration</h2>
Expand All @@ -467,11 +469,16 @@ <h2 data-i18n="configure-title">Configuration</h2>
<div id="openLocalFiles" style="display: none;">
<p data-i18n="configure-select-instructions">Please select or drag and drop a .zim file (or all the .zimaa, .zimab etc in
case of a split ZIM file):</p>
<label class="btn btn-light custom-file-upload">
<input type="file" id="archiveFiles" multiple class="btn"
accept="application/octet-stream,.zim,.zimaa,.zimab,.zimac,.zimad,.zimae,.zimaf,.zimag,.zimah,.zimai,.zimaj,.zimak,.zimal,.zimam,.ziman,.zimao,.zimap,.zimaq,.zimar,.zimas,.zimat,.zimau,.zimav,.zimaw,.zimax,.zimay,.zimaz,.zimba,.zimbb,.zimbc,.zimbd,.zimbe,.zimbf,.zimbg,.zimbh,.zimbi,.zimbj,.zimbk,.zimbl,.zimbm,.zimbn,.zimbo,.zimbp,.zimbq,.zimbr,.zimbs,.zimbt,.zimbu,.zimbv,.zimbw,.zimbx,.zimby,.zimbz" />
<span data-i18n="home-btn-fileselect">Select ZIM file(s)</span>
</label>
<span>
<label class="btn btn-light custom-file-upload">
<input type="file" id="archiveFiles" multiple class="btn"
accept="application/octet-stream,.zim,.zimaa,.zimab,.zimac,.zimad,.zimae,.zimaf,.zimag,.zimah,.zimai,.zimaj,.zimak,.zimal,.zimam,.ziman,.zimao,.zimap,.zimaq,.zimar,.zimas,.zimat,.zimau,.zimav,.zimaw,.zimax,.zimay,.zimaz,.zimba,.zimbb,.zimbc,.zimbd,.zimbe,.zimbf,.zimbg,.zimbh,.zimbi,.zimbj,.zimbk,.zimbl,.zimbm,.zimbn,.zimbo,.zimbp,.zimbq,.zimbr,.zimbs,.zimbt,.zimbu,.zimbv,.zimbw,.zimbx,.zimby,.zimbz" />
<span data-i18n="home-btn-fileselect">Select ZIM file(s)</span>
</label>
<label class="btn btn-light custom-file-upload" id="libraryBtn" data-i18n="configure-btn-library">
Browse ZIM Library
</label>
</span>
<br />
<strong id="jqueryCompatibility" data-i18n="configure-static-content">Only ZIMs with static content (e.g. Wiki-style) are supported in JQuery mode.<br /></strong>
<span data-i18n="configure-supportedarchives">For information on ZIM compatibility, see</span> <a href="#usage" data-i18n="configure-about-usage-link" class="aboutLinks">About (Usage)</a>.<br />
Expand Down
55 changes: 40 additions & 15 deletions www/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,23 +136,33 @@ darkPreference.onchange = function () {
* Resize the IFrame height, so that it fills the whole available height in the window
*/
function resizeIFrame () {
var headerStyles = getComputedStyle(document.getElementById('top'));
var iframe = document.getElementById('articleContent');
var region = document.getElementById('search-article');
if (iframe.style.display === 'none') {
// We are in About or Configuration, so we only set the region height
region.style.height = window.innerHeight + 'px';
} else {
// IE cannot retrieve computed headerStyles till the next paint, so we wait a few ticks
setTimeout(function () {
// Get header height *including* its bottom margin
var headerHeight = parseFloat(headerStyles.height) + parseFloat(headerStyles.marginBottom);
iframe.style.height = window.innerHeight - headerHeight + 'px';
// We have to allow a minimum safety margin of 10px for 'iframe' and 'header' to fit within 'region'
region.style.height = window.innerHeight + 10 + 'px';
}, 100);
const headerStyles = getComputedStyle(document.getElementById('top'));
const articleContent = document.getElementById('articleContent');
const libraryContent = document.getElementById('libraryContent');
const frames = [articleContent, libraryContent];
const region = document.getElementById('search-article');
const nestedFrame = libraryContent.contentWindow.document.getElementById('libraryIframe');

for (let i = 0; i < frames.length; i++) {
const iframe = frames[i];
if (iframe.style.display === 'none') {
// We are in About or Configuration, so we only set the region height
region.style.height = window.innerHeight + 'px';
nestedFrame.style.height = window.innerHeight - 110 + 'px';
} else {
// IE cannot retrieve computed headerStyles till the next paint, so we wait a few ticks
setTimeout(function () {
// Get header height *including* its bottom margin
const headerHeight = parseFloat(headerStyles.height) + parseFloat(headerStyles.marginBottom);
iframe.style.height = window.innerHeight - headerHeight + 'px';
// We have to allow a minimum safety margin of 10px for 'iframe' and 'header' to fit within 'region'
region.style.height = window.innerHeight + 10 + 'px';
nestedFrame.style.height = window.innerHeight - 110 + 'px';
}, 100);
}
}
}

document.addEventListener('DOMContentLoaded', function () {
getDefaultLanguageAndTranslateApp();
resizeIFrame();
Expand Down Expand Up @@ -1296,6 +1306,21 @@ function handleFileDrop (packet) {
document.getElementById('archiveFiles').value = null;
}

document.getElementById('libraryBtn').addEventListener('click', function (e) {
e.preventDefault();

const libraryContent = document.getElementById('libraryContent');
const iframe = libraryContent.contentWindow.document.getElementById('libraryIframe');
try {
// eslint-disable-next-line no-new-func
Function('try{}catch{}')();
iframe.setAttribute('src', params.libraryUrl);
uiUtil.tabTransitionToSection('library', params.showUIAnimations);
} catch (error) {
window.open(params.altLibraryUrl, '_blank')
}
});

// Add event listener to link which allows user to show file selectors
document.getElementById('selectorsDisplayLink').addEventListener('click', function (e) {
e.preventDefault();
Expand Down
37 changes: 36 additions & 1 deletion www/js/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,40 @@
* A global parameter object for storing variables that need to be remembered between page loads,
* or across different functions and modules
*
* @type Object
* @typedef {Object} AppParams
* @property {string} appVersion - The version number of the application.
* @property {string} PWAServer - The URL of the PWA server for use with the browser extensions in ServiceWorker mode.
* @property {string} storeType - A parameter to determine the Settings Store API in use.
* @property {string} keyPrefix - The key prefix used by the settingsStore.js.
* @property {boolean} hideActiveContentWarning - A boolean indicating whether to hide the active content warning.
* @property {boolean} showUIAnimations - A boolean indicating whether to show UI animations.
* @property {number} maxSearchResultsSize - The maximum number of article titles to return.
* @property {boolean} assetsCache - A boolean indicating whether to cache assets.
* @property {boolean} appCache - A boolean indicating whether to cache the PWA's code.
* @property {string} appTheme - A parameter to set the app theme and, if necessary, the CSS theme for article content.
* @property {boolean} useHomeKeyToFocusSearchBar - A global parameter to turn on/off the use of Keyboard HOME Key to focus search bar.
* @property {boolean} openExternalLinksInNewTabs - A global parameter to turn on/off opening external links in new tab (for ServiceWorker mode).
* @property {string} overrideBrowserLanguage - A global language override.
* @property {boolean} disableDragAndDrop - A parameter to disable drag-and-drop.
* @property {string} referrerExtensionURL - A parameter to access the URL of any extension that this app was launched from.
* @property {boolean} defaultModeChangeAlertDisplayed - A parameter to keep track of the fact that the user has been informed of the switch to SW mode by default.
* @property {string} contentInjectionMode - A parameter to set the content injection mode ('jquery' or 'serviceworker') used by this app.
* @property {boolean} useCanvasElementsForWebpTranscoding - A parameter to circumvent anti-fingerprinting technology in browsers that do not support WebP natively by substituting images directly with the canvas elements produced by the WebP polyfill.
* @property {string} libraryUrl - The URL of the Kiwix library.
* @property {string} altLibraryUrl - The alternative URL of the Kiwix library in non-supported browsers.
* @property {DecompressorAPI} decompressorAPI

/**
* A property of the global params object to track the assembler machine type and the last used decompressor (for reporting to the API panel)
* This is populated in the Emscripten wrappers
* @typedef {Object} DecompressorAPI
* @property {String} assemblerMachineType The assembler machine type supported and/or loaded by this app: 'ASM' or 'WASM'
* @property {String} decompressorLastUsed The decompressor that was last used to decode a compressed cluster (currently 'XZ' or 'ZSTD')
* @property {String} errorStatus A description of any detected error in loading a decompressor
*/

/**
* @type {AppParams}
*/
var params = {};

Expand Down Expand Up @@ -77,6 +110,8 @@ params['contentInjectionMode'] = getSetting('contentInjectionMode') ||
// A parameter to circumvent anti-fingerprinting technology in browsers that do not support WebP natively by substituting images
// directly with the canvas elements produced by the WebP polyfill [kiwix-js #835]. NB This is only currently used in jQuery mode.
params['useCanvasElementsForWebpTranscoding'] = null; // Value is determined in uiUtil.determineCanvasElementsWorkaround(), called when setting the content injection mode
params['libraryUrl'] = 'https://library.kiwix.org/'; // Url for iframe that will be loaded to download new zim files
params['altLibraryUrl'] = 'https://download.kiwix.org/zim/'; // Alternative Url for iframe (for use with unsupported browsers) that will be loaded to download new zim files

/**
* Apply any override parameters that might be in the querystring.
Expand Down
29 changes: 25 additions & 4 deletions www/js/lib/uiUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -500,8 +500,9 @@ function removeAnimationClasses () {
const config = document.getElementById('configuration');
const about = document.getElementById('about');
const home = document.getElementById('articleContent');
const library = document.getElementById('library');

const tabs = [config, about, home]
const tabs = [config, about, home, library]
tabs.forEach(tab => {
tab.classList.remove('slideIn_L');
tab.classList.remove('slideIn_R');
Expand Down Expand Up @@ -560,7 +561,9 @@ function fromSection () {
const isConfigPageVisible = !$('#configuration').is(':hidden');
const isAboutPageVisible = !$('#about').is(':hidden');
const isArticlePageVisible = !$('#articleContent').is(':hidden');
const isLibraryPageVisible = !$('#library').is(':hidden');
if (isConfigPageVisible) return 'config';
if (isLibraryPageVisible) return 'library';
else if (isAboutPageVisible) return 'about';
else if (isArticlePageVisible) return 'home';
}
Expand All @@ -576,6 +579,7 @@ function tabTransitionToSection (toSection, isAnimationRequired = false) {
// all the references of the sections/tabs
const config = document.getElementById('configuration');
const about = document.getElementById('about');
const library = document.getElementById('library');
const home = document.getElementById('articleContent');

// references of extra elements that are in UI but not tabs
Expand All @@ -594,29 +598,43 @@ function tabTransitionToSection (toSection, isAnimationRequired = false) {
if (toSection === 'home') {
if (from === 'config') slideToRight(home, config);
if (from === 'about') slideToRight(home, about);
if (from === 'library') slideToRight(home, library);

showElements(extraNavBtns, extraArticleSearch, extraWelcomeText, extraKiwixAlert);
} else if (toSection === 'config') {
if (from === 'about') slideToRight(config, about);
if (from === 'library') slideToRight(config, library);
if (from === 'home') slideToLeft(config, home);

hideElements(extraNavBtns, extraArticleSearch, extraWelcomeText, extraSearchingArticles, extraKiwixAlert);
} else if (toSection === 'about') {
if (from === 'library') slideToRight(about, library);
if (from === 'home') slideToLeft(about, home);
if (from === 'config') slideToLeft(about, config);

hideElements(extraNavBtns, extraArticleSearch, extraWelcomeText, extraSearchingArticles, extraKiwixAlert);
} else if (toSection === 'library') {
// it will be always coming from config page
slideToLeft(library, config);
hideElements(extraNavBtns, extraArticleSearch, extraWelcomeText, extraSearchingArticles, extraKiwixAlert);
}
} else {
if (toSection === 'home') {
hideElements(config, about);
hideElements(config, about, library);
showElements(home, extraNavBtns, extraArticleSearch, extraWelcomeText);
}
if (toSection === 'config') {
hideElements(about, home, extraNavBtns, extraArticleSearch, extraWelcomeText, extraSearchingArticles, extraKiwixAlert);
hideElements(about, home, library, extraNavBtns, extraArticleSearch, extraWelcomeText, extraSearchingArticles, extraKiwixAlert);
showElements(config);
}
if (toSection === 'about') {
hideElements(config, home);
hideElements(config, home, library);
showElements(about);
}
if (toSection === 'library') {
hideElements(config, about, home, extraNavBtns, extraArticleSearch, extraWelcomeText, extraSearchingArticles, extraKiwixAlert);
showElements(library);
}
}
}

Expand All @@ -642,6 +660,7 @@ function applyAppTheme (theme) {
var footer = document.querySelector('footer');
var oldTheme = htmlEl.dataset.theme || '';
var iframe = document.getElementById('articleContent');
const library = document.getElementById('libraryContent');
var doc = iframe.contentDocument;
var kiwixJSSheet = doc ? doc.getElementById('kiwixJSTheme') || null : null;
var oldAppTheme = oldTheme.replace(/_.*$/, '');
Expand Down Expand Up @@ -674,6 +693,7 @@ function applyAppTheme (theme) {
// If there is no ContentTheme or we are applying a different ContentTheme, remove any previously applied ContentTheme
if (oldContentTheme && oldContentTheme !== contentTheme) {
iframe.classList.remove(oldContentTheme);
library.classList.remove(oldContentTheme);
if (kiwixJSSheet) {
kiwixJSSheet.disabled = true;
kiwixJSSheet.parentNode.removeChild(kiwixJSSheet);
Expand All @@ -682,6 +702,7 @@ function applyAppTheme (theme) {
// Apply the requested ContentTheme (if not already attached)
if (contentTheme && (!kiwixJSSheet || !~kiwixJSSheet.href.search('kiwixJS' + contentTheme + '.css'))) {
iframe.classList.add(contentTheme);
library.classList.add(contentTheme);
// Use an absolute reference because Service Worker needs this (if an article loaded in SW mode is in a ZIM
// subdirectory, then relative links injected into the article will not work as expected)
// Note that location.pathname returns the path plus the filename, but is useful because it removes any query string
Expand Down
9 changes: 1 addition & 8 deletions www/js/lib/zimfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,7 @@ if (!String.prototype.startsWith) {
});
}

/**
* A global variable to track the assembler machine type and the last used decompressor (for reporting to the API panel)
* This is populated in the Emscripten wrappers
* @type {Object}
* @property {String} assemblerMachineType The assembler machine type supported and/or loaded by this app: 'ASM' or 'WASM'
* @property {String} decompressorLastUsed The decompressor that was last used to decode a compressed cluster (currently 'XZ' or 'ZSTD')
* @property {String} errorStatus A description of any detected error in loading a decompressor
*/
Rishabhg71 marked this conversation as resolved.
Show resolved Hide resolved
// to learn more read init.js:57 or search DecompressorAPI in init.js
params.decompressorAPI = {
assemblerMachineType: null,
decompressorLastUsed: null,
Expand Down
14 changes: 14 additions & 0 deletions www/library.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Placeholder for injecting an article into the iframe</title>
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self' data: * 'unsafe-inline' 'unsafe-eval'; frame-src 'self' moz-extension: chrome-extension: *; object-src 'none';"
/>
</head>
<body>
<iframe width="100%" id="libraryIframe" style="border: none;"></iframe>
</body>
</html>