web-launch-kit 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +41 -7
- package/README.md +45 -7
- package/dist/index.d.ts +7 -1
- package/dist/launch-kit.cjs +66 -3
- package/dist/launch-kit.cjs.map +1 -1
- package/dist/launch-kit.min.cjs +1 -1
- package/dist/launch-kit.min.cjs.map +1 -1
- package/dist/launch-kit.min.mjs +1 -1
- package/dist/launch-kit.min.mjs.map +1 -1
- package/dist/launch-kit.mjs +66 -3
- package/dist/launch-kit.mjs.map +1 -1
- package/dist/launch-kit.umd.js +66 -3
- package/dist/launch-kit.umd.js.map +1 -1
- package/dist/launch-kit.umd.min.js +1 -1
- package/dist/launch-kit.umd.min.js.map +1 -1
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -33,7 +33,8 @@ npm install web-launch-kit
|
|
|
33
33
|
| `LaunchKit.map(options?)` | `Promise<AppOpenedBy>` | 검색어·좌표·경로로 지도 열기. OS별 네이티브 앱과 Google Maps 웹 폴백 |
|
|
34
34
|
| `LaunchKit.filepicker(options?)` | `Promise<File[]>` | 파일 또는 디렉터리 선택 (File System Access API, input 폴백) |
|
|
35
35
|
| `LaunchKit.setting(type?)` | `Promise<void>` | 지원되는 환경에서 시스템 설정 화면 열기 |
|
|
36
|
-
| `LaunchKit.
|
|
36
|
+
| `LaunchKit.escapeWebview(options?)` | `Promise<void>` | URL(기본값은 현재 페이지)을 외부 브라우저에서 다시 열어 인앱 웹뷰에서 탈출 |
|
|
37
|
+
| `LaunchKit.utils` | object | `canOpenIntent` / `canOpenUniversal` / `canOpenSetting` / `canEscapeWebview` getter와 비동기 `getTrackId` / `getProductId` |
|
|
37
38
|
|
|
38
39
|
`AppOpenedBy`는 `"scheme"`, `"universal"`, `"intent"`, `"fallback"`, `"store"` 중 하나입니다.
|
|
39
40
|
|
|
@@ -82,8 +83,8 @@ LaunchKit.telephone({ to: '+821012345678' })
|
|
|
82
83
|
|
|
83
84
|
싱글톤의 형태는 `LaunchKitInstance`로 export되고, 모든 옵션 객체에 이름이 붙어 있습니다 —
|
|
84
85
|
`AppOpenOptions`, `AndroidAppInfo`, `IOSAppInfo`, `WindowsAppInfo`, `MacOSAppInfo`,
|
|
85
|
-
`TelephoneOptions`, `MessageOptions`, `MailOptions`, `MapOptions`, `FilepickerOptions
|
|
86
|
-
`SettingType`은 값 export이고 `AppOpenedBy`는 타입입니다.
|
|
86
|
+
`TelephoneOptions`, `MessageOptions`, `MailOptions`, `MapOptions`, `FilepickerOptions`,
|
|
87
|
+
`EscapeWebviewOptions`. `SettingType`은 값 export이고 `AppOpenedBy`는 타입입니다.
|
|
87
88
|
|
|
88
89
|
```ts
|
|
89
90
|
import LaunchKit, {
|
|
@@ -119,10 +120,10 @@ flowchart TD
|
|
|
119
120
|
A1{"intent given, but scheme /<br/>packageName / fallback missing?"}
|
|
120
121
|
A1 -->|yes| A2["parseIntentURL:<br/>derive scheme · packageName · fallback"]
|
|
121
122
|
A1 -->|no| A3
|
|
122
|
-
A2 --> A3{"scheme given
|
|
123
|
+
A2 --> A3{"allowIntent, and scheme given<br/>but intent missing?"}
|
|
123
124
|
A3 -->|yes| A4["createIntentURL:<br/>scheme + packageName + fallback"]
|
|
124
125
|
A3 -->|no| A5
|
|
125
|
-
A4 --> A5["Priority list:<br/>intent (if canOpenIntent) ⭢ scheme (if canOpenScheme)<br/>⭢ fallback ⭢ app store (if canOpenScheme) ⭢ web store"]
|
|
126
|
+
A4 --> A5["Priority list:<br/>intent (if allowIntent and canOpenIntent) ⭢ scheme (if canOpenScheme)<br/>⭢ fallback ⭢ app store (if canOpenScheme) ⭢ web store"]
|
|
126
127
|
end
|
|
127
128
|
|
|
128
129
|
subgraph IOS["ios · resolveOptions"]
|
|
@@ -202,8 +203,8 @@ const openedBy = await LaunchKit.app({
|
|
|
202
203
|
console.log(openedBy) // "universal" | "scheme" | "intent" | "fallback" | "store"
|
|
203
204
|
```
|
|
204
205
|
|
|
205
|
-
플랫폼별 필드: 안드로이드는 `intent` / `scheme` / `packageName` / `fallback`
|
|
206
|
-
서로에게서 유도됩니다), iOS는 `universal` / `scheme` / `bundleId` / `trackId`, Windows는
|
|
206
|
+
플랫폼별 필드: 안드로이드는 `intent` / `scheme` / `packageName` / `fallback` /
|
|
207
|
+
`allowIntent`(scheme ⇄ intent는 서로에게서 유도됩니다), iOS는 `universal` / `scheme` / `bundleId` / `trackId`, Windows는
|
|
207
208
|
`scheme` / `packageFamilyName` / `productId`, macOS는 `scheme` / `bundleId` / `trackId`를
|
|
208
209
|
받습니다. 모두 `fallback`, `timeout`, `allowAppStore`, `allowWebStore`를 함께 받습니다.
|
|
209
210
|
|
|
@@ -313,6 +314,32 @@ if (LaunchKit.utils.canOpenSetting) {
|
|
|
313
314
|
}
|
|
314
315
|
```
|
|
315
316
|
|
|
317
|
+
## 인앱 브라우저 탈출
|
|
318
|
+
|
|
319
|
+
`escapeWebview()`는 URL — 기본값은 현재 페이지 — 을 외부 기본 브라우저에서 다시 열어,
|
|
320
|
+
페이지가 갇혀 있는 인앱 웹뷰에서 탈출합니다.
|
|
321
|
+
|
|
322
|
+
```js
|
|
323
|
+
import LaunchKit from 'web-launch-kit'
|
|
324
|
+
|
|
325
|
+
if (LaunchKit.utils.canEscapeWebview) {
|
|
326
|
+
await LaunchKit.escapeWebview()
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// 현재 페이지 대신 특정 URL을 내보낼 수도 있습니다
|
|
330
|
+
await LaunchKit.escapeWebview({ url: 'https://example.com/checkout' })
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
경로는 환경에 따라 결정됩니다. 카카오톡은 전용 `kakaotalk://web/openExternal` 스킴을
|
|
334
|
+
제공하고, LINE은 공식 문서화된 `openExternalBrowser=1` 쿼리 파라미터를 지원합니다 —
|
|
335
|
+
둘 다 안드로이드와 iOS에서 동작합니다. 그 외 웹뷰는 OS 경로로 폴백합니다. 안드로이드는
|
|
336
|
+
Chrome을 고정한 `intent://` URL(원본 URL이 `S.browser_fallback_url`로 실려서 Chrome이
|
|
337
|
+
없는 기기는 제자리에 머뭅니다), iOS는 비공개 `x-safari-` 스킴입니다 — iOS 15와 17+에서
|
|
338
|
+
동작하고 16에서는 동작하지 않으며, 기본 브라우저 설정과 무관하게 항상 Safari를 열고,
|
|
339
|
+
Meta 계열 웹뷰(Facebook, Instagram) 안에서는 차단됩니다. http(s) URL만 다시 열 수
|
|
340
|
+
있습니다. 웹뷰 밖이거나 사용 가능한 경로가 없으면(완전 차단 웹뷰, iOS 16, 데스크톱)
|
|
341
|
+
프로미스가 reject됩니다 — 버튼 노출은 `utils.canEscapeWebview`로 게이트하세요.
|
|
342
|
+
|
|
316
343
|
---
|
|
317
344
|
|
|
318
345
|
## 참고
|
|
@@ -333,6 +360,13 @@ if (LaunchKit.utils.canOpenSetting) {
|
|
|
333
360
|
그리고 파트너가 아닌 웨이보) 안에서는 커스텀 스킴·`intent://`·`market://` 같은 스토어 스킴이
|
|
334
361
|
타임아웃을 소진하지 않고 미리 제외됩니다. 그래서 체인이 곧바로 `fallback` / 웹스토어로
|
|
335
362
|
떨어집니다. 화이트리스트에 등록된 파트너 앱은 `assumeAllowedInApp: true`로 되돌립니다.
|
|
363
|
+
- **`allowIntent: false`는 안드로이드의 `intent://` 경로를 제외합니다.** 유도된 `intent://`에는
|
|
364
|
+
`S.browser_fallback_url`이 실립니다 — `fallback`에 URL을 주면 그 URL, 아니면 `packageName`이
|
|
365
|
+
있을 때 스토어입니다. 그래서 그 이동은 브라우저가 첫 시도 안에서 직접 처리합니다. 앱이 없는
|
|
366
|
+
사용자를 스토어로 보내는 가장 빠른 경로지만, 동시에 첫 시도가 항상 어딘가에 도달하므로 그
|
|
367
|
+
뒤의 순수 스킴은 실행되지 않습니다. `allowIntent: false`로 이 경로를 아예 건너뛰면 순서를
|
|
368
|
+
체인이 결정합니다 — 스킴, `fallback`, 그리고 허용한 스토어 순입니다. 직접 넘긴 `intent`도 같이
|
|
369
|
+
제외되지만, 거기서 파싱한 scheme·`packageName`·`fallback`은 그대로 사용됩니다.
|
|
336
370
|
- **`map()`은 각 후보에 OS별 타임아웃**을 사용하고, `app()`처럼 성공한 경로를 반환합니다.
|
|
337
371
|
- **웹 폴백은 내비게이션 시점에 resolve합니다.** `fallback`이나 웹스토어 후보는 문서 자체를
|
|
338
372
|
교체하므로 기다릴 앱 전환이 없습니다. 이동을 시작하는 즉시 resolve하며, 이동이 성공한 뒤에
|
package/README.md
CHANGED
|
@@ -34,7 +34,8 @@ The bundle is self-contained (OS/locale detection is inlined) — no peer script
|
|
|
34
34
|
| `LaunchKit.map(options?)` | `Promise<AppOpenedBy>` | Open a map by query, coordinate, or directions; native app per-OS with a Google Maps web fallback |
|
|
35
35
|
| `LaunchKit.filepicker(options?)` | `Promise<File[]>` | Pick files or a directory (File System Access API, with input fallback) |
|
|
36
36
|
| `LaunchKit.setting(type?)` | `Promise<void>` | Open a system-settings pane where supported |
|
|
37
|
-
| `LaunchKit.
|
|
37
|
+
| `LaunchKit.escapeWebview(options?)` | `Promise<void>` | Reopen a URL (the current page by default) in the external browser, escaping an in-app webview |
|
|
38
|
+
| `LaunchKit.utils` | object | `canOpenIntent` / `canOpenUniversal` / `canOpenSetting` / `canEscapeWebview` getters, plus async `getTrackId` / `getProductId` |
|
|
38
39
|
|
|
39
40
|
`AppOpenedBy` is one of: `"scheme"`, `"universal"`, `"intent"`, `"fallback"`, `"store"`.
|
|
40
41
|
|
|
@@ -84,7 +85,8 @@ The global `LaunchKit` is a namespace object; the singleton is `LaunchKit.defaul
|
|
|
84
85
|
The singleton shape is exported as `LaunchKitInstance`, and every option object has a
|
|
85
86
|
named type: `AppOpenOptions`, `AndroidAppInfo`, `IOSAppInfo`, `WindowsAppInfo`,
|
|
86
87
|
`MacOSAppInfo`, `TelephoneOptions`, `MessageOptions`, `MailOptions`, `MapOptions`,
|
|
87
|
-
`FilepickerOptions`. `SettingType` is a value export;
|
|
88
|
+
`FilepickerOptions`, `EscapeWebviewOptions`. `SettingType` is a value export;
|
|
89
|
+
`AppOpenedBy` is a type.
|
|
88
90
|
|
|
89
91
|
```ts
|
|
90
92
|
import LaunchKit, {
|
|
@@ -120,10 +122,10 @@ flowchart TD
|
|
|
120
122
|
A1{"intent given, but scheme /<br/>packageName / fallback missing?"}
|
|
121
123
|
A1 -->|yes| A2["parseIntentURL:<br/>derive scheme · packageName · fallback"]
|
|
122
124
|
A1 -->|no| A3
|
|
123
|
-
A2 --> A3{"scheme given
|
|
125
|
+
A2 --> A3{"allowIntent, and scheme given<br/>but intent missing?"}
|
|
124
126
|
A3 -->|yes| A4["createIntentURL:<br/>scheme + packageName + fallback"]
|
|
125
127
|
A3 -->|no| A5
|
|
126
|
-
A4 --> A5["Priority list:<br/>intent (if canOpenIntent) ⭢ scheme (if canOpenScheme)<br/>⭢ fallback ⭢ app store (if canOpenScheme) ⭢ web store"]
|
|
128
|
+
A4 --> A5["Priority list:<br/>intent (if allowIntent and canOpenIntent) ⭢ scheme (if canOpenScheme)<br/>⭢ fallback ⭢ app store (if canOpenScheme) ⭢ web store"]
|
|
127
129
|
end
|
|
128
130
|
|
|
129
131
|
subgraph IOS["ios · resolveOptions"]
|
|
@@ -203,8 +205,8 @@ const openedBy = await LaunchKit.app({
|
|
|
203
205
|
console.log(openedBy) // "universal" | "scheme" | "intent" | "fallback" | "store"
|
|
204
206
|
```
|
|
205
207
|
|
|
206
|
-
Per-platform fields: Android accepts `intent` / `scheme` / `packageName` / `fallback`
|
|
207
|
-
(scheme ⇄ intent are derived from each other); iOS accepts `universal` / `scheme` /
|
|
208
|
+
Per-platform fields: Android accepts `intent` / `scheme` / `packageName` / `fallback` /
|
|
209
|
+
`allowIntent` (scheme ⇄ intent are derived from each other); iOS accepts `universal` / `scheme` /
|
|
208
210
|
`bundleId` / `trackId`; Windows accepts `scheme` / `packageFamilyName` / `productId`;
|
|
209
211
|
macOS accepts `scheme` / `bundleId` / `trackId`. All accept `fallback`, `timeout`,
|
|
210
212
|
`allowAppStore`, `allowWebStore`.
|
|
@@ -216,7 +218,7 @@ UI — there is otherwise nothing on screen for the length of the timeout.
|
|
|
216
218
|
await LaunchKit.app({
|
|
217
219
|
android: { scheme: 'myapp://profile/42', packageName: 'com.example.myapp', allowWebStore: true },
|
|
218
220
|
onAttempt({ by, index, total }) {
|
|
219
|
-
setStatus(by === 'store' ? '
|
|
221
|
+
setStatus(by === 'store' ? 'Go to the store' : 'Opening the app… (' + (index + 1) + '/' + total + ')')
|
|
220
222
|
},
|
|
221
223
|
})
|
|
222
224
|
```
|
|
@@ -316,6 +318,34 @@ if (LaunchKit.utils.canOpenSetting) {
|
|
|
316
318
|
}
|
|
317
319
|
```
|
|
318
320
|
|
|
321
|
+
## Escaping in-app browsers
|
|
322
|
+
|
|
323
|
+
`escapeWebview()` reopens a URL — the current page by default — in the external
|
|
324
|
+
default browser, escaping the in-app webview the page is trapped in.
|
|
325
|
+
|
|
326
|
+
```js
|
|
327
|
+
import LaunchKit from 'web-launch-kit'
|
|
328
|
+
|
|
329
|
+
if (LaunchKit.utils.canEscapeWebview) {
|
|
330
|
+
await LaunchKit.escapeWebview()
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Or send a specific URL out instead of the current page
|
|
334
|
+
await LaunchKit.escapeWebview({ url: 'https://example.com/checkout' })
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
The route depends on the environment. KakaoTalk exposes a dedicated
|
|
338
|
+
`kakaotalk://web/openExternal` scheme and LINE honors the documented
|
|
339
|
+
`openExternalBrowser=1` query parameter — both work on Android and iOS. Other
|
|
340
|
+
webviews fall back to the OS route: a Chrome-pinned `intent://` URL on Android
|
|
341
|
+
(the original URL rides along as `S.browser_fallback_url`, so a Chrome-less
|
|
342
|
+
device stays in place), and the undocumented `x-safari-` scheme on iOS — which
|
|
343
|
+
works on iOS 15 and 17+ but not 16, always opens Safari regardless of the
|
|
344
|
+
default browser, and is blocked inside Meta's webviews (Facebook, Instagram).
|
|
345
|
+
Only http(s) URLs can be reopened. Outside a webview, or where no route is
|
|
346
|
+
available (hard-blocked webviews, iOS 16, desktop), the promise rejects — gate
|
|
347
|
+
the button on `utils.canEscapeWebview`.
|
|
348
|
+
|
|
319
349
|
---
|
|
320
350
|
|
|
321
351
|
## Notes
|
|
@@ -338,6 +368,14 @@ if (LaunchKit.utils.canOpenSetting) {
|
|
|
338
368
|
burning their timeouts, so the chain falls straight through to `fallback` / web store
|
|
339
369
|
(the only routes that work there). Whitelisted partner apps opt back in with
|
|
340
370
|
`assumeAllowedInApp: true`.
|
|
371
|
+
- **`allowIntent: false` drops the Android `intent://` route.** A derived `intent://` carries
|
|
372
|
+
`S.browser_fallback_url` — your `fallback` when you gave a URL, otherwise the store when a
|
|
373
|
+
`packageName` is present — so the browser handles that hop itself during the first attempt.
|
|
374
|
+
That is the fastest route to the store for a user without the app, but it also means the
|
|
375
|
+
first attempt always lands somewhere, and the plain scheme behind it never runs. Set
|
|
376
|
+
`allowIntent: false` to skip the route entirely and let the chain decide the order: scheme,
|
|
377
|
+
then `fallback`, then the store you allowed. An explicit `intent` is gated too, though the
|
|
378
|
+
scheme, `packageName` and `fallback` parsed out of it are still used.
|
|
341
379
|
- **`map()` uses the per-OS timeout** for each candidate, and resolves with the route that
|
|
342
380
|
worked just like `app()`.
|
|
343
381
|
- **Web fallbacks resolve on navigation.** A `fallback` or web-store candidate replaces the
|
package/dist/index.d.ts
CHANGED
|
@@ -36,6 +36,7 @@ declare interface AppInfo {
|
|
|
36
36
|
declare interface AndroidAppInfo extends AppInfo {
|
|
37
37
|
intent?: URLCandidate;
|
|
38
38
|
packageName?: string;
|
|
39
|
+
allowIntent?: boolean;
|
|
39
40
|
assumeAllowedInApp?: boolean;
|
|
40
41
|
}
|
|
41
42
|
declare interface IOSAppInfo extends AppInfo {
|
|
@@ -92,10 +93,14 @@ declare interface MapOptions {
|
|
|
92
93
|
fallback?: URLStringOrFallback;
|
|
93
94
|
onAttempt?: (attempt: LaunchAttempt) => void;
|
|
94
95
|
}
|
|
96
|
+
declare interface EscapeWebviewOptions {
|
|
97
|
+
url?: URLCandidate;
|
|
98
|
+
}
|
|
95
99
|
interface LaunchKitUtils {
|
|
96
100
|
get canOpenIntent(): boolean;
|
|
97
101
|
get canOpenUniversal(): boolean;
|
|
98
102
|
get canOpenSetting(): boolean;
|
|
103
|
+
get canEscapeWebview(): boolean;
|
|
99
104
|
getTrackId(bundleId: string): Promise<string | undefined>;
|
|
100
105
|
getProductId(packageFamilyName: string): Promise<string | undefined>;
|
|
101
106
|
}
|
|
@@ -112,8 +117,9 @@ interface LaunchKitInstance {
|
|
|
112
117
|
map(options?: MapOptions): Promise<AppOpenedBy>;
|
|
113
118
|
filepicker(options?: FilepickerOptions): Promise<File[]>;
|
|
114
119
|
setting(type?: SettingType): Promise<void>;
|
|
120
|
+
escapeWebview(options?: EscapeWebviewOptions): Promise<void>;
|
|
115
121
|
}
|
|
116
122
|
declare const LaunchKit: LaunchKitInstance;
|
|
117
123
|
|
|
118
124
|
export { SettingType, LaunchKit as default };
|
|
119
|
-
export type { AndroidAppInfo, AppInfo, AppOpenOptions, AppOpenedBy, FilepickerOptions, IOSAppInfo, LaunchAttempt, LaunchError, LaunchErrorCode, LaunchKitInstance, MacOSAppInfo, MailOptions, MapOptions, MessageOptions, OpenPickerStartIn, TelephoneOptions, WindowsAppInfo };
|
|
125
|
+
export type { AndroidAppInfo, AppInfo, AppOpenOptions, AppOpenedBy, EscapeWebviewOptions, FilepickerOptions, IOSAppInfo, LaunchAttempt, LaunchError, LaunchErrorCode, LaunchKitInstance, MacOSAppInfo, MailOptions, MapOptions, MessageOptions, OpenPickerStartIn, TelephoneOptions, WindowsAppInfo };
|
package/dist/launch-kit.cjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var version$3 = "0.0.
|
|
5
|
+
var version$3 = "0.0.8";
|
|
6
6
|
var packageJSON$3 = {
|
|
7
7
|
version: version$3};
|
|
8
8
|
|
|
@@ -2347,6 +2347,7 @@ function resolveOptions(option) {
|
|
|
2347
2347
|
var timeout;
|
|
2348
2348
|
var intent;
|
|
2349
2349
|
var packageName;
|
|
2350
|
+
var allowIntent = true;
|
|
2350
2351
|
var bundleId;
|
|
2351
2352
|
var trackId;
|
|
2352
2353
|
var universal;
|
|
@@ -2356,6 +2357,7 @@ function resolveOptions(option) {
|
|
|
2356
2357
|
case 'android':
|
|
2357
2358
|
if (typeof option.android === 'undefined')
|
|
2358
2359
|
return [[], 0];
|
|
2360
|
+
allowIntent = option.android.allowIntent !== false;
|
|
2359
2361
|
intent = stripURL(option.android.intent);
|
|
2360
2362
|
scheme = stripURL(option.android.scheme);
|
|
2361
2363
|
fallback = stripURL(option.android.fallback);
|
|
@@ -2374,11 +2376,11 @@ function resolveOptions(option) {
|
|
|
2374
2376
|
if (typeof parsed.fallback !== 'undefined' && typeof fallback === 'undefined')
|
|
2375
2377
|
fallback = parsed.fallback;
|
|
2376
2378
|
}
|
|
2377
|
-
if (typeof scheme !== 'undefined' && typeof intent === 'undefined')
|
|
2379
|
+
if (allowIntent && typeof scheme !== 'undefined' && typeof intent === 'undefined')
|
|
2378
2380
|
intent = createIntentURL(scheme, packageName, fallback);
|
|
2379
2381
|
if (typeof timeout === 'undefined')
|
|
2380
2382
|
timeout = ANDROID_DEFAULT_TIMEOUT;
|
|
2381
|
-
if (typeof intent !== 'undefined' && canOpenIntent())
|
|
2383
|
+
if (allowIntent && typeof intent !== 'undefined' && canOpenIntent())
|
|
2382
2384
|
resolved.push(['intent', intent]);
|
|
2383
2385
|
if (typeof scheme !== 'undefined' && allowScheme)
|
|
2384
2386
|
resolved.push(['scheme', scheme]);
|
|
@@ -2524,6 +2526,34 @@ function isFullyBlockedInAppBrowser() {
|
|
|
2524
2526
|
function isPartnerAppOnly() {
|
|
2525
2527
|
return /weibo.*weibo__[\d.]+/i.test(PlatformKit.userAgent);
|
|
2526
2528
|
}
|
|
2529
|
+
function canOpenXSafari() {
|
|
2530
|
+
if (PlatformKit.os.name !== 'ios')
|
|
2531
|
+
return false;
|
|
2532
|
+
var version = PlatformKit.os.version;
|
|
2533
|
+
if (PlatformKit.compareVersion(version, '15.0') < 0)
|
|
2534
|
+
return false;
|
|
2535
|
+
if (PlatformKit.compareVersion(version, '16.0') >= 0 && PlatformKit.compareVersion(version, '17.0') < 0)
|
|
2536
|
+
return false;
|
|
2537
|
+
var inAppBrowser = PlatformKit.inAppBrowser;
|
|
2538
|
+
if (inAppBrowser === 'facebook' || inAppBrowser === 'instagram')
|
|
2539
|
+
return false;
|
|
2540
|
+
return canOpenScheme();
|
|
2541
|
+
}
|
|
2542
|
+
function isInWebview() {
|
|
2543
|
+
return PlatformKit.inAppBrowser !== null || PlatformKit.webview;
|
|
2544
|
+
}
|
|
2545
|
+
function canEscapeWebview() {
|
|
2546
|
+
if (!isInWebview())
|
|
2547
|
+
return false;
|
|
2548
|
+
var inAppBrowser = PlatformKit.inAppBrowser;
|
|
2549
|
+
if (inAppBrowser === 'kakaotalk' || inAppBrowser === 'line')
|
|
2550
|
+
return true;
|
|
2551
|
+
if (PlatformKit.os.name === 'android')
|
|
2552
|
+
return canOpenIntent();
|
|
2553
|
+
if (PlatformKit.os.name === 'ios')
|
|
2554
|
+
return canOpenXSafari();
|
|
2555
|
+
return false;
|
|
2556
|
+
}
|
|
2527
2557
|
function canOpenWindowsSetting(version) {
|
|
2528
2558
|
if (version.length === 0)
|
|
2529
2559
|
return false;
|
|
@@ -2621,6 +2651,24 @@ function createWebStoreURL(id, os) {
|
|
|
2621
2651
|
return os;
|
|
2622
2652
|
}
|
|
2623
2653
|
}
|
|
2654
|
+
function appendURLParameter(url, parameter) {
|
|
2655
|
+
var index = url.indexOf('#');
|
|
2656
|
+
var base = index === -1 ? url : url.substring(0, index);
|
|
2657
|
+
var hash = index === -1 ? '' : url.substring(index);
|
|
2658
|
+
return base + (base.indexOf('?') === -1 ? '?' : '&') + parameter + hash;
|
|
2659
|
+
}
|
|
2660
|
+
function createEscapeWebviewURL(url) {
|
|
2661
|
+
var inAppBrowser = PlatformKit.inAppBrowser;
|
|
2662
|
+
if (inAppBrowser === 'kakaotalk')
|
|
2663
|
+
return 'kakaotalk://web/openExternal?url=' + escapeURIComponentString(url);
|
|
2664
|
+
if (inAppBrowser === 'line')
|
|
2665
|
+
return appendURLParameter(url, 'openExternalBrowser=1');
|
|
2666
|
+
if (PlatformKit.os.name === 'android' && canOpenIntent())
|
|
2667
|
+
return createIntentURL(url, 'com.android.chrome', url);
|
|
2668
|
+
if (PlatformKit.os.name === 'ios' && canOpenXSafari())
|
|
2669
|
+
return 'x-safari-' + url;
|
|
2670
|
+
return undefined;
|
|
2671
|
+
}
|
|
2624
2672
|
function escapeURIComponentString(value) {
|
|
2625
2673
|
return encodeURIComponent(value)
|
|
2626
2674
|
.replace(/[!'()*]/g, function (char) {
|
|
@@ -2894,6 +2942,9 @@ var LaunchKit = {
|
|
|
2894
2942
|
get canOpenSetting() {
|
|
2895
2943
|
return canOpenSetting();
|
|
2896
2944
|
},
|
|
2945
|
+
get canEscapeWebview() {
|
|
2946
|
+
return canEscapeWebview();
|
|
2947
|
+
},
|
|
2897
2948
|
getTrackId: getTrackIdAsync,
|
|
2898
2949
|
getProductId: getProductIdAsync,
|
|
2899
2950
|
},
|
|
@@ -3161,6 +3212,18 @@ var LaunchKit = {
|
|
|
3161
3212
|
return openURLSequential();
|
|
3162
3213
|
});
|
|
3163
3214
|
},
|
|
3215
|
+
escapeWebview: function (options) {
|
|
3216
|
+
if (options === void 0) { options = {}; }
|
|
3217
|
+
if (!isInWebview())
|
|
3218
|
+
return Promise.reject(new Error('Escaping the webview is not needed: the current environment is not an in-app browser or webview. (userAgent: "' + PlatformKit.userAgent + '")'));
|
|
3219
|
+
var url = typeof options.url !== 'undefined' ? stripURL(options.url) : getTopmostWindow().location.href;
|
|
3220
|
+
if (!isWebURL(url))
|
|
3221
|
+
return Promise.reject(new Error('Cannot escape the webview: only http(s) URLs can be reopened in an external browser. (url: "' + url + '")'));
|
|
3222
|
+
var escape = createEscapeWebviewURL(url);
|
|
3223
|
+
if (typeof escape === 'undefined')
|
|
3224
|
+
return Promise.reject(new Error('Failed to escape the webview: no escape route is available in the current environment. (userAgent: "' + PlatformKit.userAgent + '")'));
|
|
3225
|
+
return openURL(0, escape, getDefaultTimeout(), isWebURL(escape));
|
|
3226
|
+
},
|
|
3164
3227
|
};
|
|
3165
3228
|
|
|
3166
3229
|
exports.default = LaunchKit;
|