diff --git a/.gitignore b/.gitignore index e4cdfaa..6085cc2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .DS_Store *.log .idea/ +node_modules/ diff --git a/README.md b/README.md index 22ae631..28d86a8 100644 --- a/README.md +++ b/README.md @@ -52,8 +52,14 @@ $ cordova plugin add cordova-plugin-broadcaster ```Java final Intent intent = new Intent("didShow"); -Bundle b = new Bundle(); -b.putString( "data", "test" ); +final Bundle child = new Bundle(); +child.putString( "name", "joker"); + +final Bundle b = new Bundle(); +b.putString( "data", "test"); +b.putBoolean( "valid", true ); +b.putBundle( "child", child ); + intent.putExtras( b); LocalBroadcastManager.getInstance(this).sendBroadcastSync(intent); @@ -63,15 +69,28 @@ LocalBroadcastManager.getInstance(this).sendBroadcastSync(intent); ##### Objective-C ```Objective-C -[[NSNotificationCenter defaultCenter] postNotificationName:@"didShow" - object:nil - userInfo:@{ @"data":@"test"}]; + NSDictionary * payload = @{ + @"data":@"test", + @"valid": [NSNumber numberWithBool:YES], + @"child": @{ @"name": @"joker" } + }; + + [[NSNotificationCenter defaultCenter] postNotificationName:@"TEST.EVENT" + object:nil + userInfo:payload]; ``` -##### Swift +##### Swift 5.x ```swift -let nc = NSNotificationCenter.default -nc.post(name:"didShow", object: nil, userInfo: ["data":"test"]) + + let payload:[String:Any] = [ + "data":"test", + "valid": true, + "child":[ "name": "joker" ] + ] + + let nc = NotificationCenter.default + nc.post(name:Notification.Name("didShow"), object: nil, userInfo: payload) ``` #### BROWSER @@ -124,7 +143,7 @@ LocalBroadcastManager.getInstance(this) }]; ``` -##### Swift 3.0 +##### Swift 5.x ```swift let nc = NotificationCenter.default diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..e01a6f6 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,14 @@ +{ + "name": "cordova-plugin-broadcaster", + "version": "3.1.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/cordova": { + "version": "0.0.34", + "resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-0.0.34.tgz", + "integrity": "sha1-6nrd907Ow9dimCegw54smt3HPQQ=", + "dev": true + } + } +} diff --git a/package.json b/package.json index 87e8882..a71dc64 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-plugin-broadcaster", - "version": "3.1.1", + "version": "4.0.0", "description": "Allow send message from Javascript to Native", "cordova": { "id": "cordova-plugin-broadcaster", @@ -19,15 +19,22 @@ "cordova-ios" ], "engines": { - "cordovaDependencies": { - "2.2.1": { "cordova": ">3.9.0" }, - "2.3.0": { "cordova": ">4.0.0" } + "cordovaDependencies": { + "2.2.1": { + "cordova": ">3.9.0" + }, + "2.3.0": { + "cordova": ">4.0.0" } + } }, "author": "bsorrentino", "license": "MIT", "bugs": { "url": "https://github.com/bsorrentino/cordova-broadcaster/issues" }, - "homepage": "https://github.com/bsorrentino/cordova-broadcaster#readme" + "homepage": "https://github.com/bsorrentino/cordova-broadcaster#readme", + "devDependencies": { + "@types/cordova": "0.0.34" + } } diff --git a/plugin.xml b/plugin.xml index 5678aeb..ed71014 100644 --- a/plugin.xml +++ b/plugin.xml @@ -1,6 +1,6 @@ diff --git a/src/android/CDVBroadcaster.java b/src/android/CDVBroadcaster.java index 79930ce..4ce74fb 100644 --- a/src/android/CDVBroadcaster.java +++ b/src/android/CDVBroadcaster.java @@ -14,6 +14,12 @@ import org.json.JSONException; import org.json.JSONObject; +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Iterator; + +import static java.lang.String.format; + /** * This class echoes a string called from JavaScript. */ @@ -41,13 +47,13 @@ public void run() { String method = null; if( data == null ) { - method = String.format("javascript:window.broadcaster.fireEvent( '%s', null );", eventName ); + method = format("javascript:window.broadcaster.fireEvent( '%s', null );", eventName ); } else if( data instanceof JSONObject ) { - method = String.format("javascript:window.broadcaster.fireEvent( '%s', %s );", eventName, data.toString() ); + method = format("javascript:window.broadcaster.fireEvent( '%s', %s );", eventName, data.toString() ); } else { - method = String.format("javascript:window.broadcaster.fireEvent( '%s', '%s' );", eventName, data.toString() ); + method = format("javascript:window.broadcaster.fireEvent( '%s', '%s' );", eventName, data.toString() ); } CDVBroadcaster.this.webView.loadUrl(method); } @@ -95,6 +101,11 @@ public Object onMessage(String id, Object data) { return super.onMessage( id, data ); } + /** + * + * @param eventName + * @param userData + */ private void fireNativeEvent( final String eventName, JSONObject userData ) { if( eventName == null ) { throw new IllegalArgumentException("eventName parameter is null!"); @@ -102,7 +113,9 @@ private void fireNativeEvent( final String eventName, JSONObject userData ) { final Intent intent = new Intent(eventName); - intent.putExtras(toBundle( new Bundle(), userData )); + final Bundle bundle = (userData == null ) ? new Bundle() : toBundle( userData ); + + intent.putExtras(bundle); sendBroadcast( intent ); } @@ -125,11 +138,14 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo } final JSONObject userData = args.getJSONObject(1); - + if( userData==null ) { + Log.w( TAG, "user data provided to native event is null!"); + } cordova.getThreadPool().execute(new Runnable() { @Override public void run() { + fireNativeEvent(eventName, userData); } }); @@ -201,114 +217,153 @@ public void onDestroy() { } /** - * Credit: https://github.com/darryncampbell/darryncampbell-cordova-plugin-intent + * Credit: https://github.com/napolitano/cordova-plugin-intent * * @param bundle - * @param object * @return */ - private static Bundle toBundle(final Bundle bundle, JSONObject object) { - if( bundle == null || object == null ) return bundle; - - final java.util.Iterator keys = object.keys(); - - while( keys.hasNext() ) { - final String key = keys.next(); - - if (object.isNull(key)) { - continue; - } - - final Object value = object.opt(key); - - if (value instanceof Boolean) { - bundle.putBoolean(key, (Boolean) value); - } else if (value instanceof Long) { - bundle.putLong(key, (Long) value); - } else if (value instanceof Double) { - bundle.putDouble(key, (Double) value); - } else if (value instanceof Integer) { - bundle.putInt(key, (Integer) value); - } else if (value instanceof JSONObject) { - bundle.putBundle(key, toBundle(new Bundle(), (JSONObject) value)); - } else if (value instanceof JSONArray) { - - try { - final JSONArray values = (JSONArray) value; + private static JSONObject toJsonObject(Bundle bundle) { - final JSONArray index = new JSONArray(); - for (int i = 0; i < values.length(); ++i) { - index.put(String.valueOf(i)); - } - - bundle.putBundle(key, toBundle(new Bundle(), values.toJSONObject(index))); - } catch (JSONException e) { - Log.w(TAG, String.format("error creating bundle from array for key %s", key), e); - } - } else { - bundle.putCharSequence(key, String.valueOf(value)); - } + if( bundle == null ) { + Log.w( TAG, "bundle is null!" ); + return new JSONObject(); } - - return bundle; + return (JSONObject) toJsonValue(bundle); } /** - * Credit: https://github.com/darryncampbell/darryncampbell-cordova-plugin-intent + * Credit: https://github.com/napolitano/cordova-plugin-intent * - * @param bundle + * @param value * @return + * @throws JSONException */ - private static JSONObject toJsonObject(final Bundle bundle) { - final JSONObject result = new JSONObject(); - - if( bundle != null ) { + private static Object toJsonValue(final Object value) { + // Null + if (value == null) { + return JSONObject.NULL; + } + // Bundle + else if (value instanceof Bundle) { + final JSONObject result = new JSONObject(); + final Bundle bundle = (Bundle) value; for (final String key : bundle.keySet()) { try { - result.putOpt(key, toJsonValue(bundle.get(key))); + final Object bundle_value = bundle.get(key); + result.put(key, toJsonValue(bundle_value)); } catch (JSONException e) { - Log.w( TAG, String.format("error parsing Bundle key %s", key), e); + Log.w( TAG, format( "error getting key %s from bundle\n%s", key), e); } } - + return result; } - - return result; - } - - /** - * - * Credit: https://github.com/darryncampbell/darryncampbell-cordova-plugin-intent - * - * @param value - * @return - * @throws JSONException - */ - private static Object toJsonValue(final Object value) { - - if (value == null) return JSONObject.NULL; - - if (value.getClass().isArray()) { + // Native Array + else if ((value.getClass().isArray())) { final JSONArray result = new JSONArray(); - int length = java.lang.reflect.Array.getLength(value); + int length = Array.getLength(value); for (int i = 0; i < length; ++i) { - final Object v = java.lang.reflect.Array.get(value, i); - try { - result.put(i, toJsonValue(v)); - } catch (JSONException e) { - Log.w( TAG, String.format("error parsing array element %d vaule %s", i,v), e); - } + final Object array_value = Array.get(value, i); + result.put(toJsonValue(array_value)); } return result; - } else if ( value instanceof String + } + // ArrayList + else if (value instanceof ArrayList) { + final ArrayList arrayList = (ArrayList)value; + final JSONArray result = new JSONArray(); + for ( Object array_value : arrayList ) { + result.put( toJsonValue(array_value) ); + } + return result; + } + // Boolean | Integer | Long | Double + else if ( + value instanceof String || value instanceof Boolean || value instanceof Integer || value instanceof Long || value instanceof Double) { return value; - } else { + } + // Other(s) + else { return String.valueOf(value); } } + + /** + * Credit: https://github.com/napolitano/cordova-plugin-intent + * + * @param obj + * @return + */ + private Bundle toBundle(final JSONObject obj) { + final Bundle returnBundle = new Bundle(); + + if (obj == null) { + return null; + } + + final Iterator keys = obj.keys(); + + while(keys.hasNext()) + { + final String key = (String)keys.next(); + + try { + final Object compare = obj.get(key); + // String + if (compare instanceof String) + returnBundle.putString(key, obj.getString(key)); + // Boolean + else if (compare instanceof Boolean) + returnBundle.putBoolean(key, obj.getBoolean(key)); + // Integer + else if (compare instanceof Integer) + returnBundle.putInt(key, obj.getInt(key)); + // Long + else if (compare instanceof Long) + returnBundle.putLong(key, obj.getLong(key)); + // Double + else if (compare instanceof Double) + returnBundle.putDouble(key, obj.getDouble(key)); + // Array | JSONArray + else if (compare.getClass().isArray() || compare instanceof JSONArray) { + final JSONArray jsonArray = obj.getJSONArray(key); + int length = jsonArray.length(); + if (jsonArray.get(0) instanceof String) { + final String[] stringArray = new String[length]; + for (int j = 0; j < length; j++) + stringArray[j] = jsonArray.getString(j); + returnBundle.putStringArray(key, stringArray); + //returnBundle.putParcelableArray(key, obj.get); + } else { + if (key.equals("PLUGIN_CONFIG")) { + final ArrayList bundleArray = new ArrayList(); + for (int k = 0; k < length; k++) { + bundleArray.add(toBundle(jsonArray.getJSONObject(k))); + } + returnBundle.putParcelableArrayList(key, bundleArray); + } else { + final Bundle[] bundleArray = new Bundle[length]; + for (int k = 0; k < length; k++) + bundleArray[k] = toBundle(jsonArray.getJSONObject(k)); + returnBundle.putParcelableArray(key, bundleArray); + } + } + } + // JSONObject + else if (compare instanceof JSONObject) + returnBundle.putBundle(key, toBundle((JSONObject) obj.get(key))); + } + catch (JSONException e) { + Log.w( TAG, format( "error processing key %s \n%s", key ), e ); + } + + } + + + return returnBundle; + } } diff --git a/src/ios/CDVBroadcaster.m b/src/ios/CDVBroadcaster.m index 35a88a0..6dc64ac 100644 --- a/src/ios/CDVBroadcaster.m +++ b/src/ios/CDVBroadcaster.m @@ -19,7 +19,7 @@ @interface CDVBroadcaster () { @property (nonatomic,strong) NSMutableDictionary *observerMap; - (void)fireNativeEvent:(CDVInvokedUrlCommand*)command; -- (void)fireEvent:(NSString *)eventName data:(NSDictionary*)data; +- (void)fireEvent:(NSString *)eventName data:(NSDictionary*)data ; - (void)addEventListener:(CDVInvokedUrlCommand*)command; - (void)removeEventListener:(CDVInvokedUrlCommand*)command; @@ -30,17 +30,33 @@ @implementation CDVBroadcaster - (void)dealloc { - + for ( id observer in self.observerMap) { - + [[NSNotificationCenter defaultCenter] removeObserver:observer]; - + } - + [_observerMap removeAllObjects]; - + _observerMap = nil; - + +} + +-(void)fireJsEvent:(NSString *)eventName jsonData:(NSString *)jsonDataString +{ + NSString *func = + [NSString stringWithFormat:@"window.broadcaster.fireEvent('%@', %@);", eventName, jsonDataString]; + + [self.commandDelegate evalJs:func]; +} + +-(void)fireJsEvent:(NSString *)eventName jsonData:(NSString *)jsonDataString scheduledOnRunLoop:(BOOL)scheduledOnRunLoop +{ + NSString *func = + [NSString stringWithFormat:@"window.broadcaster.fireEvent('%@', %@);", eventName, jsonDataString]; + + [self.commandDelegate evalJs:func scheduledOnRunLoop:scheduledOnRunLoop]; } -(NSMutableDictionary *)observerMap @@ -48,7 +64,7 @@ -(NSMutableDictionary *)observerMap if (!_observerMap) { _observerMap = [[NSMutableDictionary alloc] initWithCapacity:100]; } - + return _observerMap; } @@ -57,49 +73,42 @@ - (void)fireEvent:(NSString *)eventName data:(NSDictionary*)data if (!self.commandDelegate ) { return; } - + if (eventName == nil || [eventName length] == 0) { - + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"eventName is null or empty" userInfo:nil]; - } - + NSString *jsonDataString = @"{}"; - + if( data ) { - + NSError *error; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:data options:(NSJSONWritingOptions)0 error:&error]; - - if (! jsonData) { + if (! jsonData) { throwWithName(error, @"JSON Serialization exception"); return; - } - - jsonDataString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; - + jsonDataString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; } - - NSString *func = [NSString stringWithFormat:@"window.broadcaster.fireEvent('%@', %@);", eventName, jsonDataString]; - - [self.commandDelegate evalJs:func]; - - + + [self fireJsEvent:eventName jsonData:jsonDataString]; + + } - (void)addEventListener:(CDVInvokedUrlCommand*)command { CDVPluginResult* pluginResult; - + __block NSString* eventName = command.arguments[0]; - + if (eventName == nil || [eventName length] == 0) { pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"eventName is null or empty"]; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; @@ -107,27 +116,27 @@ - (void)addEventListener:(CDVInvokedUrlCommand*)command } id observer = self.observerMap[eventName]; - + if (!observer) { __typeof(self) __weak weakSelf = self; - + observer = [[NSNotificationCenter defaultCenter] addObserverForName:eventName object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { - + __typeof(self) __strong strongSelf = weakSelf; - + [strongSelf fireEvent:eventName data:note.userInfo]; - + }]; [self.observerMap setObject:observer forKey:eventName]; } - + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; - + } @@ -137,29 +146,27 @@ - (void)removeEventListener:(CDVInvokedUrlCommand*)command CDVPluginResult* pluginResult; __block NSString* eventName = command.arguments[0]; - + if (eventName == nil || [eventName length] == 0) { pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"eventName is null or empty"]; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; return; } - + id observer = self.observerMap[ eventName ]; - + if (observer) { - + [[NSNotificationCenter defaultCenter] removeObserver:observer name:eventName object:self]; } - + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - - [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; - -} + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} - (void)fireNativeEvent:(CDVInvokedUrlCommand*)command { @@ -187,4 +194,10 @@ - (void)fireNativeEvent:(CDVInvokedUrlCommand*)command [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } +/** + * Override method for onAppTerminate provided in CDVPlugin.h + */ +- (void)onAppTerminate { + [self fireJsEvent:@"willterminate" jsonData:@"{}" scheduledOnRunLoop:NO]; +} @end diff --git a/www/broadcaster.js b/www/broadcaster.js index d8f4a4d..41ecc64 100644 --- a/www/broadcaster.js +++ b/www/broadcaster.js @@ -1,99 +1,95 @@ - +"use strict"; var exec = require('cordova/exec'); var channel = require('cordova/channel'); - -function Broadcaster() { - var _debug = false; - //console.log( "NEW BROADCASTER"); - this._channels = {}; - - this.channelExists = function( c ) { - //return (c in this._channels); - return this._channels.hasOwnProperty(c); - } - - this.channelCreate = function( c ) { - if( _debug ) console.log( "CHANNEL " + c + " CREATED! "); - this._channels[c] = channel.create(c); - } - this.channelSubscribe = function( c, f ) { - var channel = this._channels[c]; - channel.subscribe(f); - if( _debug ) console.log( "CHANNEL " + c + " SUBSCRIBED! " + channel.numHandlers); - return channel.numHandlers; - } - this.channelUnsubscribe = function( c, f ) { - var channel = this._channels[c]; - channel.unsubscribe(f); - if( _debug ) console.log( "CHANNEL " + c + " UNSUBSCRIBED! " + channel.numHandlers); - return channel.numHandlers; - } - this.channelFire = function( event ) { - if( _debug ) console.log( "CHANNEL " + event.type + " FIRED! "); - this._channels[event.type].fire(event); - } - this.channelDelete = function( c ) { - delete this._channels[c]; - if( _debug ) console.log( "CHANNEL " + c + " DELETED! "); - } - -} - -Broadcaster.prototype.fireNativeEvent = function(eventname, data, success, error) { - exec(success, error, "broadcaster", "fireNativeEvent", [ eventname, data ]); -} - -Broadcaster.prototype.fireEvent = function(type, data) { - if( !this.channelExists(type) ) return; - - var event = document.createEvent('Event'); - event.initEvent(type, false, false); - if (data) { - for (var i in data) { - if (data.hasOwnProperty(i)) { - event[i] = data[i]; - } - } - } - this.channelFire( event ); -} - -function _debug( msg, o ) { - console.log( msg ); - for( var m in o ) { - console.log( "==> " + m); - } -} - -Broadcaster.prototype.addEventListener = function (eventname,f) { - - if (!this.channelExists(eventname)) { - this.channelCreate(eventname); - var me = this; - exec( function() { - me.channelSubscribe(eventname,f); - }, function(err) { - console.log( "ERROR addEventListener: ", err) - }, "broadcaster", "addEventListener", [ eventname ]); - } - else { - this.channelSubscribe(eventname,f); - } -} - -Broadcaster.prototype.removeEventListener = function(eventname, f) { - - if (this.channelExists(eventname)) { - if( this.channelUnsubscribe(eventname, f) === 0 ) { - var me = this; - exec( function() { - me.channelDelete(eventname); - }, function(err) { - console.log( "ERROR removeEventListener: ", err) - }, "broadcaster", "removeEventListener", [ eventname ]); - - } - } -} - +var Broadcaster = /** @class */ (function () { + function Broadcaster() { + var _this = this; + this._debug = false; + this._channels = {}; + this._channelCreate = function (c) { + if (_this._debug) + console.log("CHANNEL " + c + " CREATED! "); + _this._channels[c] = channel.create(c); + }; + this._channelDelete = function (c) { + delete _this._channels[c]; + if (_this._debug) + console.log("CHANNEL " + c + " DELETED! "); + }; + this._channelSubscribe = function (c, f) { + var channel = _this._channels[c]; + channel.subscribe(f); + if (_this._debug) + console.log("CHANNEL " + c + " SUBSCRIBED! " + channel.numHandlers); + return channel.numHandlers; + }; + this._channelUnsubscribe = function (c, f) { + var channel = _this._channels[c]; + channel.unsubscribe(f); + if (_this._debug) + console.log("CHANNEL " + c + " UNSUBSCRIBED! " + channel.numHandlers); + return channel.numHandlers; + }; + this._channelFire = function (event) { + if (_this._debug) + console.log("CHANNEL " + event.type + " FIRED! "); + _this._channels[event.type].fire(event); + }; + this._channelExists = function (c) { + return _this._channels.hasOwnProperty(c); + }; + } + /** + * fire native evet + * + */ + Broadcaster.prototype.fireNativeEvent = function (type, data, success, error) { + exec(success, error, "broadcaster", "fireNativeEvent", [type, data]); + }; + /** + * fire local evet + * + */ + Broadcaster.prototype.fireEvent = function (type, data) { + if (!this._channelExists(type)) + return; + var event = document.createEvent('Event'); + event.initEvent(type, false, false); + if (data) { + for (var i in data) { + if (data.hasOwnProperty(i)) { + event[i] = data[i]; + } + } + } + this._channelFire(event); + }; + /** + * add a listener + * + */ + Broadcaster.prototype.addEventListener = function (eventname, f) { + var _this = this; + if (!this._channelExists(eventname)) { + this._channelCreate(eventname); + exec(function () { return _this._channelSubscribe(eventname, f); }, function (err) { return console.log("ERROR addEventListener: ", err); }, "broadcaster", "addEventListener", [eventname]); + } + else { + this._channelSubscribe(eventname, f); + } + }; + /** + * remove a listener + * + */ + Broadcaster.prototype.removeEventListener = function (eventname, f) { + var _this = this; + if (this._channelExists(eventname)) { + if (this._channelUnsubscribe(eventname, f) === 0) { + exec(function () { return _this._channelDelete(eventname); }, function (err) { return console.log("ERROR removeEventListener: ", err); }, "broadcaster", "removeEventListener", [eventname]); + } + } + }; + return Broadcaster; +}()); module.exports = new Broadcaster(); diff --git a/www/broadcaster.ts b/www/broadcaster.ts new file mode 100644 index 0000000..0c52306 --- /dev/null +++ b/www/broadcaster.ts @@ -0,0 +1,122 @@ + +var exec = require('cordova/exec'); +var channel = require('cordova/channel'); + +type Listener = (event:Event)=>void; + +interface Channel { + subscribe( handler:Listener ):void; + unsubscribe( handler:Listener ):void; + fire( event:Event ):void; + numHandlers:number; +} + +type Channels = { + [ key:string ]:Channel; +}; + + +class Broadcaster { + + private _debug = false; + private _channels:Channels = {}; + + private _channelCreate = ( c:string ) => { + if( this._debug ) console.log( "CHANNEL " + c + " CREATED! "); + this._channels[c] = channel.create(c); + } + + private _channelDelete = ( c:string ) => { + delete this._channels[c]; + if( this._debug ) console.log( "CHANNEL " + c + " DELETED! "); + } + + private _channelSubscribe = ( c:string, f:Listener ) => { + var channel = this._channels[c]; + channel.subscribe(f); + if( this._debug ) console.log( "CHANNEL " + c + " SUBSCRIBED! " + channel.numHandlers); + return channel.numHandlers; + } + + private _channelUnsubscribe = ( c:string, f:Listener ) => { + var channel = this._channels[c]; + channel.unsubscribe(f); + if( this._debug ) console.log( "CHANNEL " + c + " UNSUBSCRIBED! " + channel.numHandlers); + return channel.numHandlers; + } + + private _channelFire = ( event:Event ) => { + if( this._debug ) console.log( "CHANNEL " + event.type + " FIRED! "); + this._channels[event.type].fire(event); + } + + private _channelExists = ( c:string ) => { + return this._channels.hasOwnProperty(c); + } + + /** + * fire native evet + * + */ + fireNativeEvent(type: string, data: object | null, success?: () => void, error?: (message: string) => void):void + { + exec(success, error, "broadcaster", "fireNativeEvent", [ type, data ]); + } + + /** + * fire local evet + * + */ + fireEvent(type:string, data: object | null ):void + { + if( !this._channelExists(type) ) return; + + var event = document.createEvent('Event'); + event.initEvent(type, false, false); + if (data) { + for (var i in data) { + if (data.hasOwnProperty(i)) { + event[i] = data[i]; + } + } + } + this._channelFire( event ); + } + + /** + * add a listener + * + */ + addEventListener(eventname:string,f:Listener):void { + + if (!this._channelExists(eventname)) { + this._channelCreate(eventname); + exec( () => this._channelSubscribe(eventname,f), + (err:any) => console.log( "ERROR addEventListener: ", err), + "broadcaster", "addEventListener", [ eventname ]); + } + else { + this._channelSubscribe(eventname,f); + } + } + + /** + * remove a listener + * + */ + removeEventListener(eventname:string, f:Listener):void { + + if (this._channelExists(eventname)) { + if( this._channelUnsubscribe(eventname, f) === 0 ) { + exec( () => this._channelDelete(eventname), + (err:any) => console.log( "ERROR removeEventListener: ", err), + "broadcaster", "removeEventListener", [ eventname ]); + + } + } + } + +} + + +module.exports = new Broadcaster(); diff --git a/www/tsconfig.json b/www/tsconfig.json new file mode 100644 index 0000000..d8c5a8a --- /dev/null +++ b/www/tsconfig.json @@ -0,0 +1,59 @@ +{ + "compilerOptions": { + /* Basic Options */ + "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + // "outDir": "./", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + } +}