/*! trentpower.fr · /verify/verify.js · authored · signed via /integrity.json */
(function () {
'use strict';
var MAP = (typeof window !== 'undefined' && window.TP_VERIFICATION_MAP) || {};
function getLang() {
var t = document.documentElement.lang || 'en';
return (typeof window.I18N === 'object' && window.I18N[t]) ? t : 'en';
}
function t(key, fallback) {
var lang = getLang();
var ref = (window.I18N && window.I18N[lang]) || {};
var parts = key.split('.');
for (var i = 0; i < parts.length; i++) {
if (ref == null || typeof ref !== 'object') return fallback || '';
ref = ref[parts[i]];
}
return (typeof ref === 'string') ? ref : (fallback || '');
}
function normalisePath(raw) {
if (!raw) return '/';
raw = String(raw).trim();
if (!raw) return '/';
if (raw.indexOf('//') !== -1) {
try {
var u = new URL(raw, location.origin);
if (u.origin !== location.origin) return null;
raw = u.pathname || '/';
} catch (_) { return null; }
}
if (raw.charAt(0) !== '/') raw = '/' + raw;
raw = raw.replace(/\/index\.html$/, '/');
if (raw.indexOf('.') === -1 && raw.charAt(raw.length - 1) !== '/') raw += '/';
return raw;
}
function el(tag, attrs, text) {
var n = document.createElement(tag);
if (attrs) for (var k in attrs) {
if (Object.prototype.hasOwnProperty.call(attrs, k)) n.setAttribute(k, attrs[k]);
}
if (text != null) n.textContent = text;
return n;
}
function safeHref(path) {
if (!path) return '#';
if (path.charAt(0) === '/') return path;
try {
var u = new URL(path, location.origin);
if (u.origin !== location.origin) return '#';
return u.pathname + (u.search || '');
} catch (_) { return '#'; }
}
function copy(text, button) {
if (!navigator.clipboard) return;
navigator.clipboard.writeText(text).then(function () {
var prev = button.textContent;
button.textContent = t('verify.action.copied', 'Copied');
button.setAttribute('data-state', 'copied');
setTimeout(function () {
button.textContent = prev;
button.removeAttribute('data-state');
}, 1400);
}, function () { /* noop */ });
}
// The "Verify locally" command blocks were removed by design: command-
// line release verification belongs on /integrity/, where the full
// signed-manifest workflow is already documented. /verify/ stays
// purely a per-page record. The Connected records strip below points
// visitors at /integrity.json and the release archive when they want
// to drop into the command-line flow.
// ─── Localised archive label ──────────────────────────────
// Mirrors cite.template.js's buildArchiveLabel , derives a human
// form from the trailing YYYY-MM in /integrity/releases/YYYY-MM/.
var LOCALE_MONTHS_VERIFY = {
en: ['January','February','March','April','May','June','July','August','September','October','November','December'],
fr: ['janvier','février','mars','avril','mai','juin','juillet','août','septembre','octobre','novembre','décembre'],
it: ['gennaio','febbraio','marzo','aprile','maggio','giugno','luglio','agosto','settembre','ottobre','novembre','dicembre'],
es: ['enero','febrero','marzo','abril','mayo','junio','julio','agosto','septiembre','octubre','noviembre','diciembre'],
de: ['Januar','Februar','März','April','Mai','Juni','Juli','August','September','Oktober','November','Dezember']
};
function archiveLabel(routePath) {
if (!routePath) return '';
var m = /(\d{4})-(\d{2})\/?$/.exec(routePath);
if (!m) return routePath;
var year = parseInt(m[1], 10), month = parseInt(m[2], 10);
if (!month || month < 1 || month > 12) return routePath;
var lang = getLang();
var months = LOCALE_MONTHS_VERIFY[lang] || LOCALE_MONTHS_VERIFY.en;
var monthName = months[month - 1];
if (lang === 'fr') return monthName.charAt(0).toUpperCase() + monthName.slice(1) + ' ' + year;
if (lang === 'it') return monthName.charAt(0).toUpperCase() + monthName.slice(1) + ' ' + year;
if (lang === 'es') return monthName.charAt(0).toUpperCase() + monthName.slice(1) + ' de ' + year;
if (lang === 'de') return monthName + ' ' + year;
return monthName + ' ' + year;
}
// ─── Section: Page record ──────────────────────────────
// Editorial dossier, not a metadata table. Each group is a small
// mono label + value: Citation (serif body, the emotional centre)
// followed by Location, Evidence, Fingerprint, Archive in mono.
// Hairlines separate groups, not rows. Actions sit at the bottom
// as quiet utility-link mono.
// ─── micro-grid row helper (phase 33 rewrite) ──────────────────────────
// emits one row of the record-grid:
//
//
LABEL
// VALUE
//
// value can be a string, a single dom node, or an array of nodes/strings
// (joined with
for the multi-line evidence row). all paths/hashes
// wrap cleanly via the css `overflow-wrap: anywhere; word-break: break-
// word` rules on `.record-grid code`.
function buildRecordRow(labelKey, labelFallback, valueNode) {
var row = el('div', { 'class': 'record-grid__row' });
row.appendChild(el('dt', null, t(labelKey, labelFallback)));
var dd = el('dd');
if (typeof valueNode === 'string') {
dd.textContent = valueNode;
} else if (Array.isArray(valueNode)) {
valueNode.forEach(function (n, i) {
if (i > 0) dd.appendChild(el('br'));
if (typeof n === 'string') dd.appendChild(document.createTextNode(n));
else dd.appendChild(n);
});
} else if (valueNode) {
dd.appendChild(valueNode);
}
row.appendChild(dd);
return row;
}
function recordLink(href, label) {
var a = el('a', { href: safeHref(href) });
a.appendChild(el('code', null, label));
return a;
}
function recordCode(text) {
return el('code', null, text);
}
function buildThisPage(record) {
// phase 33 · micro-grid record system. the card now reads as a
// signed technical certificate: header (eyebrow + title +
// status) above a definition-list of metadata rows (citation,
// location, evidence, fingerprint, archive) below a hairline
// actions strip. inspired by swiss archival records, museum
// object labels, and printed release ledgers.
// phase 51 · semantic upgrades —