@gemx-dev/clarity-visualize 3.5.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/CHANGELOG.md +7 -0
- package/README.md +26 -0
- package/package.json +63 -0
- package/rollup.config.ts +79 -0
- package/src/clarity.ts +3 -0
- package/src/data.ts +103 -0
- package/src/enrich.ts +80 -0
- package/src/global.ts +9 -0
- package/src/heatmap.ts +355 -0
- package/src/index.ts +2 -0
- package/src/interaction.ts +504 -0
- package/src/layout.ts +879 -0
- package/src/styles/blobUnavailable/chineseSimplified.svg +5 -0
- package/src/styles/blobUnavailable/chineseTraditional.svg +5 -0
- package/src/styles/blobUnavailable/dutch.svg +5 -0
- package/src/styles/blobUnavailable/english.svg +5 -0
- package/src/styles/blobUnavailable/french.svg +5 -0
- package/src/styles/blobUnavailable/german.svg +5 -0
- package/src/styles/blobUnavailable/iconOnly.svg +4 -0
- package/src/styles/blobUnavailable/italian.svg +5 -0
- package/src/styles/blobUnavailable/japanese.svg +5 -0
- package/src/styles/blobUnavailable/korean.svg +5 -0
- package/src/styles/blobUnavailable/portuguese.svg +5 -0
- package/src/styles/blobUnavailable/russian.svg +5 -0
- package/src/styles/blobUnavailable/spanish.svg +5 -0
- package/src/styles/blobUnavailable/turkish.svg +5 -0
- package/src/styles/iframeUnavailable/chineseSimplified.svg +5 -0
- package/src/styles/iframeUnavailable/chineseTraditional.svg +5 -0
- package/src/styles/iframeUnavailable/dutch.svg +5 -0
- package/src/styles/iframeUnavailable/english.svg +5 -0
- package/src/styles/iframeUnavailable/french.svg +5 -0
- package/src/styles/iframeUnavailable/german.svg +5 -0
- package/src/styles/iframeUnavailable/iconOnly.svg +4 -0
- package/src/styles/iframeUnavailable/italian.svg +5 -0
- package/src/styles/iframeUnavailable/japanese.svg +5 -0
- package/src/styles/iframeUnavailable/korean.svg +5 -0
- package/src/styles/iframeUnavailable/portuguese.svg +5 -0
- package/src/styles/iframeUnavailable/russian.svg +5 -0
- package/src/styles/iframeUnavailable/spanish.svg +5 -0
- package/src/styles/iframeUnavailable/turkish.svg +5 -0
- package/src/styles/imageMasked/chineseSimplified.svg +5 -0
- package/src/styles/imageMasked/chineseTraditional.svg +5 -0
- package/src/styles/imageMasked/dutch.svg +5 -0
- package/src/styles/imageMasked/english.svg +5 -0
- package/src/styles/imageMasked/french.svg +5 -0
- package/src/styles/imageMasked/german.svg +5 -0
- package/src/styles/imageMasked/iconOnly.svg +4 -0
- package/src/styles/imageMasked/italian.svg +5 -0
- package/src/styles/imageMasked/japanese.svg +5 -0
- package/src/styles/imageMasked/korean.svg +5 -0
- package/src/styles/imageMasked/portuguese.svg +5 -0
- package/src/styles/imageMasked/russian.svg +5 -0
- package/src/styles/imageMasked/spanish.svg +5 -0
- package/src/styles/imageMasked/turkish.svg +5 -0
- package/src/styles/pointer/click.css +31 -0
- package/src/styles/pointer/pointerIcon.svg +18 -0
- package/src/styles/shared.css +6 -0
- package/src/visualizer.ts +297 -0
- package/tsconfig.json +21 -0
- package/tslint.json +33 -0
- package/types/index.d.ts +10 -0
- package/types/string-import.d.ts +9 -0
- package/types/visualize.d.ts +235 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# @gemx-dev/clarity-visualize
|
|
2
|
+
|
|
3
|
+
## 3.5.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#29](https://github.com/ducky0209/gemx-sdk/pull/29) [`2a1dd3c`](https://github.com/ducky0209/gemx-sdk/commit/2a1dd3c561324a22f8d8af2611536ce36f1976ff) Thanks [@ducky0209](https://github.com/ducky0209)! - Feat: add package @gemx-dev/clarity-visualize
|
package/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Clarity
|
|
2
|
+
Clarity is an open-source behavioral analytics library written in typescript, with two key goals: privacy & performance.
|
|
3
|
+
|
|
4
|
+
It helps you understand how users view and use your website across all modern devices and browsers. Understanding how users navigate, interact and browse your website can provide new insights about your users. Empathizing with your users and seeing where features fail or succeed can help improve your product, grow revenue and improve user retention.
|
|
5
|
+
|
|
6
|
+
This package takes the decoded data from [clarity-decode](https://github.com/microsoft/clarity/tree/master/packages/clarity-decode) and turns it into pixel-perfect session replay, exactly how the user saw it.
|
|
7
|
+
|
|
8
|
+
We encourage the community to join us in building the best behavioral analytics library, that puts privacy first and prioritizes performance.
|
|
9
|
+
|
|
10
|
+
## Examples
|
|
11
|
+
Here are some example sessions on popular websites visualized to demonstrate the telemetry captured:
|
|
12
|
+
1. CNN (Web)
|
|
13
|
+
</br><a href="https://thumbs.gfycat.com/AggressiveLankyAbyssiniangroundhornbill-size_restricted.gif"><img src="https://thumbs.gfycat.com/AggressiveLankyAbyssiniangroundhornbill-size_restricted.gif" title="Clarity - CNN Example"/></a>
|
|
14
|
+
|
|
15
|
+
2. Cook with Manali (Mobile)
|
|
16
|
+
</br><a href="https://thumbs.gfycat.com/CoolDependableAdamsstaghornedbeetle-size_restricted.gif"><img src="https://thumbs.gfycat.com/CoolDependableAdamsstaghornedbeetle-size_restricted.gif" title="Clarity - Cook With Manali Example"/></a>
|
|
17
|
+
|
|
18
|
+
## Privacy Notice
|
|
19
|
+
Clarity handles sensitive data with care. By default sensitive content on the page is masked before uploading to the server.
|
|
20
|
+
|
|
21
|
+
## Improving Clarity
|
|
22
|
+
If you haven't already done so, start contributing by following instructions **[here](https://github.com/microsoft/clarity/blob/master/CONTRIBUTING.md)**.
|
|
23
|
+
|
|
24
|
+
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
|
25
|
+
|
|
26
|
+
Happy coding!
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gemx-dev/clarity-visualize",
|
|
3
|
+
"version": "3.5.1",
|
|
4
|
+
"description": "An analytics library that uses web page interactions to generate aggregated insights",
|
|
5
|
+
"author": "Microsoft Corp.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "build/clarity.visualize.js",
|
|
8
|
+
"module": "build/clarity.visualize.module.js",
|
|
9
|
+
"unpkg": "build/clarity.visualize.min.js",
|
|
10
|
+
"types": "types/index.d.ts",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"clarity",
|
|
13
|
+
"Microsoft",
|
|
14
|
+
"interactions",
|
|
15
|
+
"cursor",
|
|
16
|
+
"pointer",
|
|
17
|
+
"instrumentation",
|
|
18
|
+
"analytics",
|
|
19
|
+
"visualization"
|
|
20
|
+
],
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/microsoft/clarity.git",
|
|
24
|
+
"directory": "packages/clarity-visualize"
|
|
25
|
+
},
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/Microsoft/clarity/issues"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"clarity-decode": "^0.8.38"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@rollup/plugin-commonjs": "^24.0.0",
|
|
34
|
+
"@rollup/plugin-node-resolve": "^15.0.0",
|
|
35
|
+
"@rollup/plugin-terser": "^0.4.0",
|
|
36
|
+
"@rollup/plugin-typescript": "^11.0.0",
|
|
37
|
+
"rollup-plugin-string-import": "^1.2.5",
|
|
38
|
+
"del-cli": "^5.0.0",
|
|
39
|
+
"husky": "^8.0.0",
|
|
40
|
+
"lint-staged": "^13.1.0",
|
|
41
|
+
"rollup": "^3.0.0",
|
|
42
|
+
"ts-node": "^10.1.0",
|
|
43
|
+
"tslint": "^6.1.3",
|
|
44
|
+
"typescript": "^4.3.5"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "yarn build:clean && yarn build:main",
|
|
48
|
+
"build:main": "rollup -c rollup.config.ts --configPlugin @rollup/plugin-typescript",
|
|
49
|
+
"build:clean": "del-cli build/*",
|
|
50
|
+
"tslint": "tslint --project ./",
|
|
51
|
+
"tslint:fix": "tslint --fix --project ./ --force"
|
|
52
|
+
},
|
|
53
|
+
"husky": {
|
|
54
|
+
"hooks": {
|
|
55
|
+
"pre-commit": "lint-staged"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"lint-staged": {
|
|
59
|
+
"*.ts": [
|
|
60
|
+
"tslint --format codeFrame"
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
}
|
package/rollup.config.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import commonjs from "@rollup/plugin-commonjs";
|
|
2
|
+
import resolve from "@rollup/plugin-node-resolve";
|
|
3
|
+
import terser from "@rollup/plugin-terser";
|
|
4
|
+
import typescript from "@rollup/plugin-typescript";
|
|
5
|
+
import { importAsString } from 'rollup-plugin-string-import';
|
|
6
|
+
import { readFileSync } from "fs";
|
|
7
|
+
|
|
8
|
+
const pkg = JSON.parse(readFileSync("./package.json", "utf-8"));
|
|
9
|
+
|
|
10
|
+
function wrapWithBackground(svg){
|
|
11
|
+
return `background: url("data:image/svg+xml,${svg}") no-repeat center center;`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function regexEncode(svg){
|
|
15
|
+
svg = svg.replace(/>\s{1,}</g, `><`);
|
|
16
|
+
svg = svg.replace(/\s{2,}/g, ` `);
|
|
17
|
+
return svg.replace(/[\r\n%#()<>?[\\\]^`{|}]/g, encodeURIComponent)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function doublesToSingles(svg){
|
|
21
|
+
return svg.replace(/"/g, "'")
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function preprocessFile(content: string, fileName: string): string {
|
|
25
|
+
if (fileName.endsWith(".css")) {
|
|
26
|
+
// remove comments/newlines
|
|
27
|
+
return content.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, "").replace(/\r\n/g, "");
|
|
28
|
+
}
|
|
29
|
+
else if (fileName.endsWith(".svg")) {
|
|
30
|
+
// removes comments/newlines while also encoding the svg and adding some scaffolding to import it
|
|
31
|
+
return wrapWithBackground(regexEncode(doublesToSingles(content)));
|
|
32
|
+
}
|
|
33
|
+
return "";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default [
|
|
37
|
+
{
|
|
38
|
+
input: "src/index.ts",
|
|
39
|
+
output: [
|
|
40
|
+
{ file: pkg.main, format: "cjs", exports: "named" },
|
|
41
|
+
{ file: pkg.module, format: "es", exports: "named" }
|
|
42
|
+
],
|
|
43
|
+
plugins: [
|
|
44
|
+
resolve(),
|
|
45
|
+
importAsString({
|
|
46
|
+
include: ["**/*.css", "**/*.svg"],
|
|
47
|
+
transform: preprocessFile,
|
|
48
|
+
}),
|
|
49
|
+
typescript(),
|
|
50
|
+
commonjs({ include: ["node_modules/**"] })
|
|
51
|
+
],
|
|
52
|
+
onwarn(message, warn) {
|
|
53
|
+
if (message.code === 'NON_EXISTENT_EXPORT') { return; }
|
|
54
|
+
if (message.code === 'CIRCULAR_DEPENDENCY') { return; }
|
|
55
|
+
if (message.code === 'SOURCEMAP_ERROR') { return; }
|
|
56
|
+
warn(message);
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
input: "src/global.ts",
|
|
61
|
+
output: [ { file: pkg.unpkg, format: "iife", exports: "named" } ],
|
|
62
|
+
plugins: [
|
|
63
|
+
resolve(),
|
|
64
|
+
importAsString({
|
|
65
|
+
include: ["**/*.css", "**/*.svg"],
|
|
66
|
+
transform: preprocessFile,
|
|
67
|
+
}),
|
|
68
|
+
typescript(),
|
|
69
|
+
terser({output: {comments: false}}),
|
|
70
|
+
commonjs({ include: ["node_modules/**"] })
|
|
71
|
+
],
|
|
72
|
+
onwarn(message, warn) {
|
|
73
|
+
if (message.code === 'NON_EXISTENT_EXPORT') { return; }
|
|
74
|
+
if (message.code === 'CIRCULAR_DEPENDENCY') { return; }
|
|
75
|
+
if (message.code === 'SOURCEMAP_ERROR') { return; }
|
|
76
|
+
warn(message);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
];
|
package/src/clarity.ts
ADDED
package/src/data.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Data, Layout } from "clarity-js";
|
|
2
|
+
import type { Data as DecodedData, Layout as DecodedLayout } from "clarity-decode";
|
|
3
|
+
import { PlaybackState, RegionState } from "@clarity-types/visualize";
|
|
4
|
+
|
|
5
|
+
export class DataHelper {
|
|
6
|
+
regionMap = {};
|
|
7
|
+
regions: { [key: string]: RegionState} = {};
|
|
8
|
+
metrics: {[key: number]: number} = {};
|
|
9
|
+
lean = false;
|
|
10
|
+
state: PlaybackState;
|
|
11
|
+
|
|
12
|
+
constructor(state: PlaybackState) {
|
|
13
|
+
this.state = state;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
static METRIC_MAP = {
|
|
17
|
+
[Data.Metric.TotalBytes]: { name: "Total Bytes", unit: "KB" },
|
|
18
|
+
[Data.Metric.TotalCost]: { name: "Total Cost", unit: "ms" },
|
|
19
|
+
[Data.Metric.LayoutCost]: { name: "Layout Cost", unit: "ms" },
|
|
20
|
+
[Data.Metric.LargestPaint]: { name: "LCP", unit: "s" },
|
|
21
|
+
[Data.Metric.CumulativeLayoutShift]: { name: "CLS", unit: "cls" },
|
|
22
|
+
[Data.Metric.LongTaskCount]: { name: "Long Tasks" },
|
|
23
|
+
[Data.Metric.CartTotal]: { name: "Cart Total", unit: "html-price" },
|
|
24
|
+
[Data.Metric.ProductPrice]: { name: "Product Price", unit: "ld-price" },
|
|
25
|
+
[Data.Metric.ThreadBlockedTime]: { name: "Thread Blocked", unit: "ms" },
|
|
26
|
+
[Data.Dimension.InteractionNextPaint]: { name: "INP", unit: "ms" }
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
public reset = (): void => {
|
|
30
|
+
this.metrics = {};
|
|
31
|
+
this.lean = false;
|
|
32
|
+
this.regions = {};
|
|
33
|
+
this.regionMap = {};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public metric = (event: DecodedData.MetricEvent): void => {
|
|
37
|
+
if (this.state.options.metadata) {
|
|
38
|
+
let metricMarkup = [];
|
|
39
|
+
let regionMarkup = [];
|
|
40
|
+
// Copy over metrics for future reference
|
|
41
|
+
for (let m in event.data) {
|
|
42
|
+
const eventType = typeof event.data[m];
|
|
43
|
+
if (eventType === "number" || (event.event === Data.Event.Dimension && m === Data.Dimension.InteractionNextPaint.toString())) {
|
|
44
|
+
if (!(m in this.metrics)) { this.metrics[m] = 0; }
|
|
45
|
+
let key = parseInt(m, 10);
|
|
46
|
+
let value = eventType === "object" ? Number(event.data[m]?.[0]) : event.data[m];
|
|
47
|
+
if (m in DataHelper.METRIC_MAP && (DataHelper.METRIC_MAP[m].unit === "html-price" ||DataHelper.METRIC_MAP[m].unit === "ld-price")) {
|
|
48
|
+
this.metrics[m] = value;
|
|
49
|
+
} else { this.metrics[m] += value; }
|
|
50
|
+
this.lean = key === Data.Metric.Playback && value === 0 ? true : this.lean;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
for (let entry in this.metrics) {
|
|
55
|
+
if (entry in DataHelper.METRIC_MAP) {
|
|
56
|
+
let m = this.metrics[entry];
|
|
57
|
+
let map = DataHelper.METRIC_MAP[entry];
|
|
58
|
+
let unit = "unit" in map ? map.unit : Data.Constant.Empty;
|
|
59
|
+
metricMarkup.push(`<li><h2>${this.value(m, unit)}<span>${this.key(unit)}</span></h2>${map.name}</li>`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Append region information to metadata
|
|
64
|
+
for (let name in this.regions) {
|
|
65
|
+
let r = this.regions[name];
|
|
66
|
+
let className = (r.visibility === Layout.RegionVisibility.Visible ? "visible" : (r.interaction === Layout.InteractionState.Clicked ? "clicked" : Data.Constant.Empty));
|
|
67
|
+
regionMarkup.push(`<span class="${className}">${name}</span>`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this.state.options.metadata.innerHTML = `<ul>${metricMarkup.join(Data.Constant.Empty)}</ul><div>${regionMarkup.join(Data.Constant.Empty)}</div>`;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public region(event: DecodedLayout.RegionEvent): void {
|
|
75
|
+
let data = event.data;
|
|
76
|
+
for (let r of data) {
|
|
77
|
+
if (!(r.name in this.regions)) {
|
|
78
|
+
this.regions[r.name] = { interaction: r.interaction , visibility: r.visibility }
|
|
79
|
+
}
|
|
80
|
+
this.regionMap[r.id] = r.name;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private key = (unit: string): string => {
|
|
85
|
+
switch (unit) {
|
|
86
|
+
case "html-price":
|
|
87
|
+
case "ld-price":
|
|
88
|
+
case "cls":
|
|
89
|
+
return Data.Constant.Empty;
|
|
90
|
+
default: return unit;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private value = (num: number, unit: string): number => {
|
|
95
|
+
switch (unit) {
|
|
96
|
+
case "KB": return Math.round(num / 1024);
|
|
97
|
+
case "s": return Math.round(num / 10) / 100;
|
|
98
|
+
case "cls": return num / 1000;
|
|
99
|
+
case "html-price": return num / 100;
|
|
100
|
+
default: return num;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
package/src/enrich.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { helper, Layout } from "clarity-js";
|
|
2
|
+
import { Layout as DecodedLayout } from "clarity-decode";
|
|
3
|
+
import { NodeData } from "@clarity-types/visualize";
|
|
4
|
+
|
|
5
|
+
export class EnrichHelper {
|
|
6
|
+
|
|
7
|
+
children: { [key: number]: number[] };
|
|
8
|
+
nodes: { [key: number]: NodeData };
|
|
9
|
+
|
|
10
|
+
constructor() {
|
|
11
|
+
this.reset();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public reset = (): void => {
|
|
15
|
+
this.children = {};
|
|
16
|
+
this.nodes = {};
|
|
17
|
+
helper.selector.reset();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public selectors = (event: DecodedLayout.DomEvent): DecodedLayout.DomEvent => {
|
|
21
|
+
event.data.forEach && event.data.forEach(d => {
|
|
22
|
+
let parent = this.nodes[d.parent];
|
|
23
|
+
let children = this.children[d.parent] || [];
|
|
24
|
+
let node = this.nodes[d.id] || { tag: d.tag, parent: d.parent, previous: d.previous };
|
|
25
|
+
let attributes = d.attributes || {};
|
|
26
|
+
|
|
27
|
+
/* Track parent-child relationship for this element */
|
|
28
|
+
if (node.parent !== d.parent) {
|
|
29
|
+
let childIndex = d.previous === null ? 0 : children.indexOf(d.previous) + 1;
|
|
30
|
+
children.splice(childIndex, 0, d.id);
|
|
31
|
+
|
|
32
|
+
// Stop tracking this node from children of previous parent
|
|
33
|
+
if (node.parent !== d.parent) {
|
|
34
|
+
let exParent = this.children[node.parent];
|
|
35
|
+
let nodeIndex = exParent ? exParent.indexOf(d.id) : -1;
|
|
36
|
+
if (nodeIndex >= 0) {
|
|
37
|
+
this.children[node.parent].splice(nodeIndex, 1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
node.parent = d.parent;
|
|
41
|
+
} else if (children.indexOf(d.id) < 0) { children.push(d.id); }
|
|
42
|
+
|
|
43
|
+
/* Get current position */
|
|
44
|
+
node.position = this.position(d.id, d.tag, node, children, children.map(c => this.nodes[c]));
|
|
45
|
+
|
|
46
|
+
/* For backward compatibility, continue populating current selector and hash like before in addition to beta selector and hash */
|
|
47
|
+
let input: Layout.SelectorInput = { id: d.id, tag: d.tag, prefix: parent ? [parent.alpha, parent.beta] : null, position: node.position, attributes };
|
|
48
|
+
|
|
49
|
+
// Get stable selector
|
|
50
|
+
// We intentionally use "null" value for empty selectors to keep parity with v0.6.25 and before.
|
|
51
|
+
let selectorAlpha = helper.selector.get(input, Layout.Selector.Alpha);
|
|
52
|
+
d.selectorAlpha = selectorAlpha.length > 0 ? selectorAlpha : null;
|
|
53
|
+
d.hashAlpha = selectorAlpha.length > 0 ? helper.hash(d.selectorAlpha) : null;
|
|
54
|
+
|
|
55
|
+
// Get beta selector
|
|
56
|
+
let selectorBeta = helper.selector.get(input, Layout.Selector.Beta);
|
|
57
|
+
d.selectorBeta = selectorBeta.length > 0 ? selectorBeta : null;
|
|
58
|
+
d.hashBeta = selectorBeta.length > 0 ? helper.hash(d.selectorBeta) : null;
|
|
59
|
+
|
|
60
|
+
/* Track state for future reference */
|
|
61
|
+
node.alpha = selectorAlpha;
|
|
62
|
+
node.beta = selectorBeta;
|
|
63
|
+
this.nodes[d.id] = node;
|
|
64
|
+
if (d.parent) { this.children[d.parent] = children; }
|
|
65
|
+
});
|
|
66
|
+
return event;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private position = (id: number, tag: string, child: NodeData, children: number[], siblings: NodeData[]): number => {
|
|
70
|
+
child.position = 1;
|
|
71
|
+
let idx = children ? children.indexOf(id) : -1;
|
|
72
|
+
while (idx-- > 0) {
|
|
73
|
+
if (tag === siblings[idx].tag) {
|
|
74
|
+
child.position = siblings[idx].position + 1;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return child.position;
|
|
79
|
+
}
|
|
80
|
+
}
|
package/src/global.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as visualize from "@src/clarity";
|
|
2
|
+
|
|
3
|
+
// Expose clarity variable globally to allow access to public interface in a browser
|
|
4
|
+
if (typeof window !== "undefined") {
|
|
5
|
+
if ((window as any).clarity === undefined || (window as any).clarity === null) {
|
|
6
|
+
(window as any).clarity = {};
|
|
7
|
+
}
|
|
8
|
+
(window as any).clarity.visualize = visualize;
|
|
9
|
+
}
|