clarity-js 0.8.12 → 0.8.13-beta
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/build/clarity.extended.js +1 -1
- package/build/clarity.insight.js +1 -1
- package/build/clarity.js +3952 -4179
- package/build/clarity.min.js +1 -1
- package/build/clarity.module.js +3952 -4179
- package/build/clarity.performance.js +1 -1
- package/package.json +69 -76
- package/rollup.config.ts +88 -84
- package/src/clarity.ts +28 -34
- package/src/core/api.ts +1 -8
- package/src/core/config.ts +2 -2
- package/src/core/event.ts +32 -36
- package/src/core/hash.ts +6 -5
- package/src/core/history.ts +11 -10
- package/src/core/index.ts +11 -21
- package/src/core/measure.ts +5 -9
- package/src/core/report.ts +3 -3
- package/src/core/scrub.ts +20 -29
- package/src/core/task.ts +45 -73
- 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 +55 -60
- package/src/data/consent.ts +2 -2
- package/src/data/custom.ts +13 -8
- package/src/data/dimension.ts +7 -11
- package/src/data/encode.ts +30 -36
- package/src/data/envelope.ts +38 -38
- package/src/data/extract.ts +77 -86
- package/src/data/index.ts +6 -10
- package/src/data/limit.ts +1 -1
- package/src/data/metadata.ts +266 -305
- package/src/data/metric.ts +8 -18
- package/src/data/ping.ts +4 -8
- package/src/data/signal.ts +18 -18
- package/src/data/summary.ts +4 -6
- package/src/data/token.ts +8 -10
- package/src/data/upgrade.ts +3 -7
- package/src/data/upload.ts +49 -100
- package/src/data/variable.ts +20 -27
- package/src/diagnostic/encode.ts +2 -2
- package/src/diagnostic/fraud.ts +4 -3
- package/src/diagnostic/internal.ts +5 -11
- package/src/diagnostic/script.ts +8 -12
- package/src/global.ts +1 -1
- package/src/insight/blank.ts +4 -4
- package/src/insight/encode.ts +17 -23
- package/src/insight/snapshot.ts +37 -57
- package/src/interaction/change.ts +6 -9
- package/src/interaction/click.ts +28 -34
- package/src/interaction/clipboard.ts +2 -2
- package/src/interaction/encode.ts +31 -35
- package/src/interaction/input.ts +9 -11
- package/src/interaction/pointer.ts +24 -35
- package/src/interaction/resize.ts +5 -5
- package/src/interaction/scroll.ts +11 -14
- package/src/interaction/selection.ts +8 -12
- package/src/interaction/submit.ts +2 -2
- package/src/interaction/timeline.ts +9 -13
- package/src/interaction/unload.ts +1 -1
- package/src/interaction/visibility.ts +2 -2
- package/src/layout/animation.ts +41 -47
- package/src/layout/discover.ts +5 -5
- package/src/layout/document.ts +19 -31
- package/src/layout/dom.ts +91 -141
- package/src/layout/encode.ts +37 -52
- package/src/layout/mutation.ts +318 -321
- package/src/layout/node.ts +81 -104
- package/src/layout/offset.ts +6 -7
- package/src/layout/region.ts +40 -60
- package/src/layout/schema.ts +8 -15
- package/src/layout/selector.ts +25 -47
- package/src/layout/style.ts +36 -44
- package/src/layout/target.ts +10 -14
- package/src/layout/traverse.ts +11 -17
- package/src/performance/blank.ts +1 -1
- package/src/performance/encode.ts +4 -4
- package/src/performance/interaction.ts +70 -58
- package/src/performance/navigation.ts +2 -2
- package/src/performance/observer.ts +26 -59
- package/src/queue.ts +9 -16
- package/tsconfig.json +1 -1
- package/tslint.json +32 -25
- package/types/core.d.ts +13 -13
- package/types/data.d.ts +29 -33
- package/types/diagnostic.d.ts +1 -1
- package/types/interaction.d.ts +4 -4
- package/types/layout.d.ts +21 -36
- package/types/performance.d.ts +5 -6
- package/.lintstagedrc.yml +0 -3
- package/biome.json +0 -43
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import { Code, Event, Severity } from "@clarity-types/data";
|
|
2
|
+
import { LogData } from "@clarity-types/diagnostic";
|
|
3
3
|
import encode from "./encode";
|
|
4
4
|
|
|
5
5
|
let history: { [key: number]: string[] } = {};
|
|
@@ -10,21 +10,15 @@ export function start(): void {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export function log(code: Code, severity: Severity, name: string = null, message: string = null, stack: string = null): void {
|
|
13
|
-
|
|
13
|
+
let key = name ? `${name}|${message}`: "";
|
|
14
14
|
// While rare, it's possible for code to fail repeatedly during the lifetime of the same page
|
|
15
15
|
// In those cases, we only want to log the failure once and not spam logs with redundant information.
|
|
16
|
-
if (code in history && history[code].indexOf(key) >= 0) {
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
16
|
+
if (code in history && history[code].indexOf(key) >= 0) { return; }
|
|
19
17
|
|
|
20
18
|
data = { code, name, message, stack, severity };
|
|
21
19
|
|
|
22
20
|
// Maintain history of errors in memory to avoid sending redundant information
|
|
23
|
-
if (code in history) {
|
|
24
|
-
history[code].push(key);
|
|
25
|
-
} else {
|
|
26
|
-
history[code] = [key];
|
|
27
|
-
}
|
|
21
|
+
if (code in history) { history[code].push(key); } else { history[code] = [key]; }
|
|
28
22
|
|
|
29
23
|
encode(Event.Log);
|
|
30
24
|
}
|
package/src/diagnostic/script.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Event, Setting } from "@clarity-types/data";
|
|
2
|
-
import
|
|
2
|
+
import { ScriptErrorData } from "@clarity-types/diagnostic";
|
|
3
3
|
import { FunctionNames } from "@clarity-types/performance";
|
|
4
4
|
import { bind } from "@src/core/event";
|
|
5
5
|
import encode from "./encode";
|
|
@@ -14,24 +14,20 @@ export function start(): void {
|
|
|
14
14
|
|
|
15
15
|
function handler(error: ErrorEvent): boolean {
|
|
16
16
|
handler.dn = FunctionNames.ScriptHandler;
|
|
17
|
-
|
|
17
|
+
let e = error["error"] || error;
|
|
18
18
|
// While rare, it's possible for code to fail repeatedly during the lifetime of the same page
|
|
19
19
|
// In those cases, we only want to log the failure first few times and not spam logs with redundant information.
|
|
20
|
-
if (!(e.message in history)) {
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
if (history[e.message]++ >= Setting.ScriptErrorLimit) {
|
|
24
|
-
return true;
|
|
25
|
-
}
|
|
20
|
+
if (!(e.message in history)) { history[e.message] = 0; }
|
|
21
|
+
if (history[e.message]++ >= Setting.ScriptErrorLimit) { return true; }
|
|
26
22
|
|
|
27
23
|
// Send back information only if the handled error has valid information
|
|
28
|
-
if (e
|
|
24
|
+
if (e && e.message) {
|
|
29
25
|
data = {
|
|
30
26
|
message: e.message,
|
|
31
|
-
line: error
|
|
32
|
-
column: error
|
|
27
|
+
line: error["lineno"],
|
|
28
|
+
column: error["colno"],
|
|
33
29
|
stack: e.stack,
|
|
34
|
-
source: error
|
|
30
|
+
source: error["filename"]
|
|
35
31
|
};
|
|
36
32
|
|
|
37
33
|
encode(Event.ScriptError);
|
package/src/global.ts
CHANGED
package/src/insight/blank.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export
|
|
1
|
+
export let state = [];
|
|
2
|
+
export let sheetAdoptionState = [];
|
|
3
|
+
export let sheetUpdateState = [];
|
|
4
|
+
export let data = null;
|
|
5
5
|
|
|
6
6
|
/* Intentionally blank module with empty code */
|
|
7
7
|
|
package/src/insight/encode.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Event,
|
|
3
|
-
import { Constant,
|
|
1
|
+
import { Privacy } from "@clarity-types/core";
|
|
2
|
+
import { Event, Token } from "@clarity-types/data";
|
|
3
|
+
import { Constant, NodeInfo } from "@clarity-types/layout";
|
|
4
4
|
import * as scrub from "@src/core/scrub";
|
|
5
5
|
import { time } from "@src/core/time";
|
|
6
6
|
import * as baseline from "@src/data/baseline";
|
|
@@ -10,39 +10,34 @@ import * as snapshot from "@src/insight/snapshot";
|
|
|
10
10
|
import * as doc from "@src/layout/document";
|
|
11
11
|
|
|
12
12
|
export default async function (type: Event): Promise<void> {
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
let eventTime = time()
|
|
14
|
+
let tokens: Token[] = [eventTime, type];
|
|
15
15
|
switch (type) {
|
|
16
|
-
case Event.Document:
|
|
17
|
-
|
|
16
|
+
case Event.Document:
|
|
17
|
+
let d = doc.data;
|
|
18
18
|
tokens.push(d.width);
|
|
19
19
|
tokens.push(d.height);
|
|
20
20
|
baseline.track(type, d.width, d.height);
|
|
21
21
|
queue(tokens);
|
|
22
22
|
break;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const values = snapshot.values;
|
|
23
|
+
case Event.Snapshot:
|
|
24
|
+
let values = snapshot.values;
|
|
26
25
|
// Only encode and queue DOM updates if we have valid updates to report back
|
|
27
26
|
if (values.length > 0) {
|
|
28
|
-
for (
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
for (
|
|
27
|
+
for (let value of values) {
|
|
28
|
+
let privacy = value.metadata.privacy;
|
|
29
|
+
let data: NodeInfo = value.data;
|
|
30
|
+
for (let key of ["tag", "attributes", "value"]) {
|
|
32
31
|
if (data[key]) {
|
|
33
32
|
switch (key) {
|
|
34
33
|
case "tag":
|
|
35
34
|
tokens.push(value.id);
|
|
36
|
-
if (value.parent) {
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
if (value.previous) {
|
|
40
|
-
tokens.push(value.previous);
|
|
41
|
-
}
|
|
35
|
+
if (value.parent) { tokens.push(value.parent); }
|
|
36
|
+
if (value.previous) { tokens.push(value.previous); }
|
|
42
37
|
tokens.push(data[key]);
|
|
43
38
|
break;
|
|
44
39
|
case "attributes":
|
|
45
|
-
for (
|
|
40
|
+
for (let attr in data[key]) {
|
|
46
41
|
if (data[key][attr] !== undefined) {
|
|
47
42
|
tokens.push(attribute(attr, data[key][attr], privacy));
|
|
48
43
|
}
|
|
@@ -58,10 +53,9 @@ export default async function (type: Event): Promise<void> {
|
|
|
58
53
|
queue(tokenize(tokens), true);
|
|
59
54
|
}
|
|
60
55
|
break;
|
|
61
|
-
}
|
|
62
56
|
}
|
|
63
57
|
}
|
|
64
58
|
|
|
65
59
|
function attribute(key: string, value: string, privacy: Privacy): string {
|
|
66
60
|
return `${key}=${scrub.text(value, key.indexOf(Constant.DataAttribute) === 0 ? Constant.DataAttribute : key, privacy)}`;
|
|
67
|
-
}
|
|
61
|
+
}
|
package/src/insight/snapshot.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { OffsetDistance, Privacy } from "@clarity-types/core";
|
|
2
2
|
import { Event } from "@clarity-types/data";
|
|
3
|
-
import { Constant,
|
|
4
|
-
import
|
|
3
|
+
import { Constant, NodeInfo, NodeValue, TargetMetadata } from "@clarity-types/layout";
|
|
4
|
+
import * as doc from "@src/layout/document";
|
|
5
5
|
import encode from "@src/insight/encode";
|
|
6
6
|
import * as interaction from "@src/interaction";
|
|
7
|
-
import
|
|
7
|
+
import config from "@src/core/config";
|
|
8
8
|
export let values: NodeValue[] = [];
|
|
9
|
-
let index = 1;
|
|
9
|
+
let index: number = 1;
|
|
10
10
|
let idMap: WeakMap<Node, number> = null; // Maps node => id.
|
|
11
11
|
|
|
12
12
|
export function start(): void {
|
|
@@ -21,34 +21,24 @@ export function stop(): void {
|
|
|
21
21
|
doc.stop();
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export function compute(): void {
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
export function
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
export function offset(): OffsetDistance {
|
|
31
|
-
return { x: 0, y: 0 };
|
|
32
|
-
}
|
|
33
|
-
export function hashText(): void {
|
|
34
|
-
/* Intentionally Blank */
|
|
35
|
-
}
|
|
24
|
+
export function compute(): void { /* Intentionally Blank */ }
|
|
25
|
+
export function iframe(): boolean { return false; }
|
|
26
|
+
export function offset(): OffsetDistance { return { x: 0, y: 0 }; }
|
|
27
|
+
export function hashText(): void { /* Intentionally Blank */ }
|
|
36
28
|
|
|
37
29
|
export function target(evt: UIEvent): Node {
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
let path = evt.composed && evt.composedPath ? evt.composedPath() : null;
|
|
31
|
+
let node = (path && path.length > 0 ? path[0] : evt.target) as Node;
|
|
40
32
|
return node.nodeType === Node.DOCUMENT_NODE ? (node as Document).documentElement : node;
|
|
41
33
|
}
|
|
42
34
|
|
|
43
35
|
export function metadata(node: Node): TargetMetadata {
|
|
44
|
-
|
|
45
|
-
if (node) {
|
|
46
|
-
output.id = idMap.has(node) ? idMap.get(node) : getId(node);
|
|
47
|
-
}
|
|
36
|
+
let output: TargetMetadata = { id: 0, hash: null, privacy: config.conversions ? Privacy.Sensitive : Privacy.Snapshot };
|
|
37
|
+
if (node) { output.id = idMap.has(node) ? idMap.get(node) : getId(node); }
|
|
48
38
|
return output;
|
|
49
39
|
}
|
|
50
40
|
|
|
51
|
-
export function snapshot(): void {
|
|
41
|
+
export function snapshot(): void {
|
|
52
42
|
values = [];
|
|
53
43
|
traverse(document);
|
|
54
44
|
encode(Event.Snapshot);
|
|
@@ -59,50 +49,46 @@ function reset(): void {
|
|
|
59
49
|
}
|
|
60
50
|
|
|
61
51
|
function traverse(root: Node): void {
|
|
62
|
-
|
|
52
|
+
let queue = [root];
|
|
63
53
|
while (queue.length > 0) {
|
|
64
|
-
let attributes = null;
|
|
65
|
-
let
|
|
66
|
-
let value = null;
|
|
67
|
-
const node = queue.shift();
|
|
54
|
+
let attributes = null, tag = null, value = null;
|
|
55
|
+
let node = queue.shift();
|
|
68
56
|
let next = node.firstChild;
|
|
69
|
-
|
|
57
|
+
let parent = node.parentElement ? node.parentElement : (node.parentNode ? node.parentNode : null);
|
|
70
58
|
|
|
71
59
|
while (next) {
|
|
72
60
|
queue.push(next);
|
|
73
61
|
next = next.nextSibling;
|
|
74
62
|
}
|
|
75
|
-
|
|
63
|
+
|
|
76
64
|
// Process the node
|
|
77
|
-
|
|
65
|
+
let type = node.nodeType;
|
|
78
66
|
switch (type) {
|
|
79
|
-
case Node.DOCUMENT_TYPE_NODE:
|
|
80
|
-
|
|
67
|
+
case Node.DOCUMENT_TYPE_NODE:
|
|
68
|
+
let doctype = node as DocumentType;
|
|
81
69
|
tag = Constant.DocumentTag;
|
|
82
|
-
attributes = { name: doctype.name, publicId: doctype.publicId, systemId: doctype.systemId }
|
|
70
|
+
attributes = { name: doctype.name, publicId: doctype.publicId, systemId: doctype.systemId }
|
|
83
71
|
break;
|
|
84
|
-
|
|
85
|
-
case Node.TEXT_NODE:
|
|
72
|
+
case Node.TEXT_NODE:
|
|
86
73
|
value = node.nodeValue;
|
|
87
74
|
tag = idMap.get(parent) ? Constant.TextTag : tag;
|
|
88
75
|
break;
|
|
89
|
-
case Node.ELEMENT_NODE:
|
|
90
|
-
|
|
76
|
+
case Node.ELEMENT_NODE:
|
|
77
|
+
let element = node as HTMLElement;
|
|
91
78
|
attributes = getAttributes(element);
|
|
92
79
|
tag = ["NOSCRIPT", "SCRIPT", "STYLE"].indexOf(element.tagName) < 0 ? element.tagName : tag;
|
|
93
80
|
break;
|
|
94
|
-
}
|
|
95
81
|
}
|
|
96
|
-
add(node, parent, { tag, attributes, value });
|
|
82
|
+
add(node, parent, { tag, attributes, value });
|
|
97
83
|
}
|
|
98
84
|
}
|
|
99
85
|
|
|
100
86
|
/* Helper Functions - Snapshot Traversal */
|
|
101
87
|
function getAttributes(element: HTMLElement): { [key: string]: string } {
|
|
102
|
-
|
|
103
|
-
|
|
88
|
+
let output = {};
|
|
89
|
+
let attributes = element.attributes;
|
|
104
90
|
if (attributes && attributes.length > 0) {
|
|
105
|
-
for (let i = 0; i < attributes.length; i++) {
|
|
91
|
+
for (let i = 0; i < attributes.length; i++) {
|
|
106
92
|
output[attributes[i].name] = attributes[i].value;
|
|
107
93
|
}
|
|
108
94
|
}
|
|
@@ -110,26 +96,20 @@ function getAttributes(element: HTMLElement): { [key: string]: string } {
|
|
|
110
96
|
}
|
|
111
97
|
|
|
112
98
|
function getId(node: Node): number {
|
|
113
|
-
if (node === null) {
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
if (idMap.has(node)) {
|
|
117
|
-
return idMap.get(node);
|
|
118
|
-
}
|
|
99
|
+
if (node === null) { return null; }
|
|
100
|
+
if (idMap.has(node)) { return idMap.get(node); }
|
|
119
101
|
idMap.set(node, index);
|
|
120
102
|
return index++;
|
|
121
103
|
}
|
|
122
104
|
|
|
123
105
|
function add(node: Node, parent: Node, data: NodeInfo): void {
|
|
124
106
|
if (node && data && data.tag) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
107
|
+
let id = getId(node);
|
|
108
|
+
let parentId = parent ? idMap.get(parent) : null;
|
|
109
|
+
let previous = node.previousSibling ? idMap.get(node.previousSibling) : null;
|
|
110
|
+
let metadata = { active: true, suspend: false, privacy: Privacy.Snapshot, position: null, fraud: null, size: null };
|
|
129
111
|
values.push({ id, parent: parentId, previous, children: [], data, selector: null, hash: null, region: null, metadata });
|
|
130
112
|
}
|
|
131
113
|
}
|
|
132
114
|
|
|
133
|
-
export function get(_node: Node): NodeValue {
|
|
134
|
-
return null;
|
|
135
|
-
}
|
|
115
|
+
export function get(_node: Node): NodeValue {return null;}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Constant, Event, Setting } from "@clarity-types/data";
|
|
2
|
-
import
|
|
3
|
-
import { Mask } from "@clarity-types/layout";
|
|
2
|
+
import { ChangeState } from "@clarity-types/interaction";
|
|
4
3
|
import { FunctionNames } from "@clarity-types/performance";
|
|
5
4
|
import config from "@src/core/config";
|
|
6
5
|
import { bind } from "@src/core/event";
|
|
@@ -9,6 +8,7 @@ import { schedule } from "@src/core/task";
|
|
|
9
8
|
import { time } from "@src/core/time";
|
|
10
9
|
import { target } from "@src/layout/target";
|
|
11
10
|
import encode from "./encode";
|
|
11
|
+
import { Mask } from "@clarity-types/layout";
|
|
12
12
|
|
|
13
13
|
export let state: ChangeState[] = [];
|
|
14
14
|
|
|
@@ -22,16 +22,13 @@ export function observe(root: Node): void {
|
|
|
22
22
|
|
|
23
23
|
function recompute(evt: UIEvent): void {
|
|
24
24
|
recompute.dn = FunctionNames.ChangeRecompute;
|
|
25
|
-
|
|
25
|
+
let element = target(evt) as HTMLInputElement;
|
|
26
26
|
if (element) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
value && value.length >= Setting.WordLength && config.fraud && Mask.Exclude.indexOf(element.type) === -1
|
|
30
|
-
? hash(value, Setting.ChecksumPrecision)
|
|
31
|
-
: Constant.Empty;
|
|
27
|
+
let value = element.value;
|
|
28
|
+
let checksum = value && value.length >= Setting.WordLength && config.fraud && Mask.Exclude.indexOf(element.type) === -1 ? hash(value, Setting.ChecksumPrecision) : Constant.Empty;
|
|
32
29
|
state.push({ time: time(evt), event: Event.Change, data: { target: target(evt), type: element.type, value, checksum } });
|
|
33
30
|
schedule(encode.bind(this, Event.Change));
|
|
34
|
-
}
|
|
31
|
+
}
|
|
35
32
|
}
|
|
36
33
|
|
|
37
34
|
export function reset(): void {
|
package/src/interaction/click.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BooleanFlag, Constant, Event, Setting } from "@clarity-types/data";
|
|
2
|
-
import { BrowsingContext,
|
|
3
|
-
import
|
|
2
|
+
import { BrowsingContext, ClickState, TextInfo } from "@clarity-types/interaction";
|
|
3
|
+
import { Box } from "@clarity-types/layout";
|
|
4
4
|
import { FunctionNames } from "@clarity-types/performance";
|
|
5
5
|
import { bind } from "@src/core/event";
|
|
6
6
|
import { schedule } from "@src/core/task";
|
|
@@ -23,43 +23,41 @@ export function observe(root: Node): void {
|
|
|
23
23
|
|
|
24
24
|
function handler(event: Event, root: Node, evt: MouseEvent): void {
|
|
25
25
|
handler.dn = FunctionNames.ClickHandler;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
let x = "pageX" in evt ? Math.round(evt.pageX) : "clientX" in evt ? Math.round(
|
|
29
|
-
let y = "pageY" in evt ? Math.round(evt.pageY) : "clientY" in evt ? Math.round(
|
|
26
|
+
let frame = iframe(root);
|
|
27
|
+
let d = frame ? frame.contentDocument.documentElement : document.documentElement;
|
|
28
|
+
let x = "pageX" in evt ? Math.round(evt.pageX) : ("clientX" in evt ? Math.round(evt["clientX"] + d.scrollLeft) : null);
|
|
29
|
+
let y = "pageY" in evt ? Math.round(evt.pageY) : ("clientY" in evt ? Math.round(evt["clientY"] + d.scrollTop) : null);
|
|
30
30
|
// In case of iframe, we adjust (x,y) to be relative to top parent's origin
|
|
31
31
|
if (frame) {
|
|
32
|
-
|
|
32
|
+
let distance = offset(frame);
|
|
33
33
|
x = x ? x + Math.round(distance.x) : x;
|
|
34
34
|
y = y ? y + Math.round(distance.y) : y;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
let t = target(evt);
|
|
38
38
|
// Find nearest anchor tag (<a/>) parent if current target node is part of one
|
|
39
39
|
// If present, we use the returned link element to populate text and link properties below
|
|
40
|
-
|
|
40
|
+
let a = link(t);
|
|
41
41
|
|
|
42
42
|
// Get layout rectangle for the target element
|
|
43
|
-
|
|
43
|
+
let l = layout(t as Element);
|
|
44
44
|
|
|
45
45
|
// Reference: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail
|
|
46
46
|
// This property helps differentiate between a keyboard navigation vs. pointer click
|
|
47
47
|
// In case of a keyboard navigation, we use center of target element as (x,y)
|
|
48
48
|
if (evt.detail === 0 && l) {
|
|
49
|
-
x = Math.round(l.x + l.w / 2);
|
|
50
|
-
y = Math.round(l.y + l.h / 2);
|
|
49
|
+
x = Math.round(l.x + (l.w / 2));
|
|
50
|
+
y = Math.round(l.y + (l.h / 2));
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
let eX = l ? Math.max(Math.floor(((x - l.x) / l.w) * Setting.ClickPrecision), 0) : 0;
|
|
54
|
+
let eY = l ? Math.max(Math.floor(((y - l.y) / l.h) * Setting.ClickPrecision), 0) : 0;
|
|
55
55
|
|
|
56
56
|
// Check for null values before processing this event
|
|
57
57
|
if (x !== null && y !== null) {
|
|
58
58
|
const textInfo = text(t);
|
|
59
59
|
state.push({
|
|
60
|
-
time: time(evt),
|
|
61
|
-
event,
|
|
62
|
-
data: {
|
|
60
|
+
time: time(evt), event, data: {
|
|
63
61
|
target: t,
|
|
64
62
|
x,
|
|
65
63
|
y,
|
|
@@ -73,17 +71,16 @@ function handler(event: Event, root: Node, evt: MouseEvent): void {
|
|
|
73
71
|
hash: null,
|
|
74
72
|
trust: evt.isTrusted ? BooleanFlag.True : BooleanFlag.False,
|
|
75
73
|
isFullText: textInfo.isFullText,
|
|
76
|
-
}
|
|
74
|
+
}
|
|
77
75
|
});
|
|
78
76
|
schedule(encode.bind(this, event));
|
|
79
77
|
}
|
|
80
78
|
}
|
|
81
79
|
|
|
82
|
-
function link(
|
|
83
|
-
let node = inputNode;
|
|
80
|
+
function link(node: Node): HTMLAnchorElement {
|
|
84
81
|
while (node && node !== document) {
|
|
85
82
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
86
|
-
|
|
83
|
+
let element = node as HTMLElement;
|
|
87
84
|
if (element.tagName === "A") {
|
|
88
85
|
return element as HTMLAnchorElement;
|
|
89
86
|
}
|
|
@@ -98,11 +95,11 @@ function text(element: Node): TextInfo {
|
|
|
98
95
|
let isFullText = false;
|
|
99
96
|
if (element) {
|
|
100
97
|
// Grab text using "textContent" for most HTMLElements, however, use "value" for HTMLInputElements and "alt" for HTMLImageElement.
|
|
101
|
-
|
|
98
|
+
let t = element.textContent || String((element as HTMLInputElement).value || '') || (element as HTMLImageElement).alt;
|
|
102
99
|
if (t) {
|
|
103
100
|
// Replace multiple occurrence of space characters with a single white space
|
|
104
101
|
// Also, trim any spaces at the beginning or at the end of string
|
|
105
|
-
const trimmedText =
|
|
102
|
+
const trimmedText = t.replace(/\s+/g, Constant.Space).trim();
|
|
106
103
|
// Finally, send only first few characters as specified by the Setting
|
|
107
104
|
output = trimmedText.substring(0, Setting.ClickText);
|
|
108
105
|
isFullText = output.length === trimmedText.length;
|
|
@@ -114,7 +111,7 @@ function text(element: Node): TextInfo {
|
|
|
114
111
|
|
|
115
112
|
function reaction(element: Node): BooleanFlag {
|
|
116
113
|
if (element.nodeType === Node.ELEMENT_NODE) {
|
|
117
|
-
|
|
114
|
+
let tag = (element as HTMLElement).tagName.toLowerCase();
|
|
118
115
|
if (UserInputTags.indexOf(tag) >= 0) {
|
|
119
116
|
return BooleanFlag.False;
|
|
120
117
|
}
|
|
@@ -124,10 +121,10 @@ function reaction(element: Node): BooleanFlag {
|
|
|
124
121
|
|
|
125
122
|
function layout(element: Element): Box {
|
|
126
123
|
let box: Box = null;
|
|
127
|
-
|
|
124
|
+
let de = document.documentElement;
|
|
128
125
|
if (typeof element.getBoundingClientRect === "function") {
|
|
129
126
|
// getBoundingClientRect returns rectangle relative positioning to viewport
|
|
130
|
-
|
|
127
|
+
let rect = element.getBoundingClientRect();
|
|
131
128
|
|
|
132
129
|
if (rect && rect.width > 0 && rect.height > 0) {
|
|
133
130
|
// Add viewport's scroll position to rectangle to get position relative to document origin
|
|
@@ -138,7 +135,7 @@ function layout(element: Element): Box {
|
|
|
138
135
|
x: Math.floor(rect.left + ("pageXOffset" in window ? window.pageXOffset : de.scrollLeft)),
|
|
139
136
|
y: Math.floor(rect.top + ("pageYOffset" in window ? window.pageYOffset : de.scrollTop)),
|
|
140
137
|
w: Math.floor(rect.width),
|
|
141
|
-
h: Math.floor(rect.height)
|
|
138
|
+
h: Math.floor(rect.height)
|
|
142
139
|
};
|
|
143
140
|
}
|
|
144
141
|
}
|
|
@@ -146,14 +143,11 @@ function layout(element: Element): Box {
|
|
|
146
143
|
}
|
|
147
144
|
|
|
148
145
|
function context(a: HTMLAnchorElement): BrowsingContext {
|
|
149
|
-
if (a
|
|
146
|
+
if (a && a.hasAttribute(Constant.Target)) {
|
|
150
147
|
switch (a.getAttribute(Constant.Target)) {
|
|
151
|
-
case Constant.Blank:
|
|
152
|
-
|
|
153
|
-
case Constant.
|
|
154
|
-
return BrowsingContext.Parent;
|
|
155
|
-
case Constant.Top:
|
|
156
|
-
return BrowsingContext.Top;
|
|
148
|
+
case Constant.Blank: return BrowsingContext.Blank;
|
|
149
|
+
case Constant.Parent: return BrowsingContext.Parent;
|
|
150
|
+
case Constant.Top: return BrowsingContext.Top;
|
|
157
151
|
}
|
|
158
152
|
}
|
|
159
153
|
return BrowsingContext.Self;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Event } from "@clarity-types/data";
|
|
2
|
-
import { Clipboard,
|
|
2
|
+
import { Clipboard, ClipboardState } 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 { target } from "@src/layout/target";
|
|
8
7
|
import encode from "./encode";
|
|
8
|
+
import { target } from "@src/layout/target";
|
|
9
9
|
|
|
10
10
|
export let state: ClipboardState[] = [];
|
|
11
11
|
|