diff --git a/index.html b/index.html
index 9de339471..51ddec95d 100644
--- a/index.html
+++ b/index.html
@@ -54,6 +54,7 @@
+
diff --git a/js/id/actions/move_way.js b/js/id/actions/move_way.js
new file mode 100644
index 000000000..0ef14e4f3
--- /dev/null
+++ b/js/id/actions/move_way.js
@@ -0,0 +1,14 @@
+iD.actions.MoveWay = function(wayId, dxdy, projection) {
+ return function(graph) {
+ var way = graph.entity(wayId);
+
+ _.uniq(way.nodes).forEach(function(id) {
+ var node = graph.entity(id),
+ start = projection(node.loc),
+ end = projection.invert([start[0] + dxdy[0], start[1] + dxdy[1]]);
+ graph = iD.actions.MoveNode(id, end)(graph);
+ });
+
+ return graph;
+ };
+};
diff --git a/js/id/modes/select.js b/js/id/modes/select.js
index e5f208992..c0553ac61 100644
--- a/js/id/modes/select.js
+++ b/js/id/modes/select.js
@@ -22,15 +22,7 @@ iD.modes.Select = function (entity) {
mode.history.perform(iD.actions.Noop());
}
- _.uniq(_.pluck(entity.nodes, 'id'))
- .forEach(function(id) {
- var node = mode.history.graph().entity(id),
- start = mode.map.projection(node.loc),
- end = mode.map.projection.invert([
- start[0] + d3.event.dx,
- start[1] + d3.event.dy]);
- mode.history.replace(iD.actions.Move(id, end));
- });
+ mode.history.replace(iD.actions.MoveWay(entity.id, [d3.event.dx, d3.event.dy], mode.map.projection));
})
.on('dragend', function () {
if (!dragging) return;
diff --git a/test/index.html b/test/index.html
index f66c872d4..c583acee9 100644
--- a/test/index.html
+++ b/test/index.html
@@ -55,6 +55,7 @@
+
@@ -95,6 +96,7 @@
+
diff --git a/test/index_packaged.html b/test/index_packaged.html
index 1c197a5b7..2fdd8ab0f 100644
--- a/test/index_packaged.html
+++ b/test/index_packaged.html
@@ -32,6 +32,7 @@
+
diff --git a/test/spec/actions/move_way.js b/test/spec/actions/move_way.js
new file mode 100644
index 000000000..71c703e7f
--- /dev/null
+++ b/test/spec/actions/move_way.js
@@ -0,0 +1,21 @@
+describe("iD.actions.MoveWay", function () {
+ it("moves all nodes in a way by the given amount", function () {
+ var node1 = iD.Node({loc: [0, 0]}),
+ node2 = iD.Node({loc: [5, 10]}),
+ way = iD.Way({nodes: [node1.id, node2.id]}),
+ dxdy = [2, 3],
+ projection = d3.geo.mercator(),
+ graph = iD.actions.MoveWay(way.id, dxdy, projection)(iD.Graph([node1, node2, way]));
+ expect(graph.entity(node1.id).loc).to.eql([1.4400000000000002, -2.1594885414215783]);
+ expect(graph.entity(node2.id).loc).to.eql([6.440000000000008, 7.866329874099955]);
+ });
+
+ it("moves repeated nodes only once", function () {
+ var node = iD.Node({loc: [0, 0]}),
+ way = iD.Way({nodes: [node.id, node.id]}),
+ dxdy = [2, 3],
+ projection = d3.geo.mercator(),
+ graph = iD.actions.MoveWay(way.id, dxdy, projection)(iD.Graph([node, way]));
+ expect(graph.entity(node.id).loc).to.eql([1.4400000000000002, -2.1594885414215783]);
+ });
+});