chore(): organize and enhance decorators (#1171)

* fix/add decorators

* fix google maps design

* chore(): add root tsconfig to resolve imports in IDE

* updates

* more fixes
This commit is contained in:
Ibby Hadeed
2017-03-11 11:03:40 -05:00
committed by GitHub
parent 5b03b578dc
commit 63c34cade4
10 changed files with 601 additions and 545 deletions
+2 -3
View File
@@ -3,7 +3,6 @@ declare var window;
export function checkReady() {
const DEVICE_READY_TIMEOUT = 5000;
// To help developers using cordova, we listen for the device ready event and
// log an error if it didn't fire in a reasonable amount of time. Generally,
// when this happens, developers should remove and reinstall plugins, since
@@ -12,13 +11,13 @@ export function checkReady() {
let didFireReady = false;
document.addEventListener('deviceready', () => {
console.log('DEVICE READY FIRED AFTER', (Date.now() - before), 'ms');
console.log(`Ionic Native: deviceready event fired after ${(Date.now() - before)} ms`);
didFireReady = true;
});
setTimeout(() => {
if (!didFireReady && window.cordova) {
console.warn(`Native: deviceready did not fire within ${DEVICE_READY_TIMEOUT}ms. This can happen when plugins are in an inconsistent state. Try removing plugins from plugins/ and reinstalling them.`);
console.warn(`Ionic Native: deviceready did not fire within ${DEVICE_READY_TIMEOUT}ms. This can happen when plugins are in an inconsistent state. Try removing plugins from plugins/ and reinstalling them.`);
}
}, DEVICE_READY_TIMEOUT);
}
+339
View File
@@ -0,0 +1,339 @@
import { instanceAvailability, checkAvailability, wrap, wrapInstance, overrideFunction } from './plugin';
import { getPlugin, getPromise } from './util';
import { Observable } from 'rxjs/Observable';
export interface PluginConfig {
/**
* Plugin name, this should match the class name
*/
pluginName: string;
/**
* Plugin NPM package name
*/
plugin: string;
/**
* Plugin object reference
*/
pluginRef?: string;
/**
* Github repository URL
*/
repo?: string;
/**
* Custom install command
*/
install?: string;
/**
* Available installation variables
*/
installVariables?: string[];
/**
* Supported platforms
*/
platforms?: string[];
}
export interface CordovaOptions {
/**
* Set to true if the wrapped method is a sync function
*/
sync?: boolean;
/**
* Callback order. Set to reverse if the success/error callbacks are the first 2 arguments that the wrapped method takes.
*/
callbackOrder?: 'reverse';
/**
* Callback style
*/
callbackStyle?: 'node' | 'object';
/**
* Set a custom index for the success callback function. This doesn't work if callbackOrder or callbackStyle are set.
*/
successIndex?: number;
/**
* Set a custom index for the error callback function. This doesn't work if callbackOrder or callbackStyle are set.
*/
errorIndex?: number;
/**
* Success function property name. This must be set if callbackStyle is set to object.
*/
successName?: string;
/**
* Error function property name. This must be set if callbackStyle is set to object.
*/
errorName?: string;
/**
* Set to true to return an observable
*/
observable?: boolean;
/**
* If observable is set to true, this can be set to a different function name that will cancel the observable.
*/
clearFunction?: string;
/**
* This can be used if clearFunction is set. Set this to true to call the clearFunction with the same arguments used in the initial function.
*/
clearWithArgs?: boolean;
/**
* Creates an observable that wraps a global event. Replaces document.addEventListener
*/
eventObservable?: boolean;
/**
* Event name, this must be set if eventObservable is set to true
*/
event?: string;
/**
* Element to attach the event listener to, this is optional, defaults to `window`
*/
element?: any;
/**
* Set to true if the wrapped method returns a promise
*/
otherPromise?: boolean;
/**
* Supported platforms
*/
platforms?: string[];
}
export interface CordovaCheckOptions {
promise?: boolean;
observable?: boolean;
}
export interface CordovaFiniteObservableOptions extends CordovaOptions {
/**
* Function that gets a result returned from plugin's success callback, and decides whether it is last value and observable should complete.
*/
resultFinalPredicate?: (result: any) => boolean;
/**
* Function that gets called after resultFinalPredicate, and removes service data that indicates end of stream from the result.
*/
resultTransform?: (result: any) => any;
}
/**
* @private
*/
export function InstanceCheck() {
return (pluginObj: Object, methodName: string, descriptor: TypedPropertyDescriptor<any>) => {
return {
value: function(...args: any[]) {
if (instanceAvailability(pluginObj, methodName)) {
descriptor.value.apply(this, args);
} else {
return null;
}
}
};
};
}
/**
* Executes function only if plugin is available
* @private
*/
export function CordovaCheck(opts: CordovaCheckOptions = {}) {
return (pluginObj: Object, methodName: string, descriptor: TypedPropertyDescriptor<any>): TypedPropertyDescriptor<any> => {
return {
value: function(...args: any[]): any {
if (checkAvailability(pluginObj, methodName) === true) {
descriptor.value.apply(this, args);
} else {
if (opts.promise) {
return getPromise(() => {});
} else if (opts.observable) {
return new Observable<any>(observer => observer.complete());
}
return null;
}
}
};
};
}
/**
* @private
*
* Class decorator specifying Plugin metadata. Required for all plugins.
*
* @usage
* ```typescript
* @Plugin({
* pluginName: 'MyPlugin',
* plugin: 'cordova-plugin-myplugin',
* pluginRef: 'window.myplugin'
* })
* export class MyPlugin {
*
* // Plugin wrappers, properties, and functions go here ...
*
* }
* ```
*/
export function Plugin(config: PluginConfig) {
return function(cls) {
// Add these fields to the class
for (let k in config) {
cls[k] = config[k];
}
cls['installed'] = function(printWarning?: boolean) {
return !!getPlugin(config.pluginRef);
};
cls['getPlugin'] = function() {
return getPlugin(config.pluginRef);
};
cls['checkInstall'] = function() {
return checkAvailability(cls) === true;
};
cls['getPluginName'] = function() {
return config.pluginName;
};
cls['getPluginRef'] = function() {
return config.pluginRef;
};
cls['getPluginInstallName'] = function() {
return config.plugin;
};
cls['getPluginRepo'] = function() {
return config.repo;
};
cls['getSupportedPlatforms'] = function() {
return config.platforms;
};
return cls;
};
}
/**
* @private
*
* Wrap a stub function in a call to a Cordova plugin, checking if both Cordova
* and the required plugin are installed.
*/
export function Cordova(opts: CordovaOptions = {}) {
return (target: Object, methodName: string, descriptor: TypedPropertyDescriptor<any>) => {
return {
value: function(...args: any[]) {
return wrap(this, methodName, opts).apply(this, args);
}
};
};
}
/**
* @private
*
* Wrap an instance method
*/
export function CordovaInstance(opts: any = {}) {
return (target: Object, methodName: string) => {
return {
value: function(...args: any[]) {
return wrapInstance(this, methodName, opts).apply(this, args);
}
};
};
}
/**
* @private
*
*
* Before calling the original method, ensure Cordova and the plugin are installed.
*/
export function CordovaProperty(target: any, key: string) {
Object.defineProperty(target, key, {
get: () => {
if (checkAvailability(target, key) === true) {
return getPlugin(target.constructor.getPluginRef())[key];
} else {
return null;
}
},
set: (value) => {
if (checkAvailability(target, key) === true) {
getPlugin(target.constructor.getPluginRef())[key] = value;
}
}
});
}
/**
* @private
* @param target
* @param key
* @constructor
*/
export function InstanceProperty(target: any, key: string) {
Object.defineProperty(target, key, {
get: function(){
return this._objectInstance[key];
},
set: function(value){
this._objectInstance[key] = value;
}
});
}
/**
* @private
*
* Wrap a stub function in a call to a Cordova plugin, checking if both Cordova
* and the required plugin are installed.
*/
export function CordovaFunctionOverride(opts: any = {}) {
return (target: Object, methodName: string, descriptor: TypedPropertyDescriptor<any>) => {
return {
value: function(...args: any[]) {
return overrideFunction(this, methodName, opts);
}
};
};
}
/**
* @private
*
* Wraps method that returns an observable that can be completed. Provided opts.resultFinalPredicate dictates when the observable completes.
*
*/
export function CordovaFiniteObservable(opts: CordovaFiniteObservableOptions = {}) {
if (opts.observable === false) {
throw new Error('CordovaFiniteObservable decorator can only be used on methods that returns observable. Please provide correct option.');
}
opts.observable = true;
return (target: Object, methodName: string, descriptor: TypedPropertyDescriptor<any>) => {
return {
value: function(...args: any[]) {
let wrappedObservable: Observable<any> = wrap(this, methodName, opts).apply(this, args);
return new Observable<any>((observer) => {
let wrappedSubscription = wrappedObservable.subscribe({
next: (x) => {
observer.next(opts.resultTransform ? opts.resultTransform(x) : x);
if (opts.resultFinalPredicate && opts.resultFinalPredicate(x)) {
observer.complete();
}
},
error: (err) => { observer.error(err); },
complete: () => { observer.complete(); }
});
return () => {
wrappedSubscription.unsubscribe();
};
});
}
};
};
}
+2
View File
@@ -1 +1,3 @@
export * from './plugin';
export * from './decorators';
export * from './util';
+113 -420
View File
@@ -1,151 +1,61 @@
import { get } from './util';
import { get, getPlugin, getPromise, cordovaWarn, pluginWarn } from './util';
import { checkReady } from './bootstrap';
import { CordovaOptions } from './decorators';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/fromEvent';
checkReady();
declare var window;
declare var Promise;
/**
* Checks if plugin/cordova is available
* @return {boolean | { error: string } }
* @private
*/
export interface PluginConfig {
/**
* Plugin name, this should match the class name
*/
pluginName: string;
/**
* Plugin NPM package name
*/
plugin: string;
/**
* Plugin object reference
*/
pluginRef: string;
/**
* Github repository URL
*/
repo: string;
/**
* Custom install command
*/
install?: string;
/**
* Available installation variables
*/
installVariables?: string[];
/**
* Supported platforms
*/
platforms?: string[];
export function checkAvailability(pluginRef: string, methodName?: string, pluginName?: string);
export function checkAvailability(pluginObj: any, methodName?: string, pluginName?: string);
export function checkAvailability(plugin: any, methodName?: string, pluginName?: string): boolean | { error: string } {
let pluginRef, pluginInstance;
if (typeof plugin === 'string') {
pluginRef = plugin;
} else {
pluginRef = plugin.constructor.getPluginRef();
pluginName = plugin.constructor.getPluginName();
}
pluginInstance = getPlugin(pluginRef);
if (!pluginInstance || (!!methodName && pluginInstance[methodName] === 'undefined')) {
if (!window.cordova) {
cordovaWarn(pluginName, methodName);
return {
error: 'cordova_not_available'
};
}
pluginWarn(pluginName, methodName);
return {
error: 'plugin_not_installed'
};
}
return true;
}
/**
* Checks if _objectInstance exists and has the method/property
* @private
*/
export interface CordovaOptions {
/**
* Set to true if the wrapped method is a sync function
*/
sync?: boolean;
/**
* Callback order. Set to reverse if the success/error callbacks are the first 2 arguments that the wrapped method takes.
*/
callbackOrder?: 'reverse';
/**
* Callback style
*/
callbackStyle?: 'node' | 'object';
/**
* Set a custom index for the success callback function. This doesn't work if callbackOrder or callbackStyle are set.
*/
successIndex?: number;
/**
* Set a custom index for the error callback function. This doesn't work if callbackOrder or callbackStyle are set.
*/
errorIndex?: number;
/**
* Success function property name. This must be set if callbackStyle is set to object.
*/
successName?: string;
/**
* Error function property name. This must be set if callbackStyle is set to object.
*/
errorName?: string;
/**
* Set to true to return an observable
*/
observable?: boolean;
/**
* If observable is set to true, this can be set to a different function name that will cancel the observable.
*/
clearFunction?: string;
/**
* This can be used if clearFunction is set. Set this to true to call the clearFunction with the same arguments used in the initial function.
*/
clearWithArgs?: boolean;
/**
* Creates an observable that wraps a global event. Replaces document.addEventListener
*/
eventObservable?: boolean;
/**
* Event name, this must be set if eventObservable is set to true
*/
event?: string;
/**
* Element to attach the event listener to, this is optional, defaults to `window`
*/
element?: any;
/**
* Set to true if the wrapped method returns a promise
*/
otherPromise?: boolean;
/**
* Supported platforms
*/
platforms?: string[];
export function instanceAvailability(pluginObj: any, methodName: string): boolean {
return pluginObj._objectInstance && pluginObj._objectInstance[methodName] !== 'undefined';
}
/**
* @private
* @param pluginRef
* @returns {null|*}
*/
export const getPlugin = function(pluginRef: string): any {
return get(window, pluginRef);
};
/**
* @private
* @param pluginObj
* @param method
*/
export const pluginWarn = function(pluginObj: any, method?: string) {
let pluginName = pluginObj.constructor.getPluginName(), plugin = pluginObj.constructor.getPlugin();
if (method) {
console.warn('Native: tried calling ' + pluginName + '.' + method + ', but the ' + pluginName + ' plugin is not installed.');
} else {
console.warn('Native: tried accessing the ' + pluginName + ' plugin but it\'s not installed.');
}
console.warn('Install the ' + pluginName + ' plugin: \'ionic plugin add ' + plugin + '\'');
};
/**
* @private
* @param pluginName
* @param method
*/
export const cordovaWarn = function(pluginName: string, method: string) {
if (method) {
console.warn('Native: tried calling ' + pluginName + '.' + method + ', but Cordova is not available. Make sure to include cordova.js or run in a device/simulator');
} else {
console.warn('Native: tried accessing the ' + pluginName + ' plugin but Cordova is not available. Make sure to include cordova.js or run in a device/simulator');
}
};
function setIndex(args: any[], opts: any = {}, resolve?: Function, reject?: Function): any {
// ignore resolve and reject in case sync
if (opts.sync) {
@@ -212,42 +122,15 @@ function callCordovaPlugin(pluginObj: any, methodName: string, args: any[], opts
// to our promise resolve/reject handlers.
args = setIndex(args, opts, resolve, reject);
let pluginInstance = getPlugin(pluginObj.constructor.getPluginRef());
const availabilityCheck = checkAvailability(pluginObj, methodName);
if (!pluginInstance || pluginInstance[methodName] === 'undefined') {
// Do this check in here in the case that the Web API for this plugin is available (for example, Geolocation).
if (!window.cordova) {
cordovaWarn(pluginObj.constructor.getPluginName(), methodName);
return {
error: 'cordova_not_available'
};
}
pluginWarn(pluginObj, methodName);
return {
error: 'plugin_not_installed'
};
if (availabilityCheck === true) {
const pluginInstance = getPlugin(pluginObj.constructor.getPluginRef());
return pluginInstance[methodName].apply(pluginInstance, args);
} else {
return availabilityCheck;
}
return pluginInstance[methodName].apply(pluginInstance, args);
}
/**
* @private
*/
export function getPromise(cb) {
const tryNativePromise = () => {
if (window.Promise) {
return new Promise((resolve, reject) => {
cb(resolve, reject);
});
} else {
console.error('No Promise support or polyfill found. To enable Ionic Native support, please add the es6-promise polyfill before this script, or run with a library like Angular 2 or on a recent browser.');
}
};
return tryNativePromise();
}
function wrapPromise(pluginObj: any, methodName: string, args: any[], opts: any = {}) {
@@ -281,6 +164,7 @@ function wrapObservable(pluginObj: any, methodName: string, args: any[], opts: a
let pluginResult = callCordovaPlugin(pluginObj, methodName, args, opts, observer.next.bind(observer), observer.error.bind(observer));
if (pluginResult && pluginResult.error) {
observer.error(pluginResult.error);
observer.complete();
}
return () => {
try {
@@ -299,50 +183,23 @@ function wrapObservable(pluginObj: any, methodName: string, args: any[], opts: a
}
function callInstance(pluginObj: any, methodName: string, args: any[], opts: any = {}, resolve?: Function, reject?: Function) {
args = setIndex(args, opts, resolve, reject);
return pluginObj._objectInstance[methodName].apply(pluginObj._objectInstance, args);
}
function wrapInstance(pluginObj: any, methodName: string, opts: any = {}) {
return (...args) => {
if (opts.sync) {
// Sync doesn't wrap the plugin with a promise or observable, it returns the result as-is
return callInstance(pluginObj, methodName, args, opts);
} else if (opts.observable) {
return new Observable(observer => {
let pluginResult = callInstance(pluginObj, methodName, args, opts, observer.next.bind(observer), observer.error.bind(observer));
return () => {
try {
if (opts.clearWithArgs) {
return pluginObj._objectInstance[opts.clearFunction].apply(pluginObj._objectInstance, args);
}
return pluginObj._objectInstance[opts.clearFunction].call(pluginObj, pluginResult);
} catch (e) {
console.warn('Unable to clear the previous observable watch for', pluginObj.constructor.getPluginName(), methodName);
console.error(e);
}
};
});
} else if (opts.otherPromise) {
return getPromise((resolve, reject) => {
let result = callInstance(pluginObj, methodName, args, opts, resolve, reject);
result.then(resolve, reject);
});
} else {
return getPromise((resolve, reject) => {
callInstance(pluginObj, methodName, args, opts, resolve, reject);
});
}
};
args = setIndex(args, opts, resolve, reject);
if (instanceAvailability(pluginObj, methodName)) {
return pluginObj._objectInstance[methodName].apply(pluginObj._objectInstance, args);
}
}
/**
* Wrap the event with an observable
* @private
* @param event even name
* @param element The element to attach the event listener to
* @returns {Observable}
*/
function wrapEventObservable(event: string, element: any = window): Observable<any> {
export function wrapEventObservable(event: string, element: any = window): Observable<any> {
return Observable.fromEvent(element, event);
}
@@ -352,39 +209,28 @@ function wrapEventObservable(event: string, element: any = window): Observable<a
*
* Unfortunately, this is brittle and would be better wrapped as an Observable. overrideFunction
* does just this.
* @private
*/
function overrideFunction(pluginObj: any, methodName: string, args: any[], opts: any = {}): Observable<any> {
export function overrideFunction(pluginObj: any, methodName: string, args: any[], opts: any = {}): Observable<any> {
return new Observable(observer => {
let pluginInstance = getPlugin(pluginObj.constructor.getPluginRef());
const availabilityCheck = checkAvailability(pluginObj, methodName);
if (!pluginInstance) {
// Do this check in here in the case that the Web API for this plugin is available (for example, Geolocation).
if (!window.cordova) {
cordovaWarn(pluginObj.constructor.getPluginName(), methodName);
observer.error({
error: 'cordova_not_available'
});
}
pluginWarn(pluginObj, methodName);
observer.error({
error: 'plugin_not_installed'
});
return;
if (availabilityCheck === true) {
const pluginInstance = getPlugin(pluginObj.constructor.getPluginRef());
pluginInstance[methodName] = observer.next.bind(observer);
return () => pluginInstance[methodName] = () => {};
} else {
observer.error(availabilityCheck);
observer.complete();
}
pluginInstance[methodName] = observer.next.bind(observer);
});
}
/**
* @private
* @param pluginObj
* @param methodName
* @param opts
* @returns {function(...[any]): (undefined|*|Observable|*|*)}
*/
export const wrap = function(pluginObj: any, methodName: string, opts: CordovaOptions = {}) {
return (...args) => {
@@ -403,213 +249,60 @@ export const wrap = function(pluginObj: any, methodName: string, opts: CordovaOp
};
};
/**
* @private
*
* Class decorator specifying Plugin metadata. Required for all plugins.
*
* @usage
* ```typescript
* @Plugin({
* pluginName: 'MyPlugin',
* plugin: 'cordova-plugin-myplugin',
* pluginRef: 'window.myplugin'
* })
* export class MyPlugin {
*
* // Plugin wrappers, properties, and functions go here ...
*
* }
* ```
*/
export function Plugin(config: PluginConfig) {
return function(cls) {
export function wrapInstance(pluginObj: any, methodName: string, opts: any = {}) {
return (...args) => {
if (opts.sync) {
return callInstance(pluginObj, methodName, args, opts);
} else if (opts.observable) {
return new Observable(observer => {
let pluginResult = callInstance(pluginObj, methodName, args, opts, observer.next.bind(observer), observer.error.bind(observer));
if (pluginResult && pluginResult.error) {
observer.error(pluginResult.error);
observer.complete();
}
return () => {
try {
if (opts.clearWithArgs) {
return pluginObj._objectInstance[opts.clearFunction].apply(pluginObj._objectInstance, args);
}
return pluginObj._objectInstance[opts.clearFunction].call(pluginObj, pluginResult);
} catch (e) {
console.warn('Unable to clear the previous observable watch for', pluginObj.constructor.getPluginName(), methodName);
console.error(e);
}
};
});
} else if (opts.otherPromise) {
return getPromise((resolve, reject) => {
let result = callInstance(pluginObj, methodName, args, opts, resolve, reject);
if (result && !result.error) {
result.then(resolve, reject);
}
});
} else {
let pluginResult, rej;
const p = getPromise((resolve, reject) => {
pluginResult = callInstance(pluginObj, methodName, args, opts, resolve, reject);
rej = reject;
});
if (pluginResult && pluginResult.error) {
p.catch(() => { });
rej(pluginResult.error);
}
return p;
// Add these fields to the class
for (let k in config) {
cls[k] = config[k];
}
cls['installed'] = function(printWarning?: boolean) {
return !!getPlugin(config.pluginRef);
};
cls['getPlugin'] = function() {
return getPlugin(config.pluginRef);
};
cls['checkInstall'] = function() {
let pluginInstance = getPlugin(config.pluginRef);
if (!pluginInstance) {
pluginWarn(cls);
return false;
}
return true;
};
cls['getPluginName'] = function() {
return config.pluginName;
};
cls['getPluginRef'] = function() {
return config.pluginRef;
};
cls['getPluginInstallName'] = function() {
return config.plugin;
};
cls['getPluginRepo'] = function() {
return config.repo;
};
cls['getSupportedPlatforms'] = function() {
return config.platforms;
};
return cls;
};
}
/**
* @private
*
* Wrap a stub function in a call to a Cordova plugin, checking if both Cordova
* and the required plugin are installed.
*/
export function Cordova(opts: CordovaOptions = {}) {
return (target: Object, methodName: string, descriptor: TypedPropertyDescriptor<any>) => {
return {
value: function(...args: any[]) {
return wrap(this, methodName, opts).apply(this, args);
}
};
};
}
/**
* @private
*
* Wrap an instance method
*/
export function CordovaInstance(opts: any = {}) {
return (target: Object, methodName: string) => {
return {
value: function(...args: any[]) {
return wrapInstance(this, methodName, opts).apply(this, args);
}
};
};
}
/**
* @private
*
*
* Before calling the original method, ensure Cordova and the plugin are installed.
*/
export function CordovaProperty(target: any, key: string) {
const exists = () => {
let pluginInstance = getPlugin(target.pluginRef);
if (!pluginInstance || typeof pluginInstance[key] === 'undefined') {
pluginWarn(target, key);
return false;
}
return true;
};
Object.defineProperty(target, key, {
get: () => {
if (exists()) {
return getPlugin(target.pluginRef)[key];
} else {
return null;
}
},
set: (value) => {
if (exists()) {
getPlugin(target.pluginRef)[key] = value;
}
}
});
}
/**
* @private
* @param target
* @param key
* @constructor
*/
export function InstanceProperty(target: any, key: string) {
Object.defineProperty(target, key, {
get: function(){
return this._objectInstance[key];
},
set: function(value){
this._objectInstance[key] = value;
}
});
}
/**
* @private
*
* Wrap a stub function in a call to a Cordova plugin, checking if both Cordova
* and the required plugin are installed.
*/
export function CordovaFunctionOverride(opts: any = {}) {
return (target: Object, methodName: string, descriptor: TypedPropertyDescriptor<any>) => {
return {
value: function(...args: any[]) {
return overrideFunction(this, methodName, opts);
}
};
};
}
/**
* @private
*/
export interface CordovaFiniteObservableOptions extends CordovaOptions {
/**
* Function that gets a result returned from plugin's success callback, and decides whether it is last value and observable should complete.
*/
resultFinalPredicate?: (result: any) => boolean;
/**
* Function that gets called after resultFinalPredicate, and removes service data that indicates end of stream from the result.
*/
resultTransform?: (result: any) => any;
}
/**
* @private
*
* Wraps method that returns an observable that can be completed. Provided opts.resultFinalPredicate dictates when the observable completes.
*
*/
export function CordovaFiniteObservable(opts: CordovaFiniteObservableOptions = {}) {
if (opts.observable === false) {
throw new Error('CordovaFiniteObservable decorator can only be used on methods that returns observable. Please provide correct option.');
}
opts.observable = true;
return (target: Object, methodName: string, descriptor: TypedPropertyDescriptor<any>) => {
return {
value: function(...args: any[]) {
let wrappedObservable: Observable<any> = wrap(this, methodName, opts).apply(this, args);
return new Observable<any>((observer) => {
let wrappedSubscription = wrappedObservable.subscribe({
next: (x) => {
observer.next(opts.resultTransform ? opts.resultTransform(x) : x);
if (opts.resultFinalPredicate && opts.resultFinalPredicate(x)) {
observer.complete();
}
},
error: (err) => { observer.error(err); },
complete: () => { observer.complete(); }
});
return () => {
wrappedSubscription.unsubscribe();
};
});
}
};
};
}
+62 -1
View File
@@ -1,7 +1,68 @@
declare var window: any;
/**
* @private
*/
export function get(obj, path) {
for (var i = 0, path = path.split('.'), len = path.length; i < len; i++) {
path = path.split('.');
for (let i = 0; i < path.length; i++) {
if (!obj) { return null; }
obj = obj[path[i]];
}
return obj;
}
/**
* @private
*/
export function getPromise(cb) {
const tryNativePromise = () => {
if (window.Promise) {
return new Promise((resolve, reject) => {
cb(resolve, reject);
});
} else {
console.error('No Promise support or polyfill found. To enable Ionic Native support, please add the es6-promise polyfill before this script, or run with a library like Angular 2 or on a recent browser.');
}
};
return tryNativePromise();
}
/**
* @private
* @param pluginRef
* @returns {null|*}
*/
export function getPlugin(pluginRef: string): any {
return get(window, pluginRef);
};
/**
* @private
*/
export const pluginWarn = function(pluginName: string, plugin?: string, method?: string) {
if (method) {
console.warn('Native: tried calling ' + pluginName + '.' + method + ', but the ' + pluginName + ' plugin is not installed.');
} else {
console.warn('Native: tried accessing the ' + pluginName + ' plugin but it\'s not installed.');
}
if (plugin) {
console.warn('Install the ' + pluginName + ' plugin: \'ionic plugin add ' + plugin + '\'');
}
};
/**
* @private
* @param pluginName
* @param method
*/
export const cordovaWarn = function(pluginName: string, method?: string) {
if (method) {
console.warn('Native: tried calling ' + pluginName + '.' + method + ', but Cordova is not available. Make sure to include cordova.js or run in a device/simulator');
} else {
console.warn('Native: tried accessing the ' + pluginName + ' plugin but Cordova is not available. Make sure to include cordova.js or run in a device/simulator');
}
};
+5 -10
View File
@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { CordovaProperty, Plugin, pluginWarn } from '@ionic-native/core';
import { CordovaProperty, Plugin, CordovaCheck } from '@ionic-native/core';
declare var window: any;
declare var cordova: any;
@@ -461,17 +461,12 @@ export class File {
* Get free disk space in Bytes
* @returns {Promise<number>} Returns a promise that resolves with the remaining free disk space in Bytes
*/
@CordovaCheck({
promise: true
})
getFreeDiskSpace(): Promise<number> {
return new Promise<any>((resolve, reject) => {
if (!cordova || !cordova.exec) {
pluginWarn({
pluginName: 'File',
plugin: 'cordova-plugin-file'
});
reject({ error: 'plugin_not_installed' });
} else {
cordova.exec(resolve, reject, 'File', 'getFreeDiskSpace', []);
}
cordova.exec(resolve, reject, 'File', 'getFreeDiskSpace', []);
});
}
+54 -96
View File
@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { Cordova, CordovaInstance, Plugin, InstanceProperty, getPlugin, pluginWarn } from '@ionic-native/core';
import { Cordova, CordovaInstance, CordovaCheck, Plugin, InstanceProperty, InstanceCheck, checkAvailability } from '@ionic-native/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/fromEvent';
@@ -53,28 +53,19 @@ export const GoogleMapsMapTypeId = {
/**
* @private
*/
@Plugin({
pluginName: 'GoogleMaps',
plugin: 'cordova-plugin-googlemaps'
})
export class GoogleMap {
_objectInstance: any;
/**
* Checks if a map object has been created and is available.
*
* @returns {Promise<boolean>}
*/
@Cordova()
isAvailable(): Promise<boolean> { return; }
constructor(element: string | HTMLElement, options?: any) {
if (!!getPlugin('plugin.google.maps.Map')) {
if (checkAvailability('plugin.google.maps.Map', null, 'GoogleMaps') === true) {
if (typeof element === 'string') {
element = document.getElementById(<string>element);
}
this._objectInstance = plugin.google.maps.Map.getMap(element, options);
} else {
pluginWarn({
pluginName: 'GoogleMap',
plugin: 'plugin.google.maps.Map'
});
}
}
@@ -83,6 +74,7 @@ export class GoogleMap {
*
* @returns {Observable<any>}
*/
@InstanceCheck()
addEventListener(eventName: string): Observable<any> {
return Observable.fromEvent(this._objectInstance, eventName);
}
@@ -92,13 +84,9 @@ export class GoogleMap {
*
* @returns {Promise<any>}
*/
@InstanceCheck()
addListenerOnce(eventName: string): Promise<any> {
if (!this._objectInstance) {
return Promise.reject({ error: 'plugin_not_installed' });
}
return new Promise<any>(
resolve => this._objectInstance.addListenerOnce(eventName, resolve)
);
return new Promise<any>(resolve => this._objectInstance.addListenerOnce(eventName, resolve));
}
/**
@@ -121,19 +109,12 @@ export class GoogleMap {
*
* @returns {Observable<any>}
*/
@InstanceCheck()
on(eventName: string): Observable<any> {
if (!this._objectInstance) {
return new Observable((observer) => {
observer.error({ error: 'plugin_not_installed' });
});
}
return new Observable(
(observer) => {
return new Observable((observer) => {
this._objectInstance.on(eventName, observer.next.bind(observer));
return () => this._objectInstance.off(event);
}
);
});
}
/**
@@ -141,13 +122,9 @@ export class GoogleMap {
*
* @returns {Promise<any>}
*/
@InstanceCheck()
one(eventName: string): Promise<any> {
if (!this._objectInstance) {
return Promise.reject({ error: 'plugin_not_installed' });
}
return new Promise<any>(
resolve => this._objectInstance.one(eventName, resolve)
);
return new Promise<any>(resolve => this._objectInstance.one(eventName, resolve));
}
/**
@@ -237,12 +214,9 @@ export class GoogleMap {
/**
* @returns {Promise<Marker | any>}
*/
@InstanceCheck()
addMarker(options: MarkerOptions): Promise<Marker | any> {
if (!this._objectInstance) {
return Promise.reject({ error: 'plugin_not_installed' });
}
return new Promise<Marker>(
(resolve, reject) => {
return new Promise<Marker>((resolve, reject) => {
this._objectInstance.addMarker(options, (marker: any) => {
if (marker) {
resolve(new Marker(marker));
@@ -250,19 +224,15 @@ export class GoogleMap {
reject();
}
});
}
);
});
}
/**
* @returns {Promise<Circle | any>}
*/
@InstanceCheck()
addCircle(options: CircleOptions): Promise<Circle | any> {
if (!this._objectInstance) {
return Promise.reject({ error: 'plugin_not_installed' });
}
return new Promise<Circle>(
(resolve, reject) => {
return new Promise<Circle>((resolve, reject) => {
this._objectInstance.addCircle(options, (circle: any) => {
if (circle) {
resolve(new Circle(circle));
@@ -270,19 +240,15 @@ export class GoogleMap {
reject();
}
});
}
);
});
}
/**
* @returns {Promise<Polygon | any>}
*/
@InstanceCheck()
addPolygon(options: PolygonOptions): Promise<Polygon | any> {
if (!this._objectInstance) {
return Promise.reject({ error: 'plugin_not_installed' });
}
return new Promise<Polygon>(
(resolve, reject) => {
return new Promise<Polygon>((resolve, reject) => {
this._objectInstance.addPolygon(options, (polygon: any) => {
if (polygon) {
resolve(new Polygon(polygon));
@@ -290,19 +256,15 @@ export class GoogleMap {
reject();
}
});
}
);
});
}
/**
* @returns {Promise<Polyline | any>}
*/
@InstanceCheck()
addPolyline(options: PolylineOptions): Promise<Polyline | any> {
if (!this._objectInstance) {
return Promise.reject({ error: 'plugin_not_installed' });
}
return new Promise<Polyline>(
(resolve, reject) => {
return new Promise<Polyline>((resolve, reject) => {
this._objectInstance.addPolyline(options, (polyline: any) => {
if (polyline) {
resolve(new Polyline(polyline));
@@ -310,19 +272,15 @@ export class GoogleMap {
reject();
}
});
}
);
});
}
/**
* @returns {Promise<TileOverlay | any>}
*/
@InstanceCheck()
addTileOverlay(options: TileOverlayOptions): Promise<TileOverlay | any> {
if (!this._objectInstance) {
return Promise.reject({ error: 'plugin_not_installed' });
}
return new Promise<TileOverlay>(
(resolve, reject) => {
return new Promise<TileOverlay>((resolve, reject) => {
this._objectInstance.addTileOverlay(options, (tileOverlay: any) => {
if (tileOverlay) {
resolve(new TileOverlay(tileOverlay));
@@ -330,19 +288,15 @@ export class GoogleMap {
reject();
}
});
}
);
});
}
/**
* @returns {Promise<GroundOverlay | any>}
*/
@InstanceCheck()
addGroundOverlay(options: GroundOverlayOptions): Promise<GroundOverlay | any> {
if (!this._objectInstance) {
return Promise.reject({ error: 'plugin_not_installed' });
}
return new Promise<GroundOverlay>(
(resolve, reject) => {
return new Promise<GroundOverlay>((resolve, reject) => {
this._objectInstance.addGroundOverlay(options, (groundOverlay: any) => {
if (groundOverlay) {
resolve(new GroundOverlay(groundOverlay));
@@ -350,19 +304,15 @@ export class GoogleMap {
reject();
}
});
}
);
});
}
/**
* @returns {Promise<KmlOverlay | any>}
*/
@InstanceCheck()
addKmlOverlay(options: KmlOverlayOptions): Promise<KmlOverlay | any> {
if (!this._objectInstance) {
return Promise.reject({ error: 'plugin_not_installed' });
}
return new Promise<KmlOverlay>(
(resolve, reject) => {
return new Promise<KmlOverlay>((resolve, reject) => {
this._objectInstance.addKmlOverlay(options, (kmlOverlay: any) => {
if (kmlOverlay) {
resolve(new KmlOverlay(kmlOverlay));
@@ -370,8 +320,7 @@ export class GoogleMap {
reject();
}
});
}
);
});
}
@CordovaInstance({ sync: true })
@@ -500,6 +449,14 @@ export class GoogleMap {
@Injectable()
export class GoogleMaps {
/**
* Checks if a map object has been created and is available.
*
* @returns {Promise<boolean>}
*/
@Cordova()
isAvailable(): Promise<boolean> { return; }
/**
* Creates a new GoogleMap instance
* @param element {string | HTMLElement} Element ID or reference to attach the map to
@@ -1782,23 +1739,24 @@ export interface GeocoderResult {
/**
* @private
*/
@Plugin({
pluginName: 'Geocoder',
pluginRef: 'plugins.google.maps.Geocoder',
plugin: 'cordova-plugin-googlemaps',
repo: ''
})
export class Geocoder {
/**
* Converts position to address and vice versa
* @param {GeocoderRequest} request Request object with either an address or a position
* @returns {Promise<GeocoderResult[]>}
*/
@CordovaCheck({
promise: true
})
static geocode(request: GeocoderRequest): Promise<GeocoderResult[] | any> {
return new Promise<GeocoderResult[]>((resolve, reject) => {
if (!plugin || !plugin.google || !plugin.google.maps || !plugin.google.maps.Geocoder) {
pluginWarn({
pluginName: 'GoogleMap',
plugin: 'plugin.google.maps.Map'
});
reject({ error: 'plugin_not_installed' });
} else {
plugin.google.maps.Geocoder.geocode(request, resolve);
}
return new Promise<GeocoderResult[]>(resolve => {
plugin.google.maps.Geocoder.geocode(request, resolve);
});
}
}
+2 -7
View File
@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { CordovaInstance, Plugin, getPlugin, pluginWarn } from '@ionic-native/core';
import { CordovaInstance, Plugin, checkAvailability } from '@ionic-native/core';
declare var Media: any;
@@ -64,15 +64,10 @@ export class MediaObject {
* @param onStatusUpdate {Function} A callback function to be invoked when the status of the file changes
*/
constructor(src: string, onStatusUpdate?: Function) {
if (!!getPlugin('Media')) {
if (checkAvailability('Media', null, 'Media') === true) {
this.init = new Promise<any>((resolve, reject) => {
this._objectInstance = new Media(src, resolve, reject, onStatusUpdate);
});
} else {
pluginWarn({
pluginName: 'MediaPlugin',
plugin: 'cordova-plugin-media'
});
}
}
+4 -8
View File
@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { Cordova, CordovaInstance, Plugin, pluginWarn, InstanceProperty } from '@ionic-native/core';
import { Cordova, CordovaInstance, Plugin, CordovaCheck, InstanceProperty } from '@ionic-native/core';
declare var sqlitePlugin;
@@ -199,16 +199,12 @@ export class SQLite {
* @param config the config for opening the database.
* @return Promise<SQLiteObject>
*/
@CordovaCheck({
promise: true
})
create(config: any): Promise<SQLiteObject> {
return new Promise((resolve, reject) => {
if (typeof sqlitePlugin !== 'undefined') {
sqlitePlugin.openDatabase(config, db => resolve(new SQLiteObject(db)), reject);
} else {
pluginWarn({
pluginName: 'SQLite',
plugin: 'cordova-sqlite-storage'
});
}
});
}
+18
View File
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"baseUrl": ".",
"declaration": true,
"stripInternal": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"module": "es2015",
"moduleResolution": "node",
"paths": {
"@ionic-native/core": ["./dist/packages-dist/@ionic-native/core"]
},
"rootDir": ".",
"target": "es5",
"skipLibCheck": true,
"lib": ["es2015", "dom"]
}
}