Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 59 additions & 20 deletions packages/script/src/extensions/outbound-domains.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
});
}

Expand Down