@yqg/multiple-click
Version:
Monitor user's multiple click behavior and report
159 lines (128 loc) • 3.98 kB
text/typescript
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>';
}
};