@techinasia/analytics.js-integration-google-analytics-4 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/HISTORY.md +9 -0
- package/lib/index.js +312 -0
- package/package.json +58 -0
package/HISTORY.md
ADDED
package/lib/index.js
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Module dependencies.
|
|
5
|
+
*/
|
|
6
|
+
var integration = require('@segment/analytics.js-integration');
|
|
7
|
+
var reject = require('reject');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* GA4
|
|
11
|
+
*/
|
|
12
|
+
var GA4 = (module.exports = integration('Google Analytics 4')
|
|
13
|
+
.global('gtag')
|
|
14
|
+
.global('ga4DataLayer')
|
|
15
|
+
.option('measurementIds', [])
|
|
16
|
+
.option('cookieDomainName', 'auto')
|
|
17
|
+
.option('cookiePrefix', '_ga')
|
|
18
|
+
.option('cookieExpiration', 63072000)
|
|
19
|
+
.option('cookieUpdate', true)
|
|
20
|
+
.option('cookieFlags', '')
|
|
21
|
+
.option('sendAutomaticPageViewEvent', false)
|
|
22
|
+
.option('allowAllAdvertisingFeatures', false)
|
|
23
|
+
.option('allowAdvertisingPersonalization', false)
|
|
24
|
+
.option('disableGoogleAnalytics', false)
|
|
25
|
+
.option('googleReportingIdentity', 'device')
|
|
26
|
+
.option('userProperties', {})
|
|
27
|
+
/**
|
|
28
|
+
* Custom Events and Parameters setting. This setting is used by the track
|
|
29
|
+
* handler to map Segment events and fields to Google analytics events and parameters.
|
|
30
|
+
*
|
|
31
|
+
* Example:
|
|
32
|
+
* [
|
|
33
|
+
* {
|
|
34
|
+
* "googleEvent": "new_episode",
|
|
35
|
+
* "parameters": [
|
|
36
|
+
* {
|
|
37
|
+
* "key": "properties.title",
|
|
38
|
+
* "value": "title"
|
|
39
|
+
* },
|
|
40
|
+
* {
|
|
41
|
+
* "key": "properties.genre",
|
|
42
|
+
* "value": "genre"
|
|
43
|
+
* }
|
|
44
|
+
* ],
|
|
45
|
+
* "segmentEvent": "Started Episode"
|
|
46
|
+
* }
|
|
47
|
+
* ]
|
|
48
|
+
*/
|
|
49
|
+
.option('customEventsAndParameters', [])
|
|
50
|
+
.tag(
|
|
51
|
+
'<script src="//www.googletagmanager.com/gtag/js?id={{ measurementId }}&l=ga4DataLayer">'
|
|
52
|
+
));
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Initialize.
|
|
56
|
+
*
|
|
57
|
+
* https://developers.google.com/analytics/devguides/collection/ga4
|
|
58
|
+
*
|
|
59
|
+
* @api public
|
|
60
|
+
*/
|
|
61
|
+
GA4.prototype.initialize = function() {
|
|
62
|
+
window.ga4DataLayer = window.ga4DataLayer || [];
|
|
63
|
+
window.gtag = function() {
|
|
64
|
+
window.ga4DataLayer.push(arguments);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* This line is in all of the gtag examples but is not well documented. Research
|
|
69
|
+
* says that it is is related to deduplication.
|
|
70
|
+
* https://stackoverflow.com/questions/59256532/what-is-the-js-gtags-js-command
|
|
71
|
+
*/
|
|
72
|
+
window.gtag('js', new Date());
|
|
73
|
+
|
|
74
|
+
var opts = this.options;
|
|
75
|
+
var measurementIds = opts.measurementIds;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Avoid loading and configuring gtag.js if any are true:
|
|
79
|
+
* - Disable Google Analytics setting is enabled
|
|
80
|
+
* - No measurement IDs are configured
|
|
81
|
+
*/
|
|
82
|
+
if (!measurementIds.length || opts.disableGoogleAnalytics) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
var config = {
|
|
87
|
+
/**
|
|
88
|
+
* Disable Google's Automatic Page View Measurement
|
|
89
|
+
* https://developers.google.com/analytics/devguides/collection/ga4/disable-page-view
|
|
90
|
+
*/
|
|
91
|
+
send_page_view: opts.sendAutomaticPageViewEvent,
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Cookie Update
|
|
95
|
+
* https://developers.google.com/analytics/devguides/collection/ga4/cookies-user-id#cookie_update_parameter
|
|
96
|
+
*/
|
|
97
|
+
cookie_update: opts.cookieUpdate,
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Cookie Domain Name
|
|
101
|
+
* https://developers.google.com/analytics/devguides/collection/ga4/cookies-user-id#cookie_domain_configuration
|
|
102
|
+
*/
|
|
103
|
+
cookie_domain: opts.cookieDomainName,
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Cookie Prefix
|
|
107
|
+
* https://developers.google.com/analytics/devguides/collection/ga4/cookies-user-id#cookie_prefix
|
|
108
|
+
*/
|
|
109
|
+
cookie_prefix: opts.cookiePrefix,
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Cookie Expiration
|
|
113
|
+
* https://developers.google.com/analytics/devguides/collection/ga4/cookies-user-id#cookie_expiration
|
|
114
|
+
*/
|
|
115
|
+
cookie_expires: opts.cookieExpiration,
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
var sets = [
|
|
119
|
+
/**
|
|
120
|
+
* Cookie Flags
|
|
121
|
+
* https://developers.google.com/analytics/devguides/collection/ga4/cookies-user-id#cookie_flags
|
|
122
|
+
*/
|
|
123
|
+
[{ cookie_flags: opts.cookieFlags }],
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Disable All Advertising
|
|
127
|
+
* https://developers.google.com/analytics/devguides/collection/ga4/display-features#disable_all_advertising_features
|
|
128
|
+
*/
|
|
129
|
+
['allow_google_signals', opts.allowAllAdvertisingFeatures],
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Disable Advertising Personalization
|
|
133
|
+
* https://developers.google.com/analytics/devguides/collection/ga4/display-features#disable_advertising_personalization
|
|
134
|
+
*/
|
|
135
|
+
['allow_ad_personalization_signals', opts.allowAdvertisingPersonalization]
|
|
136
|
+
];
|
|
137
|
+
|
|
138
|
+
// Load gtag.js using the first measurement ID, then configure using the `config` commands built above.
|
|
139
|
+
var self = this;
|
|
140
|
+
this.load({ measurementId: measurementIds[0] }, function() {
|
|
141
|
+
/**
|
|
142
|
+
* Measurement IDs.
|
|
143
|
+
* The same configuration information is shared across all measurement IDs.
|
|
144
|
+
* https://developers.google.com/analytics/devguides/collection/ga4#add_an_additional_google_analytics_property_to_an_existing_tag
|
|
145
|
+
*/
|
|
146
|
+
for (var i = 0; i < measurementIds.length; i++) {
|
|
147
|
+
window.gtag('config', measurementIds[i], config)
|
|
148
|
+
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Set persistent values shared across all gtag.js usage.
|
|
153
|
+
* https://developers.google.com/gtagjs/reference/api#set
|
|
154
|
+
*/
|
|
155
|
+
for (var i = 0; i < sets.length; i++) {
|
|
156
|
+
// Copy the set args and append the command before calling gtag.js.
|
|
157
|
+
var args = sets[i].slice(0)
|
|
158
|
+
args.unshift('set')
|
|
159
|
+
window.gtag.apply(null, args);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
self.ready();
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Loaded?
|
|
168
|
+
*
|
|
169
|
+
* @api private
|
|
170
|
+
* @return {boolean}
|
|
171
|
+
*/
|
|
172
|
+
GA4.prototype.loaded = function() {
|
|
173
|
+
return !!(
|
|
174
|
+
window.ga4DataLayer && Array.prototype.push !== window.ga4DataLayer.push
|
|
175
|
+
);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Identify.
|
|
180
|
+
*
|
|
181
|
+
* @api public
|
|
182
|
+
* @param {Facade.Identify} event
|
|
183
|
+
*/
|
|
184
|
+
GA4.prototype.identify = function(identify) {
|
|
185
|
+
var opts = this.options;
|
|
186
|
+
var userPropertyMappings = opts.userProperties;
|
|
187
|
+
|
|
188
|
+
var userProperties = {};
|
|
189
|
+
|
|
190
|
+
// Map all customer-defined user property mappings.
|
|
191
|
+
for (var eventField in userPropertyMappings) {
|
|
192
|
+
if (!userPropertyMappings.hasOwnProperty(eventField)) {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
var userProp = userPropertyMappings[eventField];
|
|
197
|
+
var value = identify.proxy(eventField);
|
|
198
|
+
|
|
199
|
+
userProperties[userProp] = value;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Map the user_id property if the Google Reporting Identity is set one of:
|
|
204
|
+
* - By User ID, Google signals, then device (userIdSignalsAndDevice)
|
|
205
|
+
* - By User ID and Devicea (userIdAndDevice)
|
|
206
|
+
*
|
|
207
|
+
* Google's Reporting Identity: https://support.google.com/analytics/answer/9213390?hl=en
|
|
208
|
+
*
|
|
209
|
+
* Note that the user ID can be appended as part of the user_properties
|
|
210
|
+
* object instead of being configured by an explicit command.
|
|
211
|
+
* https://developers.google.com/analytics/devguides/collection/ga4/cookies-user-id#set_user_id
|
|
212
|
+
*/
|
|
213
|
+
var userId = identify.userId();
|
|
214
|
+
var validReportingIdentity = opts.googleReportingIdentity === 'userIdSignalsAndDevice' || opts.googleReportingIdentity === 'userIdAndDevice'
|
|
215
|
+
if (userId && validReportingIdentity) {
|
|
216
|
+
userProperties.user_id = userId;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (Object.keys(userProperties).length) {
|
|
220
|
+
window.gtag('set', 'user_properties', userProperties);
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Group
|
|
226
|
+
*
|
|
227
|
+
* @api public
|
|
228
|
+
* @param {Facade.Group} group
|
|
229
|
+
*/
|
|
230
|
+
GA4.prototype.group = function(group) {
|
|
231
|
+
window.gtag('event', 'join_group', {
|
|
232
|
+
group_id: group.groupId()
|
|
233
|
+
});
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Page
|
|
238
|
+
*
|
|
239
|
+
* @api public
|
|
240
|
+
* @param {Facade.Page} page
|
|
241
|
+
*/
|
|
242
|
+
GA4.prototype.page = function(page) {
|
|
243
|
+
// If the Send Google's Automatic Page View Measurement setting is set to true then
|
|
244
|
+
// don't handle page calls to avoid duplicate page_view events.
|
|
245
|
+
if (this.options.sendAutomaticPageViewEvent) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
var props = page.properties();
|
|
250
|
+
var name = page.fullName();
|
|
251
|
+
|
|
252
|
+
var pageLocation = props.url;
|
|
253
|
+
var pageReferrer = page.referrer();
|
|
254
|
+
var pageTitle = name || props.title;
|
|
255
|
+
|
|
256
|
+
window.gtag('event', 'page_view', {
|
|
257
|
+
page_location: pageLocation,
|
|
258
|
+
page_referrer: pageReferrer,
|
|
259
|
+
page_title: pageTitle
|
|
260
|
+
});
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Track
|
|
265
|
+
*
|
|
266
|
+
* @api public
|
|
267
|
+
* @param {Track} track
|
|
268
|
+
*/
|
|
269
|
+
|
|
270
|
+
GA4.prototype.track = function(track) {
|
|
271
|
+
|
|
272
|
+
var mappings = this.options.customEventsAndParameters;
|
|
273
|
+
|
|
274
|
+
for (var i = 0; i < mappings.length; i++) {
|
|
275
|
+
var mapping = mappings[i];
|
|
276
|
+
if (typeof mapping !== 'object') {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
var segmentEvent = mapping.segmentEvent;
|
|
281
|
+
var googleEvent = mapping.googleEvent;
|
|
282
|
+
|
|
283
|
+
if (!segmentEvent || !googleEvent || segmentEvent !== track.event()) {
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
var parameterMappings = mapping.parameters || [];
|
|
288
|
+
var parameters = {};
|
|
289
|
+
|
|
290
|
+
if (!(parameterMappings instanceof Array)) {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Map Segment event fields to Google Event Parameters.
|
|
295
|
+
// Text map settings that are nested in a mixed settings take on a different shape
|
|
296
|
+
// than a top-level text map setting.
|
|
297
|
+
// eg; [{ key: 'properties.genre', value: 'primary_genre }]
|
|
298
|
+
//
|
|
299
|
+
for (var j = 0; j < parameterMappings.length; j++) {
|
|
300
|
+
var map = parameterMappings[j] || {};
|
|
301
|
+
if (typeof map !== 'object' || !map.key || !map.value) {
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
var param = map.value;
|
|
306
|
+
var value = track.proxy(map.key);
|
|
307
|
+
parameters[param] = value;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
window.gtag('event', googleEvent, parameters);
|
|
311
|
+
}
|
|
312
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@techinasia/analytics.js-integration-google-analytics-4",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "Segment Analytics.js integration for Google Analytics 4",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"directories": {
|
|
7
|
+
"lib": "lib",
|
|
8
|
+
"test": "test"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "karma start"
|
|
12
|
+
},
|
|
13
|
+
"author": "Tech in Asia",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"homepage": "https://github.com/techinasia/segment-analytics.js-integration-google-analytics-4#readme",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/techinasia/segment-analytics.js-integration-google-analytics-4/issues"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/techinasia/segment-analytics.js-integration-google-analytics-4.git"
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@ndhoule/defaults": "^2.0.1",
|
|
28
|
+
"@segment/analytics.js-integration": "^3.1.0",
|
|
29
|
+
"component-each": "^0.2.6",
|
|
30
|
+
"extend": "^3.0.2",
|
|
31
|
+
"global-queue": "^1.0.1",
|
|
32
|
+
"is": "^3.1.0",
|
|
33
|
+
"lodash": "^4.17.4",
|
|
34
|
+
"obj-case": "^0.2.0",
|
|
35
|
+
"object-component": "0.0.3",
|
|
36
|
+
"reject": "0.0.1",
|
|
37
|
+
"segmentio-facade": "^3.2.7",
|
|
38
|
+
"use-https": "^0.1.1"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@segment/analytics.js-core": "^3.8.2",
|
|
42
|
+
"@segment/analytics.js-integration-tester": "^3.1.1",
|
|
43
|
+
"@segment/clear-env": "^2.1.1",
|
|
44
|
+
"browserify": "^16.2.3",
|
|
45
|
+
"eslint": "^5.16.0",
|
|
46
|
+
"karma": "^4.1.0",
|
|
47
|
+
"karma-browserify": "^6.0.0",
|
|
48
|
+
"karma-chrome-launcher": "^2.2.0",
|
|
49
|
+
"karma-mocha": "^1.3.0",
|
|
50
|
+
"karma-mocha-reporter": "^2.2.5",
|
|
51
|
+
"karma-sauce-launcher": "^2.0.2",
|
|
52
|
+
"karma-spec-reporter": "^0.0.32",
|
|
53
|
+
"karma-summary-reporter": "^1.6.0",
|
|
54
|
+
"mocha": "^6.1.4",
|
|
55
|
+
"to-array": "^0.1.4",
|
|
56
|
+
"watchify": "^3.7.0"
|
|
57
|
+
}
|
|
58
|
+
}
|