Skip to content

Mirror events on an internal event handler #941

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 14, 2016
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 44 additions & 1 deletion src/lib/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var Events = {
if(plotObj._ev instanceof EventEmitter) return plotObj;

var ev = new EventEmitter();
var internalEv = new EventEmitter();

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

/*
* Create a second event handler that will manage events *internally*.
* This allows parts of plotly to respond to thing like relayout without
* having to use the user-facing event handler. They cannot peacefully
* coexist on the same handler because a user invoking
* plotObj.removeAllListeners() would detach internal events, breaking
* plotly.
*/
plotObj._internalEv = internalEv;

/*
* Assign bound methods from the ev to the plot object. These methods
* will reference the 'this' of plot._ev even though they are methods
Expand All @@ -46,6 +57,15 @@ var Events = {
plotObj.removeListener = ev.removeListener.bind(ev);
plotObj.removeAllListeners = ev.removeAllListeners.bind(ev);

/*
* Create funtions for managing internal events. These are *only* triggered
* by the mirroring of external events via the emit function.
*/
plotObj._internalOn = internalEv.on.bind(internalEv);
plotObj._internalOnce = internalEv.once.bind(internalEv);
plotObj._removeInternalListener = internalEv.removeListener.bind(internalEv);
plotObj._removeAllInternalListeners = internalEv.removeAllListeners.bind(internalEv);

/*
* We must wrap emit to continue to support JQuery events. The idea
* is to check to see if the user is using JQuery events, if they are
Expand All @@ -58,6 +78,7 @@ var Events = {
}

ev.emit(event, data);
internalEv.emit(event, data);
};

return plotObj;
Expand All @@ -70,6 +91,7 @@ var Events = {
* triggerHandler for backwards compatibility.
*/
triggerHandler: function(plotObj, event, data) {
var i;
var jQueryHandlerValue;
var nodeEventHandlerValue;
/*
Expand Down Expand Up @@ -98,10 +120,24 @@ var Events = {
/*
* Call all the handlers except the last one.
*/
for(var i = 0; i < handlers.length; i++) {
for(i = 0; i < handlers.length; i++) {
handlers[i](data);
}

/* Do the same as for external-facing events, except trigger the same
* events on the internal handlers. This does *not* affect the return
* value. It simply mirrors triggers internally so that there's no
* conflict with external user-defined events when plotly manages
* events internally.
*/
var internalHandlers = plotObj._internalEv._events[event];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will we ever need to call Events.triggerHandler on internal events?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps not. I didn't fully understand the use-case of this. It appeared that it was for backward compatibility with earlier jquery-based events, but yeah, perhaps not.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the only reason (that I can think of) why we still have that trigger event block: https://plot.ly/javascript/hover-events/#triggering-hover-events

I'd vote for dropping it in v2.0.0 (cc @alexcjohnson)

So, I'd vote for not adding support for triggerHandlers for internal events?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More specifically, It appear to me that triggerHandler was just a more jquery-like version of emit, but I could be wrong. If there's no use-case for it, I'm glad to remove. I have no preference of particular knowledge here, so I'll defer to you on this one.

Copy link
Contributor

@etpinard etpinard Sep 14, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO

  • Let's remove this block
  • add comment on non-internal triggerHandler block mentioning that internal events don't support triggerHandler.

Copy link
Collaborator

@alexcjohnson alexcjohnson Sep 14, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd vote for dropping it in v2.0.0

👍
In case there are people using it in the wild, it would be great if our v2 release notes could describe (or link to a description of) how to convert to the new form.

if(internalHandlers) {
if(typeof internalHandlers === 'function') internalHandlers = [internalHandlers];
for(i = 0; i < internalHandlers.length; i++) {
internalHandlers[i](data);
}
}

/*
* Now call the final handler and collect its value
*/
Expand All @@ -124,6 +160,13 @@ var Events = {
delete plotObj.removeAllListeners;
delete plotObj.emit;

delete plotObj._ev;
delete plotObj._internalEv;
delete plotObj._internalOn;
delete plotObj._internalOnce;
delete plotObj._removeInternalListener;
delete plotObj._removeAllInternalListeners;

return plotObj;
}

Expand Down
41 changes: 41 additions & 0 deletions test/jasmine/tests/events_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,19 @@ describe('Events', function() {
$(plotDiv).trigger('ping', 'pong');
});
});

it('mirrors events on an internal handler', function(done) {
Events.init(plotDiv);

plotDiv._internalOn('ping', function(data) {
expect(data).toBe('pong');
done();
});

setTimeout(function() {
plotDiv.emit('ping', 'pong');
});
});
});

describe('triggerHandler', function() {
Expand Down Expand Up @@ -100,6 +113,34 @@ describe('Events', function() {
expect(result).toBe('pong');
});

it('mirrors events on the internal handler', function() {
var eventBaton = 0;
var internalEventBaton = 0;

Events.init(plotDiv);

plotDiv.on('ping', function() {
eventBaton++;
return 'ping';
});

plotDiv._internalOn('ping', function() {
internalEventBaton++;
return 'foo';
});

plotDiv.on('ping', function() {
eventBaton++;
return 'pong';
});

var result = Events.triggerHandler(plotDiv, 'ping');

expect(eventBaton).toBe(2);
expect(internalEventBaton).toBe(1);
expect(result).toBe('pong');
});

it('triggers jQuery handlers when no matching node events bound', function() {
var eventBaton = 0;

Expand Down
7 changes: 4 additions & 3 deletions test/jasmine/tests/plots_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,10 @@ describe('Test Plots', function() {

it('should unset everything in the gd except _context', function() {
var expectedKeys = [
'_ev', 'on', 'once', 'removeListener', 'removeAllListeners',
'emit', '_context', '_replotPending', '_mouseDownTime',
'_hmpixcount', '_hmlumcount'
'_ev', '_internalEv', 'on', 'once', 'removeListener', 'removeAllListeners',
'_internalOn', '_internalOnce', '_removeInternalListener',
'_removeAllInternalListeners', 'emit', '_context', '_replotPending',
'_mouseDownTime', '_hmpixcount', '_hmlumcount'
];

Plots.purge(gd);
Expand Down