1(function () { 2 "use strict"; 3 4 var PRICE = 30.00; 5 var CART_KEY = "ubfShopCart"; 6 7 // The checkout Worker prices the cart server-side and creates a Stripe 8 // Checkout Session. Same-origin path in production; the Worker is routed at 9 // /api/* by Cloudflare. For local testing with `wrangler dev`, point this at 10 // the wrangler URL (e.g. "http://127.0.0.1:8787/api/checkout"). 11 var CHECKOUT_ENDPOINT = "/api/checkout"; 12 13 function getCart() { 14 try { 15 return JSON.parse(localStorage.getItem(CART_KEY)) || []; 16 } catch (e) { 17 return []; 18 } 19 } 20 21 function saveCart(cart) { 22 localStorage.setItem(CART_KEY, JSON.stringify(cart)); 23 } 24 25 function formatMoney(amount) { 26 return amount.toFixed(2); 27 } 28 29 function cartTotal(cart) { 30 return cart.reduce(function (sum, item) { 31 return sum + item.qty * PRICE; 32 }, 0); 33 } 34 35 function cartItemCount(cart) { 36 return cart.reduce(function (sum, item) { 37 return sum + item.qty; 38 }, 0); 39 } 40 41 function addToCart(size, qty) { 42 var cart = getCart(); 43 var existing = null; 44 45 for (var i = 0; i < cart.length; i++) { 46 if (cart[i].size === size) { 47 existing = cart[i]; 48 break; 49 } 50 } 51 52 if (existing) { 53 existing.qty += qty; 54 } else { 55 cart.push({ size: size, qty: qty }); 56 } 57 58 saveCart(cart); 59 renderCart(); 60 } 61 62 function removeFromCart(index) { 63 var cart = getCart(); 64 cart.splice(index, 1); 65 saveCart(cart); 66 renderCart(); 67 } 68 69 function buildOrderSummary(cart, total) { 70 var lines = cart.map(function (item) { 71 return "- " + item.qty + " x " + item.size + " ($" + formatMoney(item.qty * PRICE) + ")"; 72 }); 73 lines.push(""); 74 lines.push("Total: $" + formatMoney(total)); 75 return "Utah Brazilian Festival T-Shirt Order\n" + lines.join("\n"); 76 } 77 78 function setStripeButtonBusy(btn, busy) { 79 if (!btn) return; 80 if (busy) { 81 btn.setAttribute("aria-disabled", "true"); 82 btn.classList.add("shop-btn-disabled"); 83 if (!btn.getAttribute("data-label")) { 84 btn.setAttribute("data-label", btn.textContent); 85 } 86 btn.textContent = "Starting checkoutâ¦"; 87 } else { 88 btn.removeAttribute("aria-disabled"); 89 btn.classList.remove("shop-btn-disabled"); 90 var label = btn.getAttribute("data-label"); 91 if (label) btn.textContent = label; 92 } 93 } 94 95 // Send the cart to the checkout Worker, which prices it server-side and 96 // returns a Stripe Checkout Session URL to redirect the customer to. 97 function startCheckout(btn) { 98 var cart = getCart(); 99 if (cart.length === 0) { 100 window.alert("Your cart is empty. Please add a t-shirt before paying."); 101 return; 102 } 103 104 var nameEl = document.getElementById("shop-name"); 105 var emailEl = document.getElementById("shop-email"); 106 var name = nameEl ? nameEl.value.trim() : ""; 107 var email = emailEl ? emailEl.value.trim() : ""; 108 109 setStripeButtonBusy(btn, true); 110 111 fetch(CHECKOUT_ENDPOINT, { 112 method: "POST", 113 headers: { "Content-Type": "application/json" }, 114 body: JSON.stringify({ cart: cart, name: name, email: email }) 115 }) 116 .then(function (resp) { 117 return resp.json().then(function (data) { 118 return { ok: resp.ok, data: data }; 119 }); 120 }) 121 .then(function (result) { 122 if (result.ok && result.data && result.data.url) { 123 window.location.href = result.data.url; 124 } else { 125 var msg = (result.data && result.data.error) || "Sorry, we couldn't start checkout. Please try again."; 126 window.alert(msg); 127 setStripeButtonBusy(btn, false); 128 } 129 }) 130 .catch(function () { 131 window.alert("Sorry, we couldn't reach checkout. Please check your connection and try again."); 132 setStripeButtonBusy(btn, false); 133 }); 134 } 135 136 function handleStripeClick() { 137 return function (e) { 138 e.preventDefault(); 139 if (e.currentTarget.getAttribute("aria-disabled") === "true" && getCart().length === 0) { 140 window.alert("Your cart is empty. Please add a t-shirt before paying."); 141 return; 142 } 143 startCheckout(e.currentTarget); 144 }; 145 } 146 147 function renderCart() { 148 var itemsEl = document.getElementById("cart-items"); 149 if (!itemsEl) { 150 return; 151 } 152 153 var cart = getCart(); 154 var emptyEl = document.getElementById("cart-empty-message"); 155 var summaryEl = document.getElementById("cart-summary"); 156 var totalEl = document.getElementById("cart-total"); 157 var countEl = document.getElementById("cart-item-count"); 158 var checkoutTotalEl = document.getElementById("checkout-total"); 159 var messageEl = document.getElementById("shop-message"); 160 var stripeBtn = document.getElementById("pay-stripe"); 161 162 itemsEl.innerHTML = ""; 163 164 if (cart.length === 0) { 165 if (emptyEl) emptyEl.hidden = false;
166 itemsEl.hidden = true; 167 if (summaryEl) summaryEl.hidden = true; 168 169 if (stripeBtn) { 170 stripeBtn.classList.add("shop-btn-disabled"); 171 stripeBtn.setAttribute("aria-disabled", "true"); 172 } 173 } else { 174 if (emptyEl) emptyEl.hidden = true; 175 itemsEl.hidden = false; 176 if (summaryEl) summaryEl.hidden = false; 177 178 if (stripeBtn) { 179 stripeBtn.classList.remove("shop-btn-disabled"); 180 stripeBtn.removeAttribute("aria-disabled"); 181 } 182 183 cart.forEach(function (item, index) { 184 var row = document.createElement("div"); 185 row.className = "shop-cart-item"; 186 187 var desc = document.createElement("span"); 188 desc.className = "shop-cart-item-desc"; 189 desc.textContent = item.qty + " Ã " + item.size; 190 191 var price = document.createElement("span"); 192 price.className = "shop-cart-item-price"; 193 price.textContent = "$" + formatMoney(item.qty * PRICE); 194 195 var remove = document.createElement("a"); 196 remove.href = "#"; 197 remove.className = "shop-cart-item-remove"; 198 remove.setAttribute("data-index", String(index)); 199 remove.setAttribute("aria-label", "Remove item"); 200 remove.textContent = "Remove"; 201 202 row.appendChild(desc); 203 row.appendChild(price); 204 row.appendChild(remove); 205 itemsEl.appendChild(row); 206 }); 207 } 208 209 var total = cartTotal(cart); 210 var count = cartItemCount(cart); 211 212 if (totalEl) totalEl.textContent = formatMoney(total); 213 if (countEl) countEl.textContent = String(count); 214 if (checkoutTotalEl) checkoutTotalEl.textContent = formatMoney(total); 215 if (messageEl) messageEl.value = cart.length ? buildOrderSummary(cart, total) : ""; 216 } 217 218 document.addEventListener("DOMContentLoaded", function () { 219 renderCart(); 220 221 var addBtn = document.getElementById("add-to-cart-btn"); 222 if (addBtn) { 223 addBtn.addEventListener("click", function (e) { 224 e.preventDefault(); 225 226 var size = document.getElementById("shirt-size").value; 227 var qty = parseInt(document.getElementById("shirt-qty").value, 10); 228 229 if (!qty || qty < 1) { 230 qty = 1; 231 } 232 233 addToCart(size, qty); 234 }); 235 } 236 237 var itemsEl = document.getElementById("cart-items"); 238 if (itemsEl) { 239 itemsEl.addEventListener("click", function (e) { 240 var target = e.target; 241 if (target && target.classList.contains("shop-cart-item-remove")) { 242 e.preventDefault(); 243 removeFromCart(parseInt(target.getAttribute("data-index"), 10)); 244 } 245 }); 246 } 247 248 var emailInput = document.getElementById("shop-email"); 249 if (emailInput) { 250 emailInput.addEventListener("input", renderCart); 251 } 252 253 var stripeBtn = document.getElementById("pay-stripe"); 254 if (stripeBtn) { 255 stripeBtn.addEventListener("click", handleStripeClick()); 256 } 257 }); 258})();
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.