@analytics/google-analytics
Version:
Google analytics v4 plugin for 'analytics' module
377 lines (309 loc) • 15.4 kB
JavaScript
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
/* global, window */
var loadedInstances = {};
/* Location of gtag script */
var gtagScriptSource = 'https://www.googletagmanager.com/gtag/js'; // See https://developers.google.com/analytics/devguides/collection/ga4/reference/config
var defaultGtagConf = {
// https://support.google.com/analytics/answer/7201382?hl=en#zippy=%2Cglobal-site-tag-websites
debug_mode: false,
/**
* Disable automatic sending of page views, instead let analytics.page() do this
* https://developers.google.com/analytics/devguides/collection/gtagjs
*/
send_page_view: false,
// https://developers.google.com/analytics/devguides/collection/gtagjs/ip-anonymization
anonymize_ip: false,
/**
* Disable All Advertising
* https://developers.google.com/analytics/devguides/collection/ga4/display-features#disable_all_advertising_features
*/
allow_google_signals: true,
/**
* Disable Advertising Personalization
* https://developers.google.com/analytics/devguides/collection/ga4/display-features#disable_advertising_personalization
*/
allow_ad_personalization_signals: true,
/**
* https://developers.google.com/analytics/devguides/collection/gtagjs/cookies-user-id#configure_cookie_field_settings
*/
// cookie_domain: 'auto',
// cookie_expires
// cookie_prefix
// cookie_update
// cookie_flags
/**
* Cookie Flags
* https://developers.google.com/analytics/devguides/collection/ga4/cookies-user-id#cookie_flags
*/
cookie_flags: ''
};
var defaultConfig = {
gtagName: 'gtag',
dataLayerName: 'ga4DataLayer',
measurementIds: [],
gtagConfig: defaultGtagConf
};
/**
* Google analytics plugin
* @link https://getanalytics.io/plugins/google-analytics/
* @link https://analytics.google.com/analytics/web/
* @link https://developers.google.com/analytics/devguides/collection/analyticsjs
* @param {object} pluginConfig - Plugin settings
* @param {string[]} pluginConfig.measurementIds - Google Analytics MEASUREMENT IDs
* @param {boolean} [pluginConfig.debug] - Enable Google Analytics debug mode
* @param {string} [pluginConfig.dataLayerName=ga4DataLayer] - The optional name for dataLayer object. Defaults to ga4DataLayer.
* @param {string} [pluginConfig.gtagName=gtag] - The optional name for dataLayer object. Defaults to `gtag`.
* @param {boolean} [pluginConfig.gtagConfig.anonymize_ip] - Enable [Anonymizing IP addresses](https://bit.ly/3c660Rd) sent to Google Analytics.
* @param {object} [pluginConfig.gtagConfig.cookie_domain] - Additional cookie properties for configuring the [ga cookie](https://developers.google.com/analytics/devguides/collection/analyticsjs/cookies-user-id#configuring_cookie_field_settings)
* @param {object} [pluginConfig.gtagConfig.cookie_expires] - Additional cookie properties for configuring the [ga cookie](https://developers.google.com/analytics/devguides/collection/analyticsjs/cookies-user-id#configuring_cookie_field_settings)
* @param {object} [pluginConfig.gtagConfig.cookie_prefix] - Additional cookie properties for configuring the [ga cookie](https://developers.google.com/analytics/devguides/collection/analyticsjs/cookies-user-id#configuring_cookie_field_settings)
* @param {object} [pluginConfig.gtagConfig.cookie_update] - Additional cookie properties for configuring the [ga cookie](https://developers.google.com/analytics/devguides/collection/analyticsjs/cookies-user-id#configuring_cookie_field_settings)
* @param {object} [pluginConfig.gtagConfig.cookie_flags] - Additional cookie properties for configuring the [ga cookie](https://developers.google.com/analytics/devguides/collection/analyticsjs/cookies-user-id#configuring_cookie_field_settings)
* @param {string} [pluginConfig.customScriptSrc] - Custom URL for google analytics script, if proxying calls
* @param {string} [pluginConfig.nonce] - Content-Security-Policy nonce value
* @return {*}
* @example
*
* googleAnalytics({
* measurementIds: ['G-abc123']
* })
*/
function googleAnalytics() {
var pluginConfig = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var pageCallCount = 0;
var measurementIds = getIds(pluginConfig.measurementIds);
var initConfig = _objectSpread(_objectSpread({}, defaultConfig), pluginConfig);
return {
name: 'google-analytics',
config: initConfig,
// Load gtag.js and define gtag
initialize: function initialize(_ref) {
var config = _ref.config,
instance = _ref.instance;
var dataLayerName = config.dataLayerName,
customScriptSrc = config.customScriptSrc,
gtagName = config.gtagName,
gtagConfig = config.gtagConfig,
debug = config.debug,
nonce = config.nonce;
/* Inject google gtag.js script if not found */
/* If other gtags are loaded already, add ours anyway */
var customLayerName = dataLayerName ? "&l=".concat(dataLayerName) : "";
var src = customScriptSrc || "".concat(gtagScriptSource, "?id=").concat(measurementIds[0]).concat(customLayerName);
if (!scriptLoaded(src)) {
var script = document.createElement('script');
script.async = true;
script.src = src;
if (nonce) {
script.setAttribute('nonce', nonce);
}
document.body.appendChild(script);
}
/* Set up gtag and datalayer */
if (!window[dataLayerName]) {
window[dataLayerName] = window[dataLayerName] || [];
}
if (!window[gtagName]) {
window[gtagName] = function () {
window[dataLayerName].push(arguments);
};
}
window[gtagName]('js', new Date()); // Initialize tracker instances on page
var gtagConf = _objectSpread(_objectSpread({}, defaultGtagConf), gtagConfig ? gtagConfig : {}); // You must explicitly delete the debug_mode parameter or all sessions will fire in debug more. Setting it false is not enough.
// https://support.google.com/analytics/answer/7201382?hl=en&ref_topic=9303319#zippy=%2Cgoogle-tag-websites:~:text=To%20disable%20debug%20mode%2C%20exclude%20the%20%27debug_mode%27%20parameter%3B%20setting%20the%20parameter%20to%20false%20doesn%27t%20disable%20debug%20mode.
if (debug === true) {
gtagConf.debug_mode = true;
} else {
delete gtagConf.debug_mode;
}
/* set custom dimensions from user traits */
var user = instance.user() || {};
var traits = user.traits || {};
if (Object.keys(traits).length) {
window[gtagName]('set', 'user_properties', traits);
}
/* Initialize all measurementIds */
for (var i = 0; i < measurementIds.length; i++) {
if (!loadedInstances[measurementIds[i]]) {
window[gtagName]('config', measurementIds[i], gtagConf);
loadedInstances[measurementIds[i]] = true;
}
}
},
// Set parameter scope at user level with 'set' method
identify: function identify(_ref2) {
var payload = _ref2.payload,
config = _ref2.config;
var gtagName = config.gtagName;
if (!window[gtagName] || !measurementIds.length) return;
if (payload.userId) {
// https://developers.google.com/analytics/devguides/collection/ga4/user-id?platform=websites#send_user_ids
window[gtagName]('set', {
user_id: payload.userId
}); // console.log('Set userid', payload.userId)
} // TODO verify this
// https://developers.google.com/analytics/devguides/collection/ga4/user-properties?technology=websites
if (Object.keys(payload.traits).length) {
/* gtag('set', 'user_properties', {
favorite_composer: 'Mahler',
favorite_instrument: 'double bass',
season_ticketholder: 'true'
}) */
window[gtagName]('set', 'user_properties', payload.traits); // console.log('Set userprops', payload.traits)
}
},
// Set parameter scope at page level with 'config' method
page: function page(_ref3) {
var payload = _ref3.payload,
config = _ref3.config,
instance = _ref3.instance;
var gtagName = config.gtagName,
gtagConfig = config.gtagConfig;
if (!window[gtagName] || !measurementIds.length) return;
var properties = payload.properties;
var send_to = properties.send_to;
var campaign = instance.getState('context.campaign'); // console.log('ga page properties', properties)
/* Create pageview-related properties */
var pageView = {
page_title: properties.title,
page_location: properties.url,
page_path: properties.path || document.location.pathname,
page_hash: properties.hash,
page_search: properties.page_search,
page_referrer: properties.referrer
};
var campaignData = addCampaignData(campaign);
var userId = instance.user('userId');
var finalPayload = _objectSpread(_objectSpread(_objectSpread(_objectSpread({}, send_to ? {
send_to: send_to
} : {}), pageView), campaignData), userId ? {
user_id: userId
} : {});
/* If send_page_view true, ignore first analytics.page call */
if (gtagConfig && gtagConfig.send_page_view && pageCallCount === 0) {
pageCallCount++; // console.log('ignore first pageCallCount', pageCallCount)
return;
} // console.log('Send page_view payload', finalPayload)
window[gtagName]('event', 'page_view', finalPayload); // Set after initial page view
pageCallCount++;
},
// Set parameter scope at event level with 'event' method
track: function track(_ref4) {
var payload = _ref4.payload,
config = _ref4.config,
instance = _ref4.instance;
var properties = payload.properties,
event = payload.event;
var campaign = instance.getState('context.campaign');
var gtagName = config.gtagName;
if (!window[gtagName] || !measurementIds.length) return;
var campaignData = addCampaignData(campaign);
var userId = instance.user('userId'); // Limits https://support.google.com/analytics/answer/9267744
var finalPayload = _objectSpread(_objectSpread(_objectSpread({}, properties), campaignData), userId ? {
user_id: userId
} : {});
/*
console.log('finalPayload', finalPayload)
console.log('event', event)
*/
/* Send data to Google Analytics
Signature gtag('event', '<event_name>', {
<event_params>key: value,
})
*/
window[gtagName]('event', event, finalPayload);
},
/* Verify gtag loaded and ready to use */
loaded: function loaded() {
var dataLayerName = initConfig.dataLayerName,
customScriptSrc = initConfig.customScriptSrc;
var hasDataLayer = dataLayerName && window[dataLayerName] && Array.prototype.push === window[dataLayerName].push;
return scriptLoaded(customScriptSrc || gtagScriptSource) && hasDataLayer;
},
/* Custom methods */
methods: {
addTag: function addTag(tagId) {
var settings = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
// https://developers.google.com/tag-platform/devguides/install-gtagjs#add_products_to_your_tag
if (window[initConfig.gtagName]) {
window[initConfig.gtagName]('config', tagId, settings); // Add tag id
if (measurementIds && !measurementIds.includes(tagId)) {
measurementIds = measurementIds.concat(tagId);
}
}
},
/* Disable gtag for user */
disable: function disable(ids) {
var gaIds = ids ? getIds(ids) : measurementIds;
for (var i = 0; i < measurementIds.length; i++) {
var gaId = measurementIds[i];
if (gaIds.includes(gaId)) {
// https://developers.google.com/analytics/devguides/collection/gtagjs/user-opt-out
window["ga-disable-".concat(gaId)] = true;
}
}
},
/* Enable gtag for user */
enable: function enable(ids) {
var gaIds = ids ? getIds(ids) : measurementIds;
for (var i = 0; i < measurementIds.length; i++) {
var gaId = measurementIds[i];
if (gaIds.includes(gaId)) {
// https://developers.google.com/analytics/devguides/collection/gtagjs/user-opt-out
window["ga-disable-".concat(gaId)] = false;
}
}
}
}
};
}
function getIds(measurementIds) {
if (!measurementIds) throw new Error('No GA Measurement ID defined');
if (Array.isArray(measurementIds)) {
return measurementIds;
}
if (typeof measurementIds === 'string') {
return [measurementIds];
}
throw new Error('GA Measurement ID must be string or array of strings');
}
/**
* Add campaign data to GA payload https://bit.ly/34qFCPn
* @param {Object} [campaignData={}] [description]
* @param {String} [campaignData.campaignName] - Name of campaign
* @param {String} [campaignData.campaignSource] - Source of campaign
* @param {String} [campaignData.campaignMedium] - Medium of campaign
* @param {String} [campaignData.campaignContent] - Content of campaign
* @param {String} [campaignData.campaignKeyword] - Keyword of campaign
*/
function addCampaignData() {
var campaignData = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var campaign = {};
var id = campaignData.id,
name = campaignData.name,
source = campaignData.source,
medium = campaignData.medium,
content = campaignData.content,
keyword = campaignData.keyword;
if (id) campaign.campaignId = id;
if (name) campaign.campaignName = name;
if (source) campaign.campaignSource = source;
if (medium) campaign.campaignMedium = medium;
if (content) campaign.campaignContent = content;
if (keyword) campaign.campaignKeyword = keyword;
return campaign;
}
function scriptLoaded(scriptSrc) {
var scripts = document.querySelectorAll('script[src]');
var regex = new RegExp("^".concat(scriptSrc));
return Boolean(Object.values(scripts).filter(function (value) {
return regex.test(value.src);
}).length);
}
/* This module will shake out unused code + work in browser and node 🎉 */
var index = googleAnalytics ;
/* init for CDN usage. globalName.init() */
var init = googleAnalytics ;
export { index as default, init };