blob: f13ac9059518ef822e99ee842eb5118e657e3fbb (
plain) (
tree)
|
|
import {getCookie} from "./cookies";
interface HTMLDetailsElement extends HTMLElement {
open: boolean
}
interface DetailsInfo {
element: HTMLDetailsElement
openByDefault: boolean
toggles: HTMLElement[]
}
// Global state
const detailsRegistry: { [id: string]: DetailsInfo } = {};
const toggled: { [id: string]: true } = {}; /* stores which <details> are not in their default state */
function lookupDetailsRegistry(id: string): DetailsInfo {
const info = detailsRegistry[id];
if (info == undefined) { throw new Error(`could not find <details> element with id '${id}'`); }
return info;
}
function onDetailsToggle(ev: Event) {
const element = ev.target as HTMLDetailsElement;
const id = element.id;
const info = lookupDetailsRegistry(id);
const isOpen = info.element.open;
for (const toggle of info.toggles) {
if (toggle.classList.contains('details-toggle-control')) {
toggle.classList.add(isOpen ? 'collapser' : 'expander');
toggle.classList.remove(isOpen ? 'expander' : 'collapser');
}
}
if (element.open == info.openByDefault) {
delete toggled[id];
} else {
toggled[id] = true;
}
rememberToggled();
}
function gatherDetailsElements() {
const els: HTMLDetailsElement[] = Array.prototype.slice.call(document.getElementsByTagName('details'));
for (const el of els) {
if (typeof el.id == "string" && el.id.length > 0) {
detailsRegistry[el.id] = {
element: el,
openByDefault: !!el.open,
toggles: [] // added later
};
el.addEventListener('toggle', onDetailsToggle);
}
}
}
function toggleDetails(id: string) {
const {element} = lookupDetailsRegistry(id);
element.open = !element.open;
}
function rememberToggled() {
const sections: string[] = Object.keys(toggled);
// cookie specific to this page; don't use setCookie which sets path=/
document.cookie = "toggled=" + encodeURIComponent(sections.join('+'));
}
function restoreToggled() {
const cookie = getCookie("toggled");
if (!cookie) { return; }
const ids = cookie.split('+');
for (const id of ids) {
const info = detailsRegistry[id];
toggled[id] = true;
if (info) {
info.element.open = !info.element.open;
}
}
}
function onToggleClick(ev: MouseEvent) {
ev.preventDefault();
const toggle = ev.currentTarget as HTMLElement;
const id = toggle.getAttribute('data-details-id');
if (!id) { throw new Error("element with class 'details-toggle' has no 'data-details-id' attribute!"); }
toggleDetails(id);
}
function initCollapseToggles() {
const toggles: HTMLElement[] = Array.prototype.slice.call(document.getElementsByClassName('details-toggle'));
toggles.forEach(toggle => {
const id = toggle.getAttribute('data-details-id');
if (!id) { throw new Error("element with class 'details-toggle' has no 'data-details-id' attribute!"); }
const info = lookupDetailsRegistry(id);
info.toggles.push(toggle);
toggle.addEventListener('click', onToggleClick);
if (toggle.classList.contains('details-toggle-control')) {
toggle.classList.add(info.element.open ? 'collapser' : 'expander');
}
});
}
export function init() {
gatherDetailsElements();
restoreToggled();
initCollapseToggles();
}
|