window.sptb = { downloadFile: function(fileName, contentType, base64) { var link = document.createElement('a'); link.download = fileName; link.href = 'data:' + contentType + ';base64,' + base64; document.body.appendChild(link); link.click(); document.body.removeChild(link); }, setTheme: function(theme) { var t = (theme || 'System').toLowerCase(); if (t === 'system') { t = (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) ? 'dark' : 'light'; } document.documentElement.setAttribute('data-theme', t); }, scrollToBottom: function(el) { if (el) el.scrollTop = el.scrollHeight; } }; // ── Easter eggs: N spaces in a row → slow full-screen fade-in ── (function () { // threshold (consecutive spaces) → image file in wwwroot var EGGS = [ { at: 5, src: 'seb-egg.jpg' }, { at: 10, src: 'easter-egg.jpg' } ]; var count = 0; function typingInField(t) { if (!t) return false; var tag = t.tagName; return tag === 'INPUT' || tag === 'TEXTAREA' || t.isContentEditable; } function reveal(src) { var id = 'sptb-egg-' + src.replace(/[^a-z0-9]/gi, '-'); if (document.getElementById(id)) return; // this egg already showing var ov = document.createElement('div'); ov.id = id; ov.style.cssText = 'position:fixed;inset:0;z-index:99999;background-color:#000;' + 'background-image:url("' + src + '");background-size:100% 100%;' + 'background-repeat:no-repeat;background-position:center;' + 'opacity:0;transition:opacity 10s ease;cursor:pointer'; ov.addEventListener('click', function () { ov.remove(); }); document.body.appendChild(ov); void ov.offsetWidth; // force reflow so transition runs ov.style.opacity = '1'; } window.addEventListener('keydown', function (e) { var isSpace = e.code === 'Space' || e.key === ' ' || e.keyCode === 32; if (isSpace && !typingInField(e.target)) { count++; for (var i = 0; i < EGGS.length; i++) { if (count === EGGS[i].at) { reveal(EGGS[i].src); break; } } } else if (!isSpace) { count = 0; } }, true); })(); // ── Easter egg: type "maze" → fake 3-level maze → screamer at the end ── (function () { var TRIGGER = 'maze'; var buf = ''; function typingInField(t) { if (!t) return false; var g = t.tagName; return g === 'INPUT' || g === 'TEXTAREA' || t.isContentEditable; } // base maze: '#' wall, '.' path, 'P' start, 'G' goal. Solvable snake corridor. var BASE = [ '#########', '#P......#', '#######.#', '#.......#', '#.#######', '#.......#', '#######.#', '#......G#', '#########' ]; function mirrorH(g) { return g.map(function (r) { return r.split('').reverse().join(''); }); } function mirrorV(g) { return g.slice().reverse(); } var LEVELS = [BASE, mirrorH(BASE), mirrorV(BASE)]; var active = false, lvl = 0, grid = null, px = 0, py = 0, rootEl = null, keyHandler = null; function parse(g) { for (var y = 0; y < g.length; y++) for (var x = 0; x < g[y].length; x++) if (g[y][x] === 'P') { px = x; py = y; } } function cellAt(x, y) { return grid[y] ? grid[y][x] : undefined; } function render() { var cols = grid[0].length, html = ''; for (var y = 0; y < grid.length; y++) { for (var x = 0; x < grid[y].length; x++) { var c = cellAt(x, y); var bg = c === '#' ? '#222' : (c === 'G' ? '#1a7f37' : '#0b0b0b'); if (x === px && y === py) bg = '#e63946'; html += '
'; } } var board = rootEl.querySelector('.mz-board'); board.style.gridTemplateColumns = 'repeat(' + cols + ',1fr)'; board.innerHTML = html; rootEl.querySelector('.mz-lvl').textContent = 'Level ' + (lvl + 1) + ' / 3'; } function move(dx, dy) { var nx = px + dx, ny = py + dy, c = cellAt(nx, ny); if (c === undefined || c === '#') return; px = nx; py = ny; if (c === 'G') { nextLevel(); return; } render(); } function nextLevel() { lvl++; if (lvl >= LEVELS.length) { end(); screamer(); return; } grid = LEVELS[lvl].slice(); parse(grid); render(); } function start() { if (active) return; active = true; lvl = 0; grid = LEVELS[0].slice(); parse(grid); rootEl = document.createElement('div'); rootEl.id = 'sptb-maze'; rootEl.style.cssText = 'position:fixed;inset:0;z-index:99998;background:#000;display:flex;' + 'flex-direction:column;align-items:center;justify-content:center;font-family:monospace;color:#ddd;gap:12px'; rootEl.innerHTML = '
' + '
' + '
Arrows / WASD to move • Esc to quit
'; document.body.appendChild(rootEl); render(); keyHandler = function (e) { var k = e.key; if (k === 'Escape') { end(); return; } var dx = 0, dy = 0; if (k === 'ArrowUp' || k === 'w' || k === 'W') dy = -1; else if (k === 'ArrowDown' || k === 's' || k === 'S') dy = 1; else if (k === 'ArrowLeft' || k === 'a' || k === 'A') dx = -1; else if (k === 'ArrowRight' || k === 'd' || k === 'D') dx = 1; else return; e.preventDefault(); move(dx, dy); }; window.addEventListener('keydown', keyHandler, true); } function end() { active = false; if (keyHandler) { window.removeEventListener('keydown', keyHandler, true); keyHandler = null; } if (rootEl) { rootEl.remove(); rootEl = null; } } function screamer() { if (!document.getElementById('sptb-shake-css')) { var st = document.createElement('style'); st.id = 'sptb-shake-css'; st.textContent = '@keyframes sptbShake{' + '0%{transform:translate(4px,-4px) scale(1.05)}' + '25%{transform:translate(-5px,3px) scale(1.08)}' + '50%{transform:translate(3px,5px) scale(1.04)}' + '75%{transform:translate(-4px,-3px) scale(1.07)}' + '100%{transform:translate(4px,4px) scale(1.05)}}'; document.head.appendChild(st); } var ov = document.createElement('div'); ov.style.cssText = 'position:fixed;inset:0;z-index:100000;background:#000 center/cover no-repeat ' + 'url("screamer.jpg");animation:sptbShake .05s linear infinite;cursor:pointer'; document.body.appendChild(ov); scream(); ov.addEventListener('click', function () { ov.remove(); }); setTimeout(function () { if (ov.parentNode) ov.remove(); }, 2500); } function scream() { try { var AC = window.AudioContext || window.webkitAudioContext; if (!AC) return; var ac = new AC(), dur = 2.0, n = Math.floor(ac.sampleRate * dur); var b = ac.createBuffer(1, n, ac.sampleRate), d = b.getChannelData(0); for (var i = 0; i < n; i++) d[i] = Math.random() * 2 - 1; // white noise var src = ac.createBufferSource(); src.buffer = b; var ng = ac.createGain(); ng.gain.value = 0.9; var osc = ac.createOscillator(); osc.type = 'sawtooth'; osc.frequency.value = 180; var og = ac.createGain(); og.gain.value = 0.4; src.connect(ng).connect(ac.destination); osc.connect(og).connect(ac.destination); src.start(); osc.start(); src.stop(ac.currentTime + dur); osc.stop(ac.currentTime + dur); } catch (e) { /* audio blocked — image jump still fires */ } } window.addEventListener('keydown', function (e) { if (active) return; if (typingInField(e.target)) { buf = ''; return; } var k = e.key; if (!k || k.length !== 1) return; buf = (buf + k.toLowerCase()).slice(-TRIGGER.length); if (buf === TRIGGER) { buf = ''; start(); } }); })();