PageSourceSearch

https://www.beelivery.com/js/homepage/homepage.mcjpgp14ok.js

js beelivery.com collected 2026-10-01 14:17:00 UTC 7,331 bytes, 133 lines download raw bytes

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.