1'use strict'; 2(function () { 3 function boot() { 4 if (window.__bbsNavV8Abort) window.__bbsNavV8Abort.abort(); 5 window.__bbsNavV8Abort = new AbortController(); 6 const navSig = window.__bbsNavV8Abort.signal; 7 8 if (window.lucide && typeof window.lucide.createIcons === 'function') { 9 window.lucide.createIcons(); 10 } 11 12 /* SITE SEARCH is owned by /assets/js/site-search.js (24 Aug 2026 rebuild). 13 One Esc handler only. Stickee pages under /broadband-deals/ are not 14 edited; this loader injects the overlay assets when they are missing. */ 15 (function ensureBsuSiteSearch() { 16 var st = document.getElementById('searchTrigger'); 17 if (st && !st.hasAttribute('data-site-search-open')) { 18 st.setAttribute('data-site-search-open', ''); 19 } 20 if (!document.querySelector('link[href*="site-search.css"]')) { 21 var l = document.createElement('link'); 22 l.rel = 'stylesheet'; 23 l.href = '/assets/css/site-search.css'; 24 document.head.appendChild(l); 25 } 26 if (!document.querySelector('script[src*="site-search.js"]')) { 27 var s = document.createElement('script'); 28 s.src = '/assets/js/site-search.js'; 29 document.body.appendChild(s); 30 } 31 })(); 32 33 /* ======================================================= 34 POSTCODE FORMS â Stickee widget on /compare/ 35 Pattern: all nav/footer postcode forms GET-submit to 36 https://broadbandswitch.uk/compare/?postcode=XXX&src=YYY. 37 The compare page's JS (see cursor-prompt.md) reads the 38 URL parameter and auto-fires the Stickee widget. 39 ======================================================= */ 40 const COMPARE_URL = '/compare/'; 41 // UK postcode regex: matches formats like SW1A 1AA, M1 1AA, B33 8TH, CR2 6XH, DN55 1PT, W1A 0AX 42 const UK_POSTCODE_RE = /^[A-Z]{1,2}[0-9][A-Z0-9]? ?[0-9][A-Z]{2}$/i; 43 44 function sanitisePostcode(raw) { 45 if (!raw) return ''; 46 const cleaned = raw.trim().toUpperCase().replace(/\s+/g, ' '); 47 return cleaned; 48 } 49 function isValidPostcode(pc) { 50 return UK_POSTCODE_RE.test(pc.replace(/\s+/g, '')); 51 } 52 function formatPostcode(pc) { 53 // Normalise to outward + space + inward (e.g. "SW1A 1AA") 54 const stripped = pc.replace(/\s+/g, '').toUpperCase(); 55 if (stripped.length < 5) return stripped; 56 return stripped.slice(0, stripped.length - 3) + ' ' + stripped.slice(-3); 57 } 58 59 function flashError(input) { 60 const existingTooltip = input.parentElement.querySelector('.postcode-error'); 61 if (existingTooltip) return; 62 const tip = document.createElement('span'); 63 tip.className = 'postcode-error'; 64 tip.textContent = 'Enter a valid UK postcode'; 65 input.parentElement.style.position = input.parentElement.style.position || 'relative'; 66 input.parentElement.appendChild(tip); 67 input.style.borderColor = '#FF4D8F'; 68 input.animate( 69 [{ transform: 'translateX(0)' }, { transform: 'translateX(-4px)' }, { transform: 'translateX(4px)' }, { transform: 'translateX(0)' }], 70 { duration: 260, easing: 'ease-in-out' } 71 ); 72 setTimeout(() => { 73 tip.remove(); 74 input.style.borderColor = ''; 75 }, 2400); 76 } 77 78 document.querySelectorAll('.postcode-form').forEach(form => { 79 form.addEventListener('submit', e => { 80 e.preventDefault(); 81 const input = form.querySelector('input[name="postcode"]'); 82 if (!input) return; 83 const raw = input.value; 84 const clean = sanitisePostcode(raw); 85 if (!isValidPostcode(clean)) { 86 flashError(input); 87 input.focus(); 88 return; 89 } 90 const formatted = formatPostcode(clean); 91 const src = form.dataset.src || 'nav'; 92 const url = `${COMPARE_URL}?postcode=${encodeURIComponent(formatted)}&src=${encodeURIComponent(src)}`; 93 // PII-safe analytics (outward code only) 94 const outward = (formatted.split(/\s+/)[0] || '').toUpperCase(); 95 if (typeof window.gtag === 'function') { 96 window.gtag('event', 'postcode_submit', { postcode_outward: outward, source: src }); 97 } 98 if (window.dataLayer) { 99 window.dataLayer.push({ event: 'postcode_submit', postcode_outward: outward, source: src }); 100 } 101 window.location.href = url; 102 }, { signal: navSig }); 103 104 const inputPc = form.querySelector('input[name="postcode"]'); 105 if (inputPc) { 106 inputPc.addEventListener('input', (e) => { 107 const pos = e.target.selectionStart; 108 e.target.value = e.target.value.toUpperCase(); 109 e.target.setSelectionRange(pos, pos); 110 }, { signal: navSig }); 111 } 112 }); 113 114 /* ======================================================= 115 MOBILE DRAWER 116 ======================================================= */ 117 const mobileDrawer = document.getElementById('mobileDrawer'); 118 const mobileTrigger = document.querySelector('.mobile-trigger'); 119 const mobileBackdrop = document.getElementById('mobileBackdrop'); 120 const mobileClose = document.getElementById('mobileClose'); 121 122 function openMobile() { 123 if (!mobileDrawer) return; 124 document.body.classList.add('mobile-open'); 125 mobileDrawer.setAttribute('aria-hidden', 'false'); 126 } 127 function closeMobile() { 128 if (!mobileDrawer) return; 129 document.body.classList.remove('mobile-open'); 130 mobileDrawer.setAttribute('aria-hidden', 'true'); 131 // Collapse any open sections 132 document.querySelectorAll('.mobile-section.is-open').forEach(s => s.classList.remove('is-open')); 133 } 134 if (mobileTrigger) mobileTrigger.addEventListener('click', openM
134obile, { signal: navSig }); 135 if (mobileBackdrop) mobileBackdrop.addEventListener('click', closeMobile, { signal: navSig }); 136 if (mobileClose) mobileClose.addEventListener('click', closeMobile, { signal: navSig }); 137 138 document.querySelectorAll('.mobile-section-head').forEach(head => { 139 head.addEventListener('click', () => { 140 const section = head.parentElement; 141 const isOpen = section.classList.contains('is-open'); 142 document.querySelectorAll('.mobile-section.is-open').forEach(s => s.classList.remove('is-open')); 143 if (!isOpen) section.classList.add('is-open'); 144 }, { signal: navSig }); 145 }); 146 147 document.addEventListener('keydown', e => { 148 if (e.key === 'Escape' && document.body.classList.contains('mobile-open')) closeMobile(); 149 }, { signal: navSig }); 150 151 } 152 153 window.BBSNavV8Boot = boot; 154 if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot); 155 else boot(); 156})();
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.