Skip to content

Commit d2c588f

Browse files
Initial commit.
0 parents  commit d2c588f

File tree

7 files changed

+542
-0
lines changed

7 files changed

+542
-0
lines changed

EventProxy.js

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
'use strict'
2+
var EventEmitter = require('events').EventEmitter
3+
var inherits = require('inherits')
4+
5+
module.exports = EventProxy
6+
7+
function EventProxy (proxied) {
8+
this._listening = {}
9+
this._ownEvents = {}
10+
this._proxied = proxied
11+
}
12+
13+
inherits(EventProxy, EventEmitter)
14+
15+
Object.assign(EventProxy.prototype, {
16+
_initEventProxy: function (eventName) {
17+
if (this._ownEvents[eventName]) return
18+
19+
// Already listening to proxied event
20+
if (this._listening[eventName] !== undefined) return
21+
22+
var self = this
23+
var listener = function (payload) {
24+
self.emit(eventName, payload)
25+
}
26+
this._listening[eventName] = listener
27+
this._proxied.addListener(eventName, listener)
28+
},
29+
_exitEventProxy: function (eventName) {
30+
if (this._ownEvents[eventName]) return
31+
32+
// Still listener for event given
33+
if (this.listenerCount(eventName) > 0) return
34+
35+
var listener = this._listening[eventName]
36+
// Called without ever being added
37+
if (listener === undefined) return
38+
39+
delete this._listening[eventName]
40+
this._proxied.removeListener(eventName, listener)
41+
},
42+
_exitAll: function () {
43+
for (var eventName of this._listening) {
44+
this._exitEventProxy(eventName)
45+
}
46+
},
47+
addListener: function (eventName, listener) {
48+
EventEmitter.prototype.addListener.call(this, eventName, listener)
49+
this._initEventProxy(eventName)
50+
return this
51+
},
52+
prependListener: function (eventName, listener) {
53+
EventEmitter.prototype.prependListener.call(this, eventName, listener)
54+
this._initEventProxy(eventName)
55+
return this
56+
},
57+
removeListener: function (eventName, listener) {
58+
EventEmitter.prototype.removeListener.call(this, eventName, listener)
59+
this._exitEventProxy(eventName)
60+
return this
61+
},
62+
removeAllListeners: function (eventName) {
63+
EventEmitter.prototype.removeAllListeners.call(this, eventName)
64+
if (arguments.length === 0) {
65+
this._exitAll()
66+
} else {
67+
this._exitEventProxy(eventName)
68+
}
69+
return this
70+
},
71+
once: function (eventName, listener) {
72+
var self = this
73+
var wrapper = function (payload) {
74+
listener(payload)
75+
self.removeListener(eventName, wrapper)
76+
}
77+
return this.addListener(eventName, wrapper)
78+
},
79+
prependOnceListener: function (eventName, listener) {
80+
var self = this
81+
var wrapper = function (payload) {
82+
listener(payload)
83+
self.removeListener(eventName, wrapper)
84+
}
85+
return this.prependListener(eventName, wrapper)
86+
},
87+
on: function (eventName, listener) {
88+
return this.addListener(eventName, listener)
89+
},
90+
off: function (eventName, listener) {
91+
return this.removeListener(eventName, listener)
92+
}
93+
})

EventProxy.test.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#!/usr/bin/env node
2+
const tape = require('tape')
3+
const events = require('events')
4+
const EventProxy = require('./EventProxy.js')
5+
6+
tape('basic listening', function (t) {
7+
var target = new events.EventEmitter()
8+
var proxy = new EventProxy(target)
9+
t.equals(proxy.listenerCount('hello'), 0, 'By default no listener is added')
10+
target.addListener('hello', function () {})
11+
t.equals(proxy.listenerCount('hello'), 0, 'Just because a listener is added to the target doesnt mean there is a listener on the source')
12+
var addResult = proxy.addListener('holla', function (data) {
13+
t.equals(data, 'mundo', 'target event is passed to the proxy')
14+
t.end()
15+
})
16+
t.equals(addResult, proxy, 'Return type of ')
17+
t.equals(target.listenerCount('holla'), 1, 'There should be a listener on the target, after adding one to the proxy')
18+
target.emit('holla', 'mundo')
19+
})
20+
21+
tape('once', function (t) {
22+
var target = new events.EventEmitter()
23+
var proxy = new EventProxy(target)
24+
var count = 0
25+
var addResult = proxy.once('hello', function (data) {
26+
t.equals(count, 0, 'event called once.')
27+
t.equals(data, 'world', 'data passed through')
28+
count += 1
29+
})
30+
t.equals(addResult, proxy, 'Result of once should be the proxy instance')
31+
t.equals(target.listenerCount('hello'), 1, 'Should have listener after once added')
32+
target.emit('hello', 'world')
33+
t.equals(target.listenerCount('hello'), 0, 'Should not have listener on target after once executed')
34+
t.equals(proxy.listenerCount('hello'), 0, 'Should not have listener on proxy after once executed')
35+
target.emit('hello', 'mundo')
36+
t.end()
37+
})
38+
39+
tape('prepend once', function (t) {
40+
var target = new events.EventEmitter()
41+
var proxy = new EventProxy(target)
42+
var count = 0
43+
target.addListener('hello', function () {
44+
count += 1
45+
})
46+
proxy.addListener('hello', function () {
47+
count += 2
48+
})
49+
var addResult = proxy.prependOnceListener('hello', function (data) {
50+
t.equals(count, 1, 'event called once, before the listeners on proxy, after the listeners on target')
51+
t.equals(data, 'world', 'data passed through')
52+
count += 1
53+
})
54+
t.equals(addResult, proxy, 'Result of prepend once is proxy instance')
55+
target.emit('hello', 'world')
56+
target.emit('hello', 'mundo')
57+
t.equals(count, 7)
58+
t.end()
59+
})
60+
61+
tape('remove all listeners', function (t) {
62+
var target = new events.EventEmitter()
63+
var proxy = new EventProxy(target)
64+
var count = 0
65+
target.addListener('hello', function () {
66+
count += 1
67+
})
68+
proxy.addListener('hello', function () {
69+
t.fail('Shouldnt be called because removed by removeAllListeners')
70+
})
71+
var removeResult = proxy.removeAllListeners('hello')
72+
t.equals(removeResult, proxy, 'Result of removeAllListeners is proxy instance')
73+
target.emit('hello', 'world')
74+
t.equals(count, 1, 'target listener is still called')
75+
t.end()
76+
})
77+
78+
tape('own events', function (t) {
79+
var target = new events.EventEmitter()
80+
var proxy = new EventProxy(target)
81+
proxy._ownEvents['hello'] = true
82+
target.on('hello', function (data) {
83+
t.equals(data, 'world', 'target called with world')
84+
})
85+
var onResult = proxy.on('hello', function (data) {
86+
t.equals(data, 'mundo', 'proxy called with mundo, target not passed to proxy')
87+
})
88+
t.equals(onResult, proxy, 'result of on is proxy instance')
89+
target.emit('hello', 'world')
90+
proxy.emit('hello', 'mundo')
91+
t.end()
92+
})

PauseWrapper.js

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
2+
'use strict'
3+
var EventProxy = require('./EventProxy.js')
4+
var EventEmitter = require('events').EventEmitter
5+
var inherits = require('inherits')
6+
7+
module.exports = PauseWrapper
8+
9+
function PauseWrapper (proxied) {
10+
if (!(this instanceof PauseWrapper)) {
11+
return new PauseWrapper(proxied)
12+
}
13+
EventProxy.call(this, proxied)
14+
this._ownEvents['pause'] = true
15+
this._ownEvents['paused'] = true
16+
this._ownEvents['resume'] = true
17+
18+
this.pauseable = true
19+
this._paused = false
20+
this._closed = false
21+
}
22+
23+
inherits(PauseWrapper, EventProxy)
24+
25+
Object.assign(PauseWrapper.prototype, {
26+
_onResume: function (handler) {
27+
if (this._closed) return
28+
if (this._paused || this.resuming) {
29+
return this._pauseStack.push(handler)
30+
}
31+
handler.call(this)
32+
},
33+
_onResumeCb: function (cb, handler) {
34+
var scope = this
35+
this._onResume(function () {
36+
handler.call(this, function (err, data) {
37+
scope._onResume(function () {
38+
if (!cb) return
39+
cb.call(scope, err, data)
40+
})
41+
})
42+
})
43+
},
44+
emit: function (eventName, payload) {
45+
if (eventName === 'close' || eventName === 'destroy') {
46+
this._closed = true
47+
return EventEmitter.prototype.emit.call(this, eventName, payload)
48+
}
49+
if (this._closed) {
50+
return
51+
}
52+
if (eventName === 'pause' || eventName === 'resume' || eventName === 'paused') {
53+
return EventEmitter.prototype.emit.call(this, eventName, payload)
54+
}
55+
var scope = this
56+
this._onResume(function () { EventEmitter.prototype.emit.call(scope, eventName, payload) })
57+
},
58+
setPaused: function (paused) {
59+
if (paused === this._paused) return false
60+
if (this._resuming) {
61+
var scope = this
62+
this._pauseStack.push(function () { scope.setPaused(paused) })
63+
return false
64+
}
65+
this._paused = paused
66+
if (paused) {
67+
this._pauseStack = []
68+
} else {
69+
while (this._pauseStack) {
70+
var pauseStack = this._pauseStack
71+
delete this._pauseStack
72+
while (pauseStack.length > 0) {
73+
var handler = pauseStack.shift()
74+
handler()
75+
}
76+
}
77+
}
78+
this.emit('paused', paused)
79+
if (paused) {
80+
this.emit('pause')
81+
} else {
82+
this.emit('resume')
83+
}
84+
return true
85+
},
86+
pause: function () {
87+
return this.setPaused(true)
88+
},
89+
resume: function () {
90+
return this.setPaused(false)
91+
}
92+
})
93+
94+
Object.defineProperties(PauseWrapper.prototype, {
95+
paused: {
96+
get: function () {
97+
return this._paused
98+
},
99+
set: function (paused) {
100+
this.setPaused(paused)
101+
return this._paused
102+
}
103+
}
104+
})

0 commit comments

Comments
 (0)