mixpanel-browser 2.58.0 → 2.59.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mixpanel-browser",
3
- "version": "2.58.0",
3
+ "version": "2.59.0",
4
4
  "description": "The official Mixpanel JavaScript browser client library",
5
5
  "main": "dist/mixpanel.cjs.js",
6
6
  "module": "dist/mixpanel.module.js",
@@ -0,0 +1,271 @@
1
+ import { _, document, safewrap, safewrapClass } from '../utils';
2
+ import { window } from '../window';
3
+ import {
4
+ getPropsForDOMEvent, logger, minDOMApisSupported,
5
+ EV_CHANGE, EV_CLICK, EV_HASHCHANGE, EV_MP_LOCATION_CHANGE, EV_POPSTATE,
6
+ EV_SCROLLEND, EV_SUBMIT
7
+ } from './utils';
8
+
9
+ var AUTOCAPTURE_CONFIG_KEY = 'autocapture';
10
+ var LEGACY_PAGEVIEW_CONFIG_KEY = 'track_pageview';
11
+
12
+ var PAGEVIEW_OPTION_FULL_URL = 'full-url';
13
+ var PAGEVIEW_OPTION_URL_WITH_PATH_AND_QUERY_STRING = 'url-with-path-and-query-string';
14
+ var PAGEVIEW_OPTION_URL_WITH_PATH = 'url-with-path';
15
+
16
+ var CONFIG_BLOCK_SELECTORS = 'block_selectors';
17
+ var CONFIG_BLOCK_URL_REGEXES = 'block_url_regexes';
18
+ var CONFIG_CAPTURE_TEXT_CONTENT = 'capture_text_content';
19
+ var CONFIG_TRACK_CLICK = 'click';
20
+ var CONFIG_TRACK_INPUT = 'input';
21
+ var CONFIG_TRACK_PAGEVIEW = 'pageview';
22
+ var CONFIG_TRACK_SCROLL = 'scroll';
23
+ var CONFIG_TRACK_SUBMIT = 'submit';
24
+
25
+ var CONFIG_DEFAULTS = {};
26
+ CONFIG_DEFAULTS[CONFIG_CAPTURE_TEXT_CONTENT] = false;
27
+ CONFIG_DEFAULTS[CONFIG_TRACK_CLICK] = true;
28
+ CONFIG_DEFAULTS[CONFIG_TRACK_INPUT] = true;
29
+ CONFIG_DEFAULTS[CONFIG_TRACK_PAGEVIEW] = PAGEVIEW_OPTION_FULL_URL;
30
+ CONFIG_DEFAULTS[CONFIG_TRACK_SCROLL] = true;
31
+ CONFIG_DEFAULTS[CONFIG_TRACK_SUBMIT] = true;
32
+
33
+ var DEFAULT_PROPS = {
34
+ '$mp_autocapture': true
35
+ };
36
+
37
+ var MP_EV_CLICK = '$mp_click';
38
+ var MP_EV_INPUT = '$mp_input_change';
39
+ var MP_EV_SCROLL = '$mp_scroll';
40
+ var MP_EV_SUBMIT = '$mp_submit';
41
+
42
+ /**
43
+ * Autocapture: manages automatic event tracking
44
+ * @constructor
45
+ */
46
+ var Autocapture = function(mp) {
47
+ this.mp = mp;
48
+ };
49
+
50
+ Autocapture.prototype.init = function() {
51
+ if (!minDOMApisSupported()) {
52
+ logger.critical('Autocapture unavailable: missing required DOM APIs');
53
+ return;
54
+ }
55
+
56
+ this.initPageviewTracking();
57
+ this.initClickTracking();
58
+ this.initInputTracking();
59
+ this.initScrollTracking();
60
+ this.initSubmitTracking();
61
+ };
62
+
63
+ Autocapture.prototype.getFullConfig = function() {
64
+ var autocaptureConfig = this.mp.get_config(AUTOCAPTURE_CONFIG_KEY);
65
+ if (!autocaptureConfig) {
66
+ // Autocapture is completely off
67
+ return {};
68
+ } else if (_.isObject(autocaptureConfig)) {
69
+ return _.extend({}, CONFIG_DEFAULTS, autocaptureConfig);
70
+ } else {
71
+ // Autocapture config is non-object truthy value, return default
72
+ return CONFIG_DEFAULTS;
73
+ }
74
+ };
75
+
76
+ Autocapture.prototype.getConfig = function(key) {
77
+ return this.getFullConfig()[key];
78
+ };
79
+
80
+ Autocapture.prototype.currentUrlBlocked = function() {
81
+ var blockUrlRegexes = this.getConfig(CONFIG_BLOCK_URL_REGEXES) || [];
82
+ if (!blockUrlRegexes || !blockUrlRegexes.length) {
83
+ return false;
84
+ }
85
+
86
+ var currentUrl = _.info.currentUrl();
87
+ for (var i = 0; i < blockUrlRegexes.length; i++) {
88
+ try {
89
+ if (currentUrl.match(blockUrlRegexes[i])) {
90
+ return true;
91
+ }
92
+ } catch (err) {
93
+ logger.critical('Error while checking block URL regex: ' + blockUrlRegexes[i], err);
94
+ return true;
95
+ }
96
+ }
97
+ return false;
98
+ };
99
+
100
+ Autocapture.prototype.pageviewTrackingConfig = function() {
101
+ // supports both autocapture config and old track_pageview config
102
+ if (this.mp.get_config(AUTOCAPTURE_CONFIG_KEY)) {
103
+ return this.getConfig(CONFIG_TRACK_PAGEVIEW);
104
+ } else {
105
+ return this.mp.get_config(LEGACY_PAGEVIEW_CONFIG_KEY);
106
+ }
107
+ };
108
+
109
+ // helper for event handlers
110
+ Autocapture.prototype.trackDomEvent = function(ev, mpEventName) {
111
+ if (this.currentUrlBlocked()) {
112
+ return;
113
+ }
114
+
115
+ var props = getPropsForDOMEvent(
116
+ ev,
117
+ this.getConfig(CONFIG_BLOCK_SELECTORS),
118
+ this.getConfig(CONFIG_CAPTURE_TEXT_CONTENT)
119
+ );
120
+ if (props) {
121
+ _.extend(props, DEFAULT_PROPS);
122
+ this.mp.track(mpEventName, props);
123
+ }
124
+ };
125
+
126
+ Autocapture.prototype.initClickTracking = function() {
127
+ window.removeEventListener(EV_CLICK, this.listenerClick);
128
+
129
+ if (!this.getConfig(CONFIG_TRACK_CLICK)) {
130
+ return;
131
+ }
132
+ logger.log('Initializing click tracking');
133
+
134
+ this.listenerClick = window.addEventListener(EV_CLICK, function(ev) {
135
+ if (!this.getConfig(CONFIG_TRACK_CLICK)) {
136
+ return;
137
+ }
138
+ this.trackDomEvent(ev, MP_EV_CLICK);
139
+ }.bind(this));
140
+ };
141
+
142
+ Autocapture.prototype.initInputTracking = function() {
143
+ window.removeEventListener(EV_CHANGE, this.listenerChange);
144
+
145
+ if (!this.getConfig(CONFIG_TRACK_INPUT)) {
146
+ return;
147
+ }
148
+ logger.log('Initializing input tracking');
149
+
150
+ this.listenerChange = window.addEventListener(EV_CHANGE, function(ev) {
151
+ if (!this.getConfig(CONFIG_TRACK_INPUT)) {
152
+ return;
153
+ }
154
+ this.trackDomEvent(ev, MP_EV_INPUT);
155
+ }.bind(this));
156
+ };
157
+
158
+ Autocapture.prototype.initPageviewTracking = function() {
159
+ window.removeEventListener(EV_POPSTATE, this.listenerPopstate);
160
+ window.removeEventListener(EV_HASHCHANGE, this.listenerHashchange);
161
+ window.removeEventListener(EV_MP_LOCATION_CHANGE, this.listenerLocationchange);
162
+
163
+ if (!this.pageviewTrackingConfig()) {
164
+ return;
165
+ }
166
+ logger.log('Initializing pageview tracking');
167
+
168
+ var previousTrackedUrl = '';
169
+ var tracked = false;
170
+ if (!this.currentUrlBlocked()) {
171
+ tracked = this.mp.track_pageview(DEFAULT_PROPS);
172
+ }
173
+ if (tracked) {
174
+ previousTrackedUrl = _.info.currentUrl();
175
+ }
176
+
177
+ this.listenerPopstate = window.addEventListener(EV_POPSTATE, function() {
178
+ window.dispatchEvent(new Event(EV_MP_LOCATION_CHANGE));
179
+ });
180
+ this.listenerHashchange = window.addEventListener(EV_HASHCHANGE, function() {
181
+ window.dispatchEvent(new Event(EV_MP_LOCATION_CHANGE));
182
+ });
183
+ var nativePushState = window.history.pushState;
184
+ if (typeof nativePushState === 'function') {
185
+ window.history.pushState = function(state, unused, url) {
186
+ nativePushState.call(window.history, state, unused, url);
187
+ window.dispatchEvent(new Event(EV_MP_LOCATION_CHANGE));
188
+ };
189
+ }
190
+ var nativeReplaceState = window.history.replaceState;
191
+ if (typeof nativeReplaceState === 'function') {
192
+ window.history.replaceState = function(state, unused, url) {
193
+ nativeReplaceState.call(window.history, state, unused, url);
194
+ window.dispatchEvent(new Event(EV_MP_LOCATION_CHANGE));
195
+ };
196
+ }
197
+ this.listenerLocationchange = window.addEventListener(EV_MP_LOCATION_CHANGE, safewrap(function() {
198
+ if (this.currentUrlBlocked()) {
199
+ return;
200
+ }
201
+
202
+ var currentUrl = _.info.currentUrl();
203
+ var shouldTrack = false;
204
+ var trackPageviewOption = this.pageviewTrackingConfig();
205
+ if (trackPageviewOption === PAGEVIEW_OPTION_FULL_URL) {
206
+ shouldTrack = currentUrl !== previousTrackedUrl;
207
+ } else if (trackPageviewOption === PAGEVIEW_OPTION_URL_WITH_PATH_AND_QUERY_STRING) {
208
+ shouldTrack = currentUrl.split('#')[0] !== previousTrackedUrl.split('#')[0];
209
+ } else if (trackPageviewOption === PAGEVIEW_OPTION_URL_WITH_PATH) {
210
+ shouldTrack = currentUrl.split('#')[0].split('?')[0] !== previousTrackedUrl.split('#')[0].split('?')[0];
211
+ }
212
+
213
+ if (shouldTrack) {
214
+ var tracked = this.mp.track_pageview(DEFAULT_PROPS);
215
+ if (tracked) {
216
+ previousTrackedUrl = currentUrl;
217
+ }
218
+ }
219
+ }.bind(this)));
220
+ };
221
+
222
+ Autocapture.prototype.initScrollTracking = function() {
223
+ window.removeEventListener(EV_SCROLLEND, this.listenerScroll);
224
+
225
+ if (!this.getConfig(CONFIG_TRACK_SCROLL)) {
226
+ return;
227
+ }
228
+ logger.log('Initializing scroll tracking');
229
+
230
+ this.listenerScroll = window.addEventListener(EV_SCROLLEND, safewrap(function() {
231
+ if (!this.getConfig(CONFIG_TRACK_SCROLL)) {
232
+ return;
233
+ }
234
+ if (this.currentUrlBlocked()) {
235
+ return;
236
+ }
237
+
238
+ var scrollTop = window.scrollY;
239
+ var props = _.extend({'$scroll_top': scrollTop}, DEFAULT_PROPS);
240
+ try {
241
+ var scrollHeight = document.body.scrollHeight;
242
+ var scrollPercentage = Math.round((scrollTop / (scrollHeight - window.innerHeight)) * 100);
243
+ props['$scroll_height'] = scrollHeight;
244
+ props['$scroll_percentage'] = scrollPercentage;
245
+ } catch (err) {
246
+ logger.critical('Error while calculating scroll percentage', err);
247
+ }
248
+ this.mp.track(MP_EV_SCROLL, props);
249
+ }.bind(this)));
250
+ };
251
+
252
+ Autocapture.prototype.initSubmitTracking = function() {
253
+ window.removeEventListener(EV_SUBMIT, this.listenerSubmit);
254
+
255
+ if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
256
+ return;
257
+ }
258
+ logger.log('Initializing submit tracking');
259
+
260
+ this.listenerSubmit = window.addEventListener(EV_SUBMIT, function(ev) {
261
+ if (!this.getConfig(CONFIG_TRACK_SUBMIT)) {
262
+ return;
263
+ }
264
+ this.trackDomEvent(ev, MP_EV_SUBMIT);
265
+ }.bind(this));
266
+ };
267
+
268
+ // TODO integrate error_reporter from mixpanel instance
269
+ safewrapClass(Autocapture);
270
+
271
+ export { Autocapture };