1/** 2 * Nexus Client â Public Subscribe Form 3 * 4 * Intercepts submissions for any `.tmb-subscribe-form` on the page, validates 5 * email, POSTs to /tmb-client/v1/tracking/subscribe, and surfaces success / 6 * error state via `.tmb-subscribe-status`. Also emits tracking events through 7 * the shared TMBClientBeacon. 8 */ 9(function () { 10 'use strict'; 11 12 if (typeof document === 'undefined') return; 13 14 var cfg = window.TMBClientBeacon || {}; 15 var i18n = cfg.i18n || {}; 16 var SUBSCRIBE_URL = (cfg.restRoot || '').replace(/\/+$/, '') + '/tracking/subscribe'; 17 var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/; 18 19 function setStatus($status, text, state) { 20 if (!$status) return; 21 $status.textContent = text || ''; 22 $status.classList.remove('is-success', 'is-error'); 23 if (state) $status.classList.add('is-' + state); 24 } 25 26 function trackSafe(event, ctx, metrics) { 27 if (typeof cfg.track === 'function') { 28 try { cfg.track(event, ctx || {}, metrics || {}); } catch (e) {} 29 } 30 } 31 32 function handleSubmit(e) { 33 var form = e.target; 34 if (!form || !form.classList || !form.classList.contains('tmb-subscribe-form')) return; 35 e.preventDefault(); 36 37 var emailInput = form.querySelector('input[name="email"]'); 38 var zipInput = form.querySelector('input[name="zip"]'); 39 var sourceInput = form.querySelector('input[name="source"]'); 40 var submitBtn = form.querySelector('button[type="submit"]'); 41 var status = form.querySelector('.tmb-subscribe-status'); 42 43 var email = emailInput ? String(emailInput.value || '').trim() : ''; 44 var zip = zipInput ? String(zipInput.value || '').trim() : ''; 45 var source = sourceInput? String(sourceInput.value|| '').trim() : (form.getAttribute('data-source') || 'inline'); 46 47 if (!email || !EMAIL_RE.test(email)) { 48 setStatus(status, i18n.emailInvalid || 'Please enter a valid email address.', 'error'); 49 if (emailInput) { 50 emailInput.setAttribute('aria-invalid', 'true'); 51 emailInput.focus(); 52 } 53 trackSafe('subscription.invalid', { reason: 'invalid_email' }); 54 return; 55 } 56 if (emailInput) emailInput.removeAttribute('aria-invalid'); 57 58 if (submitBtn) submitBtn.disabled = true; 59 setStatus(status, 'â¦', null); 60 61 trackSafe('subscription.attempt', { source: source, has_zip: !!zip }); 62 63 fetch(SUBSCRIBE_URL, { 64 method: 'POST', 65 credentials: 'same-origin', 66 headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, 67 body: JSON.stringify({ 68 email: email, 69 zip: zip, 70 source: source, 71 consent: cfg.consent || 'unknown' 72 }) 73 }).then(function (res) { 74 return res.json().then(function (data) { 75 return { ok: res.ok, data: data }; 76 }).catch(function () { 77 return { ok: res.ok, data: null }; 78 }); 79 }).then(function (out) { 80 if (out.ok && out.data && out.data.success !== false) { 81 setStatus(status, i18n.subscribeOk || 'You\'re subscribed.', 'success'); 82 trackSafe('subscription.success', { source: source }); 83 form.reset(); 84 } else { 85 setStatus(status, i18n.subscribeErr || 'Something went wrong. Try again.', 'error'); 86 trackSafe('subscription.failed', { source: source }); 87 } 88 }).catch(function () { 89 setStatus(status, i18n.subscribeErr || 'Something went wrong. Try again.', 'error'); 90 trackSafe('subscription.failed', { source: source, error: 'network' }); 91 }).then(function () { 92 if (submitBtn) submitBtn.disabled = false; 93 }); 94 } 95 96 function bind() { 97 // Delegate so dynamically-injected forms also work. 98 document.addEventListener('submit', handleSubmit, true); 99 } 100 101 if (document.readyState === 'loading') { 102 document.addEventListener('DOMContentLoaded', bind, { once: true }); 103 } else { 104 bind(); 105 } 106})();
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.