const getAllEventData = require("getAllEventData"); const getTimestampMillis = require("getTimestampMillis"); // Doesnt work atm so we made our own implementation //const getCookieValues = require("getCookieValues"); const makeNumber = require("makeNumber"); const fromBase64 = require("fromBase64"); const Math = require("Math"); const JSON = require("JSON"); const parseUrl = require("parseUrl"); const sendHttpRequest = require("sendHttpRequest"); const decodeUriComponent = require("decodeUriComponent"); const CONVERSION_PRE_VALIDATION = data.conversion_pre_validation; const COOKIE_NAME_EAD = data.cookie_name_ead; const CAMPAIGN_EXPIRATION_IN_MINUTES = makeNumber(data.campaign_expiration_in_minutes || 43200); const BLACKLIST_DOMAINS = parseDomains(data.blacklist_domains); const COOKIE_NAME_SESSION = data.cookie_name_session; const BROWSER_ID_VALUE = data.browser_id_value; const COOKIE_NAME_DO_NOT_TRACK = data.cookie_name_do_not_track; const REQUEST_URL = data.api_url; const PAGE_VIEW_EVENT_NAME = data.page_view_event_name; const CONSENT_EVENT_NAME = data.consent_event_name; const eventData = getAllEventData(); const doNotTrack = getDoNotTrack(); const hasSessionCookie = !doNotTrack ? getSession() : false; const browserId = getBrowserId(); let ead = getEad(); const isNewVisitGa = calculateNewVisitGa(); const isNewVisitAnalytics = calculateNewVisitAnalytics(); if (!doNotTrack) { ead = updateCurrentCampaign(); } switch (eventData.event_name) { case PAGE_VIEW_EVENT_NAME: case CONSENT_EVENT_NAME: if (shouldSendData()) { const consentUpdated = isConsentUpdated(); // eslint-disable-next-line no-case-declarations let eventRequestCasePageView = { id: browserId, currentCampaign: ead.currentCampaign || null, consentStatus: translateConsentStatus(data.consent_status), tagId: data.tag_id, target: eventData.context.page.url, referrer: eventData.context.page.referrer, userAgent: eventData.context.device.user_agent, type: consentUpdated ? "consentAttribution" : "entranceAttribution", viewport: eventData.context.page.viewport, isNewVisitAnalytics: consentUpdated ? true : isNewVisitAnalytics, isNewVisitGa: consentUpdated ? true : isNewVisitGa, ipAddress: eventData.context.device.ip, }; if (parseAdditionalParams(data.additional_audience_params)) { for (let param of parseAdditionalParams(data.additional_audience_params)) { eventRequestCasePageView[param.parameter] = param.value; } } sendHttpRequest( REQUEST_URL, (statusCode) => { if (statusCode >= 200 && statusCode < 300) { data.gtmOnSuccess(); } else { data.gtmOnFailure(); } }, { headers: { "content-type": "application/json" }, method: "POST" }, JSON.stringify(eventRequestCasePageView) ); } else { data.gtmOnSuccess(); } break; case "purchase": // eslint-disable-next-line no-case-declarations let eventRequestPurchase; if (CONVERSION_PRE_VALIDATION === true) { eventRequestPurchase = { tagId: data.tag_id, orderId: eventData.id, amount: makeNumber(eventData.value), margin: eventData.margin ? makeNumber(eventData.margin) : null, voucher: eventData.coupon, type: "orderValidation", ipAddress: eventData.context.device.ip, }; } else { eventRequestPurchase = { id: browserId, currentCampaign: ead.currentCampaign || null, orderId: eventData.id, amount: makeNumber(eventData.value), margin: eventData.margin ? makeNumber(eventData.margin) : null, voucher: eventData.coupon, consentStatus: translateConsentStatus(data.consent_status), tagId: data.tag_id, target: eventData.context.page.url, referrer: eventData.context.page.referrer, userAgent: eventData.context.device.user_agent, type: "orderAttribution", viewport: eventData.context.page.viewport, ipAddress: eventData.context.device.ip, }; } if (eventData?.items?.length > 0) { eventRequestPurchase.items = eventData.items.map((item) => ({ ...(item?.product || item), product_discount: item.discount, })); } if (parseAdditionalParams(data.additional_conversion_params)) { for (let param of parseAdditionalParams(data.additional_conversion_params)) { eventRequestPurchase[param.parameter] = param.value; } } if (parseAdditionalParams(data.additional_audience_params)) { for (let param of parseAdditionalParams(data.additional_audience_params)) { eventRequestPurchase[param.parameter] = param.value; } } sendHttpRequest( REQUEST_URL, (statusCode) => { if (statusCode >= 200 && statusCode < 300) { data.gtmOnSuccess(); } else { data.gtmOnFailure(); } }, { headers: { "content-type": "application/json" }, method: "POST" }, JSON.stringify(eventRequestPurchase) ); break; default: data.gtmOnSuccess(); break; } function isUrlInDomainsList(url) { const referrerObject = parseUrl(url); let foundInDomainsList = false; if (parseDomains(data.whitelist_domains)) { for (let item of parseDomains(data.whitelist_domains)) { if (item.domain.replace("www.", "") === referrerObject.host.replace("www.", "")) { foundInDomainsList = true; break; } // Case with wildcard "*" as subdomain if (item.domain.startsWith("*.")) { // Extract the main domain without the wildcard (*.example.com -> example.com) const domainWithoutWildcard = item.domain.substring(2); // Extract the parts of the referrer domain const referrerHostParts = referrerObject.host.replace("www.", "").split("."); const domainParts = domainWithoutWildcard.split("."); // If there are enough parts in the referrer domain if (referrerHostParts.length >= domainParts.length) { // Compare only the main domain and extension (the last parts) const referrerMainDomain = referrerHostParts.slice(-domainParts.length).join("."); if (referrerMainDomain === domainWithoutWildcard) { foundInDomainsList = true; break; } } } } } return foundInDomainsList; } function referrerNotExcludedFromGA() { if (!BLACKLIST_DOMAINS || BLACKLIST_DOMAINS.length <= 0) { return true; } const referralUrl = eventData.context.page.referrer ? parseUrl(eventData.context.page.referrer) : null; if (!referralUrl || !referralUrl.hostname) { return true; } let referrerNotExcludedFromGA = true; for (let item of BLACKLIST_DOMAINS) { if (referralUrl.host.replace("www.", "") === item.domain.replace("www.", "")) { referrerNotExcludedFromGA = false; break; } } return referrerNotExcludedFromGA; } function isDirectAccess() { const referralUrl = eventData.context.page.referrer ? parseUrl(eventData.context.page.referrer) : null; const targetUrl = eventData.context.page.url ? parseUrl(eventData.context.page.url) : null; return ( (!referralUrl || (referralUrl && isUrlInDomainsList(eventData.context.page.referrer))) && (!targetUrl || (!searchParamFromUrl(targetUrl, "utm_source") && !searchParamFromUrl(targetUrl, "utm_campaign") && !searchParamFromUrl(targetUrl, "utm_keyword") && !searchParamFromUrl(targetUrl, "utm_content") && !searchParamFromUrl(targetUrl, "utm_medium") && !searchParamFromUrl(targetUrl, "at_platform") && !searchParamFromUrl(targetUrl, "at_channel") && !searchParamFromUrl(targetUrl, "at_medium") && !searchParamFromUrl(targetUrl, "at_campaign") && !searchParamFromUrl(targetUrl, "at_creation") && !searchParamFromUrl(targetUrl, "at_network") && !searchParamFromUrl(targetUrl, "at_aff_type") && !searchParamFromUrl(targetUrl, "at_variant") && !searchParamFromUrl(targetUrl, "at_source") && !searchParamFromUrl(targetUrl, "cmpid") && !searchParamFromUrl(targetUrl, "gclid") && !searchParamFromUrl(targetUrl, "fbclid") && !searchParamFromUrl(targetUrl, "msclkid"))) ); } function shouldSendData() { // IF NO REFERRER if (!eventData.context.page.referrer) { return true; } // IF REFERRER IS NOT IN THE WHITELIST if (!isUrlInDomainsList(eventData.context.page.referrer)) { return true; } if (isNewVisitGa || isNewVisitAnalytics) { return true; } return false; } function isConsentUpdated() { if (eventData.consent_updated) { return true; } if (eventData.event_name === CONSENT_EVENT_NAME) { return true; } return false; } function calculateNewVisitAnalytics() { if (!ead || !hasSessionCookie) { return true; } return false; } function calculateNewVisitGa() { if ( !ead || !hasSessionCookie || checkSameReferrerToOrganic(ead.target, ead.referrer) || checkNewReferrer(ead.referrer) || checkNewUtmParameters(ead.target) || checkNewClickIdentifierParameters(ead.target) ) { return true; } return false; } function checkNewReferrer(referrer) { const referralUrl = eventData.context.page.referrer ? parseUrl(eventData.context.page.referrer) : null; const targetUrl = eventData.context.page.url ? parseUrl(eventData.context.page.url) : null; const lastTouchpointReferrer = referrer ? parseUrl(referrer) : null; if ( referralUrl && !isUrlInDomainsList(eventData.context.page.referrer) && targetUrl.hostname !== referralUrl.hostname && (!lastTouchpointReferrer || lastTouchpointReferrer.hostname !== referralUrl.hostname) ) { return true; } return false; } function checkSameReferrerToOrganic(target, referrer) { const referralUrl = eventData.context.page.referrer ? parseUrl(eventData.context.page.referrer) : null; const targetUrl = eventData.context.page.url ? parseUrl(eventData.context.page.url) : null; const lastTouchpointReferrer = referrer ? parseUrl(referrer) : null; const lastTouchpointTarget = target ? parseUrl(target) : null; if ( referralUrl && targetUrl && lastTouchpointReferrer && lastTouchpointTarget && lastTouchpointReferrer.hostname === referralUrl.hostname && !isUrlInDomainsList(eventData.context.page.referrer) && !isUrlInDomainsList(referrer) && (searchParamFromUrl(lastTouchpointTarget, "utm_source") || searchParamFromUrl(lastTouchpointTarget, "utm_campaign") || searchParamFromUrl(lastTouchpointTarget, "utm_term") || searchParamFromUrl(lastTouchpointTarget, "utm_content") || searchParamFromUrl(lastTouchpointTarget, "utm_medium") || searchParamFromUrl(lastTouchpointTarget, "at_platform") || searchParamFromUrl(lastTouchpointTarget, "at_channel") || searchParamFromUrl(lastTouchpointTarget, "at_medium") || searchParamFromUrl(lastTouchpointTarget, "at_campaign") || searchParamFromUrl(lastTouchpointTarget, "at_creation") || searchParamFromUrl(lastTouchpointTarget, "at_network") || searchParamFromUrl(lastTouchpointTarget, "at_aff_type") || searchParamFromUrl(lastTouchpointTarget, "at_variant") || searchParamFromUrl(lastTouchpointTarget, "at_source") || searchParamFromUrl(lastTouchpointTarget, "cmpid") || searchParamFromUrl(lastTouchpointTarget, "gclid") || searchParamFromUrl(lastTouchpointTarget, "fbclid") || searchParamFromUrl(lastTouchpointTarget, "msclkid")) && !searchParamFromUrl(targetUrl, "utm_source") && !searchParamFromUrl(targetUrl, "utm_campaign") && !searchParamFromUrl(targetUrl, "utm_term") && !searchParamFromUrl(targetUrl, "utm_content") && !searchParamFromUrl(targetUrl, "utm_medium") && !searchParamFromUrl(targetUrl, "at_platform") && !searchParamFromUrl(targetUrl, "at_channel") && !searchParamFromUrl(targetUrl, "at_medium") && !searchParamFromUrl(targetUrl, "at_campaign") && !searchParamFromUrl(targetUrl, "at_creation") && !searchParamFromUrl(targetUrl, "at_network") && !searchParamFromUrl(targetUrl, "at_aff_type") && !searchParamFromUrl(targetUrl, "at_variant") && !searchParamFromUrl(targetUrl, "at_source") && !searchParamFromUrl(targetUrl, "cmpid") && !searchParamFromUrl(targetUrl, "gclid") && !searchParamFromUrl(targetUrl, "fbclid") && !searchParamFromUrl(targetUrl, "msclkid") ) { return true; } return false; } function searchParamFromUrl(url, attributName) { let paramValue = undefined; if (url && url.search && url.search !== "") { for (let item of url.search.replace("?", "").split("&")) { const [attribut, value] = item.split("="); if (attribut === attributName) { paramValue = value; break; } } /** for (let [attribut, value] of url.searchParams) { if (attribut === attributName) { paramValue = value; break; } } */ } return paramValue; } function checkNewUtmParameters(target) { const lastTouchpointTarget = target ? parseUrl(target) : null; const targetUrl = eventData.context.page.url ? parseUrl(eventData.context.page.url) : null; if ( targetUrl && lastTouchpointTarget && (["source", "campaign", "term", "content", "medium"].some((utm) => { const currentUrlParam = searchParamFromUrl(targetUrl, "utm_" + utm); const lastTouchPointParam = searchParamFromUrl(lastTouchpointTarget, "utm_" + utm); return currentUrlParam && currentUrlParam !== lastTouchPointParam; }) || [ "platform", "channel", "medium", "campaign", "creation", "network", "aff_type", "variant", "source", ].some((at) => { const currentUrlParam = searchParamFromUrl(targetUrl, "at_" + at); const lastTouchPointParam = searchParamFromUrl(lastTouchpointTarget, "at_" + at); return currentUrlParam && currentUrlParam !== lastTouchPointParam; })) ) { return true; } return false; } function checkNewClickIdentifierParameters(target) { const lastTouchpointTarget = target ? parseUrl(target) : null; let targetUrl = eventData.context.page.url ? parseUrl(eventData.context.page.url) : null; if ( targetUrl && lastTouchpointTarget && ["gclid", "fbclid", "msclkid"].some((tagId) => { const currentUrlParam = searchParamFromUrl(targetUrl, tagId); const lastTouchPointParam = searchParamFromUrl(lastTouchpointTarget, tagId); return currentUrlParam && currentUrlParam !== lastTouchPointParam; }) ) { return true; } return false; } function translateConsentStatus(consent) { if (consent === "false" || consent === "0" || consent === 0) { return false; } if (consent === "true" || consent === "1" || consent === 1) { return true; } if (!consent || consent === "null") { return null; } return consent; } function getEad() { let ead = getCookieValues(COOKIE_NAME_EAD)[0] || undefined; if (ead) { ead = JSON.parse(fromBase64(decodeUriComponent(ead))); } return ead; } function updateCurrentCampaign() { let newEad = { currentCampaign: ead && ead.currentCampaign ? ead.currentCampaign : null, }; if (!ead || isNewVisitGa) { newEad.referrer = eventData.context.page.referrer; newEad.target = eventData.context.page.url; } else { newEad.referrer = ead.referrer; newEad.target = ead.target; } if ( !ead || !ead.currentCampaign || !ead.currentCampaign.target || !ead.currentCampaign.date || (!isDirectAccess() && referrerNotExcludedFromGA() && (checkSameReferrerToOrganic(ead.currentCampaign.target, ead.currentCampaign.referrer) || checkNewReferrer(ead.currentCampaign.referrer) || checkNewUtmParameters(ead.currentCampaign.target) || checkNewClickIdentifierParameters(ead.currentCampaign.target))) || (isDirectAccess() && checkDelay(ead.currentCampaign.date, CAMPAIGN_EXPIRATION_IN_MINUTES)) ) { newEad.currentCampaign = { date: getTimestampMillis(), referrer: eventData.context.page.referrer || null, target: eventData.context.page.url, }; } return newEad; } function checkDelay(timestampInMillis, periodInMinutes) { const differentInMinutes = Math.abs( Math.round((getTimestampMillis() - timestampInMillis) / 60000) ); if (differentInMinutes > periodInMinutes) { return true; } return false; } function getBrowserId() { let browserId = BROWSER_ID_VALUE || eventData.user.tcId || undefined; return browserId; } function getSession() { let session = getCookieValues(COOKIE_NAME_SESSION)[0] || undefined; if (!session) { return false; } return true; } function getDoNotTrack() { let dnt = getCookieValues(COOKIE_NAME_DO_NOT_TRACK)[0] || undefined; if (dnt) { return true; } return false; } function getCookieValues(id) { if (!("cookie" in eventData.context.device)) { return []; } return eventData.context.device.cookie .split("; ") .map((cookie) => ({ name: cookie.split("=")[0], value: cookie.split("=")[1] })) .filter(({ name }) => name === id) .map(({ value }) => value); } // No table object atm in TagCo, so replace table inputs with simple text fields that looks like "name:value,name2:value2" function parseAdditionalParams(params) { if (!params) { return null; } return params .split(",") .map((param) => ({ parameter: param.split(":")[0], value: param.split(":")[1] })); } // No table object atm in TagCo, so replace table inputs with simple text fields that looks like "domain1,domain2" function parseDomains(domains) { return domains ? domains.split(",").map((domain) => ({ domain })) : []; }