Cleanup - unused files / unused exports / duplicate exports
[ProtonMail-WebClient.git] / packages / shared / lib / helpers / browser.ts
blob07fe61f75fe489e14cb880ecdfee8de3eb4573aa
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;
9 };
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);
17     } else {
18         const dummy = document.createElement('textarea');
19         target.appendChild(dummy);
20         dummy.value = text;
21         dummy.select();
22         document.execCommand('copy');
23         target.removeChild(dummy);
24     }
25     oldActiveElement?.focus?.();
28 export const copyDomToClipboard = async (element: HTMLElement) => {
29     if (!element || !(element instanceof HTMLElement)) {
30         console.error('Invalid element provided');
31         return;
32     }
34     /** Try to use the Clipboard API if available */
35     /*
36      * Commenting the clipboard API solution for now because of 2 "issues"
37      * 1- The current solution is copying HTML only. However, we would need to copy plaintext too for editors that are not supporting HTML
38      * 2- When using the clipboard API, the content is sanitized, meaning that some parts of the content are dropped, such as classes
39      */
40     // if (navigator.clipboard && typeof navigator.clipboard.write === 'function') {
41     //     const type = 'text/html';
42     //     const blob = new Blob([element.innerHTML], { type });
43     //     const data = [new ClipboardItem({ [type]: blob })];
44     //     await navigator.clipboard.write(data);
45     // } else {
46     const activeElement = document.activeElement;
48     // Create an off-screen container for the element's HTML content
49     const tempContainer = document.createElement('div');
50     tempContainer.style.position = 'absolute';
51     tempContainer.style.left = '-9999px';
52     tempContainer.innerHTML = element.innerHTML;
54     document.body.appendChild(tempContainer);
56     const selection = window.getSelection();
57     if (!selection) {
58         console.error('Failed to get selection');
59         document.body.removeChild(tempContainer);
60         return;
61     }
63     // Select the contents of the temporary container
64     const range = document.createRange();
65     range.selectNodeContents(tempContainer);
67     selection.removeAllRanges();
68     selection.addRange(range);
70     // Copy the selected content to the clipboard
71     try {
72         document.execCommand('copy');
73     } catch (err) {
74         console.error('Failed to copy content', err);
75     }
77     // Clean up
78     document.body.removeChild(tempContainer);
79     selection.removeAllRanges();
81     // Restore previous focus
82     if (activeElement instanceof HTMLElement) {
83         activeElement.focus();
84     }
85     // }
88 export const getOS = () => {
89     const { name = 'other', version = '' } = ua.os;
90     return { name, version };
93 export const isIos11 = () => {
94     const { name, version } = getOS();
95     return name.toLowerCase() === 'ios' && parseInt(version, 10) === 11;
98 export const isAndroid = () => {
99     const { name } = getOS();
100     return name.toLowerCase().includes('android');
103 export const isStandaloneApp = () => window.matchMedia('(display-mode: standalone)').matches;
105 export const isDuckDuckGo = () => ua.browser.name === 'DuckDuckGo';
106 export const isSafari = () => ua.browser.name === 'Safari' || ua.browser.name === 'Mobile Safari';
107 export const isSafari11 = () => isSafari() && ua.browser.major === '11';
108 export const isMinimumSafariVersion = (version: number) => isSafari() && Number(ua.browser.version) >= version;
109 export const isSafariMobile = () => ua.browser.name === 'Mobile Safari';
110 export const isIE11 = () => ua.browser.name === 'IE' && ua.browser.major === '11';
111 export const isEdge = () => ua.browser.name === 'Edge';
112 export const isEdgeChromium = () => isEdge() && ua.engine.name === 'Blink';
113 export const isBrave = () => ua.browser.name === 'Brave';
114 export const isFirefox = () => ua.browser.name === 'Firefox';
115 export const isMaybeTorLessThan11 = () => {
116     const isMaybeTor =
117         isFirefox() &&
118         /\.0$/.test(ua.browser.version || '') && // The Firefox minor revision is omitted.
119         Intl.DateTimeFormat().resolvedOptions().timeZone === 'UTC' && // The timezone is set to UTC
120         !Object.prototype.hasOwnProperty.call(window, 'Components') && // It strips out Components
121         navigator.plugins.length === 0; // 0 plugins are returned
122     // Starting from tor browser 11, tor is based on firefox 91
123     return isMaybeTor && !!ua.browser.major && +ua.browser.major < 91;
125 export const isChrome = () => ua.browser.name === 'Chrome';
126 export const isChromiumBased = () => 'chrome' in window;
127 export const isJSDom = () => navigator.userAgent.includes('jsdom');
128 export const isMac = () => ua.os.name === 'Mac OS';
129 export const isWindows = () => ua.os.name === 'Windows';
130 export const isLinux = () => ua.ua.match(/(L|l)inux/);
131 export const hasTouch = typeof document === 'undefined' ? false : 'ontouchstart' in document.documentElement;
132 export const hasCookie = () => navigator.cookieEnabled;
133 export const getOs = () => ua.os;
134 export const getBrowser = () => ua.browser;
135 export const getDevice = () => ua.device;
136 export const isMobile = () => {
137     const { type } = getDevice();
138     return type === 'mobile';
140 export const isDesktop = () => {
141     const { type } = getDevice();
142     return !type;
144 export const getIsIframe = () => window.self !== window.top;
146 export const metaKey = isMac() ? '⌘' : 'Ctrl';
147 export const altKey = isMac() ? 'Option' : 'Alt';
148 export const shiftKey = 'Shift';
150 export const getActiveXObject = (name: string) => {
151     try {
152         // @ts-ignore
153         return new ActiveXObject(name);
154     } catch (error: any) {
155         return undefined;
156     }
159 export const isIos = () =>
160     // https://racase.com.np/javascript-how-to-detect-if-device-is-ios/
161     (/iPad|iPhone|iPod/.test(navigator.userAgent) && !(window as any).MSStream) ||
162     ['iPad Simulator', 'iPhone Simulator', 'iPod Simulator', 'iPad', 'iPhone', 'iPod'].includes(navigator.platform) ||
163     // iPad on iOS 13 detection
164     (navigator.userAgent.includes('Mac') && 'ontouchend' in document);
165 export const isIpad = () => isSafari() && navigator.maxTouchPoints && navigator.maxTouchPoints > 2;
166 export const hasAcrobatInstalled = () => !!(getActiveXObject('AcroPDF.PDF') || getActiveXObject('PDF.PdfCtrl'));
167 export const hasPDFSupport = () => {
168     // mimeTypes is deprecated in favor of pdfViewerEnabled.
169     return (
170         (navigator.mimeTypes && 'application/pdf' in navigator.mimeTypes) ||
171         navigator.pdfViewerEnabled ||
172         (isFirefox() && isDesktop()) ||
173         isIos() ||
174         hasAcrobatInstalled()
175     );
177 export const replaceUrl = (url = '') => document.location.replace(url);
178 export const redirectTo = (url = '') => replaceUrl(`${document.location.origin}${url}`);
181  * Detect browser requiring direct action
182  * Like opening a new tab
183  */
184 export const requireDirectAction = () => isSafari() || isFirefox() || isEdge();
187  * Open an URL inside a new tab/window and remove the referrer
188  * @links { https://mathiasbynens.github.io/rel-noopener/}
189  */
190 export const openNewTab = (url: string) => {
191     if (isIE11()) {
192         const otherWindow = window.open();
193         if (!otherWindow) {
194             return;
195         }
196         otherWindow.opener = null;
197         otherWindow.location.href = url;
198         return;
199     }
200     const anchor = document.createElement('a');
202     anchor.setAttribute('rel', 'noreferrer nofollow noopener');
203     anchor.setAttribute('target', '_blank');
204     anchor.href = url;
206     return anchor.click();
209 // On safari < 14 the Version cookie is sent for index.html file but
210 // not sent for asset requests due to https://bugs.webkit.org/show_bug.cgi?id=171566
211 export const doesNotSupportEarlyAccessVersion = () => isSafari() && Number(ua.browser.major) < 14;
213 export const getHasWebAuthnSupport = () => {
214     try {
215         return !!navigator?.credentials?.create;
216     } catch (e) {
217         return false;
218     }
221 export const browserAPI = (globalThis as any)?.browser ?? (globalThis as any)?.chrome;