async function getHTML(endpoint, options = {}) { let query = {}; if (options._query !== undefined) { query = options._query; delete options._query; } const params = new URLSearchParams(query); const res = await fetch(`${endpoint}?${params}`, options); if (!res.ok) { console.error(res); } return { body: await res.text(), status: res.status }; } export const b = { bookmarksCollectionEndpoint: '/hyperapi/bookmarks/dropdown/', bookmarkMenuState: {}, } export async function showBookmarkMenu(ev, sender, el) { if (b.bookmarkMenuState.state === undefined) { el.addEventListener('toggle', e => { if (e.newState === 'closed') { b.bookmarkMenuState.state = 'closed'; } }); } // dismiss if open and last invoker is the same button that opened it if (b.bookmarkMenuState.state === 'open' && b.bookmarkMenuState.invoker === sender) { el.hidePopover(); return; } b.bookmarkMenuState.invoker = sender; b.bookmarkMenuState.state = 'open'; b.send({ 'plain': 'Loading…' }, 'fillBookmarkMenu'); el.showPopover(); const bRect = sender.getBoundingClientRect(); const menuRect = el.getBoundingClientRect(); const preferredLeft = bRect.right - menuRect.width; const enoughSpace = preferredLeft >= 0; const scrollY = window.scrollY; if (enoughSpace) { el.style.left = `${preferredLeft}px`; } else { el.style.left = `${bRect.left}px`; } el.style.top = `${bRect.bottom + scrollY}px`; b.bookmarkMenuState.kind = sender.dataset.conceptKind; b.bookmarkMenuState.id = sender.dataset.conceptId; const bookmarkCollections = await getHTML(b.bookmarksCollectionEndpoint, { _query: { concept_kind: b.bookmarkMenuState.kind, concept_id: b.bookmarkMenuState.id, } }); b.send({ 'html': bookmarkCollections.body }, 'fillBookmarkMenu'); } export function fillBookmarkMenu(payload, __, el) { if (payload.plain) { el.innerText = payload.plain; return; } el.innerHTML = payload.html; } export async function bookmarkMenuSubmit(ev, _, el) { ev.preventDefault(); const url = el.action; const body = new URLSearchParams(new FormData(el)); const options = { body: body, method: 'POST' }; const status = (await getHTML(url, options)).status; if (status !== 204) { b.trigger('bookmarkMenuShowError'); return; } const newCollections = await getHTML(b.bookmarksCollectionEndpoint, { _query: { concept_kind: b.bookmarkMenuState.kind, concept_id: b.bookmarkMenuState.id, saved: true, } }); b.send({ 'html': newCollections.body }, 'fillBookmarkMenu'); } export function bookmarkMenuResetSavedButton(_, __, el) { el.value = 'Save'; } export function bookmarkMenuShowError(_, __, el) { if (el === undefined) { return; } if (el.classList.contains('hidden')) { el.classList.remove('hidden'); setTimeout(() => { b.trigger('bookmarkMenuHideError') }, 4000); } } export function bookmarkMenuHideError(_, __, el) { if (el === undefined) { return; } if (!el.classList.contains('hidden')) { el.classList.add('hidden'); } }