Use promise version of PluginInterface on the client
authorJack Allnutt <jack@allnutt.eu>
Thu, 21 Aug 2014 11:22:32 +0000 (12:22 +0100)
committerJack Allnutt <jack@allnutt.eu>
Thu, 21 Aug 2014 11:22:32 +0000 (12:22 +0100)
client/assets/plugins/textstyle.html
client/src/helpers/plugininterface.js
client/src/models/network.js
client/src/views/channel.js
client/src/views/controlbox.js
client/src/views/memberlist.js

index 1097b522143fe32a30c93d2fc758dda06ec6caa5..3567d4dc5055eaff4c59bc5258196ea4cd29558e 100644 (file)
             if (data.params[1]) {
                 data.params[1] = style_codes + ' ' + data.params[1];
             }
+
+            event.callback();
         });
 
 
index 16d3d96e9ef42160869876d6065c89d0e0c45368..ee025d9dbd118b24019f3e571c495bc5f63f6525 100644 (file)
-/*
- * The same functionality as EventEmitter but with the inclusion of callbacks
- */
-
-
-
-function PluginInterface () {
-    // Holder for all the bound listeners by this module
-    this._listeners = {};
+function PluginInterface() {
+    this.listeners = [];
 }
 
+PluginInterface.prototype.on = PluginInterface.prototype.addListener = function addListener(type, listener) {
+    if (typeof listener !== 'function') {
+        throw new TypeError('listener must be a function');
+    }
 
-
-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];
-            }
+    if (this.listeners[type]) {
+        if (!_.contains(this.listeners[type], listener)) {
+            this.listeners[type].push(listener);
         }
     } 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];
-            }
-        }
+        this.listeners[type] = [listener];
     }
-};
-
-
 
-// 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;
+    return this;
 };
 
-
-
-// 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();
+PluginInterface.prototype.once = function once(type, listener) {
+    if (typeof listener !== 'function') {
+        throw new TypeError('listener must be a function');
     }
 
+    var fired = false;
 
+    function g() {
+        this.removeListener(type, g);
 
-    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]();
+        if (!fired) {
+            fired = true;
+            listener.apply(this, arguments);
         }
     }
 
+    g.listener = listener;
+    this.on(type, g);
 
+    return this;
+};
 
-    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();
-
+PluginInterface.prototype.off = PluginInterface.prototype.removeListener = function removeListener(type, listener) {
+    if (!this.listeners[type]) {
         return this;
     }
 
+    this.listeners[type] = _.without(this.listeners[type], listener);
 
+    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;
-    }
+PluginInterface.prototype.emit = function emit(type, data) {
+    var that = this;
+    return new Promise(function (emit_resolve, emit_reject) {
+        var rejected = false,
+            rejected_reasons = [];
 
+        if (!that.listeners[type]) {
+            return emit_resolve(data);
+        }
 
-    return {
-        callListeners: callListeners,
-        done: addCompletedFunc,
-        prevented: addPreventedFunc
-    };
+        (that.listeners[type].reduce(function (listener_promise, listener) {
+            return listener_promise.then(function (data) {
+                return new Promise(function (resolve) {
+                    listener({
+                        callback: function () {
+                            resolve(data);
+                        },
+                        preventDefault: function (reason) {
+                            rejected = true;
+                            if (reason) {
+                                rejected_reasons.push(reason);
+                            }
+                        }
+                    }, data);
+                });
+            });
+        }, Promise.resolve(data))).then(function (data) {
+            if (rejected) {
+                emit_reject({data: data, reasons: rejected_reasons});
+            } else {
+                emit_resolve(data);
+            }
+        });
+    });
 };
 
-
-
 // If running a node module, set the exports
 if (typeof module === 'object' && typeof module.exports !== 'undefined') {
     module.exports = PluginInterface;
 }
 
+/* Test cases
 
-
-/*
- * Example usage
- */
-
-
-/*
-var modules = new PluginInterface();
-
-
-
-// A plugin
-modules.on('client:command', function (event, data) {
-    //event.wait = true;
-    setTimeout(event.callback, 2000);
+var p = new PluginInterface();
+p.on('test', function (event, data) {
+    data.a += '!';
+    event.callback();
 });
 
+p.emit('test', {a: 'hello world'}).then(function (data) {
+    if (data.a === 'hello world!') {
+        console.log('Test passed');
+    } else {
+        console.error('Test failed!');
+    }
+}, function (err) {
+    console.error('Test failed!');
+});
 
 
-
-// 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);
+var p = new PluginInterface();
+p.on('test', function (event, data) {
+    data.a += '!';
+    event.callback();
 });
-*/
\ No newline at end of file
+p.on('test', function (event) {
+    event.preventDefault('testing');
+    event.callback();
+})
+
+p.emit('test', {a:'hello world'}).then(function (){
+    console.error('Test failed!');
+}, function (data, reasons) {
+    if ((data.data.a === 'hello world!') && (data.reasons.length === 1 && data.reasons[0] === 'testing')) {
+        console.log('Test passed');
+    } else {
+        console.error('Test failed!');
+    }
+});
+
+*/
index df8ac5361999667a73f0c8d5f0f7515b70510c74..1bd3c325f04d56b84a49c4a4541763fffbdc774b 100644 (file)
 
     function onMessage(event) {
         _kiwi.global.events.emit('message:new', {network: this, message: event})
-        .done(_.bind(function() {
+        .then(_.bind(function() {
             var panel,
                 is_pm = ((event.target || '').toLowerCase() == this.get('nick').toLowerCase());
 
index f3d1ac955a723b0b0dcf198199112446326ebc0e..7961fa5bf2e7062db2281fb84072e154673f2a42 100644 (file)
@@ -61,7 +61,7 @@ _kiwi.view.Channel = _kiwi.view.Panel.extend({
         msg = this.generateMessageDisplayObj(msg);
 
         _kiwi.global.events.emit('message:display', {panel: this.model, message: msg})
-        .done(_.bind(function() {
+        .then(_.bind(function() {
             var line_msg;
 
             // Format the nick to the config defined format
index 4fbedf815e851d63f7cc0e1f8da1eb43cc136db9..29792e70cba32c8333f2df6ab105223c1c9dfbfc 100644 (file)
@@ -284,7 +284,7 @@ _kiwi.view.ControlBox = Backbone.View.extend({
         events_data = {command: command, params: params};
 
         _kiwi.global.events.emit('command', events_data)
-        .done(function() {
+        .then(function() {
             // Trigger the command events
             that.trigger('command', {command: events_data.command, params: events_data.params});
             that.trigger('command:' + events_data.command, {command: events_data.command, params: events_data.params});
index a5820873d8be4dbaca045a14c96560cfc3df1a90..8a05152ca0e7c0302ff1f40a9582e9c91d139539 100644 (file)
@@ -52,7 +52,7 @@ _kiwi.view.MemberList = Backbone.View.extend({
         menu.showFooter(false);
 
         _kiwi.global.events.emit('usermenu:created', {menu: menu, userbox: userbox})
-        .done(_.bind(function() {
+        .then(_.bind(function() {
             menu.show();
 
             var t = event.pageY,