// The rewrite rules that send a shared link here catch the original link on // the way back out too, so in Safari "Open on " lands back on this // page. Handing the address to a different browser is the way out, and the // only way to do that from a web page is that browser's own URL scheme -- // which every one of them spells differently. Some swap the scheme, some // prefix it, some take the whole address as a query parameter. /** http -> googlechrome://, https -> googlechromes:// */ const chrome = (url) => url.replace(/^http(s?):/, 'googlechrome$1:'); // Edge keeps the original scheme either way, but on macOS it stays in the // address and the scheme is prefixed, while on iOS it is folded into the // scheme itself. const edgeMacos = (url) => `microsoft-edge:${url}`; const edgeIos = (url) => url.replace(/^http(s?):/, 'microsoft-edge-http$1:'); /** Firefox and Orion take the address as a parameter instead. */ const openUrl = (scheme) => (url) => `${scheme}://open-url?url=${encodeURIComponent(url)}`; // `null` means the browser is there but ships no scheme to reach it on that // system, so there is nothing to offer beyond the plain link. export const BROWSERS = [ { id: 'default', label: 'Default browser', macos: null, ios: null }, { id: 'chrome', label: 'Chrome', macos: chrome, ios: chrome }, { id: 'edge', label: 'Edge', macos: edgeMacos, ios: edgeIos }, { id: 'firefox', label: 'Firefox', macos: null, ios: openUrl('firefox') }, { id: 'orion', label: 'Orion', macos: openUrl('orion'), ios: openUrl('orion') }, ]; /** * iPadOS calls itself MacIntel, so the platform string alone cannot tell the * two apart; the touch points can. Everything else gets the desktop table -- * the only desktop this is ever opened on is a Mac. * * @param {{ platform?: string, maxTouchPoints?: number }} [nav] */ export function detectOs(nav = globalThis.navigator) { const platform = nav?.platform ?? ''; if (/^iP(hone|ad|od)/.test(platform)) return 'ios'; if (platform === 'MacIntel' && (nav?.maxTouchPoints ?? 0) > 1) return 'ios'; return 'macos'; } /** The browsers worth offering here: the default, plus the ones this system * actually has a scheme for. */ export function browsersFor(os) { return BROWSERS.filter((browser) => browser.id === 'default' || browser[os]); } function browserById(id) { return BROWSERS.find((browser) => browser.id === id); } /** * The address to open. A browser with no scheme on this system, an unknown * id, or anything that is not an ordinary web link is handed back untouched: * a dead custom scheme opens nothing at all, which is worse than the plain * link opening in the wrong browser. */ export function openUrlFor(url, browserId, os) { const rewrite = browserById(browserId)?.[os]; if (!rewrite || !/^https?:\/\//i.test(url)) return url; return rewrite(url); }