PageSourceSearch

https://www.prestigeflowers.co.uk/styles/main/js/cart.js?v=c007b992840d5d3493cd9aef61d4f2629173124c

js prestigeflowers.co.uk collected 2026-09-24 11:24:44 UTC 18,806 bytes, 533 lines download raw bytes

1// ADDRESS FUNCTIONS
2
3const isUKPostcode = (postcode) => {
4	const ukPostcodeRegex = /^([A-Z]{1,2}[0-9][0-9A-Z]?) ?([0-9][A-Z]{2})$/i;
5
6	return ukPostcodeRegex.test(postcode);
7};
8
9const showPostcodeMessage = (cartId, message, type = "danger") => {
10	const postcodeInputEl = document.getElementById(`address_postcode_${cartId}`);
11	const postcodeMessageEl = document.getElementById(`postcode_message_${cartId}`);
12
13	if (postcodeInputEl && postcodeMessageEl) {
14		if (type === "danger") {
15			postcodeInputEl.classList.add("invalid");
16		}
17		postcodeMessageEl.classList.add("alert", "alert-" + type, "active");
18		postcodeMessageEl.textContent = message;
19	}
20};
21
22const hidePostcodeMessage = (cartId, type = "danger") => {
23	const postcodeInputEl = document.getElementById(`address_postcode_${cartId}`);
24	const postcodeMessageEl = document.getElementById(`postcode_message_${cartId}`);
25
26	if (postcodeInputEl && postcodeMessageEl) {
27		postcodeInputEl.classList.remove("invalid");
28		postcodeMessageEl.classList.remove("alert", "alert-" + type, "active");
29		postcodeMessageEl.textContent = "";
30	}
31};
32
33const handleUKPostcodeValidation = (cartId) => {
34	const postcodeInputEl = document.getElementById(`address_postcode_${cartId}`);
35	const countryInputEl = document.getElementById(`address_country_${cartId}`);
36
37	if (countryInputEl && countryInputEl.value === "United Kingdom") {
38		if (postcodeInputEl && postcodeInputEl.value === "") {
39			showPostcodeMessage(cartId, "Please enter your postcode.");
40		} else if (postcodeInputEl && !isUKPostcode(postcodeInputEl.value)) {
41			showPostcodeMessage(cartId, "Please enter a valid UK postcode.");
42		} else {
43			hidePostcodeMessage(cartId);
44			if (postcodeInputEl) postcodeInputEl.classList.remove("invalid");
45		}
46	}
47};
48
49const handleAddressSelection = (cartId) => {
50	const addressSelectEl = document.getElementById(`address_lookup_result_${cartId}`);
51	if (!addressSelectEl || addressSelectEl.selectedIndex === 0) return;
52
53	const addressData = addressSelectEl.value;
54	const addressArray = JSON.parse(addressData);
55
56	// Update address fields
57	const addressLine1El = document.getElementById(`address_line1_${cartId}`);
58	const addressLine2El = document.getElementById(`address_line2_${cartId}`);
59	const townEl = document.getElementById(`address_town_${cartId}`);
60	const countyEl = document.getElementById(`address_county_${cartId}`);
61	const countryEl = document.getElementById(`address_country_${cartId}`);
62
63	if (addressLine1El) addressLine1El.value = addressArray.line_1 || "";
64	if (addressLine2El) addressLine2El.value = addressArray.line_2 || "";
65	if (townEl) townEl.value = addressArray.town || "";
66	if (countyEl) countyEl.value = addressArray.county || "";
67	if (countryEl) countryEl.value = addressArray.country || "";
68
69	showAllAddressFields(cartId);
70	SaveCart(cartId);
71};
72
73const movePostcodeInputEl = (cartId) => {
74	const postcodeInputEl = document.getElementById("address_postcode_" + cartId);
75	const townInput = document.getElementById("address_town_" + cartId);
76	const postcodeMessageEl = document.getElementById("postcode_message_" + cartId);
77	const postcodeLookupFieldEl = postcodeInputEl?.closest("#postcode_lookup_field_" + cartId);
78	const addressLookupSelectEl = document.getElementById("address_lookup_result_" + cartId);
79
80	if (postcodeInputEl && townInput) {
81		const existingOnChange = postcodeInputEl.onchange;
82
83		postcodeInputEl.placeholder = "Postcode";
84
85		postcodeInputEl.onchange = (event) => {
86			if (existingOnChange) existingOnChange.call(postcodeInputEl, event);
87			handleUKPostcodeValidation(cartId);
88		};
89
90		// Move the entire postcode-lookup-row container if it exists, otherwise just the input
91		if (postcodeLookupFieldEl) {
92			postcodeLookupFieldEl.classList.add("moved");
93			townInput.parentNode.insertBefore(postcodeLookupFieldEl, townInput.nextSibling);
94
95			if (postcodeMessageEl) {
96				townInput.parentNode.insertBefore(postcodeMessageEl, postcodeLookupFieldEl.nextSibling);
97			}
98
99			if (addressLookupSelectEl) {
100				townInput.parentNode.insertBefore(addressLookupSelectEl, postcodeMessageEl ? postcodeMessageEl.nextSibling : postcodeLookupFieldEl.nextSibling);
101			}
102		} else {
103			// Fallback to old behavior if postcode-lookup-row doesn't exist
104			townInput.parentNode.insertBefore(postcodeInputEl, townInput.nextSibling);
105
106			if (postcodeMessageEl) {
107				townInput.parentNode.insertBefore(postcodeMessageEl, postcodeInputEl.nextSibling);
108			}
109		}
110	}
111};
112
113const showAllAddressFields = (cartId) => {
114	activeClassHandler("#full_address_fields_" + cartId, "add");
115	activeClassHandler("#hidden_address_trigger_" + cartId, "remove");
116	// movePostcodeInputEl(cartId);
117};
118
119const updateSelectedAddons = (cartId) => {
120	let selectedAddons = [];
121	const currentCartItemEl = document.getElementById(`cart_item_${cartId}`);
122	const addonsLabelEl = currentCartItemEl.querySelector(`.addons-label`);
123	const initialSelectedAddonsEls = currentCartItemEl.querySelectorAll(`.initial-selected-addon`);
124	const noInitialSelectedAddonsEl = currentCartItemEl.querySelector(`.no-initial-selected-addons`);
125	const selectedAddonsEl = currentCartItemEl.querySelector(`.selected-addons`);
126	const addonsSelected = Array.from(currentCartItemEl.querySelectorAll('[id^="attr_addons_"]:checked'));
127
128	selectedAddons = Array.from(selectedAddonsEl.querySelectorAll(".initial-selected-addon span")).map((element) => element.textContent);
129
130	addonsSelected.forEach((addon) => (!selectedAddons.includes(addon.value) ? selectedAddons.push(addon.value) : ""));
131
132	selectedAddons = selectedAddons.filter((addon) => addonsSelected.some((selected) => selected.value === addon));
133
134	if (noInitialSelectedAddonsEl) noInitialSelectedAddonsEl.style.display = "none";
135	if (initialSelectedAddonsEls) initialSelectedAddonsEls.forEach((el) => (el.style.display = "none"));
136
137	if (selectedAddons.length > 0) {
138		addonsLabelEl.style.display = "block";
139		selectedAddonsEl.style.display = "block";
140		selectedAddonsEl.innerHTML = `
141				${selectedAddons
142					.map(
143						(addon) => `
144					<div class="initial-selected-addon">
145						<span>${addon}</span>
146						${!addon.includes("(Included)") ? `<i class="fa fa-close" onclick="removeAddonSelected(${cartId}, '${addon}')"></i>` : ""}
147					</div>
148				`,
149					)
150					.join("")}
151			`;
152	} else {
153		selectedAddonsEl.innerHTML = "No Add-ons Selected";
154	}
155};
156
157const removeAddonSelected = (cartId, addonName) => {
158	const currentCartItemEl = document.getElementById(`cart_item_${cartId}`);
159	const addonCheckbox = currentCartItemEl.querySelector(`input[value="${addonName}"]`);
160
161	if (addonCheckbox) {
162		addonCheckbox.checked = false;
163		SaveCart(cartId);
164		updateSelectedAddons(cartId);
165	}
166};
167
168const showAddressSelectEl = (cartId, address) => {
169	const addressSelectEl = document.getElementById(`address_lookup_result_${cartId}`);
170	if (!addressSelectEl) return;
171
172	// Build address parts array, filtering out empty values
173	const addressParts = [address.line_1, address.line_2, address.town, address.county].filter((part) => part && part.trim() !== "");
174
175	const nameFormatted = addressParts.join(", ");
176	const valueFormatted = JSON.stringify(address);
177
178	const option = new Option(nameFormatted, valueFormatted);
179	addressSelectEl.add(option);
180	activeClassHandler(`#address_lookup_result_${cartId}`, "add");
181};
182
183const isPostcodeValid = (cartId) => {
184	const postcodeEl = document.getElementById(`address_postcode_${cartId}`);
185	const postcode = postcodeEl?.value?.trim();
186
187	if (!postcode) {
188		showPostcodeMessage(cartId, "Please enter a postcode.");
189		return false;
190	}
191
192	return true;
193};
194
195const handleAddressLookup = (cartId, event) => {
196	if (event) event.preventDefault();
197
198	// Get the encryption function from the window object if available
199	const addressLookupAuth = window.addressLookupAuth || "";
200
201	const postcodeEl = document.getElementById(`address_postcode_${cartId}`);
202	const addressLine1El = document.getElementById(`address_line1_${cartId}`);
203	const countryEl = document.getElementById(`address_country_${cartId}`);
204
205	const datas = {
206		postcode: postcodeEl?.value || "",
207		firstline: addressLine1El?.value || "",
208		country: countryEl?.value || "",
209		auth: addressLookupAuth,
210	};
211
212	activeClassHandler(`#address_lookup_result_${cartId}`, "remove");
213
214	hidePostcodeMessage(cartId);
215
216	if (!isPostcodeValid(cartId)) return;
217
218	showPostcodeMessage(cartId, "Looking up addresses...", "info");
219
220	psLoad("", "postcode_lookup", datas)
221		.then(function (dataReturned) {
222			const parsedArray = JSON.parse(JSON.parse(dataReturned));
223
224			hidePostcodeMessage(cartId);
225
226			// Remove all previously added options except the first one
227			const addressSelectEl = document.getElementById(`address_lookup_result_${cartId}`);
228			if (addressSelectEl) {
229				addressSelectEl.options.length = 1;
230
231				if (parsedArray !== null && parsedArray["addresses"] && parsedArray["addresses"].length > 0) {
232					parsedArray["addresses"].forEach((address) => showAddressSelectEl(cartId, address));
233				} else {
234					showPostcodeMessage(cartId, "No addresses found for the provided postcode.");
235				}
236			}
237		})
238		.catch(function (error) {
239			hidePostcodeMessage(cartId);
240			showPostcodeMessage(cartId, "Error looking up addresses. Please try again.");
241			console.error("Address lookup error:", error);
242		});
243};
244
245// DISCOUNT FUNCTIONS
246
247const showDiscountInputs = () => {
248	const discountTriggerEl = document.getElementById("discount_trigger");
249
250	activeClassHandler("#discount_inputs", "toggle");
251	discountTriggerEl.classList.toggle("rotate");
252};
253
254const applyCouponCode = () => {
255	const couponCode = document.getElementById("coupon_code").value;
256	const submitCodeBtn = document.getElementById("submit_coupon_code");
257	const couponResultEl = document.getElementById("coupon_submit_result");
258
259	if (couponCode.trim() === "") {
260		couponResultEl.classList.remove("success");
261		couponResultEl.classList.add("error");
262		couponResultEl.innerHTML = `
263                <i class="fa fa-exclamation-circle fa-fw"></i>
264                <span>Please enter a coupon code.</span>
265            `;
266		return;
267	}
268
269	$.post(
270		`${apiBaseUrl}/cart/apply-discount`,
271		{
272			coupon_code: couponCode,
273		},
274		(data) => {
275			const errors = data.errors ?? [];
276
277			if (data.errors && data.errors.length > 0) {
278				couponResultEl.classList.remove("success");
279				couponResultEl.classList.add("error");
280				couponResultEl.innerHTML = `
281                    <i class="fa fa-exclamation-circle fa-fw"></i>
282                    <span>${errors.join("<br>")}</span>
283                `;
284			} else if (data.success) {
285				couponResultEl.classList.remove("error");
286				couponResultEl.classList.add("success");
287				couponResultEl.innerHTML = `
288                    <i class="fa fa-check-circle fa-fw"></i>
289                    <span>${data.response.discount.message ?? "Coupon successfully applied!"}</span>
290                `;
291
292				submitCodeBtn.classList.remove("dark-blue");
293				submitCodeBtn.classList.add("cta");
294				submitCodeBtn.innerHTML = "Discount Applied";
295				fetchCartTotals();
296			} else {
297				couponResultEl.classList.remove("success");
298				couponResultEl.classList.add("error");
299				couponResultEl.innerHTML = `
300                    <i class="fa fa-exclamation-circle fa-fw"></i>
301                    <span>Sorry, something went wrong. Please try again later.</span>
302                `;
303			}
304		},
305	).fail(function () {
306		couponResultEl.classList.remove("success");
307		couponResultEl.classList.add("error");
308		couponResultEl.innerHTML = `
309                <i class="fa fa-exclamation-circle fa-fw"></i>
310                <span>Unable to apply coupon at this time. Please try again later.</span>
311            `;
312	});
313};
314
315const removeCouponCode = () => {
316	const couponResultEl = document.getElementById("coupon_submit_result");
317	const submitCodeBtn = document.getElementById("submit_coupon_code");
318
319	$.post(`${apiBaseUrl}/cart/remove-discount`, {}, (data) => {
320		if (data.success) {
321			couponResultEl.classList.remove("error");
322			couponResultEl.classList.add("success");
323			couponResultEl.innerHTML = `
324				<i class="fa fa-check-circle fa-fw"></i>
325				<span>Coupon successfully removed from your order.</span>
326			`;
327			submitCodeBtn.classList.remove("cta");
328			submitCodeBtn.classList.add("dark-blue");
329			submitCodeBtn.innerHTML = "Redeem Discount";
330			fetchCartTotals();
331		}
332	}).fail(function () {
333		alert("Unable to remove coupon at this time. Please try again later.");
334	});
335};
336
337const applyUserCredit = () => {
338	const creditAmount = document.getElementById("credit_amount").value;
339	const creditResultEl = document.getElementById("credit_amount_result");
340	const submitCreditBtn = document.getElementById("submit_credit_amount");
341
342	if (creditAmount.trim() === "") {
343		creditResultEl.classList.remove("success");
344		creditResultEl.classList.add("error");
345		creditResultEl.innerHTML = `
346                <i class="fa fa-exclamation-circle fa-fw"></i>
347                <span>Please enter a credit amount.</span>
348            `;
349		return;
350	}
351
352	$.post(
353		`${apiBaseUrl}/cart/apply-credit`,
354		{
355			credit_amount: creditAmount,
356		},
357		(data) => {
358			const errors = data.errors ?? [];
359			const creditResponse = data.response.credit ?? {};
360
361			if (data.errors && data.errors.length > 0) {
362				creditResultEl.classList.remove("success");
363				creditResultEl.classList.add("error");
364				creditResultEl.innerHTML = `
365                    <i class="fa fa-exclamation-circle fa-fw"></i>
366                    <span>${errors.join("<br>")}</span>
367                `;
368			} else if (creditResponse) {
369				creditResultEl.classList.remove("error");
370				creditResultEl.classList.add("success");
371				creditResultEl.innerHTML = `
372                    <i class="fa fa-check-circle fa-fw"></i>
373                    <span>Credit successfully applied!</span>
374                `;
375
376				submitCreditBtn.classList.remove("dark-blue");
377				submitCreditBtn.classList.add("cta");
378				submitCreditBtn.innerHTML = "Discount Applied";
379				fetchCartTotals();
380			} else {
381				creditResultEl.classList.remove("success");
382				creditResultEl.classList.add("error");
383				creditResultEl.innerHTML = `
384                    <i class="fa fa-exclamation-circle fa-fw"></i>
385                    <span>Sorry, something went wrong. Please try again later.</span>
386                `;
387			}
388			window.location.reload();
389		},
390	).fail(function () {
391		creditResultEl.classList.remove("success");
392		creditResultEl.classList.add("error");
393		creditResultEl.innerHTML = `
394                <i class="fa fa-exclamation-circle fa-fw"></i>
395                <span>Unable to apply coupon at this time. Please check your connection and try again.</span>
396            `;
397	});
398};
399
400const removeUserCredit = () => {
401	const creditResultEl = document.getElementById("credit_amount_result");
402	const submitCreditBtn = document.getElementById("submit_credit_amount");
403
404	$.post(`${apiBaseUrl}/cart/apply-credit`, { credit_amount: 0 }, (data) => {
405		if (data.success) {
406			creditResultEl.classList.remove("error");
407			creditResultEl.classList.add("success");
408			creditResultEl.innerHTML = `
409				<i class="fa fa-check-circle fa-fw"></i>
410				<span>Credit successfully removed from your order.</span>
411			`;
412			submitCreditBtn.classList.remove("cta");
413			submitCreditBtn.classList.add("dark-blue");
414			submitCreditBtn.innerHTML = "Apply Credit Balance";
415			fetchCartTotals();
416		} else {
417			creditResultEl.classList.remove("success");
418			creditResultEl.classList.add("error");
419			creditResultEl.innerHTML = `
420				<i class="fa fa-exclamation-circle fa-fw"></i>
421				<span>Sorry, something went wrong. Please try again later.</span>
422			`;
423		}
424		window.location.reload();
425	}).fail(function () {
426		creditResultEl.classList.remove("success");
427		creditResultEl.classList.add("error");
428		creditResultEl.innerHTML = `
429				<i class="fa fa-exclamation-circle fa-fw"></i>
430				<span>Unable to remove credit at this time. Please check your connection and try again.</span>
431		`;
432	});
433};
434
435const fetchCartTotals = () => {
436	const orderTotalsEls = document.querySelectorAll(".order-totals");
437
438	if (orderTotalsEls) {
439		$.get(`${apiBaseUrl}/cart/totals`, (data) => {
440			const totals = data.response.totals ?? {};
441			const subtotal = totals.product_total + totals.addon_total + totals.personalisation;
442
443			if (Object.keys(totals).length == 0) {
444				orderTotalsEls[0].innerHTML = "There was an error loading your order totals.";
445				return;
446			}
447
448			orderTotalsEls.forEach((orderTotalEl) => {
449				orderTotalEl.innerHTML = `
450					<div class="price-container">
451						<label for="sub_total">Sub Total</label>
452						<span id="sub_total">&pound;${(Number(subtotal) ?? "0.00").toFixed(2)}</span>
453					</div>
454
455					<div class="price-container">
456						<label for="delivery_total">Delivery</label>
457						<span id="delivery_total">&pound;${(Number(totals.delivery_total) ?? "0.00").toFixed(2)}</span>
458					</div>
459					
460					${
461						totals.delivery_pass_total && totals.delivery_pass_total > 0
462							? `
463						<div class="delivery-pass-total price-container">
464							<label for="delivery_pass_total">Delivery Pass</label>
465							<span id="delivery_pass_total">&pound;${(Number(totals.delivery_pass_total) ?? "0.00").toFixed(2)}</span>
466						</div>
467					`
468							: ""
469					}
470
471					${
472						totals.discount_total && totals.discount_total > 0
473							? `
474						<div class="price-container discount-total">
475							<label for="discount_total">Discount:</label>
476
477							<span id="discount_total">
478							-&pound;${(Number(totals.discount_total) ?? "0.00").toFixed(2)}
479							<i class="remove-coupon fa fa-circle-minus" onclick="removeCouponCode();"></i>
480							</span>
481						</div>
482					`
483							: ""
484					}
485
486					${
487						totals.credit_to_use && Number(totals.credit_to_use) > 0
488							? `
489						<div class="price-container credit-used">
490							<label for="credit_used">Credit:</label>
491							<span id="credit_used">
492								-&pound;${(Number(totals.credit_to_use) ?? "0.00").toFixed(2)}
493								<i class="remove-credit fa fa-circle-minus" onclick="removeUserCredit();"></i>
494							</span>
495						</div>
496						`
497							: ""
498					}
499
500					<div class="price-container grand-total">
501						<label for="grand_total">Total:</label>
502						<span id="grand_total">&pound;${(Number(totals.grand_total) ?? "0.00").toFixed(2)}</span>
503					</div>
504				`;
505			});
506		});
507	}
508};
509
510const handleCartFormSubmit = () => {
511	const cartForm = document.getElementById("product_form");
512	const cartItems = document.querySelectorAll(".cart-item");
513
514	cartForm.classList.add("form-submitted");
515
516	cartItems.forEach((cartItem) => {
517		const hiddenFields = cartItem.querySelector(".hidden-fields");
518		const requiredInputs = hiddenFields.querySelectorAll("input:required");
519
520		let hasInvalid = false;
521
522		requiredInputs.forEach((input) => {
523			if (!input.checkValidity()) {
524				hasInvalid = true;
525			}
526		});
527
528		if (hasInvalid) {
529			showAllAddressFields(cartItem.getAttribute("data-cart-id"));
530			hiddenFields.classList.add("active");
531		}
532	});
533};

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.