2016-07-18 02:04:06 +08:00
import { get } from '../util' ;
2016-09-22 04:04:46 +08:00
import { Observable } from 'rxjs/Observable' ;
2015-11-29 08:26:55 +08:00
declare var window ;
declare var Promise ;
2015-12-01 02:34:54 +08:00
2016-03-13 07:52:25 +08:00
/ * *
* @private
* @param pluginRef
* @returns { null | * }
* /
2016-04-25 18:22:17 +08:00
export const getPlugin = function ( pluginRef : string ) : any {
2015-11-30 07:20:11 +08:00
return get ( window , pluginRef ) ;
2016-03-07 04:27:26 +08:00
} ;
2016-03-13 07:52:25 +08:00
/ * *
* @private
* @param pluginObj
* @param method
* /
2016-09-26 07:17:03 +08:00
export const pluginWarn = function ( pluginObj : any , method? : string ) {
2016-06-11 22:35:41 +08:00
let pluginName = pluginObj . name , plugin = pluginObj . plugin ;
2016-07-18 23:39:53 +08:00
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.' ) ;
}
2016-06-11 22:35:41 +08:00
console . warn ( 'Install the ' + pluginName + ' plugin: \'ionic plugin add ' + plugin + '\'' ) ;
2016-03-07 04:27:26 +08:00
} ;
2016-03-13 07:52:25 +08:00
/ * *
* @private
* @param pluginName
* @param method
* /
2016-04-25 18:22:17 +08:00
export const cordovaWarn = function ( pluginName : string , method : string ) {
2016-07-18 23:39:53 +08:00
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' ) ;
}
2016-03-07 04:27:26 +08:00
} ;
2016-07-18 02:04:06 +08:00
function setIndex ( args : any [ ] , opts : any = { } , resolve? : Function , reject? : Function ) : any {
2016-10-09 03:07:01 +08:00
// ignore resolve and reject in case sync
if ( opts . sync ) {
return args ;
}
2016-04-30 11:56:49 +08:00
// If the plugin method expects myMethod(success, err, options)
if ( opts . callbackOrder === 'reverse' ) {
// Get those arguments in the order [resolve, reject, ...restOfArgs]
args . unshift ( reject ) ;
args . unshift ( resolve ) ;
2016-08-19 21:08:05 +08:00
} else if ( opts . callbackStyle === 'node' ) {
args . push ( ( err , result ) = > {
if ( err ) {
reject ( err ) ;
} else {
resolve ( result ) ;
}
} ) ;
2016-08-28 02:43:57 +08:00
} else if ( opts . callbackStyle === 'object' && opts . successName && opts . errorName ) {
let obj : any = { } ;
obj [ opts . successName ] = resolve ;
obj [ opts . errorName ] = reject ;
args . push ( obj ) ;
2016-04-30 11:56:49 +08:00
} else if ( typeof opts . successIndex !== 'undefined' || typeof opts . errorIndex !== 'undefined' ) {
// If we've specified a success/error index
2016-10-06 11:19:45 +08:00
if ( opts . successIndex > args . length ) {
args [ opts . successIndex ] = resolve ;
} else {
args . splice ( opts . successIndex , 0 , resolve ) ;
}
fix(): add the reject function at the expected errorIndex position in the args array (#436)
We don't want that the reject cb takes the position of an optional
argument that has not been defined
For example the Dialogs.alert method takes an optional 'buttonLabel'
string. In case we do not set this value, and thus want to use the
default value, the 'reject'
callback get spliced into this position due the fact that the splice
start index is bigger than the array length.
Dialogs.alert("title", "message", "My button text") --> args =
["title", resolve, "message", "My button text", reject]
Dialogs.alert("title", "message") --> args = ["title", resolve,
"message", reject] --> reject is on the position of the buttontitle!
The cordova-plugin-dialogs alert function receives the wrong arguments
—> alert: function(message, completeCallback, title, buttonLabel)
The buttonLabel will receive the "reject" callback instead of a
undefined value.
2016-08-17 19:34:11 +08:00
// We don't want that the reject cb gets spliced into the position of an optional argument that has not been defined and thus causing non expected behaviour.
if ( opts . errorIndex > args . length ) {
2016-08-23 06:19:43 +08:00
args [ opts . errorIndex ] = reject ; // insert the reject fn at the correct specific index
fix(): add the reject function at the expected errorIndex position in the args array (#436)
We don't want that the reject cb takes the position of an optional
argument that has not been defined
For example the Dialogs.alert method takes an optional 'buttonLabel'
string. In case we do not set this value, and thus want to use the
default value, the 'reject'
callback get spliced into this position due the fact that the splice
start index is bigger than the array length.
Dialogs.alert("title", "message", "My button text") --> args =
["title", resolve, "message", "My button text", reject]
Dialogs.alert("title", "message") --> args = ["title", resolve,
"message", reject] --> reject is on the position of the buttontitle!
The cordova-plugin-dialogs alert function receives the wrong arguments
—> alert: function(message, completeCallback, title, buttonLabel)
The buttonLabel will receive the "reject" callback instead of a
undefined value.
2016-08-17 19:34:11 +08:00
} else {
2016-08-23 06:19:43 +08:00
args . splice ( opts . errorIndex , 0 , reject ) ; // otherwise just splice it into the array
fix(): add the reject function at the expected errorIndex position in the args array (#436)
We don't want that the reject cb takes the position of an optional
argument that has not been defined
For example the Dialogs.alert method takes an optional 'buttonLabel'
string. In case we do not set this value, and thus want to use the
default value, the 'reject'
callback get spliced into this position due the fact that the splice
start index is bigger than the array length.
Dialogs.alert("title", "message", "My button text") --> args =
["title", resolve, "message", "My button text", reject]
Dialogs.alert("title", "message") --> args = ["title", resolve,
"message", reject] --> reject is on the position of the buttontitle!
The cordova-plugin-dialogs alert function receives the wrong arguments
—> alert: function(message, completeCallback, title, buttonLabel)
The buttonLabel will receive the "reject" callback instead of a
undefined value.
2016-08-17 19:34:11 +08:00
}
2016-04-30 11:56:49 +08:00
} else {
// Otherwise, let's tack them on to the end of the argument list
// which is 90% of cases
2016-10-09 03:07:01 +08:00
args . push ( resolve ) ;
args . push ( reject ) ;
2016-04-30 11:56:49 +08:00
}
return args ;
2016-03-29 18:50:03 +08:00
}
2016-03-30 05:53:51 +08:00
2016-07-18 02:04:06 +08:00
function callCordovaPlugin ( pluginObj : any , methodName : string , args : any [ ] , opts : any = { } , resolve? : Function , reject? : Function ) {
2015-12-01 02:34:54 +08:00
// Try to figure out where the success/error callbacks need to be bound
// to our promise resolve/reject handlers.
2016-07-18 02:04:06 +08:00
args = setIndex ( args , opts , resolve , reject ) ;
2015-12-01 02:34:54 +08:00
let pluginInstance = getPlugin ( pluginObj . pluginRef ) ;
2016-04-30 11:56:49 +08:00
if ( ! pluginInstance ) {
2016-04-30 09:46:35 +08:00
// Do this check in here in the case that the Web API for this plugin is available (for example, Geolocation).
2016-04-30 11:56:49 +08:00
if ( ! window . cordova ) {
2016-04-30 09:46:35 +08:00
cordovaWarn ( pluginObj . name , methodName ) ;
return {
error : 'cordova_not_available'
2016-04-30 11:56:49 +08:00
} ;
2016-04-30 09:46:35 +08:00
}
2015-12-01 02:34:54 +08:00
2016-04-30 09:46:35 +08:00
pluginWarn ( pluginObj , methodName ) ;
return {
error : 'plugin_not_installed'
} ;
}
2015-12-01 02:34:54 +08:00
2015-12-01 03:27:25 +08:00
// TODO: Illegal invocation needs window context
return get ( window , pluginObj . pluginRef ) [ methodName ] . apply ( pluginInstance , args ) ;
2015-12-01 02:34:54 +08:00
}
2015-12-02 03:33:08 +08:00
function getPromise ( cb ) {
2016-09-03 01:16:57 +08:00
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 1/2 or on a recent browser.' ) ;
}
} ;
2016-08-01 02:24:56 +08:00
if ( window . angular ) {
2016-09-03 01:16:57 +08:00
let injector = window . angular . element ( document . querySelector ( '[ng-app]' ) || document . body ) . injector ( ) ;
if ( injector ) {
let $q = injector . get ( '$q' ) ;
return $q ( ( resolve , reject ) = > {
cb ( resolve , reject ) ;
} ) ;
} else {
console . warn ( 'Angular 1 was detected but $q couldn\'t be retrieved. This is usually when the app is not bootstrapped on the html or body tag. Falling back to native promises which won\'t trigger an automatic digest when promises resolve.' ) ;
return tryNativePromise ( ) ;
}
2015-12-02 03:33:08 +08:00
} else {
2016-09-03 01:16:57 +08:00
return tryNativePromise ( ) ;
2015-12-02 03:33:08 +08:00
}
}
2016-07-18 02:04:06 +08:00
function wrapPromise ( pluginObj : any , methodName : string , args : any [ ] , opts : any = { } ) {
2016-04-28 02:00:57 +08:00
let pluginResult , rej ;
const p = getPromise ( ( resolve , reject ) = > {
pluginResult = callCordovaPlugin ( pluginObj , methodName , args , opts , resolve , reject ) ;
rej = reject ;
} ) ;
// Angular throws an error on unhandled rejection, but in this case we have already printed
// a warning that Cordova is undefined or the plugin is uninstalled, so there is no reason
// to error
if ( pluginResult && pluginResult . error ) {
2016-07-18 02:04:06 +08:00
p . catch ( ( ) = > { } ) ;
2016-04-28 02:00:57 +08:00
rej ( pluginResult . error ) ;
}
return p ;
2015-12-01 02:34:54 +08:00
}
2016-07-18 22:51:39 +08:00
function wrapOtherPromise ( pluginObj : any , methodName : string , args : any [ ] , opts : any = { } ) {
return getPromise ( ( resolve , reject ) = > {
let pluginResult = callCordovaPlugin ( pluginObj , methodName , args , opts ) ;
if ( pluginResult && pluginResult . error ) {
reject ( pluginResult . error ) ;
}
pluginResult . then ( resolve ) . catch ( reject ) ;
} ) ;
}
2016-04-30 11:56:49 +08:00
function wrapObservable ( pluginObj : any , methodName : string , args : any [ ] , opts : any = { } ) {
2015-12-01 03:27:25 +08:00
return new Observable ( observer = > {
2015-12-01 04:38:52 +08:00
let pluginResult = callCordovaPlugin ( pluginObj , methodName , args , opts , observer . next . bind ( observer ) , observer . error . bind ( observer ) ) ;
2016-04-28 02:00:57 +08:00
if ( pluginResult && pluginResult . error ) {
observer . error ( pluginResult . error ) ;
}
2015-12-01 02:34:54 +08:00
return ( ) = > {
2015-12-01 04:38:52 +08:00
try {
2016-06-11 03:00:35 +08:00
if ( opts . clearFunction ) {
if ( opts . clearWithArgs ) {
return get ( window , pluginObj . pluginRef ) [ opts . clearFunction ] . apply ( pluginObj , args ) ;
}
return get ( window , pluginObj . pluginRef ) [ opts . clearFunction ] . call ( pluginObj , pluginResult ) ;
2016-02-09 02:42:42 +08:00
}
2016-04-30 11:56:49 +08:00
} catch ( e ) {
2015-12-01 04:38:52 +08:00
console . warn ( 'Unable to clear the previous observable watch for' , pluginObj . name , methodName ) ;
2016-03-05 03:53:59 +08:00
console . error ( e ) ;
2015-12-01 04:38:52 +08:00
}
2016-04-30 11:56:49 +08:00
} ;
2015-12-01 02:34:54 +08:00
} ) ;
}
2016-04-30 11:56:49 +08:00
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 ) ;
2016-03-29 18:50:03 +08:00
}
2016-07-18 02:04:06 +08:00
function wrapInstance ( pluginObj : any , methodName : string , opts : any = { } ) {
2016-04-30 09:46:35 +08:00
return ( . . . args ) = > {
2016-04-25 18:22:17 +08:00
if ( opts . sync ) {
2016-07-14 23:42:56 +08:00
// Sync doesn't wrap the plugin with a promise or observable, it returns the result as-is
2016-04-30 09:46:35 +08:00
return callInstance ( pluginObj , methodName , args , opts ) ;
2016-04-25 18:22:17 +08:00
} else if ( opts . observable ) {
2016-04-30 09:46:35 +08:00
return new Observable ( observer = > {
2016-04-30 11:56:49 +08:00
let pluginResult = callInstance ( pluginObj , methodName , args , opts , observer . next . bind ( observer ) , observer . error . bind ( observer ) ) ;
2016-04-30 09:46:35 +08:00
return ( ) = > {
try {
2016-04-30 11:56:49 +08:00
if ( opts . clearWithArgs ) {
2016-04-30 09:46:35 +08:00
return pluginObj . _objectInstance [ opts . clearFunction ] . apply ( pluginObj . _objectInstance , args ) ;
2016-04-25 18:22:17 +08:00
}
2016-04-30 09:46:35 +08:00
return pluginObj . _objectInstance [ opts . clearFunction ] . call ( pluginObj , pluginResult ) ;
2016-04-30 11:56:49 +08:00
} catch ( e ) {
2016-04-30 09:46:35 +08:00
console . warn ( 'Unable to clear the previous observable watch for' , pluginObj . name , methodName ) ;
console . error ( e ) ;
2016-03-30 05:49:53 +08:00
}
2016-04-30 11:56:49 +08:00
} ;
2016-04-30 09:46:35 +08:00
} ) ;
2016-07-18 23:39:53 +08:00
} else if ( opts . otherPromise ) {
return getPromise ( ( resolve , reject ) = > {
let result = callInstance ( pluginObj , methodName , args , opts , resolve , reject ) ;
result . then ( resolve , reject ) ;
} ) ;
2016-04-25 18:22:17 +08:00
} else {
2016-04-30 09:46:35 +08:00
return getPromise ( ( resolve , reject ) = > {
callInstance ( pluginObj , methodName , args , opts , resolve , reject ) ;
} ) ;
2016-04-25 18:22:17 +08:00
}
2016-04-30 11:56:49 +08:00
} ;
2016-03-29 18:50:03 +08:00
}
2016-03-14 03:30:21 +08:00
/ * *
* Wrap the event with an observable
* @param event
* @returns { Observable }
* /
2016-07-18 02:04:06 +08:00
function wrapEventObservable ( event : string ) : Observable < any > {
2016-03-14 03:30:21 +08:00
return new Observable ( observer = > {
2016-07-17 20:54:39 +08:00
window . addEventListener ( event , observer . next . bind ( observer ) , false ) ;
return ( ) = > window . removeEventListener ( event , observer . next . bind ( observer ) , false ) ;
2016-03-14 03:30:21 +08:00
} ) ;
}
2016-09-25 06:26:23 +08:00
/ * *
* Certain plugins expect the user to override methods in the plugin . For example ,
* window . cordova . plugins . backgroundMode . onactivate = function ( ) { . . . } .
*
* Unfortunately , this is brittle and would be better wrapped as an Observable . overrideFunction
* does just this .
* /
function overrideFunction ( pluginObj : any , methodName : string , args : any [ ] , opts : any = { } ) : Observable < any > {
return new Observable ( observer = > {
let pluginInstance = getPlugin ( pluginObj . pluginRef ) ;
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 . name , methodName ) ;
observer . error ( {
error : 'cordova_not_available'
} ) ;
}
pluginWarn ( pluginObj , methodName ) ;
observer . error ( {
error : 'plugin_not_installed'
} ) ;
return ;
}
let method = pluginInstance [ methodName ] ;
if ( ! method ) {
observer . error ( {
error : 'no_such_method'
} ) ;
observer . complete ( ) ;
return ;
}
pluginInstance [ methodName ] = observer . next . bind ( observer ) ;
} ) ;
}
2016-03-13 07:52:25 +08:00
/ * *
* @private
* @param pluginObj
* @param methodName
* @param opts
* @returns { function ( . . . [ any ] ) : ( undefined | * | Observable | * | * ) }
* /
2016-07-18 02:04:06 +08:00
export const wrap = function ( pluginObj : any , methodName : string , opts : any = { } ) {
2015-12-01 02:34:54 +08:00
return ( . . . args ) = > {
2016-07-18 23:39:53 +08:00
if ( opts . sync ) {
2016-07-14 23:42:56 +08:00
// Sync doesn't wrap the plugin with a promise or observable, it returns the result as-is
2016-02-10 04:45:57 +08:00
return callCordovaPlugin ( pluginObj , methodName , args , opts ) ;
2016-07-18 23:39:53 +08:00
} else if ( opts . observable ) {
2015-12-01 02:34:54 +08:00
return wrapObservable ( pluginObj , methodName , args , opts ) ;
2016-07-18 23:39:53 +08:00
} else if ( opts . eventObservable && opts . event ) {
2016-03-14 03:30:21 +08:00
return wrapEventObservable ( opts . event ) ;
2016-07-18 23:39:53 +08:00
} else if ( opts . otherPromise ) {
2016-07-18 22:51:39 +08:00
return wrapOtherPromise ( pluginObj , methodName , args , opts ) ;
2016-07-18 23:39:53 +08:00
} else {
2015-12-01 02:34:54 +08:00
return wrapPromise ( pluginObj , methodName , args , opts ) ;
2016-07-18 23:39:53 +08:00
}
2016-04-30 11:56:49 +08:00
} ;
2016-03-07 04:27:26 +08:00
} ;
2015-11-29 08:26:55 +08:00
2015-11-30 09:54:45 +08:00
/ * *
2016-03-13 07:52:25 +08:00
* @private
*
2015-11-30 09:54:45 +08:00
* Class decorator specifying Plugin metadata . Required for all plugins .
2016-03-13 07:30:16 +08:00
*
* @usage
2016-07-20 23:17:09 +08:00
* ` ` ` typescript
2016-03-13 07:30:16 +08:00
* @Plugin ( {
* name : 'MyPlugin' ,
* plugin : 'cordova-plugin-myplugin' ,
* pluginRef : 'window.myplugin'
* } )
* export class MyPlugin {
*
* // Plugin wrappers, properties, and functions go here ...
*
* }
* ` ` `
2015-11-30 09:54:45 +08:00
* /
2015-11-29 08:26:55 +08:00
export function Plugin ( config ) {
2016-04-25 18:22:17 +08:00
return function ( cls ) {
2015-11-29 08:26:55 +08:00
// Add these fields to the class
2016-02-10 04:45:57 +08:00
for ( let k in config ) {
2015-11-29 08:26:55 +08:00
cls [ k ] = config [ k ] ;
}
2016-09-26 07:17:03 +08:00
cls [ 'installed' ] = function ( printWarning? : boolean ) {
2015-12-02 03:33:08 +08:00
return ! ! getPlugin ( config . pluginRef ) ;
2016-03-07 04:27:26 +08:00
} ;
2015-12-02 03:33:08 +08:00
2016-09-26 06:59:56 +08:00
cls [ 'getPlugin' ] = function ( ) {
return getPlugin ( config . pluginRef ) ;
} ;
2016-09-26 07:17:03 +08:00
cls [ 'checkInstall' ] = function ( ) {
let pluginInstance = getPlugin ( config . pluginRef ) ;
if ( ! pluginInstance ) {
pluginWarn ( cls ) ;
return false ;
}
return true ;
} ;
2015-11-29 08:26:55 +08:00
return cls ;
2016-04-30 11:56:49 +08:00
} ;
2015-11-29 08:26:55 +08:00
}
2015-11-30 09:54:45 +08:00
/ * *
2016-03-13 07:52:25 +08:00
* @private
*
2015-11-30 09:54:45 +08:00
* Wrap a stub function in a call to a Cordova plugin , checking if both Cordova
* and the required plugin are installed .
* /
2016-04-30 11:56:49 +08:00
export function Cordova ( opts : any = { } ) {
2016-04-25 18:22:17 +08:00
return ( target : Object , methodName : string , descriptor : TypedPropertyDescriptor < any > ) = > {
2015-11-30 09:54:45 +08:00
return {
2016-04-25 18:22:17 +08:00
value : function ( . . . args : any [ ] ) {
2015-11-30 11:50:58 +08:00
return wrap ( this , methodName , opts ) . apply ( this , args ) ;
2015-11-30 09:54:45 +08:00
}
2016-04-30 11:56:49 +08:00
} ;
} ;
2015-11-29 08:26:55 +08:00
}
2015-11-30 07:20:11 +08:00
2016-03-30 05:49:53 +08:00
/ * *
* @private
*
* Wrap an instance method
* /
2016-04-30 11:56:49 +08:00
export function CordovaInstance ( opts : any = { } ) {
return ( target : Object , methodName : string ) = > {
return {
value : function ( . . . args : any [ ] ) {
return wrapInstance ( this , methodName , opts ) . apply ( this , args ) ;
}
} ;
} ;
2016-03-29 18:50:03 +08:00
}
2015-11-30 09:54:45 +08:00
/ * *
2016-03-13 07:52:25 +08:00
* @private
*
*
2015-11-30 09:54:45 +08:00
* Before calling the original method , ensure Cordova and the plugin are installed .
* /
2016-04-25 18:22:17 +08:00
export function CordovaProperty ( target : Function , key : string , descriptor : TypedPropertyDescriptor < any > ) {
2016-03-05 03:56:22 +08:00
let originalMethod = descriptor . get ;
2015-11-30 07:20:11 +08:00
2016-04-25 18:22:17 +08:00
descriptor . get = function ( . . . args : any [ ] ) {
2016-04-30 11:56:49 +08:00
if ( ! window . cordova ) {
2015-11-30 07:20:11 +08:00
cordovaWarn ( this . name , null ) ;
2016-03-05 03:56:22 +08:00
return { } ;
2015-11-30 07:20:11 +08:00
}
2016-06-11 22:20:28 +08:00
let pluginObj : any = this ;
let pluginInstance = getPlugin ( pluginObj . pluginRef ) ;
2016-04-30 11:56:49 +08:00
if ( ! pluginInstance ) {
2016-03-05 03:52:57 +08:00
pluginWarn ( this , key ) ;
2016-07-18 02:04:06 +08:00
return { } ;
2015-11-30 07:20:11 +08:00
}
2016-02-17 07:40:54 +08:00
return originalMethod . apply ( this , args ) ;
2016-03-07 04:27:26 +08:00
} ;
2015-11-30 07:20:11 +08:00
return descriptor ;
}
2016-05-01 01:31:10 +08:00
/ * *
* @private
* @param target
* @param key
* @param descriptor
* @constructor
* /
2016-06-10 13:10:50 +08:00
export function InstanceProperty ( target : any , key : string , descriptor : TypedPropertyDescriptor < any > ) {
2016-05-01 01:31:10 +08:00
descriptor . get = function ( ) {
return this . _objectInstance [ key ] ;
} ;
2016-06-10 11:12:23 +08:00
descriptor . set = function ( . . . args : any [ ] ) {
2016-06-10 13:10:50 +08:00
this . _objectInstance [ key ] = args [ 0 ] ;
2016-05-01 01:31:10 +08:00
} ;
return descriptor ;
2016-05-21 04:59:18 +08:00
}
2016-09-25 06:26:23 +08:00
/ * *
* @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 ) ;
}
} ;
} ;
}