mixpanel-browser 2.67.0 → 2.69.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.
@@ -460,6 +460,30 @@ function querySelectorAll$1(n2, selectors) {
460
460
  function mutationObserverCtor$1() {
461
461
  return getUntaintedPrototype$1("MutationObserver").constructor;
462
462
  }
463
+ function patch$1(source, name, replacement) {
464
+ try {
465
+ if (!(name in source)) {
466
+ return function() {};
467
+ }
468
+ var original = source[name];
469
+ var wrapped = replacement(original);
470
+ if (typeof wrapped === "function") {
471
+ wrapped.prototype = wrapped.prototype || {};
472
+ Object.defineProperties(wrapped, {
473
+ __rrweb_original__: {
474
+ enumerable: false,
475
+ value: original
476
+ }
477
+ });
478
+ }
479
+ source[name] = wrapped;
480
+ return function() {
481
+ source[name] = original;
482
+ };
483
+ } catch (e) {
484
+ return function() {};
485
+ }
486
+ }
463
487
  var index$1 = {
464
488
  childNodes: childNodes$1,
465
489
  parentNode: parentNode$1,
@@ -472,7 +496,8 @@ var index$1 = {
472
496
  shadowRoot: shadowRoot$1,
473
497
  querySelector: querySelector$1,
474
498
  querySelectorAll: querySelectorAll$1,
475
- mutationObserver: mutationObserverCtor$1
499
+ mutationObserver: mutationObserverCtor$1,
500
+ patch: patch$1
476
501
  };
477
502
  function isElement(n2) {
478
503
  return n2.nodeType === n2.ELEMENT_NODE;
@@ -731,26 +756,82 @@ function absolutifyURLs(cssText, href) {
731
756
  return "url(" + maybeQuote + stack.join("/") + maybeQuote + ")";
732
757
  });
733
758
  }
734
- function normalizeCssString(cssText) {
735
- return cssText.replace(/(\/\*[^*]*\*\/)|[\s;]/g, "");
759
+ function normalizeCssString(cssText, _testNoPxNorm) {
760
+ if (_testNoPxNorm === void 0) _testNoPxNorm = false;
761
+ if (_testNoPxNorm) {
762
+ return cssText.replace(/(\/\*[^*]*\*\/)|[\s;]/g, "");
763
+ } else {
764
+ return cssText.replace(/(\/\*[^*]*\*\/)|[\s;]/g, "").replace(/0px/g, "0");
765
+ }
736
766
  }
737
- function splitCssText(cssText, style) {
767
+ function splitCssText(cssText, style, _testNoPxNorm) {
768
+ if (_testNoPxNorm === void 0) _testNoPxNorm = false;
738
769
  var childNodes2 = Array.from(style.childNodes);
739
770
  var splits = [];
771
+ var iterCount = 0;
740
772
  if (childNodes2.length > 1 && cssText && typeof cssText === "string") {
741
- var cssTextNorm = normalizeCssString(cssText);
773
+ var cssTextNorm = normalizeCssString(cssText, _testNoPxNorm);
774
+ var normFactor = cssTextNorm.length / cssText.length;
742
775
  for(var i2 = 1; i2 < childNodes2.length; i2++){
743
776
  if (childNodes2[i2].textContent && typeof childNodes2[i2].textContent === "string") {
744
- var textContentNorm = normalizeCssString(childNodes2[i2].textContent);
745
- for(var j = 3; j < textContentNorm.length; j++){
746
- var bit = textContentNorm.substring(0, j);
747
- if (cssTextNorm.split(bit).length === 2) {
748
- var splitNorm = cssTextNorm.indexOf(bit);
749
- for(var k = splitNorm; k < cssText.length; k++){
750
- if (normalizeCssString(cssText.substring(0, k)).length === splitNorm) {
777
+ var textContentNorm = normalizeCssString(childNodes2[i2].textContent, _testNoPxNorm);
778
+ var jLimit = 100;
779
+ var j = 3;
780
+ for(; j < textContentNorm.length; j++){
781
+ if (// keep consuming css identifiers (to get a decent chunk more quickly)
782
+ textContentNorm[j].match(/[a-zA-Z0-9]/) || // substring needs to be unique to this section
783
+ textContentNorm.indexOf(textContentNorm.substring(0, j), 1) !== -1) {
784
+ continue;
785
+ }
786
+ break;
787
+ }
788
+ for(; j < textContentNorm.length; j++){
789
+ var startSubstring = textContentNorm.substring(0, j);
790
+ var cssNormSplits = cssTextNorm.split(startSubstring);
791
+ var splitNorm = -1;
792
+ if (cssNormSplits.length === 2) {
793
+ splitNorm = cssNormSplits[0].length;
794
+ } else if (cssNormSplits.length > 2 && cssNormSplits[0] === "" && childNodes2[i2 - 1].textContent !== "") {
795
+ splitNorm = cssTextNorm.indexOf(startSubstring, 1);
796
+ } else if (cssNormSplits.length === 1) {
797
+ startSubstring = startSubstring.substring(0, startSubstring.length - 1);
798
+ cssNormSplits = cssTextNorm.split(startSubstring);
799
+ if (cssNormSplits.length <= 1) {
800
+ splits.push(cssText);
801
+ return splits;
802
+ }
803
+ j = jLimit + 1;
804
+ } else if (j === textContentNorm.length - 1) {
805
+ splitNorm = cssTextNorm.indexOf(startSubstring);
806
+ }
807
+ if (cssNormSplits.length >= 2 && j > jLimit) {
808
+ var prevTextContent = childNodes2[i2 - 1].textContent;
809
+ if (prevTextContent && typeof prevTextContent === "string") {
810
+ var prevMinLength = normalizeCssString(prevTextContent).length;
811
+ splitNorm = cssTextNorm.indexOf(startSubstring, prevMinLength);
812
+ }
813
+ if (splitNorm === -1) {
814
+ splitNorm = cssNormSplits[0].length;
815
+ }
816
+ }
817
+ if (splitNorm !== -1) {
818
+ var k = Math.floor(splitNorm / normFactor);
819
+ for(; k > 0 && k < cssText.length;){
820
+ iterCount += 1;
821
+ if (iterCount > 50 * childNodes2.length) {
822
+ splits.push(cssText);
823
+ return splits;
824
+ }
825
+ var normPart = normalizeCssString(cssText.substring(0, k), _testNoPxNorm);
826
+ if (normPart.length === splitNorm) {
751
827
  splits.push(cssText.substring(0, k));
752
828
  cssText = cssText.substring(k);
829
+ cssTextNorm = cssTextNorm.substring(splitNorm);
753
830
  break;
831
+ } else if (normPart.length < splitNorm) {
832
+ k += Math.max(1, Math.floor((splitNorm - normPart.length) / normFactor));
833
+ } else {
834
+ k -= Math.max(1, Math.floor((normPart.length - splitNorm) * normFactor));
754
835
  }
755
836
  }
756
837
  break;
@@ -1267,7 +1348,7 @@ function slimDOMExcluded(sn, slimDOMOptions) {
1267
1348
  } else if (sn.type === NodeType$3.Element) {
1268
1349
  if (slimDOMOptions.script && // script tag
1269
1350
  (sn.tagName === "script" || // (module)preload link
1270
- sn.tagName === "link" && (sn.attributes.rel === "preload" || sn.attributes.rel === "modulepreload") && sn.attributes.as === "script" || // prefetch link
1351
+ sn.tagName === "link" && (sn.attributes.rel === "preload" && sn.attributes.as === "script" || sn.attributes.rel === "modulepreload") || // prefetch link
1271
1352
  sn.tagName === "link" && sn.attributes.rel === "prefetch" && typeof sn.attributes.href === "string" && extractFileExtension(sn.attributes.href) === "js")) {
1272
1353
  return true;
1273
1354
  } else if (slimDOMOptions.headFavicon && (sn.tagName === "link" && sn.attributes.rel === "shortcut icon" || sn.tagName === "meta" && (lowerIfExists(sn.attributes.name).match(/^msapplication-tile(image|color)$/) || lowerIfExists(sn.attributes.name) === "application-name" || lowerIfExists(sn.attributes.rel) === "icon" || lowerIfExists(sn.attributes.rel) === "apple-touch-icon" || lowerIfExists(sn.attributes.rel) === "shortcut icon"))) {
@@ -5764,11 +5845,16 @@ function getTagName(n2) {
5764
5845
  function adaptCssForReplay(cssText, cache) {
5765
5846
  var cachedStyle = cache == null ? void 0 : cache.stylesWithHoverClass.get(cssText);
5766
5847
  if (cachedStyle) return cachedStyle;
5767
- var ast = postcss$1$1([
5768
- mediaSelectorPlugin,
5769
- pseudoClassPlugin
5770
- ]).process(cssText);
5771
- var result2 = ast.css;
5848
+ var result2 = cssText;
5849
+ try {
5850
+ var ast = postcss$1$1([
5851
+ mediaSelectorPlugin,
5852
+ pseudoClassPlugin
5853
+ ]).process(cssText);
5854
+ result2 = ast.css;
5855
+ } catch (error) {
5856
+ console.warn("Failed to adapt css for replay", error);
5857
+ }
5772
5858
  cache == null ? void 0 : cache.stylesWithHoverClass.set(cssText, result2);
5773
5859
  return result2;
5774
5860
  }
@@ -5790,11 +5876,39 @@ function applyCssSplits(n2, cssText, hackCss, cache) {
5790
5876
  while(cssTextSplits.length > 1 && cssTextSplits.length > childTextNodes.length){
5791
5877
  cssTextSplits.splice(-2, 2, cssTextSplits.slice(-2).join(""));
5792
5878
  }
5879
+ var adaptedCss = "";
5880
+ if (hackCss) {
5881
+ adaptedCss = adaptCssForReplay(cssTextSplits.join(""), cache);
5882
+ }
5883
+ var startIndex = 0;
5793
5884
  for(var i2 = 0; i2 < childTextNodes.length; i2++){
5885
+ if (i2 === cssTextSplits.length) {
5886
+ break;
5887
+ }
5794
5888
  var childTextNode = childTextNodes[i2];
5795
- var cssTextSection = cssTextSplits[i2];
5796
- if (childTextNode && cssTextSection) {
5797
- childTextNode.textContent = hackCss ? adaptCssForReplay(cssTextSection, cache) : cssTextSection;
5889
+ if (!hackCss) {
5890
+ childTextNode.textContent = cssTextSplits[i2];
5891
+ } else if (i2 < cssTextSplits.length - 1) {
5892
+ var endIndex = startIndex;
5893
+ var endSearch = cssTextSplits[i2 + 1].length;
5894
+ endSearch = Math.min(endSearch, 30);
5895
+ var found = false;
5896
+ for(; endSearch > 2; endSearch--){
5897
+ var searchBit = cssTextSplits[i2 + 1].substring(0, endSearch);
5898
+ var searchIndex = adaptedCss.substring(startIndex).indexOf(searchBit);
5899
+ found = searchIndex !== -1;
5900
+ if (found) {
5901
+ endIndex += searchIndex;
5902
+ break;
5903
+ }
5904
+ }
5905
+ if (!found) {
5906
+ endIndex += cssTextSplits[i2].length;
5907
+ }
5908
+ childTextNode.textContent = adaptedCss.substring(startIndex, endIndex);
5909
+ startIndex = endIndex;
5910
+ } else {
5911
+ childTextNode.textContent = adaptedCss.substring(startIndex);
5798
5912
  }
5799
5913
  }
5800
5914
  }
@@ -5918,7 +6032,7 @@ function buildNode(n2, options) {
5918
6032
  } else if (tagName === "meta" && n2.attributes["http-equiv"] === "Content-Security-Policy" && name === "content") {
5919
6033
  node2.setAttribute("csp-content", value.toString());
5920
6034
  continue;
5921
- } else if (tagName === "link" && (n2.attributes.rel === "preload" || n2.attributes.rel === "modulepreload") && n2.attributes.as === "script") {} else if (tagName === "link" && n2.attributes.rel === "prefetch" && typeof n2.attributes.href === "string" && n2.attributes.href.endsWith(".js")) {} else if (tagName === "img" && n2.attributes.srcset && n2.attributes.rr_dataURL) {
6035
+ } else if (tagName === "link" && (n2.attributes.rel === "preload" && n2.attributes.as === "script" || n2.attributes.rel === "modulepreload")) {} else if (tagName === "link" && n2.attributes.rel === "prefetch" && typeof n2.attributes.href === "string" && extractFileExtension(n2.attributes.href) === "js") {} else if (tagName === "img" && n2.attributes.srcset && n2.attributes.rr_dataURL) {
5922
6036
  node2.setAttribute("rrweb-original-srcset", n2.attributes.srcset);
5923
6037
  } else {
5924
6038
  node2.setAttribute(name, value.toString());
@@ -11791,6 +11905,30 @@ function querySelectorAll(n2, selectors) {
11791
11905
  function mutationObserverCtor() {
11792
11906
  return getUntaintedPrototype("MutationObserver").constructor;
11793
11907
  }
11908
+ function patch(source, name, replacement) {
11909
+ try {
11910
+ if (!(name in source)) {
11911
+ return function() {};
11912
+ }
11913
+ var original = source[name];
11914
+ var wrapped = replacement(original);
11915
+ if (typeof wrapped === "function") {
11916
+ wrapped.prototype = wrapped.prototype || {};
11917
+ Object.defineProperties(wrapped, {
11918
+ __rrweb_original__: {
11919
+ enumerable: false,
11920
+ value: original
11921
+ }
11922
+ });
11923
+ }
11924
+ source[name] = wrapped;
11925
+ return function() {
11926
+ source[name] = original;
11927
+ };
11928
+ } catch (e) {
11929
+ return function() {};
11930
+ }
11931
+ }
11794
11932
  var index = {
11795
11933
  childNodes: childNodes,
11796
11934
  parentNode: parentNode,
@@ -11803,7 +11941,8 @@ var index = {
11803
11941
  shadowRoot: shadowRoot,
11804
11942
  querySelector: querySelector,
11805
11943
  querySelectorAll: querySelectorAll,
11806
- mutationObserver: mutationObserverCtor
11944
+ mutationObserver: mutationObserverCtor,
11945
+ patch: patch
11807
11946
  };
11808
11947
  function on(type, fn, target) {
11809
11948
  if (target === void 0) target = document;
@@ -11896,30 +12035,6 @@ function hookSetter(target, key, d, isRevoked, win) {
11896
12035
  return hookSetter(target, key, original || {}, true);
11897
12036
  };
11898
12037
  }
11899
- function patch(source, name, replacement) {
11900
- try {
11901
- if (!(name in source)) {
11902
- return function() {};
11903
- }
11904
- var original = source[name];
11905
- var wrapped = replacement(original);
11906
- if (typeof wrapped === "function") {
11907
- wrapped.prototype = wrapped.prototype || {};
11908
- Object.defineProperties(wrapped, {
11909
- __rrweb_original__: {
11910
- enumerable: false,
11911
- value: original
11912
- }
11913
- });
11914
- }
11915
- source[name] = wrapped;
11916
- return function() {
11917
- source[name] = original;
11918
- };
11919
- } catch (e) {
11920
- return function() {};
11921
- }
11922
- }
11923
12038
  var nowTimestamp = Date.now;
11924
12039
  if (!/* @__PURE__ */ /[1-9][0-9]{12}/.test(Date.now().toString())) {
11925
12040
  nowTimestamp = function() {
@@ -12202,7 +12317,6 @@ var utils = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty(
12202
12317
  return nowTimestamp;
12203
12318
  },
12204
12319
  on: on,
12205
- patch: patch,
12206
12320
  polyfill: polyfill$1,
12207
12321
  queueToResolveTrees: queueToResolveTrees,
12208
12322
  shadowHostInDom: shadowHostInDom,
@@ -12659,9 +12773,17 @@ var MutationBuffer = /*#__PURE__*/ function() {
12659
12773
  _this.attributes.push(item);
12660
12774
  _this.attributeMap.set(textarea, item);
12661
12775
  }
12662
- item.attributes.value = Array.from(index.childNodes(textarea), function(cn) {
12776
+ var value = Array.from(index.childNodes(textarea), function(cn) {
12663
12777
  return index.textContent(cn) || "";
12664
12778
  }).join("");
12779
+ item.attributes.value = maskInputValue({
12780
+ element: textarea,
12781
+ maskInputOptions: _this.maskInputOptions,
12782
+ tagName: textarea.tagName,
12783
+ type: getInputType(textarea),
12784
+ value: value,
12785
+ maskInputFn: _this.maskInputFn
12786
+ });
12665
12787
  });
12666
12788
  __publicField(this, "processMutation", function(m) {
12667
12789
  if (isIgnored(m.target, _this.mirror, _this.slimDOMOptions)) {
@@ -15462,8 +15584,15 @@ function record(options) {
15462
15584
  }, window));
15463
15585
  }
15464
15586
  return function() {
15465
- handlers.forEach(function(h) {
15466
- return h();
15587
+ handlers.forEach(function(handler) {
15588
+ try {
15589
+ handler();
15590
+ } catch (error) {
15591
+ var msg = String(error).toLowerCase();
15592
+ if (!msg.includes("cross-origin")) {
15593
+ console.warn(error);
15594
+ }
15595
+ }
15467
15596
  });
15468
15597
  processedNodeManager.destroy();
15469
15598
  recording = false;
@@ -17093,21 +17222,21 @@ var Replayer = /*#__PURE__*/ function() {
17093
17222
  event: event
17094
17223
  }
17095
17224
  });
17096
- var last_index = _this.service.state.context.events.length - 1;
17097
- if (!_this.config.liveMode && event === _this.service.state.context.events[last_index]) {
17225
+ var lastIndex = _this.service.state.context.events.length - 1;
17226
+ if (!_this.config.liveMode && event === _this.service.state.context.events[lastIndex]) {
17098
17227
  var finish = function() {
17099
- if (last_index < _this.service.state.context.events.length - 1) {
17228
+ if (lastIndex < _this.service.state.context.events.length - 1) {
17100
17229
  return;
17101
17230
  }
17102
17231
  _this.backToNormal();
17103
17232
  _this.service.send("END");
17104
17233
  _this.emitter.emit(ReplayerEvents.Finish);
17105
17234
  };
17106
- var finish_buffer = 50;
17235
+ var finishBuffer = 50;
17107
17236
  if (event.type === EventType.IncrementalSnapshot && event.data.source === IncrementalSource.MouseMove && event.data.positions.length) {
17108
- finish_buffer += Math.max(0, -event.data.positions[0].timeOffset);
17237
+ finishBuffer += Math.max(0, -event.data.positions[0].timeOffset);
17109
17238
  }
17110
- setTimeout(finish, finish_buffer);
17239
+ setTimeout(finish, finishBuffer);
17111
17240
  }
17112
17241
  _this.emitter.emit(ReplayerEvents.EventCast, event);
17113
17242
  };
@@ -18300,8 +18429,9 @@ var Replayer = /*#__PURE__*/ function() {
18300
18429
  if (attributeName === "_cssText" && (target.nodeName === "LINK" || target.nodeName === "STYLE")) {
18301
18430
  try {
18302
18431
  var newSn = mirror2.getMeta(target);
18303
- Object.assign(newSn.attributes, mutation.attributes);
18304
- var newNode = buildNodeWithSN(newSn, {
18432
+ var newNode = buildNodeWithSN(_extends({}, newSn, {
18433
+ attributes: _extends({}, newSn.attributes, mutation.attributes)
18434
+ }), {
18305
18435
  doc: target.ownerDocument,
18306
18436
  // can be Document or RRDocument
18307
18437
  mirror: mirror2,
@@ -18309,6 +18439,7 @@ var Replayer = /*#__PURE__*/ function() {
18309
18439
  hackCss: true,
18310
18440
  cache: _this.cache
18311
18441
  });
18442
+ Object.assign(newSn.attributes, mutation.attributes);
18312
18443
  var siblingNode = target.nextSibling;
18313
18444
  var parentNode2 = target.parentNode;
18314
18445
  if (newNode && parentNode2) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mixpanel-browser",
3
- "version": "2.67.0",
3
+ "version": "2.69.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",
@@ -23,6 +23,18 @@
23
23
  "validate": "npm ls"
24
24
  },
25
25
  "types": "./src/index.d.ts",
26
+ "exports": {
27
+ "./src/loaders/loader-module-core": {
28
+ "types": "./src/index.d.ts",
29
+ "import": "./src/loaders/loader-module-core.js",
30
+ "require": "./src/loaders/loader-module-core.js"
31
+ },
32
+ "./src/loaders/loader-module-with-async-recorder": {
33
+ "types": "./src/index.d.ts",
34
+ "import": "./src/loaders/loader-module-with-async-recorder.js",
35
+ "require": "./src/loaders/loader-module-with-async-recorder.js"
36
+ }
37
+ },
26
38
  "repository": {
27
39
  "type": "git",
28
40
  "url": "https://github.com/mixpanel/mixpanel-js.git"
@@ -66,6 +78,6 @@
66
78
  "webpack": "1.12.2"
67
79
  },
68
80
  "dependencies": {
69
- "rrweb": "2.0.0-alpha.18"
81
+ "@mixpanel/rrweb": "2.0.0-alpha.18.1"
70
82
  }
71
83
  }
package/rollup.config.mjs CHANGED
@@ -8,7 +8,7 @@ const COMPILED_RRWEB_PATH = 'build/rrweb-compiled.js';
8
8
 
9
9
  const aliasRrweb = () => alias({
10
10
  entries: [
11
- { find: 'rrweb', replacement: COMPILED_RRWEB_PATH },
11
+ { find: '@mixpanel/rrweb', replacement: COMPILED_RRWEB_PATH },
12
12
  ]
13
13
  });
14
14
 
@@ -24,7 +24,7 @@ const MINIFY = process.env.MINIFY || process.env.FULL;
24
24
  const MAIN_BUILDS = [
25
25
  // compile rrweb first to es5 with swc, we'll replace the import later on
26
26
  {
27
- 'input': 'rrweb',
27
+ 'input': '@mixpanel/rrweb',
28
28
  'output': [
29
29
  {
30
30
  file: COMPILED_RRWEB_PATH,
@@ -5,6 +5,7 @@ import {
5
5
  EV_CHANGE, EV_CLICK, EV_HASHCHANGE, EV_MP_LOCATION_CHANGE, EV_POPSTATE,
6
6
  EV_SCROLLEND, EV_SUBMIT
7
7
  } from './utils';
8
+ import { RageClickTracker } from './rageclick';
8
9
 
9
10
  var AUTOCAPTURE_CONFIG_KEY = 'autocapture';
10
11
  var LEGACY_PAGEVIEW_CONFIG_KEY = 'track_pageview';
@@ -27,6 +28,7 @@ var CONFIG_SCROLL_CHECKPOINTS = 'scroll_depth_percent_checkpoints';
27
28
  var CONFIG_TRACK_CLICK = 'click';
28
29
  var CONFIG_TRACK_INPUT = 'input';
29
30
  var CONFIG_TRACK_PAGEVIEW = 'pageview';
31
+ var CONFIG_TRACK_RAGE_CLICK = 'rage_click';
30
32
  var CONFIG_TRACK_SCROLL = 'scroll';
31
33
  var CONFIG_TRACK_SUBMIT = 'submit';
32
34
 
@@ -44,6 +46,7 @@ CONFIG_DEFAULTS[CONFIG_SCROLL_CHECKPOINTS] = [25, 50, 75, 100];
44
46
  CONFIG_DEFAULTS[CONFIG_TRACK_CLICK] = true;
45
47
  CONFIG_DEFAULTS[CONFIG_TRACK_INPUT] = true;
46
48
  CONFIG_DEFAULTS[CONFIG_TRACK_PAGEVIEW] = PAGEVIEW_OPTION_FULL_URL;
49
+ CONFIG_DEFAULTS[CONFIG_TRACK_RAGE_CLICK] = true;
47
50
  CONFIG_DEFAULTS[CONFIG_TRACK_SCROLL] = true;
48
51
  CONFIG_DEFAULTS[CONFIG_TRACK_SUBMIT] = true;
49
52
 
@@ -53,6 +56,7 @@ var DEFAULT_PROPS = {
53
56
 
54
57
  var MP_EV_CLICK = '$mp_click';
55
58
  var MP_EV_INPUT = '$mp_input_change';
59
+ var MP_EV_RAGE_CLICK = '$mp_rage_click';
56
60
  var MP_EV_SCROLL = '$mp_scroll';
57
61
  var MP_EV_SUBMIT = '$mp_submit';
58
62
 
@@ -75,6 +79,7 @@ Autocapture.prototype.init = function() {
75
79
  this.initInputTracking();
76
80
  this.initScrollTracking();
77
81
  this.initSubmitTracking();
82
+ this.initRageClickTracking();
78
83
  };
79
84
 
80
85
  Autocapture.prototype.getFullConfig = function() {
@@ -153,6 +158,11 @@ Autocapture.prototype.trackDomEvent = function(ev, mpEventName) {
153
158
  return;
154
159
  }
155
160
 
161
+ var isCapturedForHeatMap = this.mp.is_recording_heatmap_data() && (
162
+ (mpEventName === MP_EV_CLICK && !this.getConfig(CONFIG_TRACK_CLICK)) ||
163
+ (mpEventName === MP_EV_RAGE_CLICK && !this._getRageClickConfig())
164
+ );
165
+
156
166
  var props = getPropsForDOMEvent(ev, {
157
167
  allowElementCallback: this.getConfig(CONFIG_ALLOW_ELEMENT_CALLBACK),
158
168
  allowSelectors: this.getConfig(CONFIG_ALLOW_SELECTORS),
@@ -161,7 +171,7 @@ Autocapture.prototype.trackDomEvent = function(ev, mpEventName) {
161
171
  blockSelectors: this.getConfig(CONFIG_BLOCK_SELECTORS),
162
172
  captureExtraAttrs: this.getConfig(CONFIG_CAPTURE_EXTRA_ATTRS),
163
173
  captureTextContent: this.getConfig(CONFIG_CAPTURE_TEXT_CONTENT),
164
- capturedForHeatMap: mpEventName === MP_EV_CLICK && !this.getConfig(CONFIG_TRACK_CLICK) && this.mp.is_recording_heatmap_data(),
174
+ capturedForHeatMap: isCapturedForHeatMap,
165
175
  });
166
176
  if (props) {
167
177
  _.extend(props, DEFAULT_PROPS);
@@ -169,6 +179,24 @@ Autocapture.prototype.trackDomEvent = function(ev, mpEventName) {
169
179
  }
170
180
  };
171
181
 
182
+ Autocapture.prototype._getRageClickConfig = function() {
183
+ var config = this.getConfig(CONFIG_TRACK_RAGE_CLICK);
184
+
185
+ if (!config) {
186
+ return null; // rage click tracking disabled
187
+ }
188
+
189
+ if (config === true) {
190
+ return {}; // use defaults
191
+ }
192
+
193
+ if (typeof config === 'object') {
194
+ return config; // use custom configuration
195
+ }
196
+
197
+ return {}; // fallback to defaults for any other truthy value
198
+ };
199
+
172
200
  Autocapture.prototype.initClickTracking = function() {
173
201
  window.removeEventListener(EV_CLICK, this.listenerClick);
174
202
 
@@ -270,6 +298,36 @@ Autocapture.prototype.initPageviewTracking = function() {
270
298
  }.bind(this)));
271
299
  };
272
300
 
301
+ Autocapture.prototype.initRageClickTracking = function() {
302
+ window.removeEventListener(EV_CLICK, this.listenerRageClick);
303
+
304
+ var rageClickConfig = this._getRageClickConfig();
305
+ if (!rageClickConfig && !this.mp.get_config('record_heatmap_data')) {
306
+ return;
307
+ }
308
+
309
+ logger.log('Initializing rage click tracking');
310
+ if (!this._rageClickTracker) {
311
+ this._rageClickTracker = new RageClickTracker();
312
+ }
313
+
314
+ this.listenerRageClick = function(ev) {
315
+ var currentRageClickConfig = this._getRageClickConfig();
316
+ if (!currentRageClickConfig && !this.mp.is_recording_heatmap_data()) {
317
+ return;
318
+ }
319
+
320
+ if (this.currentUrlBlocked()) {
321
+ return;
322
+ }
323
+
324
+ if (this._rageClickTracker.isRageClick(ev['pageX'], ev['pageY'], currentRageClickConfig)) {
325
+ this.trackDomEvent(ev, MP_EV_RAGE_CLICK);
326
+ }
327
+ }.bind(this);
328
+ window.addEventListener(EV_CLICK, this.listenerRageClick);
329
+ };
330
+
273
331
  Autocapture.prototype.initScrollTracking = function() {
274
332
  window.removeEventListener(EV_SCROLLEND, this.listenerScroll);
275
333
 
@@ -0,0 +1,38 @@
1
+ /** @const */ var DEFAULT_RAGE_CLICK_THRESHOLD_PX = 30;
2
+ /** @const */ var DEFAULT_RAGE_CLICK_TIMEOUT_MS = 1000;
3
+ /** @const */ var DEFAULT_RAGE_CLICK_CLICK_COUNT = 4;
4
+
5
+ function RageClickTracker() {
6
+ this.clicks = [];
7
+ }
8
+
9
+ RageClickTracker.prototype.isRageClick = function(x, y, options) {
10
+ options = options || {};
11
+ var thresholdPx = options['threshold_px'] || DEFAULT_RAGE_CLICK_THRESHOLD_PX;
12
+ var timeoutMs = options['timeout_ms'] || DEFAULT_RAGE_CLICK_TIMEOUT_MS;
13
+ var clickCount = options['click_count'] || DEFAULT_RAGE_CLICK_CLICK_COUNT;
14
+ var timestamp = Date.now();
15
+
16
+ var lastClick = this.clicks[this.clicks.length - 1];
17
+ if (
18
+ lastClick &&
19
+ timestamp - lastClick.timestamp < timeoutMs &&
20
+ Math.sqrt(Math.pow(x - lastClick.x, 2) + Math.pow(y - lastClick.y, 2)) < thresholdPx
21
+ ) {
22
+ this.clicks.push({ x: x, y: y, timestamp: timestamp });
23
+ if (this.clicks.length >= clickCount) {
24
+ this.clicks = [];
25
+ return true;
26
+ }
27
+ } else {
28
+ this.clicks = [{ x: x, y: y, timestamp: timestamp }];
29
+ }
30
+ return false;
31
+ };
32
+
33
+ export {
34
+ RageClickTracker,
35
+ DEFAULT_RAGE_CLICK_THRESHOLD_PX,
36
+ DEFAULT_RAGE_CLICK_TIMEOUT_MS,
37
+ DEFAULT_RAGE_CLICK_CLICK_COUNT
38
+ };
package/src/config.js CHANGED
@@ -1,6 +1,6 @@
1
1
  var Config = {
2
2
  DEBUG: false,
3
- LIB_VERSION: '2.67.0'
3
+ LIB_VERSION: '2.69.0'
4
4
  };
5
5
 
6
6
  export default Config;
@@ -17,6 +17,7 @@ CONFIG_DEFAULTS[CONFIG_CONTEXT] = {};
17
17
  var FeatureFlagManager = function(initOptions) {
18
18
  this.getFullApiRoute = initOptions.getFullApiRoute;
19
19
  this.getMpConfig = initOptions.getConfigFunc;
20
+ this.setMpConfig = initOptions.setConfigFunc;
20
21
  this.getMpProperty = initOptions.getPropertyFunc;
21
22
  this.track = initOptions.trackingFunc;
22
23
  };
@@ -54,6 +55,23 @@ FeatureFlagManager.prototype.isSystemEnabled = function() {
54
55
  return !!this.getMpConfig(FLAGS_CONFIG_KEY);
55
56
  };
56
57
 
58
+ FeatureFlagManager.prototype.updateContext = function(newContext, options) {
59
+ if (!this.isSystemEnabled()) {
60
+ logger.critical('Feature Flags not enabled, cannot update context');
61
+ return Promise.resolve();
62
+ }
63
+
64
+ var ffConfig = this.getMpConfig(FLAGS_CONFIG_KEY);
65
+ if (!_.isObject(ffConfig)) {
66
+ ffConfig = {};
67
+ }
68
+ var oldContext = (options && options['replace']) ? {} : this.getConfig(CONFIG_CONTEXT);
69
+ ffConfig[CONFIG_CONTEXT] = _.extend({}, oldContext, newContext);
70
+
71
+ this.setMpConfig(FLAGS_CONFIG_KEY, ffConfig);
72
+ return this.fetchFlags();
73
+ };
74
+
57
75
  FeatureFlagManager.prototype.areFlagsReady = function() {
58
76
  if (!this.isSystemEnabled()) {
59
77
  logger.error('Feature Flags not enabled');
@@ -63,7 +81,7 @@ FeatureFlagManager.prototype.areFlagsReady = function() {
63
81
 
64
82
  FeatureFlagManager.prototype.fetchFlags = function() {
65
83
  if (!this.isSystemEnabled()) {
66
- return;
84
+ return Promise.resolve();
67
85
  }
68
86
 
69
87
  var distinctId = this.getMpProperty('distinct_id');
@@ -103,6 +121,8 @@ FeatureFlagManager.prototype.fetchFlags = function() {
103
121
  this.markFetchComplete();
104
122
  logger.error(error);
105
123
  }.bind(this));
124
+
125
+ return this.fetchPromise;
106
126
  };
107
127
 
108
128
  FeatureFlagManager.prototype.markFetchComplete = function() {
@@ -215,6 +235,7 @@ FeatureFlagManager.prototype['get_variant_value'] = FeatureFlagManager.prototype
215
235
  FeatureFlagManager.prototype['get_variant_value_sync'] = FeatureFlagManager.prototype.getVariantValueSync;
216
236
  FeatureFlagManager.prototype['is_enabled'] = FeatureFlagManager.prototype.isEnabled;
217
237
  FeatureFlagManager.prototype['is_enabled_sync'] = FeatureFlagManager.prototype.isEnabledSync;
238
+ FeatureFlagManager.prototype['update_context'] = FeatureFlagManager.prototype.updateContext;
218
239
 
219
240
  // Deprecated method
220
241
  FeatureFlagManager.prototype['get_feature_data'] = FeatureFlagManager.prototype.getFeatureData;