@causalfoundry/js-sdk
Version:
Causal Foundry WEB SDK (JS/TS)
257 lines (213 loc) • 7.35 kB
text/typescript
import {
networkInterfaces as os_networkInterfaces,
platform as os_platform,
release as os_release,
} from "os";
import { v4 as uuidv4 } from "uuid";
import { EventContext } from "../core/typings";
import isBrowser from "is-in-browser";
const VISIBILITY_HIDDEN = "hidden";
const VISIBILITY_VISIBLE = "visible";
const UserIdStorageKey = "CFUserIdStorageKey";
let visibilityInitialized = false;
export interface NetworkInformation {
downlink: number;
uplink: number;
}
export function getDeviceId(): string {
const deviceIdStorageKey = "deviceIdStorageKey";
let deviceId: string;
if (isBrowser) {
deviceId = localStorage.getItem(deviceIdStorageKey) || "";
if (!deviceId) {
deviceId = uuidv4();
localStorage.setItem(deviceIdStorageKey, deviceId);
}
} else {
// In Node.js, generate a new device ID each time
deviceId = uuidv4();
}
return deviceId;
}
export function setLocalStorageUserId(userId: string) {
if (userId && isBrowser) {
localStorage.setItem(UserIdStorageKey, userId);
}
}
export function getLocalStorageUserId(): string {
if (isBrowser) {
const userId = localStorage.getItem(UserIdStorageKey);
return userId ? userId : "";
}
return "";
}
export function clearLocalStorageUserId() {
if (isBrowser) {
localStorage.setItem(UserIdStorageKey, "");
}
}
export function getDeviceOSDetails(): string {
if (!isBrowser) {
return `${os_platform()} -|- ${os_release()}`;
}
// Current web specification defines `window.navigator.userAgentData.platform
// as the official way to retrieve the operating system, but it is not
// widely adopted by browsers. To avoid that problem this function
// also takes into account the deprecated mechanism `window.navigator.platform`
if (!window?.navigator) {
return "";
}
const platform = window.navigator["userAgentData"]?.platform;
const platformLegacy = window.navigator.platform;
const userAgentValue = window.navigator?.userAgent || ";";
return `${platform || platformLegacy} -|- ${userAgentValue
?.match(/(([^)]+))/)?.[1]
.split(";")[1]
.trim()}`;
}
export function getAppVersionDetails(): string {
// Current web specification defines `window.navigator.userAgentData.platform
// as the official way to retrieve the operating system, but it is not
// widely adopted by browsers. To avoid that problem this function
// also takes into account the deprecated mechanism `window.navigator.platform`
// Also for non browser env
if (!isBrowser || !window?.navigator || window.navigator.userAgent === "") {
return "NO_BROWSER";
}
return getBrowserVersion() || "0";
}
function getBrowserVersion(): string {
const ua = navigator.userAgent;
let tem;
let M =
ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) ||
[];
if (/trident/i.test(M[1])) {
tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
return "IE " + (tem[1] || "");
}
if (M[1] === "Chrome") {
tem = ua.match(/\b(OPR|Edge)\/(\d+)/);
if (tem != null) return tem.slice(1).join(" ").replace("OPR", "Opera");
}
M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, "-?"];
if ((tem = ua.match(/version\/(\d+)/i)) != null) M.splice(1, 1, tem[1]);
return M.join(" ");
}
export function getOperatingSystem(): string {
if (!isBrowser) {
return `${os_platform()}-${os_release()}`;
}
// Current web specification defines `window.navigator.userAgentData.platform
// as the official way to retrieve the operating system, but it is not
// widely adopted by browsers. To avoid that problem this function
// also takes into account the deprecated mechanism `window.navigator.platform`
if (!window?.navigator) {
return "";
}
const platform = window.navigator["userAgentData"]?.platform;
const platformLegacy = window.navigator.platform;
return platform || platformLegacy;
}
export function getNetworkInformation(): NetworkInformation {
if (!isBrowser) {
return {
downlink: 0,
uplink: 0,
};
}
// in case the speed is not available, we have agreed to send 0
// otherwise, return the network speed in kbps
const connection =
(navigator as any).connection ||
(navigator as any).mozConnection ||
(navigator as any).webkitConnection;
if (!connection) {
return {
downlink: 0,
uplink: 0,
};
}
return {
downlink: parseInt(((connection.downlink || 0) * 1000).toString()),
uplink: 0,
};
}
export function listenLocationChangedEvent(clb) {
const originalPushState = window.history.pushState;
const originalReplaceState = window.history.replaceState;
window.history.pushState = function (data, title, url) {
let currentPath = url || ""; // first time is undefined
if (currentPath[0] != "/") {
currentPath = `/${currentPath}`;
}
clb(`${window.location.origin}${currentPath}`);
return originalPushState.call(window.history, data, title, url);
};
window.history.replaceState = function (data, title, url) {
let currentPath = url || ""; // first time is undefined
if (currentPath[0] != "/") {
currentPath = `/${currentPath}`;
}
clb(`${window.location.origin}${currentPath}`);
return originalReplaceState.call(window.history, data, title, url);
};
window.addEventListener("popstate", function (evt) {
clb((evt.target as Window).location.href);
});
}
export function listenApplicationClose(clb) {
window.addEventListener("unload", () => {
clb();
});
}
export function listenApplicationVisibilityChange(clb_visible, clb_hide) {
if (document.visibilityState === VISIBILITY_VISIBLE) {
visibilityInitialized = true;
}
document.addEventListener("visibilitychange", (e) => {
console.warn("[event] [visibilitychange] ", document.visibilityState);
if (document.visibilityState === VISIBILITY_VISIBLE) {
visibilityInitialized = true;
clb_visible();
} else if (document.visibilityState === VISIBILITY_HIDDEN) {
if (!visibilityInitialized) {
return;
}
clb_hide();
}
});
}
export function listenPageChanged(clb) {
listenLocationChangedEvent((newUrl) => {
// the reason of this delay is that the new title
// is not available yet when the location-changed
// event is fired
setTimeout(() => {
clb(newUrl, document.title);
}, 10);
});
}
export function listenPageScrolled(threshold, clb) {
// const currentScrollTop = (): number => (window.pageYOffset || document.documentElement.scrollTop) - (document.documentElement.clientTop || 0)
const getScrollPercent = () => {
const SCROLL_TOP = "scrollTop";
const SCROLL_HEIGHT = "scrollHeight";
const currentScrollTop =
document.documentElement[SCROLL_TOP] || document.body[SCROLL_TOP];
const totalScrollHeight =
document.documentElement[SCROLL_HEIGHT] || document.body[SCROLL_HEIGHT];
const visibleHeight = document.documentElement.clientHeight;
return (currentScrollTop / (totalScrollHeight - visibleHeight)) * 100;
};
window.addEventListener("scroll", (e: Event) => {
const scroll = getScrollPercent();
if (scroll < threshold) {
return;
}
clb(scroll);
});
}
export function isDevEnvironment() {
return document.location.search.includes("cfenv=dev");
}