koopa-admin-log/sites/dossiers.ngi.hacktivism.ch/cascade/_cascade/bar.js

300 lines
8.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(function () {
var POLL_MS = 30000;
var MONTHS = [
"jan",
"feb",
"mar",
"apr",
"may",
"jun",
"jul",
"aug",
"sep",
"oct",
"nov",
"dec",
];
function cookieEdition() {
var m = document.cookie.match(/(?:^|; )tdpd_edition=([^;]*)/);
if (!m) return "flask";
try {
return decodeURIComponent(m[1]) || "flask";
} catch (e) {
return "flask";
}
}
function esc(s) {
return String(s)
.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
/** ISO → "sep30 16:12" (3-letter English month + DD + HH:MM). */
function shortTime(iso) {
if (!iso) return "";
var m = String(iso).match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}:\d{2})/);
if (m) {
var mon = MONTHS[Number(m[2]) - 1] || m[2];
return mon + m[3] + " " + m[4];
}
return String(iso).slice(0, 16).replace("T", " ");
}
function fmtBuildSec(sec) {
if (sec == null || sec === "") return "";
var n = Number(sec);
if (!isFinite(n)) return "";
if (n < 10) return n.toFixed(1) + "s";
return Math.round(n) + "s";
}
function builtText(row) {
var sec = fmtBuildSec(row.build_seconds);
var at = shortTime(row.built_at || "");
if (sec && at) return sec + " " + at;
return sec || at;
}
function diffHtml(row) {
var plus = row.vs_main_plus;
var minus = row.vs_main_minus;
if (plus == null && minus == null) return "";
var p = plus == null ? 0 : Number(plus);
var m = minus == null ? 0 : Number(minus);
if (!isFinite(p) || !isFinite(m)) return "";
return (
'<span class="diff-plus">+' +
esc(String(p)) +
'</span> <span class="diff-minus">−' +
esc(String(m)) +
"</span>"
);
}
function setText(el, text) {
if (!el) return;
var t = text || "";
if (el.textContent !== t) el.textContent = t;
}
function rowTitle(row) {
var tip = row.tip || "";
var tipTime = row.tip_time || "";
var plus = row.vs_main_plus;
var minus = row.vs_main_minus;
var vs = "";
if (plus != null || minus != null) {
vs =
" vs main +" +
String(plus == null ? 0 : plus) +
"/−" +
String(minus == null ? 0 : minus);
}
var built = builtText(row);
return (
"Switch build to " +
(row.branch || "") +
(tip ? " @ " + tip : "") +
(tipTime ? " tip=" + shortTime(tipTime) : "") +
(built ? " build=" + built : "") +
vs
);
}
function structureKey(list) {
var parts = [];
for (var i = 0; i < list.length; i++) {
var row = list[i];
parts.push(
(row.edition || "") +
"\t" +
(row.branch || "") +
"\t" +
(row.prefix || "") +
"\t" +
(row.parent || "")
);
}
return parts.join("\n");
}
function pinBar(timingMsg) {
var bar = document.getElementById("cascade-bar");
if (!bar) return;
if (timingMsg) {
bar.setAttribute("data-timing", timingMsg);
bar.setAttribute("title", timingMsg);
}
var h = bar.offsetHeight || 0;
document.documentElement.style.setProperty("--cascade-pad", h + "px");
}
function markActive(root, edition) {
var links = root.querySelectorAll("a[data-edition]");
for (var i = 0; i < links.length; i++) {
var a = links[i];
var ed = a.getAttribute("data-edition");
var on =
ed === edition ||
(edition === "flask" && a.getAttribute("data-default") === "1");
if (on) {
a.classList.add("is-active");
a.setAttribute("aria-current", "page");
} else {
a.classList.remove("is-active");
a.removeAttribute("aria-current");
}
}
}
function timingText(timing, listLen) {
if (!timing || timing.wall_seconds == null) return "";
return (
"last full pass ~" +
String(timing.wall_seconds) +
"s · built " +
String(timing.built || 0) +
" / skipped " +
String(timing.skipped || 0) +
" / " +
String(timing.branches || listLen)
);
}
function updateValues(mount, data) {
var list = (data && data.branches) || [];
var defaultBranch = (data && data.default_branch) || "feat/website-views";
var timing = (data && data.rebuild_timing) || null;
var rows = mount.querySelectorAll("a.cascade-tree-row[data-edition]");
if (rows.length !== list.length) return false;
for (var i = 0; i < list.length; i++) {
var row = list[i];
var a = rows[i];
if (a.getAttribute("data-edition") !== (row.edition || "")) return false;
if (a.getAttribute("data-branch") !== (row.branch || "")) return false;
var isDefault = (row.branch || "") === defaultBranch;
if (isDefault) a.setAttribute("data-default", "1");
else a.removeAttribute("data-default");
setText(a.querySelector(".cascade-when"), shortTime(row.tip_time || ""));
setText(a.querySelector(".cascade-built"), builtText(row));
var diffEl = a.querySelector(".cascade-diff");
if (diffEl) {
var dh = diffHtml(row);
if (diffEl.innerHTML !== dh) diffEl.innerHTML = dh;
}
a.setAttribute("title", rowTitle(row));
}
var timingEl = mount.querySelector(".cascade-timing");
var t = timingText(timing, list.length);
if (t) {
if (!timingEl) {
timingEl = document.createElement("div");
timingEl.className = "cascade-timing";
mount.appendChild(timingEl);
}
if (timingEl.textContent !== t) timingEl.textContent = t;
} else if (timingEl) {
timingEl.parentNode.removeChild(timingEl);
}
markActive(mount, cookieEdition());
pinBar(t);
return true;
}
function renderTree(data, soft) {
var mount = document.getElementById("cascade-tree");
if (!mount) return;
var list = (data && data.branches) || [];
if (!list.length) {
mount.textContent = "No branches in branch-status.json";
pinBar();
return;
}
if (soft && mount.getAttribute("data-structure") === structureKey(list)) {
if (updateValues(mount, data)) return;
}
var defaultBranch = (data && data.default_branch) || "feat/website-views";
var timing = (data && data.rebuild_timing) || null;
var html = [];
html.push('<div class="cascade-tree-lines" role="group">');
for (var i = 0; i < list.length; i++) {
var row = list[i];
var edition = row.edition || "";
var branch = row.branch || "";
var prefix = row.prefix || "";
var parent = row.parent || "";
var isDefault = branch === defaultBranch;
html.push(
'<a class="cascade-tree-row" role="treeitem" href="/_cascade/use/' +
esc(edition) +
'" data-edition="' +
esc(edition) +
'" data-branch="' +
esc(branch) +
'"' +
(parent ? ' data-parent="' + esc(parent) + '"' : "") +
(isDefault ? ' data-default="1"' : "") +
' title="' +
esc(rowTitle(row)) +
'">' +
'<span class="cascade-name">' +
esc(prefix) +
"<code>" +
esc(branch) +
"</code></span>" +
'<span class="cascade-when">' +
esc(shortTime(row.tip_time || "")) +
"</span>" +
'<span class="cascade-built">' +
esc(builtText(row)) +
"</span>" +
'<span class="cascade-diff">' +
diffHtml(row) +
"</span>" +
"</a>"
);
}
html.push("</div>");
var t = timingText(timing, list.length);
if (t) {
html.push('<div class="cascade-timing">' + esc(t) + "</div>");
}
mount.innerHTML = html.join("");
mount.setAttribute("data-structure", structureKey(list));
markActive(mount, cookieEdition());
pinBar(t);
}
function loadStatus(soft) {
var mount = document.getElementById("cascade-tree");
return fetch("/branch-status.json", { cache: "no-store" })
.then(function (res) {
if (!res.ok) throw new Error("status");
return res.json();
})
.then(function (data) {
renderTree(data, !!soft);
})
.catch(function () {
if (!soft && mount && !mount.querySelector("a[data-edition]")) {
mount.innerHTML =
'<a class="cascade-tree-row" href="/_cascade/use/flask" data-edition="flask" data-branch="flask"><span class="cascade-name"><code>flask</code></span></a>' +
'<span class="tip"> (branch-status unavailable)</span>';
markActive(mount, cookieEdition());
pinBar();
}
});
}
loadStatus(false);
setInterval(function () {
loadStatus(true);
}, POLL_MS);
window.addEventListener("resize", function () {
pinBar();
});
})();