/* 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); }; })();