(() => { const root = document.querySelector('[data-cart-shipping-calc]'); if (!root) return; const els = { zip: root.querySelector('[data-cart-shipping-zip]'), country: root.querySelector('[data-cart-shipping-country]'), province: root.querySelector('[data-cart-shipping-province]'), submit: root.querySelector('[data-cart-shipping-submit]'), status: root.querySelector('[data-cart-shipping-status]'), results: root.querySelector('[data-cart-shipping-results]'), rates: root.querySelector('[data-cart-shipping-rates]'), summary: root.querySelector('[data-cart-shipping-summary]'), estTotal: root.querySelector('[data-cart-shipping-est-total]') }; const storageKey = 'cart_shipping_calc_v1'; const moneyFormat = window.Shopify?.money_format || '${{amount}}'; function formatMoney(cents) { const amount = (cents / 100).toFixed(2); return moneyFormat.replace('{{amount}}', amount); } function setStatus(message, type = 'info') { els.status.textContent = message || ''; els.status.setAttribute('data-status', type); } async function getCart() { const res = await fetch('/cart.js'); return res.json(); } async function prepareRates(zip, country, province) { const body = new URLSearchParams({ 'shipping_address[zip]': zip, 'shipping_address[country]': country }); if (province) { body.append('shipping_address[province]', province); } const res = await fetch('/cart/prepare_shipping_rates.json', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body }); if (!res.ok) { throw new Error( 'Please check the postcode and state. You can also try clearing the state selection.' ); } } async function pollRates(zip, country, province) { const params = new URLSearchParams({ 'shipping_address[zip]': zip, 'shipping_address[country]': country }); if (province) { params.append('shipping_address[province]', province); } const start = Date.now(); while (Date.now() - start < 10000) { const res = await fetch(`/cart/async_shipping_rates.json?${params}`); const data = await res.json(); if (Array.isArray(data.shipping_rates)) { return data.shipping_rates; } await new Promise(r => setTimeout(r, 400)); } throw new Error( 'Please check the postcode and state, then try again.' ); } function renderRates(rates, cartTotal) { els.rates.innerHTML = ''; els.results.hidden = false; els.summary.hidden = true; rates.forEach((rate, index) => { const priceCents = Math.round(parseFloat(rate.price) * 100); const li = document.createElement('li'); li.innerHTML = ` `; li.querySelector('input').addEventListener('change', () => { els.estTotal.textContent = formatMoney(cartTotal + priceCents); els.summary.hidden = false; localStorage.setItem(storageKey, JSON.stringify({ zip: els.zip.value, country: els.country.value, province: els.province.value, index })); }); els.rates.appendChild(li); }); } els.submit.addEventListener('click', async () => { try { setStatus('Calculating shipping rates…'); const zip = els.zip.value.trim(); if (!zip) { setStatus('Please enter a postcode.', 'warn'); return; } const cart = await getCart(); await prepareRates(zip, els.country.value, els.province.value); const rates = await pollRates(zip, els.country.value, els.province.value); renderRates(rates, cart.total_price); setStatus(''); } catch (err) { setStatus(err.message, 'error'); els.results.hidden = true; } }); })();