Skip to content

Commit

Permalink
Improving data quality and adding click sound
Browse files Browse the repository at this point in the history
Fixing data quality issues based on internal errors
  • Loading branch information
sarveshnagpal committed May 13, 2020
2 parents fd42bfa + 653dfab commit 82e7a33
Show file tree
Hide file tree
Showing 30 changed files with 132 additions and 85 deletions.
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"packages": [
"packages/*"
],
"version": "0.5.3",
"version": "0.5.4",
"npmClient": "yarn",
"useWorkspaces": true
}
4 changes: 2 additions & 2 deletions packages/clarity-decode/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "clarity-decode",
"version": "0.5.3",
"version": "0.5.4",
"description": "An analytics library that uses web page interactions to generate aggregated insights",
"author": "Microsoft Corp.",
"license": "MIT",
Expand All @@ -26,7 +26,7 @@
"url": "https://github.com/Microsoft/clarity/issues"
},
"dependencies": {
"clarity-js": "^0.5.3"
"clarity-js": "^0.5.4"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^11.1.0",
Expand Down
4 changes: 3 additions & 1 deletion packages/clarity-decode/src/diagnostic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ export function decode(tokens: Data.Token[]): DiagnosticEvent {
let internalError: Diagnostic.InternalErrorData = {
code: tokens[2] as number,
name: tokens[3] as string,
message: tokens[4] as string
message: tokens[4] as string,
stack: tokens[5] as string,
severity: tokens[6] as number
};
return { time, event, data: internalError };
}
Expand Down
1 change: 1 addition & 0 deletions packages/clarity-devtools/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ The goal of this project is to demonstrate how various components of Clarity com
3. Live session replays against any website

## Building Extension
- Setup your development environment by following instructions **[here](https://github.com/microsoft/clarity/blob/master/CONTRIBUTING.md)**
- Build everything: `yarn build`
- Extension will be built under: `extension` folder

Expand Down
8 changes: 4 additions & 4 deletions packages/clarity-devtools/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "clarity-devtools",
"version": "0.5.3",
"version": "0.5.4",
"private": true,
"description": "Adds Clarity debugging support to browser devtools",
"author": "Microsoft Corp.",
Expand All @@ -24,9 +24,9 @@
"url": "https://github.com/Microsoft/clarity/issues"
},
"dependencies": {
"clarity-decode": "^0.5.3",
"clarity-js": "^0.5.3",
"clarity-visualize": "^0.5.3"
"clarity-decode": "^0.5.4",
"clarity-js": "^0.5.4",
"clarity-visualize": "^0.5.4"
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^7.1.3",
Expand Down
4 changes: 2 additions & 2 deletions packages/clarity-devtools/static/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"manifest_version": 2,
"name": "Clarity Developer Tools",
"description": "Get insights about how customers use your website.",
"version": "0.5.3",
"version_name": "0.5.3",
"version": "0.5.4",
"version_name": "0.5.4",
"minimum_chrome_version": "50",
"devtools_page": "devtools.html",
"icons": {
Expand Down
2 changes: 1 addition & 1 deletion packages/clarity-js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "clarity-js",
"version": "0.5.3",
"version": "0.5.4",
"description": "An analytics library that uses web page interactions to generate aggregated insights",
"author": "Microsoft Corp.",
"license": "MIT",
Expand Down
13 changes: 0 additions & 13 deletions packages/clarity-js/src/core/offset.ts

This file was deleted.

4 changes: 2 additions & 2 deletions packages/clarity-js/src/core/task.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AsyncTask, Priority, RequestIdleCallbackDeadline, RequestIdleCallbackOptions } from "@clarity-types/core";
import { TaskFunction, TaskResolve, TaskTracker } from "@clarity-types/core";
import { Code, Metric } from "@clarity-types/data";
import { Code, Metric, Severity } from "@clarity-types/data";
import config from "@src/core/config";
import * as metric from "@src/data/metric";
import * as internal from "@src/diagnostic/internal";
Expand Down Expand Up @@ -66,7 +66,7 @@ function run(): void {
run();
}).catch((error: Error): void => {
// If one of the scheduled tasks failed, log, recover and continue processing rest of the tasks
internal.error(Code.RunTask, error);
internal.error(Code.RunTask, error, Severity.Warning);
activeTask = null;
run();
});
Expand Down
2 changes: 1 addition & 1 deletion packages/clarity-js/src/core/version.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
let version = "0.5.3";
let version = "0.5.4";
export default version;
3 changes: 2 additions & 1 deletion packages/clarity-js/src/data/target.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ export function reset(): void {

export function target(evt: UIEvent): Node {
let path = evt.composed && evt.composedPath ? evt.composedPath() : null;
return (path && path.length > 0 ? path[0] : evt.target) as Node;
let node = (path && path.length > 0 ? path[0] : evt.target) as Node;
return node.nodeType === Node.DOCUMENT_NODE ? (node as Document).documentElement : node;
}

export function link(node: Node): HTMLAnchorElement {
Expand Down
2 changes: 2 additions & 0 deletions packages/clarity-js/src/diagnostic/encode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export default async function(type: Event): Promise<void> {
tokens.push(internal.data.code);
tokens.push(internal.data.name);
tokens.push(internal.data.message);
tokens.push(internal.data.stack);
tokens.push(internal.data.severity);
queue(tokens);
}
break;
Expand Down
8 changes: 5 additions & 3 deletions packages/clarity-js/src/diagnostic/internal.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Code, Event } from "@clarity-types/data";
import { Code, Event, Severity } from "@clarity-types/data";
import { InternalErrorData } from "@clarity-types/diagnostic";
import encode from "./encode";

let history: { [key: number]: string[] } = {};
export let data: InternalErrorData;

export function error(code: Code, err: Error): void {
export function error(code: Code, err: Error, severity: Severity = Severity.Warning): void {
let errorKey = err ? `${err.name}|${err.message}`: "";
// While rare, it's possible for code to fail repeatedly during the lifetime of the same page
// In those cases, we only want to log the failure once and not spam logs with redundant information.
Expand All @@ -14,7 +14,9 @@ export function error(code: Code, err: Error): void {
data = {
code,
name: err ? err.name : null,
message: err ? err.message : null
message: err ? err.message : null,
stack: err ? err.stack : null,
severity
};

// Maintain history of errors in memory to avoid sending redundant information
Expand Down
2 changes: 1 addition & 1 deletion packages/clarity-js/src/interaction/click.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Event } from "@clarity-types/data";
import { ClickData } from "@clarity-types/interaction";
import { bind } from "@src/core/event";
import offset from "@src/core/offset";
import { schedule } from "@src/core/task";
import { link, target, track } from "@src/data/target";
import { iframe } from "@src/layout/dom";
import offset from "@src/layout/offset";
import encode from "./encode";

export let data: ClickData;
Expand Down
2 changes: 1 addition & 1 deletion packages/clarity-js/src/interaction/pointer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { Event } from "@clarity-types/data";
import { PointerState } from "@clarity-types/interaction";
import config from "@src/core/config";
import { bind } from "@src/core/event";
import offset from "@src/core/offset";
import { schedule } from "@src/core/task";
import { time } from "@src/core/time";
import { clearTimeout, setTimeout } from "@src/core/timeout";
import { iframe } from "@src/layout/dom";
import offset from "@src/layout/offset";
import { target, track } from "@src/data/target";
import encode from "./encode";

Expand Down
15 changes: 11 additions & 4 deletions packages/clarity-js/src/interaction/scroll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,22 @@ export function observe(root: Node): void {
}

function recompute(event: UIEvent = null): void {
let w = window as Window;
let de = document.documentElement;
let t = event ? target(event) : de;
let element = t && t === document ? de : t;
let element = event ? target(event) : de;

// If the target is a Document node, then identify corresponding documentElement and window for this document
if (element && element.nodeType === Node.DOCUMENT_NODE) {
let frame = iframe(element);
w = frame ? frame.contentWindow : w;
element = de = (element as Document).documentElement;
}

// Edge doesn't support scrollTop position on document.documentElement.
// For cross browser compatibility, looking up pageYOffset on window if the scroll is on document.
// And, if for some reason that is not available, fall back to looking up scrollTop on document.documentElement.
let x = element === de && "pageXOffset" in window ? Math.round(window.pageXOffset) : Math.round((element as HTMLElement).scrollLeft);
let y = element === de && "pageYOffset" in window ? Math.round(window.pageYOffset) : Math.round((element as HTMLElement).scrollTop);
let x = element === de && "pageXOffset" in w ? Math.round(w.pageXOffset) : Math.round((element as HTMLElement).scrollLeft);
let y = element === de && "pageYOffset" in w ? Math.round(w.pageYOffset) : Math.round((element as HTMLElement).scrollTop);
let current: ScrollState = { time: time(), event: Event.Scroll, data: {target: track(element), x, y} };

// We don't send any scroll events if this is the first event and the current position is top (0,0)
Expand Down
9 changes: 6 additions & 3 deletions packages/clarity-js/src/interaction/selection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,14 @@ function recompute(root: Node): void {
// Bail out if we don't have a valid selection
if (current === null) { return; }

// Bail out if we got valid selection but not valid nodes
// Bail out if we got a valid selection but not valid nodes
// In Edge, selectionchange gets fired even on interactions like right clicks and
// can result in null anchorNode and focusNode if there was no previous selection on page
if (current.anchorNode === null && current.focusNode === null) { return; }

// Also, ignore any selections that start and end at the exact same point
if ((current.anchorNode === null && current.focusNode === null) ||
(current.anchorNode === current.focusNode && current.anchorOffset === current.focusOffset)) {
return;
}
let startNode = data.start ? (data.start as TargetInfo).node : null;
if (previous !== null && data.start !== null && startNode !== current.anchorNode) {
clearTimeout(timeout);
Expand Down
10 changes: 6 additions & 4 deletions packages/clarity-js/src/layout/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,10 +314,12 @@ export function updates(): NodeValue[] {
}

function remove(id: number, source: Source): void {
let value = values[id];
value.metadata.active = false;
value.parent = null;
track(id, source);
if (id in values) {
let value = values[id];
value.metadata.active = false;
value.parent = null;
track(id, source);
}
}

function metadata(tag: string, id: number, parentId: number): void {
Expand Down
12 changes: 6 additions & 6 deletions packages/clarity-js/src/layout/mutation.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Priority } from "@clarity-types/core";
import { Code, Event, Metric } from "@clarity-types/data";
import { Code, Event, Metric, Severity } from "@clarity-types/data";
import { Constant, Source } from "@clarity-types/layout";
import { bind } from "@src/core/event";
import measure from "@src/core/measure";
Expand Down Expand Up @@ -42,13 +42,13 @@ export function start(): void {
export function observe(node: Node): void {
// Create a new observer for every time a new DOM tree (e.g. root document or shadowdom root) is discovered on the page
// In the case of shadow dom, any mutations that happen within the shadow dom are not bubbled up to the host document
// For this reason, we need to wire up mutations everytime we see a new shadow dom.
// For this reason, we need to wire up mutations every time we see a new shadow dom.
// Also, wrap it inside a try / catch. In certain browsers (e.g. legacy Edge), observer on shadow dom can throw errors
try {
let observer = window["MutationObserver"] ? new MutationObserver(measure(handle) as MutationCallback) : null;
observer.observe(node, { attributes: true, childList: true, characterData: true, subtree: true });
observers.push(observer);
} catch (error) { internal.error(Code.MutationObserver, error); }
} catch (error) { internal.error(Code.MutationObserver, error, Severity.Info); }
}

export function monitor(frame: HTMLIFrameElement): void {
Expand Down Expand Up @@ -108,14 +108,14 @@ async function process(): Promise<void> {
break;
case Constant.CHILD_LIST:
// Process additions
let addedLength = mutation.addedNodes.length;
let addedLength = mutation.addedNodes ? mutation.addedNodes.length : 0;
for (let j = 0; j < addedLength; j++) {
let addedNode = mutation.addedNodes[j];
dom.extractRegions(addedNode as HTMLElement);
traverse(addedNode, timer, Source.ChildListAdd);
}
// Process removes
let removedLength = mutation.removedNodes.length;
let removedLength = mutation.removedNodes ? mutation.removedNodes.length : 0;
for (let j = 0; j < removedLength; j++) {
if (task.shouldYield(timer)) { await task.suspend(timer); }
processNode(mutation.removedNodes[j], Source.ChildListRemove);
Expand All @@ -137,7 +137,7 @@ function generate(target: Node, type: MutationRecordType): void {
nextSibling: null,
oldValue: null,
previousSibling: null,
removedNodes: null,
removedNodes: [],
target,
type
}]);
Expand Down
4 changes: 2 additions & 2 deletions packages/clarity-js/src/layout/node.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Constant, Source } from "@clarity-types/layout";
import { Code } from "@clarity-types/data";
import { Code, Severity } from "@clarity-types/data";
import config from "@src/core/config";
import * as dom from "./dom";
import * as internal from "@src/diagnostic/internal";
Expand Down Expand Up @@ -144,7 +144,7 @@ function getCssRules(sheet: CSSStyleSheet): string {
let cssRules = null;
// Firefox throws a SecurityError when trying to access cssRules of a stylesheet from a different domain
try { cssRules = sheet ? sheet.cssRules : []; } catch (e) {
internal.error(Code.CssRules, e);
internal.error(Code.CssRules, e, Severity.Warning);
if (e.name !== "SecurityError") { throw e; }
}

Expand Down
19 changes: 19 additions & 0 deletions packages/clarity-js/src/layout/offset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { OffsetDistance } from "@clarity-types/core";
import { iframe } from "@src/layout/dom";

export default function(element: HTMLElement): OffsetDistance {
let output: OffsetDistance = { x: 0, y: 0 };

// Walk up the chain to ensure we compute offset distance correctly
// In case where we may have nested IFRAMEs, we keep walking up until we get to the top most parent page
if (element && element.offsetParent) {
do {
let parent = element.offsetParent as HTMLElement;
let frame = parent === null ? iframe(element.ownerDocument) : null;
output.x += element.offsetLeft;
output.y += element.offsetTop;
element = frame ? frame : parent;
} while (element);
}
return output;
}
4 changes: 2 additions & 2 deletions packages/clarity-js/src/layout/selector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ export default function(tag: string, prefix: string, attributes: Attributes, pos
case "META":
case Constant.TEXT_TAG:
case Constant.DOCUMENT_TAG:
return Constant.EMPTY_STRING;
return Constant.PLACEHOLDER_SELECTOR;
case "HTML":
return "HTML";
default:
if (prefix === null) { return Constant.EMPTY_STRING; }
if (prefix === null) { return Constant.DISCONNECTED_SELECTOR; }
tag = tag.indexOf(Constant.SVG_PREFIX) === 0 ? tag.substr(Constant.SVG_PREFIX.length) : tag;
let selector = `${prefix}${tag}${suffix}`;
if (Constant.ID_ATTRIBUTE in attributes && attributes[Constant.ID_ATTRIBUTE].length > 0) {
Expand Down
Loading

0 comments on commit 82e7a33

Please sign in to comment.