diff --git a/index.html b/index.html
index 3c2924650..122569faa 100644
--- a/index.html
+++ b/index.html
@@ -43,6 +43,7 @@
+
diff --git a/js/id/id.js b/js/id/id.js
index 42e75e994..3248740cf 100644
--- a/js/id/id.js
+++ b/js/id/id.js
@@ -134,33 +134,8 @@ window.iD = function(container) {
return d[0] + ' icon';
});
- var gc = this.append('div').attr('class', 'geocode-control map-control');
- gc.append('button')
- .attr('class','narrow')
- .html("")
- .on('click', function() {
- d3.select(this).classed('active', gcForm.classed('hide'));
- gcForm.classed('hide', !gcForm.classed('hide'));
-
- if (!gcForm.classed('hide')) d3.select('.map-overlay input').node().focus();
- else map.surface.node().focus();
- });
- var gcForm = gc.append('form');
- gcForm.attr('class','content map-overlay hide')
- .append('input')
- .attr({
- type: 'text',
- placeholder: 'find a place'
- })
- .on('keydown', function () {
- if (d3.event.keyCode !== 13) return;
- d3.event.preventDefault();
- d3.json('http://api.tiles.mapbox.com/v3/mapbox/geocode/' +
- encodeURIComponent(this.value) + '.json', function(err, resp) {
- gc.select('button').on('click').apply(gc.select('button').node());
- map.center([resp.results[0][0].lon, resp.results[0][0].lat]);
- });
- });
+ var gc = this.append('div').attr('class', 'geocode-control map-control')
+ .call(iD.geocoder().map(map));
this.append('div').attr('class', 'map-control layerswitcher-control')
.call(iD.layerswitcher(map));
diff --git a/js/id/ui/geocoder.js b/js/id/ui/geocoder.js
new file mode 100644
index 000000000..e93eaa9dc
--- /dev/null
+++ b/js/id/ui/geocoder.js
@@ -0,0 +1,57 @@
+iD.geocoder = function() {
+
+ var map;
+
+ function geocoder(selection) {
+ function keydown() {
+ if (d3.event.keyCode !== 13) return;
+ d3.event.preventDefault();
+ d3.json('http://api.tiles.mapbox.com/v3/mapbox/geocode/' +
+ encodeURIComponent(this.value) + '.json', function(err, resp) {
+ hide();
+ map.center([resp.results[0][0].lon, resp.results[0][0].lat]);
+ });
+ }
+
+ function clickoutside(selection) {
+ selection
+ .on('click.geocoder-inside', function() {
+ return d3.event.stopPropagation();
+ });
+ d3.select('body').on('click.geocoder-outside', hide);
+ }
+
+ function show() { setVisible(true); }
+ function hide() { setVisible(false); }
+ function toggle() { setVisible(gcForm.classed('hide')); }
+
+ function setVisible(show) {
+ button.classed('active', show);
+ gcForm.classed('hide', !show);
+ if (show) d3.select('.map-overlay input').node().focus();
+ else map.surface.node().focus();
+ }
+
+ var button = selection.append('button')
+ .attr('class','narrow')
+ .html('')
+ .on('click', toggle);
+
+ var gcForm = selection.append('form');
+
+ gcForm.attr('class','content map-overlay hide')
+ .append('input')
+ .attr({ type: 'text', placeholder: 'find a place' })
+ .on('keydown', keydown);
+
+ selection.call(clickoutside);
+ }
+
+ geocoder.map = function(_) {
+ if (!arguments.length) return map;
+ map = _;
+ return geocoder;
+ };
+
+ return geocoder;
+};
diff --git a/js/id/ui/layerswitcher.js b/js/id/ui/layerswitcher.js
index 5f40b3fff..18f802e1f 100644
--- a/js/id/ui/layerswitcher.js
+++ b/js/id/ui/layerswitcher.js
@@ -20,37 +20,29 @@ iD.layerswitcher = function(map) {
var content = selection
.append('div').attr('class', 'content map-overlay hide');
- var toggle = selection
+ var button = selection
.append('button')
.attr('class', 'narrow')
.html("")
- .on('click.toggle', function() {
- d3.select(this)
- .classed('active', function() {
- return content.classed('hide');
- });
- content.classed('hide', function() {
- if (content.classed('hide')) clickoutside(selection);
- else {
- d3.select('body').on('click.outside', null);
- selection.on('click.inside', null);
- }
- return !content.classed('hide');
- });
- });
+ .on('click.layerswitcher-toggle', toggle);
- function clickoutside(selection) {
- selection
- .on('click.inside', function() {
- return d3.event.stopPropagation();
- });
- d3.select('body')
- .on('click.outside', function() {
- toggle.on('click.toggle').apply(toggle.node(), d3.event);
- });
+ function show() { setVisible(true); }
+ function hide() { setVisible(false); }
+ function toggle() { setVisible(content.classed('hide')); }
+
+ function setVisible(show) {
+ button.classed('active', show);
+ content.classed('hide', !show);
}
- opa = content
+ function clickoutside(selection) {
+ selection.on('click.layerswitcher-inside', function() {
+ return d3.event.stopPropagation();
+ });
+ d3.select('body').on('click.layerswitcher-outside', hide);
+ }
+
+ var opa = content
.append('div')
.attr('class', 'opacity-options-wrapper fillL2')
.html("Layers")
@@ -116,6 +108,8 @@ iD.layerswitcher = function(map) {
.insert('span')
.attr('class','icon toggle');
+
+ selection.call(clickoutside);
selectLayer(map.background.source());
}
diff --git a/test/index.html b/test/index.html
index 85d99fc74..7e5abcda2 100644
--- a/test/index.html
+++ b/test/index.html
@@ -42,6 +42,7 @@
+
@@ -107,6 +108,7 @@
+
diff --git a/test/spec/ui/geocoder.js b/test/spec/ui/geocoder.js
new file mode 100644
index 000000000..7a2ad5f89
--- /dev/null
+++ b/test/spec/ui/geocoder.js
@@ -0,0 +1,6 @@
+describe("Geocoder", function () {
+ it('can be instantiated', function () {
+ var geocoder = iD.geocoder();
+ expect(geocoder).to.be.ok;
+ });
+});