@openreplay/tracker 3.6.2 → 4.0.1
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/.eslintignore +8 -0
- package/.prettierignore +1 -0
- package/LICENSE +1 -1
- package/cjs/app/guards.d.ts +2 -1
- package/cjs/app/guards.js +6 -3
- package/cjs/app/index.d.ts +28 -23
- package/cjs/app/index.js +107 -86
- package/cjs/app/logger.js +6 -3
- package/cjs/app/messages.d.ts +52 -0
- package/cjs/app/messages.gen.d.ts +58 -0
- package/cjs/app/messages.gen.js +501 -0
- package/cjs/app/messages.js +234 -0
- package/cjs/app/nodes.d.ts +1 -1
- package/cjs/app/nodes.js +2 -0
- package/cjs/app/observer/iframe_observer.d.ts +1 -1
- package/cjs/app/observer/iframe_observer.js +3 -3
- package/cjs/app/observer/observer.d.ts +2 -3
- package/cjs/app/observer/observer.js +50 -52
- package/cjs/app/observer/shadow_root_observer.d.ts +1 -1
- package/cjs/app/observer/shadow_root_observer.js +3 -3
- package/cjs/app/observer/top_observer.d.ts +13 -2
- package/cjs/app/observer/top_observer.js +58 -23
- package/cjs/app/sanitizer.d.ts +1 -1
- package/cjs/app/sanitizer.js +5 -5
- package/cjs/app/session.d.ts +20 -2
- package/cjs/app/session.js +65 -6
- package/cjs/app/ticker.d.ts +1 -1
- package/cjs/common/{webworker.d.ts → interaction.d.ts} +5 -5
- package/cjs/common/{types.js → interaction.js} +0 -0
- package/cjs/common/messages.gen.d.ts +388 -0
- package/cjs/common/{webworker.js → messages.gen.js} +1 -0
- package/cjs/index.d.ts +10 -9
- package/cjs/index.js +47 -36
- package/cjs/modules/adoptedStyleSheets.d.ts +2 -0
- package/cjs/modules/adoptedStyleSheets.js +127 -0
- package/cjs/modules/connection.d.ts +1 -1
- package/cjs/modules/connection.js +2 -2
- package/cjs/modules/console.d.ts +1 -1
- package/cjs/modules/console.js +7 -21
- package/cjs/modules/cssrules.d.ts +1 -1
- package/cjs/modules/cssrules.js +18 -14
- package/cjs/modules/exception.d.ts +3 -3
- package/cjs/modules/exception.js +23 -18
- package/cjs/modules/img.d.ts +1 -1
- package/cjs/modules/img.js +39 -26
- package/cjs/modules/input.d.ts +1 -1
- package/cjs/modules/input.js +21 -21
- package/cjs/modules/mouse.d.ts +1 -1
- package/cjs/modules/mouse.js +50 -43
- package/cjs/modules/performance.d.ts +1 -1
- package/cjs/modules/performance.js +2 -2
- package/cjs/modules/scroll.d.ts +1 -1
- package/cjs/modules/scroll.js +16 -7
- package/cjs/modules/timing.d.ts +1 -1
- package/cjs/modules/timing.js +14 -26
- package/cjs/modules/viewport.d.ts +1 -1
- package/cjs/modules/viewport.js +4 -4
- package/cjs/utils.js +7 -7
- package/cjs/vendors/finder/finder.js +53 -48
- package/lib/app/guards.d.ts +2 -1
- package/lib/app/guards.js +4 -2
- package/lib/app/index.d.ts +28 -23
- package/lib/app/index.js +115 -94
- package/lib/app/logger.js +6 -3
- package/lib/app/messages.d.ts +52 -0
- package/lib/app/messages.gen.d.ts +58 -0
- package/lib/app/messages.gen.js +441 -0
- package/lib/app/messages.js +181 -0
- package/lib/app/nodes.d.ts +1 -1
- package/lib/app/nodes.js +2 -0
- package/lib/app/observer/iframe_observer.d.ts +1 -1
- package/lib/app/observer/iframe_observer.js +3 -3
- package/lib/app/observer/observer.d.ts +2 -3
- package/lib/app/observer/observer.js +51 -53
- package/lib/app/observer/shadow_root_observer.d.ts +1 -1
- package/lib/app/observer/shadow_root_observer.js +3 -3
- package/lib/app/observer/top_observer.d.ts +13 -2
- package/lib/app/observer/top_observer.js +62 -27
- package/lib/app/sanitizer.d.ts +1 -1
- package/lib/app/sanitizer.js +7 -7
- package/lib/app/session.d.ts +20 -2
- package/lib/app/session.js +65 -6
- package/lib/app/ticker.d.ts +1 -1
- package/lib/common/{webworker.d.ts → interaction.d.ts} +5 -5
- package/lib/common/{types.js → interaction.js} +0 -0
- package/lib/common/messages.gen.d.ts +388 -0
- package/lib/common/messages.gen.js +2 -0
- package/lib/common/tsconfig.tsbuildinfo +1 -1
- package/lib/index.d.ts +10 -9
- package/lib/index.js +60 -49
- package/lib/modules/adoptedStyleSheets.d.ts +2 -0
- package/lib/modules/adoptedStyleSheets.js +124 -0
- package/lib/modules/connection.d.ts +1 -1
- package/lib/modules/connection.js +2 -2
- package/lib/modules/console.d.ts +1 -1
- package/lib/modules/console.js +8 -22
- package/lib/modules/cssrules.d.ts +1 -1
- package/lib/modules/cssrules.js +19 -15
- package/lib/modules/exception.d.ts +3 -3
- package/lib/modules/exception.js +23 -18
- package/lib/modules/img.d.ts +1 -1
- package/lib/modules/img.js +41 -28
- package/lib/modules/input.d.ts +1 -1
- package/lib/modules/input.js +23 -23
- package/lib/modules/mouse.d.ts +1 -1
- package/lib/modules/mouse.js +53 -46
- package/lib/modules/performance.d.ts +1 -1
- package/lib/modules/performance.js +3 -3
- package/lib/modules/scroll.d.ts +1 -1
- package/lib/modules/scroll.js +17 -8
- package/lib/modules/timing.d.ts +1 -1
- package/lib/modules/timing.js +16 -28
- package/lib/modules/viewport.d.ts +1 -1
- package/lib/modules/viewport.js +4 -4
- package/lib/utils.js +7 -7
- package/lib/vendors/finder/finder.js +53 -48
- package/package.json +27 -10
- package/cjs/common/messages.d.ts +0 -444
- package/cjs/common/messages.js +0 -794
- package/cjs/common/types.d.ts +0 -9
- package/cjs/modules/longtasks.d.ts +0 -2
- package/cjs/modules/longtasks.js +0 -26
- package/lib/common/messages.d.ts +0 -444
- package/lib/common/messages.js +0 -790
- package/lib/common/types.d.ts +0 -9
- package/lib/common/webworker.js +0 -1
- package/lib/modules/longtasks.d.ts +0 -2
- package/lib/modules/longtasks.js +0 -23
package/lib/modules/img.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { timestamp, isURL } from
|
|
2
|
-
import { ResourceTiming, SetNodeAttributeURLBased, SetNodeAttribute } from
|
|
3
|
-
import { hasTag } from
|
|
1
|
+
import { timestamp, isURL } from '../utils.js';
|
|
2
|
+
import { ResourceTiming, SetNodeAttributeURLBased, SetNodeAttribute } from '../app/messages.gen.js';
|
|
3
|
+
import { hasTag } from '../app/guards.js';
|
|
4
4
|
function resolveURL(url, location = document.location) {
|
|
5
5
|
url = url.trim();
|
|
6
6
|
if (url.startsWith('/')) {
|
|
@@ -16,19 +16,34 @@ function resolveURL(url, location = document.location) {
|
|
|
16
16
|
return location.origin + location.pathname + url;
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
-
const PLACEHOLDER_SRC =
|
|
19
|
+
const PLACEHOLDER_SRC = 'https://static.openreplay.com/tracker/placeholder.jpeg';
|
|
20
20
|
export default function (app) {
|
|
21
21
|
function sendPlaceholder(id, node) {
|
|
22
|
-
app.send(
|
|
22
|
+
app.send(SetNodeAttribute(id, 'src', PLACEHOLDER_SRC));
|
|
23
23
|
const { width, height } = node.getBoundingClientRect();
|
|
24
|
-
if (!node.hasAttribute(
|
|
25
|
-
app.send(
|
|
24
|
+
if (!node.hasAttribute('width')) {
|
|
25
|
+
app.send(SetNodeAttribute(id, 'width', String(width)));
|
|
26
26
|
}
|
|
27
|
-
if (!node.hasAttribute(
|
|
28
|
-
app.send(
|
|
27
|
+
if (!node.hasAttribute('height')) {
|
|
28
|
+
app.send(SetNodeAttribute(id, 'height', String(height)));
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
-
const
|
|
31
|
+
const sendSrcset = function (id, img) {
|
|
32
|
+
const { srcset } = img;
|
|
33
|
+
if (!srcset) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const resolvedSrcset = srcset
|
|
37
|
+
.split(',')
|
|
38
|
+
.map((str) => resolveURL(str))
|
|
39
|
+
.join(',');
|
|
40
|
+
app.send(SetNodeAttribute(id, 'srcset', resolvedSrcset));
|
|
41
|
+
};
|
|
42
|
+
const sendSrc = function (id, img) {
|
|
43
|
+
const src = img.src;
|
|
44
|
+
app.send(SetNodeAttributeURLBased(id, 'src', src, app.getBaseHref()));
|
|
45
|
+
};
|
|
46
|
+
const sendImgAttrs = app.safe(function () {
|
|
32
47
|
const id = app.nodes.getID(this);
|
|
33
48
|
if (id === undefined) {
|
|
34
49
|
return;
|
|
@@ -40,46 +55,44 @@ export default function (app) {
|
|
|
40
55
|
const resolvedSrc = resolveURL(src || ''); // Src type is null sometimes. - is it true?
|
|
41
56
|
if (naturalWidth === 0 && naturalHeight === 0) {
|
|
42
57
|
if (isURL(resolvedSrc)) {
|
|
43
|
-
app.send(
|
|
58
|
+
app.send(ResourceTiming(timestamp(), 0, 0, 0, 0, 0, resolvedSrc, 'img'));
|
|
44
59
|
}
|
|
45
60
|
}
|
|
46
61
|
else if (resolvedSrc.length >= 1e5 || app.sanitizer.isMasked(id)) {
|
|
47
62
|
sendPlaceholder(id, this);
|
|
48
63
|
}
|
|
49
64
|
else {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const resolvedSrcset = srcset.split(',').map(str => resolveURL(str)).join(',');
|
|
53
|
-
app.send(new SetNodeAttribute(id, 'srcset', resolvedSrcset));
|
|
54
|
-
}
|
|
65
|
+
sendSrc(id, this);
|
|
66
|
+
sendSrcset(id, this);
|
|
55
67
|
}
|
|
56
68
|
});
|
|
57
69
|
const observer = new MutationObserver((mutations) => {
|
|
58
70
|
for (const mutation of mutations) {
|
|
59
|
-
if (mutation.type ===
|
|
71
|
+
if (mutation.type === 'attributes') {
|
|
60
72
|
const target = mutation.target;
|
|
61
73
|
const id = app.nodes.getID(target);
|
|
62
74
|
if (id === undefined) {
|
|
63
75
|
return;
|
|
64
76
|
}
|
|
65
|
-
if (mutation.attributeName ===
|
|
66
|
-
|
|
67
|
-
app.send(new SetNodeAttributeURLBased(id, 'src', src, app.getBaseHref()));
|
|
77
|
+
if (mutation.attributeName === 'src') {
|
|
78
|
+
sendSrc(id, target);
|
|
68
79
|
}
|
|
69
|
-
if (mutation.attributeName ===
|
|
70
|
-
|
|
71
|
-
app.send(new SetNodeAttribute(id, 'srcset', srcset));
|
|
80
|
+
if (mutation.attributeName === 'srcset') {
|
|
81
|
+
sendSrcset(id, target);
|
|
72
82
|
}
|
|
73
83
|
}
|
|
74
84
|
}
|
|
75
85
|
});
|
|
86
|
+
app.attachStopCallback(() => {
|
|
87
|
+
observer.disconnect();
|
|
88
|
+
});
|
|
76
89
|
app.nodes.attachNodeCallback((node) => {
|
|
77
|
-
if (!hasTag(node,
|
|
90
|
+
if (!hasTag(node, 'IMG')) {
|
|
78
91
|
return;
|
|
79
92
|
}
|
|
80
|
-
app.nodes.attachElementListener('error', node,
|
|
81
|
-
app.nodes.attachElementListener('load', node,
|
|
82
|
-
|
|
83
|
-
observer.observe(node, { attributes: true, attributeFilter: [
|
|
93
|
+
app.nodes.attachElementListener('error', node, sendImgAttrs.bind(node));
|
|
94
|
+
app.nodes.attachElementListener('load', node, sendImgAttrs.bind(node));
|
|
95
|
+
sendImgAttrs.call(node);
|
|
96
|
+
observer.observe(node, { attributes: true, attributeFilter: ['src', 'srcset'] });
|
|
84
97
|
});
|
|
85
98
|
}
|
package/lib/modules/input.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type App from
|
|
1
|
+
import type App from '../app/index.js';
|
|
2
2
|
declare type TextEditableElement = HTMLInputElement | HTMLTextAreaElement;
|
|
3
3
|
export declare function getInputLabel(node: TextEditableElement): string;
|
|
4
4
|
export declare const enum InputMode {
|
package/lib/modules/input.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { normSpaces, IN_BROWSER, getLabelAttribute, hasOpenreplayAttribute
|
|
2
|
-
import { hasTag } from
|
|
3
|
-
import { SetInputTarget, SetInputValue, SetInputChecked } from
|
|
1
|
+
import { normSpaces, IN_BROWSER, getLabelAttribute, hasOpenreplayAttribute } from '../utils.js';
|
|
2
|
+
import { hasTag } from '../app/guards.js';
|
|
3
|
+
import { SetInputTarget, SetInputValue, SetInputChecked } from '../app/messages.gen.js';
|
|
4
4
|
const INPUT_TYPES = ['text', 'password', 'email', 'search', 'number', 'range', 'date'];
|
|
5
5
|
function isTextEditable(node) {
|
|
6
|
-
if (hasTag(node,
|
|
6
|
+
if (hasTag(node, 'TEXTAREA')) {
|
|
7
7
|
return true;
|
|
8
8
|
}
|
|
9
|
-
if (!hasTag(node,
|
|
9
|
+
if (!hasTag(node, 'INPUT')) {
|
|
10
10
|
return false;
|
|
11
11
|
}
|
|
12
12
|
return INPUT_TYPES.includes(node.type);
|
|
13
13
|
}
|
|
14
14
|
function isCheckable(node) {
|
|
15
|
-
if (!hasTag(node,
|
|
15
|
+
if (!hasTag(node, 'INPUT')) {
|
|
16
16
|
return false;
|
|
17
17
|
}
|
|
18
18
|
const type = node.type;
|
|
@@ -22,7 +22,7 @@ const labelElementFor = IN_BROWSER && 'labels' in HTMLInputElement.prototype
|
|
|
22
22
|
? (node) => {
|
|
23
23
|
let p = node;
|
|
24
24
|
while ((p = p.parentNode) !== null) {
|
|
25
|
-
if (hasTag(p,
|
|
25
|
+
if (hasTag(p, 'LABEL')) {
|
|
26
26
|
return p;
|
|
27
27
|
}
|
|
28
28
|
}
|
|
@@ -34,13 +34,13 @@ const labelElementFor = IN_BROWSER && 'labels' in HTMLInputElement.prototype
|
|
|
34
34
|
: (node) => {
|
|
35
35
|
let p = node;
|
|
36
36
|
while ((p = p.parentNode) !== null) {
|
|
37
|
-
if (hasTag(p,
|
|
37
|
+
if (hasTag(p, 'LABEL')) {
|
|
38
38
|
return p;
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
const id = node.id;
|
|
42
42
|
if (id) {
|
|
43
|
-
const labels =
|
|
43
|
+
const labels = node.ownerDocument.querySelectorAll('label[for="' + id + '"]');
|
|
44
44
|
if (labels !== null && labels.length === 1) {
|
|
45
45
|
return labels[0];
|
|
46
46
|
}
|
|
@@ -50,12 +50,13 @@ export function getInputLabel(node) {
|
|
|
50
50
|
let label = getLabelAttribute(node);
|
|
51
51
|
if (label === null) {
|
|
52
52
|
const labelElement = labelElementFor(node);
|
|
53
|
-
label =
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
label =
|
|
54
|
+
(labelElement && labelElement.innerText) ||
|
|
55
|
+
node.placeholder ||
|
|
56
|
+
node.name ||
|
|
57
|
+
node.id ||
|
|
58
|
+
node.className ||
|
|
59
|
+
node.type;
|
|
59
60
|
}
|
|
60
61
|
return normSpaces(label).slice(0, 100);
|
|
61
62
|
}
|
|
@@ -69,7 +70,7 @@ export default function (app, opts) {
|
|
|
69
70
|
function sendInputTarget(id, node) {
|
|
70
71
|
const label = getInputLabel(node);
|
|
71
72
|
if (label !== '') {
|
|
72
|
-
app.send(
|
|
73
|
+
app.send(SetInputTarget(id, label));
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
function sendInputValue(id, node) {
|
|
@@ -82,8 +83,7 @@ export default function (app, opts) {
|
|
|
82
83
|
(inputMode === 0 /* Plain */ &&
|
|
83
84
|
((options.obscureInputNumbers && node.type !== 'date' && /\d\d\d\d/.test(value)) ||
|
|
84
85
|
(options.obscureInputDates && node.type === 'date') ||
|
|
85
|
-
(options.obscureInputEmails &&
|
|
86
|
-
(node.type === 'email' || !!~value.indexOf('@')))))) {
|
|
86
|
+
(options.obscureInputEmails && (node.type === 'email' || !!~value.indexOf('@')))))) {
|
|
87
87
|
inputMode = 1 /* Obscured */;
|
|
88
88
|
}
|
|
89
89
|
let mask = 0;
|
|
@@ -97,7 +97,7 @@ export default function (app, opts) {
|
|
|
97
97
|
value = '';
|
|
98
98
|
break;
|
|
99
99
|
}
|
|
100
|
-
app.send(
|
|
100
|
+
app.send(SetInputValue(id, value, mask));
|
|
101
101
|
}
|
|
102
102
|
const inputValues = new Map();
|
|
103
103
|
const checkableValues = new Map();
|
|
@@ -135,7 +135,7 @@ export default function (app, opts) {
|
|
|
135
135
|
}
|
|
136
136
|
if (checked !== node.checked) {
|
|
137
137
|
checkableValues.set(id, node.checked);
|
|
138
|
-
app.send(
|
|
138
|
+
app.send(SetInputChecked(id, node.checked));
|
|
139
139
|
}
|
|
140
140
|
});
|
|
141
141
|
});
|
|
@@ -146,9 +146,9 @@ export default function (app, opts) {
|
|
|
146
146
|
return;
|
|
147
147
|
}
|
|
148
148
|
// TODO: support multiple select (?): use selectedOptions; Need send target?
|
|
149
|
-
if (hasTag(node,
|
|
149
|
+
if (hasTag(node, 'SELECT')) {
|
|
150
150
|
sendInputValue(id, node);
|
|
151
|
-
app.attachEventListener(node,
|
|
151
|
+
app.attachEventListener(node, 'change', () => {
|
|
152
152
|
sendInputValue(id, node);
|
|
153
153
|
});
|
|
154
154
|
}
|
|
@@ -159,7 +159,7 @@ export default function (app, opts) {
|
|
|
159
159
|
}
|
|
160
160
|
if (isCheckable(node)) {
|
|
161
161
|
checkableValues.set(id, node.checked);
|
|
162
|
-
app.send(
|
|
162
|
+
app.send(SetInputChecked(id, node.checked));
|
|
163
163
|
return;
|
|
164
164
|
}
|
|
165
165
|
}));
|
package/lib/modules/mouse.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type App from
|
|
1
|
+
import type App from '../app/index.js';
|
|
2
2
|
export default function (app: App): void;
|
package/lib/modules/mouse.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { hasTag, isSVGElement } from
|
|
2
|
-
import { normSpaces, hasOpenreplayAttribute, getLabelAttribute
|
|
3
|
-
import { MouseMove, MouseClick } from
|
|
4
|
-
import { getInputLabel } from
|
|
5
|
-
function _getSelector(target) {
|
|
1
|
+
import { hasTag, isSVGElement, isDocument } from '../app/guards.js';
|
|
2
|
+
import { normSpaces, hasOpenreplayAttribute, getLabelAttribute } from '../utils.js';
|
|
3
|
+
import { MouseMove, MouseClick } from '../app/messages.gen.js';
|
|
4
|
+
import { getInputLabel } from './input.js';
|
|
5
|
+
function _getSelector(target, document) {
|
|
6
6
|
let el = target;
|
|
7
7
|
let selector = null;
|
|
8
8
|
do {
|
|
@@ -10,9 +10,10 @@ function _getSelector(target) {
|
|
|
10
10
|
return `#${el.id}` + (selector ? ` > ${selector}` : '');
|
|
11
11
|
}
|
|
12
12
|
selector =
|
|
13
|
-
el.className
|
|
14
|
-
.
|
|
15
|
-
.
|
|
13
|
+
el.className
|
|
14
|
+
.split(' ')
|
|
15
|
+
.map((cn) => cn.trim())
|
|
16
|
+
.filter((cn) => cn !== '')
|
|
16
17
|
.reduce((sel, cn) => `${sel}.${cn}`, el.tagName.toLowerCase()) +
|
|
17
18
|
(selector ? ` > ${selector}` : '');
|
|
18
19
|
if (el === document.body) {
|
|
@@ -24,23 +25,23 @@ function _getSelector(target) {
|
|
|
24
25
|
}
|
|
25
26
|
function isClickable(element) {
|
|
26
27
|
const tag = element.tagName.toUpperCase();
|
|
27
|
-
return tag === 'BUTTON' ||
|
|
28
|
+
return (tag === 'BUTTON' ||
|
|
28
29
|
tag === 'A' ||
|
|
29
30
|
tag === 'LI' ||
|
|
30
31
|
tag === 'SELECT' ||
|
|
31
32
|
element.onclick != null ||
|
|
32
|
-
element.getAttribute('role') === 'button';
|
|
33
|
+
element.getAttribute('role') === 'button');
|
|
33
34
|
//|| element.className.includes("btn")
|
|
34
|
-
// MBTODO:
|
|
35
|
+
// MBTODO: intersept addEventListener
|
|
35
36
|
}
|
|
36
|
-
//TODO: fix (typescript
|
|
37
|
-
function getTarget(target) {
|
|
37
|
+
//TODO: fix (typescript is not sure about target variable after assignation of svg)
|
|
38
|
+
function getTarget(target, document) {
|
|
38
39
|
if (target instanceof Element) {
|
|
39
|
-
return _getTarget(target);
|
|
40
|
+
return _getTarget(target, document);
|
|
40
41
|
}
|
|
41
42
|
return null;
|
|
42
43
|
}
|
|
43
|
-
function _getTarget(target) {
|
|
44
|
+
function _getTarget(target, document) {
|
|
44
45
|
let element = target;
|
|
45
46
|
while (element !== null && element !== document.documentElement) {
|
|
46
47
|
if (hasOpenreplayAttribute(element, 'masked')) {
|
|
@@ -64,8 +65,7 @@ function _getTarget(target) {
|
|
|
64
65
|
if (tag === 'INPUT') {
|
|
65
66
|
return element;
|
|
66
67
|
}
|
|
67
|
-
if (isClickable(element) ||
|
|
68
|
-
getLabelAttribute(element) !== null) {
|
|
68
|
+
if (isClickable(element) || getLabelAttribute(element) !== null) {
|
|
69
69
|
return element;
|
|
70
70
|
}
|
|
71
71
|
element = element.parentElement;
|
|
@@ -78,7 +78,7 @@ export default function (app) {
|
|
|
78
78
|
if (dl !== null) {
|
|
79
79
|
return dl;
|
|
80
80
|
}
|
|
81
|
-
if (hasTag(target,
|
|
81
|
+
if (hasTag(target, 'INPUT')) {
|
|
82
82
|
return getInputLabel(target);
|
|
83
83
|
}
|
|
84
84
|
if (isClickable(target)) {
|
|
@@ -104,39 +104,46 @@ export default function (app) {
|
|
|
104
104
|
});
|
|
105
105
|
const sendMouseMove = () => {
|
|
106
106
|
if (mousePositionChanged) {
|
|
107
|
-
app.send(
|
|
107
|
+
app.send(MouseMove(mousePositionX, mousePositionY));
|
|
108
108
|
mousePositionChanged = false;
|
|
109
109
|
}
|
|
110
110
|
};
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
app.attachEventListener(document.documentElement, 'mouseover', (e) => {
|
|
116
|
-
const target = getTarget(e.target);
|
|
117
|
-
if (target !== mouseTarget) {
|
|
118
|
-
mouseTarget = target;
|
|
119
|
-
mouseTargetTime = performance.now();
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
app.attachEventListener(document, 'mousemove', (e) => {
|
|
123
|
-
mousePositionX = e.clientX;
|
|
124
|
-
mousePositionY = e.clientY;
|
|
125
|
-
mousePositionChanged = true;
|
|
126
|
-
}, false);
|
|
127
|
-
app.attachEventListener(document, 'click', (e) => {
|
|
128
|
-
const target = getTarget(e.target);
|
|
129
|
-
if ((!e.clientX && !e.clientY) || target === null) {
|
|
130
|
-
return;
|
|
111
|
+
const patchDocument = (document) => {
|
|
112
|
+
const selectorMap = {};
|
|
113
|
+
function getSelector(id, target) {
|
|
114
|
+
return (selectorMap[id] = selectorMap[id] || _getSelector(target, document));
|
|
131
115
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
116
|
+
app.attachEventListener(document.documentElement, 'mouseover', (e) => {
|
|
117
|
+
const target = getTarget(e.target, document);
|
|
118
|
+
if (target !== mouseTarget) {
|
|
119
|
+
mouseTarget = target;
|
|
120
|
+
mouseTargetTime = performance.now();
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
app.attachEventListener(document, 'mousemove', (e) => {
|
|
124
|
+
const { top, left } = app.observer.getDocumentOffset(document);
|
|
125
|
+
mousePositionX = e.clientX + left;
|
|
126
|
+
mousePositionY = e.clientY + top;
|
|
127
|
+
mousePositionChanged = true;
|
|
128
|
+
}, false);
|
|
129
|
+
app.attachEventListener(document, 'click', (e) => {
|
|
130
|
+
const target = getTarget(e.target, document);
|
|
131
|
+
if ((!e.clientX && !e.clientY) || target === null) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const id = app.nodes.getID(target);
|
|
135
|
+
if (id !== undefined) {
|
|
136
|
+
sendMouseMove();
|
|
137
|
+
app.send(MouseClick(id, mouseTarget === target ? Math.round(performance.now() - mouseTargetTime) : 0, getTargetLabel(target), getSelector(id, target)), true);
|
|
138
|
+
}
|
|
139
|
+
mouseTarget = null;
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
app.nodes.attachNodeCallback((node) => {
|
|
143
|
+
if (isDocument(node)) {
|
|
144
|
+
patchDocument(node);
|
|
138
145
|
}
|
|
139
|
-
mouseTarget = null;
|
|
140
146
|
});
|
|
147
|
+
patchDocument(document);
|
|
141
148
|
app.ticker.attach(sendMouseMove, 10);
|
|
142
149
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { IN_BROWSER } from
|
|
2
|
-
import { PerformanceTrack } from
|
|
1
|
+
import { IN_BROWSER } from '../utils.js';
|
|
2
|
+
import { PerformanceTrack } from '../app/messages.gen.js';
|
|
3
3
|
const perf = IN_BROWSER && 'performance' in window && 'memory' in performance // works in Chrome only
|
|
4
4
|
? performance
|
|
5
5
|
: { memory: {} };
|
|
@@ -31,7 +31,7 @@ export default function (app, opts) {
|
|
|
31
31
|
if (frames === undefined || ticks === undefined) {
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
34
|
-
app.send(
|
|
34
|
+
app.send(PerformanceTrack(frames, ticks, perf.memory.totalJSHeapSize || 0, perf.memory.usedJSHeapSize || 0));
|
|
35
35
|
ticks = frames = document.hidden ? -1 : 0;
|
|
36
36
|
};
|
|
37
37
|
app.attachStartCallback(() => {
|
package/lib/modules/scroll.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type App from
|
|
1
|
+
import type App from '../app/index.js';
|
|
2
2
|
export default function (app: App): void;
|
package/lib/modules/scroll.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import { SetViewportScroll, SetNodeScroll } from
|
|
2
|
-
import { isElementNode } from
|
|
1
|
+
import { SetViewportScroll, SetNodeScroll } from '../app/messages.gen.js';
|
|
2
|
+
import { isElementNode, isRootNode } from '../app/guards.js';
|
|
3
3
|
export default function (app) {
|
|
4
4
|
let documentScroll = false;
|
|
5
5
|
const nodeScroll = new Map();
|
|
6
|
-
|
|
6
|
+
function setNodeScroll(target) {
|
|
7
|
+
if (target instanceof Element) {
|
|
8
|
+
nodeScroll.set(target, [target.scrollLeft, target.scrollTop]);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
const sendSetViewportScroll = app.safe(() => app.send(SetViewportScroll(window.pageXOffset ||
|
|
7
12
|
(document.documentElement && document.documentElement.scrollLeft) ||
|
|
8
13
|
(document.body && document.body.scrollLeft) ||
|
|
9
14
|
0, window.pageYOffset ||
|
|
@@ -13,7 +18,7 @@ export default function (app) {
|
|
|
13
18
|
const sendSetNodeScroll = app.safe((s, node) => {
|
|
14
19
|
const id = app.nodes.getID(node);
|
|
15
20
|
if (id !== undefined) {
|
|
16
|
-
app.send(
|
|
21
|
+
app.send(SetNodeScroll(id, s[0], s[1]));
|
|
17
22
|
}
|
|
18
23
|
});
|
|
19
24
|
app.attachStartCallback(sendSetViewportScroll);
|
|
@@ -25,16 +30,20 @@ export default function (app) {
|
|
|
25
30
|
if (isStart && isElementNode(node) && node.scrollLeft + node.scrollTop > 0) {
|
|
26
31
|
nodeScroll.set(node, [node.scrollLeft, node.scrollTop]);
|
|
27
32
|
}
|
|
33
|
+
else if (isRootNode(node)) {
|
|
34
|
+
// scroll is not-composed event (https://javascript.info/shadow-dom-events)
|
|
35
|
+
app.attachEventListener(node, 'scroll', (e) => {
|
|
36
|
+
setNodeScroll(e.target);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
28
39
|
});
|
|
29
|
-
app.attachEventListener(
|
|
40
|
+
app.attachEventListener(document, 'scroll', (e) => {
|
|
30
41
|
const target = e.target;
|
|
31
42
|
if (target === document) {
|
|
32
43
|
documentScroll = true;
|
|
33
44
|
return;
|
|
34
45
|
}
|
|
35
|
-
|
|
36
|
-
nodeScroll.set(target, [target.scrollLeft, target.scrollTop]);
|
|
37
|
-
}
|
|
46
|
+
setNodeScroll(target);
|
|
38
47
|
});
|
|
39
48
|
app.ticker.attach(() => {
|
|
40
49
|
if (documentScroll) {
|
package/lib/modules/timing.d.ts
CHANGED
package/lib/modules/timing.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { hasTag } from
|
|
2
|
-
import { isURL } from
|
|
3
|
-
import { ResourceTiming, PageLoadTiming, PageRenderTiming } from
|
|
1
|
+
import { hasTag } from '../app/guards.js';
|
|
2
|
+
import { isURL } from '../utils.js';
|
|
3
|
+
import { ResourceTiming, PageLoadTiming, PageRenderTiming } from '../app/messages.gen.js';
|
|
4
4
|
function getPaintBlocks(resources) {
|
|
5
5
|
const paintBlocks = [];
|
|
6
6
|
const elements = document.getElementsByTagName('*');
|
|
@@ -8,7 +8,7 @@ function getPaintBlocks(resources) {
|
|
|
8
8
|
for (let i = 0; i < elements.length; i++) {
|
|
9
9
|
const element = elements[i];
|
|
10
10
|
let src = '';
|
|
11
|
-
if (hasTag(element,
|
|
11
|
+
if (hasTag(element, 'IMG')) {
|
|
12
12
|
src = element.currentSrc || element.src;
|
|
13
13
|
}
|
|
14
14
|
if (!src) {
|
|
@@ -34,9 +34,7 @@ function getPaintBlocks(resources) {
|
|
|
34
34
|
const bottom = Math.min(rect.bottom, window.innerHeight ||
|
|
35
35
|
(document.documentElement && document.documentElement.clientHeight) ||
|
|
36
36
|
0);
|
|
37
|
-
const right = Math.min(rect.right, window.innerWidth ||
|
|
38
|
-
(document.documentElement && document.documentElement.clientWidth) ||
|
|
39
|
-
0);
|
|
37
|
+
const right = Math.min(rect.right, window.innerWidth || (document.documentElement && document.documentElement.clientWidth) || 0);
|
|
40
38
|
if (bottom <= top || right <= left)
|
|
41
39
|
continue;
|
|
42
40
|
const area = (bottom - top) * (right - left);
|
|
@@ -46,8 +44,7 @@ function getPaintBlocks(resources) {
|
|
|
46
44
|
}
|
|
47
45
|
function calculateSpeedIndex(firstContentfulPaint, paintBlocks) {
|
|
48
46
|
let a = (Math.max((document.documentElement && document.documentElement.clientWidth) || 0, window.innerWidth || 0) *
|
|
49
|
-
Math.max((document.documentElement && document.documentElement.clientHeight) ||
|
|
50
|
-
0, window.innerHeight || 0)) /
|
|
47
|
+
Math.max((document.documentElement && document.documentElement.clientHeight) || 0, window.innerHeight || 0)) /
|
|
51
48
|
10;
|
|
52
49
|
let s = a * firstContentfulPaint;
|
|
53
50
|
for (let i = 0; i < paintBlocks.length; i++) {
|
|
@@ -76,16 +73,13 @@ export default function (app, opts) {
|
|
|
76
73
|
if (resources !== null) {
|
|
77
74
|
resources[entry.name] = entry.startTime + entry.duration;
|
|
78
75
|
}
|
|
79
|
-
app.send(
|
|
80
|
-
? entry.responseStart - entry.startTime
|
|
81
|
-
: 0, entry.transferSize > entry.encodedBodySize
|
|
82
|
-
? entry.transferSize - entry.encodedBodySize
|
|
83
|
-
: 0, entry.encodedBodySize || 0, entry.decodedBodySize || 0, entry.name, entry.initiatorType));
|
|
76
|
+
app.send(ResourceTiming(entry.startTime + performance.timing.navigationStart, entry.duration, entry.responseStart && entry.startTime ? entry.responseStart - entry.startTime : 0, entry.transferSize > entry.encodedBodySize ? entry.transferSize - entry.encodedBodySize : 0, entry.encodedBodySize || 0, entry.decodedBodySize || 0, entry.name, entry.initiatorType));
|
|
84
77
|
}
|
|
85
78
|
const observer = new PerformanceObserver((list) => list.getEntries().forEach(resourceTiming));
|
|
86
79
|
let prevSessionID;
|
|
87
80
|
app.attachStartCallback(function ({ sessionID }) {
|
|
88
|
-
if (sessionID !== prevSessionID) {
|
|
81
|
+
if (sessionID !== prevSessionID) {
|
|
82
|
+
// Send past page resources on a newly started session
|
|
89
83
|
performance.getEntriesByType('resource').forEach(resourceTiming);
|
|
90
84
|
prevSessionID = sessionID;
|
|
91
85
|
}
|
|
@@ -102,9 +96,7 @@ export default function (app, opts) {
|
|
|
102
96
|
return;
|
|
103
97
|
}
|
|
104
98
|
if (firstPaint === 0 || firstContentfulPaint === 0) {
|
|
105
|
-
performance
|
|
106
|
-
.getEntriesByType('paint')
|
|
107
|
-
.forEach((entry) => {
|
|
99
|
+
performance.getEntriesByType('paint').forEach((entry) => {
|
|
108
100
|
const { name, startTime } = entry;
|
|
109
101
|
switch (name) {
|
|
110
102
|
case 'first-paint':
|
|
@@ -119,7 +111,7 @@ export default function (app, opts) {
|
|
|
119
111
|
if (performance.timing.loadEventEnd || performance.now() > 30000) {
|
|
120
112
|
pageLoadTimingSent = true;
|
|
121
113
|
const { navigationStart, requestStart, responseStart, responseEnd, domContentLoadedEventStart, domContentLoadedEventEnd, loadEventStart, loadEventEnd, } = performance.timing;
|
|
122
|
-
app.send(
|
|
114
|
+
app.send(PageLoadTiming(requestStart - navigationStart || 0, responseStart - navigationStart || 0, responseEnd - navigationStart || 0, domContentLoadedEventStart - navigationStart || 0, domContentLoadedEventEnd - navigationStart || 0, loadEventStart - navigationStart || 0, loadEventEnd - navigationStart || 0, firstPaint, firstContentfulPaint));
|
|
123
115
|
}
|
|
124
116
|
}, 30);
|
|
125
117
|
}
|
|
@@ -142,23 +134,19 @@ export default function (app, opts) {
|
|
|
142
134
|
if (time - interactiveWindowTickTime > 50) {
|
|
143
135
|
interactiveWindowStartTime = time;
|
|
144
136
|
}
|
|
145
|
-
interactiveWindowTickTime =
|
|
146
|
-
time - interactiveWindowStartTime > 5000 ? null : time;
|
|
137
|
+
interactiveWindowTickTime = time - interactiveWindowStartTime > 5000 ? null : time;
|
|
147
138
|
}
|
|
148
|
-
if ((paintBlocks !== null && interactiveWindowTickTime === null) ||
|
|
149
|
-
time > 30000) {
|
|
139
|
+
if ((paintBlocks !== null && interactiveWindowTickTime === null) || time > 30000) {
|
|
150
140
|
pageRenderTimingSent = true;
|
|
151
141
|
resources = null;
|
|
152
142
|
const speedIndex = paintBlocks === null
|
|
153
143
|
? 0
|
|
154
144
|
: calculateSpeedIndex(firstContentfulPaint || firstPaint, paintBlocks);
|
|
155
145
|
const timeToInteractive = interactiveWindowTickTime === null
|
|
156
|
-
? Math.max(interactiveWindowStartTime, firstContentfulPaint, performance.timing.domContentLoadedEventEnd -
|
|
157
|
-
|
|
146
|
+
? Math.max(interactiveWindowStartTime, firstContentfulPaint, performance.timing.domContentLoadedEventEnd - performance.timing.navigationStart ||
|
|
147
|
+
0)
|
|
158
148
|
: 0;
|
|
159
|
-
app.send(
|
|
160
|
-
? firstContentfulPaint
|
|
161
|
-
: visuallyComplete, timeToInteractive));
|
|
149
|
+
app.send(PageRenderTiming(speedIndex, firstContentfulPaint > visuallyComplete ? firstContentfulPaint : visuallyComplete, timeToInteractive));
|
|
162
150
|
}
|
|
163
151
|
});
|
|
164
152
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type App from
|
|
1
|
+
import type App from '../app/index.js';
|
|
2
2
|
export default function (app: App): void;
|
package/lib/modules/viewport.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SetPageLocation, SetViewportSize, SetPageVisibility
|
|
1
|
+
import { SetPageLocation, SetViewportSize, SetPageVisibility } from '../app/messages.gen.js';
|
|
2
2
|
export default function (app) {
|
|
3
3
|
let url, width, height;
|
|
4
4
|
let navigationStart = performance.timing.navigationStart;
|
|
@@ -6,7 +6,7 @@ export default function (app) {
|
|
|
6
6
|
const { URL } = document;
|
|
7
7
|
if (URL !== url) {
|
|
8
8
|
url = URL;
|
|
9
|
-
app.send(
|
|
9
|
+
app.send(SetPageLocation(url, document.referrer, navigationStart));
|
|
10
10
|
navigationStart = 0;
|
|
11
11
|
}
|
|
12
12
|
});
|
|
@@ -15,12 +15,12 @@ export default function (app) {
|
|
|
15
15
|
if (innerWidth !== width || innerHeight !== height) {
|
|
16
16
|
width = innerWidth;
|
|
17
17
|
height = innerHeight;
|
|
18
|
-
app.send(
|
|
18
|
+
app.send(SetViewportSize(width, height));
|
|
19
19
|
}
|
|
20
20
|
});
|
|
21
21
|
const sendSetPageVisibility = document.hidden === undefined
|
|
22
22
|
? Function.prototype
|
|
23
|
-
: app.safe(() => app.send(
|
|
23
|
+
: app.safe(() => app.send(SetPageVisibility(document.hidden)));
|
|
24
24
|
app.attachStartCallback(() => {
|
|
25
25
|
url = '';
|
|
26
26
|
width = height = -1;
|