UNPKG

chat-engine

Version:
248 lines (186 loc) 6.6 kB
const waterfall = require('async/waterfall'); const RootEmitter = require('./root_emitter'); const augmentSender = require('../plugins/augment/sender'); /** An ChatEngine generic emitter that supports plugins and duplicates events on the root emitter. @class Emitter @extends RootEmitter */ class Emitter extends RootEmitter { constructor(chatEngine) { super(); this.chatEngine = chatEngine; this.name = 'Emitter'; /** Stores a list of plugins bound to this object @private */ this.plugins = []; /** Stores in memory keys and values @private */ this._dataset = {}; this.plugin(augmentSender(chatEngine)); /** Emit events locally. @private @param {String} event The event payload object */ this._emit = (event, data = {}) => { // all events are forwarded to ChatEngine object // so you can globally bind to events with ChatEngine.on() this.chatEngine._emit(event, data, this); // emit the event from the object that created it this.emitter.emit(event, data); return this; }; /** * Listen for a specific event and fire a callback when it's emitted. Supports wildcard matching. * @method * @param {String} event The event name * @param {Function} cb The function to run when the event is emitted * @example * * // Get notified whenever someone joins the room * object.on('event', (payload) => { * console.log('event was fired'). * }) * * // Get notified of event.a and event.b * object.on('event.*', (payload) => { * console.log('event.a or event.b was fired').; * }) */ this.on = (event, cb) => { // call the private _on property this._on(event, cb); return this; }; } // add an object as a subobject under a namespace /** * @private */ addChild(childName, childOb) { // assign the new child object as a property of parent under the // given namespace this[childName] = childOb; // assign a data set for the namespace if it doesn't exist if (!this._dataset[childName]) { this._dataset[childName] = {}; } // the new object can use ```this.parent``` to access // the root class childOb.parent = this; // bind get() and set() to the data set childOb.get = this.get.bind(this._dataset[childName]); childOb.set = this.set.bind(this._dataset[childName]); } get(key) { return this[key]; } set(key, value) { if (this[key] && !value) { delete this[key]; } else { this[key] = value; } } /** Binds a plugin to this object @param {Object} module The plugin module @tutorial using */ plugin(module) { // add this plugin to a list of plugins for this object this.plugins.push(module); // see if there are plugins to attach to this class if (module.extends && module.extends[this.name]) { // attach the plugins to this class // under their namespace this.addChild(module.namespace, new module.extends[this.name]()); this[module.namespace].ChatEngine = this.chatEngine; // if the plugin has a special construct function // run it if (this[module.namespace].construct) { this[module.namespace].construct(); } } return this; } /** * @private */ bindProtoPlugins() { if (this.chatEngine.protoPlugins[this.name]) { this.chatEngine.protoPlugins[this.name].forEach((module) => { this.plugin(module); }); } } /** Broadcasts an event locally to all listeners. @private @param {String} event The event name @param {Object} payload The event payload object */ trigger(event, payload = {}, done = () => {}) { // let plugins modify the event this.runPluginQueue('on', event, (next) => { next(null, payload); }, (reject, pluginResponse) => { if (reject) { done(reject); } else { // emit this event to any listener this._emit(event, pluginResponse); done(null, event, pluginResponse); } }); } /** Load plugins and attach a queue of functions to execute before and after events are trigger or received. @private @param {String} location Where in the middleeware the event should run (emit, trigger) @param {String} event The event name @param {String} first The first function to run before the plugins have run @param {String} last The last function to run after the plugins have run */ runPluginQueue(location, event, first, last) { // this assembles a queue of functions to run as middleware // event is a triggered event key let pluginQueue = []; // the first function is always required pluginQueue.push(first); // look through the configured plugins this.plugins.forEach((pluginItem) => { // if they have defined a function to run specifically // for this event if (pluginItem.middleware && pluginItem.middleware[location]) { if (pluginItem.middleware[location][event]) { // add the function to the queue pluginQueue.push(pluginItem.middleware[location][event]); } if (pluginItem.middleware[location]['*']) { // add the function to the queue pluginQueue.push(pluginItem.middleware[location]['*']); } } }); // waterfall runs the functions in assigned order // waiting for one to complete before moving to the next // when it's done, the ```last``` parameter is called waterfall(pluginQueue, last); } /** * @private */ onConstructed() { this.bindProtoPlugins(); this.trigger(['$', 'created', this.name.toLowerCase()].join('.')); } } module.exports = Emitter;