- site vitrine + fingerprint test + zmap + z-panel - remplacement duckdns -> riricdev.tail5ea5cd.ts.net (canonical/og/alternates/docs) - suppression du token DuckDNS (plus utilisé)
229 lines
10 KiB
JavaScript
229 lines
10 KiB
JavaScript
(function () {
|
|
var SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 16" role="presentation" focusable="false"><path d="M5 4h22L5 12h22" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></svg>';
|
|
var BRAND = '<span class="brand-mark" aria-hidden="true">' + SVG + '</span><span class="brand-name">Zektyc</span>';
|
|
|
|
var SUN = '<svg class="icon-sun" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>';
|
|
var MOON = '<svg class="icon-moon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>';
|
|
|
|
var THEME_BTN = '<button type="button" class="theme-btn" aria-label="Theme">' + MOON + SUN + '</button>';
|
|
|
|
function path() { return location.pathname; }
|
|
function lang() { return /^\/en(\/|$)/.test(path()) ? 'en' : 'fr'; }
|
|
function isHome() { var p = path(); return p === '/' || p === '/index.html' || p === '/en/' || p === '/en/index.html'; }
|
|
function isFpTest() { return /\/fingerprint-test/.test(path()); }
|
|
function isSocial() { return /\/social/.test(path()); }
|
|
|
|
function frLink() {
|
|
var p = path();
|
|
if (p === '/en/') return '/';
|
|
var m = p.match(/^(\/[^/]+)\/en\/?$/);
|
|
if (m) return m[1] + '/';
|
|
return p;
|
|
}
|
|
|
|
function enLink() {
|
|
var p = path();
|
|
if (p === '/' || p === '/index.html') return '/en/';
|
|
if (/\/en\/?$/.test(p)) return p;
|
|
return p.replace(/\/?$/, '/en/');
|
|
}
|
|
|
|
function buildHeader() {
|
|
var h = lang();
|
|
var skip = h === 'fr' ? 'Aller au contenu' : 'Skip to content';
|
|
var html = '<a class="skip-link" href="#main">' + skip + '</a>';
|
|
html += '<a class="brand" href="/">' + BRAND + '</a>';
|
|
html += '<input type="checkbox" id="nav-toggle" class="nav-toggle-input" />';
|
|
html += '<label for="nav-toggle" class="nav-burger" aria-label="Menu">';
|
|
html += '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 6h16M4 12h16M4 18h16"/></svg>';
|
|
html += '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>';
|
|
html += '</label>';
|
|
|
|
if (isSocial()) {
|
|
html += '<nav id="nav" class="site-nav" aria-label="Navigation">';
|
|
html += '<div class="lang-switch" role="group" aria-label="Langue / Language">';
|
|
html += '<button type="button" data-lang="fr" class="lang-btn' + (h === 'fr' ? ' active' : '') + '">' + (h === 'fr' ? '<strong>FR</strong>' : 'FR') + '</button>';
|
|
html += '<button type="button" data-lang="en" class="lang-btn' + (h === 'en' ? ' active' : '') + '">' + (h === 'en' ? '<strong>EN</strong>' : 'EN') + '</button>';
|
|
html += '</div>';
|
|
html += THEME_BTN;
|
|
html += '</nav>';
|
|
} else {
|
|
html += '<nav class="site-nav" aria-label="Navigation">';
|
|
|
|
html += '<a href="/blog" data-lang-path="/blog" data-i18n="nav.blog">Blog</a>';
|
|
html += '<a href="/projets" data-lang-path="/projets" data-i18n="nav.projects">Projets</a>';
|
|
html += '<a href="/equipe" data-lang-path="/equipe" data-i18n="nav.team">\u00c9quipe</a>';
|
|
if (isHome()) {
|
|
html += '<a href="#principes" data-i18n="nav.principes">Nos principes</a>';
|
|
html += '<a href="#faq" data-i18n="nav.faq">FAQ</a>';
|
|
html += '<a href="#contact" data-i18n="nav.contact">Contact</a>';
|
|
}
|
|
|
|
html += '<div class="lang-switch" role="group" aria-label="Langue / Language">';
|
|
html += '<button type="button" data-lang="fr" class="lang-btn' + (h === 'fr' ? ' active' : '') + '">' + (h === 'fr' ? '<strong>FR</strong>' : 'FR') + '</button>';
|
|
html += '<button type="button" data-lang="en" class="lang-btn' + (h === 'en' ? ' active' : '') + '">' + (h === 'en' ? '<strong>EN</strong>' : 'EN') + '</button>';
|
|
html += '</div>';
|
|
html += THEME_BTN;
|
|
html += '</nav>';
|
|
}
|
|
|
|
return '<header class="site-header">' + html + '</header>';
|
|
}
|
|
|
|
function buildFooter() {
|
|
var h = lang();
|
|
var tos = h === 'fr' ? "Conditions d\u2019utilisation" : 'Terms of Service';
|
|
var priv = h === 'fr' ? 'Confidentialit\u00e9' : 'Privacy';
|
|
var note = h === 'fr' ? 'Ce site ne collecte rien. Aucun cookie, aucun tracker.' : 'This site collects nothing. No cookies, no trackers.';
|
|
var tosHref = h === 'fr' ? '/tos' : '/en/tos';
|
|
var privHref = h === 'fr' ? '/privacy-policy' : '/en/privacy-policy';
|
|
|
|
var html = '';
|
|
if (isSocial()) {
|
|
var backText = h === 'fr' ? 'Retour au site' : 'Back to site';
|
|
html += '<p><a href="/" class="footer-back">' + backText + ' \u2190</a></p>';
|
|
}
|
|
html += '<p>Zektyc \u00b7 France</p>';
|
|
html += '<p class="footer-note">' + note + '</p>';
|
|
html += '<nav class="footer-nav" aria-label="Legal">';
|
|
html += '<a rel="me" href="https://piaille.fr/@zektyc"><img src="/icons/mastodon.svg" alt="" width="14" height="14" class="footer-icon" /> Mastodon</a>';
|
|
html += '<span class="footer-sep" aria-hidden="true">\u00b7</span>';
|
|
html += '<a href="' + tosHref + '">' + tos + '</a>';
|
|
html += '<span class="footer-sep" aria-hidden="true">\u00b7</span>';
|
|
html += '<a href="' + privHref + '">' + priv + '</a>';
|
|
html += '<span class="footer-sep" aria-hidden="true">\u00b7</span>';
|
|
html += '<a href="/docs/maxspeed">API</a>';
|
|
html += '<span class="footer-sep" aria-hidden="true">\u00b7</span>';
|
|
html += '<a href="/.well-known/security.txt">security.txt</a>';
|
|
html += '<span class="footer-sep" aria-hidden="true">\u00b7</span>';
|
|
html += '<a href="http://l5cbkqmok4rnkhbtpltmtlf3pa5i5rfw23z2mtj5tugpo3quta3b5hqd.onion">Tor</a>';
|
|
html += '</nav>';
|
|
return '<footer class="site-footer">' + html + '</footer>';
|
|
}
|
|
|
|
function inject() {
|
|
var hs = document.getElementById('h');
|
|
var fs = document.getElementById('f');
|
|
if (hs) hs.outerHTML = buildHeader();
|
|
if (fs) fs.outerHTML = buildFooter();
|
|
|
|
var nav = document.getElementById('nav-toggle');
|
|
if (nav) {
|
|
document.addEventListener('click', function (e) {
|
|
if (e.target.closest && e.target.closest('.site-nav')) nav.checked = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
function isTorBrowser() {
|
|
var dbg = {
|
|
hostname: location.hostname,
|
|
userAgent: navigator.userAgent,
|
|
hasTorSensor: !!window.TorSensor,
|
|
uaMatch: /TorBrowser|Tor\/|Tor Project/i.test(navigator.userAgent),
|
|
timezoneOffset: new Date().getTimezoneOffset(),
|
|
gmt: (new Date().getTimezoneOffset() === 0),
|
|
screenW: window.screen && window.screen.width,
|
|
screenH: window.screen && window.screen.height,
|
|
innerW: window.innerWidth,
|
|
innerH: window.innerHeight,
|
|
letterboxed: !!(window.screen &&
|
|
window.screen.width === window.innerWidth &&
|
|
window.screen.height === window.innerHeight),
|
|
pluginCount: (navigator.plugins && navigator.plugins.length) || 0,
|
|
noPlugins: !navigator.plugins || navigator.plugins.length === 0,
|
|
vendor: navigator.vendor
|
|
};
|
|
|
|
if (isOnion()) {
|
|
dbg.result = 'onion:' + location.hostname;
|
|
console.log('[TorToast] on .onion, skip', dbg);
|
|
return true;
|
|
}
|
|
|
|
if (dbg.hasTorSensor) {
|
|
dbg.result = 'TorSensor';
|
|
console.log('[TorToast] detected via TorSensor', dbg);
|
|
return true;
|
|
}
|
|
|
|
if (dbg.uaMatch) {
|
|
dbg.result = 'userAgent';
|
|
console.log('[TorToast] detected via userAgent', dbg);
|
|
return true;
|
|
}
|
|
|
|
if (dbg.gmt && dbg.letterboxed) {
|
|
dbg.result = 'gmt+letterbox';
|
|
console.log('[TorToast] detected via gmt+letterbox', dbg);
|
|
return true;
|
|
}
|
|
|
|
if (dbg.gmt && dbg.letterboxed && dbg.noPlugins) {
|
|
dbg.result = 'gmt+letterbox+noPlugins';
|
|
console.log('[TorToast] detected via gmt+letterbox+noPlugins', dbg);
|
|
return true;
|
|
}
|
|
|
|
if (dbg.vendor === '' && dbg.gmt && dbg.noPlugins) {
|
|
dbg.result = 'vendorEmpty+gmt+noPlugins';
|
|
console.log('[TorToast] detected via vendorEmpty+gmt+noPlugins', dbg);
|
|
return true;
|
|
}
|
|
|
|
if (dbg.gmt && dbg.letterboxed && dbg.vendor === '') {
|
|
dbg.result = 'gmt+letterbox+vendorEmpty';
|
|
console.log('[TorToast] detected via gmt+letterbox+vendorEmpty', dbg);
|
|
return true;
|
|
}
|
|
|
|
dbg.result = 'NOT tor';
|
|
console.log('[TorToast] not detected as Tor', dbg);
|
|
return false;
|
|
}
|
|
|
|
function isOnion() {
|
|
return location.hostname.indexOf('.onion') !== -1;
|
|
}
|
|
|
|
function showTorToast() {
|
|
if (!isTorBrowser() || isOnion()) return;
|
|
|
|
var onionBase = 'http://l5cbkqmok4rnkhbtpltmtlf3pa5i5rfw23z2mtj5tugpo3quta3b5hqd.onion';
|
|
var path = location.pathname + location.search + location.hash;
|
|
var onionUrl = onionBase + path;
|
|
|
|
var toast = document.createElement('div');
|
|
toast.className = 'tor-toast';
|
|
toast.setAttribute('role', 'alert');
|
|
|
|
var h = lang();
|
|
var msg = h === 'fr'
|
|
? 'Vous utilisez Tor. Acc\u00e9dez \u00e0 la version .onion pour plus de s\u00e9curit\u00e9.'
|
|
: 'You are using Tor. Access the .onion version for better security.';
|
|
|
|
toast.innerHTML =
|
|
'<p>' + msg + '</p>' +
|
|
'<a href="' + onionUrl + '" class="tor-toast-btn">' + (h === 'fr' ? 'Ouvrir en .onion' : 'Open .onion') + '</a>' +
|
|
'<button type="button" class="tor-toast-close" aria-label="Fermer">×</button>';
|
|
|
|
document.body.appendChild(toast);
|
|
|
|
toast.querySelector('.tor-toast-close').addEventListener('click', function () {
|
|
toast.remove();
|
|
});
|
|
|
|
setTimeout(function () { toast.remove(); }, 30000);
|
|
}
|
|
|
|
function boot() {
|
|
inject();
|
|
showTorToast();
|
|
}
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', boot);
|
|
} else {
|
|
boot();
|
|
}
|
|
})();
|