clarity-js 0.8.10-beta → 0.8.10
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/.lintstagedrc.yml +3 -0
- package/biome.json +43 -0
- package/build/clarity.extended.js +1 -1
- package/build/clarity.insight.js +1 -1
- package/build/clarity.js +3581 -3019
- package/build/clarity.min.js +1 -1
- package/build/clarity.module.js +3581 -3019
- package/build/clarity.performance.js +1 -1
- package/package.json +17 -10
- package/rollup.config.ts +84 -88
- package/src/clarity.ts +34 -28
- package/src/core/config.ts +2 -2
- package/src/core/event.ts +36 -32
- package/src/core/hash.ts +5 -6
- package/src/core/history.ts +10 -11
- package/src/core/index.ts +21 -11
- package/src/core/measure.ts +9 -5
- package/src/core/report.ts +9 -5
- package/src/core/scrub.ts +29 -20
- package/src/core/task.ts +73 -45
- package/src/core/time.ts +3 -3
- package/src/core/timeout.ts +2 -2
- package/src/core/version.ts +1 -1
- package/src/data/baseline.ts +60 -55
- package/src/data/consent.ts +2 -2
- package/src/data/custom.ts +8 -13
- package/src/data/dimension.ts +11 -7
- package/src/data/encode.ts +36 -30
- package/src/data/envelope.ts +38 -38
- package/src/data/extract.ts +86 -77
- package/src/data/index.ts +10 -6
- package/src/data/limit.ts +1 -1
- package/src/data/metadata.ts +305 -266
- package/src/data/metric.ts +18 -8
- package/src/data/ping.ts +8 -4
- package/src/data/signal.ts +18 -18
- package/src/data/summary.ts +6 -4
- package/src/data/token.ts +10 -8
- package/src/data/upgrade.ts +7 -3
- package/src/data/upload.ts +100 -49
- package/src/data/variable.ts +27 -20
- package/src/diagnostic/encode.ts +2 -2
- package/src/diagnostic/fraud.ts +3 -4
- package/src/diagnostic/internal.ts +11 -5
- package/src/diagnostic/script.ts +12 -8
- package/src/global.ts +1 -1
- package/src/insight/blank.ts +4 -4
- package/src/insight/encode.ts +23 -17
- package/src/insight/snapshot.ts +57 -37
- package/src/interaction/change.ts +9 -6
- package/src/interaction/click.ts +34 -28
- package/src/interaction/clipboard.ts +2 -2
- package/src/interaction/encode.ts +35 -31
- package/src/interaction/input.ts +11 -9
- package/src/interaction/pointer.ts +41 -30
- package/src/interaction/resize.ts +5 -5
- package/src/interaction/scroll.ts +20 -17
- package/src/interaction/selection.ts +12 -8
- package/src/interaction/submit.ts +2 -2
- package/src/interaction/timeline.ts +13 -9
- package/src/interaction/unload.ts +1 -1
- package/src/interaction/visibility.ts +2 -2
- package/src/layout/animation.ts +47 -41
- package/src/layout/discover.ts +5 -5
- package/src/layout/document.ts +31 -19
- package/src/layout/dom.ts +141 -91
- package/src/layout/encode.ts +52 -37
- package/src/layout/mutation.ts +321 -318
- package/src/layout/node.ts +104 -81
- package/src/layout/offset.ts +7 -6
- package/src/layout/region.ts +66 -43
- package/src/layout/schema.ts +15 -8
- package/src/layout/selector.ts +47 -25
- package/src/layout/style.ts +44 -36
- package/src/layout/target.ts +14 -10
- package/src/layout/traverse.ts +17 -11
- package/src/performance/blank.ts +1 -1
- package/src/performance/encode.ts +4 -4
- package/src/performance/interaction.ts +58 -70
- package/src/performance/navigation.ts +2 -2
- package/src/performance/observer.ts +59 -26
- package/src/queue.ts +16 -9
- package/tsconfig.json +2 -2
- package/tslint.json +25 -32
- package/types/core.d.ts +13 -13
- package/types/data.d.ts +32 -29
- package/types/diagnostic.d.ts +1 -1
- package/types/interaction.d.ts +5 -4
- package/types/layout.d.ts +36 -21
- package/types/performance.d.ts +6 -5
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { Constant, Event, Token } from "@clarity-types/data";
|
|
1
|
+
import { Constant, Event, type Token } from "@clarity-types/data";
|
|
2
2
|
import * as scrub from "@src/core/scrub";
|
|
3
3
|
import { time } from "@src/core/time";
|
|
4
4
|
import * as baseline from "@src/data/baseline";
|
|
5
5
|
import { queue } from "@src/data/upload";
|
|
6
|
-
import { metadata } from "@src/layout/target";
|
|
7
6
|
import * as change from "@src/interaction/change";
|
|
8
7
|
import * as click from "@src/interaction/click";
|
|
9
8
|
import * as clipboard from "@src/interaction/clipboard";
|
|
@@ -16,9 +15,10 @@ import * as submit from "@src/interaction/submit";
|
|
|
16
15
|
import * as timeline from "@src/interaction/timeline";
|
|
17
16
|
import * as unload from "@src/interaction/unload";
|
|
18
17
|
import * as visibility from "@src/interaction/visibility";
|
|
18
|
+
import { metadata } from "@src/layout/target";
|
|
19
19
|
|
|
20
20
|
export default async function (type: Event, ts: number = null): Promise<void> {
|
|
21
|
-
|
|
21
|
+
const t = ts || time();
|
|
22
22
|
let tokens: Token[] = [t, type];
|
|
23
23
|
switch (type) {
|
|
24
24
|
case Event.MouseDown:
|
|
@@ -30,15 +30,15 @@ export default async function (type: Event, ts: number = null): Promise<void> {
|
|
|
30
30
|
case Event.TouchEnd:
|
|
31
31
|
case Event.TouchMove:
|
|
32
32
|
case Event.TouchCancel:
|
|
33
|
-
for (
|
|
34
|
-
|
|
33
|
+
for (const entry of pointer.state) {
|
|
34
|
+
const pTarget = metadata(entry.data.target as Node, entry.event);
|
|
35
35
|
if (pTarget.id > 0) {
|
|
36
36
|
tokens = [entry.time, entry.event];
|
|
37
37
|
tokens.push(pTarget.id);
|
|
38
38
|
tokens.push(entry.data.x);
|
|
39
39
|
tokens.push(entry.data.y);
|
|
40
|
-
if (entry.data.id !== undefined) {
|
|
41
|
-
tokens.push(entry.data.id);
|
|
40
|
+
if (entry.data.id !== undefined) {
|
|
41
|
+
tokens.push(entry.data.id);
|
|
42
42
|
|
|
43
43
|
if (entry.data.isPrimary !== undefined) {
|
|
44
44
|
tokens.push(entry.data.isPrimary.toString());
|
|
@@ -53,10 +53,10 @@ export default async function (type: Event, ts: number = null): Promise<void> {
|
|
|
53
53
|
pointer.reset();
|
|
54
54
|
break;
|
|
55
55
|
case Event.Click:
|
|
56
|
-
for (
|
|
57
|
-
|
|
56
|
+
for (const entry of click.state) {
|
|
57
|
+
const cTarget = metadata(entry.data.target as Node, entry.event, entry.data.text);
|
|
58
58
|
tokens = [entry.time, entry.event];
|
|
59
|
-
|
|
59
|
+
const cHash = cTarget.hash ? cTarget.hash.join(Constant.Dot) : Constant.Empty;
|
|
60
60
|
tokens.push(cTarget.id);
|
|
61
61
|
tokens.push(entry.data.x);
|
|
62
62
|
tokens.push(entry.data.y);
|
|
@@ -76,9 +76,9 @@ export default async function (type: Event, ts: number = null): Promise<void> {
|
|
|
76
76
|
click.reset();
|
|
77
77
|
break;
|
|
78
78
|
case Event.Clipboard:
|
|
79
|
-
for (
|
|
79
|
+
for (const entry of clipboard.state) {
|
|
80
80
|
tokens = [entry.time, entry.event];
|
|
81
|
-
|
|
81
|
+
const target = metadata(entry.data.target as Node, entry.event);
|
|
82
82
|
if (target.id > 0) {
|
|
83
83
|
tokens.push(target.id);
|
|
84
84
|
tokens.push(entry.data.action);
|
|
@@ -87,24 +87,26 @@ export default async function (type: Event, ts: number = null): Promise<void> {
|
|
|
87
87
|
}
|
|
88
88
|
clipboard.reset();
|
|
89
89
|
break;
|
|
90
|
-
case Event.Resize:
|
|
91
|
-
|
|
90
|
+
case Event.Resize: {
|
|
91
|
+
const r = resize.data;
|
|
92
92
|
tokens.push(r.width);
|
|
93
93
|
tokens.push(r.height);
|
|
94
94
|
baseline.track(type, r.width, r.height);
|
|
95
95
|
resize.reset();
|
|
96
96
|
queue(tokens);
|
|
97
97
|
break;
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
}
|
|
99
|
+
case Event.Unload: {
|
|
100
|
+
const u = unload.data;
|
|
100
101
|
tokens.push(u.name);
|
|
101
102
|
tokens.push(u.persisted);
|
|
102
103
|
unload.reset();
|
|
103
104
|
queue(tokens);
|
|
104
105
|
break;
|
|
106
|
+
}
|
|
105
107
|
case Event.Input:
|
|
106
|
-
for (
|
|
107
|
-
|
|
108
|
+
for (const entry of input.state) {
|
|
109
|
+
const iTarget = metadata(entry.data.target as Node, entry.event, entry.data.value);
|
|
108
110
|
tokens = [entry.time, entry.event];
|
|
109
111
|
tokens.push(iTarget.id);
|
|
110
112
|
tokens.push(scrub.text(entry.data.value, "input", iTarget.privacy, false, entry.data.type));
|
|
@@ -112,11 +114,11 @@ export default async function (type: Event, ts: number = null): Promise<void> {
|
|
|
112
114
|
}
|
|
113
115
|
input.reset();
|
|
114
116
|
break;
|
|
115
|
-
case Event.Selection:
|
|
116
|
-
|
|
117
|
+
case Event.Selection: {
|
|
118
|
+
const s = selection.data;
|
|
117
119
|
if (s) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
+
const startTarget = metadata(s.start as Node, type);
|
|
121
|
+
const endTarget = metadata(s.end as Node, type);
|
|
120
122
|
tokens.push(startTarget.id);
|
|
121
123
|
tokens.push(s.startOffset);
|
|
122
124
|
tokens.push(endTarget.id);
|
|
@@ -125,9 +127,10 @@ export default async function (type: Event, ts: number = null): Promise<void> {
|
|
|
125
127
|
queue(tokens);
|
|
126
128
|
}
|
|
127
129
|
break;
|
|
130
|
+
}
|
|
128
131
|
case Event.Scroll:
|
|
129
|
-
for (
|
|
130
|
-
|
|
132
|
+
for (const entry of scroll.state) {
|
|
133
|
+
const sTarget = metadata(entry.data.target as Node, entry.event);
|
|
131
134
|
const top = metadata(entry.data.top as Node, entry.event);
|
|
132
135
|
const bottom = metadata(entry.data.bottom as Node, entry.event);
|
|
133
136
|
const sTopHash = top?.hash ? top.hash.join(Constant.Dot) : Constant.Empty;
|
|
@@ -146,9 +149,9 @@ export default async function (type: Event, ts: number = null): Promise<void> {
|
|
|
146
149
|
scroll.reset();
|
|
147
150
|
break;
|
|
148
151
|
case Event.Change:
|
|
149
|
-
for (
|
|
152
|
+
for (const entry of change.state) {
|
|
150
153
|
tokens = [entry.time, entry.event];
|
|
151
|
-
|
|
154
|
+
const target = metadata(entry.data.target as Node, entry.event);
|
|
152
155
|
if (target.id > 0) {
|
|
153
156
|
tokens = [entry.time, entry.event];
|
|
154
157
|
tokens.push(target.id);
|
|
@@ -161,9 +164,9 @@ export default async function (type: Event, ts: number = null): Promise<void> {
|
|
|
161
164
|
change.reset();
|
|
162
165
|
break;
|
|
163
166
|
case Event.Submit:
|
|
164
|
-
for (
|
|
167
|
+
for (const entry of submit.state) {
|
|
165
168
|
tokens = [entry.time, entry.event];
|
|
166
|
-
|
|
169
|
+
const target = metadata(entry.data.target as Node, entry.event);
|
|
167
170
|
if (target.id > 0) {
|
|
168
171
|
tokens.push(target.id);
|
|
169
172
|
queue(tokens);
|
|
@@ -172,7 +175,7 @@ export default async function (type: Event, ts: number = null): Promise<void> {
|
|
|
172
175
|
submit.reset();
|
|
173
176
|
break;
|
|
174
177
|
case Event.Timeline:
|
|
175
|
-
for (
|
|
178
|
+
for (const entry of timeline.updates) {
|
|
176
179
|
tokens = [entry.time, entry.event];
|
|
177
180
|
tokens.push(entry.data.type);
|
|
178
181
|
tokens.push(entry.data.hash);
|
|
@@ -184,12 +187,13 @@ export default async function (type: Event, ts: number = null): Promise<void> {
|
|
|
184
187
|
}
|
|
185
188
|
timeline.reset();
|
|
186
189
|
break;
|
|
187
|
-
case Event.Visibility:
|
|
188
|
-
|
|
190
|
+
case Event.Visibility: {
|
|
191
|
+
const v = visibility.data;
|
|
189
192
|
tokens.push(v.visible);
|
|
190
193
|
queue(tokens);
|
|
191
194
|
baseline.visibility(t, v.visible);
|
|
192
195
|
visibility.reset();
|
|
193
196
|
break;
|
|
197
|
+
}
|
|
194
198
|
}
|
|
195
199
|
}
|
package/src/interaction/input.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Event } from "@clarity-types/data";
|
|
2
|
-
import { InputData, InputState, Setting } from "@clarity-types/interaction";
|
|
2
|
+
import { type InputData, type InputState, Setting } from "@clarity-types/interaction";
|
|
3
3
|
import { FunctionNames } from "@clarity-types/performance";
|
|
4
4
|
import { bind } from "@src/core/event";
|
|
5
5
|
import { schedule } from "@src/core/task";
|
|
6
6
|
import { time } from "@src/core/time";
|
|
7
7
|
import { clearTimeout, setTimeout } from "@src/core/timeout";
|
|
8
8
|
import { get } from "@src/layout/dom";
|
|
9
|
-
import encode from "./encode";
|
|
10
9
|
import { target } from "@src/layout/target";
|
|
10
|
+
import encode from "./encode";
|
|
11
11
|
|
|
12
12
|
let timeout: number = null;
|
|
13
13
|
export let state: InputState[] = [];
|
|
@@ -22,11 +22,11 @@ export function observe(root: Node): void {
|
|
|
22
22
|
|
|
23
23
|
function recompute(evt: UIEvent): void {
|
|
24
24
|
recompute.dn = FunctionNames.InputRecompute;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (input
|
|
25
|
+
const input = target(evt) as HTMLInputElement;
|
|
26
|
+
const value = get(input);
|
|
27
|
+
if (input?.type && value) {
|
|
28
28
|
let v = input.value;
|
|
29
|
-
|
|
29
|
+
const t = input.type;
|
|
30
30
|
switch (input.type) {
|
|
31
31
|
case "radio":
|
|
32
32
|
case "checkbox":
|
|
@@ -34,10 +34,12 @@ function recompute(evt: UIEvent): void {
|
|
|
34
34
|
break;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
const data: InputData = { target: input, value: v, type: t };
|
|
38
38
|
|
|
39
39
|
// If last entry in the queue is for the same target node as the current one, remove it so we can later swap it with current data.
|
|
40
|
-
if (state.length > 0 &&
|
|
40
|
+
if (state.length > 0 && state[state.length - 1].data.target === data.target) {
|
|
41
|
+
state.pop();
|
|
42
|
+
}
|
|
41
43
|
|
|
42
44
|
state.push({ time: time(evt), event: Event.Input, data });
|
|
43
45
|
|
|
@@ -57,4 +59,4 @@ export function reset(): void {
|
|
|
57
59
|
export function stop(): void {
|
|
58
60
|
clearTimeout(timeout);
|
|
59
61
|
reset();
|
|
60
|
-
}
|
|
62
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Event } from "@clarity-types/data";
|
|
2
|
-
import { PointerState, Setting } from "@clarity-types/interaction";
|
|
2
|
+
import { type PointerState, Setting } from "@clarity-types/interaction";
|
|
3
3
|
import { FunctionNames } from "@clarity-types/performance";
|
|
4
4
|
import { bind } from "@src/core/event";
|
|
5
5
|
import { schedule } from "@src/core/task";
|
|
@@ -34,45 +34,47 @@ export function observe(root: Node): void {
|
|
|
34
34
|
|
|
35
35
|
function mouse(event: Event, root: Node, evt: MouseEvent): void {
|
|
36
36
|
mouse.dn = FunctionNames.PointerMouse;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
let x = "pageX" in evt ? Math.round(evt.pageX) :
|
|
40
|
-
let y = "pageY" in evt ? Math.round(evt.pageY) :
|
|
37
|
+
const frame = iframe(root);
|
|
38
|
+
const d = frame ? frame.contentDocument.documentElement : document.documentElement;
|
|
39
|
+
let x = "pageX" in evt ? Math.round(evt.pageX) : "clientX" in evt ? Math.round((evt as MouseEvent).clientX + d.scrollLeft) : null;
|
|
40
|
+
let y = "pageY" in evt ? Math.round(evt.pageY) : "clientY" in evt ? Math.round((evt as MouseEvent).clientY + d.scrollTop) : null;
|
|
41
41
|
// In case of iframe, we adjust (x,y) to be relative to top parent's origin
|
|
42
42
|
if (frame) {
|
|
43
|
-
|
|
43
|
+
const distance = offset(frame);
|
|
44
44
|
x = x ? x + Math.round(distance.x) : x;
|
|
45
45
|
y = y ? y + Math.round(distance.y) : y;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
// Check for null values before processing this event
|
|
49
|
-
if (x !== null && y !== null) {
|
|
49
|
+
if (x !== null && y !== null) {
|
|
50
|
+
handler({ time: time(evt), event, data: { target: target(evt), x, y } });
|
|
51
|
+
}
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
function touch(event: Event, root: Node, evt: TouchEvent): void {
|
|
53
55
|
touch.dn = FunctionNames.PointerTouch;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
const frame = iframe(root);
|
|
57
|
+
const d = frame ? frame.contentDocument.documentElement : document.documentElement;
|
|
58
|
+
const touches = evt.changedTouches;
|
|
57
59
|
|
|
58
|
-
|
|
60
|
+
const t = time(evt);
|
|
59
61
|
if (touches) {
|
|
60
62
|
for (let i = 0; i < touches.length; i++) {
|
|
61
|
-
|
|
62
|
-
let x = "clientX" in entry ? Math.round(entry
|
|
63
|
-
let y = "clientY" in entry ? Math.round(entry
|
|
63
|
+
const entry = touches[i];
|
|
64
|
+
let x = "clientX" in entry ? Math.round(entry.clientX + d.scrollLeft) : null;
|
|
65
|
+
let y = "clientY" in entry ? Math.round(entry.clientY + d.scrollTop) : null;
|
|
64
66
|
x = x && frame ? x + Math.round(frame.offsetLeft) : x;
|
|
65
67
|
y = y && frame ? y + Math.round(frame.offsetTop) : y;
|
|
66
68
|
|
|
67
69
|
// We cannot rely on identifier to determine primary touch as its value doesn't always start with 0.
|
|
68
70
|
// Safari/Webkit uses the address of the UITouch object as the identifier value for each touch point.
|
|
69
|
-
const id = "identifier" in entry ? entry
|
|
71
|
+
const id = "identifier" in entry ? entry.identifier : undefined;
|
|
70
72
|
|
|
71
|
-
switch(event) {
|
|
73
|
+
switch (event) {
|
|
72
74
|
case Event.TouchStart:
|
|
73
75
|
if (activeTouchPointIds.size === 0) {
|
|
74
76
|
// Track presence of primary touch separately to handle scenarios when same id is repeated
|
|
75
|
-
hasPrimaryTouch = true;
|
|
77
|
+
hasPrimaryTouch = true;
|
|
76
78
|
primaryTouchId = id;
|
|
77
79
|
}
|
|
78
80
|
activeTouchPointIds.add(id);
|
|
@@ -85,11 +87,15 @@ function touch(event: Event, root: Node, evt: TouchEvent): void {
|
|
|
85
87
|
const isPrimary = hasPrimaryTouch && primaryTouchId === id;
|
|
86
88
|
|
|
87
89
|
// Check for null values before processing this event
|
|
88
|
-
if (x !== null && y !== null) {
|
|
90
|
+
if (x !== null && y !== null) {
|
|
91
|
+
handler({ time: t, event, data: { target: target(evt), x, y, id, isPrimary } });
|
|
92
|
+
}
|
|
89
93
|
|
|
90
94
|
// Reset primary touch point id once touch event ends
|
|
91
95
|
if (event === Event.TouchCancel || event === Event.TouchEnd) {
|
|
92
|
-
if (primaryTouchId === id) {
|
|
96
|
+
if (primaryTouchId === id) {
|
|
97
|
+
hasPrimaryTouch = false;
|
|
98
|
+
}
|
|
93
99
|
}
|
|
94
100
|
}
|
|
95
101
|
}
|
|
@@ -99,15 +105,18 @@ function handler(current: PointerState): void {
|
|
|
99
105
|
switch (current.event) {
|
|
100
106
|
case Event.MouseMove:
|
|
101
107
|
case Event.MouseWheel:
|
|
102
|
-
case Event.TouchMove:
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (last && similar(last, current)) {
|
|
108
|
+
case Event.TouchMove: {
|
|
109
|
+
const length = state.length;
|
|
110
|
+
const last = length > 1 ? state[length - 2] : null;
|
|
111
|
+
if (last && similar(last, current)) {
|
|
112
|
+
state.pop();
|
|
113
|
+
}
|
|
106
114
|
state.push(current);
|
|
107
115
|
|
|
108
116
|
clearTimeout(timeout);
|
|
109
117
|
timeout = setTimeout(process, Setting.LookAhead, current.event);
|
|
110
118
|
break;
|
|
119
|
+
}
|
|
111
120
|
default:
|
|
112
121
|
state.push(current);
|
|
113
122
|
process(current.event);
|
|
@@ -124,17 +133,19 @@ export function reset(): void {
|
|
|
124
133
|
}
|
|
125
134
|
|
|
126
135
|
function similar(last: PointerState, current: PointerState): boolean {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
136
|
+
const dx = last.data.x - current.data.x;
|
|
137
|
+
const dy = last.data.y - current.data.y;
|
|
138
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
139
|
+
const gap = current.time - last.time;
|
|
140
|
+
const match = current.data.target === last.data.target;
|
|
141
|
+
const sameId = current.data.id !== undefined ? current.data.id === last.data.id : true;
|
|
133
142
|
return current.event === last.event && match && distance < Setting.Distance && gap < Setting.Interval && sameId;
|
|
134
143
|
}
|
|
135
144
|
|
|
136
145
|
export function stop(): void {
|
|
137
146
|
clearTimeout(timeout);
|
|
138
147
|
// Send out any pending pointer events in the pipeline
|
|
139
|
-
if (state.length > 0) {
|
|
148
|
+
if (state.length > 0) {
|
|
149
|
+
process(state[state.length - 1].event);
|
|
150
|
+
}
|
|
140
151
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Event } from "@clarity-types/data";
|
|
2
|
-
import { ResizeData, Setting } from "@clarity-types/interaction";
|
|
2
|
+
import { type ResizeData, Setting } from "@clarity-types/interaction";
|
|
3
3
|
import { FunctionNames } from "@clarity-types/performance";
|
|
4
|
-
import { clearTimeout, setTimeout } from "@src/core/timeout";
|
|
5
4
|
import { bind } from "@src/core/event";
|
|
6
|
-
import encode from "./encode";
|
|
7
5
|
import { schedule } from "@src/core/task";
|
|
6
|
+
import { clearTimeout, setTimeout } from "@src/core/timeout";
|
|
7
|
+
import encode from "./encode";
|
|
8
8
|
|
|
9
9
|
export let data: ResizeData;
|
|
10
10
|
let timeout: number = null;
|
|
@@ -18,7 +18,7 @@ export function start(): void {
|
|
|
18
18
|
|
|
19
19
|
function recompute(): void {
|
|
20
20
|
recompute.dn = FunctionNames.ResizeRecompute;
|
|
21
|
-
|
|
21
|
+
const de = document.documentElement;
|
|
22
22
|
// window.innerWidth includes width of the scrollbar and is not a true representation of the viewport width.
|
|
23
23
|
// Therefore, when possible, use documentElement's clientWidth property.
|
|
24
24
|
data = {
|
|
@@ -45,4 +45,4 @@ export function reset(): void {
|
|
|
45
45
|
|
|
46
46
|
export function stop(): void {
|
|
47
47
|
reset();
|
|
48
|
-
}
|
|
48
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { Constant, Dimension, Event } from "@clarity-types/data";
|
|
2
|
-
import { ScrollState, Setting } from "@clarity-types/interaction";
|
|
2
|
+
import { type ScrollState, Setting } from "@clarity-types/interaction";
|
|
3
3
|
import { FunctionNames } from "@clarity-types/performance";
|
|
4
4
|
import { bind } from "@src/core/event";
|
|
5
5
|
import { schedule } from "@src/core/task";
|
|
6
6
|
import { time } from "@src/core/time";
|
|
7
7
|
import { clearTimeout, setTimeout } from "@src/core/timeout";
|
|
8
|
+
import * as dimension from "@src/data/dimension";
|
|
8
9
|
import { iframe } from "@src/layout/dom";
|
|
9
|
-
import {
|
|
10
|
+
import { metadata, target } from "@src/layout/target";
|
|
10
11
|
import encode from "./encode";
|
|
11
|
-
import * as dimension from "@src/data/dimension";
|
|
12
12
|
|
|
13
13
|
export let state: ScrollState[] = [];
|
|
14
14
|
let initialTop: Node = null;
|
|
@@ -21,8 +21,8 @@ export function start(): void {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export function observe(root: Node): void {
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
const frame = iframe(root);
|
|
25
|
+
const node = frame ? frame.contentWindow : root === document ? window : root;
|
|
26
26
|
bind(node, "scroll", recompute, true);
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -34,7 +34,7 @@ function recompute(event: UIEvent = null): void {
|
|
|
34
34
|
|
|
35
35
|
// If the target is a Document node, then identify corresponding documentElement and window for this document
|
|
36
36
|
if (element && element.nodeType === Node.DOCUMENT_NODE) {
|
|
37
|
-
|
|
37
|
+
const frame = iframe(element);
|
|
38
38
|
w = frame ? frame.contentWindow : w;
|
|
39
39
|
element = de = (element as Document).documentElement;
|
|
40
40
|
}
|
|
@@ -42,8 +42,8 @@ function recompute(event: UIEvent = null): void {
|
|
|
42
42
|
// Edge doesn't support scrollTop position on document.documentElement.
|
|
43
43
|
// For cross browser compatibility, looking up pageYOffset on window if the scroll is on document.
|
|
44
44
|
// And, if for some reason that is not available, fall back to looking up scrollTop on document.documentElement.
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
const x = element === de && "pageXOffset" in w ? Math.round(w.pageXOffset) : Math.round((element as HTMLElement).scrollLeft);
|
|
46
|
+
const y = element === de && "pageYOffset" in w ? Math.round(w.pageYOffset) : Math.round((element as HTMLElement).scrollTop);
|
|
47
47
|
const width = window.innerWidth;
|
|
48
48
|
const height = window.innerHeight;
|
|
49
49
|
const xPosition = width / 3;
|
|
@@ -53,18 +53,20 @@ function recompute(event: UIEvent = null): void {
|
|
|
53
53
|
const top = getPositionNode(xPosition, startYPosition);
|
|
54
54
|
const bottom = getPositionNode(xPosition, endYPosition);
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
const current: ScrollState = { time: time(event), event: Event.Scroll, data: { target: element, x, y, top, bottom } };
|
|
57
57
|
|
|
58
58
|
// We don't send any scroll events if this is the first event and the current position is top (0,0)
|
|
59
|
-
if ((event === null && x === 0 && y === 0) ||
|
|
59
|
+
if ((event === null && x === 0 && y === 0) || x === null || y === null) {
|
|
60
60
|
initialTop = top;
|
|
61
61
|
initialBottom = bottom;
|
|
62
62
|
return;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (last && similar(last, current)) {
|
|
65
|
+
const length = state.length;
|
|
66
|
+
const last = length > 1 ? state[length - 2] : null;
|
|
67
|
+
if (last && similar(last, current)) {
|
|
68
|
+
state.pop();
|
|
69
|
+
}
|
|
68
70
|
state.push(current);
|
|
69
71
|
|
|
70
72
|
clearTimeout(timeout);
|
|
@@ -74,9 +76,10 @@ function recompute(event: UIEvent = null): void {
|
|
|
74
76
|
function getPositionNode(x: number, y: number): Node {
|
|
75
77
|
let node: Node;
|
|
76
78
|
if ("caretPositionFromPoint" in document) {
|
|
79
|
+
// biome-ignore lint/suspicious/noExplicitAny: caretPositionFromPoint is not defined on all browsers, makes typescript unhappy
|
|
77
80
|
node = (document as any).caretPositionFromPoint(x, y)?.offsetNode;
|
|
78
81
|
} else if ("caretRangeFromPoint" in document) {
|
|
79
|
-
node = (document as
|
|
82
|
+
node = (document as Document).caretRangeFromPoint(x, y)?.startContainer;
|
|
80
83
|
}
|
|
81
84
|
if (!node) {
|
|
82
85
|
node = document.elementFromPoint(x, y) as Node;
|
|
@@ -99,9 +102,9 @@ function process(event: Event): void {
|
|
|
99
102
|
}
|
|
100
103
|
|
|
101
104
|
function similar(last: ScrollState, current: ScrollState): boolean {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return
|
|
105
|
+
const dx = last.data.x - current.data.x;
|
|
106
|
+
const dy = last.data.y - current.data.y;
|
|
107
|
+
return dx * dx + dy * dy < Setting.Distance * Setting.Distance && current.time - last.time < Setting.Interval;
|
|
105
108
|
}
|
|
106
109
|
|
|
107
110
|
export function compute(): void {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Event } from "@clarity-types/data";
|
|
2
|
-
import { SelectionData, Setting } from "@clarity-types/interaction";
|
|
2
|
+
import { type SelectionData, Setting } from "@clarity-types/interaction";
|
|
3
3
|
import { FunctionNames } from "@clarity-types/performance";
|
|
4
4
|
import { bind } from "@src/core/event";
|
|
5
5
|
import { schedule } from "@src/core/task";
|
|
@@ -21,21 +21,25 @@ export function observe(root: Node): void {
|
|
|
21
21
|
|
|
22
22
|
function recompute(root: Node): void {
|
|
23
23
|
recompute.dn = FunctionNames.SelectionRecompute;
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
const doc = root.nodeType === Node.DOCUMENT_NODE ? (root as Document) : document;
|
|
25
|
+
const current = doc.getSelection();
|
|
26
26
|
|
|
27
27
|
// Bail out if we don't have a valid selection
|
|
28
|
-
if (current === null) {
|
|
28
|
+
if (current === null) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
29
31
|
|
|
30
32
|
// Bail out if we got a valid selection but not valid nodes
|
|
31
33
|
// In Edge, selectionchange gets fired even on interactions like right clicks and
|
|
32
34
|
// can result in null anchorNode and focusNode if there was no previous selection on page
|
|
33
35
|
// Also, ignore any selections that start and end at the exact same point
|
|
34
|
-
if (
|
|
35
|
-
(current.anchorNode ===
|
|
36
|
+
if (
|
|
37
|
+
(current.anchorNode === null && current.focusNode === null) ||
|
|
38
|
+
(current.anchorNode === current.focusNode && current.anchorOffset === current.focusOffset)
|
|
39
|
+
) {
|
|
36
40
|
return;
|
|
37
41
|
}
|
|
38
|
-
|
|
42
|
+
const startNode = data.start ? data.start : null;
|
|
39
43
|
if (previous !== null && data.start !== null && startNode !== current.anchorNode) {
|
|
40
44
|
clearTimeout(timeout);
|
|
41
45
|
process(Event.Selection);
|
|
@@ -45,7 +49,7 @@ function recompute(root: Node): void {
|
|
|
45
49
|
start: current.anchorNode,
|
|
46
50
|
startOffset: current.anchorOffset,
|
|
47
51
|
end: current.focusNode,
|
|
48
|
-
endOffset: current.focusOffset
|
|
52
|
+
endOffset: current.focusOffset,
|
|
49
53
|
};
|
|
50
54
|
previous = current;
|
|
51
55
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Event } from "@clarity-types/data";
|
|
2
|
-
import { SubmitState } from "@clarity-types/interaction";
|
|
2
|
+
import type { SubmitState } from "@clarity-types/interaction";
|
|
3
3
|
import { FunctionNames } from "@clarity-types/performance";
|
|
4
4
|
import { bind } from "@src/core/event";
|
|
5
5
|
import { schedule } from "@src/core/task";
|
|
6
6
|
import { time } from "@src/core/time";
|
|
7
|
-
import encode from "./encode";
|
|
8
7
|
import { target } from "@src/layout/target";
|
|
8
|
+
import encode from "./encode";
|
|
9
9
|
|
|
10
10
|
export let state: SubmitState[] = [];
|
|
11
11
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BooleanFlag, Event } from "@clarity-types/data";
|
|
2
|
-
import { BrowsingContext, Setting, TimelineState } from "@clarity-types/interaction";
|
|
2
|
+
import { BrowsingContext, Setting, type TimelineState } from "@clarity-types/interaction";
|
|
3
3
|
import * as baseline from "@src/data/baseline";
|
|
4
4
|
import * as envelope from "@src/data/envelope";
|
|
5
5
|
import encode from "@src/interaction/encode";
|
|
@@ -16,13 +16,15 @@ export function reset(): void {
|
|
|
16
16
|
updates = [];
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export function track(
|
|
19
|
+
export function track(
|
|
20
|
+
time: number,
|
|
20
21
|
event: Event,
|
|
21
22
|
hash: string,
|
|
22
23
|
x: number,
|
|
23
24
|
y: number,
|
|
24
25
|
reaction: number = BooleanFlag.True,
|
|
25
|
-
context: number = BrowsingContext.Self
|
|
26
|
+
context: number = BrowsingContext.Self,
|
|
27
|
+
): void {
|
|
26
28
|
state.push({
|
|
27
29
|
time,
|
|
28
30
|
event: Event.Timeline,
|
|
@@ -32,8 +34,8 @@ export function track(time: number,
|
|
|
32
34
|
x,
|
|
33
35
|
y,
|
|
34
36
|
reaction,
|
|
35
|
-
context
|
|
36
|
-
}
|
|
37
|
+
context,
|
|
38
|
+
},
|
|
37
39
|
});
|
|
38
40
|
|
|
39
41
|
// Since timeline only keeps the data for configured time, we still want to continue tracking these values
|
|
@@ -45,12 +47,14 @@ export function track(time: number,
|
|
|
45
47
|
export function compute(): void {
|
|
46
48
|
const temp = [];
|
|
47
49
|
updates = [];
|
|
48
|
-
|
|
49
|
-
|
|
50
|
+
const max = envelope.data.start + envelope.data.duration;
|
|
51
|
+
const min = Math.max(max - Setting.TimelineSpan, 0);
|
|
50
52
|
|
|
51
|
-
for (
|
|
53
|
+
for (const s of state) {
|
|
52
54
|
if (s.time >= min) {
|
|
53
|
-
if (s.time <= max) {
|
|
55
|
+
if (s.time <= max) {
|
|
56
|
+
updates.push(s);
|
|
57
|
+
}
|
|
54
58
|
temp.push(s);
|
|
55
59
|
}
|
|
56
60
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BooleanFlag, Event } from "@clarity-types/data";
|
|
2
|
-
import { UnloadData } from "@clarity-types/interaction";
|
|
2
|
+
import type { UnloadData } from "@clarity-types/interaction";
|
|
3
3
|
import { FunctionNames } from "@clarity-types/performance";
|
|
4
4
|
import * as clarity from "@src/clarity";
|
|
5
5
|
import { bind } from "@src/core/event";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Event } from "@clarity-types/data";
|
|
2
|
-
import { VisibilityData } from "@clarity-types/interaction";
|
|
2
|
+
import type { VisibilityData } from "@clarity-types/interaction";
|
|
3
3
|
import { FunctionNames } from "@clarity-types/performance";
|
|
4
4
|
import { bind } from "@src/core/event";
|
|
5
5
|
import { time } from "@src/core/time";
|
|
@@ -24,4 +24,4 @@ export function reset(): void {
|
|
|
24
24
|
|
|
25
25
|
export function stop(): void {
|
|
26
26
|
reset();
|
|
27
|
-
}
|
|
27
|
+
}
|