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

351 lines
10 KiB
JavaScript
Raw Permalink 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 freezeOk(row) {
return row && row.freeze_ok === true;
}
function freezeLabel(row) {
if (freezeOk(row)) return "freeze ok";
var err = row && row.freeze_error ? String(row.freeze_error) : "";
return err ? "freeze failed: " + err : "freeze failed";
}
function freezeMarkHtml(row) {
var ok = freezeOk(row);
return (
'<span class="cascade-mark ' +
(ok ? "cascade-ok" : "cascade-fail") +
'" title="' +
esc(freezeLabel(row)) +
'" aria-label="' +
esc(freezeLabel(row)) +
'">' +
(ok ? "✓" : "✗") +
"</span>"
);
}
function plusHtml(row) {
if (row.vs_parent_plus != null) {
var p = Number(row.vs_parent_plus);
if (!isFinite(p)) return "";
return '<span class="diff-plus">+' + esc(String(p)) + "</span>";
}
if (row.loc != null) {
var n = Number(row.loc);
if (!isFinite(n)) return "";
return '<span class="diff-loc">' + esc(String(n)) + "</span>";
}
return "";
}
function minusHtml(row) {
if (row.vs_parent_minus != null) {
var m = Number(row.vs_parent_minus);
if (!isFinite(m)) return "";
return '<span class="diff-minus">−' + esc(String(m)) + "</span>";
}
if (row.loc != null) {
return '<span class="diff-loc-unit">LOC</span>';
}
return "";
}
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 vs = "";
if (row.vs_parent_plus != null || row.vs_parent_minus != null) {
vs =
" vs parent +" +
String(row.vs_parent_plus == null ? 0 : row.vs_parent_plus) +
"/−" +
String(row.vs_parent_minus == null ? 0 : row.vs_parent_minus);
} else if (row.loc != null) {
vs = " loc=" + String(row.loc);
}
var built = builtText(row);
return (
"Switch build to " +
(row.branch || "") +
(tip ? " @ " + tip : "") +
(tipTime ? " tip=" + shortTime(tipTime) : "") +
(built ? " build=" + built : "") +
" " +
freezeLabel(row) +
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 markEl = a.querySelector(".cascade-mark");
if (markEl) {
var markHtml = freezeMarkHtml(row);
if (markEl.outerHTML !== markHtml) markEl.outerHTML = markHtml;
}
var plusEl = a.querySelector(".cascade-plus");
if (plusEl) {
var ph = plusHtml(row);
if (plusEl.innerHTML !== ph) plusEl.innerHTML = ph;
}
var minusEl = a.querySelector(".cascade-minus");
if (minusEl) {
var mh = minusHtml(row);
if (minusEl.innerHTML !== mh) minusEl.innerHTML = mh;
}
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)) +
'">' +
freezeMarkHtml(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-plus">' +
plusHtml(row) +
"</span>" +
'<span class="cascade-minus">' +
minusHtml(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();
});
})();