/* pjax — client-side navigation. swaps the page content instead of a full
   reload, so the cursor, shell and theme stay alive (no flashing, no re-fetch
   of css/js). pure progressive enhancement: if fetch/history/DOMParser are
   missing, or anything goes wrong, links fall back to normal navigation. */
(function () {
  "use strict";
  if (!window.history || !window.fetch || !window.DOMParser) return;
  var MAIN = "#main-content";
  if (!document.querySelector(MAIN)) return;

  function internal(a) {
    return a && a.href && a.origin === location.origin &&
      a.getAttribute("href").charAt(0) !== "#" &&
      !a.hasAttribute("download") &&
      (a.getAttribute("target") || "") !== "_blank";
  }

  function swap(html, url) {
    var doc = new DOMParser().parseFromString(html, "text/html");
    var nm = doc.querySelector(MAIN), om = document.querySelector(MAIN);
    if (!nm || !om) { location.href = url; return; }

    om.innerHTML = nm.innerHTML;
    if (doc.title) document.title = doc.title;
    document.body.className = doc.body.className;

    // breadcrumb (in the persistent header) — swap its inner links
    var np = doc.querySelector(".prompt__path"), op = document.querySelector(".prompt__path");
    if (np && op) op.innerHTML = np.innerHTML;

    // per-section typeable routes live in .topbar — replace them
    var topbar = document.querySelector(".topbar");
    var oldR = document.querySelector(".routes");
    if (oldR && oldR.parentNode) oldR.parentNode.removeChild(oldR);
    var newR = doc.querySelector(".routes");
    if (newR && topbar) topbar.appendChild(document.importNode(newR, true));

    window.scrollTo(0, 0);
    document.dispatchEvent(new CustomEvent("pjax:load"));
  }

  // prefetch cache: hovering a link fetches its HTML so the click is instant
  var cache = {};
  function fetchHTML(url) {
    return fetch(url, { credentials: "same-origin" })
      .then(function (r) { if (!r.ok) throw new Error(r.status); return r.text(); });
  }
  function prefetch(url) {
    if (!cache[url]) cache[url] = fetchHTML(url).catch(function () { delete cache[url]; return null; });
  }

  var busy = false;
  function go(url, push) {
    if (busy) return;
    busy = true;
    (cache[url] || fetchHTML(url))
      .then(function (html) {
        busy = false;
        if (!html) { location.href = url; return; }   // prefetch had failed
        if (push) history.pushState({ pjax: true }, "", url);
        swap(html, url);
      })
      .catch(function () { busy = false; location.href = url; });
  }

  document.addEventListener("mouseover", function (e) {
    var a = e.target.closest ? e.target.closest("a[href]") : null;
    if (internal(a) && a.href.split("#")[0] !== location.href.split("#")[0]) prefetch(a.href);
  }, { passive: true });

  document.addEventListener("click", function (e) {
    if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
    var a = e.target.closest ? e.target.closest("a[href]") : null;
    if (!internal(a)) return;
    if (a.href.split("#")[0] === location.href.split("#")[0]) return; // same document
    e.preventDefault();
    go(a.href, true);
  });

  window.addEventListener("popstate", function () { go(location.href, false); });
  history.replaceState({ pjax: true }, "", location.href);

  // bridge for programmatic navigation (shell commands, filebrowser Enter)
  window.pjax = function (url) { go(url, true); };
})();
