const kProgressiveAttr = "data-src"; let categoriesLoaded = false; window.quartoListingCategory = (category) => { if (categoriesLoaded) { activateCategory(category); setCategoryHash(category); } }; window["quarto-listing-loaded"] = () => { // Process any existing hash const hash = getHash(); if (hash) { // If there is a category, switch to that if (hash.category) { activateCategory(hash.category); } // Paginate a specific listing const listingIds = Object.keys(window["quarto-listings"]); for (const listingId of listingIds) { const page = hash[getListingPageKey(listingId)]; if (page) { showPage(listingId, page); } } } const listingIds = Object.keys(window["quarto-listings"]); for (const listingId of listingIds) { // The actual list const list = window["quarto-listings"][listingId]; // Update the handlers for pagination events refreshPaginationHandlers(listingId); // Render any visible items that need it renderVisibleProgressiveImages(list); // Whenever the list is updated, we also need to // attach handlers to the new pagination elements // and refresh any newly visible items. list.on("updated", function () { renderVisibleProgressiveImages(list); setTimeout(() => refreshPaginationHandlers(listingId)); // Show or hide the no matching message toggleNoMatchingMessage(list); }); } }; window.document.addEventListener("DOMContentLoaded", function (_event) { // Attach click handlers to categories const categoryEls = window.document.querySelectorAll( ".quarto-listing-category .category" ); for (const categoryEl of categoryEls) { const category = categoryEl.getAttribute("data-category"); categoryEl.onclick = () => { activateCategory(category); setCategoryHash(category); }; } // Attach a click handler to the category title // (there should be only one, but since it is a class name, handle N) const categoryTitleEls = window.document.querySelectorAll( ".quarto-listing-category-title" ); for (const categoryTitleEl of categoryTitleEls) { categoryTitleEl.onclick = () => { activateCategory(""); setCategoryHash(""); }; } categoriesLoaded = true; }); function toggleNoMatchingMessage(list) { const selector = `#${list.listContainer.id} .listing-no-matching`; const noMatchingEl = window.document.querySelector(selector); if (noMatchingEl) { if (list.visibleItems.length === 0) { noMatchingEl.classList.remove("d-none"); } else { if (!noMatchingEl.classList.contains("d-none")) { noMatchingEl.classList.add("d-none"); } } } } function setCategoryHash(category) { setHash({ category }); } function setPageHash(listingId, page) { const currentHash = getHash() || {}; currentHash[getListingPageKey(listingId)] = page; setHash(currentHash); } function getListingPageKey(listingId) { return `${listingId}-page`; } function refreshPaginationHandlers(listingId) { const listingEl = window.document.getElementById(listingId); const paginationEls = listingEl.querySelectorAll( ".pagination li.page-item:not(.disabled) .page.page-link" ); for (const paginationEl of paginationEls) { paginationEl.onclick = (sender) => { setPageHash(listingId, sender.target.getAttribute("data-i")); showPage(listingId, sender.target.getAttribute("data-i")); return false; }; } } function renderVisibleProgressiveImages(list) { // Run through the visible items and render any progressive images for (const item of list.visibleItems) { const itemEl = item.elm; if (itemEl) { const progressiveImgs = itemEl.querySelectorAll( `img[${kProgressiveAttr}]` ); for (const progressiveImg of progressiveImgs) { const srcValue = progressiveImg.getAttribute(kProgressiveAttr); if (srcValue) { progressiveImg.setAttribute("src", srcValue); } progressiveImg.removeAttribute(kProgressiveAttr); } } } } function getHash() { // Hashes are of the form // #name:value|name1:value1|name2:value2 const currentUrl = new URL(window.location); const hashRaw = currentUrl.hash ? currentUrl.hash.slice(1) : undefined; return parseHash(hashRaw); } const kAnd = "&"; const kEquals = "="; function parseHash(hash) { if (!hash) { return undefined; } const hasValuesStrs = hash.split(kAnd); const hashValues = hasValuesStrs .map((hashValueStr) => { const vals = hashValueStr.split(kEquals); if (vals.length === 2) { return { name: vals[0], value: vals[1] }; } else { return undefined; } }) .filter((value) => { return value !== undefined; }); const hashObj = {}; hashValues.forEach((hashValue) => { hashObj[hashValue.name] = decodeURIComponent(hashValue.value); }); return hashObj; } function makeHash(obj) { return Object.keys(obj) .map((key) => { return `${key}${kEquals}${obj[key]}`; }) .join(kAnd); } function setHash(obj) { const hash = makeHash(obj); window.history.pushState(null, null, `#${hash}`); } function showPage(listingId, page) { const list = window["quarto-listings"][listingId]; if (list) { list.show((page - 1) * list.page + 1, list.page); } } function activateCategory(category) { // Deactivate existing categories const activeEls = window.document.querySelectorAll( ".quarto-listing-category .category.active" ); for (const activeEl of activeEls) { activeEl.classList.remove("active"); } // Activate this category const categoryEl = window.document.querySelector( `.quarto-listing-category .category[data-category='${category}'` ); if (categoryEl) { categoryEl.classList.add("active"); } // Filter the listings to this category filterListingCategory(category); } function filterListingCategory(category) { const listingIds = Object.keys(window["quarto-listings"]); for (const listingId of listingIds) { const list = window["quarto-listings"][listingId]; if (list) { if (category === "") { // resets the filter list.filter(); } else { // filter to this category list.filter(function (item) { const itemValues = item.values(); if (itemValues.categories !== null) { const categories = itemValues.categories.split(","); return categories.includes(category); } else { return false; } }); } } } }