mirror of https://github.com/kiwix/libkiwix.git
Merge pull request #711 from kiwix/tagFilter
Add tag filtering in kiwix-serve
This commit is contained in:
commit
12e0fb6934
|
@ -198,7 +198,7 @@ To use JS provided by kiwix-serve you can use the following template to start wi
|
||||||
```
|
```
|
||||||
<form id='kiwixSearchForm'>
|
<form id='kiwixSearchForm'>
|
||||||
<input type="text" name="q" placeholder="Search" id="searchFilter" class='kiwixSearch filter'>
|
<input type="text" name="q" placeholder="Search" id="searchFilter" class='kiwixSearch filter'>
|
||||||
<input type="submit" class="searchButton" value="Search"/>
|
<input type="submit" class="kiwixButton" value="Search"/>
|
||||||
</form>
|
</form>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -77,6 +77,13 @@
|
||||||
return queryNode != null ? queryNode.innerHTML : "";
|
return queryNode != null ? queryNode.innerHTML : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateTagLink(tagValue) {
|
||||||
|
tagValue = tagValue.toLowerCase();
|
||||||
|
const humanFriendlyTagValue = humanFriendlyTitle(tagValue);
|
||||||
|
const tagMessage = `Filter by tag "${humanFriendlyTagValue}"`;
|
||||||
|
return `<span class='tag__link' aria-label='${tagMessage}' title='${tagMessage}' data-tag=${tagValue}>${humanFriendlyTagValue}</span>`
|
||||||
|
}
|
||||||
|
|
||||||
function generateBookHtml(book, sort = false) {
|
function generateBookHtml(book, sort = false) {
|
||||||
const link = book.querySelector('link[type="text/html"]').getAttribute('href');
|
const link = book.querySelector('link[type="text/html"]').getAttribute('href');
|
||||||
let iconUrl;
|
let iconUrl;
|
||||||
|
@ -91,9 +98,9 @@
|
||||||
const langCode = getInnerHtml(book, 'language');
|
const langCode = getInnerHtml(book, 'language');
|
||||||
const language = languages[langCode];
|
const language = languages[langCode];
|
||||||
const tags = getInnerHtml(book, 'tags');
|
const tags = getInnerHtml(book, 'tags');
|
||||||
let tagHtml = tags.split(';').filter(tag => {return !(tag.split(':')[0].startsWith('_'))})
|
const tagList = tags.split(';').filter(tag => {return !(tag.startsWith('_'))});
|
||||||
.map((tag) => {return tag.charAt(0).toUpperCase() + tag.slice(1)})
|
const tagFilterLinks = tagList.map((tagValue) => generateTagLink(tagValue));
|
||||||
.join(' | ').replace(/_/g, ' ');
|
const tagHtml = tagFilterLinks.join(' | ');
|
||||||
let downloadLink;
|
let downloadLink;
|
||||||
let zimSize = 0;
|
let zimSize = 0;
|
||||||
try {
|
try {
|
||||||
|
@ -113,17 +120,21 @@
|
||||||
}
|
}
|
||||||
const faviconAttr = iconUrl != undefined ? `style="background-image: url('${iconUrl}')"` : '';
|
const faviconAttr = iconUrl != undefined ? `style="background-image: url('${iconUrl}')"` : '';
|
||||||
const languageAttr = langCode != '' ? `title="${language}" aria-label="${language}"` : 'style="background-color: transparent"';
|
const languageAttr = langCode != '' ? `title="${language}" aria-label="${language}"` : 'style="background-color: transparent"';
|
||||||
divTag.innerHTML = `<a class="book__link" href="${link}" data-hover="Preview">
|
divTag.innerHTML = `
|
||||||
<div class="book__wrapper">
|
<div class="book__wrapper">
|
||||||
|
<a class="book__link" href="${link}" data-hover="Preview">
|
||||||
|
<div class="book__link__wrapper">
|
||||||
<div class="book__icon" ${faviconAttr}></div>
|
<div class="book__icon" ${faviconAttr}></div>
|
||||||
<div class="book__header">
|
<div class="book__header">
|
||||||
<div id="book__title">${title}</div>
|
<div id="book__title">${title}</div>
|
||||||
${downloadLink ? `<div class="book__download"><span data-link="${downloadLink}">Download ${humanFriendlyZimSize ? ` - ${humanFriendlyZimSize}</span></div>`: ''}` : ''}
|
${downloadLink ? `<div class="book__download"><span data-link="${downloadLink}">Download ${humanFriendlyZimSize ? ` - ${humanFriendlyZimSize}</span></div>`: ''}` : ''}
|
||||||
</div>
|
</div>
|
||||||
<div class="book__description" title="${description}">${description}</div>
|
<div class="book__description" title="${description}">${description}</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
<div class="book__languageTag" ${languageAttr}>${getLanguageCodeToDisplay(langCode)}</div>
|
<div class="book__languageTag" ${languageAttr}>${getLanguageCodeToDisplay(langCode)}</div>
|
||||||
<div class="book__tags"><div class="book__tags--wrapper">${tagHtml}</div></div>
|
<div class="book__tags"><div class="book__tags--wrapper">${tagHtml}</div></div>
|
||||||
</div></div></a>`;
|
</div></div>`;
|
||||||
return divTag;
|
return divTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,6 +278,16 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setNoResultsContent() {
|
||||||
|
const kiwixHomeBody = document.querySelector('.kiwixHomeBody');
|
||||||
|
const divTag = document.createElement('div');
|
||||||
|
divTag.setAttribute('class', 'noResults');
|
||||||
|
divTag.innerHTML = `No result. Would you like to <a href="?lang=">reset filter</a>?`;
|
||||||
|
kiwixHomeBody.append(divTag);
|
||||||
|
kiwixHomeBody.setAttribute('style', 'display: flex; justify-content: center; align-items: center');
|
||||||
|
loader.setAttribute('style', 'position: absolute; top: 50%');
|
||||||
|
}
|
||||||
|
|
||||||
function checkAndInjectEmptyMessage() {
|
function checkAndInjectEmptyMessage() {
|
||||||
const kiwixHomeBody = document.querySelector('.kiwixHomeBody');
|
const kiwixHomeBody = document.querySelector('.kiwixHomeBody');
|
||||||
if (!bookOrderMap.size) {
|
if (!bookOrderMap.size) {
|
||||||
|
@ -274,28 +295,7 @@
|
||||||
noResultInjected = true;
|
noResultInjected = true;
|
||||||
iso.remove(document.getElementsByClassName('book__list')[0].getElementsByTagName('div'));
|
iso.remove(document.getElementsByClassName('book__list')[0].getElementsByTagName('div'));
|
||||||
iso.layout();
|
iso.layout();
|
||||||
setTimeout(() => {
|
setTimeout(setNoResultsContent, 300);
|
||||||
const divTag = document.createElement('div');
|
|
||||||
divTag.setAttribute('class', 'noResults');
|
|
||||||
divTag.innerHTML = `No result. Would you like to <a href="/?lang=">reset filter</a>?`;
|
|
||||||
kiwixHomeBody.append(divTag);
|
|
||||||
kiwixHomeBody.setAttribute('style', 'display: flex; justify-content: center; align-items: center');
|
|
||||||
divTag.getElementsByTagName('a')[0].onclick = (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
window.history.pushState({}, null, `${window.location.href.split('?')[0]}?lang=`);
|
|
||||||
setCookie(filterCookieName, 'lang=');
|
|
||||||
resetAndFilter();
|
|
||||||
document.querySelectorAll('.filter').forEach(filter => {
|
|
||||||
filter.value = params.get(filter.name) || '';
|
|
||||||
if (filter.value) {
|
|
||||||
filter.style = 'background-color: #858585; color: #fff';
|
|
||||||
} else {
|
|
||||||
filter.style = 'background-color: #ffffff; color: black';
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
loader.setAttribute('style', 'position: absolute; top: 50%');
|
|
||||||
}, 300);
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if (noResultInjected) {
|
} else if (noResultInjected) {
|
||||||
|
@ -344,6 +344,7 @@
|
||||||
insertModal(downloadButton);
|
insertModal(downloadButton);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
refreshTagLinks();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resetAndFilter(filterType = '', filterValue = '') {
|
async function resetAndFilter(filterType = '', filterValue = '') {
|
||||||
|
@ -355,22 +356,16 @@
|
||||||
params = new URLSearchParams(window.location.search);
|
params = new URLSearchParams(window.location.search);
|
||||||
if (filterType) {
|
if (filterType) {
|
||||||
params.set(filterType, filterValue);
|
params.set(filterType, filterValue);
|
||||||
window.history.pushState({}, null, `${window.location.href.split('?')[0]}?${params.toString()}`);
|
window.history.pushState({}, null, `?${params.toString()}`);
|
||||||
setCookie(filterCookieName, params.toString());
|
setCookie(filterCookieName, params.toString());
|
||||||
}
|
}
|
||||||
document.querySelectorAll('.filter').forEach(filter => {
|
updateFilterColors();
|
||||||
if (filter.value) {
|
|
||||||
filter.style = 'background-color: #858585; color: #fff';
|
|
||||||
} else {
|
|
||||||
filter.style = 'background-color: #ffffff; color: black';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await loadAndDisplayBooks(true);
|
await loadAndDisplayBooks(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('popstate', async () => {
|
window.addEventListener('popstate', async () => {
|
||||||
await resetAndFilter();
|
await resetAndFilter();
|
||||||
document.querySelectorAll('.filter').forEach(filter => {filter.value = params.get(filter.name) || ''});
|
updateVisibleParams();
|
||||||
});
|
});
|
||||||
|
|
||||||
async function loadSubset() {
|
async function loadSubset() {
|
||||||
|
@ -384,6 +379,56 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateFilterColors() {
|
||||||
|
document.querySelectorAll('.filter').forEach(filter => {
|
||||||
|
if (filter.value) {
|
||||||
|
filter.style = 'background-color: #858585; color: #fff';
|
||||||
|
} else {
|
||||||
|
filter.style = 'background-color: #ffffff; color: black';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addTagElement(tagValue, resetFilter) {
|
||||||
|
const tagElement = document.getElementsByClassName('tagFilterLabel')[0];
|
||||||
|
tagElement.style.display = 'inline-block';
|
||||||
|
const humanFriendlyTagValue = humanFriendlyTitle(tagValue);
|
||||||
|
tagElement.innerHTML = `${humanFriendlyTagValue}`;
|
||||||
|
const tagMessage = `Stop filtering by tag "${humanFriendlyTagValue}"`;
|
||||||
|
tagElement.setAttribute('aria-label', tagMessage);
|
||||||
|
tagElement.setAttribute('title', tagMessage);
|
||||||
|
if (resetFilter)
|
||||||
|
resetAndFilter('tag', tagValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshTagLinks() {
|
||||||
|
const tagLinks = document.getElementsByClassName('tag__link');
|
||||||
|
[...tagLinks].forEach(elem => {
|
||||||
|
if (!elem.getAttribute('click-listener')) {
|
||||||
|
elem.addEventListener('click', () => addTagElement(elem.dataset.tag, true));
|
||||||
|
elem.setAttribute('click-listener', 'true');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeTagElement(resetFilter) {
|
||||||
|
const tagElement = document.getElementsByClassName('tagFilterLabel')[0];
|
||||||
|
tagElement.style.display = 'none';
|
||||||
|
if (resetFilter)
|
||||||
|
resetAndFilter('tag', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateVisibleParams() {
|
||||||
|
document.querySelectorAll('.filter').forEach(filter => {filter.value = params.get(filter.name) || ''});
|
||||||
|
updateFilterColors();
|
||||||
|
const tagKey = params.get('tag');
|
||||||
|
if (tagKey !== null && tagKey.trim() !== '') {
|
||||||
|
addTagElement(tagKey, false);
|
||||||
|
} else {
|
||||||
|
removeTagElement(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
window.addEventListener('resize', (event) => {
|
window.addEventListener('resize', (event) => {
|
||||||
if (timer) {clearTimeout(timer)}
|
if (timer) {clearTimeout(timer)}
|
||||||
timer = setTimeout(() => {
|
timer = setTimeout(() => {
|
||||||
|
@ -419,15 +464,16 @@
|
||||||
document.querySelectorAll('.filter').forEach(filter => {
|
document.querySelectorAll('.filter').forEach(filter => {
|
||||||
filter.addEventListener('change', () => {resetAndFilter(filter.name, filter.value)});
|
filter.addEventListener('change', () => {resetAndFilter(filter.name, filter.value)});
|
||||||
});
|
});
|
||||||
|
const tagElement = document.getElementsByClassName('tagFilterLabel')[0];
|
||||||
|
tagElement.addEventListener('click', () => removeTagElement(true));
|
||||||
if (filters) {
|
if (filters) {
|
||||||
window.history.pushState({}, null, `${window.location.href.split('?')[0]}?${params.toString()}`);
|
const currentLink = window.location.search;
|
||||||
|
const newLink = `?${params.toString()}`;
|
||||||
|
if (currentLink != newLink) {
|
||||||
|
window.history.pushState({}, null, newLink);
|
||||||
}
|
}
|
||||||
params.forEach((value, key) => {
|
|
||||||
const selectBox = document.getElementsByName(key)[0];
|
|
||||||
if (selectBox) {
|
|
||||||
selectBox.value = value
|
|
||||||
}
|
}
|
||||||
});
|
updateVisibleParams();
|
||||||
document.getElementById('kiwixSearchForm').onsubmit = (event) => {event.preventDefault()};
|
document.getElementById('kiwixSearchForm').onsubmit = (event) => {event.preventDefault()};
|
||||||
if (!window.location.search) {
|
if (!window.location.search) {
|
||||||
const browserLang = navigator.language.split('-')[0];
|
const browserLang = navigator.language.split('-')[0];
|
||||||
|
@ -438,13 +484,7 @@
|
||||||
langFilter.dispatchEvent(new Event('change'));
|
langFilter.dispatchEvent(new Event('change'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
document.querySelectorAll('.filter').forEach(filter => {
|
|
||||||
if (filter.value) {
|
|
||||||
filter.style = 'background-color: #858585; color: #fff';
|
|
||||||
} else {
|
|
||||||
filter.style = 'background-color: #ffffff; color: black';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
setCookie(filterCookieName, params.toString());
|
setCookie(filterCookieName, params.toString());
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,8 @@
|
||||||
</div>
|
</div>
|
||||||
<form id='kiwixSearchForm' class='kiwixNav__SearchForm'>
|
<form id='kiwixSearchForm' class='kiwixNav__SearchForm'>
|
||||||
<input type="text" name="q" placeholder="Search" id="searchFilter" class='kiwixSearch filter'>
|
<input type="text" name="q" placeholder="Search" id="searchFilter" class='kiwixSearch filter'>
|
||||||
<input type="submit" class="searchButton" value="Search"/>
|
<span class="kiwixButton tagFilterLabel"></span>
|
||||||
|
<input type="submit" class="kiwixButton kiwixButtonHover" value="Search"/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="kiwixHomeBody">
|
<div class="kiwixHomeBody">
|
||||||
|
|
|
@ -179,12 +179,12 @@ R"EXPECTEDRESULT( src="/ROOT/skin/jquery-ui/external/jquery/jquery.js?cache
|
||||||
src="/ROOT/skin/jquery-ui/jquery-ui.min.js?cacheid=d927c2ff"
|
src="/ROOT/skin/jquery-ui/jquery-ui.min.js?cacheid=d927c2ff"
|
||||||
href="/ROOT/skin/jquery-ui/jquery-ui.min.css?cacheid=e1de77b3"
|
href="/ROOT/skin/jquery-ui/jquery-ui.min.css?cacheid=e1de77b3"
|
||||||
href="/ROOT/skin/jquery-ui/jquery-ui.theme.min.css?cacheid=2a5841f9"
|
href="/ROOT/skin/jquery-ui/jquery-ui.theme.min.css?cacheid=2a5841f9"
|
||||||
href="/ROOT/skin/index.css?cacheid=1aca980a"
|
href="/ROOT/skin/index.css?cacheid=a1acc52f"
|
||||||
src: url("/ROOT/skin/fonts/Poppins.ttf?cacheid=af705837") format("truetype");
|
src: url("/ROOT/skin/fonts/Poppins.ttf?cacheid=af705837") format("truetype");
|
||||||
src: url("/ROOT/skin/fonts/Roboto.ttf?cacheid=84d10248") format("truetype");
|
src: url("/ROOT/skin/fonts/Roboto.ttf?cacheid=84d10248") format("truetype");
|
||||||
<script src="/ROOT/skin/isotope.pkgd.min.js?cacheid=2e48d392" defer></script>
|
<script src="/ROOT/skin/isotope.pkgd.min.js?cacheid=2e48d392" defer></script>
|
||||||
<script src="/ROOT/skin/iso6391To3.js?cacheid=ecde2bb3"></script>
|
<script src="/ROOT/skin/iso6391To3.js?cacheid=ecde2bb3"></script>
|
||||||
<script type="text/javascript" src="/ROOT/skin/index.js?cacheid=31ffa1f7" defer></script>
|
<script type="text/javascript" src="/ROOT/skin/index.js?cacheid=e99ed2dd" defer></script>
|
||||||
)EXPECTEDRESULT"
|
)EXPECTEDRESULT"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue