diff --git a/data/core.yaml b/data/core.yaml
index f06a69983..828e9715b 100644
--- a/data/core.yaml
+++ b/data/core.yaml
@@ -229,12 +229,13 @@ en:
annotation:
create: Added a turn restriction
delete: Deleted a turn restriction
- detachNode:
+ detach_node:
title: Detach
- key: T
+ key: E
description: Detach this node from these lines/areas.
- annotation: Detached a node from owning lines/areas.
- via_restriction: "This can't be detached because it would damage a turn restriction."
+ annotation: Detached a node from parent lines/areas.
+ restriction: "This node can't be detached because it would damage a \"{relation}\" relation."
+ connected_to_hidden: This node can't be detached because it is connected to a hidden feature.
restriction:
controls:
distance: Distance
@@ -1140,6 +1141,7 @@ en:
continue_line: "Continue a line at the selected node"
merge: "Combine (merge) selected features"
disconnect: "Disconnect features at the selected node"
+ detach_node: "Detach selected node from parent lines/areas"
split: "Split a line into two at the selected node"
reverse: "Reverse a line"
move: "Move selected features"
diff --git a/data/shortcuts.json b/data/shortcuts.json
index ee0c9cdc9..0da3dfdce 100644
--- a/data/shortcuts.json
+++ b/data/shortcuts.json
@@ -123,23 +123,19 @@
"text": "shortcuts.browsing.vertex_selected.previous"
},
{
- "shortcuts": ["]","↘"
- ],
+ "shortcuts": ["]","↘"],
"text": "shortcuts.browsing.vertex_selected.next"
},
{
- "shortcuts": ["{","⇞"
- ],
+ "shortcuts": ["{","⇞"],
"text": "shortcuts.browsing.vertex_selected.first"
},
{
- "shortcuts": ["}","⇟"
- ],
+ "shortcuts": ["}","⇟"],
"text": "shortcuts.browsing.vertex_selected.last"
},
{
- "shortcuts": ["\\","shortcuts.key.pause"
- ],
+ "shortcuts": ["\\","shortcuts.key.pause"],
"text": "shortcuts.browsing.vertex_selected.change_parent"
}
]
@@ -157,33 +153,27 @@
"text": "shortcuts.editing.drawing.title"
},
{
- "shortcuts": ["1"
- ],
+ "shortcuts": ["1"],
"text": "shortcuts.editing.drawing.add_point"
},
{
- "shortcuts": ["2"
- ],
+ "shortcuts": ["2"],
"text": "shortcuts.editing.drawing.add_line"
},
{
- "shortcuts": ["3"
- ],
+ "shortcuts": ["3"],
"text": "shortcuts.editing.drawing.add_area"
},
{
- "shortcuts": ["Left-click","shortcuts.key.space"
- ],
+ "shortcuts": ["Left-click","shortcuts.key.space"],
"text": "shortcuts.editing.drawing.place_point"
},
{
- "shortcuts": ["⌥"
- ],
+ "shortcuts": ["⌥"],
"text": "shortcuts.editing.drawing.disable_snap"
},
{
- "shortcuts": ["↵","⎋"
- ],
+ "shortcuts": ["↵","⎋"],
"text": "shortcuts.editing.drawing.stop_line"
},
{
@@ -192,32 +182,27 @@
},
{
"modifiers": ["⌘"],
- "shortcuts": ["C"
- ],
+ "shortcuts": ["C"],
"text": "shortcuts.editing.commands.copy"
},
{
"modifiers": ["⌘"],
- "shortcuts": ["V"
- ],
+ "shortcuts": ["V"],
"text": "shortcuts.editing.commands.paste"
},
{
"modifiers": ["⌘"],
- "shortcuts": ["Z"
- ],
+ "shortcuts": ["Z"],
"text": "shortcuts.editing.commands.undo"
},
{
"modifiers": ["⌘","⇧"],
- "shortcuts": ["Z"
- ],
+ "shortcuts": ["Z"],
"text": "shortcuts.editing.commands.redo"
},
{
"modifiers": ["⌘"],
- "shortcuts": ["S"
- ],
+ "shortcuts": ["S"],
"text": "shortcuts.editing.commands.save"
}
]
@@ -229,64 +214,56 @@
"text": "shortcuts.editing.operations.title"
},
{
- "shortcuts": ["operations.continue.key"
- ],
+ "shortcuts": ["operations.continue.key"],
"text": "shortcuts.editing.operations.continue_line"
},
{
- "shortcuts": ["operations.merge.key"
- ],
+ "shortcuts": ["operations.merge.key"],
"text": "shortcuts.editing.operations.merge"
},
{
- "shortcuts": ["operations.disconnect.key"
- ],
+ "shortcuts": ["operations.disconnect.key"],
"text": "shortcuts.editing.operations.disconnect"
},
{
- "shortcuts": ["operations.split.key"
- ],
+ "shortcuts": ["operations.detach_node.key"],
+ "text": "shortcuts.editing.operations.detach_node"
+ },
+ {
+ "shortcuts": ["operations.split.key"],
"text": "shortcuts.editing.operations.split"
},
{
- "shortcuts": ["operations.reverse.key"
- ],
+ "shortcuts": ["operations.reverse.key"],
"text": "shortcuts.editing.operations.reverse"
},
{
- "shortcuts": ["operations.move.key"
- ],
+ "shortcuts": ["operations.move.key"],
"text": "shortcuts.editing.operations.move"
},
{
- "shortcuts": ["operations.rotate.key"
- ],
+ "shortcuts": ["operations.rotate.key"],
"text": "shortcuts.editing.operations.rotate"
},
{
- "shortcuts": ["operations.orthogonalize.key"
- ],
+ "shortcuts": ["operations.orthogonalize.key"],
"text": "shortcuts.editing.operations.orthogonalize"
},
{
- "shortcuts": ["operations.circularize.key"
- ],
+ "shortcuts": ["operations.circularize.key"],
"text": "shortcuts.editing.operations.circularize"
},
{
- "shortcuts": ["operations.reflect.key.long"
- ],
+ "shortcuts": ["operations.reflect.key.long"],
"text": "shortcuts.editing.operations.reflect_long"
},
{
- "shortcuts": ["operations.reflect.key.short"
- ],
+ "shortcuts": ["operations.reflect.key.short"],
"text": "shortcuts.editing.operations.reflect_short"
},
{
"modifiers": ["⌘"],
- "shortcuts": ["⌫"
- ],
+ "shortcuts": ["⌫"],
"text": "shortcuts.editing.operations.delete"
}
]
@@ -305,32 +282,27 @@
},
{
"modifiers": ["⌘"],
- "shortcuts": ["info_panels.key"
- ],
+ "shortcuts": ["info_panels.key"],
"text": "shortcuts.tools.info.all"
},
{
"modifiers": ["⌘","⇧"],
- "shortcuts": ["info_panels.background.key"
- ],
+ "shortcuts": ["info_panels.background.key"],
"text": "shortcuts.tools.info.background"
},
{
"modifiers": ["⌘","⇧"],
- "shortcuts": ["info_panels.history.key"
- ],
+ "shortcuts": ["info_panels.history.key"],
"text": "shortcuts.tools.info.history"
},
{
"modifiers": ["⌘","⇧"],
- "shortcuts": ["info_panels.location.key"
- ],
+ "shortcuts": ["info_panels.location.key"],
"text": "shortcuts.tools.info.location"
},
{
"modifiers": ["⌘","⇧"],
- "shortcuts": ["info_panels.measurement.key"
- ],
+ "shortcuts": ["info_panels.measurement.key"],
"text": "shortcuts.tools.info.measurement"
}
]
diff --git a/dist/locales/en.json b/dist/locales/en.json
index 8363a101f..47a1f0100 100644
--- a/dist/locales/en.json
+++ b/dist/locales/en.json
@@ -298,12 +298,13 @@
"delete": "Deleted a turn restriction"
}
},
- "detachNode": {
+ "detach_node": {
"title": "Detach",
- "key": "T",
+ "key": "E",
"description": "Detach this node from these lines/areas.",
- "annotation": "Detached a node from owning lines/areas.",
- "via_restriction": "This can't be detached because it would damage a turn restriction."
+ "annotation": "Detached a node from parent lines/areas.",
+ "restriction": "This node can't be detached because it would damage a \"{relation}\" relation.",
+ "connected_to_hidden": "This node can't be detached because it is connected to a hidden feature."
}
},
"restriction": {
@@ -1315,6 +1316,7 @@
"continue_line": "Continue a line at the selected node",
"merge": "Combine (merge) selected features",
"disconnect": "Disconnect features at the selected node",
+ "detach_node": "Detach selected node from parent lines/areas",
"split": "Split a line into two at the selected node",
"reverse": "Reverse a line",
"move": "Move selected features",
diff --git a/modules/actions/detach_node.js b/modules/actions/detach_node.js
index 7ff5cd8e3..39bcd59fa 100644
--- a/modules/actions/detach_node.js
+++ b/modules/actions/detach_node.js
@@ -1,39 +1,48 @@
import { osmNode } from '../osm';
-export function actionDetachNode(nodeId) {
- return function (graph) {
- // Get the point in question
- var node = graph.entity(nodeId);
- // Get all of the ways it's currently attached to
- var parentWays = graph.parentWays(node);
+
+export function actionDetachNode(nodeID) {
+
+ var action = function(graph) {
+ var node = graph.entity(nodeID);
+
// Create a new node to replace the one we will detach
- var replacementNode = osmNode({ loc: node.loc });
- // We need to process each way in turn, updating the graph as we go
- var updatedWaysGraph = parentWays
- .reduce(function (accGraph, parentWay) {
- // Make a note of where in the way our target node is inside this way
- var originalIndex = parentWay.nodes.indexOf(nodeId);
- // Swap out the target node for the replacement
- var updatedWay = parentWay.removeNode(nodeId) // Remove our target node from the parent way
- .addNode(replacementNode.id, originalIndex); // Add in the replacement node in its place
- // Update the graph with the updated way and pass into the next cycle of the reduce operation
- return accGraph.replace(updatedWay);
- },
- // Seed the reduction with the input graph, updated to include the replacementNode so
- // that is accessible to the ways when we add it in to them
- graph.replace(replacementNode));
+ var replacement = osmNode({ loc: node.loc });
+ graph = graph.replace(replacement);
+
+ // Process each way in turn, updating the graph as we go
+ graph = graph.parentWays(node)
+ .reduce(function(accGraph, parentWay) {
+ return accGraph.replace(parentWay.replaceNode(nodeID, replacement.id));
+ }, graph);
+
// Process any relations too
- var parentRels = updatedWaysGraph.parentRelations(node);
- return parentRels
- .reduce(function (accGraph, parentRel) {
- // Move the relationship to the new node
- var originalMember = parentRel.memberById(nodeId);
- var newMember = { id: replacementNode.id, type: 'node', role: originalMember.role };
- // Remove & replace with the new member
- var updatedRel = parentRel.removeMembersWithID(nodeId)
- .addMember(newMember, originalMember.index);
- // Update graph and pass into the next cycle of the reduce operation
- return accGraph.replace(updatedRel);
- }, updatedWaysGraph);
+ return graph.parentRelations(node)
+ .reduce(function(accGraph, parentRel) {
+ return accGraph.replace(parentRel.replaceMember(node, replacement));
+ }, graph);
};
+
+
+ action.disabled = function(graph) {
+ var node = graph.entity(nodeID);
+ var parentRels = graph.parentRelations(node);
+
+ for (var i = 0; i < parentRels.length; i++) {
+ var relation = parentRels[i];
+ if (!relation.isValidRestriction()) continue;
+
+ for (var j = 0; j < relation.members.length; j++) {
+ var m = relation.members[j];
+ if (m.id === nodeID && (m.role === 'via' || m.role === 'location_hint')) {
+ return 'restriction';
+ }
+ }
+ }
+
+ return false;
+ };
+
+
+ return action;
}
diff --git a/modules/core/graph.js b/modules/core/graph.js
index bfe2c1cf2..31c06c410 100644
--- a/modules/core/graph.js
+++ b/modules/core/graph.js
@@ -52,9 +52,8 @@ coreGraph.prototype = {
transient: function(entity, key, fn) {
- var id = entity.id,
- transients = this.transients[id] ||
- (this.transients[id] = {});
+ var id = entity.id;
+ var transients = this.transients[id] || (this.transients[id] = {});
if (transients[key] !== undefined) {
return transients[key];
@@ -67,8 +66,8 @@ coreGraph.prototype = {
parentWays: function(entity) {
- var parents = this._parentWays[entity.id],
- result = [];
+ var parents = this._parentWays[entity.id];
+ var result = [];
if (parents) {
for (var i = 0; i < parents.length; i++) {
@@ -92,8 +91,8 @@ coreGraph.prototype = {
parentRelations: function(entity) {
- var parents = this._parentRels[entity.id],
- result = [];
+ var parents = this._parentRels[entity.id];
+ var result = [];
if (parents) {
for (var i = 0; i < parents.length; i++) {
@@ -134,8 +133,8 @@ coreGraph.prototype = {
// data into each state. To external consumers, it should appear as if the
// graph always contained the newly downloaded data.
rebase: function(entities, stack, force) {
- var base = this.base(),
- i, j, k, id;
+ var base = this.base();
+ var i, j, k, id;
for (i = 0; i < entities.length; i++) {
var entity = entities[i];
@@ -168,8 +167,8 @@ coreGraph.prototype = {
_updateRebased: function() {
- var base = this.base(),
- i, k, child, id, keys;
+ var base = this.base();
+ var i, k, child, id, keys;
keys = Object.keys(this._parentWays);
for (i = 0; i < keys.length; i++) {
@@ -206,17 +205,13 @@ coreGraph.prototype = {
// Updates calculated properties (parentWays, parentRels) for the specified change
_updateCalculated: function(oldentity, entity, parentWays, parentRels) {
-
parentWays = parentWays || this._parentWays;
parentRels = parentRels || this._parentRels;
- var type = entity && entity.type || oldentity && oldentity.type,
- removed, added, ways, rels, i;
+ var type = entity && entity.type || oldentity && oldentity.type;
+ var removed, added, ways, rels, i;
-
- if (type === 'way') {
-
- // Update parentWays
+ if (type === 'way') { // Update parentWays
if (oldentity && entity) {
removed = _difference(oldentity.nodes, entity.nodes);
added = _difference(entity.nodes, oldentity.nodes);
@@ -236,9 +231,7 @@ coreGraph.prototype = {
parentWays[added[i]] = ways;
}
- } else if (type === 'relation') {
-
- // Update parentRels
+ } else if (type === 'relation') { // Update parentRels
if (oldentity && entity) {
removed = _difference(oldentity.members, entity.members);
added = _difference(entity.members, oldentity);
@@ -262,8 +255,7 @@ coreGraph.prototype = {
replace: function(entity) {
- if (this.entities[entity.id] === entity)
- return this;
+ if (this.entities[entity.id] === entity) return this;
return this.update(function() {
this._updateCalculated(this.entities[entity.id], entity);
@@ -281,11 +273,9 @@ coreGraph.prototype = {
revert: function(id) {
- var baseEntity = this.base().entities[id],
- headEntity = this.entities[id];
-
- if (headEntity === baseEntity)
- return this;
+ var baseEntity = this.base().entities[id];
+ var headEntity = this.entities[id];
+ if (headEntity === baseEntity) return this;
return this.update(function() {
this._updateCalculated(headEntity, baseEntity);
@@ -296,7 +286,6 @@ coreGraph.prototype = {
update: function() {
var graph = this.frozen ? coreGraph(this, true) : this;
-
for (var i = 0; i < arguments.length; i++) {
arguments[i].call(graph, graph);
}
diff --git a/modules/operations/detach_node.js b/modules/operations/detach_node.js
index bb1f985cf..268b8554e 100644
--- a/modules/operations/detach_node.js
+++ b/modules/operations/detach_node.js
@@ -1,104 +1,85 @@
-import { actionDetachNode } from '../actions/index';
-import { behaviorOperation } from '../behavior/index';
-import { modeMove } from '../modes/index';
+import _some from 'lodash-es/some';
+
+import { actionDetachNode, actionMoveNode } from '../actions';
+import { behaviorOperation } from '../behavior';
+import { modeMove } from '../modes';
import { t } from '../util/locale';
-import _flatMap from 'lodash-es/flatMap';
-import _uniq from 'lodash-es/uniq';
+
export function operationDetachNode(selectedIDs, context) {
- var selectedNode = selectedIDs[0];
+ var nodeID = selectedIDs.length && selectedIDs[0];
+ var action = actionDetachNode(nodeID);
+
var operation = function () {
- context.perform(actionDetachNode(selectedNode));
- context.enter(modeMove(context, [selectedNode], context.graph));
- };
- var hasTags = function (entity) {
- return Object.keys(entity.tags).length > 0;
+ context.perform(action); // do the detach
+
+ var mouse = context.map().mouseCoordinates();
+ if (mouse.some(isNaN)) {
+ enterMoveMode();
+
+ } else {
+ // move detached node to the mouse location (transitioned)
+ context.perform(actionMoveNode(nodeID, mouse));
+
+ // after transition completes, put at final mouse location and enter move mode.
+ window.setTimeout(function() {
+ mouse = context.map().mouseCoordinates();
+ context.replace(actionMoveNode(nodeID, mouse));
+ enterMoveMode();
+ }, 150);
+ }
+
+ function enterMoveMode() {
+ var baseGraph = context.graph();
+ context.enter(modeMove(context, [nodeID], baseGraph));
+ }
};
+
+
operation.available = function () {
- // Check multiple items aren't selected
- if (selectedIDs.length !== 1) {
- return false;
- }
- // Get the entity itself
+ if (selectedIDs.length !== 1) return false;
+
var graph = context.graph();
- var entity = graph.hasEntity(selectedNode);
- if (!entity) {
- // This probably isn't possible
- return false;
- }
- // Confirm entity is a node with tags
- if (entity.type === 'node' && hasTags(entity)) {
- // Confirm that the node is owned by at least 1 parent way
- var parentWays = graph.parentWays(entity);
- return parentWays && parentWays.length > 0;
- }
- // Not appropriate
- return false;
+ var entity = graph.hasEntity(nodeID);
+ if (!entity) return false;
+
+ return entity.type === 'node' &&
+ entity.hasInterestingTags() &&
+ graph.parentWays(entity).length > 0;
};
+
+
operation.disabled = function () {
- return false;
+ var reason;
+ if (_some(selectedIDs, context.hasHiddenConnections)) {
+ reason = 'connected_to_hidden';
+ }
+ return action.disabled(context.graph()) || reason;
};
+
+
operation.tooltip = function () {
var disableReason = operation.disabled();
- return disableReason
- ? t('operations.detachNode.' + disableReason)
- : t('operations.detachNode.description');
+ if (disableReason) {
+ return t('operations.detach_node.' + disableReason,
+ { relation: context.presets().item('type/restriction').name() });
+ } else {
+ return t('operations.detach_node.description');
+ }
};
+
+
operation.annotation = function () {
- return t('operations.detachNode.annotation');
+ return t('operations.detach_node.annotation');
};
- operation.id = 'detachNode';
- operation.keys = [t('operations.detachNode.key')];
- operation.title = t('operations.detachNode.title');
+
+
+ operation.id = 'detach-node';
+ operation.keys = [t('operations.detach_node.key')];
+ operation.title = t('operations.detach_node.title');
operation.behavior = behaviorOperation(context).which(operation);
- operation.disabled = function () {
- // We should prevent the node being detached if it represents a via/location_hint node of a turn restriction
- var graph = context.graph();
- // Get nodes for the Ids (although there should only be one, we can handle multiple here)
- var nodes = selectedIDs.map(function (i) { return graph.hasEntity(i); })
- .filter(isNotNullOrUndefined);
- // Get all via nodes of restrictions involving the target nodes
- var restrictionNodeIds = _flatMap(nodes, function (node) {
- // Get the relations that this node belongs to
- var relationsFromNode = graph.parentRelations(node);
- // Check each relation in turn
- return _flatMap(relationsFromNode, function (relation) {
- // Check to see if this is a restriction relation, if not return null
- if (!relation.isValidRestriction()) {
- return null;
- }
- // We have identified that it is a restriction.
- // https://wiki.openstreetmap.org/wiki/Relation:restriction indicates that
- // from & to roles are only appropriate for Ways
- // The via members can be either nodes or ways. Via-Ways do not prevent us removing a node
- // from within them, as it is the way itself which is in the relation with the via role,
- // and not the consitutent nodes (so if we switch out a constituent node, the way id
- // does not change and therefore the relation will not be affected). Therefore we
- // only need to examine the standalone nodes
- return relation.members.filter(function (m) {
- return (m.role === 'via' || m.role === 'location_hint') && m.type === 'node';
- }).map(function (m) { return m.id; });
- });
- }).filter(isNotNullOrUndefined);
- // Get unique list of ids in restrictionNodeIds to simplify checking
- var nodeIds = _uniq(restrictionNodeIds);
-
- // Now we have a list of via/location_hint nodes, we should prevent detachment if the target node is in this list
- var anyInhibits = nodes.filter(function (n) {
- return nodeIds.indexOf(n.id) !== -1;
- });
- if (anyInhibits.length > 0) {
- // The node is a via/location_hint, do not permit
- return 'via_restriction';
- }
- // We are ok to proceed
- return false;
- };
return operation;
}
-function isNotNullOrUndefined(i) {
- return i !== undefined && i !== null;
-}
\ No newline at end of file
diff --git a/modules/osm/relation.js b/modules/osm/relation.js
index 8532990ed..1ba283df4 100644
--- a/modules/osm/relation.js
+++ b/modules/osm/relation.js
@@ -42,8 +42,7 @@ _extend(osmRelation.prototype, {
copy: function(resolver, copies) {
- if (copies[this.id])
- return copies[this.id];
+ if (copies[this.id]) return copies[this.id];
var copy = osmEntity.prototype.copy.call(this, resolver, copies);
diff --git a/modules/osm/way.js b/modules/osm/way.js
index af1b3d476..c4da27c25 100644
--- a/modules/osm/way.js
+++ b/modules/osm/way.js
@@ -31,8 +31,7 @@ _extend(osmWay.prototype, {
copy: function(resolver, copies) {
- if (copies[this.id])
- return copies[this.id];
+ if (copies[this.id]) return copies[this.id];
var copy = osmEntity.prototype.copy.call(this, resolver, copies);
@@ -239,9 +238,9 @@ _extend(osmWay.prototype, {
unclose: function() {
if (!this.isClosed()) return this;
- var nodes = this.nodes.slice(),
- connector = this.first(),
- i = nodes.length - 1;
+ var nodes = this.nodes.slice();
+ var connector = this.first();
+ var i = nodes.length - 1;
// remove trailing connectors..
while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
@@ -260,9 +259,9 @@ _extend(osmWay.prototype, {
// Consecutive duplicates are eliminated including existing ones.
// Circularity is always preserved when adding a node.
addNode: function(id, index) {
- var nodes = this.nodes.slice(),
- isClosed = this.isClosed(),
- max = isClosed ? nodes.length - 1 : nodes.length;
+ var nodes = this.nodes.slice();
+ var isClosed = this.isClosed();
+ var max = isClosed ? nodes.length - 1 : nodes.length;
if (index === undefined) {
index = max;
@@ -309,9 +308,9 @@ _extend(osmWay.prototype, {
// Consecutive duplicates are eliminated including existing ones.
// Circularity is preserved when updating a node.
updateNode: function(id, index) {
- var nodes = this.nodes.slice(),
- isClosed = this.isClosed(),
- max = nodes.length - 1;
+ var nodes = this.nodes.slice();
+ var isClosed = this.isClosed();
+ var max = nodes.length - 1;
if (index === undefined || index < 0 || index > max) {
throw new RangeError('index ' + index + ' out of range 0..' + max);
@@ -353,13 +352,13 @@ _extend(osmWay.prototype, {
// Replaces each occurrence of node id needle with replacement.
// Consecutive duplicates are eliminated including existing ones.
// Circularity is preserved.
- replaceNode: function(needle, replacement) {
- var nodes = this.nodes.slice(),
- isClosed = this.isClosed();
+ replaceNode: function(needleID, replacementID) {
+ var nodes = this.nodes.slice();
+ var isClosed = this.isClosed();
for (var i = 0; i < nodes.length; i++) {
- if (nodes[i] === needle) {
- nodes[i] = replacement;
+ if (nodes[i] === needleID) {
+ nodes[i] = replacementID;
}
}
@@ -374,12 +373,12 @@ _extend(osmWay.prototype, {
},
- // Removes each occurrence of node id needle with replacement.
+ // Removes each occurrence of node id.
// Consecutive duplicates are eliminated including existing ones.
// Circularity is preserved.
removeNode: function(id) {
- var nodes = this.nodes.slice(),
- isClosed = this.isClosed();
+ var nodes = this.nodes.slice();
+ var isClosed = this.isClosed();
nodes = nodes
.filter(function(node) { return node !== id; })
diff --git a/svg/iD-sprite/operations/operation-detach-node.svg b/svg/iD-sprite/operations/operation-detach-node.svg
new file mode 100644
index 000000000..a10edfc66
--- /dev/null
+++ b/svg/iD-sprite/operations/operation-detach-node.svg
@@ -0,0 +1,6 @@
+
+
+
diff --git a/svg/iD-sprite/operations/operation-detachNode.svg b/svg/iD-sprite/operations/operation-detachNode.svg
deleted file mode 100644
index 504acc2cb..000000000
--- a/svg/iD-sprite/operations/operation-detachNode.svg
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/test/spec/actions/detach_node.js b/test/spec/actions/detach_node.js
index 16fcc5e3a..1bac58e44 100644
--- a/test/spec/actions/detach_node.js
+++ b/test/spec/actions/detach_node.js
@@ -1,20 +1,22 @@
describe('iD.actionDetachNode', function () {
var tags = { 'name': 'test' };
+
function createTargetNode(id, lonlat) {
- return iD.Node({ id: id, loc: lonlat, tags: tags });
+ return iD.osmNode({ id: id, loc: lonlat, tags: tags });
}
- describe('simple way', function () {
+
+ describe('linear way', function () {
var graph;
beforeEach(function () {
- // Set up a simple way
- // a-b-c-d
- // (0,0)-(1,0)-(2,0)-(3,0)
- graph = iD.Graph([
- iD.Node({ id: 'a', loc: [0, 0] }),
- iD.Node({ id: 'b', loc: [1, 0] }),
- iD.Node({ id: 'c', loc: [2, 0] }),
- iD.Node({ id: 'd', loc: [3, 0] }),
- iD.Way({ id: 'w', nodes: ['a', 'b', 'c', 'd'] })
+ //
+ // a -- b -- c -- d
+ //
+ graph = iD.coreGraph([
+ iD.osmNode({ id: 'a', loc: [0, 0] }),
+ iD.osmNode({ id: 'b', loc: [1, 0] }),
+ iD.osmNode({ id: 'c', loc: [2, 0] }),
+ iD.osmNode({ id: 'd', loc: [3, 0] }),
+ iD.osmWay({ id: '-', nodes: ['a', 'b', 'c', 'd'] })
]);
});
@@ -30,7 +32,7 @@ describe('iD.actionDetachNode', function () {
var assertionGraph = iD.actionDetachNode('a')(graph);
// Confirm that the way still has 4 nodes
- var target = assertionGraph.entity('w');
+ var target = assertionGraph.entity('-');
expect(target.nodes.length).to.eql(4);
});
@@ -39,11 +41,11 @@ describe('iD.actionDetachNode', function () {
var assertionGraph = iD.actionDetachNode('a')(graph);
// Confirm that the way is ordered correctly
- var target = assertionGraph.entity('w');
+ var target = assertionGraph.entity('-');
// Note that we can't be sure of the id of the replacement node
// so we only assert the nodes we know the ids for
// As we have already confirmed the size of the array we can assume
- // that the replacement node is in the correct posisiton by a process of elimination
+ // that the replacement node is in the correct posisiton by a process of elimination
expect(target.nodes[1]).to.eql('b');
expect(target.nodes[2]).to.eql('c');
expect(target.nodes[3]).to.eql('d');
@@ -54,7 +56,7 @@ describe('iD.actionDetachNode', function () {
var assertionGraph = iD.actionDetachNode('a')(graph);
// Confirm that the nodes have not moved, including the replacement node
- var nodes = assertionGraph.entity('w').nodes;
+ var nodes = assertionGraph.entity('-').nodes;
expect(assertionGraph.entity(nodes[0]).loc).to.eql([0, 0]);
expect(assertionGraph.entity(nodes[1]).loc).to.eql([1, 0]);
expect(assertionGraph.entity(nodes[2]).loc).to.eql([2, 0]);
@@ -65,7 +67,7 @@ describe('iD.actionDetachNode', function () {
// Act
var assertionGraph = iD.actionDetachNode('a')(graph);
- var nodes = assertionGraph.entity('w').nodes;
+ var nodes = assertionGraph.entity('-').nodes;
// Confirm that the target is no longer "a"
expect(nodes[0]).not.to.eql('a');
// and that the tags are not present
@@ -100,7 +102,7 @@ describe('iD.actionDetachNode', function () {
var assertionGraph = iD.actionDetachNode('b')(graph);
// Confirm that the way still has 4 nodes
- var target = assertionGraph.entity('w');
+ var target = assertionGraph.entity('-');
expect(target.nodes.length).to.eql(4);
});
@@ -109,11 +111,11 @@ describe('iD.actionDetachNode', function () {
var assertionGraph = iD.actionDetachNode('b')(graph);
// Confirm that the way is ordered correctly
- var target = assertionGraph.entity('w');
+ var target = assertionGraph.entity('-');
// Note that we can't be sure of the id of the replacement node
// so we only assert the nodes we know the ids for
// As we have already confirmed the size of the array we can assume
- // that the replacement node is in the correct posisiton by a process of elimination
+ // that the replacement node is in the correct posisiton by a process of elimination
expect(target.nodes[0]).to.eql('a');
expect(target.nodes[2]).to.eql('c');
expect(target.nodes[3]).to.eql('d');
@@ -124,7 +126,7 @@ describe('iD.actionDetachNode', function () {
var assertionGraph = iD.actionDetachNode('b')(graph);
// Confirm that the nodes have not moved, including the replacement node
- var nodes = assertionGraph.entity('w').nodes;
+ var nodes = assertionGraph.entity('-').nodes;
expect(assertionGraph.entity(nodes[0]).loc).to.eql([0, 0]);
expect(assertionGraph.entity(nodes[1]).loc).to.eql([1, 0]);
expect(assertionGraph.entity(nodes[2]).loc).to.eql([2, 0]);
@@ -135,7 +137,7 @@ describe('iD.actionDetachNode', function () {
// Act
var assertionGraph = iD.actionDetachNode('b')(graph);
- var nodes = assertionGraph.entity('w').nodes;
+ var nodes = assertionGraph.entity('-').nodes;
// Confirm that the target is no longer "a"
expect(nodes[1]).not.to.eql('b');
// and that the tags are not present
@@ -158,19 +160,22 @@ describe('iD.actionDetachNode', function () {
});
});
});
+
+
describe('closed way', function () {
var graph;
beforeEach(function () {
- // Set up a closed way
- // a-b (0,0)-(1,0)
- // | |
- // d-c (0,1)-(1,1)
- graph = iD.Graph([
- iD.Node({ id: 'a', loc: [0, 0] }),
- iD.Node({ id: 'b', loc: [1, 0] }),
- iD.Node({ id: 'c', loc: [1, 1] }),
- iD.Node({ id: 'd', loc: [0, 1] }),
- iD.Way({ id: 'w', nodes: ['a', 'b', 'c', 'd', 'a'] })
+ //
+ // d -- c
+ // | |
+ // a -- b
+ //
+ graph = iD.coreGraph([
+ iD.osmNode({ id: 'a', loc: [0, 0] }),
+ iD.osmNode({ id: 'b', loc: [1, 0] }),
+ iD.osmNode({ id: 'c', loc: [1, 1] }),
+ iD.osmNode({ id: 'd', loc: [0, 1] }),
+ iD.osmWay({ id: '-', nodes: ['a', 'b', 'c', 'd', 'a'] })
]);
});
@@ -186,7 +191,7 @@ describe('iD.actionDetachNode', function () {
var assertionGraph = iD.actionDetachNode('a')(graph);
// Confirm that the way still has 5 nodes
- var target = assertionGraph.entity('w');
+ var target = assertionGraph.entity('-');
expect(target.nodes.length).to.eql(5);
});
@@ -195,11 +200,11 @@ describe('iD.actionDetachNode', function () {
var assertionGraph = iD.actionDetachNode('a')(graph);
// Confirm that the way is ordered correctly
- var target = assertionGraph.entity('w');
+ var target = assertionGraph.entity('-');
// Note that we can't be sure of the id of the replacement node
// so we only assert the nodes we know the ids for
// As we have already confirmed the size of the array we can assume
- // that the replacement node is in the correct posisiton by a process of elimination
+ // that the replacement node is in the correct posisiton by a process of elimination
expect(target.nodes[1]).to.eql('b');
expect(target.nodes[2]).to.eql('c');
expect(target.nodes[3]).to.eql('d');
@@ -212,7 +217,7 @@ describe('iD.actionDetachNode', function () {
var assertionGraph = iD.actionDetachNode('a')(graph);
// Confirm that the nodes have not moved, including the replacement node
- var nodes = assertionGraph.entity('w').nodes;
+ var nodes = assertionGraph.entity('-').nodes;
expect(assertionGraph.entity(nodes[0]).loc).to.eql([0, 0]);
expect(assertionGraph.entity(nodes[1]).loc).to.eql([1, 0]);
expect(assertionGraph.entity(nodes[2]).loc).to.eql([1, 1]);
@@ -224,7 +229,7 @@ describe('iD.actionDetachNode', function () {
// Act
var assertionGraph = iD.actionDetachNode('a')(graph);
- var nodes = assertionGraph.entity('w').nodes;
+ var nodes = assertionGraph.entity('-').nodes;
// Confirm that the target is no longer "a"
expect(nodes[0]).not.to.eql('a');
// .. also in the tail position
@@ -261,7 +266,7 @@ describe('iD.actionDetachNode', function () {
var assertionGraph = iD.actionDetachNode('b')(graph);
// Confirm that the way still has 5 nodes
- var target = assertionGraph.entity('w');
+ var target = assertionGraph.entity('-');
expect(target.nodes.length).to.eql(5);
});
@@ -270,11 +275,11 @@ describe('iD.actionDetachNode', function () {
var assertionGraph = iD.actionDetachNode('b')(graph);
// Confirm that the way is ordered correctly
- var target = assertionGraph.entity('w');
+ var target = assertionGraph.entity('-');
// Note that we can't be sure of the id of the replacement node
// so we only assert the nodes we know the ids for
// As we have already confirmed the size of the array we can assume
- // that the replacement node is in the correct posisiton by a process of elimination
+ // that the replacement node is in the correct posisiton by a process of elimination
expect(target.nodes[0]).to.eql('a');
expect(target.nodes[2]).to.eql('c');
expect(target.nodes[3]).to.eql('d');
@@ -286,7 +291,7 @@ describe('iD.actionDetachNode', function () {
var assertionGraph = iD.actionDetachNode('b')(graph);
// Confirm that the nodes have not moved, including the replacement node
- var nodes = assertionGraph.entity('w').nodes;
+ var nodes = assertionGraph.entity('-').nodes;
expect(assertionGraph.entity(nodes[0]).loc).to.eql([0, 0]);
expect(assertionGraph.entity(nodes[1]).loc).to.eql([1, 0]);
expect(assertionGraph.entity(nodes[2]).loc).to.eql([1, 1]);
@@ -298,7 +303,7 @@ describe('iD.actionDetachNode', function () {
// Act
var assertionGraph = iD.actionDetachNode('b')(graph);
- var nodes = assertionGraph.entity('w').nodes;
+ var nodes = assertionGraph.entity('-').nodes;
// Confirm that the target is no longer "a"
expect(nodes[1]).not.to.eql('b');
// and that the tags are not present
@@ -321,23 +326,29 @@ describe('iD.actionDetachNode', function () {
});
});
});
- describe('intersecting simple ways', function () {
+
+
+ describe('intersecting linear ways', function () {
var graph;
beforeEach(function () {
- // Set up two simple ways
- // a-b-c-d (0,0)-(1,0)-(2,0)-(3,0)
- // e (2,1)
- // f (2,2)
+ //
+ // f
+ // ‖
+ // e
+ // ‖
+ // a -- b -- c -- d
+ //
// Node c represents the target
- graph = iD.Graph([
- iD.Node({ id: 'a', loc: [0, 0] }),
- iD.Node({ id: 'b', loc: [1, 0] }),
- iD.Node({ id: 'c', loc: [2, 0], tags: tags }),
- iD.Node({ id: 'd', loc: [3, 0] }),
- iD.Node({ id: 'e', loc: [2, 1] }),
- iD.Node({ id: 'f', loc: [2, 2] }),
- iD.Way({ id: 'w', nodes: ['a', 'b', 'c', 'd'] }),
- iD.Way({ id: 'x', nodes: ['c', 'e', 'f'] })
+ //
+ graph = iD.coreGraph([
+ iD.osmNode({ id: 'a', loc: [0, 0] }),
+ iD.osmNode({ id: 'b', loc: [1, 0] }),
+ iD.osmNode({ id: 'c', loc: [2, 0], tags: tags }),
+ iD.osmNode({ id: 'd', loc: [3, 0] }),
+ iD.osmNode({ id: 'e', loc: [2, 1] }),
+ iD.osmNode({ id: 'f', loc: [2, 2] }),
+ iD.osmWay({ id: '-', nodes: ['a', 'b', 'c', 'd'] }),
+ iD.osmWay({ id: '=', nodes: ['c', 'e', 'f'] })
]);
});
@@ -346,10 +357,10 @@ describe('iD.actionDetachNode', function () {
var assertionGraph = iD.actionDetachNode('c')(graph);
// Confirm that the way still has 4 nodes
- var target = assertionGraph.entity('w');
+ var target = assertionGraph.entity('-');
expect(target.nodes.length).to.eql(4);
// .. and second way has 3
- target = assertionGraph.entity('x');
+ target = assertionGraph.entity('=');
expect(target.nodes.length).to.eql(3);
});
@@ -358,16 +369,16 @@ describe('iD.actionDetachNode', function () {
var assertionGraph = iD.actionDetachNode('c')(graph);
// Confirm that the way is ordered correctly
- var target = assertionGraph.entity('w');
+ var target = assertionGraph.entity('-');
// Note that we can't be sure of the id of the replacement node
// so we only assert the nodes we know the ids for
// As we have already confirmed the size of the array we can assume
- // that the replacement node is in the correct posisiton by a process of elimination
+ // that the replacement node is in the correct posisiton by a process of elimination
expect(target.nodes[0]).to.eql('a');
expect(target.nodes[1]).to.eql('b');
expect(target.nodes[3]).to.eql('d');
// and second way
- target = assertionGraph.entity('x');
+ target = assertionGraph.entity('=');
expect(target.nodes[1]).to.eql('e');
expect(target.nodes[2]).to.eql('f');
});
@@ -377,13 +388,13 @@ describe('iD.actionDetachNode', function () {
var assertionGraph = iD.actionDetachNode('c')(graph);
// Confirm that the nodes have not moved, including the replacement node
- var nodes = assertionGraph.entity('w').nodes;
+ var nodes = assertionGraph.entity('-').nodes;
expect(assertionGraph.entity(nodes[0]).loc).to.eql([0, 0]);
expect(assertionGraph.entity(nodes[1]).loc).to.eql([1, 0]);
expect(assertionGraph.entity(nodes[2]).loc).to.eql([2, 0]);
expect(assertionGraph.entity(nodes[3]).loc).to.eql([3, 0]);
// and second way
- nodes = assertionGraph.entity('x').nodes;
+ nodes = assertionGraph.entity('=').nodes;
expect(assertionGraph.entity(nodes[0]).loc).to.eql([2, 0]);
expect(assertionGraph.entity(nodes[1]).loc).to.eql([2, 1]);
expect(assertionGraph.entity(nodes[2]).loc).to.eql([2, 2]);
@@ -393,20 +404,20 @@ describe('iD.actionDetachNode', function () {
// Act
var assertionGraph = iD.actionDetachNode('c')(graph);
// Confirm both ways have the same replacement node
- expect(assertionGraph.entity('w').nodes[2]).to.eql(assertionGraph.entity('x').nodes[0]);
+ expect(assertionGraph.entity('-').nodes[2]).to.eql(assertionGraph.entity('=').nodes[0]);
});
it('does replace target node', function () {
// Act
var assertionGraph = iD.actionDetachNode('c')(graph);
- var nodes = assertionGraph.entity('w').nodes;
+ var nodes = assertionGraph.entity('-').nodes;
// Confirm that the target is no longer "c"
expect(nodes[2]).not.to.eql('c');
// and that the tags are not present
expect(assertionGraph.entity(nodes[2]).tags).to.eql({});
// Confirm that the second way's first node is the same
- expect(assertionGraph.entity('x').nodes[0]).to.eql(nodes[2]);
+ expect(assertionGraph.entity('=').nodes[0]).to.eql(nodes[2]);
});
it('does detach target node', function () {
@@ -424,26 +435,30 @@ describe('iD.actionDetachNode', function () {
expect(assertionGraph.parentWays(targetNode)).to.eql([]);
});
});
+
+
describe('intersecting closed way', function () {
var graph;
beforeEach(function () {
- // Set up two intersecting closed ways
- // a-b (0,0)-(1,0)
- // | |
- // d-c-e (0,1)-(1,1)-(2,1)
- // | |
- // g f (0,2) - (1,2)
- // C is the target node
- graph = iD.Graph([
- iD.Node({ id: 'a', loc: [0, 0] }),
- iD.Node({ id: 'b', loc: [1, 0] }),
- iD.Node({ id: 'c', loc: [1, 1], tags: tags }),
- iD.Node({ id: 'd', loc: [0, 1] }),
- iD.Node({ id: 'e', loc: [2, 1] }),
- iD.Node({ id: 'f', loc: [1, 2] }),
- iD.Node({ id: 'g', loc: [0, 2] }),
- iD.Way({ id: 'w', nodes: ['a', 'b', 'c', 'd', 'a'] }),
- iD.Way({ id: 'x', nodes: ['c', 'e', 'f', 'g', 'c'] })
+ //
+ // g == f
+ // ‖ ‖
+ // d -- c == e
+ // | |
+ // a -- b
+ //
+ // c is the target node
+ //
+ graph = iD.coreGraph([
+ iD.osmNode({ id: 'a', loc: [0, 0] }),
+ iD.osmNode({ id: 'b', loc: [1, 0] }),
+ iD.osmNode({ id: 'c', loc: [1, 1], tags: tags }),
+ iD.osmNode({ id: 'd', loc: [0, 1] }),
+ iD.osmNode({ id: 'e', loc: [2, 1] }),
+ iD.osmNode({ id: 'f', loc: [2, 2] }),
+ iD.osmNode({ id: 'g', loc: [1, 2] }),
+ iD.osmWay({ id: '-', nodes: ['a', 'b', 'c', 'd', 'a'] }),
+ iD.osmWay({ id: '=', nodes: ['c', 'e', 'f', 'g', 'c'] })
]);
});
@@ -452,10 +467,10 @@ describe('iD.actionDetachNode', function () {
var assertionGraph = iD.actionDetachNode('c')(graph);
// Confirm that the way still has 5 nodes
- var target = assertionGraph.entity('w');
+ var target = assertionGraph.entity('-');
expect(target.nodes.length).to.eql(5);
// and the second
- target = assertionGraph.entity('x');
+ target = assertionGraph.entity('=');
expect(target.nodes.length).to.eql(5);
});
@@ -464,18 +479,18 @@ describe('iD.actionDetachNode', function () {
var assertionGraph = iD.actionDetachNode('c')(graph);
// Confirm that the way is ordered correctly
- var target = assertionGraph.entity('w');
+ var target = assertionGraph.entity('-');
// Note that we can't be sure of the id of the replacement node
// so we only assert the nodes we know the ids for
// As we have already confirmed the size of the array we can assume
- // that the replacement node is in the correct posisiton by a process of elimination
+ // that the replacement node is in the correct posisiton by a process of elimination
expect(target.nodes[0]).to.eql('a');
expect(target.nodes[1]).to.eql('b');
expect(target.nodes[3]).to.eql('d');
// Need to confirm that the id of the first & last node is the same so that the way remains closed
expect(target.nodes[0]).to.eql(target.nodes[4]);
// and the same for the other way
- target = assertionGraph.entity('x');
+ target = assertionGraph.entity('=');
expect(target.nodes[1]).to.eql('e');
expect(target.nodes[2]).to.eql('f');
expect(target.nodes[3]).to.eql('g');
@@ -487,32 +502,32 @@ describe('iD.actionDetachNode', function () {
var assertionGraph = iD.actionDetachNode('c')(graph);
// Confirm that the nodes have not moved, including the replacement node
- var nodes = assertionGraph.entity('w').nodes;
+ var nodes = assertionGraph.entity('-').nodes;
expect(assertionGraph.entity(nodes[0]).loc).to.eql([0, 0]);
expect(assertionGraph.entity(nodes[1]).loc).to.eql([1, 0]);
expect(assertionGraph.entity(nodes[2]).loc).to.eql([1, 1]);
expect(assertionGraph.entity(nodes[3]).loc).to.eql([0, 1]);
// We don't need to assert node[4] location as we've already confirmed that it is the same as node 0
// and the other way
- nodes = assertionGraph.entity('x').nodes;
+ nodes = assertionGraph.entity('=').nodes;
expect(assertionGraph.entity(nodes[0]).loc).to.eql([1, 1]);
expect(assertionGraph.entity(nodes[1]).loc).to.eql([2, 1]);
- expect(assertionGraph.entity(nodes[2]).loc).to.eql([1, 2]);
- expect(assertionGraph.entity(nodes[3]).loc).to.eql([0, 2]);
+ expect(assertionGraph.entity(nodes[2]).loc).to.eql([2, 2]);
+ expect(assertionGraph.entity(nodes[3]).loc).to.eql([1, 2]);
});
it('uses same replacement node at intersection', function () {
// Act
var assertionGraph = iD.actionDetachNode('c')(graph);
// Confirm both ways have the same replacement node
- expect(assertionGraph.entity('w').nodes[2]).to.eql(assertionGraph.entity('x').nodes[0]);
+ expect(assertionGraph.entity('-').nodes[2]).to.eql(assertionGraph.entity('=').nodes[0]);
});
it('does replace target node', function () {
// Act
var assertionGraph = iD.actionDetachNode('c')(graph);
- var nodes = assertionGraph.entity('w').nodes;
+ var nodes = assertionGraph.entity('-').nodes;
// Confirm that the target is no longer "c"
expect(nodes[0]).not.to.eql('c');
// .. also in the tail position
@@ -537,25 +552,24 @@ describe('iD.actionDetachNode', function () {
expect(assertionGraph.parentWays(targetNode)).to.eql([]);
});
});
+
+
describe('with relation', function () {
var graph;
beforeEach(function () {
- // Set up a simple way
- // a-b-c (0,0)-(1,0)-(2,0)
+ //
+ // a -- b -- c
+ //
// Node b represents the target
// With a relationship for the way including b
- graph = iD.Graph([
- iD.Node({ id: 'a', loc: [0, 0] }),
- iD.Node({ id: 'b', loc: [1, 0], tags: tags }),
- iD.Node({ id: 'c', loc: [2, 0] }),
- iD.Way({ id: 'w', nodes: ['a', 'b', 'c'] }),
- iD.Relation({
- id: 'r',
- tags: {
- type: 'route',
- route: 'foot'
- },
+ //
+ graph = iD.coreGraph([
+ iD.osmNode({ id: 'a', loc: [0, 0] }),
+ iD.osmNode({ id: 'b', loc: [1, 0], tags: tags }),
+ iD.osmNode({ id: 'c', loc: [2, 0] }),
+ iD.osmWay({ id: '-', nodes: ['a', 'b', 'c'] }),
+ iD.osmRelation({id: 'r', tags: {type: 'route', route: 'foot'},
members: [
{ id: 'a', type: 'node', role: 'point' },
{ id: 'b', type: 'node', role: 'point' },
@@ -577,7 +591,7 @@ describe('iD.actionDetachNode', function () {
var assertionGraph = iD.actionDetachNode('b')(graph);
// Find the new node
- var targetWay = assertionGraph.entity('w');
+ var targetWay = assertionGraph.entity('-');
var newNodeId = targetWay.nodes.filter(function (m) {
return m !== 'a' && m !== 'b' && m !== 'c';
})[0];
@@ -592,7 +606,7 @@ describe('iD.actionDetachNode', function () {
var assertionGraph = iD.actionDetachNode('b')(graph);
// Find the new node
- var targetWay = assertionGraph.entity('w');
+ var targetWay = assertionGraph.entity('-');
var newNodeId = targetWay.nodes.filter(function (m) {
return m !== 'a' && m !== 'b' && m !== 'c';
})[0];
@@ -607,4 +621,4 @@ describe('iD.actionDetachNode', function () {
});
});
-});
\ No newline at end of file
+});
diff --git a/test/spec/operations/detach_node.js b/test/spec/operations/detach_node.js
index 357d390fa..e79bf8349 100644
--- a/test/spec/operations/detach_node.js
+++ b/test/spec/operations/detach_node.js
@@ -2,13 +2,13 @@ describe('iD.operationDetachNode', function () {
var fakeContext;
var graph;
- // Some common setup functions
// Set up the fake context
fakeContext = {};
- fakeContext.graph = function () {
- return graph;
- };
+ fakeContext.graph = function () { return graph; };
+ fakeContext.hasHiddenConnections = function () { return false; };
+
var fakeTags = { 'name': 'fake' };
+
// Set up graph
var createFakeNode = function (id, hasTags) {
return hasTags
@@ -24,112 +24,108 @@ describe('iD.operationDetachNode', function () {
// d - node with no tags, 2 parent ways
// e - node with tags, no parent way
// f - node with no tags, no parent way
- graph = iD.Graph([
- iD.Node(createFakeNode('a', true)),
- iD.Node(createFakeNode('b', true)),
- iD.Node(createFakeNode('c', false)),
- iD.Node(createFakeNode('d', false)),
- iD.Node(createFakeNode('e', true)),
- iD.Node(createFakeNode('f', false)),
- iD.Way({ id: 'x', nodes: ['a', 'b', 'c', 'd'] }),
- iD.Way({ id: 'y', nodes: ['b', 'd'] })
+ graph = iD.coreGraph([
+ iD.osmNode(createFakeNode('a', true)),
+ iD.osmNode(createFakeNode('b', true)),
+ iD.osmNode(createFakeNode('c', false)),
+ iD.osmNode(createFakeNode('d', false)),
+ iD.osmNode(createFakeNode('e', true)),
+ iD.osmNode(createFakeNode('f', false)),
+ iD.osmWay({ id: 'x', nodes: ['a', 'b', 'c', 'd'] }),
+ iD.osmWay({ id: 'y', nodes: ['b', 'd'] })
]);
});
it('is not available for no selected ids', function () {
var result = iD.operationDetachNode([], fakeContext).available();
- expect(result).to.eql(false);
+ expect(result).to.be.not.ok;
});
it('is not available for two selected ids', function () {
var result = iD.operationDetachNode(['a', 'b'], fakeContext).available();
- expect(result).to.eql(false);
+ expect(result).to.be.not.ok;
});
- it('is not available for unkown selected id', function () {
+ it('is not available for unknown selected id', function () {
var result = iD.operationDetachNode(['z'], fakeContext).available();
- expect(result).to.eql(false);
+ expect(result).to.be.not.ok;
});
it('is not available for selected way', function () {
var result = iD.operationDetachNode(['x'], fakeContext).available();
- expect(result).to.eql(false);
+ expect(result).to.be.not.ok;
});
it('is not available for selected node with tags, no parent way', function () {
var result = iD.operationDetachNode(['e'], fakeContext).available();
- expect(result).to.eql(false);
+ expect(result).to.be.not.ok;
});
it('is not available for selected node with no tags, no parent way', function () {
var result = iD.operationDetachNode(['f'], fakeContext).available();
- expect(result).to.eql(false);
+ expect(result).to.be.not.ok;
});
it('is not available for selected node with no tags, parent way', function () {
var result = iD.operationDetachNode(['c'], fakeContext).available();
- expect(result).to.eql(false);
+ expect(result).to.be.not.ok;
});
it('is not available for selected node with no tags, two parent ways', function () {
var result = iD.operationDetachNode(['d'], fakeContext).available();
- expect(result).to.eql(false);
+ expect(result).to.be.not.ok;
});
it('is available for selected node with tags, parent way', function () {
var result = iD.operationDetachNode(['a'], fakeContext).available();
- expect(result).to.eql(true);
+ expect(result).to.be.ok;
});
it('is available for selected node with tags, two parent ways', function () {
var result = iD.operationDetachNode(['b'], fakeContext).available();
- expect(result).to.eql(true);
+ expect(result).to.be.ok;
});
});
+
describe('disabled', function () {
it('returns enabled for non-related node', function () {
- graph = iD.Graph([
- iD.Node(createFakeNode('a', false)),
- iD.Node(createFakeNode('b', true)),
- iD.Node(createFakeNode('c', false)),
- iD.Way({ id: 'x', nodes: ['a', 'b', 'c'] })
+ graph = iD.coreGraph([
+ iD.osmNode(createFakeNode('a', false)),
+ iD.osmNode(createFakeNode('b', true)),
+ iD.osmNode(createFakeNode('c', false)),
+ iD.osmWay({ id: 'x', nodes: ['a', 'b', 'c'] })
]);
var result = iD.operationDetachNode(['b'], fakeContext).disabled();
- expect(result).to.eql(false);
+ expect(result).to.be.not.ok;
});
it('returns enabled for non-restriction related node', function () {
- graph = iD.Graph([
- iD.Node(createFakeNode('a', false)),
- iD.Node(createFakeNode('b', true)),
- iD.Node(createFakeNode('c', false)),
- iD.Way({ id: 'x', nodes: ['a', 'b', 'c'] }),
- iD.Relation({ id: 'r', members: [{ id: 'b', role: 'label' }] })
+ graph = iD.coreGraph([
+ iD.osmNode(createFakeNode('a', false)),
+ iD.osmNode(createFakeNode('b', true)),
+ iD.osmNode(createFakeNode('c', false)),
+ iD.osmWay({ id: 'x', nodes: ['a', 'b', 'c'] }),
+ iD.osmRelation({ id: 'r', members: [{ id: 'b', role: 'label' }] })
]);
var result = iD.operationDetachNode(['b'], fakeContext).disabled();
- expect(result).to.eql(false);
+ expect(result).to.be.not.ok;
});
it('returns not-enabled for via node in restriction', function () {
// https://wiki.openstreetmap.org/wiki/Relation:restriction indicates that
// from & to roles are only appropriate for Ways
- graph = iD.Graph([
- iD.Node(createFakeNode('a', false)),
- iD.Node(createFakeNode('b', false)),
- iD.Node(createFakeNode('c', false)),
- iD.Node(createFakeNode('d', true)),
- iD.Node(createFakeNode('e', false)),
- iD.Node(createFakeNode('f', false)),
- iD.Node(createFakeNode('g', false)),
- iD.Way({ id: 'x', nodes: ['a', 'b', 'c'] }),
- iD.Way({ id: 'y', nodes: ['e', 'f', 'g'] }),
- iD.Relation({
- id: 'r',
- tags: {
- type: 'restriction',
- restriction: 'no_right_turn'
- },
+ graph = iD.coreGraph([
+ iD.osmNode(createFakeNode('a', false)),
+ iD.osmNode(createFakeNode('b', false)),
+ iD.osmNode(createFakeNode('c', false)),
+ iD.osmNode(createFakeNode('d', true)),
+ iD.osmNode(createFakeNode('e', false)),
+ iD.osmNode(createFakeNode('f', false)),
+ iD.osmNode(createFakeNode('g', false)),
+ iD.osmWay({ id: 'x', nodes: ['a', 'b', 'c'] }),
+ iD.osmWay({ id: 'y', nodes: ['e', 'f', 'g'] }),
+ iD.osmRelation({id: 'r', tags: {type: 'restriction', restriction: 'no_right_turn'},
members: [
{ id: 'x', type: 'way', role: 'from' },
{ id: 'd', type: 'node', role: 'via' },
@@ -138,63 +134,23 @@ describe('iD.operationDetachNode', function () {
})
]);
var result = iD.operationDetachNode(['d'], fakeContext).disabled();
- expect(result).not.to.eql(false);
- });
-
- it('returns not-enabled for via node in restriction and other non-restriction relation', function () {
- graph = iD.Graph([
- iD.Node(createFakeNode('a', false)),
- iD.Node(createFakeNode('b', false)),
- iD.Node(createFakeNode('c', false)),
- iD.Node(createFakeNode('d', true)),
- iD.Node(createFakeNode('e', false)),
- iD.Node(createFakeNode('f', false)),
- iD.Node(createFakeNode('g', false)),
- iD.Way({ id: 'x', nodes: ['a', 'b', 'c'] }),
- iD.Way({ id: 'y', nodes: ['e', 'f', 'g'] }),
- iD.Relation({
- id: 'r',
- tags: {
- type: 'restriction',
- restriction: 'no_right_turn'
- },
- members: [
- { id: 'x', type: 'way', role: 'from' },
- { id: 'd', type: 'node', role: 'via' },
- { id: 'z', type: 'way', role: 'to' }
- ]
- }),
- iD.Relation({
- id: 's',
- members: [
- { id: 'x', type: 'way' },
- { id: 'd', type: 'node' },
- ]
- })
- ]);
- var result = iD.operationDetachNode(['d'], fakeContext).disabled();
- expect(result).not.to.eql(false);
+ expect(result).to.eql('restriction');
});
it('returns not-enabled for location_hint node in restriction', function () {
// https://wiki.openstreetmap.org/wiki/Relation:restriction indicates that
// from & to roles are only appropriate for Ways
- graph = iD.Graph([
- iD.Node(createFakeNode('a', false)),
- iD.Node(createFakeNode('b', false)),
- iD.Node(createFakeNode('c', false)),
- iD.Node(createFakeNode('d', true)),
- iD.Node(createFakeNode('e', false)),
- iD.Node(createFakeNode('f', false)),
- iD.Node(createFakeNode('g', false)),
- iD.Way({ id: 'x', nodes: ['a', 'b'] }),
- iD.Way({ id: 'y', nodes: ['e', 'f', 'g'] }),
- iD.Relation({
- id: 'r',
- tags: {
- type: 'restriction',
- restriction: 'no_right_turn'
- },
+ graph = iD.coreGraph([
+ iD.osmNode(createFakeNode('a', false)),
+ iD.osmNode(createFakeNode('b', false)),
+ iD.osmNode(createFakeNode('c', false)),
+ iD.osmNode(createFakeNode('d', true)),
+ iD.osmNode(createFakeNode('e', false)),
+ iD.osmNode(createFakeNode('f', false)),
+ iD.osmNode(createFakeNode('g', false)),
+ iD.osmWay({ id: 'x', nodes: ['a', 'b'] }),
+ iD.osmWay({ id: 'y', nodes: ['e', 'f', 'g'] }),
+ iD.osmRelation({id: 'r', tags: {type: 'restriction', restriction: 'no_right_turn'},
members: [
{ id: 'x', type: 'way', role: 'from' },
{ id: 'c', type: 'node', role: 'via' },
@@ -204,43 +160,7 @@ describe('iD.operationDetachNode', function () {
})
]);
var result = iD.operationDetachNode(['d'], fakeContext).disabled();
- expect(result).not.to.eql(false);
- });
-
- it('returns not-enabled for location_hint node in restriction and other non-restriction relation', function () {
- graph = iD.Graph([
- iD.Node(createFakeNode('a', false)),
- iD.Node(createFakeNode('b', false)),
- iD.Node(createFakeNode('c', false)),
- iD.Node(createFakeNode('d', true)),
- iD.Node(createFakeNode('e', false)),
- iD.Node(createFakeNode('f', false)),
- iD.Node(createFakeNode('g', false)),
- iD.Way({ id: 'x', nodes: ['a', 'b'] }),
- iD.Way({ id: 'y', nodes: ['e', 'f', 'g'] }),
- iD.Relation({
- id: 'r',
- tags: {
- type: 'restriction',
- restriction: 'no_right_turn'
- },
- members: [
- { id: 'x', type: 'way', role: 'from' },
- { id: 'c', type: 'node', role: 'via' },
- { id: 'd', type: 'node', role: 'location_hint' },
- { id: 'z', type: 'way', role: 'to' }
- ]
- }),
- iD.Relation({
- id: 's',
- members: [
- { id: 'x', type: 'way' },
- { id: 'd', type: 'node' },
- ]
- })
- ]);
- var result = iD.operationDetachNode(['d'], fakeContext).disabled();
- expect(result).not.to.eql(false);
+ expect(result).to.eql('restriction');
});
});
});