Display spinner icon when asynchronous tasks are done

This commit is contained in:
mossroy 2013-06-06 22:05:58 +02:00
parent ff6072bb26
commit ec5e4cd2f0
3 changed files with 339 additions and 329 deletions

BIN
img/spinner.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 583 B

View File

@ -1,93 +1,97 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Evopedia</title> <title>Evopedia</title>
<meta name="description" content="Offline wikipedia reader"> <meta name="description" content="Offline wikipedia reader">
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<!-- <!--
Port of Evopedia (offline wikipedia reader) in HTML5/Javascript, with Firefox OS as the primary target Port of Evopedia (offline wikipedia reader) in HTML5/Javascript, with Firefox OS as the primary target
The original application is at http://www.evopedia.info/ The original application is at http://www.evopedia.info/
It uses wikipedia dumps located at http://dumpathome.evopedia.info/dumps/finished It uses wikipedia dumps located at http://dumpathome.evopedia.info/dumps/finished
Author : Mossroy - mossroy@free.fr Author : Mossroy - mossroy@free.fr
License: License:
This program is free software; you can redistribute it and/or modify it This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the 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 Free Software Foundation; either version 3 of the License, or (at your
option) any later version. option) any later version.
This program is distributed in the hope that it will be useful, but This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details. General Public License for more details.
You should have received a copy of the GNU General Public You should have received a copy of the GNU General Public
License along with this program; if not, see License along with this program; if not, see
<http://www.gnu.org/licenses/>. <http://www.gnu.org/licenses/>.
--> -->
<!-- TODO : implement CSS <link rel="stylesheet" href="css/app.css"> --> <!-- TODO : implement CSS <link rel="stylesheet" href="css/app.css"> -->
</head> </head>
<body> <body>
<h1>Evopedia</h1> <h1>Evopedia</h1>
<div id="content"> <div id="content">
<input type="button" id="showHideAbout" value="About" /> <input type="button" id="showHideAbout" value="About" />
<div id="about" style="display: none;"> <div id="about" style="display: none;">
This is a preliminary work on the port of Evopedia (offline wikipedia reader) in HTML5/Javascript, with Firefox OS as the primary target This is a preliminary work on the port of Evopedia (offline wikipedia reader) in HTML5/Javascript, with Firefox OS as the primary target
<br /> <br />
The original application is at <a href="http://www.evopedia.info/">http://www.evopedia.info/</a> The original application is at <a href="http://www.evopedia.info/">http://www.evopedia.info/</a>
<br /> <br />
<br /> <br />
To use it, you have to first download locally a dump from <a href="http://dumpathome.evopedia.info/dumps/finished">http://dumpathome.evopedia.info/dumps/finished</a> (with a Bittorrent client). To use it, you have to first download locally a dump from <a href="http://dumpathome.evopedia.info/dumps/finished">http://dumpathome.evopedia.info/dumps/finished</a> (with a Bittorrent client).
<br /> <br />
<ul> <ul>
<li>On desktops, it works on recent Firefox and Chrome, and maybe on other browsers. In this case, you have to select manually some files from your dump (see below)</li> <li>On desktops, it works on recent Firefox and Chrome, and maybe on other browsers. In this case, you have to select manually some files from your dump (see below)</li>
<li>On the Firefos OS simulator, you have to put the archive files in a "fake-sdcard" folder of your firefox profile (ex : ~/.mozilla/firefox/xxxx.default/extensions/r2d2b2g@mozilla.org/profile/fake-sdcard). It looks for file titles.idx in it. You need to install the application from the dashboard of the simulator (due to security restrictions in Firefox OS : only certified webapps can access the sdcard)</li> <li>On the Firefos OS simulator, you have to put the archive files in a "fake-sdcard" folder of your firefox profile (ex : ~/.mozilla/firefox/xxxx.default/extensions/r2d2b2g@mozilla.org/profile/fake-sdcard). It looks for file titles.idx in it. You need to install the application from the dashboard of the simulator (due to security restrictions in Firefox OS : only certified webapps can access the sdcard)</li>
<li>On a real Firefox OS device, you simply have to put the archive files anywhere in your sdcard, so that it finds titles.idx on it. For now, the application has to be installed manually (use the push feature of the Firefos OS Simulator)</li> <li>On a real Firefox OS device, you simply have to put the archive files anywhere in your sdcard, so that it finds titles.idx on it. For now, the application has to be installed manually (use the push feature of the Firefos OS Simulator)</li>
</ul> </ul>
<br /> <br />
It's still a beta version : there are many many ways this could be enhanced (suggestions and patches are welcome : the source code is on <a href="https://github.com/mossroy/evopedia-html5">github</a>). In particular : It's still a beta version : there are many many ways this could be enhanced (suggestions and patches are welcome : the source code is on <a href="https://github.com/mossroy/evopedia-html5">github</a>). In particular :
<ul> <ul>
<li>The performance has to be optimized when searching titles and reading an article</li> <li>The performance has to be optimized when searching titles and reading an article</li>
<li>Some searches (for example with prefix "a" on the French dump) do not give any result even if they should</li> <li>Some searches (for example with prefix "a" on the French dump) do not give any result even if they should</li>
<li>In some cases, the links inside an article do not work, or do not lead to the right article</li> <li>In some cases, the links inside an article do not work, or do not lead to the right article</li>
<li>It is not easy to use on the Peak device from Geeksphone because the buttons and inputs are too small</li> <li>It is not easy to use on the Peak device from Geeksphone because the buttons and inputs are too small</li>
<li>Some features of the original application still have to be implemented : in particular geolocation of articles and math formulas</li> <li>Some features of the original application still have to be implemented : in particular geolocation of articles and math formulas</li>
</ul> </ul>
</div> </div>
<div id="openLocalFiles" style="display: none;"> <div id="openLocalFiles" style="display: none;">
<br /> Please select the file titles.idx :<br /> <input type="file" <br /> Please select the file titles.idx :<br /> <input type="file"
id="titleFile" /><br /> Please select the files wikipedia_*.dat id="titleFile" /><br /> Please select the files wikipedia_*.dat
from the same dump :<br /> <input type="file" id="dataFiles" multiple /> from the same dump :<br /> <input type="file" id="dataFiles" multiple />
</div> </div>
<div id="scanningForArchives" style="display: none;"> <div id="scanningForArchives" style="display: none;">
<br /> Scanning your sdcard for archives... Please wait <br /> Scanning your sdcard for archives... Please wait <img src="img/spinner.gif" />
</div> </div>
<div id="chooseArchiveFromLocalStorage" style="display: none;"> <div id="chooseArchiveFromLocalStorage" style="display: none;">
<br /> Please select the archive you want to use : <select id="archiveList"></select> <br /> Please select the archive you want to use : <select id="archiveList"></select>
</div> </div>
<br /> Find titles starting with : <br /> Find titles starting with :
<input type="text" id="prefix" value="" />&nbsp; <input type="text" id="prefix" value="" />&nbsp;
<input type="button" id="searchTitles" value="Search titles" /> <input type="button" id="searchTitles" value="Search titles" />
<br /> Choose a title from the filtered list : <img id="searchingForTitles" src="img/spinner.gif" style="display: none;"/>
<select id="titleList"></select> <br /> Choose a title from the filtered list :
<br /> <select id="titleList"></select>
<input type="button" id="readData" value="Read article from dump" /> <br />
<div id="articleContent">&nbsp;</div> <input type="button" id="readData" value="Read article from dump" />
<hr /> <div id="readingArticle" style="display: none;">
</div> Reading article from dump... Please wait <img src="img/spinner.gif" />
</div>
<!-- Using require.js, a module system for javascript, include the <div id="articleContent">&nbsp;</div>
js files. This loads "main.js", which in turn can load other <hr />
files, all handled by require.js: </div>
http://requirejs.org/docs/api.html#jsfiles -->
<script type="text/javascript" <!-- Using require.js, a module system for javascript, include the
data-main="js/init.js" js files. This loads "main.js", which in turn can load other
src="js/lib/require.js"></script> files, all handled by require.js:
</body> http://requirejs.org/docs/api.html#jsfiles -->
</html> <script type="text/javascript"
data-main="js/init.js"
src="js/lib/require.js"></script>
</body>
</html>

478
js/app.js
View File

@ -1,236 +1,242 @@
// This uses require.js to structure javascript: // This uses require.js to structure javascript:
// http://requirejs.org/docs/api.html#define // http://requirejs.org/docs/api.html#define
define(function(require) { define(function(require) {
// Zepto provides nice js and DOM methods (very similar to jQuery, // Zepto provides nice js and DOM methods (very similar to jQuery,
// and a lot smaller): // and a lot smaller):
// http://zeptojs.com/ // http://zeptojs.com/
var $ = require('zepto'); var $ = require('zepto');
// Evopedia javascript dependencies // Evopedia javascript dependencies
var evopedia = require('evopedia'); var evopedia = require('evopedia');
var localArchive = null; var localArchive = null;
// Define behavior of HTML elements // Define behavior of HTML elements
$('#about').hide(); $('#about').hide();
$('#showHideAbout').on('click', function(e) { $('#showHideAbout').on('click', function(e) {
$('#about').toggle(); $('#about').toggle();
}); });
$('#searchTitles').on('click', function(e) { $('#searchTitles').on('click', function(e) {
searchTitlesFromPrefix($('#prefix').val()); searchTitlesFromPrefix($('#prefix').val());
}); });
$('#readData').on('click', function(e) { $('#readData').on('click', function(e) {
var titleId = $('#titleList').val(); var titleId = $('#titleList').val();
findTitleFromTitleIdAndLaunchArticleRead(titleId); findTitleFromTitleIdAndLaunchArticleRead(titleId);
var title = evopedia.Title.parseTitleId(localArchive,titleId); var title = evopedia.Title.parseTitleId(localArchive,titleId);
pushBrowserHistoryState(title.name); pushBrowserHistoryState(title.name);
}); });
$('#prefix').on('keyup', function(e) { $('#prefix').on('keyup', function(e) {
onKeyUpPrefix(e); onKeyUpPrefix(e);
}); });
// Detect if DeviceStorage is available // Detect if DeviceStorage is available
var storage = null; var storage = null;
if ($.isFunction(navigator.getDeviceStorage)) { if ($.isFunction(navigator.getDeviceStorage)) {
storage = navigator.getDeviceStorage('sdcard'); storage = navigator.getDeviceStorage('sdcard');
} }
if (storage != null) { if (storage != null) {
// If DeviceStorage is available, we look for archives in it // If DeviceStorage is available, we look for archives in it
$('#scanningForArchives').show(); $('#scanningForArchives').show();
evopedia.LocalArchive.scanForArchives(storage,populateDropDownListOfArchives); evopedia.LocalArchive.scanForArchives(storage,populateDropDownListOfArchives);
} }
else { else {
// If DeviceStorage is not available, we display the file select components // If DeviceStorage is not available, we display the file select components
displayFileSelect(); displayFileSelect();
setLocalArchiveFromFileSelect(); setLocalArchiveFromFileSelect();
} }
// Display the article when the user goes back in the browser history // Display the article when the user goes back in the browser history
window.onpopstate = function(event) { window.onpopstate = function(event) {
var titleName = event.state.titleName; var titleName = event.state.titleName;
goToArticle(titleName); goToArticle(titleName);
}; };
/** /**
* Populate the drop-down list of titles with the given list * Populate the drop-down list of titles with the given list
*/ */
function populateDropDownListOfArchives(archiveDirectories) { function populateDropDownListOfArchives(archiveDirectories) {
$('#scanningForArchives').hide(); $('#scanningForArchives').hide();
$('#chooseArchiveFromLocalStorage').show(); $('#chooseArchiveFromLocalStorage').show();
var comboArchiveList = document.getElementById('archiveList'); var comboArchiveList = document.getElementById('archiveList');
comboArchiveList.options.length = 0; comboArchiveList.options.length = 0;
for (var i=0; i<archiveDirectories.length; i++) { for (var i=0; i<archiveDirectories.length; i++) {
var archiveDirectory = archiveDirectories[i]; var archiveDirectory = archiveDirectories[i];
comboArchiveList.options[i] = new Option (archiveDirectory, archiveDirectory); comboArchiveList.options[i] = new Option (archiveDirectory, archiveDirectory);
} }
$('#archiveList').on('change', setLocalArchiveFromArchiveList); $('#archiveList').on('change', setLocalArchiveFromArchiveList);
if (archiveDirectories.length>0) { if (archiveDirectories.length>0) {
// Set the localArchive from the first result // Set the localArchive from the first result
setLocalArchiveFromArchiveList(); setLocalArchiveFromArchiveList();
} }
else { else {
alert("No Evopedia archive found in your sdcard. Please see 'About' for more info"); alert("No Evopedia archive found in your sdcard. Please see 'About' for more info");
} }
} }
/** /**
* Sets the localArchive from the selected archive in the drop-down list * Sets the localArchive from the selected archive in the drop-down list
*/ */
function setLocalArchiveFromArchiveList() { function setLocalArchiveFromArchiveList() {
var archiveDirectory = $('#archiveList').val(); var archiveDirectory = $('#archiveList').val();
localArchive = new evopedia.LocalArchive(); localArchive = new evopedia.LocalArchive();
localArchive.readTitleFile(storage, archiveDirectory); localArchive.readTitleFile(storage, archiveDirectory);
localArchive.readDataFiles(storage, archiveDirectory, 0); localArchive.readDataFiles(storage, archiveDirectory, 0);
} }
/** /**
* Displays the zone to select files from the dump * Displays the zone to select files from the dump
*/ */
function displayFileSelect() { function displayFileSelect() {
$('#openLocalFiles').show(); $('#openLocalFiles').show();
$('#dataFiles').on('change', setLocalArchiveFromFileSelect); $('#dataFiles').on('change', setLocalArchiveFromFileSelect);
$('#titleFile').on('change', setLocalArchiveFromFileSelect); $('#titleFile').on('change', setLocalArchiveFromFileSelect);
} }
/** /**
* Sets the localArchive from the File selects populated by user * Sets the localArchive from the File selects populated by user
*/ */
function setLocalArchiveFromFileSelect() { function setLocalArchiveFromFileSelect() {
dataFiles=document.getElementById('dataFiles').files; dataFiles=document.getElementById('dataFiles').files;
titleFile=document.getElementById('titleFile').files[0]; titleFile=document.getElementById('titleFile').files[0];
localArchive = new evopedia.LocalArchive(); localArchive = new evopedia.LocalArchive();
localArchive.dataFiles = dataFiles; localArchive.dataFiles = dataFiles;
localArchive.titleFile = titleFile; localArchive.titleFile = titleFile;
} }
/** /**
* Handle Enter key in the prefix input zone * Handle Enter key in the prefix input zone
*/ */
function onKeyUpPrefix(evt) { function onKeyUpPrefix(evt) {
if (evt.keyCode == 13) { if (evt.keyCode == 13) {
document.getElementById("searchTitles").click(); document.getElementById("searchTitles").click();
} }
} }
/** /**
* Search the index for titles that start with the given prefix (implemented * Search the index for titles that start with the given prefix (implemented
* with a binary search inside the index file) * with a binary search inside the index file)
*/ */
function searchTitlesFromPrefix(prefix) { function searchTitlesFromPrefix(prefix) {
if (localArchive.titleFile) { $('#searchingForTitles').show();
localArchive.findTitlesWithPrefix(prefix.trim(), populateDropDownListOfTitles); if (localArchive.titleFile) {
} else { localArchive.findTitlesWithPrefix(prefix.trim(), populateDropDownListOfTitles);
alert("Title file not set"); } else {
} alert("Title file not set");
} }
}
/**
* Populate the drop-down list of titles with the given list /**
*/ * Populate the drop-down list of titles with the given list
function populateDropDownListOfTitles(titleList) { */
var comboTitleList = document.getElementById('titleList'); function populateDropDownListOfTitles(titleList) {
// Remove previous results var comboTitleList = document.getElementById('titleList');
comboTitleList.options.length = 0; // Remove previous results
for (var i=0; i<titleList.length; i++) { comboTitleList.options.length = 0;
var title = titleList[i]; for (var i=0; i<titleList.length; i++) {
comboTitleList.options[i] = new Option (title.name, title.toStringId()); var title = titleList[i];
} comboTitleList.options[i] = new Option (title.name, title.toStringId());
} }
$('#searchingForTitles').hide();
}
/**
* Creates an instance of title from given titleId (including resolving redirects),
* and call the function to read the corresponding article /**
*/ * Creates an instance of title from given titleId (including resolving redirects),
function findTitleFromTitleIdAndLaunchArticleRead(titleId) { * and call the function to read the corresponding article
if (localArchive.dataFiles && localArchive.dataFiles.length>0) { */
var title = evopedia.Title.parseTitleId(localArchive,titleId); function findTitleFromTitleIdAndLaunchArticleRead(titleId) {
$("#articleContent").html("Loading from dump article " + title.name + " ..."); if (localArchive.dataFiles && localArchive.dataFiles.length>0) {
if (title.fileNr == 255) { var title = evopedia.Title.parseTitleId(localArchive,titleId);
localArchive.resolveRedirect(title, readArticle); $("#readingArticle").show();
} $("#articleContent").html("Loading article " + title.name);
else { if (title.fileNr == 255) {
readArticle(title); localArchive.resolveRedirect(title, readArticle);
} }
} else {
else { readArticle(title);
alert("Data files not set"); }
} }
} else {
alert("Data files not set");
/** }
* Read the article corresponding to the given title }
*/
function readArticle(title) { /**
if ($.isArray(title)) { * Read the article corresponding to the given title
title = title[0]; */
if (title.fileNr == 255) { function readArticle(title) {
localArchive.resolveRedirect(title, readArticle); if ($.isArray(title)) {
return; title = title[0];
} if (title.fileNr == 255) {
} localArchive.resolveRedirect(title, readArticle);
localArchive.readArticle(title, displayArticleInForm); return;
} }
}
/** localArchive.readArticle(title, displayArticleInForm);
* Display the the given HTML article in the web page, }
* and convert links to javascript calls
*/ /**
function displayArticleInForm(title, htmlArticle) { * Display the the given HTML article in the web page,
// Display the article inside the web page. * and convert links to javascript calls
$('#articleContent').html(htmlArticle); */
function displayArticleInForm(title, htmlArticle) {
// Convert links into javascript calls $("#readingArticle").hide();
$('#articleContent').find('a').each(function(){
// Store current link's url // Display the article inside the web page.
var url = $(this).attr("href"); $('#articleContent').html(htmlArticle);
if(url.slice(0, 1) == "#") { // Convert links into javascript calls
// It's an anchor link : do nothing $('#articleContent').find('a').each(function(){
} // Store current link's url
else if (url.substring(0,4) === "http") { var url = $(this).attr("href");
// It's an external link : do nothing
} if(url.slice(0, 1) == "#") {
else if (url.substring(0,2) === ".." || url.substring(0,4) === "./..") { // It's an anchor link : do nothing
// It's a link to another language : TODO redirect to the online article? }
} else if (url.substring(0,4) === "http") {
else { // It's an external link : do nothing
// It's a link to another article : add an onclick event to go to this article }
// instead of following the link else if (url.substring(0,2) === ".." || url.substring(0,4) === "./..") {
$(this).on('click', function(e) { // It's a link to another language : TODO redirect to the online article?
var titleName = decodeURIComponent($(this).attr("href")); }
pushBrowserHistoryState(titleName); else {
goToArticle(titleName); // It's a link to another article : add an onclick event to go to this article
return false; // instead of following the link
}); $(this).on('click', function(e) {
} var titleName = decodeURIComponent($(this).attr("href"));
}); pushBrowserHistoryState(titleName);
} goToArticle(titleName);
return false;
/** });
* Changes the URL of the browser page }
*/ });
function pushBrowserHistoryState(titleName) { }
if (titleName) {
var stateObj = { titleName: titleName}; /**
window.history.pushState(stateObj,"Wikipedia Article : " + titleName,"#" + titleName); * Changes the URL of the browser page
} */
} function pushBrowserHistoryState(titleName) {
if (titleName) {
var stateObj = { titleName: titleName};
/** window.history.pushState(stateObj,"Wikipedia Article : " + titleName,"#" + titleName);
* Replace article content with the one of the given title }
*/ }
function goToArticle(titleName) {
$("#articleContent").html("Loading from dump article " + titleName + " ...");
localArchive.getTitleByName(titleName, readArticle); /**
} * Replace article content with the one of the given title
*/
}); function goToArticle(titleName) {
$("#readingArticle").show();
$("#articleContent").html("Loading article " + titleName);
localArchive.getTitleByName(titleName, readArticle);
}
});