Skip to content

Commit

Permalink
Add active content warning #466 (#467)
Browse files Browse the repository at this point in the history
Closes #466 and #468.
  • Loading branch information
Jaifroid authored Jan 14, 2019
1 parent c353468 commit e595a49
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 8 deletions.
12 changes: 12 additions & 0 deletions www/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,18 @@
width: 200px;
}

.alert {
margin-bottom: 0 !important;
}

.alert-link {
text-decoration: underline;
}

#alertBoxHeader {
text-align: center;
}

@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
Expand Down
23 changes: 21 additions & 2 deletions www/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,8 @@ <h2>Configuration</h2>
<div id="openLocalFiles" style="display: none;">
Please select the .zim file (or all the .zimaa, .zimab etc in case of a split ZIM file)<br />
<input type="file" id="archiveFiles" multiple class="btn" accept=".zim,.dat,.idx,.txt,.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" /><br />
<strong>Only Mediawiki-based (wiki*.zim* files, like wikipedia) and StackExchange contents have been tested</strong> for now. Audio/video/dynamic contents are not supported for now.<br />
<strong>Only Mediawiki-based (wiki*.zim* files, like wikipedia), StackExchange and some video-based ZIMs (e.g. TEDx) have been tested</strong>.
Dynamic content is not currently supported in jQuery mode.<br />
</div>
<div id="scanningForArchives" style="display: none;">
<br /> Scanning for archives... Please wait <img src="img/spinner.gif" alt="Please wait..." />
Expand All @@ -216,6 +217,22 @@ <h2>Configuration</h2>
</div>

<div class="container">
<div class="row">
<h3>Display settings</h3>
<div class="column">
<div class="panel panel-info" id="displaySettingsDiv">
<div class="panel-heading">Display options:</div>
<div class="panel-body">
<div class="checkbox">
<label>
<input type="checkbox" name="hideActiveContentWarning" id="hideActiveContentWarningCheck">
<strong>Permanently hide active content warning</strong> (for experienced users)
</label>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<h3>Expert settings</h3>
<div class="column">
Expand All @@ -236,7 +253,7 @@ <h3>Expert settings</h3>
</div>
</div>
</div>
<div class="panel panel-info" id="apiStatusDiv">
<div class="panel panel-warning" id="apiStatusDiv">
<div class="panel-heading">API Status</div>
<div class="panel-body">
<div id="serviceWorkerStatus"></div>
Expand All @@ -261,6 +278,8 @@ <h3>Expert settings</h3>
<div id="articleList" class="list-group">
</div>
</div>
<!-- Bootstrap alert box -->
<div id="alertBoxHeader"></div>
<iframe id="articleContent" class="articleIFrame" src="article.html"></iframe>
</article>
<footer>
Expand Down
53 changes: 48 additions & 5 deletions www/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,18 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFiles
*/
var selectedArchive = null;

/**
* A global parameter object for storing variables that need to be remembered between page loads
* or across different functions
*
* @type Object
*/
var params = {};

// Set parameters and associated UI elements from cookie
params['hideActiveContentWarning'] = cookies.getItem('hideActiveContentWarning') === 'true';
document.getElementById('hideActiveContentWarningCheck').checked = params.hideActiveContentWarning;

/**
* Resize the IFrame height, so that it fills the whole available height in the window
*/
Expand Down Expand Up @@ -159,6 +171,7 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFiles
$('#articleListWithHeader').hide();
$("#searchingArticles").hide();
$('#articleContent').hide();
$('.alert').hide();
refreshAPIStatus();
return false;
});
Expand All @@ -178,22 +191,31 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFiles
$('#articleListWithHeader').hide();
$("#searchingArticles").hide();
$('#articleContent').hide();
$('.alert').hide();
return false;
});
$('input:radio[name=contentInjectionMode]').on('change', function(e) {
// Do the necessary to enable or disable the Service Worker
setContentInjectionMode(this.value);
});

$('input:checkbox[name=hideActiveContentWarning]').on('change', function (e) {
params.hideActiveContentWarning = this.checked ? true : false;
cookies.setItem('hideActiveContentWarning', params.hideActiveContentWarning, Infinity);
});

/**
* Displays of refreshes the API status shown to the user
*/
function refreshAPIStatus() {
var apiStatusPanel = document.getElementById('apiStatusDiv');
apiStatusPanel.classList.remove('panel-success', 'panel-warning');
var apiPanelClass = 'panel-success';
if (isMessageChannelAvailable()) {
$('#messageChannelStatus').html("MessageChannel API available");
$('#messageChannelStatus').removeClass("apiAvailable apiUnavailable")
.addClass("apiAvailable");
} else {
apiPanelClass = 'panel-warning';
$('#messageChannelStatus').html("MessageChannel API unavailable");
$('#messageChannelStatus').removeClass("apiAvailable apiUnavailable")
.addClass("apiUnavailable");
Expand All @@ -204,15 +226,19 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFiles
$('#serviceWorkerStatus').removeClass("apiAvailable apiUnavailable")
.addClass("apiAvailable");
} else {
apiPanelClass = 'panel-warning';
$('#serviceWorkerStatus').html("ServiceWorker API available, but not registered");
$('#serviceWorkerStatus').removeClass("apiAvailable apiUnavailable")
.addClass("apiUnavailable");
}
} else {
apiPanelClass = 'panel-warning';
$('#serviceWorkerStatus').html("ServiceWorker API unavailable");
$('#serviceWorkerStatus').removeClass("apiAvailable apiUnavailable")
.addClass("apiUnavailable");
}
apiStatusPanel.classList.add(apiPanelClass);

}

var contentInjectionMode;
Expand Down Expand Up @@ -640,6 +666,7 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFiles
*/
function searchDirEntriesFromPrefix(prefix) {
if (selectedArchive !== null && selectedArchive.isReady()) {
$('#activeContent').alert('close');
selectedArchive.findDirEntriesWithPrefix(prefix.trim(), MAX_SEARCH_RESULT_SIZE, populateListOfArticles);
} else {
$('#searchingArticles').hide();
Expand Down Expand Up @@ -713,12 +740,11 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFiles
$("#searchingArticles").show();
if (dirEntry.isRedirect()) {
selectedArchive.resolveRedirect(dirEntry, readArticle);
}
else {
} else {
params.isLandingPage = false;
readArticle(dirEntry);
}
}
else {
} else {
alert("Data files not set");
}
}
Expand Down Expand Up @@ -823,6 +849,13 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFiles
// remove any relative or absolute path from ZIM-style URLs.
// DEV: If you want to support more namespaces, add them to the END of the character set [-IJ] (not to the beginning)
var regexpTagsWithZimUrl = /(<(?:img|script|link|video|audio|source|track)\b[^>]*?\s)(?:src|href)(\s*=\s*["'])(?:\.\.\/|\/)+(?=[-IJ]\/)/ig;
// Regex below tests the html of an article for active content [kiwix-js #466]
// It inspects every <script> block in the html and matches in the following cases: 1) the script loads a UI application called app.js;
// 2) the script block has inline content that does not contain "importScript()" or "toggleOpenSection" (these strings are used widely
// in our fully supported wikimedia ZIMs, so they are excluded); 3) the script block is not of type "math" (these are MathJax markup
// scripts used extensively in Stackexchange ZIMs). Note that the regex will match ReactJS <script type="text/html"> markup, which is
// common in unsupported packaged UIs, e.g. PhET ZIMs.
var regexpActiveContent = /<script\b(?:(?![^>]+src\b)|(?=[^>]+src\b=["'][^"']+?app\.js))(?!>[^<]+(?:importScript\(\)|toggleOpenSection))(?![^>]+type\s*=\s*["'](?:math\/|[^"']*?math))/i;

// Cache for CSS styles contained in ZIM.
// It significantly speeds up subsequent page display. See kiwix-js issue #335
Expand All @@ -836,6 +869,11 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFiles
* @param {String} htmlArticle
*/
function displayArticleContentInIframe(dirEntry, htmlArticle) {
// Display Bootstrap warning alert if the landing page contains active content
if (!params.hideActiveContentWarning && params.isLandingPage) {
if (regexpActiveContent.test(htmlArticle)) uiUtil.displayActiveContentWarning();
}

// Replaces ZIM-style URLs of img, script, link and media tags with a data-kiwixurl to prevent 404 errors [kiwix-js #272 #376]
// This replacement also processes the URL to remove the path so that the URL is ready for subsequent jQuery functions
htmlArticle = htmlArticle.replace(regexpTagsWithZimUrl, '$1data-kiwixurl$2');
Expand Down Expand Up @@ -1132,6 +1170,8 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFiles
$("#searchingArticles").hide();
alert("Article with title " + title + " not found in the archive");
} else {
params.isLandingPage = false;
$('#activeContent').alert('close');
readArticle(dirEntry);
}
}).fail(function(e) { alert("Error reading article with title " + title + " : " + e); });
Expand All @@ -1145,6 +1185,8 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFiles
alert("Error finding random article.");
} else {
if (dirEntry.namespace === 'A') {
params.isLandingPage = false;
$('#activeContent').alert('close');
readArticle(dirEntry);
} else {
// If the random title search did not end up on an article,
Expand All @@ -1164,6 +1206,7 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies','abstractFiles
$("#welcomeText").show();
} else {
if (dirEntry.namespace === 'A') {
params.isLandingPage = true;
readArticle(dirEntry);
} else {
console.error("The main page of this archive does not seem to be an article");
Expand Down
38 changes: 37 additions & 1 deletion www/js/lib/uiUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,48 @@ define([], function() {
return url.replace(regexpRemoveUrlParameters, "$1");
}

/**
* Displays a Bootstrap warning alert with information about how to access content in a ZIM with unsupported active UI
*/
function displayActiveContentWarning() {
// We have to add the alert box in code, because Bootstrap removes it completely from the DOM when the user dismisses it
var alertHTML =
'<div id="activeContent" class="alert alert-warning alert-dismissible fade in">' +
'<a href="#" class="close" data-dismiss="alert" aria-label="close">&times;</a>' +
'<strong>Unable to display active content:</strong> This ZIM is not fully supported in jQuery mode.<br />' +
'Content may be available by searching above (type a space or a letter of the alphabet), or else ' +
'<a id="swModeLink" href="#contentInjectionModeDiv" class="alert-link">switch to Service Worker mode</a> ' +
'if your platform supports it. &nbsp;[<a id="stop" href="#displaySettingsDiv" class="alert-link">Permanently hide</a>]' +
'</div>';
document.getElementById('alertBoxHeader').innerHTML = alertHTML;
['swModeLink', 'stop'].forEach(function(id) {
// Define event listeners for both hyperlinks in alert box: these take the user to the Config tab and highlight
// the options that the user needs to select
document.getElementById(id).addEventListener('click', function () {
var elementID = id === 'stop' ? 'hideActiveContentWarningCheck' : 'serviceworkerModeRadio';
var thisLabel = document.getElementById(elementID).parentNode;
thisLabel.style.borderColor = 'red';
thisLabel.style.borderStyle = 'solid';
var btnHome = document.getElementById('btnHome');
[thisLabel, btnHome].forEach(function (ele) {
// Define event listeners to cancel the highlighting both on the highlighted element and on the Home tab
ele.addEventListener('mousedown', function () {
thisLabel.style.borderColor = '';
thisLabel.style.borderStyle = '';
});
});
document.getElementById('btnConfigure').click();
});
});
}

/**
* Functions and classes exposed by this module
*/
return {
feedNodeWithBlob: feedNodeWithBlob,
replaceCSSLinkWithInlineCSS: replaceCSSLinkWithInlineCSS,
removeUrlParameters: removeUrlParameters
removeUrlParameters: removeUrlParameters,
displayActiveContentWarning: displayActiveContentWarning
};
});

0 comments on commit e595a49

Please sign in to comment.