├── .gitignore ├── package.json ├── index.js ├── README.md └── test └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "forward-emitter", 3 | "version": "0.1.0", 4 | "description": "Forward events from any Node EventEmitter to another EventEmitter.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha test/" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/STRML/forward-emitter" 12 | }, 13 | "keywords": [ 14 | "eventemitter", 15 | "events", 16 | "forward" 17 | ], 18 | "devDependencies": { 19 | "mocha": "*", 20 | "should": "*" 21 | }, 22 | "author": "Samuel Reed (http://strml.net/)", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/STRML/forward-emitter/issues" 26 | }, 27 | "homepage": "https://github.com/STRML/forward-emitter" 28 | } 29 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function forwardEmitter(src, dest, filterFn) { 4 | // If no filter is passed, forward all events. 5 | if (typeof filterFn !== 'function') { 6 | filterFn = function() { return true; }; 7 | } 8 | 9 | function __forward_emitter_newListener(eventName, listener) { 10 | if (filterFn(eventName) && listener.name.indexOf('__forward_emitter_') !== 0) src.on(eventName, listener); 11 | } 12 | 13 | function __forward_emitter_removeListener(eventName, listener) { 14 | src.removeListener(eventName, listener); 15 | } 16 | 17 | // Listeners bound to the destination emitter should be bound to the source emitter. 18 | dest.on('newListener', __forward_emitter_newListener); 19 | 20 | // When a listener is removed from the destination emitter, remove it from the source emitter 21 | // (otherwise it will continue to be called). 22 | dest.on('removeListener', __forward_emitter_removeListener); 23 | 24 | }; 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Forward-emitter 2 | ============== 3 | 4 | Forward events from one Node EventEmitter to another. 5 | 6 | Installation 7 | ------------ 8 | 9 | `npm install forward-emitter` 10 | 11 | Usage 12 | ----- 13 | 14 | ```javascript 15 | var forward = require('forward-emitter'); 16 | 17 | // Basic forwarding 18 | forward(src, dest); 19 | 20 | // Forward only selected events 21 | // In this example, only the 'connect' event is forwarded. 22 | forward(src, dest, function (eventName) { 23 | return eventName === 'connect'; 24 | }); 25 | 26 | // Forward all events except exclusions 27 | var exclusions = ['connect', 'disconnect']; 28 | forward(src, dest, function(eventName) { 29 | return exclusions.indexOf(eventName) === -1; 30 | }); 31 | 32 | ``` 33 | 34 | How It Works 35 | ------------ 36 | 37 | `forward-emitter` listens to the `newListener` and `removeListener` events, introduced in Node `0.8` and 38 | Node `0.10` respectively. When a callback is bound to an event on the destination emitter, the listener 39 | is forwarded to the source emitter. 40 | 41 | For that reason, this module should not be used in Node `< 0.10` if you plan to use `removeListener`. 42 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var should = require('should'); 3 | var EventEmitter = require('events').EventEmitter; 4 | var forward = require('..'); 5 | 6 | /*global describe:true, it:true */ 7 | describe('forward(src, dest)', function() { 8 | it('should forward from src to dest', function(done) { 9 | var src = new EventEmitter(); 10 | var dest = new EventEmitter(); 11 | forward(src, dest); 12 | 13 | dest.on('test', function(a, b, c) { 14 | [].slice.call(arguments).should.eql([1,2,3]); 15 | done(); 16 | }); 17 | 18 | src.emit('test', 1, 2, 3); 19 | }); 20 | 21 | it('emitters on src should continue to work', function(done){ 22 | var src = new EventEmitter(); 23 | var dest = new EventEmitter(); 24 | forward(src, dest); 25 | 26 | src.on('test', done); 27 | src.emit('test'); 28 | }); 29 | 30 | it('should forward in a chain', function() { 31 | var a = new EventEmitter(); 32 | var b = new EventEmitter(); 33 | var c = new EventEmitter(); 34 | 35 | forward(a, b); 36 | forward(b, c); 37 | 38 | var calls = 0; 39 | a.on('test', increment); 40 | b.on('test', increment); 41 | c.on('test', increment); 42 | function increment() { 43 | calls++; 44 | } 45 | 46 | a.emit('test', 'event payload'); 47 | calls.should.equal(3); 48 | }); 49 | 50 | it('should forward errors', function(done){ 51 | var src = new EventEmitter(); 52 | var dest = new EventEmitter(); 53 | forward(src, dest); 54 | 55 | dest.on('error', function(err){ 56 | err.message.should.equal('test error'); 57 | done(); 58 | }); 59 | 60 | src.emit('error', new Error('test error')); 61 | }); 62 | 63 | it('should not loop forever', function(done) { 64 | var src1 = new EventEmitter(); 65 | var src2 = new EventEmitter(); 66 | var src3 = new EventEmitter(); 67 | var src4 = new EventEmitter(); 68 | var dest = new EventEmitter(); 69 | forward(src1, dest); 70 | forward(src2, dest); 71 | forward(src3, dest); 72 | forward(src4, dest); 73 | 74 | done(); 75 | }); 76 | }); 77 | 78 | describe('forward(src, dest, filterFn)', function() { 79 | it('should forward only an accepted event', function(done) { 80 | var src = new EventEmitter(); 81 | var dest = new EventEmitter(); 82 | forward(src, dest, function(eventName) { 83 | return eventName === 'accepted'; 84 | }); 85 | 86 | var calls = 0; 87 | function increment() { 88 | calls++; 89 | } 90 | 91 | dest.on('accepted', increment); 92 | dest.on('foo', increment); 93 | dest.on('bar', increment); 94 | 95 | src.emit('accepted'); 96 | src.emit('foo'); 97 | src.emit('bar'); 98 | 99 | calls.should.equal(1); 100 | done(); 101 | }); 102 | 103 | it('should forward everything but exclusions', function(done) { 104 | var src = new EventEmitter(); 105 | var dest = new EventEmitter(); 106 | var exclusions = ['foo', 'bar']; 107 | forward(src, dest, function(eventName) { 108 | return exclusions.indexOf(eventName) === -1; 109 | }); 110 | 111 | var calls = 0; 112 | function increment() { 113 | calls++; 114 | } 115 | 116 | dest.on('accepted', increment); 117 | dest.on('foo', increment); 118 | dest.on('bar', increment); 119 | 120 | src.emit('accepted'); 121 | src.emit('foo'); 122 | src.emit('bar'); 123 | 124 | calls.should.equal(1); 125 | done(); 126 | }); 127 | }); 128 | --------------------------------------------------------------------------------