diff --git a/www/js/lib/transformStyles.js b/www/js/lib/transformStyles.js index b712e64b..f7f62e4c 100644 --- a/www/js/lib/transformStyles.js +++ b/www/js/lib/transformStyles.js @@ -2,22 +2,22 @@ * transformStyles.js: Provides transformations in CSS of Wikipedia articles contained in the ZIM file * This allows the user to choose the presentation style for the page to be viewed. * Currently available are "mobile" and "desktop" display modes. - * + * * Copyright 2017 Kiwix developers * License GPL v3: - * + * * This file is part of Kiwix. - * + * * Kiwix is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * Kiwix is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with Kiwix (file LICENSE-GPLv3.txt). If not, see */ @@ -25,50 +25,52 @@ * transformStyles.js: Provides transformations in CSS of Wikipedia articles contained in the ZIM file * This allows the user to choose the presentation style for the page to be viewed. * Currently available are "mobile" and "desktop" display modes. - * + * * Copyright 2017 Kiwix developers * License GPL v3: - * + * * This file is part of Kiwix. - * + * * Kiwix is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * Kiwix is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with Kiwix (file LICENSE-GPLv3.txt). If not, see */ 'use strict'; import util from './util.js'; -import uiUtil from './uiUtil.js'; + +/* global params */ + var prefix = (window.location.protocol + '//' + window.location.host + window.location.pathname).replace(/\/[^/]*$/, '/'); /* zl = zimLink; zim = zimType; cc = cssCache; cs = cssSource; i] */ -function filterCSS(zl, zim, cc, cs, i) { - var rtnFunction = "injectCSS"; - if ((zim != cs) && zl.match(/(-\/(?:s\/css_modules\/|mw\/)?style\.css)|minerva|mobile|parsoid/i)) { //If it's the wrong ZIM type and style matches main styles... - if (zl.match(/-\/(?:s\/|mw\/)?style\.css|minerva|style-mobile\.css/i)) { //If it matches one of the required styles... - zl = (cs == "mobile") ? prefix + "-/s/style-mobile.css" : prefix + "-/s/style.css"; //Take it from cache, because not in the ZIM - console.log("Matched #" + i + " [" + zl + "] from local filesystem because style is not in ZIM" + - "\nbut your display options require a " + cs + " style"); +function filterCSS (zl, zim, cc, cs, i) { + var rtnFunction = 'injectCSS'; + if ((zim !== cs) && zl.match(/(-\/(?:s\/css_modules\/|mw\/)?style\.css)|minerva|mobile|parsoid/i)) { // If it's the wrong ZIM type and style matches main styles... + if (zl.match(/-\/(?:s\/|mw\/)?style\.css|minerva|style-mobile\.css/i)) { // If it matches one of the required styles... + zl = (cs === 'mobile') ? prefix + '-/s/style-mobile.css' : prefix + '-/s/style.css'; // Take it from cache, because not in the ZIM + console.log('Matched #' + i + ' [' + zl + '] from local filesystem because style is not in ZIM' + + '\nbut your display options require a ' + cs + ' style'); } - if (cs == "desktop" && /minerva|mobile|parsoid|(css_modules|mw)\/style\.css/.test(zl) && !/(css_modules|mw)\/mobile_main_page\.css/.test(zl)) { - //If user selected desktop style and style is one of the mobile styles, but not mobile_main_page for newstyle all image homepages - console.log("Voiding #" + i + " [" + zl + "] from document header \nbecause your display options require a desktop style"); - zl = "#"; //Void these mobile styles + if (cs === 'desktop' && /minerva|mobile|parsoid|(css_modules|mw)\/style\.css/.test(zl) && !/(css_modules|mw)\/mobile_main_page\.css/.test(zl)) { + // If user selected desktop style and style is one of the mobile styles, but not mobile_main_page for newstyle all image homepages + console.log('Voiding #' + i + ' [' + zl + '] from document header \nbecause your display options require a desktop style'); + zl = '#'; // Void these mobile styles } // Rename this required mobile style so that we don't trigger reading ZIM as mobile in print intercept - zl = /(css_modules|mw)\/mobile_main_page\.css/.test(zl) && cs == 'desktop' ? prefix + "-/s/css_modules/newstyle_main_page.css" : zl; + zl = /(css_modules|mw)\/mobile_main_page\.css/.test(zl) && cs === 'desktop' ? prefix + '-/s/css_modules/newstyle_main_page.css' : zl; } else { - //If this is a standard Wikipedia css use stylesheet cached in the filesystem... - //DEV: Although "." matches any character in regex, there is enough specificity in the patterns below - //DEV: Add any local stylesheets you wish to include here + // If this is a standard Wikipedia css use stylesheet cached in the filesystem... + // DEV: Although "." matches any character in regex, there is enough specificity in the patterns below + // DEV: Add any local stylesheets you wish to include here if (cc && (/-\/(?:s\/|s\/css_modules\/|mw\/)?style.css/i.test(zl) || /\/mediawiki.toc.css/i.test(zl) || @@ -102,47 +104,47 @@ function filterCSS(zl, zim, cc, cs, i) { /\/style-mobile.css/i.test(zl) || /\/skins.minerva.base.reset\|skins.minerva.content.styles\|ext.cite.style\|mediawiki.page.gallery.styles\|mobile.app.pagestyles.android\|mediawiki.skinning.content.parsoid.css/i.test(zl) )) { - zl = zl.replace(/\|/ig, "_"); //Replace "|" with "_" (legacy for some stylesheets with pipes in filename - but next line renders this redundant in current implementation) - if (/(-\/(?:s\/css_modules\/|mw\/)?style\.css)|minerva|inserted_style_mobile/i.test(zl)) { //If it matches one of the required styles... - zl = cs == "mobile" ? "-/s/style-mobile.css" : "-/s/style.css"; + zl = zl.replace(/\|/ig, '_'); // Replace "|" with "_" (legacy for some stylesheets with pipes in filename - but next line renders this redundant in current implementation) + if (/(-\/(?:s\/css_modules\/|mw\/)?style\.css)|minerva|inserted_style_mobile/i.test(zl)) { // If it matches one of the required styles... + zl = cs === 'mobile' ? '-/s/style-mobile.css' : '-/s/style.css'; } // Rename this required mobile style so that we don't trigger reading ZIM as mobile in print intercept - zl = /\/mobile_main_page\.css/.test(zl) ? "-/mw/newstyle_main_page.css" : zl; - zl = /\/content\.parsoid\.css/.test(zl) ? "-/s/css_modules/content.parsoid.css" : zl; - zl = /\/sotoki\.css/.test(zl) ? "C/static/css/sotoki.css" : zl; + zl = /\/mobile_main_page\.css/.test(zl) ? '-/mw/newstyle_main_page.css' : zl; + zl = /\/content\.parsoid\.css/.test(zl) ? '-/s/css_modules/content.parsoid.css' : zl; + zl = /\/sotoki\.css/.test(zl) ? 'C/static/css/sotoki.css' : zl; // Replace bootstrap with own: DEV: when upgrading to Bootstrap 4, stop doing this! - zl = zl.replace(/.+(bootstrap[^\/]*?\.css)/i, "css/$1"); - console.debug("Matched #" + i + " [" + zl + "] from local filesystem"); - //Make link absolute + zl = zl.replace(/.+(bootstrap[^/]*?\.css)/i, 'css/$1'); + console.debug('Matched #' + i + ' [' + zl + '] from local filesystem'); + // Make link absolute zl = zl.replace(/^[/.]*/, prefix); - //injectCSS(); - } else if (params.contentInjectionMode == 'jquery') { //Try to get the stylesheet from the ZIM file unless it's the wrong ZIM type - zl = zl.replace(/^[./]+/, ""); //Remove the directory path - console.debug("Attempting to resolve CSS link #" + i + " [" + zl + "] from ZIM file..." + - (cc ? "\n(Consider adding file #" + i + " to the local filesystem)" : "")); - rtnFunction = "resolveCSS"; + // injectCSS(); + } else if (params.contentInjectionMode === 'jquery') { // Try to get the stylesheet from the ZIM file unless it's the wrong ZIM type + zl = zl.replace(/^[./]+/, ''); // Remove the directory path + console.debug('Attempting to resolve CSS link #' + i + ' [' + zl + '] from ZIM file...' + + (cc ? '\n(Consider adding file #' + i + ' to the local filesystem)' : '')); + rtnFunction = 'resolveCSS'; } } return { zl: zl, rtnFunction: rtnFunction }; } -function toMobileCSS(html, zim, cc, cs, css) { - //DEV: Careful not to add styles twice... - //NB Can't relocate to filterCSS function above because it filters styles serially and code would be called for every style... - if (zim == "desktop" && zim != cs) { //If ZIM doesn't match user-requested style, add in stylesheets if they're missing - css += /\/content\.parsoid\.css/i.test(css) ? "" : '\r\n'; - css += /\/inserted_style_mobile\.css/i.test(css) ? "" : '\r\n'; - css += /\/mobile\.css/i.test(css) ? "" : '\r\n'; +function toMobileCSS (html, zim, cc, cs, css) { + // DEV: Careful not to add styles twice... + // NB Can't relocate to filterCSS function above because it filters styles serially and code would be called for every style... + if (zim === 'desktop' && zim !== cs) { // If ZIM doesn't match user-requested style, add in stylesheets if they're missing + css += /\/content\.parsoid\.css/i.test(css) ? '' : '\r\n'; + css += /\/inserted_style_mobile\.css/i.test(css) ? '' : '\r\n'; + css += /\/mobile\.css/i.test(css) ? '' : '\r\n'; } - if (cc || (zim == "desktop")) { //If user requested cached styles OR the ZIM does not contain mobile styles - console.log(zim == "desktop" ? "Transforming display style to mobile..." : "Optimizing cached styles for mobile display..."); + if (cc || (zim === 'desktop')) { // If user requested cached styles OR the ZIM does not contain mobile styles + console.log(zim === 'desktop' ? 'Transforming display style to mobile...' : 'Optimizing cached styles for mobile display...'); // Add styling to image captions that is hard-coded in Wikipedia mobile html = html.replace(/class\s*=\s*["']\s*thumbcaption\s*["']\s*/ig, 'style="margin: 0.5em 0 0.5em; font-size: 0.8em; line-height: 1.5; padding: 0 !important; color: #54595d; width: auto !important;"'); // Wrap

tags in
to control bottom border width if there's an infobox, but not if it's a new-style ZIM with collapsible details tags if (!/]*class\s*=\s*["'][^"']*(?:mw-stack|infobox|vertical-navbox|qbRight|wv-quickbar))/i.test(html) ? html.replace(/()/ig, '
$1
') : html; } - if (zim == "desktop") { + if (zim === 'desktop') { var infobox = []; if (/]+(?:mw-stack|infobox|vertical-navbox|qbRight|wv-quickbar)/i.test(html)) { infobox = util.matchOuter(html, ']+(?:mw-stack|infobox|vertical-navbox|qbRight|wv-quickbar)[^>]+>', '', 'i'); @@ -152,84 +154,84 @@ function toMobileCSS(html, zim, cc, cs, css) { } } if (infobox.length) { - var temphtml = html.replace(infobox[0], ""); + var temphtml = html.replace(infobox[0], ''); var paras = util.matchInner(temphtml, ']*>', '

', 'gi'); var matched = false; if (paras.length) { for (var g = 0; g < 3; g++) { - //Check if the paragraph is a proper sentence, i.e. contains at least 50 non-HTML-delimetered non-full-stop characters, followed by a punctuation character - if (paras[g] && /[^.]{50,}[^.]*[.,;:?!-]/.test(paras[g].replace(/<[^>]*>/g, ""))) { matched = true; break; } + // Check if the paragraph is a proper sentence, i.e. contains at least 50 non-HTML-delimetered non-full-stop characters, followed by a punctuation character + if (paras[g] && /[^.]{50,}[^.]*[.,;:?!-]/.test(paras[g].replace(/<[^>]*>/g, ''))) { matched = true; break; } } if (matched) { - //If there are navboxes below the infobox, hide them in mobile view + // If there are navboxes below the infobox, hide them in mobile view temphtml = temphtml.replace(/(]+navbox))(?:([^>]+?)style\s*=\s*["']([^"']+)["'])?/ig, '$1$2style="display:none;$3"'); - //Ensure mobile styling in infobox + // Ensure mobile styling in infobox infobox[0] = infobox[0].replace(/(<(?:table|div)\b(?=[^>]+?class\s*=\s*["'][^"']*(?:mw-stack|infobox|navbox)))([^>]+?style\s*=\s*["'])(?:([^"']*?)margin\s*:[^;"']*[;"'])?/ig, '$1$2margin:0 auto;$3'); - //Clear any fixed width but set it to max-width + // Clear any fixed width but set it to max-width infobox[0] = infobox[0].replace(/^(<(?:table|div)\b[^>]+?;\s*width\s*:\s*)([^;"']+)/i, '$1auto;max-width:$2'); - //Hide any navboxes inside the infobox + // Hide any navboxes inside the infobox infobox[0] = infobox[0].replace(/(<(?:table|div)\b(?=[^>]+?class\s*=\s*["'][^"']*navbox))(?:([^>]+?)style\s*=\s*["'])(?:([^"']*?)display\s*:[^;"']*[;"'])?/ig, '$1$2style="display:none;$3'); - //We already deleted the table above - html = ""; - //First try to move the lead paragraph - html = temphtml.replace(/(]*?\bid\s*=\s*["']mw-content-text\s*[^>]*>\s*)/i, "$1\r\n" + paras[g].replace(/(", infobox[0]); + // We already deleted the table above + html = ''; + // First try to move the lead paragraph + html = temphtml.replace(/(]*?\bid\s*=\s*["']mw-content-text\s*[^>]*>\s*)/i, '$1\r\n' + paras[g].replace(/(', infobox[0]); } else { - //So there was no match, let's try just swapping para and infobox - html = temphtml.replace(paras[g], paras[g].replace(/(", ""); + // So there was no match, let's try just swapping para and infobox + html = temphtml.replace(paras[g], paras[g].replace(/(', ''); } } } } } - //Set infobox styling hard-coded in Wikipedia mobile + // Set infobox styling hard-coded in Wikipedia mobile html = html.replace(/(table\s+(?=[^>]*class\s*=\s*["'][^"']*(?:mw-stack|infobox|vertical-navbox|qbRight|wv-quickbar))[^>]*style\s*=\s*["'][^"']+[^;'"]);?\s*["']/ig, '$1; position: relative; border: 1px solid #eaecf0; text-align: left; background-color: #f8f9fa;"'); } - //Remove hard-coded style on h1 + // Remove hard-coded style on h1 html = html.replace(/(]+)background-color\s*:\s*white;\s*/i, '$1'); - - return { html : html, css : css }; + + return { html: html, css: css }; } -function toDesktopCSS(html, zim, cc, cs, css) { - if (cc || (zim != cs)) { +function toDesktopCSS (html, zim, cc, cs, css) { + if (cc || (zim !== cs)) { if (/class\s*=\s*["']gallery/i.test(html) && !/gallery/i.test(css)) { - console.log("Inserting missing css required for gallery display [mediawiki.page.gallery.styles.css]..."); - css += /\/mediawiki\.page\.gallery\.styles\.css/i.test(css) ? "" : '\r\n'; + console.log('Inserting missing css required for gallery display [mediawiki.page.gallery.styles.css]...'); + css += /\/mediawiki\.page\.gallery\.styles\.css/i.test(css) ? '' : '\r\n'; } } - if (cc || (zim == "mobile")) { //If user requested cached styles OR the ZIM does not contain desktop styles - console.log(zim == "mobile" ? "Transforming display style to desktop..." : "Optimizing cached styles for desktop display..."); - //If it's in mobile position, move info-box above lead paragraph like on Wikipedia desktop - if (zim == "mobile") { - //Attempt to match div-style infobox first + if (cc || (zim === 'mobile')) { // If user requested cached styles OR the ZIM does not contain desktop styles + console.log(zim === 'mobile' ? 'Transforming display style to desktop...' : 'Optimizing cached styles for desktop display...'); + // If it's in mobile position, move info-box above lead paragraph like on Wikipedia desktop + if (zim === 'mobile') { + // Attempt to match div-style infobox first var tableBox = util.matchOuter(html, ']+?(?:infobox[^-]|vertical-navbox|qbRight|wv-quickbar)[^>]*>', '
', 'i'); - //If above failed we may have traditional table-style infobox + // If above failed we may have traditional table-style infobox tableBox = !(tableBox && tableBox.length) ? util.matchOuter(html, ']+?(?:mw-stack|infobox[^-]|vertical-navbox|qbRight|wv-quickbar)[^>]*>', '', 'i') : tableBox; if (tableBox && tableBox.length) { - html = html.replace(tableBox, ""); - html = html.replace(/(<\/h1>\s*)/i, "$1" + tableBox); + html = html.replace(tableBox, ''); + html = html.replace(/(<\/h1>\s*)/i, '$1' + tableBox); } } - //Ensure white background colour - html = html.replace(/(class\s*=\s*["']\s*mw-body\s*["'][^>]*?style\s*=\s*["'])/i, "$1background-color:white;"); - //Void empty header title - html = html.replace(/]+titleHeading[^>]+>\s*<\/h1>\s*/ig, ""); + // Ensure white background colour + html = html.replace(/(class\s*=\s*["']\s*mw-body\s*["'][^>]*?style\s*=\s*["'])/i, '$1background-color:white;'); + // Void empty header title + html = html.replace(/]+titleHeading[^>]+>\s*<\/h1>\s*/ig, ''); } - //Remove hard-coded style on h1 + // Remove hard-coded style on h1 html = html.replace(/(]+)background-color\s*:\s*white;\s*/i, '$1'); - //Remove hard-coded style on infobox + // Remove hard-coded style on infobox html = html.replace(/(]*?\bclass=['"][^'"]*infobox)[^>]*\bstyle=['"][^'"]*\b)(?:float:\s*none\s*;?\s*)(?:clear:\s*none\s*;?\s*)?/gi, '$1'); - //Remove any residual references to mobile styles (e.g. in data-kiwixhref) because they trigger page reload when printing + // Remove any residual references to mobile styles (e.g. in data-kiwixhref) because they trigger page reload when printing css = css.replace(/(]+)(minerva|mobile)/ig, '$1'); - return { html : html, css : css }; + return { html: html, css: css }; } export default { toMobileCSS: toMobileCSS, toDesktopCSS: toDesktopCSS, filterCSS: filterCSS -}; \ No newline at end of file +};