The overlay markup stays per-site (each site keeps its own colours), and
this file finds it by id. `data-gate-key` names the localStorage slot, so
each site remembers its own unlock.
Optional: data-gate-input / data-gate-button / data-gate-error /
data-gate-overlay to match different markup without editing this file.
=========================================================================== */
(function () {
'use strict';
var self = document.currentScript;
if (!self) return;
var attr = function (name, fallback) {
var v = self.getAttribute(name);
return v === null ? fallback : v;
};
/* ---- the canonical PIN: DDMMYYHH -------------------------------------- */
function standardPin(now) {
var pz = function (n) { return n < 10 ? '0' + n : '' + n; };
return pz(now.getDate())
+ pz(now.getMonth() + 1)
+ String(now.getFullYear()).slice(-2)
+ pz(now.getHours());
}
var KEY = attr('data-gate-key', 'site_pin_8');
var ov = document.getElementById(attr('data-gate-overlay', 'pin-overlay'))
|| document.getElementById('gate');
var input = document.getElementById(attr('data-gate-input', 'pin-input'))
|| document.getElementById('gate-input');
var button = document.getElementById(attr('data-gate-button', 'pin-submit'))
|| document.getElementById('gate-btn');
var err = document.getElementById(attr('data-gate-error', 'pin-error'))
|| document.getElementById('gate-err');
if (!ov) return; // nothing to gate on this page
var pin = standardPin(new Date());
function unlock() {
ov.style.display = 'none';
ov.classList.add('hidden');
document.body.classList.remove('gated');
}
function store(v) { try { localStorage.setItem(KEY, v); } catch (e) {} }
function recall() { try { return localStorage.getItem(KEY); } catch (e) { return null; } }
// Already unlocked this hour? Then stay unlocked.
if (recall() === pin) { unlock(); }
else { document.body.classList.add('gated'); }
function submit() {
if (input && input.value.trim() === pin) {
store(pin);
unlock();
} else {
if (err) err.textContent = 'Invalid PIN';
if (input) { input.value = ''; input.focus(); }
}
}
if (button) button.addEventListener('click', submit);
if (input) {
// accept one press of Enter as well as a click
input.addEventListener('keydown', function (e) {
if (e.key === 'Enter') { e.preventDefault(); submit(); }
});
if (recall() !== pin) input.focus();
}
/* ---- source deterrents ------------------------------------------------
Blocks the casual "view source / inspect" route. Trivially bypassed by
anyone who disables JavaScript or uses a proxy; it is a speed bump. */
function deterrents() {
document.addEventListener('contextmenu', function (e) {
e.preventDefault(); return false;
});
document.addEventListener('keydown', function (e) {
var k = (e.key || '').toUpperCase();
if (k === 'F12' ||
(e.ctrlKey && e.shiftKey && ['I', 'J', 'C', 'K'].indexOf(k) >= 0) ||
(e.ctrlKey && k === 'U')) {
e.preventDefault(); return false;
}
}, true);
// let people select text inside form fields, but not the page at large
var inField = function (e) {
return !!(e.target && e.target.closest &&
e.target.closest('input,textarea,select'));
};
document.addEventListener('selectstart', function (e) {
if (!inField(e)) e.preventDefault();
});
document.addEventListener('copy', function (e) {
if (!inField(e)) e.preventDefault();
});
}
deterrents();
})();