UNPKG

@yqg/multiple-click

Version:

Monitor user's multiple click behavior and report

159 lines (128 loc) 3.98 kB
import {SELECTOR_MAX_STRING_LENGTH, SELECTOR_MAX_TRAVERSE_HEIGHT} from './constant'; import {MouseEventHandler} from './type'; /** 监听器绑定 */ export const bindHandler = (handler: MouseEventHandler) => { // 在documenet元素的事件捕获阶段进行监听 document.addEventListener('click', handler, {capture: true}); }; /** 监听器解绑 */ export const unbindHandler = (handler: MouseEventHandler) => { document.removeEventListener('click', handler, {capture: true}); }; /** 获取操作系统类型 */ export const getOsType = () => { const ua = navigator.userAgent; if (ua.indexOf('Win') !== -1) { return 'Windows'; } if (ua.indexOf('iPhone') !== -1) { return 'IOS'; } if (ua.indexOf('Mac') !== -1) { return 'MacOS'; } if (ua.indexOf('Android') !== -1) { return 'Android'; } if (ua.indexOf('Linux') !== -1) { return 'Linux'; } return null; }; /** * 获取国家信息 * CN - 中国 * IDN - 印度尼西亚 * BRA - 巴西 * INDIA - 印度 * PHI - 菲律宾 * MEX - 墨西哥 * THA - 泰国 * POL - 波兰 */ export const getCountry = () => { let lang = navigator.userAgent.match(/Language\/([a-z]{2})(?:\s|$)/)?.[1]; if (!lang) { lang = navigator.language.split('-')[0]; } const localeMap = { zh: 'CN', id: 'IDN', pt: 'BRA', hi: 'INDIA', fil: 'PHI', es: 'MEX', th: 'THA', pl: 'POL', }; return localeMap[lang as keyof typeof localeMap] || 'CN'; }; export const pick = <T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> => { const ret: any = {}; keys.forEach(key => { // eslint-disable-next-line no-prototype-builtins if (obj.hasOwnProperty(key)) { ret[key] = obj[key]; } }); return ret; }; export const isArray = (arr: any) => Object.prototype.toString.call(arr) === '[object Array]'; export const castArray = <T>(origin: T | T[]): T[] => isArray(origin) ? (origin as T[]) : [origin as T]; const htmlElementAsString = (elem: Element): string => { if (!elem || !elem.tagName) { return ''; } const out = []; out.push(elem.tagName.toLowerCase()); if (elem.id) { out.push(`#${elem.id}`); } const className = elem.className; if (className && Object.prototype.toString.call(className) === '[object String]') { const classes = className.split(/\s+/); for (const c of classes) { out.push(`.${c}`); } } const allowedAttrs = ['aria-label', 'type', 'name', 'title', 'alt']; for (const k of allowedAttrs) { const attr = elem.getAttribute(k); if (attr) { out.push(`[${k}="${attr}"]`); } } return out.join(''); }; /** 抄的sentry的实现,详见:https://github.com/getsentry/sentry-javascript/blob/develop/packages/utils/src/browser.ts */ export const htmlTreeAsString = (el: Element | Node): string => { if (!el) { return '<unknown>'; } try { const separator = ' > '; const sepLength = separator.length; const maxTraverseHeight = SELECTOR_MAX_TRAVERSE_HEIGHT; const maxStringLength = SELECTOR_MAX_STRING_LENGTH; const out = []; let height = 0; let len = 0; let currentElem = el; let nextStr; while (currentElem && height++ < maxTraverseHeight) { nextStr = htmlElementAsString(currentElem as Element); if ( nextStr === 'html' || (height > 1 && len + out.length * sepLength + nextStr.length >= maxStringLength) ) { break; } out.push(nextStr); len += nextStr.length; currentElem = currentElem.parentNode; } return out.reverse().join(separator); } catch (err) { return '<unknown>'; } };