PageSourceSearch

https://tysse.no/Files/Templates/Designs/Swift/Assets/_src/js/_cart.js

js tysse.no collected 2026-10-02 21:47:07 UTC 12,591 bytes, 354 lines download raw bytes

1const Cart = function () {
2	let productId;
3	let productVariantId;
4	let productUnitId;
5	let productName;
6	let productVariantName;
7	let productCurrency;
8	let productReferer;
9	let productPrice;
10	let addQuantity;
11	let stockQuantity;
12	let isAmountReservation = false;
13	let isPendingQuote = "false";
14  	let orderType;
15  	let trailerType;
16
17	return {
18		Update: async function (e) {
19			//NP: clickedButton is not always the button. Sometimes it is the qty input field if [enter] is pressed
20			const clickedButton = e.currentTarget != undefined ? e.currentTarget : e;
21			const form = clickedButton.closest("form");
22			const quantityField = form.querySelector('[name="Quantity"]');
23
24			//Setup the form data
25			let formData = new FormData(form);
26			productId = formData.get("ProductId");
27			productVariantId = formData.get("VariantId");
28			productUnitId = formData.get("UnitID");
29			productName = formData.get("ProductName");
30			productVariantName = formData.get("ProductVariantName");
31			productCurrency = formData.get("ProductCurrency");
32			productReferer = formData.get("ProductReferer");
33			productPrice = formData.get("ProductPrice");
34			addQuantity = formData.get("Quantity") ? formData.get("Quantity") : 1;
35			stockQuantity = formData.get("Stock") ? formData.get("Stock") : 9999999;
36			isAmountReservation = formData.get("GetReservedAmount") ? formData.get("GetReservedAmount") : "false";
37			isPendingQuote = formData.get("PendingQuote") ? formData.get("PendingQuote") : "false";
38          	orderType = formData.get("OrderType");
39          	trailerType = formData.get("TrailerType");
40
41			this.PushDataToGoogleAnalytics();
42
43			let event = new CustomEvent("update.swift.cart", {
44			//Fire the 'update' event
45				cancelable: true,
46				detail: {
47					formData: formData,
48					parentEvent: e
49				}
50			});
51			let globalDispatcher = document.dispatchEvent(event);
52			let localDispatcher = clickedButton.dispatchEvent(event);
53			
54			if (globalDispatcher != false && localDispatcher != false) {
55				//Remove reserved amount from the form data, so that we only use it for the special reserved amount call
56				if (isAmountReservation == "true") {
57					formData.delete("GetReservedAmount");
58				}
59
60				//Special call to get only the reserved amount
61				let reservedAmount = 0;				
62				if (isAmountReservation == "true" && stockQuantity != 9999999) {
63					reservedAmount = await this.GetReservedAmount(form);
64				}
65
66				const isMinQuantityValid = this.ValidateMinQuantity(quantityField);
67				const isStepQuantityValid = this.ValidateStepQuantity(quantityField);
68				const isValid = isMinQuantityValid && isStepQuantityValid;
69				quantityField.classList.remove("is-invalid");
70
71				//The actual cart call (add to cart)
72				if (isPendingQuote == "true") {
73					const pendingQuoteMessage = form.querySelector(".js-pending-quote-notice").innerHTML;
74					document.querySelector("#DynamicModalContent").innerHTML = pendingQuoteMessage;
75
76					let dynamicModal = new bootstrap.Modal(document.querySelector('#DynamicModal'), {
77						backdrop: 'static'
78					});
79
80					dynamicModal.show();
81				}
82				else if (isValid && (isAmountReservation == "false" || ((parseInt(addQuantity) + parseInt(reservedAmount)) <= parseInt(stockQuantity)))) {
83					//UI updates
84					const clickedButtonWidth = clickedButton.offsetWidth + "px";
85
86					clickedButton.setAttribute("data-content", clickedButton.innerHTML);
87					clickedButton.style.width = clickedButtonWidth;
88					clickedButton.classList.add("disabled");
89					clickedButton.disabled = true;
90					clickedButton.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 512 512"><title>circle-notch</title><g fill="#ffffff"><path d="M288 24.103v8.169a11.995 11.995 0 0 0 9.698 11.768C396.638 63.425 472 150.461 472 256c0 118.663-96.055 216-216 216-118.663 0-216-96.055-216-216 0-104.534 74.546-192.509 174.297-211.978A11.993 11.993 0 0 0 224 32.253v-8.147c0-7.523-6.845-13.193-14.237-11.798C94.472 34.048 7.364 135.575 8.004 257.332c.72 137.052 111.477 246.956 248.531 246.667C393.255 503.711 504 392.789 504 256c0-121.187-86.924-222.067-201.824-243.704C294.807 10.908 288 16.604 288 24.103z"></path></g></svg>';
91
92					Cart.GetMiniCarts(formData.get("minicartid")).forEach(function (el) {
93						el.classList.add("mini-cart-quantity-added");
94					});
95
96					//Fetch
97					const fetchOptions = {
98						method: 'POST',
99						body: formData
100					};
101
102					let response = await fetch(form.action, fetchOptions);
103
104					if (response.ok) {
105						Cart.Success(response, clickedButton, formData, reservedAmount);
106					} else {
107						Cart.Error(response, clickedButton);
108					}
109				} else if (!isValid) {
110					this.AddToCartValidate(quantityField);
111				} else {
112					const outOfStockMessage = form.querySelector(".js-out-of-stock-notice").innerHTML;
113					document.querySelector("#DynamicModalContent").innerHTML = outOfStockMessage;
114
115					if (quantityField != null) {
116						if (quantityField.min != null) {
117							quantityField.value = quantityField.min > 0 ? quantityField.min : 1;
118						} else {
119							quantityField.value = 1;
120						}
121					}
122
123					let dynamicModal = new bootstrap.Modal(document.querySelector('#DynamicModal'), {
124						backdrop: 'static'
125					});
126
127					dynamicModal.show();
128				}
129			}
130		},
131
132		UpdateOnEnterKey: function (e) {
133			const input = e.currentTarget != undefined ? e.currentTarget : e;
134			e.preventDefault();
135
136			input.onkeydown = (e) => {
137				if (e.keyCode === 13) {
138					Cart.Update(e);
139				}
140			};
141		},
142
143		Success: async function (response, clickedButton, formData, reservedAmount = 0) {
144			let html = await response.text().then(function (text) {
145				return text;
146			});
147
148			//Fire the 'updated'´event
149			let event = new CustomEvent("updated.swift.cart", {
150				cancelable: true,
151				detail: {
152					formData: formData,
153					html: html
154				}
155			});
156			let globalDispatcher = document.dispatchEvent(event);
157			let localDispatcher = clickedButton.dispatchEvent(event);
158
159			if (globalDispatcher != false && localDispatcher != false) {
160				//Cleanup
161				clickedButton.classList.remove("disabled");
162				clickedButton.style.width = "";
163				clickedButton.disabled = false;
164				clickedButton.innerHTML = clickedButton.getAttribute("data-content");
165				clickedButton.setAttribute("data-content", "");
166
167				let removeFocusCssClassTimer = setTimeout(function () {
168					Cart.GetMiniCarts(formData.get("minicartid")).forEach(function (el) {
169						el.classList.remove("mini-cart-quantity-added");
170					});
171				}, 200);
172
173				//Replace the markup (Update min carts counters)
174				let totalQuantity = html != undefined ? html : 0;
175				Cart.GetMiniCarts(formData.get("minicartid")).forEach(function (el) {
176					el.innerHTML = "(" + totalQuantity.trim() + ")";
177				});
178
179				//Update stock
180				if (isAmountReservation == "true") {
181					this.UpdateStock(reservedAmount, clickedButton)
182				}
183			}
184		},
185
186		Error: async function (response, clickedButton) {
187			//Cleanup
188			let removeFocusCssClassTimer = setTimeout(function () {
189				document.querySelectorAll(".js-cart-qty").forEach(function (el) {
190					el.classList.remove("mini-cart-quantity-added");
191				});
192			}, 200);
193
194			clickedButton.classList.remove("disabled");
195			clickedButton.style.width = "";
196			clickedButton.disabled = false;
197			clickedButton.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 512 512"><title>circle-notch</title><g fill="#ffffff"><path d="M288 24.103v8.169a11.995 11.995 0 0 0 9.698 11.768C396.638 63.425 472 150.461 472 256c0 118.663-96.055 216-216 216-118.663 0-216-96.055-216-216 0-104.534 74.546-192.509 174.297-211.978A11.993 11.993 0 0 0 224 32.253v-8.147c0-7.523-6.845-13.193-14.237-11.798C94.472 34.048 7.364 135.575 8.004 257.332c.72 137.052 111.477 246.956 248.531 246.667C393.255 503.711 504 392.789 504 256c0-121.187-86.924-222.067-201.824-243.704C294.807 10.908 288 16.604 288 24.103z"></path></g></svg>';
198		},
199
200		PushDataToGoogleAnalytics: function() {
201			if (typeof gtag !== "undefined") {
202				gtag("event", "add_to_cart", {
203					currency: productCurrency,
204					value: productPrice,
205					items: [
206						{
207							item_id: productId,
208							item_name: productName,
209							item_variant: productVariantName,
210							currency: productCurrency,
211							item_list_id: productReferer,
212							price: productPrice,
213							quantity: addQuantity,
214                          	item_category: orderType,
215                          	item_category2: trailerType
216						}
217					]
218				});
219			}
220		},
221
222		GetMiniCarts: function (miniCartId) {
223			let miniCarts = [];
224
225			if (miniCartId != null) {
226				const miniCartElement = document.querySelector("#Cart_" + miniCartId);
227
228				if (miniCartElement) {
229					miniCarts = [miniCartElement];
230				} else {
231					console.log("Mini cart page is missing");
232				}
233			} else {
234				miniCarts = document.querySelectorAll(".js-cart-qty");
235			}
236
237			return miniCarts;
238		},
239
240		GetReservedAmount: async function (form) {
241			const getReservedFormData = new FormData();
242			getReservedFormData.append("GetReservedAmount", true);
243			getReservedFormData.append("ProductId", productId);
244
245			if (addQuantity != null) {
246				getReservedFormData.append("Quantity", addQuantity);
247			}
248
249			if (productUnitId != null) {
250				getReservedFormData.append("UnitId", productUnitId);
251			}
252
253			if (productVariantId != null) {
254				getReservedFormData.append("VariantId", productVariantId);
255			}
256
257			const getReservedAmountfetchOptions = {
258				method: 'POST',
259				body: getReservedFormData
260			};
261
262			let response = await fetch(form.action, getReservedAmountfetchOptions);
263
264			if (response.ok) {
265				let amount = await response.text().then(function (text) {
266					return text;
267				});
268
269				return amount;
270			} else {
271				Cart.Error(response, clickedButton);
272
273				return 0;
274			}
275		},
276
277		UpdateStock: function (reservedAmount, clickedButton) {
278			const stockLevelElement = clickedButton.closest(".js-product") ? clickedButton.closest(".js-product").querySelector(".js-text-stock") : clickedButton.closest("#content").querySelector(".js-text-stock");
279
280			if (stockLevelElement) {
281				if (stockQuantity != 9999999) {
282					stockLevelElement.innerHTML = (parseInt(stockQuantity) - parseInt(addQuantity) - parseInt(reservedAmount));
283				}
284			}
285		},
286
287		ValidateMinQuantity: function (quantityField) {
288			let isValid = true;
289
290			if (quantityField != null) {
291				const quantity = parseInt(quantityField.value);
292				const minQuantity = parseInt(quantityField.min);
293				isValid = quantity < minQuantity ? false : isValid;
294			}
295
296			return isValid;
297		},
298		
299		ValidateStepQuantity: function (quantityField) {
300			let isValid = true;
301
302			if (quantityField != null) {
303				isValid = quantityField.checkValidity();
304			}
305
306			return isValid;
307		},
308		
309		AddToCartValidate: function (event) {
310			let quantityField = event.currentTarget != undefined ? event.currentTarget : event;
311			const form = quantityField.closest("form");
312
313			const minQuantityWarning = form.querySelector(".js-min-quantity-warning");
314			const stepQuantityWarning = form.querySelector(".js-step-quantity-warning");
315
316			const isMinQuantityValid = this.ValidateMinQuantity(quantityField);
317			const isStepQuantityValid = this.ValidateStepQuantity(quantityField);
318			const isValid = isMinQuantityValid && isStepQuantityValid;
319
320			const dynamicModal = new bootstrap.Modal(document.querySelector('#DynamicModal'), { });
321
322			if (!isMinQuantityValid && minQuantityWarning) {
323				const message = minQuantityWarning.innerHTML;
324				const minQuantity = parseInt(quantityField.min);
325				document.querySelector("#DynamicModalContent").innerHTML = message;
326
327				if (!document.querySelector('#DynamicModal').classList.contains("show")) {
328					dynamicModal.show();
329				}
330
331				if (form.querySelector('[name="Quantity"]')) {
332					form.querySelector('[name="Quantity"]').value = minQuantity;
333				}
334			}
335
336			if (!isStepQuantityValid && stepQuantityWarning) {
337				const message = stepQuantityWarning.innerHTML;
338				document.querySelector("#DynamicModalContent").innerHTML = message;
339
340				if (!document.querySelector('#DynamicModal').classList.contains("show")) {
341					dynamicModal.show();
342				}
343			}
344			
345			if (!isValid) {
346				quantityField.classList.add("is-invalid");
347			} else {
348				quantityField.classList.remove("is-invalid");
349			}
350		}
351	}
352}();
353
354export { Cart };

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.