import { canUseDOM } from '../other/dom.ts'; export const IPHONE_SAFARI_BOTTOM_BAR = 45; export const IPHONE_X_SAFARI_BOTTOM_BAR = 85; export const IPHONE_KEYBOARD_REJECT_OFFSET = 180; // 44 iPhone, 55 iPad, iPad Pro 69 export const IOS_NO_KEYBOARD_ALLOWED_OFFSET = 70; export function detectIOS(ua?: string): { isIPad: boolean; isIPhone: boolean; isIOS: boolean; isIPadOS: boolean; iosMajor: number; iosMinor: number; isWKWebView: boolean; isScrollBasedViewport: boolean; isIPhoneX: boolean; isIOSChrome: boolean; } { if (!ua) { ua = canUseDOM ? navigator.userAgent : ''; } ua = ua.toLowerCase(); const isIPadOS = checkIPadOS(ua); const isIPad = isIPadOS || ua.includes('ipad'); const isIPhone = !isIPad && ua.search(/iphone|ipod/) !== -1; const isIOS = isIPhone || isIPad; let iosVersion: string[] | typeof isIOS | ReturnType = isIOS && ua.match(/os ([\d_]+) like mac os x/i); let iosMajor = 0; let iosMinor = 0; if (isIPadOS) { iosMajor = 13; iosMinor = 0; } else if (iosVersion) { iosVersion = iosVersion[1].split('_'); iosMajor = +iosVersion[0]; iosMinor = +iosVersion[1]; } iosVersion = null; const isScrollBasedViewport = iosMajor < 13 && !(iosMajor === 11 && iosMinor < 3); const isWKWebView = isIOS && checkWKWebView(ua); let isIPhoneX = false; if (canUseDOM) { isIPhoneX = isIOS && screen.width === 375 && screen.height === 812 && window.devicePixelRatio === 3; } const isIOSChrome = ua.search(/crios/i) !== -1; return { isIPad, isIPhone, isIOS, isIPadOS, iosMajor, iosMinor, isWKWebView, isScrollBasedViewport, isIPhoneX, isIOSChrome, }; } const detect = /*#__PURE__*/ detectIOS(); export const isIPad: boolean = /*#__PURE__*/ (() => detect.isIPad)(); export const isIPhone: boolean = /*#__PURE__*/ (() => detect.isIPhone)(); export const isIOS: boolean = /*#__PURE__*/ (() => detect.isIOS)(); export const isIPadOS: boolean = /*#__PURE__*/ (() => detect.isIPadOS)(); export const iosMajor: number = /*#__PURE__*/ (() => detect.iosMajor)(); export const iosMinor: number = /*#__PURE__*/ (() => detect.iosMinor)(); export const isWKWebView: boolean = /*#__PURE__*/ (() => detect.isWKWebView)(); export const isScrollBasedViewport: boolean = /*#__PURE__*/ (() => detect.isScrollBasedViewport)(); export const isIPhoneX: boolean = /*#__PURE__*/ (() => detect.isIPhoneX)(); export const isIOSChrome: boolean = /*#__PURE__*/ (() => detect.isIOSChrome)(); export function isLandscapePhone(): boolean { return Math.abs(window.orientation) === 90 && !isIPad; } // Reference: // https://stackoverflow.com/questions/28795476/detect-if-page-is-loaded-inside-wkwebview-in-javascript/30495399#30495399 function checkWKWebView(ua: string) { if (!canUseDOM) { return false; } const webkit = (window as any).webkit; if (webkit && webkit.messageHandlers) { return true; } const lte9 = /constructor/i.test(String(window.HTMLElement)); const idb = !!window.indexedDB; if (ua.includes('safari') && ua.includes('version') && !(navigator as any).standalone) { // Safari (WKWebView/Nitro since 6+) } else if ((!idb && lte9) || !(window.statusbar && window.statusbar.visible)) { // UIWebView } else if (!lte9 || idb) { // WKWebView return true; } return false; } /** * В Safari на iPadOS поле User Agent содержит почти такую же информацию, что и в Safari на MacOS. * Из-за чего мы не можем ориентироваться на User Agent. * * Вместо этого мы пробуем определять есть ли событие 'ontouchend' в объекте document. * * см. https://developer.apple.com/forums/thread/119186?answerId=705140022#705140022 */ export function checkIPadOS(ua: string): boolean { if (!canUseDOM) { return false; } const isNotIOS = !/ipPad|iPhone|iPod/i.test(ua); const isMacOS = /Mac OS/i.test(ua); return isNotIOS && isMacOS && 'ontouchend' in document; }