diff --git a/Makefile b/Makefile
index c9d13ff60..70ac208d6 100644
--- a/Makefile
+++ b/Makefile
@@ -38,6 +38,8 @@ all: \
js/id/behavior/*.js \
js/id/modes.js \
js/id/modes/*.js \
+ js/id/operations.js \
+ js/id/operations/*.js \
js/id/controller/*.js \
js/id/graph/*.js \
js/id/renderer/*.js \
diff --git a/index.html b/index.html
index af436a86e..cc45f4a48 100644
--- a/index.html
+++ b/index.html
@@ -106,6 +106,13 @@
+
+
+
+
+
+
+
diff --git a/js/id/actions/circular.js b/js/id/actions/circular.js
index d6af08560..da7965c79 100644
--- a/js/id/actions/circular.js
+++ b/js/id/actions/circular.js
@@ -51,7 +51,7 @@ iD.actions.Circular = function(wayId, map) {
};
action.enabled = function(graph) {
- return true;
+ return graph.entity(wayId).isClosed();
};
return action;
diff --git a/js/id/actions/delete_node.js b/js/id/actions/delete_node.js
index 48e704ac3..a93129ce3 100644
--- a/js/id/actions/delete_node.js
+++ b/js/id/actions/delete_node.js
@@ -1,6 +1,6 @@
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteNodeAction.as
iD.actions.DeleteNode = function(nodeId) {
- var action = function(graph) {
+ return function(graph) {
var node = graph.entity(nodeId);
graph.parentWays(node)
@@ -20,10 +20,4 @@ iD.actions.DeleteNode = function(nodeId) {
return graph.remove(node);
};
-
- action.enabled = function(graph) {
- return true;
- };
-
- return action;
};
diff --git a/js/id/actions/delete_way.js b/js/id/actions/delete_way.js
index eb58223cb..211447228 100644
--- a/js/id/actions/delete_way.js
+++ b/js/id/actions/delete_way.js
@@ -1,6 +1,6 @@
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteWayAction.as
iD.actions.DeleteWay = function(wayId) {
- var action = function(graph) {
+ return function(graph) {
var way = graph.entity(wayId);
graph.parentRelations(way)
@@ -27,10 +27,4 @@ iD.actions.DeleteWay = function(wayId) {
return graph.remove(way);
};
-
- action.enabled = function(graph) {
- return true;
- };
-
- return action;
};
diff --git a/js/id/actions/reverse_way.js b/js/id/actions/reverse_way.js
index 4a7d392b7..160017637 100644
--- a/js/id/actions/reverse_way.js
+++ b/js/id/actions/reverse_way.js
@@ -53,7 +53,7 @@ iD.actions.ReverseWay = function(wayId) {
}
}
- var action = function(graph) {
+ return function(graph) {
var way = graph.entity(wayId),
nodes = way.nodes.slice().reverse(),
tags = {}, key, role;
@@ -73,10 +73,4 @@ iD.actions.ReverseWay = function(wayId) {
return graph.replace(way.update({nodes: nodes, tags: tags}));
};
-
- action.enabled = function(graph) {
- return true;
- };
-
- return action;
};
diff --git a/js/id/modes/select.js b/js/id/modes/select.js
index fb4e5dc06..8871dd2ec 100644
--- a/js/id/modes/select.js
+++ b/js/id/modes/select.js
@@ -138,7 +138,7 @@ iD.modes.Select = function(entity, initial) {
})
.classed('selected', true);
- radialMenu = iD.ui.RadialMenu(entity, history, mode.map);
+ radialMenu = iD.ui.RadialMenu(entity, mode);
if (d3.event && !initial) {
var loc = map.mouseCoordinates();
diff --git a/js/id/operations.js b/js/id/operations.js
new file mode 100644
index 000000000..2786d046f
--- /dev/null
+++ b/js/id/operations.js
@@ -0,0 +1 @@
+iD.operations = {}
diff --git a/js/id/operations/circular.js b/js/id/operations/circular.js
new file mode 100644
index 000000000..0af64aa20
--- /dev/null
+++ b/js/id/operations/circular.js
@@ -0,0 +1,34 @@
+iD.operations.Circular = function(entityId, mode) {
+ var action = iD.actions.Circular(entityId, mode.map);
+
+ var operation = function(history) {
+ var graph = history.graph(),
+ entity = graph.entity(entityId),
+ geometry = entity.geometry(graph);
+
+ if (geometry === 'line') {
+ history.perform(
+ action,
+ 'made a line circular');
+
+ } else if (geometry === 'area') {
+ history.perform(
+ action,
+ 'made an area circular');
+ }
+ };
+
+ operation.available = function(graph) {
+ var entity = graph.entity(entityId);
+ return entity.geometry(graph) === 'area' || entity.geometry(graph) === 'line';
+ };
+
+ operation.enabled = function(graph) {
+ return action.enabled(graph);
+ };
+
+ operation.id = "circular";
+ operation.title = "Circular";
+
+ return operation;
+};
diff --git a/js/id/operations/delete.js b/js/id/operations/delete.js
new file mode 100644
index 000000000..53038dbc4
--- /dev/null
+++ b/js/id/operations/delete.js
@@ -0,0 +1,42 @@
+iD.operations.Delete = function(entityId) {
+ var operation = function(history) {
+ var graph = history.graph(),
+ entity = graph.entity(entityId),
+ geometry = entity.geometry(graph);
+
+ if (geometry === 'vertex') {
+ history.perform(
+ iD.actions.DeleteNode(entityId),
+ 'deleted a vertex');
+
+ } else if (geometry === 'point') {
+ history.perform(
+ iD.actions.DeleteNode(entityId),
+ 'deleted a point');
+
+ } else if (geometry === 'line') {
+ history.perform(
+ iD.actions.DeleteWay(entityId),
+ 'deleted a line');
+
+ } else if (geometry === 'area') {
+ history.perform(
+ iD.actions.DeleteWay(entityId),
+ 'deleted an area');
+ }
+ };
+
+ operation.available = function(graph) {
+ var entity = graph.entity(entityId);
+ return _.contains(['vertex', 'point', 'line', 'area'], entity.geometry(graph));
+ };
+
+ operation.enabled = function() {
+ return true;
+ };
+
+ operation.id = "delete";
+ operation.title = "Delete";
+
+ return operation;
+};
diff --git a/js/id/operations/reverse.js b/js/id/operations/reverse.js
new file mode 100644
index 000000000..941d8a4da
--- /dev/null
+++ b/js/id/operations/reverse.js
@@ -0,0 +1,21 @@
+iD.operations.Reverse = function(entityId) {
+ var operation = function(history) {
+ history.perform(
+ iD.actions.ReverseWay(entityId),
+ 'reversed a line');
+ };
+
+ operation.available = function(graph) {
+ var entity = graph.entity(entityId);
+ return entity.geometry(graph) === 'line';
+ };
+
+ operation.enabled = function() {
+ return true;
+ };
+
+ operation.id = "reverse";
+ operation.title = "Reverse";
+
+ return operation;
+};
diff --git a/js/id/operations/split.js b/js/id/operations/split.js
new file mode 100644
index 000000000..838dac88e
--- /dev/null
+++ b/js/id/operations/split.js
@@ -0,0 +1,21 @@
+iD.operations.Split = function(entityId) {
+ var action = iD.actions.SplitWay(entityId);
+
+ var operation = function(history) {
+ history.perform(action, 'split a way');
+ };
+
+ operation.available = function(graph) {
+ var entity = graph.entity(entityId);
+ return entity.geometry(graph) === 'vertex';
+ };
+
+ operation.enabled = function(graph) {
+ return action.enabled(graph);
+ };
+
+ operation.id = "split";
+ operation.title = "Split";
+
+ return operation;
+};
diff --git a/js/id/operations/unjoin.js b/js/id/operations/unjoin.js
new file mode 100644
index 000000000..985a41697
--- /dev/null
+++ b/js/id/operations/unjoin.js
@@ -0,0 +1,21 @@
+iD.operations.Unjoin = function(entityId) {
+ var action = iD.actions.UnjoinNode(entityId);
+
+ var operation = function(history) {
+ history.perform(action, 'unjoined lines');
+ };
+
+ operation.available = function(graph) {
+ var entity = graph.entity(entityId);
+ return entity.geometry(graph) === 'vertex';
+ };
+
+ operation.enabled = function(graph) {
+ return action.enabled(graph);
+ };
+
+ operation.id = "unjoin";
+ operation.title = "Unjoin";
+
+ return operation;
+};
diff --git a/js/id/ui/radial_menu.js b/js/id/ui/radial_menu.js
index ff895ea81..a891b7d8e 100644
--- a/js/id/ui/radial_menu.js
+++ b/js/id/ui/radial_menu.js
@@ -1,83 +1,12 @@
-iD.ui.RadialMenu = function(entity, history, map) {
+iD.ui.RadialMenu = function(entity, mode) {
var arcs;
var radialMenu = function(selection, center) {
- var operations,
+ var history = mode.map.history(),
graph = history.graph(),
- geometry = entity.geometry(graph);
-
- if (geometry === 'vertex') {
- operations = [
- {
- id: 'delete',
- text: 'Delete',
- description: 'deleted a node',
- action: iD.actions.DeleteNode(entity.id)
- },
- {
- id: 'split',
- text: 'Split Way',
- description: 'split a way',
- action: iD.actions.SplitWay(entity.id)
- },
- {
- id: 'unjoin',
- text: 'Unjoin',
- description: 'unjoined lines',
- action: iD.actions.UnjoinNode(entity.id)
- }
- ];
- } else if (geometry === 'point') {
- operations = [
- {
- id: 'delete',
- text: 'Delete',
- description: 'deleted a point',
- action: iD.actions.DeleteNode(entity.id)
- }
- ];
- } else if (geometry === 'line') {
- operations = [
- {
- id: 'delete',
- text: 'Delete',
- description: 'deleted a line',
- action: iD.actions.DeleteWay(entity.id)
- },
- {
- id: 'reverse',
- text: 'Reverse',
- description: 'reversed a way',
- action: iD.actions.ReverseWay(entity.id)
- }
- ];
- if (entity.isClosed()) {
- operations.push({
- id: 'circlar',
- text: 'Circular',
- description: 'made way circular',
- action: iD.actions.Circular(entity.id, map)
- });
- }
- } else if (geometry === 'area') {
- operations = [
- {
- id: 'delete',
- text: 'Delete',
- description: 'deleted an area',
- action: iD.actions.DeleteWay(entity.id)
- },
- {
- id: 'circlar',
- text: 'Circular',
- description: 'made area circular',
- action: iD.actions.Circular(entity.id, map)
- }
- ];
- } else {
- // Relation, not implemented yet.
- return;
- }
+ operations = d3.values(iD.operations)
+ .map(function (o) { return o(entity.id, mode); })
+ .filter(function (o) { return o.available(graph); });
var arc = d3.svg.arc()
.outerRadius(70)
@@ -98,14 +27,14 @@ iD.ui.RadialMenu = function(entity, history, map) {
arcs.append('path')
.attr('class', function (d) { return 'radial-menu-item radial-menu-item-' + d.id; })
.attr('d', arc)
- .classed('disabled', function (d) { return !d.action.enabled(history.graph()); })
- .on('click', function (d) { history.perform(d.action, d.description); });
+ .classed('disabled', function (d) { return !d.enabled(graph); })
+ .on('click', function (d) { d(history); });
arcs.append('text')
.attr("transform", function(d, i) { return "translate(" + arc.centroid(d, i) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
- .text(function(d) { return d.text; });
+ .text(function(d) { return d.title; });
};
radialMenu.close = function(selection) {
diff --git a/test/index.html b/test/index.html
index 859d5b95d..de83d89e0 100644
--- a/test/index.html
+++ b/test/index.html
@@ -100,6 +100,13 @@
+
+
+
+
+
+
+