Skip to content

Commit edae9fc

Browse files
committed
Mirror events on an internal event handler
1 parent 668428d commit edae9fc

File tree

2 files changed

+85
-1
lines changed

2 files changed

+85
-1
lines changed

src/lib/events.js

+44-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ var Events = {
2424
if(plotObj._ev instanceof EventEmitter) return plotObj;
2525

2626
var ev = new EventEmitter();
27+
var internalEv = new EventEmitter();
2728

2829
/*
2930
* Assign to plot._ev while we still live in a land
@@ -32,6 +33,16 @@ var Events = {
3233
*/
3334
plotObj._ev = ev;
3435

36+
/*
37+
* Create a second event handler that will manage events *internally*.
38+
* This allows parts of plotly to respond to thing like relayout without
39+
* having to use the user-facing event handler. They cannot peacefully
40+
* coexist on the same handler because a user invoking
41+
* plotObj.removeAllListeners() would detach internal events, breaking
42+
* plotly.
43+
*/
44+
plotObj._internalEv = internalEv;
45+
3546
/*
3647
* Assign bound methods from the ev to the plot object. These methods
3748
* will reference the 'this' of plot._ev even though they are methods
@@ -46,6 +57,15 @@ var Events = {
4657
plotObj.removeListener = ev.removeListener.bind(ev);
4758
plotObj.removeAllListeners = ev.removeAllListeners.bind(ev);
4859

60+
/*
61+
* Create funtions for managing internal events. These are *only* triggered
62+
* by the mirroring of external events via the emit function.
63+
*/
64+
plotObj._internalOn = internalEv.on.bind(internalEv);
65+
plotObj._internalOnce = internalEv.once.bind(internalEv);
66+
plotObj._removeInternalListener = internalEv.removeListener.bind(internalEv);
67+
plotObj._removeAllInternalListeners = internalEv.removeAllListeners.bind(internalEv);
68+
4969
/*
5070
* We must wrap emit to continue to support JQuery events. The idea
5171
* is to check to see if the user is using JQuery events, if they are
@@ -58,6 +78,7 @@ var Events = {
5878
}
5979

6080
ev.emit(event, data);
81+
internalEv.emit(event, data);
6182
};
6283

6384
return plotObj;
@@ -70,6 +91,7 @@ var Events = {
7091
* triggerHandler for backwards compatibility.
7192
*/
7293
triggerHandler: function(plotObj, event, data) {
94+
var i;
7395
var jQueryHandlerValue;
7496
var nodeEventHandlerValue;
7597
/*
@@ -98,10 +120,24 @@ var Events = {
98120
/*
99121
* Call all the handlers except the last one.
100122
*/
101-
for(var i = 0; i < handlers.length; i++) {
123+
for(i = 0; i < handlers.length; i++) {
102124
handlers[i](data);
103125
}
104126

127+
/* Do the same as for external-facing events, except trigger the same
128+
* events on the internal handlers. This does *not* affect the return
129+
* value. It simply mirrors triggers internally so that there's no
130+
* conflict with external user-defined events when plotly manages
131+
* events internally.
132+
*/
133+
var internalHandlers = plotObj._internalEv._events[event];
134+
if (internalHandlers) {
135+
if(typeof internalHandlers === 'function') internalHandlers = [internalHandlers];
136+
for(i = 0; i < internalHandlers.length; i++) {
137+
internalHandlers[i](data);
138+
}
139+
}
140+
105141
/*
106142
* Now call the final handler and collect its value
107143
*/
@@ -124,6 +160,13 @@ var Events = {
124160
delete plotObj.removeAllListeners;
125161
delete plotObj.emit;
126162

163+
delete plotObj._ev;
164+
delete plotObj._internalEv;
165+
delete plotObj._internalOn;
166+
delete plotObj._internalOnce;
167+
delete plotObj._removeInternalListener;
168+
delete plotObj._removeAllInternalListeners;;
169+
127170
return plotObj;
128171
}
129172

test/jasmine/tests/events_test.js

+41
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,19 @@ describe('Events', function() {
7070
$(plotDiv).trigger('ping', 'pong');
7171
});
7272
});
73+
74+
it('mirrors events on an internal handler', function(done) {
75+
Events.init(plotDiv);
76+
77+
plotDiv._internalOn('ping', function(data) {
78+
expect(data).toBe('pong');
79+
done();
80+
});
81+
82+
setTimeout(function() {
83+
plotDiv.emit('ping', 'pong');
84+
});
85+
});
7386
});
7487

7588
describe('triggerHandler', function() {
@@ -100,6 +113,34 @@ describe('Events', function() {
100113
expect(result).toBe('pong');
101114
});
102115

116+
it('mirrors events on the internal handler', function() {
117+
var eventBaton = 0;
118+
var internalEventBaton = 0;
119+
120+
Events.init(plotDiv);
121+
122+
plotDiv.on('ping', function() {
123+
eventBaton++;
124+
return 'ping';
125+
});
126+
127+
plotDiv._internalOn('ping', function() {
128+
internalEventBaton++;
129+
return 'foo';
130+
});
131+
132+
plotDiv.on('ping', function() {
133+
eventBaton++;
134+
return 'pong';
135+
});
136+
137+
var result = Events.triggerHandler(plotDiv, 'ping');
138+
139+
expect(eventBaton).toBe(2);
140+
expect(internalEventBaton).toBe(1);
141+
expect(result).toBe('pong');
142+
});
143+
103144
it('triggers jQuery handlers when no matching node events bound', function() {
104145
var eventBaton = 0;
105146

0 commit comments

Comments
 (0)