clarity-js 0.6.33 → 0.6.36
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.js +288 -373
- package/build/clarity.min.js +1 -1
- package/build/clarity.module.js +288 -373
- package/package.json +1 -1
- package/src/core/config.ts +1 -0
- package/src/core/history.ts +27 -27
- package/src/core/timeout.ts +1 -1
- package/src/core/version.ts +1 -1
- package/src/data/metadata.ts +15 -11
- package/src/data/metric.ts +3 -3
- package/src/diagnostic/encode.ts +9 -0
- package/src/diagnostic/fraud.ts +29 -0
- package/src/diagnostic/index.ts +2 -0
- package/src/diagnostic/script.ts +1 -11
- package/src/interaction/click.ts +2 -1
- package/src/interaction/encode.ts +4 -3
- package/src/interaction/input.ts +1 -8
- package/src/layout/dom.ts +70 -82
- package/src/layout/encode.ts +15 -15
- package/src/layout/index.ts +0 -3
- package/src/layout/mutation.ts +29 -41
- package/src/layout/node.ts +1 -1
- package/src/layout/target.ts +7 -4
- package/src/performance/observer.ts +10 -2
- package/types/core.d.ts +2 -0
- package/types/data.d.ts +15 -5
- package/types/diagnostic.d.ts +6 -0
- package/types/interaction.d.ts +1 -0
- package/types/layout.d.ts +7 -6
- package/types/performance.d.ts +0 -7
- package/src/layout/box.ts +0 -83
package/src/layout/encode.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Privacy, Task, Timer } from "@clarity-types/core";
|
|
2
|
-
import { Event, Token } from "@clarity-types/data";
|
|
2
|
+
import { Event, Setting, Token } from "@clarity-types/data";
|
|
3
3
|
import { Constant, NodeInfo, NodeValue } from "@clarity-types/layout";
|
|
4
4
|
import config from "@src/core/config";
|
|
5
5
|
import scrub from "@src/core/scrub";
|
|
@@ -8,7 +8,7 @@ import { time } from "@src/core/time";
|
|
|
8
8
|
import tokenize from "@src/data/token";
|
|
9
9
|
import * as baseline from "@src/data/baseline";
|
|
10
10
|
import { queue } from "@src/data/upload";
|
|
11
|
-
import * as
|
|
11
|
+
import * as fraud from "@src/diagnostic/fraud";
|
|
12
12
|
import * as doc from "./document";
|
|
13
13
|
import * as dom from "./dom";
|
|
14
14
|
import * as region from "./region";
|
|
@@ -35,16 +35,6 @@ export default async function (type: Event, timer: Timer = null, ts: number = nu
|
|
|
35
35
|
}
|
|
36
36
|
region.reset();
|
|
37
37
|
break;
|
|
38
|
-
case Event.Box:
|
|
39
|
-
let b = box.data;
|
|
40
|
-
for (let entry of b) {
|
|
41
|
-
tokens.push(entry.id);
|
|
42
|
-
tokens.push(entry.width);
|
|
43
|
-
tokens.push(entry.height);
|
|
44
|
-
}
|
|
45
|
-
box.reset();
|
|
46
|
-
queue(tokens);
|
|
47
|
-
break;
|
|
48
38
|
case Event.Discover:
|
|
49
39
|
case Event.Mutation:
|
|
50
40
|
// Check if we are operating within the context of the current page
|
|
@@ -62,18 +52,17 @@ export default async function (type: Event, timer: Timer = null, ts: number = nu
|
|
|
62
52
|
let privacy = value.metadata.privacy;
|
|
63
53
|
let mangle = shouldMangle(value);
|
|
64
54
|
let keys = active ? ["tag", "attributes", "value"] : ["tag"];
|
|
65
|
-
box.compute(value.id);
|
|
66
55
|
for (let key of keys) {
|
|
67
56
|
if (data[key]) {
|
|
68
57
|
switch (key) {
|
|
69
58
|
case "tag":
|
|
70
|
-
let
|
|
59
|
+
let box = size(value);
|
|
71
60
|
let factor = mangle ? -1 : 1;
|
|
72
61
|
tokens.push(value.id * factor);
|
|
73
62
|
if (value.parent && active) { tokens.push(value.parent); }
|
|
74
63
|
if (value.previous && active) { tokens.push(value.previous); }
|
|
75
64
|
tokens.push(suspend ? Constant.SuspendMutationTag : data[key]);
|
|
76
|
-
if (
|
|
65
|
+
if (box && box.length === 2) { tokens.push(`${Constant.Box}${str(box[0])}.${str(box[1])}`); }
|
|
77
66
|
break;
|
|
78
67
|
case "attributes":
|
|
79
68
|
for (let attr in data[key]) {
|
|
@@ -83,6 +72,7 @@ export default async function (type: Event, timer: Timer = null, ts: number = nu
|
|
|
83
72
|
}
|
|
84
73
|
break;
|
|
85
74
|
case "value":
|
|
75
|
+
fraud.check(value.metadata.fraud, value.id, data[key]);
|
|
86
76
|
tokens.push(scrub(data[key], data.tag, privacy, mangle));
|
|
87
77
|
break;
|
|
88
78
|
}
|
|
@@ -101,6 +91,16 @@ function shouldMangle(value: NodeValue): boolean {
|
|
|
101
91
|
return value.data.tag === Constant.TextTag && !(privacy === Privacy.None || privacy === Privacy.Sensitive);
|
|
102
92
|
}
|
|
103
93
|
|
|
94
|
+
function size(value: NodeValue): number[] {
|
|
95
|
+
if (value.metadata.size !== null && value.metadata.size.length === 0) {
|
|
96
|
+
let img = dom.getNode(value.id) as HTMLImageElement;
|
|
97
|
+
if (img) {
|
|
98
|
+
return [Math.floor(img.offsetWidth * Setting.BoxPrecision), Math.floor(img.offsetHeight * Setting.BoxPrecision)];
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return value.metadata.size;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
104
|
function str(input: number): string {
|
|
105
105
|
return input.toString(36);
|
|
106
106
|
}
|
package/src/layout/index.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as box from "@src/layout/box";
|
|
2
1
|
import * as discover from "@src/layout/discover";
|
|
3
2
|
import * as doc from "@src/layout/document";
|
|
4
3
|
import * as dom from "@src/layout/dom";
|
|
@@ -13,13 +12,11 @@ export function start(): void {
|
|
|
13
12
|
dom.start();
|
|
14
13
|
mutation.start();
|
|
15
14
|
discover.start();
|
|
16
|
-
box.start();
|
|
17
15
|
}
|
|
18
16
|
|
|
19
17
|
export function stop(): void {
|
|
20
18
|
region.stop();
|
|
21
19
|
dom.stop();
|
|
22
20
|
mutation.stop();
|
|
23
|
-
box.stop();
|
|
24
21
|
doc.end();
|
|
25
22
|
}
|
package/src/layout/mutation.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Priority, Task, Timer } from "@clarity-types/core";
|
|
|
2
2
|
import { Code, Event, Metric, Severity } from "@clarity-types/data";
|
|
3
3
|
import { Constant, MutationHistory, MutationQueue, Setting, Source } from "@clarity-types/layout";
|
|
4
4
|
import api from "@src/core/api";
|
|
5
|
+
import * as core from "@src/core";
|
|
5
6
|
import { bind } from "@src/core/event";
|
|
6
7
|
import measure from "@src/core/measure";
|
|
7
8
|
import * as task from "@src/core/task";
|
|
@@ -35,32 +36,38 @@ export function start(): void {
|
|
|
35
36
|
activePeriod = 0;
|
|
36
37
|
history = {};
|
|
37
38
|
|
|
38
|
-
if (insertRule === null) { insertRule = CSSStyleSheet.prototype.insertRule; }
|
|
39
|
-
if (deleteRule === null) { deleteRule = CSSStyleSheet.prototype.deleteRule; }
|
|
40
|
-
if (attachShadow === null) { attachShadow = Element.prototype.attachShadow; }
|
|
41
|
-
|
|
42
39
|
// Some popular open source libraries, like styled-components, optimize performance
|
|
43
40
|
// by injecting CSS using insertRule API vs. appending text node. A side effect of
|
|
44
41
|
// using javascript API is that it doesn't trigger DOM mutation and therefore we
|
|
45
42
|
// need to override the insertRule API and listen for changes manually.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return deleteRule.apply(this, arguments);
|
|
54
|
-
};
|
|
43
|
+
if (insertRule === null) {
|
|
44
|
+
insertRule = CSSStyleSheet.prototype.insertRule;
|
|
45
|
+
CSSStyleSheet.prototype.insertRule = function(): number {
|
|
46
|
+
if (core.active()) { schedule(this.ownerNode); }
|
|
47
|
+
return insertRule.apply(this, arguments);
|
|
48
|
+
};
|
|
49
|
+
}
|
|
55
50
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
51
|
+
if (deleteRule === null) {
|
|
52
|
+
deleteRule = CSSStyleSheet.prototype.deleteRule;
|
|
53
|
+
CSSStyleSheet.prototype.deleteRule = function(): void {
|
|
54
|
+
if (core.active()) { schedule(this.ownerNode); }
|
|
55
|
+
return deleteRule.apply(this, arguments);
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Add a hook to attachShadow API calls
|
|
60
|
+
// In case we are unable to add a hook and browser throws an exception,
|
|
61
|
+
// reset attachShadow variable and resume processing like before
|
|
62
|
+
if (attachShadow === null) {
|
|
63
|
+
attachShadow = Element.prototype.attachShadow;
|
|
64
|
+
try {
|
|
65
|
+
Element.prototype.attachShadow = function (): ShadowRoot {
|
|
66
|
+
if (core.active()) { return schedule(attachShadow.apply(this, arguments)) as ShadowRoot; }
|
|
67
|
+
else { return attachShadow.apply(this, arguments)}
|
|
68
|
+
}
|
|
69
|
+
} catch { attachShadow = null; }
|
|
70
|
+
}
|
|
64
71
|
}
|
|
65
72
|
|
|
66
73
|
export function observe(node: Node): void {
|
|
@@ -90,25 +97,6 @@ export function monitor(frame: HTMLIFrameElement): void {
|
|
|
90
97
|
export function stop(): void {
|
|
91
98
|
for (let observer of observers) { if (observer) { observer.disconnect(); } }
|
|
92
99
|
observers = [];
|
|
93
|
-
|
|
94
|
-
// Restoring original insertRule
|
|
95
|
-
if (insertRule !== null) {
|
|
96
|
-
CSSStyleSheet.prototype.insertRule = insertRule;
|
|
97
|
-
insertRule = null;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Restoring original deleteRule
|
|
101
|
-
if (deleteRule !== null) {
|
|
102
|
-
CSSStyleSheet.prototype.deleteRule = deleteRule;
|
|
103
|
-
deleteRule = null;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Restoring original attachShadow
|
|
107
|
-
if (attachShadow != null) {
|
|
108
|
-
Element.prototype.attachShadow = attachShadow;
|
|
109
|
-
attachShadow = null;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
100
|
history = {};
|
|
113
101
|
mutations = [];
|
|
114
102
|
queue = [];
|
|
@@ -126,7 +114,7 @@ function handle(m: MutationRecord[]): void {
|
|
|
126
114
|
summary.track(Event.Mutation, now);
|
|
127
115
|
mutations.push({ time: now, mutations: m});
|
|
128
116
|
task.schedule(process, Priority.High).then((): void => {
|
|
129
|
-
|
|
117
|
+
setTimeout(doc.compute)
|
|
130
118
|
measure(region.compute)();
|
|
131
119
|
});
|
|
132
120
|
}
|
package/src/layout/node.ts
CHANGED
|
@@ -7,7 +7,7 @@ import * as interaction from "@src/interaction";
|
|
|
7
7
|
import * as mutation from "@src/layout/mutation";
|
|
8
8
|
import * as schema from "@src/layout/schema";
|
|
9
9
|
|
|
10
|
-
const IGNORE_ATTRIBUTES = ["title", "alt", "onload", "onfocus", "onerror"];
|
|
10
|
+
const IGNORE_ATTRIBUTES = ["title", "alt", "onload", "onfocus", "onerror", "data-drupal-form-submit-last"];
|
|
11
11
|
const newlineRegex = /[\r\n]+/g;
|
|
12
12
|
|
|
13
13
|
export default function (node: Node, source: Source): Node {
|
package/src/layout/target.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Privacy } from "@clarity-types/core";
|
|
2
2
|
import { Event } from "@clarity-types/data";
|
|
3
3
|
import { TargetMetadata } from "@clarity-types/layout";
|
|
4
|
-
import
|
|
4
|
+
import * as fraud from "@src/diagnostic/fraud";
|
|
5
|
+
import * as region from "@src/layout/region";
|
|
5
6
|
import * as dom from "@src/layout/dom";
|
|
6
7
|
import * as mutation from "@src/layout/mutation";
|
|
7
8
|
|
|
@@ -25,16 +26,18 @@ export function link(node: Node): HTMLAnchorElement {
|
|
|
25
26
|
return null;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
export function metadata(node: Node, event: Event): TargetMetadata {
|
|
29
|
+
export function metadata(node: Node, event: Event, text: string = null): TargetMetadata {
|
|
29
30
|
// If the node is null, we return a reserved value for id: 0. Valid assignment of id begins from 1+.
|
|
30
31
|
let output: TargetMetadata = { id: 0, hash: null, privacy: Privacy.Text, node };
|
|
31
32
|
if (node) {
|
|
32
33
|
let value = dom.get(node);
|
|
33
34
|
if (value !== null) {
|
|
35
|
+
let metadata = value.metadata;
|
|
34
36
|
output.id = value.id;
|
|
35
37
|
output.hash = value.hash;
|
|
36
|
-
output.privacy =
|
|
37
|
-
if (value.region) { track(value.region, event); }
|
|
38
|
+
output.privacy = metadata.privacy;
|
|
39
|
+
if (value.region) { region.track(value.region, event); }
|
|
40
|
+
if (metadata.fraud) { fraud.check(metadata.fraud, value.id, text || value.data.value); }
|
|
38
41
|
}
|
|
39
42
|
}
|
|
40
43
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Code, Constant, Dimension, Metric, Severity } from "@clarity-types/data";
|
|
1
|
+
import { Code, Constant, Dimension, Metric, Setting, Severity } from "@clarity-types/data";
|
|
2
|
+
import config from "@src/core/config";
|
|
2
3
|
import { bind } from "@src/core/event";
|
|
3
4
|
import measure from "@src/core/measure";
|
|
4
5
|
import { setTimeout } from "@src/core/timeout";
|
|
@@ -56,7 +57,9 @@ function process(entries: PerformanceEntryList): void {
|
|
|
56
57
|
navigation.compute(entry as PerformanceNavigationTiming);
|
|
57
58
|
break;
|
|
58
59
|
case Constant.Resource:
|
|
59
|
-
|
|
60
|
+
let name = entry.name;
|
|
61
|
+
dimension.log(Dimension.NetworkHosts, host(name));
|
|
62
|
+
if (name === config.upload || name === config.fallback) { metric.max(Metric.UploadTime, entry.duration); }
|
|
60
63
|
break;
|
|
61
64
|
case Constant.LongTask:
|
|
62
65
|
metric.count(Metric.LongTaskCount);
|
|
@@ -73,6 +76,11 @@ function process(entries: PerformanceEntryList): void {
|
|
|
73
76
|
break;
|
|
74
77
|
}
|
|
75
78
|
}
|
|
79
|
+
if (performance && Constant.Memory in performance && performance[Constant.Memory].usedJSHeapSize) {
|
|
80
|
+
// Track consumed memory (MBs) where "memory" API is available
|
|
81
|
+
// Reference: https://developer.mozilla.org/en-US/docs/Web/API/Performance/memory
|
|
82
|
+
metric.max(Metric.UsedMemory, Math.abs(performance[Constant.Memory].usedJSHeapSize / Setting.MegaByte));
|
|
83
|
+
}
|
|
76
84
|
}
|
|
77
85
|
|
|
78
86
|
export function stop(): void {
|
package/types/core.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ type TaskFunction = () => Promise<void>;
|
|
|
4
4
|
type TaskResolve = () => void;
|
|
5
5
|
type UploadCallback = (data: string) => void;
|
|
6
6
|
type Region = [number /* RegionId */, string /* Query Selector */];
|
|
7
|
+
type Fraud = [number /* FraudId */, string /* Query Selector */];
|
|
7
8
|
export type Extract = ExtractSource /* Extraction Source */ | number /* Extract Id */ | string | string[] /* Hash or Query Selector or String Token */;
|
|
8
9
|
|
|
9
10
|
/* Enum */
|
|
@@ -127,6 +128,7 @@ export interface Config {
|
|
|
127
128
|
regions?: Region[];
|
|
128
129
|
extract?: Extract[];
|
|
129
130
|
cookies?: string[];
|
|
131
|
+
fraud?: Fraud[];
|
|
130
132
|
report?: string;
|
|
131
133
|
upload?: string | UploadCallback;
|
|
132
134
|
fallback?: string;
|
package/types/data.d.ts
CHANGED
|
@@ -4,9 +4,12 @@ export type Token = (string | number | number[] | string[]);
|
|
|
4
4
|
export type DecodedToken = (any | any[]);
|
|
5
5
|
|
|
6
6
|
export type MetadataCallback = (data: Metadata, playback: boolean) => void;
|
|
7
|
+
export interface MetadataCallbackOptions {
|
|
8
|
+
callback: MetadataCallback,
|
|
9
|
+
wait: boolean
|
|
10
|
+
}
|
|
7
11
|
|
|
8
12
|
/* Enum */
|
|
9
|
-
|
|
10
13
|
export const enum Event {
|
|
11
14
|
/* Data */
|
|
12
15
|
Metric = 0,
|
|
@@ -52,10 +55,14 @@ export const enum Event {
|
|
|
52
55
|
Variable = 34,
|
|
53
56
|
Limit = 35,
|
|
54
57
|
Summary = 36,
|
|
58
|
+
/**
|
|
59
|
+
* @deprecated No longer support Box event
|
|
60
|
+
*/
|
|
55
61
|
Box = 37,
|
|
56
62
|
Clipboard = 38,
|
|
57
63
|
Submit = 39,
|
|
58
|
-
Extract = 40
|
|
64
|
+
Extract = 40,
|
|
65
|
+
Fraud = 41
|
|
59
66
|
}
|
|
60
67
|
|
|
61
68
|
export const enum Metric {
|
|
@@ -86,7 +93,10 @@ export const enum Metric {
|
|
|
86
93
|
CartTotal = 24,
|
|
87
94
|
EventCount = 25,
|
|
88
95
|
Automation = 26,
|
|
89
|
-
Mobile = 27
|
|
96
|
+
Mobile = 27,
|
|
97
|
+
UploadTime = 28,
|
|
98
|
+
SinglePage = 29,
|
|
99
|
+
UsedMemory = 30
|
|
90
100
|
}
|
|
91
101
|
|
|
92
102
|
export const enum Dimension {
|
|
@@ -177,7 +187,6 @@ export const enum Setting {
|
|
|
177
187
|
CollectionLimit = 128, // Number of unique entries for dimensions
|
|
178
188
|
ClickPrecision = 32767, // 2^15 - 1
|
|
179
189
|
BoxPrecision = 100, // Up to 2 decimal points (e.g. 34.56)
|
|
180
|
-
ResizeObserverThreshold = 15, // At least 15 characters before we attach a resize observer for the node
|
|
181
190
|
ScriptErrorLimit = 5, // Do not send the same script error more than 5 times per page
|
|
182
191
|
DimensionLimit = 256, // Do not extract dimensions which are over 256 characters
|
|
183
192
|
WordLength = 5, // Estimated average size of a word,
|
|
@@ -187,6 +196,7 @@ export const enum Setting {
|
|
|
187
196
|
ViewportIntersectionRatio = 0.05, // Ratio of intersection area in comparison to viewport area before it's marked visible
|
|
188
197
|
IntersectionRatio = 0.8, // Ratio of intersection area in comparison to element's area before it's marked visible
|
|
189
198
|
MaxFirstPayloadBytes = 1 * 1024 * 1024, // 1MB: Cap the very first payload to a maximum of 1MB
|
|
199
|
+
MegaByte = 1024 * 1024, // 1MB
|
|
190
200
|
UploadFactor = 3, // Slow down sequence by specified factor
|
|
191
201
|
MinUploadDelay = 100, // Minimum time before we are ready to flush events to the server
|
|
192
202
|
MaxUploadDelay = 30 * Time.Second, // Do flush out payload once every 30s,
|
|
@@ -212,6 +222,7 @@ export const enum Constant {
|
|
|
212
222
|
Pause = "pause",
|
|
213
223
|
Resume = "resume",
|
|
214
224
|
Report = "report",
|
|
225
|
+
Memory = "memory",
|
|
215
226
|
Empty = "",
|
|
216
227
|
Space = " ",
|
|
217
228
|
Expires = "expires=",
|
|
@@ -236,7 +247,6 @@ export const enum Constant {
|
|
|
236
247
|
UserId = "userId",
|
|
237
248
|
SessionId = "sessionId",
|
|
238
249
|
PageId = "pageId",
|
|
239
|
-
ResizeObserver = "ResizeObserver",
|
|
240
250
|
Mask = "•",
|
|
241
251
|
SessionStorage = "sessionStorage",
|
|
242
252
|
Cookie = "cookie",
|
package/types/diagnostic.d.ts
CHANGED
package/types/interaction.d.ts
CHANGED
package/types/layout.d.ts
CHANGED
|
@@ -28,6 +28,11 @@ export const enum RegionVisibility {
|
|
|
28
28
|
ScrolledToEnd = 13
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
export const enum Mask {
|
|
32
|
+
Text = "password,secret,pass,social,ssn,name,code,dob,cell,mob,contact,hidden,account,cvv,ccv,email,tel,phone,address,addr,card,zip",
|
|
33
|
+
Disable = "radio,checkbox,range,button,reset,submit"
|
|
34
|
+
}
|
|
35
|
+
|
|
31
36
|
export const enum Constant {
|
|
32
37
|
Empty = "",
|
|
33
38
|
SvgPrefix = "svg:",
|
|
@@ -42,6 +47,7 @@ export const enum Constant {
|
|
|
42
47
|
Box = "#",
|
|
43
48
|
Bang = "!",
|
|
44
49
|
Period = ".",
|
|
50
|
+
Comma = ",",
|
|
45
51
|
MaskData = "data-clarity-mask",
|
|
46
52
|
UnmaskData = "data-clarity-unmask",
|
|
47
53
|
RegionData = "data-clarity-region",
|
|
@@ -162,6 +168,7 @@ export interface NodeMeta {
|
|
|
162
168
|
suspend: boolean;
|
|
163
169
|
privacy: Privacy;
|
|
164
170
|
position: number;
|
|
171
|
+
fraud: number;
|
|
165
172
|
size: number[];
|
|
166
173
|
}
|
|
167
174
|
|
|
@@ -198,12 +205,6 @@ export interface RegionData {
|
|
|
198
205
|
name: string;
|
|
199
206
|
}
|
|
200
207
|
|
|
201
|
-
export interface BoxData {
|
|
202
|
-
id: number;
|
|
203
|
-
width: number;
|
|
204
|
-
height: number;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
208
|
export interface TargetMetadata {
|
|
208
209
|
id: number;
|
|
209
210
|
hash: [string, string];
|
package/types/performance.d.ts
CHANGED
package/src/layout/box.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { Event, Setting } from "@clarity-types/data";
|
|
2
|
-
import { BoxData } from "@clarity-types/layout";
|
|
3
|
-
import * as dom from "@src/layout/dom";
|
|
4
|
-
import encode from "@src/layout/encode";
|
|
5
|
-
|
|
6
|
-
export let data: BoxData[] = [];
|
|
7
|
-
let enabled = false;
|
|
8
|
-
let observer: ResizeObserver = null;
|
|
9
|
-
|
|
10
|
-
export function start(): void {
|
|
11
|
-
reset();
|
|
12
|
-
observer = null;
|
|
13
|
-
enabled = window["ResizeObserver"] ? true : false;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function compute(id: number): void {
|
|
17
|
-
if (enabled === false) { return; }
|
|
18
|
-
observer = observer === null ? new ResizeObserver(handler) : observer;
|
|
19
|
-
if (observer) {
|
|
20
|
-
let value = dom.getValue(id);
|
|
21
|
-
// If this is the first time computing size for this node, go ahead and wire up ResizeObserver
|
|
22
|
-
// In all other cases, value.metadata.size will be null or an array with two elements [width, height]
|
|
23
|
-
// And, in those cases, we will skip through the following section and not attach the observer
|
|
24
|
-
if (value && value.metadata.size !== null && value.metadata.size.length === 0) {
|
|
25
|
-
let node = dom.getNode(id);
|
|
26
|
-
if (node && node.nodeType === Node.ELEMENT_NODE) {
|
|
27
|
-
let e = node as HTMLElement;
|
|
28
|
-
let r = e.getBoundingClientRect();
|
|
29
|
-
value.metadata.size = [Math.floor(r.width * Setting.BoxPrecision), Math.floor(r.height * Setting.BoxPrecision)];
|
|
30
|
-
observer.observe(e);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function handler(entries: ResizeObserverEntry[]): void {
|
|
37
|
-
window.requestAnimationFrame(() => {
|
|
38
|
-
for (let entry of entries) {
|
|
39
|
-
let target = entry.target;
|
|
40
|
-
let id = target ? dom.getId(target) : null;
|
|
41
|
-
if (id) {
|
|
42
|
-
let v = dom.getValue(id);
|
|
43
|
-
let s = v.metadata.size;
|
|
44
|
-
let b = entry.borderBoxSize as any;
|
|
45
|
-
let w = null;
|
|
46
|
-
let h = null;
|
|
47
|
-
// Check if browser supports borderBoxSize property on ResizeObserverEntry object
|
|
48
|
-
// Otherwise, fall back to using getBoundingClientRect() to be cross browser compatible
|
|
49
|
-
// Reference: https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry/borderBoxSize
|
|
50
|
-
if(b && b.length > 0) {
|
|
51
|
-
w = Math.floor(b[0].inlineSize * Setting.BoxPrecision);
|
|
52
|
-
h = Math.floor(b[0].blockSize * Setting.BoxPrecision);
|
|
53
|
-
} else {
|
|
54
|
-
let r = target.getBoundingClientRect();
|
|
55
|
-
w = Math.floor(r.width * Setting.BoxPrecision);
|
|
56
|
-
h = Math.floor(r.height * Setting.BoxPrecision);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Capture new width & height only if they are different from what we have in in-memory cache
|
|
60
|
-
if (w !== s[0] && h !== s[1]) {
|
|
61
|
-
s = [w,h];
|
|
62
|
-
data.push({ id, width: w, height: h });
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Schedule encode only when we have at least one valid data entry
|
|
68
|
-
if (data.length > 0) { encode(Event.Box); }
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function reset(): void {
|
|
73
|
-
data = [];
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export function stop(): void {
|
|
77
|
-
reset();
|
|
78
|
-
if (observer) {
|
|
79
|
-
observer.disconnect();
|
|
80
|
-
observer = null;
|
|
81
|
-
}
|
|
82
|
-
enabled = false;
|
|
83
|
-
}
|