Skip to content

Instantly share code, notes, and snippets.

@sahava
Last active August 6, 2020 20:58
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sahava/d5f068bf10f0f8e7c638bb40ba8591ec to your computer and use it in GitHub Desktop.
Save sahava/d5f068bf10f0f8e7c638bb40ba8591ec to your computer and use it in GitHub Desktop.
One customTask to rule them all.
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