diff --git a/js/id/behavior/draw_way.js b/js/id/behavior/draw_way.js index ba28fe14b..1cc62b755 100644 --- a/js/id/behavior/draw_way.js +++ b/js/id/behavior/draw_way.js @@ -139,7 +139,7 @@ iD.behavior.DrawWay = function(wayId, index, mode, baseGraph) { var way = history.graph().entity(wayId); if (way) { - controller.enter(iD.modes.Select(way, true)); + controller.enter(iD.modes.Select([way.id], true)); } else { controller.enter(iD.modes.Browse()); } diff --git a/js/id/modes/add_point.js b/js/id/modes/add_point.js index 61ff51424..feff66f43 100644 --- a/js/id/modes/add_point.js +++ b/js/id/modes/add_point.js @@ -20,7 +20,7 @@ iD.modes.AddPoint = function() { iD.actions.AddEntity(node), t('operations.add.annotation.point')); - controller.enter(iD.modes.Select(node, true)); + controller.enter(iD.modes.Select([node.id], true)); } function addWay(way, loc, index) { diff --git a/js/id/modes/browse.js b/js/id/modes/browse.js index c7358bdea..b1fb1c8ae 100644 --- a/js/id/modes/browse.js +++ b/js/id/modes/browse.js @@ -24,7 +24,7 @@ iD.modes.Browse = function() { surface.on('click.browse', function () { var datum = d3.select(d3.event.target).datum(); if (datum instanceof iD.Entity) { - mode.controller.enter(iD.modes.Select(datum)); + mode.controller.enter(iD.modes.Select([datum.id])); } }); }; diff --git a/js/id/modes/move_way.js b/js/id/modes/move_way.js index d977f6719..50c228d8e 100644 --- a/js/id/modes/move_way.js +++ b/js/id/modes/move_way.js @@ -34,12 +34,12 @@ iD.modes.MoveWay = function(wayId) { function finish() { d3.event.stopPropagation(); - controller.enter(iD.modes.Select(way, true)); + controller.enter(iD.modes.Select([way.id], true)); } function cancel() { history.pop(); - controller.enter(iD.modes.Select(way, true)); + controller.enter(iD.modes.Select([way.id], true)); } function undone() { diff --git a/js/id/modes/select.js b/js/id/modes/select.js index 83e0d72b8..37b2dee1d 100644 --- a/js/id/modes/select.js +++ b/js/id/modes/select.js @@ -1,8 +1,7 @@ -iD.modes.Select = function(entity, initial) { +iD.modes.Select = function(selection, initial) { var mode = { id: 'select', - button: 'browse', - entity: entity + button: 'browse' }; var inspector = iD.ui.inspector().initial(!!initial), @@ -11,20 +10,29 @@ iD.modes.Select = function(entity, initial) { radialMenu; function changeTags(d, tags) { - if (!_.isEqual(entity.tags, tags)) { + if (!_.isEqual(singular().tags, tags)) { mode.history.perform( iD.actions.ChangeTags(d.id, tags), t('operations.change_tags.annotation')); } } + function singular() { + if (selection.length === 1) { + return mode.map.history().graph().entity(selection[0]); + } + } + + mode.selection = function() { + return selection; + }; + mode.enter = function() { var map = mode.map, - graph = map.history().graph(), history = map.history(), - surface = mode.map.surface; - - inspector.graph(graph); + graph = history.graph(), + surface = map.surface, + entity = singular(); behaviors = [ iD.behavior.Hover(), @@ -36,7 +44,7 @@ iD.modes.Select = function(entity, initial) { }); var operations = d3.values(iD.operations) - .map(function (o) { return o(entity.id, mode); }) + .map(function (o) { return o(selection, mode); }) .filter(function (o) { return o.available(); }); operations.forEach(function(operation) { @@ -49,46 +57,51 @@ iD.modes.Select = function(entity, initial) { var q = iD.util.stringQs(location.hash.substring(1)); location.replace('#' + iD.util.qsString(_.assign(q, { - id: entity.id + id: selection.join(',') }), true)); - d3.select('.inspector-wrap') - .style('display', 'block') - .style('opacity', 1) - .datum(entity) - .call(inspector); + if (entity) { + inspector.graph(graph); - if (d3.event) { - // Pan the map if the clicked feature intersects with the position - // of the inspector - var inspector_size = d3.select('.inspector-wrap').size(), - map_size = mode.map.size(), - offset = 50, - shift_left = d3.event.x - map_size[0] + inspector_size[0] + offset, - center = (map_size[0] / 2) + shift_left + offset; + d3.select('.inspector-wrap') + .style('display', 'block') + .style('opacity', 1) + .datum(entity) + .call(inspector); - if (shift_left > 0 && inspector_size[1] > d3.event.y) { - mode.map.centerEase(mode.map.projection.invert([center, map_size[1]/2])); + if (d3.event) { + // Pan the map if the clicked feature intersects with the position + // of the inspector + var inspector_size = d3.select('.inspector-wrap').size(), + map_size = mode.map.size(), + offset = 50, + shift_left = d3.event.x - map_size[0] + inspector_size[0] + offset, + center = (map_size[0] / 2) + shift_left + offset; + + if (shift_left > 0 && inspector_size[1] > d3.event.y) { + mode.map.centerEase(mode.map.projection.invert([center, map_size[1]/2])); + } } + + inspector + .on('changeTags', changeTags) + .on('close', function() { mode.controller.exit(); }); + + history.on('change.select', function() { + // Exit mode if selected entity gets undone + var oldEntity = entity, + newEntity = history.graph().entity(selection[0]); + + if (!newEntity) { + mode.controller.enter(iD.modes.Browse()); + } else if (!_.isEqual(oldEntity.tags, newEntity.tags)) { + inspector.tags(newEntity.tags); + } + + surface.call(radialMenu.close); + }); } - inspector - .on('changeTags', changeTags) - .on('close', function() { mode.controller.exit(); }); - - history.on('change.select', function() { - // Exit mode if selected entity gets undone - var old = entity; - entity = history.graph().entity(entity.id); - if (!entity) { - mode.controller.enter(iD.modes.Browse()); - } else if(!_.isEqual(entity.tags, old.tags)) { - inspector.tags(entity.tags); - } - - surface.call(radialMenu.close); - }); - map.on('move.select', function() { surface.call(radialMenu.close); }); @@ -96,17 +109,17 @@ iD.modes.Select = function(entity, initial) { function click() { var datum = d3.select(d3.event.target).datum(); if (datum instanceof iD.Entity) { - mode.controller.enter(iD.modes.Select(datum)); + mode.controller.enter(iD.modes.Select([datum.id])); } else { mode.controller.enter(iD.modes.Browse()); } } function dblclick() { - var selection = d3.select(d3.event.target), - datum = selection.datum(); + var target = d3.select(d3.event.target), + datum = target.datum(); - if (datum instanceof iD.Way && !selection.classed('fill')) { + if (datum instanceof iD.Way && !target.classed('fill')) { var choice = iD.geo.chooseIndex(datum, d3.mouse(mode.map.surface.node()), mode.map), node = iD.Node({ loc: choice.loc }); @@ -128,9 +141,7 @@ iD.modes.Select = function(entity, initial) { .call(keybinding); surface.selectAll("*") - .filter(function (d) { - return d && entity && d.id === entity.id; - }) + .filter(function (d) { return d && selection.indexOf(d.id) >= 0; }) .classed('selected', true); radialMenu = iD.ui.RadialMenu(operations); @@ -138,7 +149,7 @@ iD.modes.Select = function(entity, initial) { if (d3.event && !initial) { var loc = map.mouseCoordinates(); - if (entity.type === 'node') { + if (entity && entity.type === 'node') { loc = entity.loc; } @@ -150,8 +161,8 @@ iD.modes.Select = function(entity, initial) { var surface = mode.map.surface, history = mode.history; - if (entity) { - changeTags(entity, inspector.tags()); + if (singular()) { + changeTags(singular(), inspector.tags()); } d3.select('.inspector-wrap') diff --git a/js/id/operations/circularize.js b/js/id/operations/circularize.js index 91921dc2a..4b0f27bf9 100644 --- a/js/id/operations/circularize.js +++ b/js/id/operations/circularize.js @@ -1,5 +1,6 @@ -iD.operations.Circularize = function(entityId, mode) { - var history = mode.map.history(), +iD.operations.Circularize = function(selection, mode) { + var entityId = selection[0], + history = mode.map.history(), action = iD.actions.Circularize(entityId, mode.map); var operation = function() { @@ -13,7 +14,7 @@ iD.operations.Circularize = function(entityId, mode) { operation.available = function() { var graph = history.graph(), entity = graph.entity(entityId); - return entity.type === 'way'; + return selection.length === 1 && entity.type === 'way'; }; operation.enabled = function() { diff --git a/js/id/operations/delete.js b/js/id/operations/delete.js index 993d3eef4..12df22a09 100644 --- a/js/id/operations/delete.js +++ b/js/id/operations/delete.js @@ -1,5 +1,6 @@ -iD.operations.Delete = function(entityId, mode) { - var history = mode.map.history(); +iD.operations.Delete = function(selection, mode) { + var entityId = selection[0], + history = mode.map.history(); var operation = function() { var graph = history.graph(), @@ -15,7 +16,8 @@ iD.operations.Delete = function(entityId, mode) { operation.available = function() { var graph = history.graph(), entity = graph.entity(entityId); - return entity.type === 'way' || entity.type === 'node'; + return selection.length === 1 && + (entity.type === 'way' || entity.type === 'node'); }; operation.enabled = function() { diff --git a/js/id/operations/move.js b/js/id/operations/move.js index 5034c2f75..10405cc53 100644 --- a/js/id/operations/move.js +++ b/js/id/operations/move.js @@ -1,5 +1,6 @@ -iD.operations.Move = function(entityId, mode) { - var history = mode.map.history(); +iD.operations.Move = function(selection, mode) { + var entityId = selection[0], + history = mode.map.history(); var operation = function() { mode.controller.enter(iD.modes.MoveWay(entityId)); @@ -7,7 +8,8 @@ iD.operations.Move = function(entityId, mode) { operation.available = function() { var graph = history.graph(); - return graph.entity(entityId).type === 'way'; + return selection.length === 1 && + graph.entity(entityId).type === 'way'; }; operation.enabled = function() { diff --git a/js/id/operations/reverse.js b/js/id/operations/reverse.js index ca9dd6440..78b8789dd 100644 --- a/js/id/operations/reverse.js +++ b/js/id/operations/reverse.js @@ -1,5 +1,6 @@ -iD.operations.Reverse = function(entityId, mode) { - var history = mode.map.history(); +iD.operations.Reverse = function(selection, mode) { + var entityId = selection[0], + history = mode.map.history(); var operation = function() { history.perform( @@ -10,7 +11,8 @@ iD.operations.Reverse = function(entityId, mode) { operation.available = function() { var graph = history.graph(), entity = graph.entity(entityId); - return entity.geometry(graph) === 'line'; + return selection.length === 1 && + entity.geometry(graph) === 'line'; }; operation.enabled = function() { diff --git a/js/id/operations/split.js b/js/id/operations/split.js index ff3f8af20..4274fc41b 100644 --- a/js/id/operations/split.js +++ b/js/id/operations/split.js @@ -1,5 +1,6 @@ -iD.operations.Split = function(entityId, mode) { - var history = mode.map.history(), +iD.operations.Split = function(selection, mode) { + var entityId = selection[0], + history = mode.map.history(), action = iD.actions.SplitWay(entityId); var operation = function() { @@ -9,7 +10,8 @@ iD.operations.Split = function(entityId, mode) { operation.available = function() { var graph = history.graph(), entity = graph.entity(entityId); - return entity.geometry(graph) === 'vertex'; + return selection.length === 1 && + entity.geometry(graph) === 'vertex'; }; operation.enabled = function() { diff --git a/js/id/operations/unjoin.js b/js/id/operations/unjoin.js index 175c13973..2a40ec067 100644 --- a/js/id/operations/unjoin.js +++ b/js/id/operations/unjoin.js @@ -1,5 +1,6 @@ -iD.operations.Unjoin = function(entityId, mode) { - var history = mode.map.history(), +iD.operations.Unjoin = function(selection, mode) { + var entityId = selection[0], + history = mode.map.history(), action = iD.actions.UnjoinNode(entityId); var operation = function() { @@ -9,7 +10,8 @@ iD.operations.Unjoin = function(entityId, mode) { operation.available = function() { var graph = history.graph(), entity = graph.entity(entityId); - return entity.geometry(graph) === 'vertex'; + return selection.length === 1 && + entity.geometry(graph) === 'vertex'; }; operation.enabled = function() { diff --git a/js/id/renderer/hash.js b/js/id/renderer/hash.js index 384705543..f4204531f 100644 --- a/js/id/renderer/hash.js +++ b/js/id/renderer/hash.js @@ -49,7 +49,7 @@ iD.Hash = function() { var entity = map.history().graph().entity(id); if (entity === undefined) return; else selectoff(); - controller.enter(iD.modes.Select(entity)); + controller.enter(iD.modes.Select([entity.id])); map.on('drawn.hash', null); }); controller.on('enter.hash', function() { diff --git a/js/id/ui/save.js b/js/id/ui/save.js index 8784b2ca4..b8085e21a 100644 --- a/js/id/ui/save.js +++ b/js/id/ui/save.js @@ -60,7 +60,7 @@ iD.ui.save = function() { .on('fix', function(d) { map.extent(d.entity.extent(map.history().graph())); if (map.zoom() > 19) map.zoom(19); - controller.enter(iD.modes.Select(d.entity)); + controller.enter(iD.modes.Select([d.entity.id])); modal.remove(); }) .on('save', commit)); diff --git a/test/spec/modes/add_point.js b/test/spec/modes/add_point.js index 68fe80403..a284cf682 100644 --- a/test/spec/modes/add_point.js +++ b/test/spec/modes/add_point.js @@ -28,7 +28,7 @@ describe("iD.modes.AddPoint", function () { it("selects the node", function () { happen.click(map.surface.node(), {}); expect(controller.mode.id).to.equal('select'); - expect(controller.mode.entity).to.equal(history.changes().created[0]); + expect(controller.mode.selection()).to.eql([history.changes().created[0].id]); }); });