Last active
October 22, 2022 08:53
-
-
Save Startouf/39d1f37c0a0413a19cf9e0da6afc0cdf to your computer and use it in GitHub Desktop.
Modern UTM & Referral Cookie Replicator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script id="gtm-modern-cookie-replicator"> | |
/** | |
* Modern UTM & Referral Cookie Replicator | |
* | |
* Inspired from the UTMZ cookie replicator, | |
* whose goal was to makes a generally faithful representation | |
* of the old __utmz cookie from Classic Analytics. ; | |
* | |
* the "Modern" cookie replicator | |
* - stores and decodes the cookie as/from JSON format | |
* - stores raw UTM parameters along with google "friendly" equivalents using prefixes | |
* - stores the time at which utm of ga referral variables were updated | |
* - stores the first and last visited hosts | |
* - stores the first referrer | |
* - generates a unique visit ID | |
* | |
* Its goal is to be | |
* - data warehouse friendly (keep all raw data) | |
* - session friendly (you know exactly when the UTM were first set or updated) | |
* | |
* Stores the data in a cookie named cookieName (default `__cookie_utms`). | |
* Also sets a session cookie named sessionCookieName (default `__cookie_utms_ses`). | |
* | |
* Data is stored in the cookie_utms cookie in JSON format | |
* brackets indicate optional data: | |
* | |
* { | |
* ga_source: SOURCE | |
* ga_medium: MEDIUM | |
* ga_campaign: CAMPAIGN | |
* saved_at: CAMPAIGN | |
* first_visited_host: HOST | |
* last_visited_host: HOST | |
* referrer: STRING | null | |
* visit_id: STRING | |
* [|utm_campaign: UTM_CAMPAIGN] | |
* [|utm_medium: UTM_MEDIUM] | |
* [|utm_source: UTM_CAMPAIGN] | |
* [|utm_content: UTM_CONTENT] | |
* [|utm_term: UTM_TERM] | |
* [|utm_keyword: UTM_KEYWORD] | |
* } | |
*/ | |
(function(document) { | |
var referrer = document.referrer; | |
var gaReferral = { | |
'ga_source': '(direct)', | |
'ga_medium': '(none)', | |
'ga_campaign': '(not set)' | |
}; | |
var cookieName = '__cookie_utms' | |
var sessionCookieName = '__cookie_utms_ses' | |
var thisHostname = document.location.hostname; | |
var thisDomain = getDomain_(thisHostname); | |
var referringDomain = getDomain_(document.referrer); | |
var sessionCookie = getCookie_(sessionCookieName); | |
// Expiration 4 hours from UTM registration | |
var cookieExpiration = new Date(Date.now() + 4 * (60 * 60 * 1000) ); | |
var qs = document.location.search.replace('?', ''); | |
var hash = document.location.hash.replace('#', ''); | |
var gaParams = parseGoogleParams(qs + '#' + hash); | |
var referringInfo = parseGaReferrer(referrer); | |
var storedVals = getCookie_(cookieName); | |
var referralOrUTMChanged = false | |
var newCookieVals = {}; | |
var key; | |
// Always rewrite previous values | |
// (we need to extend the cookie expiration) | |
if (storedVals) { | |
for (key in storedVals) { | |
if (typeof storedVals[key] !== 'undefined') { | |
newCookieVals[key] = storedVals[key]; | |
} | |
} | |
} else { | |
for (key in gaReferral) { | |
if (typeof gaReferral[key] !== 'undefined') { | |
newCookieVals[key] = gaReferral[key]; | |
} | |
} | |
newCookieVals['visit_id'] = generateId() | |
newCookieVals['referrer'] = referrer | |
} | |
// Do not erase existing stuff when browsing same domain | |
if (sessionCookie && referringDomain === thisDomain) { | |
gaParams = null; | |
referringInfo = null; | |
} | |
if (gaParams && (gaParams.utm_source || gaParams.gclid || gaParams.dclid)) { | |
referralOrUTMChanged = true | |
// Override ga_ with actual UTM values | |
for (key in gaParams) { | |
if (typeof gaParams[key] !== 'undefined') { | |
gaReferral[key] = gaParams[key]; | |
} | |
} | |
// Google stuff | |
if (gaParams.gclid || gaParams.dclid) { | |
gaReferral.ga_source = 'google'; | |
gaReferral.ga_medium = gaReferral.ga_utmgclid ? 'cpc' : 'cpm'; | |
} | |
} else if (referringInfo) { | |
referralOrUTMChanged = true | |
// Override ga_ with actual referring info values | |
gaReferral.ga_source = referringInfo.source; | |
gaReferral.ga_medium = referringInfo.medium; | |
if (referringInfo.term) { | |
gaReferral.ga_term = referringInfo.term | |
} | |
} | |
if (!storedVals || !storedVals.first_visited_host) { | |
newCookieVals.first_visited_host = thisHostname | |
} | |
newCookieVals.last_visited_host = thisHostname | |
if (referralOrUTMChanged) { | |
for (key in gaReferral) { | |
if (typeof gaReferral[key] !== 'undefined') { | |
newCookieVals[key] = gaReferral[key]; | |
} | |
} | |
newCookieVals.saved_at = new Date().toISOString(); | |
} | |
writeCookie_(cookieName, newCookieVals, cookieExpiration, '/', thisDomain); | |
writeCookie_(sessionCookieName, 1, null, '/', thisDomain); | |
function parseGoogleParams(str) { | |
var campaignParams = ['source', 'medium', 'campaign', 'term', 'content', 'keyword']; | |
var regex = new RegExp('(utm_(' + campaignParams.join('|') + ')|(d|g)clid)=.*?([^&#]*|$)', 'gi'); | |
var gaParams = str.match(regex); | |
var paramsObj, | |
vals, | |
len, | |
i; | |
if (gaParams) { | |
paramsObj = {}; | |
len = gaParams.length; | |
for (i = 0; i < len; i++) { | |
vals = gaParams[i].split('='); | |
if (vals) { | |
paramsObj[vals[0]] = vals[1]; | |
} | |
} | |
} | |
return paramsObj; | |
} | |
function parseGaReferrer(referrer) { | |
if (!referrer) return; | |
var searchEngines = { | |
'daum.net': { | |
'p': 'q', | |
'n': 'daum' | |
}, | |
'eniro.se': { | |
'p': 'search_word', | |
'n': 'eniro ' | |
}, | |
'naver.com': { | |
'p': 'query', | |
'n': 'naver ' | |
}, | |
'yahoo.com': { | |
'p': 'p', | |
'n': 'yahoo' | |
}, | |
'msn.com': { | |
'p': 'q', | |
'n': 'msn' | |
}, | |
'bing.com': { | |
'p': 'q', | |
'n': 'live' | |
}, | |
'aol.com': { | |
'p': 'q', | |
'n': 'aol' | |
}, | |
'lycos.com': { | |
'p': 'q', | |
'n': 'lycos' | |
}, | |
'ask.com': { | |
'p': 'q', | |
'n': 'ask' | |
}, | |
'altavista.com': { | |
'p': 'q', | |
'n': 'altavista' | |
}, | |
'search.netscape.com': { | |
'p': 'query', | |
'n': 'netscape' | |
}, | |
'cnn.com': { | |
'p': 'query', | |
'n': 'cnn' | |
}, | |
'about.com': { | |
'p': 'terms', | |
'n': 'about' | |
}, | |
'mamma.com': { | |
'p': 'query', | |
'n': 'mama' | |
}, | |
'alltheweb.com': { | |
'p': 'q', | |
'n': 'alltheweb' | |
}, | |
'voila.fr': { | |
'p': 'rdata', | |
'n': 'voila' | |
}, | |
'search.virgilio.it': { | |
'p': 'qs', | |
'n': 'virgilio' | |
}, | |
'baidu.com': { | |
'p': 'wd', | |
'n': 'baidu' | |
}, | |
'alice.com': { | |
'p': 'qs', | |
'n': 'alice' | |
}, | |
'yandex.com': { | |
'p': 'text', | |
'n': 'yandex' | |
}, | |
'najdi.org.mk': { | |
'p': 'q', | |
'n': 'najdi' | |
}, | |
'seznam.cz': { | |
'p': 'q', | |
'n': 'seznam' | |
}, | |
'search.com': { | |
'p': 'q', | |
'n': 'search' | |
}, | |
'wp.pl': { | |
'p': 'szukaj ', | |
'n': 'wirtulana polska' | |
}, | |
'online.onetcenter.org': { | |
'p': 'qt', | |
'n': 'o*net' | |
}, | |
'szukacz.pl': { | |
'p': 'q', | |
'n': 'szukacz' | |
}, | |
'yam.com': { | |
'p': 'k', | |
'n': 'yam' | |
}, | |
'pchome.com': { | |
'p': 'q', | |
'n': 'pchome' | |
}, | |
'kvasir.no': { | |
'p': 'q', | |
'n': 'kvasir' | |
}, | |
'sesam.no': { | |
'p': 'q', | |
'n': 'sesam' | |
}, | |
'ozu.es': { | |
'p': 'q', | |
'n': 'ozu ' | |
}, | |
'terra.com': { | |
'p': 'query', | |
'n': 'terra' | |
}, | |
'mynet.com': { | |
'p': 'q', | |
'n': 'mynet' | |
}, | |
'ekolay.net': { | |
'p': 'q', | |
'n': 'ekolay' | |
}, | |
'rambler.ru': { | |
'p': 'words', | |
'n': 'rambler' | |
}, | |
'google': { | |
'p': 'q', | |
'n': 'google' | |
} | |
}; | |
var a = document.createElement('a'); | |
var values = {}; | |
var searchEngine, | |
termRegex, | |
term; | |
a.href = referrer; | |
// Shim for the billion google search engines | |
if (a.hostname.indexOf('google') > -1) { | |
referringDomain = 'google'; | |
} | |
if (searchEngines[referringDomain]) { | |
searchEngine = searchEngines[referringDomain]; | |
termRegex = new RegExp(searchEngine.p + '=.*?([^&#]*|$)', 'gi'); | |
term = a.search.match(termRegex); | |
values.source = searchEngine.n; | |
values.medium = 'organic'; | |
values.term = (term ? term[0].split('=')[1] : '') || '(not provided)'; | |
} else if (referringDomain !== thisDomain) { | |
values.source = a.hostname; | |
values.medium = 'referral'; | |
} | |
return values; | |
} | |
function writeCookie_(name, value, expiration, path, domain) { | |
var str = name + '=' + JSON.stringify(value) + '; SameSite=Lax; Secure'; | |
if (expiration) str += 'Expires=' + expiration.toGMTString() + ';'; | |
if (path) str += 'Path=' + path + ';'; | |
if (domain) str += 'Domain=' + domain + ';'; | |
document.cookie = str; | |
} | |
function getCookie_(cname) { | |
var matchingCookie = document.cookie.split('; ').find(function (row) { | |
return row.startsWith(cname + '='); | |
}) | |
if(!matchingCookie) { return null; } | |
var cookie = matchingCookie.split('=')[1] | |
if (cookie.startsWith('{')) { | |
return JSON.parse(cookie); | |
} else { | |
return null; | |
} | |
} | |
function getDomain_(url) { | |
if (!url) return; | |
var a = document.createElement('a'); | |
a.href = url; | |
try { | |
return a.hostname.match(/[^.]*\.[^.]{2,3}(?:\.[^.]{2,3})?$/)[0]; | |
} catch(squelch) {} | |
} | |
// https://stackoverflow.com/a/2117523/1177228 | |
function generateId() { | |
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, function (c) { | |
return (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16); | |
}); | |
} | |
})(document); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment