Skip to content

Commit 920a333

Browse files
authored
Merge pull request #941 from plotly/internal-event-handler
Mirror events on an internal event handler
2 parents 668428d + dc9f58d commit 920a333

File tree

3 files changed

+77
-3
lines changed

3 files changed

+77
-3
lines changed

src/lib/events.js

+32
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;
@@ -68,6 +89,10 @@ var Events = {
6889
* all handlers for a particular event and returns the return value
6990
* of the LAST handler. This function also triggers jQuery's
7091
* triggerHandler for backwards compatibility.
92+
*
93+
* Note: triggerHandler has been recommended for deprecation in v2.0.0,
94+
* so the additional behavior of triggerHandler triggering internal events
95+
* is deliberate excluded in order to avoid reinforcing more usage.
7196
*/
7297
triggerHandler: function(plotObj, event, data) {
7398
var jQueryHandlerValue;
@@ -124,6 +149,13 @@ var Events = {
124149
delete plotObj.removeAllListeners;
125150
delete plotObj.emit;
126151

152+
delete plotObj._ev;
153+
delete plotObj._internalEv;
154+
delete plotObj._internalOn;
155+
delete plotObj._internalOnce;
156+
delete plotObj._removeInternalListener;
157+
delete plotObj._removeAllInternalListeners;
158+
127159
return plotObj;
128160
}
129161

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('does *not* mirror triggerHandler 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(0);
141+
expect(result).toBe('pong');
142+
});
143+
103144
it('triggers jQuery handlers when no matching node events bound', function() {
104145
var eventBaton = 0;
105146

test/jasmine/tests/plots_test.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -301,9 +301,10 @@ describe('Test Plots', function() {
301301

302302
it('should unset everything in the gd except _context', function() {
303303
var expectedKeys = [
304-
'_ev', 'on', 'once', 'removeListener', 'removeAllListeners',
305-
'emit', '_context', '_replotPending', '_mouseDownTime',
306-
'_hmpixcount', '_hmlumcount'
304+
'_ev', '_internalEv', 'on', 'once', 'removeListener', 'removeAllListeners',
305+
'_internalOn', '_internalOnce', '_removeInternalListener',
306+
'_removeAllInternalListeners', 'emit', '_context', '_replotPending',
307+
'_mouseDownTime', '_hmpixcount', '_hmlumcount'
307308
];
308309

309310
Plots.purge(gd);

0 commit comments

Comments
 (0)