@mdn/bob
Version:
Builder of Bits aka The MDN Web Docs interactive examples, example builder
98 lines (81 loc) • 2.58 kB
text/typescript
import { postParentMessage } from "./events.js";
import { getStorageItem, storeItem } from "./utils.js";
const ACTION_COUNTS_KEY = "action-counts";
type ActionCounts = Record<string, number>;
/**
* Reads action counts from local storage.
* Ignores action counts that belong to another example.
*
* @returns The action counts from the previous session, if available.
*/
function getActionsCounts() {
const json = getStorageItem(ACTION_COUNTS_KEY);
if (!json) {
return {};
}
const value = JSON.parse(json);
if (value && value.href === window.location.href && value.counts) {
return value.counts;
}
return {};
}
/**
* Writes action counts to local storage, to persist them between reloads.
*
* @param {object} counts - The current action counts.
*/
function storeActionCounts(counts: ActionCounts) {
storeItem(
ACTION_COUNTS_KEY,
JSON.stringify({
href: window.location.href,
counts,
}),
);
}
/**
* Action counts by key.
* Used to distinguish 1st/2nd/etc occurrences of the same event.
*/
let actionCounts: ActionCounts = {};
/**
* Last observed action.
* Used to ignore multiple input actions until another action happened.
*/
let lastAction: string | null = null;
/**
* Records an action, by counting it and forwarding it to the window parent.
*
* @param {string} key - Distinct name of the type of action.
* @param {boolean} deduplicate - Should multiple actions of the same type be ignored until another action occurred?
*/
export function recordAction(key: string, deduplicate = false) {
if (deduplicate && key === lastAction) {
return;
} else {
lastAction = key;
}
actionCounts[key] = (actionCounts[key] ?? 0) + 1;
storeActionCounts(actionCounts);
const source = `${key} -> ${actionCounts[key]}`;
postParentMessage("action", { source });
}
export function initTelemetry() {
actionCounts = getActionsCounts();
lastAction = null;
document.addEventListener("DOMContentLoaded", () => {
// User focuses the iframe.
window.addEventListener("focus", () => recordAction("focus"));
// User copies / cuts / paste text in the iframe.
window.addEventListener("copy", () => recordAction("copy"));
window.addEventListener("cut", () => recordAction("cut"));
window.addEventListener("paste", () => recordAction("paste"));
// User clicks on any element with an id.
window.addEventListener("click", (event) => {
const id = (event.target as HTMLElement | null)?.id;
if (id) {
recordAction(`click@${id}`);
}
});
});
}