trala-angulartics2
Version:
Vendor-agnostic web analytics for Angular2 applications
1,135 lines (1,119 loc) • 100 kB
JavaScript
import * as i0 from '@angular/core';
import { InjectionToken, Injectable, Inject, Directive, Input, NgModule } from '@angular/core';
import { BehaviorSubject, ReplaySubject } from 'rxjs';
import { filter, map, delay } from 'rxjs/operators';
import * as i1 from '@angular/router';
import { NavigationEnd, NavigationStart, NavigationError } from '@angular/router';
import * as i2 from '@angular/common';
import * as i2$1 from '@angular/platform-browser';
class DefaultConfig {
constructor() {
this.pageTracking = {
autoTrackVirtualPages: true,
basePath: '',
excludedRoutes: [],
clearIds: false,
clearHash: false,
clearQueryParams: false,
idsRegExp: /^\d+$|^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/,
};
this.developerMode = false;
this.ga = {};
this.appInsights = {};
this.gtm = {};
this.gst = {};
}
}
const ANGULARTICS2_TOKEN = new InjectionToken('ANGULARTICS2');
class RouterlessTracking {
trackLocation(settings) {
return new BehaviorSubject({ url: '/' });
}
prepareExternalUrl(url) {
return url;
}
}
class Angulartics2 {
constructor(tracker, setup) {
this.tracker = tracker;
this.pageTrack = new ReplaySubject(10);
this.eventTrack = new ReplaySubject(10);
this.exceptionTrack = new ReplaySubject(10);
this.setAlias = new ReplaySubject(10);
this.setUsername = new ReplaySubject(10);
this.setUserProperties = new ReplaySubject(10);
this.setUserPropertiesOnce = new ReplaySubject(10);
this.setSuperProperties = new ReplaySubject(10);
this.setSuperPropertiesOnce = new ReplaySubject(10);
this.userTimings = new ReplaySubject(10);
const defaultConfig = new DefaultConfig();
this.settings = { ...defaultConfig, ...setup.settings };
this.settings.pageTracking = {
...defaultConfig.pageTracking,
...setup.settings.pageTracking,
};
this.tracker
.trackLocation(this.settings)
.subscribe((event) => this.trackUrlChange(event.url));
}
/** filters all events when developer mode is true */
filterDeveloperMode() {
return filter((value, index) => !this.settings.developerMode);
}
trackUrlChange(url) {
if (this.settings.pageTracking.autoTrackVirtualPages && !this.matchesExcludedRoute(url)) {
const clearedUrl = this.clearUrl(url);
let path;
if (this.settings.pageTracking.basePath.length) {
path = this.settings.pageTracking.basePath + clearedUrl;
}
else {
path = this.tracker.prepareExternalUrl(clearedUrl);
}
this.pageTrack.next({ path });
}
}
/**
* Use string literals or regular expressions to exclude routes
* from automatic pageview tracking.
*
* @param url location
*/
matchesExcludedRoute(url) {
for (const excludedRoute of this.settings.pageTracking.excludedRoutes) {
const matchesRegex = excludedRoute instanceof RegExp && excludedRoute.test(url);
if (matchesRegex || url.indexOf(excludedRoute) !== -1) {
return true;
}
}
return false;
}
/**
* Removes id's from tracked route.
* EX: `/project/12981/feature` becomes `/project/feature`
*
* @param url current page path
*/
clearUrl(url) {
if (this.settings.pageTracking.clearIds ||
this.settings.pageTracking.clearQueryParams ||
this.settings.pageTracking.clearHash) {
return url
.split('/')
.map(part => (this.settings.pageTracking.clearQueryParams ? part.split('?')[0] : part))
.map(part => (this.settings.pageTracking.clearHash ? part.split('#')[0] : part))
.filter(part => !this.settings.pageTracking.clearIds ||
!part.match(this.settings.pageTracking.idsRegExp))
.join('/');
}
return url;
}
}
Angulartics2.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2, deps: [{ token: RouterlessTracking }, { token: ANGULARTICS2_TOKEN }], target: i0.ɵɵFactoryTarget.Injectable });
Angulartics2.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: function () { return [{ type: RouterlessTracking }, { type: undefined, decorators: [{
type: Inject,
args: [ANGULARTICS2_TOKEN]
}] }]; } });
/**
* Track Route changes for applications using Angular's
* default router
*
* @link https://angular.io/api/router/Router
*/
class AngularRouterTracking {
constructor(router, location) {
this.router = router;
this.location = location;
}
trackLocation(settings) {
return this.router.events.pipe(filter(e => e instanceof NavigationEnd), filter(() => !settings.developerMode), map((e) => {
return { url: e.urlAfterRedirects };
}), delay(0));
}
prepareExternalUrl(url) {
return this.location.prepareExternalUrl(url);
}
}
AngularRouterTracking.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: AngularRouterTracking, deps: [{ token: i1.Router }, { token: i2.Location }], target: i0.ɵɵFactoryTarget.Injectable });
AngularRouterTracking.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: AngularRouterTracking, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: AngularRouterTracking, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: function () { return [{ type: i1.Router }, { type: i2.Location }]; } });
class Angulartics2On {
constructor(elRef, angulartics2, renderer) {
this.elRef = elRef;
this.angulartics2 = angulartics2;
this.renderer = renderer;
this.angularticsProperties = {};
}
ngAfterContentInit() {
this.renderer.listen(this.elRef.nativeElement, this.angulartics2On || 'click', (event) => this.eventTrack(event));
}
eventTrack(event) {
const action = this.angularticsAction; // || this.inferEventName();
const properties = {
...this.angularticsProperties,
eventType: event.type,
};
if (this.angularticsCategory) {
properties.category = this.angularticsCategory;
}
if (this.angularticsLabel) {
properties.label = this.angularticsLabel;
}
if (this.angularticsValue) {
properties.value = this.angularticsValue;
}
this.angulartics2.eventTrack.next({
action,
properties,
});
}
}
Angulartics2On.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2On, deps: [{ token: i0.ElementRef }, { token: Angulartics2 }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive });
Angulartics2On.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.0.3", type: Angulartics2On, selector: "[angulartics2On]", inputs: { angulartics2On: "angulartics2On", angularticsAction: "angularticsAction", angularticsCategory: "angularticsCategory", angularticsLabel: "angularticsLabel", angularticsValue: "angularticsValue", angularticsProperties: "angularticsProperties" }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2On, decorators: [{
type: Directive,
args: [{ selector: '[angulartics2On]' }]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: Angulartics2 }, { type: i0.Renderer2 }]; }, propDecorators: { angulartics2On: [{
type: Input,
args: ['angulartics2On']
}], angularticsAction: [{
type: Input
}], angularticsCategory: [{
type: Input
}], angularticsLabel: [{
type: Input
}], angularticsValue: [{
type: Input
}], angularticsProperties: [{
type: Input
}] } });
class Angulartics2OnModule {
}
Angulartics2OnModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2OnModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
Angulartics2OnModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2OnModule, declarations: [Angulartics2On], exports: [Angulartics2On] });
Angulartics2OnModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2OnModule });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2OnModule, decorators: [{
type: NgModule,
args: [{
declarations: [Angulartics2On],
exports: [Angulartics2On],
}]
}] });
class Angulartics2Module {
static forRoot(settings = {}) {
return {
ngModule: Angulartics2Module,
providers: [
{ provide: ANGULARTICS2_TOKEN, useValue: { settings } },
{ provide: RouterlessTracking, useClass: AngularRouterTracking },
Angulartics2,
],
};
}
}
Angulartics2Module.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2Module, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
Angulartics2Module.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2Module, imports: [Angulartics2OnModule], exports: [Angulartics2On] });
Angulartics2Module.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2Module, imports: [[Angulartics2OnModule]] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2Module, decorators: [{
type: NgModule,
args: [{
imports: [Angulartics2OnModule],
exports: [Angulartics2On],
}]
}] });
class Angulartics2RouterlessModule {
static forRoot(settings = {}) {
return {
ngModule: Angulartics2RouterlessModule,
providers: [
{ provide: ANGULARTICS2_TOKEN, useValue: { settings } },
RouterlessTracking,
Angulartics2,
],
};
}
}
Angulartics2RouterlessModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2RouterlessModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
Angulartics2RouterlessModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2RouterlessModule, imports: [Angulartics2OnModule] });
Angulartics2RouterlessModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2RouterlessModule, imports: [[Angulartics2OnModule]] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2RouterlessModule, decorators: [{
type: NgModule,
args: [{
imports: [Angulartics2OnModule],
}]
}] });
class Angulartics2AdobeAnalytics {
constructor(angulartics2, location) {
this.angulartics2 = angulartics2;
this.location = location;
this.angulartics2.setUserProperties.subscribe(x => this.setUserProperties(x));
}
startTracking() {
this.angulartics2.pageTrack
.pipe(this.angulartics2.filterDeveloperMode())
.subscribe(x => this.pageTrack(x.path));
this.angulartics2.eventTrack
.pipe(this.angulartics2.filterDeveloperMode())
.subscribe(x => this.eventTrack(x.action, x.properties));
}
pageTrack(path) {
if (typeof s !== 'undefined' && s) {
s.clearVars();
s.t({ pageName: path });
}
}
/**
* Track Event in Adobe Analytics
*
* @param action associated with the event
* @param properties action detials
*
* @link https://marketing.adobe.com/resources/help/en_US/sc/implement/js_implementation.html
*/
eventTrack(action, properties) {
// TODO: make interface
// @property {string} properties.category
// @property {string} properties.label
// @property {number} properties.value
// @property {boolean} properties.noninteraction
if (!properties) {
properties = properties || {};
}
if (typeof s !== 'undefined' && s) {
if (typeof properties === 'object') {
this.setUserProperties(properties);
}
if (action) {
// if linkName property is passed, use that; otherwise, the action is the linkName
const linkName = properties['linkName'] ? properties['linkName'] : action;
// note that 'this' should refer the link element, but we can't get that in this function. example:
// <a href="http://anothersite.com" onclick="s.tl(this,'e','AnotherSite',null)">
// if disableDelay property is passed, use that to turn off/on the 500ms delay; otherwise, it uses this
const disableDelay = !!properties['disableDelay'] ? true : this;
// if action property is passed, use that; otherwise, the action remains unchanged
if (properties['action']) {
action = properties['action'];
}
this.setPageName();
if (action.toUpperCase() === 'DOWNLOAD') {
s.tl(disableDelay, 'd', linkName);
}
else if (action.toUpperCase() === 'EXIT') {
s.tl(disableDelay, 'e', linkName);
}
else {
s.tl(disableDelay, 'o', linkName);
}
}
}
}
setPageName() {
const path = this.location.path(true);
const hashNdx = path.indexOf('#');
if (hashNdx > 0 && hashNdx < path.length) {
s.pageName = path.substring(hashNdx + 1);
}
else {
s.pageName = path;
}
}
setUserProperties(properties) {
if (typeof s !== 'undefined' && s) {
if (typeof properties === 'object') {
for (const key in properties) {
if (properties.hasOwnProperty(key)) {
s[key] = properties[key];
}
}
}
}
}
}
Angulartics2AdobeAnalytics.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2AdobeAnalytics, deps: [{ token: Angulartics2 }, { token: i2.Location }], target: i0.ɵɵFactoryTarget.Injectable });
Angulartics2AdobeAnalytics.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2AdobeAnalytics, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2AdobeAnalytics, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: function () { return [{ type: Angulartics2 }, { type: i2.Location }]; } });
class AppInsightsDefaults {
constructor() {
this.userId = null;
}
}
class Angulartics2AppInsights {
constructor(angulartics2, title, router) {
this.angulartics2 = angulartics2;
this.title = title;
this.router = router;
this.loadStartTime = null;
this.loadTime = null;
this.metrics = null;
this.dimensions = null;
this.measurements = null;
if (typeof appInsights === 'undefined') {
console.warn('appInsights not found');
}
const defaults = new AppInsightsDefaults();
// Set the default settings for this module
this.angulartics2.settings.appInsights = {
...defaults,
...this.angulartics2.settings.appInsights,
};
this.angulartics2.setUsername.subscribe((x) => this.setUsername(x));
this.angulartics2.setUserProperties.subscribe((x) => this.setUserProperties(x));
}
startTracking() {
this.angulartics2.pageTrack
.pipe(this.angulartics2.filterDeveloperMode())
.subscribe((x) => this.pageTrack(x.path));
this.angulartics2.eventTrack
.pipe(this.angulartics2.filterDeveloperMode())
.subscribe((x) => this.eventTrack(x.action, x.properties));
this.angulartics2.exceptionTrack
.pipe(this.angulartics2.filterDeveloperMode())
.subscribe((x) => this.exceptionTrack(x));
this.router.events
.pipe(this.angulartics2.filterDeveloperMode(), filter((event) => event instanceof NavigationStart))
.subscribe((event) => this.startTimer());
this.router.events
.pipe(filter((event) => event instanceof NavigationError || event instanceof NavigationEnd))
.subscribe((error) => this.stopTimer());
}
startTimer() {
this.loadStartTime = Date.now();
this.loadTime = null;
}
stopTimer() {
this.loadTime = Date.now() - this.loadStartTime;
this.loadStartTime = null;
}
/**
* Page Track in Baidu Analytics
*
* @param path - Location 'path'
*
* @link https://github.com/Microsoft/ApplicationInsights-JS/blob/master/API-reference.md#trackpageview
*/
pageTrack(path) {
appInsights.trackPageView(this.title.getTitle(), path, this.dimensions, this.metrics, this.loadTime);
}
/**
* Log a user action or other occurrence.
*
* @param name Name to identify this event in the portal.
* @param properties Additional data used to filter events and metrics in the portal. Defaults to empty.
*
* @link https://github.com/Microsoft/ApplicationInsights-JS/blob/master/API-reference.md#trackevent
*/
eventTrack(name, properties) {
appInsights.trackEvent(name, properties, this.measurements);
}
/**
* Exception Track Event in GA
*
* @param properties - Comprised of the mandatory fields 'appId' (string), 'appName' (string) and 'appVersion' (string) and
* optional fields 'fatal' (boolean) and 'description' (string), error
*
* @link https://github.com/Microsoft/ApplicationInsights-JS/blob/master/API-reference.md#trackexception
*/
exceptionTrack(properties) {
const description = properties.event || properties.description || properties;
appInsights.trackException(description);
}
/**
* @link https://github.com/Microsoft/ApplicationInsights-JS/blob/master/API-reference.md#setauthenticatedusercontext
*/
setUsername(userId) {
this.angulartics2.settings.appInsights.userId = userId;
appInsights.setAuthenticatedUserContext(userId);
}
setUserProperties(properties) {
if (properties.userId) {
this.angulartics2.settings.appInsights.userId = properties.userId;
}
if (properties.accountId) {
appInsights.setAuthenticatedUserContext(this.angulartics2.settings.appInsights.userId, properties.accountId);
}
else {
appInsights.setAuthenticatedUserContext(this.angulartics2.settings.appInsights.userId);
}
}
}
Angulartics2AppInsights.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2AppInsights, deps: [{ token: Angulartics2 }, { token: i2$1.Title }, { token: i1.Router }], target: i0.ɵɵFactoryTarget.Injectable });
Angulartics2AppInsights.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2AppInsights, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2AppInsights, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: function () { return [{ type: Angulartics2 }, { type: i2$1.Title }, { type: i1.Router }]; } });
class Angulartics2BaiduAnalytics {
constructor(angulartics2) {
this.angulartics2 = angulartics2;
if (typeof _hmt === 'undefined') {
_hmt = [];
}
else {
_hmt.push(['_setAutoPageview', false]);
}
this.angulartics2.setUsername
.subscribe((x) => this.setUsername(x));
this.angulartics2.setUserProperties
.subscribe((x) => this.setUserProperties(x));
}
startTracking() {
this.angulartics2.pageTrack
.pipe(this.angulartics2.filterDeveloperMode())
.subscribe((x) => this.pageTrack(x.path));
this.angulartics2.eventTrack
.pipe(this.angulartics2.filterDeveloperMode())
.subscribe((x) => this.eventTrack(x.action, x.properties));
}
/**
* Page Track in Baidu Analytics
*
* @param path Required url 'path'
*
* @link http://tongji.baidu.com/open/api/more?p=ref_trackPageview
*/
pageTrack(path) {
if (typeof _hmt !== 'undefined' && _hmt) {
_hmt.push(['_trackPageview', path]);
}
}
/**
* Track Event in Baidu Analytics
*
* @param action Name associated with the event
* @param properties Comprised of:
* - 'category' (string)
* - 'opt_label' (string)
* - 'opt_value' (string)
*
* @link http://tongji.baidu.com/open/api/more?p=ref_trackEvent
*/
eventTrack(action, properties) {
// baidu analytics requires category
if (!properties || !properties.category) {
properties = properties || {};
properties.category = 'Event';
properties.opt_label = 'default';
properties.opt_value = 'default';
}
if (typeof _hmt !== 'undefined' && _hmt) {
_hmt.push([
'_trackEvent',
properties.category,
action,
properties.opt_label,
properties.opt_value,
]);
}
}
setUsername(userId) {
// set default custom variables name to 'identity' and 'value'
_hmt.push(['_setCustomVar', 1, 'identity', userId]);
}
setUserProperties(properties) {
_hmt.push(['_setCustomVar', 2, 'user', JSON.stringify(properties)]);
}
}
Angulartics2BaiduAnalytics.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2BaiduAnalytics, deps: [{ token: Angulartics2 }], target: i0.ɵɵFactoryTarget.Injectable });
Angulartics2BaiduAnalytics.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2BaiduAnalytics, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2BaiduAnalytics, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: function () { return [{ type: Angulartics2 }]; } });
const facebookEventList = [
'ViewContent',
'Search',
'AddToCart',
'AddToWishlist',
'InitiateCheckout',
'AddPaymentInfo',
'Purchase',
'Lead',
'CompleteRegistration',
];
class Angulartics2Facebook {
constructor(angulartics2) {
this.angulartics2 = angulartics2;
}
startTracking() {
this.angulartics2.eventTrack
.pipe(this.angulartics2.filterDeveloperMode())
.subscribe(x => this.eventTrack(x.action, x.properties));
}
/**
* Send interactions to the Pixel, i.e. for event tracking in Pixel
*
* @param action action associated with the event
*/
eventTrack(action, properties = {}) {
if (typeof fbq === 'undefined') {
return;
}
if (facebookEventList.indexOf(action) === -1) {
return fbq('trackCustom', action, properties);
}
return fbq('track', action, properties);
}
}
Angulartics2Facebook.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2Facebook, deps: [{ token: Angulartics2 }], target: i0.ɵɵFactoryTarget.Injectable });
Angulartics2Facebook.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2Facebook, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2Facebook, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: function () { return [{ type: Angulartics2 }]; } });
class GoogleAnalyticsDefaults {
constructor() {
this.additionalAccountNames = [];
this.userId = null;
this.transport = '';
this.anonymizeIp = false;
}
}
class Angulartics2GoogleAnalytics {
constructor(angulartics2) {
this.angulartics2 = angulartics2;
this.dimensionsAndMetrics = [];
const defaults = new GoogleAnalyticsDefaults();
// Set the default settings for this module
this.angulartics2.settings.ga = {
...defaults,
...this.angulartics2.settings.ga,
};
this.settings = this.angulartics2.settings.ga;
this.angulartics2.setUsername.subscribe((x) => this.setUsername(x));
this.angulartics2.setUserProperties.subscribe(x => this.setUserProperties(x));
}
startTracking() {
this.angulartics2.pageTrack
.pipe(this.angulartics2.filterDeveloperMode())
.subscribe(x => this.pageTrack(x.path));
this.angulartics2.eventTrack
.pipe(this.angulartics2.filterDeveloperMode())
.subscribe(x => this.eventTrack(x.action, x.properties));
this.angulartics2.exceptionTrack
.pipe(this.angulartics2.filterDeveloperMode())
.subscribe(x => this.exceptionTrack(x));
this.angulartics2.userTimings
.pipe(this.angulartics2.filterDeveloperMode())
.subscribe(x => this.userTimings(x));
}
pageTrack(path) {
if (typeof _gaq !== 'undefined' && _gaq) {
_gaq.push(['_trackPageview', path]);
for (const accountName of this.angulartics2.settings.ga.additionalAccountNames) {
_gaq.push([accountName + '._trackPageview', path]);
}
}
if (typeof ga !== 'undefined' && ga) {
if (this.angulartics2.settings.ga.userId) {
ga('set', '&uid', this.angulartics2.settings.ga.userId);
for (const accountName of this.angulartics2.settings.ga.additionalAccountNames) {
ga(accountName + '.set', '&uid', this.angulartics2.settings.ga.userId);
}
}
if (this.angulartics2.settings.ga.anonymizeIp) {
ga('set', 'anonymizeIp', true);
for (const accountName of this.angulartics2.settings.ga.additionalAccountNames) {
ga(accountName + '.set', 'anonymizeIp', true);
}
}
ga('send', 'pageview', path);
for (const accountName of this.angulartics2.settings.ga.additionalAccountNames) {
ga(accountName + '.send', 'pageview', path);
}
}
}
/**
* Track Event in GA
*
* @param action Associated with the event
* @param properties Comprised of:
* - category (string) and optional
* - label (string)
* - value (integer)
* - noninteraction (boolean)
*
* @link https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide#SettingUpEventTracking
* @link https://developers.google.com/analytics/devguides/collection/analyticsjs/events
*/
eventTrack(action, properties) {
// Google Analytics requires an Event Category
if (!properties || !properties.category) {
properties = properties || {};
properties.category = 'Event';
}
// GA requires that eventValue be an integer, see:
// https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#eventValue
// https://github.com/luisfarzati/angulartics/issues/81
if (properties.value) {
const parsed = parseInt(properties.value, 10);
properties.value = isNaN(parsed) ? 0 : parsed;
}
if (typeof ga !== 'undefined') {
const eventOptions = {
eventCategory: properties.category,
eventAction: action,
eventLabel: properties.label,
eventValue: properties.value,
nonInteraction: properties.noninteraction,
page: properties.page || location.hash.substring(1) || location.pathname,
userId: this.angulartics2.settings.ga.userId,
hitCallback: properties.hitCallback,
...(this.angulartics2.settings.ga.transport && {
transport: this.angulartics2.settings.ga.transport,
}),
};
// add custom dimensions and metrics
this.setDimensionsAndMetrics(properties);
ga('send', 'event', eventOptions);
for (const accountName of this.angulartics2.settings.ga.additionalAccountNames) {
ga(accountName + '.send', 'event', eventOptions);
}
}
else if (typeof _gaq !== 'undefined') {
_gaq.push([
'_trackEvent',
properties.category,
action,
properties.label,
properties.value,
properties.noninteraction,
]);
}
}
/**
* Exception Track Event in GA
*
* @param properties Comprised of the optional fields:
* - fatal (string)
* - description (string)
*
* @https://developers.google.com/analytics/devguides/collection/analyticsjs/exceptions
*
* @link https://developers.google.com/analytics/devguides/collection/analyticsjs/events
*/
exceptionTrack(properties) {
if (properties.fatal === undefined) {
console.log('No "fatal" provided, sending with fatal=true');
properties.fatal = true;
}
properties.exDescription = properties.description;
const eventOptions = {
exFatal: properties.fatal,
exDescription: properties.description,
};
ga('send', 'exception', eventOptions);
for (const accountName of this.angulartics2.settings.ga.additionalAccountNames) {
ga(accountName + '.send', 'exception', eventOptions);
}
}
/**
* User Timings Event in GA
*
* @param properties Comprised of the mandatory fields:
* - timingCategory (string)
* - timingVar (string)
* - timingValue (number)
* Properties can also have the optional fields:
* - timingLabel (string)
*
* @link https://developers.google.com/analytics/devguides/collection/analyticsjs/user-timings
*/
userTimings(properties) {
if (!properties ||
!properties.timingCategory ||
!properties.timingVar ||
!properties.timingValue) {
console.error('Properties timingCategory, timingVar, and timingValue are required to be set.');
return;
}
if (typeof ga !== 'undefined') {
ga('send', 'timing', properties);
for (const accountName of this.angulartics2.settings.ga.additionalAccountNames) {
ga(accountName + '.send', 'timing', properties);
}
}
}
setUsername(userId) {
this.angulartics2.settings.ga.userId = userId;
if (typeof ga === 'undefined') {
return;
}
ga('set', 'userId', userId);
}
setUserProperties(properties) {
this.setDimensionsAndMetrics(properties);
}
setDimensionsAndMetrics(properties) {
if (typeof ga === 'undefined') {
return;
}
// clean previously used dimensions and metrics that will not be overriden
this.dimensionsAndMetrics.forEach(elem => {
if (!properties.hasOwnProperty(elem)) {
ga('set', elem, undefined);
this.angulartics2.settings.ga.additionalAccountNames.forEach((accountName) => {
ga(`${accountName}.set`, elem, undefined);
});
}
});
this.dimensionsAndMetrics = [];
// add custom dimensions and metrics
Object.keys(properties).forEach(key => {
if (key.lastIndexOf('dimension', 0) === 0 || key.lastIndexOf('metric', 0) === 0) {
ga('set', key, properties[key]);
this.angulartics2.settings.ga.additionalAccountNames.forEach((accountName) => {
ga(`${accountName}.set`, key, properties[key]);
});
this.dimensionsAndMetrics.push(key);
}
});
}
}
Angulartics2GoogleAnalytics.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2GoogleAnalytics, deps: [{ token: Angulartics2 }], target: i0.ɵɵFactoryTarget.Injectable });
Angulartics2GoogleAnalytics.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2GoogleAnalytics, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2GoogleAnalytics, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: function () { return [{ type: Angulartics2 }]; } });
class Angulartics2GoogleAnalyticsEnhancedEcommerce {
/**
* Add impression in GA enhanced ecommerce tracking
* @link https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce#measuring-activities
*/
ecAddImpression(properties) {
ga('ec:addImpression', properties);
}
/**
* Add product in GA enhanced ecommerce tracking
* @link https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce
*/
ecAddProduct(product) {
ga('ec:addProduct', product);
}
/**
* Set action in GA enhanced ecommerce tracking
* @link https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce
*/
ecSetAction(action, properties) {
ga('ec:setAction', action, properties);
}
}
Angulartics2GoogleAnalyticsEnhancedEcommerce.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2GoogleAnalyticsEnhancedEcommerce, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
Angulartics2GoogleAnalyticsEnhancedEcommerce.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2GoogleAnalyticsEnhancedEcommerce, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2GoogleAnalyticsEnhancedEcommerce, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class GoogleTagManagerDefaults {
constructor() {
this.userId = null;
}
}
class Angulartics2GoogleTagManager {
constructor(angulartics2) {
this.angulartics2 = angulartics2;
// The dataLayer needs to be initialized
if (typeof dataLayer !== 'undefined' && dataLayer) {
dataLayer = window.dataLayer = window.dataLayer || [];
}
const defaults = new GoogleTagManagerDefaults();
// Set the default settings for this module
this.angulartics2.settings.gtm = { ...defaults, ...this.angulartics2.settings.gtm };
this.angulartics2.setUsername.subscribe((x) => this.setUsername(x));
}
startTracking() {
this.angulartics2.pageTrack
.pipe(this.angulartics2.filterDeveloperMode())
.subscribe(x => this.pageTrack(x.path));
this.angulartics2.eventTrack
.pipe(this.angulartics2.filterDeveloperMode())
.subscribe(x => this.eventTrack(x.action, x.properties));
this.angulartics2.exceptionTrack
.pipe(this.angulartics2.filterDeveloperMode())
.subscribe((x) => this.exceptionTrack(x));
}
pageTrack(path) {
this.pushLayer({
event: 'Page View',
'content-name': path,
userId: this.angulartics2.settings.gtm.userId,
});
}
/**
* Send Data Layer
*
* @layer data layer object
*/
pushLayer(layer) {
if (typeof dataLayer !== 'undefined' && dataLayer) {
dataLayer.push(layer);
}
}
/**
* Send interactions to the dataLayer, i.e. for event tracking in Google Analytics
*
* @param action associated with the event
*/
eventTrack(action, properties) {
// TODO: make interface
// @param {string} properties.category
// @param {string} [properties.label]
// @param {number} [properties.value]
// @param {boolean} [properties.noninteraction]
// Set a default GTM category
properties = properties || {};
this.pushLayer({
event: properties.event || 'interaction',
target: properties.category || 'Event',
action,
label: properties.label,
value: properties.value,
interactionType: properties.noninteraction,
userId: this.angulartics2.settings.gtm.userId,
...properties.gtmCustom,
});
}
/**
* Exception Track Event in GTM
*
*/
exceptionTrack(properties) {
// TODO: make interface
// @param {Object} properties
// @param {string} properties.appId
// @param {string} properties.appName
// @param {string} properties.appVersion
// @param {string} [properties.description]
// @param {boolean} [properties.fatal]
if (!properties || !properties.appId || !properties.appName || !properties.appVersion) {
console.error('Must be setted appId, appName and appVersion.');
return;
}
if (properties.fatal === undefined) {
console.log('No "fatal" provided, sending with fatal=true');
properties.exFatal = true;
}
properties.exDescription = properties.event ? properties.event.stack : properties.description;
this.eventTrack(`Exception thrown for ${properties.appName} <${properties.appId}@${properties.appVersion}>`, {
category: 'Exception',
label: properties.exDescription,
});
}
/**
* Set userId for use with Universal Analytics User ID feature
*
* @param userId used to identify user cross-device in Google Analytics
*/
setUsername(userId) {
this.angulartics2.settings.gtm.userId = userId;
}
}
Angulartics2GoogleTagManager.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2GoogleTagManager, deps: [{ token: Angulartics2 }], target: i0.ɵɵFactoryTarget.Injectable });
Angulartics2GoogleTagManager.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2GoogleTagManager, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2GoogleTagManager, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: function () { return [{ type: Angulartics2 }]; } });
class GoogleGlobalSiteTagDefaults {
constructor() {
this.trackingIds = [];
if (typeof ga !== 'undefined' && ga) {
// See: https://developers.google.com/analytics/devguides/collection/analyticsjs/ga-object-methods-reference
ga(() => {
ga.getAll().forEach((tracker) => {
const id = tracker.get('trackingId');
// If set both in forRoot and HTML page, we want to avoid duplicates
if (id !== undefined && this.trackingIds.indexOf(id) === -1) {
this.trackingIds.push(id);
}
});
});
}
}
}
class Angulartics2GoogleGlobalSiteTag {
constructor(angulartics2) {
this.angulartics2 = angulartics2;
this.dimensionsAndMetrics = {};
const defaults = new GoogleGlobalSiteTagDefaults();
// Set the default settings for this module
this.angulartics2.settings.gst = { ...defaults, ...this.angulartics2.settings.gst };
}
startTracking() {
this.angulartics2.pageTrack
.pipe(this.angulartics2.filterDeveloperMode())
.subscribe(x => this.pageTrack(x.path));
this.angulartics2.eventTrack
.pipe(this.angulartics2.filterDeveloperMode())
.subscribe(x => this.eventTrack(x.action, x.properties));
this.angulartics2.exceptionTrack
.pipe(this.angulartics2.filterDeveloperMode())
.subscribe((x) => this.exceptionTrack(x));
this.angulartics2.userTimings
.pipe(this.angulartics2.filterDeveloperMode())
.subscribe(x => this.userTimings(this.convertTimings(x)));
this.angulartics2.setUsername
.pipe(this.angulartics2.filterDeveloperMode())
.subscribe((x) => this.setUsername(x));
this.angulartics2.setUserProperties
.pipe(this.angulartics2.filterDeveloperMode())
.subscribe((x) => this.setUserProperties(x));
}
/**
* Manually track page view, see:
*
* https://developers.google.com/analytics/devguides/collection/gtagjs/single-page-applications#tracking_virtual_pageviews
*
* @param path relative url
*/
pageTrack(path) {
if (typeof gtag !== 'undefined' && gtag) {
const params = {
page_path: path,
page_location: window.location.protocol + '//' + window.location.host + path,
...this.dimensionsAndMetrics,
};
// Custom map must be reset with all config to stay valid.
if (this.angulartics2.settings.gst.customMap) {
params.custom_map = this.angulartics2.settings.gst.customMap;
}
if (this.angulartics2.settings.gst.userId) {
params.user_id = this.angulartics2.settings.gst.userId;
}
if (this.angulartics2.settings.gst.anonymizeIp) {
params.anonymize_ip = this.angulartics2.settings.gst.anonymizeIp;
}
for (const id of this.angulartics2.settings.gst.trackingIds) {
gtag('config', id, params);
}
}
}
/**
* Send interactions to gtag, i.e. for event tracking in Google Analytics. See:
*
* https://developers.google.com/analytics/devguides/collection/gtagjs/events
*
* @param action associated with the event
*/
eventTrack(action, properties = {}) {
this.eventTrackInternal(action, {
event_category: properties.category || 'interaction',
event_label: properties.label,
value: properties.value,
non_interaction: properties.noninteraction,
...properties.gstCustom,
});
}
/**
* Exception Track Event in GST. See:
*
* https://developers.google.com/analytics/devguides/collection/gtagjs/exceptions
*
*/
exceptionTrack(properties) {
// TODO: make interface
// @param {Object} properties
// @param {string} [properties.description]
// @param {boolean} [properties.fatal]
if (properties.fatal === undefined) {
console.log('No "fatal" provided, sending with fatal=true');
properties.fatal = true;
}
properties.exDescription = properties.event ? properties.event.stack : properties.description;
this.eventTrack('exception', {
gstCustom: {
description: properties.exDescription,
fatal: properties.fatal,
...properties.gstCustom,
},
});
}
/**
* User Timings Event in GST.
*
* @param properties Comprised of the mandatory fields:
* - name (string)
* - value (number - integer)
* Properties can also have the optional fields:
* - category (string)
* - label (string)
*
* @link https://developers.google.com/analytics/devguides/collection/gtagjs/user-timings
*/
userTimings(properties) {
if (!properties) {
console.error('User timings - "properties" parameter is required to be set.');
return;
}
this.eventTrackInternal('timing_complete', {
name: properties.name,
value: properties.value,
event_category: properties.category,
event_label: properties.label,
});
}
convertTimings(properties) {
return {
name: properties.timingVar,
value: properties.timingValue,
category: properties.timingCategory,
label: properties.timingLabel,
};
}
setUsername(userId) {
this.angulartics2.settings.gst.userId = userId;
if (typeof gtag !== 'undefined' && gtag) {
gtag('set', { user_id: typeof userId === 'string' || !userId ? userId : userId.userId });
}
}
setUserProperties(properties) {
this.setDimensionsAndMetrics(properties);
}
setDimensionsAndMetrics(properties) {
// We want the dimensions and metrics to accumulate, so we merge with previous value
this.dimensionsAndMetrics = {
...this.dimensionsAndMetrics,
...properties,
};
// Remove properties that are null or undefined
Object.keys(this.dimensionsAndMetrics).forEach(key => {
const val = this.dimensionsAndMetrics[key];
if (val === undefined || val === null) {
delete this.dimensionsAndMetrics[key];
}
});
if (typeof gtag !== 'undefined' && gtag) {
gtag('set', this.dimensionsAndMetrics);
}
}
eventTrackInternal(action, properties = {}) {
this.cleanProperties(properties);
if (typeof gtag !== 'undefined' && gtag) {
gtag('event', action, properties);
}
}
cleanProperties(properties) {
// GA requires that eventValue be an non-negative integer, see:
// https://developers.google.com/analytics/devguides/collection/gtagjs/events
if (properties.value) {
const parsed = parseInt(properties.value, 10);
properties.value = isNaN(parsed) ? 0 : parsed;
}
}
}
Angulartics2GoogleGlobalSiteTag.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2GoogleGlobalSiteTag, deps: [{ token: Angulartics2 }], target: i0.ɵɵFactoryTarget.Injectable });
Angulartics2GoogleGlobalSiteTag.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2GoogleGlobalSiteTag, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.3", ngImport: i0, type: Angulartics2GoogleGlobalSiteTag, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: function () { r