UNPKG

@benshi.ai/js-sdk

Version:

Benshi SDK

281 lines (227 loc) 8.25 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' 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 { if (!isBrowser) { const interfaces = os_networkInterfaces() const macs = Object.keys(interfaces) .map(key => interfaces[key] .filter(info => !info.internal) .map(info => info.mac) ) .flat() if (macs.length !== 0) { return macs[0].split(':').join('') } else { return '' } } const deviceIdStorageKey = 'deviceIdStorageKey' let deviceId; if (localStorage.getItem(deviceIdStorageKey) === null) { deviceId = uuidv4() localStorage.setItem(deviceIdStorageKey, deviceId) } else { deviceId = localStorage.getItem(deviceIdStorageKey) } return deviceId } 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 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) { clb(`${window.location.origin}/${url}`) return originalPushState.call(window.history, data, title, url); } window.history.replaceState = function (data, title, url) { const currentPath = url || '' // first time is undefined 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('bsenv=dev') } export function init(): void { window.addEventListener('beforeunload', () => { }) }