Skip to content

Commit e5e2430

Browse files
committed
Add sourceEvent to custom events.
The drag behavior, zoom behavior and brush component now set a "sourceEvent" property on their events, defined as the triggering mouse or touch event. The d3.mouse and d3.touches helpers likewise use the source event, letting you query the mouse or touch location when handling a custom event.
1 parent 7bd0530 commit e5e2430

10 files changed

Lines changed: 170 additions & 150 deletions

File tree

d3.v2.js

Lines changed: 83 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ d3.ns = {
546546
}
547547
};
548548
d3.dispatch = function() {
549-
var dispatch = new d3_dispatch(),
549+
var dispatch = new d3_dispatch,
550550
i = -1,
551551
n = arguments.length;
552552
while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
@@ -862,6 +862,49 @@ function d3_eventCancel() {
862862
d3.event.stopPropagation();
863863
d3.event.preventDefault();
864864
}
865+
866+
function d3_eventSource() {
867+
var e = d3.event, s;
868+
while (s = e.sourceEvent) e = s;
869+
return e;
870+
}
871+
872+
// Like d3.dispatch, but for custom events abstracting native UI events. These
873+
// events have a target component (such as a brush), a target element (such as
874+
// the svg:g element containing the brush) and the standard arguments `d` (the
875+
// target element's data) and `i` (the selection index of the target element).
876+
function d3_eventDispatch(target) {
877+
var dispatch = new d3_dispatch,
878+
i = 0,
879+
n = arguments.length;
880+
881+
while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
882+
883+
// Creates a dispatch context for the specified `thiz` (typically, the target
884+
// DOM element that received the source event) and `argumentz` (typically, the
885+
// data `d` and index `i` of the target element). The returned function can be
886+
// used to dispatch an event to any registered listeners; the function takes a
887+
// single argument as input, being the event to dispatch. The event must have
888+
// a "type" attribute which corresponds to a type registered in the
889+
// constructor. This context will automatically populate the "sourceEvent" and
890+
// "target" attributes of the event, as well as setting the `d3.event` global
891+
// for the duration of the notification.
892+
dispatch.of = function(thiz, argumentz) {
893+
return function(e1) {
894+
try {
895+
var e0 =
896+
e1.sourceEvent = d3.event;
897+
e1.target = target;
898+
d3.event = e1;
899+
dispatch[e1.type].apply(thiz, argumentz);
900+
} finally {
901+
d3.event = e0;
902+
}
903+
};
904+
};
905+
906+
return dispatch;
907+
}
865908
d3.interpolate = function(a, b) {
866909
var i = d3.interpolators.length, f;
867910
while (--i >= 0 && !(f = d3.interpolators[i](a, b)));
@@ -2461,7 +2504,7 @@ function d3_transformCombine(a, b, k) {
24612504

24622505
var d3_transformDegrees = 180 / Math.PI;
24632506
d3.mouse = function(container) {
2464-
return d3_mousePoint(container, d3.event);
2507+
return d3_mousePoint(container, d3_eventSource());
24652508
};
24662509

24672510
// https://bugs.webkit.org/show_bug.cgi?id=44083
@@ -2495,8 +2538,7 @@ function d3_mousePoint(container, e) {
24952538
return [e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop];
24962539
};
24972540
d3.touches = function(container, touches) {
2498-
if (arguments.length < 2) touches = d3.event.touches;
2499-
2541+
if (arguments.length < 2) touches = d3_eventSource().touches;
25002542
return touches ? d3_array(touches).map(function(touch) {
25012543
var point = d3_mousePoint(container, touch);
25022544
point.identifier = touch.identifier;
@@ -4185,7 +4227,7 @@ function d3_svg_axisSubdivide(scale, ticks, m) {
41854227
return subticks;
41864228
}
41874229
d3.svg.brush = function() {
4188-
var event = d3.dispatch("brushstart", "brush", "brushend"),
4230+
var event = d3_eventDispatch(brush, "brushstart", "brush", "brushend"),
41894231
x, // x-scale, optional
41904232
y, // y-scale, optional
41914233
extent = [[0, 0], [0, 0]]; // [x0, y0], [x1, y1]
@@ -4293,24 +4335,12 @@ d3.svg.brush = function() {
42934335
d3_svg_brushY = !/^(e|w)$/.test(resize) && y;
42944336

42954337
// Notify listeners.
4296-
d3_svg_brushDispatch = dispatcher(this, arguments);
4297-
d3_svg_brushDispatch("brushstart");
4338+
d3_svg_brushEvent = event.of(this, arguments);
4339+
d3_svg_brushEvent({type: "brushstart"});
42984340
d3_svg_brushMove();
42994341
d3_eventCancel();
43004342
}
43014343

4302-
function dispatcher(that, argumentz) {
4303-
return function(type) {
4304-
var e = d3.event;
4305-
try {
4306-
d3.event = {type: type, target: brush};
4307-
event[type].apply(that, argumentz);
4308-
} finally {
4309-
d3.event = e;
4310-
}
4311-
};
4312-
}
4313-
43144344
brush.x = function(z) {
43154345
if (!arguments.length) return x;
43164346
x = z;
@@ -4385,7 +4415,7 @@ d3.svg.brush = function() {
43854415
};
43864416

43874417
var d3_svg_brush,
4388-
d3_svg_brushDispatch,
4418+
d3_svg_brushEvent,
43894419
d3_svg_brushTarget,
43904420
d3_svg_brushX,
43914421
d3_svg_brushY,
@@ -4484,7 +4514,7 @@ function d3_svg_brushMove() {
44844514
// Final redraw and notify listeners.
44854515
if (moved) {
44864516
d3_svg_brushRedraw(g, d3_svg_brushExtent);
4487-
d3_svg_brushDispatch("brush");
4517+
d3_svg_brushEvent({type: "brush"});
44884518
}
44894519
}
44904520
}
@@ -4537,9 +4567,9 @@ function d3_svg_brushUp() {
45374567
d3_svg_brushMove();
45384568
d3.select(d3_svg_brushTarget).style("pointer-events", "all").selectAll(".resize").style("display", d3_svg_brush.empty() ? "none" : null);
45394569
d3.select("body").style("cursor", null);
4540-
d3_svg_brushDispatch("brushend");
4570+
d3_svg_brushEvent({type: "brushend"});
45414571
d3_svg_brush =
4542-
d3_svg_brushDispatch =
4572+
d3_svg_brushEvent =
45434573
d3_svg_brushTarget =
45444574
d3_svg_brushX =
45454575
d3_svg_brushY =
@@ -4566,7 +4596,7 @@ d3.behavior = {};
45664596
// TODO Track touch points by identifier.
45674597

45684598
d3.behavior.drag = function() {
4569-
var event = d3.dispatch("drag", "dragstart", "dragend"),
4599+
var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"),
45704600
origin = null;
45714601

45724602
function drag() {
@@ -4584,13 +4614,11 @@ d3.behavior.drag = function() {
45844614

45854615
// snapshot the local context for subsequent dispatch
45864616
function start() {
4587-
d3_behavior_dragEvent = event;
45884617
d3_behavior_dragEventTarget = d3.event.target;
4589-
d3_behavior_dragTarget = this;
4590-
d3_behavior_dragArguments = arguments;
4618+
d3_behavior_dragEvent = event.of(d3_behavior_dragTarget = this, arguments);
45914619
d3_behavior_dragOrigin = d3_behavior_dragPoint();
45924620
if (origin) {
4593-
d3_behavior_dragOffset = origin.apply(d3_behavior_dragTarget, d3_behavior_dragArguments);
4621+
d3_behavior_dragOffset = origin.apply(d3_behavior_dragTarget, arguments);
45944622
d3_behavior_dragOffset = [d3_behavior_dragOffset.x - d3_behavior_dragOrigin[0], d3_behavior_dragOffset.y - d3_behavior_dragOrigin[1]];
45954623
} else {
45964624
d3_behavior_dragOffset = [0, 0];
@@ -4600,7 +4628,7 @@ d3.behavior.drag = function() {
46004628

46014629
function mousedown() {
46024630
start.apply(this, arguments);
4603-
d3_behavior_dragDispatch("dragstart");
4631+
d3_behavior_dragEvent({type: "dragstart"});
46044632
}
46054633

46064634
drag.origin = function(x) {
@@ -4615,40 +4643,14 @@ d3.behavior.drag = function() {
46154643
var d3_behavior_dragEvent,
46164644
d3_behavior_dragEventTarget,
46174645
d3_behavior_dragTarget,
4618-
d3_behavior_dragArguments,
46194646
d3_behavior_dragOffset,
46204647
d3_behavior_dragOrigin,
46214648
d3_behavior_dragMoved;
46224649

4623-
function d3_behavior_dragDispatch(type) {
4624-
var p = d3_behavior_dragPoint(),
4625-
o = d3.event,
4626-
e = d3.event = {type: type};
4627-
4628-
if (p) {
4629-
e.x = p[0] + d3_behavior_dragOffset[0];
4630-
e.y = p[1] + d3_behavior_dragOffset[1];
4631-
e.dx = p[0] - d3_behavior_dragOrigin[0];
4632-
e.dy = p[1] - d3_behavior_dragOrigin[1];
4633-
d3_behavior_dragMoved |= e.dx | e.dy;
4634-
d3_behavior_dragOrigin = p;
4635-
}
4636-
4637-
try {
4638-
d3_behavior_dragEvent[type].apply(d3_behavior_dragTarget, d3_behavior_dragArguments);
4639-
} finally {
4640-
d3.event = o;
4641-
}
4642-
4643-
o.stopPropagation();
4644-
}
4645-
46464650
function d3_behavior_dragPoint() {
46474651
var p = d3_behavior_dragTarget.parentNode,
46484652
t = d3.event.changedTouches;
4649-
return p && (t
4650-
? d3.touches(p, t)[0]
4651-
: d3.mouse(p));
4653+
return t ? d3.touches(p, t)[0] : d3.mouse(p);
46524654
}
46534655

46544656
function d3_behavior_dragMove() {
@@ -4658,13 +4660,26 @@ function d3_behavior_dragMove() {
46584660
// O NOES! The drag element was removed from the DOM.
46594661
if (!parent) return d3_behavior_dragUp();
46604662

4661-
d3_behavior_dragDispatch("drag");
4663+
var p = d3_behavior_dragPoint(),
4664+
dx = p[0] - d3_behavior_dragOrigin[0],
4665+
dy = p[1] - d3_behavior_dragOrigin[1];
4666+
4667+
d3_behavior_dragMoved |= dx | dy;
4668+
d3_behavior_dragOrigin = p;
46624669
d3_eventCancel();
4670+
4671+
d3_behavior_dragEvent({
4672+
type: "drag",
4673+
x: p[0] + d3_behavior_dragOffset[0],
4674+
y: p[1] + d3_behavior_dragOffset[1],
4675+
dx: dx,
4676+
dy: dy
4677+
});
46634678
}
46644679

46654680
function d3_behavior_dragUp() {
46664681
if (!d3_behavior_dragTarget) return;
4667-
d3_behavior_dragDispatch("dragend");
4682+
d3_behavior_dragEvent({type: "dragend"});
46684683

46694684
// If the node was moved, prevent the mouseup from propagating.
46704685
// Also prevent the subsequent click from propagating (e.g., for anchors).
@@ -4676,7 +4691,6 @@ function d3_behavior_dragUp() {
46764691
d3_behavior_dragEvent =
46774692
d3_behavior_dragEventTarget =
46784693
d3_behavior_dragTarget =
4679-
d3_behavior_dragArguments =
46804694
d3_behavior_dragOffset =
46814695
d3_behavior_dragOrigin = null;
46824696
}
@@ -4693,7 +4707,7 @@ d3.behavior.zoom = function() {
46934707
scale = 1,
46944708
scale0, // scale when we started touching
46954709
scaleExtent = d3_behavior_zoomInfinity,
4696-
event = d3.dispatch("zoom"),
4710+
event = d3_eventDispatch(zoom, "zoom"),
46974711
x0,
46984712
x1,
46994713
y0,
@@ -4762,20 +4776,17 @@ d3.behavior.zoom = function() {
47624776
translate[1] += p[1] - l[1];
47634777
}
47644778

4765-
function dispatch(argumentz) {
4766-
var o = d3.event; // events can by reentrant (e.g., focus);
4767-
d3.event = {scale: scale, translate: translate, target: zoom};
4779+
function dispatch(event) {
47684780
if (x1) x1.domain(x0.range().map(function(x) { return (x - translate[0]) / scale; }).map(x0.invert));
47694781
if (y1) y1.domain(y0.range().map(function(y) { return (y - translate[1]) / scale; }).map(y0.invert));
4770-
try { event.zoom.apply(zoom, argumentz); }
4771-
finally { d3.event = o; }
4772-
o.preventDefault();
4782+
d3.event.preventDefault();
4783+
event({type: "zoom", scale: scale, translate: translate});
47734784
}
47744785

47754786
function mousedown() {
47764787
var moved = 0,
47774788
that = this,
4778-
argumentz = arguments,
4789+
event_ = event.of(that, arguments),
47794790
target = d3.event.target,
47804791
w = d3.select(window).on("mousemove.zoom", mousemove).on("mouseup.zoom", mouseup),
47814792
l = location(d3.mouse(that));
@@ -4786,7 +4797,7 @@ d3.behavior.zoom = function() {
47864797
function mousemove() {
47874798
moved = 1;
47884799
translateTo(d3.mouse(that), l);
4789-
dispatch(argumentz);
4800+
dispatch(event_);
47904801
}
47914802

47924803
function mouseup() {
@@ -4804,7 +4815,7 @@ d3.behavior.zoom = function() {
48044815
if (!translate0) translate0 = location(d3.mouse(this));
48054816
scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * scale);
48064817
translateTo(d3.mouse(this), translate0);
4807-
dispatch(arguments);
4818+
dispatch(event.of(this, arguments));
48084819
}
48094820

48104821
function mousemove() {
@@ -4815,7 +4826,7 @@ d3.behavior.zoom = function() {
48154826
var p = d3.mouse(this), l = location(p);
48164827
scaleTo(d3.event.shiftKey ? scale / 2 : scale * 2);
48174828
translateTo(p, l);
4818-
dispatch(arguments);
4829+
dispatch(event.of(this, arguments));
48194830
}
48204831

48214832
function touchstart() {
@@ -4831,7 +4842,7 @@ d3.behavior.zoom = function() {
48314842
var p = touches[0], l = location(touches[0]);
48324843
scaleTo(scale * 2);
48334844
translateTo(p, l);
4834-
dispatch(arguments);
4845+
dispatch(event.of(this, arguments));
48354846
}
48364847
touchtime = now;
48374848
}
@@ -4847,7 +4858,7 @@ d3.behavior.zoom = function() {
48474858
scaleTo(d3.event.scale * scale0);
48484859
}
48494860
translateTo(p0, l0);
4850-
dispatch(arguments);
4861+
dispatch(event.of(this, arguments));
48514862
}
48524863

48534864
return d3.rebind(zoom, event, "on");
@@ -5386,7 +5397,6 @@ function d3_layout_forceDragOut(d) {
53865397
}
53875398

53885399
function d3_layout_forceDragEnd() {
5389-
d3_layout_forceDrag();
53905400
d3_layout_forceDragNode.fixed &= 1;
53915401
d3_layout_forceDragForce = d3_layout_forceDragNode = null;
53925402
}

d3.v2.min.js

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)