diff --git a/css/app.css b/css/app.css
index 62b5afaea..cf466dada 100644
--- a/css/app.css
+++ b/css/app.css
@@ -756,6 +756,7 @@ a:hover .icon.out-link { background-position: -500px -14px;}
width:100%;
}
+.geocode-item,
.feature-list-item {
width: 100%;
position: relative;
@@ -1949,48 +1950,6 @@ img.wiki-image {
display:inline-block;
}
-/* Geocoder */
-
-.geocode-control {
- position: relative;
-}
-
-.geocode-control form {
- position: absolute;
- top: 0;
- padding: 5px;
-}
-
-.geocode-control input {
- width: 100%;
-}
-
-.geocode-control div.map-overlay {
- position: absolute;
- top: 40px;
- left: -260px;
- border-top: 1px solid #CCC;
- z-index: 100;
- max-height: 240px;
- overflow-y: auto;
- padding: 0;
-}
-
-.geocode-control div.map-overlay span {
- display: inline-block;
- border-bottom: 1px solid #CCC;
- padding: 5px 10px;
- width: 100%;
-}
-.geocode-control div.map-overlay span.not-found {
- line-height: 28px;
- width: 100%;
-}
-
-.geocode-control a:focus {
- text-decoration: underline;
-}
-
/* Geolocator */
.geolocate-control {
diff --git a/data/core.yaml b/data/core.yaml
index 377d3f806..a530e3f57 100644
--- a/data/core.yaml
+++ b/data/core.yaml
@@ -164,9 +164,8 @@ en:
list: "Edits by {users}"
truncated_list: "Edits by {users} and {count} others"
geocoder:
- title: Find a place
- placeholder: Find a place
- no_results: "Couldn't locate a place named '{name}'"
+ search: Continue search on server...
+ no_results: No results found
geolocate:
title: Show My Location
inspector:
diff --git a/dist/locales/en.json b/dist/locales/en.json
index aa0587a9b..5c2f41949 100644
--- a/dist/locales/en.json
+++ b/dist/locales/en.json
@@ -205,9 +205,8 @@
"truncated_list": "Edits by {users} and {count} others"
},
"geocoder": {
- "title": "Find a place",
- "placeholder": "Find a place",
- "no_results": "Couldn't locate a place named '{name}'"
+ "search": "Continue search on server...",
+ "no_results": "No results found"
},
"geolocate": {
"title": "Show My Location"
diff --git a/index.html b/index.html
index de2f1f51a..6fc21254d 100644
--- a/index.html
+++ b/index.html
@@ -75,7 +75,6 @@
-
diff --git a/js/id/behavior/hash.js b/js/id/behavior/hash.js
index 8004287f4..7fb24eeb4 100644
--- a/js/id/behavior/hash.js
+++ b/js/id/behavior/hash.js
@@ -37,33 +37,6 @@ iD.behavior.Hash = function(context) {
}
}
- // the hash can declare that the map should select a feature, but it can
- // do so before any features are loaded. thus wait for the feature to
- // be loaded and then select
- function willselect(id, hasMap) {
- if (!hasMap) {
- context.connection().loadEntity(id, function(error, entity) {
- if (entity) {
- context.map().zoomTo(entity);
- }
- });
- }
-
- context.map().on('drawn.hash', function() {
- if (!context.hasEntity(id)) return;
- selectoff();
- context.enter(iD.modes.Select(context, [id]));
- });
-
- context.on('enter.hash', function() {
- if (context.mode().id !== 'browse') selectoff();
- });
- }
-
- function selectoff() {
- context.map().on('drawn.hash', null);
- }
-
function hash() {
context.map()
.on('move.hash', move);
@@ -73,7 +46,7 @@ iD.behavior.Hash = function(context) {
if (location.hash) {
var q = iD.util.stringQs(location.hash.substring(1));
- if (q.id) willselect(q.id, q.map);
+ if (q.id) context.loadEntity(q.id, !q.map);
hashchange();
if (q.map) hash.hadHash = true;
}
diff --git a/js/id/id.js b/js/id/id.js
index 851257329..301ef2dbb 100644
--- a/js/id/id.js
+++ b/js/id/id.js
@@ -119,6 +119,30 @@ window.iD = function () {
}
};
+ context.loadEntity = function(id, zoomTo) {
+ if (zoomTo !== false) {
+ connection.loadEntity(id, function(error, entity) {
+ if (entity) {
+ map.zoomTo(entity);
+ }
+ });
+ }
+
+ map.on('drawn.loadEntity', function() {
+ if (!context.hasEntity(id)) return;
+ map.on('drawn.loadEntity', null);
+ context.on('enter.loadEntity', null);
+ context.enter(iD.modes.Select(context, [id]));
+ });
+
+ context.on('enter.loadEntity', function() {
+ if (mode.id !== 'browse') {
+ map.on('drawn.loadEntity', null);
+ context.on('enter.loadEntity', null);
+ }
+ });
+ };
+
context.editable = function() {
return map.editable() && mode && mode.id !== 'save';
};
diff --git a/js/id/ui.js b/js/id/ui.js
index a908fa3ee..b647e7fc2 100644
--- a/js/id/ui.js
+++ b/js/id/ui.js
@@ -75,12 +75,6 @@ iD.ui = function(context) {
.attr('class', 'map-control background-control')
.call(iD.ui.Background(context));
- if (!context.embed()) {
- controls.append('div')
- .attr('class', 'map-control geocode-control')
- .call(iD.ui.Geocoder(context));
- }
-
controls.append('div')
.attr('class', 'map-control help-control')
.call(iD.ui.Help(context));
diff --git a/js/id/ui/feature_list.js b/js/id/ui/feature_list.js
index d25376171..b4c22c2f4 100644
--- a/js/id/ui/feature_list.js
+++ b/js/id/ui/feature_list.js
@@ -1,4 +1,6 @@
iD.ui.FeatureList = function(context) {
+ var geocodeResults;
+
function featureList(selection) {
var header = selection.append('div')
.attr('class', 'header fillL cf');
@@ -14,6 +16,7 @@ iD.ui.FeatureList = function(context) {
}
function inputevent() {
+ geocodeResults = undefined;
drawList();
}
@@ -61,9 +64,10 @@ iD.ui.FeatureList = function(context) {
var name = iD.util.displayName(entity) || '';
if (name.toLowerCase().indexOf(q) >= 0) {
result.push({
+ id: entity.id,
entity: entity,
geometry: context.geometry(entity.id),
- preset: context.presets().match(entity, graph),
+ type: context.presets().match(entity, graph).name(),
name: name
});
}
@@ -78,22 +82,56 @@ iD.ui.FeatureList = function(context) {
addEntity(visible[i].__data__);
}
+ (geocodeResults || []).forEach(function(d) {
+ result.push({
+ id: iD.Entity.id.fromOSM(d.osm_type, d.osm_id),
+ geometry: d.osm_type === 'relation' ? 'relation' : d.osm_type === 'way' ? 'line' : 'point',
+ type: (d.type.charAt(0).toUpperCase() + d.type.slice(1)).replace('_', ' '),
+ name: d.display_name,
+ extent: new iD.geo.Extent(
+ [parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])],
+ [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
+ })
+ });
+
return result;
}
function drawList() {
- list.classed('filtered', search.property('value').length);
+ var value = search.property('value');
+
+ list.classed('filtered', value.length);
+
+ var geocodeButton = list.selectAll('.geocode-item')
+ .data([0])
+ .enter().append('button')
+ .attr('class', 'geocode-item')
+ .on('click', geocode);
+
+ var label = geocodeButton.append('div')
+ .attr('class', 'label');
+
+ label.append('span')
+ .attr('class', 'entity-name');
+
+ var noResults = geocodeResults && geocodeResults.length === 0;
+
+ list.selectAll('.geocode-item')
+ .style('display', noResults || (value && geocodeResults === undefined) ? 'block' : 'none')
+ .property('disabled', noResults)
+ .selectAll('.entity-name')
+ .text(noResults ? t('geocoder.no_results') : t('geocoder.search'));
var items = list.selectAll('.feature-list-item')
- .data(features(), function(d) { return d.entity.id; });
+ .data(features(), function(d) { return d.id; });
- var enter = items.enter().append('button')
+ var enter = items.enter().insert('button', '.geocode-item')
.attr('class', 'feature-list-item')
- .on('mouseover', function(d) { mouseover(d.entity); })
- .on('mouseout', function(d) { mouseout(); })
- .on('click', function(d) { click(d.entity); });
+ .on('mouseover', mouseover)
+ .on('mouseout', mouseout)
+ .on('click', click);
- var label = enter.append('div')
+ label = enter.append('div')
.attr('class', 'label');
label.append('span')
@@ -101,7 +139,7 @@ iD.ui.FeatureList = function(context) {
label.append('span')
.attr('class', 'entity-type')
- .text(function(d) { return d.preset.name(); });
+ .text(function(d) { return d.type; });
label.append('span')
.attr('class', 'entity-name')
@@ -117,8 +155,8 @@ iD.ui.FeatureList = function(context) {
.remove();
}
- function mouseover(entity) {
- context.surface().selectAll(iD.util.entityOrMemberSelector([entity.id], context.graph()))
+ function mouseover(d) {
+ context.surface().selectAll(iD.util.entityOrMemberSelector([d.id], context.graph()))
.classed('hover', true);
}
@@ -127,8 +165,20 @@ iD.ui.FeatureList = function(context) {
.classed('hover', false);
}
- function click(entity) {
- context.enter(iD.modes.Select(context, [entity.id]));
+ function click(d) {
+ if (d.entity) {
+ context.enter(iD.modes.Select(context, [d.entity.id]));
+ } else {
+ context.loadEntity(d.id);
+ }
+ }
+
+ function geocode() {
+ var searchVal = encodeURIComponent(search.property('value'));
+ d3.json('http://nominatim.openstreetmap.org/search/' + searchVal + '?limit=10&format=json', function(err, resp) {
+ geocodeResults = resp || [];
+ drawList();
+ });
}
}
diff --git a/js/id/ui/geocoder.js b/js/id/ui/geocoder.js
deleted file mode 100644
index d837f111e..000000000
--- a/js/id/ui/geocoder.js
+++ /dev/null
@@ -1,171 +0,0 @@
-iD.ui.Geocoder = function(context) {
-
- var key = 'f';
-
- function resultExtent(bounds) {
- return new iD.geo.Extent(
- [parseFloat(bounds[3]), parseFloat(bounds[0])],
- [parseFloat(bounds[2]), parseFloat(bounds[1])]);
- }
-
- function truncate(d) {
- if (d.display_name.length > 80) {
- return d.display_name.substr(0, 80) + '…';
- } else {
- return d.display_name;
- }
- }
-
- function geocoder(selection) {
-
- var shown = false;
-
- function keydown() {
- if (d3.event.keyCode !== 13) return;
- d3.event.preventDefault();
- var searchVal = this.value;
- inputNode.classed('loading', true);
- d3.json('http://nominatim.openstreetmap.org/search/' +
- encodeURIComponent(searchVal) + '?limit=10&format=json', function(err, resp) {
- inputNode.classed('loading', false);
- if (err) return hide();
- if (!resp.length) {
- resultsList.html('')
- .call(iD.ui.Toggle(true))
- .append('span')
- .attr('class', 'not-found')
- .text(t('geocoder.no_results', { name: searchVal }));
- } else if (resp.length > 1) {
- var spans = resultsList.html('').selectAll('span')
- .data(resp, function(d) { return d.place_id; });
-
- spans.enter()
- .append('span')
- .text(function(d) {
- return d.type.charAt(0).toUpperCase() + d.type.slice(1) + ': ';
- })
- .append('a')
- .attr('tabindex', 1)
- .text(truncate)
- .on('click', clickResult)
- .on('keydown', function(d) {
- // support tabbing to and accepting this
- // entry
- if (d3.event.keyCode == 13) clickResult(d);
- });
- spans.exit().remove();
- resultsList.call(iD.ui.Toggle(true));
- } else {
- hide();
- applyBounds(resultExtent(resp[0].boundingbox));
- selectId(resp[0].osm_type, resp[0].osm_id);
- }
- });
- }
-
- function clickResult(d) {
- selectId(d.osm_type, d.osm_id);
- applyBounds(resultExtent(d.boundingbox));
- }
-
- function applyBounds(extent) {
- var map = context.map();
- map.extent(extent);
- if (map.zoom() > 19) map.zoom(19);
- }
-
- function selectId(type, id) {
- id = type[0] + id;
-
- if (context.hasEntity(id)) {
- context.enter(iD.modes.Select(context, [id]));
- } else {
- context.map().on('drawn.geocoder', function() {
- if (!context.hasEntity(id)) return;
- context.map().on('drawn.geocoder', null);
- context.enter(iD.modes.Select(context, [id]));
- });
- }
- }
-
- var tooltip = bootstrap.tooltip()
- .placement('right')
- .html(true)
- .title(iD.ui.tooltipHtml(t('geocoder.title'), key));
-
- var gcForm = selection.append('form');
-
- var inputNode = gcForm.attr('class', 'fillL map-overlay content hide')
- .append('input')
- .attr({ type: 'text', placeholder: t('geocoder.placeholder') })
- .attr('tabindex', 1)
- .on('keydown', keydown);
-
- var resultsList = selection.append('div')
- .attr('class', 'fillL map-overlay hide');
-
- var keybinding = d3.keybinding('geocoder');
-
- function hide() { setVisible(false); }
- function toggle() {
- if (d3.event) d3.event.preventDefault();
- tooltip.hide(button);
- setVisible(!button.classed('active'));
- }
-
- function setVisible(show) {
- if (show !== shown) {
- button.classed('active', show);
- shown = show;
-
- if (!show && !resultsList.classed('hide')) {
- resultsList.call(iD.ui.Toggle(show));
- // remove results so that they lose focus. if the user has
- // tabbed into the list, then they will have focus still,
- // even if they're hidden.
- resultsList.selectAll('span').remove();
- }
-
- if (show) {
- selection.on('mousedown.geocoder-inside', function() {
- return d3.event.stopPropagation();
- });
- gcForm.style('display', 'block')
- .style('left', '0px')
- .transition()
- .duration(200)
- .style('left', '-260px');
- inputNode.node().focus();
- } else {
- selection.on('mousedown.geocoder-inside', null);
- gcForm.style('display', 'block')
- .style('left', '-260px')
- .transition()
- .duration(200)
- .style('left', '0px')
- .each('end', function() {
- d3.select(this).style('display', 'none');
- });
- inputNode.node().blur();
- }
- }
- }
- var button = selection.append('button')
- .attr('tabindex', -1)
- .on('click', toggle)
- .call(tooltip);
-
- button.append('span')
- .attr('class', 'icon geocode');
-
- keybinding.on(key, toggle);
-
- d3.select(document)
- .call(keybinding);
-
- context.surface().on('mousedown.geocoder-outside', hide);
- context.container().on('mousedown.b.geocoder-outside', hide);
-
- }
- return geocoder;
-};
diff --git a/test/index.html b/test/index.html
index ef21d8ba2..5f7be8643 100644
--- a/test/index.html
+++ b/test/index.html
@@ -75,7 +75,6 @@
-
@@ -245,7 +244,6 @@
-
diff --git a/test/index_packaged.html b/test/index_packaged.html
index 309bf84dc..793cad825 100644
--- a/test/index_packaged.html
+++ b/test/index_packaged.html
@@ -75,7 +75,6 @@
-
diff --git a/test/spec/ui/geocoder.js b/test/spec/ui/geocoder.js
deleted file mode 100644
index 91e107ee2..000000000
--- a/test/spec/ui/geocoder.js
+++ /dev/null
@@ -1,6 +0,0 @@
-describe("iD.ui.Geocoder", function () {
- it('can be instantiated', function () {
- var geocoder = iD.ui.Geocoder();
- expect(geocoder).to.be.ok;
- });
-});