Commit 6f54ea12 by James Ooi

Add support for custom dimensions and metrics

parent 00b0cb82
# Foresight # Foresight
### Foresight is an analytics library that allows for declarative event tracking. ### Foresight is an analytics library that allows for declarative event tracking.
* Lightweight (<3kb minified and gzipped) * Lightweight (<4kb minified and gzipped)
* Declarative event tracking * Declarative event tracking
* Performant * Performant
...@@ -92,6 +92,23 @@ The above markup would send the following event data when the `div` enters the v ...@@ -92,6 +92,23 @@ The above markup would send the following event data when the `div` enters the v
--- ---
### Tracking custom metrics and custom dimensions
Custom metrics and dimensions can be tracked by attaching additional HTML attributes
under the `data-track:metrics:<metric_name>` or `data-track:dimensions:<metric_name>` namespace. The value of the attribute will be the value of the metric/dimension. You can have multiple custom metrics/dimensions per element.
The follow example will send a value of `1` to the custom metric in the `metric1` index.
```html
<button data-track="clicks;register-btn" data-track:metrics:metric1="1">
Register
</button
```
Refer to Google Analytics documentation on what `metric_name` should be.
* **[analytics.js](https://developers.google.com/analytics/devguides/collection/analyticsjs/custom-dims-mets)**
* **[gtag.js](https://developers.google.com/analytics/devguides/collection/gtagjs/custom-dims-mets)**`metric_name` should be the name provided in `custom_map`
## Configuration ## Configuration
```js ```js
......
...@@ -122,7 +122,6 @@ __webpack_require__(1); ...@@ -122,7 +122,6 @@ __webpack_require__(1);
var Utils = __webpack_require__(2); var Utils = __webpack_require__(2);
/** /**
* Foresight is an analytics library that allows for declarative event tracking. * Foresight is an analytics library that allows for declarative event tracking.
* @class
*/ */
var Foresight = /** @class */ (function () { var Foresight = /** @class */ (function () {
/** /**
...@@ -220,11 +219,7 @@ var Foresight = /** @class */ (function () { ...@@ -220,11 +219,7 @@ var Foresight = /** @class */ (function () {
return this.options.sendEventFn(data); return this.options.sendEventFn(data);
} }
if (typeof window['gtag'] === 'function') { if (typeof window['gtag'] === 'function') {
return window['gtag']('event', data.action, { return window['gtag']('event', data.action, __assign({ 'event_label': data.label, 'event_category': data.category, 'non_interaction': !data.interaction }, data.metrics, data.dimensions));
'event_label': data.label,
'event_category': data.category,
'non_interaction': !data.interaction,
});
} }
if (typeof window['ga'] === 'function') { if (typeof window['ga'] === 'function') {
return window['ga']('send', { return window['ga']('send', {
...@@ -233,7 +228,7 @@ var Foresight = /** @class */ (function () { ...@@ -233,7 +228,7 @@ var Foresight = /** @class */ (function () {
eventAction: data.action, eventAction: data.action,
eventLabel: data.label, eventLabel: data.label,
nonInteraction: !data.interaction, nonInteraction: !data.interaction,
}); }, __assign({}, data.metrics, data.dimensions));
} }
throw 'No available analytics backend found. Has Google Analytics been loaded yet?'; throw 'No available analytics backend found. Has Google Analytics been loaded yet?';
}; };
...@@ -290,6 +285,31 @@ var Foresight = /** @class */ (function () { ...@@ -290,6 +285,31 @@ var Foresight = /** @class */ (function () {
Foresight.prototype._onTrackedClick = function (element, event) { Foresight.prototype._onTrackedClick = function (element, event) {
var s = element.getAttribute('data-track'); var s = element.getAttribute('data-track');
var data = this._parseEventString(s); var data = this._parseEventString(s);
// Get custom metrics
data.metrics = Utils
.toArray(element.attributes)
.filter(function (attr) { return attr.name.startsWith('data-track:metrics'); })
.reduce(function (acc, attr) {
var key = attr.name.replace(/data-track:metrics:/, '');
var value = attr.value === "" ? 1 : attr.value;
acc[key] = value;
return acc;
}, {});
// Get custom dimensions
data.dimensions = Utils
.toArray(element.attributes)
.filter(function (attr) { return attr.name.startsWith('data-track:dimensions'); })
.reduce(function (acc, attr) {
if (attr.value === "") {
console.warn("No dimension value provided for " + attr.name);
return acc;
}
var key = attr.name.replace(/data-track:dimensions:/, '');
var value = attr.value;
acc[key] = value;
return acc;
}, {});
// Get interaction flag
data.interaction = this.options.clicksAreInteractions; data.interaction = this.options.clicksAreInteractions;
if (element.getAttribute('data-track:non-interaction') !== null) { if (element.getAttribute('data-track:non-interaction') !== null) {
data.interaction = false; data.interaction = false;
...@@ -303,6 +323,31 @@ var Foresight = /** @class */ (function () { ...@@ -303,6 +323,31 @@ var Foresight = /** @class */ (function () {
Foresight.prototype._onTrackedView = function (element, observer) { Foresight.prototype._onTrackedView = function (element, observer) {
var s = element.getAttribute('data-track-view'); var s = element.getAttribute('data-track-view');
var data = this._parseEventString(s); var data = this._parseEventString(s);
// Get custom metrics
data.metrics = Utils
.toArray(element.attributes)
.filter(function (attr) { return attr.name.startsWith('data-track-view:metrics'); })
.reduce(function (acc, attr) {
var key = attr.name.replace(/data-track-view:metrics:/, '');
var value = attr.value === "" ? 1 : attr.value;
acc[key] = value;
return acc;
}, {});
// Get custom dimensions
data.dimensions = Utils
.toArray(element.attributes)
.filter(function (attr) { return attr.name.startsWith('data-track-view:dimensions'); })
.reduce(function (acc, attr) {
if (attr.value === "") {
console.warn("No dimension value provided for " + attr.name);
return acc;
}
var key = attr.name.replace(/data-track-view:dimensions:/, '');
var value = attr.value;
acc[key] = value;
return acc;
}, {});
// Get interaction flag
data.interaction = this.options.viewsAreInteractions; data.interaction = this.options.viewsAreInteractions;
if (element.getAttribute('data-track-view:interaction') !== null) { if (element.getAttribute('data-track-view:interaction') !== null) {
data.interaction = true; data.interaction = true;
......
...@@ -5,4 +5,4 @@ ...@@ -5,4 +5,4 @@
* @author James Ooi <james.ooi@forefront.com.my> * @author James Ooi <james.ooi@forefront.com.my>
* @license MIT * @license MIT
* @copyright 2018 (c) FOREFRONT International Sdn Bhd * @copyright 2018 (c) FOREFRONT International Sdn Bhd
*/var r=this&&this.__assign||function(){return(r=Object.assign||function(t){for(var e,n=1,r=arguments.length;n<r;n++)for(var i in e=arguments[n])Object.prototype.hasOwnProperty.call(e,i)&&(t[i]=e[i]);return t}).apply(this,arguments)};n(1);var i=n(2),o=function(){function t(e){void 0===e&&(e={});var n=this;(this._untrackFns=new Map,this._observer=null,this.options=r({},t.defaultOptions,e),this._observer=new IntersectionObserver(function(t,e){t.forEach(function(t){t.isIntersecting&&(n._onTrackedView(t.target,e),e.unobserve(t.target))})},this.options.observerOptions),this.options.defer)||("complete"===document.readyState||"interactive"===document.readyState?this.start():window.addEventListener("DOMContentLoaded",function(){return n.start()}))}return t.prototype.start=function(t){var e=this;void 0===t&&(t=document.body),i.toArray(t.querySelectorAll("[data-track], [data-track-view]")).map(function(t){return e.track(t)})},t.prototype.track=function(t){this._untrackFns.has(t)||this._untrackFns.set(t,{click:null,view:null});var e=this._untrackFns.get(t);null!==t.getAttribute("data-track")&&null==e.click&&(e.click=this._trackClicks(t)),null!==t.getAttribute("data-track-view")&&null==e.view&&(e.view=this._trackViews(t)),this._untrackFns.set(t,e)},t.prototype.untrack=function(t){var e=this._untrackFns.get(t);void 0!==e&&(null!==e.click&&e.click(),null!==e.view&&e.view(),this._untrackFns.delete(t))},t.prototype.send=function(t){if(null!==this.options.sendEventFn)return this.options.sendEventFn(t);if("function"==typeof window.gtag)return window.gtag("event",t.action,{event_label:t.label,event_category:t.category,non_interaction:!t.interaction});if("function"==typeof window.ga)return window.ga("send",{hitType:"event",eventCategory:t.category,eventAction:t.action,eventLabel:t.label,nonInteraction:!t.interaction});throw"No available analytics backend found. Has Google Analytics been loaded yet?"},t.prototype._parseEventString=function(t){var e=t.split(";"),n=e[0],r=e[1],i=e[2];return 1===e.length&&(r=n,n=void 0),{category:n,action:r,label:i,interaction:!0}},t.prototype._trackClicks=function(t){var e=this,n=function(n){return e._onTrackedClick(t,n)};return t.addEventListener("click",n),t.addEventListener("auxclick",n),function(){t.removeEventListener("click",n),t.removeEventListener("auxclick",n)}},t.prototype._trackViews=function(t){var e=this;return this._observer.observe(t),function(){e._observer.unobserve(t)}},t.prototype._onTrackedClick=function(t,e){var n=t.getAttribute("data-track"),r=this._parseEventString(n);r.interaction=this.options.clicksAreInteractions,null!==t.getAttribute("data-track:non-interaction")&&(r.interaction=!1),this.send(r)},t.prototype._onTrackedView=function(t,e){var n=t.getAttribute("data-track-view"),r=this._parseEventString(n);r.interaction=this.options.viewsAreInteractions,null!==t.getAttribute("data-track-view:interaction")&&(r.interaction=!0),this.send(r)},t.defaultOptions={defer:!1,observerOptions:{},clicksAreInteractions:!0,viewsAreInteractions:!1,sendEventFn:null},t}();t.exports=o},function(t,e){!function(t,e){"use strict";if("IntersectionObserver"in t&&"IntersectionObserverEntry"in t&&"intersectionRatio"in t.IntersectionObserverEntry.prototype)"isIntersecting"in t.IntersectionObserverEntry.prototype||Object.defineProperty(t.IntersectionObserverEntry.prototype,"isIntersecting",{get:function(){return this.intersectionRatio>0}});else{var n=[];i.prototype.THROTTLE_TIMEOUT=100,i.prototype.POLL_INTERVAL=null,i.prototype.USE_MUTATION_OBSERVER=!0,i.prototype.observe=function(t){if(!this._observationTargets.some(function(e){return e.element==t})){if(!t||1!=t.nodeType)throw new Error("target must be an Element");this._registerInstance(),this._observationTargets.push({element:t,entry:null}),this._monitorIntersections(),this._checkForIntersections()}},i.prototype.unobserve=function(t){this._observationTargets=this._observationTargets.filter(function(e){return e.element!=t}),this._observationTargets.length||(this._unmonitorIntersections(),this._unregisterInstance())},i.prototype.disconnect=function(){this._observationTargets=[],this._unmonitorIntersections(),this._unregisterInstance()},i.prototype.takeRecords=function(){var t=this._queuedEntries.slice();return this._queuedEntries=[],t},i.prototype._initThresholds=function(t){var e=t||[0];return Array.isArray(e)||(e=[e]),e.sort().filter(function(t,e,n){if("number"!=typeof t||isNaN(t)||t<0||t>1)throw new Error("threshold must be a number between 0 and 1 inclusively");return t!==n[e-1]})},i.prototype._parseRootMargin=function(t){var e=(t||"0px").split(/\s+/).map(function(t){var e=/^(-?\d*\.?\d+)(px|%)$/.exec(t);if(!e)throw new Error("rootMargin must be specified in pixels or percent");return{value:parseFloat(e[1]),unit:e[2]}});return e[1]=e[1]||e[0],e[2]=e[2]||e[0],e[3]=e[3]||e[1],e},i.prototype._monitorIntersections=function(){this._monitoringIntersections||(this._monitoringIntersections=!0,this.POLL_INTERVAL?this._monitoringInterval=setInterval(this._checkForIntersections,this.POLL_INTERVAL):(o(t,"resize",this._checkForIntersections,!0),o(e,"scroll",this._checkForIntersections,!0),this.USE_MUTATION_OBSERVER&&"MutationObserver"in t&&(this._domObserver=new MutationObserver(this._checkForIntersections),this._domObserver.observe(e,{attributes:!0,childList:!0,characterData:!0,subtree:!0}))))},i.prototype._unmonitorIntersections=function(){this._monitoringIntersections&&(this._monitoringIntersections=!1,clearInterval(this._monitoringInterval),this._monitoringInterval=null,s(t,"resize",this._checkForIntersections,!0),s(e,"scroll",this._checkForIntersections,!0),this._domObserver&&(this._domObserver.disconnect(),this._domObserver=null))},i.prototype._checkForIntersections=function(){var e=this._rootIsInDom(),n=e?this._getRootRect():{top:0,bottom:0,left:0,right:0,width:0,height:0};this._observationTargets.forEach(function(i){var o=i.element,s=a(o),c=this._rootContainsTarget(o),u=i.entry,h=e&&c&&this._computeTargetAndRootIntersection(o,n),l=i.entry=new r({time:t.performance&&performance.now&&performance.now(),target:o,boundingClientRect:s,rootBounds:n,intersectionRect:h});u?e&&c?this._hasCrossedThreshold(u,l)&&this._queuedEntries.push(l):u&&u.isIntersecting&&this._queuedEntries.push(l):this._queuedEntries.push(l)},this),this._queuedEntries.length&&this._callback(this.takeRecords(),this)},i.prototype._computeTargetAndRootIntersection=function(n,r){if("none"!=t.getComputedStyle(n).display){for(var i=a(n),o=h(n),s=!1;!s;){var u=null,l=1==o.nodeType?t.getComputedStyle(o):{};if("none"==l.display)return;if(o==this.root||o==e?(s=!0,u=r):o!=e.body&&o!=e.documentElement&&"visible"!=l.overflow&&(u=a(o)),u&&!(i=c(u,i)))break;o=h(o)}return i}},i.prototype._getRootRect=function(){var t;if(this.root)t=a(this.root);else{var n=e.documentElement,r=e.body;t={top:0,left:0,right:n.clientWidth||r.clientWidth,width:n.clientWidth||r.clientWidth,bottom:n.clientHeight||r.clientHeight,height:n.clientHeight||r.clientHeight}}return this._expandRectByRootMargin(t)},i.prototype._expandRectByRootMargin=function(t){var e=this._rootMarginValues.map(function(e,n){return"px"==e.unit?e.value:e.value*(n%2?t.width:t.height)/100}),n={top:t.top-e[0],right:t.right+e[1],bottom:t.bottom+e[2],left:t.left-e[3]};return n.width=n.right-n.left,n.height=n.bottom-n.top,n},i.prototype._hasCrossedThreshold=function(t,e){var n=t&&t.isIntersecting?t.intersectionRatio||0:-1,r=e.isIntersecting?e.intersectionRatio||0:-1;if(n!==r)for(var i=0;i<this.thresholds.length;i++){var o=this.thresholds[i];if(o==n||o==r||o<n!=o<r)return!0}},i.prototype._rootIsInDom=function(){return!this.root||u(e,this.root)},i.prototype._rootContainsTarget=function(t){return u(this.root||e,t)},i.prototype._registerInstance=function(){n.indexOf(this)<0&&n.push(this)},i.prototype._unregisterInstance=function(){var t=n.indexOf(this);-1!=t&&n.splice(t,1)},t.IntersectionObserver=i,t.IntersectionObserverEntry=r}function r(t){this.time=t.time,this.target=t.target,this.rootBounds=t.rootBounds,this.boundingClientRect=t.boundingClientRect,this.intersectionRect=t.intersectionRect||{top:0,bottom:0,left:0,right:0,width:0,height:0},this.isIntersecting=!!t.intersectionRect;var e=this.boundingClientRect,n=e.width*e.height,r=this.intersectionRect,i=r.width*r.height;this.intersectionRatio=n?i/n:this.isIntersecting?1:0}function i(t,e){var n=e||{};if("function"!=typeof t)throw new Error("callback must be a function");if(n.root&&1!=n.root.nodeType)throw new Error("root must be an Element");this._checkForIntersections=function(t,e){var n=null;return function(){n||(n=setTimeout(function(){t(),n=null},e))}}(this._checkForIntersections.bind(this),this.THROTTLE_TIMEOUT),this._callback=t,this._observationTargets=[],this._queuedEntries=[],this._rootMarginValues=this._parseRootMargin(n.rootMargin),this.thresholds=this._initThresholds(n.threshold),this.root=n.root||null,this.rootMargin=this._rootMarginValues.map(function(t){return t.value+t.unit}).join(" ")}function o(t,e,n,r){"function"==typeof t.addEventListener?t.addEventListener(e,n,r||!1):"function"==typeof t.attachEvent&&t.attachEvent("on"+e,n)}function s(t,e,n,r){"function"==typeof t.removeEventListener?t.removeEventListener(e,n,r||!1):"function"==typeof t.detatchEvent&&t.detatchEvent("on"+e,n)}function c(t,e){var n=Math.max(t.top,e.top),r=Math.min(t.bottom,e.bottom),i=Math.max(t.left,e.left),o=Math.min(t.right,e.right),s=o-i,c=r-n;return s>=0&&c>=0&&{top:n,bottom:r,left:i,right:o,width:s,height:c}}function a(t){var e;try{e=t.getBoundingClientRect()}catch(t){}return e?(e.width&&e.height||(e={top:e.top,right:e.right,bottom:e.bottom,left:e.left,width:e.right-e.left,height:e.bottom-e.top}),e):{top:0,bottom:0,left:0,right:0,width:0,height:0}}function u(t,e){for(var n=e;n;){if(n==t)return!0;n=h(n)}return!1}function h(t){var e=t.parentNode;return e&&11==e.nodeType&&e.host?e.host:e}}(window,document)},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(3);e.toArray=r.default},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.default=function(t){return[].slice.call(t)}}])}); */var r=this&&this.__assign||function(){return(r=Object.assign||function(t){for(var e,n=1,r=arguments.length;n<r;n++)for(var i in e=arguments[n])Object.prototype.hasOwnProperty.call(e,i)&&(t[i]=e[i]);return t}).apply(this,arguments)};n(1);var i=n(2),o=function(){function t(e){void 0===e&&(e={});var n=this;(this._untrackFns=new Map,this._observer=null,this.options=r({},t.defaultOptions,e),this._observer=new IntersectionObserver(function(t,e){t.forEach(function(t){t.isIntersecting&&(n._onTrackedView(t.target,e),e.unobserve(t.target))})},this.options.observerOptions),this.options.defer)||("complete"===document.readyState||"interactive"===document.readyState?this.start():window.addEventListener("DOMContentLoaded",function(){return n.start()}))}return t.prototype.start=function(t){var e=this;void 0===t&&(t=document.body),i.toArray(t.querySelectorAll("[data-track], [data-track-view]")).map(function(t){return e.track(t)})},t.prototype.track=function(t){this._untrackFns.has(t)||this._untrackFns.set(t,{click:null,view:null});var e=this._untrackFns.get(t);null!==t.getAttribute("data-track")&&null==e.click&&(e.click=this._trackClicks(t)),null!==t.getAttribute("data-track-view")&&null==e.view&&(e.view=this._trackViews(t)),this._untrackFns.set(t,e)},t.prototype.untrack=function(t){var e=this._untrackFns.get(t);void 0!==e&&(null!==e.click&&e.click(),null!==e.view&&e.view(),this._untrackFns.delete(t))},t.prototype.send=function(t){if(null!==this.options.sendEventFn)return this.options.sendEventFn(t);if("function"==typeof window.gtag)return window.gtag("event",t.action,r({event_label:t.label,event_category:t.category,non_interaction:!t.interaction},t.metrics,t.dimensions));if("function"==typeof window.ga)return window.ga("send",{hitType:"event",eventCategory:t.category,eventAction:t.action,eventLabel:t.label,nonInteraction:!t.interaction},r({},t.metrics,t.dimensions));throw"No available analytics backend found. Has Google Analytics been loaded yet?"},t.prototype._parseEventString=function(t){var e=t.split(";"),n=e[0],r=e[1],i=e[2];return 1===e.length&&(r=n,n=void 0),{category:n,action:r,label:i,interaction:!0}},t.prototype._trackClicks=function(t){var e=this,n=function(n){return e._onTrackedClick(t,n)};return t.addEventListener("click",n),t.addEventListener("auxclick",n),function(){t.removeEventListener("click",n),t.removeEventListener("auxclick",n)}},t.prototype._trackViews=function(t){var e=this;return this._observer.observe(t),function(){e._observer.unobserve(t)}},t.prototype._onTrackedClick=function(t,e){var n=t.getAttribute("data-track"),r=this._parseEventString(n);r.metrics=i.toArray(t.attributes).filter(function(t){return t.name.startsWith("data-track:metrics")}).reduce(function(t,e){var n=e.name.replace(/data-track:metrics:/,""),r=""===e.value?1:e.value;return t[n]=r,t},{}),r.dimensions=i.toArray(t.attributes).filter(function(t){return t.name.startsWith("data-track:dimensions")}).reduce(function(t,e){if(""===e.value)return console.warn("No dimension value provided for "+e.name),t;var n=e.name.replace(/data-track:dimensions:/,""),r=e.value;return t[n]=r,t},{}),r.interaction=this.options.clicksAreInteractions,null!==t.getAttribute("data-track:non-interaction")&&(r.interaction=!1),this.send(r)},t.prototype._onTrackedView=function(t,e){var n=t.getAttribute("data-track-view"),r=this._parseEventString(n);r.metrics=i.toArray(t.attributes).filter(function(t){return t.name.startsWith("data-track-view:metrics")}).reduce(function(t,e){var n=e.name.replace(/data-track-view:metrics:/,""),r=""===e.value?1:e.value;return t[n]=r,t},{}),r.dimensions=i.toArray(t.attributes).filter(function(t){return t.name.startsWith("data-track-view:dimensions")}).reduce(function(t,e){if(""===e.value)return console.warn("No dimension value provided for "+e.name),t;var n=e.name.replace(/data-track-view:dimensions:/,""),r=e.value;return t[n]=r,t},{}),r.interaction=this.options.viewsAreInteractions,null!==t.getAttribute("data-track-view:interaction")&&(r.interaction=!0),this.send(r)},t.defaultOptions={defer:!1,observerOptions:{},clicksAreInteractions:!0,viewsAreInteractions:!1,sendEventFn:null},t}();t.exports=o},function(t,e){!function(t,e){"use strict";if("IntersectionObserver"in t&&"IntersectionObserverEntry"in t&&"intersectionRatio"in t.IntersectionObserverEntry.prototype)"isIntersecting"in t.IntersectionObserverEntry.prototype||Object.defineProperty(t.IntersectionObserverEntry.prototype,"isIntersecting",{get:function(){return this.intersectionRatio>0}});else{var n=[];i.prototype.THROTTLE_TIMEOUT=100,i.prototype.POLL_INTERVAL=null,i.prototype.USE_MUTATION_OBSERVER=!0,i.prototype.observe=function(t){if(!this._observationTargets.some(function(e){return e.element==t})){if(!t||1!=t.nodeType)throw new Error("target must be an Element");this._registerInstance(),this._observationTargets.push({element:t,entry:null}),this._monitorIntersections(),this._checkForIntersections()}},i.prototype.unobserve=function(t){this._observationTargets=this._observationTargets.filter(function(e){return e.element!=t}),this._observationTargets.length||(this._unmonitorIntersections(),this._unregisterInstance())},i.prototype.disconnect=function(){this._observationTargets=[],this._unmonitorIntersections(),this._unregisterInstance()},i.prototype.takeRecords=function(){var t=this._queuedEntries.slice();return this._queuedEntries=[],t},i.prototype._initThresholds=function(t){var e=t||[0];return Array.isArray(e)||(e=[e]),e.sort().filter(function(t,e,n){if("number"!=typeof t||isNaN(t)||t<0||t>1)throw new Error("threshold must be a number between 0 and 1 inclusively");return t!==n[e-1]})},i.prototype._parseRootMargin=function(t){var e=(t||"0px").split(/\s+/).map(function(t){var e=/^(-?\d*\.?\d+)(px|%)$/.exec(t);if(!e)throw new Error("rootMargin must be specified in pixels or percent");return{value:parseFloat(e[1]),unit:e[2]}});return e[1]=e[1]||e[0],e[2]=e[2]||e[0],e[3]=e[3]||e[1],e},i.prototype._monitorIntersections=function(){this._monitoringIntersections||(this._monitoringIntersections=!0,this.POLL_INTERVAL?this._monitoringInterval=setInterval(this._checkForIntersections,this.POLL_INTERVAL):(o(t,"resize",this._checkForIntersections,!0),o(e,"scroll",this._checkForIntersections,!0),this.USE_MUTATION_OBSERVER&&"MutationObserver"in t&&(this._domObserver=new MutationObserver(this._checkForIntersections),this._domObserver.observe(e,{attributes:!0,childList:!0,characterData:!0,subtree:!0}))))},i.prototype._unmonitorIntersections=function(){this._monitoringIntersections&&(this._monitoringIntersections=!1,clearInterval(this._monitoringInterval),this._monitoringInterval=null,s(t,"resize",this._checkForIntersections,!0),s(e,"scroll",this._checkForIntersections,!0),this._domObserver&&(this._domObserver.disconnect(),this._domObserver=null))},i.prototype._checkForIntersections=function(){var e=this._rootIsInDom(),n=e?this._getRootRect():{top:0,bottom:0,left:0,right:0,width:0,height:0};this._observationTargets.forEach(function(i){var o=i.element,s=a(o),c=this._rootContainsTarget(o),u=i.entry,h=e&&c&&this._computeTargetAndRootIntersection(o,n),l=i.entry=new r({time:t.performance&&performance.now&&performance.now(),target:o,boundingClientRect:s,rootBounds:n,intersectionRect:h});u?e&&c?this._hasCrossedThreshold(u,l)&&this._queuedEntries.push(l):u&&u.isIntersecting&&this._queuedEntries.push(l):this._queuedEntries.push(l)},this),this._queuedEntries.length&&this._callback(this.takeRecords(),this)},i.prototype._computeTargetAndRootIntersection=function(n,r){if("none"!=t.getComputedStyle(n).display){for(var i=a(n),o=h(n),s=!1;!s;){var u=null,l=1==o.nodeType?t.getComputedStyle(o):{};if("none"==l.display)return;if(o==this.root||o==e?(s=!0,u=r):o!=e.body&&o!=e.documentElement&&"visible"!=l.overflow&&(u=a(o)),u&&!(i=c(u,i)))break;o=h(o)}return i}},i.prototype._getRootRect=function(){var t;if(this.root)t=a(this.root);else{var n=e.documentElement,r=e.body;t={top:0,left:0,right:n.clientWidth||r.clientWidth,width:n.clientWidth||r.clientWidth,bottom:n.clientHeight||r.clientHeight,height:n.clientHeight||r.clientHeight}}return this._expandRectByRootMargin(t)},i.prototype._expandRectByRootMargin=function(t){var e=this._rootMarginValues.map(function(e,n){return"px"==e.unit?e.value:e.value*(n%2?t.width:t.height)/100}),n={top:t.top-e[0],right:t.right+e[1],bottom:t.bottom+e[2],left:t.left-e[3]};return n.width=n.right-n.left,n.height=n.bottom-n.top,n},i.prototype._hasCrossedThreshold=function(t,e){var n=t&&t.isIntersecting?t.intersectionRatio||0:-1,r=e.isIntersecting?e.intersectionRatio||0:-1;if(n!==r)for(var i=0;i<this.thresholds.length;i++){var o=this.thresholds[i];if(o==n||o==r||o<n!=o<r)return!0}},i.prototype._rootIsInDom=function(){return!this.root||u(e,this.root)},i.prototype._rootContainsTarget=function(t){return u(this.root||e,t)},i.prototype._registerInstance=function(){n.indexOf(this)<0&&n.push(this)},i.prototype._unregisterInstance=function(){var t=n.indexOf(this);-1!=t&&n.splice(t,1)},t.IntersectionObserver=i,t.IntersectionObserverEntry=r}function r(t){this.time=t.time,this.target=t.target,this.rootBounds=t.rootBounds,this.boundingClientRect=t.boundingClientRect,this.intersectionRect=t.intersectionRect||{top:0,bottom:0,left:0,right:0,width:0,height:0},this.isIntersecting=!!t.intersectionRect;var e=this.boundingClientRect,n=e.width*e.height,r=this.intersectionRect,i=r.width*r.height;this.intersectionRatio=n?i/n:this.isIntersecting?1:0}function i(t,e){var n=e||{};if("function"!=typeof t)throw new Error("callback must be a function");if(n.root&&1!=n.root.nodeType)throw new Error("root must be an Element");this._checkForIntersections=function(t,e){var n=null;return function(){n||(n=setTimeout(function(){t(),n=null},e))}}(this._checkForIntersections.bind(this),this.THROTTLE_TIMEOUT),this._callback=t,this._observationTargets=[],this._queuedEntries=[],this._rootMarginValues=this._parseRootMargin(n.rootMargin),this.thresholds=this._initThresholds(n.threshold),this.root=n.root||null,this.rootMargin=this._rootMarginValues.map(function(t){return t.value+t.unit}).join(" ")}function o(t,e,n,r){"function"==typeof t.addEventListener?t.addEventListener(e,n,r||!1):"function"==typeof t.attachEvent&&t.attachEvent("on"+e,n)}function s(t,e,n,r){"function"==typeof t.removeEventListener?t.removeEventListener(e,n,r||!1):"function"==typeof t.detatchEvent&&t.detatchEvent("on"+e,n)}function c(t,e){var n=Math.max(t.top,e.top),r=Math.min(t.bottom,e.bottom),i=Math.max(t.left,e.left),o=Math.min(t.right,e.right),s=o-i,c=r-n;return s>=0&&c>=0&&{top:n,bottom:r,left:i,right:o,width:s,height:c}}function a(t){var e;try{e=t.getBoundingClientRect()}catch(t){}return e?(e.width&&e.height||(e={top:e.top,right:e.right,bottom:e.bottom,left:e.left,width:e.right-e.left,height:e.bottom-e.top}),e):{top:0,bottom:0,left:0,right:0,width:0,height:0}}function u(t,e){for(var n=e;n;){if(n==t)return!0;n=h(n)}return!1}function h(t){var e=t.parentNode;return e&&11==e.nodeType&&e.host?e.host:e}}(window,document)},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(3);e.toArray=r.default},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.default=function(t){return[].slice.call(t)}}])});
\ No newline at end of file \ No newline at end of file
...@@ -12,7 +12,11 @@ ...@@ -12,7 +12,11 @@
window.dataLayer = window.dataLayer || []; window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);} function gtag(){dataLayer.push(arguments);}
gtag('js', new Date()); gtag('js', new Date());
gtag('config', 'UA-93571739-3'); gtag('config', 'UA-93571739-3', {
'custom_map': {
'metric1': 'campaign-detached-fac-aug-2018'
}
});
</script> </script>
</head> </head>
...@@ -32,7 +36,12 @@ ...@@ -32,7 +36,12 @@
data-track-view="views;landing : view : facebook" data-track-view="views;landing : view : facebook"
data-track="landing : section : facebook" data-track="landing : section : facebook"
> >
<a href="https://facebook.com" class="btn btn-secondary" data-track="clicks;landing" data-track:non-interaction> <a
href="https://facebook.com"
class="btn btn-secondary"
data-track="clicks;landing"
data-track:non-interaction
data-track:metrics:campaign-detached-fac-aug-2018>
Facebook Facebook
</a> </a>
</div> </div>
......
...@@ -9,8 +9,6 @@ ...@@ -9,8 +9,6 @@
import 'intersection-observer'; import 'intersection-observer';
import * as Utils from './utils'; import * as Utils from './utils';
declare var gtag: (action: string, ...args: any[]) => any;
/** /**
* Available options for configuring Foresight. * Available options for configuring Foresight.
...@@ -32,20 +30,22 @@ interface ForesightConfig { ...@@ -32,20 +30,22 @@ interface ForesightConfig {
sendEventFn?: (data: EventData) => any sendEventFn?: (data: EventData) => any
} }
/** /**
* Represents a particular event's data. * Represents an event data.
*/ */
interface EventData { interface EventData {
category: string category: string
action: string action: string
label: string label: string
interaction: boolean interaction: boolean
metrics?: { [key: string]: any }
dimensions?: { [key: string]: any }
} }
/** /**
* Foresight is an analytics library that allows for declarative event tracking. * Foresight is an analytics library that allows for declarative event tracking.
* @class
*/ */
class Foresight { class Foresight {
...@@ -178,6 +178,8 @@ class Foresight { ...@@ -178,6 +178,8 @@ class Foresight {
'event_label': data.label, 'event_label': data.label,
'event_category': data.category, 'event_category': data.category,
'non_interaction': !data.interaction, 'non_interaction': !data.interaction,
...data.metrics,
...data.dimensions
}); });
} }
...@@ -188,7 +190,7 @@ class Foresight { ...@@ -188,7 +190,7 @@ class Foresight {
eventAction: data.action, eventAction: data.action,
eventLabel: data.label, eventLabel: data.label,
nonInteraction: !data.interaction, nonInteraction: !data.interaction,
}) }, { ...data.metrics, ...data.dimensions });
} }
throw 'No available analytics backend found. Has Google Analytics been loaded yet?'; throw 'No available analytics backend found. Has Google Analytics been loaded yet?';
...@@ -198,7 +200,7 @@ class Foresight { ...@@ -198,7 +200,7 @@ class Foresight {
* Parse an event string and returns a `EventData` object. * Parse an event string and returns a `EventData` object.
* @private * @private
*/ */
private _parseEventString(eventString: string): EventData { private _parseEventString(eventString: string): Partial<EventData> {
const split = eventString.split(';'); const split = eventString.split(';');
let [ category, action, label ] = split; let [ category, action, label ] = split;
...@@ -256,12 +258,39 @@ class Foresight { ...@@ -256,12 +258,39 @@ class Foresight {
const s = element.getAttribute('data-track'); const s = element.getAttribute('data-track');
const data = this._parseEventString(s); const data = this._parseEventString(s);
// Get custom metrics
data.metrics = Utils
.toArray<Attr>(element.attributes)
.filter(attr => attr.name.startsWith('data-track:metrics'))
.reduce((acc, attr) => {
const key = attr.name.replace(/data-track:metrics:/, '');
const value = attr.value === "" ? 1 : attr.value;
acc[key] = value;
return acc;
}, {});
// Get custom dimensions
data.dimensions = Utils
.toArray<Attr>(element.attributes)
.filter(attr => attr.name.startsWith('data-track:dimensions'))
.reduce((acc, attr) => {
if (attr.value === "") {
console.warn(`No dimension value provided for ${attr.name}`);
return acc;
}
const key = attr.name.replace(/data-track:dimensions:/, '');
const value = attr.value;
acc[key] = value;
return acc;
}, {});
// Get interaction flag
data.interaction = this.options.clicksAreInteractions; data.interaction = this.options.clicksAreInteractions;
if (element.getAttribute('data-track:non-interaction') !== null) { if (element.getAttribute('data-track:non-interaction') !== null) {
data.interaction = false; data.interaction = false;
} }
this.send(data); this.send(<EventData> data);
} }
/** /**
...@@ -272,12 +301,39 @@ class Foresight { ...@@ -272,12 +301,39 @@ class Foresight {
const s = element.getAttribute('data-track-view'); const s = element.getAttribute('data-track-view');
const data = this._parseEventString(s); const data = this._parseEventString(s);
// Get custom metrics
data.metrics = Utils
.toArray<Attr>(element.attributes)
.filter(attr => attr.name.startsWith('data-track-view:metrics'))
.reduce((acc, attr) => {
const key = attr.name.replace(/data-track-view:metrics:/, '');
const value = attr.value === "" ? 1 : attr.value;
acc[key] = value;
return acc;
}, {});
// Get custom dimensions
data.dimensions = Utils
.toArray<Attr>(element.attributes)
.filter(attr => attr.name.startsWith('data-track-view:dimensions'))
.reduce((acc, attr) => {
if (attr.value === "") {
console.warn(`No dimension value provided for ${attr.name}`);
return acc;
}
const key = attr.name.replace(/data-track-view:dimensions:/, '');
const value = attr.value;
acc[key] = value;
return acc;
}, {});
// Get interaction flag
data.interaction = this.options.viewsAreInteractions; data.interaction = this.options.viewsAreInteractions;
if (element.getAttribute('data-track-view:interaction') !== null) { if (element.getAttribute('data-track-view:interaction') !== null) {
data.interaction = true; data.interaction = true;
} }
this.send(data); this.send(<EventData> data);
} }
} }
......
...@@ -22,17 +22,22 @@ interface ForesightConfig { ...@@ -22,17 +22,22 @@ interface ForesightConfig {
sendEventFn?: (data: EventData) => any; sendEventFn?: (data: EventData) => any;
} }
/** /**
* Represents a particular event's data. * Represents an event data.
*/ */
interface EventData { interface EventData {
category: string; category: string;
action: string; action: string;
label: string; label: string;
interaction: boolean; interaction: boolean;
metrics?: {
[key: string]: any;
};
dimensions?: {
[key: string]: any;
};
} }
/** /**
* Foresight is an analytics library that allows for declarative event tracking. * Foresight is an analytics library that allows for declarative event tracking.
* @class
*/ */
declare class Foresight { declare class Foresight {
/** /**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment