UNPKG

@causalfoundry/js-sdk

Version:

Causal Foundry WEB SDK (JS/TS)

307 lines (250 loc) 9.09 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 type DeviceInfoObject = { brand: string, model: string, os: string, os_ver: string, } export type AppInfoObject = { version_code: number, version_name: string, version: string, } export function getDeviceId(): string { const deviceIdStorageKey = 'deviceIdStorageKey' let deviceId = localStorage.getItem(deviceIdStorageKey) if (!deviceId) { deviceId = uuidv4() localStorage.setItem(deviceIdStorageKey, deviceId) } return deviceId } export function setLocalStorageUserId(userId : string) { if (userId) { localStorage.setItem(UserIdStorageKey, userId) } } export function getLocalStorageUserId(): string { const userId = localStorage.getItem(UserIdStorageKey) return userId? userId : '' } export function clearLocalStorageUserId() { localStorage.setItem(UserIdStorageKey, '') } export function getDeviceDetails(): DeviceInfoObject { if (!isBrowser) { return { brand: `NO_BROWSER`, model: `NO_BROWSER`, os: `${os_platform()}`, os_ver: `${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 { brand: '', model: '', os: '', os_ver: '' } } const platform = window.navigator["userAgentData"]?.platform const platformLegacy = window.navigator.platform const userAgentValue = window.navigator.userAgent return { brand: platform || platformLegacy, model: userAgentValue.match(/\(([^)]+)\)/)[1].split(';')[0], os: platform || platformLegacy, os_ver: userAgentValue.match(/\(([^)]+)\)/)[1].split(';')[1].trim() } } export function getAppDetails(): AppInfoObject { // 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 { version_code: 0, version_name: 'NO_BROWSER', version: 'NO_BROWSER', } } const appVersion = getBrowserVersion() return { version_code: parseInt(appVersion.split(" ")[1] || "0"), version_name: appVersion.split(" ")[0] || "0", version: appVersion || "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) { let originalPushState = window.history.pushState; let 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) }) } // legacy function. Leave it here for reusing it when needed async function getContext(): Promise<EventContext> { const context: EventContext = {} if (!!navigator) { const battery = (await (navigator as any).getBattery()) || null context.browser = { user_agent: window.navigator.userAgent, languages: [...window.navigator.languages], online: navigator.onLine, battery: battery ? { charging: battery.charging, chargingTime: battery.chargingTime, dischargingTime: battery.dischargingTime, level: battery.level } : null } } return context } export function isDevEnvironment() { return document.location.search.includes('cfenv=dev') } export function init(): void { window.addEventListener('beforeunload', () => { }) }