'use strict'; const Pager = (() => { const SCREEN_WIDTH = 480; const SCREEN_HEIGHT = 222; const FB_STRIDE = SCREEN_WIDTH * 4; const PAGER_WIDTH = 745; const KEY_MAP = { 'LEFT.png': 'ArrowLeft', 'UP.png': 'ArrowUp', 'RIGHT.png': 'ArrowRight', 'DOWN.png': 'ArrowDown', 'A_Button.png': 'Enter', 'B_Button.png': 'Escape' }; let panel = null; let bodyEl = null; let table = null; let pager = null; let canvas = null; let screenerr = null; let keyws = null; let screenws = null; function ensure() { if (table) return; panel = document.getElementById('pager-panel'); bodyEl = document.getElementById('pager-body'); table = document.getElementById('pager_ui'); pager = document.getElementById('pager'); canvas = document.getElementById('pager_canvas'); screenerr = document.getElementById('pager_error'); table.querySelectorAll('img.pager-btn').forEach((img) => { const src = img.getAttribute('src').split('/').pop(); const key = KEY_MAP[src]; if (!key) return; img.addEventListener('click', () => press(img, key)); }); const retry = document.getElementById('screen_retry'); if (retry) retry.addEventListener('click', () => connect()); } function press(el, key) { el.classList.add('pressed'); setTimeout(() => el.classList.remove('pressed'), 80); sendKey(key); } function sendKey(k) { if (keyws && keyws.readyState === WebSocket.OPEN) keyws.send(k); } function renderRGBAFrame(bytes) { if (bytes.length < FB_STRIDE * SCREEN_HEIGHT) { console.warn('pager frame too small for 480x222', bytes.length); return; } const ctx = canvas.getContext('2d'); const imageData = ctx.createImageData(SCREEN_WIDTH, SCREEN_HEIGHT); const dst = imageData.data; let di = 0; for (let y = 0; y < SCREEN_HEIGHT; y++) { const rowStart = y * FB_STRIDE; for (let x = 0; x < SCREEN_WIDTH; x++) { const si = rowStart + x * 4; dst[di] = bytes[si]; dst[di + 1] = bytes[si + 1]; dst[di + 2] = bytes[si + 2]; dst[di + 3] = bytes[si + 3]; di += 4; } } ctx.putImageData(imageData, 0, 0); pager.src = canvas.toDataURL('image/png'); } function connect() { disconnect(); try { const sock = new WebSocket(App.pagerScreenWs); sock.binaryType = 'arraybuffer'; screenws = sock; sock.onopen = () => { screenerr.hidden = true; }; sock.onmessage = (ev) => { if (ev.data instanceof ArrayBuffer) renderRGBAFrame(new Uint8Array(ev.data)); else if (ev.data && ev.data.arrayBuffer) ev.data.arrayBuffer().then((b) => renderRGBAFrame(new Uint8Array(b))); }; sock.onerror = () => { screenerr.hidden = false; }; sock.onclose = () => { if (screenws === sock) screenws = null; screenerr.hidden = false; }; } catch (e) { screenerr.hidden = false; } try { const sock = new WebSocket(App.pagerKeysWs); keyws = sock; sock.onclose = () => { if (keyws === sock) keyws = null; }; sock.onerror = () => { try { sock.close(); } catch (e2) {} }; } catch (e) { keyws = null; } } function disconnect() { if (screenws) { try { screenws.close(); } catch (e) {} screenws = null; } if (keyws) { try { keyws.close(); } catch (e) {} keyws = null; } } function applyScale() { if (!bodyEl || !table) return; const availableWidth = bodyEl.clientWidth - 20; const scale = Math.min(1, availableWidth / PAGER_WIDTH); table.style.zoom = String(scale); table.style.marginBottom = ''; } function toggle() { ensure(); if (panel.classList.contains('hidden')) { panel.classList.remove('hidden'); document.getElementById('pager-btn').classList.add('active'); applyScale(); connect(); try { pager.focus(); } catch (e) {} } else { panel.classList.add('hidden'); document.getElementById('pager-btn').classList.remove('active'); disconnect(); } } function isOpen() { return !!panel && !panel.classList.contains('hidden'); } function close() { if (isOpen()) toggle(); } window.addEventListener('resize', () => { if (table && panel && !panel.classList.contains('hidden')) applyScale(); }); document.addEventListener('keydown', (e) => { if (!isOpen()) return; const t = e.target; if (t && (t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' || t.tagName === 'SELECT' || t.tagName === 'BUTTON' || t.tagName === 'A' || t.isContentEditable)) return; if (e.ctrlKey || e.metaKey || e.altKey) return; const keys = ['ArrowLeft', 'ArrowUp', 'ArrowRight', 'ArrowDown', 'Enter', 'Escape']; if (keys.indexOf(e.key) === -1) return; if (e.key.indexOf('Arrow') === 0) e.preventDefault(); if (!e.repeat) sendKey(e.key); }); return { toggle, close, isOpen }; })();