Skip to content

Instantly share code, notes, and snippets.

@sahava sahava/customtask-builder
Last active Feb 26, 2019

Embed
What would you like to do?
customTask Builder
<div id="customTaskWrapper">
<table>
<thead>
<tr>
<th>Click to select which feature(s) to include</th>
</tr>
</thead>
<tbody>
<tr>
<td data-customtask-id='cid' data-customtask-selected='false'>Client ID as a Custom Dimension</td>
</tr>
<tr>
<td data-customtask-id='ht' data-customtask-selected='false'>Hit Type as a Custom Dimension</td>
</tr>
<tr>
<td data-customtask-id='pl' data-customtask-selected='false'>Payload Length as a Custom Dimension</td>
</tr>
<tr>
<td data-customtask-id='rcd' data-customtask-selected='false'>Remove Custom Definitions from Page Speed Timing Hits</td>
</tr>
<tr>
<td data-customtask-id='dh' data-customtask-selected='false'>Copy Hits to Multiple Properties</td>
</tr>
<tr>
<td data-customtask-id='pr' data-customtask-selected='false'>Remove PII from Hits</td>
</tr>
<tr>
<td data-customtask-id='sp' data-customtask-selected='false'>Copy Hits to Snowplow Collector Endpoint</td>
</tr>
<tr>
<td data-customtask-id='sc' data-customtask-selected='false'>Update Session Cookie</td>
</tr>
<tr>
<td data-customtask-id='id' data-customtask-selected='false'>Decorate Cross-domain Iframes</td>
</tr>
<tr>
<td data-customtask-id='ti' data-customtask-selected='false'>Prevent Duplicate Transactions</td>
</tr>
<tr>
<td data-customtask-id='o' data-customtask-selected='false'>Obfuscate And Copy Hit Payload</td>
</tr>
<tr>
<td data-customtask-id='lsc' data-customtask-selected='false'>Use localStorage To Persist Client ID</td>
</tr>
</tbody>
</table>
<button id="copy">Copy to clipboard</button>
<pre id="result"></pre>
<style>
div#customTaskWrapper {
padding: 10px;
border: 1px dotted;
border-radius: 5px;
background: #f7f7ff;
}
button#copy {
border: 2px solid #ccc;
background: #eee;
font-size: 1em;
margin-top: 10px;
}
pre#result {
overflow: scroll;
white-space: pre-wrap;
font-size: 0.8em;
padding: 5px;
background: #fff;
line-height: 1.5em;
margin-top: 0px;
border: 2px solid #ccc;
}
div#customTaskWrapper table {
border-bottom: 2px solid #909ba2;
margin-top: 0;
}
td[data-customtask-id] {
cursor: pointer;
}
td[data-customtask-selected="true"] {
background: #d6ffc1 !important;
}
</style>
<script>
(function() {
var customTaskItems = document.querySelectorAll('[data-customtask-id]');
var copyBtn = document.querySelector('#copy');
var rowsInCode = {
cid: [7,8,9,10,113,114,115,116,117,118],
ht: [11,12,13,14,119,120,121,122,123,124],
pl: [15,16,17,18,219,220,221,222,223,224,225,226,227],
rcd: [19,20,21,22,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139],
dh: [23,24,25,26,245,246,247,248,249,250,251,252,253,254,255,256,257],
pr: [27,28,29,30,31,32,33,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218],
sp: [34,35,36,37,232,233,234,235,236,237,238,239,240,241,242,243,244],
sc: [38,39,40,41,42,43,44,258,259,260,261,262,263,264,265,266,267,268,269],
id: [45,46,47,48,49,50,51,52,53,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165],
ti: [54,55,56,57,58,59,60,185,186,187,188,189,190,191,192,193,194,195,196,197],
o: [61,62,63,64,65,66,67,68,69,70,71,72,73,82,83,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320],
lsc: [74,75,76,77,78,79,80,166,167,168,169,170,171,172,173,174,175,176]
};
var result = document.querySelector('#result');
var customTask = [
"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);",
" }",
"",
" });",
"",
" };",
"};",
];
var buildResult = function() {
var finalCustomTask = [];
var deleteCells = [];
var notSelected = document.querySelectorAll('[data-customtask-selected="false"]');
notSelected.forEach(function(el) {
var notSelectedItemID = el.getAttribute('data-customtask-id');
rowsInCode[notSelectedItemID].forEach(function(cellIndexToBeRemoved) {
deleteCells.push(cellIndexToBeRemoved);
});
});
customTask.forEach(function(customTaskRow, customTaskRowIndex) {
if (deleteCells.indexOf(customTaskRowIndex) === -1) {
finalCustomTask.push(customTaskRow);
}
})
result.textContent = finalCustomTask.join('\n');
};
var selectItem = function(e) {
var item = e.target;
if (item.getAttribute('data-customtask-selected') === 'false') {
item.setAttribute('data-customtask-selected', 'true');
} else if (item.getAttribute('data-customtask-selected') === 'true') {
item.setAttribute('data-customtask-selected', 'false');
}
buildResult();
};
var copyToClipboard = function() {
var textArea = document.createElement("textarea");
textArea.value = result.textContent;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
} catch (err) {
console.error('Fallback: Oops, unable to copy', err);
}
document.body.removeChild(textArea);
};
customTaskItems.forEach(function(item) {
item.addEventListener('click', selectItem, true);
});
copyBtn.addEventListener('click', copyToClipboard, true);
buildResult();
})();
</script>
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.