@benshi.ai/js-sdk
Version:
Benshi SDK
281 lines (227 loc) • 8.25 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'
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', () => {
})
}