Merge pull request #60 from brodybits/events-singleton

Cordova events singleton object
diff --git a/src/events.js b/src/events.js
index 7038643..d16485c 100644
--- a/src/events.js
+++ b/src/events.js
@@ -17,56 +17,65 @@
     under the License.
 */
 
-var EventEmitter = require('events').EventEmitter;
+const EventEmitter = require('events').EventEmitter;
 
-var INSTANCE = new EventEmitter();
-INSTANCE.setMaxListeners(20);
-var EVENTS_RECEIVER;
+const MAX_LISTENERS = 20;
 
-module.exports = INSTANCE;
+const INSTANCE_KEY = Symbol.for('org.apache.cordova.common.CordovaEvents');
 
-/**
- * Sets up current instance to forward emitted events to another EventEmitter
- *   instance.
- *
- * @param   {EventEmitter}  [eventEmitter]  The emitter instance to forward
- *   events to. Falsy value, when passed, disables forwarding.
- */
-module.exports.forwardEventsTo = function (eventEmitter) {
+let EVENTS_RECEIVER = null;
 
-    // If no argument is specified disable events forwarding
-    if (!eventEmitter) {
-        EVENTS_RECEIVER = undefined;
-        return;
+class CordovaEventEmitter extends EventEmitter {
+    /**
+     * Sets up current instance to forward emitted events to another EventEmitter
+     *   instance.
+     *
+     * @param   {EventEmitter}  [eventEmitter]  The emitter instance to forward
+     *   events to. Falsy value, when passed, disables forwarding.
+     */
+    forwardEventsTo (eventEmitter) {
+        // If no argument is specified disable events forwarding
+        if (!eventEmitter) {
+            EVENTS_RECEIVER = undefined;
+            return;
+        }
+
+        if (!(eventEmitter instanceof EventEmitter)) {
+            throw new Error('Cordova events can be redirected to another EventEmitter instance only');
+        }
+
+        // CB-10940 Skipping forwarding to self to avoid infinite recursion.
+        // This is the case when the modules are npm-linked.
+        if (this !== eventEmitter) {
+            EVENTS_RECEIVER = eventEmitter;
+        } else {
+            // Reset forwarding if we are subscribing to self
+            EVENTS_RECEIVER = undefined;
+        }
     }
 
-    if (!(eventEmitter instanceof EventEmitter)) { throw new Error('Cordova events can be redirected to another EventEmitter instance only'); }
+    /**
+     * Sets up current instance to forward emitted events to another EventEmitter
+     *   instance.
+     *
+     * @param   {EventEmitter}  [eventEmitter]  The emitter instance to forward
+     *   events to. Falsy value, when passed, disables forwarding.
+     */
+    emit (eventName, ...args) {
+        if (EVENTS_RECEIVER) {
+            EVENTS_RECEIVER.emit(eventName, ...args);
+        }
 
-    // CB-10940 Skipping forwarding to self to avoid infinite recursion.
-    // This is the case when the modules are npm-linked.
-    if (this !== eventEmitter) {
-        EVENTS_RECEIVER = eventEmitter;
-    } else {
-        // Reset forwarding if we are subscribing to self
-        EVENTS_RECEIVER = undefined;
+        return super.emit(eventName, ...args);
     }
-};
+}
 
-var emit = INSTANCE.emit;
+// This singleton instance pattern is based on the ideas from
+// https://derickbailey.com/2016/03/09/creating-a-true-singleton-in-node-js-with-es6-symbols/
+if (Object.getOwnPropertySymbols(global).indexOf(INSTANCE_KEY) === -1) {
+    const events = new CordovaEventEmitter();
+    events.setMaxListeners(MAX_LISTENERS);
+    global[INSTANCE_KEY] = events;
+}
 
-/**
- * This method replaces original 'emit' method to allow events forwarding.
- *
- * @return  {eventEmitter}  Current instance to allow calls chaining, as
- *   original 'emit' does
- */
-module.exports.emit = function () {
-
-    var args = Array.prototype.slice.call(arguments);
-
-    if (EVENTS_RECEIVER) {
-        EVENTS_RECEIVER.emit.apply(EVENTS_RECEIVER, args);
-    }
-
-    return emit.apply(this, args);
-};
+module.exports = global[INSTANCE_KEY];