Home→
Traffic Source Mapper
πŸ—ΊοΈ

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.

AttributionIntermediateAttribution & Tracking

Key Features

βœ“Automatic channel classification
βœ“First-touch attribution tracking
βœ“Custom source mapping rules
βœ“Referrer domain analysis
βœ“UTM parameter integration
βœ“Social platform detection

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.