mixpanel-browser 2.39.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.
@@ -95,6 +95,7 @@ RequestQueue.prototype.fillBatch = function(batchSize) {
95
95
  for (var i = 0; i < storedQueue.length; i++) {
96
96
  var item = storedQueue[i];
97
97
  if (new Date().getTime() > item['flushAfter'] && !idsInBatch[item['id']]) {
98
+ item.orphaned = true;
98
99
  batch.push(item);
99
100
  if (batch.length >= batchSize) {
100
101
  break;
@@ -151,6 +152,52 @@ RequestQueue.prototype.removeItemsByID = function(ids, cb) {
151
152
  }, this.pid);
152
153
  };
153
154
 
155
+ // internal helper for RequestQueue.updatePayloads
156
+ var updatePayloads = function(existingItems, itemsToUpdate) {
157
+ var newItems = [];
158
+ _.each(existingItems, function(item) {
159
+ var id = item['id'];
160
+ if (id in itemsToUpdate) {
161
+ var newPayload = itemsToUpdate[id];
162
+ if (newPayload !== null) {
163
+ item['payload'] = newPayload;
164
+ newItems.push(item);
165
+ }
166
+ } else {
167
+ // no update
168
+ newItems.push(item);
169
+ }
170
+ });
171
+ return newItems;
172
+ };
173
+
174
+ /**
175
+ * Update payloads of given items in both in-memory queue and
176
+ * persisted queue. Items set to null are removed from queues.
177
+ */
178
+ RequestQueue.prototype.updatePayloads = function(itemsToUpdate, cb) {
179
+ this.memQueue = updatePayloads(this.memQueue, itemsToUpdate);
180
+ this.lock.withLock(_.bind(function lockAcquired() {
181
+ var succeeded;
182
+ try {
183
+ var storedQueue = this.readFromStorage();
184
+ storedQueue = updatePayloads(storedQueue, itemsToUpdate);
185
+ succeeded = this.saveToStorage(storedQueue);
186
+ } catch(err) {
187
+ logger.error('Error updating items', itemsToUpdate);
188
+ succeeded = false;
189
+ }
190
+ if (cb) {
191
+ cb(succeeded);
192
+ }
193
+ }, this), function lockFailure(err) {
194
+ logger.error('Error acquiring storage lock', err);
195
+ if (cb) {
196
+ cb(false);
197
+ }
198
+ }, this.pid);
199
+ };
200
+
154
201
  /**
155
202
  * Read and parse items array from localStorage entry, handling
156
203
  * malformed/missing data if necessary.
package/src/utils.js CHANGED
@@ -67,6 +67,19 @@ var console = {
67
67
  }
68
68
  },
69
69
  /** @type {function(...*)} */
70
+ warn: function() {
71
+ if (Config.DEBUG && !_.isUndefined(windowConsole) && windowConsole) {
72
+ var args = ['Mixpanel warning:'].concat(_.toArray(arguments));
73
+ try {
74
+ windowConsole.warn.apply(windowConsole, args);
75
+ } catch (err) {
76
+ _.each(args, function(arg) {
77
+ windowConsole.warn(arg);
78
+ });
79
+ }
80
+ }
81
+ },
82
+ /** @type {function(...*)} */
70
83
  error: function() {
71
84
  if (Config.DEBUG && !_.isUndefined(windowConsole) && windowConsole) {
72
85
  var args = ['Mixpanel error:'].concat(_.toArray(arguments));
@@ -232,13 +245,13 @@ _.toArray = function(iterable) {
232
245
  return _.values(iterable);
233
246
  };
234
247
 
235
- _.map = function(arr, callback) {
248
+ _.map = function(arr, callback, context) {
236
249
  if (nativeMap && arr.map === nativeMap) {
237
- return arr.map(callback);
250
+ return arr.map(callback, context);
238
251
  } else {
239
252
  var results = [];
240
253
  _.each(arr, function(item) {
241
- results.push(callback(item));
254
+ results.push(callback.call(context, item));
242
255
  });
243
256
  return results;
244
257
  }
@@ -266,10 +279,6 @@ _.values = function(obj) {
266
279
  return results;
267
280
  };
268
281
 
269
- _.identity = function(value) {
270
- return value;
271
- };
272
-
273
282
  _.include = function(obj, target) {
274
283
  var found = false;
275
284
  if (obj === null) {
@@ -932,9 +941,37 @@ _.UUID = (function() {
932
941
  // _.isBlockedUA()
933
942
  // This is to block various web spiders from executing our JS and
934
943
  // sending false tracking data
944
+ var BLOCKED_UA_STRS = [
945
+ 'baiduspider',
946
+ 'bingbot',
947
+ 'bingpreview',
948
+ 'facebookexternal',
949
+ 'pinterest',
950
+ 'screaming frog',
951
+ 'yahoo! slurp',
952
+ 'yandexbot',
953
+
954
+ // a whole bunch of goog-specific crawlers
955
+ // https://developers.google.com/search/docs/advanced/crawling/overview-google-crawlers
956
+ 'adsbot-google',
957
+ 'apis-google',
958
+ 'duplexweb-google',
959
+ 'feedfetcher-google',
960
+ 'google favicon',
961
+ 'google web preview',
962
+ 'google-read-aloud',
963
+ 'googlebot',
964
+ 'googleweblight',
965
+ 'mediapartners-google',
966
+ 'storebot-google'
967
+ ];
935
968
  _.isBlockedUA = function(ua) {
936
- if (/(google web preview|baiduspider|yandexbot|bingbot|googlebot|yahoo! slurp)/i.test(ua)) {
937
- return true;
969
+ var i;
970
+ ua = ua.toLowerCase();
971
+ for (i = 0; i < BLOCKED_UA_STRS.length; i++) {
972
+ if (ua.indexOf(BLOCKED_UA_STRS[i]) !== -1) {
973
+ return true;
974
+ }
938
975
  }
939
976
  return false;
940
977
  };
@@ -979,10 +1016,6 @@ _.getQueryParam = function(url, param) {
979
1016
  }
980
1017
  };
981
1018
 
982
- _.getHashParam = function(hash, param) {
983
- var matches = hash.match(new RegExp(param + '=([^&]*)'));
984
- return matches ? matches[1] : null;
985
- };
986
1019
 
987
1020
  // _.cookie
988
1021
  // Methods partially borrowed from quirksmode.org/js/cookies.html
@@ -1649,28 +1682,6 @@ var cheap_guid = function(maxlen) {
1649
1682
  return maxlen ? guid.substring(0, maxlen) : guid;
1650
1683
  };
1651
1684
 
1652
- /**
1653
- * Check deterministically whether to include or exclude from a feature rollout/test based on the
1654
- * given string and the desired percentage to include.
1655
- * @param {String} str - string to run the check against (for instance a project's token)
1656
- * @param {String} feature - name of feature (for inclusion in hash, to ensure different results
1657
- * for different features)
1658
- * @param {Number} percent_allowed - percentage chance that a given string will be included
1659
- * @returns {Boolean} whether the given string should be included
1660
- */
1661
- var determine_eligibility = _.safewrap(function(str, feature, percent_allowed) {
1662
- str = str + feature;
1663
-
1664
- // Bernstein's hash: http://www.cse.yorku.ca/~oz/hash.html#djb2
1665
- var hash = 5381;
1666
- for (var i = 0; i < str.length; i++) {
1667
- hash = ((hash << 5) + hash) + str.charCodeAt(i);
1668
- hash = hash & hash;
1669
- }
1670
- var dart = (hash >>> 0) % 100;
1671
- return dart < percent_allowed;
1672
- });
1673
-
1674
1685
  // naive way to extract domain name (example.com) from full hostname (my.sub.example.com)
1675
1686
  var SIMPLE_DOMAIN_MATCH_REGEX = /[a-z0-9][a-z0-9-]*\.[a-z]+$/i;
1676
1687
  // this next one attempts to account for some ccSLDs, e.g. extracting oxford.ac.uk from www.oxford.ac.uk
@@ -1729,9 +1740,9 @@ export {
1729
1740
  navigator,
1730
1741
  cheap_guid,
1731
1742
  console_with_prefix,
1732
- determine_eligibility,
1733
1743
  extract_domain,
1734
1744
  localStorageSupported,
1735
1745
  JSONStringify,
1736
- JSONParse
1746
+ JSONParse,
1747
+ slice
1737
1748
  };
package/tunnel.log ADDED
File without changes
package/.travis.yml DELETED
@@ -1,8 +0,0 @@
1
- node_js:
2
- - '10'
3
- - '12'
4
- language: node_js
5
- script:
6
- - npm test
7
- branches:
8
- only: master
@@ -1,192 +0,0 @@
1
- import { _ } from './utils';
2
-
3
- /*
4
- * Get the className of an element, accounting for edge cases where element.className is an object
5
- * @param {Element} el - element to get the className of
6
- * @returns {string} the element's class
7
- */
8
- export function getClassName(el) {
9
- switch(typeof el.className) {
10
- case 'string':
11
- return el.className;
12
- case 'object': // handle cases where className might be SVGAnimatedString or some other type
13
- return el.className.baseVal || el.getAttribute('class') || '';
14
- default: // future proof
15
- return '';
16
- }
17
- }
18
-
19
- /*
20
- * Get the direct text content of an element, protecting against sensitive data collection.
21
- * Concats textContent of each of the element's text node children; this avoids potential
22
- * collection of sensitive data that could happen if we used element.textContent and the
23
- * element had sensitive child elements, since element.textContent includes child content.
24
- * Scrubs values that look like they could be sensitive (i.e. cc or ssn number).
25
- * @param {Element} el - element to get the text of
26
- * @returns {string} the element's direct text content
27
- */
28
- export function getSafeText(el) {
29
- var elText = '';
30
-
31
- if (shouldTrackElement(el) && el.childNodes && el.childNodes.length) {
32
- _.each(el.childNodes, function(child) {
33
- if (isTextNode(child) && child.textContent) {
34
- elText += _.trim(child.textContent)
35
- // scrub potentially sensitive values
36
- .split(/(\s+)/).filter(shouldTrackValue).join('')
37
- // normalize whitespace
38
- .replace(/[\r\n]/g, ' ').replace(/[ ]+/g, ' ')
39
- // truncate
40
- .substring(0, 255);
41
- }
42
- });
43
- }
44
-
45
- return _.trim(elText);
46
- }
47
-
48
- /*
49
- * Check whether an element has nodeType Node.ELEMENT_NODE
50
- * @param {Element} el - element to check
51
- * @returns {boolean} whether el is of the correct nodeType
52
- */
53
- export function isElementNode(el) {
54
- return el && el.nodeType === 1; // Node.ELEMENT_NODE - use integer constant for browser portability
55
- }
56
-
57
- /*
58
- * Check whether an element is of a given tag type.
59
- * Due to potential reference discrepancies (such as the webcomponents.js polyfill),
60
- * we want to match tagNames instead of specific references because something like
61
- * element === document.body won't always work because element might not be a native
62
- * element.
63
- * @param {Element} el - element to check
64
- * @param {string} tag - tag name (e.g., "div")
65
- * @returns {boolean} whether el is of the given tag type
66
- */
67
- export function isTag(el, tag) {
68
- return el && el.tagName && el.tagName.toLowerCase() === tag.toLowerCase();
69
- }
70
-
71
- /*
72
- * Check whether an element has nodeType Node.TEXT_NODE
73
- * @param {Element} el - element to check
74
- * @returns {boolean} whether el is of the correct nodeType
75
- */
76
- export function isTextNode(el) {
77
- return el && el.nodeType === 3; // Node.TEXT_NODE - use integer constant for browser portability
78
- }
79
-
80
- /*
81
- * Check whether a DOM event should be "tracked" or if it may contain sentitive data
82
- * using a variety of heuristics.
83
- * @param {Element} el - element to check
84
- * @param {Event} event - event to check
85
- * @returns {boolean} whether the event should be tracked
86
- */
87
- export function shouldTrackDomEvent(el, event) {
88
- if (!el || isTag(el, 'html') || !isElementNode(el)) {
89
- return false;
90
- }
91
- var tag = el.tagName.toLowerCase();
92
- switch (tag) {
93
- case 'html':
94
- return false;
95
- case 'form':
96
- return event.type === 'submit';
97
- case 'input':
98
- if (['button', 'submit'].indexOf(el.getAttribute('type')) === -1) {
99
- return event.type === 'change';
100
- } else {
101
- return event.type === 'click';
102
- }
103
- case 'select':
104
- case 'textarea':
105
- return event.type === 'change';
106
- default:
107
- return event.type === 'click';
108
- }
109
- }
110
-
111
- /*
112
- * Check whether a DOM element should be "tracked" or if it may contain sentitive data
113
- * using a variety of heuristics.
114
- * @param {Element} el - element to check
115
- * @returns {boolean} whether the element should be tracked
116
- */
117
- export function shouldTrackElement(el) {
118
- for (var curEl = el; curEl.parentNode && !isTag(curEl, 'body'); curEl = curEl.parentNode) {
119
- var classes = getClassName(curEl).split(' ');
120
- if (_.includes(classes, 'mp-sensitive') || _.includes(classes, 'mp-no-track')) {
121
- return false;
122
- }
123
- }
124
-
125
- if (_.includes(getClassName(el).split(' '), 'mp-include')) {
126
- return true;
127
- }
128
-
129
- // don't send data from inputs or similar elements since there will always be
130
- // a risk of clientside javascript placing sensitive data in attributes
131
- if (
132
- isTag(el, 'input') ||
133
- isTag(el, 'select') ||
134
- isTag(el, 'textarea') ||
135
- el.getAttribute('contenteditable') === 'true'
136
- ) {
137
- return false;
138
- }
139
-
140
- // don't include hidden or password fields
141
- var type = el.type || '';
142
- if (typeof type === 'string') { // it's possible for el.type to be a DOM element if el is a form with a child input[name="type"]
143
- switch(type.toLowerCase()) {
144
- case 'hidden':
145
- return false;
146
- case 'password':
147
- return false;
148
- }
149
- }
150
-
151
- // filter out data from fields that look like sensitive fields
152
- var name = el.name || el.id || '';
153
- if (typeof name === 'string') { // it's possible for el.name or el.id to be a DOM element if el is a form with a child input[name="name"]
154
- var sensitiveNameRegex = /^cc|cardnum|ccnum|creditcard|csc|cvc|cvv|exp|pass|pwd|routing|seccode|securitycode|securitynum|socialsec|socsec|ssn/i;
155
- if (sensitiveNameRegex.test(name.replace(/[^a-zA-Z0-9]/g, ''))) {
156
- return false;
157
- }
158
- }
159
-
160
- return true;
161
- }
162
-
163
- /*
164
- * Check whether a string value should be "tracked" or if it may contain sentitive data
165
- * using a variety of heuristics.
166
- * @param {string} value - string value to check
167
- * @returns {boolean} whether the element should be tracked
168
- */
169
- export function shouldTrackValue(value) {
170
- if (value === null || _.isUndefined(value)) {
171
- return false;
172
- }
173
-
174
- if (typeof value === 'string') {
175
- value = _.trim(value);
176
-
177
- // check to see if input value looks like a credit card number
178
- // see: https://www.safaribooksonline.com/library/view/regular-expressions-cookbook/9781449327453/ch04s20.html
179
- var ccRegex = /^(?:(4[0-9]{12}(?:[0-9]{3})?)|(5[1-5][0-9]{14})|(6(?:011|5[0-9]{2})[0-9]{12})|(3[47][0-9]{13})|(3(?:0[0-5]|[68][0-9])[0-9]{11})|((?:2131|1800|35[0-9]{3})[0-9]{11}))$/;
180
- if (ccRegex.test((value || '').replace(/[- ]/g, ''))) {
181
- return false;
182
- }
183
-
184
- // check to see if input value looks like a social security number
185
- var ssnRegex = /(^\d{3}-?\d{2}-?\d{4}$)/;
186
- if (ssnRegex.test(value)) {
187
- return false;
188
- }
189
- }
190
-
191
- return true;
192
- }