mixpanel-browser 2.41.0 → 2.42.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/.github/workflows/tests.yml +25 -0
- package/CHANGELOG.md +6 -0
- package/README.md +4 -2
- package/dist/mixpanel.amd.js +32 -43
- package/dist/mixpanel.cjs.js +32 -43
- package/dist/mixpanel.globals.js +32 -43
- package/dist/mixpanel.min.js +139 -139
- package/dist/mixpanel.umd.js +32 -43
- package/doc/build-docs.js +16 -0
- package/doc/readme.io/javascript-full-api-reference.md +18 -0
- package/package.json +3 -3
- package/src/config.js +1 -1
- package/src/mixpanel-core.js +26 -19
- package/src/mixpanel-group.js +4 -0
- package/src/request-batcher.js +2 -2
- package/src/utils.js +0 -23
- package/tunnel.log +0 -0
- package/.travis.yml +0 -6
package/dist/mixpanel.umd.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
var Config = {
|
|
8
8
|
DEBUG: false,
|
|
9
|
-
LIB_VERSION: '2.
|
|
9
|
+
LIB_VERSION: '2.42.0'
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
// since es6 imports are static and we run unit tests from the console, window won't be defined when importing this file
|
|
@@ -1688,28 +1688,6 @@
|
|
|
1688
1688
|
return maxlen ? guid.substring(0, maxlen) : guid;
|
|
1689
1689
|
};
|
|
1690
1690
|
|
|
1691
|
-
/**
|
|
1692
|
-
* Check deterministically whether to include or exclude from a feature rollout/test based on the
|
|
1693
|
-
* given string and the desired percentage to include.
|
|
1694
|
-
* @param {String} str - string to run the check against (for instance a project's token)
|
|
1695
|
-
* @param {String} feature - name of feature (for inclusion in hash, to ensure different results
|
|
1696
|
-
* for different features)
|
|
1697
|
-
* @param {Number} percent_allowed - percentage chance that a given string will be included
|
|
1698
|
-
* @returns {Boolean} whether the given string should be included
|
|
1699
|
-
*/
|
|
1700
|
-
var determine_eligibility = _.safewrap(function(str, feature, percent_allowed) {
|
|
1701
|
-
str = str + feature;
|
|
1702
|
-
|
|
1703
|
-
// Bernstein's hash: http://www.cse.yorku.ca/~oz/hash.html#djb2
|
|
1704
|
-
var hash = 5381;
|
|
1705
|
-
for (var i = 0; i < str.length; i++) {
|
|
1706
|
-
hash = ((hash << 5) + hash) + str.charCodeAt(i);
|
|
1707
|
-
hash = hash & hash;
|
|
1708
|
-
}
|
|
1709
|
-
var dart = (hash >>> 0) % 100;
|
|
1710
|
-
return dart < percent_allowed;
|
|
1711
|
-
});
|
|
1712
|
-
|
|
1713
1691
|
// naive way to extract domain name (example.com) from full hostname (my.sub.example.com)
|
|
1714
1692
|
var SIMPLE_DOMAIN_MATCH_REGEX = /[a-z0-9][a-z0-9-]*\.[a-z]+$/i;
|
|
1715
1693
|
// this next one attempts to account for some ccSLDs, e.g. extracting oxford.ac.uk from www.oxford.ac.uk
|
|
@@ -2448,9 +2426,9 @@
|
|
|
2448
2426
|
} else if (
|
|
2449
2427
|
_.isObject(res) &&
|
|
2450
2428
|
res.xhr_req &&
|
|
2451
|
-
(res.xhr_req['status'] >= 500 || res.xhr_req['status']
|
|
2429
|
+
(res.xhr_req['status'] >= 500 || res.xhr_req['status'] === 429 || res.error === 'timeout')
|
|
2452
2430
|
) {
|
|
2453
|
-
// network or API error, retry
|
|
2431
|
+
// network or API error, or 429 Too Many Requests, retry
|
|
2454
2432
|
var retryMS = this.flushInterval * 2;
|
|
2455
2433
|
var headers = res.xhr_req['responseHeaders'];
|
|
2456
2434
|
if (headers) {
|
|
@@ -3020,9 +2998,13 @@
|
|
|
3020
2998
|
* Permanently delete a group.
|
|
3021
2999
|
*
|
|
3022
3000
|
* ### Usage:
|
|
3001
|
+
*
|
|
3023
3002
|
* mixpanel.get_group('company', 'mixpanel').delete();
|
|
3003
|
+
*
|
|
3004
|
+
* @param {Function} [callback] If provided, the callback will be called after the tracking event
|
|
3024
3005
|
*/
|
|
3025
3006
|
MixpanelGroup.prototype['delete'] = addOptOutCheckMixpanelGroup(function(callback) {
|
|
3007
|
+
// bracket notation above prevents a minification error related to reserved words
|
|
3026
3008
|
var data = this.delete_action();
|
|
3027
3009
|
return this._send_request(data, callback);
|
|
3028
3010
|
});
|
|
@@ -5924,7 +5906,7 @@
|
|
|
5924
5906
|
'inapp_protocol': '//',
|
|
5925
5907
|
'inapp_link_new_window': false,
|
|
5926
5908
|
'ignore_dnt': false,
|
|
5927
|
-
'batch_requests':
|
|
5909
|
+
'batch_requests': true,
|
|
5928
5910
|
'batch_size': 50,
|
|
5929
5911
|
'batch_flush_interval_ms': 5000,
|
|
5930
5912
|
'batch_request_timeout_ms': 90000,
|
|
@@ -6043,17 +6025,7 @@
|
|
|
6043
6025
|
this['config'] = {};
|
|
6044
6026
|
this['_triggered_notifs'] = [];
|
|
6045
6027
|
|
|
6046
|
-
|
|
6047
|
-
// (only if they have not specified a value in their init config
|
|
6048
|
-
// and they aren't using a custom API host)
|
|
6049
|
-
var variable_features = {};
|
|
6050
|
-
var api_host = config['api_host'];
|
|
6051
|
-
var is_custom_api = !!api_host && !api_host.match(/\.mixpanel\.com$/);
|
|
6052
|
-
if (!('batch_requests' in config) && !is_custom_api && determine_eligibility(token, 'batch', 60)) {
|
|
6053
|
-
variable_features['batch_requests'] = true;
|
|
6054
|
-
}
|
|
6055
|
-
|
|
6056
|
-
this.set_config(_.extend({}, DEFAULT_CONFIG, variable_features, config, {
|
|
6028
|
+
this.set_config(_.extend({}, DEFAULT_CONFIG, config, {
|
|
6057
6029
|
'name': name,
|
|
6058
6030
|
'token': token,
|
|
6059
6031
|
'callback_fn': ((name === PRIMARY_INSTANCE_NAME) ? name : PRIMARY_INSTANCE_NAME + '.' + name) + '._jsc'
|
|
@@ -6079,15 +6051,32 @@
|
|
|
6079
6051
|
} else {
|
|
6080
6052
|
this.init_batchers();
|
|
6081
6053
|
if (sendBeacon && window$1.addEventListener) {
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6054
|
+
// Before page closes or hides (user tabs away etc), attempt to flush any events
|
|
6055
|
+
// queued up via navigator.sendBeacon. Since sendBeacon doesn't report success/failure,
|
|
6056
|
+
// events will not be removed from the persistent store; if the site is loaded again,
|
|
6057
|
+
// the events will be flushed again on startup and deduplicated on the Mixpanel server
|
|
6058
|
+
// side.
|
|
6059
|
+
// There is no reliable way to capture only page close events, so we lean on the
|
|
6060
|
+
// visibilitychange and pagehide events as recommended at
|
|
6061
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/Window/unload_event#usage_notes.
|
|
6062
|
+
// These events fire when the user clicks away from the current page/tab, so will occur
|
|
6063
|
+
// more frequently than page unload, but are the only mechanism currently for capturing
|
|
6064
|
+
// this scenario somewhat reliably.
|
|
6065
|
+
var flush_on_unload = _.bind(function() {
|
|
6087
6066
|
if (!this.request_batchers.events.stopped) {
|
|
6088
6067
|
this.request_batchers.events.flush({unloading: true});
|
|
6089
6068
|
}
|
|
6090
|
-
}, this)
|
|
6069
|
+
}, this);
|
|
6070
|
+
window$1.addEventListener('pagehide', function(ev) {
|
|
6071
|
+
if (ev['persisted']) {
|
|
6072
|
+
flush_on_unload();
|
|
6073
|
+
}
|
|
6074
|
+
});
|
|
6075
|
+
window$1.addEventListener('visibilitychange', function() {
|
|
6076
|
+
if (document$1['visibilityState'] === 'hidden') {
|
|
6077
|
+
flush_on_unload();
|
|
6078
|
+
}
|
|
6079
|
+
});
|
|
6091
6080
|
}
|
|
6092
6081
|
}
|
|
6093
6082
|
}
|
package/doc/build-docs.js
CHANGED
|
@@ -97,6 +97,22 @@ function doxToMD(items) {
|
|
|
97
97
|
});
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
// Captures prototype bracket notation property assignment, e.g.:
|
|
101
|
+
// `MixpanelGroup.prototype['delete'] = addOptOutCheckMixpanelGroup(function(callback) {`
|
|
102
|
+
// Based on https://github.com/tj/dox/blob/9fe92e17dfcd31c9b6512f6e5bf0b52c2b6b84d4/lib/dox.js#L592
|
|
103
|
+
dox.contextPatternMatchers.push(function (str) {
|
|
104
|
+
if (/^\s*([\w$.]+)\s*\.\s*prototype\s*\['\s*([\w$]+)'\]\s*=\s*([^\n;]+)/.exec(str)) {
|
|
105
|
+
return {
|
|
106
|
+
type: 'property'
|
|
107
|
+
, constructor: RegExp.$1
|
|
108
|
+
, cons: RegExp.$1
|
|
109
|
+
, name: RegExp.$2
|
|
110
|
+
, value: RegExp.$3.trim()
|
|
111
|
+
, string: RegExp.$1 + '.prototype.' + RegExp.$2
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
100
116
|
const rawCode = fs.readFileSync(SOURCE_FILE).toString().trim();
|
|
101
117
|
const parsed = dox.parseComments(rawCode);
|
|
102
118
|
|
|
@@ -990,6 +990,24 @@ mixpanel.people.unset(['gender', 'Company']);
|
|
|
990
990
|
# mixpanel.group
|
|
991
991
|
|
|
992
992
|
|
|
993
|
+
___
|
|
994
|
+
## mixpanel.group.delete
|
|
995
|
+
Permanently delete a group.
|
|
996
|
+
|
|
997
|
+
|
|
998
|
+
### Usage:
|
|
999
|
+
|
|
1000
|
+
```javascript
|
|
1001
|
+
mixpanel.get_group('company', 'mixpanel').delete();
|
|
1002
|
+
```
|
|
1003
|
+
|
|
1004
|
+
|
|
1005
|
+
|
|
1006
|
+
| Argument | Type | Description |
|
|
1007
|
+
| ------------- | ------------- | ----- |
|
|
1008
|
+
| **callback** | <span class="mp-arg-type">Function</span></br></span><span class="mp-arg-optional">optional</span> | If provided, the callback will be called after the tracking event |
|
|
1009
|
+
|
|
1010
|
+
|
|
993
1011
|
___
|
|
994
1012
|
## mixpanel.group.remove
|
|
995
1013
|
Remove a property from a group. The value will be ignored if doesn't exist.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mixpanel-browser",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.42.0",
|
|
4
4
|
"description": "The official Mixpanel JavaScript browser client library",
|
|
5
5
|
"main": "dist/mixpanel.cjs.js",
|
|
6
6
|
"directories": {
|
|
@@ -46,10 +46,10 @@
|
|
|
46
46
|
"jsdom": "11.12.0",
|
|
47
47
|
"jsdom-global": "3.0.2",
|
|
48
48
|
"localStorage": "1.0.4",
|
|
49
|
-
"lodash": "4.17.
|
|
49
|
+
"lodash": "4.17.21",
|
|
50
50
|
"mocha": "7.1.1",
|
|
51
51
|
"morgan": "1.9.1",
|
|
52
|
-
"rdme": "
|
|
52
|
+
"rdme": "4.0.0",
|
|
53
53
|
"request": "2.88.0",
|
|
54
54
|
"rollup": "0.25.8",
|
|
55
55
|
"rollup-plugin-npm": "1.4.0",
|
package/src/config.js
CHANGED
package/src/mixpanel-core.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint camelcase: "off" */
|
|
2
2
|
import Config from './config';
|
|
3
|
-
import { _, console, userAgent, window, document, navigator,
|
|
3
|
+
import { _, console, userAgent, window, document, navigator, slice } from './utils';
|
|
4
4
|
import { FormTracker, LinkTracker } from './dom-trackers';
|
|
5
5
|
import { RequestBatcher } from './request-batcher';
|
|
6
6
|
import { MixpanelGroup } from './mixpanel-group';
|
|
@@ -117,7 +117,7 @@ var DEFAULT_CONFIG = {
|
|
|
117
117
|
'inapp_protocol': '//',
|
|
118
118
|
'inapp_link_new_window': false,
|
|
119
119
|
'ignore_dnt': false,
|
|
120
|
-
'batch_requests':
|
|
120
|
+
'batch_requests': true,
|
|
121
121
|
'batch_size': 50,
|
|
122
122
|
'batch_flush_interval_ms': 5000,
|
|
123
123
|
'batch_request_timeout_ms': 90000,
|
|
@@ -236,17 +236,7 @@ MixpanelLib.prototype._init = function(token, config, name) {
|
|
|
236
236
|
this['config'] = {};
|
|
237
237
|
this['_triggered_notifs'] = [];
|
|
238
238
|
|
|
239
|
-
|
|
240
|
-
// (only if they have not specified a value in their init config
|
|
241
|
-
// and they aren't using a custom API host)
|
|
242
|
-
var variable_features = {};
|
|
243
|
-
var api_host = config['api_host'];
|
|
244
|
-
var is_custom_api = !!api_host && !api_host.match(/\.mixpanel\.com$/);
|
|
245
|
-
if (!('batch_requests' in config) && !is_custom_api && determine_eligibility(token, 'batch', 60)) {
|
|
246
|
-
variable_features['batch_requests'] = true;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
this.set_config(_.extend({}, DEFAULT_CONFIG, variable_features, config, {
|
|
239
|
+
this.set_config(_.extend({}, DEFAULT_CONFIG, config, {
|
|
250
240
|
'name': name,
|
|
251
241
|
'token': token,
|
|
252
242
|
'callback_fn': ((name === PRIMARY_INSTANCE_NAME) ? name : PRIMARY_INSTANCE_NAME + '.' + name) + '._jsc'
|
|
@@ -272,15 +262,32 @@ MixpanelLib.prototype._init = function(token, config, name) {
|
|
|
272
262
|
} else {
|
|
273
263
|
this.init_batchers();
|
|
274
264
|
if (sendBeacon && window.addEventListener) {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
265
|
+
// Before page closes or hides (user tabs away etc), attempt to flush any events
|
|
266
|
+
// queued up via navigator.sendBeacon. Since sendBeacon doesn't report success/failure,
|
|
267
|
+
// events will not be removed from the persistent store; if the site is loaded again,
|
|
268
|
+
// the events will be flushed again on startup and deduplicated on the Mixpanel server
|
|
269
|
+
// side.
|
|
270
|
+
// There is no reliable way to capture only page close events, so we lean on the
|
|
271
|
+
// visibilitychange and pagehide events as recommended at
|
|
272
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/Window/unload_event#usage_notes.
|
|
273
|
+
// These events fire when the user clicks away from the current page/tab, so will occur
|
|
274
|
+
// more frequently than page unload, but are the only mechanism currently for capturing
|
|
275
|
+
// this scenario somewhat reliably.
|
|
276
|
+
var flush_on_unload = _.bind(function() {
|
|
280
277
|
if (!this.request_batchers.events.stopped) {
|
|
281
278
|
this.request_batchers.events.flush({unloading: true});
|
|
282
279
|
}
|
|
283
|
-
}, this)
|
|
280
|
+
}, this);
|
|
281
|
+
window.addEventListener('pagehide', function(ev) {
|
|
282
|
+
if (ev['persisted']) {
|
|
283
|
+
flush_on_unload();
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
window.addEventListener('visibilitychange', function() {
|
|
287
|
+
if (document['visibilityState'] === 'hidden') {
|
|
288
|
+
flush_on_unload();
|
|
289
|
+
}
|
|
290
|
+
});
|
|
284
291
|
}
|
|
285
292
|
}
|
|
286
293
|
}
|
package/src/mixpanel-group.js
CHANGED
|
@@ -110,9 +110,13 @@ MixpanelGroup.prototype.union = addOptOutCheckMixpanelGroup(function(list_name,
|
|
|
110
110
|
* Permanently delete a group.
|
|
111
111
|
*
|
|
112
112
|
* ### Usage:
|
|
113
|
+
*
|
|
113
114
|
* mixpanel.get_group('company', 'mixpanel').delete();
|
|
115
|
+
*
|
|
116
|
+
* @param {Function} [callback] If provided, the callback will be called after the tracking event
|
|
114
117
|
*/
|
|
115
118
|
MixpanelGroup.prototype['delete'] = addOptOutCheckMixpanelGroup(function(callback) {
|
|
119
|
+
// bracket notation above prevents a minification error related to reserved words
|
|
116
120
|
var data = this.delete_action();
|
|
117
121
|
return this._send_request(data, callback);
|
|
118
122
|
});
|
package/src/request-batcher.js
CHANGED
|
@@ -148,9 +148,9 @@ RequestBatcher.prototype.flush = function(options) {
|
|
|
148
148
|
} else if (
|
|
149
149
|
_.isObject(res) &&
|
|
150
150
|
res.xhr_req &&
|
|
151
|
-
(res.xhr_req['status'] >= 500 || res.xhr_req['status']
|
|
151
|
+
(res.xhr_req['status'] >= 500 || res.xhr_req['status'] === 429 || res.error === 'timeout')
|
|
152
152
|
) {
|
|
153
|
-
// network or API error, retry
|
|
153
|
+
// network or API error, or 429 Too Many Requests, retry
|
|
154
154
|
var retryMS = this.flushInterval * 2;
|
|
155
155
|
var headers = res.xhr_req['responseHeaders'];
|
|
156
156
|
if (headers) {
|
package/src/utils.js
CHANGED
|
@@ -1682,28 +1682,6 @@ var cheap_guid = function(maxlen) {
|
|
|
1682
1682
|
return maxlen ? guid.substring(0, maxlen) : guid;
|
|
1683
1683
|
};
|
|
1684
1684
|
|
|
1685
|
-
/**
|
|
1686
|
-
* Check deterministically whether to include or exclude from a feature rollout/test based on the
|
|
1687
|
-
* given string and the desired percentage to include.
|
|
1688
|
-
* @param {String} str - string to run the check against (for instance a project's token)
|
|
1689
|
-
* @param {String} feature - name of feature (for inclusion in hash, to ensure different results
|
|
1690
|
-
* for different features)
|
|
1691
|
-
* @param {Number} percent_allowed - percentage chance that a given string will be included
|
|
1692
|
-
* @returns {Boolean} whether the given string should be included
|
|
1693
|
-
*/
|
|
1694
|
-
var determine_eligibility = _.safewrap(function(str, feature, percent_allowed) {
|
|
1695
|
-
str = str + feature;
|
|
1696
|
-
|
|
1697
|
-
// Bernstein's hash: http://www.cse.yorku.ca/~oz/hash.html#djb2
|
|
1698
|
-
var hash = 5381;
|
|
1699
|
-
for (var i = 0; i < str.length; i++) {
|
|
1700
|
-
hash = ((hash << 5) + hash) + str.charCodeAt(i);
|
|
1701
|
-
hash = hash & hash;
|
|
1702
|
-
}
|
|
1703
|
-
var dart = (hash >>> 0) % 100;
|
|
1704
|
-
return dart < percent_allowed;
|
|
1705
|
-
});
|
|
1706
|
-
|
|
1707
1685
|
// naive way to extract domain name (example.com) from full hostname (my.sub.example.com)
|
|
1708
1686
|
var SIMPLE_DOMAIN_MATCH_REGEX = /[a-z0-9][a-z0-9-]*\.[a-z]+$/i;
|
|
1709
1687
|
// this next one attempts to account for some ccSLDs, e.g. extracting oxford.ac.uk from www.oxford.ac.uk
|
|
@@ -1762,7 +1740,6 @@ export {
|
|
|
1762
1740
|
navigator,
|
|
1763
1741
|
cheap_guid,
|
|
1764
1742
|
console_with_prefix,
|
|
1765
|
-
determine_eligibility,
|
|
1766
1743
|
extract_domain,
|
|
1767
1744
|
localStorageSupported,
|
|
1768
1745
|
JSONStringify,
|
package/tunnel.log
ADDED
|
File without changes
|