Path: blob/main/src/resources/projects/website/listing/quarto-listing.js
12923 views
const kProgressiveAttr = "data-src";1let categoriesLoaded = false;23window.quartoListingCategory = (category) => {4// category is URI encoded in EJS template for UTF-8 support5category = decodeURIComponent(atob(category));6if (categoriesLoaded) {7activateCategory(category);8setCategoryHash(category);9}10};1112window["quarto-listing-loaded"] = () => {13// Process any existing hash14const hash = getHash();1516if (hash) {17// If there is a category, switch to that18if (hash.category) {19// category hash are URI encoded so we need to decode it before processing20// so that we can match it with the category element processed in JS21activateCategory(decodeURIComponent(hash.category));22}23// Paginate a specific listing24const listingIds = Object.keys(window["quarto-listings"]);25for (const listingId of listingIds) {26const page = hash[getListingPageKey(listingId)];27if (page) {28showPage(listingId, page);29}30}31}3233const listingIds = Object.keys(window["quarto-listings"]);34for (const listingId of listingIds) {35// The actual list36const list = window["quarto-listings"][listingId];3738// Update the handlers for pagination events39refreshPaginationHandlers(listingId);4041// Render any visible items that need it42renderVisibleProgressiveImages(list);4344// Whenever the list is updated, we also need to45// attach handlers to the new pagination elements46// and refresh any newly visible items.47list.on("updated", function () {48renderVisibleProgressiveImages(list);49setTimeout(() => refreshPaginationHandlers(listingId));5051// Show or hide the no matching message52toggleNoMatchingMessage(list);53});54}55};5657window.document.addEventListener("DOMContentLoaded", function (_event) {58// Attach click handlers to categories59const categoryEls = window.document.querySelectorAll(60".quarto-listing-category .category"61);6263for (const categoryEl of categoryEls) {64// category needs to support non ASCII characters65const category = decodeURIComponent(66atob(categoryEl.getAttribute("data-category"))67);68categoryEl.onclick = () => {69activateCategory(category);70setCategoryHash(category);71};72}7374// Attach a click handler to the category title75// (there should be only one, but since it is a class name, handle N)76const categoryTitleEls = window.document.querySelectorAll(77".quarto-listing-category-title"78);79for (const categoryTitleEl of categoryTitleEls) {80categoryTitleEl.onclick = () => {81activateCategory("");82setCategoryHash("");83};84}8586categoriesLoaded = true;87});8889function toggleNoMatchingMessage(list) {90const selector = `#${list.listContainer.id} .listing-no-matching`;91const noMatchingEl = window.document.querySelector(selector);92if (noMatchingEl) {93if (list.visibleItems.length === 0) {94noMatchingEl.classList.remove("d-none");95} else {96if (!noMatchingEl.classList.contains("d-none")) {97noMatchingEl.classList.add("d-none");98}99}100}101}102103function setCategoryHash(category) {104setHash({ category });105}106107function setPageHash(listingId, page) {108const currentHash = getHash() || {};109currentHash[getListingPageKey(listingId)] = page;110setHash(currentHash);111}112113function getListingPageKey(listingId) {114return `${listingId}-page`;115}116117function refreshPaginationHandlers(listingId) {118const listingEl = window.document.getElementById(listingId);119const paginationEls = listingEl.querySelectorAll(120".pagination li.page-item:not(.disabled) .page.page-link"121);122for (const paginationEl of paginationEls) {123paginationEl.onclick = (sender) => {124setPageHash(listingId, sender.target.getAttribute("data-i"));125showPage(listingId, sender.target.getAttribute("data-i"));126return false;127};128}129}130131function renderVisibleProgressiveImages(list) {132// Run through the visible items and render any progressive images133for (const item of list.visibleItems) {134const itemEl = item.elm;135if (itemEl) {136const progressiveImgs = itemEl.querySelectorAll(137`img[${kProgressiveAttr}]`138);139for (const progressiveImg of progressiveImgs) {140const srcValue = progressiveImg.getAttribute(kProgressiveAttr);141if (srcValue) {142progressiveImg.setAttribute("src", srcValue);143}144progressiveImg.removeAttribute(kProgressiveAttr);145}146}147}148}149150function getHash() {151// Hashes are of the form152// #name:value|name1:value1|name2:value2153const currentUrl = new URL(window.location);154const hashRaw = currentUrl.hash ? currentUrl.hash.slice(1) : undefined;155return parseHash(hashRaw);156}157158const kAnd = "&";159const kEquals = "=";160161function parseHash(hash) {162if (!hash) {163return undefined;164}165const hasValuesStrs = hash.split(kAnd);166const hashValues = hasValuesStrs167.map((hashValueStr) => {168const vals = hashValueStr.split(kEquals);169if (vals.length === 2) {170return { name: vals[0], value: vals[1] };171} else {172return undefined;173}174})175.filter((value) => {176return value !== undefined;177});178179const hashObj = {};180hashValues.forEach((hashValue) => {181hashObj[hashValue.name] = decodeURIComponent(hashValue.value);182});183return hashObj;184}185186function makeHash(obj) {187return Object.keys(obj)188.map((key) => {189return `${key}${kEquals}${obj[key]}`;190})191.join(kAnd);192}193194function setHash(obj) {195const hash = makeHash(obj);196window.history.pushState(null, null, `#${hash}`);197}198199function showPage(listingId, page) {200const list = window["quarto-listings"][listingId];201if (list) {202list.show((page - 1) * list.page + 1, list.page);203}204}205206function activateCategory(category) {207// Deactivate existing categories208const activeEls = window.document.querySelectorAll(209".quarto-listing-category .category.active"210);211for (const activeEl of activeEls) {212activeEl.classList.remove("active");213}214215// Activate this category216const categoryEl = window.document.querySelector(217`.quarto-listing-category .category[data-category='${btoa(218encodeURIComponent(category)219)}']`220);221if (categoryEl) {222categoryEl.classList.add("active");223}224225// Filter the listings to this category226filterListingCategory(category);227}228229function filterListingCategory(category) {230const listingIds = Object.keys(window["quarto-listings"]);231for (const listingId of listingIds) {232const list = window["quarto-listings"][listingId];233if (list) {234if (category === "") {235// resets the filter236list.filter();237} else {238// filter to this category239list.filter(function (item) {240const itemValues = item.values();241if (itemValues.categories !== null) {242const categories = decodeURIComponent(243atob(itemValues.categories)244).split(",");245return categories.includes(category);246} else {247return false;248}249});250}251}252}253}254255256