Files
iD/js/id/behavior/drag.js
2012-12-21 12:10:44 -08:00

152 lines
4.5 KiB
JavaScript

/*
`iD.behavior.drag` is like `d3.behavior.drag`, with the following differences:
* The `origin` function is expected to return an [x, y] tuple rather than an
{x, y} object.
* The events are `start`, `move`, and `end`.
(https://github.com/mbostock/d3/issues/563)
* The `start` event is not dispatched until the first cursor movement occurs.
(https://github.com/mbostock/d3/pull/368)
* The `move` event has a `point` and `delta` [x, y] tuple properties rather
than `x`, `y`, `dx`, and `dy` properties.
* The `end` event is not dispatched if no movement occurs.
* An `off` function is available that unbinds the drag's internal event handlers.
* Delegation is supported via the `delegate` function.
*/
iD.behavior.drag = function () {
function d3_eventCancel() {
d3.event.stopPropagation();
d3.event.preventDefault();
}
var event = d3.dispatch("start", "move", "end"),
origin = null,
selector = '';
event.of = function(thiz, argumentz) {
return function(e1) {
try {
var e0 = e1.sourceEvent = d3.event;
e1.target = drag;
d3.event = e1;
event[e1.type].apply(thiz, argumentz);
} finally {
d3.event = e0;
}
};
};
function mousedown() {
var target = this,
event_ = event.of(target, arguments),
eventTarget = d3.event.target,
touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
offset,
origin_ = point(),
moved = 0;
var w = d3.select(window)
.on(touchId != null ? "touchmove.drag-" + touchId : "mousemove.drag", dragmove)
.on(touchId != null ? "touchend.drag-" + touchId : "mouseup.drag", dragend, true);
if (origin) {
offset = origin.apply(target, arguments);
offset = [ offset[0] - origin_[0], offset[1] - origin_[1] ];
} else {
offset = [ 0, 0 ];
}
if (touchId == null) d3_eventCancel();
function point() {
var p = target.parentNode;
return touchId != null ? d3.touches(p).filter(function (p) {
return p.identifier === touchId;
})[0] : d3.mouse(p);
}
function dragmove() {
if (!target.parentNode) return dragend();
var p = point(),
dx = p[0] - origin_[0],
dy = p[1] - origin_[1];
if (!moved) {
event_({
type: "start"
});
}
moved |= dx | dy;
origin_ = p;
d3_eventCancel();
event_({
type: "move",
point: [p[0] + offset[0], p[1] + offset[1]],
delta: [dx, dy]
});
}
function dragend() {
if (moved) {
event_({
type: "end"
});
d3_eventCancel();
if (d3.event.target === eventTarget) w.on("click.drag", click, true);
}
w.on(touchId != null ? "touchmove.drag-" + touchId : "mousemove.drag", null)
.on(touchId != null ? "touchend.drag-" + touchId : "mouseup.drag", null);
}
function click() {
d3_eventCancel();
w.on("click.drag", null);
}
}
function drag(selection) {
var matchesSelector = iD.util.prefixDOMProperty('matchesSelector'),
delegate = mousedown;
if (selector) {
delegate = function() {
var root = this,
target = d3.event.target;
for (; target && target !== root; target = target.parentNode) {
if (target[matchesSelector](selector)) {
return mousedown.call(target, target.__data__);
}
}
}
}
selection.on("mousedown.drag" + selector, delegate)
.on("touchstart.drag" + selector, delegate);
}
drag.off = function(selection) {
selection.on("mousedown.drag" + selector, null)
.on("touchstart.drag" + selector, null);
};
drag.delegate = function(_) {
if (!arguments.length) return selector;
selector = _;
return drag;
};
drag.origin = function (_) {
if (!arguments.length) return origin;
origin = _;
return drag;
};
return d3.rebind(drag, event, "on");
};