// Hero meta columns: author/affiliation tracing, "Show all" toggles, copy // buttons, the Cite popover and PDF access for `pdfProOnly` articles. async function copyText(text: string): Promise { try { await navigator.clipboard.writeText(text); return true; } catch {} const ta = document.createElement("textarea"); ta.value = text; ta.setAttribute("readonly", ""); ta.style.cssText = "position:fixed;opacity:0"; document.body.appendChild(ta); ta.select(); let ok = false; try { ok = document.execCommand("copy"); } catch {} ta.remove(); return ok; } function wireTracing(box: HTMLElement) { const authors = [...box.querySelectorAll(".meta-authors li[data-aff]")]; const affs = [...box.querySelectorAll(".meta-affs li[data-aff]")]; const trace = (li: HTMLElement) => { const ids = (li.dataset.aff || "").split(" "); const isAuthor = !!li.closest(".meta-authors"); box.classList.add("is-tracing"); authors.forEach((a) => a.classList.toggle( "on", isAuthor ? a === li : (a.dataset.aff || "").split(" ").some((x) => ids.includes(x)), ), ); affs.forEach((f) => f.classList.toggle("on", ids.includes(f.dataset.aff || ""))); }; const clear = () => { box.classList.remove("is-tracing"); box.querySelectorAll(".on").forEach((el) => el.classList.remove("on")); }; [...authors, ...affs].forEach((li) => { li.addEventListener("mouseenter", () => trace(li)); li.addEventListener("focusin", () => trace(li)); li.addEventListener("mouseleave", clear); li.addEventListener("focusout", clear); }); } function wireCitePopover(root: HTMLElement) { let open: { btn: HTMLElement; pop: HTMLElement } | null = null; const close = (focusBtn: boolean) => { if (!open) return; open.pop.hidden = true; open.btn.setAttribute("aria-expanded", "false"); if (focusBtn) open.btn.focus(); open = null; }; root.querySelectorAll("[data-pop]").forEach((btn) => { const pop = document.getElementById(btn.dataset.pop || ""); if (!pop) return; btn.addEventListener("click", () => { const wasOpen = !pop.hidden; close(false); if (wasOpen) return; pop.hidden = false; btn.setAttribute("aria-expanded", "true"); // Keep the popover inside the viewport pop.style.translate = ""; const r = pop.getBoundingClientRect(); const vw = document.documentElement.clientWidth; const dx = r.left < 12 ? 12 - r.left : r.right > vw - 12 ? vw - 12 - r.right : 0; if (dx) pop.style.translate = `calc(-50% + ${Math.round(dx)}px) 0`; pop.classList.remove("is-in"); void pop.offsetWidth; pop.classList.add("is-in"); (pop.querySelector("button, a") || pop).focus({ preventScroll: true }); open = { btn, pop }; }); pop.addEventListener("keydown", (e) => { if (e.key === "Escape") { e.stopPropagation(); close(true); } }); }); document.addEventListener("click", (e) => { const t = e.target as Node; if (open && !open.pop.contains(t) && !open.btn.contains(t)) close(false); }); } function wirePdfAccess(el: HTMLElement) { if (el.dataset.pdfProOnly !== "true") return; // Outside of the Hugging Face Space iframe there is no plan to ask for: // the PDF stays available (same fallback as before). const FALLBACK_MS = 3000; let done = false; const apply = (isPro: boolean) => { done = true; el.dataset.state = isPro ? "open" : "locked"; }; if (window.parent && window.parent !== window) { window.addEventListener("message", (event) => { if (event.data?.type === "USER_PLAN") apply(event.data.plan?.user === "pro"); }); window.parent.postMessage({ type: "USER_PLAN_REQUEST" }, "*"); setTimeout(() => !done && apply(true), FALLBACK_MS); } else { apply(true); } } export function initMeta() { const box = document.querySelector(".meta-cols"); if (!box) return; const live = box.querySelector("[data-meta-live]"); if (box.hasAttribute("data-meta-trace")) wireTracing(box); wireCitePopover(box); box.querySelectorAll(".meta-pdf").forEach(wirePdfAccess); // "Show all N" expands the list in place and stays open until "Show fewer" box.querySelectorAll(".meta-expand").forEach((btn) => btn.addEventListener("click", () => { const list = document.getElementById(btn.getAttribute("aria-controls") || ""); if (!list) return; const opening = list.classList.contains("is-collapsed"); list.classList.toggle("is-collapsed", !opening); btn.setAttribute("aria-expanded", String(opening)); btn.textContent = opening ? "Show fewer" : btn.dataset.more || ""; }), ); box.querySelectorAll(".meta-copy[data-copy-value], .meta-copy[data-copy-from]").forEach((btn) => btn.addEventListener("click", async () => { const text = btn.dataset.copyValue ?? document.getElementById(btn.dataset.copyFrom || "")?.textContent ?? ""; await copyText(text); btn.textContent = "Copied"; if (live) live.textContent = btn.dataset.copyDone || "Copied"; setTimeout(() => (btn.textContent = "Copy"), 1400); }), ); }