mirror of https://github.com/kiwix/libkiwix.git
Merge pull request #534 from kiwix/filter_library
Add filters to kiwix-serve welcome page
This commit is contained in:
commit
2ef488816c
|
@ -304,7 +304,7 @@ InternalServer::get_matching_if_none_match_etag(const RequestContext& r) const
|
||||||
|
|
||||||
std::unique_ptr<Response> InternalServer::build_homepage(const RequestContext& request)
|
std::unique_ptr<Response> InternalServer::build_homepage(const RequestContext& request)
|
||||||
{
|
{
|
||||||
return ContentResponse::build(*this, RESOURCE::templates::index_html, get_default_data(), "text/html; charset=utf-8");
|
return ContentResponse::build(*this, RESOURCE::templates::index_html, get_default_data(), "text/html; charset=utf-8", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Response> InternalServer::handle_meta(const RequestContext& request)
|
std::unique_ptr<Response> InternalServer::handle_meta(const RequestContext& request)
|
||||||
|
|
|
@ -106,7 +106,7 @@ class InternalServer {
|
||||||
std::string m_server_id;
|
std::string m_server_id;
|
||||||
|
|
||||||
friend std::unique_ptr<Response> Response::build(const InternalServer& server);
|
friend std::unique_ptr<Response> Response::build(const InternalServer& server);
|
||||||
friend std::unique_ptr<ContentResponse> ContentResponse::build(const InternalServer& server, const std::string& content, const std::string& mimetype);
|
friend std::unique_ptr<ContentResponse> ContentResponse::build(const InternalServer& server, const std::string& content, const std::string& mimetype, bool isHomePage);
|
||||||
friend std::unique_ptr<Response> ItemResponse::build(const InternalServer& server, const RequestContext& request, const zim::Item& item);
|
friend std::unique_ptr<Response> ItemResponse::build(const InternalServer& server, const RequestContext& request, const zim::Item& item);
|
||||||
friend std::unique_ptr<Response> Response::build_500(const InternalServer& server, const std::string& msg);
|
friend std::unique_ptr<Response> Response::build_500(const InternalServer& server, const std::string& msg);
|
||||||
|
|
||||||
|
|
|
@ -349,21 +349,21 @@ ContentResponse::ContentResponse(const std::string& root, bool verbose, bool wit
|
||||||
add_header(MHD_HTTP_HEADER_CONTENT_TYPE, m_mimeType);
|
add_header(MHD_HTTP_HEADER_CONTENT_TYPE, m_mimeType);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<ContentResponse> ContentResponse::build(const InternalServer& server, const std::string& content, const std::string& mimetype)
|
std::unique_ptr<ContentResponse> ContentResponse::build(const InternalServer& server, const std::string& content, const std::string& mimetype, bool isHomePage)
|
||||||
{
|
{
|
||||||
return std::unique_ptr<ContentResponse>(new ContentResponse(
|
return std::unique_ptr<ContentResponse>(new ContentResponse(
|
||||||
server.m_root,
|
server.m_root,
|
||||||
server.m_verbose.load(),
|
server.m_verbose.load(),
|
||||||
server.m_withTaskbar,
|
server.m_withTaskbar && !isHomePage,
|
||||||
server.m_withLibraryButton,
|
server.m_withLibraryButton,
|
||||||
server.m_blockExternalLinks,
|
server.m_blockExternalLinks,
|
||||||
content,
|
content,
|
||||||
mimetype));
|
mimetype));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<ContentResponse> ContentResponse::build(const InternalServer& server, const std::string& template_str, kainjow::mustache::data data, const std::string& mimetype) {
|
std::unique_ptr<ContentResponse> ContentResponse::build(const InternalServer& server, const std::string& template_str, kainjow::mustache::data data, const std::string& mimetype, bool isHomePage) {
|
||||||
auto content = render_template(template_str, data);
|
auto content = render_template(template_str, data);
|
||||||
return ContentResponse::build(server, content, mimetype);
|
return ContentResponse::build(server, content, mimetype, isHomePage);
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemResponse::ItemResponse(bool verbose, const zim::Item& item, const std::string& mimetype, const ByteRange& byterange) :
|
ItemResponse::ItemResponse(bool verbose, const zim::Item& item, const std::string& mimetype, const ByteRange& byterange) :
|
||||||
|
|
|
@ -79,8 +79,8 @@ class Response {
|
||||||
class ContentResponse : public Response {
|
class ContentResponse : public Response {
|
||||||
public:
|
public:
|
||||||
ContentResponse(const std::string& root, bool verbose, bool withTaskbar, bool withLibraryButton, bool blockExternalLinks, const std::string& content, const std::string& mimetype);
|
ContentResponse(const std::string& root, bool verbose, bool withTaskbar, bool withLibraryButton, bool blockExternalLinks, const std::string& content, const std::string& mimetype);
|
||||||
static std::unique_ptr<ContentResponse> build(const InternalServer& server, const std::string& content, const std::string& mimetype);
|
static std::unique_ptr<ContentResponse> build(const InternalServer& server, const std::string& content, const std::string& mimetype, bool isHomePage = false);
|
||||||
static std::unique_ptr<ContentResponse> build(const InternalServer& server, const std::string& template_str, kainjow::mustache::data data, const std::string& mimetype);
|
static std::unique_ptr<ContentResponse> build(const InternalServer& server, const std::string& template_str, kainjow::mustache::data data, const std::string& mimetype, bool isHomePage = false);
|
||||||
|
|
||||||
void set_taskbar(const std::string& bookName, const std::string& bookTitle);
|
void set_taskbar(const std::string& bookName, const std::string& bookTitle);
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,10 @@ skin/jquery-ui/jquery-ui.theme.min.css
|
||||||
skin/jquery-ui/jquery-ui.min.css
|
skin/jquery-ui/jquery-ui.min.css
|
||||||
skin/caret.png
|
skin/caret.png
|
||||||
skin/taskbar.js
|
skin/taskbar.js
|
||||||
|
skin/langList.js
|
||||||
|
skin/categoryList.js
|
||||||
|
skin/iso6391To3.js
|
||||||
|
skin/isotope.pkgd.min.js
|
||||||
skin/index.js
|
skin/index.js
|
||||||
skin/taskbar.css
|
skin/taskbar.css
|
||||||
skin/block_external.js
|
skin/block_external.js
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
const categoryList = {
|
||||||
|
"other": "Other",
|
||||||
|
"gutenberg": "Gutenberg",
|
||||||
|
"mooc": "Mooc",
|
||||||
|
"phet": "Phet",
|
||||||
|
"psiram": "Psiram",
|
||||||
|
"stack_exchange": "Stack Exchange",
|
||||||
|
"ted": "Ted",
|
||||||
|
"vikidia": "Vikidia",
|
||||||
|
"wikibooks": "Wikibooks",
|
||||||
|
"wikinews": "Wikinews",
|
||||||
|
"wikipedia": "Wikipedia",
|
||||||
|
"wikiquote": "Wikiquote",
|
||||||
|
"wikisource": "Wikisource",
|
||||||
|
"wikiversity": "Wikiversity",
|
||||||
|
"wikivoyage": "Wikivoyage",
|
||||||
|
"wiktionary": "Wiktionary"
|
||||||
|
}
|
|
@ -4,13 +4,25 @@
|
||||||
start: 0,
|
start: 0,
|
||||||
count: viewPortToCount()
|
count: viewPortToCount()
|
||||||
};
|
};
|
||||||
|
const filterTypes = ['lang', 'category', 'q'];
|
||||||
|
const bookMap = new Map();
|
||||||
|
let footer;
|
||||||
|
let fadeOutDiv;
|
||||||
|
let iso;
|
||||||
let isFetching = false;
|
let isFetching = false;
|
||||||
|
let noResultInjected = false;
|
||||||
|
let params = new URLSearchParams(window.location.search);
|
||||||
let timer;
|
let timer;
|
||||||
|
|
||||||
function queryUrlBuilder() {
|
function queryUrlBuilder() {
|
||||||
let url = `${root}/catalog/search?`;
|
let url = `${root}/catalog/search?`;
|
||||||
url += Object.keys(incrementalLoadingParams).map(key => `${key}=${incrementalLoadingParams[key]}`).join("&");
|
url += Object.keys(incrementalLoadingParams).map(key => `${key}=${incrementalLoadingParams[key]}`).join("&");
|
||||||
return url;
|
params.forEach((value, key) => {url+= value ? `&${key}=${value}` : ''});
|
||||||
|
return (url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function htmlEncode(str) {
|
||||||
|
return str.replace(/[\u00A0-\u9999<>\&]/gim, (i) => `&#${i.charCodeAt(0)};`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function viewPortToCount(){
|
function viewPortToCount(){
|
||||||
|
@ -21,7 +33,7 @@
|
||||||
return node.querySelector(query).innerHTML;
|
return node.querySelector(query).innerHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateBookHtml(book) {
|
function generateBookHtml(book, sort = false) {
|
||||||
const link = book.querySelector('link').getAttribute('href');
|
const link = book.querySelector('link').getAttribute('href');
|
||||||
const title = getInnerHtml(book, 'title');
|
const title = getInnerHtml(book, 'title');
|
||||||
const description = getInnerHtml(book, 'summary');
|
const description = getInnerHtml(book, 'summary');
|
||||||
|
@ -30,36 +42,144 @@
|
||||||
const articleCount = getInnerHtml(book, 'articleCount');
|
const articleCount = getInnerHtml(book, 'articleCount');
|
||||||
const mediaCount = getInnerHtml(book, 'mediaCount');
|
const mediaCount = getInnerHtml(book, 'mediaCount');
|
||||||
|
|
||||||
return `<a href='${link}' data-id='${id}'><div class='book'>
|
const linkTag = document.createElement('a');
|
||||||
<div class='book__background' style="background-image: url('${iconUrl}');">
|
linkTag.setAttribute('class', 'book');
|
||||||
|
linkTag.setAttribute('data-id', id);
|
||||||
|
linkTag.setAttribute('href', link);
|
||||||
|
if (sort) {
|
||||||
|
linkTag.setAttribute('data-idx', bookMap[id]);
|
||||||
|
}
|
||||||
|
linkTag.innerHTML = `<div class='book__background' style="background-image: url('${iconUrl}');">
|
||||||
<div class='book__title' title='${title}'>${title}</div>
|
<div class='book__title' title='${title}'>${title}</div>
|
||||||
<div class='book__description' title='${description}'>${description}</div>
|
<div class='book__description' title='${description}'>${description}</div>
|
||||||
<div class='book__info'>${articleCount} articles, ${mediaCount} medias</div>
|
<div class='book__info'>${articleCount} articles, ${mediaCount} medias</div>
|
||||||
</div>
|
</div>`;
|
||||||
</div></a>`;
|
return linkTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadAndDisplayBooks() {
|
function toggleFooter(show=false) {
|
||||||
if (isFetching) return;
|
if (show) {
|
||||||
isFetching = true;
|
footer.style.display = 'block';
|
||||||
fetch(queryUrlBuilder()).then(async (resp) => {
|
} else {
|
||||||
|
footer.style.display = 'none';
|
||||||
|
fadeOutDiv.style.display = 'block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadBooks() {
|
||||||
|
const loader = document.querySelector('.loader');
|
||||||
|
loader.style.display = 'block';
|
||||||
|
return await fetch(queryUrlBuilder()).then(async (resp) => {
|
||||||
const data = new window.DOMParser().parseFromString(await resp.text(), 'application/xml');
|
const data = new window.DOMParser().parseFromString(await resp.text(), 'application/xml');
|
||||||
const books = data.querySelectorAll('entry');
|
const books = data.querySelectorAll('entry');
|
||||||
let bookHtml = '';
|
books.forEach((book, idx) => {
|
||||||
books.forEach((book) => {bookHtml += generateBookHtml(book)});
|
bookMap.set(getInnerHtml(book, 'id'), idx);
|
||||||
document.querySelector('.book__list').innerHTML += bookHtml;
|
});
|
||||||
incrementalLoadingParams.start += books.length;
|
incrementalLoadingParams.start += books.length;
|
||||||
if (books.length < incrementalLoadingParams.count) {
|
if (parseInt(data.querySelector('totalResults').innerHTML) === bookMap.size) {
|
||||||
incrementalLoadingParams.count = 0;
|
incrementalLoadingParams.count = 0;
|
||||||
|
toggleFooter(true);
|
||||||
|
} else {
|
||||||
|
toggleFooter();
|
||||||
}
|
}
|
||||||
isFetching = false;
|
loader.style.display = 'none';
|
||||||
|
return books;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadAndDisplayOptions(nodeQuery, query) {
|
||||||
|
// currently taking an object in place of query, will replace it with query while fetching data from backend later on.
|
||||||
|
document.querySelector(nodeQuery).innerHTML += Object.keys(query)
|
||||||
|
.map((option) => {return `<option value='${option}'>${htmlEncode(query[option])}</option>`})
|
||||||
|
.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAndInjectEmptyMessage() {
|
||||||
|
if (!bookMap.size) {
|
||||||
|
if (!noResultInjected) {
|
||||||
|
noResultInjected = true;
|
||||||
|
iso.remove(document.getElementsByClassName('book__list')[0].getElementsByTagName('a'));
|
||||||
|
iso.layout();
|
||||||
|
const spanTag = document.createElement('span');
|
||||||
|
spanTag.setAttribute('class', 'noResults');
|
||||||
|
spanTag.innerHTML = `No result. Would you like to <a href="/?lang=">reset filter?</a>`;
|
||||||
|
document.querySelector('body').append(spanTag);
|
||||||
|
spanTag.getElementsByTagName('a')[0].onclick = (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
window.history.pushState({}, null, `${window.location.href.split('?')[0]}?lang=`);
|
||||||
|
resetAndFilter();
|
||||||
|
filterTypes.forEach(key => {document.getElementsByName(key)[0].value = params.get(key) || ''});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (noResultInjected) {
|
||||||
|
noResultInjected = false;
|
||||||
|
document.getElementsByClassName('noResults')[0].remove();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadAndDisplayBooks(sort = false) {
|
||||||
|
if (isFetching) return;
|
||||||
|
isFetching = true;
|
||||||
|
await loadAndDisplayBooksUnguarded(sort);
|
||||||
|
isFetching = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadAndDisplayBooksUnguarded(sort) {
|
||||||
|
let books = await loadBooks();
|
||||||
|
if (checkAndInjectEmptyMessage()) {return}
|
||||||
|
const booksToFilter = new Set();
|
||||||
|
const booksToDelete = new Set();
|
||||||
|
iso.arrange({
|
||||||
|
filter: function (idx, elem) {
|
||||||
|
const id = elem.getAttribute('data-id');
|
||||||
|
const retVal = bookMap.has(id);
|
||||||
|
if (retVal) {
|
||||||
|
booksToFilter.add(id);
|
||||||
|
if (sort) {
|
||||||
|
elem.setAttribute('data-idx', bookMap[id]);
|
||||||
|
iso.updateSortData(elem);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
booksToDelete.add(elem);
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
books = [...books].filter((book) => {return !booksToFilter.has(getInnerHtml(book, 'id'))});
|
||||||
|
booksToDelete.forEach(book => {iso.remove(book);});
|
||||||
|
books.forEach((book) => {iso.insert(generateBookHtml(book, sort))});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resetAndFilter(filterType = '', filterValue = '') {
|
||||||
|
isFetching = false;
|
||||||
|
incrementalLoadingParams.start = 0;
|
||||||
|
incrementalLoadingParams.count = viewPortToCount();
|
||||||
|
fadeOutDiv.style.display = 'none';
|
||||||
|
bookMap.clear();
|
||||||
|
params = new URLSearchParams(window.location.search);
|
||||||
|
if (filterType) {
|
||||||
|
params.set(filterType, filterValue);
|
||||||
|
window.history.pushState({}, null, `${window.location.href.split('?')[0]}?${params.toString()}`);
|
||||||
|
}
|
||||||
|
await loadAndDisplayBooks(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('popstate', async () => {
|
||||||
|
await resetAndFilter();
|
||||||
|
filterTypes.forEach(key => {document.getElementsByName(key)[0].value = params.get(key) || ''});
|
||||||
|
});
|
||||||
|
|
||||||
async function loadSubset() {
|
async function loadSubset() {
|
||||||
if (incrementalLoadingParams.count && window.innerHeight + window.scrollY >= document.body.offsetHeight) {
|
if (window.innerHeight + window.scrollY >= document.body.offsetHeight) {
|
||||||
|
if (incrementalLoadingParams.count) {
|
||||||
loadAndDisplayBooks();
|
loadAndDisplayBooks();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
fadeOutDiv.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('resize', (event) => {
|
window.addEventListener('resize', (event) => {
|
||||||
|
@ -73,6 +193,37 @@
|
||||||
window.addEventListener('scroll', loadSubset);
|
window.addEventListener('scroll', loadSubset);
|
||||||
|
|
||||||
window.onload = async () => {
|
window.onload = async () => {
|
||||||
loadAndDisplayBooks();
|
iso = new Isotope( '.book__list', {
|
||||||
|
itemSelector: '.book',
|
||||||
|
getSortData:{
|
||||||
|
weight: function( itemElem ) {
|
||||||
|
const index = itemElem.getAttribute('data-idx');
|
||||||
|
return index ? parseInt(index) : Infinity;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sortBy: 'weight'
|
||||||
|
});
|
||||||
|
footer = document.getElementById('kiwixfooter');
|
||||||
|
fadeOutDiv = document.getElementById('fadeOut');
|
||||||
|
await loadAndDisplayBooks();
|
||||||
|
await loadAndDisplayOptions('#languageFilter', langList);
|
||||||
|
await loadAndDisplayOptions('#categoryFilter', categoryList);
|
||||||
|
filterTypes.forEach((filter) => {
|
||||||
|
const filterTag = document.getElementsByName(filter)[0];
|
||||||
|
filterTag.addEventListener('change', () => {resetAndFilter(filterTag.name, filterTag.value)});
|
||||||
|
});
|
||||||
|
params.forEach((value, key) => {document.getElementsByName(key)[0].value = value});
|
||||||
|
document.getElementById('kiwixSearchForm').onsubmit = (event) => {event.preventDefault()};
|
||||||
|
if (!window.location.search) {
|
||||||
|
const browserLang = navigator.language.split('-')[0];
|
||||||
|
if (browserLang.length === 3) {
|
||||||
|
document.getElementById('languageFilter').value = browserLang;
|
||||||
|
langFilter.dispatchEvent(new Event('change'));
|
||||||
|
} else {
|
||||||
|
const langFilter = document.getElementById('languageFilter');
|
||||||
|
langFilter.value = iso6391To3[browserLang];
|
||||||
|
langFilter.dispatchEvent(new Event('change'));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
const iso6391To3 = {
|
||||||
|
"aa": "aar",
|
||||||
|
"af": "afr",
|
||||||
|
"ak": "aka",
|
||||||
|
"am": "amh",
|
||||||
|
"ar": "ara",
|
||||||
|
"as": "asm",
|
||||||
|
"az": "aze",
|
||||||
|
"ba": "bak",
|
||||||
|
"be": "bel",
|
||||||
|
"bg": "bul",
|
||||||
|
"bm": "bam",
|
||||||
|
"bn": "ben",
|
||||||
|
"bo": "bod",
|
||||||
|
"br": "bre",
|
||||||
|
"bs": "bos",
|
||||||
|
"ca": "cat",
|
||||||
|
"ce": "che",
|
||||||
|
"co": "cos",
|
||||||
|
"cs": "ces",
|
||||||
|
"cv": "chv",
|
||||||
|
"cy": "cym",
|
||||||
|
"da": "dan",
|
||||||
|
"de": "deu",
|
||||||
|
"dz": "dzo",
|
||||||
|
"ee": "ewe",
|
||||||
|
"en": "eng",
|
||||||
|
"es": "spa",
|
||||||
|
"et": "est",
|
||||||
|
"eu": "eus",
|
||||||
|
"fa": "fas",
|
||||||
|
"ff": "ful",
|
||||||
|
"fi": "fin",
|
||||||
|
"fo": "fao",
|
||||||
|
"fr": "fra",
|
||||||
|
"ga": "gle",
|
||||||
|
"gl": "glg",
|
||||||
|
"gn": "grn",
|
||||||
|
"gu": "guj",
|
||||||
|
"gv": "glv",
|
||||||
|
"ha": "hau",
|
||||||
|
"he": "heb",
|
||||||
|
"hi": "hin",
|
||||||
|
"hr": "hrv",
|
||||||
|
"hu": "hun",
|
||||||
|
"hy": "hye",
|
||||||
|
"id": "ind",
|
||||||
|
"ig": "ibo",
|
||||||
|
"is": "isl",
|
||||||
|
"it": "ita",
|
||||||
|
"iu": "iku",
|
||||||
|
"ja": "jpn",
|
||||||
|
"jv": "jav",
|
||||||
|
"ka": "kat",
|
||||||
|
"ki": "kik",
|
||||||
|
"kk": "kaz",
|
||||||
|
"km": "khm",
|
||||||
|
"kn": "kan",
|
||||||
|
"ko": "kor",
|
||||||
|
"ks": "kas",
|
||||||
|
"ku": "kur",
|
||||||
|
"kw": "cor",
|
||||||
|
"ky": "kir",
|
||||||
|
"lb": "ltz",
|
||||||
|
"lg": "lug",
|
||||||
|
"ln": "lin",
|
||||||
|
"lo": "lao",
|
||||||
|
"lt": "lit",
|
||||||
|
"lv": "lav",
|
||||||
|
"mg": "mlg",
|
||||||
|
"mi": "mri",
|
||||||
|
"mk": "mkd",
|
||||||
|
"ml": "mal",
|
||||||
|
"mn": "mon",
|
||||||
|
"mr": "mar",
|
||||||
|
"mt": "mlt",
|
||||||
|
"my": "mya",
|
||||||
|
"nl": "nld",
|
||||||
|
"ny": "nya",
|
||||||
|
"om": "orm",
|
||||||
|
"pl": "pol",
|
||||||
|
"pt": "por",
|
||||||
|
"qu": "que",
|
||||||
|
"rm": "roh",
|
||||||
|
"rn": "run",
|
||||||
|
"ro": "ron",
|
||||||
|
"ru": "rus",
|
||||||
|
"rw": "kin",
|
||||||
|
"sa": "san",
|
||||||
|
"sd": "snd",
|
||||||
|
"sg": "sag",
|
||||||
|
"si": "sin",
|
||||||
|
"sk": "slk",
|
||||||
|
"sl": "slv",
|
||||||
|
"sn": "sna",
|
||||||
|
"so": "som",
|
||||||
|
"sq": "sqi",
|
||||||
|
"sr": "srp",
|
||||||
|
"ss": "ssw",
|
||||||
|
"sv": "swe",
|
||||||
|
"ta": "tam",
|
||||||
|
"te": "tel",
|
||||||
|
"tg": "tgk",
|
||||||
|
"th": "tha",
|
||||||
|
"ti": "tir",
|
||||||
|
"tk": "tuk",
|
||||||
|
"tn": "tsn",
|
||||||
|
"tr": "tur",
|
||||||
|
"ts": "tso",
|
||||||
|
"tt": "tat",
|
||||||
|
"ug": "uig",
|
||||||
|
"uk": "ukr",
|
||||||
|
"ur": "urd",
|
||||||
|
"uz": "uzb",
|
||||||
|
"ve": "ven",
|
||||||
|
"vi": "vie",
|
||||||
|
"wa": "wln",
|
||||||
|
"wo": "wol",
|
||||||
|
"xh": "xho",
|
||||||
|
"yo": "yor",
|
||||||
|
"zh": "zho",
|
||||||
|
"zu": "zul"
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,124 @@
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
const langList = {
|
||||||
|
"aar": "Afaraf",
|
||||||
|
"afr": "Afrikaans",
|
||||||
|
"aka": "Akan",
|
||||||
|
"amh": "አማርኛ",
|
||||||
|
"ara": "اللغة العربية",
|
||||||
|
"asm": "অসমীয়া",
|
||||||
|
"aze": "azərbaycan dili",
|
||||||
|
"bak": "башҡорт теле",
|
||||||
|
"bel": "беларуская мова",
|
||||||
|
"bul": "български език",
|
||||||
|
"bam": "bamanankan",
|
||||||
|
"ben": "বাংলা",
|
||||||
|
"bod": "བོད་ཡིག",
|
||||||
|
"bre": "brezhoneg",
|
||||||
|
"bos": "bosanski jezik",
|
||||||
|
"cat": "Català",
|
||||||
|
"che": "нохчийн мотт",
|
||||||
|
"cos": "corsu",
|
||||||
|
"ces": "čeština",
|
||||||
|
"chv": "чӑваш чӗлхи",
|
||||||
|
"cym": "Cymraeg",
|
||||||
|
"dan": "dansk",
|
||||||
|
"deu": "Deutsch",
|
||||||
|
"dzo": "རྫོང་ཁ",
|
||||||
|
"ewe": "Eʋegbe",
|
||||||
|
"eng": "English",
|
||||||
|
"spa": "Español",
|
||||||
|
"est": "eesti",
|
||||||
|
"eus": "euskara",
|
||||||
|
"fas": "فارسی",
|
||||||
|
"ful": "Fulfulde",
|
||||||
|
"fin": "suomi",
|
||||||
|
"fao": "føroyskt",
|
||||||
|
"fra": "Français",
|
||||||
|
"gle": "Gaeilge",
|
||||||
|
"glg": "galego",
|
||||||
|
"grn": "Avañe'ẽ",
|
||||||
|
"guj": "ગુજરાતી",
|
||||||
|
"glv": "Gaelg",
|
||||||
|
"hau": "هَوُسَ",
|
||||||
|
"heb": "עברית",
|
||||||
|
"hin": "हिन्दी",
|
||||||
|
"hrv": "hrvatski jezik",
|
||||||
|
"hun": "magyar",
|
||||||
|
"hye": "Հայերեն",
|
||||||
|
"ind": "Bahasa Indonesia",
|
||||||
|
"ibo": "Asụsụ Igbo",
|
||||||
|
"isl": "Íslenska",
|
||||||
|
"ita": "Italiano",
|
||||||
|
"iku": "ᐃᓄᒃᑎᑐᑦ",
|
||||||
|
"jpn": "日本語",
|
||||||
|
"jav": "basa Jawa",
|
||||||
|
"kat": "ქართული",
|
||||||
|
"kik": "Gĩkũyũ",
|
||||||
|
"kaz": "қазақ тілі",
|
||||||
|
"khm": "ខេមរភាសា",
|
||||||
|
"kan": "ಕನ್ನಡ",
|
||||||
|
"kor": "한국어",
|
||||||
|
"kas": "कश्मीरी",
|
||||||
|
"kur": "Kurdî",
|
||||||
|
"cor": "Kernewek",
|
||||||
|
"kir": "Кыргызча",
|
||||||
|
"ltz": "Lëtzebuergesch",
|
||||||
|
"lug": "Luganda",
|
||||||
|
"lin": "Lingála",
|
||||||
|
"lao": "ພາສາ",
|
||||||
|
"lit": "lietuvių kalba",
|
||||||
|
"lav": "latviešu valoda",
|
||||||
|
"mlg": "fiteny malagasy",
|
||||||
|
"mri": "te reo Māori",
|
||||||
|
"mkd": "македонски јазик",
|
||||||
|
"mal": "മലയാളം",
|
||||||
|
"mon": "Монгол хэл",
|
||||||
|
"mar": "मराठी",
|
||||||
|
"mlt": "Malti",
|
||||||
|
"mya": "ဗမာစာ",
|
||||||
|
"nld": "Nederlands",
|
||||||
|
"nya": "chiCheŵa",
|
||||||
|
"orm": "Afaan Oromoo",
|
||||||
|
"pol": "język polski",
|
||||||
|
"por": "Português",
|
||||||
|
"que": "Runa Simi",
|
||||||
|
"roh": "rumantsch grischun",
|
||||||
|
"run": "Ikirundi",
|
||||||
|
"ron": "Română",
|
||||||
|
"rus": "Русский",
|
||||||
|
"kin": "Ikinyarwanda",
|
||||||
|
"san": "संस्कृतम्",
|
||||||
|
"snd": "सिन्धी",
|
||||||
|
"sag": "yângâ tî sängö",
|
||||||
|
"sin": "සිංහල",
|
||||||
|
"slk": "slovenčina",
|
||||||
|
"slv": "slovenski jezik",
|
||||||
|
"sna": "chiShona",
|
||||||
|
"som": "Soomaaliga",
|
||||||
|
"sqi": "Shqip",
|
||||||
|
"srp": "српски језик",
|
||||||
|
"ssw": "SiSwati",
|
||||||
|
"swe": "svenska",
|
||||||
|
"tam": "தமிழ்",
|
||||||
|
"tel": "తెలుగు",
|
||||||
|
"tgk": "тоҷикӣ",
|
||||||
|
"tha": "ไทย",
|
||||||
|
"tir": "ትግርኛ",
|
||||||
|
"tuk": "Türkmen",
|
||||||
|
"tsn": "Setswana",
|
||||||
|
"tur": "Türkçe",
|
||||||
|
"tso": "Xitsonga",
|
||||||
|
"tat": "татар теле",
|
||||||
|
"uig": "ئۇيغۇرچە",
|
||||||
|
"ukr": "Українська",
|
||||||
|
"urd": "اردو",
|
||||||
|
"uzb": "Ўзбек",
|
||||||
|
"ven": "Tshivenḓa",
|
||||||
|
"vie": "Tiếng Việt",
|
||||||
|
"wln": "walon",
|
||||||
|
"wol": "Wollof",
|
||||||
|
"xho": "isiXhosa",
|
||||||
|
"yor": "Yorùbá",
|
||||||
|
"zho": "中文",
|
||||||
|
"zul": "isiZulu"
|
||||||
|
}
|
|
@ -22,6 +22,10 @@
|
||||||
rel="Stylesheet"
|
rel="Stylesheet"
|
||||||
/>
|
/>
|
||||||
<style>
|
<style>
|
||||||
|
html {
|
||||||
|
min-height: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
body {
|
body {
|
||||||
background: radial-gradient(#eeeeee 15%, transparent 16%) 0 0,
|
background: radial-gradient(#eeeeee 15%, transparent 16%) 0 0,
|
||||||
radial-gradient(#eeeeee 15%, transparent 16%) 8px 8px,
|
radial-gradient(#eeeeee 15%, transparent 16%) 8px 8px,
|
||||||
|
@ -32,10 +36,17 @@
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
max-width: 1100px;
|
max-width: 1100px;
|
||||||
|
min-height: 100%;
|
||||||
}
|
}
|
||||||
.book__list {
|
.book__list {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
.kiwixHomeBody {
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
min-height: 100%;
|
||||||
|
margin: 0 0 15px;
|
||||||
|
}
|
||||||
.book {
|
.book {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
|
@ -49,7 +60,33 @@
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
background-color: #f1f1f1;
|
background-color: #f1f1f1;
|
||||||
box-shadow: 2px 2px 5px 0px #ccc;
|
box-shadow: 2px 2px 5px 0 #ccc;
|
||||||
|
}
|
||||||
|
#kiwixfooter {
|
||||||
|
text-align: center;
|
||||||
|
margin: 0.5em;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 46%;
|
||||||
|
}
|
||||||
|
.kiwixHomeNavbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.kiwixFilter {
|
||||||
|
margin: 8px 10px;
|
||||||
|
}
|
||||||
|
.kiwixSearch, .searchButton {
|
||||||
|
margin: 0 13px 0 0;
|
||||||
|
}
|
||||||
|
.kiwixSearchForm {
|
||||||
|
margin: 8px 10px;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
@media (max-width: 1100px) {
|
||||||
|
.kiwixHomeBody {
|
||||||
|
padding: 0 125px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.book:hover {
|
.book:hover {
|
||||||
background-color: #f9f9f9;
|
background-color: #f9f9f9;
|
||||||
|
@ -70,7 +107,7 @@
|
||||||
line-height: 1em;
|
line-height: 1em;
|
||||||
}
|
}
|
||||||
.book__description {
|
.book__description {
|
||||||
padding: 5px 55px 5px 0px;
|
padding: 5px 55px 5px 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@ -83,14 +120,87 @@
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 1em;
|
line-height: 1em;
|
||||||
}
|
}
|
||||||
|
a:link {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
a:visited {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.noResults {
|
||||||
|
position: absolute;
|
||||||
|
top: 48%;
|
||||||
|
left: 42%;
|
||||||
|
}
|
||||||
|
.loader-spinner {
|
||||||
|
position: absolute;
|
||||||
|
top: -50%;
|
||||||
|
left: 50%;
|
||||||
|
border: 5px solid #f3f3f3;
|
||||||
|
border-radius: 50%;
|
||||||
|
border-top: 5px solid #3498db;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
margin: auto;
|
||||||
|
-webkit-animation: spin 1s linear infinite; /* Safari */
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
margin-top: 35px;
|
||||||
|
margin-bottom: -35px;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
/* Safari */
|
||||||
|
@-webkit-keyframes spin {
|
||||||
|
0% { -webkit-transform: rotate(0deg); }
|
||||||
|
100% { -webkit-transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
@keyframes spin {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
.loader {
|
||||||
|
position: relative;
|
||||||
|
height: 70px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.fadeOut {
|
||||||
|
position: fixed;
|
||||||
|
display: none;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1;
|
||||||
|
background: linear-gradient(180deg, rgba(232, 232, 232, 0) 0%, rgb(232, 232, 232) 100%);
|
||||||
|
height: 80px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.spacer {
|
||||||
|
height: 20px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<script type="text/javascript" src="{{root}}/skin/index.js" async></script>
|
<script src="{{root}}/skin/isotope.pkgd.min.js" defer></script>
|
||||||
|
<script src="{{root}}/skin/categoryList.js"></script>
|
||||||
|
<script src="{{root}}/skin/langList.js"></script>
|
||||||
|
<script src="{{root}}/skin/iso6391To3.js"></script>
|
||||||
|
<script type="text/javascript" src="{{root}}/skin/index.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="kiwix">
|
<body class="kiwix">
|
||||||
<div class="kiwix">
|
<div class='kiwixHomeNavbar'>
|
||||||
<div class="book__list"></div>
|
<select name="lang" id="languageFilter" class='kiwixFilter'>
|
||||||
|
<option value="" selected>All languages</option>
|
||||||
|
</select>
|
||||||
|
<select name="category" id="categoryFilter" class='kiwixFilter'>
|
||||||
|
<option value="" selected>All categories</option>
|
||||||
|
</select>
|
||||||
|
<form id='kiwixSearchForm' class='kiwixSearchForm'>
|
||||||
|
<input type="text" name="q" id="searchFilter" class='kiwixSearch'>
|
||||||
|
<input type="submit" class="searchButton" value="Search"/>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="kiwixHomeBody">
|
||||||
|
<div class="book__list"></div>
|
||||||
|
<div id="fadeOut" class="fadeOut"></div>
|
||||||
|
</div>
|
||||||
|
<div class="loader"><div class="loader-spinner"></div></div>
|
||||||
|
<div class="spacer"></div>
|
||||||
<div id="kiwixfooter">Powered by <a href="https://kiwix.org">Kiwix</a></div>
|
<div id="kiwixfooter">Powered by <a href="https://kiwix.org">Kiwix</a></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Reference in New Issue