mirror of
https://github.com/kiwix/kiwix-js.git
synced 2025-09-24 04:54:51 -04:00
Hide the explanations on the main page. They can be shown with an
"About" button
This commit is contained in:
parent
59ba7bf1cc
commit
b35482fa1b
189
www/index.html
189
www/index.html
@ -1,93 +1,96 @@
|
|||||||
<!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/>.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<link rel="stylesheet" href="css/app.css">
|
<link rel="stylesheet" href="css/app.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- Use this installation button to install locally without going
|
<!-- Use this installation button to install locally without going
|
||||||
through the marketpace (see app.js) -->
|
through the marketpace (see app.js) -->
|
||||||
<!-- <button id="install-btn">Install</button> -->
|
<!-- <button id="install-btn">Install</button> -->
|
||||||
|
|
||||||
<!-- Write your application here -->
|
<!-- Write your application here -->
|
||||||
|
|
||||||
<h1>Evopedia</h1>
|
<h1>Evopedia</h1>
|
||||||
This is a preliminary work on the port of Evopedia (offline wikipedia reader) in HTML5/Javascript, with Firefox OS as the primary target
|
<input type="button" id="showHideAbout" value="About" />
|
||||||
<br />
|
<div id="about">
|
||||||
The original application is at <a href="http://www.evopedia.info/">http://www.evopedia.info/</a>
|
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 />
|
||||||
<br />
|
The original application is at <a href="http://www.evopedia.info/">http://www.evopedia.info/</a>
|
||||||
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), and select some of the dowloaded files below.
|
<br />
|
||||||
<br />
|
<br />
|
||||||
I have tested it with the <a href="http://evopedia.info/dumps/wikipedia_small_2010-08-14.torrent">small dump (2010-08-14)</a>, the <a href="http://evopedia.info/dumps/wikipedia_fr_2013-02-16.torrent">French dump (2013-02-16)</a>, the <a href="http://evopedia.info/dumps/wikipedia_frwiktionary_2011-03-16.torrent">French wiktionary dump (2011-03-16)</a> and the <a href="http://evopedia.info/dumps/wikipedia_en_2012-02-11.torrent">English dump (2012-02-11)</a>
|
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), and select some of the dowloaded files below.
|
||||||
<br />
|
<br />
|
||||||
<br />
|
I have tested it with the <a href="http://evopedia.info/dumps/wikipedia_small_2010-08-14.torrent">small dump (2010-08-14)</a>, the <a href="http://evopedia.info/dumps/wikipedia_fr_2013-02-16.torrent">French dump (2013-02-16)</a>, the <a href="http://evopedia.info/dumps/wikipedia_frwiktionary_2011-03-16.torrent">French wiktionary dump (2011-03-16)</a> and the <a href="http://evopedia.info/dumps/wikipedia_en_2012-02-11.torrent">English dump (2012-02-11)</a>
|
||||||
<ul>
|
<br />
|
||||||
<li>On desktops, it works on recent Firefox and Chrome, and maybe on other browsers</li>
|
<br />
|
||||||
<li>On the Firefos OS simulator, you have (for now) to put the French dump files in a "fake-sdcard/evopedia" folder of your firefox profile (ex : ~/.mozilla/firefox/xxxx.default/extensions/r2d2b2g@mozilla.org/profile/fake-sdcard). It looks for evopedia/wikipedia_fr_2013-02-16/titles.idx in it. You also need to install the application from the dashboard of the simulator instead of accessing via the browser (due to security restrictions in Firefox OS : only certified webapps can access the sdcard)</li>
|
<ul>
|
||||||
<li>On a real Firefox OS device, you also have (for now) to put the French dump files in an "evopedia" directory at the root of your sdcard, so that it finds a file /evopedia/wikipedia_fr_2013-02-16/titles.idx on it</li>
|
<li>On desktops, it works on recent Firefox and Chrome, and maybe on other browsers</li>
|
||||||
</ul>
|
<li>On the Firefos OS simulator, you have (for now) to put the French dump files in a "fake-sdcard/evopedia" folder of your firefox profile (ex : ~/.mozilla/firefox/xxxx.default/extensions/r2d2b2g@mozilla.org/profile/fake-sdcard). It looks for evopedia/wikipedia_fr_2013-02-16/titles.idx in it. You also need to install the application from the dashboard of the simulator instead of accessing via the browser (due to security restrictions in Firefox OS : only certified webapps can access the sdcard)</li>
|
||||||
<br />
|
<li>On a real Firefox OS device, you also have (for now) to put the French dump files in an "evopedia" directory at the root of your sdcard, so that it finds a file /evopedia/wikipedia_fr_2013-02-16/titles.idx on it</li>
|
||||||
It's only a proof of concept so far : 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>
|
<br />
|
||||||
<li>The performance has to be optimized when reading an article</li>
|
It's only a proof of concept so far : 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 :
|
||||||
<li>Some searches (for example with prefix "a" on the French dump) do not give any result even if they should</li>
|
<ul>
|
||||||
<li>In some cases, the links inside an article do not work, or do not lead to the right article</li>
|
<li>The performance has to be optimized when reading an article</li>
|
||||||
<li>On a real device, reading an article sometimes crashes because it loads too many things in memory</li>
|
<li>Some searches (for example with prefix "a" on the French dump) do not give any result even if they should</li>
|
||||||
<li>It is hardly usable on a device because the buttons and inputs are too small</li>
|
<li>In some cases, the links inside an article do not work, or do not lead to the right article</li>
|
||||||
<li>Following the links in an article does not populate the history of the browser, which prevents the use of the back button</li>
|
<li>On a real device, reading an article sometimes crashes because it loads too many things in memory</li>
|
||||||
</ul>
|
<li>It is hardly usable on a device because the buttons and inputs are too small</li>
|
||||||
<br />
|
<li>Following the links in an article does not populate the history of the browser, which prevents the use of the back button</li>
|
||||||
<div id="openLocalFiles" style="display: none;">
|
</ul>
|
||||||
<br /> Please select the file titles.idx :<br /> <input type="file"
|
<br />
|
||||||
id="titleFile" /><br /> Please select the files wikipedia_*.dat
|
</div>
|
||||||
from the same dump :<br /> <input type="file" id="dataFiles" multiple />
|
<div id="openLocalFiles" style="display: none;">
|
||||||
</div>
|
<br /> Please select the file titles.idx :<br /> <input type="file"
|
||||||
<br /> Find titles from the prefix :
|
id="titleFile" /><br /> Please select the files wikipedia_*.dat
|
||||||
<input type="text" id="prefix" value="" />
|
from the same dump :<br /> <input type="file" id="dataFiles" multiple />
|
||||||
<input type="button" id="searchTitles" value="Search titles" />
|
</div>
|
||||||
<br /> Choose a title from the filtered list :
|
<br /> Find titles starting with :
|
||||||
<select id="titleList"></select>
|
<input type="text" id="prefix" value="" />
|
||||||
<br />
|
<input type="button" id="searchTitles" value="Search titles" />
|
||||||
<input type="button" id="readData" value="Read article from dump" />
|
<br /> Choose a title from the filtered list :
|
||||||
<div id="articleContent"> </div>
|
<select id="titleList"></select>
|
||||||
<hr />
|
<br />
|
||||||
|
<input type="button" id="readData" value="Read article from dump" />
|
||||||
<!-- Using require.js, a module system for javascript, include the
|
<div id="articleContent"> </div>
|
||||||
js files. This loads "main.js", which in turn can load other
|
<hr />
|
||||||
files, all handled by require.js:
|
|
||||||
http://requirejs.org/docs/api.html#jsfiles -->
|
<!-- Using require.js, a module system for javascript, include the
|
||||||
<script type="text/javascript"
|
js files. This loads "main.js", which in turn can load other
|
||||||
data-main="js/init.js"
|
files, all handled by require.js:
|
||||||
src="js/lib/require.js"></script>
|
http://requirejs.org/docs/api.html#jsfiles -->
|
||||||
</body>
|
<script type="text/javascript"
|
||||||
</html>
|
data-main="js/init.js"
|
||||||
|
src="js/lib/require.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
378
www/js/app.js
378
www/js/app.js
@ -1,187 +1,191 @@
|
|||||||
|
|
||||||
// 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');
|
||||||
|
|
||||||
// Need to verify receipts? This library is included by default.
|
// Need to verify receipts? This library is included by default.
|
||||||
// https://github.com/mozilla/receiptverifier
|
// https://github.com/mozilla/receiptverifier
|
||||||
require('receiptverifier');
|
require('receiptverifier');
|
||||||
|
|
||||||
// Want to install the app locally? This library hooks up the
|
// Want to install the app locally? This library hooks up the
|
||||||
// installation button. See <button class="install-btn"> in
|
// installation button. See <button class="install-btn"> in
|
||||||
// index.html
|
// index.html
|
||||||
require('./install-button');
|
require('./install-button');
|
||||||
|
|
||||||
// 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
|
||||||
$('#searchTitles').on('click', function(e) {
|
$('#about').hide();
|
||||||
searchTitlesFromPrefix($('#prefix').val());
|
$('#showHideAbout').on('click', function(e) {
|
||||||
});
|
$('#about').toggle();
|
||||||
$('#readData').on('click', function(e) {
|
});
|
||||||
findTitleFromTitleIdAndLaunchArticleRead($('#titleList').val());
|
$('#searchTitles').on('click', function(e) {
|
||||||
});
|
searchTitlesFromPrefix($('#prefix').val());
|
||||||
$('#prefix').on('keyup', function(e) {
|
});
|
||||||
onKeyUpPrefix(e);
|
$('#readData').on('click', function(e) {
|
||||||
});
|
findTitleFromTitleIdAndLaunchArticleRead($('#titleList').val());
|
||||||
|
});
|
||||||
|
$('#prefix').on('keyup', function(e) {
|
||||||
// Detect if DeviceStorage is available
|
onKeyUpPrefix(e);
|
||||||
var storage = null;
|
});
|
||||||
if ($.isFunction(navigator.getDeviceStorage)) {
|
|
||||||
storage = navigator.getDeviceStorage('sdcard');
|
|
||||||
}
|
// Detect if DeviceStorage is available
|
||||||
|
var storage = null;
|
||||||
if (storage != null) {
|
if ($.isFunction(navigator.getDeviceStorage)) {
|
||||||
//var directory = 'evopedia/wikipedia_small_2010-08-14';
|
storage = navigator.getDeviceStorage('sdcard');
|
||||||
var directory = 'evopedia/wikipedia_fr_2013-02-16';
|
}
|
||||||
localArchive = new evopedia.LocalArchive();
|
|
||||||
localArchive.readTitleFile(storage, directory);
|
if (storage != null) {
|
||||||
localArchive.readDataFiles(storage, directory, 0);
|
//var directory = 'evopedia/wikipedia_small_2010-08-14';
|
||||||
}
|
var directory = 'evopedia/wikipedia_fr_2013-02-16';
|
||||||
else {
|
localArchive = new evopedia.LocalArchive();
|
||||||
displayFileSelect();
|
localArchive.readTitleFile(storage, directory);
|
||||||
setLocalArchiveFromFileSelect();
|
localArchive.readDataFiles(storage, directory, 0);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
/**
|
displayFileSelect();
|
||||||
* Displays the zone to select files from the dump
|
setLocalArchiveFromFileSelect();
|
||||||
*/
|
}
|
||||||
function displayFileSelect() {
|
|
||||||
$('#openLocalFiles').show();
|
/**
|
||||||
$('#dataFiles').on('change', setLocalArchiveFromFileSelect);
|
* Displays the zone to select files from the dump
|
||||||
$('#titleFile').on('change', setLocalArchiveFromFileSelect);
|
*/
|
||||||
}
|
function displayFileSelect() {
|
||||||
|
$('#openLocalFiles').show();
|
||||||
function setLocalArchiveFromFileSelect() {
|
$('#dataFiles').on('change', setLocalArchiveFromFileSelect);
|
||||||
dataFiles=document.getElementById('dataFiles').files;
|
$('#titleFile').on('change', setLocalArchiveFromFileSelect);
|
||||||
titleFile=document.getElementById('titleFile').files[0];
|
}
|
||||||
localArchive = new evopedia.LocalArchive();
|
|
||||||
localArchive.dataFiles = dataFiles;
|
function setLocalArchiveFromFileSelect() {
|
||||||
localArchive.titleFile = titleFile;
|
dataFiles=document.getElementById('dataFiles').files;
|
||||||
}
|
titleFile=document.getElementById('titleFile').files[0];
|
||||||
|
localArchive = new evopedia.LocalArchive();
|
||||||
/**
|
localArchive.dataFiles = dataFiles;
|
||||||
* Handle Enter key in the prefix input zone
|
localArchive.titleFile = titleFile;
|
||||||
*/
|
}
|
||||||
function onKeyUpPrefix(evt) {
|
|
||||||
if (evt.keyCode == 13) {
|
/**
|
||||||
document.getElementById("searchTitles").click();
|
* Handle Enter key in the prefix input zone
|
||||||
}
|
*/
|
||||||
}
|
function onKeyUpPrefix(evt) {
|
||||||
|
if (evt.keyCode == 13) {
|
||||||
|
document.getElementById("searchTitles").click();
|
||||||
|
}
|
||||||
/**
|
}
|
||||||
* Search the index for titles that start with the given prefix (implemented
|
|
||||||
* with a binary search inside the index file)
|
|
||||||
*/
|
|
||||||
function searchTitlesFromPrefix(prefix) {
|
/**
|
||||||
if (localArchive.titleFile) {
|
* Search the index for titles that start with the given prefix (implemented
|
||||||
localArchive.findTitlesWithPrefix(prefix, populateDropDownListOfTitles);
|
* with a binary search inside the index file)
|
||||||
} else {
|
*/
|
||||||
alert("Title file not set");
|
function searchTitlesFromPrefix(prefix) {
|
||||||
}
|
if (localArchive.titleFile) {
|
||||||
}
|
localArchive.findTitlesWithPrefix(prefix, populateDropDownListOfTitles);
|
||||||
|
} else {
|
||||||
/**
|
alert("Title file not set");
|
||||||
* Populate the drop-down list of titles with the given list
|
}
|
||||||
*/
|
}
|
||||||
function populateDropDownListOfTitles(titleList) {
|
|
||||||
var comboTitleList = document.getElementById('titleList');
|
/**
|
||||||
// Remove previous results
|
* Populate the drop-down list of titles with the given list
|
||||||
comboTitleList.options.length = 0;
|
*/
|
||||||
for (var i=0; i<titleList.length; i++) {
|
function populateDropDownListOfTitles(titleList) {
|
||||||
var title = titleList[i];
|
var comboTitleList = document.getElementById('titleList');
|
||||||
comboTitleList.options[i] = new Option (title.name, title.toStringId());
|
// Remove previous results
|
||||||
}
|
comboTitleList.options.length = 0;
|
||||||
}
|
for (var i=0; i<titleList.length; i++) {
|
||||||
|
var title = titleList[i];
|
||||||
|
comboTitleList.options[i] = new Option (title.name, title.toStringId());
|
||||||
/**
|
}
|
||||||
* Creates an instance of title from given titleId (including resolving redirects),
|
}
|
||||||
* and call the function to read the corresponding article
|
|
||||||
*/
|
|
||||||
function findTitleFromTitleIdAndLaunchArticleRead(titleId) {
|
/**
|
||||||
$("#articleContent").html("Loading article from dump...");
|
* Creates an instance of title from given titleId (including resolving redirects),
|
||||||
if (localArchive.dataFiles && localArchive.dataFiles.length>0) {
|
* and call the function to read the corresponding article
|
||||||
var title = evopedia.Title.parseTitleId(localArchive,titleId);
|
*/
|
||||||
if (title.fileNr == 255) {
|
function findTitleFromTitleIdAndLaunchArticleRead(titleId) {
|
||||||
localArchive.resolveRedirect(title, readArticle);
|
$("#articleContent").html("Loading article from dump...");
|
||||||
}
|
if (localArchive.dataFiles && localArchive.dataFiles.length>0) {
|
||||||
else {
|
var title = evopedia.Title.parseTitleId(localArchive,titleId);
|
||||||
readArticle(title);
|
if (title.fileNr == 255) {
|
||||||
}
|
localArchive.resolveRedirect(title, readArticle);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
alert("Data files not set");
|
readArticle(title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
/**
|
alert("Data files not set");
|
||||||
* Read the article corresponding to the given title
|
}
|
||||||
*/
|
}
|
||||||
function readArticle(title) {
|
|
||||||
if ($.isArray(title)) {
|
/**
|
||||||
title = title[0];
|
* Read the article corresponding to the given title
|
||||||
if (title.fileNr == 255) {
|
*/
|
||||||
localArchive.resolveRedirect(title, readArticle);
|
function readArticle(title) {
|
||||||
return;
|
if ($.isArray(title)) {
|
||||||
}
|
title = title[0];
|
||||||
}
|
if (title.fileNr == 255) {
|
||||||
localArchive.readArticle(title, displayArticleInForm);
|
localArchive.resolveRedirect(title, readArticle);
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
/**
|
}
|
||||||
* Display the the given HTML article in the web page,
|
localArchive.readArticle(title, displayArticleInForm);
|
||||||
* and convert links to javascript calls
|
}
|
||||||
*/
|
|
||||||
function displayArticleInForm(htmlArticle) {
|
/**
|
||||||
// Display the article inside the web page.
|
* Display the the given HTML article in the web page,
|
||||||
$('#articleContent').html(htmlArticle);
|
* and convert links to javascript calls
|
||||||
|
*/
|
||||||
// Convert links into javascript calls
|
function displayArticleInForm(htmlArticle) {
|
||||||
$('#articleContent').find('a').each(function(){
|
// Display the article inside the web page.
|
||||||
// Store current link's url
|
$('#articleContent').html(htmlArticle);
|
||||||
var url = $(this).attr("href");
|
|
||||||
|
// Convert links into javascript calls
|
||||||
if(url.slice(0, 1) == "#") {
|
$('#articleContent').find('a').each(function(){
|
||||||
// It's an anchor link : do nothing
|
// Store current link's url
|
||||||
}
|
var url = $(this).attr("href");
|
||||||
else if (url.substring(0,4) === "http") {
|
|
||||||
// It's an external link : do nothing
|
if(url.slice(0, 1) == "#") {
|
||||||
}
|
// It's an anchor link : do nothing
|
||||||
else if (url.substring(0,2) === ".." || url.substring(0,4) === "./..") {
|
}
|
||||||
// It's a link to another language : TODO redirect to the online article?
|
else if (url.substring(0,4) === "http") {
|
||||||
}
|
// It's an external link : do nothing
|
||||||
else {
|
}
|
||||||
// It's a link to another article : add an onclick event to go to this article
|
else if (url.substring(0,2) === ".." || url.substring(0,4) === "./..") {
|
||||||
// instead of following the link
|
// It's a link to another language : TODO redirect to the online article?
|
||||||
$(this).on('click', function(e) {
|
}
|
||||||
goToArticle(decodeURIComponent($(this).attr("href")));
|
else {
|
||||||
return false;
|
// It's a link to another article : add an onclick event to go to this article
|
||||||
});
|
// instead of following the link
|
||||||
}
|
$(this).on('click', function(e) {
|
||||||
});
|
goToArticle(decodeURIComponent($(this).attr("href")));
|
||||||
}
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
/**
|
});
|
||||||
* Replace article content with the one of the given title
|
}
|
||||||
*/
|
|
||||||
function goToArticle(title) {
|
|
||||||
$("#articleContent").html("Loading article from dump...");
|
/**
|
||||||
localArchive.getTitleByName(title, readArticle);
|
* Replace article content with the one of the given title
|
||||||
}
|
*/
|
||||||
|
function goToArticle(title) {
|
||||||
});
|
$("#articleContent").html("Loading article from dump...");
|
||||||
|
localArchive.getTitleByName(title, readArticle);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user