type Boxes = Set; let reqBoxes: Boxes; let excBoxes: Boxes; let allBoxes: Boxes; let tags: Map; let itemsByYear: Map>; let showSingles = false; function fillSets(): [Set, Set] { function checkedValues(boxes: Boxes) { return new Set([...boxes].filter(b => b.checked).map(b => b.value)); } return [checkedValues(reqBoxes), checkedValues(excBoxes)]; } function updateItems() { const [reqTags, excTags] = fillSets(); const anyReq = reqTags.size > 0; for (const [year, items] of itemsByYear) { let hideMarker = true; for (const item of items) { const req = tags.get(item)?.some(x => reqTags.has(x)) ?? false; const exc = tags.get(item)?.some(x => excTags.has(x)) ?? false; const hidden = exc || (anyReq && !req); item.hidden = hidden; hideMarker &&= hidden; } const marker = document.getElementById(`marker-${year}`); if (marker !== null) marker.hidden = hideMarker; } function disp(pfx: string, tags: Iterable) { return [...tags].map(x => pfx + x).join('\u2003'); // em space } const plus = disp('+\u2009', reqTags); // thin space const minus = disp('-\u2009', excTags); document.getElementById('filters-details')!.dataset.filters = `${plus}\u2003${minus}`.trim(); } function update() { updateItems(); history.pushState(null, "", makeFragment()); } function converseId(id: string) { if (id.match(/^require/)) { return id.replace('require', 'exclude'); } else { return id.replace('exclude', 'require'); } } function toggle(checkbox: HTMLInputElement) { if (checkbox.checked) { const converse = document.getElementById(converseId(checkbox.id)) as HTMLInputElement; converse.checked = false; } update(); } function clearForm() { allBoxes.forEach(b => b.checked = b.defaultChecked); } function clear(e: Event) { clearForm(); update(); e.preventDefault(); } function toggleSingles(e: Event) { showSingles = !showSingles; const elems = Array.from(document.querySelectorAll('.filterlist li')) as HTMLElement[]; for (const li of elems) { const countStr = li.querySelector('label')?.dataset.count; const count = countStr ? +countStr : 0; if (count <= 1 && li instanceof HTMLElement) { li.hidden = !showSingles; } } e.preventDefault(); } function makeFragment() { const allBoxesArr = Array.from(allBoxes); const ids = allBoxesArr.filter(b => b.checked).map(b => b.id); if (ids.length == 0) { return '#all'; } else if (allBoxesArr.every(b => b.checked == b.defaultChecked)) { return '#'; } else { return '#' + ids.join(';'); } } type Shortcuts = { [short: string]: Set }; const shortcuts: Shortcuts = { summary: new Set(['require_artsummary']), colourexamples: new Set(['require_colourexample']), flatexamples: new Set(['require_flatexample']), sketchexamples: new Set(['require_sketchexample']), iconexamples: new Set(['require_iconexample']), curated: new Set(['require_curated']), gecs: new Set(['require_niss', 'require_nisse']), qt: new Set(['require_q_t_']), kesi: new Set(['require_kesi']), }; function useFragment() { const frag = decodeURIComponent(location.hash).replace(/^#/, ''); const details = document.getElementById('filters-details') as HTMLDetailsElement; const fromShortcut = shortcuts[frag]; if (!frag) { clearForm(); } else if (frag == 'all') { allBoxes.forEach(b => b.checked = false); details.open = false; } else if (fromShortcut) { allBoxes.forEach(b => b.checked = fromShortcut.has(b.id)); } else { const pieces = frag.split(';'); const set = new Set(pieces); const re = /^(require|exclude)_|hide_filters/; if (pieces.every(x => re.test(x))) { allBoxes.forEach(b => b.checked = set.has(b.id)); } } updateItems(); } function sortFilters(cmp: (a: Node, b: Node) => number) { function sort1(id: string) { const elt = document.getElementById(id); if (elt === null) return; const children = Array.from(elt.childNodes); children.sort(cmp); for (const c of children) { elt.removeChild(c); elt.appendChild(c); } } sort1('require'); sort1('exclude'); } function sortFiltersAlpha(e: Event) { function getName(node: Node): string { if (node instanceof Element) { return node.getElementsByTagName('input')[0]?.value ?? ''; } else { return ''; } } sortFilters((a, b) => getName(a).localeCompare(getName(b))); e.preventDefault(); } function sortFiltersUses(e: Event) { function getUses(node: Node): number { if (node instanceof Element) { const countStr = node.getElementsByTagName('label')[0]?.dataset.count; return countStr ? +countStr : 0; } else { return 0; } } sortFilters((a, b) => getUses(b) - getUses(a)); e.preventDefault(); } function setup() { function inputs(id: string): Boxes { const iter = document.getElementById(id)!.getElementsByTagName('input'); return new Set(Array.from(iter)); } const items = Array.from(document.getElementsByClassName('post')) as HTMLElement[]; itemsByYear = new Map; for (const item of items) { const year = item.dataset.year; if (year !== undefined) { if (!itemsByYear.has(year)) { itemsByYear.set(year, new Set([item])); } else { itemsByYear.get(year)?.add(item); } } } reqBoxes = inputs('require'); excBoxes = inputs('exclude'); allBoxes = new Set([...reqBoxes, ...excBoxes]); tags = new Map(items.map(item => [item, item.dataset.tags?.split(';') ?? []])); allBoxes.forEach(b => b.addEventListener('change', () => toggle(b))); function addClick(id: string, f: (e: Event) => void) { document.getElementById(id)!.addEventListener('click', f); } addClick('clear', clear); addClick('sortalpha', sortFiltersAlpha); addClick('sortuses', sortFiltersUses); addClick('singles', toggleSingles); window.addEventListener('popstate', useFragment); useFragment(); } window.addEventListener('DOMContentLoaded', setup); export {};