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.