Server: Removing plugin listeners from PluginInterface prototype
[KiwiIRC.git] / server / plugininterface.js
1 /*
2 * The same functionality as EventEmitter but with the inclusion of callbacks
3 */
4
5 /*
6 * Promise style object to emit events to listeners
7 */
8 function EmitCall (event_name, event_data) {
9 var that = this,
10 completed = false,
11 completed_fn = [];
12
13
14 // Emit this event to an array of listeners
15 function callListeners(listeners) {
16 var current_event_idx = -1;
17
18 // Make sure we have some data to pass to the listeners
19 event_data = event_data || undefined;
20
21 // If no bound listeners for this event, leave now
22 if (listeners.length === 0) {
23 emitComplete();
24 return;
25 }
26
27
28 // Call the next listener in our array
29 function nextListener() {
30 var listener, event_obj;
31
32 // We want the next listener
33 current_event_idx++;
34
35 // If we've ran out of listeners end this emit call
36 if (!listeners[current_event_idx]) {
37 emitComplete();
38 return;
39 }
40
41 // Object the listener ammends to tell us what it's going to do
42 event_obj = {
43 // If changed to true, expect this listener is going to callback
44 wait: false,
45
46 // If wait is true, this callback must be called to continue running listeners
47 callback: function () {
48 // Invalidate this callback incase a listener decides to call it again
49 callback = undefined;
50
51 nextListener.apply(that);
52 }
53 };
54
55
56 listener = listeners[current_event_idx];
57 listener[1].call(listener[2] || that, event_obj, event_data);
58
59 // If the listener hasn't signalled it's going to wait, proceed to next listener
60 if (!event_obj.wait) {
61 // Invalidate the callback just incase a listener decides to call it anyway
62 event_obj.callback = undefined;
63
64 nextListener();
65 }
66 }
67
68 nextListener();
69 }
70
71
72
73 function emitComplete() {
74 completed = true;
75
76 // Call the completed functions
77 (completed_fn || []).forEach(function (fn) {
78 if (typeof fn === 'function') fn();
79 });
80 }
81
82
83
84 function done(fn) {
85 // Only accept functions
86 if (typeof fn !== 'function') return false;
87
88 completed_fn.push(fn);
89
90 // If we have already completed the emits, call this now
91 if (completed) fn();
92 }
93
94
95 return {
96 callListeners: callListeners,
97 done: done
98 };
99 }
100
101
102
103
104
105
106 function PluginInterface () {
107 // Holder for all the bound listeners by this module
108 this._listeners = {};
109 }
110
111
112 PluginInterface.prototype.on = function (event_name, fn, scope) {
113 this._listeners[event_name] = this._listeners[event_name] || [];
114 this._listeners[event_name].push(['on', fn, scope]);
115 };
116
117
118
119 PluginInterface.prototype.once = function (event_name, fn, scope) {
120 this._listeners[event_name] = this._listeners[event_name] || [];
121 this._listeners[event_name].push(['once', fn, scope]);
122 };
123
124
125
126 PluginInterface.prototype.off = function (event_name, fn, scope) {
127 var idx;
128
129 if (typeof event_name === 'undefined') {
130 // Remove all listeners
131 this._listeners = {};
132
133 } else if (typeof fn === 'undefined') {
134 // Remove all of 1 event type
135 delete this._listeners[event_name];
136
137 } else if (typeof scope === 'undefined') {
138 // Remove a single event type + callback
139 for (idx in (this._listeners[event_name] || [])) {
140 if (this._listeners[event_name][idx][1] === fn) {
141 delete this._listeners[event_name][idx];
142 }
143 }
144 } else {
145 // Remove a single event type + callback + scope
146 for (idx in (this._listeners[event_name] || [])) {
147 if (this._listeners[event_name][idx][1] === fn && this._listeners[event_name][idx][2] === scope) {
148 delete this._listeners[event_name][idx];
149 }
150 }
151 }
152 };
153
154
155
156 // Call all the listeners for a certain event, passing them some event data that may be changed
157 PluginInterface.prototype.emit = function (event_name, event_data) {
158 var emitter = new EmitCall(event_name, event_data);
159 var listeners = this._listeners[event_name] || [];
160
161 // Once emitted, remove any 'once' bound listeners
162 emitter.done(function () {
163 listeners.forEach(function (listener, idx) {
164 if (listener[0] === 'once') {
165 listeners[idx] = undefined;
166 }
167 });
168 });
169
170 // Emit the event to the listeners and return
171 emitter.callListeners(listeners);
172 return emitter;
173 };
174
175
176 module.exports = PluginInterface;
177
178
179
180 /*
181 * Example usage
182 */
183
184
185 /*
186 var modules = new PluginInterface();
187
188
189
190 // A plugin
191 modules.on('client:command', function (event, data) {
192 //event.wait = true;
193 setTimeout(event.callback, 2000);
194 });
195
196
197
198
199 // Core code that is being extended by plugins
200 var data = {
201 nick: 'prawnsalald',
202 command: '/dothis'
203 };
204
205
206 modules.emit('client:command', data).done(function () {
207 console.log('Your command is: ' + data.command);
208 });
209 */