PageSourceSearch

https://broadbandswitch.uk/assets/js/nav-v8.js?v=20260824s

js broadbandswitch.uk collected 2026-10-04 08:38:27 UTC 6,409 bytes, 156 lines download raw bytes

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.