Skip to content

Commit a159ee7

Browse files
Event module and tests
1 parent e1fe4c9 commit a159ee7

File tree

2 files changed

+242
-0
lines changed

2 files changed

+242
-0
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
'use strict';
2+
3+
var EventEmitter = require('events').EventEmitter;
4+
5+
var Events = {
6+
7+
init: function(plotObj) {
8+
9+
/*
10+
* If we have already instantiated an emitter for this plot
11+
* return early.
12+
*/
13+
if (plotObj._ev instanceof EventEmitter) return plotObj;
14+
15+
var ev = new EventEmitter();
16+
17+
/*
18+
* Assign to plot._ev while we still live in a land
19+
* where plot is a DOM element with stuff attached to it.
20+
* In the future we can make plot the event emitter itself.
21+
*/
22+
plotObj._ev = ev;
23+
24+
/*
25+
* Assign bound methods from the ev to the plot object. These methods
26+
* will reference the 'this' of plot._ev even though they are methods
27+
* of plot. This will keep the event machinery away from the plot object
28+
* which currently is often a DOM element but presents an API that will
29+
* continue to function when plot becomes an emitter. Not all EventEmitter
30+
* methods have been bound to `plot` as some do not currently add value to
31+
* the Plotly event API.
32+
*/
33+
plotObj.on = ev.on.bind(ev);
34+
plotObj.once = ev.once.bind(ev);
35+
plotObj.removeListener = ev.removeListener.bind(ev);
36+
plotObj.removeAllListeners = ev.removeAllListeners.bind(ev);
37+
38+
/*
39+
* We must wrap emit to continue to support JQuery events. The idea
40+
* is to check to see if the user is using JQuery events, if they are
41+
* we emit JQuery events to trigger user handlers as well as the EventEmitter
42+
* events.
43+
*/
44+
plotObj.emit = function(event, data) {
45+
if (typeof $ !== 'undefined') {
46+
$(plotObj).trigger(event, data);
47+
}
48+
49+
ev.emit(event, data);
50+
};
51+
52+
return plotObj;
53+
},
54+
55+
/*
56+
* This function behaves like jQueries triggerHandler. It calls
57+
* all handlers for a particular event and returns the return value
58+
* of the LAST handler. This function also triggers jQuery's
59+
* triggerHandler for backwards compatibility.
60+
*/
61+
triggerHandler: function(plotObj, event, data) {
62+
var jQueryHandlerValue;
63+
var nodeEventHandlerValue;
64+
/*
65+
* If Jquery exists run all its handlers for this event and
66+
* collect the return value of the LAST handler function
67+
*/
68+
if (typeof $ !== 'undefined') {
69+
jQueryHandlerValue = $(plotObj).triggerHandler(event, data);
70+
}
71+
72+
/*
73+
* Now run all the node style event handlers
74+
*/
75+
var ev = plotObj._ev;
76+
if (!ev) return;
77+
78+
var handlers = ev._events[event];
79+
if (!handlers) return;
80+
81+
/*
82+
* handlers can be function or an array of functions
83+
*/
84+
if (typeof handlers === 'function') handlers = [handlers];
85+
var lastHandler = handlers.pop();
86+
87+
/*
88+
* Call all the handlers except the last one.
89+
*/
90+
for (var i = 0; i < handlers.length; i++) {
91+
handlers[i](data);
92+
}
93+
94+
/*
95+
* Now call the final handler and collect its value
96+
*/
97+
nodeEventHandlerValue = lastHandler(data);
98+
99+
/*
100+
* Return either the jquery handler value if it exists or the
101+
* nodeEventHandler value. Jquery event value superceeds nodejs
102+
* events for backwards compatability reasons.
103+
*/
104+
return jQueryHandlerValue !== undefined ? jQueryHandlerValue :
105+
nodeEventHandlerValue;
106+
}
107+
};
108+
109+
module.exports = Events;
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
* Note this test requires JQuery in the global scope.
3+
* we should keep it that way to keep testing our backward
4+
* compatibility with JQuery events.
5+
*/
6+
7+
8+
var Events = require('../src/events');
9+
10+
describe('Events', function () {
11+
'use strict';
12+
13+
var plotObj;
14+
var plotDiv;
15+
16+
beforeEach(function() {
17+
plotObj = {};
18+
plotDiv = document.createElement('div');
19+
});
20+
21+
describe('init', function() {
22+
23+
it('instantiates an emitter on incoming plot object', function() {
24+
expect(plotObj._ev).not.toBeDefined();
25+
expect(Events.init(plotObj)._ev).toBeDefined();
26+
});
27+
28+
it('maps function onto incoming plot object', function() {
29+
Events.init(plotObj);
30+
31+
expect(typeof plotObj.on).toBe('function');
32+
expect(typeof plotObj.once).toBe('function');
33+
expect(typeof plotObj.removeListener).toBe('function');
34+
expect(typeof plotObj.removeAllListeners).toBe('function');
35+
});
36+
37+
it('is idempotent', function() {
38+
Events.init(plotObj);
39+
plotObj.emit = function () {
40+
return 'initial';
41+
};
42+
43+
Events.init(plotObj);
44+
expect(plotObj.emit()).toBe('initial');
45+
});
46+
47+
it('triggers node style events', function(done) {
48+
Events.init(plotObj);
49+
50+
plotObj.on('ping', function(data) {
51+
expect(data).toBe('pong');
52+
done();
53+
});
54+
55+
setTimeout(function() {
56+
plotObj.emit('ping', 'pong');
57+
});
58+
});
59+
60+
it('triggers jquery events', function(done) {
61+
Events.init(plotDiv);
62+
63+
64+
$(plotDiv).bind('ping', function(event, data) {
65+
expect(data).toBe('pong');
66+
done();
67+
});
68+
69+
setTimeout(function() {
70+
$(plotDiv).trigger('ping', 'pong');
71+
});
72+
});
73+
});
74+
75+
describe('triggerHandler', function() {
76+
77+
it('triggers node handlers and returns last value', function() {
78+
var eventBaton = 0;
79+
80+
Events.init(plotDiv);
81+
82+
plotDiv.on('ping', function() {
83+
eventBaton++;
84+
return 'ping';
85+
});
86+
87+
plotDiv.on('ping', function() {
88+
eventBaton++;
89+
return 'ping';
90+
});
91+
92+
plotDiv.on('ping', function() {
93+
eventBaton++;
94+
return 'pong';
95+
});
96+
97+
var result = Events.triggerHandler(plotDiv, 'ping');
98+
99+
expect(eventBaton).toBe(3);
100+
expect(result).toBe('pong');
101+
});
102+
103+
104+
it('triggers jQuery + nodejs handlers and returns last jQuery value', function() {
105+
var eventBaton = 0;
106+
107+
Events.init(plotDiv);
108+
109+
$(plotDiv).bind('ping', function() {
110+
eventBaton++;
111+
return 'ping';
112+
});
113+
114+
plotDiv.on('ping', function() {
115+
eventBaton++;
116+
return 'ping';
117+
});
118+
119+
$(plotDiv).bind('ping', function() {
120+
eventBaton++;
121+
return 'pong';
122+
});
123+
124+
var result = Events.triggerHandler(plotDiv, 'ping');
125+
126+
expect(eventBaton).toBe(3);
127+
expect(result).toBe('pong');
128+
});
129+
130+
131+
});
132+
133+
});

0 commit comments

Comments
 (0)