UNPKG

@causalfoundry/js-sdk

Version:

Causal Foundry WEB SDK (JS/TS)

257 lines (213 loc) 7.35 kB
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"); }