rhiannon morris
7d20b2a761
- inside the list, give selected filters a background - when the list is collapsed, display the current selection next to it
204 lines
4.6 KiB
JavaScript
204 lines
4.6 KiB
JavaScript
(function() {
|
|
'use strict';
|
|
|
|
let reqBoxes;
|
|
let excBoxes;
|
|
let allBoxes;
|
|
let tags;
|
|
let itemsByYear;
|
|
|
|
let showSingles = false;
|
|
|
|
|
|
function fillSets() {
|
|
let checkedValues = boxes =>
|
|
new Set(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));
|
|
let exc = tags.get(item).some(x => excTags.has(x));
|
|
let hidden = exc || (anyReq && !req);
|
|
|
|
item.hidden = hidden;
|
|
hide &&= hidden;
|
|
}
|
|
|
|
document.getElementById(`marker-${year}`).hidden = hide;
|
|
}
|
|
|
|
function disp(pfx, tags) {
|
|
return Array(...tags).map(x => pfx + x).join('\u2003'); // em space
|
|
}
|
|
let plus = disp('+\u2009', reqTags); // thin space
|
|
let minus = disp('-\u2009', excTags);
|
|
document.getElementById('filters-details').dataset.filters =
|
|
`${plus}\u2003${minus}`.trim();
|
|
}
|
|
|
|
function update() {
|
|
updateItems();
|
|
history.pushState(null, "", makeFragment());
|
|
}
|
|
|
|
function converseId(id) {
|
|
if (id.match(/^require/)) {
|
|
return id.replace('require', 'exclude');
|
|
} else {
|
|
return id.replace('exclude', 'require');
|
|
}
|
|
}
|
|
|
|
function toggle(checkbox) {
|
|
if (checkbox.checked)
|
|
document.getElementById(converseId(checkbox.id)).checked = false;
|
|
|
|
update();
|
|
}
|
|
|
|
|
|
function clearForm() {
|
|
allBoxes.forEach(b => b.checked = b.defaultChecked);
|
|
}
|
|
|
|
function clear(e) {
|
|
clearForm();
|
|
update();
|
|
if (e) e.preventDefault();
|
|
}
|
|
|
|
function toggleSingles(e) {
|
|
showSingles = !showSingles;
|
|
|
|
for (let li of document.querySelectorAll('.filterlist li')) {
|
|
let count = +li.querySelector('label').dataset.count;
|
|
if (count <= 1) {
|
|
li.hidden = !showSingles;
|
|
}
|
|
}
|
|
|
|
if (e) e.preventDefault();
|
|
}
|
|
|
|
|
|
function makeFragment() {
|
|
let ids = allBoxes.filter(b => b.checked).map(b => b.id);
|
|
if (ids.length == 0) {
|
|
return '#all';
|
|
} else if (allBoxes.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');
|
|
|
|
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) {
|
|
function sort1(id) {
|
|
let elt = document.getElementById(id);
|
|
let children = [...elt.childNodes];
|
|
children.sort(cmp);
|
|
for (let c of children) {
|
|
elt.removeChild(c);
|
|
elt.appendChild(c);
|
|
}
|
|
}
|
|
|
|
sort1('require');
|
|
sort1('exclude');
|
|
}
|
|
|
|
function sortFiltersAlpha(e) {
|
|
function getName(x) {
|
|
if (x.nodeType == Node.ELEMENT_NODE) {
|
|
return x.getElementsByTagName('input')[0].value;
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
sortFilters((a, b) => getName(a).localeCompare(getName(b)));
|
|
e.preventDefault();
|
|
}
|
|
|
|
function sortFiltersUses(e) {
|
|
function getUses(x) {
|
|
if (x.nodeType == Node.ELEMENT_NODE) {
|
|
return parseInt(x.getElementsByTagName('label')[0].dataset.count);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
sortFilters((a, b) => getUses(b) - getUses(a));
|
|
e.preventDefault();
|
|
}
|
|
|
|
|
|
function setup() {
|
|
function inputs(id) {
|
|
let iter = document.getElementById(id).getElementsByTagName('input');
|
|
return Array.from(iter);
|
|
}
|
|
|
|
let items = Array.from(document.getElementsByClassName('post'));
|
|
|
|
itemsByYear = new Map;
|
|
for (let item of items) {
|
|
let year = item.dataset.year;
|
|
if (!itemsByYear.has(year)) itemsByYear.set(year, new Set);
|
|
itemsByYear.get(year).add(item);
|
|
}
|
|
|
|
reqBoxes = inputs('require');
|
|
excBoxes = inputs('exclude');
|
|
allBoxes = [...reqBoxes, ...excBoxes];
|
|
|
|
tags = new Map(items.map(item => [item, item.dataset.tags.split(';')]));
|
|
|
|
|
|
allBoxes.forEach(b => b.addEventListener('change', () => toggle(b)));
|
|
|
|
function addClick(id, f) {
|
|
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);
|
|
})();
|