platform.ts
1 import { 2 parseUserAgent, 3 flagsExtension, 4 compareExtension, 5 } from '@amp/runtime-detect'; 6 import { launchClient, type LaunchCallback } from './launch/launch-client'; 7 8 type NavigatorLike = { 9 userAgent: string; 10 maxTouchPoints?: number; 11 }; 12 13 /** 14 * Detect a Platform descriptor from the browsers user agent. 15 */ 16 function detectDescriptor(options?: { 17 window?: Window; 18 navigator?: NavigatorLike; 19 }) { 20 const defaultNavigator: NavigatorLike = 21 typeof options?.window?.navigator !== 'undefined' 22 ? options.window.navigator 23 : { 24 userAgent: '', 25 maxTouchPoints: 0, 26 }; 27 28 return parseUserAgent(options?.navigator ?? defaultNavigator, { 29 extensions: [flagsExtension, compareExtension], 30 }); 31 } 32 33 export type PlatformDescriptor = ReturnType<typeof detectDescriptor>; 34 35 export class Platform { 36 static detect( 37 this: typeof Platform, 38 options?: { window?: Window; navigator?: NavigatorLike }, 39 ) { 40 const window = options?.window ?? globalThis?.window; 41 return new this({ 42 window: window, 43 descriptor: detectDescriptor({ 44 window: window, 45 navigator: options?.navigator, 46 }), 47 }); 48 } 49 50 /** 51 * Descriptor from detecting platform data. 52 */ 53 readonly descriptor: PlatformDescriptor; 54 55 /** 56 * Navigator value used to create the platform descriptor. 57 */ 58 readonly navigator: NavigatorLike; 59 60 /** 61 * Reference to the platform Window object. This might be `undefined` in some 62 * environments. 63 */ 64 readonly window: Window | undefined; 65 66 /** 67 * User Agent string the platform descriptor was parsed from. 68 */ 69 readonly ua: string; 70 71 /** 72 * Browser descriptor for the Platform. 73 */ 74 readonly browser: PlatformDescriptor['browser']; 75 76 /** 77 * Browser Engine descriptor for the Platform. 78 */ 79 readonly engine: PlatformDescriptor['engine']; 80 81 /** 82 * Operating System descriptor for the Platform. 83 */ 84 readonly os: PlatformDescriptor['os']; 85 86 constructor(config: { 87 descriptor: PlatformDescriptor; 88 window?: Window; 89 navigator?: NavigatorLike; 90 }) { 91 const { descriptor } = config; 92 this.descriptor = descriptor; 93 this.navigator = config.navigator ?? descriptor.navigator; 94 this.window = config.window; 95 96 this.ua = descriptor.ua; 97 this.browser = descriptor.browser; 98 this.engine = descriptor.engine; 99 this.os = descriptor.os; 100 } 101 102 /** 103 * Check if Apple native applications can be opened on the Platform. 104 */ 105 canOpenNative(): boolean { 106 return this.ismacOS() || this.isiOS(); 107 } 108 109 /** 110 * Check if the Platform is running a mobile browser. 111 */ 112 isMobile(): boolean { 113 return this.browser.isMobile; 114 } 115 116 /** 117 * Check if the Platform registers as running the Android operating system. 118 */ 119 isAndroid(): boolean { 120 return this.os.isAndroid; 121 } 122 123 /** 124 * Check if the Platform registers as running the iOS operating system. 125 */ 126 isiOS(): boolean { 127 return this.os.isIOS; 128 } 129 130 /** 131 * Check if the Platform registers as running the iPadOS operating system. 132 */ 133 isiPadOS(): boolean { 134 return this.os.isIPadOS; 135 } 136 137 /** 138 * Check if the Platform registers as running the macOS operating system. 139 */ 140 ismacOS(): boolean { 141 return this.os.isMacOS; 142 } 143 144 /** 145 * Check if the Platform registers as running the Windows operating system. 146 */ 147 isWindows(): boolean { 148 return this.os.isWindows; 149 } 150 151 /** 152 * Check if the Platform registers as running a Linux operating system. 153 */ 154 isLinux(): boolean { 155 return this.os.isLinux; 156 } 157 158 /** 159 * Check if the Platform is running the Apple Safari browser. 160 */ 161 isSafari(): boolean { 162 return this.browser.isSafari; 163 } 164 165 /** 166 * Check if the Platform is running the Google Chrome browser. 167 */ 168 isChrome(): boolean { 169 return this.browser.isChrome; 170 } 171 172 /** 173 * Check if the Platform is running the Mozilla Firefox browser. 174 */ 175 isFirefox(): boolean { 176 return this.browser.isFirefox; 177 } 178 179 /** 180 * Check if the Platform is running the Microsoft Edge browser. 181 */ 182 isEdge(): boolean { 183 return this.browser.isEdge; 184 } 185 186 /** 187 * Get name for the Platform browser. 188 * @deprecated Use `platform.browser.name` directly 189 */ 190 clientName(): string { 191 return this.browser.name[0].toUpperCase() + this.browser.name.slice(1); 192 } 193 194 /** 195 * Get the Platform browser major version number. 196 * @deprecated Use `platform.browser.major` directly 197 */ 198 majorVersion(): number { 199 return this.browser.major ?? 0; 200 } 201 202 /** 203 * Get the Platform browser minor version number. 204 * @deprecated Use `platform.browser.minor` directly 205 */ 206 minorVersion(): number { 207 return this.browser.minor ?? 0; 208 } 209 210 /** 211 * Get the name for the Platform operating system. 212 * @deprecated Use `platform.os.name` directly 213 */ 214 osName(): string { 215 return this.os.name; 216 } 217 218 /** 219 * Attempt to launch a native client for the given web URL. 220 * 221 * The callback is called with a report if the attempt was successful. 222 * 223 * @example 224 * ```javascript 225 * platform.launchClient( 226 * 'https://music.apple.com/browse', 227 * function ({ link, success }) { 228 * if (success) { 229 * console.log(`Opened client with ${link}`); 230 * } else { 231 * console.log(`Failed to open client with ${link}`); 232 * } 233 * } 234 * ); 235 * ``` 236 */ 237 launchClient(url: string, callback?: LaunchCallback): void { 238 launchClient(url, this, callback); 239 } 240 241 /** 242 * Check if the platform has full support for playing encrypted HLS content. 243 */ 244 hasEncryptedPlaybackSupport(): boolean { 245 return !this.os.isIOS || this.os.gte('17.5'); 246 } 247 } 248 249 export const platform = Platform.detect();