(function() { const root = $(`link[type='root']`).attr('href'); const incrementalLoadingParams = { start: 0, count: viewPortToCount() }; const bookOrderMap = new Map(); const filterCookieName = 'filters'; const oneDayDelta = 86400000; let loader; let footer; let fadeOutDiv; let iso; let isFetching = false; let noResultInjected = false; let filters = getCookie(filterCookieName); let params = new URLSearchParams(window.location.search || filters || ''); let timer; function queryUrlBuilder() { let url = `${root}/catalog/search?`; url += Object.keys(incrementalLoadingParams).map(key => `${key}=${incrementalLoadingParams[key]}`).join("&"); params.forEach((value, key) => {url+= value ? `&${key}=${value}` : ''}); return (url); } function setCookie(cookieName, cookieValue) { const date = new Date(); date.setTime(date.getTime() + oneDayDelta); document.cookie = `${cookieName}=${cookieValue};expires=${date.toUTCString()};sameSite=Strict`; } function getCookie(cookieName) { const name = cookieName + "="; let result; decodeURIComponent(document.cookie).split('; ').forEach(val => { if (val.indexOf(name) === 0) { result = val.substring(name.length); } }); return result; } const humanFriendlySize = (fileSize) => { if (fileSize === 0) { return ''; } const units = ['bytes', 'kB', 'MB', 'GB', 'TB']; let quotient = Math.floor(Math.log10(fileSize) / 3); quotient = quotient < units.length ? quotient : units.length - 1; fileSize /= (1000 ** quotient); return `${+fileSize.toFixed(2)} ${units[quotient]}`; }; const humanFriendlyTitle = (title) => { title = title.replace(/_/g, ' '); return htmlEncode(title[0].toUpperCase() + title.slice(1)); } function htmlEncode(str) { return str.replace(/[\u00A0-\u9999<>\&]/gim, (i) => `&#${i.charCodeAt(0)};`); } function viewPortToCount(){ const zoom = Math.floor((( window.outerWidth - 10 ) / window.innerWidth) * 100); return Math.floor(window.innerHeight/(3*zoom) + 1)*(window.innerWidth/(2.5*zoom) + 1); } function getInnerHtml(node, query) { const queryNode = node.querySelector(query); return queryNode != null ? queryNode.innerHTML : ""; } function generateBookHtml(book, sort = false) { const link = book.querySelector('link[type="text/html"]').getAttribute('href'); let iconUrl; book.querySelectorAll('link[rel="http://opds-spec.org/image/thumbnail"]').forEach(link => { if (link.getAttribute('type').split(';')[1] == 'width=48' && !iconUrl) { iconUrl = link.getAttribute('href'); } }); const title = getInnerHtml(book, 'title'); const description = getInnerHtml(book, 'summary'); const id = getInnerHtml(book, 'id'); const language = getInnerHtml(book, 'language'); const tags = getInnerHtml(book, 'tags'); let tagHtml = tags.split(';').filter(tag => {return !(tag.split(':')[0].startsWith('_'))}) .map((tag) => {return tag.charAt(0).toUpperCase() + tag.slice(1)}) .join(' | ').replace(/_/g, ' '); let downloadLink; let zimSize = 0; try { const downloadBookLink = book.querySelector('link[type="application/x-zim"]') zimSize = parseInt(downloadBookLink.getAttribute('length')); downloadLink = downloadBookLink.getAttribute('href').split('.meta4')[0]; } catch { downloadLink = ''; } const humanFriendlyZimSize = humanFriendlySize(zimSize); const divTag = document.createElement('div'); divTag.setAttribute('class', 'book'); divTag.setAttribute('data-id', id); if (sort) { divTag.setAttribute('data-idx', bookOrderMap.get(id)); } const faviconAttr = iconUrl != undefined ? `style="background-image: url('${root}${iconUrl}')"` : ''; const languageAttr = language != '' ? '' : 'style="background-color: transparent"'; divTag.innerHTML = `
${title}
${downloadLink ? `
Download ${humanFriendlyZimSize ? ` - ${humanFriendlyZimSize}
`: ''}` : ''}
${description}
${language.substr(0, 2).toUpperCase()}
${tagHtml}
`; return divTag; } function toggleFooter(show=false) { if (show) { footer.style.display = 'block'; } else { footer.style.display = 'none'; fadeOutDiv.style.display = 'block'; } } function insertModal(button) { const downloadLink = button.getAttribute('data-link'); button.addEventListener('click', (event) => { event.preventDefault(); document.body.insertAdjacentHTML('beforeend', ``); }) } async function getBookCount(query) { const url = `${root}/catalog/search?${query}`; return await fetch(url).then(async (resp) => { const data = new window.DOMParser().parseFromString(await resp.text(), 'application/xml'); return parseInt(data.querySelector('totalResults').innerHTML); }); } async function loadBooks() { loader.style.display = 'block'; return await fetch(queryUrlBuilder()).then(async (resp) => { const data = new window.DOMParser().parseFromString(await resp.text(), 'application/xml'); const books = data.querySelectorAll('entry'); books.forEach((book, idx) => { bookOrderMap.set(getInnerHtml(book, 'id'), idx); }); incrementalLoadingParams.start += books.length; const results = parseInt(data.querySelector('totalResults').innerHTML) if (results === bookOrderMap.size) { incrementalLoadingParams.count = 0; toggleFooter(true); } else { toggleFooter(); } const kiwixResultText = document.querySelector('.kiwixHomeBody__results') if (results) { let resultText = `${results} books`; if (results === 1) { resultText = `${results} book`; } kiwixResultText.innerHTML = resultText; } else { kiwixResultText.innerHTML = ``; } loader.style.display = 'none'; return books; }); } async function loadAndDisplayOptions(nodeQuery, query, valueEntryNode) { await fetch(query).then(async (resp) => { const data = new window.DOMParser().parseFromString(await resp.text(), 'application/xml'); let optionStr = ''; data.querySelectorAll('entry').forEach(entry => { const title = getInnerHtml(entry, 'title'); const value = getInnerHtml(entry, valueEntryNode); optionStr += ``; }); document.querySelector(nodeQuery).innerHTML += optionStr; }); } function checkAndInjectEmptyMessage() { const kiwixHomeBody = document.querySelector('.kiwixHomeBody'); if (!bookOrderMap.size) { if (!noResultInjected) { noResultInjected = true; iso.remove(document.getElementsByClassName('book__list')[0].getElementsByTagName('div')); iso.layout(); setTimeout(() => { const divTag = document.createElement('div'); divTag.setAttribute('class', 'noResults'); divTag.innerHTML = `No result. Would you like to reset filter?`; 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; } else if (noResultInjected) { noResultInjected = false; document.getElementsByClassName('noResults')[0].remove(); kiwixHomeBody.removeAttribute('style'); } loader.removeAttribute('style'); 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 = bookOrderMap.has(id); if (retVal) { booksToFilter.add(id); if (sort) { elem.setAttribute('data-idx', bookOrderMap.get(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)) const downloadButton = document.querySelector(`[data-id="${getInnerHtml(book, 'id')}"] .book__download`); if (downloadButton) { insertModal(downloadButton); } }); } async function resetAndFilter(filterType = '', filterValue = '') { isFetching = false; incrementalLoadingParams.start = 0; incrementalLoadingParams.count = viewPortToCount(); fadeOutDiv.style.display = 'none'; bookOrderMap.clear(); params = new URLSearchParams(window.location.search); if (filterType) { params.set(filterType, filterValue); window.history.pushState({}, null, `${window.location.href.split('?')[0]}?${params.toString()}`); setCookie(filterCookieName, params.toString()); } document.querySelectorAll('.filter').forEach(filter => { if (filter.value) { filter.style = 'background-color: #858585; color: #fff'; } else { filter.style = 'background-color: #ffffff; color: black'; } }); await loadAndDisplayBooks(true); } window.addEventListener('popstate', async () => { await resetAndFilter(); document.querySelectorAll('.filter').forEach(filter => {filter.value = params.get(filter.name) || ''}); }); async function loadSubset() { if (window.innerHeight + window.scrollY >= document.body.offsetHeight) { if (incrementalLoadingParams.count) { loadAndDisplayBooks(); } else { fadeOutDiv.style.display = 'none'; } } } 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 () => { iso = new Isotope( '.book__list', { itemSelector: '.book', getSortData:{ weight: function( itemElem ) { const index = itemElem.getAttribute('data-idx'); return index ? parseInt(index) : Infinity; } }, sortBy: 'weight', layoutMode: 'cellsByRow', cellsByRow: { columnWidth: '.book', rowHeight: '.book' } }); footer = document.getElementById('kiwixfooter'); fadeOutDiv = document.getElementById('fadeOut'); loader = document.querySelector('.loader'); await loadAndDisplayBooks(); await loadAndDisplayOptions('#languageFilter', `${root}/catalog/v2/languages`, 'language'); await loadAndDisplayOptions('#categoryFilter', `${root}/catalog/v2/categories`, 'title'); document.querySelectorAll('.filter').forEach(filter => { filter.addEventListener('change', () => {resetAndFilter(filter.name, filter.value)}); }); if (filters) { window.history.pushState({}, null, `${window.location.href.split('?')[0]}?${params.toString()}`); } params.forEach((value, key) => { const selectBox = document.getElementsByName(key)[0]; if (selectBox) { selectBox.value = value } }); document.getElementById('kiwixSearchForm').onsubmit = (event) => {event.preventDefault()}; if (!window.location.search) { const browserLang = navigator.language.split('-')[0]; const langFilter = document.getElementById('languageFilter'); const lang = browserLang.length === 3 ? browserLang : iso6391To3[browserLang]; if (await getBookCount(`lang=${lang}`)) { langFilter.value = lang; 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()); } })();