index.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. /*! Capacitor: https://capacitorjs.com/ - MIT License */
  2. var ExceptionCode;
  3. (function (ExceptionCode) {
  4. /**
  5. * API is not implemented.
  6. *
  7. * This usually means the API can't be used because it is not implemented for
  8. * the current platform.
  9. */
  10. ExceptionCode["Unimplemented"] = "UNIMPLEMENTED";
  11. /**
  12. * API is not available.
  13. *
  14. * This means the API can't be used right now because:
  15. * - it is currently missing a prerequisite, such as network connectivity
  16. * - it requires a particular platform or browser version
  17. */
  18. ExceptionCode["Unavailable"] = "UNAVAILABLE";
  19. })(ExceptionCode || (ExceptionCode = {}));
  20. class CapacitorException extends Error {
  21. constructor(message, code, data) {
  22. super(message);
  23. this.message = message;
  24. this.code = code;
  25. this.data = data;
  26. }
  27. }
  28. const getPlatformId = (win) => {
  29. var _a, _b;
  30. if (win === null || win === void 0 ? void 0 : win.androidBridge) {
  31. return 'android';
  32. }
  33. else if ((_b = (_a = win === null || win === void 0 ? void 0 : win.webkit) === null || _a === void 0 ? void 0 : _a.messageHandlers) === null || _b === void 0 ? void 0 : _b.bridge) {
  34. return 'ios';
  35. }
  36. else {
  37. return 'web';
  38. }
  39. };
  40. const createCapacitor = (win) => {
  41. const capCustomPlatform = win.CapacitorCustomPlatform || null;
  42. const cap = win.Capacitor || {};
  43. const Plugins = (cap.Plugins = cap.Plugins || {});
  44. const getPlatform = () => {
  45. return capCustomPlatform !== null ? capCustomPlatform.name : getPlatformId(win);
  46. };
  47. const isNativePlatform = () => getPlatform() !== 'web';
  48. const isPluginAvailable = (pluginName) => {
  49. const plugin = registeredPlugins.get(pluginName);
  50. if (plugin === null || plugin === void 0 ? void 0 : plugin.platforms.has(getPlatform())) {
  51. // JS implementation available for the current platform.
  52. return true;
  53. }
  54. if (getPluginHeader(pluginName)) {
  55. // Native implementation available.
  56. return true;
  57. }
  58. return false;
  59. };
  60. const getPluginHeader = (pluginName) => { var _a; return (_a = cap.PluginHeaders) === null || _a === void 0 ? void 0 : _a.find((h) => h.name === pluginName); };
  61. const handleError = (err) => win.console.error(err);
  62. const registeredPlugins = new Map();
  63. const registerPlugin = (pluginName, jsImplementations = {}) => {
  64. const registeredPlugin = registeredPlugins.get(pluginName);
  65. if (registeredPlugin) {
  66. console.warn(`Capacitor plugin "${pluginName}" already registered. Cannot register plugins twice.`);
  67. return registeredPlugin.proxy;
  68. }
  69. const platform = getPlatform();
  70. const pluginHeader = getPluginHeader(pluginName);
  71. let jsImplementation;
  72. const loadPluginImplementation = async () => {
  73. if (!jsImplementation && platform in jsImplementations) {
  74. jsImplementation =
  75. typeof jsImplementations[platform] === 'function'
  76. ? (jsImplementation = await jsImplementations[platform]())
  77. : (jsImplementation = jsImplementations[platform]);
  78. }
  79. else if (capCustomPlatform !== null && !jsImplementation && 'web' in jsImplementations) {
  80. jsImplementation =
  81. typeof jsImplementations['web'] === 'function'
  82. ? (jsImplementation = await jsImplementations['web']())
  83. : (jsImplementation = jsImplementations['web']);
  84. }
  85. return jsImplementation;
  86. };
  87. const createPluginMethod = (impl, prop) => {
  88. var _a, _b;
  89. if (pluginHeader) {
  90. const methodHeader = pluginHeader === null || pluginHeader === void 0 ? void 0 : pluginHeader.methods.find((m) => prop === m.name);
  91. if (methodHeader) {
  92. if (methodHeader.rtype === 'promise') {
  93. return (options) => cap.nativePromise(pluginName, prop.toString(), options);
  94. }
  95. else {
  96. return (options, callback) => cap.nativeCallback(pluginName, prop.toString(), options, callback);
  97. }
  98. }
  99. else if (impl) {
  100. return (_a = impl[prop]) === null || _a === void 0 ? void 0 : _a.bind(impl);
  101. }
  102. }
  103. else if (impl) {
  104. return (_b = impl[prop]) === null || _b === void 0 ? void 0 : _b.bind(impl);
  105. }
  106. else {
  107. throw new CapacitorException(`"${pluginName}" plugin is not implemented on ${platform}`, ExceptionCode.Unimplemented);
  108. }
  109. };
  110. const createPluginMethodWrapper = (prop) => {
  111. let remove;
  112. const wrapper = (...args) => {
  113. const p = loadPluginImplementation().then((impl) => {
  114. const fn = createPluginMethod(impl, prop);
  115. if (fn) {
  116. const p = fn(...args);
  117. remove = p === null || p === void 0 ? void 0 : p.remove;
  118. return p;
  119. }
  120. else {
  121. throw new CapacitorException(`"${pluginName}.${prop}()" is not implemented on ${platform}`, ExceptionCode.Unimplemented);
  122. }
  123. });
  124. if (prop === 'addListener') {
  125. p.remove = async () => remove();
  126. }
  127. return p;
  128. };
  129. // Some flair ✨
  130. wrapper.toString = () => `${prop.toString()}() { [capacitor code] }`;
  131. Object.defineProperty(wrapper, 'name', {
  132. value: prop,
  133. writable: false,
  134. configurable: false,
  135. });
  136. return wrapper;
  137. };
  138. const addListener = createPluginMethodWrapper('addListener');
  139. const removeListener = createPluginMethodWrapper('removeListener');
  140. const addListenerNative = (eventName, callback) => {
  141. const call = addListener({ eventName }, callback);
  142. const remove = async () => {
  143. const callbackId = await call;
  144. removeListener({
  145. eventName,
  146. callbackId,
  147. }, callback);
  148. };
  149. const p = new Promise((resolve) => call.then(() => resolve({ remove })));
  150. p.remove = async () => {
  151. console.warn(`Using addListener() without 'await' is deprecated.`);
  152. await remove();
  153. };
  154. return p;
  155. };
  156. const proxy = new Proxy({}, {
  157. get(_, prop) {
  158. switch (prop) {
  159. // https://github.com/facebook/react/issues/20030
  160. case '$$typeof':
  161. return undefined;
  162. case 'toJSON':
  163. return () => ({});
  164. case 'addListener':
  165. return pluginHeader ? addListenerNative : addListener;
  166. case 'removeListener':
  167. return removeListener;
  168. default:
  169. return createPluginMethodWrapper(prop);
  170. }
  171. },
  172. });
  173. Plugins[pluginName] = proxy;
  174. registeredPlugins.set(pluginName, {
  175. name: pluginName,
  176. proxy,
  177. platforms: new Set([...Object.keys(jsImplementations), ...(pluginHeader ? [platform] : [])]),
  178. });
  179. return proxy;
  180. };
  181. // Add in convertFileSrc for web, it will already be available in native context
  182. if (!cap.convertFileSrc) {
  183. cap.convertFileSrc = (filePath) => filePath;
  184. }
  185. cap.getPlatform = getPlatform;
  186. cap.handleError = handleError;
  187. cap.isNativePlatform = isNativePlatform;
  188. cap.isPluginAvailable = isPluginAvailable;
  189. cap.registerPlugin = registerPlugin;
  190. cap.Exception = CapacitorException;
  191. cap.DEBUG = !!cap.DEBUG;
  192. cap.isLoggingEnabled = !!cap.isLoggingEnabled;
  193. return cap;
  194. };
  195. const initCapacitorGlobal = (win) => (win.Capacitor = createCapacitor(win));
  196. const Capacitor = /*#__PURE__*/ initCapacitorGlobal(typeof globalThis !== 'undefined'
  197. ? globalThis
  198. : typeof self !== 'undefined'
  199. ? self
  200. : typeof window !== 'undefined'
  201. ? window
  202. : typeof global !== 'undefined'
  203. ? global
  204. : {});
  205. const registerPlugin = Capacitor.registerPlugin;
  206. /**
  207. * Base class web plugins should extend.
  208. */
  209. class WebPlugin {
  210. constructor() {
  211. this.listeners = {};
  212. this.retainedEventArguments = {};
  213. this.windowListeners = {};
  214. }
  215. addListener(eventName, listenerFunc) {
  216. let firstListener = false;
  217. const listeners = this.listeners[eventName];
  218. if (!listeners) {
  219. this.listeners[eventName] = [];
  220. firstListener = true;
  221. }
  222. this.listeners[eventName].push(listenerFunc);
  223. // If we haven't added a window listener for this event and it requires one,
  224. // go ahead and add it
  225. const windowListener = this.windowListeners[eventName];
  226. if (windowListener && !windowListener.registered) {
  227. this.addWindowListener(windowListener);
  228. }
  229. if (firstListener) {
  230. this.sendRetainedArgumentsForEvent(eventName);
  231. }
  232. const remove = async () => this.removeListener(eventName, listenerFunc);
  233. const p = Promise.resolve({ remove });
  234. return p;
  235. }
  236. async removeAllListeners() {
  237. this.listeners = {};
  238. for (const listener in this.windowListeners) {
  239. this.removeWindowListener(this.windowListeners[listener]);
  240. }
  241. this.windowListeners = {};
  242. }
  243. notifyListeners(eventName, data, retainUntilConsumed) {
  244. const listeners = this.listeners[eventName];
  245. if (!listeners) {
  246. if (retainUntilConsumed) {
  247. let args = this.retainedEventArguments[eventName];
  248. if (!args) {
  249. args = [];
  250. }
  251. args.push(data);
  252. this.retainedEventArguments[eventName] = args;
  253. }
  254. return;
  255. }
  256. listeners.forEach((listener) => listener(data));
  257. }
  258. hasListeners(eventName) {
  259. return !!this.listeners[eventName].length;
  260. }
  261. registerWindowListener(windowEventName, pluginEventName) {
  262. this.windowListeners[pluginEventName] = {
  263. registered: false,
  264. windowEventName,
  265. pluginEventName,
  266. handler: (event) => {
  267. this.notifyListeners(pluginEventName, event);
  268. },
  269. };
  270. }
  271. unimplemented(msg = 'not implemented') {
  272. return new Capacitor.Exception(msg, ExceptionCode.Unimplemented);
  273. }
  274. unavailable(msg = 'not available') {
  275. return new Capacitor.Exception(msg, ExceptionCode.Unavailable);
  276. }
  277. async removeListener(eventName, listenerFunc) {
  278. const listeners = this.listeners[eventName];
  279. if (!listeners) {
  280. return;
  281. }
  282. const index = listeners.indexOf(listenerFunc);
  283. this.listeners[eventName].splice(index, 1);
  284. // If there are no more listeners for this type of event,
  285. // remove the window listener
  286. if (!this.listeners[eventName].length) {
  287. this.removeWindowListener(this.windowListeners[eventName]);
  288. }
  289. }
  290. addWindowListener(handle) {
  291. window.addEventListener(handle.windowEventName, handle.handler);
  292. handle.registered = true;
  293. }
  294. removeWindowListener(handle) {
  295. if (!handle) {
  296. return;
  297. }
  298. window.removeEventListener(handle.windowEventName, handle.handler);
  299. handle.registered = false;
  300. }
  301. sendRetainedArgumentsForEvent(eventName) {
  302. const args = this.retainedEventArguments[eventName];
  303. if (!args) {
  304. return;
  305. }
  306. delete this.retainedEventArguments[eventName];
  307. args.forEach((arg) => {
  308. this.notifyListeners(eventName, arg);
  309. });
  310. }
  311. }
  312. const WebView = /*#__PURE__*/ registerPlugin('WebView');
  313. /******** END WEB VIEW PLUGIN ********/
  314. /******** COOKIES PLUGIN ********/
  315. /**
  316. * Safely web encode a string value (inspired by js-cookie)
  317. * @param str The string value to encode
  318. */
  319. const encode = (str) => encodeURIComponent(str)
  320. .replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent)
  321. .replace(/[()]/g, escape);
  322. /**
  323. * Safely web decode a string value (inspired by js-cookie)
  324. * @param str The string value to decode
  325. */
  326. const decode = (str) => str.replace(/(%[\dA-F]{2})+/gi, decodeURIComponent);
  327. class CapacitorCookiesPluginWeb extends WebPlugin {
  328. async getCookies() {
  329. const cookies = document.cookie;
  330. const cookieMap = {};
  331. cookies.split(';').forEach((cookie) => {
  332. if (cookie.length <= 0)
  333. return;
  334. // Replace first "=" with CAP_COOKIE to prevent splitting on additional "="
  335. let [key, value] = cookie.replace(/=/, 'CAP_COOKIE').split('CAP_COOKIE');
  336. key = decode(key).trim();
  337. value = decode(value).trim();
  338. cookieMap[key] = value;
  339. });
  340. return cookieMap;
  341. }
  342. async setCookie(options) {
  343. try {
  344. // Safely Encoded Key/Value
  345. const encodedKey = encode(options.key);
  346. const encodedValue = encode(options.value);
  347. // Clean & sanitize options
  348. const expires = `; expires=${(options.expires || '').replace('expires=', '')}`; // Default is "; expires="
  349. const path = (options.path || '/').replace('path=', ''); // Default is "path=/"
  350. const domain = options.url != null && options.url.length > 0 ? `domain=${options.url}` : '';
  351. document.cookie = `${encodedKey}=${encodedValue || ''}${expires}; path=${path}; ${domain};`;
  352. }
  353. catch (error) {
  354. return Promise.reject(error);
  355. }
  356. }
  357. async deleteCookie(options) {
  358. try {
  359. document.cookie = `${options.key}=; Max-Age=0`;
  360. }
  361. catch (error) {
  362. return Promise.reject(error);
  363. }
  364. }
  365. async clearCookies() {
  366. try {
  367. const cookies = document.cookie.split(';') || [];
  368. for (const cookie of cookies) {
  369. document.cookie = cookie.replace(/^ +/, '').replace(/=.*/, `=;expires=${new Date().toUTCString()};path=/`);
  370. }
  371. }
  372. catch (error) {
  373. return Promise.reject(error);
  374. }
  375. }
  376. async clearAllCookies() {
  377. try {
  378. await this.clearCookies();
  379. }
  380. catch (error) {
  381. return Promise.reject(error);
  382. }
  383. }
  384. }
  385. const CapacitorCookies = registerPlugin('CapacitorCookies', {
  386. web: () => new CapacitorCookiesPluginWeb(),
  387. });
  388. // UTILITY FUNCTIONS
  389. /**
  390. * Read in a Blob value and return it as a base64 string
  391. * @param blob The blob value to convert to a base64 string
  392. */
  393. const readBlobAsBase64 = async (blob) => new Promise((resolve, reject) => {
  394. const reader = new FileReader();
  395. reader.onload = () => {
  396. const base64String = reader.result;
  397. // remove prefix "data:application/pdf;base64,"
  398. resolve(base64String.indexOf(',') >= 0 ? base64String.split(',')[1] : base64String);
  399. };
  400. reader.onerror = (error) => reject(error);
  401. reader.readAsDataURL(blob);
  402. });
  403. /**
  404. * Normalize an HttpHeaders map by lowercasing all of the values
  405. * @param headers The HttpHeaders object to normalize
  406. */
  407. const normalizeHttpHeaders = (headers = {}) => {
  408. const originalKeys = Object.keys(headers);
  409. const loweredKeys = Object.keys(headers).map((k) => k.toLocaleLowerCase());
  410. const normalized = loweredKeys.reduce((acc, key, index) => {
  411. acc[key] = headers[originalKeys[index]];
  412. return acc;
  413. }, {});
  414. return normalized;
  415. };
  416. /**
  417. * Builds a string of url parameters that
  418. * @param params A map of url parameters
  419. * @param shouldEncode true if you should encodeURIComponent() the values (true by default)
  420. */
  421. const buildUrlParams = (params, shouldEncode = true) => {
  422. if (!params)
  423. return null;
  424. const output = Object.entries(params).reduce((accumulator, entry) => {
  425. const [key, value] = entry;
  426. let encodedValue;
  427. let item;
  428. if (Array.isArray(value)) {
  429. item = '';
  430. value.forEach((str) => {
  431. encodedValue = shouldEncode ? encodeURIComponent(str) : str;
  432. item += `${key}=${encodedValue}&`;
  433. });
  434. // last character will always be "&" so slice it off
  435. item.slice(0, -1);
  436. }
  437. else {
  438. encodedValue = shouldEncode ? encodeURIComponent(value) : value;
  439. item = `${key}=${encodedValue}`;
  440. }
  441. return `${accumulator}&${item}`;
  442. }, '');
  443. // Remove initial "&" from the reduce
  444. return output.substr(1);
  445. };
  446. /**
  447. * Build the RequestInit object based on the options passed into the initial request
  448. * @param options The Http plugin options
  449. * @param extra Any extra RequestInit values
  450. */
  451. const buildRequestInit = (options, extra = {}) => {
  452. const output = Object.assign({ method: options.method || 'GET', headers: options.headers }, extra);
  453. // Get the content-type
  454. const headers = normalizeHttpHeaders(options.headers);
  455. const type = headers['content-type'] || '';
  456. // If body is already a string, then pass it through as-is.
  457. if (typeof options.data === 'string') {
  458. output.body = options.data;
  459. }
  460. // Build request initializers based off of content-type
  461. else if (type.includes('application/x-www-form-urlencoded')) {
  462. const params = new URLSearchParams();
  463. for (const [key, value] of Object.entries(options.data || {})) {
  464. params.set(key, value);
  465. }
  466. output.body = params.toString();
  467. }
  468. else if (type.includes('multipart/form-data') || options.data instanceof FormData) {
  469. const form = new FormData();
  470. if (options.data instanceof FormData) {
  471. options.data.forEach((value, key) => {
  472. form.append(key, value);
  473. });
  474. }
  475. else {
  476. for (const key of Object.keys(options.data)) {
  477. form.append(key, options.data[key]);
  478. }
  479. }
  480. output.body = form;
  481. const headers = new Headers(output.headers);
  482. headers.delete('content-type'); // content-type will be set by `window.fetch` to includy boundary
  483. output.headers = headers;
  484. }
  485. else if (type.includes('application/json') || typeof options.data === 'object') {
  486. output.body = JSON.stringify(options.data);
  487. }
  488. return output;
  489. };
  490. // WEB IMPLEMENTATION
  491. class CapacitorHttpPluginWeb extends WebPlugin {
  492. /**
  493. * Perform an Http request given a set of options
  494. * @param options Options to build the HTTP request
  495. */
  496. async request(options) {
  497. const requestInit = buildRequestInit(options, options.webFetchExtra);
  498. const urlParams = buildUrlParams(options.params, options.shouldEncodeUrlParams);
  499. const url = urlParams ? `${options.url}?${urlParams}` : options.url;
  500. const response = await fetch(url, requestInit);
  501. const contentType = response.headers.get('content-type') || '';
  502. // Default to 'text' responseType so no parsing happens
  503. let { responseType = 'text' } = response.ok ? options : {};
  504. // If the response content-type is json, force the response to be json
  505. if (contentType.includes('application/json')) {
  506. responseType = 'json';
  507. }
  508. let data;
  509. let blob;
  510. switch (responseType) {
  511. case 'arraybuffer':
  512. case 'blob':
  513. blob = await response.blob();
  514. data = await readBlobAsBase64(blob);
  515. break;
  516. case 'json':
  517. data = await response.json();
  518. break;
  519. case 'document':
  520. case 'text':
  521. default:
  522. data = await response.text();
  523. }
  524. // Convert fetch headers to Capacitor HttpHeaders
  525. const headers = {};
  526. response.headers.forEach((value, key) => {
  527. headers[key] = value;
  528. });
  529. return {
  530. data,
  531. headers,
  532. status: response.status,
  533. url: response.url,
  534. };
  535. }
  536. /**
  537. * Perform an Http GET request given a set of options
  538. * @param options Options to build the HTTP request
  539. */
  540. async get(options) {
  541. return this.request(Object.assign(Object.assign({}, options), { method: 'GET' }));
  542. }
  543. /**
  544. * Perform an Http POST request given a set of options
  545. * @param options Options to build the HTTP request
  546. */
  547. async post(options) {
  548. return this.request(Object.assign(Object.assign({}, options), { method: 'POST' }));
  549. }
  550. /**
  551. * Perform an Http PUT request given a set of options
  552. * @param options Options to build the HTTP request
  553. */
  554. async put(options) {
  555. return this.request(Object.assign(Object.assign({}, options), { method: 'PUT' }));
  556. }
  557. /**
  558. * Perform an Http PATCH request given a set of options
  559. * @param options Options to build the HTTP request
  560. */
  561. async patch(options) {
  562. return this.request(Object.assign(Object.assign({}, options), { method: 'PATCH' }));
  563. }
  564. /**
  565. * Perform an Http DELETE request given a set of options
  566. * @param options Options to build the HTTP request
  567. */
  568. async delete(options) {
  569. return this.request(Object.assign(Object.assign({}, options), { method: 'DELETE' }));
  570. }
  571. }
  572. const CapacitorHttp = registerPlugin('CapacitorHttp', {
  573. web: () => new CapacitorHttpPluginWeb(),
  574. });
  575. /******** END HTTP PLUGIN ********/
  576. export { Capacitor, CapacitorCookies, CapacitorException, CapacitorHttp, ExceptionCode, WebPlugin, WebView, buildRequestInit, registerPlugin };
  577. //# sourceMappingURL=index.js.map