UNPKG

@capacitor/core

Version:

Capacitor: Cross-platform apps with JavaScript and the web

594 lines (586 loc) 22.6 kB
/*! Capacitor: https://capacitorjs.com/ - MIT License */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); exports.ExceptionCode = void 0; (function (ExceptionCode) { /** * API is not implemented. * * This usually means the API can't be used because it is not implemented for * the current platform. */ ExceptionCode["Unimplemented"] = "UNIMPLEMENTED"; /** * API is not available. * * This means the API can't be used right now because: * - it is currently missing a prerequisite, such as network connectivity * - it requires a particular platform or browser version */ ExceptionCode["Unavailable"] = "UNAVAILABLE"; })(exports.ExceptionCode || (exports.ExceptionCode = {})); class CapacitorException extends Error { constructor(message, code, data) { super(message); this.message = message; this.code = code; this.data = data; } } const getPlatformId = (win) => { var _a, _b; if (win === null || win === void 0 ? void 0 : win.androidBridge) { return 'android'; } 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) { return 'ios'; } else { return 'web'; } }; const createCapacitor = (win) => { const capCustomPlatform = win.CapacitorCustomPlatform || null; const cap = win.Capacitor || {}; const Plugins = (cap.Plugins = cap.Plugins || {}); const getPlatform = () => { return capCustomPlatform !== null ? capCustomPlatform.name : getPlatformId(win); }; const isNativePlatform = () => getPlatform() !== 'web'; const isPluginAvailable = (pluginName) => { const plugin = registeredPlugins.get(pluginName); if (plugin === null || plugin === void 0 ? void 0 : plugin.platforms.has(getPlatform())) { // JS implementation available for the current platform. return true; } if (getPluginHeader(pluginName)) { // Native implementation available. return true; } return false; }; const getPluginHeader = (pluginName) => { var _a; return (_a = cap.PluginHeaders) === null || _a === void 0 ? void 0 : _a.find((h) => h.name === pluginName); }; const handleError = (err) => win.console.error(err); const registeredPlugins = new Map(); const registerPlugin = (pluginName, jsImplementations = {}) => { const registeredPlugin = registeredPlugins.get(pluginName); if (registeredPlugin) { console.warn(`Capacitor plugin "${pluginName}" already registered. Cannot register plugins twice.`); return registeredPlugin.proxy; } const platform = getPlatform(); const pluginHeader = getPluginHeader(pluginName); let jsImplementation; const loadPluginImplementation = async () => { if (!jsImplementation && platform in jsImplementations) { jsImplementation = typeof jsImplementations[platform] === 'function' ? (jsImplementation = await jsImplementations[platform]()) : (jsImplementation = jsImplementations[platform]); } else if (capCustomPlatform !== null && !jsImplementation && 'web' in jsImplementations) { jsImplementation = typeof jsImplementations['web'] === 'function' ? (jsImplementation = await jsImplementations['web']()) : (jsImplementation = jsImplementations['web']); } return jsImplementation; }; const createPluginMethod = (impl, prop) => { var _a, _b; if (pluginHeader) { const methodHeader = pluginHeader === null || pluginHeader === void 0 ? void 0 : pluginHeader.methods.find((m) => prop === m.name); if (methodHeader) { if (methodHeader.rtype === 'promise') { return (options) => cap.nativePromise(pluginName, prop.toString(), options); } else { return (options, callback) => cap.nativeCallback(pluginName, prop.toString(), options, callback); } } else if (impl) { return (_a = impl[prop]) === null || _a === void 0 ? void 0 : _a.bind(impl); } } else if (impl) { return (_b = impl[prop]) === null || _b === void 0 ? void 0 : _b.bind(impl); } else { throw new CapacitorException(`"${pluginName}" plugin is not implemented on ${platform}`, exports.ExceptionCode.Unimplemented); } }; const createPluginMethodWrapper = (prop) => { let remove; const wrapper = (...args) => { const p = loadPluginImplementation().then((impl) => { const fn = createPluginMethod(impl, prop); if (fn) { const p = fn(...args); remove = p === null || p === void 0 ? void 0 : p.remove; return p; } else { throw new CapacitorException(`"${pluginName}.${prop}()" is not implemented on ${platform}`, exports.ExceptionCode.Unimplemented); } }); if (prop === 'addListener') { p.remove = async () => remove(); } return p; }; // Some flair ✨ wrapper.toString = () => `${prop.toString()}() { [capacitor code] }`; Object.defineProperty(wrapper, 'name', { value: prop, writable: false, configurable: false, }); return wrapper; }; const addListener = createPluginMethodWrapper('addListener'); const removeListener = createPluginMethodWrapper('removeListener'); const addListenerNative = (eventName, callback) => { const call = addListener({ eventName }, callback); const remove = async () => { const callbackId = await call; removeListener({ eventName, callbackId, }, callback); }; const p = new Promise((resolve) => call.then(() => resolve({ remove }))); p.remove = async () => { console.warn(`Using addListener() without 'await' is deprecated.`); await remove(); }; return p; }; const proxy = new Proxy({}, { get(_, prop) { switch (prop) { // https://github.com/facebook/react/issues/20030 case '$$typeof': return undefined; case 'toJSON': return () => ({}); case 'addListener': return pluginHeader ? addListenerNative : addListener; case 'removeListener': return removeListener; default: return createPluginMethodWrapper(prop); } }, }); Plugins[pluginName] = proxy; registeredPlugins.set(pluginName, { name: pluginName, proxy, platforms: new Set([...Object.keys(jsImplementations), ...(pluginHeader ? [platform] : [])]), }); return proxy; }; // Add in convertFileSrc for web, it will already be available in native context if (!cap.convertFileSrc) { cap.convertFileSrc = (filePath) => filePath; } cap.getPlatform = getPlatform; cap.handleError = handleError; cap.isNativePlatform = isNativePlatform; cap.isPluginAvailable = isPluginAvailable; cap.registerPlugin = registerPlugin; cap.Exception = CapacitorException; cap.DEBUG = !!cap.DEBUG; cap.isLoggingEnabled = !!cap.isLoggingEnabled; return cap; }; const initCapacitorGlobal = (win) => (win.Capacitor = createCapacitor(win)); const Capacitor = /*#__PURE__*/ initCapacitorGlobal(typeof globalThis !== 'undefined' ? globalThis : typeof self !== 'undefined' ? self : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : {}); const registerPlugin = Capacitor.registerPlugin; /** * Base class web plugins should extend. */ class WebPlugin { constructor() { this.listeners = {}; this.retainedEventArguments = {}; this.windowListeners = {}; } addListener(eventName, listenerFunc) { let firstListener = false; const listeners = this.listeners[eventName]; if (!listeners) { this.listeners[eventName] = []; firstListener = true; } this.listeners[eventName].push(listenerFunc); // If we haven't added a window listener for this event and it requires one, // go ahead and add it const windowListener = this.windowListeners[eventName]; if (windowListener && !windowListener.registered) { this.addWindowListener(windowListener); } if (firstListener) { this.sendRetainedArgumentsForEvent(eventName); } const remove = async () => this.removeListener(eventName, listenerFunc); const p = Promise.resolve({ remove }); return p; } async removeAllListeners() { this.listeners = {}; for (const listener in this.windowListeners) { this.removeWindowListener(this.windowListeners[listener]); } this.windowListeners = {}; } notifyListeners(eventName, data, retainUntilConsumed) { const listeners = this.listeners[eventName]; if (!listeners) { if (retainUntilConsumed) { let args = this.retainedEventArguments[eventName]; if (!args) { args = []; } args.push(data); this.retainedEventArguments[eventName] = args; } return; } listeners.forEach((listener) => listener(data)); } hasListeners(eventName) { return !!this.listeners[eventName].length; } registerWindowListener(windowEventName, pluginEventName) { this.windowListeners[pluginEventName] = { registered: false, windowEventName, pluginEventName, handler: (event) => { this.notifyListeners(pluginEventName, event); }, }; } unimplemented(msg = 'not implemented') { return new Capacitor.Exception(msg, exports.ExceptionCode.Unimplemented); } unavailable(msg = 'not available') { return new Capacitor.Exception(msg, exports.ExceptionCode.Unavailable); } async removeListener(eventName, listenerFunc) { const listeners = this.listeners[eventName]; if (!listeners) { return; } const index = listeners.indexOf(listenerFunc); this.listeners[eventName].splice(index, 1); // If there are no more listeners for this type of event, // remove the window listener if (!this.listeners[eventName].length) { this.removeWindowListener(this.windowListeners[eventName]); } } addWindowListener(handle) { window.addEventListener(handle.windowEventName, handle.handler); handle.registered = true; } removeWindowListener(handle) { if (!handle) { return; } window.removeEventListener(handle.windowEventName, handle.handler); handle.registered = false; } sendRetainedArgumentsForEvent(eventName) { const args = this.retainedEventArguments[eventName]; if (!args) { return; } delete this.retainedEventArguments[eventName]; args.forEach((arg) => { this.notifyListeners(eventName, arg); }); } } const WebView = /*#__PURE__*/ registerPlugin('WebView'); /******** END WEB VIEW PLUGIN ********/ /******** COOKIES PLUGIN ********/ /** * Safely web encode a string value (inspired by js-cookie) * @param str The string value to encode */ const encode = (str) => encodeURIComponent(str) .replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent) .replace(/[()]/g, escape); /** * Safely web decode a string value (inspired by js-cookie) * @param str The string value to decode */ const decode = (str) => str.replace(/(%[\dA-F]{2})+/gi, decodeURIComponent); class CapacitorCookiesPluginWeb extends WebPlugin { async getCookies() { const cookies = document.cookie; const cookieMap = {}; cookies.split(';').forEach((cookie) => { if (cookie.length <= 0) return; // Replace first "=" with CAP_COOKIE to prevent splitting on additional "=" let [key, value] = cookie.replace(/=/, 'CAP_COOKIE').split('CAP_COOKIE'); key = decode(key).trim(); value = decode(value).trim(); cookieMap[key] = value; }); return cookieMap; } async setCookie(options) { try { // Safely Encoded Key/Value const encodedKey = encode(options.key); const encodedValue = encode(options.value); // Clean & sanitize options const expires = `; expires=${(options.expires || '').replace('expires=', '')}`; // Default is "; expires=" const path = (options.path || '/').replace('path=', ''); // Default is "path=/" const domain = options.url != null && options.url.length > 0 ? `domain=${options.url}` : ''; document.cookie = `${encodedKey}=${encodedValue || ''}${expires}; path=${path}; ${domain};`; } catch (error) { return Promise.reject(error); } } async deleteCookie(options) { try { document.cookie = `${options.key}=; Max-Age=0`; } catch (error) { return Promise.reject(error); } } async clearCookies() { try { const cookies = document.cookie.split(';') || []; for (const cookie of cookies) { document.cookie = cookie.replace(/^ +/, '').replace(/=.*/, `=;expires=${new Date().toUTCString()};path=/`); } } catch (error) { return Promise.reject(error); } } async clearAllCookies() { try { await this.clearCookies(); } catch (error) { return Promise.reject(error); } } } const CapacitorCookies = registerPlugin('CapacitorCookies', { web: () => new CapacitorCookiesPluginWeb(), }); // UTILITY FUNCTIONS /** * Read in a Blob value and return it as a base64 string * @param blob The blob value to convert to a base64 string */ const readBlobAsBase64 = async (blob) => new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => { const base64String = reader.result; // remove prefix "data:application/pdf;base64," resolve(base64String.indexOf(',') >= 0 ? base64String.split(',')[1] : base64String); }; reader.onerror = (error) => reject(error); reader.readAsDataURL(blob); }); /** * Normalize an HttpHeaders map by lowercasing all of the values * @param headers The HttpHeaders object to normalize */ const normalizeHttpHeaders = (headers = {}) => { const originalKeys = Object.keys(headers); const loweredKeys = Object.keys(headers).map((k) => k.toLocaleLowerCase()); const normalized = loweredKeys.reduce((acc, key, index) => { acc[key] = headers[originalKeys[index]]; return acc; }, {}); return normalized; }; /** * Builds a string of url parameters that * @param params A map of url parameters * @param shouldEncode true if you should encodeURIComponent() the values (true by default) */ const buildUrlParams = (params, shouldEncode = true) => { if (!params) return null; const output = Object.entries(params).reduce((accumulator, entry) => { const [key, value] = entry; let encodedValue; let item; if (Array.isArray(value)) { item = ''; value.forEach((str) => { encodedValue = shouldEncode ? encodeURIComponent(str) : str; item += `${key}=${encodedValue}&`; }); // last character will always be "&" so slice it off item.slice(0, -1); } else { encodedValue = shouldEncode ? encodeURIComponent(value) : value; item = `${key}=${encodedValue}`; } return `${accumulator}&${item}`; }, ''); // Remove initial "&" from the reduce return output.substr(1); }; /** * Build the RequestInit object based on the options passed into the initial request * @param options The Http plugin options * @param extra Any extra RequestInit values */ const buildRequestInit = (options, extra = {}) => { const output = Object.assign({ method: options.method || 'GET', headers: options.headers }, extra); // Get the content-type const headers = normalizeHttpHeaders(options.headers); const type = headers['content-type'] || ''; // If body is already a string, then pass it through as-is. if (typeof options.data === 'string') { output.body = options.data; } // Build request initializers based off of content-type else if (type.includes('application/x-www-form-urlencoded')) { const params = new URLSearchParams(); for (const [key, value] of Object.entries(options.data || {})) { params.set(key, value); } output.body = params.toString(); } else if (type.includes('multipart/form-data') || options.data instanceof FormData) { const form = new FormData(); if (options.data instanceof FormData) { options.data.forEach((value, key) => { form.append(key, value); }); } else { for (const key of Object.keys(options.data)) { form.append(key, options.data[key]); } } output.body = form; const headers = new Headers(output.headers); headers.delete('content-type'); // content-type will be set by `window.fetch` to includy boundary output.headers = headers; } else if (type.includes('application/json') || typeof options.data === 'object') { output.body = JSON.stringify(options.data); } return output; }; // WEB IMPLEMENTATION class CapacitorHttpPluginWeb extends WebPlugin { /** * Perform an Http request given a set of options * @param options Options to build the HTTP request */ async request(options) { const requestInit = buildRequestInit(options, options.webFetchExtra); const urlParams = buildUrlParams(options.params, options.shouldEncodeUrlParams); const url = urlParams ? `${options.url}?${urlParams}` : options.url; const response = await fetch(url, requestInit); const contentType = response.headers.get('content-type') || ''; // Default to 'text' responseType so no parsing happens let { responseType = 'text' } = response.ok ? options : {}; // If the response content-type is json, force the response to be json if (contentType.includes('application/json')) { responseType = 'json'; } let data; let blob; switch (responseType) { case 'arraybuffer': case 'blob': blob = await response.blob(); data = await readBlobAsBase64(blob); break; case 'json': data = await response.json(); break; case 'document': case 'text': default: data = await response.text(); } // Convert fetch headers to Capacitor HttpHeaders const headers = {}; response.headers.forEach((value, key) => { headers[key] = value; }); return { data, headers, status: response.status, url: response.url, }; } /** * Perform an Http GET request given a set of options * @param options Options to build the HTTP request */ async get(options) { return this.request(Object.assign(Object.assign({}, options), { method: 'GET' })); } /** * Perform an Http POST request given a set of options * @param options Options to build the HTTP request */ async post(options) { return this.request(Object.assign(Object.assign({}, options), { method: 'POST' })); } /** * Perform an Http PUT request given a set of options * @param options Options to build the HTTP request */ async put(options) { return this.request(Object.assign(Object.assign({}, options), { method: 'PUT' })); } /** * Perform an Http PATCH request given a set of options * @param options Options to build the HTTP request */ async patch(options) { return this.request(Object.assign(Object.assign({}, options), { method: 'PATCH' })); } /** * Perform an Http DELETE request given a set of options * @param options Options to build the HTTP request */ async delete(options) { return this.request(Object.assign(Object.assign({}, options), { method: 'DELETE' })); } } const CapacitorHttp = registerPlugin('CapacitorHttp', { web: () => new CapacitorHttpPluginWeb(), }); /******** END HTTP PLUGIN ********/ exports.Capacitor = Capacitor; exports.CapacitorCookies = CapacitorCookies; exports.CapacitorException = CapacitorException; exports.CapacitorHttp = CapacitorHttp; exports.WebPlugin = WebPlugin; exports.WebView = WebView; exports.buildRequestInit = buildRequestInit; exports.registerPlugin = registerPlugin; //# sourceMappingURL=index.cjs.js.map