Last active
December 5, 2017 08:32
-
-
Save smhmic/671e7058cff9dd1c4baf40fe740bf87f to your computer and use it in GitHub Desktop.
This is for a site on UA, that needs to track sessions across some digital assets/platforms that are stuck on legacy versions of GA. This only works for traffic clicking from the UA site to the asset. (But after clicking from UA > Asset once, all subsequent Asset > UA clicks do not need cross-domain tracking and will not break the session).
This file contains 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> | |
/** | |
* This script facilitates cross-domain tracking when one site is runs | |
* GA Universal and another runs legacy GA (Traditional or Async/Classic). | |
* | |
* This integration currently unilateral, working only for users | |
* navigating from Universal > Legacy. For example: if a user clicks | |
* on a paid ad and lands directly on your asset/platform (legacy), | |
* then goes to your main site (UA), then clicks back to your asset/platform | |
* and converts, it will be tracked as two users: one that bounced from a | |
* paid ad, and one with a conversion attributed to whatever channel last | |
* brought them to the site (or if they're a new user, a self-referral or | |
* direct, depending on your Referral Exclusion List settings). | |
* THE MORAL: Do not use this if you get traffic directly to your asset/platform! | |
* | |
* To implement: | |
* 1) Ensure GA Traditional/Async tracking allows linker (@see http://j.mp/1Y9h4QH). | |
* 2) Place this script on site(s) running Universal (If using GTM, place in | |
* Custom HTML Tag (wrapped in `script` tags), firing on 'All Pages'). | |
* 3) Update `GA_LEGACY_DOMAIN_HERE` to match URLs of | |
* site(s)/page(s) running legacy GA. | |
*/ | |
(function decorateLinksForAsyncGA(){ | |
// Only links matching this pattern will be decorated. | |
// v v v v v v UPDATE THIS REGEX v v v v v v | |
var traditionalUrlRegex = /^https?:\/\/(GA_LEGACY_DOMAIN_HERE)([\/?#]|$)/i; | |
// ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ | |
var | |
gaObjName = window.GoogleAnalyticsObject || 'ga', | |
ga = window[gaObjName], | |
linkerParams; | |
// Delay execution if ... | |
if( ! ga // GA command queue is not instantiated, | |
|| ! ga.getAll // GA is not loaded, | |
|| ! ga.getAll().length // or a tracker has been created. | |
) return window.setTimeout( decorateLinksForAsyncGA, 100 ); | |
function makeLinkerParams(){ | |
var | |
num = 1, | |
c = ga.getAll()[0].get( 'clientId' ).split('.'), | |
hash = function( a ){var b=1,c=0,d;if(a)for(b=0,d=a.length-1; | |
0<=d;d--)c=a.charCodeAt(d),b=(b<<6&268435455)+c+(c<<14), | |
c=b&266338304,b=0!=c?b^c>>21:b;return b}, | |
domainHash = hash( location.host ), | |
params = [ | |
/* utma */ [ domainHash, c[0], c[1], c[1], c[1], num ].join('.'), | |
/* utmb */ [ domainHash, 1, 10, c[1] ].join('.'), | |
/* utmc */ domainHash, | |
/* utmx */ '-', | |
/* utmz */ [ domainHash, c[1], num, num, 'utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)' ].join('.'), | |
/* utmv */ '-' | |
]; | |
return '__utma=' + params[0] | |
+ '&__utmb=' + params[1] | |
+ '&__utmc=' + params[2] | |
+ '&__utmx=' + params[3] | |
+ '&__utmz=' + params[4] | |
+ '&__utmv=' + params[5] | |
+ '&__utmk=' + hash( params.join( '' ) ); | |
} | |
function onclickDecorateLink(){ | |
var el = ( arguments[0] || window.e ).target, p; | |
if( el.tagName !== 'A' ) return; | |
if( ! el.href.match( traditionalUrlRegex ) ) return; | |
if( ! linkerParams ) linkerParams = makeLinkerParams(); | |
else if( el.href.indexOf( linkerParams ) ) return; | |
p = /([^#?]*)([^#]*)(.*)/.exec( el.href ); | |
el.href = p[1] + p[2] + ( p[2] ? '&' : '?' ) + linkerParams + p[3]; | |
} | |
if( document.addEventListener ) | |
document.addEventListener( 'click', onclickDecorateLink, false ); | |
else | |
document.attachEvent( 'onclick', onclickDecorateLink ); | |
})(); | |
</script> |
This file contains 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
(/** @callback - Runs repeatedly until a UA tracker is created. */ | |
function decorateLinksForAsyncGA(){ | |
var | |
/** | |
* Only links matching this pattern will be decorated. | |
* @const {RegExp} */ | |
traditionalUrlRegex = /^https?:\/\/(GA_LEGACY_DOMAIN_HERE)([\/?#]|$)/i, | |
/** | |
* Variable name of the global Universal GA object. | |
* @var {Object} */ | |
gaObjName = window.GoogleAnalyticsObject || 'ga', | |
/** | |
* The global Universal GA object. | |
* @var {Object} ga | |
* @prop {function} ga.getAll - Returns an array of tracker objects. */ | |
ga = window[gaObjName], | |
/** | |
* GA legacy linker params; created on first matching link click. | |
* @var {string} */ | |
linkerParams; | |
// Delay execution if ... | |
if( ! ga // GA command queue is not instantiated, | |
|| ! ga.getAll // GA is not loaded, | |
|| ! ga.getAll().length // or a tracker has been created. | |
) return window.setTimeout( decorateLinksForAsyncGA, 100 ); | |
/** | |
* @function - Creates GA legacy linker parameters using UA client id. | |
* @returns {string} Linker url query parameters w/o leading "?" or "&". | |
*/ | |
function makeLinkerParams(){ | |
var | |
/** | |
* Integer used multiple places in GA legacy linker/cookie | |
* structure. Often 1, sometimes higher. Meaning/significance unknown. | |
* @const {number} */ | |
num = 1, | |
/** | |
* Parts of the UA client id (cid). Yields two strings of | |
* digits (unless cid structure is customized). Second part used | |
* multiple places in GA legacy linker/cookie structure. | |
* @var {Array} */ | |
c = ga.getAll()[0].get( 'clientId' ).split('.'), | |
/** | |
* Generate hash based on a string. Identical to the hash functions | |
* in ga.js and analytics.js. | |
* @function | |
* @param {string} The string to hash. | |
* @returns {string} The hash of the passed string. */ | |
hash = function( a ){var b=1,c=0,d;if(a)for(b=0,d=a.length-1; | |
0<=d;d--)c=a.charCodeAt(d),b=(b<<6&268435455)+c+(c<<14), | |
c=b&266338304,b=0!=c?b^c>>21:b;return b}, | |
/** | |
* Hash of current domain. Used multiple places in GA Classic | |
* linker/cookie structure. | |
* @var {string} */ | |
domainHash = hash( location.host ), | |
/** @var {string[]} - GA Classic linker parameter values. */ | |
params = [ | |
/* utma */ [ domainHash, c[0], c[1], c[1], c[1], num ].join('.'), | |
/* utmb */ [ domainHash, 1, 10, c[1] ].join('.'), | |
/* utmc */ domainHash, | |
/* utmx */ '-', | |
/* utmz */ [ domainHash, c[1], num, num, 'utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)' ].join('.'), | |
/* utmv */ '-' | |
]; | |
return '__utma=' + params[0] | |
+ '&__utmb=' + params[1] | |
+ '&__utmc=' + params[2] | |
+ '&__utmx=' + params[3] | |
+ '&__utmz=' + params[4] | |
+ '&__utmv=' + params[5] | |
+ '&__utmk=' + hash( params.join( '' ) ); | |
} | |
/** | |
* Click handler; decorates clicked link with GA Traditional linker | |
* parameters (as defined in `makeLinkerParams()`) if the link url | |
* matches the `traditionalUrlRegex` pattern. | |
* @callback | |
*/ | |
function onclickDecorateLink(){ | |
/** | |
* @name e | |
* @global | |
* @type {Object} | |
* @prop {Object} e.target | |
*/ | |
var /** @var {Object} - The clicked element. */ | |
el = ( arguments[0] || window.e ).target, | |
/** @var {string[]} - Link url parts. */ | |
p; // href parts | |
// Abort if not an html link. | |
if( el.tagName !== 'A' ) return; | |
// Abort if link doesn't match the configured URL pattern. | |
if( ! el.href.match( traditionalUrlRegex ) ) return; | |
// Generate linker parameters once on this page. | |
if( ! linkerParams ) linkerParams = makeLinkerParams(); | |
// Abort if link URL was already decorated with the same parameters. | |
else if( el.href.indexOf( linkerParams ) ) return; | |
// Update link url with linker parameters. | |
p = /([^#?]*)([^#]*)(.*)/.exec( el.href ); | |
el.href = p[1] + p[2] + ( p[2] ? '&' : '?' ) + linkerParams + p[3]; | |
} | |
// Listen for all clicks. | |
if( document.addEventListener ) | |
document.addEventListener( 'click', onclickDecorateLink, false ); | |
else | |
document.attachEvent( 'onclick', onclickDecorateLink ); | |
})(); |
This file contains 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
// Run this in the console to check if the GA Legacy (Traditional or Async/Classic) tracking on the page allow the linker paramaters needed for cross-domain tracking. | |
(function(){ | |
var t, v, trackers, _gat = window._gat; | |
if( ! _gat ) return console.error('Legacy GA not found on page.'); | |
trackers = _gat._getTrackers(); | |
console.log( trackers.length+' legacy trackers found.' ); | |
for(var i=0; i<trackers.length; i++ ){ | |
t = trackers[i]; v = t.get('x10'); | |
// Sanity check, since this is an undocumented way to access data from ga.js. | |
if( t._setAllowLinker(!v), t.get('x10')===!v && (t._setAllowLinker(v)||1) ) | |
console.log( t._getAccount()+' setAllowLinker: '+t.get('x10') ); | |
else console.error('This code does not work for this version of ga.js'); | |
}; | |
})() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment