mixpanel-browser 2.48.0 → 2.49.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.
@@ -0,0 +1,114 @@
1
+ # Mixpanel JavaScript SDK wrapper for Google Tag Manager
2
+ The purpose of this wrapper is to provide a JavaScript interface for interacting with the `window.mixpanel` interface.
3
+
4
+ The wrapper has been designed with [Google Tag Manager](https://tagmanager.google.com) in mind. GTM's [custom templates](https://developers.google.com/tag-manager/templates) offer a way to deploy custom JavaScript without having to resort to Custom HTML tags and with the ability to craft a user interface for the scripts within Google Tag Manager.
5
+
6
+ However, one of the defining features of custom templates is their [sandboxed JavaScript API](https://developers.google.com/tag-manager/templates/sandboxed-javascript) inventory, which severely restricts what type of browser JavaScript can be executed in the template code.
7
+
8
+ Mixpanel's [JavaScript SDK](https://developer.mixpanel.com/docs/javascript-full-api-reference) makes use of JavaScript features which are not permitted by the sandbox of GTM's custom templates (e.g. object instances initiated with the `new` keyword, `this` and `prototype`, etc.).
9
+
10
+ Thus, in order to interact with Mixpanel's JavaScript SDK via Google Tag Manager's custom templates (or any other context where the aforementioned JavaScript features cannot be used), this wrapper is required. [The template loads this wrapper](https://github.com/mixpanel/mixpanel-gtm-template/blob/2f577d826acc7d96d138367db339035b8f9df359/src/template.tpl#L1383) in the sandboxed template code.
11
+
12
+ # Deployment
13
+ The wrapper is served by the Mixpanel CDN at https://cdn.mxpnl.com/libs/mixpanel-js-wrapper.js
14
+
15
+ # How it works
16
+ When the wrapper JavaScript is loaded in the browser, the global method `window._mixpanel()` is created for interacting with the wrapper.
17
+
18
+ This namespace includes all the methods supported by the [JavaScript SDK](https://developer.mixpanel.com/docs/javascript-full-api-reference) with some exceptions (see below). Each method can be invoked by passing the command name as the first argument of the call to `window._mixpanel()`.
19
+
20
+ If this command name is prefixed with `<string>.`, then `<string>` will be used as the [library name](https://developer.mixpanel.com/docs/javascript-full-api-reference#mixpanelinit). After the command, all additional arguments are processed as arguments to the command method itself.
21
+
22
+ For example, to dispatch an event named `Add To Cart` using a custom library name, you could use this command:
23
+
24
+ ```
25
+ window._mixpanel(
26
+ 'myTracker.track', // Run the track command and utilize the library name "myTracker"
27
+ 'Add To Cart', // Event name is the first argument to the track command
28
+ {product_id: 'shirt123'} // (optional) Event parameters are the second argument to the track command
29
+ );
30
+ ```
31
+
32
+ ## `group`
33
+ [Link to specification](https://developer.mixpanel.com/docs/javascript-full-api-reference#mixpanelgroup)
34
+
35
+ Use this command to interact with group properties.
36
+
37
+ Syntax:
38
+
39
+ `window._mixpanel('<library_name.>group.<group_command>', ['<group_key>', '<group_id>'], <parameters>)`
40
+
41
+ Example:
42
+ ```
43
+ // Union a property for a group
44
+ window._mixpanel(
45
+ 'group.union',
46
+ ['my_group_key', 'my_group_id'],
47
+ 'location',
48
+ ['San Francisco', 'London']
49
+ );
50
+ ```
51
+
52
+ | Parameter name | Description |
53
+ |----------------|-------------|
54
+ | `library_name` | (Optional) target a specific library/instance name with this command |
55
+ | `group_command` | (Required) one of `set`, `set_once`, `remove`, `union`, `unset` |
56
+ | `parameters` | All the parameters you want to submit to the `group` command. |
57
+
58
+ ## `people`
59
+ [Link to specification](https://developer.mixpanel.com/docs/javascript-full-api-reference#mixpanelpeople)
60
+
61
+ Interact with the people analytics property.
62
+
63
+ Syntax:
64
+
65
+ `window._mixpanel('<library_name.>people.<people_command>', <parameters>)`
66
+
67
+ Example:
68
+ ```
69
+ // Set the "gender" property "n/a"
70
+ window._mixpanel(
71
+ 'people.set',
72
+ 'gender',
73
+ 'n/a'
74
+ );
75
+ ```
76
+
77
+ | Parameter name | Description |
78
+ |----------------|-------------|
79
+ | `library_name` | (Optional) target a specific library/instance name with this command |
80
+ | `people_command` | (Required) one of `append`, `clear_charge`, `delete_user`, `increment`, `remove`, `set`, `set_once`, `track_charge`, `union`, `unset` |
81
+ | `parameters` | All the parameters you want to submit to the `people` command. |
82
+
83
+ ## Other commands
84
+
85
+ All other commands can be sent to the `_mixpanel` interface like this:
86
+
87
+ `window._mixpanel('<library_name.><command_name>', <parameters>)`
88
+
89
+ Example:
90
+
91
+ ```
92
+ window._mixpanel(
93
+ 'my_mixpanel.register',
94
+ {'Account Type': 'Free'}
95
+ );
96
+ ```
97
+
98
+ ## Exceptions
99
+
100
+ Because the wrapper only pipes commands to the actual `window.mixpanel` interface, the wrapper cannot be used for get-methods. So the following commands **are not** supported by the wrapper:
101
+
102
+ `get_config`
103
+ `get_distinct_id`
104
+ `get_group`
105
+ `get_property`
106
+ `has_opted_in_tracking`
107
+ `has_opted_out_tracking`
108
+
109
+ The following commands are also disabled due to not being relevant with the wrapper:
110
+
111
+ `push`
112
+ `init`
113
+
114
+ Initialization is done with the `window.mixpanel` interface directly, and `push` is already used by the wrapper to forward calls to the main interface.
@@ -106,10 +106,12 @@ var DEFAULT_CONFIG = {
106
106
  'cookie_domain': '',
107
107
  'cookie_name': '',
108
108
  'loaded': NOOP_FUNC,
109
+ 'mp_loader': null,
109
110
  'track_marketing': true,
110
111
  'track_pageview': false,
111
112
  'skip_first_touch_marketing': false,
112
113
  'store_google': true,
114
+ 'stop_utm_persistence': false,
113
115
  'save_referrer': true,
114
116
  'test': false,
115
117
  'verbose': false,
@@ -343,8 +345,9 @@ MixpanelLib.prototype._init = function(token, config, name) {
343
345
  }, '');
344
346
  }
345
347
 
346
- if (this.get_config('track_pageview')) {
347
- this.track_pageview();
348
+ var track_pageview_option = this.get_config('track_pageview');
349
+ if (track_pageview_option) {
350
+ this._init_url_change_tracking(track_pageview_option);
348
351
  }
349
352
  };
350
353
 
@@ -353,13 +356,26 @@ MixpanelLib.prototype._init = function(token, config, name) {
353
356
  MixpanelLib.prototype._loaded = function() {
354
357
  this.get_config('loaded')(this);
355
358
  this._set_default_superprops();
359
+ this['people'].set_once(this['persistence'].get_referrer_info());
360
+
361
+ // The original 'store_google' functionality will be deprecated and the config will be
362
+ // used to clear previously managed UTM parameters from persistence.
363
+ // stop_utm_persistence is `false` by default now but will be default `true` in the future.
364
+ if (this.get_config('store_google') && this.get_config('stop_utm_persistence')) {
365
+ var utm_params = _.info.campaignParams(null);
366
+ _.each(utm_params, function(_utm_value, utm_key) {
367
+ // We need to unregister persisted UTM parameters so old values
368
+ // are not mixed with the new UTM parameters
369
+ this.unregister(utm_key);
370
+ }.bind(this));
371
+ }
356
372
  };
357
373
 
358
374
  // update persistence with info on referrer, UTM params, etc
359
375
  MixpanelLib.prototype._set_default_superprops = function() {
360
376
  this['persistence'].update_search_keyword(document.referrer);
361
- if (this.get_config('store_google')) {
362
- this.register(_.info.campaignParams(), {persistent: false});
377
+ if (this.get_config('store_google') && !this.get_config('stop_utm_persistence')) {
378
+ this.register(_.info.campaignParams());
363
379
  }
364
380
  if (this.get_config('save_referrer')) {
365
381
  this['persistence'].update_referrer_info(document.referrer);
@@ -396,6 +412,55 @@ MixpanelLib.prototype._track_dom = function(DomClass, args) {
396
412
  return dt.track.apply(dt, args);
397
413
  };
398
414
 
415
+ MixpanelLib.prototype._init_url_change_tracking = function(track_pageview_option) {
416
+ var previous_tracked_url = '';
417
+ var tracked = this.track_pageview();
418
+ if (tracked) {
419
+ previous_tracked_url = _.info.currentUrl();
420
+ }
421
+
422
+ if (_.include(['full-url', 'url-with-path-and-query-string', 'url-with-path'], track_pageview_option)) {
423
+ window.addEventListener('popstate', function() {
424
+ window.dispatchEvent(new Event('mp_locationchange'));
425
+ });
426
+ window.addEventListener('hashchange', function() {
427
+ window.dispatchEvent(new Event('mp_locationchange'));
428
+ });
429
+ var nativePushState = window.history.pushState;
430
+ if (typeof nativePushState === 'function') {
431
+ window.history.pushState = function(state, unused, url) {
432
+ nativePushState.call(window.history, state, unused, url);
433
+ window.dispatchEvent(new Event('mp_locationchange'));
434
+ };
435
+ }
436
+ var nativeReplaceState = window.history.replaceState;
437
+ if (typeof nativeReplaceState === 'function') {
438
+ window.history.replaceState = function(state, unused, url) {
439
+ nativeReplaceState.call(window.history, state, unused, url);
440
+ window.dispatchEvent(new Event('mp_locationchange'));
441
+ };
442
+ }
443
+ window.addEventListener('mp_locationchange', function() {
444
+ var current_url = _.info.currentUrl();
445
+ var should_track = false;
446
+ if (track_pageview_option === 'full-url') {
447
+ should_track = current_url !== previous_tracked_url;
448
+ } else if (track_pageview_option === 'url-with-path-and-query-string') {
449
+ should_track = current_url.split('#')[0] !== previous_tracked_url.split('#')[0];
450
+ } else if (track_pageview_option === 'url-with-path') {
451
+ should_track = current_url.split('#')[0].split('?')[0] !== previous_tracked_url.split('#')[0].split('?')[0];
452
+ }
453
+
454
+ if (should_track) {
455
+ var tracked = this.track_pageview();
456
+ if (tracked) {
457
+ previous_tracked_url = current_url;
458
+ }
459
+ }
460
+ }.bind(this));
461
+ }
462
+ };
463
+
399
464
  /**
400
465
  * _prepare_callback() should be called by callers of _send_request for use
401
466
  * as the callback argument.
@@ -864,7 +929,7 @@ MixpanelLib.prototype.track = addOptOutCheckMixpanelLib(function(event_name, pro
864
929
  // update properties with pageview info and super-properties
865
930
  properties = _.extend(
866
931
  {},
867
- _.info.properties(),
932
+ _.info.properties({'mp_loader': this.get_config('mp_loader')}),
868
933
  marketing_properties,
869
934
  this['persistence'].properties(),
870
935
  this.unpersisted_superprops,
@@ -1028,10 +1093,9 @@ MixpanelLib.prototype.get_group = function (group_key, group_id) {
1028
1093
 
1029
1094
  /**
1030
1095
  * Track a default Mixpanel page view event, which includes extra default event properties to
1031
- * improve page view data. The `config.track_pageview` option for <a href="#mixpanelinit">mixpanel.init()</a>
1032
- * may be turned on for tracking page loads automatically.
1096
+ * improve page view data.
1033
1097
  *
1034
- * ### Usage
1098
+ * ### Usage:
1035
1099
  *
1036
1100
  * // track a default $mp_web_page_view event
1037
1101
  * mixpanel.track_pageview();
@@ -1048,6 +1112,23 @@ MixpanelLib.prototype.get_group = function (group_key, group_id) {
1048
1112
  * // views on different products or internal applications that are considered completely separate
1049
1113
  * mixpanel.track_pageview({'page': 'customer-search'}, {'event_name': '[internal] Admin Page View'});
1050
1114
  *
1115
+ * ### Notes:
1116
+ *
1117
+ * The `config.track_pageview` option for <a href="#mixpanelinit">mixpanel.init()</a>
1118
+ * may be turned on for tracking page loads automatically.
1119
+ *
1120
+ * // track only page loads
1121
+ * mixpanel.init(PROJECT_TOKEN, {track_pageview: true});
1122
+ *
1123
+ * // track when the URL changes in any manner
1124
+ * mixpanel.init(PROJECT_TOKEN, {track_pageview: 'full-url'});
1125
+ *
1126
+ * // track when the URL changes, ignoring any changes in the hash part
1127
+ * mixpanel.init(PROJECT_TOKEN, {track_pageview: 'url-with-path-and-query-string'});
1128
+ *
1129
+ * // track when the path changes, ignoring any query parameter or hash changes
1130
+ * mixpanel.init(PROJECT_TOKEN, {track_pageview: 'url-with-path'});
1131
+ *
1051
1132
  * @param {Object} [properties] An optional set of additional properties to send with the page view event
1052
1133
  * @param {Object} [options] Page view tracking options
1053
1134
  * @param {String} [options.event_name] - Alternate name for the tracking event
@@ -1798,7 +1879,7 @@ MixpanelLib.prototype._gdpr_call_func = function(func, options) {
1798
1879
  /**
1799
1880
  * Opt the user in to data tracking and cookies/localstorage for this Mixpanel instance
1800
1881
  *
1801
- * ### Usage
1882
+ * ### Usage:
1802
1883
  *
1803
1884
  * // opt user in
1804
1885
  * mixpanel.opt_in_tracking();
@@ -1838,7 +1919,7 @@ MixpanelLib.prototype.opt_in_tracking = function(options) {
1838
1919
  /**
1839
1920
  * Opt the user out of data tracking and cookies/localstorage for this Mixpanel instance
1840
1921
  *
1841
- * ### Usage
1922
+ * ### Usage:
1842
1923
  *
1843
1924
  * // opt user out
1844
1925
  * mixpanel.opt_out_tracking();
@@ -1879,7 +1960,7 @@ MixpanelLib.prototype.opt_out_tracking = function(options) {
1879
1960
  /**
1880
1961
  * Check whether the user has opted in to data tracking and cookies/localstorage for this Mixpanel instance
1881
1962
  *
1882
- * ### Usage
1963
+ * ### Usage:
1883
1964
  *
1884
1965
  * var has_opted_in = mixpanel.has_opted_in_tracking();
1885
1966
  * // use has_opted_in value
@@ -1896,7 +1977,7 @@ MixpanelLib.prototype.has_opted_in_tracking = function(options) {
1896
1977
  /**
1897
1978
  * Check whether the user has opted out of data tracking and cookies/localstorage for this Mixpanel instance
1898
1979
  *
1899
- * ### Usage
1980
+ * ### Usage:
1900
1981
  *
1901
1982
  * var has_opted_out = mixpanel.has_opted_out_tracking();
1902
1983
  * // use has_opted_out value
@@ -1913,7 +1994,7 @@ MixpanelLib.prototype.has_opted_out_tracking = function(options) {
1913
1994
  /**
1914
1995
  * Clear the user's opt in/out status of data tracking and cookies/localstorage for this Mixpanel instance
1915
1996
  *
1916
- * ### Usage
1997
+ * ### Usage:
1917
1998
  *
1918
1999
  * // clear user's opt-in/out status
1919
2000
  * mixpanel.clear_opt_in_out_tracking();
@@ -57,7 +57,6 @@ MixpanelPeople.prototype.set = addOptOutCheckMixpanelPeople(function(prop, to, c
57
57
  data[SET_ACTION] = _.extend(
58
58
  {},
59
59
  _.info.people_properties(),
60
- this._mixpanel['persistence'].get_referrer_info(),
61
60
  data[SET_ACTION]
62
61
  );
63
62
  return this._send_request(data, callback);
package/src/utils.js CHANGED
@@ -899,6 +899,7 @@ _.UUID = (function() {
899
899
  // sending false tracking data
900
900
  var BLOCKED_UA_STRS = [
901
901
  'ahrefsbot',
902
+ 'ahrefssiteaudit',
902
903
  'baiduspider',
903
904
  'bingbot',
904
905
  'bingpreview',
@@ -1619,7 +1620,14 @@ _.info = {
1619
1620
  return '';
1620
1621
  },
1621
1622
 
1622
- properties: function() {
1623
+ currentUrl: function() {
1624
+ return win.location.href;
1625
+ },
1626
+
1627
+ properties: function(extra_props) {
1628
+ if (typeof extra_props !== 'object') {
1629
+ extra_props = {};
1630
+ }
1623
1631
  return _.extend(_.strip_empty_properties({
1624
1632
  '$os': _.info.os(),
1625
1633
  '$browser': _.info.browser(userAgent, navigator.vendor, windowOpera),
@@ -1627,7 +1635,7 @@ _.info = {
1627
1635
  '$referring_domain': _.info.referringDomain(document.referrer),
1628
1636
  '$device': _.info.device(userAgent)
1629
1637
  }), {
1630
- '$current_url': win.location.href,
1638
+ '$current_url': _.info.currentUrl(),
1631
1639
  '$browser_version': _.info.browserVersion(userAgent, navigator.vendor, windowOpera),
1632
1640
  '$screen_height': screen.height,
1633
1641
  '$screen_width': screen.width,
@@ -1635,7 +1643,7 @@ _.info = {
1635
1643
  '$lib_version': Config.LIB_VERSION,
1636
1644
  '$insert_id': cheap_guid(),
1637
1645
  'time': _.timestamp() / 1000 // epoch time in seconds
1638
- });
1646
+ }, _.strip_empty_properties(extra_props));
1639
1647
  },
1640
1648
 
1641
1649
  people_properties: function() {
@@ -1,4 +0,0 @@
1
- /* eslint camelcase: "off" */
2
- import { init_from_snippet } from './mixpanel-core';
3
-
4
- init_from_snippet();