1// Lightweight interactivity for secondary pages â mobile drawer + newsletter form. 2// (Main page uses main.js which is heavier and includes feed loading.) 3(function() { 4 document.addEventListener('DOMContentLoaded', function() { 5 // Mobile drawer toggle 6 var trigger = document.getElementById('mobileTrigger'); 7 var drawer = document.getElementById('mobileDrawer'); 8 var closeBtn = document.getElementById('mobileMenuClose'); 9 var overlay = document.getElementById('mobileOverlay'); 10 11 // NOTE: the overlay state class is 'active' â that's what style.css defines 12 // (.mobile-overlay.active), and what main.js uses on the homepage. Keep the 13 // two in sync: a mismatch here leaves the scrim display:none, which also 14 // makes it unclickable and silently kills tap-outside-to-close below. 15 function openDrawer() { 16 if (drawer) drawer.classList.add('open'); 17 if (overlay) overlay.classList.add('active'); 18 document.body.style.overflow = 'hidden'; 19 } 20 function closeDrawer() { 21 if (drawer) drawer.classList.remove('open'); 22 if (overlay) overlay.classList.remove('active'); 23 document.body.style.overflow = ''; 24 } 25 if (trigger) trigger.addEventListener('click', openDrawer); 26 if (closeBtn) closeBtn.addEventListener('click', closeDrawer); 27 if (overlay) overlay.addEventListener('click', closeDrawer); 28 // Escape closes the drawer, matching main.js on the homepage. Without this 29 // there is no keyboard route out of the drawer once it's open. 30 document.addEventListener('keydown', function(e) { 31 if (e.key === 'Escape') closeDrawer(); 32 }); 33 }); 34 35 // Newsletter form. This MUST post for real: every secondary page (about, 36 // whats-new, privacy-policy, newsletter-signup) loads chrome.js and NOT 37 // main.js, so this is the only handler behind their header/drawer forms. 38 // It was previously a placeholder that flipped the button to 'SUBSCRIBED' 39 // and reset the form without sending anything, so those pages silently 40 // discarded every signup while telling the reader it had worked. 41 // Mirrors main.js's handler â keep the two in sync. 42 var SUBSCRIBE = 'https://offthepressdashboard.com/subscribe'; 43 44 window.handleNewsletter = function(e) { 45 e.preventDefault(); 46 var form = e.target; 47 var input = form.querySelector('input[type="email"]'); 48 var email = ((input && input.value) || '').trim(); 49 var btn = form.querySelector('button[type="submit"]') || form.querySelector('button'); 50 if (!btn) return; 51 if (!email) { if (input) input.focus(); return; } 52 53 var orig = btn.textContent; 54 btn.textContent = 'SUBSCRIBING...'; 55 btn.disabled = true; 56 function done(msg, ok) { 57 btn.textContent = msg; 58 btn.style.background = ok ? '#2a7a3b' : '#a3282b'; 59 if (ok && form.reset) form.reset(); 60 setTimeout(function() { 61 btn.textContent = orig; 62 btn.style.background = ''; 63 btn.disabled = false; 64 }, 3000); 65 } 66 // Form-encoded body keeps this a CORS-simple request (no preflight); the 67 // relay still returns JSON {ok:...} we can read when the origin is allowed. 68 fetch(SUBSCRIBE, { 69 method: 'POST', 70 headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 71 body: 'email=' + encodeURIComponent(email) 72 }) 73 .then(function(r) { return r.json().catch(function() { return { ok: r.ok }; }); }) 74 .then(function(d) { done((d && d.ok) ? 'â SUBSCRIBED!' : 'TRY AGAIN', !!(d && d.ok)); }) 75 .catch(function() { done('TRY AGAIN', false); }); 76 }; 77})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.