diff --git a/index.html b/index.html
index 0979fa558..25554ea27 100644
--- a/index.html
+++ b/index.html
@@ -87,6 +87,7 @@
+
diff --git a/js/id/behavior/add_way.js b/js/id/behavior/add_way.js
new file mode 100644
index 000000000..7c02355fb
--- /dev/null
+++ b/js/id/behavior/add_way.js
@@ -0,0 +1,58 @@
+iD.behavior.AddWay = function(mode) {
+ var map = mode.map,
+ history = mode.history,
+ controller = mode.controller,
+ event = d3.dispatch('startFromNode', 'startFromWay', 'start'),
+ hover, draw;
+
+ function add() {
+ var datum = d3.select(d3.event.target).datum() || {};
+
+ if (datum.type === 'node') {
+ event.startFromNode(datum);
+ } else if (datum.type === 'way') {
+ var choice = iD.geo.chooseIndex(datum, d3.mouse(map.surface.node()), map);
+ event.startFromWay(datum, choice.loc, choice.index);
+ } else if (datum.midpoint) {
+ var way = history.graph().entity(datum.way);
+ event.startFromWay(way, datum.loc, datum.index);
+ } else {
+ event.start(map.mouseCoordinates());
+ }
+ }
+
+ var addWay = function(surface) {
+ map.fastEnable(false)
+ .minzoom(16)
+ .dblclickEnable(false);
+
+ surface.call(hover)
+ .call(draw);
+ };
+
+ addWay.off = function(surface) {
+ map.fastEnable(true)
+ .minzoom(0)
+ .tail(false);
+
+ window.setTimeout(function() {
+ map.dblclickEnable(true);
+ }, 1000);
+
+ surface.call(hover.off)
+ .call(draw.off);
+ };
+
+ addWay.cancel = function() {
+ controller.exit();
+ };
+
+ hover = iD.behavior.Hover();
+
+ draw = iD.behavior.Draw()
+ .on('add', add)
+ .on('cancel', addWay.cancel)
+ .on('finish', addWay.cancel);
+
+ return d3.rebind(addWay, event, 'on');
+};
diff --git a/js/id/modes/add_area.js b/js/id/modes/add_area.js
index 9c5404232..a83ed20a1 100644
--- a/js/id/modes/add_area.js
+++ b/js/id/modes/add_area.js
@@ -6,82 +6,75 @@ iD.modes.AddArea = function() {
description: 'Add parks, buildings, lakes, or other areas to the map.'
};
- var behavior;
+ var behavior,
+ defaultTags = {area: 'yes'};
mode.enter = function() {
var map = mode.map,
- surface = map.surface,
history = mode.history,
controller = mode.controller;
- map.dblclickEnable(false)
- .tail('Click on the map to start drawing an area, like a park, lake, or building.', true);
+ function startFromNode(a) {
+ var way = iD.Way({tags: defaultTags}),
+ b = iD.Node({loc: a.loc});
- function add() {
- var datum = d3.select(d3.event.target).datum() || {},
- way = iD.Way({tags: { area: 'yes' }}),
- node;
-
- if (datum.type === 'node') {
- // start from an existing node
- node = datum;
- history.perform(
- iD.actions.AddWay(way),
- iD.actions.AddWayNode(way.id, node.id),
- iD.actions.AddWayNode(way.id, node.id));
-
- } else if (datum.type === 'way') {
- // begin a new way starting from an existing way
- var choice = iD.geo.chooseIndex(datum, d3.mouse(map.surface.node()), map);
- node = iD.Node({ loc: choice.loc });
-
- history.perform(
- iD.actions.AddWay(way),
- iD.actions.AddNode(node),
- iD.actions.AddWayNode(datum.id, node.id, choice.index),
- iD.actions.AddWayNode(way.id, node.id),
- iD.actions.AddWayNode(way.id, node.id));
-
- } else {
- // start from a new node
- node = iD.Node({loc: map.mouseCoordinates()});
- history.perform(
- iD.actions.AddWay(way),
- iD.actions.AddNode(node),
- iD.actions.AddWayNode(way.id, node.id),
- iD.actions.AddWayNode(way.id, node.id));
- }
-
- node = iD.Node({loc: node.loc});
-
- history.replace(
- iD.actions.AddNode(node),
- iD.actions.AddWayNode(way.id, node.id, -1),
+ history.perform(
+ iD.actions.AddNode(b),
+ iD.actions.AddWay(way),
+ iD.actions.AddWayNode(way.id, a.id),
+ iD.actions.AddWayNode(way.id, b.id),
+ iD.actions.AddWayNode(way.id, a.id),
'started an area');
controller.enter(iD.modes.DrawArea(way.id));
}
- function cancel() {
- controller.exit();
+ function startFromWay(other, loc, index) {
+ var a = iD.Node({loc: loc}),
+ b = iD.Node({loc: loc}),
+ way = iD.Way({tags: defaultTags});
+
+ history.perform(
+ iD.actions.AddNode(a),
+ iD.actions.AddNode(b),
+ iD.actions.AddWay(way),
+ iD.actions.AddWayNode(way.id, a.id),
+ iD.actions.AddWayNode(way.id, b.id),
+ iD.actions.AddWayNode(way.id, a.id),
+ iD.actions.AddWayNode(other.id, a.id, index),
+ 'started an area');
+
+ controller.enter(iD.modes.DrawArea(way.id));
}
- behavior = iD.behavior.Draw()
- .on('add', add)
- .on('cancel', cancel)
- .on('finish', cancel)
- (surface);
+ function start(loc) {
+ var a = iD.Node({loc: loc}),
+ b = iD.Node({loc: loc}),
+ way = iD.Way({tags: defaultTags});
+
+ history.perform(
+ iD.actions.AddNode(a),
+ iD.actions.AddNode(b),
+ iD.actions.AddWay(way),
+ iD.actions.AddWayNode(way.id, a.id),
+ iD.actions.AddWayNode(way.id, b.id),
+ iD.actions.AddWayNode(way.id, a.id),
+ 'started an area');
+
+ controller.enter(iD.modes.DrawArea(way.id));
+ }
+
+ behavior = iD.behavior.AddWay(mode)
+ .on('startFromNode', startFromNode)
+ .on('startFromWay', startFromWay)
+ .on('start', start);
+
+ mode.map.surface.call(behavior);
+ mode.map.tail('Click on the map to start drawing an area, like a park, lake, or building.', true);
};
mode.exit = function() {
- var map = mode.map,
- surface = map.surface;
-
- window.setTimeout(function() {
- map.dblclickEnable(true);
- }, 1000);
- map.tail(false);
- behavior.off(surface);
+ mode.map.surface.call(behavior.off);
};
return mode;
diff --git a/js/id/modes/add_line.js b/js/id/modes/add_line.js
index e5ff5ee55..729c382f4 100644
--- a/js/id/modes/add_line.js
+++ b/js/id/modes/add_line.js
@@ -6,91 +6,94 @@ iD.modes.AddLine = function() {
description: 'Lines can be highways, streets, pedestrian paths, or even canals.'
};
- var behavior;
+ var behavior,
+ defaultTags = {highway: 'residential'};
mode.enter = function() {
var map = mode.map,
- surface = map.surface,
- graph = map.history().graph(),
history = mode.history,
controller = mode.controller;
- map.dblclickEnable(false)
- .tail('Click on the map to start drawing an road, path, or route.', true);
-
- function add() {
- var datum = d3.select(d3.event.target).datum() || {},
- way = iD.Way({ tags: { highway: 'residential' } }),
- direction = 'forward',
- node;
-
- if (datum.type === 'node') {
- // continue an existing way
- node = datum;
- var parents = history.graph(graph).parentWays(node);
- var isLine = parents.length && parents[0].geometry(graph) === 'line';
- if (isLine && parents[0].first() === node.id) {
- way = parents[0];
- direction = 'backward';
- } else if (isLine && parents[0].last() === node.id) {
- way = parents[0];
- } else {
- history.perform(
- iD.actions.AddWay(way),
- iD.actions.AddWayNode(way.id, node.id));
- }
-
- } else if (datum.type === 'way') {
- // begin a new way starting from an existing way
- var choice = iD.geo.chooseIndex(datum, d3.mouse(map.surface.node()), map);
- node = iD.Node({ loc: choice.loc });
+ function startFromNode(a) {
+ var b = iD.Node({loc: a.loc}),
+ graph = history.graph(),
+ parent = graph.parentWays(a)[0],
+ isLine = parent && parent.geometry(graph) === 'line';
+ if (isLine && parent.first() === a.id) {
history.perform(
- iD.actions.AddWay(way),
- iD.actions.AddNode(node),
- iD.actions.AddWayNode(datum.id, node.id, choice.index),
- iD.actions.AddWayNode(way.id, node.id));
+ iD.actions.AddNode(b),
+ iD.actions.AddWayNode(parent.id, b.id, 0),
+ 'continued a line');
+
+ controller.enter(iD.modes.DrawLine(parent.id, 'backward'));
+
+ } else if (isLine && parent.last() === a.id) {
+ history.perform(
+ iD.actions.AddNode(b),
+ iD.actions.AddWayNode(parent.id, b.id),
+ 'continued a line');
+
+ controller.enter(iD.modes.DrawLine(parent.id, 'forward'));
} else {
- // begin a new way
- node = iD.Node({loc: map.mouseCoordinates()});
+ var way = iD.Way({tags: defaultTags});
history.perform(
+ iD.actions.AddNode(b),
iD.actions.AddWay(way),
- iD.actions.AddNode(node),
- iD.actions.AddWayNode(way.id, node.id));
+ iD.actions.AddWayNode(way.id, a.id),
+ iD.actions.AddWayNode(way.id, b.id),
+ 'continued a line');
+
+ controller.enter(iD.modes.DrawLine(way.id, 'forward'));
}
+ }
- var index = (direction === 'forward') ? way.nodes.length : 0,
+ function startFromWay(other, loc, index) {
+ var a = iD.Node({loc: loc}),
+ b = iD.Node({loc: loc}),
+ way = iD.Way({tags: defaultTags});
- node = iD.Node({loc: node.loc});
-
- history.replace(
- iD.actions.AddNode(node),
- iD.actions.AddWayNode(way.id, node.id, index),
+ history.perform(
+ iD.actions.AddNode(a),
+ iD.actions.AddNode(b),
+ iD.actions.AddWay(way),
+ iD.actions.AddWayNode(way.id, a.id),
+ iD.actions.AddWayNode(way.id, b.id),
+ iD.actions.AddWayNode(other.id, a.id, index),
'started a line');
- controller.enter(iD.modes.DrawLine(way.id, direction, node));
+ controller.enter(iD.modes.DrawLine(way.id, 'forward'));
}
- function cancel() {
- controller.exit();
+ function start(loc) {
+ var a = iD.Node({loc: loc}),
+ b = iD.Node({loc: loc}),
+ way = iD.Way({tags: defaultTags});
+
+ history.perform(
+ iD.actions.AddNode(a),
+ iD.actions.AddNode(b),
+ iD.actions.AddWay(way),
+ iD.actions.AddWayNode(way.id, a.id),
+ iD.actions.AddWayNode(way.id, b.id),
+ 'started a line');
+
+ controller.enter(iD.modes.DrawLine(way.id, 'forward'));
}
- behavior = iD.behavior.Draw()
- .on('add', add)
- .on('cancel', cancel)
- .on('finish', cancel)
- (surface);
+ behavior = iD.behavior.AddWay(mode)
+ .on('startFromNode', startFromNode)
+ .on('startFromWay', startFromWay)
+ .on('start', start);
+
+ mode.map.surface.call(behavior);
+ mode.map.tail('Click on the map to start drawing an road, path, or route.', true);
};
mode.exit = function() {
- var map = mode.map,
- surface = map.surface;
-
- map.dblclickEnable(true);
- map.tail(false);
- behavior.off(surface);
+ mode.map.surface.call(behavior.off);
};
return mode;
diff --git a/test/index.html b/test/index.html
index 10bba28ea..b3505f903 100644
--- a/test/index.html
+++ b/test/index.html
@@ -82,6 +82,7 @@
+