1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
import { createClientLink } from './scheme';
import type { Platform } from '../platform';
/**
* Navigator for older Microsoft (MS) browsers like Internet Explorer.
*/
type MSNavigator = Navigator & {
msLaunchUri: (
href: string | URL,
successCallback: () => void,
failureCallback: () => void,
) => void;
};
/**
* Check if the given value is an MSNavigator.
*/
function isMSNavigator(value: Partial<MSNavigator>): value is MSNavigator {
return typeof value?.msLaunchUri === 'function';
}
/**
* Callback for client launches.
*/
export type LaunchCallback = (result: {
link: URL;
success: boolean;
}) => void | Promise<void>;
/**
* Attempt to launch the native client for the given Web URL.
*/
export function launchClient(
url: string | URL,
platform: Platform,
callback: LaunchCallback = () => {},
): void {
const { window, browser, os } = platform;
/** URL for opening the native application */
const link = createClientLink(url, { platform });
// macOS Safari
if (os.isMacOS && browser.isSafari) {
launchOnMacOS(link, platform, callback);
}
// Proprietary msLaunchUri method (IE 10+ on Windows 8+)
else if (isMSNavigator(platform.navigator)) {
platform.navigator.msLaunchUri(
String(link),
() => callback({ link, success: true }),
() => callback({ link, success: false }),
);
}
// Other platforms
else {
try {
// on iOS, Windows and Android simply opening the href works
window!.top!.window.location.href = String(link);
callback({ link, success: true });
} catch (e) {
// we know this is NOT installed
callback({ link, success: false });
}
}
}
function launchOnMacOS(
link: URL,
platform: Platform,
callback: LaunchCallback,
): void {
const { window } = platform;
if (typeof window === 'undefined') {
callback({ link, success: false });
return;
}
/** Timer for blur fallback */
let timer: number;
/** IFrame reference for opening the client link */
let iframe: HTMLIFrameElement | undefined;
/** Cleanup function run after the client launch has been initiated */
function finalize() {
clearTimeout(timer);
window!.removeEventListener('blur', finalize);
if (iframe !== undefined) {
window!.document.body.removeChild(iframe);
}
callback({ link, success: true });
}
// Add an iFrame window to the current document to open the URL
iframe = window.document.createElement('iframe');
iframe.id = 'launch-client-opener';
iframe.style.display = 'none';
window.document.body.appendChild(iframe);
// Redirect the iFrame to the client link to trigger it to open
iframe.contentWindow!.location.href = String(link);
// Wait a tiny amount of time for the client launch to appear
window.addEventListener('blur', finalize);
timer = setTimeout(finalize, 50) as unknown as number;
}
|