/**
* Copyright IBM Corp. 2020, 2023
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
import root from 'window-or-global';
/**
* @constant {boolean} scrollTracker determines whether scroll tracking analytics is enabled
* @private
*/
const _scrollTracker =
(process && process.env.SCROLL_TRACKING === 'true') || false;
/**
* Current NODE_ENV
*
* @type {string | string}
* @private
*/
const _env = (process && process.env.NODE_ENV) || 'development';
/**
* Analytics API class with methods for firing analytics events on
* ibm.com
*/
class AnalyticsAPI {
/**
* This method checks that the analytics script has been loaded
* and fires an event to Coremetrics
*
* @param {object} eventData Object with standard IBM metric event properties and values to send to Coremetrics
* @example
* import { AnalyticsAPI } from '@carbon/ibmdotcom-services';
*
* function fireEvent() {
* const eventData = {
* type: 'element',
* primaryCategory: 'MASTHEAD',
* eventName: 'CLICK',
* executionPath: 'masthead__profile',
* execPathReturnCode: 'none',
* targetTitle: 'profile'
* }
* AnalyticsAPI.registerEvent(eventData);
* }
*/
static registerEvent(eventData) {
if (root.ibmStats) {
root.ibmStats.event(eventData);
}
}
/**
* Initializes all analytics global tracking init functions
*/
static initAll() {
this.initScrollTracker();
this.initDynamicTabs();
this.initModals();
}
/**
*
* If scroll tracking is enabled, this method will fire an event for every 400px
* user scrolls down the page. Only the deepest depth will fire the event (e.g if
* user scrolls back up the page, the event will not be triggered)
*
* @example
* import { AnalyticsAPI } from '@carbon/ibmdotcom-services';
*
* function init() {
* AnalyticsAPI.initScrollTracker();
* }
* @deprecated
*/
static initScrollTracker() {
if (_scrollTracker) {
console.warn(
'Scroll tracker service has been deprecated. Please refer to documentation for IBM DBDM gestures 2.0.'
);
}
}
/**
* This instantiates an event listener to trigger an event if the Carbon
* Tabs component is being interacted with by the user
*
* @example
* import { AnalyticsAPI } from '@carbon/ibmdotcom-services';
*
* function init() {
* AnalyticsAPI.initDynamicTabs();
* }
*/
static initDynamicTabs() {
const tabSelected = this.triggerTabSelected.bind(this);
root.document.addEventListener('tab-selected', function (evt) {
tabSelected(evt.target.id, evt.detail.item.innerText);
});
}
/**
* Triggers to CLICK event for the dynamic tabs
*
* @param {string} executionPath Target ID
* @param {string} targetTitle Target innerText
*/
static triggerTabSelected(executionPath, targetTitle) {
try {
this.registerEvent({
type: 'element',
primaryCategory: 'WIDGET',
eventName: 'CLICK',
eventCategoryGroup: 'TABS DYNAMIC',
executionPath: executionPath,
targetTitle: targetTitle,
});
} catch (err) {
if (_env !== 'production') {
console.error('Error triggering tab event:', err);
}
}
}
/**
* This instantiates an event listener to trigger an event if the Carbon
* Modal component is being interacted with by the user
*
* @example
* import { AnalyticsAPI } from '@carbon/ibmdotcom-services';
*
* function init() {
* AnalyticsAPI.initModals();
* }
*/
static initModals() {
const modalHide = this.triggerModalHide.bind(this);
root.document.addEventListener('modal-hidden', function (evt) {
modalHide(evt.target.id, evt.detail.launchingElement.innerText);
});
const modalShow = this.triggerModalShow.bind(this);
root.document.addEventListener('modal-shown', function (evt) {
modalShow(evt.target.id, evt.detail.launchingElement.innerText);
});
}
/**
* Triggers the HIDE event for the modal
*
* @param {string} executionPath Target ID
* @param {string} targetTitle Target innerText
*/
static triggerModalHide(executionPath, targetTitle) {
try {
this.registerEvent({
type: 'element',
primaryCategory: 'WIDGET',
eventName: 'HIDE',
eventCategoryGroup: 'SHOWHIDE',
executionPath: executionPath,
targetTitle: targetTitle,
});
} catch (err) {
if (_env !== 'production') {
console.error('Error triggering modal hide event:', err);
}
}
}
/**
* Triggers the SHOW event for the modal
*
* @param {string} executionPath Target ID
* @param {string} targetTitle Target innerText
*/
static triggerModalShow(executionPath, targetTitle) {
try {
this.registerEvent({
type: 'element',
primaryCategory: 'WIDGET',
eventName: 'SHOW',
eventCategoryGroup: 'SHOWHIDE',
executionPath: executionPath,
targetTitle: targetTitle,
});
} catch (err) {
if (_env !== 'production') {
console.error('Error triggering modal show event:', err);
}
}
}
/**
* Sends video player metrics data
*
* @param {object} data event data from the video player
* @example
* import { AnalyticsAPI } from '@carbon/ibmdotcom-services';
*
*function init() {
* const data = {
* playerType: 'kaltura',
* title: 'Folgers Coffee',
* currentTime: 1,
* duration: 60,
* playerState: 1,
* mediaId: '1_9h94wo6b',
* };
*
* AnalyticsAPI.videoPlayerStats(data);
*}
*/
static videoPlayerStats(data) {
let playerState = data?.playerState || '',
currentTime = Math.floor(data.currentTime),
duration = Math.floor(data.duration),
percentWatched = Math.floor((currentTime / duration) * 100);
// Set nicenames for player states for event.
switch (data.playerState) {
case 0:
playerState = 'launched';
break;
case 1:
playerState = 'paused';
break;
case 2:
playerState = 'played';
break;
case 3:
playerState = 'ended';
break;
case 99:
playerState = 'error';
break;
default:
if (typeof playerState === 'number') {
playerState = '';
}
}
if (currentTime === 0) {
currentTime = 'start';
percentWatched = '0';
}
if (currentTime >= duration || data.playerState === 3) {
currentTime = 'end';
percentWatched = '100';
}
// If went to the end of the video, and fired "pause" event, don't fire pause event b/c it's really
// the end of the video, so just let "end" event fire and tag metrics.
if (currentTime === 'end' && data.playerState === 1) {
return;
}
const eventData = {
type: 'video',
primaryCategory: 'VIDEO',
eventName: data.title,
eventCategoryGroup: data.playerType,
executionPath: data.videoId || data.mediaId,
execPathReturnCode: playerState,
eventVidStatus: data.playerState,
eventVidTimeStamp: currentTime,
eventVidLength: duration,
eventVidPlayed: percentWatched + '%',
};
if (data?.customMetricsData) {
Object.keys(data.customMetricsData).forEach((customMetricsKey) => {
eventData[customMetricsKey] = data.customMetricsData[customMetricsKey];
});
}
try {
this.registerEvent(eventData);
} catch (err) {
if (_env !== 'production') {
console.error('Error firing video metrics:', err);
}
}
}
}
export default AnalyticsAPI;