Traffic Source Mapper
Attribution & Channel Classification
Comprehensive traffic source attribution system that automatically classifies visitors by channel (organic, paid, social, direct, referral) with first-touch attribution tracking and detailed source mapping.
Key Features
Script Overview
What It Does
This script creates a comprehensive traffic source mapping system that tracks the original source of each visitor, classifies traffic into meaningful categories, and maintains detailed interaction logs. It goes beyond basic UTM tracking to provide complete attribution intelligence for your marketing campaigns.
Use Case
Essential for understanding true traffic source attribution and customer journey mapping. Perfect for businesses that need detailed insights into which channels drive the most valuable traffic, especially for complex multi-touch attribution scenarios.
Key Benefits
- β’Complete traffic source attribution
- β’First-touch visitor source tracking
- β’Detailed referrer analysis and classification
- β’Enhanced attribution modeling capabilities
- β’Cross-session source persistence
- β’Comprehensive analytics foundation
JavaScript Code
<script>
(function() {
'use strict';
// Traffic Source Mapper Script
// Version: 1.1
// Description: Comprehensive traffic source attribution and mapping
// Last Updated: 2025-09-09
// Configuration
var config = {
// Storage keys
storageKeys: {
sourceData: 'traffic_source_data',
sessionData: 'session_source_data'
},
// DataLayer event names
events: {
sourceCapture: 'traffic_source_captured',
sessionStart: 'session_source_logged'
},
// Data expiration (90 days for first-touch, session only for current)
expirationDays: 90,
// Source classification mapping
sourceClassification: {
organic: ['google', 'bing', 'yahoo', 'duckduckgo', 'yandex', 'baidu'],
social: ['facebook.com', 'twitter.com', 'linkedin.com', 'instagram.com', 'tiktok.com', 'youtube.com', 'pinterest.com'],
email: ['gmail.com', 'outlook.com', 'yahoo.com', 'mail.'],
paid: ['googleads', 'facebook.com/tr', 'bing.com/ads', 'linkedin.com/ads'],
referral: [] // Will be populated with any non-classified referrers
},
// Debug mode
debug: false
};
// Utility functions
function debugLog(message, data) {
if (config.debug) {
console.log('[Traffic Source] ' + message, data || '');
}
}
function getCurrentTimestamp() {
return {
timestamp: Date.now(),
iso: new Date().toISOString(),
readable: new Date().toLocaleString()
};
}
function parseUrl(url) {
try {
return new URL(url);
} catch (e) {
return null;
}
}
function classifyTrafficSource(referrer, currentUrl) {
if (!referrer || referrer === currentUrl) {
return {
source: 'direct',
medium: 'none',
category: 'direct'
};
}
var referrerUrl = parseUrl(referrer);
if (!referrerUrl) {
return {
source: 'unknown',
medium: 'referral',
category: 'referral'
};
}
var hostname = referrerUrl.hostname.toLowerCase();
var searchParams = referrerUrl.searchParams;
// Check for UTM parameters first
var currentUrlObj = parseUrl(currentUrl);
if (currentUrlObj && currentUrlObj.searchParams) {
var utmSource = currentUrlObj.searchParams.get('utm_source');
var utmMedium = currentUrlObj.searchParams.get('utm_medium');
var utmCampaign = currentUrlObj.searchParams.get('utm_campaign');
if (utmSource || utmMedium) {
return {
source: utmSource || 'unknown',
medium: utmMedium || 'unknown',
category: 'campaign',
campaign: utmCampaign || '',
referrer: referrer
};
}
}
// Check for Google Ads click IDs
if (currentUrlObj && (currentUrlObj.searchParams.get('gclid') || currentUrlObj.searchParams.get('wbraid') || currentUrlObj.searchParams.get('gbraid'))) {
return {
source: 'google',
medium: 'cpc',
category: 'paid',
referrer: referrer,
clickId: currentUrlObj.searchParams.get('gclid') || currentUrlObj.searchParams.get('wbraid') || currentUrlObj.searchParams.get('gbraid')
};
}
// Classify based on referrer domain
for (var category in config.sourceClassification) {
var domains = config.sourceClassification[category];
for (var i = 0; i < domains.length; i++) {
if (hostname.includes(domains[i])) {
return {
source: hostname,
medium: category,
category: category,
referrer: referrer
};
}
}
}
// Check if it's a search engine
if (searchParams.get('q') || searchParams.get('query') || searchParams.get('search')) {
return {
source: hostname,
medium: 'organic',
category: 'organic',
referrer: referrer,
searchQuery: searchParams.get('q') || searchParams.get('query') || searchParams.get('search')
};
}
// Default to referral
return {
source: hostname,
medium: 'referral',
category: 'referral',
referrer: referrer
};
}
function getStoredSourceData() {
try {
var stored = localStorage.getItem(config.storageKeys.sourceData);
if (stored) {
var data = JSON.parse(stored);
// Check expiration
if (data.timestamp && config.expirationDays) {
var expirationTime = data.timestamp + (config.expirationDays * 24 * 60 * 60 * 1000);
if (Date.now() > expirationTime) {
debugLog('Source data expired, clearing storage');
localStorage.removeItem(config.storageKeys.sourceData);
return null;
}
}
return data;
}
} catch (e) {
debugLog('Error reading stored source data:', e);
}
return null;
}
function storeSourceData(sourceInfo) {
try {
var timeInfo = getCurrentTimestamp();
var dataToStore = {
...sourceInfo,
...timeInfo,
pageUrl: window.location.href,
userAgent: navigator.userAgent,
screenResolution: screen.width + 'x' + screen.height,
language: navigator.language
};
localStorage.setItem(config.storageKeys.sourceData, JSON.stringify(dataToStore));
debugLog('Source data stored:', dataToStore);
return dataToStore;
} catch (e) {
debugLog('Error storing source data:', e);
return null;
}
}
function logSessionSource(sourceInfo) {
try {
var timeInfo = getCurrentTimestamp();
var sessionData = {
...sourceInfo,
...timeInfo,
sessionId: Date.now() + '_' + Math.random().toString(36).substr(2, 9),
pageUrl: window.location.href
};
// Store session data (will be cleared when browser tab closes)
sessionStorage.setItem(config.storageKeys.sessionData, JSON.stringify(sessionData));
debugLog('Session source logged:', sessionData);
return sessionData;
} catch (e) {
debugLog('Error logging session source:', e);
return null;
}
}
function pushToDataLayer(sourceData, isFirstTouch) {
window.dataLayer = window.dataLayer || [];
var eventData = {
event: isFirstTouch ? config.events.sourceCapture : config.events.sessionStart,
traffic_source: sourceData.source,
traffic_medium: sourceData.medium,
traffic_category: sourceData.category,
traffic_referrer: sourceData.referrer || '',
is_first_touch: isFirstTouch,
source_timestamp: sourceData.timestamp,
page_url: sourceData.pageUrl
};
// Add optional fields if they exist
if (sourceData.campaign) eventData.traffic_campaign = sourceData.campaign;
if (sourceData.clickId) eventData.click_id = sourceData.clickId;
if (sourceData.searchQuery) eventData.search_query = sourceData.searchQuery;
if (sourceData.sessionId) eventData.session_id = sourceData.sessionId;
window.dataLayer.push(eventData);
debugLog('Data pushed to dataLayer:', eventData);
}
function getSourceInteractionLevel() {
// Simple interaction scoring based on current page
var interactionLevel = 'low';
var currentPath = window.location.pathname.toLowerCase();
// High-intent pages
if (currentPath.includes('pricing') ||
currentPath.includes('demo') ||
currentPath.includes('trial') ||
currentPath.includes('contact') ||
currentPath.includes('signup')) {
interactionLevel = 'high';
}
// Medium-intent pages
else if (currentPath.includes('features') ||
currentPath.includes('product') ||
currentPath.includes('solutions') ||
currentPath.includes('about')) {
interactionLevel = 'medium';
}
return interactionLevel;
}
// Main execution
function initTrafficSourceMapping() {
debugLog('Initializing traffic source mapping');
var currentUrl = window.location.href;
var referrer = document.referrer;
// Classify current traffic source
var currentSource = classifyTrafficSource(referrer, currentUrl);
currentSource.interactionLevel = getSourceInteractionLevel();
debugLog('Current source classification:', currentSource);
// Check for existing first-touch data
var storedSource = getStoredSourceData();
if (!storedSource) {
// First visit - store as first-touch attribution
var firstTouchData = storeSourceData(currentSource);
if (firstTouchData) {
pushToDataLayer(firstTouchData, true);
debugLog('First-touch source captured');
}
} else {
debugLog('Using existing first-touch data:', storedSource);
// Push existing first-touch data for reference
pushToDataLayer(storedSource, false);
}
// Always log current session source (for multi-touch attribution)
var sessionData = logSessionSource(currentSource);
if (sessionData) {
// Push session-specific event
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'session_traffic_source',
session_source: currentSource.source,
session_medium: currentSource.medium,
session_category: currentSource.category,
session_interaction_level: currentSource.interactionLevel,
session_id: sessionData.sessionId
});
}
}
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initTrafficSourceMapping);
} else {
initTrafficSourceMapping();
}
// Expose utility functions
window.trafficSourceMapper = {
getFirstTouchData: function() {
return getStoredSourceData();
},
getCurrentSessionData: function() {
try {
var stored = sessionStorage.getItem(config.storageKeys.sessionData);
return stored ? JSON.parse(stored) : null;
} catch (e) {
return null;
}
},
clearFirstTouchData: function() {
localStorage.removeItem(config.storageKeys.sourceData);
debugLog('First-touch data cleared');
},
refreshMapping: function() {
initTrafficSourceMapping();
}
};
})();
</script>
π‘ Pro Tip: Copy the entire code block above and paste it directly into a GTM Custom HTML tag. The script is self-contained and ready to use immediately.