102 lines
3.2 KiB
HTML
102 lines
3.2 KiB
HTML
<script>
|
|
(function () {
|
|
// Same-origin on clearnet and onion (nginx proxies both to GoatCounter).
|
|
// Sessions are server-side (IP+UA → ephemeral UUID); no tracking cookies.
|
|
window.goatcounter = { endpoint: "/count" };
|
|
|
|
const s = document.createElement("script");
|
|
s.async = true;
|
|
s.src = "/js/count.js";
|
|
document.head.appendChild(s);
|
|
})();
|
|
</script>
|
|
|
|
<script>
|
|
(function () {
|
|
// Tor + clearnet share one GoatCounter site → one TOTAL.
|
|
const GC = "";
|
|
const nf = new Intl.NumberFormat();
|
|
const SING = "visit";
|
|
const PLUR = "visits";
|
|
|
|
function ready(fn) {
|
|
if (document.readyState === "complete" || document.readyState === "interactive") setTimeout(fn, 0);
|
|
else document.addEventListener("DOMContentLoaded", fn);
|
|
}
|
|
|
|
function gcPath() {
|
|
try {
|
|
return window.goatcounter && window.goatcounter.get_data
|
|
? window.goatcounter.get_data().p
|
|
: location.pathname;
|
|
} catch (_) {
|
|
return location.pathname;
|
|
}
|
|
}
|
|
|
|
async function fetchCount(path, period) {
|
|
try {
|
|
const q = new URLSearchParams({ t: String(Date.now()) });
|
|
if (period) q.set("start", period);
|
|
const url = `${GC}/counter/${encodeURIComponent(path)}.json?${q}`;
|
|
const r = await fetch(url, { credentials: "omit" });
|
|
if (r.status === 404) return 0;
|
|
if (!r.ok) throw 0;
|
|
const j = await r.json();
|
|
// GoatCounter "count" is visits (session-deduped), not raw pageviews.
|
|
const n = Number(String(j.count).replace(/,/g, ""));
|
|
return Number.isFinite(n) ? n : 0;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
const labelVisits = (n) =>
|
|
n === null ? "—" : `${nf.format(n)} ${n === 1 ? SING : PLUR}`;
|
|
|
|
ready(async function () {
|
|
const post = document.querySelector(".post-single");
|
|
if (post) {
|
|
const meta = post.querySelector(
|
|
".post-header .post-meta, .post-header .entry-meta, .post-meta"
|
|
);
|
|
const visits = await fetchCount(gcPath());
|
|
if (meta) {
|
|
let span = meta.querySelector(".post-views");
|
|
if (!span) {
|
|
span = document.createElement("span");
|
|
span.className = "post-views";
|
|
meta.appendChild(span);
|
|
}
|
|
span.textContent = labelVisits(visits ?? 0);
|
|
}
|
|
}
|
|
|
|
const cards = document.querySelectorAll("article.post-entry");
|
|
await Promise.all(
|
|
Array.from(cards).map(async (card) => {
|
|
const meta = card.querySelector(".entry-footer, .post-meta");
|
|
const link = card.querySelector("a.entry-link, h2 a, .entry-title a");
|
|
if (!meta || !link) return;
|
|
try {
|
|
const u = new URL(link.getAttribute("href"), location.origin);
|
|
if (u.origin !== location.origin) return;
|
|
let span = meta.querySelector(".post-views");
|
|
if (!span) {
|
|
span = document.createElement("span");
|
|
span.className = "post-views";
|
|
meta.appendChild(span);
|
|
}
|
|
span.textContent = labelVisits((await fetchCount(u.pathname)) ?? 0);
|
|
} catch {}
|
|
})
|
|
);
|
|
|
|
const uvEl = document.getElementById("gc_site_visitors_week");
|
|
if (uvEl) {
|
|
const n = await fetchCount("TOTAL", "week");
|
|
uvEl.textContent = n === null ? "—" : nf.format(n);
|
|
}
|
|
});
|
|
})();
|
|
</script>
|