UNPKG

trala-angulartics2

Version:

Vendor-agnostic web analytics for Angular2 applications

1,135 lines (1,119 loc) 100 kB
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