GLBE_PARAMS_2 = {
        isExternal: false,
        appUrl: "https://crossborder-integration.global-e.com/",
        pixelUrl: "https://utils.global-e.com",
        pixelEnabled: true,
        geAppUrl: "https://web.global-e.com/",
        env: "Production",
        geCDNUrl: "https://web-she.global-e.com/",
        analyticsSDKCDN : "https://globale-analytics-sdk.global-e.com/PROD/bundle.js",
        apiUrl: "https://api.global-e.com/",
        emi: "ttruh",
        mid: "30000463",
        hiddenElements: ".ge-hide,.afterpay-paragraph,form[action='https://payments.amazon.com/checkout/signin']",
        operatedCountries: ["AD","AE","AG","AI","AL","AM","AO","AR","AT","AW","AZ","BA","BB","BD","BE","BF","BG","BH","BI","BJ","BL","BM","BN","BO","BR","BS","BT","BW","BZ","CA","CG","CH","CI","CK","CL","CM","CN","CO","CR","CV","CW","CY","CZ","DE","DJ","DK","DM","DO","DZ","EC","EE","EG","ES","ET","FI","FJ","FK","FO","FR","GA","GB","GD","GE","GF","GG","GH","GI","GL","GM","GN","GP","GQ","GR","GT","GW","GY","HK","HN","HR","HT","HU","ID","IE","IL","IM","IN","IS","IT","JE","JM","JO","JP","KE","KG","KH","KI","KM","KN","KR","KW","KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT","LU","LV","MA","MC","MD","ME","MF","MG","MK","MN","MO","MQ","MR","MS","MT","MU","MV","MW","MX","MY","MZ","NA","NC","NG","NI","NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF","PG","PH","PK","PL","PT","PY","QA","RE","RO","RS","RW","SA","SB","SC","SE","SG","SH","SI","SK","SL","SM","SN","SR","ST","SV","SX","SZ","TC","TD","TG","TH","TL","TM","TN","TO","TT","TV","TW","TZ","UG","US","UY","UZ","VA","VC","VE","VG","VN","VU","WF","WS","YT","ZA","ZM","ZW"],
        c1Enabled:"true",
        siteId: "7ce243a1e1a2",
        isTokenEnabled: "true",
        cartAttributesSkipPagesRegex: "",
        suppressCartAttributesUpdate: false,
        isShopPayEnabled : false,
        suppressCartAttributesUpdateOnNotOperated: false,
        useBrowsingStartEventInsteadOfPageViewed: true,
        isAnalyticsPageViewEventsDisabled : true,
        ft3DayAttribution: false, 
        masValidUtmSourceList: "", 
        masUtmStorageLifetime: "4320", 
        isCountrySelectorEnabled: false,
        countrySelectorPosition: "left",
        allowedCountries: [],
        updateCartStockAttribute:  false,
        fulfillmentConstraintErrorHandling:  true,
        fulfillmentConstraintSetCartAttributeForNonOperatedCountries:  false,
        isLegacyAnalyticsSDKEnabled: true,
        isAnalyticsSDKEnabled:false,
        masSkipCssFile:true,
        masShopify_DisableCountryFulfillmentConstraint: [""], 
        masShopifyBrowsingCookieConsent:false   
};
// Version v4
function GlobaleApp() {
    this.init();
}

GlobaleApp.prototype = (function () {
    var self = this;
    var events = {
        onXHRRequest: "onXHRRequest",
        CountryFulfillmentConstraintCartAttributeUpdatedEvent: "CartMapVariantsByCountryUpdated",
        FulfillmentConstraintDisabledCountriesUpdatedEvent: "FulfillmentConstraintDisabledCountriesUpdated",
        CartAttributesNewSessionEvent: "CartAttributesNewSession"
    };

    var initGlobale = function (merchantId) {
        if (!window.GlobalE) {
            window.GlobalE = {};
        }

        window.GlobalE.MandatoryConsentGroups = {
            Essentials: 1,
            Analytics: 2,
            Marketing: 3,
        }

        window.GlobalE.ConsentPermissions = {
            NotAllowed: 0,
            Allowed: 1
        }

        window.GlobalE.MerchantID = merchantId;
        window.GlobalE.NECDisabled = false;
        window.GlobalE.LogErrors = true;
        window.GlobalE.IsCookieConsentScriptLoaded = false;
        window.GlobalE.GE_CONSENT_COOKIE = "GlobalE_Consent";
        window.GlobalE.GE_UTM_PARAMS_SESSION_STORAGE = "GE_UTM_PARAMS";
        window.GlobalE.GE_CT_COOKIE = "GlobalE_CT_Data";
        window.GlobalE.GE_CT_TRACKED = "GlobalE_CT_Tracked";
        window.GlobalE.TAGS_COOKIE = [
            "GlobalE_AB_Data",
            "GlobalE_Ref",
            "GlobalE_sess_ID",
            "_ga", "_gcl_au", "_gid", "cid", "_fbp",
            "_VWO",
            "_vwo_uuid_v2",
            "_vwo_ssm",
            "fs_uid",
            "fs_cid",
            "fs_session",
            "fs_csrftoken",
            "fs_trusted_device",
            "fs_last_activity",
            "_fs_tab_id",
            "raygun4js-sid",
            "raygun4js-userid"
        ];
        window.GlobalE.GE_ESSENTIALS_ONLY = "GE_ESSENTIALS_ONLY";

        window.GlobalE.IsUndefined = function (obj) {
            if (typeof obj == "undefined" || obj == null)
                return true;
            else
                return false;
        }

        window.GlobalE.IsUndefinedOrEmpty = function (obj) {
            if (window.GlobalE.IsUndefined(obj)) return true;
            if (typeof obj == "string" && obj == "") return true;
            return false;
        }

        window.GlobalE.HandleError = function (ex, f) {
            if (window.GlobalE.LogErrors) {
                console.log("Exception in GEClient." + f + " : " + ex.message);
            }
        }

        window.GlobalE.GetCookie = function (c_name, isJson) {
            try {
                var c_value = document.cookie;
                var c_start = c_value.indexOf(" " + c_name + "=");
                if (c_start == -1) {
                    c_start = c_value.indexOf(c_name + "=");
                }
                if (c_start == -1) {
                    c_value = null;
                } else {
                    c_start = c_value.indexOf("=", c_start) + 1;
                    var c_end = c_value.indexOf(";", c_start);
                    if (c_end == -1) {
                        c_end = c_value.length;
                    }
                    c_value = unescape(c_value.substring(c_start, c_end));
                }

                if (!window.GlobalE.IsUndefined(isJson) && isJson) {
                    return JSON.parse(c_value);
                }

                return c_value;
            } catch (ex) {
                window.GlobalE.HandleError(ex, "GetCookie");
            }
        }

        window.GlobalE.SetCookie = function (c_name, value, expire, isJson, isMinutes, setExpireAsSession) {
            try {
                if (window.GlobalE.CookieDomain == null) {
                    window.GlobalE.CookieDomain = document.domain;
                }
                if (!window.GlobalE.IsUndefined(isJson) && isJson) {
                    value = JSON.stringify(value);
                }
                var c_value = "";
                if (!setExpireAsSession) {
                    var exdate = new Date();
                    if (!isMinutes)
                        exdate.setDate(exdate.getDate() + expire);
                    else
                        exdate.setTime(exdate.getTime() + expire * 60 * 1000);

                    c_value = escape(value) + ((expire == null) ? "" : "; expires=" + exdate.toUTCString()) + ";domain=" + window.GlobalE.CookieDomain + ";path=/";
                } else {
                    c_value = escape(value) + ";domain=" + this.CookieDomain + ";path=/";
                }

                document.cookie = c_name + "=" + c_value;
            } catch (ex) {
                window.GlobalE.HandleError(ex, "SetCookie");
            }
        }

        window.GlobalE.EraseCookie = function (name) {
            if (window.GlobalE.CookieDomain == null) {
                window.GlobalE.CookieDomain = document.domain;
            }
            document.cookie = name + "=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;domain=" + window.GlobalE.CookieDomain + ";path=/";
        }

        window.GlobalE.IsAnalyticsAllowed = function () {
            return window.GlobalE.IsGroupAllowed(window.GlobalE.MandatoryConsentGroups.Analytics);
        }

        window.GlobalE.IsMarketingAllowed = function () {
            return window.GlobalE.IsGroupAllowed(window.GlobalE.MandatoryConsentGroups.Marketing);
        }

        window.GlobalE.IsGroupAllowed = function (groupId) {
            if (groupId == window.GlobalE.MandatoryConsentGroups.Essentials) {
                return true;
            }

            try {
                var groups = window.GlobalE.GetConsentGroupsFromCookie();
                if (Object.keys(groups).length === 0) {
                    return false;
                }

                var groupPermission = groups[groupId];

                if (groupPermission != null && groupPermission == window.GlobalE.ConsentPermissions.Allowed) {
                    return true;
                }
                return false;

            } catch (ex) {
                return false;
            }
        }

        window.GlobalE.GetConsentGroupsFromCookie = function () {
            var groups = {};

            try {
                var cookieValue = window.GlobalE.GetCookie(window.GlobalE.GE_CONSENT_COOKIE, true);

                if (typeof cookieValue != undefined &&
                    cookieValue != null &&
                    cookieValue != "" &&
                    cookieValue.groups != null) {
                    Object.keys(cookieValue.groups).forEach(function (key) {
                        groups[key] = cookieValue.groups[key];
                    });
                }

                return groups;
            } catch (ex) {
                return groups;
            }
        }

        window.GlobalE.DisableNEC = function (expirationDays) {
            //    enabled = this.IsUndefined(enabled) ? true : enabled;
            window.GlobalE.SetCookie(window.GlobalE.GE_ESSENTIALS_ONLY, true, expirationDays, true);
            //delete all none essentials cookie
            window.GlobalE.EraseCookie(window.GlobalE.GE_CT_TRACKED);
            window.GlobalE.EraseCookie(window.GlobalE.GE_CT_COOKIE);
            window.GlobalE.EraseCookie(self.trackingCookieName);
            window.GlobalE.TAGS_COOKIE.forEach(function (cookie) {
                var value = self.utils.getCookie(cookie);
                if (value) {
                    window.GlobalE.EraseCookie(cookie);
                }
            });
        }

        window.GlobalE.ForceCleanCookies = function () {
            if (window.GlobalE.NECDisabled) { //global setting that allows merchants to entirely disable NEC
                var sesCookie = self.utils.getCookie(window.GlobalE.GE_CT_COOKIE, true);
                var trackCookie = self.utils.getCookie(window.GlobalE.GE_CT_TRACKED, true);
                var trackingCookie = self.utils.getCookie(self.trackingCookieName);

                var tags = false;
                window.GlobalE.TAGS_COOKIE.forEach(function (cookie) {
                    if (!tags) {
                        var value = self.utils.getCookie(cookie);
                        if (value) {
                            tags = true;
                        }
                    }
                });


                if (sesCookie != null || trackCookie != null || trackingCookie != null || tags) {
                    //cookie exists from older sessions, clear
                    window.GlobalE.DisableNEC(365);
                }
            }
        }

        window.GlobalE.AreNoneEseentialCookieAllowed = function (callback) {
            if (typeof GECC != "undefined" && typeof GECC.IsAllowed != "undefined") {
                GECC.IsAllowed(function (response) {
                    var expiration = typeof response.consentexpiration == "number" ? response.consentexpiration : 365;

                    if (typeof response.allow == "boolean") {

                        callback({allow: response.allow, expiration: expiration});
                    } else {
                        callback({allow: undefined, expiration: expiration});
                    }
                });
            } else {
                callback({allow: undefined, expiration: 365});
            }
        }

        window.GlobalE.IsNECAllowed = function (callback) {
            if (self.masShopifyBrowsingCookieConsent) {
                //new logic - check shopify customer consent
                if (typeof Shopify !== 'undefined' &&
                    Shopify.customerPrivacy &&
                    typeof Shopify.customerPrivacy.currentVisitorConsent === 'function' )
                {

                    var visitorConsent = Shopify.customerPrivacy.currentVisitorConsent();
                    if (visitorConsent && visitorConsent.analytics == "no")
                    {
                        window.GlobalE.NECDisabled = true;
                        window.GlobalE.ForceCleanCookies();
                        callback(false);
                        return;
                    }
                    else
                    {
                        callback(true);
                        return;
                    }
                }
                else
                {
                    callback(true);
                    return;
                }
            }
            else
            {
                //old consent logic
                if (window.GlobalE.NECDisabled) {
                    callback(false);
                }
                else {
                    window.GlobalE.AreNoneEseentialCookieAllowed(function (ccresponse) {
                        var necValueStr = window.GlobalE.GetCookie(window.GlobalE.GE_ESSENTIALS_ONLY, false);
                        if (necValueStr != null) {
                            try {
                                var necOnly = necValueStr == "true";
                                if (ccresponse.allow != undefined && ccresponse.allow != !necOnly) {
                                    //cookie value consent is different from consent script logic, in this case , we overrite cookie value with consent script respone
                                    window.GlobalE.SetCookie(window.GlobalE.GE_ESSENTIALS_ONLY, !ccresponse.allow, ccresponse.expiration, true);
                                    callback(ccresponse.allow);
                                } else {
                                    //consent script retuened same value as cookie , or was undefined, in this case we return cookie value
                                    callback(!necOnly);
                                }
                            } catch (err) {
                                callback(true);
                            }
                        } else {
                            //check merchant cookie consent
                            if (typeof ccresponse != "undefined" &&
                                typeof ccresponse.allow == "boolean" && ccresponse.allow == false) {
                                //block cookies
                                window.GlobalE.DisableNEC(ccresponse.expiration);
                                callback(false);
                            } else {
                                callback(true);//default true
                            }
                        }

                    });
                }
            }
        }

        window.GlobalE.TryToGetConsentProviderId = function () {
            var providerId = 0

            try {
                var optanonConsentStr = self.utils.getCookie("OptanonConsent", false);

                //Identify OneTrust by global obj
                if (typeof OneTrust !== "undefined" && OneTrust != null) {
                    return 2;
                }

                //Identify OneTrust by cookie
                if (optanonConsentStr != null && optanonConsentStr.length > 0) {
                    return 2;
                }
                return providerId;
            } catch (e) {
                return providerId;
            }
        };

        window.GlobalE.GetCookieConsentScript = function (isCheckoutPage, callback) {
            if (window.GlobalE.IsCookieConsentScriptLoaded) {
                return;
            }

            try {
                var identifiedProviderId = window.GlobalE.TryToGetConsentProviderId();

                var url = self.geCDNUrl + 'merchant/cookieConsentScript?merchantId=' + self.merchantId + "&country=" + self.countryCode + "&culture=" + "&providerId=" + identifiedProviderId;

                var cacheBuster = Math.floor((new Date()).getTime() / (1000 * 60 * 10)) //cache for 10 min
                url += "&cb=" + cacheBuster;
                self.utils.getScript(url).then(function () {
                    try {
                        window.GEConsent = new GECCV2();
                        window.GEConsent.Init(isCheckoutPage);
                        window.GlobalE.IsCookieConsentScriptLoaded = true;

                        if (!window.GlobalE.IsAnalyticsAllowed()) {
                            window.GlobalE.NECDisabled = true;
                            window.GlobalE.ForceCleanCookies();
                        } else {
                            callback();
                        }

                    } catch (e) {
                    }
                });
            } catch (e) {
            }
        }



        window.GlobalE.GetCDNUrl = function () {
            //var cdnEnabled = GlobalE.MPH.Get(MPH.K.CDNEnabled, MPH.T.Bool);

            //var url = GlobalE.MPH.Get(MPH.K.CDNUrl, MPH.T.String);
            var url = GLBE_PARAMS.geCDNUrl;
            // if (url == null || !cdnEnabled) {
            //     url = this.GEBaseURL;
            // }

            var valid = url.indexOf("/", url.length - 1) !== -1;
            if (!valid) url += "/";

            return url;
        }

        window.GlobalE.WriteLog = function (message1, message2, handler, identifier, isEnableClientLogs, isError) {
            try {

                if (!isEnableClientLogs) {
                    return;
                }

                if (message2 !== null) {
                    message1 += message2;
                }

                var url = GlobalE.GetCDNUrl();

                url = url + "shared/WriteLog?message=" + message1 + "&handler=" + handler + "&identifier=" + identifier + "&iserror=" + isError;

                var iframe = document.getElementById("ge_srv_log");
                if (!iframe) {
                    iframe = document.createElement("iframe");
                    iframe.setAttribute("id", "ge_srv_log");
                    iframe.setAttribute("title", "ge_srv_log");
                    iframe.setAttribute("width", "1px");
                    iframe.setAttribute("height", "1px");
                    iframe.style.display = "none";
                    document.getElementsByTagName("body")[0].appendChild(iframe);
                }
                iframe.setAttribute("src", url);
            }
            catch (e) {
            }
        }

        window.GlobalE.GetUTMData = function () {
            var utmParamsFromQuery = null;

            utmParamsFromQuery = localStorage.getItem(this.GE_UTM_PARAMS_SESSION_STORAGE);
            if (utmParamsFromQuery) {
                utmParamsFromQuery = JSON.parse(utmParamsFromQuery);
                //check if expired, take from session
                if (new Date(utmParamsFromQuery.expiry) < new Date())
                {
                    localStorage.removeItem(this.GE_UTM_PARAMS_SESSION_STORAGE);
                    utmParamsFromQuery = null;
                }
            }

            if (!utmParamsFromQuery) {
                var sessionValue = sessionStorage.getItem(this.GE_UTM_PARAMS_SESSION_STORAGE);
                if (sessionValue) {
                    utmParamsFromQuery =
                        {
                            value: sessionValue,
                            expiry: null
                        };
                }
            }

            return utmParamsFromQuery;
        }

        window.GlobalE.SafeExecute = function (func) {
            return function () {
                try {
                    return func.apply(this, arguments);
                }
                catch (e) {
                    console.log('SafeExecute ex: ' + e);
                }
            };
        };

        // Get UTM data from the URL and save it on session storage if exists
        // then create Iframe that saves the data on session storage.
        // The Iframe will be created oif analytics is allow by cookie consent and the UTM data exists.
        window.GlobalE.SaveUTMData = function (isEnableClientLogs, isError) {
            try {

                //removing the check for cookie consent since there are no personal details
                // Try to get UTM parameters from cookies/session storage to avoid saving them again.
                var utmParamsFromStorage = window.GlobalE.GetUTMData();

                var queryStringToUse = window.GlobalE.GetUtmParamQueryString();
                var utmParamsFromUrl = window.GlobalE.GetUtmParams(queryStringToUse);
                var utmParamsFromUrlObj = parseUtmFromQueryString(utmParamsFromUrl);

                var is3DASourceAndActive = self.ft3DayAttribution && JSON.parse(self.masValidUtmSourceList)?.some(utmSource => utmSource.toLowerCase() === utmParamsFromUrlObj.utmSource?.toLowerCase());

                // Exit function if UTM parameters found.
                if (!this.IsUndefined(utmParamsFromStorage) && !is3DASourceAndActive){
                    sessionStorage.removeItem('GE_UTM_PARAMS_RAW');
                    return;
                }

                // Parse URL for UTM params and exit if no params found.
                utmParamsFromStorage = this.SetTrafficSourceParams();
                if (utmParamsFromStorage === null) {
                    return;
                }

                if (is3DASourceAndActive) {
                    var storedItem = {
                        value: utmParamsFromStorage,
                        expiry: new Date(new Date().getTime() + self.masUtmStorageLifetime * 60000)
                    };

                    localStorage.setItem(this.GE_UTM_PARAMS_SESSION_STORAGE, JSON.stringify(storedItem));
                } else {
                    sessionStorage.setItem(this.GE_UTM_PARAMS_SESSION_STORAGE, utmParamsFromStorage);
                }

                sessionStorage.removeItem('GE_UTM_PARAMS_RAW');
            }
            catch (err) {
                var msg = "GEClient.SaveUTMData failed with " + err.message + ";MerchantId:" + GlobalE.MerchantID;
                var safeLog = GlobalE.SafeExecute(GlobalE.WriteLog);
                safeLog("SaveUTMData logs:", msg, "MerchantClientSDK", GlobalE.MerchantID, true, true);
            }
        }

        function parseUtmFromQueryString(queryString) {
            var urlParams = new URLSearchParams(queryString);
            var utmParams = {
                utmSource: urlParams.has('utm_source') ? urlParams.get('utm_source') : null,
                utmMedium: urlParams.has('utm_medium') ? urlParams.get('utm_medium') : null,
                utmCampaign: urlParams.has('utm_campaign') ? urlParams.get('utm_campaign') : null,
                bfDirectToCheckout: urlParams.has('bf_direct_to_checkout') ? urlParams.get('bf_direct_to_checkout') : null,
                bfDirectToCheckoutEnabled: urlParams.has('bf_direct_to_checkout_enabled') ? urlParams.get('bf_direct_to_checkout_enabled') : null,
                clientId: urlParams.has('bf_client_id') ? urlParams.get('bf_client_id') : null
            };

            return utmParams;
        }

        // Flow of searching the UTM params
        window.GlobalE.SetTrafficSourceParams = function() {
            var self = this;
            try{
                // Get the query string from the URL.

                var queryString = window.GlobalE.GetUtmParamQueryString();


                if(!self.IsUndefinedOrEmpty(queryString)) {
                    // first check for the Google click ID (gclid) parameter.
                    var urlObj = new URL(window.location.href);
                    var gclidValue = urlObj.searchParams.get("gclid");
                    if(gclidValue != null) {
                        return "gclid="+gclidValue;
                    }

                    var utmParams = self.GetUtmParams(queryString);
                    if(utmParams != null) {
                        return utmParams;
                    }
                }

                // Get document.referrer and return it only if it is different from current site domain
                var documentReferrer = gleTags.GetReferrer();
                if(!self.IsUndefinedOrEmpty(documentReferrer)) {
                    if(documentReferrer.includes('google')) {
                        return "utm_source=google&utm_medium=organic&utm_campaign=(not-set)";
                    } else{
                        var referrerToReport = gleTags.GetDomain(document.referrer);
                        return "utm_source=" + referrerToReport + "&utm_medium=referral&utm_campaign=(not-set)";
                    }
                }

                return "utm_source=direct&utm_medium=(none)&utm_campaign=(not-set)";
            }
            catch (err) {
                var msg = "GEClient.setTrafficSourceParams failed with " + err.message + ";MerchantId:" + GlobalE.MerchantID;
                var safeLog = GlobalE.SafeExecute(GlobalE.WriteLog);
                safeLog("setTrafficSourceParams logs:", msg, "MerchantClientSDK", GlobalE.MerchantID, true, true);
                return null;
            }

        }

        window.GlobalE.GetUtmParamQueryString = function () {
            try {
                var urlSearch = window.location.search;
                var storedUtm = sessionStorage.getItem('GE_UTM_PARAMS_RAW');

                // If URL contains UTM params, use that
                if (urlSearch.includes('utm_') || urlSearch.includes('bf_')) {
                    return urlSearch;
                }

                // Otherwise use stored UTM params if they exist, or fall back to URL search

                return storedUtm || urlSearch;
            } catch (e) {
                return window.location.search;
            }
        }
        // returns the value of the "utm" query string parameters if exists otherwise returns null.
        window.GlobalE.GetUtmParams = function(queryString) {
            if (!queryString) return null;

            try {

                // Remove '?' from begin
                queryString = queryString.slice(1);

                // Split the query string into parameters.
                var params = queryString.split('&');

                // Iterate over the parameters and find the UTM params.
                var utmParams = [];
                for (var i = 0; i < params.length; i++) {
                    var param = params[i];
                    var keyValue = param.split('=');
                    var key = keyValue[0];
                    var value = keyValue[1];

                    if ((key.startsWith('utm_') || key.startsWith('bf_')) && typeof(value) !== 'undefined') {
                        utmParams.push(key + '=' + value);
                    }
                }

                // If there are no UTM params, return null.
                if (utmParams.length === 0) {
                    return null;
                }

                // Join the UTM params into a query string.
                return utmParams.join('&');
            }
            catch (err) {
                var msg = "GEClient.GetUtmParams failed with " + err.message + ";MerchantId:" + GlobalE.MerchantID;
                var safeLog = GlobalE.SafeExecute(GlobalE.WriteLog);
                safeLog("GetUtmParams logs:", msg, "MerchantClientSDK", GlobalE.MerchantID, true, true);
                return null;
            }
        }

        // POC for MixedPanel We add the UTM params to the URL
        window.GlobalE.SetMixPanelUTMParams = function (url) {
            // Example of possible value in session storage: 
            // 1) utm_source=google&utm_medium=organic&utm_campaign=(not-set)
            // 2) gclid=gclidValue"

            try{
                var geUtmValue = window.GlobalE.GetUTMData();


                if(!GlobalE.IsUndefinedOrEmpty(geUtmValue)) {
                    // Split the query string into parameters.
                    var params = geUtmValue.value.split('&');

                    var jParam = { "borderfreeProperties": {} };

                    // Iterate over the parameters and find the UTM params.
                    for (var i = 0; i < params.length; i++) {
                        var param = params[i];
                        var keyValue = param.split('=');
                        var key = keyValue[0];
                        var value = keyValue[1];

                        if (key.startsWith('gclid')) {
                            url = self.utils.addParameter(url, 'gclid', value);
                        }
                        if (key.startsWith('utm_source')) {
                            url = self.utils.addParameter(url, 'cs', value);
                        }
                        if (key.startsWith('utm_medium')) {
                            url = self.utils.addParameter(url, 'cm', value);
                        }
                        if (key.startsWith('utm_campaign')) {
                            url = self.utils.addParameter(url, 'cn', value);
                        }
                        if (key.startsWith('bf_direct_to_checkout_enabled')) {
                            jParam.borderfreeProperties.directToCheckoutEnabled = value;
                        }
                        else {
                            if (key.startsWith('bf_direct_to_checkout')) {
                                jParam.borderfreeProperties.directToCheckout = value;
                            }
                        }
                        if (key.startsWith('bf_client_id')) {
                            jParam.borderfreeProperties.clientId = value;
                        }

                        //utm_term and utm_content: to be implemented
                    }

                    if (jParam.borderfreeProperties.directToCheckout != 'undefined' || jParam.borderfreeProperties.directToCheckoutEnabled != 'undefined' || jParam.borderfreeProperties.clientId != 'undefined') {
                        url = self.utils.addParameter(url, 'j',JSON.stringify(jParam));
                    }
                }

                return url;
            }catch (e) {
                var safeLog = GlobalE.SafeExecute(GlobalE.WriteLog);
                safeLog("SetMixPanelUTMParams:", e.message + ";MerchantId: " + GlobalE.MerchantID, "MerchantClientSDK", GlobalE.MerchantID, true, true);
                return url;
            }
        }

        if(!window.gleTags){
            window.gleTags = {};
        }

        //extract UTM tags
        window.gleTags.GetReferrer = function () {
            //handle referer
            if (document.referrer != "") {
                //referrer exists - check that its different from current domain
                if (window.gleTags.GetDomain(document.location.href) != window.gleTags.GetDomain(document.referrer)) {
                    //referer exists and not a local referer - REPORT
                    //url = GlobalE.AddParameter(url, "dr", document.referrer);            
                    return document.referrer;
                }
            }

            return null;
        }
        window.gleTags.GetHostName = function (url) {
            var match = url.match(/:\/\/(www[0-9]?\.)?(.[^/:]+)/i);
            if (match != null && match.length > 2 && typeof match[2] === 'string' && match[2].length > 0) {
                return match[2];
            }
            else {
                return null;
            }
        }
        window.gleTags.GetDomain = function (url) {

            var hostName = window.gleTags.GetHostName(url);
            var domain = hostName;

            if (hostName != null) {
                var parts = hostName.split('.').reverse();

                if (parts != null && parts.length > 1) {
                    domain = parts[1] + '.' + parts[0];

                    if (hostName.toLowerCase().indexOf('.co.uk') != -1 && parts.length > 2) {
                        domain = parts[2] + '.' + domain;
                    }
                }
            }

            return domain;
        }

        window.gleTags.GetReferrerToReport = function () {
            var referrer = document.referrer;
            if (referrer) {
                var referrer = self.utils.parseUrl(document.referrer).hostname;
                if (referrer == document.location.hostname) {
                    return null;
                }
                var storedRef = self.utils.getCookie(self.referreCookieName);
                if (storedRef && referrer == storedRef) {
                    return null;
                }
                return referrer;
            }
            return null;
        };

        window.gleTags.SaveReferrer = function () {
            var referrer = window.gleTags.GetReferrer();
            if (referrer != null) {
                GlobalE.SetCookie(self.referreCookieName, referrer, 30, false, true);
            }
        }

        window.gleTags.GetHitCount = function () {
            if (sessionStorage) {
                var count = sessionStorage.getItem(self.hitInfoKey);
                if (count) {
                    try {
                        return parseInt(count);
                    }
                    catch (err) {
                        return 0;
                    }
                }
                return 0;
            }
            else {
                return 0;
            }
        }

        window.gleTags.UpdateHitCount = function (count) {
            if (sessionStorage) {
                sessionStorage.setItem(self.hitInfoKey, count);
            }
        }

        window.gleTags.GetSessionUID = function () {
            var uid = self.utils.getCookie(self.trackingCookieName);
            var jsonUid = !uid? null: tryParseJSONObject(uid);
            if (!self.useBrowsingStartEventInsteadOfPageViewed && uid && !jsonUid)
            {
                jsonUid = {sid:uid, expiry:null};

            }
            var isNew = false;
            if (!jsonUid || (jsonUid.expiry && new Date(jsonUid.expiry) < new Date()))  {
                isNew = true;
            }
            return { id: !isNew ? jsonUid?.sid : null, isNew: isNew };
        }

        function tryParseJSONObject (jsonString){
            try {
                var o = JSON.parse(jsonString);
                if (o && typeof o === "object") {
                    return o;
                }
            }
            catch (e) { }

            return null;
        };

        window.gleTags.CreateSessionUID = function ()
        {
            var generator = function (min, max) {
                return Math.floor(Math.random() * (max - min + 1) + min);
            }
            var maxGen = 999999999;
            var minGen = 100000000;
            var leftPart = generator(minGen, maxGen);
            var rightPart = generator(minGen, maxGen);
            uid = leftPart + "." + rightPart;
            uid += "." + self.merchantId;

            return uid;
        }

        window.gleTags.SetSessionUID = function (sid) {
            if (self.useBrowsingStartEventInsteadOfPageViewed)
            {
                self.utils.setCookie(self.trackingCookieName, {"sid": sid, "expiry": new Date(new Date().getTime() + 30 * 60000)}, 365 * 2, true);

            }
            else
            {
                self.utils.setCookie(self.trackingCookieName, sid, 365 * 2, false);
            }
        }
        

        function updateCartAttributesNewSession(id) {
            const evt = new CustomEvent(events.CartAttributesNewSessionEvent, {
                detail: [{
                    key: "GLBE_SESS_ID",
                    value:id
                }]
            });
            document.dispatchEvent(evt); 
        }

        window.gleTags.Track = async function () {
            try {
                if (self.pixelEnabled) {
                    var uid = window.gleTags.GetSessionUID();
                    if (!uid.id) { //here we create a new SID 
                        uid.id = window.gleTags.CreateSessionUID();

                    }
                    var hits = window.gleTags.GetHitCount();
                    var environment = self.environment == "Production" ? "live" : "local";
                    var baseUrl = self.pixelUrl + "/set";
                    var doLog = self.utils.getQueryParam("gatracklog") != null ? self.utils.getQueryParam("gatracklog") : "false";
                    var url =baseUrl;
                    var addNewCountryLogicSession = false;
                    var detectedCountry;
                    if (self.useBrowsingStartEventInsteadOfPageViewed && uid.isNew) {
                        url = self.utils.addParameter(baseUrl, "t", "Browsing Start")

                        try {
                            if(GeShopify.routes){

                                let result = await fetch(GeShopify.routes.root + 'browsing_context_suggestions.json' + '?country[enabled]=true' + `&country[exclude]=${GeShopify.country}` + '&language[enabled]=true' + `&language[exclude]=${GeShopify.language}`).then(r => r.json());

                                if (result && result.detected_values && result.detected_values.country && result.detected_values.country.handle) {
                                    detectedCountry = result.detected_values.country.handle;

                                    //1.New Country != Old Country
                                    //2.Old Country is not an operated country
                                    if (detectedCountry !== self.countryCode && !isCountryOperated(self.countryCode)) {
                                        addNewCountryLogicSession = true;
                                    }
                                }
                            }
                        } catch (e) {
                        }
                    }
                    else
                    {
                        if (!self.isAnalyticsPageViewEventsDisabled)
                        {
                            url = self.utils.addParameter(baseUrl, "t", "pv");
                        }
                    }
                    url = self.utils.addParameter(url, "co", (detectedCountry || self.countryCode));

                    if (!self.isAnalyticsPageViewEventsDisabled ||
                        (self.useBrowsingStartEventInsteadOfPageViewed && uid.isNew))
                    {
                        url = self.utils.addParameter(url, "sid", uid.id);
                        var locationUrl = location.href;
                        if (addNewCountryLogicSession)
                        {
                            locationUrl = self.utils.addParameter(locationUrl, "NewCountryLogicSession", "true");
                        }
                        url = self.utils.addParameter(url, "p",encodeURIComponent(locationUrl));
                        url = self.utils.addParameter(url, "ti", document.title);
                        url = self.utils.addParameter(url, "e", environment);
                        url = self.utils.addParameter(url, "hc", hits.toString());
                        url = self.utils.addParameter(url, "log", doLog);
                        url = self.utils.addParameter(url, "m", self.merchantId);
                        url = self.utils.addParameter(url, "cdu", self.geCDNUrl);
                        url = self.utils.addParameter(url, "f", "gleTags.handlePixelResponse");
                        url = self.utils.addParameter(url, "mpups", isMixpanelUserProfileSetupNeeded());

                        var referrer = window.gleTags.GetReferrerToReport();
                        if (referrer) {
                            url = self.utils.addParameter(url, "dr", encodeURIComponent(referrer));
                            self.utils.setCookie(self.referreCookieName, referrer, 30, false, true);
                        }

                        window.gleTags.SaveReferrer();

                        // POC for MixedPanel We add the UTM params to the URL
                        url = GlobalE.SetMixPanelUTMParams(url);

                        var i = document.createElement("img");
                        i.width = 1;
                        i.height = 1;
                        i.src = url;
                    }

                    window.gleTags.SetSessionUID(uid.id);
                    window.gleTags.UpdateHitCount(++hits);

                    if (uid.isNew)
                    {
                        updateCartAttributesNewSession(uid.id);
                       
                    }

                    function isMixpanelUserProfileSetupNeeded() {
                        const isSetupNeeded = { no: "0", yes: "1" }
                        const key = "GEMPUPS";
                        const value = sessionStorage.getItem(key);

                        // if record in session storage doesn't exist or set to yes, 
                        // then return positive result and switch of the trigger
                        if (value === null || value ===  isSetupNeeded.yes) {
                            sessionStorage.setItem(key, isSetupNeeded.no);
                            return isSetupNeeded.yes;
                        }

                        // otherwise no setup needed
                        return isSetupNeeded.no;
                    }
                }
            }
            catch (err) {
                console.log('glbe error:', err);
            }
        };

        window.GlobalE.LegacyAnalyticsCallback= function () {
            if (self.isLegacyAnalyticsSDKEnabled) {
                window.GlobalE.SaveUTMData();
                window.gleTags.Track();
            }
        }

        if (self.masShopifyBrowsingCookieConsent) {
            window.GlobalE.IsNECAllowed(function (isNecAllowed)
            {
                if (isNecAllowed)
                    window.GlobalE.LegacyAnalyticsCallback();
            });
        }
        else //no mas 
        {
            window.GlobalE.GetCookieConsentScript(false,
                function()
                {
                    window.GlobalE.LegacyAnalyticsCallback();
                });

        }
    }

    var initFreeShippingBanner = function () {
        //SHE-12408 Thank you page 
        if(typeof GeShopify != "undefined" && typeof GeShopify.Checkout != "undefined")
            return;

        //add missing gleTags function
        if (window.gleTags) //handle existing GEM integration
        {
            window.gleTags.freeShippingBannerShowed = function () { };
        }
        else {
            window.gleTags = {
                freeShippingBannerShowed: function () { }
            };
        }

        if (window.GlobalE)//handle existing GEM integration
            window.GlobalE.IsMobile = false;
        else
            window.GlobalE = { IsMobile: false };

        var url = self.geCDNUrl + 'merchant/freeShippingBanner?merchantId=' + self.merchantId + "&country=" + self.countryCode + "&currency=" + self.currencyCode + "&culture=";
        var cacheBuster = Math.floor((new Date()).getTime() / (1000 * 60 * 10)) //cache for 10 min
        url += "&cb=" + cacheBuster;
        self.utils.getScript(url);
    };

    var initRestrictions = function () {
        if (isCountryOperated(self.countryCode)) {
            self.utils.contentLoaded(window, function () {
                if (document.location.pathname == '/cart') {
                    self.utils.httpRequest("/cart.js?cb=" + Date.now(), {
                        method: "GET",
                        contentType: "application/json"
                    }).then(function (cart) {
                        var variants = cart.items.map(function (i) { return 'productCode=' + i.variant_id; });
                        var query = 'encMerchantId=' + self.merchantCode + '&countryCode=' + self.countryCode + '&' + variants.join('&');
                        self.utils.httpRequest(self.apiUrl + "browsing/ProductCountryS?" + query, {
                            method: "POST",
                            contentType: "application/json"
                        }).then(function (response) {
                            var restricted = response.filter(function (e) { return e.IsRestricted || e.IsForbidden }).map(function (e) { return e.ProductCode })
                            if (restricted.length) {
                                var restrctedProducts = cart.items.filter(function (p) {
                                    return restricted.indexOf(p.variant_id.toString()) > -1;
                                }).map(function (p) {
                                    return p.product_title;
                                });
                                var notification = self.utils.notification('The following item(s) cannot be shipped to your country:\n' + restrctedProducts.join('\n') + '\nPlease edit your shopping cart to continue', 'warning');
                                var checkoutBtns = document.querySelectorAll('form[action$="/cart"] [type="submit"]');
                                checkoutBtns.forEach(function (b) { b.disabled = true });
                                self.utils.addListener(events.onXHRRequest, function (e) {
                                    var cartChangeUrls = ["/add.js", "/update.js", "/change.js"];
                                    var changed = false;
                                    for (var i = 0; i < cartChangeUrls.length; i++) {
                                        if (e.url.indexOf(cartChangeUrls[i]) > -1) {
                                            changed = true;
                                        }
                                    }
                                    if (changed) {
                                        self.utils.httpRequest("/cart.js?cb=" + Date.now(), {
                                            method: "GET",
                                            contentType: "application/json"
                                        }).then(function (cart) {
                                            var restrctedProducts = cart.items.filter(function (p) {
                                                return restricted.indexOf(p.variant_id.toString()) > -1;
                                            });
                                            if (restrctedProducts.length === 0) {
                                                notification.parentElement.removeChild(notification);
                                                checkoutBtns.forEach(function (b) { b.disabled = false; });
                                            }
                                        });
                                    }
                                });
                            }
                        });
                    }).catch(function(error){
                        //ignore error. Endpoint might not exists on the headless merchant.
                    });
                }
                else {
                    let variants = typeof window.meta !== 'undefined' && window.meta.product && window.meta.product.variants;
                    if (variants) {
                        var query = 'encMerchantId=' + self.merchantCode + '&countryCode=' + self.countryCode + '&' + variants.map(function (v) { return 'productCode=' + v.id }).join('&');
                        self.utils.httpRequest(self.apiUrl + "browsing/ProductCountryS?" + query, {
                            method: "POST",
                            contentType: "application/json"
                        }).then(function (response) {
                            var isRestricted = !!response.filter(function (e) { return e.IsRestricted || e.IsForbidden }).length;
                            if (isRestricted) {
                                document.querySelectorAll('[action$="/cart/add"]').forEach(function (e) {
                                    e.style.display = 'none';
                                });
                                self.utils.notification('This product cannot be shipped to your country', 'warning');
                            }
                        });
                    }
                }
            });
        }
    };

    var isCountryOperated = function (countryCode) {
        return self.operatedCountries.indexOf(countryCode) > -1
    };

    function addAnalyticsSDKScript(src, callback) {
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = src;

        // If a callback is provided, set it to execute once the script is loaded
        if (callback) {
            script.onload = callback;
        }

        document.head.appendChild(script);
    }
    var addStyles = function () {
        if(!GLBE_PARAMS.masSkipCssFile){
            var cssLinkUrl = GLBE_PARAMS.appUrl + "resources\/css\/" + GLBE_PARAMS.mid + "\/" + GLBE_PARAMS.countryCode;

            if(self.utils.isLinkAdded(cssLinkUrl))  //can be already added in liquid file
                return;

            var cssElement = document.createElement('link');
            cssElement.rel = "stylesheet";
            cssElement.type = "text/css"
            cssElement.href = cssLinkUrl;
            document.getElementsByTagName('head')[0].appendChild(cssElement);
        }

        if (!isCountryOperated(self.countryCode))
            return;

        //to add common styles faster
        var styles = `
/* hide express buttons for GE operated countries */
[data-alternative-payments],
[data-testid="GooglePay-button"],
iframe[title="Checkout with PayPal"],
.ge-hide-display-none {
display: none;
}

/* Hide shop pay checkout modal */
#shopify-pay-modal {
display: none !important;
}

.ge-hide, .shopify-payment-button, .afterpay-paragraph, form[action='https://payments.amazon.com/checkout/signin'], .ProductMeta__Klarna, div.ProductMeta div.Affirm {
visibility: hidden;
}
`;

        if(!self.isShopPayEnabled){
            const shopPayStyle = `
               .additional-checkout-buttons,
               [data-shopify="payment-button"]
            [data-testid="ShopifyPay-button"]
            {
                display: none;
            }
            `;
            styles += shopPayStyle;
        }else{
            const shopPayStyle = `
            [data-shopify="payment-button"],
            .shopify-payment-button{
                visibility: visible !important;
            }
            
            shopify-accelerated-checkout > :not(shop-pay-wallet-button){
                display: none;
            }
            
             shopify-accelerated-checkout-cart > :not(shop-pay-wallet-button){
                display: none;
            }
            
            
            `
            styles += shopPayStyle;
        }

        var styleSheet = document.createElement("style")
        styleSheet.innerHTML = styles
        document.head.appendChild(styleSheet)
    };

    var setXHRListener = function () {
        var _open = XMLHttpRequest.prototype.open;
        XMLHttpRequest.prototype.open = function (method, url) {
            if (!this.isGlobalE) {
                this.addEventListener('load', function () {
                    self.utils.emitEvent(events.onXHRRequest, { xhr: this, url: url });
                });
            }
            _open.apply(this, arguments);
        };

        const _fetch = fetch;
        fetch = function (resource, initOptions) {
            const url = resource instanceof Request ? resource.url : resource.toString();
            if (!this.isGlobalE) {
                return _fetch(resource, initOptions).then(function (res) {
                    self.utils.emitEvent(events.onXHRRequest, { xhr: this, url: url });
                    return res;
                });
            }
            else {
                return _fetch(resource, initOptions);
            }
        };
    };

    var createReplacementCookie = function () {
        var cookieDomain = window.location.hostname; //can require custom value on stores whene checkout page domain is different

        function getParameterByName(name, url = window.location.href) {
            name = name.replace(/[\[\]]/g, '\\$&');
            var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'), results = regex.exec(url);
            if (!results) return null;
            if (!results[2]) return '';
            return decodeURIComponent(results[2].replace(/\+/g, ' '));
        }

        function ticksToDate(ticks) {
            return Number.isInteger(ticks) ? new Date(ticks / 1e+4 + new Date('0001-01-01T00:00:00Z').getTime()) : null;
        }

        var existingReplacementCookie = document.cookie.split(';').map(function (x) { return x.trim().split('='); }).reduce(function (a, b) { a[b[0]] = b[1]; return a; }, {})['GE_Replacement'];
        var existingReplacement = null;
        if (existingReplacementCookie && existingReplacementCookie.length > 0 && existingReplacementCookie != null) {
            existingReplacement = JSON.parse(decodeURIComponent(existingReplacementCookie));
        }

        //* If URL contains the query parameter replacementExpire, create cookie GE_Replacement
        let replacementExpireParam = parseInt(getParameterByName('replacementExpire'));
        if (replacementExpireParam !== null && replacementExpireParam > Date.now()) {
            //debugger;
            var countryCode = getParameterByName('glCountry');
            var currencyCode = getParameterByName('glCurrency');

            const cookieStringifiedValue = encodeURIComponent(JSON.stringify({ glCountry: countryCode, glCurrency: currencyCode }));
            const expirationInUTC = ticksToDate(replacementExpireParam).toUTCString();

            document.cookie = 'GE_Replacement=' + cookieStringifiedValue + ';expires=' + expirationInUTC + ';path=/;domain=.' + cookieDomain;

            // Change country and currency
            if (existingReplacement == null || existingReplacement.glCountry != countryCode || existingReplacement.glCurrency != currencyCode) {
                if (GLBE_PARAMS.countryCode != countryCode || GLBE_PARAMS.currencyCode != currencyCode) {
                    const form = document.createElement('form'); form.method = 'post'; form.action = '/localization';
                    const params = { form_type: 'localization', _method: 'put', return_to: document.location.pathname + document.location.search, country_code: countryCode, currency_code: currencyCode }
                    for (var key in params) { if (params.hasOwnProperty(key)) { var hiddenField = document.createElement('input'); hiddenField.type = 'hidden'; hiddenField.name = key; hiddenField.value = params[key]; form.appendChild(hiddenField); } }
                    document.body.appendChild(form);
                    form.submit();
                }
            }
        }

        if (existingReplacement != null) {
            //detect 404 checkout page. SHE-67, SHE-201
            var url = window.location.href;
            var regexp = /(\/[a-z\-]+\/)[0-9]+\/checkouts\/[a-z0-9]+/g;
            var match = regexp.exec(url);
            if (match != null) {
                var newUrl = url.replace(match[1], "/");
                window.location.href = newUrl;
            }
        }
    };

    var calculatePrice = function (value, fxRate, vatRateTypes, productLocalVatRate, isGrossPrices, currencyDecimalPlaces, quantity,
                                   productClassCoefficient, priceCoefficient, productCountryVatRate, roundingRules, skipRounding) {
        if (value == 0)
            return (0).toFixed(currencyDecimalPlaces);
        var merchantVatRate = vatRateTypes.localVATRateType.rate / 100;
        if (productLocalVatRate !== null) {
            if (productLocalVatRate == 0) {
                merchantVatRate = 0;
            } else {
                merchantVatRate = productLocalVatRate / 100;
            }
        }
        var countryVatRate = vatRateTypes.vatRateType.rate / 100;
        if (productCountryVatRate || productCountryVatRate === 0) {
            if (productCountryVatRate == 0) {
                countryVatRate = 0;
            } else {
                countryVatRate = productCountryVatRate / 100;
            }
        }
        if (isGrossPrices) {
            if (vatRateTypes.includeVATTypeId == 0 || vatRateTypes.includeVATTypeId == 8 || (vatRateTypes.includeVATTypeId == 6 && vatRateTypes.useCountryVAT)) {
                value = value / (1 + merchantVatRate);
                if (vatRateTypes.includeVATTypeId == 6) {
                    value += value * countryVatRate;
                }
            }
        } else {
            if (vatRateTypes.includeVATTypeId == 2 || vatRateTypes.includeVATTypeId == 4 || vatRateTypes.includeVATTypeId == 6) {
                if (vatRateTypes.useCountryVAT) {
                    value += value * countryVatRate;
                } else {
                    value += value * merchantVatRate;
                }
            }
        }
        value = value * fxRate;
        if (productClassCoefficient) {
            value = value / priceCoefficient * productClassCoefficient;
        }
        value = value.toFixed(currencyDecimalPlaces);
        if (skipRounding || roundingRules == null) {
            value = value * quantity;
        } else {
            var ranges = roundingRules.roundingRanges;
            var range = null;
            for (var r in ranges) {
                var rg = ranges[r];
                if (rg.from < value && value <= rg.to) {
                    range = rg;
                    break;
                }
            }
            if (range != null) {
                // convert range to form of absolute
                range = convertRoundingRangeToAbsolute(value, range);
                // apply logic of absolute range rounding
                value = absoluteRounding(value, range) * quantity;
                if (value < 0) {
                    value = 0;
                }
            }
        }
        return value;
    };
    var convertRoundingRangeToAbsolute = function (price, range) {
        var result = null;
        if (range.rangeBehavior == 1) {
            // range already has absolute behavior
            result = range;
        } else {
            result = new Object();
            result.rangeBehavior = range.rangeBehavior;
            result.roundingExceptions = [];
            result.from = range.from;
            result.to = range.to;
            var int_part = Math.floor(price);
            if (range.rangeBehavior == 2) {
                //Relative Decimal
                result.lowerTarget = int_part - 1 + range.lowerTarget;
                result.upperTarget = int_part + range.upperTarget;
                result.threshold = int_part + range.threshold;
                for (var ex in range.roundingExceptions) {
                    range.roundingExceptions[ex].exceptionValue += int_part;
                    result.roundingExceptions.push(range.roundingExceptions[ex]);
                }
            } else if (range.rangeBehavior == 3) {
                // Relative Whole
                if (range.targetBehaviorHelperValue == 0) {
                    range.targetBehaviorHelperValue = 10;
                }
                var base = Math.floor(price / range.targetBehaviorHelperValue) * range.targetBehaviorHelperValue;
                result.lowerTarget = base - range.targetBehaviorHelperValue +
                    range.lowerTarget;
                result.upperTarget = base + range.upperTarget;
                result.threshold = base + range.threshold;
                for (var ex in range.roundingExceptions) {
                    range.roundingExceptions[ex].exceptionValue += base;
                    result.roundingExceptions.push(range.roundingExceptions[ex]);
                }
            } else if (range.rangeBehavior == 4) {
                // Nearest target
                if (range.targetBehaviorHelperValue == 0) {
                    range.targetBehaviorHelperValue = 5;
                }
                var base = Math.floor(price / range.targetBehaviorHelperValue) * range.targetBehaviorHelperValue;
                result.lowerTarget = base - 1 + range.lowerTarget;
                result.upperTarget = base - 1 + range.targetBehaviorHelperValue + range.upperTarget;
                result.threshold = base + range.threshold;
                for (var ex in range.roundingExceptions) {
                    range.roundingExceptions[ex].exceptionValue += base;
                    result.roundingExceptions.push(range.roundingExceptions[ex]);
                }
            }
        }

        return result;
    };
    var absoluteRounding = function (price, range) {
        var result = null;
        // check exceptions
        if (range.roundingExceptions.length > 0) {
            for (var e in range.roundingExceptions) {
                ex = range.roundingExceptions[e];
                if (price == ex.exceptionValue) {
                    result = price;
                }
            }
        }
        // no exception was found
        if (!result) {
            // check threshold
            if (price < range.threshold) {
                result = range.lowerTarget;
            } else {
                result = range.upperTarget;
            }
        }
        return result;
    };

    var initGEActive = function () {
        /* Wait for the DOM to completely loads */
        function DOMready(callback) {
            if (document.readyState != 'loading') callback();
            else document.addEventListener('DOMContentLoaded', callback);
        }
        /* Check if the GLBE_PARAMS object exists, then check if country is on the operated countries array */ /* if on operated country, get the body element and add an attribute to it called "ge-active" with the value true otherwise false */
        DOMready(function () {
            if (GLBE_PARAMS) {
                var OperatedCountryArray = GLBE_PARAMS.operatedCountries;
                var MyCountry = GLBE_PARAMS.countryCode || GLBE_PARAMS.shippingCountry;
                var geActive = false;
                var b = document.querySelector("body");
                for (var i = 0; i < OperatedCountryArray.length; i++) {
                    if (MyCountry == OperatedCountryArray[i]) {
                        geActive = true;
                    }
                }
                if (geActive == true) {
                    b.setAttribute("ge-active", true);
                } else {
                    b.setAttribute("ge-active", false);
                }
            }
        });
    };

    let setCartAttributes = function () {
        if (!self.c1Enabled)
            return;

        if (typeof self.suppressCartAttributesUpdateOnNotOperated !== 'undefined'
            &&  self.suppressCartAttributesUpdateOnNotOperated == true
            && !isCountryOperated(self.countryCode)) {
            return;
        }

        if(typeof self.cartAttributesSkipPagesRegex !== 'undefined'
            && self.cartAttributesSkipPagesRegex !== null
            && self.cartAttributesSkipPagesRegex !== ""
        )
        {
            var regex = RegExp(self.cartAttributesSkipPagesRegex, "i");
            if(regex.test(location.href))
                return;
        }

        function updateCartAttributes(attributes, existingCartData = null){
            return new Promise(function (resolve, reject) {
                let cartToken = self.utils.getCookie("cart");

                if (!("cartToken" in attributes)) {
                    if (cartToken)
                        attributes.cartToken = cartToken;
                }

                //cache mechanism to not call get cart several times on the same page     
                if (!("geCartAttributes" in window))
                    window.geCartAttributes = {};

                let needUpdate = false;

                for (const [key, value] of Object.entries(attributes)) {
                    if (!(key in window.geCartAttributes) || JSON.stringify(window.geCartAttributes[key]) !== JSON.stringify(value)) {
                        needUpdate = true;
                        window.geCartAttributes[key] = value;
                    }
                }

                if (!self.suppressCartAttributesUpdate && !needUpdate) {
                    resolve();
                    return;
                }

                const cartDataPromise = existingCartData 
                    ? Promise.resolve(existingCartData)
                    : self.utils.fetch('/cart.js?cb=' + Date.now()).then(response => response.json());

                cartDataPromise
                    .then(cartData => {
                        if (cartData.token) {
                            let attrsToUpdate = {};
                            needUpdate = false;
                            for (const [key, value] of Object.entries(window.geCartAttributes)) {
                                if (!(key in cartData.attributes) || JSON.stringify(cartData.attributes[key]) !== JSON.stringify(value)) {
                                    attrsToUpdate[key] = value;
                                    needUpdate = true;
                                }
                            }

                            if (needUpdate) {
                                self.utils.fetch('/cart/update.js?from_update_cart_attributes', {
                                    method: 'POST',
                                    headers: {
                                        'Content-Type': 'application/json',
                                        'Accept': 'application/json'
                                    },
                                    body: JSON.stringify({attributes: attrsToUpdate})
                                }).then(() => resolve());
                            }else{
                                resolve();
                            }
                        }else{
                            resolve();
                        }
                    })
                    .catch(function(error){
                        //ignore. Endpoint might not exists on the headless merchant.
                    });
            });
        }

        function updateCartFulfillmentConstraintDisabledCountries() {
            if (!Array.isArray(self.masShopify_DisableCountryFulfillmentConstraint)
                || !self.masShopify_DisableCountryFulfillmentConstraint.filter(item => item).length) {
                return;
            }
            const evt = new CustomEvent(events.FulfillmentConstraintDisabledCountriesUpdatedEvent, {
                detail: [{
                    key: "GE_FulfillmentConstraintDisabledCountries",
                    value: JSON.stringify(self.masShopify_DisableCountryFulfillmentConstraint)
                }]
            });
            document.dispatchEvent(evt);

        }
        /*
         adds stock attribute to cart with array of countries that can fulfill the entire order
        */
        function updateCartStockAttribute() {

            if ((!isCountryOperated(self.countryCode) && !self.fulfillmentConstraintSetCartAttributeForNonOperatedCountries)
                || !self.updateCartStockAttribute) {
                return Promise.resolve();
            }
            const getCountryFulfillmentConstraintCartAttributeUpdatedEvent = function(attributeData= null) {

                const result={rule:attributeData,fulfillmentConstraintErrorHandlingMAS:self.fulfillmentConstraintErrorHandling};
                return new CustomEvent(events.CountryFulfillmentConstraintCartAttributeUpdatedEvent, {
                    detail: [{
                        key: "GE_CanBeFulfilledFrom",
                        value: JSON.stringify(result)
                    }]
                });

            }

            return self.utils.fetch('/cart.js?cb=' + Date.now())
                .then(response => response.json())
                .then(cart => {

                    if (cart.items.length === 0) {
                        return null;
                    }

                    const queryParams = ["adminDomain=" + self.shop];
                    const variantMap = {};


                    cart.items.forEach(function(cartItem) { // Aggregate 
                        if (variantMap.hasOwnProperty(cartItem.variant_id)) {
                            variantMap[cartItem.variant_id] += cartItem.quantity;
                        } else {
                            variantMap[cartItem.variant_id] = cartItem.quantity;
                        }
                    });

                    let index = 0;
                    for (let variant_id in variantMap) {
                        queryParams.push(`variants[${index}].v=${variant_id}`);
                        queryParams.push(`variants[${index}].q=${variantMap[variant_id]}`);
                        index++;
                    }

                    return self.utils.getScript(self.appUrl + "resources/SingleCountryFulfillmentConstraintCartAttribute?" + queryParams.join('&'), {
                            jsonp: "jsoncallback",
                            jsonpFunction: "variantsByCountryCallback",
                            contentType: "application/json"
                        }
                    )
                        .then(function (attributeData) {
                            const evt = getCountryFulfillmentConstraintCartAttributeUpdatedEvent(attributeData);
                            document.dispatchEvent(evt);
                        })

                });
        }
        
        
        const buffer = self.utils.createStorageBuffer('ge_cart_attributes_buffer');
        const MAX_RETRIES = 2;
        let isProcessing = false;
        
        function getRetryDelay(tries) {
            return 1000 * (1 + (tries - 1) * 0.5);//1000ms, 1500ms etc
        }

        function addToAttributesBuffer(attributes) {
            const items = buffer.get() || {};
            
            for (const [key, value] of Object.entries(attributes)) {
                items[key] = { value, processAfter: null, tries: 0, processed: false };
            }
            
            buffer.set(items);
            setTimeout(() => processPendingAttributesBuffer(), 0); // put the processBuffer in the next event loop, let other events be added to the buffer first    
        }

        async function processPendingAttributesBuffer() {
            
            if(isProcessing) return;

            const items = buffer.get() || {};
            const isBufferEmpty=(Object.keys(items).length === 0);
            if (isBufferEmpty) return;

            isProcessing = true;

            // Verify previously processed items first
            const processedKeys = Object.keys(items).filter(key => items[key].processed);
            let cartData = null;
            if (processedKeys.length > 0) {
                cartData = await verifyCompletedBufferItemsInCart(processedKeys);
            }

            const now = Date.now();
            const keysToProcess = Object.keys(items).filter(key => !items[key].processed && (!items[key].processAfter || items[key].processAfter <= now)); 
            
        


            if ( keysToProcess.length === 0) { // buffer not empty so next process will be scheduled
                scheduleNext();
                return;
            }
            
            const attrsToUpdate = {};
            keysToProcess.forEach(key => attrsToUpdate[key] = items[key].value);

            try {
                if (!self.suppressCartAttributesUpdate)
                {
                    await updateCartAttributes(attrsToUpdate, cartData);
                }
                
                // Mark items as processed
                const updatedItems = buffer.get() || {};
                keysToProcess.forEach(key => {
                    if (updatedItems[key]) {
                        updatedItems[key].processed = true;
                    }
                });
                buffer.set(updatedItems);
                
                scheduleNext();
            } catch (error) {
                 const { newBuffer } = handleError(keysToProcess, items);
                buffer.set(newBuffer);
                scheduleNext();
            }
        }

        async function verifyCompletedBufferItemsInCart(keysToVerify = []) {
            const items = buffer.get() || {};
            const now = Date.now();
            
            if (keysToVerify.length === 0) {
                return null;
            }
            
            try {
                const cartData = await (await self.utils.fetch('/cart.js?cb=' + Date.now())).json();
                for (const key of keysToVerify) {
                    const item = items[key];
                    if (!item) {
                        continue;
                    }

                    const cartValue = cartData.attributes?.[key];
                    const match = JSON.stringify(cartValue) === JSON.stringify(item.value);
                    
                    if (match) {
                        delete items[key];
                    } else {
                        item.tries++;
                        if (item.tries > MAX_RETRIES) {
                            delete items[key];
                        } else {
                            item.processAfter = now + getRetryDelay(item.tries);
                            item.processed = false; // Reset processed flag for retry
                        }
                    }
                }

                buffer.set(items);                
                return cartData;

            } catch (error) {
                return null;
            }
            
             
        }

        function handleError(keys, allItems) {
            const now = Date.now();

            for (const key of keys) {
                const item = allItems[key];
                if(!item) continue;
        
                item.tries++;
                if (item.tries > MAX_RETRIES) {
                     delete allItems[key];
                } else {
                     item.processAfter = now + getRetryDelay(item.tries);
                }
            }

            return { newBuffer: allItems };
        }

        function scheduleNext() {
            isProcessing = false;
            setTimeout(() => {
                const items = buffer.get() || {};
                if (Object.keys(items).length > 0) {
                    processPendingAttributesBuffer();
                }
            }, 500);
        }

        self.utils.contentLoaded(window, function () {
            document.addEventListener("cartAttributesReady", onAttributeChanged);
            document.addEventListener(events.CountryFulfillmentConstraintCartAttributeUpdatedEvent, onAttributeChanged);
            document.addEventListener(events.FulfillmentConstraintDisabledCountriesUpdatedEvent, onAttributeChanged);
            document.addEventListener(events.CartAttributesNewSessionEvent, onAttributeChanged);

            function onAttributeChanged(evt) {
                let attributes = {};
                evt.detail.forEach((el) => {
                    let val = el.value;

                    //boolean values cannot be saved correctly
                    if (typeof val == "boolean")
                        val = val.toString();

                    attributes[el.key] = val;

                });

                // Add to buffer instead of immediate update
                addToAttributesBuffer(attributes);
            }


            self.utils.addListener(events.onXHRRequest, function (e) {
                const cartAddUrls = ["/cart/add"];
                const allCartUpdateUrls = ["/cart/add","/cart/update", "/cart/change","cart/clear"];
                const excludeIfContains = ["from_update_cart_attributes"];

                const isAddToCart = cartAddUrls.some(url => e.url.indexOf(url) !== -1);
                const isCartUpdated = allCartUpdateUrls.some(url => e.url.indexOf(url) !== -1);
                const isExcluded = excludeIfContains.some(url => e.url.indexOf(url) !== -1);


                if (isCartUpdated && !isExcluded) {
                    glbeApp.getCartToken().then(cartToken => {
                        if (cartToken) {
                            if (isAddToCart && (!window.geCartAttributes || window.geCartAttributes.cartToken !== cartToken || !window.geCartAttributes.GE_utmParams)) {
                                document.dispatchEvent(new Event("glbeParamsReady"));
                            }
                            updateCartStockAttribute();
                            updateCartFulfillmentConstraintDisabledCountries();
                        }
                    });
                }
            });

            //wait for glbeApp  and FraudTokenManager wariables, wait for event subscription in FraudTokenManager
            function whenAvailable(callback) {
                var checkInterval = 10; // ms
                var tokenManagerExecutionInterval = 10; // ms

                if ("glbeApp" in window && "FraudTokenManager" in window && (!self.isAnalyticsSDKEnabled || (self.isAnalyticsSDKEnabled && "GEAnalyticsSDKScriptReady" in window))) {
                    window.setTimeout(callback, tokenManagerExecutionInterval);
                }else{
                    window.setTimeout(function() {
                        whenAvailable(callback);
                    }, checkInterval);
                }
            }

            whenAvailable(function(t) {
                if (!self.suppressCartAttributesUpdate || self.utils.getCookie("cart") != null)
                {
                    document.dispatchEvent(new Event("glbeParamsReady"));

                    updateCartStockAttribute();
                    processPendingAttributesBuffer();
                }
            });

        });
    };

    let initCountrySelector = function () {

        let allowedCountries= GLBE_PARAMS.allowedCountries;


        const isCountrySelectorEnabled = GLBE_PARAMS.isCountrySelectorEnabled;
        const position = GLBE_PARAMS.countrySelectorPosition;

        const style = `
        #ge.country-selector {
            width: 90%;
            max-width: 400px;
            cursor: pointer;
            padding: 10px;
            border: 1px solid #ccc;
            border-radius: 5px;
            background-color: #fff;
            transition: border-color 0.3s;
            text-align: left;
            position: relative;
            margin: 20px auto; 
        }
        #ge.country-selector.left {
            margin-left: 20px;
            text-align: left;
        }
        #ge.country-selector.right {
            margin-right: 20px;
            text-align: left;
        }
        #ge.country-selector:hover {
            border-color: #aaa;
        }
        #ge.country-selector::after {
            content: '';
            position: absolute;
            top: 50%;
            right: 10px;
            transform: translateY(-50%);
            border: 5px solid transparent;
            border-left-color: #333;
        }
        #ge .selector-options {
            display: none;
            position: absolute;
            z-index: 1000;
            background-color: white;
            border: 1px solid #ccc;
            border-radius: 5px;
            max-height: 500px;
            overflow-y: auto;
            width: 100%;
            max-width: 400px;
            bottom: 100%; 
            margin-bottom: 5px; 
            left: 0;
        }
        #ge .country-option {
            padding: 10px;
            cursor: pointer;
            display: flex;
            align-items: left;
        }
        #ge .country-option:hover {
            background-color: #f0f0f0;
        }
        #ge .country-option img {
            margin-right: 5px;
            width: 20px;
            height: 20px;
        }
        #ge .country-search {
            padding: 10px;
            width: calc(100% - 20px);
            margin: 5px;
            border: 1px solid #ccc;
            border-radius: 3px;
        }
    `;

        const createStyleSheet = () => {
            const styleSheet = document.createElement('style');
            styleSheet.innerHTML = style;
            document.head.appendChild(styleSheet);
        };

        const formPost = (path, params, method = 'post') => {
            const form = document.createElement('form');
            form.method = method;
            form.action = path;

            Object.keys(params).forEach(key => {
                const hiddenField = document.createElement('input');
                hiddenField.type = 'hidden';
                hiddenField.name = key;
                hiddenField.value = params[key];
                form.appendChild(hiddenField);
            });

            document.body.appendChild(form);
            form.submit();
        };

        const createCountrySelector = () => {
            if (!isCountrySelectorEnabled || allowedCountries.length === 0) return;

            const selectedCountry = allowedCountries.find(c => c.code === GLBE_PARAMS.countryCode);
            const switcher = document.createElement('div');
            switcher.id = 'ge';
            switcher.classList.add('country-selector');
            switcher.classList.add(position);
            switcher.innerHTML = `<span>${selectedCountry ? `${selectedCountry.name} (${selectedCountry.currencyCode})` : 'Select country'}</span>`;


            const optionsContainer = document.createElement('div');
            optionsContainer.classList.add('selector-options');

            const searchBox = document.createElement('input');
            searchBox.classList.add('country-search');
            searchBox.type = 'text';
            searchBox.placeholder = 'Search';

            optionsContainer.appendChild(searchBox);

            allowedCountries.forEach(country => {
                const countryOption = document.createElement('div');
                countryOption.classList.add('country-option');
                countryOption.innerHTML = `${country.name} (${country.currencyCode})`;
                countryOption.onclick = () => {
                    formPost('/localization', {
                        form_type: 'localization',
                        _method: 'put',
                        return_to: document.location.pathname,
                        country_code: country.code
                    });
                };
                optionsContainer.appendChild(countryOption);
            });

            switcher.appendChild(optionsContainer);
            document.body.appendChild(switcher);

            switcher.onclick = (e) => {
                e.stopPropagation();
                optionsContainer.style.display = optionsContainer.style.display === 'block' ? 'none' : 'block';
            };

            searchBox.onkeyup = () => {
                const searchTerm = searchBox.value.toLowerCase();
                const options = optionsContainer.querySelectorAll('.country-option');
                options.forEach(option => {
                    const isVisible = option.innerText.toLowerCase().includes(searchTerm) || option.innerHTML.toLowerCase().includes(searchTerm);
                    option.style.display = isVisible ? 'flex' : 'none';
                });
            };

            searchBox.onclick = (e) => {
                e.stopPropagation();
            };

            document.body.addEventListener('click', () => {
                optionsContainer.style.display = 'none';
            });
        };

        createStyleSheet();
        createCountrySelector();
    };

    return {
        constructor: GlobaleApp,
        init: function () {
            self = this;
            window.GeShopify = window.Shopify || {};

            let parms = {};
            if(typeof GLBE_PARAMS_2 !== 'undefined'){
                parms = GLBE_PARAMS_2; //new app.js endpoint
            }else{
                parms = GLBE_PARAMS; //to support old app.js endpoint. To remove later
            }


            if (typeof GeShopify.country !== 'undefined')
                parms.countryCode = GeShopify.country;

            if (typeof GeShopify.currency !== 'undefined' && typeof GeShopify.currency.active !== 'undefined')
                parms.currencyCode = GeShopify.currency.active;

            if (typeof GeShopify.locale !== 'undefined')
                parms.locale = GeShopify.locale;

            if (typeof GeShopify.shop !== 'undefined')
                parms.shop = GeShopify.shop;


            if(typeof GLBE_PARAMS !== 'undefined' && GLBE_PARAMS.isExternal === true){
                parms = self.utils.extend(parms, GLBE_PARAMS);
            }

            GLBE_PARAMS = parms;
            document.dispatchEvent(new Event("glbeParamsSet"));

            this.shop = GLBE_PARAMS.shop;
            this.merchantCode = GLBE_PARAMS.emi;
            this.merchantId = GLBE_PARAMS.mid;
            this.countryCode = GLBE_PARAMS.countryCode;
            this.currencyCode = GLBE_PARAMS.currencyCode
            this.locale = GLBE_PARAMS.locale;
            this.operatedCountries = GLBE_PARAMS.operatedCountries;
            this.appUrl = GLBE_PARAMS.appUrl;
            this.apiUrl = GLBE_PARAMS.apiUrl;
            this.geAppUrl = GLBE_PARAMS.geAppUrl;
            this.geCDNUrl = GLBE_PARAMS.geCDNUrl;
            this.hiddenElements = GLBE_PARAMS.hiddenElements;
            this.listeners = {};
            this.pixelUrl = GLBE_PARAMS.pixelUrl || 'https://utils.global-e.com';
            this.pixelEnabled = GLBE_PARAMS.pixelEnabled;
            this.environment = GLBE_PARAMS.env;
            this.trackingCookieName = "GLBE_SESS_ID";
            this.hitInfoKey = "GE_C_HCOUNT";
            this.referreCookieName = "GlobalE_Ref";
            this.c1Enabled = GLBE_PARAMS.c1Enabled;
            this.isTokenEnabled = GLBE_PARAMS.isTokenEnabled;
            this.siteId = GLBE_PARAMS.siteId;
            this.cartAttributesSkipPagesRegex = GLBE_PARAMS.cartAttributesSkipPagesRegex;
            this.suppressCartAttributesUpdate = GLBE_PARAMS.suppressCartAttributesUpdate;
            this.suppressCartAttributesUpdateOnNotOperated = GLBE_PARAMS.suppressCartAttributesUpdateOnNotOperated;
            this.useBrowsingStartEventInsteadOfPageViewed = GLBE_PARAMS.useBrowsingStartEventInsteadOfPageViewed;
            this.isAnalyticsPageViewEventsDisabled = GLBE_PARAMS.isAnalyticsPageViewEventsDisabled;
            this.ft3DayAttribution = GLBE_PARAMS.ft3DayAttribution;
            this.masValidUtmSourceList = GLBE_PARAMS.masValidUtmSourceList;
            this.masUtmStorageLifetime = GLBE_PARAMS.masUtmStorageLifetime;
            this.isCountrySelectorEnabled = GLBE_PARAMS.isCountrySelectorEnabled;
            this.countrySelectorPosition = GLBE_PARAMS.countrySelectorPosition;
            this.isShopPayEnabled = GLBE_PARAMS.isShopPayEnabled;
            this.allowedCountries = GLBE_PARAMS.allowedCountries;
            this.updateCartStockAttribute = GLBE_PARAMS.updateCartStockAttribute;
            this.fulfillmentConstraintErrorHandling = GLBE_PARAMS.fulfillmentConstraintErrorHandling;
            this.fulfillmentConstraintSetCartAttributeForNonOperatedCountries = GLBE_PARAMS.fulfillmentConstraintSetCartAttributeForNonOperatedCountries;
            this.isLegacyAnalyticsSDKEnabled = GLBE_PARAMS.isLegacyAnalyticsSDKEnabled;
            this.isAnalyticsSDKEnabled = GLBE_PARAMS.isAnalyticsSDKEnabled;
            this.analyticsSDKCDN = GLBE_PARAMS.analyticsSDKCDN;
            this.masSkipCssFile = GLBE_PARAMS.masSkipCssFile;
            this.masShopify_DisableCountryFulfillmentConstraint = GLBE_PARAMS.masShopify_DisableCountryFulfillmentConstraint;
            this.masShopifyBrowsingCookieConsent = GLBE_PARAMS.masShopifyBrowsingCookieConsent;

            //replace GlobalE.MerchantID with the info we have?
            //pass RAW in setUtmParameters
            //ask about useBrowsingStartEventInsteadOfPageViewed logic 

            if (this.isAnalyticsSDKEnabled)
            {
                addAnalyticsSDKScript(this.analyticsSDKCDN, async function () {
                    try {
                        // create Analytics Client
                        GlobaleApp.AnalyticsSDK = new window.AnalyticsSDK.default();

                        // initialize Analytics Client with merchant settings
                        let detectedCountry = self.countryCode;

                        if (GeShopify.routes) {
                            const url = `${GeShopify.routes.root}browsing_context_suggestions.json` +
                                `?country[enabled]=true&country[exclude]=${GeShopify.country}` +
                                `&language[enabled]=true&language[exclude]=${GeShopify.language}`;

                            const response = await fetch(url);
                            const result = await response.json();

                            if (result?.detected_values?.country?.handle) {
                                detectedCountry = result.detected_values.country.handle.toUpperCase();
                            }
                        }

                        await GlobaleApp.AnalyticsSDK.init(self.merchantId, detectedCountry, {
                            cdn: GlobalE.GetCDNUrl(),
                            browser: window,
                            utms: window.GlobalE.GetUtmParamQueryString()
                        });

                        window.GEAnalyticsSDKScriptReady = true;

                        GlobalE.IsNECAllowed(function (isNecAllowed) {
                            if (!isNecAllowed || !GlobalE.IsAnalyticsAllowed()) return;

                            if (self.useBrowsingStartEventInsteadOfPageViewed) {
                                GlobaleApp.AnalyticsSDK.sendBrowsingStartEvent().then(function (isBrowsingStartSent) {
                                    if (isBrowsingStartSent === false) {
                                        if (!self.isAnalyticsPageViewEventsDisabled) {
                                            GlobaleApp.AnalyticsSDK.sendPageViewEvent();
                                        }
                                    }
                                });
                            }
                        });

                    } catch (error) {
                        console.debug(error);
                    }
                });
            }

            addStyles();
            setXHRListener();
            initRestrictions();
            initFreeShippingBanner();
            createReplacementCookie();
            initGEActive();
            setCartAttributes();
            initGlobale();
            initCountrySelector();
        },
        utils: {
            getQueryParam: function (name, optUrl) {
                var url = !optUrl ? window.location.search : optUrl;
                var match = RegExp('[?&]' + name + '=([^&]*)').exec(url);
                return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
            },
            addParameter: function (url, parameterName, parameterValue, atStart) {
                if (url.indexOf('#') > 0) {
                    var cl = url.indexOf('#');
                    urlhash = url.substring(url.indexOf('#'), url.length);
                } else {
                    urlhash = '';
                    cl = url.length;
                }
                sourceUrl = url.substring(0, cl);

                var urlParts = sourceUrl.split("?");
                var newQueryString = "";

                if (urlParts.length > 1) {
                    var parameters = urlParts[1].split("&");
                    for (var i = 0; (i < parameters.length); i++) {
                        var parameterParts = parameters[i].split("=");
                        if (!(parameterParts[0] == parameterName)) {
                            if (newQueryString == "")
                                newQueryString = "?";
                            else
                                newQueryString += "&";
                            newQueryString += parameterParts[0] + "=" + (parameterParts[1] ? parameterParts[1] : '');
                        }
                    }
                }
                if (newQueryString == "")
                    newQueryString = "?";

                if (atStart) {
                    newQueryString = '?' + parameterName + "=" + parameterValue + (newQueryString.length > 1 ? '&' + newQueryString.substring(1) : '');
                } else {
                    if (newQueryString !== "" && newQueryString != '?')
                        newQueryString += "&";
                    newQueryString += parameterName + "=" + (parameterValue ? parameterValue : '');
                }
                return urlParts[0] + newQueryString + urlhash;
            },
            getCookie: function (c_name, isJson) {
                try {
                    var c_value = document.cookie;
                    var c_start = c_value.indexOf(" " + c_name + "=");
                    if (c_start == -1) {
                        c_start = c_value.indexOf(c_name + "=");
                    }
                    if (c_start == -1) {
                        c_value = null;
                    }
                    else {
                        c_start = c_value.indexOf("=", c_start) + 1;
                        var c_end = c_value.indexOf(";", c_start);
                        if (c_end == -1) {
                            c_end = c_value.length;
                        }
                        c_value = unescape(c_value.substring(c_start, c_end));
                    }

                    if (isJson) {
                        return JSON.parse(c_value);
                    }

                    return c_value;
                }
                catch (ex) {
                    console.log('glbe error', ex);
                }
            },
            setCookie: function (c_name, value, expire, isJson, isMinutes, setExpireAsSession) {
                try {
                    if (isJson) {
                        value = JSON.stringify(value);
                    }
                    var c_value = "";
                    if (!setExpireAsSession) {
                        var exdate = new Date();
                        if (!isMinutes)
                            exdate.setDate(exdate.getDate() + expire);
                        else
                            exdate.setTime(exdate.getTime() + expire * 60 * 1000);

                        c_value = escape(value) + ((expire == null) ? "" : "; expires=" + exdate.toUTCString()) + ";domain=" + document.domain + ";path=/";
                    } else {
                        c_value = escape(value) + ";domain=" + document.domain + ";path=/";
                    }
                    c_value += ";SameSite=Lax";
                    document.cookie = c_name + "=" + c_value;
                }
                catch (ex) {
                    console.log('glbe error', ex);
                    console.log('glbe error', ex);
                }
            },
            parseUrl: function (href) {
                var parser = document.createElement("a");
                parser.href = href;
                return {
                    protocol: parser.protocol, // => "http:"
                    host: parser.host,         // => "example.com:3000"
                    hostname: parser.hostname, // => "example.com"
                    port: parser.port,         // => "3000"
                    pathname: parser.pathname, // => "/pathname/"
                    hash: parser.hash,         // => "#hash"
                    search: parser.search,     // => "?search=test"
                    origin: parser.origin      // => "http://example.com:3000"
                }
            },
            addListener: function (type, listener) {
                if (typeof self.listeners[type] == "undefined") {
                    self.listeners[type] = [];
                }

                self.listeners[type].push(listener);
            },
            emitEvent: function (event, data) {
                if (!event) {
                    throw new Error("Event object missing 'type' property.");
                }

                if (self.listeners[event] instanceof Array) {
                    var listeners = self.listeners[event];
                    for (var i = 0, len = listeners.length; i < len; i++) {
                        listeners[i].call(self, data);
                    }
                }
            },
            notification: function (message, style) {
                var styles = {
                    close: {
                        position: 'absolute',
                        top: '3px',
                        right: '11px',
                        fontSize: '22px',
                        cursor: 'pointer',
                        lineHeight: '22px',
                        fontFamily: 'Helvetica, "Helvetica Neue", Arial, "Lucida Grande", sans-serif',
                    },
                    base: {
                        position: 'absolute',
                        top: '90px',
                        backgroundColor: 'rgb(44 174 230)',
                        color: '#fff',
                        left: '50%',
                        zIndex: '99999999',
                        padding: '16px',
                        fontSize: '15px',
                        fontFamily: 'Helvetica, "Helvetica Neue", Arial, "Lucida Grande", sans-serif',
                        borderRadius: '3px',
                        boxShadow: '0px 0px 5px #999',
                        transform: 'translateX(-50%)',
                        backgroundRepeat: 'no-repeat',
                        backgroundPosition: '15px center'
                    },
                    warning: {
                        backgroundColor: '#F89405',
                        padding: '16px 50px',
                        backgroundImage: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=)',
                    }
                };

                var el = document.createElement('div');
                self.utils.extend(el.style, styles.base, styles[style]);
                el.className = 'glbe-notification';
                el.innerText = message;
                var closeBtn = document.createElement('div');
                closeBtn.innerText = '×';
                closeBtn.className = 'glbe-notification-close';
                self.utils.extend(closeBtn.style, styles.close);

                closeBtn.addEventListener('click', function () {
                    el.parentElement.removeChild(el);
                });
                document.body.appendChild(el);
                return el;
            },
            contentLoaded: function (win, fn) {
                var done = false, top = true,

                    doc = win.document,
                    root = doc.documentElement,
                    modern = doc.addEventListener,

                    add = modern ? 'addEventListener' : 'attachEvent',
                    rem = modern ? 'removeEventListener' : 'detachEvent',
                    pre = modern ? '' : 'on',

                    init = function (e) {
                        if (e.type == 'readystatechange' && doc.readyState != 'complete') return;
                        (e.type == 'load' ? win : doc)[rem](pre + e.type, init, false);
                        if (!done && (done = true)) fn.call(win, e.type || e);
                    },

                    poll = function () {
                        try { root.doScroll('left'); } catch (e) { setTimeout(poll, 50); return; }
                        init('poll');
                    };

                if (doc.readyState == 'complete') fn.call(win, 'lazy');
                else {
                    if (!modern && root.doScroll) {
                        try { top = !win.frameElement; } catch (e) { }
                        if (top) poll();
                    }
                    doc[add](pre + 'DOMContentLoaded', init, false);
                    doc[add](pre + 'readystatechange', init, false);
                    win[add](pre + 'load', init, false);
                }

            },
            extend: function (out) {
                out = out || {};
                for (var i = 1; i < arguments.length; i++) {
                    if (!arguments[i])
                        continue;

                    for (var key in arguments[i]) {
                        if (arguments[i].hasOwnProperty(key) &&
                            arguments[i][key] !== null &&
                            typeof arguments[i][key] != 'undefined')

                            out[key] = arguments[i][key];
                    }
                }
                return out;
            },
            fetch: function () {
                this.isGlobalE = true;
                return window.fetch.apply(this, arguments);
            },
            httpRequest: function (url, opts) {
                var self = this;
                return new Promise(function (resolve, reject) {
                    var req = new XMLHttpRequest();
                    req.isGlobalE = true;
                    opts = self.extend({
                        method: 'GET',
                        contentType: 'application/x-www-form-urlencoded',
                        cache: false
                    }, opts);

                    req.open(opts.method, url, true);

                    req.setRequestHeader('Content-Type', opts.contentType);
                    if (opts.cache === false) {
                        url += (url.indexOf('?') > -1 ? '&_=' : '?_=') + (new Date()).getTime();
                    }

                    var data = opts.data ? JSON.stringify(opts.data) : null;

                    req.onload = function () {
                        if (req.status == 200) {
                            var res = req.response;
                            if (opts.contentType == "application/json") {
                                try {
                                    res = JSON.parse(res);
                                } catch (e) {
                                    //response is not JSON
                                }
                            }
                            resolve(res);
                        }
                        else {
                            reject(new Error(req.statusText == null || req.statusText == "" ? req.status : req.statusText));
                        }
                    };

                    req.onerror = function () {
                        reject(Error("Network Error"));
                    };
                    req.send(data);
                });
            },
            deferred: function () {
                var dfd = {};
                dfd.promise = new Promise(function (resolve, reject) {
                    dfd.resolve = resolve;
                    dfd.reject = reject;
                });
                return dfd;
            },
            getScript: function (url, opts) {
                return new Promise(function (res, rej) {
                    if (opts) {
                        if (opts.uriParams) {
                            for (var p in opts.uriParams) {
                                if (opts.uriParams.hasOwnProperty(p)) {
                                    url += (url.indexOf('?') > -1 ? '&' : '?') + p + '=' + encodeURIComponent(opts.uriParams[p]);
                                }
                            }
                        }

                        if (opts.cache === false) {
                            url += (url.indexOf('?') > -1 ? '&_=' : '?_=') + (new Date()).getTime();
                        }
                    }

                    var s = document.createElement('script');
                    s.type = 'text/javascript';
                    s.async = true;

                    if (opts && opts.jsonp) {
                        var jsonp = 'jsonp_' + (new Date()).getTime();
                        if (opts.jsonpFunction)
                            jsonp = opts.jsonpFunction;

                        url += (url.indexOf('?') > -1 ? '&' : '?') + opts.jsonp + '=' + jsonp;
                        window[jsonp] = function (data) {
                            res(data);
                        }
                    }
                    else {
                        s.onload = function () {
                            res();
                        }
                    }

                    s.src = url;
                    document.getElementsByTagName('head')[0].appendChild(s);
                });
            },
            formPost: function (path, params, method = 'post') {
                var form = document.createElement('form');
                form.method = method;
                form.action = path;

                for (var key in params) {
                    if (params.hasOwnProperty(key)) {
                        var hiddenField = document.createElement('input');
                        hiddenField.type = 'hidden';
                        hiddenField.name = key;
                        hiddenField.value = params[key];

                        form.appendChild(hiddenField);
                    }
                }

                document.body.appendChild(form);
                form.submit();
            },
            isLinkAdded: function (search) {
                var items = document.getElementsByTagName('link');
                for (var i = items.length; i--;) {
                    if (items[i].href.indexOf(search) > -1) return true;
                }
                return false;
            },
            createStorageBuffer: function(key) {
                const useSession = typeof sessionStorage !== 'undefined' && sessionStorage !== null;
                const winKey = '_buffer_' + key;

                return {
                    get: () => {
                        const data = useSession ? sessionStorage.getItem(key) : window[winKey];
                        return data ? (useSession ? JSON.parse(data) : data) : {};
                    },
                    set: (data) => useSession ? sessionStorage.setItem(key, JSON.stringify(data)) : window[winKey] = data,
                    clear: () => useSession ? sessionStorage.removeItem(key) : delete window[winKey]
                };
            }
        },
        modules: {
            priceInfo: {
                load: function () {
                    if (isCountryOperated(GLBE_PARAMS.countryCode)) {
                        return self.utils.getScript(self.appUrl + "resources/PriceInfo?shop=" + self.shop + "&countryCode=" + GLBE_PARAMS.countryCode + "&currencyCode=" + GLBE_PARAMS.currencyCode, {
                            jsonp: "jsoncallback",
                            jsonpFunction: "priceInfoCallback",
                            contentType: "application/json"
                        }).then(function (data) {
                            if (!data.success)
                                return;

                            GLBE_PARAMS.PriceInfo = data;
                            return;
                        })
                    } else
                        return Promise.resolve();
                },
                convertPrice: function (basePrice) {
                    if (!isCountryOperated(GLBE_PARAMS.countryCode))
                        return basePrice;

                    if (!GLBE_PARAMS.PriceInfo)
                        throw "Module is not loaded";

                    //if (!GLBE_PARAMS.PriceInfo.isOperatedByGlobale)
                    //    return basePrice;

                    var productLocalVatRate = GLBE_PARAMS.PriceInfo.vatSettings.localVATRate;
                    var productCountryVatRate = GLBE_PARAMS.PriceInfo.vatSettings.distanceSellingVATRate;
                    var priceCoefficient = GLBE_PARAMS.PriceInfo.countryCoefficientRate;
                    var productClassCoefficient = 0;
                    var fxRate = GLBE_PARAMS.PriceInfo.currencyConversionRate * priceCoefficient;

                    var vatRateTypes = {
                        localVATRateType: { rate: GLBE_PARAMS.PriceInfo.vatSettings.localVATRate },
                        vatRateType: { rate: GLBE_PARAMS.PriceInfo.vatSettings.distanceSellingVATRate },
                        includeVATTypeId: GLBE_PARAMS.PriceInfo.vatSettings.vatTypeId,
                        useCountryVAT: GLBE_PARAMS.PriceInfo.vatSettings.useDistanceSellingVAT
                    };
                    var skipRounding = false;

                    return calculatePrice(basePrice, fxRate, vatRateTypes, productLocalVatRate, GLBE_PARAMS.PriceInfo.isGrossPrices, GLBE_PARAMS.PriceInfo.currencyDecimalPlaces, 1,
                        productClassCoefficient, priceCoefficient, productCountryVatRate, GLBE_PARAMS.PriceInfo.roundingRules, skipRounding);
                }
            }
        },

        getCartToken: function(){
            return new Promise(function (resolve, reject) {
                let cartToken = self.utils.getCookie("cart");
                if (!cartToken){
                    self.utils.fetch('/cart.js?cb=' + Date.now())
                        .then(response => response.json())
                        .then(cartData => {
                            if (cartData.token) {
                                resolve(cartData.token);
                            } else
                                reject();
                        })
                        .catch(function(error){
                            //ignore. Endpoint might not exists on the headless merchant.
                        });
                }else{
                    resolve(cartToken);
                }
            });
        }

        

    };
})();

let paramsIsFromCheckoutLiquid = typeof GLBE_PARAMS !== 'undefined' && GLBE_PARAMS.checkoutId;
if (!paramsIsFromCheckoutLiquid) { //make sure GLBE_PARAMS is not from checkout.liquid
    var glbeApp = new GlobaleApp();
}