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 })) : [];
}