1; (function () { 2 'use strict'; 3 const configElement = document.querySelector('script[data-amplitude-api-key]'); 4 const apiKey = configElement?.getAttribute('data-amplitude-api-key'); 5 6 if (!apiKey) { 7 return; 8 } 9 10 const script = document.createElement('script'); 11 script.src = `https://cdn.amplitude.com/script/${encodeURIComponent(apiKey)}.js`; 12 script.onload = function () { 13 const enrichUserAgentPlugin = () => { 14 return { 15 name: 'enrichUserAgentPlugin', 16 execute: async (event) => { 17 event.event_properties = { 18 ...event.event_properties, 19 user_agent: window.navigator.userAgent 20 }; 21 return event; 22 }, 23 }; 24 }; 25 26 window.amplitude?.add(enrichUserAgentPlugin()); 27 window.amplitude?.init(apiKey); 28 }; 29 30 document.head.appendChild(script); 31 32 // comma separated list of domains (without scheme or trailing slash) 33 const domainsToDecorate = parseList(configElement.getAttribute('data-amplitude-decorate-domains')) 34 .map(function (domain) { 35 return domain.toLowerCase(); 36 }); 37 // comma separated list of query parameters to carry over from the current page 38 const queryParams = parseList(configElement.getAttribute('data-amplitude-decorate-params')); 39 40 // nothing to decorate, so do not bother listening for link activation 41 if (!domainsToDecorate.length) { 42 return; 43 } 44 45 function parseList(value) { 46 return (value || '') 47 .split(',') 48 .map(function (item) { 49 return item.trim(); 50 }) 51 .filter(Boolean); 52 } 53 54 // matches the host itself or any subdomain of it, so a domain name appearing 55 // elsewhere in the URL (path, query) does not trigger decoration 56 function shouldDecorate(url) { 57 return domainsToDecorate.some(function (domain) { 58 return url.hostname === domain || url.hostname.endsWith('.' + domain); 59 }); 60 } 61 62 // carries the tracking params over from the current page, leaving any the link 63 // already specifies untouched, then stamps the Amplitude identifiers. 64 // returns false when Amplitude has not produced its identifiers yet. 65 function decorate(url) { 66 const deviceId = window.amplitude?.getDeviceId?.(); 67 const sessionId = window.amplitude?.getSessionId?.(); 68 if (!deviceId || !sessionId) { 69 return false; 70 } 71 72 const currentParams = new URLSearchParams(window.location.search); 73 queryParams.forEach(function (param) { 74 // a value authored on the link itself wins over the current page's 75 if (url.searchParams.has(param)) { 76 return; 77 } 78 79 const value = currentParams.get(param); 80 if (value !== null) { 81 url.searchParams.set(param, value); 82 } 83 }); 84 url.searchParams.set('ampDeviceId', deviceId); 85 url.searchParams.set('ampSessionId', sessionId); 86 url.searchParams.set('ampTimestamp', Date.now() + 5 * 60 * 1000); 87 return true; 88 } 89 90 function decorateLink(event) { 91 // closest('a') also matches SVG anchors, whose href is an SVGAnimatedString 92 // rather than a string. restrict to HTML anchors so the URL parse below 93 // cannot fail on one. 94 const link = event.target?.closest?.('a'); 95 if (!(link instanceof HTMLAnchorElement) || !link.href) { 96 return; 97 } 98 99 let url; 100 try { 101 url = new URL(link.href, window.location.href); 102 } catch { 103 return; 104 } 105 106 if (shouldDecorate(url) && decorate(url)) { 107 link.href = url.toString(); 108 } 109 } 110 111 // decorate on activation rather than at load time, so links added to the page 112 // after this script runs are covered too. capture phase, so the href is 113 // rewritten before any other handler acts on it. 114 // mousedown precedes click, auxclick and contextmenu for every mouse button, 115 // so it covers primary, middle and right button activation. contextmenu also 116 // fires without a preceding mousedown when the menu is opened from the 117 // keyboard or by long press. click covers keyboard activation via Enter. 118 ['mousedown', 'contextmenu', 'click'].forEach(function (eventName) { 119 document.addEventListener(eventName, decorateLink, true); 120 });
121})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.