From daf91662980dfeb6549c2f44c88c428cdcb001a7 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Thu, 18 Apr 2013 14:30:22 -0700 Subject: [PATCH] Fix edit links on browse and changeset pages --- app/views/site/_id.html.erb | 15 +- vendor/assets/iD/iD.js | 888 ++++++++++++++++++++---------------- 2 files changed, 499 insertions(+), 404 deletions(-) diff --git a/app/views/site/_id.html.erb b/app/views/site/_id.html.erb index d94f871b7..8cb7bc853 100644 --- a/app/views/site/_id.html.erb +++ b/app/views/site/_id.html.erb @@ -7,12 +7,17 @@ coord.lon = <%= @lon %>; coord.zoom = <%= @zoom %>; <% else -%> - var params = OSM.mapParams(); - coord.lat = params.lat; - coord.lon = params.lon; - coord.zoom = params.zoom; + coord = OSM.mapParams(); <% end -%> - $('#id-embed').attr('src', 'id_iframe#map=' + coord.zoom + '/' + coord.lon + '/' + coord.lat); + + var hash; + if (coord.object && coord.object.type !== 'relation') { + hash = '#id=' + coord.object.type[0] + coord.object.id; + } else { + hash = '#map=' + (coord.zoom || 17) + '/' + coord.lon + '/' + coord.lat + } + + $('#id-embed').attr('src', 'id_iframe' + hash); <% else %> diff --git a/vendor/assets/iD/iD.js b/vendor/assets/iD/iD.js index 6adb5fef0..44f893227 100644 --- a/vendor/assets/iD/iD.js +++ b/vendor/assets/iD/iD.js @@ -14797,7 +14797,7 @@ window.iD = function () { container, ui = iD.ui(context), map = iD.Map(context), - connection = iD.Connection(context, iD.data.keys[0]); + connection = iD.Connection(); connection.on('load.context', function loadContext(err, result) { history.merge(result); @@ -14976,377 +14976,6 @@ iD.detect = function() { return browser; }; -iD.Connection = function(context, options) { - - var event = d3.dispatch('auth', 'loading', 'load', 'loaded'), - url = options.url || 'http://www.openstreetmap.org', - connection = {}, - user = {}, - inflight = {}, - loadedTiles = {}, - loadingModal, - oauth = osmAuth(_.extend({ - loading: authLoading, - done: authDone - }, options)), - ndStr = 'nd', - tagStr = 'tag', - memberStr = 'member', - nodeStr = 'node', - wayStr = 'way', - relationStr = 'relation', - off; - - connection.changesetUrl = function(changesetId) { - return url + '/browse/changeset/' + changesetId; - }; - - connection.entityURL = function(entity) { - return url + '/browse/' + entity.type + '/' + entity.osmId(); - }; - - connection.userUrl = function(username) { - return url + "/user/" + username; - }; - - connection.loadFromURL = function(url, callback) { - function done(dom) { - return callback(null, parse(dom)); - } - return d3.xml(url).get().on('load', done); - }; - - function authLoading() { - loadingModal = iD.ui.Loading(context) - .message(t('loading_auth')); - - context.container() - .call(loadingModal); - } - - function authDone() { - if (loadingModal) loadingModal.close(); - } - - function getNodes(obj) { - var elems = obj.getElementsByTagName(ndStr), - nodes = new Array(elems.length); - for (var i = 0, l = elems.length; i < l; i++) { - nodes[i] = 'n' + elems[i].attributes.ref.nodeValue; - } - return nodes; - } - - function getTags(obj) { - var elems = obj.getElementsByTagName(tagStr), - tags = {}; - for (var i = 0, l = elems.length; i < l; i++) { - var attrs = elems[i].attributes; - tags[attrs.k.nodeValue] = attrs.v.nodeValue; - } - return tags; - } - - function getMembers(obj) { - var elems = obj.getElementsByTagName(memberStr), - members = new Array(elems.length); - for (var i = 0, l = elems.length; i < l; i++) { - var attrs = elems[i].attributes; - members[i] = { - id: attrs.type.nodeValue[0] + attrs.ref.nodeValue, - type: attrs.type.nodeValue, - role: attrs.role.nodeValue - }; - } - return members; - } - - var parsers = { - node: function nodeData(obj) { - var attrs = obj.attributes; - return new iD.Node({ - id: iD.Entity.id.fromOSM(nodeStr, attrs.id.nodeValue), - loc: [parseFloat(attrs.lon.nodeValue), parseFloat(attrs.lat.nodeValue)], - version: attrs.version.nodeValue, - changeset: attrs.changeset.nodeValue, - user: attrs.user && attrs.user.nodeValue, - uid: attrs.uid && attrs.uid.nodeValue, - visible: attrs.visible.nodeValue, - timestamp: attrs.timestamp.nodeValue, - tags: getTags(obj) - }); - }, - - way: function wayData(obj) { - var attrs = obj.attributes; - return new iD.Way({ - id: iD.Entity.id.fromOSM(wayStr, attrs.id.nodeValue), - version: attrs.version.nodeValue, - changeset: attrs.changeset.nodeValue, - user: attrs.user && attrs.user.nodeValue, - uid: attrs.uid && attrs.uid.nodeValue, - visible: attrs.visible.nodeValue, - timestamp: attrs.timestamp.nodeValue, - tags: getTags(obj), - nodes: getNodes(obj) - }); - }, - - relation: function relationData(obj) { - var attrs = obj.attributes; - return new iD.Relation({ - id: iD.Entity.id.fromOSM(relationStr, attrs.id.nodeValue), - version: attrs.version.nodeValue, - changeset: attrs.changeset.nodeValue, - user: attrs.user && attrs.user.nodeValue, - uid: attrs.uid && attrs.uid.nodeValue, - visible: attrs.visible.nodeValue, - timestamp: attrs.timestamp.nodeValue, - tags: getTags(obj), - members: getMembers(obj) - }); - } - }; - - function parse(dom) { - if (!dom || !dom.childNodes) return new Error('Bad request'); - - var root = dom.childNodes[0], - children = root.childNodes, - entities = {}; - - var i, o, l; - for (i = 0, l = children.length; i < l; i++) { - var child = children[i], - parser = parsers[child.nodeName]; - if (parser) { - o = parser(child); - entities[o.id] = o; - } - } - - return entities; - } - - connection.authenticated = function() { - return oauth.authenticated(); - }; - - // Generate Changeset XML. Returns a string. - connection.changesetJXON = function(tags) { - return { - osm: { - changeset: { - tag: _.map(tags, function(value, key) { - return { '@k': key, '@v': value }; - }), - '@version': 0.3, - '@generator': 'iD' - } - } - }; - }; - - // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange) - // XML. Returns a string. - connection.osmChangeJXON = function(userid, changeset_id, changes) { - function nest(x, order) { - var groups = {}; - for (var i = 0; i < x.length; i++) { - var tagName = Object.keys(x[i])[0]; - if (!groups[tagName]) groups[tagName] = []; - groups[tagName].push(x[i][tagName]); - } - var ordered = {}; - order.forEach(function(o) { - if (groups[o]) ordered[o] = groups[o]; - }); - return ordered; - } - - function rep(entity) { - return entity.asJXON(changeset_id); - } - - return { - osmChange: { - '@version': 0.3, - '@generator': 'iD', - 'create': nest(changes.created.map(rep), ['node', 'way', 'relation']), - 'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']), - 'delete': _.extend(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), {'@if-unused': true}) - } - }; - }; - - connection.putChangeset = function(changes, comment, imagery_used, callback) { - oauth.xhr({ - method: 'PUT', - path: '/api/0.6/changeset/create', - options: { header: { 'Content-Type': 'text/xml' } }, - content: JXON.stringify(connection.changesetJXON({ - imagery_used: imagery_used.join(';'), - comment: comment, - created_by: 'iD ' + iD.version - })) - }, function(err, changeset_id) { - if (err) return callback(err); - oauth.xhr({ - method: 'POST', - path: '/api/0.6/changeset/' + changeset_id + '/upload', - options: { header: { 'Content-Type': 'text/xml' } }, - content: JXON.stringify(connection.osmChangeJXON(user.id, changeset_id, changes)) - }, function(err) { - if (err) return callback(err); - oauth.xhr({ - method: 'PUT', - path: '/api/0.6/changeset/' + changeset_id + '/close' - }, function(err) { - callback(err, changeset_id); - }); - }); - }); - }; - - connection.userDetails = function(callback) { - function done(err, user_details) { - if (err) return callback(err); - var u = user_details.getElementsByTagName('user')[0], - img = u.getElementsByTagName('img'), - image_url = ''; - if (img && img[0].getAttribute('href')) { - image_url = img[0].getAttribute('href'); - } - callback(undefined, connection.user({ - display_name: u.attributes.display_name.nodeValue, - image_url: image_url, - id: u.attributes.id.nodeValue - }).user()); - } - oauth.xhr({ method: 'GET', path: '/api/0.6/user/details' }, done); - }; - - connection.status = function(callback) { - function done(capabilities) { - var apiStatus = capabilities.getElementsByTagName('status'); - callback(undefined, apiStatus[0].getAttribute('api')); - } - d3.xml(url + '/api/capabilities').get() - .on('load', done) - .on('error', callback); - }; - - function abortRequest(i) { i.abort(); } - - connection.loadTiles = function(projection, dimensions) { - - if (off) return; - - var scaleExtent = [16, 16], - s = projection.scale() * 2 * Math.PI, - tiles = d3.geo.tile() - .scaleExtent(scaleExtent) - .scale(s) - .size(dimensions) - .translate(projection.translate())(), - z = Math.max(Math.log(s) / Math.log(2) - 8, 0), - rz = Math.max(scaleExtent[0], Math.min(scaleExtent[1], Math.floor(z))), - ts = 256 * Math.pow(2, z - rz), - tile_origin = [ - s / 2 - projection.translate()[0], - s / 2 - projection.translate()[1]]; - - function bboxUrl(tile) { - var x = (tile[0] * ts) - tile_origin[0]; - var y = (tile[1] * ts) - tile_origin[1]; - var b = [ - projection.invert([x, y]), - projection.invert([x + ts, y + ts])]; - - return url + '/api/0.6/map?bbox=' + [b[0][0], b[1][1], b[1][0], b[0][1]]; - } - - _.filter(inflight, function(v, i) { - var wanted = _.find(tiles, function(tile) { - return i === tile.toString(); - }); - if (!wanted) delete inflight[i]; - return !wanted; - }).map(abortRequest); - - tiles.forEach(function(tile) { - var id = tile.toString(); - - if (loadedTiles[id] || inflight[id]) return; - - if (_.isEmpty(inflight)) { - event.loading(); - } - - inflight[id] = connection.loadFromURL(bboxUrl(tile), function(err, parsed) { - loadedTiles[id] = true; - delete inflight[id]; - - event.load(err, parsed); - - if (_.isEmpty(inflight)) { - event.loaded(); - } - }); - }); - }; - - connection.switch = function(options) { - url = options.url; - oauth.options(_.extend({ - loading: authLoading, - done: authDone - }, options)); - event.auth(); - connection.flush(); - return connection; - }; - - connection.toggle = function(_) { - off = !_; - return connection; - }; - - connection.user = function(_) { - if (!arguments.length) return user; - user = _; - return connection; - }; - - connection.flush = function() { - _.forEach(inflight, abortRequest); - loadedTiles = {}; - inflight = {}; - return connection; - }; - - connection.loadedTiles = function(_) { - if (!arguments.length) return loadedTiles; - loadedTiles = _; - return connection; - }; - - connection.logout = function() { - oauth.logout(); - event.auth(); - return connection; - }; - - connection.authenticate = function(callback) { - function done(err, res) { - event.auth(); - if (callback) callback(err, res); - } - return oauth.authenticate(done); - }; - - return d3.rebind(connection, event, 'on'); -}; iD.taginfo = function() { var taginfo = {}, endpoint = 'http://taginfo.openstreetmap.org/api/4/', @@ -17279,6 +16908,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(); @@ -18721,6 +18356,387 @@ iD.operations.Split = function(selection, context) { return operation; }; +iD.Connection = function() { + + var event = d3.dispatch('authenticating', 'authenticated', 'auth', 'loading', 'load', 'loaded'), + url = 'http://www.openstreetmap.org', + connection = {}, + user = {}, + inflight = {}, + loadedTiles = {}, + oauth = osmAuth({ + url: 'http://www.openstreetmap.org', + oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT', + oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL', + loading: authenticating, + done: authenticated + }), + ndStr = 'nd', + tagStr = 'tag', + memberStr = 'member', + nodeStr = 'node', + wayStr = 'way', + relationStr = 'relation', + off; + + connection.changesetURL = function(changesetId) { + return url + '/browse/changeset/' + changesetId; + }; + + connection.entityURL = function(entity) { + return url + '/browse/' + entity.type + '/' + entity.osmId(); + }; + + connection.userURL = function(username) { + return url + "/user/" + username; + }; + + connection.loadFromURL = function(url, callback) { + function done(dom) { + return callback(null, parse(dom)); + } + 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(); + } + + function authenticated() { + event.authenticated(); + } + + function getNodes(obj) { + var elems = obj.getElementsByTagName(ndStr), + nodes = new Array(elems.length); + for (var i = 0, l = elems.length; i < l; i++) { + nodes[i] = 'n' + elems[i].attributes.ref.nodeValue; + } + return nodes; + } + + function getTags(obj) { + var elems = obj.getElementsByTagName(tagStr), + tags = {}; + for (var i = 0, l = elems.length; i < l; i++) { + var attrs = elems[i].attributes; + tags[attrs.k.nodeValue] = attrs.v.nodeValue; + } + return tags; + } + + function getMembers(obj) { + var elems = obj.getElementsByTagName(memberStr), + members = new Array(elems.length); + for (var i = 0, l = elems.length; i < l; i++) { + var attrs = elems[i].attributes; + members[i] = { + id: attrs.type.nodeValue[0] + attrs.ref.nodeValue, + type: attrs.type.nodeValue, + role: attrs.role.nodeValue + }; + } + return members; + } + + var parsers = { + node: function nodeData(obj) { + var attrs = obj.attributes; + return new iD.Node({ + id: iD.Entity.id.fromOSM(nodeStr, attrs.id.nodeValue), + loc: [parseFloat(attrs.lon.nodeValue), parseFloat(attrs.lat.nodeValue)], + version: attrs.version.nodeValue, + changeset: attrs.changeset.nodeValue, + user: attrs.user && attrs.user.nodeValue, + uid: attrs.uid && attrs.uid.nodeValue, + visible: attrs.visible.nodeValue, + timestamp: attrs.timestamp.nodeValue, + tags: getTags(obj) + }); + }, + + way: function wayData(obj) { + var attrs = obj.attributes; + return new iD.Way({ + id: iD.Entity.id.fromOSM(wayStr, attrs.id.nodeValue), + version: attrs.version.nodeValue, + changeset: attrs.changeset.nodeValue, + user: attrs.user && attrs.user.nodeValue, + uid: attrs.uid && attrs.uid.nodeValue, + visible: attrs.visible.nodeValue, + timestamp: attrs.timestamp.nodeValue, + tags: getTags(obj), + nodes: getNodes(obj) + }); + }, + + relation: function relationData(obj) { + var attrs = obj.attributes; + return new iD.Relation({ + id: iD.Entity.id.fromOSM(relationStr, attrs.id.nodeValue), + version: attrs.version.nodeValue, + changeset: attrs.changeset.nodeValue, + user: attrs.user && attrs.user.nodeValue, + uid: attrs.uid && attrs.uid.nodeValue, + visible: attrs.visible.nodeValue, + timestamp: attrs.timestamp.nodeValue, + tags: getTags(obj), + members: getMembers(obj) + }); + } + }; + + function parse(dom) { + if (!dom || !dom.childNodes) return new Error('Bad request'); + + var root = dom.childNodes[0], + children = root.childNodes, + entities = {}; + + var i, o, l; + for (i = 0, l = children.length; i < l; i++) { + var child = children[i], + parser = parsers[child.nodeName]; + if (parser) { + o = parser(child); + entities[o.id] = o; + } + } + + return entities; + } + + connection.authenticated = function() { + return oauth.authenticated(); + }; + + // Generate Changeset XML. Returns a string. + connection.changesetJXON = function(tags) { + return { + osm: { + changeset: { + tag: _.map(tags, function(value, key) { + return { '@k': key, '@v': value }; + }), + '@version': 0.3, + '@generator': 'iD' + } + } + }; + }; + + // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange) + // XML. Returns a string. + connection.osmChangeJXON = function(userid, changeset_id, changes) { + function nest(x, order) { + var groups = {}; + for (var i = 0; i < x.length; i++) { + var tagName = Object.keys(x[i])[0]; + if (!groups[tagName]) groups[tagName] = []; + groups[tagName].push(x[i][tagName]); + } + var ordered = {}; + order.forEach(function(o) { + if (groups[o]) ordered[o] = groups[o]; + }); + return ordered; + } + + function rep(entity) { + return entity.asJXON(changeset_id); + } + + return { + osmChange: { + '@version': 0.3, + '@generator': 'iD', + 'create': nest(changes.created.map(rep), ['node', 'way', 'relation']), + 'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']), + 'delete': _.extend(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), {'@if-unused': true}) + } + }; + }; + + connection.putChangeset = function(changes, comment, imagery_used, callback) { + oauth.xhr({ + method: 'PUT', + path: '/api/0.6/changeset/create', + options: { header: { 'Content-Type': 'text/xml' } }, + content: JXON.stringify(connection.changesetJXON({ + imagery_used: imagery_used.join(';'), + comment: comment, + created_by: 'iD ' + iD.version + })) + }, function(err, changeset_id) { + if (err) return callback(err); + oauth.xhr({ + method: 'POST', + path: '/api/0.6/changeset/' + changeset_id + '/upload', + options: { header: { 'Content-Type': 'text/xml' } }, + content: JXON.stringify(connection.osmChangeJXON(user.id, changeset_id, changes)) + }, function(err) { + if (err) return callback(err); + oauth.xhr({ + method: 'PUT', + path: '/api/0.6/changeset/' + changeset_id + '/close' + }, function(err) { + callback(err, changeset_id); + }); + }); + }); + }; + + connection.userDetails = function(callback) { + function done(err, user_details) { + if (err) return callback(err); + var u = user_details.getElementsByTagName('user')[0], + img = u.getElementsByTagName('img'), + image_url = ''; + if (img && img[0].getAttribute('href')) { + image_url = img[0].getAttribute('href'); + } + callback(undefined, connection.user({ + display_name: u.attributes.display_name.nodeValue, + image_url: image_url, + id: u.attributes.id.nodeValue + }).user()); + } + oauth.xhr({ method: 'GET', path: '/api/0.6/user/details' }, done); + }; + + connection.status = function(callback) { + function done(capabilities) { + var apiStatus = capabilities.getElementsByTagName('status'); + callback(undefined, apiStatus[0].getAttribute('api')); + } + d3.xml(url + '/api/capabilities').get() + .on('load', done) + .on('error', callback); + }; + + function abortRequest(i) { i.abort(); } + + connection.loadTiles = function(projection, dimensions) { + + if (off) return; + + var scaleExtent = [16, 16], + s = projection.scale() * 2 * Math.PI, + tiles = d3.geo.tile() + .scaleExtent(scaleExtent) + .scale(s) + .size(dimensions) + .translate(projection.translate())(), + z = Math.max(Math.log(s) / Math.log(2) - 8, 0), + rz = Math.max(scaleExtent[0], Math.min(scaleExtent[1], Math.floor(z))), + ts = 256 * Math.pow(2, z - rz), + tile_origin = [ + s / 2 - projection.translate()[0], + s / 2 - projection.translate()[1]]; + + function bboxUrl(tile) { + var x = (tile[0] * ts) - tile_origin[0]; + var y = (tile[1] * ts) - tile_origin[1]; + var b = [ + projection.invert([x, y]), + projection.invert([x + ts, y + ts])]; + + return url + '/api/0.6/map?bbox=' + [b[0][0], b[1][1], b[1][0], b[0][1]]; + } + + _.filter(inflight, function(v, i) { + var wanted = _.find(tiles, function(tile) { + return i === tile.toString(); + }); + if (!wanted) delete inflight[i]; + return !wanted; + }).map(abortRequest); + + tiles.forEach(function(tile) { + var id = tile.toString(); + + if (loadedTiles[id] || inflight[id]) return; + + if (_.isEmpty(inflight)) { + event.loading(); + } + + inflight[id] = connection.loadFromURL(bboxUrl(tile), function(err, parsed) { + loadedTiles[id] = true; + delete inflight[id]; + + event.load(err, parsed); + + if (_.isEmpty(inflight)) { + event.loaded(); + } + }); + }); + }; + + connection.switch = function(options) { + url = options.url; + oauth.options(_.extend({ + loading: authenticating, + done: authenticated + }, options)); + event.auth(); + connection.flush(); + return connection; + }; + + connection.toggle = function(_) { + off = !_; + return connection; + }; + + connection.user = function(_) { + if (!arguments.length) return user; + user = _; + return connection; + }; + + connection.flush = function() { + _.forEach(inflight, abortRequest); + loadedTiles = {}; + inflight = {}; + return connection; + }; + + connection.loadedTiles = function(_) { + if (!arguments.length) return loadedTiles; + loadedTiles = _; + return connection; + }; + + connection.logout = function() { + oauth.logout(); + event.auth(); + return connection; + }; + + connection.authenticate = function(callback) { + function done(err, res) { + event.auth(); + if (callback) callback(err, res); + } + return oauth.authenticate(done); + }; + + return d3.rebind(connection, event, 'on'); +}; /* iD.Difference represents the difference between two graphs. It knows how to calculate the set of entities that were @@ -18734,7 +18750,7 @@ iD.Difference = function(base, head) { _.each(head.entities, function(h, id) { var b = base.entities[id]; - if (h !== b) { + if (!_.isEqual(h, b)) { changes[id] = {base: b, head: h}; length++; } @@ -18742,7 +18758,7 @@ iD.Difference = function(base, head) { _.each(base.entities, function(b, id) { var h = head.entities[id]; - if (!changes[id] && h !== b) { + if (!changes[id] && !_.isEqual(h, b)) { changes[id] = {base: b, head: h}; length++; } @@ -18880,6 +18896,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; @@ -20788,6 +20808,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); @@ -22256,6 +22282,17 @@ iD.ui = function(context) { .call(iD.ui.Splash(context)) .call(iD.ui.Restore(context)); + var authenticating = iD.ui.Loading(context) + .message(t('loading_auth')); + + context.connection() + .on('authenticating.ui', function() { + context.container() + .call(authenticating); + }) + .on('authenticated.ui', function() { + authenticating.close(); + }); }; }; @@ -22281,7 +22318,7 @@ iD.ui.Account = function(context) { // Link var userLink = selection.append('a') - .attr('href', connection.userUrl(details.display_name)) + .attr('href', connection.userURL(details.display_name)) .attr('target', '_blank'); // Add thumbnail or dont @@ -22817,7 +22854,7 @@ iD.ui.Commit = function(context) { userLink.append('a') .attr('class','user-info') .text(user.display_name) - .attr('href', connection.userUrl(user.display_name)) + .attr('href', connection.userURL(user.display_name)) .attr('tabindex', -1) .attr('target', '_blank'); @@ -22954,7 +22991,7 @@ iD.ui.Contributors = function(context) { .enter() .append('a') .attr('class', 'user-link') - .attr('href', function(d) { return context.connection().userUrl(d); }) + .attr('href', function(d) { return context.connection().userURL(d); }) .attr('target', '_blank') .attr('tabindex', -1) .text(String); @@ -23486,6 +23523,11 @@ iD.ui.Inspector = function(context, entity) { } inspector.close = function(selection) { + + // Blur focused element so that tag changes are dispatched + // See #1295 + document.activeElement.blur(); + selection.transition() .style('right', '-500px') .each('end', function() { @@ -23989,7 +24031,7 @@ iD.ui.preset = function(context, entity, preset) { d3.event.preventDefault(); var t = {}; field.keys.forEach(function(key) { - t[key] = original ? original.tags[key] : undefined; + t[key] = original ? original.tags[key] || '' : ''; }); event.change(t); } @@ -24672,6 +24714,8 @@ iD.ui.Save = function(context) { }; }; iD.ui.SourceSwitch = function(context) { + var keys; + function click() { d3.event.preventDefault(); @@ -24682,7 +24726,7 @@ iD.ui.SourceSwitch = function(context) { .classed('live'); context.connection() - .switch(live ? iD.data.keys[1] : iD.data.keys[0]); + .switch(live ? keys[1] : keys[0]); context.map() .flush(); @@ -24692,7 +24736,7 @@ iD.ui.SourceSwitch = function(context) { .classed('live', !live); } - return function(selection) { + var sourceSwitch = function(selection) { selection.append('a') .attr('href', '#') .text(t('source_switch.live')) @@ -24700,6 +24744,14 @@ iD.ui.SourceSwitch = function(context) { .attr('tabindex', -1) .on('click', click); }; + + sourceSwitch.keys = function(_) { + if (!arguments.length) return keys; + keys = _; + return sourceSwitch; + }; + + return sourceSwitch; }; iD.ui.Spinner = function(context) { var connection = context.connection(); @@ -24819,7 +24871,7 @@ iD.ui.Success = function(connection) { } var message = (m || 'Edited OSM!') + - connection.changesetUrl(changeset.id); + connection.changesetURL(changeset.id); var links = body.append('div').attr('class','modal-actions cf'); @@ -24827,7 +24879,7 @@ iD.ui.Success = function(connection) { .attr('class','col6 osm') .attr('target', '_blank') .attr('href', function() { - return connection.changesetUrl(changeset.id); + return connection.changesetURL(changeset.id); }) .text(t('view_on_osm')); @@ -25556,6 +25608,7 @@ iD.ui.preset.address = function(field, context) { housenumber, street, city, + postcode, entity; function getStreets() { @@ -25627,6 +25680,14 @@ iD.ui.preset.address = function(field, context) { .on('blur', change) .on('change', change) .call(close()); + + postcode = wrap.append('input') + .property('type', 'text') + .attr('placeholder', field.t('placeholders.postcode')) + .attr('class', 'addr-postcode') + .on('blur', change) + .on('change', change) + .call(close()); } function change() { @@ -25634,7 +25695,8 @@ iD.ui.preset.address = function(field, context) { 'addr:housename': housename.property('value'), 'addr:housenumber': housenumber.property('value'), 'addr:street': street.property('value'), - 'addr:city': city.property('value') + 'addr:city': city.property('value'), + 'addr:postcode': postcode.property('value') }); } @@ -25649,6 +25711,7 @@ iD.ui.preset.address = function(field, context) { housenumber.property('value', tags['addr:housenumber'] || ''); street.property('value', tags['addr:street'] || ''); city.property('value', tags['addr:city'] || ''); + postcode.property('value', tags['addr:postcode'] || ''); return address; }; @@ -26024,13 +26087,16 @@ iD.ui.preset.maxspeed = function(field, context) { var event = d3.dispatch('change', 'close'), entity, imperial, + unitInput, + combobox, input; var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120], imperialValues = [20, 25, 30, 40, 45, 50, 55, 65, 70]; function maxspeed(selection) { - var combobox = d3.combobox(); + combobox = d3.combobox(); + var unitCombobox = d3.combobox().data(['km/h', 'mph'].map(comboValues)); input = selection.append('input') .attr('type', 'text') @@ -26048,18 +26114,33 @@ iD.ui.preset.maxspeed = function(field, context) { }); }); - selection.append('span') + unitInput = selection.append('input') + .attr('type', 'text') .attr('class', 'maxspeed-unit') - .text(imperial ? 'mph' : 'km/h'); + .on('blur', changeUnits) + .on('change', changeUnits) + .call(unitCombobox); + + function changeUnits() { + imperial = unitInput.property('value') === 'mph'; + unitInput.property('value', imperial ? 'mph' : 'km/h'); + setSuggestions(); + change(); + } - combobox.data((imperial ? imperialValues : metricValues).map(function(d) { - return { - value: d.toString(), - title: d.toString() - }; - })); } + function setSuggestions() { + combobox.data((imperial ? imperialValues : metricValues).map(comboValues)); + unitInput.property('value', imperial ? 'mph' : 'km/h'); + } + + function comboValues(d) { + return { + value: d.toString(), + title: d.toString() + }; + } function change() { var value = input.property('value'); @@ -26078,7 +26159,16 @@ iD.ui.preset.maxspeed = function(field, context) { maxspeed.tags = function(tags) { var value = tags[field.key]; - if (value && isNaN(value) && value.indexOf('mph') >= 0) value = parseInt(value, 10); + + if (value && value.indexOf('mph') >= 0) { + value = parseInt(value, 10); + imperial = true; + } else if (value) { + imperial = false; + } + + setSuggestions(); + input.property('value', value || ''); }; @@ -54213,14 +54303,12 @@ iD.data = { { "url": "http://www.openstreetmap.org", "oauth_consumer_key": "5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT", - "oauth_secret": "aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL", - "oauth_signature_method": "HMAC-SHA1" + "oauth_secret": "aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL" }, { "url": "http://api06.dev.openstreetmap.org", "oauth_consumer_key": "zwQZFivccHkLs3a8Rq5CoS412fE5aPCXDw9DZj7R", - "oauth_secret": "aMnOOCwExO2XYtRVWJ1bI9QOdqh1cay2UgpbhA6p", - "oauth_signature_method": "HMAC-SHA1" + "oauth_secret": "aMnOOCwExO2XYtRVWJ1bI9QOdqh1cay2UgpbhA6p" } ], "imagery": [ @@ -60629,7 +60717,8 @@ iD.data = { "addr:housename", "addr:housenumber", "addr:street", - "addr:city" + "addr:city", + "addr:postcode" ], "icon": "address", "universal": true, @@ -60639,7 +60728,8 @@ iD.data = { "housename": "Housename", "number": "123", "street": "Street", - "city": "City" + "city": "City", + "postcode": "Postal code" } } }, -- 2.43.2