You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					244 lines
				
				6.5 KiB
			
		
		
			
		
	
	
					244 lines
				
				6.5 KiB
			| 
											3 years ago
										 | 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; | ||
|  |           } | ||
|  |         }); | ||
|  |       } | ||
|  |     } | ||
|  |   } | ||
|  | } |