UNPKG

@sberid/js-sdk

Version:

Javascript SDK для партнеров Сбер ID, упрощающая подключение SberbankID на сайте.

312 lines (260 loc) 10.7 kB
import {BrowserName} from 'constants/common'; import {Browser} from './browser'; import { AbstractBrowserItemModeDetect, AbstractBrowserModeDetector, BrowserMode, } from './interfaces'; type WebkitNavigatorExtended = Navigator & {webkitTemporaryStorage?: any}; type SafariWindowExtended = Window & typeof globalThis & { safariIncognito?: any; openDatabase?: (a: any, b: any, c: any, d: any) => void; }; type safariBlob = { size: number; payload: string; lastModifiedDate: Date; name: string; }; export class BrowserModeDetector implements AbstractBrowserModeDetector { browserMode: BrowserMode = 'unknown'; retry(ready: () => boolean): Promise<boolean> { return new Promise((resolve) => { let iteration = 0; const maxRetry = 50; const interval = 10; let isTimeout = false; const id = window.setInterval(() => { if (ready()) { window.clearInterval(id); resolve(isTimeout); } if (iteration++ > maxRetry) { window.clearInterval(id); isTimeout = true; resolve(isTimeout); } }, interval); }); } run(): Promise<BrowserMode> { return new Promise((resolve) => { new BrowserFactory().browser(this).detectBrowsingMode(); this.retry(() => this.browserMode !== 'unknown').then(() => { resolve(this.browserMode); }); }); } } export class BrowserFactory { private browserData: any = undefined; browser(BrowserModeDetector: AbstractBrowserModeDetector): any { if (this.browserData && typeof this.browserData === 'object') { return this.browserData; } this.browserData = this.resolve(BrowserModeDetector); return this.browserData; } private resolve(BrowserModeDetector: AbstractBrowserModeDetector) { const browserName = Browser.getParser(window.navigator.userAgent).getBrowserName(); switch (browserName) { case BrowserName.SAFARI: return new SafariBrowser(BrowserModeDetector); case BrowserName.FIREFOX: return new FirefoxBrowser(BrowserModeDetector); case BrowserName.INTERNET_EXPLORER: return new IE10EdgeBrowser(BrowserModeDetector); default: if ('webkitRequestFileSystem' in window || 'requestFileSystem' in window) { return new WebkitBrowser(BrowserModeDetector); } else if (window.PointerEvent || 'MSPointerEvent' in window) { return new IE10EdgeBrowser(BrowserModeDetector); } return new OtherBrowser(BrowserModeDetector); } } } export class WebkitBrowser implements AbstractBrowserItemModeDetect { private navigator: WebkitNavigatorExtended = navigator; private readonly STORAGE_QUOTA_LIMIT = 120000000; private readonly TEMP_STORAGE_QUOTA_LIMIT = 110000000; BrowserModeDetector: AbstractBrowserModeDetector; constructor(BrowserModeDetector: AbstractBrowserModeDetector) { this.BrowserModeDetector = BrowserModeDetector; } private callbackWhenWebkitTemporaryStorageQuotaNotLimited() { this.BrowserModeDetector.browserMode = 'normal'; return; } private callbackWhenWebkitTemporaryStorageQuotaLimited() { this.BrowserModeDetector.browserMode = 'incognito'; return; } private callbackWhenWebkitStorageQuotaLimited() { this.BrowserModeDetector.browserMode = 'incognito'; return; } private checkWebkitTemporaryStorageQuota() { if ( typeof this.navigator.webkitTemporaryStorage !== 'undefined' && typeof this.navigator.webkitTemporaryStorage.queryUsageAndQuota !== 'undefined' ) { this.navigator.webkitTemporaryStorage.queryUsageAndQuota( (usedBytes: number, grantedBytes: number): void => { if (grantedBytes < this.TEMP_STORAGE_QUOTA_LIMIT) { this.callbackWhenWebkitTemporaryStorageQuotaLimited(); } else { this.callbackWhenWebkitTemporaryStorageQuotaNotLimited(); } }, () => { this.callbackWhenWebkitTemporaryStorageQuotaNotLimited(); }, ); } else { this.callbackWhenWebkitTemporaryStorageQuotaNotLimited(); } } private checkStorageQuota() { if ( typeof navigator.storage !== 'undefined' && typeof navigator.storage.estimate !== 'undefined' ) { navigator.storage.estimate().then((estimate) => { if (estimate?.quota && estimate?.quota < this.STORAGE_QUOTA_LIMIT) { this.callbackWhenWebkitStorageQuotaLimited(); } else { this.callbackWhenWebkitStorageQuotaNotLimited(); } }); } else { this.callbackWhenWebkitStorageQuotaNotLimited(); } } private callbackWhenWebkitStorageQuotaNotLimited() { this.checkWebkitTemporaryStorageQuota(); return; } detectBrowsingMode(): void { this.checkStorageQuota(); } } export class FirefoxBrowser implements AbstractBrowserItemModeDetect { private db!: IDBOpenDBRequest; BrowserModeDetector: AbstractBrowserModeDetector; constructor(BrowserModeDetector: AbstractBrowserModeDetector) { this.BrowserModeDetector = BrowserModeDetector; } private callbackWhenIndexedDBNotWorking(): void { // On Firefox ESR versions, actually IndexedDB don't works. this.BrowserModeDetector.browserMode = 'incognito'; } private callbackWhenIndexedDBWorking(): void { if (this.BrowserModeDetector.browserMode === 'unknown') { this.BrowserModeDetector.retry(() => this.db.readyState === 'done').then( (isTimeout: boolean) => { if (isTimeout) { return this.callbackWhenIndexedDBNotWorking(); } if (this.db.result) { this.BrowserModeDetector.browserMode = 'normal'; } }, ); } } detectBrowsingMode(): void { this.db = indexedDB.open('i'); this.db.onsuccess = this.callbackWhenIndexedDBWorking.bind(this); this.db.onerror = this.callbackWhenIndexedDBNotWorking.bind(this); } } export class IE10EdgeBrowser implements AbstractBrowserItemModeDetect { BrowserModeDetector: AbstractBrowserModeDetector; constructor(BrowserModeDetector: AbstractBrowserModeDetector) { this.BrowserModeDetector = BrowserModeDetector; } detectBrowsingMode(): void { this.BrowserModeDetector.browserMode = 'normal'; try { if (!window.indexedDB) { this.BrowserModeDetector.browserMode = 'incognito'; } } catch (e) { this.BrowserModeDetector.browserMode = 'incognito'; } } } export class SafariBrowser implements AbstractBrowserItemModeDetect { BrowserModeDetector: AbstractBrowserModeDetector; constructor(BrowserModeDetector: AbstractBrowserModeDetector) { this.BrowserModeDetector = BrowserModeDetector; } private tryFillLocalStorage(chunkSize: number): boolean { const size = chunkSize / 4; const content = new Array(size + 1).join('aあ'); const blob: safariBlob = { size: chunkSize, payload: content, lastModifiedDate: new Date(), name: ~~(Math.random() * 100000) + 100000 + '.txt', }; try { localStorage.setItem(blob.name, JSON.stringify(blob)); } catch (e) { try { localStorage.removeItem(blob.name); } catch (e) {} return false; } try { localStorage.removeItem(blob.name); } catch (e) {} return true; } detectBrowsingMode(): void { const extendWindow: SafariWindowExtended = window; const browser = Browser.getParser(window.navigator.userAgent); const osMajorVersion = parseInt((<string>browser.getOSVersion())?.split('.')[0], 10) || 0; const browserMajorVersion = parseInt((<string>browser.getBrowserVersion())?.split('.')[0], 10) || 0; const osName = browser.getOSName(true); if (extendWindow.safariIncognito || !navigator.cookieEnabled) { this.BrowserModeDetector.browserMode = 'incognito'; return; } try { extendWindow.openDatabase && extendWindow.openDatabase(null, null, null, null); extendWindow.localStorage.setItem('test', '1'); } catch (e) { this.BrowserModeDetector.browserMode = 'incognito'; } if (this.BrowserModeDetector.browserMode === 'unknown') { extendWindow.localStorage.removeItem('test'); if ( (osName === 'ios' && osMajorVersion >= 14) || (osName === 'macos' && browserMajorVersion >= 14) || (osName === 'ios' && browserMajorVersion === 0) ) { this.BrowserModeDetector.browserMode = 'normal'; } else { const LOCAL_STORAGE_DATA_SIZE = 1024 * 1024 * 5; // 5MB if (this.tryFillLocalStorage(LOCAL_STORAGE_DATA_SIZE)) { this.BrowserModeDetector.browserMode = 'incognito'; } else { this.BrowserModeDetector.browserMode = 'normal'; } } } } } export class OtherBrowser implements AbstractBrowserItemModeDetect { BrowserModeDetector: AbstractBrowserModeDetector; constructor(BrowserModeDetector: AbstractBrowserModeDetector) { this.BrowserModeDetector = BrowserModeDetector; } detectBrowsingMode(): void { this.BrowserModeDetector.browserMode = 'normal'; } }