PageSourceSearch

https://www.utahbrazilianfestival.com/Shop.js

js utahbrazilianfestival.com collected 2026-10-02 22:48:27 UTC 7,769 bytes, 258 lines download raw bytes

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.