@openreplay/tracker 4.0.0 → 4.1.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.
Files changed (64) hide show
  1. package/cjs/app/guards.d.ts +1 -0
  2. package/cjs/app/guards.js +6 -1
  3. package/cjs/app/index.d.ts +1 -1
  4. package/cjs/app/index.js +15 -9
  5. package/cjs/app/messages.gen.d.ts +1 -0
  6. package/cjs/app/messages.gen.js +10 -1
  7. package/cjs/app/nodes.d.ts +1 -1
  8. package/cjs/app/nodes.js +3 -5
  9. package/cjs/app/observer/iframe_observer.js +1 -0
  10. package/cjs/app/observer/iframe_offsets.d.ts +8 -0
  11. package/cjs/app/observer/iframe_offsets.js +59 -0
  12. package/cjs/app/observer/observer.js +4 -4
  13. package/cjs/app/observer/top_observer.d.ts +2 -4
  14. package/cjs/app/observer/top_observer.js +11 -21
  15. package/cjs/app/sanitizer.d.ts +10 -4
  16. package/cjs/app/sanitizer.js +33 -15
  17. package/cjs/app/session.js +1 -1
  18. package/cjs/common/messages.gen.d.ts +8 -2
  19. package/cjs/common/messages.gen.js +1 -0
  20. package/cjs/index.d.ts +1 -0
  21. package/cjs/index.js +7 -7
  22. package/cjs/modules/constructedStyleSheets.d.ts +4 -0
  23. package/cjs/modules/{adoptedStyleSheets.js → constructedStyleSheets.js} +21 -20
  24. package/cjs/modules/cssrules.js +65 -18
  25. package/cjs/modules/img.js +27 -19
  26. package/cjs/modules/input.js +2 -2
  27. package/cjs/modules/mouse.js +11 -7
  28. package/cjs/modules/scroll.js +32 -12
  29. package/cjs/utils.d.ts +5 -3
  30. package/cjs/utils.js +18 -13
  31. package/lib/app/guards.d.ts +1 -0
  32. package/lib/app/guards.js +4 -0
  33. package/lib/app/index.d.ts +1 -1
  34. package/lib/app/index.js +15 -9
  35. package/lib/app/messages.gen.d.ts +1 -0
  36. package/lib/app/messages.gen.js +8 -0
  37. package/lib/app/nodes.d.ts +1 -1
  38. package/lib/app/nodes.js +3 -5
  39. package/lib/app/observer/iframe_observer.js +1 -0
  40. package/lib/app/observer/iframe_offsets.d.ts +8 -0
  41. package/lib/app/observer/iframe_offsets.js +56 -0
  42. package/lib/app/observer/observer.js +4 -4
  43. package/lib/app/observer/top_observer.d.ts +2 -4
  44. package/lib/app/observer/top_observer.js +11 -21
  45. package/lib/app/sanitizer.d.ts +10 -4
  46. package/lib/app/sanitizer.js +32 -15
  47. package/lib/app/session.js +1 -1
  48. package/lib/common/messages.gen.d.ts +8 -2
  49. package/lib/common/messages.gen.js +1 -0
  50. package/lib/common/tsconfig.tsbuildinfo +1 -1
  51. package/lib/index.d.ts +1 -0
  52. package/lib/index.js +5 -6
  53. package/lib/modules/constructedStyleSheets.d.ts +4 -0
  54. package/lib/modules/{adoptedStyleSheets.js → constructedStyleSheets.js} +20 -21
  55. package/lib/modules/cssrules.js +67 -19
  56. package/lib/modules/img.js +28 -20
  57. package/lib/modules/input.js +3 -3
  58. package/lib/modules/mouse.js +11 -7
  59. package/lib/modules/scroll.js +33 -13
  60. package/lib/utils.d.ts +5 -3
  61. package/lib/utils.js +17 -11
  62. package/package.json +1 -1
  63. package/cjs/modules/adoptedStyleSheets.d.ts +0 -2
  64. package/lib/modules/adoptedStyleSheets.d.ts +0 -2
package/cjs/index.js CHANGED
@@ -1,12 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Messages = exports.App = void 0;
3
+ exports.SanitizeLevel = exports.Messages = exports.App = void 0;
4
4
  const index_js_1 = require("./app/index.js");
5
5
  var index_js_2 = require("./app/index.js");
6
6
  Object.defineProperty(exports, "App", { enumerable: true, get: function () { return index_js_2.default; } });
7
7
  const messages_gen_js_1 = require("./app/messages.gen.js");
8
8
  const _Messages = require("./app/messages.gen.js");
9
9
  exports.Messages = _Messages;
10
+ var sanitizer_js_1 = require("./app/sanitizer.js");
11
+ Object.defineProperty(exports, "SanitizeLevel", { enumerable: true, get: function () { return sanitizer_js_1.SanitizeLevel; } });
10
12
  const connection_js_1 = require("./modules/connection.js");
11
13
  const console_js_1 = require("./modules/console.js");
12
14
  const exception_js_1 = require("./modules/exception.js");
@@ -18,7 +20,7 @@ const performance_js_1 = require("./modules/performance.js");
18
20
  const scroll_js_1 = require("./modules/scroll.js");
19
21
  const viewport_js_1 = require("./modules/viewport.js");
20
22
  const cssrules_js_1 = require("./modules/cssrules.js");
21
- const adoptedStyleSheets_js_1 = require("./modules/adoptedStyleSheets.js");
23
+ const constructedStyleSheets_js_1 = require("./modules/constructedStyleSheets.js");
22
24
  const utils_js_1 = require("./utils.js");
23
25
  const DOCS_SETUP = '/installation/setup-or';
24
26
  function processOptions(obj) {
@@ -97,7 +99,7 @@ class API {
97
99
  if (app !== null) {
98
100
  (0, viewport_js_1.default)(app);
99
101
  (0, cssrules_js_1.default)(app);
100
- (0, adoptedStyleSheets_js_1.default)(app);
102
+ (0, constructedStyleSheets_js_1.default)(app);
101
103
  (0, connection_js_1.default)(app);
102
104
  (0, console_js_1.default)(app, options);
103
105
  (0, exception_js_1.default)(app, options);
@@ -131,7 +133,7 @@ class API {
131
133
  // no-cors issue only with text/plain or not-set Content-Type
132
134
  // req.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
133
135
  req.send(JSON.stringify({
134
- trackerVersion: '4.0.0',
136
+ trackerVersion: '4.0.1',
135
137
  projectKey: options.projectKey,
136
138
  doNotTrack,
137
139
  // TODO: add precise reason (an exact API missing)
@@ -163,9 +165,7 @@ class API {
163
165
  return;
164
166
  }
165
167
  this.app.stop();
166
- const sessionHash = this.app.session.getSessionHash();
167
- this.app.session.reset();
168
- return sessionHash;
168
+ return this.app.session.getSessionHash();
169
169
  }
170
170
  getSessionToken() {
171
171
  if (this.app === null) {
@@ -0,0 +1,4 @@
1
+ import type App from '../app/index.js';
2
+ export declare function nextID(): number;
3
+ export declare const styleSheetIDMap: Map<CSSStyleSheet, number>;
4
+ export default function (app: App | null): void;
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.styleSheetIDMap = exports.nextID = void 0;
3
4
  const messages_gen_js_1 = require("../app/messages.gen.js");
4
5
  const guards_js_1 = require("../app/guards.js");
5
6
  function hasAdoptedSS(node) {
@@ -7,6 +8,13 @@ function hasAdoptedSS(node) {
7
8
  // @ts-ignore
8
9
  !!node.adoptedStyleSheets);
9
10
  }
11
+ // TODO: incapsulate to be init-ed on-start and join with cssrules.ts under one folder
12
+ let _id = 0xf;
13
+ function nextID() {
14
+ return _id++;
15
+ }
16
+ exports.nextID = nextID;
17
+ exports.styleSheetIDMap = new Map();
10
18
  function default_1(app) {
11
19
  if (app === null) {
12
20
  return;
@@ -18,10 +26,9 @@ function default_1(app) {
18
26
  });
19
27
  return;
20
28
  }
21
- let nextID = 0xf;
22
29
  const styleSheetIDMap = new Map();
23
30
  const adoptedStyleSheetsOwnings = new Map();
24
- const updateAdoptedStyleSheets = (root) => {
31
+ const sendAdoptedStyleSheetsUpdate = (root) => {
25
32
  let nodeID = app.nodes.getID(root);
26
33
  if (root === document) {
27
34
  nodeID = 0; // main document doesn't have nodeID. ID count starts from the documentElement
@@ -39,7 +46,7 @@ function default_1(app) {
39
46
  let sheetID = styleSheetIDMap.get(s);
40
47
  const init = !sheetID;
41
48
  if (!sheetID) {
42
- sheetID = ++nextID;
49
+ sheetID = nextID();
43
50
  }
44
51
  nowOwning.push(sheetID);
45
52
  if (!pastOwning.includes(sheetID)) {
@@ -65,12 +72,20 @@ function default_1(app) {
65
72
  Object.defineProperty(prototype, 'adoptedStyleSheets', Object.assign(Object.assign({}, nativeAdoptedStyleSheetsDescriptor), { set: function (value) {
66
73
  // @ts-ignore
67
74
  const retVal = nativeAdoptedStyleSheetsDescriptor.set.call(this, value);
68
- updateAdoptedStyleSheets(this);
75
+ sendAdoptedStyleSheetsUpdate(this);
69
76
  return retVal;
70
77
  } }));
71
78
  }
72
79
  }
73
80
  const patchContext = (context) => {
81
+ // @ts-ignore
82
+ if (context.__openreplay_adpss_patched__) {
83
+ return;
84
+ }
85
+ else {
86
+ // @ts-ignore
87
+ context.__openreplay_adpss_patched__ = true;
88
+ }
74
89
  patchAdoptedStyleSheets(context.Document.prototype);
75
90
  patchAdoptedStyleSheets(context.ShadowRoot.prototype);
76
91
  //@ts-ignore TODO: configure ts (use necessary lib)
@@ -93,20 +108,6 @@ function default_1(app) {
93
108
  }
94
109
  return replaceSync.call(this, text);
95
110
  };
96
- context.CSSStyleSheet.prototype.insertRule = function (rule, index = 0) {
97
- const sheetID = styleSheetIDMap.get(this);
98
- if (sheetID) {
99
- app.send((0, messages_gen_js_1.AdoptedSSInsertRuleURLBased)(sheetID, rule, index, app.getBaseHref()));
100
- }
101
- return insertRule.call(this, rule, index);
102
- };
103
- context.CSSStyleSheet.prototype.deleteRule = function (index) {
104
- const sheetID = styleSheetIDMap.get(this);
105
- if (sheetID) {
106
- app.send((0, messages_gen_js_1.AdoptedSSDeleteRule)(sheetID, index));
107
- }
108
- return deleteRule.call(this, index);
109
- };
110
111
  };
111
112
  patchContext(window);
112
113
  app.observer.attachContextCallback(patchContext);
@@ -116,11 +117,11 @@ function default_1(app) {
116
117
  });
117
118
  // So far main Document is not triggered with nodeCallbacks
118
119
  app.attachStartCallback(() => {
119
- updateAdoptedStyleSheets(document);
120
+ sendAdoptedStyleSheetsUpdate(document);
120
121
  });
121
122
  app.nodes.attachNodeCallback((node) => {
122
123
  if (hasAdoptedSS(node)) {
123
- updateAdoptedStyleSheets(node);
124
+ sendAdoptedStyleSheetsUpdate(node);
124
125
  }
125
126
  });
126
127
  }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const messages_gen_js_1 = require("../app/messages.gen.js");
4
4
  const guards_js_1 = require("../app/guards.js");
5
+ const constructedStyleSheets_js_1 = require("./constructedStyleSheets.js");
5
6
  function default_1(app) {
6
7
  if (app === null) {
7
8
  return;
@@ -10,42 +11,88 @@ function default_1(app) {
10
11
  app.send((0, messages_gen_js_1.TechnicalInfo)('no_stylesheet_prototype_in_window', ''));
11
12
  return;
12
13
  }
13
- const processOperation = app.safe((stylesheet, index, rule) => {
14
- const sendMessage = typeof rule === 'string'
15
- ? (nodeID) => app.send((0, messages_gen_js_1.CSSInsertRuleURLBased)(nodeID, rule, index, app.getBaseHref()))
16
- : (nodeID) => app.send((0, messages_gen_js_1.CSSDeleteRule)(nodeID, index));
17
- // TODO: Extend messages to maintain nested rules (CSSGroupingRule prototype, as well as CSSKeyframesRule)
18
- if (stylesheet.ownerNode == null) {
19
- throw new Error('Owner Node not found');
20
- }
21
- const nodeID = app.nodes.getID(stylesheet.ownerNode);
22
- if (nodeID !== undefined) {
23
- sendMessage(nodeID);
24
- } // else error?
14
+ const sendInserDeleteRule = app.safe((sheet, index, rule) => {
15
+ const sheetID = constructedStyleSheets_js_1.styleSheetIDMap.get(sheet);
16
+ if (!sheetID) {
17
+ // OK-case. Sheet haven't been registered yet. Rules will be sent on registration.
18
+ return;
19
+ }
20
+ if (typeof rule === 'string') {
21
+ app.send((0, messages_gen_js_1.AdoptedSSInsertRuleURLBased)(sheetID, rule, index, app.getBaseHref()));
22
+ }
23
+ else {
24
+ app.send((0, messages_gen_js_1.AdoptedSSDeleteRule)(sheetID, index));
25
+ }
26
+ });
27
+ // TODO: proper rule insertion/removal (how?)
28
+ const sendReplaceGroupingRule = app.safe((rule) => {
29
+ let topmostRule = rule;
30
+ while (topmostRule.parentRule) {
31
+ topmostRule = topmostRule.parentRule;
32
+ }
33
+ const sheet = topmostRule.parentStyleSheet;
34
+ if (!sheet) {
35
+ app.debug.warn('No parent StyleSheet found for', topmostRule, rule);
36
+ return;
37
+ }
38
+ const sheetID = constructedStyleSheets_js_1.styleSheetIDMap.get(sheet);
39
+ if (!sheetID) {
40
+ app.debug.warn('No sheedID found for', sheet, constructedStyleSheets_js_1.styleSheetIDMap);
41
+ return;
42
+ }
43
+ const cssText = topmostRule.cssText;
44
+ const ruleList = sheet.cssRules;
45
+ const idx = Array.from(ruleList).indexOf(topmostRule);
46
+ if (idx >= 0) {
47
+ app.send((0, messages_gen_js_1.AdoptedSSInsertRuleURLBased)(sheetID, cssText, idx, app.getBaseHref()));
48
+ app.send((0, messages_gen_js_1.AdoptedSSDeleteRule)(sheetID, idx + 1)); // Remove previous clone
49
+ }
50
+ else {
51
+ app.debug.warn('Rule index not found in', sheet, topmostRule);
52
+ }
25
53
  });
26
54
  const patchContext = (context) => {
27
55
  const { insertRule, deleteRule } = context.CSSStyleSheet.prototype;
56
+ const { insertRule: groupInsertRule, deleteRule: groupDeleteRule } = context.CSSGroupingRule.prototype;
28
57
  context.CSSStyleSheet.prototype.insertRule = function (rule, index = 0) {
29
- processOperation(this, index, rule);
58
+ sendInserDeleteRule(this, index, rule);
30
59
  return insertRule.call(this, rule, index);
31
60
  };
32
61
  context.CSSStyleSheet.prototype.deleteRule = function (index) {
33
- processOperation(this, index);
62
+ sendInserDeleteRule(this, index);
34
63
  return deleteRule.call(this, index);
35
64
  };
65
+ context.CSSGroupingRule.prototype.insertRule = function (rule, index = 0) {
66
+ const result = groupInsertRule.call(this, rule, index);
67
+ sendReplaceGroupingRule(this);
68
+ return result;
69
+ };
70
+ context.CSSGroupingRule.prototype.deleteRule = function (index = 0) {
71
+ const result = groupDeleteRule.call(this, index);
72
+ sendReplaceGroupingRule(this);
73
+ return result;
74
+ };
36
75
  };
37
76
  patchContext(window);
38
77
  app.observer.attachContextCallback(patchContext);
39
78
  app.nodes.attachNodeCallback((node) => {
40
- if (!(0, guards_js_1.hasTag)(node, 'STYLE') || !node.sheet) {
79
+ if (!((0, guards_js_1.hasTag)(node, 'STYLE') || (0, guards_js_1.hasTag)(node, 'style')) || !node.sheet) {
41
80
  return;
42
81
  }
43
82
  if (node.textContent !== null && node.textContent.trim().length > 0) {
44
- return; // Only fully virtual sheets maintained so far
83
+ return; // Non-virtual styles captured by the observer as a text
84
+ }
85
+ const nodeID = app.nodes.getID(node);
86
+ if (!nodeID) {
87
+ return;
45
88
  }
46
- const rules = node.sheet.cssRules;
89
+ const sheet = node.sheet;
90
+ const sheetID = (0, constructedStyleSheets_js_1.nextID)();
91
+ constructedStyleSheets_js_1.styleSheetIDMap.set(sheet, sheetID);
92
+ app.send((0, messages_gen_js_1.AdoptedSSAddOwner)(sheetID, nodeID));
93
+ const rules = sheet.cssRules;
47
94
  for (let i = 0; i < rules.length; i++) {
48
- processOperation(node.sheet, i, rules[i].cssText);
95
+ sendInserDeleteRule(sheet, i, rules[i].cssText);
49
96
  }
50
97
  });
51
98
  }
@@ -10,7 +10,7 @@ function resolveURL(url, location = document.location) {
10
10
  }
11
11
  else if (url.startsWith('http://') ||
12
12
  url.startsWith('https://') ||
13
- url.startsWith('data:') // any other possible value here?
13
+ url.startsWith('data:') // any other possible value here? https://bugzilla.mozilla.org/show_bug.cgi?id=1758035
14
14
  ) {
15
15
  return url;
16
16
  }
@@ -18,6 +18,10 @@ function resolveURL(url, location = document.location) {
18
18
  return location.origin + location.pathname + url;
19
19
  }
20
20
  }
21
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1607081
22
+ function isSVGInFireFox(url) {
23
+ return utils_js_1.IS_FIREFOX && (url.startsWith('data:image/svg+xml') || url.match(/.svg$|/i));
24
+ }
21
25
  const PLACEHOLDER_SRC = 'https://static.openreplay.com/tracker/placeholder.jpeg';
22
26
  function default_1(app) {
23
27
  function sendPlaceholder(id, node) {
@@ -42,30 +46,34 @@ function default_1(app) {
42
46
  app.send((0, messages_gen_js_1.SetNodeAttribute)(id, 'srcset', resolvedSrcset));
43
47
  };
44
48
  const sendSrc = function (id, img) {
45
- const src = img.src;
46
- app.send((0, messages_gen_js_1.SetNodeAttributeURLBased)(id, 'src', src, app.getBaseHref()));
49
+ if (img.src.length > utils_js_1.MAX_STR_LEN) {
50
+ sendPlaceholder(id, img);
51
+ }
52
+ app.send((0, messages_gen_js_1.SetNodeAttributeURLBased)(id, 'src', img.src, app.getBaseHref()));
47
53
  };
48
- const sendImgAttrs = app.safe(function () {
49
- const id = app.nodes.getID(this);
54
+ const sendImgError = app.safe(function (img) {
55
+ const resolvedSrc = resolveURL(img.src || ''); // Src type is null sometimes. - is it true?
56
+ if ((0, utils_js_1.isURL)(resolvedSrc)) {
57
+ app.send((0, messages_gen_js_1.ResourceTiming)((0, utils_js_1.timestamp)(), 0, 0, 0, 0, 0, resolvedSrc, 'img'));
58
+ }
59
+ });
60
+ const sendImgAttrs = app.safe(function (img) {
61
+ const id = app.nodes.getID(img);
50
62
  if (id === undefined) {
51
63
  return;
52
64
  }
53
- const { src, complete, naturalWidth, naturalHeight, srcset } = this;
54
- if (!complete) {
65
+ if (!img.complete) {
55
66
  return;
56
67
  }
57
- const resolvedSrc = resolveURL(src || ''); // Src type is null sometimes. - is it true?
58
- if (naturalWidth === 0 && naturalHeight === 0) {
59
- if ((0, utils_js_1.isURL)(resolvedSrc)) {
60
- app.send((0, messages_gen_js_1.ResourceTiming)((0, utils_js_1.timestamp)(), 0, 0, 0, 0, 0, resolvedSrc, 'img'));
61
- }
68
+ if (img.naturalHeight === 0 && img.naturalWidth === 0 && !isSVGInFireFox(img.src)) {
69
+ sendImgError(img);
62
70
  }
63
- else if (resolvedSrc.length >= 1e5 || app.sanitizer.isMasked(id)) {
64
- sendPlaceholder(id, this);
71
+ else if (app.sanitizer.isHidden(id) || app.sanitizer.isObscured(id)) {
72
+ sendPlaceholder(id, img);
65
73
  }
66
74
  else {
67
- sendSrc(id, this);
68
- sendSrcset(id, this);
75
+ sendSrc(id, img);
76
+ sendSrcset(id, img);
69
77
  }
70
78
  });
71
79
  const observer = new MutationObserver((mutations) => {
@@ -92,9 +100,9 @@ function default_1(app) {
92
100
  if (!(0, guards_js_1.hasTag)(node, 'IMG')) {
93
101
  return;
94
102
  }
95
- app.nodes.attachElementListener('error', node, sendImgAttrs.bind(node));
96
- app.nodes.attachElementListener('load', node, sendImgAttrs.bind(node));
97
- sendImgAttrs.call(node);
103
+ app.nodes.attachNodeListener(node, 'error', () => sendImgError(node));
104
+ app.nodes.attachNodeListener(node, 'load', () => sendImgAttrs(node));
105
+ sendImgAttrs(node);
98
106
  observer.observe(node, { attributes: true, attributeFilter: ['src', 'srcset'] });
99
107
  });
100
108
  }
@@ -80,10 +80,10 @@ function default_1(app, opts) {
80
80
  function sendInputValue(id, node) {
81
81
  let value = node.value;
82
82
  let inputMode = options.defaultInputMode;
83
- if (node.type === 'password' || (0, utils_js_1.hasOpenreplayAttribute)(node, 'hidden')) {
83
+ if (node.type === 'password' || app.sanitizer.isHidden(id)) {
84
84
  inputMode = 2 /* Hidden */;
85
85
  }
86
- else if ((0, utils_js_1.hasOpenreplayAttribute)(node, 'obscured') ||
86
+ else if (app.sanitizer.isObscured(id) ||
87
87
  (inputMode === 0 /* Plain */ &&
88
88
  ((options.obscureInputNumbers && node.type !== 'date' && /\d\d\d\d/.test(value)) ||
89
89
  (options.obscureInputDates && node.type === 'date') ||
@@ -98,11 +98,13 @@ function default_1(app) {
98
98
  let mousePositionChanged = false;
99
99
  let mouseTarget = null;
100
100
  let mouseTargetTime = 0;
101
+ let selectorMap = {};
101
102
  app.attachStopCallback(() => {
102
103
  mousePositionX = -1;
103
104
  mousePositionY = -1;
104
105
  mousePositionChanged = false;
105
106
  mouseTarget = null;
107
+ selectorMap = {};
106
108
  });
107
109
  const sendMouseMove = () => {
108
110
  if (mousePositionChanged) {
@@ -110,25 +112,27 @@ function default_1(app) {
110
112
  mousePositionChanged = false;
111
113
  }
112
114
  };
113
- const patchDocument = (document) => {
114
- const selectorMap = {};
115
+ const patchDocument = (document, topframe = false) => {
115
116
  function getSelector(id, target) {
116
117
  return (selectorMap[id] = selectorMap[id] || _getSelector(target, document));
117
118
  }
118
- app.attachEventListener(document.documentElement, 'mouseover', (e) => {
119
+ const attachListener = topframe
120
+ ? app.attachEventListener.bind(app) // attached/removed on start/stop
121
+ : app.nodes.attachNodeListener.bind(app.nodes); // attached/removed on node register/unregister
122
+ attachListener(document.documentElement, 'mouseover', (e) => {
119
123
  const target = getTarget(e.target, document);
120
124
  if (target !== mouseTarget) {
121
125
  mouseTarget = target;
122
126
  mouseTargetTime = performance.now();
123
127
  }
124
128
  });
125
- app.attachEventListener(document, 'mousemove', (e) => {
126
- const { top, left } = app.observer.getDocumentOffset(document);
129
+ attachListener(document, 'mousemove', (e) => {
130
+ const [left, top] = app.observer.getDocumentOffset(document); // MBTODO?: document-id related message
127
131
  mousePositionX = e.clientX + left;
128
132
  mousePositionY = e.clientY + top;
129
133
  mousePositionChanged = true;
130
134
  }, false);
131
- app.attachEventListener(document, 'click', (e) => {
135
+ attachListener(document, 'click', (e) => {
132
136
  const target = getTarget(e.target, document);
133
137
  if ((!e.clientX && !e.clientY) || target === null) {
134
138
  return;
@@ -146,7 +150,7 @@ function default_1(app) {
146
150
  patchDocument(node);
147
151
  }
148
152
  });
149
- patchDocument(document);
153
+ patchDocument(document, true);
150
154
  app.ticker.attach(sendMouseMove, 10);
151
155
  }
152
156
  exports.default = default_1;
@@ -2,21 +2,34 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const messages_gen_js_1 = require("../app/messages.gen.js");
4
4
  const guards_js_1 = require("../app/guards.js");
5
+ function getDocumentScroll(doc) {
6
+ const win = doc.defaultView;
7
+ return [
8
+ (win && win.pageXOffset) ||
9
+ (doc.documentElement && doc.documentElement.scrollLeft) ||
10
+ (doc.body && doc.body.scrollLeft) ||
11
+ 0,
12
+ (win && win.pageYOffset) ||
13
+ (doc.documentElement && doc.documentElement.scrollTop) ||
14
+ (doc.body && doc.body.scrollTop) ||
15
+ 0,
16
+ ];
17
+ }
5
18
  function default_1(app) {
6
19
  let documentScroll = false;
7
20
  const nodeScroll = new Map();
8
21
  function setNodeScroll(target) {
9
- if (target instanceof Element) {
22
+ if (!(0, guards_js_1.isNode)(target)) {
23
+ return;
24
+ }
25
+ if ((0, guards_js_1.isElementNode)(target)) {
10
26
  nodeScroll.set(target, [target.scrollLeft, target.scrollTop]);
11
27
  }
28
+ if ((0, guards_js_1.isDocument)(target)) {
29
+ nodeScroll.set(target, getDocumentScroll(target));
30
+ }
12
31
  }
13
- const sendSetViewportScroll = app.safe(() => app.send((0, messages_gen_js_1.SetViewportScroll)(window.pageXOffset ||
14
- (document.documentElement && document.documentElement.scrollLeft) ||
15
- (document.body && document.body.scrollLeft) ||
16
- 0, window.pageYOffset ||
17
- (document.documentElement && document.documentElement.scrollTop) ||
18
- (document.body && document.body.scrollTop) ||
19
- 0)));
32
+ const sendSetViewportScroll = app.safe(() => app.send((0, messages_gen_js_1.SetViewportScroll)(...getDocumentScroll(document))));
20
33
  const sendSetNodeScroll = app.safe((s, node) => {
21
34
  const id = app.nodes.getID(node);
22
35
  if (id !== undefined) {
@@ -29,12 +42,19 @@ function default_1(app) {
29
42
  nodeScroll.clear();
30
43
  });
31
44
  app.nodes.attachNodeCallback((node, isStart) => {
32
- if (isStart && (0, guards_js_1.isElementNode)(node) && node.scrollLeft + node.scrollTop > 0) {
33
- nodeScroll.set(node, [node.scrollLeft, node.scrollTop]);
45
+ // MBTODO: iterate over all the nodes on start instead of using isStart hack
46
+ if (isStart) {
47
+ if ((0, guards_js_1.isElementNode)(node) && node.scrollLeft + node.scrollTop > 0) {
48
+ nodeScroll.set(node, [node.scrollLeft, node.scrollTop]);
49
+ }
50
+ else if ((0, guards_js_1.isDocument)(node)) {
51
+ // DRY somehow?
52
+ nodeScroll.set(node, getDocumentScroll(node));
53
+ }
34
54
  }
35
- else if ((0, guards_js_1.isRootNode)(node)) {
55
+ if ((0, guards_js_1.isRootNode)(node)) {
36
56
  // scroll is not-composed event (https://javascript.info/shadow-dom-events)
37
- app.attachEventListener(node, 'scroll', (e) => {
57
+ app.nodes.attachNodeListener(node, 'scroll', (e) => {
38
58
  setNodeScroll(e.target);
39
59
  });
40
60
  }
package/cjs/utils.d.ts CHANGED
@@ -1,9 +1,11 @@
1
- export declare function timestamp(): number;
1
+ export declare const IN_BROWSER: boolean;
2
+ export declare const IS_FIREFOX: false | RegExpMatchArray | null;
3
+ export declare const MAX_STR_LEN = 100000;
4
+ export declare const timestamp: () => number;
2
5
  export declare const stars: (str: string) => string;
3
6
  export declare function normSpaces(str: string): string;
4
7
  export declare function isURL(s: string): boolean;
5
- export declare const IN_BROWSER: boolean;
6
8
  export declare const DOCS_HOST = "https://docs.openreplay.com";
7
9
  export declare function deprecationWarn(nameOfFeature: string, useInstead: string, docsPath?: string): void;
8
10
  export declare function getLabelAttribute(e: Element): string | null;
9
- export declare function hasOpenreplayAttribute(e: Element, name: string): boolean;
11
+ export declare function hasOpenreplayAttribute(e: Element, attr: string): boolean;
package/cjs/utils.js CHANGED
@@ -1,10 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.hasOpenreplayAttribute = exports.getLabelAttribute = exports.deprecationWarn = exports.DOCS_HOST = exports.IN_BROWSER = exports.isURL = exports.normSpaces = exports.stars = exports.timestamp = void 0;
4
- function timestamp() {
5
- return Math.round(performance.now()) + performance.timing.navigationStart;
6
- }
7
- exports.timestamp = timestamp;
3
+ exports.hasOpenreplayAttribute = exports.getLabelAttribute = exports.deprecationWarn = exports.DOCS_HOST = exports.isURL = exports.normSpaces = exports.stars = exports.timestamp = exports.MAX_STR_LEN = exports.IS_FIREFOX = exports.IN_BROWSER = void 0;
4
+ const DEPRECATED_ATTRS = { htmlmasked: 'hidden', masked: 'obscured' };
5
+ exports.IN_BROWSER = !(typeof window === 'undefined');
6
+ exports.IS_FIREFOX = exports.IN_BROWSER && navigator.userAgent.match(/firefox|fxios/i);
7
+ exports.MAX_STR_LEN = 1e5;
8
+ const navigationStart = (exports.IN_BROWSER && performance.timing.navigationStart) || performance.timeOrigin;
9
+ // performance.now() is buggy in some browsers
10
+ exports.timestamp = exports.IN_BROWSER && performance.now() && navigationStart
11
+ ? () => Math.round(performance.now() + navigationStart)
12
+ : () => Date.now();
8
13
  exports.stars = 'repeat' in String.prototype
9
14
  ? (str) => '*'.repeat(str.length)
10
15
  : (str) => str.replace(/./g, '*');
@@ -17,7 +22,6 @@ function isURL(s) {
17
22
  return s.startsWith('https://') || s.startsWith('http://');
18
23
  }
19
24
  exports.isURL = isURL;
20
- exports.IN_BROWSER = !(typeof window === 'undefined');
21
25
  // TODO: JOIN IT WITH LOGGER somehow (use logging decorators?); Don't forget about index.js loggin when there is no logger instance.
22
26
  exports.DOCS_HOST = 'https://docs.openreplay.com';
23
27
  const warnedFeatures = {};
@@ -41,14 +45,15 @@ function getLabelAttribute(e) {
41
45
  return value;
42
46
  }
43
47
  exports.getLabelAttribute = getLabelAttribute;
44
- function hasOpenreplayAttribute(e, name) {
45
- const newName = `data-openreplay-${name}`;
48
+ function hasOpenreplayAttribute(e, attr) {
49
+ const newName = `data-openreplay-${attr}`;
46
50
  if (e.hasAttribute(newName)) {
47
- return true;
48
- }
49
- const oldName = `data-asayer-${name}`;
50
- if (e.hasAttribute(oldName)) {
51
- deprecationWarn(`"${oldName}" attribute`, `"${newName}" attribute`, '/installation/sanitize-data');
51
+ // @ts-ignore
52
+ if (DEPRECATED_ATTRS[attr]) {
53
+ deprecationWarn(`"${newName}" attribute`,
54
+ // @ts-ignore
55
+ `"${DEPRECATED_ATTRS[attr]}" attribute`, '/installation/sanitize-data');
56
+ }
52
57
  return true;
53
58
  }
54
59
  return false;
@@ -1,3 +1,4 @@
1
+ export declare function isNode(sth: any): sth is Node;
1
2
  export declare function isSVGElement(node: Element): node is SVGElement;
2
3
  export declare function isElementNode(node: Node): node is Element;
3
4
  export declare function isTextNode(node: Node): node is Text;
package/lib/app/guards.js CHANGED
@@ -1,3 +1,7 @@
1
+ //@ts-ignore
2
+ export function isNode(sth) {
3
+ return !!sth && sth.nodeType != null;
4
+ }
1
5
  export function isSVGElement(node) {
2
6
  return node.namespaceURI === 'http://www.w3.org/2000/svg';
3
7
  }
@@ -77,7 +77,7 @@ export default class App {
77
77
  private _debug;
78
78
  send(message: Message, urgent?: boolean): void;
79
79
  private commit;
80
- safe<T extends (...args: any[]) => void>(fn: T): T;
80
+ safe<T extends (this: any, ...args: any[]) => void>(fn: T): T;
81
81
  attachCommitCallback(cb: CommitCallback): void;
82
82
  attachStartCallback(cb: StartCallback, useSafe?: boolean): void;
83
83
  attachStopCallback(cb: () => any, useSafe?: boolean): void;