you WOULD type a script
This commit is contained in:
parent
8cb6752168
commit
7a08c05cea
11 changed files with 385 additions and 359 deletions
220
script/gallery.ts
Normal file
220
script/gallery.ts
Normal file
|
@ -0,0 +1,220 @@
|
|||
type Boxes = Set<HTMLInputElement>;
|
||||
|
||||
let reqBoxes: Boxes;
|
||||
let excBoxes: Boxes;
|
||||
let allBoxes: Boxes;
|
||||
let tags: Map<HTMLElement, string[]>;
|
||||
let itemsByYear: Map<string, Set<HTMLElement>>;
|
||||
|
||||
let showSingles = false;
|
||||
|
||||
|
||||
function fillSets() {
|
||||
function checkedValues(boxes: Boxes) {
|
||||
return new Set(Array.from(boxes).filter(b => b.checked).map(b => b.value));
|
||||
}
|
||||
|
||||
return [checkedValues(reqBoxes), checkedValues(excBoxes)];
|
||||
}
|
||||
|
||||
function updateItems() {
|
||||
let [reqTags, excTags] = fillSets();
|
||||
let anyReq = reqTags.size > 0;
|
||||
|
||||
for (let [year, items] of itemsByYear) {
|
||||
let hide = true;
|
||||
|
||||
for (let item of items) {
|
||||
let req = tags.get(item)?.some(x => reqTags.has(x)) ?? false;
|
||||
let exc = tags.get(item)?.some(x => excTags.has(x)) ?? false;
|
||||
let hidden = exc || (anyReq && !req);
|
||||
|
||||
item.hidden = hidden;
|
||||
hide &&= hidden;
|
||||
}
|
||||
|
||||
let marker = document.getElementById(`marker-${year}`);
|
||||
if (marker !== null) marker.hidden = hide;
|
||||
}
|
||||
|
||||
function disp(pfx: string, tags: string[]) {
|
||||
return Array(...tags).map(x => pfx + x).join('\u2003'); // em space
|
||||
}
|
||||
let plus = disp('+\u2009', Array.from(reqTags)); // thin space
|
||||
let minus = disp('-\u2009', Array.from(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) {
|
||||
let 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;
|
||||
|
||||
let elems = Array.from(document.querySelectorAll('.filterlist li'));
|
||||
for (let li of elems) {
|
||||
let countStr = li.querySelector('label')?.dataset.count;
|
||||
let count = countStr ? +countStr : 0;
|
||||
if (count <= 1 && li instanceof HTMLElement) {
|
||||
li.hidden = !showSingles;
|
||||
}
|
||||
}
|
||||
|
||||
if (e) e.preventDefault();
|
||||
}
|
||||
|
||||
|
||||
function makeFragment() {
|
||||
let allBoxesArr = Array.from(allBoxes);
|
||||
let 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(';');
|
||||
}
|
||||
}
|
||||
|
||||
function useFragment() {
|
||||
let frag = decodeURIComponent(location.hash).replace(/^#/, '');
|
||||
let details = document.getElementById('filters-details') as HTMLDetailsElement;
|
||||
|
||||
if (!frag) {
|
||||
clearForm();
|
||||
} else if (frag == 'all') {
|
||||
allBoxes.forEach(b => b.checked = false);
|
||||
details.open = false;
|
||||
} else {
|
||||
let set = new Set(frag.split(';'));
|
||||
let re = /^(require|exclude)_|hide_filters/;
|
||||
if (Array.from(set).every(x => re.test(x))) {
|
||||
allBoxes.forEach(b => b.checked = set.has(b.id));
|
||||
details.open = !frag.match(/hide_filters|example\b/);
|
||||
}
|
||||
}
|
||||
|
||||
updateItems();
|
||||
}
|
||||
|
||||
|
||||
function sortFilters(cmp: (a: Node, b: Node) => number) {
|
||||
function sort1(id: string) {
|
||||
let elt = document.getElementById(id);
|
||||
if (elt === null) return;
|
||||
|
||||
let children = Array.from(elt.childNodes);
|
||||
children.sort(cmp);
|
||||
for (let c of children) {
|
||||
elt.removeChild(c);
|
||||
elt.appendChild(c);
|
||||
}
|
||||
}
|
||||
|
||||
sort1('require');
|
||||
sort1('exclude');
|
||||
}
|
||||
|
||||
function sortFiltersAlpha(e: Event) {
|
||||
function getName(node: Node) {
|
||||
if (node.nodeType == Node.ELEMENT_NODE) {
|
||||
let elt = node as Element;
|
||||
return elt.getElementsByTagName('input')[0].value;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
sortFilters((a, b) => getName(a).localeCompare(getName(b)));
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
function sortFiltersUses(e: Event) {
|
||||
function getUses(node: Node) {
|
||||
if (node.nodeType == Node.ELEMENT_NODE) {
|
||||
let elt = node as Element;
|
||||
let countStr = elt.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) {
|
||||
let iter = document.getElementById(id)!.getElementsByTagName('input');
|
||||
return new Set(Array.from(iter));
|
||||
}
|
||||
|
||||
let items = Array.from(document.getElementsByClassName('post')) as HTMLElement[];
|
||||
|
||||
itemsByYear = new Map;
|
||||
for (let item of items) {
|
||||
let 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 {};
|
Loading…
Add table
Add a link
Reference in a new issue