mirror of https://github.com/kiwix/libkiwix.git
Merge pull request #541 from kiwix/adding_dynamic_and_subset_loading
Dynamic and subset loading of catalogue in kiwix-serve
This commit is contained in:
commit
3a4e8303a0
|
@ -4,3 +4,4 @@ subprojects/googletest-release*
|
|||
*.class
|
||||
build/
|
||||
.vscode/
|
||||
builddir/
|
||||
|
|
|
@ -281,27 +281,6 @@ MustacheData InternalServer::get_default_data() const
|
|||
return data;
|
||||
}
|
||||
|
||||
MustacheData InternalServer::homepage_data() const
|
||||
{
|
||||
auto data = get_default_data();
|
||||
|
||||
MustacheData books{MustacheData::type::list};
|
||||
for (auto& bookId: mp_library->filter(kiwix::Filter().local(true).valid(true))) {
|
||||
auto& currentBook = mp_library->getBookById(bookId);
|
||||
|
||||
MustacheData book;
|
||||
book.set("name", mp_nameMapper->getNameForId(bookId));
|
||||
book.set("title", currentBook.getTitle());
|
||||
book.set("description", currentBook.getDescription());
|
||||
book.set("articleCount", beautifyInteger(currentBook.getArticleCount()));
|
||||
book.set("mediaCount", beautifyInteger(currentBook.getMediaCount()));
|
||||
books.push_back(book);
|
||||
}
|
||||
|
||||
data.set("books", books);
|
||||
return data;
|
||||
}
|
||||
|
||||
bool InternalServer::etag_not_needed(const RequestContext& request) const
|
||||
{
|
||||
const std::string url = request.get_url();
|
||||
|
@ -325,7 +304,7 @@ InternalServer::get_matching_if_none_match_etag(const RequestContext& r) const
|
|||
|
||||
std::unique_ptr<Response> InternalServer::build_homepage(const RequestContext& request)
|
||||
{
|
||||
return ContentResponse::build(*this, RESOURCE::templates::index_html, homepage_data(), "text/html; charset=utf-8");
|
||||
return ContentResponse::build(*this, RESOURCE::templates::index_html, get_default_data(), "text/html; charset=utf-8");
|
||||
}
|
||||
|
||||
std::unique_ptr<Response> InternalServer::handle_meta(const RequestContext& request)
|
||||
|
@ -663,7 +642,7 @@ std::vector<std::string>
|
|||
InternalServer::search_catalog(const RequestContext& request,
|
||||
kiwix::OPDSDumper& opdsDumper)
|
||||
{
|
||||
auto filter = kiwix::Filter().valid(true).local(true).remote(true);
|
||||
auto filter = kiwix::Filter().valid(true).local(true);
|
||||
string query("<Empty query>");
|
||||
size_t count(10);
|
||||
size_t startIndex(0);
|
||||
|
|
|
@ -84,7 +84,6 @@ class InternalServer {
|
|||
kiwix::OPDSDumper& opdsDumper);
|
||||
|
||||
MustacheData get_default_data() const;
|
||||
MustacheData homepage_data() const;
|
||||
|
||||
std::shared_ptr<Reader> get_reader(const std::string& bookName) const;
|
||||
bool etag_not_needed(const RequestContext& r) const;
|
||||
|
|
|
@ -19,6 +19,7 @@ skin/jquery-ui/jquery-ui.theme.min.css
|
|||
skin/jquery-ui/jquery-ui.min.css
|
||||
skin/caret.png
|
||||
skin/taskbar.js
|
||||
skin/index.js
|
||||
skin/taskbar.css
|
||||
skin/block_external.js
|
||||
templates/search_result.html
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
(function() {
|
||||
const root = $(`link[type='root']`).attr('href');
|
||||
const incrementalLoadingParams = {
|
||||
start: 0,
|
||||
count: viewPortToCount()
|
||||
};
|
||||
let isFetching = false;
|
||||
let timer;
|
||||
|
||||
function queryUrlBuilder() {
|
||||
let url = `${root}/catalog/search?`;
|
||||
url += Object.keys(incrementalLoadingParams).map(key => `${key}=${incrementalLoadingParams[key]}`).join("&");
|
||||
return url;
|
||||
}
|
||||
|
||||
function viewPortToCount(){
|
||||
return Math.floor(window.innerHeight/100 + 1)*(window.innerWidth>1000 ? 3 : 2);
|
||||
}
|
||||
|
||||
function getInnerHtml(node, query) {
|
||||
return node.querySelector(query).innerHTML;
|
||||
}
|
||||
|
||||
function generateBookHtml(book) {
|
||||
const link = book.querySelector('link').getAttribute('href');
|
||||
const title = getInnerHtml(book, 'title');
|
||||
const description = getInnerHtml(book, 'summary');
|
||||
const id = getInnerHtml(book, 'id');
|
||||
const iconUrl = getInnerHtml(book, 'icon');
|
||||
const articleCount = getInnerHtml(book, 'articleCount');
|
||||
const mediaCount = getInnerHtml(book, 'mediaCount');
|
||||
|
||||
return `<a href='${link}' data-id='${id}'><div class='book'>
|
||||
<div class='book__background' style="background-image: url('${iconUrl}');">
|
||||
<div class='book__title' title='${title}'>${title}</div>
|
||||
<div class='book__description' title='${description}'>${description}</div>
|
||||
<div class='book__info'>${articleCount} articles, ${mediaCount} medias</div>
|
||||
</div>
|
||||
</div></a>`;
|
||||
}
|
||||
|
||||
async function loadAndDisplayBooks() {
|
||||
if (isFetching) return;
|
||||
isFetching = true;
|
||||
fetch(queryUrlBuilder()).then(async (resp) => {
|
||||
const data = new window.DOMParser().parseFromString(await resp.text(), 'application/xml');
|
||||
const books = data.querySelectorAll('entry');
|
||||
let bookHtml = '';
|
||||
books.forEach((book) => {bookHtml += generateBookHtml(book)});
|
||||
document.querySelector('.book__list').innerHTML += bookHtml;
|
||||
incrementalLoadingParams.start += books.length;
|
||||
if (books.length < incrementalLoadingParams.count) {
|
||||
incrementalLoadingParams.count = 0;
|
||||
}
|
||||
isFetching = false;
|
||||
});
|
||||
}
|
||||
|
||||
async function loadSubset() {
|
||||
if (incrementalLoadingParams.count && window.innerHeight + window.scrollY >= document.body.offsetHeight) {
|
||||
loadAndDisplayBooks();
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('resize', (event) => {
|
||||
if (timer) {clearTimeout(timer)}
|
||||
timer = setTimeout(() => {
|
||||
incrementalLoadingParams.count = incrementalLoadingParams.count && viewPortToCount();
|
||||
loadSubset();
|
||||
}, 100, event);
|
||||
});
|
||||
|
||||
window.addEventListener('scroll', loadSubset);
|
||||
|
||||
window.onload = async () => {
|
||||
loadAndDisplayBooks();
|
||||
}
|
||||
})();
|
|
@ -1,66 +1,96 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Welcome to Kiwix Server</title>
|
||||
<script type="text/javascript" src="{{root}}/skin/jquery-ui/external/jquery/jquery.js"></script>
|
||||
<script type="text/javascript" src="{{root}}/skin/jquery-ui/jquery-ui.min.js"></script>
|
||||
<link type="text/css" href="{{root}}/skin/jquery-ui/jquery-ui.min.css" rel="Stylesheet" />
|
||||
<link type="text/css" href="{{root}}/skin/jquery-ui/jquery-ui.theme.min.css" rel="Stylesheet" />
|
||||
<style>
|
||||
body {
|
||||
background:
|
||||
radial-gradient(#EEEEEE 15%, transparent 16%) 0 0,
|
||||
radial-gradient(#EEEEEE 15%, transparent 16%) 8px 8px,
|
||||
radial-gradient(rgba(255,255,255,.1) 15%, transparent 20%) 0 1px,
|
||||
radial-gradient(rgba(255,255,255,.1) 15%, transparent 20%) 8px 9px;
|
||||
background-color:#E8E8E8;
|
||||
background-size:16px 16px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 1100px;
|
||||
}
|
||||
.book__list { text-align: center; }
|
||||
.book {
|
||||
display: inline-block; vertical-align: bottom; margin: 8px; padding: 12px 15px; width: 300px;
|
||||
border: 1px solid #ccc; border-radius: 8px;
|
||||
text-align: left; color: #000; font-family: sans-serif; font-size: 13px;
|
||||
background-color:#F1F1F1;
|
||||
box-shadow: 2px 2px 5px 0px #ccc;
|
||||
}
|
||||
.book:hover { background-color: #F9F9F9; box-shadow: none;}
|
||||
.book__background { background-repeat: no-repeat; background-size: 48px 48px; background-position: top right; }
|
||||
.book__title {
|
||||
padding: 0 55px 0 0;overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
font-size: 18px; color: #0645ad; line-height: 1em;
|
||||
}
|
||||
.book__description {
|
||||
padding: 5px 55px 5px 0px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||
font-size: 15px; line-height: 1em;
|
||||
}
|
||||
.book__info { color: #777; font-weight: bold; font-size: 13px; line-height: 1em; }
|
||||
</style>
|
||||
<script type="text/javascript" src="{{root}}/skin/taskbar.js" async></script>
|
||||
</head>
|
||||
<body class="kiwix">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Welcome to Kiwix Server</title>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="{{root}}/skin/jquery-ui/external/jquery/jquery.js"
|
||||
></script>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="{{root}}/skin/jquery-ui/jquery-ui.min.js"
|
||||
></script>
|
||||
<link
|
||||
type="text/css"
|
||||
href="{{root}}/skin/jquery-ui/jquery-ui.min.css"
|
||||
rel="Stylesheet"
|
||||
/>
|
||||
<link
|
||||
type="text/css"
|
||||
href="{{root}}/skin/jquery-ui/jquery-ui.theme.min.css"
|
||||
rel="Stylesheet"
|
||||
/>
|
||||
<style>
|
||||
body {
|
||||
background: radial-gradient(#eeeeee 15%, transparent 16%) 0 0,
|
||||
radial-gradient(#eeeeee 15%, transparent 16%) 8px 8px,
|
||||
radial-gradient(rgba(255, 255, 255, 0.1) 15%, transparent 20%) 0 1px,
|
||||
radial-gradient(rgba(255, 255, 255, 0.1) 15%, transparent 20%) 8px 9px;
|
||||
background-color: #e8e8e8;
|
||||
background-size: 16px 16px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 1100px;
|
||||
}
|
||||
.book__list {
|
||||
text-align: center;
|
||||
}
|
||||
.book {
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
margin: 8px;
|
||||
padding: 12px 15px;
|
||||
width: 300px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 8px;
|
||||
text-align: left;
|
||||
color: #000;
|
||||
font-family: sans-serif;
|
||||
font-size: 13px;
|
||||
background-color: #f1f1f1;
|
||||
box-shadow: 2px 2px 5px 0px #ccc;
|
||||
}
|
||||
.book:hover {
|
||||
background-color: #f9f9f9;
|
||||
box-shadow: none;
|
||||
}
|
||||
.book__background {
|
||||
background-repeat: no-repeat;
|
||||
background-size: 48px 48px;
|
||||
background-position: top right;
|
||||
}
|
||||
.book__title {
|
||||
padding: 0 55px 0 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 18px;
|
||||
color: #0645ad;
|
||||
line-height: 1em;
|
||||
}
|
||||
.book__description {
|
||||
padding: 5px 55px 5px 0px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 15px;
|
||||
line-height: 1em;
|
||||
}
|
||||
.book__info {
|
||||
color: #777;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
line-height: 1em;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript" src="{{root}}/skin/index.js" async></script>
|
||||
</head>
|
||||
<body class="kiwix">
|
||||
<div class="kiwix">
|
||||
<div class="book__list"></div>
|
||||
</div>
|
||||
|
||||
<div class="kiwix">
|
||||
<div class='book__list'>
|
||||
{{#books}}
|
||||
<a href="{{root}}/{{name}}"><div class='book'>
|
||||
<div class='book__background' style="background-image: url('{{root}}/meta?content={{#urlencoded}}{{{name}}}{{/urlencoded}}&name=favicon');">
|
||||
<div class='book__title' title='{{title}}'>{{title}}</div>
|
||||
<div class='book__description' title='{{description}}'>{{description}}</div>
|
||||
<div class='book__info'>{{articleCount}} articles, {{mediaCount}} medias</div>
|
||||
</div>
|
||||
</div></a>
|
||||
{{/books}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="kiwixfooter">
|
||||
Powered by <a href="https://kiwix.org">Kiwix</a>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
<div id="kiwixfooter">Powered by <a href="https://kiwix.org">Kiwix</a></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
Loading…
Reference in New Issue