

/* Event listener function for the COPY BUTTON */
document.addEventListener("DOMContentLoaded", function () {
    document.querySelectorAll("pre.src").forEach(function (codeBlock) {
	const button = document.createElement("button");
	button.innerText = "Copy";
	button.className = "copy-btn";

	// Append button inside <pre>
	codeBlock.appendChild(button);

	button.addEventListener("click", function () {
	    const text = codeBlock.innerText.replace(button.innerText, ""); // exclude button text
	    navigator.clipboard.writeText(text.trim()).then(() => {
		button.innerText = "Copied!";
		setTimeout(() => (button.innerText = "Copy"), 1500);
	    });
	});
    });
});


/* Event listener for footnotes and sidenotes*/
document.addEventListener("DOMContentLoaded", () => {
    document.querySelectorAll('a.footref[href^="#fn"]').forEach(ref => {
	const sup = ref.closest("sup") || ref;
	// idempotent: don't insert twice
	if (sup.nextElementSibling && sup.nextElementSibling.classList?.contains("footnote-sidenote")) return;
	
	const targetId = ref.getAttribute("href").replace(/^#/, ""); // works for fn.2 or fn2

	const anchor   = document.getElementById(targetId);
	if (!anchor) return;
	
	const footdef = anchor.closest(".footdef") || anchor.parentElement;
	if (!footdef) return;
	
	// 1) Prefer leaf paragraphs to avoid div+p duplication
	let paras = footdef.querySelectorAll("p.footpara");
	if (!paras.length) {
	    // fallback: any .footpara elements that don't contain another .footpara
	    paras = footdef.querySelectorAll(".footpara:not(:has(.footpara))");
	}

	// 2) Build HTML, de-duplicating by text content
	let parts = [];
	if (paras.length) {
	    const seen = new Set();
	    parts = Array.from(paras).map(p => {
		const txt = p.textContent.trim().replace(/\s+/g, " ");
		if (seen.has(txt)) return "";
		seen.add(txt);
		return p.innerHTML.trim();
	    }).filter(Boolean);
	}

	// 3) Fallback: clean full block if no paras found
	if (!parts.length) {
	    const clone = footdef.cloneNode(true);
	    clone.querySelectorAll("sup.footnum, a[role='doc-backlink']").forEach(n => n.remove());
	    parts = [clone.innerHTML.trim()];
	}

	// 4) Insert the sidenote
	const sn = document.createElement("span");
	sn.className = "sidenote footnote-sidenote";
	sn.setAttribute("data-fn", (ref.textContent || "").trim());
	sn.innerHTML = parts.join(" ");
	
	sup.insertAdjacentElement("afterend", sn);
    });
});


/* Function for setting the theme */
(function(){
    const root = document.documentElement;
    const storageKey = "theme";
    const saved = localStorage.getItem(storageKey);
    if (saved === "dark" || saved === "light") {
	root.setAttribute("data-theme", saved);
    }
    const btn = document.getElementById("theme-toggle");
    if (!btn) return;
    btn.addEventListener("click", () => {
	const current = root.getAttribute("data-theme");
	const next = current === "dark" ? "light" : "dark";
	// If no current (auto), assume we’re toggling to dark first
	const target = current ? next : "dark";
	root.setAttribute("data-theme", target);
	localStorage.setItem(storageKey, target);
    });
})();


// Simple, timezone-safe if you pass UTC (…Z) in the datetime
document.addEventListener('DOMContentLoaded', () => {
  const els = document.querySelectorAll('time.countdown');
  if (!els.length) return;

  const plural = (n, w) => `${n} ${w}${n === 1 ? '' : 's'}`;

  const render = (el) => {
    const raw = el.getAttribute('datetime');
    const label = el.dataset.label || '';
    const target = new Date(raw);           // Prefer ISO like 2025-12-31T00:00:00Z
    if (isNaN(target)) { el.textContent = '—'; return; }

    const now = new Date();
    let diff = target - now;

    if (diff <= 0) {
      el.textContent = `${label ? label + ' ' : ''}today`;
      el.classList.add('expired');
      return;
    }

    const d = Math.floor(diff / 86400000);  diff -= d * 86400000;
    const h = Math.floor(diff / 3600000);   diff -= h * 3600000;
    const m = Math.floor(diff / 60000);     diff -= m * 60000;
    const s = Math.floor(diff / 1000);

    const pieces = [];
    if (d) pieces.push(plural(d, 'day'));
    pieces.push(`${h}h ${m}m ${s}s`);

    el.textContent = `${label ? label + ' in : ' : ''}${pieces.join(' ')}`;
  };

  const tick = () => els.forEach(render);
  tick();
  setInterval(tick, 1000); // update every second
});



/* Event listener for scrolling and changing the active label on the TOC */
document.addEventListener("DOMContentLoaded", () => {
    const toc = document.querySelector("#text-table-of-contents");
    if (!toc) { console.warn("No #text-table-of-contents found"); return; }

    const links = toc.querySelectorAll('a[href^="#"]'); // '^=' is a starts with operator.
    // <a href="#introduction">Intro</a>   matches  
    if (!links.length) { console.warn("No ToC links found"); return; }

    // Map: id -> link
    const linkById = new Map();
    links.forEach(a => {
	const id = decodeURIComponent(a.getAttribute("href").slice(1));
	const el = document.getElementById(id);
	if (el) linkById.set(id, a);
    });
    if (!linkById.size) { console.warn("No matching headings with IDs"); return; }

    // Headings to observe (h2–h4 usually)
    const headings = [...document.querySelectorAll("h2[id], h3[id], h4[id]")]
	  .filter(h => linkById.has(h.id));

    // Helper to mark active
    const setActive = (id) => {
	links.forEach(a => {
	    const active = a.getAttribute("href") === `#${id}`;
	    a.classList.toggle("is-active", active);
	    if (active) a.setAttribute("aria-current", "true");
	    else a.removeAttribute("aria-current");
	});
    };

    // Calculate sticky header offset in px
    const headerOffsetPx = 6.5 * parseFloat(getComputedStyle(document.documentElement).fontSize);

    // Track visible headings (id -> distance from top)
    const visible = new Map();

    const observer = new IntersectionObserver((entries) => {
	entries.forEach(entry => {
	    const id = entry.target.id;
	    if (entry.isIntersecting) {
		// How far from the top (after header offset)
		const dist = entry.target.getBoundingClientRect().top - headerOffsetPx;
		visible.set(id, dist);
	    } else {
		visible.delete(id);
	    }
	});
	
	if (visible.size) {
	    // Choose the heading closest to the top (>= -headerOffset)
	    const topMost = [...visible.entries()]
		  .sort((a,b) => Math.abs(a[1]) - Math.abs(b[1]))[0][0];
	    setActive(topMost);
	    // console.log("Active:", topMost, visible);
	}
    }, {
	root: null,                                   // track relative to viewport
	rootMargin: `-${headerOffsetPx}px 0px -70% 0px`,
	threshold: [0, 0.01, 0.1]                     // fire as soon as it enters
    });
    
    headings.forEach(h => observer.observe(h));

    // Initial highlight (in case load mid‑page)
    let bestId = null, bestDist = Infinity;
    headings.forEach(h => {
	const top = h.getBoundingClientRect().top - headerOffsetPx;
	const dist = top < 0 ? Math.abs(top) : top + 1e6;
	if (dist < bestDist) { bestDist = dist; bestId = h.id; }
    });
    if (bestId) setActive(bestId);
    
    // smooth-scroll ToC clicks
    toc.addEventListener("click", (e) => {
	const a = e.target.closest('a[href^="#"]');
	if (!a) return;
	const id = decodeURIComponent(a.hash.slice(1));
	const el = document.getElementById(id);
	if (!el) return;
	e.preventDefault();
	el.scrollIntoView({ behavior: "smooth", block: "start" });
	el.setAttribute("tabindex", "-1");
	el.focus({ preventScroll: true });
	history.pushState(null, "", `#${id}`);
    });
});


document.addEventListener("click", (e) => {
  const link = e.target.closest("a");
  if (!link) return;

  const href = link.getAttribute("href");
  if (!href) return;

  // Ignore pure fragment links (#foo)
  if (href.startsWith("#")) return;

  // Resolve relative → absolute
  const url = new URL(href, window.location.href);

  // Only intercept same-origin HTML pages
  if (url.origin !== window.location.origin) return;
  if (!url.pathname.endsWith(".html")) return;

  e.preventDefault();

  pushPane(url.pathname + url.hash);
});

async function pushPane(urlWithHash) {
  const [url, hash] = urlWithHash.split("#");

  const track = document.querySelector(".stack-track");

const existing = [...track.children].find(p => p.dataset.url === urlWithHash);
if (existing) {
  existing.scrollIntoView({ behavior: "smooth", inline: "end" });
  return;
}
  const res = await fetch(url);
  const html = await res.text();
  const doc = new DOMParser().parseFromString(html, "text/html");

  const content = doc.querySelector("#content");
  if (!content) return;

  const pane = document.createElement("article");
  pane.className = "stack-pane";
  pane.dataset.url = urlWithHash;
  
  pane.innerHTML = `
  <div class="pane-header">
    <button class="pane-close" aria-label="Close pane">×</button>
  </div>
`;

  pane.appendChild(content);
  track.appendChild(pane);
  pane.scrollIntoView({ behavior: "smooth", inline: "end" });

  // Scroll to the anchor if present
  if (hash) {
    requestAnimationFrame(() => {
      const target = pane.querySelector(`#${CSS.escape(hash)}`);
      target?.scrollIntoView({ behavior: "smooth", block: "start" });
    });
  }
pane.querySelector(".pane-close").addEventListener("click", () => {
  pane.remove();
  updateURL();
});

  updateURL();
}

function updateURL() {
  const panes = [...document.querySelectorAll(".stack-pane")];

  const urls = panes.map(p => p.dataset.url);

  const params = new URLSearchParams(window.location.search);
  params.set("stackedNotes", urls.join("|"));

  history.replaceState({}, "", "?" + params.toString());
}

window.addEventListener("DOMContentLoaded", async () => {
  const params = new URLSearchParams(window.location.search);
  const stack = params.get("stackedNotes");
  if (!stack) return;

  const urls = stack.split("|");

  // First pane is already rendered by Org
  const base = urls[0];
  const current = window.location.pathname + window.location.hash;

  // Only continue if URL matches base
  if (base !== current && !base.startsWith(window.location.pathname)) {
    console.warn("Stack base mismatch:", base, current);
    return;
  }

  // Load remaining panes sequentially
  for (const url of urls.slice(1)) {
    await pushPane(url);
  }

  // Scroll to last pane
  const panes = document.querySelectorAll(".stack-pane");
  panes[panes.length - 1]?.scrollIntoView({
    behavior: "auto",
    inline: "end"
  });
});
