1// Storefront homepage interactivity (Static SSR â no Blazor circuit). Wires the 2// postcode search, geolocate, and country selector, and starts the slider. Runs on 3// initial load and again after Blazor enhanced navigations; idempotent per element. 4(function () { 5 const H = window.beeHome; 6 7 const noDeliveryMessage = "Sorry, we don't currently deliver to this area."; 8 9 function placeholderFor(country) { 10 switch ((country || 'gb').toLowerCase()) { 11 case 'gb': return 'postcode'; 12 case 'ie': return 'eircode'; 13 default: return 'zipcode'; 14 } 15 } 16 17 async function runSearch(input, button, errorEl, geo) { 18 const postcode = (input.value || '').trim(); 19 if (!postcode && !geo) { 20 showError(errorEl, 'Please enter your ' + placeholderFor(H.getCookie('country')) + '.'); 21 return; 22 } 23 setProcessing(button, true); 24 25 const country = (H.getCookie('country') || 'gb').toLowerCase(); 26 27 // Our own same-origin /delivery/check, NOT the legacy API origin. The availability 28 // answer carries the ShopConfigIds price-tier cookie; asking the legacy origin 29 // directly makes that a SameSite=None THIRD-PARTY cookie, which browsers block by 30 // default â the tier then silently falls back to base and prices are wrong with no 31 // error. /delivery/check calls the same legacy rule server-to-server and re-issues 32 // the cookie first-party. (The v=2.0 Accept discriminator now lives on the server's 33 // upstream client, so it no longer belongs on this request.) 34 let url = '/delivery/check?countryIso2Code=' + encodeURIComponent(country); 35 url += geo ? ('&lat=' + geo.lat + '&lng=' + geo.lng) : ('&postcode=' + encodeURIComponent(postcode)); 36 37 try { 38 const res = await fetch(url, { headers: { 'Accept': 'application/json' } }); 39 const data = await res.json().catch(() => null); 40 if (data && data.locationHasBeeliveryDrivers && data.shopSlug) { 41 if (postcode) H.setCookie('Postcode', postcode, 1); 42 H.goTo(data.shopSlug); 43 return; 44 } 45 // Prefer the server's reason: "we couldn't check right now" and "we don't 46 // deliver here" are different outcomes, and only the server knows which. 47 showError(errorEl, (data && data.errorMessage) || noDeliveryMessage); 48 } catch (e) { 49 showError(errorEl, noDeliveryMessage); 50 } 51 setProcessing(button, false); 52 } 53 54 function showError(errorEl, msg) { if (errorEl) { errorEl.textContent = msg; errorEl.classList.add('show'); } } 55 function setProcessing(button, on) { 56 const label = button.querySelector('span'); 57 const spinner = button.querySelector('.processing-search'); 58 if (on) { button.setAttribute('disabled', 'disabled'); if (label) label.style.display = 'none'; if (spinner) spinner.classList.add('show'); } 59 else { button.removeAttribute('disabled'); if (label) label.style.display = ''; if (spinner) spinner.classList.remove('show'); } 60 } 61 62 function initPostcode(root) { 63 const container = root.querySelector('.postcode-search-container'); 64 const button = root.querySelector('.place-order-container button'); 65 if (!container || !button || button.dataset.beeWired) return; 66 button.dataset.beeWired = '1'; 67 68 const input = container.querySelector('input[type="text"]'); 69 const errorEl = root.querySelector('.search-error'); 70 const geoBtn = container.querySelector('.geolocate'); 71 72 // Reflect the saved postcode + country on load. 73 const savedPostcode = H.getCookie('Postcode'); 74 if (savedPostcode && input && !input.value) input.value = savedPostcode; 75 applyCountry(root, H.getCookie('country') || 'gb', false); 76 77 button.addEventListener('click', e => { e.preventDefault(); runSearch(input, button, errorEl, null); }); 78 if (input) input.addEventListener('keydown', e => { if (e.key === 'Enter') { e.preventDefault(); runSearch(input, button, errorEl, null); } }); 79 if (geoBtn) geoBtn.addEventListener('click', async () => { 80 try { const coords = await H.geolocate(); runSearch(input, button, errorEl, coords); } 81 catch (e) { showError(errorEl, 'Could not get your location. Please enter your postcode.'); } 82 }); 83 } 84 85 function applyCountry(root, code, persist) { 86 code = (code || 'gb').toLowerCase(); 87 const selected = root.querySelector('[data-country-toggle]'); 88 if (selected) selected.className = 'selected-country flag-icon flag-icon-' + code; 89 const input = root.querySelector('.postcode-search-container input[type="text"]'); 90 if (input) input.placeholder = 'Enter your ' + placeholderFor(code); 91 if (persist) H.setCookie('country', code, 1); 92 } 93 94 function initCountrySelector(root) { 95 const toggle = root.querySelector('[data-country-toggle]'); 96 const list = root.querySelector('[data-country-list]'); 97 if (!toggle || !list || toggle.dataset.beeWired) return; 98 toggle.dataset.beeWired = '1'; 99 100 toggle.addEventListener('click', e => { e.stopPropagation(); list.classList.toggle('show'); }); 101 list.querySelectorAll('[data-country]').forEach(flag => { 102 flag.addEventListener('click', () => { applyCountry(root, flag.getAttribute('data-country'), true); list.classList.remove('show'); }); 103 }); 104 document.addEventListener('click', () => list.classList.remove('show')); 105 } 106
107 function init() { 108 const root = document; 109 110 // The slider is HOMEPAGE-ONLY and must stay gated on the homepage itself, not on 111 // the postcode widget. initializeSlider installs a permanent MutationObserver on 112 // document.body when #slider is absent (slider.js:19-23, there to catch the 113 // slider arriving via enhanced navigation), so calling it on the order page would 114 // leak an observer for the life of the page. `.page-content` is the homepage's 115 // own wrapper (Home.razor:12) and appears nowhere else. 116 if (root.querySelector('.page-content') && window.initializeSlider) window.initializeSlider(); 117 118 // The postcode widget is wired wherever one is rendered â the homepage hero AND 119 // the order page's postcode gate (Components/Order/PostcodeModal.razor), which 120 // reuses this markup contract on purpose so the "do we deliver?" flow has ONE 121 // implementation. Both surfaces render at most one .place-order-container, so the 122 // document-scoped querySelectors below stay unambiguous. 123 if (!root.querySelector('.place-order-container')) return; 124 initPostcode(root); 125 initCountrySelector(root); 126 } 127 128 if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init); 129 else init(); 130 // Re-init after Blazor enhanced navigation swaps the DOM. 131 if (window.Blazor && window.Blazor.addEventListener) window.Blazor.addEventListener('enhancedload', init); 132 else document.addEventListener('DOMContentLoaded', () => { if (window.Blazor && window.Blazor.addEventListener) window.Blazor.addEventListener('enhancedload', init); }); 133})();
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.