web-launch-kit 0.0.1 → 0.0.2

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.md CHANGED
@@ -28,14 +28,13 @@ The bundle is self-contained (OS/locale detection is inlined) — no peer script
28
28
  | `LaunchKit.telephone(options?)` | `Promise<void>` | Open the dialer (`tel:`) |
29
29
  | `LaunchKit.message(options?)` | `Promise<void>` | Open the SMS composer (`sms:`) |
30
30
  | `LaunchKit.mail(options?)` | `Promise<void>` | Open the mail composer (`mailto:`) |
31
+ | `LaunchKit.map(options?)` | `Promise<void>` | Open a map by query, coordinate, or directions; native app per-OS with a Google Maps web fallback |
31
32
  | `LaunchKit.filepicker(options?)` | `Promise<File[]>` | Pick files or a directory (File System Access API, with input fallback) |
32
33
  | `LaunchKit.setting(type?)` | `Promise<void>` | Open a system-settings pane where supported |
33
34
  | `LaunchKit.utils` | object | `canOpenIntent` / `canOpenUniversal` / `canOpenSetting` getters, plus async `getTrackId` / `getProductId` |
34
35
 
35
36
  `AppOpenedBy` is one of: `"scheme"`, `"universal"`, `"intent"`, `"fallback"`, `"store"`.
36
37
 
37
- Named exports `getProductId` / `getTrackId` (synchronous store-id lookups) are also available.
38
-
39
38
  ---
40
39
 
41
40
  ## Launching an app
@@ -43,7 +42,6 @@ Named exports `getProductId` / `getTrackId` (synchronous store-id lookups) are a
43
42
  `app()` takes per-platform options and only acts on the block matching the current
44
43
  OS. It builds an ordered list of candidates and tries each until one launches the
45
44
  app, resolving with the route that worked.
46
-
47
45
  ```mermaid
48
46
  flowchart TD
49
47
  A([LaunchKit.app called]) --> B{Detect OS via PlatformKit}
@@ -60,14 +58,14 @@ flowchart TD
60
58
  A2 --> A3{"scheme given, but intent missing?"}
61
59
  A3 -->|yes| A4["createIntentURL:<br/>scheme + packageName + fallback"]
62
60
  A3 -->|no| A5
63
- A4 --> A5["Priority list:<br/>intent (if canOpenIntent) ⭢ scheme fallback<br/>⭢ app store ⭢ web store (by packageName)"]
61
+ A4 --> A5["Priority list:<br/>intent (if canOpenIntent) ⭢ scheme (if canOpenScheme)<br/>⭢ fallback ⭢ app store (if canOpenScheme) ⭢ web store"]
64
62
  end
65
63
 
66
64
  subgraph IOS["ios · resolveOptions"]
67
65
  I1{"bundleId given, but trackId missing?"}
68
66
  I1 -->|yes| I2["getTrackId:<br/>iTunes lookup API (bundleId ⭢ trackId)"]
69
67
  I1 -->|no| I3
70
- I2 --> I3["Priority list:<br/>universal (if iOS ≥ 9) ⭢ scheme fallback<br/>⭢ app store ⭢ web store (by trackId)"]
68
+ I2 --> I3["Priority list:<br/>universal (if canOpenUniversal) ⭢ scheme (if canOpenScheme)<br/>⭢ fallback ⭢ app store (if canOpenScheme) ⭢ web store"]
71
69
  end
72
70
 
73
71
  subgraph WIN["windows · resolveOptions"]
@@ -123,43 +121,18 @@ flowchart TD
123
121
  import LaunchKit from 'web-launch-kit'
124
122
 
125
123
  const openedBy = await LaunchKit.app({
126
- android: {
127
- scheme: 'ms-excel://',
128
- packageName: 'com.microsoft.office.excel',
129
- intent: 'intent://#Intent;scheme=ms-excel;package=com.microsoft.office.excel;action=android.intent.action.VIEW;category=android.intent.category.BROWSABLE;S.browser_fallback_url=https%3A%2F%2Fplay.google.com%2Fstore%2Fapps%2Fdetails%3Fid%3Dcom.microsoft.office.excel;end',
130
- fallback: 'https://www.microsoft.com/ko-kr/microsoft-365/excel',
131
- allowAppStore: true,
132
- allowWebStore: false,
133
- timeout: 1000,
134
- },
135
- ios: {
136
- scheme: 'ms-excel://',
137
- bundleId: 'com.microsoft.Office.Excel',
138
- trackId: '586683407',
139
- universal: 'https://1drv.ms/x/c/7f3d9a02c81b4e65/IQBk2wYfN8pTQ5vHmR9xLzUcAeXtP0jWnK4oD3iFgZs7bQY?e=Rk9mZ2',
140
- fallback: 'https://www.microsoft.com/ko-kr/microsoft-365/excel',
141
- allowAppStore: true,
142
- allowWebStore: false,
143
- timeout: 2000,
144
- },
145
- windows: {
146
- scheme: 'ms-excel://',
147
- packageFamilyName: 'Microsoft.Office.Desktop_8wekyb3d8bbwe',
148
- productId: 'cfq7ttc0pr28',
149
- fallback: 'https://www.microsoft.com/ko-kr/microsoft-365/excel',
150
- allowAppStore: true,
151
- allowWebStore: false,
152
- timeout: 750,
153
- },
154
- macos: {
155
- scheme: 'ms-excel://',
156
- bundleId: 'com.microsoft.Excel',
157
- trackId: '462058435',
158
- fallback: 'https://www.microsoft.com/ko-kr/microsoft-365/excel',
159
- allowAppStore: true,
160
- allowWebStore: false,
161
- timeout: 750,
162
- }
124
+ android: {
125
+ scheme: 'myapp://profile/42',
126
+ packageName: 'com.example.myapp',
127
+ allowAppStore: true,
128
+ allowWebStore: true,
129
+ },
130
+ ios: {
131
+ universal: 'https://example.com/profile/42',
132
+ scheme: 'myapp://profile/42',
133
+ bundleId: 'com.example.myapp', // resolved to a trackId for the store fallback
134
+ allowAppStore: true,
135
+ },
163
136
  })
164
137
 
165
138
  console.log(openedBy) // "universal" | "scheme" | "intent" | "fallback" | "store"
@@ -169,7 +142,12 @@ Per-platform fields: Android accepts `intent` / `scheme` / `packageName` / `fall
169
142
  (scheme ⇄ intent are derived from each other); iOS accepts `universal` / `scheme` /
170
143
  `bundleId` / `trackId`; Windows accepts `scheme` / `packageFamilyName` / `productId`;
171
144
  macOS accepts `scheme` / `bundleId` / `trackId`. All accept `fallback`, `timeout`,
172
- `allowAppStore`, `allowWebStore`.
145
+ `allowAppStore`, `allowWebStore`.
146
+
147
+ Android and iOS additionally accept
148
+ `assumeAllowedInApp` — declare it `true` only when your app is whitelisted by a
149
+ partner-gated in-app browser (e.g. Weibo) so scheme / universal-link candidates are
150
+ kept there; it never bypasses hard-blocked webviews or OS version requirements.
173
151
 
174
152
  ## Communication intents
175
153
 
@@ -200,6 +178,33 @@ const files = await LaunchKit.filepicker({ accept: ['image/*', '.pdf'], multiple
200
178
  const tree = await LaunchKit.filepicker({ directory: true })
201
179
  ```
202
180
 
181
+ ## Maps
182
+
183
+ `map()` takes an intent — a search query, a coordinate, or a route — and builds the
184
+ right URL for the current OS (`maps://` on iOS/macOS, `geo:` on Android, `bingmaps:`
185
+ on Windows), falling back to Google Maps on the web.
186
+
187
+ ```js
188
+ import LaunchKit from 'web-launch-kit'
189
+
190
+ // Search for a place
191
+ await LaunchKit.map({ query: 'Seoul City Hall' })
192
+
193
+ // Show a coordinate with a labelled pin
194
+ await LaunchKit.map({ coordinate: [37.5665, 126.9780], label: 'Seoul City Hall', zoom: 15 })
195
+
196
+ // Directions (origin defaults to current location when omitted)
197
+ await LaunchKit.map({ directions: { destination: 'Seoul Station', origin: [37.5665, 126.9780] } })
198
+ ```
199
+
200
+ Provide exactly one of `query` / `coordinate` / `directions`; when several are set,
201
+ `directions` wins, then `coordinate`, then `query`. A `label` with `coordinate`
202
+ renders a named pin on iOS/macOS/Windows and is ignored on Android, where `geo:`
203
+ handlers other than Google Maps misread the labelled form as a search. Android's
204
+ `geo:` scheme also has no standard directions support, so routes use the Google Maps
205
+ fallback there, and on Windows the discontinued Maps app means `bingmaps:` usually
206
+ defers to the web fallback too.
207
+
203
208
  ## System settings
204
209
 
205
210
  ```js
@@ -239,3 +244,10 @@ const { default: LaunchKit } = require('web-launch-kit')
239
244
  exports are synchronous (blocking XHR) and intended for internal/legacy use.
240
245
  - **Store-id lookups depend on remote APIs** (iTunes Lookup, Microsoft display catalog)
241
246
  and are cached for one hour; failures resolve to `undefined` rather than throwing.
247
+ - **In-app browsers are gated per candidate.** Inside webviews that block launches
248
+ (WeChat, QQ, Qzone, Baidu; Weibo unless partnered), dead candidates — custom schemes,
249
+ `intent://`, and store schemes like `market://` — are skipped up front instead of
250
+ burning their timeouts, so the chain falls straight through to `fallback` / web store
251
+ (the only routes that work there). Whitelisted partner apps opt back in with
252
+ `assumeAllowedInApp: true`.
253
+ - **`map()` uses the per-OS timeout** for each candidate.
package/dist/index.d.ts CHANGED
@@ -82,11 +82,13 @@ declare interface AppInfo {
82
82
  declare interface AndroidAppInfo extends AppInfo {
83
83
  intent?: URLCandidate;
84
84
  packageName?: string;
85
+ assumeAllowedInApp?: boolean;
85
86
  }
86
87
  declare interface IOSAppInfo extends AppInfo {
87
88
  universal?: URLCandidate;
88
89
  bundleId?: string;
89
90
  trackId?: string;
91
+ assumeAllowedInApp?: boolean;
90
92
  }
91
93
  declare interface WindowsAppInfo extends AppInfo {
92
94
  packageFamilyName?: string;
@@ -123,6 +125,17 @@ declare interface FilepickerOptions {
123
125
  multiple?: boolean;
124
126
  startIn?: OpenPickerStartIn;
125
127
  }
128
+ declare interface MapOptions {
129
+ query?: string;
130
+ coordinate?: [number, number];
131
+ label?: string;
132
+ directions?: {
133
+ destination: string | [number, number];
134
+ origin?: string | [number, number];
135
+ };
136
+ zoom?: number;
137
+ fallback?: URLStringOrFallback;
138
+ }
126
139
  interface LaunchKitUtils {
127
140
  get canOpenIntent(): boolean;
128
141
  get canOpenUniversal(): boolean;
@@ -138,10 +151,11 @@ interface LaunchKitInstance {
138
151
  telephone(options?: TelephoneOptions): Promise<void>;
139
152
  message(options?: MessageOptions): Promise<void>;
140
153
  mail(options?: MailOptions): Promise<void>;
154
+ map(options?: MapOptions): Promise<void>;
141
155
  filepicker(options?: FilepickerOptions): Promise<File[]>;
142
156
  setting(type?: SettingType): Promise<void>;
143
157
  }
144
158
  declare const LaunchKit: LaunchKitInstance;
145
159
 
146
160
  export { SettingType, LaunchKit as default };
147
- export type { AndroidAppInfo, AppInfo, AppOpenOptions, AppOpenedBy, FilepickerOptions, IOSAppInfo, LaunchKitInstance, MacOSAppInfo, MailOptions, MessageOptions, OpenPickerStartIn, TelephoneOptions, URLCandidate, URLCandidateOrFallback, URLStringOrFallback, WindowsAppInfo };
161
+ export type { AndroidAppInfo, AppInfo, AppOpenOptions, AppOpenedBy, FilepickerOptions, IOSAppInfo, LaunchKitInstance, MacOSAppInfo, MailOptions, MapOptions, MessageOptions, OpenPickerStartIn, TelephoneOptions, URLCandidate, URLCandidateOrFallback, URLStringOrFallback, WindowsAppInfo };
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var version$1 = "0.0.1";
5
+ var version$1 = "0.0.2";
6
6
  var packageJSON$1 = {
7
7
  version: version$1};
8
8
 
@@ -940,7 +940,7 @@ var ANDROID_DEFAULT_TIMEOUT = 1000;
940
940
  var IOS_DEFAULT_TIMEOUT = 2000;
941
941
  var WINDOWS_DEFAULT_TIMEOUT = 750;
942
942
  var MACOS_DEFAULT_TIMEOUT = 750;
943
- var SETTING_DEFAULT_TIMEOUT = 750;
943
+ var DEFAULT_TIMEOUT = 750;
944
944
  var NAVIGATOR = globalThis.navigator;
945
945
  var CLEANUP_INPUT_ELEMENT = null;
946
946
  function createTypeGuard(tag) {
@@ -974,7 +974,7 @@ function getDefaultTimeout() {
974
974
  case 'macos':
975
975
  return MACOS_DEFAULT_TIMEOUT;
976
976
  default:
977
- return WINDOWS_DEFAULT_TIMEOUT;
977
+ return DEFAULT_TIMEOUT;
978
978
  }
979
979
  }
980
980
  function isDocumentHidden() {
@@ -1080,7 +1080,7 @@ function restoreFocus() {
1080
1080
  return hasFocus(topDocument);
1081
1081
  }
1082
1082
  function stripURL(value) {
1083
- return typeof value === 'undefined' ? undefined : isURL(value) ? value.toString() : value;
1083
+ return isURL(value) ? value.toString() : value;
1084
1084
  }
1085
1085
  function resolveFocusEventConfig() {
1086
1086
  var top = getTopmostWindow();
@@ -1294,6 +1294,8 @@ function resolveOptions(option) {
1294
1294
  var fallback;
1295
1295
  var allowAppStore;
1296
1296
  var allowWebStore;
1297
+ var allowScheme;
1298
+ var assumeAllowedInApp;
1297
1299
  var timeout;
1298
1300
  var intent;
1299
1301
  var packageName;
@@ -1310,8 +1312,10 @@ function resolveOptions(option) {
1310
1312
  scheme = stripURL(option.android.scheme);
1311
1313
  fallback = stripURL(option.android.fallback);
1312
1314
  packageName = option.android.packageName;
1315
+ assumeAllowedInApp = option.android.assumeAllowedInApp === true;
1313
1316
  allowAppStore = option.android.allowAppStore;
1314
1317
  allowWebStore = option.android.allowWebStore;
1318
+ allowScheme = canOpenScheme(assumeAllowedInApp);
1315
1319
  timeout = option.android.timeout;
1316
1320
  if (typeof intent !== 'undefined' && (typeof scheme === 'undefined' || typeof packageName === 'undefined' || typeof fallback === 'undefined')) {
1317
1321
  var parsed = parseIntentURL(intent);
@@ -1328,12 +1332,12 @@ function resolveOptions(option) {
1328
1332
  timeout = ANDROID_DEFAULT_TIMEOUT;
1329
1333
  if (typeof intent !== 'undefined' && canOpenIntent())
1330
1334
  resolved.push(['intent', intent]);
1331
- if (typeof scheme !== 'undefined')
1335
+ if (typeof scheme !== 'undefined' && allowScheme)
1332
1336
  resolved.push(['scheme', scheme]);
1333
1337
  if (typeof fallback !== 'undefined')
1334
1338
  resolved.push(['fallback', fallback]);
1335
1339
  if (typeof packageName !== 'undefined') {
1336
- if (allowAppStore)
1340
+ if (allowAppStore && allowScheme)
1337
1341
  resolved.push(['store', createAppStoreURL(packageName, os)]);
1338
1342
  if (allowWebStore)
1339
1343
  resolved.push(['store', createWebStoreURL(packageName, os)]);
@@ -1347,21 +1351,23 @@ function resolveOptions(option) {
1347
1351
  fallback = stripURL(option.ios.fallback);
1348
1352
  bundleId = option.ios.bundleId;
1349
1353
  trackId = option.ios.trackId;
1354
+ assumeAllowedInApp = option.ios.assumeAllowedInApp === true;
1350
1355
  allowAppStore = option.ios.allowAppStore;
1351
1356
  allowWebStore = option.ios.allowWebStore;
1357
+ allowScheme = canOpenScheme(assumeAllowedInApp);
1352
1358
  timeout = option.ios.timeout;
1353
1359
  if (typeof bundleId !== 'undefined' && typeof trackId === 'undefined')
1354
1360
  trackId = getTrackId(bundleId);
1355
1361
  if (typeof timeout === 'undefined')
1356
1362
  timeout = IOS_DEFAULT_TIMEOUT;
1357
- if (typeof universal !== 'undefined' && canOpenUniversal())
1363
+ if (typeof universal !== 'undefined' && canOpenUniversal(assumeAllowedInApp))
1358
1364
  resolved.push(['universal', universal]);
1359
- if (typeof scheme !== 'undefined')
1365
+ if (typeof scheme !== 'undefined' && allowScheme)
1360
1366
  resolved.push(['scheme', scheme]);
1361
1367
  if (typeof fallback !== 'undefined')
1362
1368
  resolved.push(['fallback', fallback]);
1363
1369
  if (typeof trackId !== 'undefined') {
1364
- if (allowAppStore)
1370
+ if (allowAppStore && allowScheme)
1365
1371
  resolved.push(['store', createAppStoreURL(trackId, os)]);
1366
1372
  if (allowWebStore)
1367
1373
  resolved.push(['store', createWebStoreURL(trackId, os)]);
@@ -1434,10 +1440,41 @@ function canOpenIntent() {
1434
1440
  return false;
1435
1441
  if (browser === 'opera' && PlatformKit.compareVersion(version, '14.0') < 0)
1436
1442
  return false;
1437
- return !(/(?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/[\w.]+;/i.test(PlatformKit.userAgent) || /instagram[\/ ][-\w.]+/i.test(PlatformKit.userAgent) || /micromessenger\/([\w.]+)/i.test(PlatformKit.userAgent) || /musical_ly(?:.+app_?version\/|_)[\w.]+/i.test(PlatformKit.userAgent) || /ultralite app_version\/[\w.]+/i.test(PlatformKit.userAgent));
1443
+ if (isFullyBlockedInAppBrowser() || isPartnerAppOnly())
1444
+ return false;
1445
+ return !(/(?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/[\w.]+;/i.test(PlatformKit.userAgent) || /instagram[\/ ][-\w.]+/i.test(PlatformKit.userAgent) || /musical_ly(?:.+app_?version\/|_)[\w.]+/i.test(PlatformKit.userAgent) || /ultralite app_version\/[\w.]+/i.test(PlatformKit.userAgent));
1446
+ }
1447
+ function canOpenUniversal(assumeAllowedInApp) {
1448
+ if (assumeAllowedInApp === void 0) { assumeAllowedInApp = false; }
1449
+ if (PlatformKit.os.name !== 'ios' || PlatformKit.compareVersion(PlatformKit.os.version, '9.0') < 0)
1450
+ return false;
1451
+ if (isPartnerAppOnly() && !assumeAllowedInApp)
1452
+ return false;
1453
+ var userAgent = PlatformKit.userAgent;
1454
+ var wechat = userAgent.match(/micromessenger\/(\d+(?:\.\d+)*)/i);
1455
+ if (wechat !== null && PlatformKit.compareVersion(wechat[1], '7.0.5') < 0)
1456
+ return false;
1457
+ if (/ qq\/[\d.]+/i.test(userAgent) || /m?qqbrowser\/[\d.]+/i.test(userAgent) || /ucbrowser\/[\d.]+/i.test(userAgent))
1458
+ return false;
1459
+ return !/qzone\/.*_qz_[\d.]+/i.test(userAgent);
1460
+ }
1461
+ function canOpenScheme(assumeAllowedInApp) {
1462
+ if (assumeAllowedInApp === void 0) { assumeAllowedInApp = false; }
1463
+ var os = PlatformKit.os.name;
1464
+ if (os === 'android')
1465
+ return !isFullyBlockedInAppBrowser() && (!isPartnerAppOnly() || assumeAllowedInApp);
1466
+ if (os === 'ios')
1467
+ return !/micromessenger\/[\w.]+/i.test(PlatformKit.userAgent) && (!isPartnerAppOnly() || assumeAllowedInApp);
1468
+ return true;
1469
+ }
1470
+ function isFullyBlockedInAppBrowser() {
1471
+ if (PlatformKit.os.name !== 'android')
1472
+ return false;
1473
+ var userAgent = PlatformKit.userAgent;
1474
+ return /micromessenger\/[\w.]+/i.test(userAgent) || / qq\/[\d.]+/i.test(userAgent) || /qzone\/.*_qz_[\d.]+/i.test(userAgent) || /baiduboxapp\/[\d.]+/i.test(userAgent);
1438
1475
  }
1439
- function canOpenUniversal() {
1440
- return PlatformKit.os.name === 'ios' && PlatformKit.compareVersion(PlatformKit.os.version, '9.0') >= 0;
1476
+ function isPartnerAppOnly() {
1477
+ return /weibo.*weibo__[\d.]+/i.test(PlatformKit.userAgent);
1441
1478
  }
1442
1479
  function canOpenSetting() {
1443
1480
  var os = PlatformKit.os.name;
@@ -1544,6 +1581,18 @@ function escapeURIComponentMailAddressString(value) {
1544
1581
  function encodeMailBody(value) {
1545
1582
  return escapeURIComponentString(value.replace(/\r\n|\n|\r/g, '\r\n'));
1546
1583
  }
1584
+ function encodeMapValue(value) {
1585
+ if (isArray(value))
1586
+ return value[0] + ',' + value[1];
1587
+ return escapeURIComponentString(value);
1588
+ }
1589
+ function encodeBingRoutePoint(value) {
1590
+ if (typeof value === 'undefined')
1591
+ return '';
1592
+ if (isArray(value))
1593
+ return 'pos.' + value[0] + '_' + value[1];
1594
+ return escapeURIComponentString(value);
1595
+ }
1547
1596
  function toArray(value) {
1548
1597
  return typeof value === 'undefined' ? [] : typeof value === 'string' ? [value] : value;
1549
1598
  }
@@ -1790,12 +1839,12 @@ var LaunchKit = {
1790
1839
  catch (error) {
1791
1840
  return Promise.reject(error);
1792
1841
  }
1793
- var tried = [];
1794
1842
  var urls = resolved[0];
1795
1843
  var timeout = resolved[1];
1796
1844
  if (urls.length === 0)
1797
1845
  return Promise.reject(new Error('No openable URL candidates were resolved for the current OS ("' + PlatformKit.os.name + '"). Provide at least one of: scheme, intent, universal, fallback, or store id for this platform.'));
1798
1846
  return new Promise(function (resolve, reject) {
1847
+ var tried = [];
1799
1848
  function openURLSequential(index) {
1800
1849
  if (index === void 0) { index = 0; }
1801
1850
  if (index >= urls.length)
@@ -1867,6 +1916,112 @@ var LaunchKit = {
1867
1916
  url += '?' + joining(params, undefined, '&');
1868
1917
  return openURL(0, url, getDefaultTimeout());
1869
1918
  },
1919
+ map: function (options) {
1920
+ if (options === void 0) { options = {}; }
1921
+ var params = [];
1922
+ var urls = [];
1923
+ var url = '';
1924
+ switch (PlatformKit.os.name) {
1925
+ case 'android':
1926
+ if (typeof options.coordinate !== 'undefined') {
1927
+ url = 'geo:' + options.coordinate[0] + ',' + options.coordinate[1];
1928
+ }
1929
+ else if (typeof options.query !== 'undefined') {
1930
+ url = 'geo:0,0';
1931
+ params.push('q=' + escapeURIComponentString(options.query));
1932
+ }
1933
+ else {
1934
+ url = 'geo:0,0';
1935
+ }
1936
+ if (typeof options.zoom !== 'undefined')
1937
+ params.push('z=' + options.zoom);
1938
+ if (params.length > 0)
1939
+ url = url + '?' + joining(params, undefined, '&');
1940
+ urls.push(url);
1941
+ break;
1942
+ case 'ios':
1943
+ case 'macos':
1944
+ if (typeof options.directions !== 'undefined') {
1945
+ if (typeof options.directions.destination !== 'undefined')
1946
+ params.push('daddr=' + encodeMapValue(options.directions.destination));
1947
+ if (typeof options.directions.origin !== 'undefined')
1948
+ params.push('saddr=' + encodeMapValue(options.directions.origin));
1949
+ }
1950
+ else if (typeof options.coordinate !== 'undefined') {
1951
+ params.push('ll=' + options.coordinate[0] + ',' + options.coordinate[1]);
1952
+ if (typeof options.label !== 'undefined')
1953
+ params.push('q=' + escapeURIComponentString(options.label));
1954
+ }
1955
+ else if (typeof options.query !== 'undefined') {
1956
+ params.push('q=' + escapeURIComponentString(options.query));
1957
+ }
1958
+ if (typeof options.zoom !== 'undefined')
1959
+ params.push('z=' + options.zoom);
1960
+ urls.push('maps://?' + joining(params, undefined, '&'));
1961
+ break;
1962
+ case 'windows':
1963
+ if (typeof options.directions !== 'undefined') {
1964
+ params.push('rtp=' + encodeBingRoutePoint(options.directions.origin) + '~' + encodeBingRoutePoint(options.directions.destination));
1965
+ }
1966
+ else if (typeof options.coordinate !== 'undefined') {
1967
+ params.push('cp=' + options.coordinate[0] + '~' + options.coordinate[1]);
1968
+ if (typeof options.label !== 'undefined')
1969
+ params.push('q=' + escapeURIComponentString(options.label));
1970
+ }
1971
+ else if (typeof options.query !== 'undefined') {
1972
+ params.push('where=' + escapeURIComponentString(options.query));
1973
+ }
1974
+ if (typeof options.zoom !== 'undefined')
1975
+ params.push('lvl=' + options.zoom);
1976
+ urls.push('bingmaps:?' + joining(params, undefined, '&'));
1977
+ }
1978
+ if (typeof options.fallback !== 'undefined') {
1979
+ urls.push(stripURL(options.fallback));
1980
+ }
1981
+ else {
1982
+ var params_1 = ['api=1'];
1983
+ if (typeof options.directions !== 'undefined') {
1984
+ params_1.push('destination=' + encodeMapValue(options.directions.destination));
1985
+ if (typeof options.directions.origin !== 'undefined')
1986
+ params_1.push('origin=' + encodeMapValue(options.directions.origin));
1987
+ urls.push('https://www.google.com/maps/dir/?' + joining(params_1, undefined, '&'));
1988
+ }
1989
+ else {
1990
+ if (typeof options.coordinate !== 'undefined')
1991
+ params_1.push('query=' + options.coordinate[0] + ',' + options.coordinate[1]);
1992
+ else if (typeof options.query !== 'undefined')
1993
+ params_1.push('query=' + escapeURIComponentString(options.query));
1994
+ if (typeof options.zoom !== 'undefined')
1995
+ params_1.push('zoom=' + options.zoom);
1996
+ urls.push('https://www.google.com/maps/search/?' + joining(params_1, undefined, '&'));
1997
+ }
1998
+ }
1999
+ return new Promise(function (resolve, reject) {
2000
+ var tried = [];
2001
+ function openURLSequential(index) {
2002
+ if (index === void 0) { index = 0; }
2003
+ if (index >= urls.length)
2004
+ return reject(new Error('Failed to open a map using all available URLs.\n\nAttempted URLs:\n' + joining(tried, undefined, '\n↓\n')));
2005
+ var url = urls[index];
2006
+ if (typeof url === 'string') {
2007
+ tried[index] = url;
2008
+ return openURL(index, url, getDefaultTimeout())
2009
+ .then(function () {
2010
+ resolve();
2011
+ })
2012
+ .catch(function () {
2013
+ openURLSequential(index + 1);
2014
+ });
2015
+ }
2016
+ else {
2017
+ tried[index] = '[function fallback]';
2018
+ url();
2019
+ resolve();
2020
+ }
2021
+ }
2022
+ return openURLSequential();
2023
+ });
2024
+ },
1870
2025
  filepicker: function (options) {
1871
2026
  if (options === void 0) { options = {}; }
1872
2027
  var module;
@@ -1969,7 +2124,7 @@ var LaunchKit = {
1969
2124
  if (index === void 0) { index = 0; }
1970
2125
  if (index >= urls.length)
1971
2126
  return reject(new Error('Failed to open the "' + type + '" settings pane using all available URLs.\n\nAttempted URLs:\n' + joining(urls, undefined, '\n↓\n')));
1972
- return openURL(index, urls[index], SETTING_DEFAULT_TIMEOUT)
2127
+ return openURL(index, urls[index], DEFAULT_TIMEOUT)
1973
2128
  .then(function () {
1974
2129
  resolve();
1975
2130
  })