Server side PluginInterface ported into the browser
authorDarren <darren@darrenwhitlen.com>
Thu, 15 May 2014 15:14:24 +0000 (16:14 +0100)
committerDarren <darren@darrenwhitlen.com>
Thu, 15 May 2014 15:14:24 +0000 (16:14 +0100)
client/src/app.js
client/src/helpers/plugininterface.js [new file with mode: 0644]
server/plugininterface.js
server/settingsgenerator.js

index d65887a82453f9e4aa928941e3f6d110fcdba509..748a28b4c87468d4803cc7f728cdecbf1837113b 100644 (file)
@@ -17,7 +17,8 @@ _kiwi.applets = {};
 _kiwi.global = {\r
     build_version: '',  // Kiwi IRC version this is built from (Set from index.html)\r
     settings: undefined, // Instance of _kiwi.model.DataStore\r
-    plugins: undefined,\r
+    plugins: undefined, // Instance of _kiwi.model.PluginManager\r
+    events: undefined, // Instance of PluginInterface\r
     utils: {}, // TODO: Re-usable methods\r
 \r
     addMediaMessageType: function(match, buildHtml) {\r
@@ -116,6 +117,9 @@ _kiwi.global = {
             // Start the client up\r
             _kiwi.app.initializeInterfaces();\r
 \r
+            // Event emitter to let plugins interface with parts of kiwi\r
+            _kiwi.global.events  = new PluginInterface();\r
+\r
             // Now everything has started up, load the plugin manager for third party plugins\r
             _kiwi.global.plugins = new _kiwi.model.PluginManager();\r
 \r
diff --git a/client/src/helpers/plugininterface.js b/client/src/helpers/plugininterface.js
new file mode 100644 (file)
index 0000000..16d3d96
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * The same functionality as EventEmitter but with the inclusion of callbacks
+ */
+
+
+
+function PluginInterface () {
+    // Holder for all the bound listeners by this module
+    this._listeners = {};
+}
+
+
+
+PluginInterface.prototype.on = function (event_name, fn, scope) {
+    this._listeners[event_name] = this._listeners[event_name] || [];
+    this._listeners[event_name].push(['on', fn, scope]);
+};
+
+
+
+PluginInterface.prototype.once = function (event_name, fn, scope) {
+    this._listeners[event_name] = this._listeners[event_name] || [];
+    this._listeners[event_name].push(['once', fn, scope]);
+};
+
+
+
+PluginInterface.prototype.off = function (event_name, fn, scope) {
+    var idx;
+
+    if (typeof event_name === 'undefined') {
+        // Remove all listeners
+        this._listeners = {};
+
+    } else if (typeof fn === 'undefined') {
+        // Remove all of 1 event type
+        delete this._listeners[event_name];
+
+    } else if (typeof scope === 'undefined') {
+        // Remove a single event type + callback
+        for (idx in (this._listeners[event_name] || [])) {
+            if (this._listeners[event_name][idx][1] === fn) {
+                delete this._listeners[event_name][idx];
+            }
+        }
+    } else {
+        // Remove a single event type + callback + scope
+        for (idx in (this._listeners[event_name] || [])) {
+            if (this._listeners[event_name][idx][1] === fn && this._listeners[event_name][idx][2] === scope) {
+                delete this._listeners[event_name][idx];
+            }
+        }
+    }
+};
+
+
+
+// Call all the listeners for a certain event, passing them some event data that may be changed
+PluginInterface.prototype.emit = function (event_name, event_data) {
+    var emitter = new this.EmitCall(event_name, event_data);
+    var listeners = this._listeners[event_name] || [];
+
+    // Once emitted, remove any 'once' bound listeners
+    emitter.done(function () {
+        var len = listeners.length,
+            idx;
+
+        for(idx = 0; idx < len; idx++) {
+            if (listeners[idx][0] === 'once') {
+                listeners[idx] = undefined;
+            }
+        }
+    });
+
+    // Emit the event to the listeners and return
+    emitter.callListeners(listeners);
+    return emitter;
+};
+
+
+
+// Promise style object to emit events to listeners
+PluginInterface.prototype.EmitCall = function EmitCall (event_name, event_data) {
+    var that = this,
+        completed = false,
+        completed_fn = [],
+
+        // Has event.preventDefault() been called
+        prevented = false,
+        prevented_fn = [];
+
+
+    // Emit this event to an array of listeners
+    function callListeners(listeners) {
+        var current_event_idx = -1;
+
+        // Make sure we have some data to pass to the listeners
+        event_data = event_data || undefined;
+
+        // If no bound listeners for this event, leave now
+        if (listeners.length === 0) {
+            emitComplete();
+            return;
+        }
+
+
+        // Call the next listener in our array
+        function nextListener() {
+            var listener, event_obj;
+
+            // We want the next listener
+            current_event_idx++;
+
+            // If we've ran out of listeners end this emit call
+            if (!listeners[current_event_idx]) {
+                emitComplete();
+                return;
+            }
+
+            // Object the listener ammends to tell us what it's going to do
+            event_obj = {
+                // If changed to true, expect this listener is going to callback
+                wait: false,
+
+                // If wait is true, this callback must be called to continue running listeners
+                callback: function () {
+                    // Invalidate this callback incase a listener decides to call it again
+                    event_obj.callback = undefined;
+
+                    nextListener.apply(that);
+                },
+
+                // Prevents the default 'done' functions from executing
+                preventDefault: function () {
+                    prevented = true;
+                }
+            };
+
+
+            listener = listeners[current_event_idx];
+            listener[1].call(listener[2] || that, event_obj, event_data);
+
+            // If the listener hasn't signalled it's going to wait, proceed to next listener
+            if (!event_obj.wait) {
+                // Invalidate the callback just incase a listener decides to call it anyway
+                event_obj.callback = undefined;
+
+                nextListener();
+            }
+        }
+
+        nextListener();
+    }
+
+
+
+    function emitComplete() {
+        completed = true;
+
+        var funcs = prevented ? prevented_fn : completed_fn;
+        funcs = funcs || [];
+
+        // Call the completed/prevented functions
+        for (var idx = 0; idx < funcs.length; idx++) {
+            if (typeof funcs[idx] === 'function') funcs[idx]();
+        }
+    }
+
+
+
+    function addCompletedFunc(fn) {
+        // Only accept functions
+        if (typeof fn !== 'function') return false;
+
+        completed_fn.push(fn);
+
+        // If we have already completed the emits, call this now
+        if (completed && !prevented) fn();
+
+        return this;
+    }
+
+
+
+    function addPreventedFunc(fn) {
+        // Only accept functions
+        if (typeof fn !== 'function') return false;
+
+        prevented_fn.push(fn);
+
+        // If we have already completed the emits, call this now
+        if (completed && prevented) fn();
+
+        return this;
+    }
+
+
+    return {
+        callListeners: callListeners,
+        done: addCompletedFunc,
+        prevented: addPreventedFunc
+    };
+};
+
+
+
+// If running a node module, set the exports
+if (typeof module === 'object' && typeof module.exports !== 'undefined') {
+    module.exports = PluginInterface;
+}
+
+
+
+/*
+ * Example usage
+ */
+
+
+/*
+var modules = new PluginInterface();
+
+
+
+// A plugin
+modules.on('client:command', function (event, data) {
+    //event.wait = true;
+    setTimeout(event.callback, 2000);
+});
+
+
+
+
+// Core code that is being extended by plugins
+var data = {
+    nick: 'prawnsalald',
+    command: '/dothis'
+};
+
+
+modules.emit('client:command', data).done(function () {
+    console.log('Your command is: ' + data.command);
+});
+*/
\ No newline at end of file
index 3a02e39f5a1a5c320ff4e71113173cebdae9393c..16d3d96e9ef42160869876d6065c89d0e0c45368 100644 (file)
@@ -2,10 +2,85 @@
  * The same functionality as EventEmitter but with the inclusion of callbacks
  */
 
-/*
- * Promise style object to emit events to listeners
- */
-function EmitCall (event_name, event_data) {
+
+
+function PluginInterface () {
+    // Holder for all the bound listeners by this module
+    this._listeners = {};
+}
+
+
+
+PluginInterface.prototype.on = function (event_name, fn, scope) {
+    this._listeners[event_name] = this._listeners[event_name] || [];
+    this._listeners[event_name].push(['on', fn, scope]);
+};
+
+
+
+PluginInterface.prototype.once = function (event_name, fn, scope) {
+    this._listeners[event_name] = this._listeners[event_name] || [];
+    this._listeners[event_name].push(['once', fn, scope]);
+};
+
+
+
+PluginInterface.prototype.off = function (event_name, fn, scope) {
+    var idx;
+
+    if (typeof event_name === 'undefined') {
+        // Remove all listeners
+        this._listeners = {};
+
+    } else if (typeof fn === 'undefined') {
+        // Remove all of 1 event type
+        delete this._listeners[event_name];
+
+    } else if (typeof scope === 'undefined') {
+        // Remove a single event type + callback
+        for (idx in (this._listeners[event_name] || [])) {
+            if (this._listeners[event_name][idx][1] === fn) {
+                delete this._listeners[event_name][idx];
+            }
+        }
+    } else {
+        // Remove a single event type + callback + scope
+        for (idx in (this._listeners[event_name] || [])) {
+            if (this._listeners[event_name][idx][1] === fn && this._listeners[event_name][idx][2] === scope) {
+                delete this._listeners[event_name][idx];
+            }
+        }
+    }
+};
+
+
+
+// Call all the listeners for a certain event, passing them some event data that may be changed
+PluginInterface.prototype.emit = function (event_name, event_data) {
+    var emitter = new this.EmitCall(event_name, event_data);
+    var listeners = this._listeners[event_name] || [];
+
+    // Once emitted, remove any 'once' bound listeners
+    emitter.done(function () {
+        var len = listeners.length,
+            idx;
+
+        for(idx = 0; idx < len; idx++) {
+            if (listeners[idx][0] === 'once') {
+                listeners[idx] = undefined;
+            }
+        }
+    });
+
+    // Emit the event to the listeners and return
+    emitter.callListeners(listeners);
+    return emitter;
+};
+
+
+
+// Promise style object to emit events to listeners
+PluginInterface.prototype.EmitCall = function EmitCall (event_name, event_data) {
     var that = this,
         completed = false,
         completed_fn = [],
@@ -18,10 +93,10 @@ function EmitCall (event_name, event_data) {
     // Emit this event to an array of listeners
     function callListeners(listeners) {
         var current_event_idx = -1;
-        
+
         // Make sure we have some data to pass to the listeners
         event_data = event_data || undefined;
-        
+
         // If no bound listeners for this event, leave now
         if (listeners.length === 0) {
             emitComplete();
@@ -83,11 +158,12 @@ function EmitCall (event_name, event_data) {
         completed = true;
 
         var funcs = prevented ? prevented_fn : completed_fn;
+        funcs = funcs || [];
 
         // Call the completed/prevented functions
-        (funcs || []).forEach(function (fn) {
-            if (typeof fn === 'function') fn();
-        });
+        for (var idx = 0; idx < funcs.length; idx++) {
+            if (typeof funcs[idx] === 'function') funcs[idx]();
+        }
     }
 
 
@@ -124,84 +200,14 @@ function EmitCall (event_name, event_data) {
         done: addCompletedFunc,
         prevented: addPreventedFunc
     };
-}
-
-
-
-
-
-
-function PluginInterface () {
-    // Holder for all the bound listeners by this module
-    this._listeners = {};
-}
-
-
-PluginInterface.prototype.on = function (event_name, fn, scope) {
-    this._listeners[event_name] = this._listeners[event_name] || [];
-    this._listeners[event_name].push(['on', fn, scope]);
 };
 
 
 
-PluginInterface.prototype.once = function (event_name, fn, scope) {
-    this._listeners[event_name] = this._listeners[event_name] || [];
-    this._listeners[event_name].push(['once', fn, scope]);
-};
-
-
-
-PluginInterface.prototype.off = function (event_name, fn, scope) {
-    var idx;
-
-    if (typeof event_name === 'undefined') {
-        // Remove all listeners
-        this._listeners = {};
-
-    } else if (typeof fn === 'undefined') {
-        // Remove all of 1 event type
-        delete this._listeners[event_name];
-
-    } else if (typeof scope === 'undefined') {
-        // Remove a single event type + callback
-        for (idx in (this._listeners[event_name] || [])) {
-            if (this._listeners[event_name][idx][1] === fn) {
-                delete this._listeners[event_name][idx];
-            }
-        }
-    } else {
-        // Remove a single event type + callback + scope
-        for (idx in (this._listeners[event_name] || [])) {
-            if (this._listeners[event_name][idx][1] === fn && this._listeners[event_name][idx][2] === scope) {
-                delete this._listeners[event_name][idx];
-            }
-        }
-    }
-};
-
-
-
-// Call all the listeners for a certain event, passing them some event data that may be changed
-PluginInterface.prototype.emit = function (event_name, event_data) {
-    var emitter = new EmitCall(event_name, event_data);
-    var listeners = this._listeners[event_name] || [];
-
-    // Once emitted, remove any 'once' bound listeners
-    emitter.done(function () {
-        listeners.forEach(function (listener, idx) {
-            if (listener[0] === 'once') {
-                listeners[idx] = undefined;
-            }
-        });
-    });
-
-    // Emit the event to the listeners and return
-    emitter.callListeners(listeners);
-    return emitter;
-};
-
-
-module.exports = PluginInterface;
+// If running a node module, set the exports
+if (typeof module === 'object' && typeof module.exports !== 'undefined') {
+    module.exports = PluginInterface;
+}
 
 
 
index e6a3c4af328064599024d9bd0a225ec634958596..ce09159a32f42a56dc16fd3dc68643505fb02abe 100644 (file)
@@ -221,7 +221,8 @@ function addScripts(vars, debug) {
             'src/models/pluginmanager.js',
             'src/models/datastore.js',
             'src/helpers/utils.js',
-            'src/helpers/formatdate.js'
+            'src/helpers/formatdate.js',
+            'src/helpers/plugininterface.js'
         ],
 
         // Some views extend these, so make sure they're loaded beforehand