1 import UAParser from 'ua-parser-js';
3 const uaParser = new UAParser();
4 const ua = uaParser.getResult();
6 export const hasModulesSupport = () => {
7 const script = document.createElement('script');
8 return 'noModule' in script;
11 export const isFileSaverSupported = () => !!new Blob();
13 export const textToClipboard = (text = '', target = document.body) => {
14 const oldActiveElement = document.activeElement as HTMLElement;
15 if (navigator.clipboard) {
16 void navigator.clipboard.writeText(text);
18 const dummy = document.createElement('textarea');
19 target.appendChild(dummy);
22 document.execCommand('copy');
23 target.removeChild(dummy);
25 oldActiveElement?.focus?.();
28 export const getOS = () => {
29 const { name = 'other', version = '' } = ua.os;
30 return { name, version };
33 export const isIos11 = () => {
34 const { name, version } = getOS();
35 return name.toLowerCase() === 'ios' && parseInt(version, 10) === 11;
38 export const isAndroid = () => {
39 const { name } = getOS();
40 return name.toLowerCase().includes('android');
43 export const isSafari = () => ua.browser.name === 'Safari' || ua.browser.name === 'Mobile Safari';
44 export const isSafari11 = () => isSafari() && ua.browser.major === '11';
45 export const isMinimumSafariVersion = (version: number) => isSafari() && Number(ua.browser.version) >= version;
46 export const isSafariMobile = () => ua.browser.name === 'Mobile Safari';
47 export const isIE11 = () => ua.browser.name === 'IE' && ua.browser.major === '11';
48 export const isEdge = () => ua.browser.name === 'Edge';
49 export const isEdgeChromium = () => isEdge() && ua.engine.name === 'Blink';
50 export const isBrave = () => ua.browser.name === 'Brave';
51 export const isFirefox = () => ua.browser.name === 'Firefox';
52 export const isMaybeTorLessThan11 = () => {
55 /\.0$/.test(ua.browser.version || '') && // The Firefox minor revision is omitted.
56 Intl.DateTimeFormat().resolvedOptions().timeZone === 'UTC' && // The timezone is set to UTC
57 !Object.prototype.hasOwnProperty.call(window, 'Components') && // It strips out Components
58 navigator.plugins.length === 0; // 0 plugins are returned
59 // Starting from tor browser 11, tor is based on firefox 91
60 return isMaybeTor && !!ua.browser.major && +ua.browser.major < 91;
62 export const isChrome = () => ua.browser.name === 'Chrome';
63 export const isChromiumBased = () => 'chrome' in window;
64 export const isJSDom = () => navigator.userAgent.includes('jsdom');
65 export const isMac = () => ua.os.name === 'Mac OS';
66 export const isWindows = () => ua.os.name === 'Windows';
67 export const isLinux = () => ua.ua.match(/(L|l)inux/);
68 export const hasTouch = typeof document === 'undefined' ? false : 'ontouchstart' in document.documentElement;
69 export const hasCookie = () => navigator.cookieEnabled;
70 export const getBrowser = () => ua.browser;
71 export const getDevice = () => ua.device;
72 export const isMobile = () => {
73 const { type } = getDevice();
74 return type === 'mobile';
76 export const isDesktop = () => {
77 const { type } = getDevice();
80 export const getIsIframe = () => window.self !== window.top;
82 export const metaKey = isMac() ? '⌘' : 'Ctrl';
83 export const altKey = isMac() ? 'Option' : 'Alt';
84 export const shiftKey = 'Shift';
86 export const getActiveXObject = (name: string) => {
89 return new ActiveXObject(name);
90 } catch (error: any) {
95 export const isIos = () =>
96 // https://racase.com.np/javascript-how-to-detect-if-device-is-ios/
97 (/iPad|iPhone|iPod/.test(navigator.userAgent) && !(window as any).MSStream) ||
98 ['iPad Simulator', 'iPhone Simulator', 'iPod Simulator', 'iPad', 'iPhone', 'iPod'].includes(navigator.platform) ||
99 // iPad on iOS 13 detection
100 (navigator.userAgent.includes('Mac') && 'ontouchend' in document);
101 export const isIpad = () => isSafari() && navigator.maxTouchPoints && navigator.maxTouchPoints > 2;
102 export const hasAcrobatInstalled = () => !!(getActiveXObject('AcroPDF.PDF') || getActiveXObject('PDF.PdfCtrl'));
103 export const hasPDFSupport = () => {
104 // mimeTypes is deprecated in favor of pdfViewerEnabled.
106 (navigator.mimeTypes && 'application/pdf' in navigator.mimeTypes) ||
107 navigator.pdfViewerEnabled ||
108 (isFirefox() && isDesktop()) ||
110 hasAcrobatInstalled()
113 export const replaceUrl = (url = '') => document.location.replace(url);
114 export const redirectTo = (url = '') => replaceUrl(`${document.location.origin}${url}`);
117 * Detect browser requiring direct action
118 * Like opening a new tab
120 export const requireDirectAction = () => isSafari() || isFirefox() || isEdge();
123 * Open an URL inside a new tab/window and remove the referrer
124 * @links { https://mathiasbynens.github.io/rel-noopener/}
126 export const openNewTab = (url: string) => {
128 const otherWindow = window.open();
132 otherWindow.opener = null;
133 otherWindow.location.href = url;
136 const anchor = document.createElement('a');
138 anchor.setAttribute('rel', 'noreferrer nofollow noopener');
139 anchor.setAttribute('target', '_blank');
142 return anchor.click();
145 // On safari < 14 the Version cookie is sent for index.html file but
146 // not sent for asset requests due to https://bugs.webkit.org/show_bug.cgi?id=171566
147 export const doesNotSupportEarlyAccessVersion = () => isSafari() && Number(ua.browser.major) < 14;
149 export const getHasWebAuthnSupport = () => {
151 return !!navigator?.credentials?.create;
157 export const browserAPI = (globalThis as any)?.browser ?? (globalThis as any)?.chrome;