@capacitor/core
Version:
Capacitor: Cross-platform apps with JavaScript and the web
594 lines (586 loc) • 22.6 kB
JavaScript
/*! Capacitor: https://capacitorjs.com/ - MIT License */
;
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