1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
/* ============================================================================
SillyLaird per-page visit counter (network-wide).
Injected by nginx before </body> on HTML responses across *.sillylaird.ca
(see /etc/nginx/snippets/sillylaird-visit-counter.conf). Also safe to load
from site.js / nav.js. Runs once per page load (window.__slVisitCounterInit).
Storage is localStorage + sessionStorage, keyed by host + path so each page
(and each subdomain) keeps its own count. Language variants share a key.
Also beacons a privacy-friendly hit to /api/hit.php (no IP stored server-side;
only host/path, coarse browser·OS label, language, referrer host, local count).
============================================================================ */
(function () {
"use strict";
if (window.__slVisitCounterInit) return;
window.__slVisitCounterInit = true;
var path = location.pathname || "/";
// Skip admin / API / auth / binary endpoints — no counter chrome there.
if (/\/(admin|api|login|logout|auth|captcha|relay|meta|submit)(\/|$|\.)/i.test(path)) {
return;
}
if (/\.(xml|json|m3u|pls|css|js|map|ico|png|jpe?g|gif|webp|svg|woff2?|mp3|flac|webm|mp4)(\?|$)/i.test(path)) {
return;
}
var HIT_URL = "https://www.sillylaird.ca/api/hit.php";
function pageKey() {
var p = (location.pathname || "/").replace(/\/+$/, "") || "/";
// /foo/index.php and /foo/index_jp.php → /foo
p = p.replace(/\/index(?:_[a-z]{2})?\.php$/i, "") || "/";
// /links_jp.php / links_zh.php → /links.php
p = p.replace(/_(jp|zh)\.php$/i, ".php");
return location.hostname + p;
}
function ensureStyles() {
if (document.getElementById("sl-visit-counter-style")) return;
var s = document.createElement("style");
s.id = "sl-visit-counter-style";
s.textContent =
".site-visit-counter{margin:.5rem 0 0;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;" +
"font-size:0.8rem;letter-spacing:0.08em;color:rgba(160,160,170,.9);}" +
".site-visit-counter-digits{color:#39ff14;text-shadow:0 0 6px rgba(57,255,20,.35);}" +
"body.light-mode .site-visit-counter{color:rgba(40,40,50,.75);}" +
"body.light-mode .site-visit-counter-digits{color:#1a7a12;text-shadow:none;}";
(document.head || document.documentElement).appendChild(s);
}
function ensureMarkup() {
var digits = document.getElementById("site-visit-digits");
if (digits) return digits;
ensureStyles();
var wrap = document.createElement("p");
wrap.className = "site-visit-counter";
wrap.id = "site-visit-counter";
wrap.setAttribute("aria-live", "polite");
wrap.innerHTML =
'<span class="site-visit-counter-label">You are visitor</span> ' +
'<span class="site-visit-counter-digits" id="site-visit-digits">------</span>';
var footerInner = document.querySelector(".site-footer .footer-inner, footer .footer-inner");
var footer = document.querySelector("footer.site-footer, footer");
if (footerInner) {
footerInner.appendChild(wrap);
} else if (footer) {
footer.appendChild(wrap);
} else {
wrap.style.cssText =
"position:fixed;right:10px;bottom:8px;z-index:40;margin:0;padding:4px 8px;" +
"background:rgba(0,0,0,.55);border-radius:4px;backdrop-filter:blur(4px);";
document.body.appendChild(wrap);
}
return document.getElementById("site-visit-digits");
}
function reportHit(localCount, isNew) {
var payload = {
host: location.hostname,
path: location.pathname || "/",
local_count: localCount,
unique: !!isNew,
lang: (navigator.language || "").slice(0, 16),
ref: document.referrer || "",
};
var body = JSON.stringify(payload);
try {
if (navigator.sendBeacon) {
var blob = new Blob([body], { type: "application/json" });
if (navigator.sendBeacon(HIT_URL, blob)) return;
}
} catch (_) {}
try {
fetch(HIT_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: body,
mode: "cors",
credentials: "omit",
keepalive: true,
}).catch(function () {});
} catch (_) {}
}
function count() {
var el = ensureMarkup();
if (!el) return;
var page = pageKey();
var KEY = "sillylaird_visit_count_v2:" + page;
var SESS = "sillylaird_visit_sess_v2:" + page;
var n = 0;
var isNew = false;
try {
n = parseInt(localStorage.getItem(KEY) || "0", 10) || 0;
} catch (_) {}
try {
if (!sessionStorage.getItem(SESS)) {
n += 1;
isNew = true;
localStorage.setItem(KEY, String(n));
sessionStorage.setItem(SESS, "1");
}
} catch (_) {}
el.textContent = String(n).padStart(6, "0");
// Log every page view server-side (unique flag only when new session for page).
reportHit(n, isNew);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", count);
} else {
count();
}
})();
|