PageSourceSearch

https://www.bodylab.fi/bundles/voyado/js/voyado.js?1791233033

js bodylab.fi collected 2026-10-06 13:57:32 UTC 63,657 bytes, 1,166 lines download raw bytes

1(function() {
2    // Initialize state
3    if (typeof window.voyadoStarted === 'undefined') {
4        window.voyadoStarted = false;
5    }
6    
7    /**
8     * Sets the voyado-analytics consent cookie
9     * Cookie expires in 1 year (31536000 seconds)
10     */
11    function setVoyadoConsentCookie() {
12        try {
13            // Get current domain for cookie
14            var domain = window.location.hostname;
15            document.cookie = "voyado-analytics=1; path=/; max-age=31536000; SameSite=Lax";
16            
17            // Verify cookie was actually set
18            var cookieSet = document.cookie.indexOf('voyado-analytics=1') !== -1;
19            
20            if (!cookieSet) {
21                console.warn("Voyado: Cookie could not be set. Check browser cookie settings.");
22            }
23        } catch (e) {
24            console.error("Voyado: Failed to set cookie:", e);
25        }
26    }
27
28    /**
29     * Clears the voyado-analytics consent cookie
30     */
31    function clearVoyadoConsentCookie() {
32        document.cookie = "voyado-analytics=; path=/; max-age=0; SameSite=Lax";
33    }
34
35    /**
36     * Gets cookie value (early version for use before DOM ready)
37     */
38    function getCookieValueEarly(cookieName) {
39        var name = cookieName + "=";
40        var decodedCookie = decodeURIComponent(document.cookie);
41        var cookieArray = decodedCookie.split(';');
42        for (var i = 0; i < cookieArray.length; i++) {
43            var cookie = cookieArray[i].trim();
44            if (cookie.indexOf(name) === 0) {
45                return cookie.substring(name.length, cookie.length);
46            }
47        }
48        return null;
49    }
50
51    /**
52     * Initializes Voyado tracking (only once)
53     * Dispatches 'voyadoReady' event when tracking is started
54     */
55    function startVoyado() {
56        if (window.voyadoStarted) {
57            return;
58        }
59        window.voyadoStarted = true;
60        
61        // Dispatch ready event for any listeners
62        window.dispatchEvent(new CustomEvent('voyadoReady'));
63        
64        // Also call initVoyadoTracking directly if DOM is already loaded
65        if (document.readyState === 'complete' || document.readyState === 'interactive') {
66            if (typeof initVoyadoTracking === 'function') {
67                setTimeout(initVoyadoTracking, 0);
68            }
69        }
70    }
71
72    /**
73     * Stops Voyado tracking and clears consent
74     * Dispatches 'voyadoStopped' event when tracking is stopped
75     */
76    function stopVoyado() {
77        clearVoyadoConsentCookie();
78        window.voyadoStarted = false;
79        window.voyadoTrackingInitialized = false;
80        window.dispatchEvent(new CustomEvent('voyadoStopped'));
81    }
82
83    // Listen for consent granted event from CMP or custom integration
84    // Usage: window.dispatchEvent(new CustomEvent('voyadoConsentGranted'));
85    window.addEventListener('voyadoConsentGranted', function() {
86        console.log("Voyado: voyadoConsentGranted event received");
87        setVoyadoConsentCookie();
88        startVoyado();
89    });
90
91    // Listen for consent revoked event from CMP or custom integration
92    // Usage: window.dispatchEvent(new CustomEvent('voyadoConsentRevoked'));
93    window.addEventListener('voyadoConsentRevoked', function() {
94        stopVoyado();
95    });
96
97    // Check for existing consent at script load
98    var existingCookie = getCookieValueEarly('voyado-analytics');
99    
100    // Auto-start if consent cookie already exists (returning users or Shopware cookie banner)
101    if (existingCookie === '1') {
102        startVoyado();
103    }
104
105    // Handle late consent: check if flag was set before script loaded
106    // Useful for CMPs that grant consent before Voyado script is loaded
107    if (window.voyadoConsentAlreadyGranted === true) {
108        setVoyadoConsentCookie();
109        startVoyado();
110    }
111    // Expose helper for manual testing
112    window.voyadoGrantConsent = function() {
113        window.dispatchEvent(new CustomEvent('voyadoConsentGranted'));
114    };
115    window.voyadoRevokeConsent = function() {
116        window.dispatchEvent(new CustomEvent('voyadoConsentRevoked'));
117    };
118})();
119
120/**
121 * Helper function to check if tracking consent is currently active
122 * This dynamically checks the cookie value each time it's called
123 */
124function hasVoyadoConsent() {
125    // Check voyadoStarted flag first (set by consent mechanism)
126    if (window.voyadoStarted === true) {
127        return true;
128    }
129    // Also check cookie as fallback
130    return getCookieValue('voyado-analytics') === '1';
131}
132
133// Set up voyadoReady listener BEFORE DOMContentLoaded to catch early events
134window.addEventListener('voyadoReady', function() {
135    initVoyadoTracking();
136});
137document.addEventListener("DOMContentLoaded", function() {
138    initVoyadoTracking();
139});
140
141function initVoyadoTracking() {
142    // Skip if already initialized
143    if (window.voyadoTrackingInitialized) {
144        return;
145    }
146    
147    // Skip if no consent yet (will be called again when voyadoReady fires)
148    if (!hasVoyadoConsent()) {
149        return;
150    }
151    let currentUrl = window.location.href;
152    let originUrl = window.location.origin;
153    const urlParams = new URLSearchParams(window.location.search);
154    const eclub = urlParams.get("eclub");
155    if (currentUrl.includes('?') && eclub === null && currentUrl.includes('orderId=')) {
156        // Remove everything after and including the '?'
157        currentUrl = currentUrl.split('?')[0];
158    }
159    const voyadoAnalytics = hasVoyadoConsent() ? '1' : getCookieValue('voyado-analytics');
160    const apiUrl = "https://accelerator.voyado.com";
161    const voyadoCid = localStorage.getItem('voyado-cid');
162    const voyadoTrackingElement = document.getElementById("voyado-tracking-data");
163    // Script to remove session ID and email from storage
164    const loginPageElement = document.getElementById("login-page-url");
165    if (loginPageElement) {
166        const loginPageUrl = loginPageElement.getAttribute("data-login-page-url");
167        if (typeof loginPageUrl !== 'undefined') {
168            if(currentUrl === loginPageUrl)
169            {
170                if (typeof sessionStorage !== 'undefined') {
171                    // Check if voyado-session-id exists in sessionStorage before removing it
172                    if (sessionStorage.getItem('voyado-session-id')) {
173                        sessionStorage.removeItem('voyado-session-id');
174                    }
175                }
176            }
177        }
178    }
179
180    // Script to Store Voyado ContactId
181    if(voyadoAnalytics === '1')
182    {
183        const customerDataElement = document.getElementById("voyado-customer-data");
184        if (customerDataElement) {
185            const accountPageUrl = customerDataElement.getAttribute("data-account-page-url");
186            if (typeof accountPageUrl !== 'undefined') {
187                if(currentUrl === accountPageUrl)
188                {
189                    const customerId = customerDataElement.getAttribute("data-customer-id");
190                    const customerEmail = customerDataElement.getAttribute("data-customer-email");
191                    const customerData = {
192                        customerId: customerDataElement.getAttribute("data-customer-id"),
193                        customerEmail: customerDataElement.getAttribute("data-customer-email"),
194                        shopId: customerDataElement.getAttribute("data-shop-id"),
195                        saleschannelId: customerDataElement.getAttribute("data-saleschannel-id")
196                    };
197                    if (customerId) {
198                        fetch(apiUrl + "/api/v1/Shopware/store-contactid", {
199                            method: 'POST',
200                            headers: {
201                                'Content-Type': 'application/json'
202                            },
203                            body: JSON.stringify(customerData)
204                        })
205                        .then(response => {
206                            return response.text(); // Get the response as text first
207                        })
208                        .then(text => {
209                            try {
210                                const voyadoContactID = text;
211                                localStorage.setItem('voyado-cid', voyadoContactID);
212                            } catch (error) {
213                                // Handle JSON parsing error
214                                console.error('Error parsing JSON:', error);
215                            }
216                        })
217                        .catch(error => {
218                            console.log('Error sending data to webhook:', error);
219                        });
220
221                    } else {
222                        // If customerId doesn't exist, remove voyadoContactID from localStorage & sessionStorage
223                        localStorage.removeItem('voyado-cid');
224                    }
225                }
226            }
227        }
228    }
229    // Script to handle the Product Tracking Functionality and Back in Stock Functionality
230    productPageElement = document.getElementById("voyado-product-tracking-buy-widget-form");
231    if (productPageElement) {
232        const productPageUrl = productPageElement.getAttribute("data-product-page-url");
233        if (typeof productPageUrl !== 'undefined') {
234                const productAvailableStock = productPageElement.getAttribute("data-product-available-stock");
235                if(productAvailableStock != 1)
236                {
237                    // Script to handle the PO Editor Data
238                    const shopPrivacyPolicyLink = {
239                        shopId: productPageElement.getAttribute("data-app-shop-id"),
240                        saleschannelId: productPageElement.getAttribute("data-saleschannel-id"),
241                        languageId: productPageElement.getAttribute("data-language-id"),
242                        shopUrl: originUrl
243                    };
244                    fetch(apiUrl + "/api/v2/shopware/get-privacy-policy", {
245                        method: 'POST',
246                        headers: {
247                            'Content-Type': 'application/json'
248                        },
249                        body: JSON.stringify(shopPrivacyPolicyLink) // Send the JSON data
250                    })
251                    .then(response => {
252                        if (!response.ok) {
253                            throw new Error('Network response was not ok');
254                        }
255                        return response.json();
256                    })
257                    .then(data => {
258                        if (data) {
259                            if(data.privacyPolicy)
260                            {
261                                let privacyPolicyLink = document.querySelector('.voyado-privacy-policy-link'); // Select the <a> tag
262                                privacyPolicyLink.href = data.privacyPolicy; // Set the href with the received URL
263                                privacyPolicyLink.target = "_blank"; // Open link in a new tab
264                                privacyPolicyLink.rel = "noopener noreferrer";
265                            }
266                            if(data.resourceData)
267                            {
268                                let notifyTitle = document.querySelector("#voyadoNotifyCustomerForm h4");
269                                if (notifyTitle) {
270                                    notifyTitle.textContent = data.resourceData.BackInStockWidgetTitle || "Notify me when available";
271                                }
272
273                                // Update Email Label
274                                let emailLabel = document.querySelector("#voyadoNotifyCustomerForm label[for='email']");
275                                if (emailLabel) {
276                                    emailLabel.textContent = (data.resourceData.BackInStockWidgetEmail || "Email") + ":";
277                                }
278
279                                // Update Checkbox Label
280                                let emailSubscriptionLabel = document.querySelector("#voyadoNotifyCustomerForm label[for='agree_terms']");
281                                if (emailSubscriptionLabel) {
282                                    emailSubscriptionLabel.textContent = data.resourceData.BackInStockWidgetAcceptsEmail || "Email me with news and offer";
283                                }
284
285                                // Update Submit Button Text
286                                let submitButton = document.querySelector("#voyadoNotifyCustomerForm button[type='submit']");
287                                if (submitButton) {
288                                    submitButton.textContent = data.resourceData.BackInStockWidgetButton || "Notify Me";
289                                }
290
291                                let privacyPolicy = document.querySelector("#voyadoNotifyCustomerForm a");
292                                if (privacyPolicy) {
293                                    privacyPolicy.textContent = data.resourceData.BackInStockWidgetPrivacyLink || "Privacy Policy";
294                                }
295
296                                let voyadoResponseMessage = document.querySelector("#voyadoResponseMessage");
297                                if (voyadoResponseMessage) {
298                                    voyadoResponseMessage.textContent = data.resourceData.BackInStockWidgetResponse || "Thank you! We will notify you when the product is available.";
299                                }
300                            }
301                        }
302                    })
303                    .catch(() => {});
304                    const voyadoNotifyCustomerForm = document.getElementById("voyadoNotifyCustomerForm");
305                    const voyadoResponseMessage = document.getElementById("voyadoResponseMessage");
306                    voyadoNotifyCustomerForm.addEventListener("submit", function(event) {
307                        event.preventDefault(); // Prevent default form submission
308                        const formData = new FormData(voyadoNotifyCustomerForm); // Create a FormData object from the form
309                        const formObject = {}; // Initialize an empty object
310                        // Convert FormData to a plain JavaScript object
311                        formData.forEach(function(value, key) {
312                            formObject[key] = value;
313                        });
314                        // Add origin URL
315                        formObject.shopUrl = originUrl;
316                        // Convert the plain object to a JSON string
317                        const jsonData = JSON.stringify(formObject);
318                        // Send the form data via AJAX
319                        fetch(apiUrl + "/api/v2/shopware/back-in-stock/subscribe", {
320                            method: 'POST',
321                            headers: {
322                                'Content-Type': 'application/json'
323                            },
324                            body: jsonData // Send the JSON data
325                        })
326                        .then(response => {
327                            if (!response.ok) {
328                                throw new Error('Network response was not ok');
329                            }
330                            return response.text();
331                        })
332                        .then(data => {
333                            voyadoResponseMessage.innerText = data;
334                            document.querySelector('.voyadoResponseMessage').style.display = 'block';
335                            document.querySelector('.notify-customer').style.display = 'none';
336                        })
337                        .catch(error => {
338                            console.error('Error submitting form:', error);
339                        });
340                    });
341                }
342                if(voyadoAnalytics === '1' && voyadoCid)
343                {
344                    let voyadoSessionId = sessionStorage.getItem('voyado-session-id');
345                    let newSession;
346
347                    if (!voyadoSessionId) {
348                        // Generate a new session ID if none exists
349                        voyadoSessionId = generateSessionID();
350                        sessionStorage.setItem('voyado-session-id', voyadoSessionId);
351                        newSession = true; // This is a new session
352                    } else {
353                        // Session ID exists, no need to generate a new one
354                        newSession = false;
355                    }
356                    const productData = {
357                        customerId: voyadoCid,
358                        id: productPageElement.getAttribute("data-product-id"),
359                        productNumber: productPageElement.getAttribute("data-product-number"),
360                        url: productPageElement.getAttribute("data-product-page-url"),
361                        time: getCurrentDateTime(),
362                        language_id: productPageElement.getAttribute("data-language-id"),
363                        saleschannelId: productPageElement.getAttribute("data-saleschannel-id"),
364                        shopId : productPageElement.getAttribute("data-app-shop-id"),
365                        sessionId: voyadoSessionId,
366                        newSession: newSession,
367                        shopUrl: originUrl
368                    };
369                    // Send product data to webhook endpoint via fetch API
370                    fetch(apiUrl + "/api/v2/shopware/product-tracking", {
371                        method: 'POST',
372                        headers: {
373                            'Content-Type': 'application/json'
374                        },
375                        body: JSON.stringify(productData)
376                    })
377                    .then(response => {
378                        if (!response.ok) {
379                            throw new Error('Network response was not ok');
380                        }
381                        return response.text();
382                    })
383                    .catch(error => {
384                        console.log('Error sending data to webhook:');
385                    });
386                }
387        }
388    }
389    if(voyadoAnalytics === '1' && voyadoCid)
390    {
391        // Script to handle the Cart Tracking Functionality When click on add to cart button and display promotions
392        const buyButtons = document.querySelectorAll('.btn-buy');
393        // Add click event listener to each button
394        if(buyButtons)
395        {
396            buyButtons.forEach(function (button) {
397                button.addEventListener('click', function () {
398                    // Delay execution by 10 seconds
399                    setTimeout(function() {
400                        const cartElement = document.getElementById('voyad
400o-cart-tracking');
401                        let productDataArray;
402                        if (cartElement) {
403                            const cartData = JSON.parse(cartElement.getAttribute('data-cart'));
404                            productDataArray = extractCartData(cartData);
405                        }
406                        else
407                        {
408                            let cartTrackingData;
409                            const productPageUrl = productPageElement.getAttribute("data-product-page-url");
410                            if (typeof productPageUrl !== 'undefined') {
411                                if(currentUrl === productPageUrl)
412                                {
413                                    const quantityInput = document.querySelector('.quantity-selector-group-input');
414
415                                    if (quantityInput) {
416                                        cartTrackingData = {
417                                            productId: productPageElement.getAttribute("data-product-id"),
418                                            itemNumber: productPageElement.getAttribute("data-product-number"),
419                                            quantity: quantityInput.value
420                                        };
421                                    }
422                                }
423                            }
424                            else
425                            {
426                                const cartTrackingElement = document.getElementById("voyado-product-data");
427
428                                if (cartTrackingElement) {
429                                    cartTrackingData = {
430                                        productId: cartTrackingElement.getAttribute("data-product-id"),
431                                        itemNumber: cartTrackingElement.getAttribute("data-item-number"),
432                                        quantity: cartTrackingElement.getAttribute("data-quantity")
433                                    };
434                                }
435                            }
436                            productDataArray = cartTrackingData;
437                        }
438                        const previousCartData = JSON.parse(localStorage.getItem('previousCartData')) || [];
439                        if (!productDataArray || productDataArray.length === 0) {
440                            // Cart is empty, remove previous cart data from local storage
441                            localStorage.removeItem('previousCartData');
442                            return;
443                        }
444
445                        // Check if the cart has changed
446                        if (hasCartChanged(previousCartData, productDataArray)) {
447                            const cartId = generateCartId(32);
448                            const cartNewData = {
449                                customerId: voyadoCid,
450                                cartId: generateCartId(32),  // Using the random cart ID
451                                productdata: productDataArray,
452                                url: currentUrl,
453                                time: getCurrentDateTime(),
454                                language: voyadoTrackingElement.getAttribute("data-language"),
455                                shopId: voyadoTrackingElement.getAttribute("data-app-shop-id"),
456                                saleschannelId: voyadoTrackingElement.getAttribute("data-saleschannel-id"),
457                                shopUrl: originUrl
458                            };
459                            // First fetch request (cart tracking)
460                            fetch(apiUrl + "/api/v2/shopware/cart-tracking", {
461                                method: 'POST',
462                                headers: { 'Content-Type': 'application/json' },
463                                body: JSON.stringify(cartNewData)
464                            }).then(response => {
465                                if (!response.ok) {
466                                    throw new Error('Network response was not ok');
467                                }
468                                return response.text();
469                            }).catch(error => {
470                                console.error('Error sending cart data to webhook:', error);
471                            });
472
473                            // Update previous cart data in local storage
474                            localStorage.setItem('previousCartData', JSON.stringify(productDataArray));
475                            localStorage.setItem('voyado-cartid', cartId);
476                        }
477                    }, 2000); // 10-second delay
478                    // First check if promotions should be shown
479                    const promotionCheckData = {
480                            shopId: voyadoTrackingElement.getAttribute("data-app-shop-id"),
481                            saleschannelId: voyadoTrackingElement.getAttribute("data-saleschannel-id"),
482                            shopUrl: originUrl
483                        };
484                    fetch(apiUrl + "/api/v1/shopware/check-promotion-widget", {
485                        method: 'POST',
486                        headers: {
487                            'Content-Type': 'application/json'
488                        },
489                        body: JSON.stringify(promotionCheckData)
490                    }).then(response => {
491                        if (!response.ok) {
492                            throw new Error('Network response was not ok');
493                        }
494                        return response.json();
495                    }).then(data => {
496                        const promotionContainer = document.querySelector('.voyado-promotion');
497                        if (data.showPromotionToggle && promotionContainer) {
498                            // Script to  Display Promotions in minicart
499                            const promotionData = {
500                                    voyadoCid: voyadoCid,
501                                    shopId: voyadoTrackingElement.getAttribute("data-app-shop-id"),
502                                    saleschannelId: voyadoTrackingElement.getAttribute("data-saleschannel-id"),
503                                    shopUrl: originUrl
504                                };
505                            // Fetch and display promotions
506                            fetch(apiUrl + "/api/v2/shopware/get-promotions", {
507                                method: 'POST',
508                                headers: {
509                                    'Content-Type': 'application/json'
510                                },
511                                body: JSON.stringify(promotionData)
512                            }).then(response => {
513                                if (!response.ok) {
514                                    throw new Error('Network response was not ok');
515                                }
516                                return response.text();
517                            }).then(text => {
518                                const promotionresponse = JSON.parse(text);
519                                // Scenario 1: Widget enabled + active offers -> show widget
520                                if (promotionresponse && promotionresponse.length > 0) {
521                                    promotionContainer.style.display = 'block';
522                                    displayPromotionData(promotionresponse);
523                                } else {
524                                    // Scenario 3: Widget enabled but no offers -> remove completely
525                                    promotionContainer.remove();
526                                }
527                            }).catch(error => {
528                                console.log('Error fetching promotions:', error);
529                                promotionContainer.remove();
530                            });
531                        } else if (promotionContainer) {
532                            // Scenario 2: Widget disabled -> remove completely
533                            promotionContainer.remove();
534                        }
535                    }).catch(error => {
536                        console.log('Error checking promotions enabled:', error);
537                        const promotionContainer = document.querySelector('.voyado-promotion');
538                        if (promotionContainer) promotionContainer.remove();
539                    });
540                });
541            });
542        }
543        // Script to handle the Cart Tracking Functionality on CheckoutConfirmPage
544        const checkoutconfirmPageElement = document.getElementById("voyado-checkout-confirm-page-data");
545        if (checkoutconfirmPageElement) {
546            const checkoutConfirmPageUrl = checkoutconfirmPageElement.getAttribute("data-checkout-confirm-page-url");
547            if (typeof checkoutConfirmPageUrl !== 'undefined') {
548                if(currentUrl === checkoutConfirmPageUrl)
549                {
550                    const previousCartData = JSON.parse(localStorage.getItem('previousCartData')) || [];
551                    const productDataRaw = checkoutconfirmPageElement.getAttribute("data-product-data");
552                    const productDataArray = productDataRaw ? JSON.parse(productDataRaw) : [];
553                    if (!productDataArray || productDataArray.length === 0) {
554                        // Cart is empty, remove previous cart data from local storage
555                        localStorage.removeItem('previousCartData');
556                        return;
557                    }
558                    if (hasCartChanged(previousCartData, productDataArray)) {
559                        const cartId = localStorage.getItem('voyado-cartid');
560                        if (!cartId) {
561                            const cartId = generateCartId(32);
562                            localStorage.setItem('voyado-cartid', cartId);
563                        }
564                        const cartTrackingData = {
565                            customerId: voyadoCid,
566                            cartId: cartId,
567                            productdata: JSON.parse(checkoutconfirmPageElement.getAttribute("data-product-data")),
568                            url: checkoutconfirmPageElement.getAttribute("data-checkout-confirm-page-url"),
569                            time: checkoutconfirmPageElement.getAttribute("data-time"),
570                            language: checkoutconfirmPageElement.getAttribute("data-language"),
571                            saleschannelId: checkoutconfirmPageElement.getAttribute("data-saleschannel-id"),
572                            shopId: checkoutconfirmPageElement.getAttribute("data-shop-id"),
573                            shopUrl: originUrl
574                        };
575                        fetch(apiUrl + "/api/v2/shopware/cart-tracking", {
576                            method: 'POST',
577                            headers: { 'Content-Type': 'application/json' },
578                            body: JSON.stringify(cartTrackingData)
579                        }).then(response => {
580                            if (!response.ok) {
581                                throw new Error('Network response was not ok');
582                            }
583                            return response.text();
584                        }).catch(error => {
585                            console.error('Error sending cart data to webhook:', error);
586                        });
587
588                        // Update previous cart data in local storage
589                        localStorage.setItem('previousCartData', JSON.stringify(productDataArray));
590                    }
591                }
592            }
593        }
594        // Script to handle the Cart Tracking Functionality on CheckoutFinishPage
595        const checkoutfinishPageElement = document.getElementById("voyado-checkout-finish-page-data");
596        if (checkoutfinishPageElement) {
597            const checkoutFinishPageUrl = checkoutfinishPageElement.getAttribute("data-checkout-finish-page-url");
598            if (typeof checkoutFinishPageUrl !== 'undefined') {
599                if(currentUrl === checkoutFinishPageUrl)
600                {
601                    const previousCartData = JSON.parse(localStorage.getItem('previousCartData')) || [];
602                    const productDataArray = checkoutfinishPageElement.getAttribute("data-product-data");
603                    if (productDataArray.length > 0 && previousCartData.length > 0) {
604                        if (hasCartChangedFinishPage(previousCartData, productDataArray)) {
605                            const cartTrackingData = {
606                                customerId: voyadoCid,
607                                cartId: localStorage.getItem('voyado-cartid'),
608                                productdata: [],
609                                url: checkoutfinishPageElement.getAttribute("data-checkout-finish-page-url"),
610                                time: checkoutfinishPageElement.getAttribute("data-time"),
611                                language: checkoutfinishPageElement.getAttribute("data-language"),
612                                saleschannelId: checkoutfinishPageElement.getAttribute("data-saleschannel-id"),
613                                shopId: checkoutfinishPageElement.getAttribute("data-shop-id"),
614                                shopUrl: originUrl
615                            };
616                            // First fetch request (cart tracking)
617                            fetch(apiUrl + "/api/v2/shopware/cart-tracking", {
618                                method: 'POST',
619                                headers: { 'Content-Type': 'application/json' },
620                                body: JSON.stringify(cartTrackingData)
621                            }).then(response => {
622                                if (!response.ok) {
623                                    throw new Error('Network response was not ok');
624                                }
625                                return response.text();
626                            }).catch(error => {
627                                console.error('Error sending cart data to webhook:', error);
628                            });
629
630                            localStorage.removeItem('previousCartData');
631                            localStorage.removeItem('voyado-cartid');
632
633                        }
634                    }
635                }
636            }
637        }
638    }
639    // Script to handle the Cart Tracking Functionality on CheckoutCartPage and Display Promotions
640    const checkoutcartPageElement = document.getElementById("voyado-cart-page-data");
641    if (checkoutcartPageElement) {
642        const checkoutCartPageUrl = checkoutcartPageElement.getAttribute("data-checkout-cart-page-url");
643        if (typeof checkoutCartPageUrl !== 'undefined') {
644            if(currentUrl === checkoutCartPageUrl)
645            {
646                if(voyadoCid)
647                {
648                    const promotionItems = JSON.parse(checkoutcartPageElement.getAttribute("data-promotion-items"));
649                    if (promotionItems.length > 0) {
650                        const tagContainer = document.querySelector('.voyado-promotion-tags');
651
652                        // Clear existing tags
653                        tagContainer.innerHTML = '';
654
655                        // Loop through promotions and display them
656                        promotionItems.forEach((promotion) => {
657                            const promotionCode = promotion.referencedId;
658
659                            // Create a tag element
660                            const tag = document.createElement('div');
661                            tag.className = 'voyado-promotion-tag';
662                            tag.innerHTML = `
663                                <span>${promotionCode}</span>
664                                <button class="voyado-remove-tag" data-id="${promotion.id}" title="voyado-remove-tag">X</button>
665                            `;
666                            tagContainer.appendChild(tag);
667                        });
668                        document.querySelectorAll('.voyado-remove-tag').forEach(button => {
669                            button.addEventListener('click', function() {
670                                const lineItemId = this.getAttribute('data-id');
671                                // Generate the delete URL dynamically
672                                const deleteUrl = "{{ path('frontend.checkout.line-item.delete', {id: '__LINE_ITEM_ID__'}) }}".replace('__LINE_ITEM_ID__', lineItemId);
673                                // Dynamically find the form using the generated delete path
674                                const formSelector = `form[action^="${deleteUrl.split('?')[0]}"]`;
675                                const form = document.querySelector(formSelector);
676
677                                if (!form) {
678                                    console.error('Remove promotion form not found.');
679                                    return;
680                                }
681
682                                // Update the form's action URL to include the correct promotion ID
683                                form.setAttribute('action', deleteUrl);
684
685                                // Submit the form programmatically
686                                form.submit();
687                            });
688                        });
689                    }
690                    // First check if promotions widget is enabled
691                    const promotionCheckDataCart = {
692                        shopId: checkoutcartPageElement.getAttribute("data-shop-id"),
693                        saleschannelId: checkoutcartPageElement.getAttribute("data-saleschannel-id"),
694                        shopUrl: originUrl
695                    };
696                    const promotionContainerCart = document.querySelector('.voyado-promotion');
697                    fetch(apiUrl + "/api/v1/shopware/check-promotion-widget", {
698                        method: 'POST',
699                        headers: {
700                            'Content-Type': 'application/json'
701                        },
702                        body: JSON.stringify(promotionCheckDataCart)
703                    }).then(response => {
704                        if (!response.ok) {
705                            throw new Error('Network response was not ok');
706                        }
707                        return response.json();
708                    }).then(data => {
709                        if (data.showPromotionToggle) {
710                            // Widget enabled - fetch promotions
711                            const promotionData = {
712                                voyadoCid: voyadoCid,
713                                shopId: checkoutcartPageElement.getAttribute("data-shop-id"),
714                                saleschannelId: checkoutcartPageElement.getAttribute("data-saleschannel-id"),
715                                shopUrl: originUrl
716                            };
717                            fetch(apiUrl + "/api/v2/shopware/get-promotions", {
718                                method: 'POST',
719                                headers: {
720                                    'Content-Type': 'application/json'
721                                },
722                                body: JSON.stringify(promotionData)
723                            }).then(response => {
724                                if (!response.ok) {
725                                    throw new Error('Network response was not ok');
726                                }
727                                return response.text();
728                            }).then(text => {
729                                const promotionresponse = JSON.parse(text);
730                                // Scenario 1: Widget enabled + active offers -> show widget
731                                if (promotionresponse && promotionresponse.length > 0) {
732                                    if (promotionContainerCart) {
733                                        promotionContainerCart.style.display = 'block';
734                                    }
735                                    displayPromotionData(promotionresponse, promotionContainerCart);
736                                    const checkoutAsideAction = document.querySelector('.voyado-checkout-aside-action');
737                                    const promotionTags = document.querySelector('.voyado-promotion-tags');
738                                    const defaultCheckoutAside = document.querySelector('.checkout-aside-action');
739                                    if (checkoutAsideAction) checkoutAsideAction.style.display = 'flex';
740                                    if (promotionTags) promotionTags.style.display = 'block';
741                                    if (defaultCheckoutAside) defaultCheckoutAside.style.display = 'none';
742                                } else {
743                                    // Scenario 3: Widget enabled but no offers -> remove completely
744                                    if (promotionContainerCart) {
745                                        promotionContainerCart.remove();
746                                    }
747                                    document.querySelector('.voyado-checkout-aside-action')?.remove();
748                                    document.querySelector('.voyado-promotion-tags')?.remove();
749                                    const checkoutAsideActionHide = document.querySelector('.checkout-aside-action');
750                                    if (checkoutAsideActionHide) checkoutAsideActionHide.style.display = 'block';
751                                }
752                            }).catch(error => {
753                                if (promotionContainerCart) {
754                                    promotionContainerCart.remove();
755                                }
756                                document.querySelector('.voyado-checkout-aside-action')?.remove();
757                                document.querySelector('.voyado-promotion-tags')?.remove();
758                                const checkoutAsideActionErr1 = document.querySelector('.checkout-aside-action');
759                                if (checkoutAsideActionErr1) checkoutAsideActionErr1.style.display = 'block';
760                            });
761                        } else {
762                            // Scenario 2: Widget disabled -> remove completely
763                            if (promotionContainerCart) {
764                                promotionContainerCart.remove();
765                            }
766                            document.querySelector('.voyado-checkout-aside-action')?.remove();
767                            document.querySelector('.voyado-promotion-tags')?.remove();
768                            const checkoutAsideActionDisabled = document.querySelector('.checkout-aside-action');
769                            if (checkoutAsideActionDisabled) checkoutAsideActionDisabled.style.display = 'block';
770                        }
771                    }).catch(error => {
772                        // Error checking widget status -> remove completely
773                        if (promotionContainerCart) {
774                            promotionContainerCart.remove();
775                        }
776                        document.querySelector('.voyado-checkout-aside-action')?.remove();
777                        document.querySelector('.voyado-promotion-tags')?.remove();
778                        const checkoutAsideActionErr2 = document.querySelector('.checkout-aside-action');
779                        if (checkoutAsideActionErr2) checkoutAsideActionErr2.style.display = 'block';
780                    });
781                    if(voyadoAnalytics === '1')
782                    {
783                        const previousCartData = JSON.parse(localStorage.getItem('previousCartData')) || [];
784                        const productDataRaw = checkoutcartPageElement.getAttribute("data-product-data");
785                        const productDataArray = productDataRaw ? JSON.parse(productDataRaw) : [];
786                        if (!productDataArray || productDataArray.length === 0) {
787                            // Cart is empty, remove previous cart data from local storage
788                            localStorage.removeItem('previousCartData');
789                            return;
790                        }
791                        //Check for changes and update cart ID if necessary
792                        if (hasCartChanged(previousCartData, productDataArray)) {
793                            const cartId = localStorage.getItem('voyado-cartid');
794                            if (!cartId) {
795                                const cartId = generateCartId(32);
796                                localStorage.setItem('voyado-cartid', cartId);
797                            }
798                            const cartTrackingData = {
799                                customerId: voyadoCid,
800                                cartId: cartId,
801                                productdata: JSON.parse(checkoutcartPageElement.getAttribute("data-product-data")),
802                                url: checkoutcartPageElement.getAttribute("data-checkout-cart-page-url"),
803                                time: checkoutcartPageElement.getAttribute("data-time"),
804                                language: checkoutcartPageElement.getAttribute("data-language"),
805                                saleschannelId: checkoutcartPageElement.getAttribute("data-saleschannel-id"),
806                                shopId: checkoutcartPageElement.getAttribute("data-shop-id"),
807                                shopUrl: originUrl
808                            };
809                            // First fetch request (cart tracking)
810                            fetch(apiUrl + "/api/v2/shopware/cart-tracking", {
811                                method: 'POST',
812                                headers: {
813                                    'Content-Type': 'application/json'
814                                },
815                                body: JSON.stringify(cartTrackingData)
816                            }).then(response => {
817                                if (!response.ok) {
818                                    throw new Error('Network response was not ok');
819                                }
820                                return response.text();
821                            }).catch(error => {
822                                console.error('Error sending cart data to webhook:', error);
823                            });
824                            // Update previous cart data in local storage
825                            localStorage.setItem('previousCartData', JSON.stringify(productDataArray));
826                        }
827                    }
828                }
829
830            }
831        }
832    }
833    // Script to display promotions in minicart page
834    const cartButton = document.querySelector(".header-cart-btn");
835    if (cartButton) {
836        cartButton.addEventListener("click", function () {
837            if(voyadoCid)
838            {
839                // First check if promotions widget is enabled
840                const promotionCheckDataMinicart = {
841                    shopId: voyadoTrackingElement.getAttribute("data-app-shop-id"),
842                    saleschannelId: voyadoTrackingElement.getAttribute("data-saleschannel-id"),
843                    shopUrl: originUrl
844                };
845                const promotionContainerMinicart = document.querySelector('.voyado-promotion');
846                fetch(apiUrl + "/api/v1/shopware/check-promotion-widget", {
847                    method: 'POST',
848                    headers: {
849                        'Content-Type': 'application/json'
850                    },
851                    body: JSON.stringify(promotionCheckDataMinicart)
852                }).then(response => {
853                    if (!response.ok) {
854                        throw new Error('Network response was not ok');
855                    }
856                    return response.json();
857                }).then(data => {
858                    if (data.showPromotionToggle) {
859                        // Widget enabled - fetch promotions
860                        const promotionData = {
861                            voyadoCid: voyadoCid,
862                            shopId: voyadoTrackingElement.getAttribute("data-app-shop-id"),
863                            saleschannelId: voyadoTrackingElement.getAttribute("data-saleschannel-id"),
864                            shopUrl: originUrl
865                        };
866                        fetch(apiUrl + "/api/v2/shopware/get-promotions", {
867                            method: 'POST',
868                            headers: {
869                                'Content-Type': 'application/json'
870                            },
871                            body: JSON.stringify(promotionData)
872                        }).then(response => {
873                            if (!response.ok) {
874                                throw new Error('Network response was not ok');
875                            }
876                            return response.text();
877                        }).then(text => {
878                            const promotionresponse = JSON.parse(text);
879                            // Scenario 1: Widget enabled + active offers -> show widget
880                            if (promotionresponse && promotionresponse.length > 0) {
881                                if (promotionContainerMinicart) {
882                                    promotionContainerMinicart.style.display = 'block';
883                                }
884                                displayPromotionData(promotionresponse);
885                            } else {
886                                // Scenario 3: Widget enabled but no offers -> remove completely
887                                if (promotionContainerMinicart) {
888                                    promotionContainerMinicart.remove();
889                                }
890                            }
891                        }).catch(error => {
892                            console.log('Error fetching promotions:', error);
893                            if (promotionContainerMinicart) {
894                                promotionContainerMinicart.remove();
895                            }
896                        });
897                    } else {
898                        // Scenario 2: Widget disabled -> remove completely
899                        if (promotionContainerMinicart) {
900                            promotionContainerMinicart.remove();
901                        }
902                    }
903                }).catch(error => {
904                    console.log('Error checking promotions enabled:', error);
905                    if (promotionContainerMinicart) {
906                        promotionContainerMinicart.remove();
907                    }
908                });
909            }
910        });
911    }
912    // Script to assign promotions through soft identification
913    if (eclub) {
914        try {
915            const decryptionData = {
916                shopId: voyadoTrackingElement.getAttribute("data-app-shop-id"),
917                saleschannelId: voyadoTrackingElement.getAttribute("data-saleschannel-id"),
918                eclub: eclub
919            };
920            // Step 1: Get decryption key from API
921            fetch(apiUrl + "/api/v2/shopware/getSoftDecryptionKey", {
922                method: "POST",
923                headers: { "Content-Type": "application/json" },
924                body: JSON.stringify(decryptionData),
925            })
926            .then(response => {
927                if (!response.ok) {
928                    throw new Error("Network response was not ok");
929                }
930                return response.json(); // parse JSON response
931            })
932            .then(result => {
933                // Step 1: Parse the stringified JSON
934                const parsedData = JSON.parse(result.decryptionkey);
935                // Step 2: Store contactId
936                localStorage.setItem("voyado-cid", parsedData.contactId);
937                const voyadoCid = localStorage.getItem("voyado-cid");
938
939                // Step 3: Send promotion data
940                const promotionData = {
941                    voyadoCid: voyadoCid,
942                    email: parsedData.email,
943                    shopId: voyadoTrackingElement.getAttribute("data-app-shop-id"),
944                    saleschannelId: voyadoTrackingElement.getAttribute("data-saleschannel-id"),
945                    shopUrl: originUrl
946                };
947                return fetch(apiUrl + "/api/v2/shopware/assignPromotionBySoftid", {
948                    method: "POST",
949                    headers: { "Content-Type": "application/json" },
950                    body: JSON.stringify(promotionData),
951                });
952            })
953            .then(response => {
954                if (response && !response.ok) {
955                    throw new Error("Promotion API failed");
956                }
957                if (response) return response.text();
958            })
959            .then(result => {
960                if (result) console.log("Promotion assigned:", result);
961            })
962            .catch(error => {
963                console.error("Error:", error);
964            });
965
966        } catch (err) {
967            console.error("Decryption failed:", err);
968        }
969    }
970    // Mark tracking as initialized to prevent duplicate initialization
971    window.voyadoTrackingInitialized = true;
972}
973// Get Cookie Value
974function getCookieValue(cookieName) {
975    const name = cookieName + "=";
976    const decodedCookie = decodeURIComponent(document.cookie);
977    const cookieArray = decodedCookie.split(';');
978
979    for (let i = 0; i < cookieArray.length; i++) {
980        let cookie = cookieArray[i].trim();
981        if (cookie.indexOf(name) === 0) {
982            return cookie.substring(name.length, cookie.length);
983        }
984    }
985    return null; // return null if the cookie is not found
986}
987
988// Generate a random cart ID
989function generateCartId(length) {
990    const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
991    let cartId = '';
992    for (let i = 0; i < length; i++) {
993        const randomIndex = Math.floor(Math.random() * charset.length);
994        cartId += charset[randomIndex];
995    }
996    return cartId;
997}
998
999// Generate a random integer within a given range [min, max]
1000function randomIndexFromInterval(min, max) {
1001    return Math.floor(Math.random() * (max - min + 1) + min);
1002}
1003
1004//Function to check if the cart has changed
1005function hasCartChanged(previousData, currentData) {
1006    if (!Array.isArray(previousData)) {
1007        previousData = [previousData];
1008    }
1009    if (!Array.isArray(currentData)) {
1010        currentData = [currentData];
1011    }
1012    if (previousData.length !== currentData.length) {
1013        return true; // Different number of items
1014    }
1015    for (let i = 0; i < currentData.length; i++) {
1016        const currentItem = currentData[i];
1017        const previousItem = previousData.find(item => item.productId === currentItem.productId);
1018        if (!previousItem || previousItem.quantity !== currentItem.quantity) {
1019            return true; // Item quantity has changed or item is new
1020        }
1021    }
1022    return false; // No changes detected
1023}
1024
1025//Function to check cart information in checkout finishpage
1026function hasCartChangedFinishPage(previousData, currentData) {
1027    // Parse data if it's a JSON string
1028    if (typeof previousData === "string") {
1029        try {
1030            previousData = JSON.parse(previousData);
1031        } catch (e) {
1032            console.error("Error parsing previousData:", e);
1033            previousData = [];
1034        }
1035    }
1036    if (typeof currentData === "string") {
1037        try {
1038            currentData = JSON.parse(currentData);
1039        } catch (e) {
1040            console.error("Error parsing currentData:", e);
1041            currentData = [];
1042        }
1043    }
1044    previousData = Array.isArray(previousData) ? previousData : [previousData];
1045    currentData = Array.isArray(currentData) ? currentData : [currentData];
1046    // Sort both arrays by productId for consistent comparison
1047    previousData = [...previousData].sort((a, b) => a.productId.localeCompare(b.productId));
1048    currentData = [...currentData].sort((a, b) => a.productId.localeCompare(b.productId));
1049    // Compare each item in both sorted arrays
1050    return previousData.every((prevItem, index) => {
1051        const currItem = currentData[index];
1052        return currItem && prevItem.productId === currItem.productId && prevItem.quantity === currItem.quantity;
1053    });
1054}
1055//Function to display promotions
1056function displayPromotionData(promotionResponse, targetContainer) {
1057        const container = targetContainer || document.querySelector('.voyado-promotion');
1058        if (!container) {
1059            return;
1060        }
1061        container.innerHTML = ''; // Clear previous content
1062
1063        // Add header
1064        const header = document.createElement('div');
1065        header.className = 'voyado-promotion-header';
1066        header.innerHTML = `<span> <svg width="18" height="18" viewBox="0 0 19 17" fill="none" xmlns="http://www.w3.org/2000/svg">
1067            <path d="M16.7191 4.44906C17.6214 4.44906 18.385 5.21262 18.385 6.115V9.44687C18.385 9.75924 18.1073 10.0022 17.8297 10.0022H17.2744V15C17.2744 15.9371 16.5108 16.6659 15.6084 16.6659H3.39156C2.45447 16.6659 1.72562 15.9371 1.72562 15V10.0022H1.17031C0.857949 10.0022 0.615 9.75924 0.615 9.44687V6.115C0.615 5.21262 1.34385 4.44906 2.28094 4.44906H3.63451C3.28744 3.99787 3.11391 3.44256 3.11391 2.78312C3.11391 1.25602 4.25924 0.00656211 5.68223 0.00656211C7.13992 0.00656211 8.0076 0.908945 9.5 3.33844C10.9577 0.908945 11.8254 0.00656211 13.2831 0.00656211C14.7061 0.00656211 15.8861 1.25602 15.8861 2.78312C15.8861 3.44256 15.6779 3.99787 15.3308 4.44906H16.7191ZM10.1941 4.34494L10.1247 4.44906H13.2831C14.0813 4.44906 14.7755 3.72021 14.7755 2.78312C14.7755 1.88074 14.0813 1.11719 13.2831 1.11719C12.4848 1.11719 11.8948 1.46426 10.1941 4.34494ZM5.68223 1.11719C4.88396 1.11719 4.22453 1.88074 4.18982 2.78312C4.18982 3.72021 4.88396 4.44906 5.68223 4.44906H8.84057L8.77115 4.34494C7.07051 1.46426 6.48049 1.11719 5.68223 1.11719ZM7.27875 15.5553V10.0022H2.83625V15C2.83625 15.3124 3.0792 15.5553 3.39156 15.5553H7.27875ZM7.27875 8.89156V5.55969H2.28094C1.96857 5.55969 1.72562 5.83734 1.72562 6.115V8.89156H7.27875ZM10.6106 15.5553V5.55969H8.38937V15.5553H10.6106ZM16.1638 15V10.0022H11.7213V15.5553H15.6084C15.8861 15.5553 16.1638 15.3124 16.1638 15ZM17.2744 8.89156V6.115C17.2744 5.83734 16.9967 5.55969 16.7191 5.55969H11.7213V8.89156H17.2744Z" fill="#21211F"/>
1068            </svg>Your Offers</span>
1069             <button id="voyadodropdownIcon" class="voyado-dropdown-icon" style="margin-bottom:5px" title="offer-button"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16">
1070            <path d="M7.247 11.14l-4.796-5.481c-.566-.648.208-1.659 1.034-1.127l4.763 4.57 4.763-4.57c.825-.532 1.6.48 1.034 1.127L8.753 11.14a1 1 0 0 1-1.506 0z"></path>
1071        </svg></button>`;
1072        container.appendChild(header);
1073        // Add promotion list
1074        const promotionList = document.createElement('ul');
1075        promotionList.className = 'voyado-promotion-list';
1076        promotionList.style.display = 'none';
1077        promotionResponse.forEach((promotion) => {
1078                const card = document.createElement('li');
1079                card.className = 'voyado-promotion-card';
1080
1081                // Promotion info
1082                const info = document.createElement('div');
1083                info.className = 'voyado-promotion-info';
1084
1085                const title = document.createElement('span');
1086                title.className = 'voyado-promotion-title';
1087                title.innerText = promotion.name;
1088
1089                const description = document.createElement('p');
1090                description.className = 'voyado-promotion-description';
1091                description.innerText = promotion.value;
1092
1093                const expiry = document.createElement('p');
1094                expiry.className = 'voyado-promotion-expiry';
1095                expiry.innerHTML = `
1096                <svg width="15" height="16" viewBox="0 0 15 16" fill="none" xmlns="http://www.w3.org/2000/svg">
1097                    <path fill-rule="evenodd" clip-rule="evenodd" d="M8.35611 0.878906C11.7501 0.878906 14.5 3.62879 14.5 7.02279C14.5 9.93145 12.4804 12.367 9.76649 13.0041L10.2741 13.884C10.6003 14.4335 10.1883 15.1203 9.57013 15.1203H1.32844C0.693144 15.1203 0.298229 14.4335 0.607293 13.884L2.91623 9.88185C2.46665 9.02793 2.21222 8.05516 2.21222 7.02279C2.21222 3.62879 4.96211 0.878906 8.35611 0.878906ZM3.39858 9.04578L4.72814 6.74121C5.0372 6.19177 5.8442 6.19177 6.15326 6.74121L9.34945 12.2813C11.8255 11.8136 13.7072 9.63159 13.7072 7.02279C13.7072 4.07472 11.2794 1.67167 8.35611 1.67167C5.38326 1.67167 3.00498 4.09949 3.00498 7.02279C3.00498 7.74009 3.14488 8.42277 3.39858 9.04578ZM10.016 9.22766L7.98451 7.76601C7.91018 7.69169 7.88541 7.61736 7.88541 7.51827V3.55447C7.88541 3.40583 8.00928 3.25719 8.1827 3.25719H8.52953C8.67817 3.25719 8.82681 3.40583 8.82681 3.55447V7.19621L10.561 8.45967C10.6848 8.55877 10.7344 8.73218 10.6353 8.88083L10.4123 9.15334C10.338 9.2772 10.1398 9.32675 10.016 9.22766ZM5.8442 9.19655C5.8442 9.12787 5.80986 9.07636 5.74118 9.07636H5.14022C5.07154 9.07636 5.0372 9.12787 5.0372 9.19655L5.15739 12.5619C5.15739 12.6134 5.2089 12.6478 5.26041 12.6478H5.62099C5.6725 12.6478 5.72401 12.6134 5.72401 12.5619L5.8442 9.19655ZM5.44929 12.9912C5.70684 12.9912 5.93005 13.2144 5.93005 13.4719C5.93005 13.7467 5.70684 13.9527 5.44929 13.9527C5.17456 13.9527 4.96852 13.7467 4.96852 13.4719C4.96852 13.2144 5.17456 12.9912 5.44929 12.9912Z" fill="black"/>
1098                </svg>
1099                 ${promotion.expiresOn}.`;
1100
1101                info.appendChild(title);
1102                info.appendChild(description);
1103                info.appendChild(expiry);
1104                const applyButton = document.createElement('button');
1105                applyButton.className = 'btn btn-primary voyado-promotion-apply-button';
1106                applyButton.innerText = 'Apply';
1107                applyButton.title = "voyado-promotion-apply-button";
1108                applyButton.onclick = () => {
1109                let inputGroup = document.querySelector('.js-offcanvas-cart-promotion');
1110                if (!inputGroup) {
1111                    inputGroup = document.querySelector('.voyado-checkout-aside-action');
1112                }
1113                const inputField = inputGroup.querySelector('input[type="text"]');
1114                // Set the promotion code
1115                inputField.value = promotion.value;
1116                inputField.dispatchEvent(new Event('input'));
1117            };
1118
1119            card.appendChild(info);
1120            card.appendChild(applyButton);
1121            promotionList.appendChild(card);
1122        });
1123
1124        container.appendChild(promotionList);
1125
1126        const dropdownButton = header.querySelector("#voyadodropdownIcon");
1127        dropdownButton.addEventListener("click", () => {
1128        const isOpen = promotionList.style.display === "block";
1129        promotionList.style.display = isOpen ? "none" : "block";
1130        promotionList.style.border = isOpen ? "none" : "block";
1131        dropdownButton.innerHTML = isOpen
1132            ? `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16">
1133                <path d="M7.247 11.14l-4.796-5.481c-.566-.648.208-1.659 1.034-1.127l4.763 4.57 4.763-4.57c.825-.532 1.6.48 1.034 1.127L8.753 11.14a1 1 0 0 1-1.506 0z"></path>
1134            </svg>`
1135            : `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16">
1136                <path d="M7.247 4.86l-4.796 5.481c-.566.648.208 1.659 1.034 1.127l4.763-4.57 4.763 4.57c.825.532 1.6-.48 1.034-1.127L8.753 4.86a1 1 0 0 0-1.506 0z"></path>
1137            </svg>`;
1138    });
1139}
1140
1141// Generate a random session ID using alphanumeric characters
1142function generateSessionID() {
1143    return Math.random().toString(36).substr(2, 12); // Generate a random string and take a substring of length 12
1144}
1145// Function to format the current date and time
1146function getCurrentDateTime() {
1147    const now = new Date();
1148    const year = now.getFullYear();
1149    const month = String(now.getMonth() + 1).padStart(2, '0'); // Months are 0-based
1150    const day = String(now.getDate()).padStart(2, '0');
1151    const hours = String(now.getHours()).padStart(2, '0');
1152    const minutes = String(now.getMinutes()).padStart(2, '0');
1153    const seconds = String(now.getSeconds()).padStart(2, '0');
1154    return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
1155}
1156
1157// Function to extract desired data from cart line items
1158function extractCartData(cartItems) {
1159    return cartItems.map(item => {
1160        return {
1161            productId: item.id,
1162            quantity: item.quantity,
1163            itemNumber: item.payload.productNumber
1164        };
1165    });
1166}

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.