PageSourceSearch

https://broadbandswitch.uk/assets/bsw-postcode.js?v=61e92e4d

js broadbandswitch.uk collected 2026-10-04 08:38:41 UTC 7,519 bytes, 179 lines download raw bytes

1/* BSW header postcode search.  Vanilla, no dependencies, no network calls.
2   The form is a real GET form, so it still works if this script never loads. */
3(function(){
4  if(window.__bswPcInit){ return; } window.__bswPcInit = 1;
5  var UK = /^(GIR ?0AA|[A-PR-UWYZ][A-HK-Y]?[0-9][0-9A-HJKPS-UW]? ?[0-9][ABD-HJLNP-UW-Z]{2})$/i;
6  function tidy(v){
7    v = (v||'').toUpperCase().replace(/[^A-Z0-9]/g,'');
8    return v.length > 3 ? v.slice(0, v.length-3) + ' ' + v.slice(-3) : v;
9  }
10  function wire(f, n){
11    if(f.__bswPc){ return; } f.__bswPc = 1;
12    var i = f.querySelector('.bsw-pc__input');
13    var e = f.querySelector('.bsw-pc__err');
14    if(!i){ return; }
15    if(e && !e.id){ e.id = 'bsw-pc-err-' + n; i.setAttribute('aria-describedby', e.id); }
16    function err(m){
17      if(!e){ return; }
18      if(m){ e.textContent = m; e.hidden = false; i.setAttribute('aria-invalid','true'); }
19      else { e.hidden = true; e.textContent = ''; i.removeAttribute('aria-invalid'); }
20    }
21    i.addEventListener('input', function(){ err(''); });
22    f.addEventListener('submit', function(ev){
23      var v = tidy(i.value);
24      if(!v){ ev.preventDefault(); err('Enter your postcode'); i.focus(); return; }
25      if(!UK.test(v)){ ev.preventDefault(); err('Try the format SW1A 1AA'); i.focus(); return; }
26      i.value = v;
27    });
28  }
29  /* If the header cannot fit the control on one line, wrap ancestors until it does.
30     Stops at header/body so unrelated page layout is not touched. */
31  function fit(){
32    var forms = document.querySelectorAll('form.bsw-pc');
33    for(var n = 0; n < forms.length; n++){
34      var f = forms[n], node = f.parentElement, chain = [], hops = 0;
35      while(node && hops < 5 && node.tagName !== 'BODY' && node.tagName !== 'HTML'){
36        chain.push(node);
37        if(node.tagName === 'HEADER') break;
38        node = node.parentElement;
39        hops++;
40      }
41      for(var i = 0; i < chain.length; i++) chain[i].classList.remove('bsw-pc-wrap');
42      var r = f.getBoundingClientRect();
43      var vw = document.documentElement.clientWidth;
44      if(r.width === 0 && r.height === 0){ continue; }
45      for(var j = 0; j < chain.length; j++){
46        if(r.right <= vw + 1 && r.left >= -1) break;
47        chain[j].classList.add('bsw-pc-wrap');
48        r = f.getBoundingClientRect();
49      }
50    }
51  }
52  var t = null;
53  function onResize(){ clearTimeout(t); t = setTimeout(fit, 120); }
54
55  function init(){
56    var forms = document.querySelectorAll('form.bsw-pc');
57    for(var n = 0; n < forms.length; n++){ wire(forms[n], n); }
58    fit();
59    window.addEventListener('resize', onResize);
60    if(document.fonts && document.fonts.ready && document.fonts.ready.then){ document.fonts.ready.then(fit); }
61  }
62  if(document.readyState === 'loading'){ document.addEventListener('DOMContentLoaded', init); }
63  else { init(); }
64})();
65
66/* BSW: read ?postcode= on /compare/ and start the search.
67   Does nothing at all unless the parameter is present and is a valid UK postcode.
68   Never writes the value into the DOM as HTML.  Never throws. */
69(function(){
70  if(window.__bswPcRecv){ return; } window.__bswPcRecv = 1;
71  try{
72    if(location.pathname.toLowerCase().indexOf('compare') === -1){ return; }
73    var q = new URLSearchParams(location.search).get('postcode');
74    if(!q){ return; }
75    var v = q.toUpperCase().replace(/[^A-Z0-9]/g,'');
76    v = v.length > 3 ? v.slice(0, v.length-3) + ' ' + v.slice(-3) : v;
77    var UK = /^(GIR ?0AA|[A-PR-UWYZ][A-HK-Y]?[0-9][0-9A-HJKPS-UW]? ?[0-9][ABD-HJLNP-UW-Z]{2})$/i;
78    if(!UK.test(v)){
79      try{
80        var u = new URL(location.href);
81        u.searchParams.delete('postcode');
82        var s = u.searchParams.toString();
83        history.replaceState({}, '', u.pathname + (s ? '?' + s : '') + u.hash);
84      }catch(e2){}
85      return;
86    }
87
88    var SEL = 'input[name="postcode"],input#postcode,input[autocomplete="postal-code"],'
89            + 'input[name*="postcode" i],input[id*="postcode" i],input[placeholder*="postcode" i]';
90    var tries = 0, done = false;
91
92    function setValue(el, val){
93      var proto = (el instanceof HTMLTextAreaElement) ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
94      var d = Object.getOwnPropertyDescriptor(proto, 'value');
95      if(d && d.set){ d.set.call(el, val); } else { el.value = val; }
96      el.dispatchEvent(new Event('input', {bubbles:true}));
97      el.dispatchEvent(new Event('change', {bubbles:true}));
98    }
99
100    function pick(){
101      var all = document.querySelectorAll(SEL);
102      for(var n = 0; n < all.length; n++){
103        var el = all[n];
104        if(el.closest('form.bsw-pc')){ continue; }   /* never target the header form itself */
105        if(el.type === 'hidden' || el.disabled){ continue; }
106        return el;
107      }
108      return null;
109    }
110
111    function go(){
112      if(done){ return true; }
113      var el = pick();
114      if(!el){ return false; }
115      done = true;
116      setValue(el, v);
117      var form = el.form || el.closest('form');
118      var btn = form && form.querySelector('button[type="submit"],input[type="submit"]');
119      if(!btn){
120        var scope = el.closest('section,form,div,main,body') || document.body;
121        var WORDS = /(deal|compare|search|find|check|see my|go)/i;
122        var cands = scope.querySelectorAll('button,input[type="button"],a[role="button"]');
123        for(var k = 0; k < cands.length; k++){
124          var t = (cands[k].textContent || cands[k].value || '').trim();
125          if(t && WORDS.test(t)){ btn = cands[k]; break; }
126        }
127      }
128      setTimeout(function(){
129        /* If the box lives in a GET form we are already on /compare/?postcode=, submitting
130           it navigates again.  SPA fixtures have a button and no form, so they still click. */
131        if(form){ return; }
132        if(btn){ btn.click(); }
133        else {
134          el.dispatchEvent(new KeyboardEvent('keydown', {key:'Enter', keyCode:13, which:13, bubbles:true}));
135          el.dispatchEvent(new KeyboardEvent('keyup',   {key:'Enter', keyCode:13, which:13, bubbles:true}));
136        }
137      }, 120);
138      return true;
139    }
140
141    function poll(){
142      if(go()){ return; }
143      if(++tries > 40){ return; }   /* give up quietly after about six seconds */
144      setTimeout(poll, 150);
145    }
146    if(document.readyState === 'loading'){ document.addEventListener('DOMContentLoaded', poll); }
147    else { poll(); }
148  }catch(e){ /* never break the page */ }
149})();
150
151/* Homepage ?postcode= prefill for directory forms.  No auto-submit. */
152(function(){
153  try{
154    var path = (location.pathname || '/').replace(/index\.html$/i, '');
155    if(path !== '/' && path !== ''){ return; }
156    var raw = new URLSearchParams(location.search).get('postcode');
157    if(!raw){ return; }
158    var v = String(raw).trim().toUpperCase().replace(/[^A-Z0-9 ]/g, '').slice(0, 8);
159    if(!v){ return; }
160    var tries = 0;
161    function fill(){
162      var el = document.getElementById('bswPc');
163      if(!el){
164        el = document.querySelector('#bswForm input[name="postcode"], .bsw-hero input[name="postcode"]');
165      }
166      if(!el){
167        if(++tries < 40){ setTimeout(fill, 50); }
168        return;
169      }
170      var proto = HTMLInputElement.prototype;
171      var d = Object.getOwnPropertyDescriptor(proto, 'value');
172      if(d && d.set){ d.set.call(el, v); } else { el.value = v; }
173      el.dispatchEvent(new Event('input', {bubbles:true}));
174      el.focus();
175    }
176    if(document.readyState === 'loading'){ document.addEventListener('DOMContentLoaded', fill); }
177    else { fill(); }
178  }catch(e){}
179})();

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.