UNPKG

clarity-js

Version:

An analytics library that uses web page interactions to generate aggregated insights

115 lines (101 loc) 4.18 kB
import { OffsetDistance, Privacy } from "@clarity-types/core"; import { Event } from "@clarity-types/data"; import { Constant, NodeInfo, NodeValue, TargetMetadata } from "@clarity-types/layout"; import * as doc from "@src/layout/document"; import encode from "@src/insight/encode"; import * as interaction from "@src/interaction"; import config from "@src/core/config"; export let values: NodeValue[] = []; let index: number = 1; let idMap: WeakMap<Node, number> = null; // Maps node => id. export function start(): void { reset(); doc.start(); getId(document.documentElement); // Pre-discover ID for page root interaction.observe(document); } export function stop(): void { reset(); doc.stop(); } export function compute(): void { /* Intentionally Blank */ } export function iframe(): boolean { return false; } export function offset(): OffsetDistance { return { x: 0, y: 0 }; } export function hashText(): void { /* Intentionally Blank */ } export function target(evt: UIEvent): Node { let path = evt.composed && evt.composedPath ? evt.composedPath() : null; 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 metadata(node: Node): TargetMetadata { let output: TargetMetadata = { id: 0, hash: null, privacy: config.conversions ? Privacy.Sensitive : Privacy.Snapshot }; if (node) { output.id = idMap.has(node) ? idMap.get(node) : getId(node); } return output; } export function snapshot(): void { values = []; traverse(document); encode(Event.Snapshot); } function reset(): void { idMap = new WeakMap(); } function traverse(root: Node): void { let queue = [root]; while (queue.length > 0) { let attributes = null, tag = null, value = null; let node = queue.shift(); let next = node.firstChild; let parent = node.parentElement ? node.parentElement : (node.parentNode ? node.parentNode : null); while (next) { queue.push(next); next = next.nextSibling; } // Process the node let type = node.nodeType; switch (type) { case Node.DOCUMENT_TYPE_NODE: let doctype = node as DocumentType; tag = Constant.DocumentTag; attributes = { name: doctype.name, publicId: doctype.publicId, systemId: doctype.systemId } break; case Node.TEXT_NODE: value = node.nodeValue; tag = idMap.get(parent) ? Constant.TextTag : tag; break; case Node.ELEMENT_NODE: let element = node as HTMLElement; attributes = getAttributes(element); tag = !["NOSCRIPT", "SCRIPT", "STYLE"].includes(element.tagName) ? element.tagName : tag; break; } add(node, parent, { tag, attributes, value }); } } /* Helper Functions - Snapshot Traversal */ function getAttributes(element: HTMLElement): { [key: string]: string } { let output = {}; let attributes = element.attributes; if (attributes && attributes.length > 0) { for (let i = 0; i < attributes.length; i++) { output[attributes[i].name] = attributes[i].value; } } return output; } function getId(node: Node): number { if (node === null) { return null; } if (idMap.has(node)) { return idMap.get(node); } idMap.set(node, index); return index++; } function add(node: Node, parent: Node, data: NodeInfo): void { if (node && data && data.tag) { let id = getId(node); let parentId = parent ? idMap.get(parent) : null; let previous = node.previousSibling ? idMap.get(node.previousSibling) : null; let metadata = { active: true, suspend: false, privacy: Privacy.Snapshot, position: null, fraud: null, size: null }; values.push({ id, parent: parentId, previous, children: [], data, selector: null, hash: null, region: null, metadata }); } } export function get(_node: Node): NodeValue {return null;}