/* ===================================================
JAESWIFT.XYZ — Scan The Visitor
Typewriter-animated visitor recon overlay.
=================================================== */
(function () {
'use strict';
const DISMISS_KEY = 'scanDismissed';
const DISMISS_DAYS = 7;
const BADGE_HIDE_KEY = 'scanBadgeHidden';
const TYPE_SPEED = 22; // ms per char
const LINE_PAUSE = 120;
// ─── Check if dismissed recently ────────────────
function isDismissed() {
try {
const raw = localStorage.getItem(DISMISS_KEY);
if (!raw) return false;
const ts = parseInt(raw, 10);
if (!ts) return false;
const ageDays = (Date.now() - ts) / (1000 * 60 * 60 * 24);
return ageDays < DISMISS_DAYS;
} catch (e) { return false; }
}
function setDismissed() {
try { localStorage.setItem(DISMISS_KEY, Date.now().toString()); } catch (e) {}
}
function isBadgeHidden() {
try { return localStorage.getItem(BADGE_HIDE_KEY) === '1'; } catch (e) { return false; }
}
// ─── Gather local client info ───────────────────
function localInfo() {
const scr = `${screen.width}x${screen.height}`;
const dpr = (window.devicePixelRatio || 1);
let conn = 'Unknown';
try {
const c = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
if (c) {
const t = c.effectiveType || c.type || '';
conn = t ? t.toUpperCase() : 'Unknown';
if (c.downlink) conn += `, ${c.downlink}Mbps`;
if (c.rtt) conn += `, ${c.rtt}ms`;
}
} catch (e) {}
return {
screen: `${scr} @ ${dpr}x`,
connection: conn,
tz: Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC',
lang: navigator.language || 'en',
};
}
// ─── Build scan lines list ──────────────────────
function buildLines(data, local) {
const locStr = data.city ? `${data.city}, ${data.country}` : data.country;
const browser = data.browser + (data.browser_version ? ' ' + data.browser_version : '');
const osStr = data.os + (data.os_version ? ' ' + data.os_version : '');
const flag = data.country_flag || '';
const threatColor = data.threat_level === 'GREEN' ? 'green'
: data.threat_level === 'AMBER' ? 'amber'
: 'red';
const bar = (() => {
if (data.threat_level === 'GREEN') return '[█░░░░] GREEN — TRUSTED';
if (data.threat_level === 'AMBER') return '[███░░] AMBER — MONITORED';
if (data.threat_level === 'RED') return '[█████] RED — BLOCKED';
return '[░░░░░] UNKNOWN';
})();
return [
{ cls: 'green', text: '>>> ESTABLISHING SECURE CONNECTION...' },
{ cls: 'green', text: '>>> HANDSHAKE OK · TLS 1.3 · AES-256-GCM' },
{ cls: 'green', text: '>>> SCANNING OPERATOR...' },
{ cls: 'dim', text: '' },
{ cls: 'green', text: `>>> IP ADDRESS....... ${data.ip_masked}` },
{ cls: 'green', text: `>>> GEOLOCATION...... ${locStr} ${flag}` },
{ cls: 'green', text: `>>> NETWORK.......... ${data.isp}` },
data.hostname ? { cls: 'dim', text: `>>> HOSTNAME......... ${data.hostname}` } : null,
{ cls: 'green', text: `>>> BROWSER.......... ${browser}` },
{ cls: 'green', text: `>>> OS............... ${osStr}` },
{ cls: 'green', text: `>>> DEVICE........... ${data.device}` },
{ cls: 'green', text: `>>> SCREEN........... ${local.screen}` },
{ cls: 'green', text: `>>> LANGUAGE......... ${local.lang}` },
{ cls: 'green', text: `>>> TIMEZONE......... ${local.tz}` },
{ cls: 'green', text: `>>> CONNECTION....... ${local.connection}` },
{ cls: 'dim', text: '' },
{ cls: threatColor, text: `>>> THREAT LEVEL..... ${bar}` },
{ cls: threatColor, text: `>>> STATUS........... ${data.threat_reason}` },
{ cls: 'dim', text: '' },
{ cls: 'green', text: '>>> ACCESS GRANTED — WELCOME, OPERATOR' },
].filter(Boolean);
}
// ─── Typewriter: types text into element ─────────
function typeInto(el, text, speed) {
return new Promise(function (resolve) {
let i = 0;
function tick() {
if (i < text.length) {
el.textContent += text.charAt(i);
i++;
setTimeout(tick, speed);
} else {
resolve();
}
}
if (!text.length) { resolve(); return; }
tick();
});
}
// ─── Build overlay DOM ───────────────────────────
function buildOverlay() {
const overlay = document.createElement('div');
overlay.className = 'scan-overlay';
overlay.id = 'scanOverlay';
overlay.innerHTML = `
`;
return overlay;
}
// ─── Build badge (collapsed state) ───────────────
function buildBadge(data) {
const flag = data.country_flag || '🌐';
const cc = data.country_code || 'XX';
const locShort = data.city ? `${data.city}, ${cc}` : (data.country || cc);
const badge = document.createElement('div');
badge.className = 'scan-badge';
badge.id = 'scanBadge';
badge.title = 'Click to re-run scan';
badge.innerHTML = `
${flag}
OPERATOR
${locShort}
`;
badge.addEventListener('click', function (e) {
if (e.target && e.target.id === 'scanBadgeClose') return;
// Re-run scan on click
try { localStorage.removeItem(DISMISS_KEY); } catch (err) {}
badge.remove();
runScan();
});
badge.querySelector('#scanBadgeClose').addEventListener('click', function (e) {
e.stopPropagation();
try { localStorage.setItem(BADGE_HIDE_KEY, '1'); } catch (err) {}
badge.classList.add('scan-closing');
setTimeout(() => badge.remove(), 220);
});
document.body.appendChild(badge);
}
// ─── Render scan lines one-by-one ────────────────
async function renderLines(body, lines) {
for (let i = 0; i < lines.length; i++) {
const item = lines[i];
const span = document.createElement('span');
span.className = `scan-line scan-line-${item.cls}`;
body.appendChild(span);
if (item.text) {
await typeInto(span, item.text, TYPE_SPEED);
body.scrollTop = body.scrollHeight;
} else {
span.textContent = '\u00A0';
}
await new Promise(r => setTimeout(r, LINE_PAUSE));
}
}
// ─── Run full scan sequence ──────────────────────
async function runScan() {
// fade in overlay first with 'CONNECTING...'
const overlay = buildOverlay();
document.body.appendChild(overlay);
const body = overlay.querySelector('#scanBody');
const statusEl = overlay.querySelector('#scanStatus');
const closeBtn = overlay.querySelector('#scanClose');
const dismissBtn = overlay.querySelector('#scanDismissBtn');
const ackBtn = overlay.querySelector('#scanAcceptBtn');
let scanData = null;
let apiFailed = false;
// Seed initial connecting line before fetch completes
const initLine = document.createElement('span');
initLine.className = 'scan-line scan-line-green';
body.appendChild(initLine);
typeInto(initLine, '>>> INITIALISING RECON PROTOCOL...', TYPE_SPEED);
function closeAll(dismiss) {
overlay.classList.add('scan-closing');
if (dismiss) setDismissed();
setTimeout(() => {
overlay.remove();
if (scanData && !isBadgeHidden()) {
buildBadge(scanData);
}
}, 260);
}
closeBtn.addEventListener('click', () => closeAll(false));
dismissBtn.addEventListener('click', () => closeAll(true));
ackBtn.addEventListener('click', () => closeAll(false));
// allow esc to close
function escHandler(e) {
if (e.key === 'Escape') {
closeAll(false);
document.removeEventListener('keydown', escHandler);
}
}
document.addEventListener('keydown', escHandler);
try {
const r = await fetch('/api/visitor/scan', { credentials: 'same-origin' });
if (r.ok) {
scanData = await r.json();
} else if (r.status === 429) {
apiFailed = true;
} else {
apiFailed = true;
}
} catch (e) {
apiFailed = true;
}
// clear init line
body.innerHTML = '';
if (apiFailed || !scanData) {
const errSpan = document.createElement('span');
errSpan.className = 'scan-line scan-line-red';
body.appendChild(errSpan);
await typeInto(errSpan, '>>> RECON SERVICE UNAVAILABLE — STANDBY', TYPE_SPEED);
statusEl.textContent = '● OFFLINE';
statusEl.style.color = 'var(--danger)';
ackBtn.disabled = false; ackBtn.style.opacity = '1'; ackBtn.style.cursor = 'pointer';
return;
}
const local = localInfo();
const lines = buildLines(scanData, local);
await renderLines(body, lines);
statusEl.textContent = '● SCAN COMPLETE';
ackBtn.disabled = false; ackBtn.style.opacity = '1'; ackBtn.style.cursor = 'pointer';
ackBtn.focus();
}
// ─── Boot ────────────────────────────────────────
function boot() {
if (isDismissed()) {
// still show compact badge if not hidden — fetch silently
if (!isBadgeHidden()) {
fetch('/api/visitor/scan', { credentials: 'same-origin' })
.then(r => r.ok ? r.json() : null)
.then(d => { if (d && d.country_code) buildBadge(d); })
.catch(() => {});
}
return;
}
// Slight delay so page renders first
setTimeout(runScan, 700);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', boot);
} else {
boot();
}
// Expose for debug / re-trigger
window.__jaeScan = {
run: runScan,
reset: function () {
try {
localStorage.removeItem(DISMISS_KEY);
localStorage.removeItem(BADGE_HIDE_KEY);
} catch (e) {}
const b = document.getElementById('scanBadge'); if (b) b.remove();
const o = document.getElementById('scanOverlay'); if (o) o.remove();
runScan();
},
};
})();