diff --git a/js/id/behavior/hash.js b/js/id/behavior/hash.js
index e017aabed..5e2edb860 100644
--- a/js/id/behavior/hash.js
+++ b/js/id/behavior/hash.js
@@ -41,6 +41,12 @@ iD.behavior.Hash = function(context) {
// do so before any features are loaded. thus wait for the feature to
// be loaded and then select
function willselect(id) {
+ context.connection().loadEntity(id, function(error, entity) {
+ if (entity) {
+ context.map().zoomTo(entity);
+ }
+ });
+
context.map().on('drawn.hash', function() {
if (!context.entity(id)) return;
selectoff();
diff --git a/js/id/core/connection.js b/js/id/core/connection.js
index 8a58f97d0..4f07e6d85 100644
--- a/js/id/core/connection.js
+++ b/js/id/core/connection.js
@@ -40,6 +40,18 @@ iD.Connection = function() {
return d3.xml(url).get().on('load', done);
};
+ connection.loadEntity = function(id, callback) {
+ var type = iD.Entity.id.type(id),
+ osmID = iD.Entity.id.toOSM(id);
+
+ connection.loadFromURL(
+ url + '/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : ''),
+ function(err, entities) {
+ event.load(err, entities);
+ if (callback) callback(err, entities && entities[id]);
+ });
+ };
+
function authenticating() {
event.authenticating();
}
diff --git a/js/id/core/entity.js b/js/id/core/entity.js
index 71ac7c5cb..ab6f76bc6 100644
--- a/js/id/core/entity.js
+++ b/js/id/core/entity.js
@@ -25,6 +25,10 @@ iD.Entity.id.toOSM = function(id) {
return id.slice(1);
};
+iD.Entity.id.type = function(id) {
+ return {'n': 'node', 'w': 'way', 'r': 'relation'}[id[0]];
+};
+
// A function suitable for use as the second argument to d3.selection#data().
iD.Entity.key = function(entity) {
return entity.id;
diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js
index 2b301a502..ff50ee19a 100644
--- a/js/id/renderer/map.js
+++ b/js/id/renderer/map.js
@@ -322,6 +322,12 @@ iD.Map = function(context) {
return redraw();
};
+ map.zoomTo = function(entity) {
+ var extent = entity.extent(context.graph()),
+ zoom = map.extentZoom(extent);
+ map.centerZoom(extent.center(), zoom);
+ };
+
map.centerZoom = function(loc, z) {
var centered = setCenter(loc),
zoomed = setZoom(z);
diff --git a/test/spec/core/connection.js b/test/spec/core/connection.js
index 541ba1783..830bc4247 100644
--- a/test/spec/core/connection.js
+++ b/test/spec/core/connection.js
@@ -79,6 +79,59 @@ describe('iD.Connection', function () {
});
});
+ describe('#loadEntity', function () {
+ var server,
+ nodeXML = '',
+ wayXML = '' +
+ '' +
+ '' +
+ '';
+
+ beforeEach(function() {
+ server = sinon.fakeServer.create();
+ });
+
+ afterEach(function() {
+ server.restore();
+ });
+
+ it('loads a node', function(done) {
+ c.loadEntity('n1', function(error, entity) {
+ expect(entity).to.be.an.instanceOf(iD.Node);
+ expect(entity.id).to.eql('n1');
+ done();
+ });
+
+ server.respondWith("GET", "http://www.openstreetmap.org/api/0.6/node/1",
+ [200, { "Content-Type": "text/xml" }, nodeXML]);
+ server.respond();
+ });
+
+ it('loads a way', function(done) {
+ c.loadEntity('w1', function(error, entity) {
+ expect(entity).to.be.an.instanceOf(iD.Way);
+ expect(entity.id).to.eql('w1');
+ done();
+ });
+
+ server.respondWith("GET", "http://www.openstreetmap.org/api/0.6/way/1/full",
+ [200, { "Content-Type": "text/xml" }, wayXML]);
+ server.respond();
+ });
+
+ it('emits a load event', function(done) {
+ c.loadEntity('n1');
+ c.on('load', function(error, result) {
+ expect(result.n1).to.be.an.instanceOf(iD.Node);
+ done();
+ });
+
+ server.respondWith("GET", "http://www.openstreetmap.org/api/0.6/node/1",
+ [200, { "Content-Type": "text/xml" }, nodeXML]);
+ server.respond();
+ });
+ });
+
describe('#osmChangeJXON', function() {
it('converts change data to JXON', function() {
var jxon = c.osmChangeJXON('jfire', '1234', {created: [], modified: [], deleted: []});