ngx-matomo-client
Version:
Matomo (fka. Piwik) client for Angular applications
1 lines • 139 kB
Source Map (JSON)
{"version":3,"file":"ngx-matomo-client-core.mjs","sources":["../../../projects/ngx-matomo-client/core/utils/function.ts","../../../projects/ngx-matomo-client/core/utils/url.ts","../../../projects/ngx-matomo-client/core/tracker/script-factory.ts","../../../projects/ngx-matomo-client/core/utils/coercion.ts","../../../projects/ngx-matomo-client/core/utils/script-injector.ts","../../../projects/ngx-matomo-client/core/tracker/configuration.ts","../../../projects/ngx-matomo-client/core/holder.ts","../../../projects/ngx-matomo-client/core/tracker/internal-matomo-tracker.service.ts","../../../projects/ngx-matomo-client/core/testing/testing-tracker.ts","../../../projects/ngx-matomo-client/core/tracker/errors.ts","../../../projects/ngx-matomo-client/core/tracker/matomo-tracker.service.ts","../../../projects/ngx-matomo-client/core/tracker/matomo-initializer.service.ts","../../../projects/ngx-matomo-client/core/providers.ts","../../../projects/ngx-matomo-client/core/directives/matomo-opt-out-form.component.ts","../../../projects/ngx-matomo-client/core/directives/matomo-opt-out-form.component.html","../../../projects/ngx-matomo-client/core/directives/matomo-track-click.directive.ts","../../../projects/ngx-matomo-client/core/directives/matomo-tracker.directive.ts","../../../projects/ngx-matomo-client/core/matomo.module.ts","../../../projects/ngx-matomo-client/core/public-api.ts","../../../projects/ngx-matomo-client/core/ngx-matomo-client-core.ts"],"sourcesContent":["function coerceErrorHandler<T, ARGS extends unknown[]>(\n errorOrHandler: string | ((...args: ARGS) => T | never),\n): (...args: ARGS) => T | never {\n return typeof errorOrHandler === 'string'\n ? () => {\n throw new Error(errorOrHandler);\n }\n : errorOrHandler;\n}\n\n/** Wrap a function to ensure it is called only once, ignoring all subsequent calls */\nexport function runOnce<T, ARGS extends unknown[]>(\n fn: (...args: ARGS) => T,\n): (...args: ARGS) => T | void;\n/** Wrap a function to ensure it is called only once, calling an error handler otherwise */\nexport function runOnce<T, ARGS extends unknown[], U = T>(\n fn: (...args: ARGS) => T,\n errorOrHandler: string | ((...args: ARGS) => U | never),\n): (...args: ARGS) => T | U;\nexport function runOnce<T, ARGS extends unknown[], U = T>(\n fn: (...args: ARGS) => T,\n errorOrHandler?: string | ((...args: ARGS) => U | never),\n): (...args: ARGS) => T | U | void {\n const errorHandler = errorOrHandler ? coerceErrorHandler(errorOrHandler) : () => undefined;\n let run = false;\n\n return (...args: ARGS) => {\n if (run) {\n return errorHandler(...args);\n }\n\n run = true;\n\n return fn(...args);\n };\n}\n","export function appendTrailingSlash(str: string): string {\n return str.endsWith('/') ? str : `${str}/`;\n}\n","import { InjectionToken } from '@angular/core';\n\nexport type MatomoScriptFactory = (scriptUrl: string, document: Document) => HTMLScriptElement;\n\nexport const createDefaultMatomoScriptElement: MatomoScriptFactory = (scriptUrl, document) => {\n const g = document.createElement('script');\n\n g.type = 'text/javascript';\n g.defer = true;\n g.async = true;\n g.src = scriptUrl;\n\n return g;\n};\n\nexport const MATOMO_SCRIPT_FACTORY = new InjectionToken<MatomoScriptFactory>(\n 'MATOMO_SCRIPT_FACTORY',\n {\n providedIn: 'root',\n factory: () => createDefaultMatomoScriptElement,\n },\n);\n","export type CssSizeInput = string | number | null | undefined;\n\nexport function requireNonNull<T>(value: T | null | undefined, message: string): T {\n if (value === null || value === undefined) {\n throw new Error('Unexpected ' + value + ' value: ' + message);\n }\n\n return value;\n}\n\n/** Coerce a data-bound value to a boolean */\nexport function coerceCssSizeBinding(value: CssSizeInput): string {\n if (value == null) {\n return '';\n }\n\n return typeof value === 'string' ? value : `${value}px`;\n}\n","import { inject, Service, INJECTOR, runInInjectionContext, DOCUMENT } from '@angular/core';\nimport { MATOMO_SCRIPT_FACTORY } from '../tracker/script-factory';\nimport { requireNonNull } from './coercion';\n\n@Service({ autoProvided: false })\nexport class ScriptInjector {\n private readonly scriptFactory = inject(MATOMO_SCRIPT_FACTORY);\n private readonly injector = inject(INJECTOR);\n private readonly document = inject(DOCUMENT);\n\n injectDOMScript(scriptUrl: string): void {\n const scriptElement = runInInjectionContext(this.injector, () =>\n this.scriptFactory(scriptUrl, this.document),\n );\n const selfScript = requireNonNull(\n this.document.getElementsByTagName('script')[0],\n 'no existing script found',\n );\n const parent = requireNonNull(selfScript.parentNode, \"no script's parent node found\");\n\n parent.insertBefore(scriptElement, selfScript);\n }\n}\n","import { inject, InjectionToken } from '@angular/core';\nimport { requireNonNull } from '../utils/coercion';\n\nconst CONFIG_NOT_FOUND =\n 'No Matomo configuration found! Have you included Matomo module using MatomoModule.forRoot() or provideMatomo()?';\n\n/** Internal marker token to detect that router has been enabled */\nexport const MATOMO_ROUTER_ENABLED = new InjectionToken<boolean>('MATOMO_ROUTER_ENABLED', {\n factory() {\n return false;\n },\n});\n\n/** Injection token for {@link MatomoConfiguration} */\nexport const MATOMO_CONFIGURATION = new InjectionToken<MatomoConfiguration>('MATOMO_CONFIGURATION');\n\n/**\n * For internal use only. Injection token for {@link InternalMatomoConfiguration}\n *\n */\nexport const INTERNAL_MATOMO_CONFIGURATION = new InjectionToken<InternalMatomoConfiguration>(\n 'INTERNAL_MATOMO_CONFIGURATION',\n);\n\nexport function createInternalMatomoConfiguration(): InternalMatomoConfiguration {\n const { mode, requireConsent, ...restConfig } = requireNonNull(\n inject(MATOMO_CONFIGURATION, { optional: true }),\n CONFIG_NOT_FOUND,\n );\n\n return {\n mode: mode ? coerceInitializationMode(mode) : undefined,\n disabled: false,\n enableLinkTracking: true,\n trackAppInitialLoad: !inject(MATOMO_ROUTER_ENABLED),\n requireConsent: requireConsent ? coerceConsentRequirement(requireConsent) : 'none',\n enableJSErrorTracking: false,\n runOutsideAngularZone: true,\n disableCampaignParameters: false,\n acceptDoNotTrack: false,\n excludedQueryParams: [],\n ...restConfig,\n };\n}\n\n/**\n * For internal use only. Injection token for deferred {@link InternalMatomoConfiguration}.\n *\n */\nexport const DEFERRED_INTERNAL_MATOMO_CONFIGURATION =\n new InjectionToken<DeferredInternalMatomoConfiguration>('DEFERRED_INTERNAL_MATOMO_CONFIGURATION');\n\nexport function createDeferredInternalMatomoConfiguration(): DeferredInternalMatomoConfiguration {\n const base = inject(INTERNAL_MATOMO_CONFIGURATION);\n let resolveFn: ((configuration: InternalMatomoConfiguration) => void) | undefined;\n const configuration = new Promise<InternalMatomoConfiguration>(resolve => (resolveFn = resolve));\n\n return {\n configuration,\n markReady(configuration) {\n requireNonNull(\n resolveFn,\n 'resolveFn',\n )({\n ...base,\n ...configuration,\n });\n },\n };\n}\n\n/**\n * For internal use only. Injection token for fully loaded async {@link InternalMatomoConfiguration}.\n *\n */\nexport const ASYNC_INTERNAL_MATOMO_CONFIGURATION = new InjectionToken<\n Promise<InternalMatomoConfiguration>\n>('ASYNC_INTERNAL_MATOMO_CONFIGURATION');\n\n/**\n * For internal use only. Module configuration merged with default values.\n *\n */\nexport type InternalMatomoConfiguration = Omit<MatomoConfiguration, 'mode' | 'requireConsent'> &\n Omit<Required<BaseMatomoConfiguration>, 'requireConsent'> & {\n mode?: MatomoInitializationBehavior;\n requireConsent: MatomoConsentRequirement;\n };\n\nexport interface DeferredInternalMatomoConfiguration {\n readonly configuration: Promise<InternalMatomoConfiguration>;\n\n markReady(configuration: InternalMatomoConfiguration): void;\n}\n\n/**\n * - __auto__: automatically inject matomo script using provided configuration\n * - __manual__: do not inject Matomo script. In this case, initialization script must be provided\n * - __deferred__: automatically inject matomo script when deferred tracker configuration is provided using `MatomoInitializerService.initializeTracker()`.\n */\nexport type MatomoInitializationBehavior = 'auto' | 'manual' | 'deferred';\n\n/** @deprecated Use {@link MatomoInitializationBehavior} instead */\nexport enum MatomoInitializationMode {\n /**\n * Automatically inject matomo script using provided configuration\n *\n * @deprecated Use `'auto'` instead\n */\n AUTO,\n /**\n * Do not inject Matomo script. In this case, initialization script must be provided\n *\n * @deprecated Use `'manual'` instead\n */\n MANUAL,\n /**\n * Automatically inject matomo script when deferred tracker configuration is provided using `MatomoInitializerService.initializeTracker()`.\n *\n * @deprecated Use `'deferred'` instead\n */\n AUTO_DEFERRED,\n}\n\nexport function coerceInitializationMode(\n value: MatomoInitializationBehaviorInput,\n): MatomoInitializationBehavior {\n switch (value) {\n case MatomoInitializationMode.AUTO:\n return 'auto';\n case MatomoInitializationMode.MANUAL:\n return 'manual';\n case MatomoInitializationMode.AUTO_DEFERRED:\n return 'deferred';\n default:\n return value;\n }\n}\n\nexport type MatomoConsentRequirement = 'none' | 'cookie' | 'tracking';\n\n/** @deprecated Use {@link MatomoConsentRequirement} instead */\nexport enum MatomoConsentMode {\n /** Do not require any consent, always track users */\n NONE,\n /** Require cookie consent */\n COOKIE,\n /** Require tracking consent */\n TRACKING,\n}\n\nexport function coerceConsentRequirement(\n value: MatomoConsentMode | MatomoConsentRequirement,\n): MatomoConsentRequirement {\n switch (value) {\n case MatomoConsentMode.NONE:\n return 'none';\n case MatomoConsentMode.COOKIE:\n return 'cookie';\n case MatomoConsentMode.TRACKING:\n return 'tracking';\n default:\n return value;\n }\n}\n\nexport interface MatomoTrackerConfiguration {\n /** Matomo site id */\n siteId: number | string;\n\n /** Matomo server url */\n trackerUrl: string;\n\n /** The trackerUrlSuffix is always appended to the trackerUrl. It defaults to matomo.php */\n trackerUrlSuffix?: string;\n}\n\nexport interface MultiTrackersConfiguration {\n /**\n * Configure multiple tracking servers. <b>Order matters: if no custom script url is\n * provided, Matomo script will be downloaded from first tracker.</b>\n */\n trackers: MatomoTrackerConfiguration[];\n}\n\nexport interface BaseMatomoConfiguration {\n /** Set to `true` to disable tracking */\n disabled?: boolean;\n\n /** If `true`, track a page view when app loads (default `false`) */\n trackAppInitialLoad?: boolean;\n\n /**\n * Configure link clicks tracking\n *\n * If `true` (the default value), enable link tracking, excluding middle-clicks and contextmenu events.\n * If `enable-pseudo`, enable link tracking, including middle-clicks and contextmenu events.\n * If `false`, to disable this Matomo feature (default `true`).\n *\n * Used when {@link trackAppInitialLoad} is `true` and when automatic page tracking is enabled.\n *\n * @see {@link MatomoTracker.enableLinkTracking} for more details\n */\n enableLinkTracking?: boolean | 'enable-pseudo';\n\n /** Set to `true` to not track users who opt out of tracking using <i>Do Not Track</i> setting */\n acceptDoNotTrack?: boolean;\n\n /**\n * Configure user consent requirement\n *\n * To identify whether you need to ask for any consent, you need to determine whether your lawful\n * basis for processing personal data is \"Consent\" or \"Legitimate interest\", or whether you can\n * avoid collecting personal data altogether.\n *\n * Matomo differentiates between cookie and tracking consent:\n * - In the context of <b>tracking consent</b> no cookies will be used and no tracking request\n * will be sent unless consent was given. As soon as consent was given, tracking requests will\n * be sent and cookies will be used.\n * - In the context of <b>cookie consent</b> tracking requests will always be sent. However,\n * cookies will be only used if consent for storing and using cookies was given by the user.\n *\n * Note that cookies impact reports accuracy.\n *\n * See Matomo guide: {@link https://developer.matomo.org/guides/tracking-consent}\n */\n requireConsent?: MatomoConsentRequirement | MatomoConsentMode;\n\n /** Set to `true` to enable Javascript errors tracking as <i>events</i> (with category <i>JavaScript Errors</i>) */\n enableJSErrorTracking?: boolean;\n\n /** Set to `true` to run matomo calls outside of angular NgZone. This may fix angular freezes. This has no effect in zoneless applications. */\n runOutsideAngularZone?: boolean;\n\n /**\n * Set to `true` to avoid sending campaign parameters\n *\n * By default, Matomo will send campaign parameters (mtm, utm, etc.) to the tracker and record that information.\n * Some privacy regulations may not allow for this information to be collected.\n *\n * <b>This is available as of Matomo 5.1 only.</b>\n */\n disableCampaignParameters?: boolean;\n\n /** Set an array of query parameters to be excluded if in the url */\n excludedQueryParams?: string[];\n}\n\n/**\n * Mapping type to extend input types with legacy `MatomoInitializationMode` type\n *\n * TODO remove when `MatomoInitializationMode` is removed\n */\nexport interface MatomoInitializationBehaviorInputMapping {\n manual: MatomoInitializationMode.MANUAL;\n auto: MatomoInitializationMode.AUTO;\n deferred: MatomoInitializationMode.AUTO_DEFERRED;\n}\n\n/**\n * Special type to map a `MatomoInitializationBehavior` to either itself or the legacy equivalent `MatomoInitializationMode`\n *\n * @example\n * MatomoInitializationBehaviorInput<'auto'>\n * // Equivalent to:\n * 'auto' | MatomoInitializationMode.AUTO\n *\n * MatomoInitializationBehaviorInput<'manual'>\n * // Equivalent to:\n * 'manual' | MatomoInitializationMode.MANUAL\n *\n * MatomoInitializationBehaviorInput<'deferred'>\n * // Equivalent to:\n * 'deferred' | MatomoInitializationMode.AUTO_DEFERRED\n *\n * TODO remove when `MatomoInitializationMode` is removed and use `MatomoInitializationBehavior` instead\n */\nexport type MatomoInitializationBehaviorInput<\n T extends MatomoInitializationBehavior = MatomoInitializationBehavior,\n> = T | MatomoInitializationBehaviorInputMapping[T];\n\nexport interface BaseAutoMatomoConfiguration<M extends 'auto' | 'deferred' = 'auto'> {\n /**\n * Set the script initialization mode (default is `'auto'`)\n *\n * @see MatomoInitializationBehavior\n */\n mode?: MatomoInitializationBehaviorInput<M>;\n\n /** Matomo script url (default is `matomo.js` appended to main tracker url) */\n scriptUrl: string;\n}\n\ntype Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };\ntype XOR<T, U> = T | U extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U;\ntype XOR3<T, U, V> = XOR<T, XOR<U, V>>;\n\nexport type ManualMatomoConfiguration = {\n /**\n * Set the script initialization mode (default is `'auto'`)\n *\n * @see MatomoInitializationBehavior\n */\n mode: MatomoInitializationBehaviorInput<'manual'>;\n};\n\nexport type DeferredMatomoConfiguration = {\n /**\n * Set the script initialization mode (default is `'auto'`)\n *\n * @see MatomoInitializationBehavior\n */\n mode: MatomoInitializationBehaviorInput<'deferred'>;\n};\n\nexport type ExplicitAutoConfiguration<M extends 'auto' | 'deferred' = 'auto'> = Partial<\n BaseAutoMatomoConfiguration<M>\n> &\n XOR<MatomoTrackerConfiguration, MultiTrackersConfiguration>;\n\nexport type EmbeddedAutoConfiguration<M extends 'auto' | 'deferred' = 'auto'> =\n BaseAutoMatomoConfiguration<M> & Partial<MultiTrackersConfiguration>;\n\nexport type AutoMatomoConfiguration<M extends 'auto' | 'deferred' = 'auto'> = XOR<\n ExplicitAutoConfiguration<M>,\n EmbeddedAutoConfiguration<M>\n>;\n\nexport type MatomoConfiguration = BaseMatomoConfiguration &\n XOR3<AutoMatomoConfiguration, ManualMatomoConfiguration, DeferredMatomoConfiguration>;\n\nexport function isAutoConfigurationMode(\n config: MatomoConfiguration | InternalMatomoConfiguration,\n): config is AutoMatomoConfiguration {\n return (\n config.mode == null || config.mode === 'auto' || config.mode === MatomoInitializationMode.AUTO\n );\n}\n\nfunction hasMainTrackerConfiguration<M extends 'auto' | 'deferred'>(\n config: AutoMatomoConfiguration<M>,\n): config is ExplicitAutoConfiguration<M> {\n // If one is undefined, both should be\n return config.siteId != null && config.trackerUrl != null;\n}\n\nexport function isEmbeddedTrackerConfiguration<M extends 'auto' | 'deferred'>(\n config: AutoMatomoConfiguration<M>,\n): config is EmbeddedAutoConfiguration<M> {\n return config.scriptUrl != null && !hasMainTrackerConfiguration(config);\n}\n\nexport function isExplicitTrackerConfiguration<M extends 'auto' | 'deferred'>(\n config: AutoMatomoConfiguration<M>,\n): config is ExplicitAutoConfiguration<M> {\n return hasMainTrackerConfiguration(config) || isMultiTrackerConfiguration(config);\n}\n\nexport function isMultiTrackerConfiguration(\n config: AutoMatomoConfiguration<'auto' | 'deferred'>,\n): config is MultiTrackersConfiguration {\n return Array.isArray(config.trackers);\n}\n\nexport function getTrackersConfiguration(\n config: ExplicitAutoConfiguration<'auto' | 'deferred'>,\n): MatomoTrackerConfiguration[] {\n return isMultiTrackerConfiguration(config)\n ? config.trackers\n : [\n {\n trackerUrl: config.trackerUrl,\n siteId: config.siteId,\n trackerUrlSuffix: config.trackerUrlSuffix,\n },\n ];\n}\n","declare let window: MatomoHolder;\n\nexport interface MatomoHolder extends Window {\n _paq: { push: Array<unknown>['push'] };\n}\n\nexport function initializeMatomoHolder() {\n window._paq = window._paq || [];\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport { inject, Service, NgZone, PLATFORM_ID } from '@angular/core';\nimport { initializeMatomoHolder, MatomoHolder } from '../holder';\nimport { Getters, NonEmptyArray, PrefixedType } from '../utils/types';\nimport { INTERNAL_MATOMO_CONFIGURATION } from './configuration';\n\ndeclare const window: MatomoHolder;\n\ntype ReturnType<T> = T extends (...args: any) => infer R ? R : any;\n\nfunction trimTrailingUndefinedElements<T>(array: T[]): T[] {\n const trimmed = [...array];\n\n while (trimmed.length > 0 && trimmed[trimmed.length - 1] === undefined) {\n trimmed.pop();\n }\n\n return trimmed;\n}\n\nexport type InternalMatomoTrackerType = Pick<\n InternalMatomoTracker<unknown, string>,\n 'get' | 'push' | 'pushFn'\n>;\n\nexport function createInternalMatomoTracker(): InternalMatomoTrackerType {\n const disabled = inject(INTERNAL_MATOMO_CONFIGURATION).disabled;\n const isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n return disabled || !isBrowser ? new NoopMatomoTracker() : new InternalMatomoTracker();\n}\n\n@Service({ autoProvided: false })\nexport class InternalMatomoTracker<MATOMO, PREFIX extends string = ''> {\n private readonly ngZone = inject(NgZone);\n private readonly config = inject(INTERNAL_MATOMO_CONFIGURATION);\n\n constructor() {\n initializeMatomoHolder();\n }\n\n /** Asynchronously call provided method name on matomo tracker instance */\n get<K extends Getters<PrefixedType<MATOMO, PREFIX>>>(\n getter: K extends keyof PrefixedType<MATOMO, PREFIX> ? K : never,\n ): Promise<ReturnType<PrefixedType<MATOMO, PREFIX>[K]>> {\n return this.pushFn(matomo => (matomo[getter as keyof PrefixedType<MATOMO, PREFIX>] as any)());\n }\n\n pushFn<T>(fn: (matomo: PrefixedType<MATOMO, PREFIX>) => T): Promise<T> {\n return new Promise(resolve => {\n this.push([\n function (this: PrefixedType<MATOMO, PREFIX>): void {\n resolve(fn(this));\n },\n ]);\n });\n }\n\n push(args: NonEmptyArray<unknown>): void {\n if (this.config.runOutsideAngularZone) {\n this.ngZone.runOutsideAngular(() => {\n window._paq.push(trimTrailingUndefinedElements(args));\n });\n } else {\n window._paq.push(trimTrailingUndefinedElements(args));\n }\n }\n}\n\n@Service({ autoProvided: false })\nexport class NoopMatomoTracker<\n MATOMO = unknown,\n PREFIX extends string = '',\n> implements InternalMatomoTrackerType {\n /** Asynchronously call provided method name on matomo tracker instance */\n async get<K extends keyof PrefixedType<MATOMO, PREFIX>>(_: K): Promise<never> {\n return Promise.reject('MatomoTracker is disabled');\n }\n\n push(_: unknown[]): void {\n // No-op\n }\n\n async pushFn<T>(_: (matomo: PrefixedType<MATOMO, PREFIX>) => T): Promise<T> {\n return Promise.reject('MatomoTracker is disabled');\n }\n}\n","import { ApplicationInitStatus, inject, Service, Provider } from '@angular/core';\nimport {\n InternalMatomoTracker,\n InternalMatomoTrackerType,\n} from '../tracker/internal-matomo-tracker.service';\nimport { PrefixedType } from '../utils/types';\n\nexport function provideTestingTracker(): Provider[] {\n return [\n MatomoTestingTracker,\n {\n provide: InternalMatomoTracker,\n useExisting: MatomoTestingTracker,\n },\n ];\n}\n\n@Service({ autoProvided: false })\nexport class MatomoTestingTracker<\n MATOMO = unknown,\n PREFIX extends string = '',\n> implements InternalMatomoTrackerType {\n private readonly initStatus = inject(ApplicationInitStatus);\n\n /** Get list of all calls until initialization */\n callsOnInit: unknown[][] = [];\n /** Get list of all calls after initialization */\n callsAfterInit: unknown[][] = [];\n\n /** Get a copy of all calls since application startup */\n get calls(): unknown[] {\n return [...this.callsOnInit, ...this.callsAfterInit];\n }\n\n countCallsAfterInit(command: string): number {\n return this.callsAfterInit.filter(call => call[0] === command).length;\n }\n\n reset() {\n this.callsOnInit = [];\n this.callsAfterInit = [];\n }\n\n /** Asynchronously call provided method name on matomo tracker instance */\n async get<K extends keyof PrefixedType<MATOMO, PREFIX>>(_: K): Promise<never> {\n return Promise.reject('MatomoTracker is disabled');\n }\n\n push(arg: unknown[]): void {\n if (this.initStatus.done) {\n this.callsAfterInit.push(arg);\n } else {\n this.callsOnInit.push(arg);\n }\n }\n\n async pushFn<T>(_: (matomo: PrefixedType<MATOMO, PREFIX>) => T): Promise<T> {\n return Promise.reject('MatomoTracker is disabled');\n }\n}\n","export const ALREADY_INJECTED_ERROR = 'Matomo trackers have already been initialized';\nexport const ALREADY_INITIALIZED_ERROR = 'Matomo has already been initialized';\n","import { DestroyRef, inject, Service } from '@angular/core';\nimport { Subject } from 'rxjs';\nimport { NonEmptyReadonlyArray, RequireAtLeastOne } from '../utils/types';\nimport { InternalMatomoTracker } from './internal-matomo-tracker.service';\n\nexport interface MatomoECommerceItem {\n productSKU: string;\n productName?: string;\n productCategory?: string;\n price?: number;\n quantity?: number;\n}\n\nexport type MatomoECommerceItemView = Omit<MatomoECommerceItem, 'quantity'>;\nexport type MatomoECommerceCategoryView = Required<Pick<MatomoECommerceItem, 'productCategory'>>;\nexport type MatomoECommerceView = MatomoECommerceItemView | MatomoECommerceCategoryView;\n\nexport type PagePerformanceTimings = RequireAtLeastOne<{\n networkTimeInMs?: number;\n serverTimeInMs?: number;\n transferTimeInMs?: number;\n domProcessingTimeInMs?: number;\n domCompletionTimeInMs?: number;\n onloadTimeInMs?: number;\n}>;\n\nfunction isECommerceCategoryView(\n param: string | MatomoECommerceView,\n): param is MatomoECommerceCategoryView {\n return (\n typeof param === 'object' && Object.keys(param).length === 1 && param.productCategory != null\n );\n}\n\nfunction isECommerceItemView(\n param: string | MatomoECommerceView,\n): param is MatomoECommerceItemView {\n return typeof param === 'object' && 'productSKU' in param;\n}\n\n/** Matomo's internal tracker instance */\nexport interface MatomoInstance {\n getMatomoUrl(): string;\n\n getPiwikUrl(): string;\n\n getCurrentUrl(): string;\n\n getLinkTrackingTimer(): number;\n\n getVisitorId(): string;\n\n // see https://github.com/matomo-org/matomo/blob/3cee577117ad92db739dd3f22571520207eca02e/js/piwik.js#L3306\n getVisitorInfo(): unknown[];\n\n /**\n *\n * @return array of getAttributionCampaignName, getAttributionCampaignKeyword, getAttributionReferrerTimestamp, getAttributionReferrerUrl\n */\n getAttributionInfo(): string[];\n\n getAttributionCampaignName(): string;\n\n getAttributionCampaignKeyword(): string;\n\n getAttributionReferrerTimestamp(): string;\n\n getAttributionReferrerUrl(): string;\n\n getUserId(): string;\n\n getPageViewId(): string;\n\n getCustomData(): unknown;\n\n getCustomVariable(index: number, scope: string): string;\n\n getCustomDimension(customDimensionId: number): string;\n\n getEcommerceItems(): MatomoECommerceItem[];\n\n hasCookies(): boolean;\n\n getCrossDomainLinkingUrlParameter(): string;\n\n hasRememberedConsent(): boolean;\n\n hasConsent(): boolean;\n\n getRememberedConsent(): number | string;\n\n getRememberedCookieConsent(): number | string;\n\n isConsentRequired(): boolean;\n\n areCookiesEnabled(): boolean;\n\n isUserOptedOut(): boolean;\n\n getCustomPagePerformanceTiming(): string;\n\n getExcludedReferrers(): string[];\n\n getIgnoreCampaignsForReferrers(): string[];\n}\n\n@Service({ autoProvided: false })\nexport class MatomoTracker {\n private readonly delegate: InternalMatomoTracker<MatomoInstance> = inject(\n InternalMatomoTracker<MatomoInstance>,\n );\n\n private readonly _pageViewTracked = new Subject<void>();\n\n readonly pageViewTracked = this._pageViewTracked.asObservable();\n\n constructor() {\n inject(DestroyRef).onDestroy(() => this._pageViewTracked.complete());\n }\n\n /**\n * Logs a visit to this page.\n *\n * @param [customTitle] Optional title of the visited page.\n */\n trackPageView(customTitle?: string): void {\n this.delegate.push(['trackPageView', customTitle]);\n this._pageViewTracked.next();\n }\n\n /**\n * Logs an event with an event category (Videos, Music, Games…), an event action (Play, Pause, Duration,\n * Add Playlist, Downloaded, Clicked…), and an optional event name and optional numeric value.\n *\n * @param category Category of the event.\n * @param action Action of the event.\n * @param [name] Optional name of the event.\n * @param [value] Optional value for the event.\n * @param [customData] Optional custom data for the event.\n */\n trackEvent(\n category: string,\n action: string,\n name?: string,\n value?: number,\n customData?: unknown,\n ): void {\n this.delegate.push(['trackEvent', category, action, name, value, customData]);\n }\n\n /**\n * Logs an internal site search for a specific keyword, in an optional category,\n * specifying the optional count of search results in the page.\n *\n * @param keyword Keywords of the search query.\n * @param [category] Optional category of the search query.\n * @param [resultsCount] Optional number of results returned by the search query.\n * @param [customData] Optional custom data for the search query.\n */\n trackSiteSearch(\n keyword: string,\n category?: string,\n resultsCount?: number,\n customData?: unknown,\n ): void {\n this.delegate.push(['trackSiteSearch', keyword, category, resultsCount, customData]);\n }\n\n /**\n * Manually logs a conversion for the numeric goal ID, with an optional numeric custom revenue customRevenue.\n *\n * @param idGoal numeric ID of the goal to log a conversion for.\n * @param [customRevenue] Optional custom revenue to log for the goal.\n * @param [customData] Optional custom data for the goal.\n */\n trackGoal(idGoal: number, customRevenue?: number, customData?: unknown): void {\n this.delegate.push(['trackGoal', idGoal, customRevenue, customData]);\n }\n\n /**\n * Manually logs a click from your own code.\n *\n * @param url Full URL which is to be tracked as a click.\n * @param linkType Either 'link' for an outlink or 'download' for a download.\n * @param [customData] Optional custom data for the link.\n */\n trackLink(url: string, linkType: 'link' | 'download', customData?: unknown): void {\n this.delegate.push(['trackLink', url, linkType, customData]);\n }\n\n /**\n * Scans the entire DOM for all content blocks and tracks all impressions once the DOM ready event has been triggered.\n *\n */\n trackAllContentImpressions(): void {\n this.delegate.push(['trackAllContentImpressions']);\n }\n\n /**\n * Scans the entire DOM for all content blocks as soon as the page is loaded.<br />\n * It tracks an impression only if a content block is actually visible.\n *\n * @param checkOnScroll If true, checks for new content blocks while scrolling the page.\n * @param timeInterval Duration, in milliseconds, between two checks upon scroll.\n */\n trackVisibleContentImpressions(checkOnScroll: boolean, timeInterval: number): void {\n this.delegate.push(['trackVisibleContentImpressions', checkOnScroll, timeInterval]);\n }\n\n /**\n * Scans the given DOM node and its children for content blocks and tracks an impression for them\n * if no impression was already tracked for it.\n *\n * @param node DOM node in which to look for content blocks which have not been previously tracked.\n */\n trackContentImpressionsWithinNode(node: Node): void {\n this.delegate.push(['trackContentImpressionsWithinNode', node]);\n }\n\n /**\n * Tracks an interaction with the given DOM node/content block.\n *\n * @param node DOM node for which to track a content interaction.\n * @param contentInteraction Name of the content interaction.\n */\n trackContentInteractionNode(node: Node, contentInteraction: string): void {\n this.delegate.push(['trackContentInteractionNode', node, contentInteraction]);\n }\n\n /**\n * Tracks a content impression using the specified values.\n *\n * @param contentName Content name.\n * @param contentPiece Content piece.\n * @param contentTarget Content target.\n */\n trackContentImpression(contentName: string, contentPiece: string, contentTarget: string): void {\n this.delegate.push(['trackContentImpression', contentName, contentPiece, contentTarget]);\n }\n\n /**\n * Tracks a content interaction using the specified values.\n *\n * @param contentInteraction Content interaction.\n * @param contentName Content name.\n * @param contentPiece Content piece.\n * @param contentTarget Content target.\n */\n trackContentInteraction(\n contentInteraction: string,\n contentName: string,\n contentPiece: string,\n contentTarget: string,\n ): void {\n this.delegate.push([\n 'trackContentInteraction',\n contentInteraction,\n contentName,\n contentPiece,\n contentTarget,\n ]);\n }\n\n /**\n * Logs all found content blocks within a page to the console. This is useful to debug / test content tracking.\n */\n logAllContentBlocksOnPage(): void {\n this.delegate.push(['logAllContentBlocksOnPage']);\n }\n\n /**\n * Send a ping request\n * <p>\n * Ping requests do not track new actions.\n * If they are sent within the standard visit length, they will update the existing visit time.\n * If sent after the standard visit length, ping requests will be ignored.\n * See also {@link #enableHeartBeatTimer enableHeartBeatTimer()}.\n *\n * @see enableHeartBeatTimer\n */\n ping(): void {\n this.delegate.push(['ping']);\n }\n\n /**\n * Install a Heart beat timer that will regularly send requests to Matomo in order to better measure the time spent on the page.<br />\n * These requests will be sent only when the user is actively viewing the page (when the tab is active and in focus).<br />\n * These requests will not track additional actions or page views.<br />\n * By default, the delay is set to 15 seconds.\n *\n * @param delay Delay, in seconds, between two heart beats to the server.\n */\n enableHeartBeatTimer(delay: number): void {\n this.delegate.push(['enableHeartBeatTimer', delay]);\n }\n\n /**\n * Disable heartbeat if it was previously activated\n */\n disableHeartBeatTimer(): void {\n this.delegate.push(['disableHeartBeatTimer']);\n }\n\n /**\n * Installs link tracking on all applicable link elements.\n *\n * @param usePseudoClickHandler Set to `true` to use pseudo click-handler (treat middle click and open contextmenu as\n * left click).<br />\n * A right click (or any click that opens the context menu) on a link will be tracked as clicked even if \"Open in new tab\"\n * is not selected.<br />\n * If \"false\" (default), nothing will be tracked on open context menu or middle click.\n */\n enableLinkTracking(usePseudoClickHandler = false): void {\n this.delegate.push(['enableLinkTracking', usePseudoClickHandler]);\n }\n\n /** Disables page performance tracking */\n disablePerformanceTracking(): void {\n this.delegate.push(['disablePerformanceTracking']);\n }\n\n /**\n * Enables cross domain linking. By default, the visitor ID that identifies a unique visitor is stored in the browser's\n * first party cookies.<br />\n * This means the cookie can only be accessed by pages on the same domain.<br />\n * If you own multiple domains and would like to track all the actions and pageviews of a specific visitor into the same visit,\n * you may enable cross domain linking.<br />\n * Whenever a user clicks on a link it will append a URL parameter pk_vid to the clicked URL which forwards the current\n * visitor ID value to the page of the different domain.\n *\n */\n enableCrossDomainLinking(): void {\n this.delegate.push(['enableCrossDomainLinking']);\n }\n\n /**\n * By default, the two visits across domains will be linked together when the link is clicked and the page is loaded within\n * a 180 seconds timeout window.\n *\n * @param timeout Timeout, in seconds, between two actions across two domains before creating a new visit.\n */\n setCrossDomainLinkingTimeout(timeout: number): void {\n this.delegate.push(['setCrossDomainLinkingTimeout', timeout]);\n }\n\n /**\n * Get the query parameter to append to links to handle cross domain linking.\n *\n * Use this to add cross domain support for links that are added to the DOM dynamically\n */\n getCrossDomainLinkingUrlParameter(): Promise<string> {\n return this.delegate.get('getCrossDomainLinkingUrlParameter');\n }\n\n /**\n * Set array of referrers where campaign parameters should be ignored\n */\n setIgnoreCampaignsForReferrers(referrers: string | string[]): void {\n this.delegate.push(['setIgnoreCampaignsForReferrers', referrers]);\n }\n\n /**\n * Get array of referrers where campaign parameters should be ignored\n */\n getIgnoreCampaignsForReferrers(): Promise<string[]> {\n return this.delegate.get('getIgnoreCampaignsForReferrers');\n }\n\n /**\n * Overrides document.title\n *\n * @param title Title of the document.\n */\n setDocumentTitle(title: string): void {\n this.delegate.push(['setDocumentTitle', title]);\n }\n\n /**\n * Set array of hostnames or domains to be treated as local.<br />\n * For wildcard subdomains, you can use: `setDomains('.example.com')`; or `setDomains('*.example.com');`.<br />\n * You can also specify a path along a domain: `setDomains('*.example.com/subsite1');`.\n *\n * @param domains List of hostnames or domains, with or without path, to be treated as local.\n */\n setDomains(domains: string[]): void {\n this.delegate.push(['setDomains', domains]);\n }\n\n /**\n * Override the page's reported URL.\n *\n * @param url URL to be reported for the page.\n */\n setCustomUrl(url: string): void {\n this.delegate.push(['setCustomUrl', url]);\n }\n\n /**\n * Overrides the detected Http-Referer.\n *\n * @param url URL to be reported for the referer.\n */\n setReferrerUrl(url: string): void {\n this.delegate.push(['setReferrerUrl', url]);\n }\n\n /**\n * Specifies the website ID.<br />\n * Redundant: can be specified in getTracker() constructor.\n *\n * @param siteId Site ID for the tracker.\n */\n setSiteId(siteId: number | string): void {\n this.delegate.push(['setSiteId', siteId]);\n }\n\n /**\n * Specify the Matomo HTTP API URL endpoint. Points to the root directory of matomo,\n * e.g. https://matomo.example.org/ or https://example.org/matomo/.\n *\n * This function is only useful when the 'Overlay' report is not working. By default, you do not need to use this function.\n *\n * @deprecated use `setAPIUrl` instead\n */\n setApiUrl(url: string): void {\n this.setAPIUrl(url);\n }\n\n /**\n * Set the URL of the Matomo API. It is used for Page Overlay.\n *\n * This method should only be called when the API URL differs from the tracker URL.\n * By default, you do not need to use this function.\n *\n * @param url URL for the Matomo API.\n */\n setAPIUrl(url: string): void {\n this.delegate.push(['setAPIUrl', url]);\n }\n\n /**\n * Specifies the Matomo server URL.<br />\n * Redundant: can be specified in getTracker() constructor.\n *\n * @param url URL for the Matomo server.\n */\n setTrackerUrl(url: string): void {\n this.delegate.push(['setTrackerUrl', url]);\n }\n\n /**\n * Register an additional Matomo server<br />\n * Redundant: can be specified in getTracker() constructor.\n *\n * @param url URL for the Matomo server.\n * @param siteId Site ID for the tracker\n */\n addTracker(url: string, siteId: number | string): void {\n this.delegate.push(['addTracker', url, siteId]);\n }\n\n /**\n * Returns the Matomo server URL.\n *\n * @returns Promise for the Matomo server URL.\n */\n getMatomoUrl(): Promise<string> {\n return this.delegate.get('getMatomoUrl');\n }\n\n /** @deprecated use `getMatomoUrl` instead */\n getPiwikUrl(): Promise<string> {\n return this.delegate.get('getPiwikUrl');\n }\n\n /**\n * Returns the current url of the page that is currently being visited.<br />\n * If a custom URL was set before calling this method, the custom URL will be returned.\n *\n * @returns Promise for the URL of the current page.\n */\n getCurrentUrl(): Promise<string> {\n return this.delegate.get('getCurrentUrl');\n }\n\n /**\n * Set classes to be treated as downloads (in addition to piwik_download).\n *\n * @param classes Class, or list of classes to be treated as downloads.\n */\n setDownloadClasses(classes: string | string[]): void {\n this.delegate.push(['setDownloadClasses', classes]);\n }\n\n /**\n * Set list of file extensions to be recognized as downloads.<br />\n * Example: `'docx'` or `['docx', 'xlsx']`.\n *\n * @param extensions Extension, or list of extensions to be recognized as downloads.\n */\n setDownloadExtensions(extensions: string | string[]): void {\n this.delegate.push(['setDownloadExtensions', extensions]);\n }\n\n /**\n * Set additional file extensions to be recognized as downloads.<br />\n * Example: `'docx'` or `['docx', 'xlsx']`.\n *\n * @param extensions Extension, or list of extensions to be recognized as downloads.\n */\n addDownloadExtensions(extensions: string | string[]): void {\n this.delegate.push(['addDownloadExtensions', extensions]);\n }\n\n /**\n * Set file extensions to be removed from the list of download file extensions.<br />\n * Example: `'docx'` or `['docx', 'xlsx']`.\n *\n * @param extensions Extension, or list of extensions not to be recognized as downloads.\n */\n removeDownloadExtensions(extensions: string | string[]): void {\n this.delegate.push(['removeDownloadExtensions', extensions]);\n }\n\n /**\n * Set classes to be ignored if present in link (in addition to piwik_ignore).\n *\n * @param classes Class, or list of classes to be ignored if present in link.\n */\n setIgnoreClasses(classes: string | string[]): void {\n this.delegate.push(['setIgnoreClasses', classes]);\n }\n\n /**\n * Set classes to be treated as outlinks (in addition to piwik_link).\n *\n * @param classes Class, or list of classes to be treated as outlinks.\n */\n setLinkClasses(classes: string | string[]): void {\n this.delegate.push(['setLinkClasses', classes]);\n }\n\n /**\n * Set delay for link tracking (in milliseconds).\n *\n * @param delay Delay, in milliseconds, for link tracking.\n */\n setLinkTrackingTimer(delay: number): void {\n this.delegate.push(['setLinkTrackingTimer', delay]);\n }\n\n /**\n * Returns delay for link tracking.\n *\n * @returns Promise for the delay in milliseconds.\n */\n getLinkTrackingTimer(): Promise<number> {\n return this.delegate.get('getLinkTrackingTimer');\n }\n\n /**\n * Set to true to not record the hash tag (anchor) portion of URLs.\n *\n * @param value If true, the hash tag portion of the URLs won't be recorded.\n */\n discardHashTag(value: boolean): void {\n this.delegate.push(['discardHashTag', value]);\n }\n\n /**\n * By default, Matomo uses the browser DOM Timing API to accurately determine the time it takes to generate and download\n * the page. You may overwrite this value with this function.\n *\n * <b>This feature has been deprecated since Matomo 4. Any call will be ignored with Matomo 4. Use {@link setPagePerformanceTiming setPagePerformanceTiming()} instead.</b>\n *\n * @param generationTime Time, in milliseconds, of the page generation.\n */\n setGenerationTimeMs(generationTime: number): void {\n this.delegate.push(['setGenerationTimeMs', generationTime]);\n }\n\n /**\n * Manually set performance metrics in milliseconds in a Single Page App or when Matomo cannot detect some metrics.\n *\n * You can set parameters to undefined if you do not want to track this metric. At least one parameter needs to be set.\n * The set performance timings will be tracked only on the next page view. If you track another page view then you will need to set the performance timings again.\n *\n * <b>Requires Matomo 4.5 or newer.</b>\n *\n */\n setPagePerformanceTiming(timings: PagePerformanceTimings): void;\n /**\n * Manually set performance metrics in milliseconds in a Single Page App or when Matomo cannot detect some metrics.\n *\n * You can set parameters to undefined if you do not want to track this metric. At least one parameter needs to be set.\n * The set performance timings will be tracked only on the next page view. If you track another page view then you will need to set the performance timings again.\n *\n * <b>Requires Matomo 4.5 or newer.</b>\n *\n */\n setPagePerformanceTiming(\n networkTimeInMs: number | undefined,\n serverTimeInMs?: number,\n transferTimeInMs?: number,\n domProcessingTimeInMs?: number,\n domCompletionTimeInMs?: number,\n onloadTimeInMs?: number,\n ): void;\n setPagePerformanceTiming(\n networkTimeInMsOrTimings: PagePerformanceTimings | number | undefined,\n serverTimeInMs?: number,\n transferTimeInMs?: number,\n domProcessingTimeInMs?: number,\n domCompletionTimeInMs?: number,\n onloadTimeInMs?: number,\n ): void {\n let networkTimeInMs: number | undefined;\n\n if (typeof networkTimeInMsOrTimings === 'object' && !!networkTimeInMsOrTimings) {\n networkTimeInMs = networkTimeInMsOrTimings.networkTimeInMs;\n serverTimeInMs = networkTimeInMsOrTimings.serverTimeInMs;\n transferTimeInMs = networkTimeInMsOrTimings.transferTimeInMs;\n domProcessingTimeInMs = networkTimeInMsOrTimings.domProcessingTimeInMs;\n domCompletionTimeInMs = networkTimeInMsOrTimings.domCompletionTimeInMs;\n onloadTimeInMs = networkTimeInMsOrTimings.onloadTimeInMs;\n } else {\n networkTimeInMs = networkTimeInMsOrTimings;\n }\n\n this.delegate.push([\n 'setPagePerformanceTiming',\n networkTimeInMs,\n serverTimeInMs,\n transferTimeInMs,\n domProcessingTimeInMs,\n domCompletionTimeInMs,\n onloadTimeInMs,\n ]);\n }\n\n getCustomPagePerformanceTiming(): Promise<string> {\n return this.delegate.get('getCustomPagePerformanceTiming');\n }\n\n /**\n * Appends a custom string to the end of the HTTP request to matomo.php.\n *\n * @param appendToUrl String to append to the end of the HTTP request to matomo.php.\n */\n appendToTrackingUrl(appendToUrl: string): void {\n this.delegate.push(['appendToTrackingUrl', appendToUrl]);\n }\n\n /** Set to `true` to not track users who opt out of tracking using <i>Do Not Track</i> setting */\n setDoNotTrack(doNotTrack: boolean): void {\n this.delegate.push(['setDoNotTrack', doNotTrack]);\n }\n\n /**\n * Enables a frame-buster to prevent the tracked web page from being framed/iframed.\n */\n killFrame(): void {\n this.delegate.push(['killFrame']);\n }\n\n /**\n * Forces the browser to load the live URL if the tracked web page is loaded from a local file\n * (e.g., saved to someone's desktop).\n *\n * @param url URL to track instead of file:// URLs.\n */\n redirectFile(url: string): void {\n this.delegate.push(['redirectFile', url]);\n }\n\n /**\n * Records how long the page has been viewed if the minimumVisitLength is attained;\n * the heartBeatDelay determines how frequently to update the server.\n *\n * @param minimumVisitLength Duration before notifying the server for the duration of the visit to a page.\n * @param heartBeatDelay Delay, in seconds, between two updates to the server.\n */\n setHeartBeatTimer(minimumVisitLength: number, heartBeatDelay: number): void {\n this.delegate.push(['setHeartBeatTimer', minimumVisitLength, heartBeatDelay]);\n }\n\n /**\n * Returns the 16 characters ID for the visitor.\n *\n * @returns Promise for the the 16 characters ID for the visitor.\n */\n getVisitorId(): Promise<string> {\n return this.delegate.get('getVisitorId');\n }\n\n /**\n * Set the 16 characters ID for the visitor\n * <p/>\n * The visitorId needs to be a 16 digit hex string.\n * It won't be persisted in a cookie and needs to be set on every new page load.\n *\n * @param visitorId a 16 digit hex string\n */\n setVisitorId(visitorId: string): void {\n this.delegate.push(['setVisitorId', visitorId]);\n }\n\n /**\n * Returns the visitor cookie contents in an array.\n *\n * @returns Promise for the cookie contents in an array.\n *\n * TODO better return type\n */\n getVisitorInfo(): Promise<unknown[]> {\n return this.delegate.get('getVisitorInfo');\n }\n\n /**\n * Returns the visitor attribution array (Referer information and/or Campaign name & keyword).<br />\n * Attribution information is used by Matomo to credit the correct referrer (first or last referrer)\n * used when a user triggers a goal conversion.\n *\n * @returns Promise for the visitor attribution array (Referer information and/or Campaign name & keyword).\n */\n getAttributionInfo(): Promise<string[]> {\n return this.delegate.get('getAttributionInfo');\n }\n\n /**\n * Returns the attribution campaign name.\n *\n * @returns Promise for the the attribution campaign name.\n */\n getAttributionCampaignName(): Promise<string> {\n return this.delegate.get('getAttributionCampaignName');\n }\n\n /**\n * Returns the attribution campaign keyword.\n *\n * @returns Promise for the attribution campaign keyword.\n */\n getAttributionCampaignKeyword(): Promise<string> {\n return this.delegate.get('getAttributionCampaignKeyword');\n }\n\n /**\n * Returns the attribution referrer timestamp.\n *\n * @returns Promise for the attribution referrer timestamp (as string).\n */\n getAttributionReferrerTimestamp(): Promise<string> {\n return this.delegate.get('getAttributionReferrerTimestamp');\n }\n\n /**\n * Returns the attribution referrer URL.\n *\n * @returns Promise for the attribution referrer URL\n */\n getAttributionReferrerUrl(): Promise<string> {\n return this.delegate.get('getAttributionReferrerUrl');\n }\n\n /**\n * Returns the User ID string if it was set.\n *\n * @returns Promise for the User ID for the visitor.\n */\n getUserId(): Promise<string> {\n return this.delegate.get('getUserId');\n }\n\n /**\n * Set a User ID to this user (such as an email address or a username).\n *\n * @param userId User ID to set for the current visitor.\n */\n setUserId(userId: string): void {\n this.delegate.push(['setUserId', userId]);\n }\n\n /**\n * Reset the User ID which also generates a new Visitor ID.\n *\n */\n resetUserId(): void {\n this.delegate.push(['resetUserId']);\n }\n\n /**\n * Override PageView id for every use of logPageView() <b>THIS SHOULD PROBABLY NOT BE CALLED IN A SINGLE-PAGE APP!</b>\n *\n * Do not use this if you call trackPageView() multiple times during tracking (e.g. when tracking a single page application)\n *\n * @param pageView\n */\n setPageViewId(pageView: string): void {\n this.delegate.push(['setPageViewId', pageView]);\n }\n\n /**\n * Returns the PageView id. If not set manually using setPageViewId, this method will return t