summaryrefslogtreecommitdiff
path: root/cgithub/public/static/refs.js
diff options
context:
space:
mode:
authorJoe Mou <dev@mou.fo>2026-09-18 07:27:21 +0100
committerJoe Mou <dev@mou.fo>2026-09-18 07:27:21 +0100
commit9659525bf2387af6deab9d401784b94b1f44d395 (patch)
treec67dc1dd05bf2a8fa48dc123d6353d255a658ef1 /cgithub/public/static/refs.js
parent9819d8e5504d6f03d27ec6b8786eec8fe5a752a5 (diff)
parent01494e6fa1345a509560e5e19a7f08cb2aac5cc8 (diff)
Merge commit 'FILTERED_HEAD'HEADmain
Diffstat (limited to 'cgithub/public/static/refs.js')
-rw-r--r--cgithub/public/static/refs.js71
1 files changed, 71 insertions, 0 deletions
diff --git a/cgithub/public/static/refs.js b/cgithub/public/static/refs.js
new file mode 100644
index 0000000..1035593
--- /dev/null
+++ b/cgithub/public/static/refs.js
@@ -0,0 +1,71 @@
+// Progressive enhancement for the bits of data GitHub itself loads lazily:
+// the branch/tag dropdown, the latest-commit bar above a file listing, and the
+// language breakdown. All degrade gracefully without JS: the button's
+// command/commandfor still opens the (empty) dialog natively; the placeholders
+// just never fill in.
+document.addEventListener("DOMContentLoaded", () => {
+ const dialog = document.querySelector(".ref-selector-menu");
+ if (dialog) {
+ const list = dialog.querySelector(".ref-selector-list");
+ // The view and path let the picker link to the same place on another ref
+ // rather than to the repository root.
+ const { owner, repo, view, path = "" } = dialog.dataset;
+ const cache = {};
+
+ async function load(type) {
+ if (Object.prototype.hasOwnProperty.call(cache, type)) {
+ list.innerHTML = cache[type];
+ return;
+ }
+ list.textContent = "Loading…";
+ try {
+ const res = await fetch(`/api/${owner}/${repo}/refs/${type}/${view}/${path}`);
+ const html = res.ok ? await res.text() : "";
+ cache[type] = html || "Failed to load.";
+ list.innerHTML = cache[type];
+ } catch {
+ list.textContent = "Failed to load.";
+ }
+ }
+
+ let opened = false;
+ dialog.addEventListener("toggle", () => {
+ if (dialog.open && !opened) {
+ opened = true;
+ load("branches");
+ }
+ });
+
+ for (const tab of dialog.querySelectorAll("[data-ref-type]")) {
+ tab.addEventListener("click", () => {
+ for (const other of dialog.querySelectorAll("[data-ref-type]")) {
+ other.classList.toggle("active", other === tab);
+ }
+ load(tab.dataset.refType);
+ });
+ }
+ }
+
+ for (const placeholder of document.querySelectorAll(".placeholder[data-src]")) {
+ fetch(placeholder.dataset.src)
+ .then((res) => (res.ok ? res.text() : ""))
+ .then((html) => {
+ if (html.trim()) {
+ placeholder.outerHTML = html;
+ relocate();
+ } else {
+ placeholder.remove();
+ }
+ })
+ .catch(() => placeholder.remove());
+ }
+
+ // A fragment may carry a node that belongs elsewhere on the page, where an
+ // empty element of that id holds its spot: the sidebar's latest release
+ // reads as part of the release count, far from the languages it arrives with.
+ function relocate() {
+ for (const node of document.querySelectorAll("[data-target]")) {
+ document.getElementById(node.dataset.target)?.replaceWith(node);
+ }
+ }
+});