diff --git a/css/app.css b/css/app.css
index 65f98dfd6..99904d953 100644
--- a/css/app.css
+++ b/css/app.css
@@ -1398,6 +1398,13 @@ a.success-action {
border-radius: 4px;
}
+.lasso-box {
+ fill-opacity:0.2;
+ fill: #bde5aa;
+ stroke: #000;
+ stroke-width: 1;
+}
+
/* Media Queries
------------------------------------------------------- */
@@ -1408,10 +1415,6 @@ a.success-action {
.save .label, .apply .label, .cancel .label { display: block;}
}
-
-
-
-
div.combobox {
width:155px;
z-index: 9999;
diff --git a/index.html b/index.html
index 9f4c23757..31f06870a 100644
--- a/index.html
+++ b/index.html
@@ -75,6 +75,7 @@
+
@@ -102,6 +103,7 @@
+
diff --git a/js/id/behavior/lasso.js b/js/id/behavior/lasso.js
new file mode 100644
index 000000000..08dda8cd3
--- /dev/null
+++ b/js/id/behavior/lasso.js
@@ -0,0 +1,57 @@
+iD.behavior.Lasso = function(context) {
+
+ var behavior = function(selection) {
+
+ var timeout = null,
+ // the position of the first mousedown
+ pos = null,
+ lasso;
+
+ function mousedown() {
+ if (d3.event.shiftKey === true) {
+
+ lasso = iD.ui.lasso().a(d3.mouse(context.surface().node()));
+
+ context.surface().call(lasso);
+
+ selection
+ .on('mousemove.lasso', mousemove)
+ .on('mouseup.lasso', mouseup);
+
+ d3.event.stopPropagation();
+ d3.event.preventDefault();
+
+ }
+ }
+
+ function mousemove() {
+ lasso.b(d3.mouse(context.surface().node()));
+ }
+
+ function mouseup() {
+ var extent = iD.geo.Extent(
+ context.projection.invert(lasso.a()),
+ context.projection.invert(lasso.b()));
+
+ lasso.close();
+
+ var selected = context.graph().intersects(extent);
+
+ if (selected.length) {
+ context.enter(iD.modes.Select(context, _.pluck(selected, 'id')));
+ }
+
+ selection
+ .on('mousemove.lasso', null);
+ }
+
+ selection
+ .on('mousedown.select', mousedown);
+ };
+
+ behavior.off = function(selection) {
+ selection.on('mousedown.lasso', null);
+ };
+
+ return behavior;
+};
diff --git a/js/id/modes/browse.js b/js/id/modes/browse.js
index 97f0d6935..5df94d69d 100644
--- a/js/id/modes/browse.js
+++ b/js/id/modes/browse.js
@@ -10,6 +10,7 @@ iD.modes.Browse = function(context) {
var behaviors = [
iD.behavior.Hover(),
iD.behavior.Select(context),
+ iD.behavior.Lasso(context),
iD.behavior.DragNode(context)];
mode.enter = function() {
diff --git a/js/id/ui/lasso.js b/js/id/ui/lasso.js
new file mode 100644
index 000000000..1ca079fda
--- /dev/null
+++ b/js/id/ui/lasso.js
@@ -0,0 +1,63 @@
+iD.ui.lasso = function() {
+
+ var center, box,
+ group,
+ a = [0, 0],
+ b = [0, 0];
+
+ function lasso(selection) {
+
+ group = selection.append('g')
+ .attr('class', 'lasso')
+ .attr('opacity', 0);
+
+ box = group.append('rect')
+ .attr('class', 'lasso-box');
+
+ group.transition()
+ .style('opacity', 1);
+
+ }
+
+ // top-left
+ function topLeft(d) {
+ return 'translate(' +
+ [Math.min(d[0][0], d[1][0]), Math.min(d[0][1], d[1][1])] + ')';
+ }
+
+ function width(d) { return Math.abs(d[0][0] - d[1][0]); }
+ function height(d) { return Math.abs(d[0][1] - d[1][1]); }
+
+ function draw() {
+ if (box) {
+ box.data([[a, b]])
+ .attr('transform', topLeft)
+ .attr('width', width)
+ .attr('height', height);
+ }
+ }
+
+ lasso.a = function(_) {
+ if (!arguments.length) return a;
+ a = _;
+ draw();
+ return lasso;
+ };
+
+ lasso.b = function(_) {
+ if (!arguments.length) return b;
+ b = _;
+ draw();
+ return lasso;
+ };
+
+ lasso.close = function(selection) {
+ if (group) {
+ group.transition()
+ .attr('opacity', 0)
+ .remove();
+ }
+ };
+
+ return lasso;
+};