UNPKG

automatic-analytics-trigger

Version:
186 lines (147 loc) 5.46 kB
'use strict'; var defaultConfig = { mutations: ['show'], events: ['click'], target: document.documentElement || document.body, maxAncestorsChecks: 5, }; const DATASET = 'data-analytics'; class EventListener { constructor(events, target, dispatcheEvent, finder) { this.target = target; this.events = events; this.dispatcheEvent = dispatcheEvent; this.listeners = {}; this.finder = finder; } registerEventsListeners() { for (const event of this.events) { this.listeners[event] = e => this._eventHandler(e, `${DATASET}-${event}`); this.target.addEventListener(event, this.listeners[event], false); } } removeEventsListeners() { for (const event of this.events) { this.target.removeEventListener(event, this.listeners[event], false); } } _eventHandler(event, datasetEvent) { const markedElement = this.finder.firstWithAttribute(event, datasetEvent); if (markedElement == undefined) return; this.dispatcheEvent(markedElement); } } class EventElementFinder { constructor(maxAncestorsChecks = 0) { this.maxAncestorsChecks = maxAncestorsChecks; } firstWithAttribute(event, attribute) { let elements = event.composedPath(); elements = this._removeWindowAndDocumentFromList(elements); elements = this._applyLimitIfNecessary(elements); return elements.find(element => element.hasAttribute(attribute)); } _removeWindowAndDocumentFromList(elements) { return elements.slice(0, elements.length - 2); } _applyLimitIfNecessary(elements) { if (this._notNecessaryApplyLimit(this.maxAncestorsChecks)) { return elements; } return elements.slice(0, this.maxAncestorsChecks); } _notNecessaryApplyLimit(elements) { return this.maxAncestorsChecks == 0 || elements.length <= this.maxAncestorsChecks; } } const DATASET$1 = 'data-analytics'; class MutationListener { constructor(mutations, target, dispatcheEvent) { this.target = target; this.mutations = mutations; this.dispatcheEvent = dispatcheEvent; } registerMutationsListeners() { this.observer = new MutationObserver(this._mutationHandler.bind(this)); this.observer.observe(this.target, { attributes: true, childList: true, subtree: true, attributeFilter: ['style'], }); } removeMutationsListeners() { if (this.observer) { this.observer.disconnect(); this.observer = null; } } _mutationHandler(mutations) { for (let mutation of mutations) { this._checkShowAttributes(mutation); this._checkShowAddedNodes(mutation); } } _checkShowAttributes(mutation) { if (mutation.type === 'attributes' && isShowEvent(mutation.target) && mutation.attributeName === 'style') { if (isVisibleElement(mutation.target)) this.dispatcheEvent(mutation.target); } } _checkShowAddedNodes(mutation) { if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { for (let node of mutation.addedNodes) { if (isShowEvent(node) && isVisibleElement(node)) this.dispatcheEvent(node); } } } } const isShowEvent = node => node.dataset && node.hasAttribute([`${DATASET$1}-show`]); const isVisibleElement = element => element.style.display != 'none' && element.style.visibility != 'hidden'; class AutomaticAnalyticsTrigger { constructor(callback, customConfig) { const config = { ...defaultConfig, ...customConfig }; this.callback = callback; this.target = config.target; this.events = config.events; this.mutations = config.mutations; this.maxAncestorsChecks = config.maxAncestorsChecks; this.finder = new EventElementFinder(this.maxAncestorsChecks); this._dispatcheEventData = this._dispatcheEventData.bind(this); } init() { if (this.events) { this.eventListener = new EventListener(this.events, this.target, this._dispatcheEventData, this.finder); this.eventListener.registerEventsListeners(); } if (this.mutations) { this.mutationListener = new MutationListener(this.mutations, this.target, this._dispatcheEventData); this.mutationListener.registerMutationsListeners(); } } close() { if (this.events) this.eventListener.removeEventsListeners(); if (this.mutations) this.mutationListener.removeMutationsListeners(); } _dispatcheEventData(target) { const eventData = this._getEventDataFromDataAttributes(target.dataset); this.callback(eventData); } _getEventDataFromDataAttributes(dataset) { const dimensions = Object.keys(dataset) .filter(key => key.includes('analyticsDimension')) .reduce((obj, key) => { obj[key.replace('analyticsDimension', 'dimension')] = dataset[key]; return obj; }, {}); return { ...(dataset.analyticsEvent && { event: dataset.analyticsEvent }), ...(dataset.analyticsEventCategory && { eventCategory: dataset.analyticsEventCategory }), ...(dataset.analyticsEventAction && { eventAction: dataset.analyticsEventAction }), ...(dataset.analyticsEventLabel && { eventLabel: dataset.analyticsEventLabel }), ...(dataset.analyticsEventValue && { eventValue: dataset.analyticsEventValue }), ...(dataset.analyticsScreenName && { screenName: dataset.analyticsScreenName }), ...(Object.keys(dimensions).length > 0 && { eventDimensions: dimensions }), }; } } module.exports = AutomaticAnalyticsTrigger;