const k = document.getElementById("kaomoji");
if (k) k.textContent = FACES[Math.floor(Math.random() * FACES.length)];
function copy(b) {
const was = b.innerHTML;
const done = () => { b.textContent = "♡"; setTimeout(() => (b.innerHTML = was), 1400); };
if (navigator.clipboard) return navigator.clipboard.writeText(b.dataset.clone).then(done);
const t = Object.assign(document.createElement("textarea"), { value: b.dataset.clone });
document.body.append(t); t.select(); document.execCommand("copy"); t.remove(); done();
}
const main = document.querySelector("main"), people = document.getElementById("people");
const q = document.getElementById("q");
const items = [...document.querySelectorAll("[data-q]")];
const sections = [...people.querySelectorAll(".person")];
function threads() {
document.querySelectorAll(".repos").forEach(u => {
const vis = [...u.children].filter(l => !l.hidden && getComputedStyle(l).display !== "none");
u.querySelectorAll("li").forEach(l => l.classList.remove("last"));
if (vis.length) vis[vis.length - 1].classList.add("last");
});
}
function hl(el, term) {
el.querySelectorAll("[data-t], .desc").forEach(n => {
const s = n.dataset.t || (n.dataset.t = n.textContent);
if (!term) { n.textContent = s; return; }
const i = s.toLowerCase().indexOf(term);
if (i < 0) { n.textContent = s; return; }
n.innerHTML = "";
n.append(s.slice(0, i));
const m = document.createElement("mark");
m.textContent = s.slice(i, i + term.length);
n.append(m, s.slice(i + term.length));
});
}
// the server split the columns by row counts, this one by drawn heights
function layout() {
const cols = [...people.children];
if (cols.length < 2 || getComputedStyle(cols[0]).display === "contents") return;
const heights = cols.map(() => 0);
sections.forEach(s => {
const i = heights.indexOf(Math.min(...heights));
cols[i].append(s);
if (!s.hidden) heights[i] += s.offsetHeight + 30;
});
}
function apply() {
const t = q.value.trim().toLowerCase();
let n = 0;
items.forEach(el => {
const on = !t || el.dataset.q.includes(t);
el.hidden = !on;
if (on) { n++; hl(el, t); }
});
document.querySelectorAll("[data-group]").forEach(g => {
g.hidden = !g.querySelector("[data-q]:not([hidden])");
});
main.classList.toggle("searching", !!t);
document.getElementById("empty").hidden = n > 0 || !items.length;
threads();
layout();
}
q.addEventListener("input", apply);
addEventListener("keydown", e => {
if (e.key === "/" && !e.target.matches("input, textarea") && !e.metaKey && !e.ctrlKey && !e.altKey) {
e.preventDefault(); q.focus(); q.select();
} else if (e.key === "Escape" && document.activeElement === q) {
q.value = ""; apply(); q.blur();
}
});
main.addEventListener("change", e => { if (e.target.matches(".u-chk")) layout(); });
addEventListener("resize", layout);
const ages = [...document.querySelectorAll(".age[data-at]")];
function age(s) {
const h = 3600, d = 24 * h;
if (s < h) return Math.max(1, Math.floor(s / 60)) + "m";
if (s < d) return Math.floor(s / h) + "h";
if (s < 7 * d) return Math.floor(s / d) + "d";
if (s < 30 * d) return Math.floor(s / (7 * d)) + "w";
if (s < 365 * d) return Math.floor(s / (30 * d)) + "mo";
return Math.floor(s / (365 * d)) + "y";
}
function refreshAges() {
const now = Date.now() / 1000;
ages.forEach(a => { a.textContent = age(Math.max(0, now - a.dataset.at)); });
}
refreshAges();
setInterval(refreshAges, 60000);
// on phones the copy button sits after the age, and the fold stops short of it
function fit() {
const r = document.querySelector(".repos .row"), a = r && r.querySelector(".age");
if (!a) return;
const w = r.getBoundingClientRect().right - a.getBoundingClientRect().right;
document.documentElement.style.setProperty("--cw", Math.max(0, w) + "px");
}
addEventListener("resize", fit);
fit();
(() => {
const th = Object.assign(document.createElement("div"), { id: "th" });
document.body.append(th);
let fade, drag = null;
const se = document.scrollingElement;
const place = () => {
const ch = innerHeight, sh = se.scrollHeight;
if (sh <= ch + 2) { th.style.display = "none"; return; }
th.style.display = "";
const h = Math.max(28, ch * ch / sh);
th.style.height = h + "px";
th.style.top = 2 + se.scrollTop / (sh - ch) * (ch - h - 4) + "px";
};
const flash = () => {
th.classList.add("on");
clearTimeout(fade);
fade = setTimeout(() => { if (!drag) th.classList.remove("on"); }, 900);
};
addEventListener("scroll", () => { place(); flash(); });
addEventListener("resize", place);
new ResizeObserver(place).observe(document.body);
th.addEventListener("mousedown", e => {
e.preventDefault();
drag = { y: e.clientY, top: se.scrollTop, k: (se.scrollHeight - innerHeight) / (innerHeight - th.offsetHeight - 4) };
th.classList.add("drag", "on");
});
addEventListener("mousemove", e => {
if (drag) se.scrollTop = drag.top + (e.clientY - drag.y) * drag.k;
else if (e.clientX > innerWidth - 14) flash();
});
addEventListener("mouseup", () => {
if (!drag) return;
drag = null; th.classList.remove("drag"); flash();
});
place();
})();