diff --git a/packages/script/src/extensions/outbound-domains.js b/packages/script/src/extensions/outbound-domains.js index 51f3edd..2b59412 100644 --- a/packages/script/src/extensions/outbound-domains.js +++ b/packages/script/src/extensions/outbound-domains.js @@ -8,36 +8,75 @@ const initOutboundDomains = () => { } = window._dubAnalytics; let outboundLinksUpdated = new Set(); // Track processed links + function normalizeDomain(domain) { + return domain.replace(/^www\./, '').trim(); + } + + function isMatchingDomain(url, domain) { + try { + const urlHostname = new URL(url).hostname; + const normalizedUrlHostname = normalizeDomain(urlHostname); + const normalizedDomain = normalizeDomain(domain); + + // Exact match after removing www. + return normalizedUrlHostname === normalizedDomain; + } catch (e) { + return false; + } + } + function addOutboundTracking(clickId) { - // Parse comma-separated outbound domains - const outboundDomains = DOMAINS_CONFIG.outbound - ?.split(',') - .map((d) => d.trim()); + // Handle both string and array configurations for outbound domains + const outboundDomains = Array.isArray(DOMAINS_CONFIG.outbound) + ? DOMAINS_CONFIG.outbound + : DOMAINS_CONFIG.outbound?.split(',').map((d) => d.trim()); + if (!outboundDomains?.length) return; - const currentDomain = HOSTNAME.replace(/^www\./, ''); - const filteredDomains = outboundDomains.filter((d) => d !== currentDomain); + const currentDomain = normalizeDomain(HOSTNAME); + const filteredDomains = outboundDomains + .map(normalizeDomain) + .filter((d) => d !== currentDomain); const existingCookie = clickId || cookieManager.get(DUB_ID_VAR); if (!existingCookie) return; - const selector = filteredDomains - .map((domain) => `a[href*="${domain}"]`) - .join(','); + // Get all links and iframes + const elements = document.querySelectorAll('a[href], iframe[src]'); + if (!elements || elements.length === 0) return; - const links = document.querySelectorAll(selector); - if (!links || links.length === 0) return; - - links.forEach((link) => { - // Skip already processed links - if (outboundLinksUpdated.has(link)) return; + elements.forEach((element) => { + // Skip already processed elements + if (outboundLinksUpdated.has(element)) return; try { - const url = new URL(link.href); - url.searchParams.set(DUB_ID_VAR, existingCookie); - link.href = url.toString(); - outboundLinksUpdated.add(link); - } catch (e) {} + const urlString = element.href || element.src; + if (!urlString) return; + + // Check if the URL matches any of our outbound domains + const isOutbound = filteredDomains.some((domain) => + isMatchingDomain(urlString, domain), + ); + if (!isOutbound) return; + + const url = new URL(urlString); + + // Only add the tracking parameter if it's not already present + if (!url.searchParams.has(DUB_ID_VAR)) { + url.searchParams.set(DUB_ID_VAR, existingCookie); + + // Update the appropriate attribute based on element type + if (element.tagName.toLowerCase() === 'a') { + element.href = url.toString(); + } else if (element.tagName.toLowerCase() === 'iframe') { + element.src = url.toString(); + } + + outboundLinksUpdated.add(element); + } + } catch (e) { + console.error('Error processing element:', e); + } }); }