Last active
August 6, 2020 20:58
-
-
Save sahava/d5f068bf10f0f8e7c638bb40ba8591ec to your computer and use it in GitHub Desktop.
One customTask to rule them all.
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
var _customTask = function () { | |
// customTask Builder by Simo Ahava | |
// | |
// More information about customTask: https://www.simoahava.com/analytics/customtask-the-guide/ | |
// | |
// Change the default values for the settings below. | |
// clientIdIndex: The Custom Dimension index to where you want to send the visitor's Client ID. | |
// https://bit.ly/2Ms0ZcC | |
var clientIdIndex = 1; | |
// hitTypeIndex: The Custom Dimension index to where you want to send the hit type of the request. | |
// https://bit.ly/2KZqLA9 | |
var hitTypeIndex = 2; | |
// payloadLengthIndex: The Custom Dimension index to where you want to send the length of the payload of the request. | |
// https://bit.ly/2PdhPKM | |
var payloadLengthIndex = 3; | |
// removeCustomDefinitionsFromTimingHits: Set to true if you want to remove Custom Dimensions and Custom Metrics from the page speed timing hit. | |
// https://bit.ly/2nGzw8T | |
var removeCustomDefinitionsFromTimingHits = true; | |
// duplicateHitToTrackingIds: Array of all Universal Analytics tracking IDs to where you want to duplicate the initial hit. | |
// https://bit.ly/2MwFGGP | |
var duplicateHitToTrackingIds = ['UA-12345-1']; | |
// piiRegex: Array of {name, regex} objects, where the regular expression matches a pattern you want to replace with [REDACTED name]. | |
// https://bit.ly/2wcJym2 | |
var piiRegex = [{ | |
name: 'EMAIL', | |
regex: /.{4}@.{4}/g | |
}]; | |
// snowplowEndpoint: The Snowplow collector endpoint to which you want to send the GA request hit payload. | |
// https://bit.ly/2OCBzXc | |
var snowplowEndpoint = 'https://collector.simoahava.com/'; | |
// updateSessionCookie: Object which contains both an expiration time (in milliseconds) and domain name. | |
// https://bit.ly/2wh1xsH | |
var updateSessionCookie = { | |
expiresMs: 1000 * 60 * 30, | |
domain: 'mydomain.com' | |
}; | |
// iframeDecorator: Configuration object for decorating any iframe with cross-domain parameters when this customTask is run. | |
// https://bit.ly/2LUoWFf | |
var iframeDecorator = { | |
selector: 'iframe#decorateMe', | |
attempts: 10, | |
intervalMs: 1000, | |
useAnchor: false | |
}; | |
// transactionDeduper: Configuration object for preventing duplicate transactions from being recorded. | |
// https://bit.ly/2AvSZ2Y | |
var transactionDeduper = { | |
keyName: '_transaction_ids', | |
cookieExpiresDays: 365 | |
}; | |
// obfuscate: Obfuscates the entire hit payload (using a dictionary of words consistently) and dispatches it to the trackingId you provide. | |
// https://bit.ly/2RectUl | |
var obfuscate = { | |
tid: 'UA-12345-1', | |
dict: ['tumble', 'noble', 'flourish', 'abandon', 'liberal', 'team', 'conflict', 'collar', 'tiger', 'stun', 'grace', 'resource', 'phantom', 'imagine', 'information', 'hall', 'sweet', 'agriculture', 'bingo', 'relative'], | |
stringParams: ['uid','ua','dr','cn','cs','cm','ck','cc','ci','gclid','dclid','dl','dh','dp','dt','cd','cg[1-5]','linkid','an','aid','av','aiid','ec','ea','el','ti','ta','in','ic','iv','pr\\d{1,3}id','pr\\d{1,3}nm','pr\\d{1,3}br','pr\\d{1,3}ca','pr\\d{1,3}va','pr\\d{1,3}cc','pr\\d{1,3}cd\\d{1,3}','tcc','pal','col','il\\d{1,3}nm','il\\d{1,3}pi\\d{1,3}id','il\\d{1,3}pi\\d{1,3}nm','il\\d{1,3}pi\\d{1,3}br','il\\d{1,3}pi\\d{1,3}ca','il\\d{1,3}pi\\d{1,3}va','il\\d{1,3}pi\\d{1,3}cd\\d{1,3}','promo\\d{1,3}id','promo\\d{1,3}nm','promo\\d{1,3}cr','promo\\d{1,3}ps','sn','sa','st','utc','utv','utl','exd','cd\\d{1,3}','xid','exp','_utmz'], | |
priceParams: ['tr','ts','tt','ip','pr\\d{1,3}pr','id\\d{1,3}pi\\d{1,3}pr'], | |
priceModifier: Math.random(), | |
medium: ['organic', 'referral', 'social', 'cpc'], | |
replaceString: function(t){if(''===t)return t;'function'==typeof window.btoa&&(t=btoa(t));var n=t.split('').map(function(t){return t.charCodeAt(0)}).join('')%obfuscate.dict.length;return obfuscate.dict[n]}, | |
init: function(){var c=[];obfuscate.dict.forEach(function(t){obfuscate.dict.forEach(function(o){c.push(t+'-'+o)})}),obfuscate.dict=obfuscate.dict.concat(c)} | |
}; | |
// localStorageCid: Use localStorage to persist Client ID with Google Analytics tags | |
// https://bit.ly/2GNElc4 | |
var localStorageCid = { | |
objectName: 'ga_client_id', | |
expires: 1000*60*60*24*365*2 | |
}; | |
// DO NOT EDIT ANYTHING BELOW THIS LINE | |
if (typeof obfuscate === 'object' && typeof obfuscate.init === 'function') obfuscate.init(); | |
var readFromStorage = function (key) { | |
if (!window.Storage) { | |
// From: https://stackoverflow.com/a/15724300/2367037 | |
var value = '; ' + document.cookie; | |
var parts = value.split('; ' + key + '='); | |
if (parts.length === 2) { | |
return parts.pop().split(';').shift(); | |
} | |
} else { | |
return window.localStorage.getItem(key); | |
} | |
}; | |
var writeToStorage = function (key, value, expireDays) { | |
if (!window.Storage) { | |
var expiresDate = new Date(); | |
expiresDate.setDate(expiresDate.getDate() + expireDays); | |
document.cookie = key + '=' + value + ';expires=' + expiresDate.toUTCString(); | |
} else { | |
window.localStorage.setItem(key, value); | |
} | |
}; | |
var globalSendHitTaskName = '_ga_originalSendHitTask'; | |
return function (customTaskModel) { | |
window[globalSendHitTaskName] = window[globalSendHitTaskName] || customTaskModel.get('sendHitTask'); | |
// clientIdIndex | |
if (typeof clientIdIndex === 'number') { | |
customTaskModel.set('dimension' + clientIdIndex, customTaskModel.get('clientId')); | |
} | |
// /clientIdIndex | |
// hitTypeIndex | |
if (typeof hitTypeIndex === 'number') { | |
customTaskModel.set('dimension' + hitTypeIndex, customTaskModel.get('hitType')); | |
} | |
// /hitTypeIndex | |
// removeCustomDefinitionsFromTimingHits | |
if (typeof removeCustomDefinitionsFromTimingHits === 'boolean' && removeCustomDefinitionsFromTimingHits === true) { | |
if (customTaskModel.get('hitType') === 'timing') { | |
var _rcd_tempFieldObject = {}; | |
var _rcd_dimensionIndex = 1; | |
while (_rcd_dimensionIndex !== 201) { | |
_rcd_tempFieldObject['dimension' + _rcd_dimensionIndex] = undefined; | |
_rcd_tempFieldObject['metric' + _rcd_dimensionIndex] = undefined; | |
_rcd_dimensionIndex++; | |
} | |
customTaskModel.set(_rcd_tempFieldObject); | |
} | |
} | |
// /removeCustomDefinitionsFromTimingHits | |
// iframeDecorator | |
if (typeof iframeDecorator === 'object' && typeof iframeDecorator.selector === 'string' && typeof iframeDecorator.attempts === 'number' && typeof iframeDecorator.intervalMs === 'number') { | |
var _id_decorateTimer; | |
var _id_count = 0; | |
var _id_ga = window[window['GoogleAnalyticsObject']]; | |
var _id_tracker = _id_ga.getByName(customTaskModel.get('name')); | |
var _id_decorateIframe = function () { | |
var _id_iframe = document.querySelector(iframeDecorator.selector); | |
if (_id_iframe !== null && /[?&]_ga=/.test(_id_iframe.src)) { | |
window.clearInterval(_id_decorateTimer); | |
return; | |
} | |
if (_id_iframe === null) { | |
_id_count += 1; | |
if (_id_count === iframeDecorator.attempts) { | |
window.clearInterval(_id_decorateTimer); | |
} | |
return; | |
} | |
window.clearInterval(_id_decorateTimer); | |
_id_iframe.src = (new window.gaplugins.Linker(_id_tracker)).decorate(_id_iframe.src, iframeDecorator.useAnchor); | |
}; | |
_id_decorateTimer = window.setInterval(_id_decorateIframe, iframeDecorator.intervalMs); | |
} | |
// /iframeDecorator | |
// localStorageCid | |
if (typeof localStorageCid === 'object' && typeof localStorageCid.objectName === 'string' && typeof localStorageCid.expires === 'number' && window.Storage) { | |
var _lsc_clientId = customTaskModel.get('clientId'); | |
var _lsc_obj = JSON.stringify({ | |
clientId: _lsc_clientId, | |
expires: localStorageCid.expires | |
}); | |
window.localStorage.setItem(localStorageCid.objectName, _lsc_obj); | |
} | |
// /localStorageCid | |
customTaskModel.set('sendHitTask', function (sendHitTaskModel) { | |
var originalSendHitTaskModel = sendHitTaskModel, | |
originalSendHitTask = window[globalSendHitTaskName], | |
canSendHit = true; | |
try { | |
// transactionDeduper | |
if (typeof transactionDeduper === 'object' && transactionDeduper.hasOwnProperty('keyName') && transactionDeduper.hasOwnProperty('cookieExpiresDays') && typeof sendHitTaskModel.get('&ti') !== 'undefined') { | |
var _td_transactionId = sendHitTaskModel.get('&ti'); | |
var _td_storedIds = JSON.parse(readFromStorage(transactionDeduper.keyName) || '[]'); | |
if (_td_storedIds.indexOf(_td_transactionId) > -1 && ['transaction', 'item'].indexOf(sendHitTaskModel.get('hitType')) === -1) { | |
canSendHit = false; | |
} else if (_td_storedIds.indexOf(_td_transactionId) === -1) { | |
_td_storedIds.push(_td_transactionId); | |
writeToStorage(transactionDeduper.keyName, JSON.stringify(_td_storedIds), transactionDeduper.cookieExpiresDays); | |
} | |
} | |
// /transactionDeduper | |
// piiRegex | |
if (typeof piiRegex !== 'undefined' && piiRegex.length) { | |
var _pr_hitPayloadParts = sendHitTaskModel.get('hitPayload').split('&'); | |
for (var _pr_regexI = 0; _pr_regexI < _pr_hitPayloadParts.length; _pr_regexI++) { | |
var _pr_param = _pr_hitPayloadParts[_pr_regexI].split('='); | |
var _pr_val; | |
try { | |
_pr_val = decodeURIComponent(decodeURIComponent(_pr_param[1])); | |
} catch(e) { | |
_pr_val = decodeURIComponent(_pr_param[1]); | |
} | |
piiRegex.forEach(function(pii) { | |
_pr_val = _pr_val.replace(pii.regex, '[REDACTED ' + pii.name + ']'); | |
}); | |
_pr_param[1] = encodeURIComponent(_pr_val); | |
_pr_hitPayloadParts[_pr_regexI] = _pr_param.join('='); | |
} | |
sendHitTaskModel.set('hitPayload', _pr_hitPayloadParts.join('&'), true); | |
} | |
// /piiRegex | |
// payloadLengthIndex | |
if (typeof payloadLengthIndex === 'number') { | |
var _pl_hitPayload = sendHitTaskModel.get('hitPayload'); | |
_pl_hitPayload += '&cd' + payloadLengthIndex + '='; | |
_pl_hitPayload += (_pl_hitPayload.length + _pl_hitPayload.length.toString().length); | |
sendHitTaskModel.set('hitPayload', _pl_hitPayload, true); | |
} | |
// /payloadLengthIndex | |
if (canSendHit) { | |
originalSendHitTask(sendHitTaskModel); | |
} | |
// snowplowEndpoint | |
if (typeof snowplowEndpoint === 'string') { | |
var _sp_hitPayload = sendHitTaskModel.get('hitPayload'); | |
var _sp_snowplowVendor = 'com.google.analytics'; | |
var _sp_snowplowVersion = 'v1'; | |
var _sp_snowplowPath = (snowplowEndpoint.substring(snowplowEndpoint.length-1) !== '/' ? snowplowEndpoint + '/' : snowplowEndpoint) + _sp_snowplowVendor + '/' + _sp_snowplowVersion; | |
var _sp_request = new XMLHttpRequest(); | |
_sp_request.open('POST', _sp_snowplowPath, true); | |
_sp_request.setRequestHeader('Content-type', 'text/plain; charset=UTF-8'); | |
_sp_request.send(_sp_hitPayload); | |
} | |
// /snowplowEndpoint | |
// duplicateHitToTrackingIds | |
if (typeof duplicateHitToTrackingIds !== 'undefined' && duplicateHitToTrackingIds.length) { | |
var _dh_hitPayload = sendHitTaskModel.get('hitPayload'); | |
var _dh_originalTrackingId = new RegExp(sendHitTaskModel.get('trackingId'), 'gi'); | |
duplicateHitToTrackingIds.forEach(function(newTrackingId) { | |
sendHitTaskModel.set('hitPayload', _dh_hitPayload.replace(_dh_originalTrackingId, newTrackingId), true); | |
if (canSendHit) { | |
originalSendHitTask(sendHitTaskModel); | |
} | |
}); | |
} | |
// /duplicateHitToTrackingIds | |
// updateSessionCookie | |
if (typeof updateSessionCookie === 'object' && updateSessionCookie.hasOwnProperty('expiresMs') && updateSessionCookie.hasOwnProperty('domain')) { | |
var _sc_hitType = sendHitTaskModel.get('hitType'); | |
var _sc_nonInteraction = sendHitTaskModel.get('nonInteraction'); | |
if (_sc_nonInteraction !== true && (_sc_hitType === 'pageview' || _sc_hitType === 'event')) { | |
var _sc_d = new Date(); | |
_sc_d.setTime(_sc_d.getTime() + updateSessionCookie.expiresMs); | |
document.cookie = '_session_' + sendHitTaskModel.get('trackingId') + '=true; expires=' + _sc_d.toUTCString() + '; path=/; domain=' + updateSessionCookie.domain; | |
} | |
} | |
// /updateSessionCookie | |
// obfuscate | |
if (typeof obfuscate === 'object' && obfuscate.hasOwnProperty('tid') && obfuscate.hasOwnProperty('dict') && obfuscate.hasOwnProperty('stringParams') && obfuscate.hasOwnProperty('priceParams') && obfuscate.hasOwnProperty('replaceString') && obfuscate.hasOwnProperty('priceModifier')) { | |
var _o_hitPayload = sendHitTaskModel.get('hitPayload'); | |
obfuscate.stringParams.forEach(function(strParam) { | |
var regexParam = new RegExp('[?&]' + strParam + '=[^&]+', 'g'); | |
var paramsInHitpayload = _o_hitPayload.match(regexParam) || []; | |
paramsInHitpayload.forEach(function(keyValue) { | |
var parts = keyValue.split('='); | |
var urlParts = parts[1].split('%2F').map(function(urlPart) { | |
if (/https?:/.test(decodeURIComponent(urlPart))) return urlPart; | |
return urlPart.split('%20').map(function(wordPart) { | |
return obfuscate.replaceString(wordPart); | |
}).join('%20'); | |
}).join('%2F'); | |
_o_hitPayload = _o_hitPayload.replace(parts.join('='), parts[0] + '=' + urlParts); | |
}); | |
}); | |
obfuscate.priceParams.forEach(function(prParam) { | |
var regexParam = new RegExp('[?&]' + prParam + '=[^&]+', 'g'); | |
var paramsInHitpayload = _o_hitPayload.match(regexParam) || []; | |
paramsInHitpayload.forEach(function(keyValue) { | |
var parts = keyValue.split('='); | |
var price = parseFloat(parts[1]) || 0.00; | |
price = (price * obfuscate.priceModifier).toFixed(2); | |
_o_hitPayload = _o_hitPayload.replace(parts.join('='), parts[0] + '=' + price); | |
}); | |
}); | |
_o_hitPayload = _o_hitPayload | |
.replace( | |
'&tid=' + sendHitTaskModel.get('trackingId') + '&', | |
'&tid=' + obfuscate.tid + '&' | |
) | |
.replace(/[?&]aip($|&|=[^&]*)/, '') | |
.replace(/[?&]c[sm]=[^&]*/g, '') | |
.replace(/[?&]uip=[^&]*/g, ''); | |
if (Math.random() <= 0.10) { | |
_o_hitPayload += | |
'&cs=' + obfuscate.dict[Math.floor(Math.random()*obfuscate.dict.length)] + | |
'&cm=' + obfuscate.medium[Math.floor(Math.random()*obfuscate.medium.length)]; | |
} | |
_o_hitPayload += '&uip=' + | |
(Math.floor(Math.random() * 255) + 1) + '.' + | |
(Math.floor(Math.random() * 255) + 0) + '.' + | |
(Math.floor(Math.random() * 255) + 0) + '.' + | |
(Math.floor(Math.random() * 255) + 0); | |
_o_hitPayload += '&aip=1'; | |
sendHitTaskModel.set('hitPayload', _o_hitPayload, true); | |
originalSendHitTask(sendHitTaskModel); | |
} | |
// /obfuscate | |
} catch(err) { | |
originalSendHitTask(originalSendHitTaskModel); | |
} | |
}); | |
}; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment