(function () { const DEBUG = true; // Set to false to disable debug logs function debugLog(...args) { if (DEBUG) console.log('[TRACKER-DEBUG]', ...args); } var _cfScriptEl = document.currentScript || document.querySelector('script[src*="/api/tracker/external/visits"]'); var _cfBase = ""; // Vercel deployment-protection bypass, carried over from our own script src. // It must ride the endpoint URLs as a QUERY param: the JSON POSTs trigger a // CORS preflight, and OPTIONS requests carry neither cookies nor custom // headers — the query string is the only bypass channel that survives into // the preflight. Empty in production (prod srcs have no bypass param). var _cfBypassQuery = ""; if (_cfScriptEl && _cfScriptEl.src) { try { var _cfSrcUrl = new URL(_cfScriptEl.src); _cfBase = _cfSrcUrl.origin; var _cfBypassValue = _cfSrcUrl.searchParams.get("x-vercel-protection-bypass"); if (_cfBypassValue) { _cfBypassQuery = "?x-vercel-protection-bypass=" + encodeURIComponent(_cfBypassValue); } } catch(e) { debugLog("Failed to parse script src:", e); } } if (!_cfBase) { _cfBase = "https://app.clickfastads.com.br"; } var TRACKING_VISIT_ENDPOINT = _cfBase + "/api/tracker/visits" + _cfBypassQuery; var TRACKING_CLICK_ENDPOINT = _cfBase + "/api/tracker/clicks" + _cfBypassQuery; const botUserAgents = [ "googlebot", "Googlebot", "AdsBot", "google-adwords", "Google-AdWords-Express", "AdsBot-Google-Mobile", "google-structured-data", "appengine-google", "feedfetcher-google", "adsbot-google", "facebookexternalhit", "facebot", "facebookcatalog", "bingbot", "msnbot", "bingpreview", "Pinterestbot", "YandexBot", "YandexImages", "DuckDuckBot", "Baiduspider", "Twitterbot", "Applebot", "LinkedInBot", "Slackbot", "WhatsApp", "TelegramBot", "MJ12bot", "AhrefsBot", "SEMrushBot", "DotBot", "PetalBot", "adidxbot", "Sogou", "BingPreview", "Sogou web spider", "Exabot", "facebot", "ia_archiver", ]; function isBot() { const ua = navigator.userAgent.toLowerCase(); if (window.navigator.webdriver) return true; if (botUserAgents.some(bot => ua.includes(bot))) return true; if (ua.includes("headless")) return true; if (ua.includes("webdriver")) return true; if (ua.includes("selenium")) return true; if (ua.includes("playwright")) return true; if (ua.includes("puppeteer")) return true; if (ua.includes("cypress")) return true; if (ua.includes("testcafe")) return true; if (ua.includes("nightwatch")) return true; return false; } const botDetected = isBot(); debugLog('Bot detected:', botDetected); const REQUEST_TIMEOUT = 500; const TRAFFIC_SOURCES = { google_ads: /[?&]gclid=/i, facebook_ads: /[?&]fbclid=/i, microsoft_ads: /[?&]msclkid=/i, tiktok_ads: /[?&]ttclid=/i, facebook: /facebook\.com|fb\.com|m\.facebook\.com/i, instagram: /instagram\.com|ig\.me/i, twitter: /twitter\.com|x\.com|t\.co/i, linkedin: /linkedin\.com|lnkd\.in/i, pinterest: /pinterest\.com|pin\.it/i, youtube: /youtube\.com|youtu\.be/i, whatsapp: /whatsapp\.com|wa\.me/i, telegram: /telegram\.org|t\.me/i, discord: /discord\.com/i, tiktok: /tiktok\.com/i, reddit: /reddit\.com/i, twitch: /twitch\.tv/i, email: /utm_medium=email/i, google_organic: /google\.com\/search|google\.[a-z]+\/url/i, bing_organic: /bing\.com\/search/i, yahoo_organic: /yahoo\.com\/search/i, duckduckgo_organic: /duckduckgo\.com\/?q=/i, yandex_organic: /yandex\.(com|ru)\/search/i, baidu_organic: /baidu\.com\/s/i, ask_organic: /ask\.com\/web/i, aol_organic: /aol\.com\/search/i, ecosia_organic: /ecosia\.org\/search/i, referral: () => { const ref = document.referrer; return ref && !ref.includes(window.location.hostname); } }; function generateCfId() { return ( "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { const r = (Math.random() * 16) | 0; const v = c === "x" ? r : (r & 0x3) | 0x8; return v.toString(16); }) + "_cfid" ); } var screenResolution = (typeof window !== "undefined" && window.screen && typeof window.screen.width === "number" && typeof window.screen.height === "number") ? window.screen.width + "x" + window.screen.height : "unknown"; const trackingData = { cfId: generateCfId(), pageUrl: window.location.href, trafficSource: null, inboundParams: {}, outboundClicks: [], pageLoadTime: new Date().toISOString(), userAgent: navigator.userAgent, screenResolution: screenResolution, isBot: botDetected, }; // Inline the traffic detection logic from our utility function function detectTrafficSource() { debugLog('Detecting traffic source...'); debugLog('Current URL:', window.location.href); debugLog('Document referrer:', document.referrer); const url = window.location.href; const referrer = document.referrer || ''; // Check for paid traffic by URL parameters if (/[?&]gclid=/.test(url) || /[?&]gbraid=/.test(url) || /[?&]wbraid=/.test(url)) { debugLog('Detected Google Ads'); return "google_ads"; } if (/[?&]fbclid=/.test(url)) { debugLog('Detected Facebook Ads'); return "facebook_ads"; } if (/[?&]msclkid=/.test(url)) { debugLog('Detected Microsoft Ads'); return "microsoft_ads"; } if (/[?&]ttclid=/.test(url)) { debugLog('Detected TikTok Ads'); return "tiktok_ads"; } // Parse URL parameters (case insensitive) let urlParams = {}; try { const urlObj = new URL(url); urlObj.searchParams.forEach((value, key) => { urlParams[key.toLowerCase()] = value; }); } catch (e) { debugLog("URL parsing error:", e); } // Check UTM parameters for paid traffic if (urlParams.utm_source && urlParams.utm_medium) { const utmSource = urlParams.utm_source.toLowerCase(); const utmMedium = urlParams.utm_medium.toLowerCase(); if (utmMedium === 'cpc' || utmMedium === 'ppc' || utmMedium === 'paid') { if (utmSource.includes('google')) { debugLog('Detected Google Ads via UTM'); return "google_ads"; } if (utmSource.includes('facebook') || utmSource.includes('fb')) { debugLog('Detected Facebook Ads via UTM'); return "facebook_ads"; } if (utmSource.includes('microsoft') || utmSource.includes('bing')) { debugLog('Detected Microsoft Ads via UTM'); return "microsoft_ads"; } } debugLog('Detected UTM source:', utmSource); return utmSource; } // Check for direct traffic if (!referrer || referrer === "") { debugLog('Detected direct traffic'); return "direct"; } // Check referrer patterns const referrerLower = referrer.toLowerCase(); // Search engines (organic) - return specific source instead of generic "organic" if (referrerLower.includes('google.com/search') || (referrerLower.includes('google.') && referrerLower.includes('/search'))) { debugLog('Detected Google organic'); return "google"; } if (referrerLower.includes('bing.com/search')) { debugLog('Detected Bing organic'); return "bing"; } if (referrerLower.includes('yahoo.com/search')) { debugLog('Detected Yahoo organic'); return "yahoo"; } if (referrerLower.includes('duckduckgo.com')) { debugLog('Detected DuckDuckGo organic'); return "duckduckgo"; } if (referrerLower.includes('yandex.') && referrerLower.includes('/search')) { debugLog('Detected Yandex organic'); return "yandex"; } if (referrerLower.includes('baidu.com/s')) { debugLog('Detected Baidu organic'); return "baidu"; } // Social media platforms - return specific source instead of generic "social" if (referrerLower.includes('facebook.com') || referrerLower.includes('fb.com') || referrerLower.includes('m.facebook.com')) { debugLog('Detected Facebook'); return "facebook"; } if (referrerLower.includes('instagram.com') || referrerLower.includes('ig.me')) { debugLog('Detected Instagram'); return "instagram"; } if (referrerLower.includes('youtube.com') || referrerLower.includes('youtu.be')) { debugLog('Detected YouTube'); return "youtube"; } if (referrerLower.includes('twitter.com') || referrerLower.includes('x.com') || referrerLower.includes('t.co/')) { debugLog('Detected Twitter/X'); return "twitter"; } if (referrerLower.includes('linkedin.com') || referrerLower.includes('lnkd.in')) { debugLog('Detected LinkedIn'); return "linkedin"; } if (referrerLower.includes('tiktok.com')) { debugLog('Detected TikTok'); return "tiktok"; } if (referrerLower.includes('pinterest.com') || referrerLower.includes('pin.it')) { debugLog('Detected Pinterest'); return "pinterest"; } if (referrerLower.includes('reddit.com')) { debugLog('Detected Reddit'); return "reddit"; } if (referrerLower.includes('whatsapp.com') || referrerLower.includes('wa.me')) { debugLog('Detected WhatsApp'); return "whatsapp"; } if (referrerLower.includes('telegram.org') || referrerLower.includes('t.me')) { debugLog('Detected Telegram'); return "telegram"; } // If we have a referrer but it doesn't match known patterns try { const referrerUrl = new URL(referrer); debugLog('Detected referral from:', referrerUrl.hostname); return "other"; } catch (e) { debugLog('Error parsing referrer URL:', e); } debugLog('No traffic source detected, defaulting to direct'); return "direct"; } trackingData.trafficSource = detectTrafficSource(); (function initializeTracking() { try { const url = new URL(window.location.href); url.searchParams.forEach((value, key) => { trackingData.inboundParams[key] = value; }); } catch (e) { debugLog("URL parsing error:", e); } sendTrackingData("inbound"); })(); document.addEventListener( "click", async function (e) { const element = e.target.closest("a, button, [onclick]"); if (!element) return; const url = getUrlFromElement(element); if (!url || !isExternalUrl(url)) return; e.preventDefault(); // Resolve through the tracker contract before recording OR navigating. // track-conversions.js rewrites href attributes in place, but only from // DOMContentLoaded onward and only once that script has loaded — it is // served no-store with a DB lookup per request, and the review/landing // pipelines inject it as an async end-of-body loader. Until it lands, // element.href is still the raw destination, so navigating with it hands // the producer a click carrying no tracking params (and reports empty // outboundParams). Going through the hook makes this listener agree with // every presell's own redirectToUrl by construction rather than by the // coincidence of updateAllUrls having already run. var targetUrl = normalizeUrl(url); if (typeof window.__clickfastAppendParamsToUrl === "function") { try { targetUrl = window.__clickfastAppendParamsToUrl(targetUrl) || targetUrl; } catch (error) { debugLog("Failed to append outbound params:", error); } } try { trackingData.outboundClicks.push({ elementType: element.tagName.toLowerCase(), url: targetUrl, params: getUrlParams(targetUrl), timestamp: new Date().toISOString(), timeOnPage: Math.round((Date.now() - pageStartTime) / 1000), }); await Promise.race([ sendTrackingData("outbound"), new Promise((resolve) => setTimeout(resolve, REQUEST_TIMEOUT)), ]); } catch (error) { debugLog("Tracking error:", error); } window.location.href = targetUrl; }, true ); // Helper functions function getUrlFromElement(element) { if (element.tagName === "A") return element.href; const onclick = element.onclick?.toString() || element.getAttribute("onclick"); return ( onclick?.match(/window.location.hrefs*=s*['"]([^'"]+)['"]/)?.[1] || onclick?.match(/['"](https?:\/\/[^'"]+)['"]/)?.[1] ); } function isExternalUrl(url) { try { return new URL(normalizeUrl(url)).hostname !== location.hostname; } catch { return false; } } function normalizeUrl(url) { return url.startsWith("http") ? url : "https://" + url; } function getUrlParams(url) { const params = {}; try { new URL(normalizeUrl(url)).searchParams.forEach( (v, k) => (params[k] = v) ); } catch { } return params; } async function sendTrackingData(type) { // Traffic source is now a simple string const trafficSourceValue = trackingData.trafficSource || "direct"; debugLog('Sending traffic source:', trafficSourceValue); const payload = { source: type === "inbound" ? "visit" : "click", cfId: trackingData.cfId, trafficSource: trafficSourceValue, inboundUrl: trackingData.pageUrl, inboundParams: trackingData.inboundParams, outboundUrl: trackingData.outboundClicks?.[0]?.url, outboundParams: trackingData.outboundClicks?.[0]?.params || {}, userAgent: trackingData.userAgent, screenResolution: trackingData.screenResolution, isBot: trackingData.isBot, outboundClicks: trackingData.outboundClicks, pageLoadTime: trackingData.pageLoadTime, timestamp: new Date().toISOString(), eventType: type, }; const endpoint = type === "inbound" ? TRACKING_VISIT_ENDPOINT : TRACKING_CLICK_ENDPOINT; try { const response = await fetch(endpoint, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(payload), // Outbound clicks fire right before the presell navigates away; without // keepalive the browser cancels the in-flight POST (and its CORS // preflight) on unload, so clicks only land when they win the race. keepalive: true, }); if (!response.ok) { debugLog("Tracking server error:", response.status); // A 403 has three unrelated causes (unregistered presell URL, inactive // product, product marked "Testado"). Preview deployments echo which one // as a "reason" code; production keeps the response opaque, so this // simply logs nothing extra there. try { var errorBody = await response.clone().json(); if (errorBody && errorBody.reason) { debugLog("Tracking rejected, reason:", errorBody.reason); } } catch (parseError) { debugLog("Tracking error body was not JSON"); } } return response; } catch (error) { debugLog("Failed to send tracking:", error); throw error; } } // Track page load time const pageStartTime = Date.now(); debugLog("Advanced tracking initialized"); }) ();