mirror of
https://github.com/kiwix/libkiwix.git
synced 2025-08-03 02:06:05 -04:00

The language selector on the welcome page has been replaced with a smaller button that opens a modal language selector. Though the code for introducing such a modal language selector has been added in i18n.js, its appearance relies on styles defined in index.css. Once this new UI for changing the UI language is approved, it must be used in the ZIM viewer too. Known issues: - selecting the language with arrow keys (using the keyboard only, without pressing space first, so that the full list of languages is shown) doesn't work because as soon as the current language is changed the modal language selector disappears.
544 lines
23 KiB
JavaScript
544 lines
23 KiB
JavaScript
(function() {
|
|
const root = document.querySelector(`link[type='root']`).getAttribute('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 || '');
|
|
params.delete('userlang');
|
|
let timer;
|
|
let languages = {};
|
|
|
|
function updateFeedLink() {
|
|
const inputParams = new URLSearchParams(window.location.search);
|
|
const filteredParams = new URLSearchParams();
|
|
for (const [key, value] of inputParams) {
|
|
if ( value != '' ) {
|
|
filteredParams.append(key, value);
|
|
}
|
|
}
|
|
const feedLink = `${root}/catalog/v2/entries?${filteredParams.toString()}`;
|
|
document.querySelector('#headFeedLink').href = feedLink;
|
|
document.querySelector('#feedLink').href = feedLink;
|
|
}
|
|
|
|
function changeUILanguage() {
|
|
window.modalUILanguageSelector.close();
|
|
const s = document.getElementById("ui_language");
|
|
const lang = s.options[s.selectedIndex].value;
|
|
setPermanentGlobalCookie('userlang', lang);
|
|
window.location.reload();
|
|
}
|
|
|
|
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, ttl) {
|
|
let exp = "";
|
|
if ( ttl ) {
|
|
const date = new Date();
|
|
date.setTime(date.getTime() + ttl);
|
|
exp = `expires=${date.toUTCString()};`;
|
|
}
|
|
document.cookie = `${cookieName}=${cookieValue};${exp}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) => {
|
|
if (typeof title === 'string' && title.length > 0) {
|
|
title = title.replace(/_/g, ' ');
|
|
if (title.length > 0) {
|
|
return htmlEncode(title[0].toUpperCase() + title.slice(1));
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
|
|
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 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) {
|
|
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 langCode = getInnerHtml(book, 'language');
|
|
const language = languages[langCode];
|
|
const tags = getInnerHtml(book, 'tags');
|
|
const tagList = tags.split(';').filter(tag => {return !(tag.startsWith('_'))});
|
|
const tagFilterLinks = tagList.map((tagValue) => generateTagLink(tagValue));
|
|
const tagHtml = tagFilterLinks.join(' | ');
|
|
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 bookName = link.split('/').pop();
|
|
const viewerLink = `${root}/viewer#${bookName}`;
|
|
|
|
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('${iconUrl}')"` : '';
|
|
const languageAttr = langCode != '' ? `title="${language}" aria-label="${language}"` : 'style="background-color: transparent"';
|
|
divTag.innerHTML = `
|
|
<div class="book__wrapper">
|
|
<a class="book__link" href="${viewerLink}" data-hover="Preview">
|
|
<div class="book__link__wrapper">
|
|
<div class="book__icon" ${faviconAttr}></div>
|
|
<div class="book__header">
|
|
<div id="book__title">${title}</div>
|
|
${downloadLink ? `<div class="book__download"><span data-link="${downloadLink}">${$t("download")} ${humanFriendlyZimSize ? ` - ${humanFriendlyZimSize}</span></div>`: ''}` : ''}
|
|
</div>
|
|
<div class="book__description" title="${description}">${description}</div>
|
|
</div>
|
|
</a>
|
|
<div class="book__languageTag" ${languageAttr}>${getLanguageCodeToDisplay(langCode)}</div>
|
|
<div class="book__tags"><div class="book__tags--wrapper">${tagHtml}</div></div>
|
|
</div></div>`;
|
|
return divTag;
|
|
}
|
|
|
|
function getLanguageCodeToDisplay(langCode3Letter) {
|
|
const langCode2Letter = (Object.keys(iso6391To3).find(key => iso6391To3[key] === langCode3Letter));
|
|
const res = (langCode2Letter != undefined) ? langCode2Letter : langCode3Letter;
|
|
return res.toUpperCase();
|
|
}
|
|
|
|
function toggleFooter(show=false) {
|
|
if (show) {
|
|
footer.style.display = 'block';
|
|
} else {
|
|
footer.style.display = 'none';
|
|
fadeOutDiv.style.display = 'block';
|
|
}
|
|
}
|
|
|
|
async function getMagnetLink(downloadLink) {
|
|
const magnetUrl = downloadLink + '.magnet';
|
|
const controller = new AbortController();
|
|
setTimeout(() => controller.abort(), 5000);
|
|
const magnetLink = await fetch(magnetUrl, { signal: controller.signal }).then(response => {
|
|
return response.ok ? response.text() : '';
|
|
}).catch((_error) => '');
|
|
return magnetLink;
|
|
}
|
|
|
|
function insertModal(button) {
|
|
const downloadLink = button.getAttribute('data-link');
|
|
button.addEventListener('click', async (event) => {
|
|
event.preventDefault();
|
|
const magnetLink = await getMagnetLink(downloadLink);
|
|
document.body.insertAdjacentHTML('beforeend', `<div class="modal-wrapper">
|
|
<div class="modal">
|
|
<div class="modal-heading">
|
|
<div class="modal-title">
|
|
<div>
|
|
Download
|
|
</div>
|
|
</div>
|
|
<div onclick="closeModal()" class="modal-close-button">
|
|
<div>
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" fill="none">
|
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.7071 1.70711C14.0976 1.31658 14.0976
|
|
0.683417 13.7071 0.292893C13.3166 -0.0976311 12.6834 -0.0976311 12.2929 0.292893L7 5.58579L1.70711
|
|
0.292893C1.31658 -0.0976311 0.683417 -0.0976311 0.292893 0.292893C-0.0976311 0.683417
|
|
-0.0976311 1.31658 0.292893 1.70711L5.58579 7L0.292893 12.2929C-0.0976311 12.6834
|
|
-0.0976311 13.3166 0.292893 13.7071C0.683417 14.0976 1.31658 14.0976 1.70711 13.7071L7
|
|
8.41421L12.2929 13.7071C12.6834 14.0976 13.3166 14.0976 13.7071 13.7071C14.0976 13.3166
|
|
14.0976 12.6834 13.7071 12.2929L8.41421 7L13.7071 1.70711Z" fill="black" />
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-content">
|
|
<div class="modal-regular-download">
|
|
<a href="${downloadLink}" download>
|
|
<img src="${root}/skin/download.png?KIWIXCACHEID" alt="${$t("direct-download-alt-text")}" />
|
|
<div>${$t("direct-download-link-text")}</div>
|
|
</a>
|
|
</div>
|
|
<div class="modal-regular-download">
|
|
<a href="${downloadLink}.sha256" download>
|
|
<img src="${root}/skin/hash.png?KIWIXCACHEID" alt="${$t("hash-download-alt-text")}" />
|
|
<div>${$t("hash-download-link-text")}</div>
|
|
</a>
|
|
</div>
|
|
${magnetLink ?
|
|
`<div class="modal-regular-download">
|
|
<a href="${magnetLink}" target="_blank">
|
|
<img src="${root}/skin/magnet.png?KIWIXCACHEID" alt="${$t("magnet-alt-text")}" />
|
|
<div>${$t("magnet-link-text")}</div>
|
|
</a>
|
|
</div>` : ``}
|
|
<div class="modal-regular-download">
|
|
<a href="${downloadLink}.torrent" download>
|
|
<img src="${root}/skin/bittorrent.png?KIWIXCACHEID" alt="${$t("torrent-download-alt-text")}" />
|
|
<div>${$t("torrent-download-link-text")}</div>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>`);
|
|
})
|
|
}
|
|
|
|
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 text = results
|
|
? $t("count-of-matching-books", {COUNT: results})
|
|
: '';
|
|
document.querySelector('.kiwixHomeBody__results').innerHTML = text;
|
|
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);
|
|
const hfTitle = humanFriendlyTitle(title);
|
|
if (valueEntryNode == 'language') {
|
|
languages[value] = hfTitle;
|
|
}
|
|
optionStr += (hfTitle != '') ? `<option value="${value}">${hfTitle}</option>` : '';
|
|
});
|
|
document.querySelector(nodeQuery).innerHTML += optionStr;
|
|
});
|
|
}
|
|
|
|
function setNoResultsContent() {
|
|
const kiwixHomeBody = document.querySelector('.kiwixHomeBody');
|
|
const divTag = document.createElement('div');
|
|
divTag.setAttribute('class', 'noResults');
|
|
divTag.innerHTML = $t("welcome-page-overzealous-filter");
|
|
kiwixHomeBody.append(divTag);
|
|
kiwixHomeBody.setAttribute('style', 'display: flex; justify-content: center; align-items: center');
|
|
loader.setAttribute('style', 'position: absolute; top: 50%');
|
|
}
|
|
|
|
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(setNoResultsContent, 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 (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 span`);
|
|
if (downloadButton) {
|
|
insertModal(downloadButton);
|
|
}
|
|
});
|
|
refreshTagLinks();
|
|
}
|
|
|
|
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, `?${params.toString()}`);
|
|
setCookie(filterCookieName, params.toString(), oneDayDelta);
|
|
}
|
|
updateFilterColors();
|
|
updateFeedLink();
|
|
await loadAndDisplayBooks(true);
|
|
}
|
|
|
|
window.addEventListener('popstate', async () => {
|
|
await resetAndFilter();
|
|
updateVisibleParams();
|
|
});
|
|
|
|
async function loadSubset() {
|
|
if (window.innerHeight + window.scrollY >= (document.body.offsetHeight * 0.98)) {
|
|
if (incrementalLoadingParams.count) {
|
|
loadAndDisplayBooks();
|
|
}
|
|
else {
|
|
fadeOutDiv.style.display = 'none';
|
|
}
|
|
}
|
|
}
|
|
|
|
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) => {
|
|
if (timer) {clearTimeout(timer)}
|
|
timer = setTimeout(() => {
|
|
incrementalLoadingParams.count = incrementalLoadingParams.count && viewPortToCount();
|
|
loadSubset();
|
|
}, 100, event);
|
|
});
|
|
|
|
window.addEventListener('scroll', loadSubset);
|
|
|
|
window.addEventListener('keydown', function (event) {
|
|
if (event.key === "Escape" ) {
|
|
closeModal();
|
|
}
|
|
});
|
|
|
|
function updateUIText() {
|
|
footer.innerHTML = $t("powered-by-kiwix-html");
|
|
const searchText = $t("search");
|
|
document.getElementById('searchFilter').placeholder = searchText;
|
|
document.getElementById('searchButton').value = searchText;
|
|
document.getElementById('categoryFilter').children[0].innerHTML = $t("book-filtering-all-categories");
|
|
document.getElementById('languageFilter').children[0].innerHTML = $t("book-filtering-all-languages");
|
|
const feedLogoElem = document.getElementById('feedLogo');
|
|
const libraryOpdsFeedHint = $t("library-opds-feed");
|
|
for (const attr of ["alt", "aria-label", "title"] ) {
|
|
feedLogoElem.setAttribute(attr, libraryOpdsFeedHint);
|
|
}
|
|
}
|
|
|
|
async function onload() {
|
|
initUILanguageSelector(getUserLanguage(), changeUILanguage);
|
|
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: 'masonry',
|
|
masonry: {
|
|
fitWidth: true
|
|
}
|
|
});
|
|
footer = document.getElementById('kiwixfooter');
|
|
updateUIText();
|
|
fadeOutDiv = document.getElementById('fadeOut');
|
|
loader = document.querySelector('.loader');
|
|
await loadAndDisplayOptions('#languageFilter', `${root}/catalog/v2/languages`, 'language');
|
|
await loadAndDisplayOptions('#categoryFilter', `${root}/catalog/v2/categories`, 'title');
|
|
await loadAndDisplayBooks();
|
|
document.querySelectorAll('.filter').forEach(filter => {
|
|
filter.addEventListener('change', () => {resetAndFilter(filter.name, filter.value)});
|
|
});
|
|
const tagElement = document.getElementsByClassName('tagFilterLabel')[0];
|
|
tagElement.addEventListener('click', () => removeTagElement(true));
|
|
if (filters) {
|
|
const currentLink = window.location.search;
|
|
const newLink = `?${params.toString()}`;
|
|
if (currentLink != newLink) {
|
|
window.history.pushState({}, null, newLink);
|
|
}
|
|
}
|
|
updateVisibleParams();
|
|
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'));
|
|
}
|
|
}
|
|
updateFeedLink();
|
|
setCookie(filterCookieName, params.toString(), oneDayDelta);
|
|
};
|
|
|
|
// required by i18n.js:setUserLanguage()
|
|
window.setPermanentGlobalCookie = function(name, value) {
|
|
document.cookie = `${name}=${value};path=${root};max-age=31536000`;
|
|
}
|
|
|
|
window.onload = () => { setUserLanguage(getUserLanguage(), onload); }
|
|
})();
|
|
|