]+id=["']cite[-_]note[-_]([^"']+)[^>]+>(?![^/]+?[↑^])/ig, function (match, id) {
var fnReturnMatch = '';
try {
var fnSearchRegxp = new RegExp('id=["' + "'](cite[-_]ref[-_]" + id.replace(/[-_()+?]/g, '[-_()]+?') + '[^"' + "']*)", 'i');
fnReturnMatch = htmlArticle.match(fnSearchRegxp);
} catch (err) {
console.error('Error constructiong regular expression in app.js', err);
}
var fnReturnID = fnReturnMatch ? fnReturnMatch[1] : '';
return match + '\r\n
^ ';
});
// Exempt Nautilus and YouTube based ZIMs from stylesheet preloading
var nautilus = params.contentInjectionMode === 'serviceworker'
? htmlArticle.match(/\r\n' +
'\r\n$1');
}
// Prevent the script that detects whether wombat is loaded from running
if (params.zimType === 'zimit') htmlArticle = htmlArticle.replace(/!(window._WBWombat)/, '$1');
// Add doctype if missing so that scripts run in standards mode
// (quirks mode prevents katex from running, and is incompatible with jQuery)
params.transformedHTML = !/^\s*(?:\n' + htmlArticle : htmlArticle;
params.transDirEntry = dirEntry;
// We will need the encoded URL on article load so that we can set the iframe's src correctly,
// but we must not encode the '/' character or else relative links may fail [kiwix-js #498]
var encodedUrl = params.zimType === 'zimit' ? dirEntry.url : encodeURI(dirEntry.url);
// .replace(/[^/]+/g, function (matchedSubstring) {
// return encodeURIComponent(matchedSubstring);
// });
// If the request was not initiated by an existing controlled window, we instantiate the request here
if (!appstate.messageChannelWaiting) {
// We put the ZIM filename as a prefix in the URL, so that browser caches are separate for each ZIM file
var newLocation = '../' + appstate.selectedArchive._file.name + '/' + dirEntry.namespace + '/' + encodedUrl;
if (navigator.serviceWorker.controller) {
loaded = false;
articleWindow.location.href = newLocation;
}
}
return;
}
// Write article html to the article container
// articleWindow.document.open('text/html', 'replace');
// articleWindow.document.write(htmlArticle);
// articleWindow.document.close();
if (appstate.target === 'iframe') {
// Store the frame article's target in the top-level window, so that when we retrieve the window with
// history manipulation, we'll know where to place the iframe contentWindow
window.kiwixType = appstate.target;
articleContainer.onload = articleLoaded;
articleContainer.src = 'article.html';
} else {
// Attempt to establish an independent history record for windows (jQuery / window-tab mode)
articleWindow.onpopstate = historyPop;
// The articleWindow has already been set in the click event of the ZIM link and the dummy article was loaded there
// (to avoid popup blockers). Firefox loads windows asynchronously, so we need to wait for onclick load to be fully
// cleared, or else Firefox overwrites the window immediately after we load the html content into it.
setTimeout(articleLoaded, 400);
}
// Failsafe for spinner
setTimeout(function () {
uiUtil.clearSpinner();
}, 6000);
} // End of injectHtml
} // End of displayArticleInForm()
function parseAnchorsJQuery (dirEntry) {
var currentProtocol = articleWindow.location.protocol;
currentProtocol = currentProtocol === 'about:' ? ':' : currentProtocol;
var currentHost = articleWindow.location.host;
// Percent-encode dirEntry.url and add regex escape character \ to the RegExp special characters - see https://www.regular-expressions.info/characters.html;
// NB dirEntry.url can also contain path separator / in some ZIMs (Stackexchange). } and ] do not need to be escaped as they have no meaning on their own.
var escapedUrl = encodeURIComponent(dirEntry.url).replace(/([\\$^.|?*+/()[{])/g, '\\$1');
// Pattern to match a local anchor in an href even if prefixed by escaped url; will also match # on its own
// Note that we exclude any # with a semicolon between it and the end of the string, to avoid accidentally matching e.g. '
var regexpLocalAnchorHref = new RegExp('^(?:#|' + escapedUrl + '#)([^#;]*$)');
Array.prototype.slice.call(articleDocument.querySelectorAll('a, area')).forEach(function (anchor) {
// Attempts to access any properties of 'this' with malformed URLs causes app crash in Edge/UWP [kiwix-js #430]
try {
var testHref = anchor.href;
} catch (err) {
console.error('Malformed href caused error:' + err.message);
return;
}
var href = anchor.getAttribute('href');
if (href === null || href === undefined || /^javascript:/i.test(anchor.protocol)) return;
var anchorTarget = href.match(regexpLocalAnchorHref);
if (href.length === 0) {
// It's a link with an empty href, pointing to the current page: do nothing.
} else if (anchorTarget) {
// It's a local anchor link : remove escapedUrl if any (see above)
anchor.setAttribute('href', '#' + anchorTarget[1]);
} else if (anchor.protocol && anchor.protocol !== currentProtocol || anchor.host && anchor.host !== currentHost) {
// It's an external URL : we should open it in a new tab
anchor.addEventListener('click', function (event) {
if (anchor.protocol === 'bingmaps:') {
anchor.removeAttribute('target');
event.preventDefault();
window.location = href;
} else {
// Find the closest enclosing A tag
var clickedAnchor = uiUtil.closestAnchorEnclosingElement(event.target);
uiUtil.warnAndOpenExternalLinkInNewTab(event, clickedAnchor);
}
});
} else {
// Intercept YouTube videos in Zimit archives
if (params.zimType === 'zimit' && /youtu(?:be(?:-nocookie)?\.com|\.be)\//i.test(href)) {
transformZimit.transformVideoUrl(href, articleDocument, function (transHref) {
addListenersToLink(anchor, transHref, params.baseURL);
});
} else {
addListenersToLink(anchor, href, params.baseURL);
}
}
});
// Add event listeners to the main heading so user can open current document in new tab or window by clicking on it
if (articleWindow.document.body) {
var h1 = articleWindow.document.body.querySelector('h1');
if (h1) addListenersToLink(h1, encodeURIComponent(dirEntry.url.replace(/[^/]+\//g, '')), params.baseURL);
}
}
/**
* Add event listeners to a hyperlinked element to extract the linked article or file from the ZIM instead
* of following links
* @param {Node} a The anchor or other linked element to which event listeners will be attached
* @param {String} href The href of the linked element
* @param {String} baseUrl The baseUrl against which relative links will be calculated
*/
function addListenersToLink (a, href, baseUrl) {
var uriComponent = uiUtil.removeUrlParameters(href);
// var namespace = baseUrl.replace(/^([-ABCIJMUVWX])\/.+/, '$1');
var loadingContainer = false;
var contentType;
var downloadAttrValue;
// Some file types need to be downloaded rather than displayed (e.g. *.epub)
// The HTML download attribute can be Boolean or a string representing the specified filename for saving the file
// For Boolean values, getAttribute can return any of the following: download="" download="download" download="true"
// So we need to test hasAttribute first: see https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute
// However, we cannot rely on the download attribute having been set, so we also need to test for known download file types
var isDownloadableLink = a.hasAttribute('download') || regexpDownloadLinks.test(href);
if (isDownloadableLink) {
// if (!/UWP/.test(params.appType) && params.contentInjectionMode === 'serviceworker') return;
downloadAttrValue = a.getAttribute('download');
// Normalize the value to a true Boolean or a filename string or true if there is no download attribute
downloadAttrValue = /^(download|true|\s*)$/i.test(downloadAttrValue) || downloadAttrValue || true;
contentType = a.getAttribute('type');
}
// DEV: We need to use the '#' location trick here for cross-browser compatibility with opening a new tab/window
// if (params.windowOpener && a.tagName !== 'IFRAME') a.setAttribute('href', '#' + href);
// Store the current values, as they may be changed if user switches to another tab before returning to this one
var kiwixTarget = appstate.target;
var thisWindow = articleWindow;
var thisContainer = articleContainer;
var reset = function () {
if (appstate.target === 'window') {
// By delaying unblocking of the touch event, we prevent multiple touch events launching the same window
a.touched = false;
a.newcontainer = false;
}
loadingContainer = false;
};
var onDetectedClick = function (e) {
// Restore original values for this window/tab
appstate.target = kiwixTarget;
articleWindow = thisWindow;
articleContainer = thisContainer;
var isNautilusPopup = a.dataset.popup && !/0|false/i.test(a.dataset.popup);
if (a.tagName === 'H1' || isNautilusPopup) {
// We have registered a click on the header or on a dynamic link (e.g. in Nautilus archives)
if (isNautilusPopup) {
// Pop-up window sometimes opens out of view, so we have to scroll into view
iframe.contentWindow.scrollTo({
top: '0',
behavior: 'smooth'
});
}
if (!a.newcontainer) return; // A new tab wasn't requested, so ignore
}
if (params.windowOpener) {
// This processes Ctrl-click, Command-click, the long-press event, and middle-click
if (a.newcontainer) {
// We open the new window immediately so that it is a direct result of user action (click)
// and we'll populate it later - this avoids most popup blockers
loadingContainer = true;
articleContainer = window.open('article.html', params.windowOpener === 'tab' ? '_blank' : a.title,
params.windowOpener === 'window' ? 'toolbar=0,location=0,menubar=0,width=800,height=600,resizable=1,scrollbars=1' : null);
appstate.target = 'window';
// We have to make this conditional, because sometimes this action is blocked by the browser
if (articleContainer) {
articleContainer.kiwixType = appstate.target;
articleWindow = articleContainer;
}
}
}
e.preventDefault();
e.stopPropagation();
anchorParameter = href.match(/#([^#;]+)$/);
anchorParameter = anchorParameter ? anchorParameter[1] : '';
var indexRoot = window.location.pathname.replace(/[^/]+$/, '') + encodeURI(appstate.selectedArchive._file.name) + '/';
var zimRoot = indexRoot.replace(/^.+?\/www\//, '/');
var zimUrl;
var zimUrlFullEncoding;
if (params.zimType === 'zimit') {
if (!href.indexOf(indexRoot)) { // If begins with indexRoot
zimUrl = href.replace(indexRoot, '').replace('#' + anchorParameter, '');
} else if (!href.indexOf(zimRoot)) { // If begins with zimRoot
zimUrl = href.replace(zimRoot, '').replace('#' + anchorParameter, '');
} else {
// Zimit ZIMs store URLs percent-encoded and with querystring and
// deriveZimUrlFromRelativeUrls strips any querystring and decodes
zimUrl = encodeURI(uiUtil.deriveZimUrlFromRelativeUrl(href, baseUrl)) +
href.replace(encodeURI(uriComponent), '').replace('#' + anchorParameter, '');
zimUrlFullEncoding = encodeURI(uiUtil.deriveZimUrlFromRelativeUrl(href, baseUrl) +
href.replace(encodeURI(uriComponent), '').replace('#' + anchorParameter, ''));
}
} else {
zimUrl = uiUtil.deriveZimUrlFromRelativeUrl(uriComponent, baseUrl);
}
// @TODO: We are getting double activations of the click event. This needs debugging. For now, we use a flag to prevent this.
a.newcontainer = true; // Prevents double activation
goToArticle(zimUrl, downloadAttrValue, contentType, zimUrlFullEncoding);
setTimeout(reset, 1400);
};
a.addEventListener('touchstart', function (e) {
if (!params.windowOpener || a.touched) return;
e.stopPropagation();
// e.preventDefault();
a.touched = true;
loadingContainer = true;
var event = e;
// The link will be clicked if the user long-presses for more than 800ms (if the option is enabled)
setTimeout(function () {
// DEV: appstate.startVector indicates that the app is processing a touch zoom event, so we cancel any new windows
// see uiUtil.pointermove_handler
if (!a.touched || a.newcontainer || appstate.startVector) return;
e.preventDefault();
a.newcontainer = true;
onDetectedClick(event);
}, 800);
}, { passive: false });
a.addEventListener('touchend', function () {
a.touched = false;
a.newcontainer = false;
loadingContainer = false;
});
// This detects right-click in all browsers (only if the option is enabled)
a.addEventListener('contextmenu', function (e) {
console.debug('contextmenu');
if (!params.windowOpener) return;
if (params.rightClickType === 'double' && !a.touched) {
a.touched = true;
setTimeout(function () {
a.touched = false;
}, 700);
} else {
if (a.newcontainer) return; // Prevent accidental double activation
e.preventDefault();
e.stopPropagation();
a.newcontainer = true;
a.touched = false;
onDetectedClick(e);
}
});
// This traps the middle-click event before tha auxclick event fires
a.addEventListener('mousedown', function (e) {
console.debug('mosuedown');
if (!params.windowOpener) return;
e.preventDefault();
e.stopPropagation();
if (a.touched || a.newcontainer) return; // Prevent double activations
if (e.ctrlKey || e.metaKey || e.which === 2 || e.button === 4) {
a.newcontainer = true;
onDetectedClick(e);
} else {
console.debug('suppressed mousedown');
}
});
// This detects the middle-click event that opens a new tab in recent Firefox and Chrome
// See https://developer.mozilla.org/en-US/docs/Web/API/Element/auxclick_event
a.addEventListener('auxclick', function (e) {
console.debug('auxclick');
if (!params.windowOpener) return;
e.preventDefault();
e.stopPropagation();
});
// The main click routine (called by other events above as well)
a.addEventListener('click', function (e) {
console.log('Click event', e);
// Prevent opening multiple windows
if (loadingContainer || a.touched || a.newcontainer) {
e.preventDefault();
e.stopPropagation();
} else {
onDetectedClick(e);
}
});
}
/**
* Unhides all hidden divs or tables, for use in Wikimedia mobile display style, which hides some crucial
* elements that users want optionally to be able to access
*/
function displayHiddenBlockElements (win, doc) {
if (!doc) return;
console.debug('Searching for hidden block elements to display...');
Array.prototype.slice.call(doc.querySelectorAll('table, div')).forEach(function (element) {
if (win.getComputedStyle(element).display === 'none') {
element.style.setProperty('display', 'block', 'important');
if (!params.noHiddenElementsWarning) {
var message;
if (!appstate.wikimediaZimLoaded) {
message = '
The way the Display hidden block elements setting works has changed! Because it is currently set ' +
'to always, it will now apply to any ZIM type. This can have unexpected effects in non-Wikipedia ZIMs.
' +
'
We strongly recommend that you change this setting to auto in Configuration. The new auto setting allows the ' +
'app to decide when to apply the setting. If you never want to see hidden elements, even in Wikimedia ZIMs, change the ' +
'setting to never.
';
}
// else if (params.displayHiddenBlockElements === 'auto') {
// message = '
There is a new auto setting in Configuration to display hidden elements (navigation boxes, series tables) ' +
// "in Wikimedia ZIMs. This is now on by default. If you don't want to see these elements, change the 'Display hidden block elements' " +
// "setting to never (under 'Display style').
";
// if (params.cssSource !== 'desktop') {
// message += '
Please note that hidden elements are always displayed in Desktop style (regardless of the setting)' +
// (params.cssSource !== 'desktop' ? '. You can switch the display style to Desktop in Configuration' : '') + '.
';
// }
// }
if (message) {
message += '
This message will not be displayed again, unless you reset the app.
';
params.noHiddenElementsWarning = true;
uiUtil.systemAlert(message, 'One-time message!').then(function () {
settingsStore.setItem('noHiddenElementsWarning', true, Infinity);
});
}
}
}
});
// Ensure images are picked up by lazy loading
win.scrollBy(0, 5);
win.scrollBy(0, -5);
}
var dropup = document.getElementById('dropup');
dropup.addEventListener('click', function () {
var ToCList = document.getElementById('ToCList');
ToCList.style.display = ToCList.style.display === 'block' ? 'none' : 'block';
});
function setupTableOfContents () {
var iframe = document.getElementById('articleContent');
var innerDoc = iframe.contentDocument;
var tableOfContents = new uiUtil.ToC(innerDoc);
var headings = tableOfContents.getHeadingObjects();
dropup.style.fontSize = ~~(params.relativeUIFontSize * 0.14) + 'px';
var dropupHtml = '';
headings.forEach(function (heading) {
if (/^h1$/i.test(heading.tagName)) {
dropupHtml += '
' + heading.textContent + '';
} else if (/^h2$/i.test(heading.tagName)) {
dropupHtml += '
' + heading.textContent + '';
} else if (/^h3$/i.test(heading.tagName)) {
dropupHtml += '
' + heading.textContent + '';
} else if (/^h4$/i.test(heading.tagName)) {
dropupHtml += '
' + heading.textContent + '';
}
// Skip smaller headings (if there are any) to avoid making list too long
});
var ToCList = document.getElementById('ToCList');
ToCList.style.maxHeight = ~~(window.innerHeight * 0.75) + 'px';
ToCList.style.marginLeft = ~~(window.innerWidth / 2) - ~~(window.innerWidth * 0.16) + 'px';
ToCList.innerHTML = dropupHtml;
Array.prototype.slice.call(ToCList.getElementsByTagName('a')).forEach(function (listElement) {
listElement.addEventListener('click', function () {
var sectionEle = innerDoc.getElementById(this.dataset.headingId);
var csec = util.closest(sectionEle, 'details, section');
csec = csec && /DETAILS|SECTION/.test(csec.parentElement.tagName) ? csec.parentElement : csec;
openAllSections(true, csec);
// Scroll to element
sectionEle.scrollIntoView();
// Scrolling up then down ensures that the toolbars show according to user settings
iframe.contentWindow.scrollBy(0, -5);
setTimeout(function () {
iframe.contentWindow.scrollBy(0, 5);
iframe.contentWindow.focus();
}, 250);
ToCList.style.display = 'none';
});
});
}
/**
* Sets the state of collapsible sections for the iframe document, or for the given node
* @param {Boolean} override An optional value that overrides params.openAllSections (true to open, false to close)
* @param {Node} node An optional node within which elements will be opened or closed (this will normally be a details element)
*/
// Sets state of collapsible sections
function openAllSections (override, node) {
var open = override === false ? false : override || params.openAllSections;
var container = node || articleDocument;
var blocks = container.querySelectorAll('details, section:not([data-mw-section-id="0"]), .collapsible-block, .collapsible-heading');
if (node) processSection(open, node);
for (var x = blocks.length; x--;) {
processSection(open, blocks[x]);
}
}
function processSection (open, node) {
if (/DETAILS|SECTION/.test(node.tagName)) {
if (open) node.setAttribute('open', '');
else node.removeAttribute('open');
if (typeof HTMLDetailsElement === 'undefined' || node.tagName === 'SECTION') {
var children = node.children;
for (var y = children.length; y--;) {
if (/SUMMARY|H\d/.test(children[y].tagName)) continue;
if (open) {
if (/DETAILS|SECTION/.test(children[y].tagName)) children[y].setAttribute('open', '');
children[y].style.removeProperty('display');
} else {
if (/DETAILS|SECTION/.test(children[y].tagName)) children[y].removeAttribute('open');
children[y].style.display = 'none';
}
}
}
} else {
if (open) node.classList.add('open-block');
else node.classList.remove('open-block');
}
}
// Attach listeners to headers to open-close following sections
function setupHeadings () {
var headings = document.getElementById('articleContent').querySelectorAll('h2, h3, h4, h5');
for (var i = headings.length; i--;) {
// Prevent heading from being selected when user clicks on it
headings[i].style.userSelect = 'none';
headings[i].style.msUserSelect = 'none';
headings[i].addEventListener('click', function (e) {
// Override the built-in simplistic polyfill
e.preventDefault();
var that = e.currentTarget;
var detailsEl = util.closest(that, 'details, section');
if (detailsEl) {
var toggle = !detailsEl.hasAttribute('open');
openAllSections(toggle, detailsEl);
}
});
}
}
params.preloadAllImages = function () {
if (params.preloadingAllImages !== true) {
setTimeout(function () {
if (params.preloadingAllImages) {
uiUtil.pollSpinner('Extracting images...');
}
}, 1000);
params.preloadingAllImages = true;
if (params.imageDisplay) {
params.contentInjectionMode === 'jquery'
? images.prepareImagesJQuery(articleWindow, true) : images.prepareImagesServiceWorker(articleWindow, true);
}
return;
}
// All images should now be loaded, or else user did not request loading images
uiUtil.clearSpinner();
uiUtil.extractHTML();
uiUtil.clearSpinner();
};
// Load Javascript content
function loadJavaScriptJQuery () {
$('#articleContent').contents().find('script[data-kiwixurl]').each(function () {
var script = $(this);
var scriptUrl = script.attr('data-kiwixurl');
// TODO check that the type of the script is text/javascript or application/javascript
var title = uiUtil.removeUrlParameters(decodeURIComponent(scriptUrl));
appstate.selectedArchive.getDirEntryByPath(title).then(function (dirEntry) {
if (dirEntry === null) {
console.log('Error: js file not found: ' + title);
} else {
appstate.selectedArchive.readBinaryFile(dirEntry, function (fileDirEntry, content) {
// TODO : JavaScript support not yet functional [kiwix-js #152]
uiUtil.feedNodeWithBlob(script, 'src', content, 'text/javascript', params.manipulateImages || params.allowHTMLExtraction);
});
}
}).catch(function (e) {
console.error('could not find DirEntry for javascript : ' + title, e);
});
});
}
/**
* Changes the URL of the browser page, so that the user might go back to it
*
* @param {String} title The title of the article to store (if storing an article)
* @param {String} titleSearch The title of the search (if storing a search)
*/
function pushBrowserHistoryState (title, titleSearch) {
// DEV: Note that appstate.target will always be 'iframe' for title searches, so we do not need to account for that
var targetWin = appstate.target === 'iframe' ? window : articleWindow;
var stateObj = {};
var urlParameters;
var stateLabel;
if (title && !(title === '')) {
// Prevents creating a double history for the same page (wrapped to prevent exception in IE and Edge Legacy for tabs)
try {
if (targetWin.history.state && targetWin.history.state.title === title) return;
} catch (err) { console.error('Unable to access History for this window', err); return; }
stateObj.title = title;
urlParameters = '?title=' + title;
stateLabel = 'Wikipedia Article : ' + title;
} else if (titleSearch && !(titleSearch === '')) {
stateObj.titleSearch = titleSearch;
urlParameters = '?titleSearch=' + titleSearch;
stateLabel = 'Wikipedia search : ' + titleSearch;
} else return;
// Edge Legacy and IE cannot push history state to another window/tab and produce an exception;
// independent navigation history is therefore disabled for these browsers
try {
targetWin.history.pushState(stateObj, stateLabel, urlParameters);
} catch (error) {
history.pushState(stateObj, stateLabel, urlParameters);
}
}
/**
* Extracts the content of the given article pathname, or a downloadable file, from the ZIM
*
* @param {String} path The pathname (namespace + filename) to the article or file to be extracted
* @param {Boolean|String} download A Bolean value that will trigger download of title, or the filename that should
* be used to save the file in local FS (in HTML5 spec, a string value for the download attribute is optional)
* @param {String} contentType The mimetype of the downloadable file, if known
* @param {String} pathEnc The fully encoded version of the path for use with some Zimit archives
*/
function goToArticle (path, download, contentType, pathEnc) {
var pathForServiceWorker = path;
path = path.replace(/\??isKiwixHref/, '');
appstate.expectedArticleURLToBeDisplayed = path;
// This removes any search highlighting
clearFindInArticle();
var shortTitle = path.replace(/[^/]+\//g, '').substring(0, 18);
uiUtil.pollSpinner('Loading ' + shortTitle);
var zimName = appstate.selectedArchive._file.name.replace(/\.[^.]+$/, '').replace(/_\d+-\d+$/, '');
if (~path.indexOf(params.cachedStartPages[zimName])) {
goToMainArticle();
return;
}
appstate.selectedArchive.getDirEntryByPath(path).then(function (dirEntry) {
var mimetype = contentType || dirEntry ? dirEntry.getMimetype() : '';
if (dirEntry === null || dirEntry === undefined) {
uiUtil.clearSpinner();
console.error('Article with title ' + path + ' not found in the archive');
if (params.zimType === 'zimit') {
if (pathEnc) {
// We failed to get path, so we should try the fully encoded version instead
goToArticle(pathEnc, download, contentType);
} else {
var anchor = {
href: path.replace(/^(C\/)?A\//, ''),
target: '_blank'
};
uiUtil.warnAndOpenExternalLinkInNewTab(null, anchor)
setTab();
}
} else {
uiUtil.systemAlert('
Sorry, but we couldn\'t find the article:
' + path + '
in this archive!
');
}
} else if (download || /\/(epub|pdf|zip|.*opendocument|.*officedocument|tiff|mp4|webm|mpeg|octet-stream)\b/i.test(mimetype)) {
// PDFs can be treated as a special case, as they can be displayed directly in a browser window or tab in most browsers (but not UWP)
if (!/UWP/.test(params.appType) && params.contentInjectionMode === 'serviceworker' && (/\/pdf\b/.test(mimetype) || /\.pdf([?#]|$)/i.test(dirEntry.url))) {
window.open(document.location.pathname.replace(/[^/]+$/, '') + appstate.selectedArchive._file.name + '/' + pathForServiceWorker,
params.windowOpener === 'tab' ? '_blank' : 'Download PDF',
params.windowOpener === 'window' ? 'toolbar=0,location=0,menubar=0,width=800,height=600,resizable=1,scrollbars=1' : null);
} else {
download = true;
appstate.selectedArchive.readBinaryFile(dirEntry, function (fileDirEntry, content) {
uiUtil.displayFileDownloadAlert(path, download, mimetype, content);
uiUtil.clearSpinner();
});
}
} else {
// params.isLandingPage = false;
document.querySelectorAll('.alert').forEach(function (el) {
el.style.display = 'none';
});
readArticle(dirEntry);
}
}).catch(function (e) {
console.error('Error reading article with title ' + path, e);
if (params.appIsLaunching) goToMainArticle();
// Line below prevents bootloop
params.appIsLaunching = false;
});
}
function goToRandomArticle () {
if (appstate.selectedArchive !== null && appstate.selectedArchive.isReady()) {
uiUtil.pollSpinner();
appstate.selectedArchive.getRandomDirEntry(function (dirEntry) {
if (dirEntry === null || dirEntry === undefined) {
uiUtil.clearSpinner();
uiUtil.systemAlert('Error finding random article', 'Error finding article');
} else {
// We fall back to the old A namespace to support old ZIM files without a text/html MIME type for articles
// DEV: If minorVersion is 1, then we are using a v1 article-only title listing. By definition,
// all dirEntries in an article-only listing must be articles.
if (appstate.selectedArchive._file.minorVersion === 1 || /text\/html\b/i.test(dirEntry.getMimetype()) ||
params.zimType !== 'zimit' && dirEntry.namespace === 'A') {
params.isLandingPage = false;
alertBoxHeader.style.display = 'none';
readArticle(dirEntry);
} else {
// If the random title search did not end up on an article,
// we try again, until we find one
goToRandomArticle();
}
}
});
} else {
// Showing the relevant error message and redirecting to config page for adding the ZIM file
uiUtil.systemAlert('Archive not set: please select an archive', 'No archive selected').then(function () {
document.getElementById('btnConfigure').click();
});
}
}
function goToMainArticle () {
uiUtil.pollSpinner();
params.isLandingPage = true;
appstate.selectedArchive.getMainPageDirEntry(function (dirEntry) {
if (dirEntry === null || dirEntry === undefined) {
params.isLandingPage = false;
console.error('Error finding main article.');
uiUtil.clearSpinner();
document.getElementById('welcomeText').style.display = '';
uiUtil.systemAlert('We cannot find the landing page!
' +
'Please check that this ZIM archive is valid. You may be able to search for other pages in the ZIM above.',
'Main page not found!');
} else {
// DEV: see comment above under goToRandomArticle()
if (dirEntry.redirect || /text/.test(dirEntry.getMimetype()) || dirEntry.namespace === 'A') {
params.isLandingPage = true;
appstate.selectedArchive.landingPageUrl = dirEntry.namespace + '/' + dirEntry.url;
readArticle(dirEntry);
} else {
params.isLandingPage = false;
console.error('The main page of this archive does not seem to be an article');
uiUtil.clearSpinner();
document.getElementById('welcomeText').style.display = '';
uiUtil.systemAlert('The main page of this archive does not seem to be an article!
' +
'Please check that this ZIM archive is valid. You may be able to search for other pages in the ZIM above.',
'Invalid article!');
}
}
});
}
export default {};