From b5c95af3b7eb6e213f35dc072d0a1b4e0f65f35a Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 19 Oct 2016 01:00:20 -0400 Subject: [PATCH] Refactor from `coreConnection` to `serviceOsm` (closes #3497) --- modules/core/context.js | 8 +- modules/core/index.js | 1 - modules/index.js | 4 +- modules/services/index.js | 2 + .../{core/connection.js => services/osm.js} | 434 +++++++++--------- modules/ui/account.js | 2 +- modules/ui/spinner.js | 4 +- .../{core/connection.js => services/osm.js} | 0 8 files changed, 226 insertions(+), 229 deletions(-) rename modules/{core/connection.js => services/osm.js} (59%) rename test/spec/{core/connection.js => services/osm.js} (100%) diff --git a/modules/core/context.js b/modules/core/context.js index 48b768692..f814908ae 100644 --- a/modules/core/context.js +++ b/modules/core/context.js @@ -1,7 +1,6 @@ import * as d3 from 'd3'; import _ from 'lodash'; import { t, addTranslation, setLocale } from '../util/locale'; -import { coreConnection } from './connection'; import { coreHistory } from './history'; import { dataLocales, dataEn } from '../../data/index'; import { geoRawMercator } from '../geo/raw_mercator'; @@ -336,14 +335,13 @@ export function coreContext(root) { /* reset (aka flush) */ context.reset = context.flush = function() { context.debouncedSave.cancel(); - connection.flush(); - features.reset(); - history.reset(); _.each(services, function(service) { if (typeof service.reset === 'function') { service.reset(context); } }); + features.reset(); + history.reset(); return context; }; @@ -383,7 +381,7 @@ export function coreContext(root) { ui = uiInit(context); - connection = coreConnection(); + connection = services.osm; background = rendererBackground(context); features = rendererFeatures(context); diff --git a/modules/core/index.js b/modules/core/index.js index 8e6edf3fe..da7f872d8 100644 --- a/modules/core/index.js +++ b/modules/core/index.js @@ -1,4 +1,3 @@ -export { coreConnection } from './connection'; export { coreContext } from './context'; export { coreDifference } from './difference'; export { coreGraph } from './graph'; diff --git a/modules/index.js b/modules/index.js index fadce1059..1151760f5 100644 --- a/modules/index.js +++ b/modules/index.js @@ -18,7 +18,9 @@ export * from './lib/index'; export * from './validations/index'; /* export some legacy symbols: */ -export { coreConnection as Connection } from './core/connection'; +import { services } from './services/index'; +var Connection = services.osm; +export { Connection }; export { coreContext as Context, setAreaKeys } from './core/context'; export { coreDifference as Difference } from './core/difference'; export { coreGraph as Graph } from './core/graph'; diff --git a/modules/services/index.js b/modules/services/index.js index 5a5f1e0c6..1f20acbc5 100644 --- a/modules/services/index.js +++ b/modules/services/index.js @@ -1,5 +1,6 @@ import serviceMapillary from './mapillary'; import serviceNominatim from './nominatim'; +import serviceOsm from './osm'; import serviceTaginfo from './taginfo'; import serviceWikidata from './wikidata'; import serviceWikipedia from './wikipedia'; @@ -7,6 +8,7 @@ import serviceWikipedia from './wikipedia'; export var services = { mapillary: serviceMapillary, nominatim: serviceNominatim, + osm: serviceOsm, taginfo: serviceTaginfo, wikidata: serviceWikidata, wikipedia: serviceWikipedia diff --git a/modules/core/connection.js b/modules/services/osm.js similarity index 59% rename from modules/core/connection.js rename to modules/services/osm.js index 1a5cbd603..44c2afad8 100644 --- a/modules/core/connection.js +++ b/modules/services/osm.js @@ -10,230 +10,241 @@ import { utilRebind } from '../util/rebind'; import { utilFunctor } from '../util/index'; -export function coreConnection(useHttps) { - if (typeof useHttps !== 'boolean') { - useHttps = window.location.protocol === 'https:'; +var dispatch = d3.dispatch('authenticating', 'authenticated', 'auth', 'loading', 'loaded'), + useHttps = window.location.protocol === 'https:', + protocol = useHttps ? 'https:' : 'http:', + url = protocol + '//www.openstreetmap.org', + inflight = {}, + loadedTiles = {}, + tileZoom = 16, + oauth = osmAuth({ + url: protocol + '//www.openstreetmap.org', + oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT', + oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL', + loading: authenticating, + done: authenticated + }), + userDetails, + off; + + +function authenticating() { + dispatch.call('authenticating'); +} + + +function authenticated() { + dispatch.call('authenticated'); +} + + +function abortRequest(i) { + i.abort(); +} + + +function getLoc(attrs) { + var lon = attrs.lon && attrs.lon.value, + lat = attrs.lat && attrs.lat.value; + return [parseFloat(lon), parseFloat(lat)]; +} + + +function getNodes(obj) { + var elems = obj.getElementsByTagName('nd'), + nodes = new Array(elems.length); + for (var i = 0, l = elems.length; i < l; i++) { + nodes[i] = 'n' + elems[i].attributes.ref.value; + } + return nodes; +} + + +function getTags(obj) { + var elems = obj.getElementsByTagName('tag'), + tags = {}; + for (var i = 0, l = elems.length; i < l; i++) { + var attrs = elems[i].attributes; + tags[attrs.k.value] = attrs.v.value; + } + return tags; +} + + +function getMembers(obj) { + var elems = obj.getElementsByTagName('member'), + 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.value[0] + attrs.ref.value, + type: attrs.type.value, + role: attrs.role.value + }; + } + return members; +} + + +function getVisible(attrs) { + return (!attrs.visible || attrs.visible.value !== 'false'); +} + + +var parsers = { + node: function nodeData(obj) { + var attrs = obj.attributes; + return new osmNode({ + id: osmEntity.id.fromOSM('node', attrs.id.value), + loc: getLoc(attrs), + version: attrs.version.value, + user: attrs.user && attrs.user.value, + tags: getTags(obj), + visible: getVisible(attrs) + }); + }, + + way: function wayData(obj) { + var attrs = obj.attributes; + return new osmWay({ + id: osmEntity.id.fromOSM('way', attrs.id.value), + version: attrs.version.value, + user: attrs.user && attrs.user.value, + tags: getTags(obj), + nodes: getNodes(obj), + visible: getVisible(attrs) + }); + }, + + relation: function relationData(obj) { + var attrs = obj.attributes; + return new osmRelation({ + id: osmEntity.id.fromOSM('relation', attrs.id.value), + version: attrs.version.value, + user: attrs.user && attrs.user.value, + tags: getTags(obj), + members: getMembers(obj), + visible: getVisible(attrs) + }); + } +}; + + +function parse(dom) { + if (!dom || !dom.childNodes) return; + + var root = dom.childNodes[0], + children = root.childNodes, + entities = []; + + for (var i = 0, l = children.length; i < l; i++) { + var child = children[i], + parser = parsers[child.nodeName]; + if (parser) { + entities.push(parser(child)); + } } - var dispatch = d3.dispatch('authenticating', 'authenticated', 'auth', 'loading', 'loaded'), - protocol = useHttps ? 'https:' : 'http:', - url = protocol + '//www.openstreetmap.org', - connection = {}, - inflight = {}, - loadedTiles = {}, - tileZoom = 16, - oauth = osmAuth({ - url: protocol + '//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', - userDetails, - off; + return entities; +} - connection.changesetURL = function(changesetId) { +export default { + + init: function() { + this.event = utilRebind(this, dispatch, 'on'); + }, + + + reset: function() { + userDetails = undefined; + _.forEach(inflight, abortRequest); + loadedTiles = {}; + inflight = {}; + return this; + }, + + + changesetURL: function(changesetId) { return url + '/changeset/' + changesetId; - }; + }, - connection.changesetsURL = function(center, zoom) { + changesetsURL: function(center, zoom) { var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)); return url + '/history#map=' + Math.floor(zoom) + '/' + center[1].toFixed(precision) + '/' + center[0].toFixed(precision); - }; + }, - connection.entityURL = function(entity) { + entityURL: function(entity) { return url + '/' + entity.type + '/' + entity.osmId(); - }; + }, - connection.userURL = function(username) { + userURL: function(username) { return url + '/user/' + username; - }; + }, - connection.loadFromURL = function(url, callback) { + loadFromURL: function(url, callback) { function done(err, dom) { return callback(err, parse(dom)); } return d3.xml(url).get(done); - }; + }, - connection.loadEntity = function(id, callback) { + loadEntity: function(id, callback) { var type = osmEntity.id.type(id), osmID = osmEntity.id.toOSM(id); - connection.loadFromURL( + this.loadFromURL( url + '/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : ''), function(err, entities) { if (callback) callback(err, {data: entities}); }); - }; + }, - connection.loadEntityVersion = function(id, version, callback) { + loadEntityVersion: function(id, version, callback) { var type = osmEntity.id.type(id), osmID = osmEntity.id.toOSM(id); - connection.loadFromURL( + this.loadFromURL( url + '/api/0.6/' + type + '/' + osmID + '/' + version, function(err, entities) { if (callback) callback(err, {data: entities}); }); - }; + }, - connection.loadMultiple = function(ids, callback) { + loadMultiple: function(ids, callback) { + var that = this; _.each(_.groupBy(_.uniq(ids), osmEntity.id.type), function(v, k) { var type = k + 's', osmIDs = _.map(v, osmEntity.id.toOSM); _.each(_.chunk(osmIDs, 150), function(arr) { - connection.loadFromURL( + that.loadFromURL( url + '/api/0.6/' + type + '?' + type + '=' + arr.join(), function(err, entities) { if (callback) callback(err, {data: entities}); }); }); }); - }; + }, - function authenticating() { - dispatch.call('authenticating'); - } - - - function authenticated() { - dispatch.call('authenticated'); - } - - - function getLoc(attrs) { - var lon = attrs.lon && attrs.lon.value, - lat = attrs.lat && attrs.lat.value; - return [parseFloat(lon), parseFloat(lat)]; - } - - - 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.value; - } - 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.value] = attrs.v.value; - } - 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.value[0] + attrs.ref.value, - type: attrs.type.value, - role: attrs.role.value - }; - } - return members; - } - - - function getVisible(attrs) { - return (!attrs.visible || attrs.visible.value !== 'false'); - } - - - var parsers = { - node: function nodeData(obj) { - var attrs = obj.attributes; - return new osmNode({ - id: osmEntity.id.fromOSM(nodeStr, attrs.id.value), - loc: getLoc(attrs), - version: attrs.version.value, - user: attrs.user && attrs.user.value, - tags: getTags(obj), - visible: getVisible(attrs) - }); - }, - - way: function wayData(obj) { - var attrs = obj.attributes; - return new osmWay({ - id: osmEntity.id.fromOSM(wayStr, attrs.id.value), - version: attrs.version.value, - user: attrs.user && attrs.user.value, - tags: getTags(obj), - nodes: getNodes(obj), - visible: getVisible(attrs) - }); - }, - - relation: function relationData(obj) { - var attrs = obj.attributes; - return new osmRelation({ - id: osmEntity.id.fromOSM(relationStr, attrs.id.value), - version: attrs.version.value, - user: attrs.user && attrs.user.value, - tags: getTags(obj), - members: getMembers(obj), - visible: getVisible(attrs) - }); - } - }; - - - function parse(dom) { - if (!dom || !dom.childNodes) return; - - var root = dom.childNodes[0], - children = root.childNodes, - entities = []; - - for (var i = 0, l = children.length; i < l; i++) { - var child = children[i], - parser = parsers[child.nodeName]; - if (parser) { - entities.push(parser(child)); - } - } - - return entities; - } - - - connection.authenticated = function() { + authenticated: function() { return oauth.authenticated(); - }; + }, // Generate Changeset XML. Returns a string. - connection.changesetJXON = function(tags) { + changesetJXON: function(tags) { return { osm: { changeset: { @@ -245,12 +256,12 @@ export function coreConnection(useHttps) { } } }; - }; + }, // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange) // XML. Returns a string. - connection.osmChangeJXON = function(changeset_id, changes) { + osmChangeJXON: function(changeset_id, changes) { function nest(x, order) { var groups = {}; for (var i = 0; i < x.length; i++) { @@ -278,10 +289,10 @@ export function coreConnection(useHttps) { 'delete': _.extend(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), {'@if-unused': true}) } }; - }; + }, - connection.changesetTags = function(version, comment, imageryUsed) { + changesetTags: function(version, comment, imageryUsed) { var detected = utilDetect(), tags = { created_by: ('iD ' + version).substr(0, 255), @@ -295,22 +306,23 @@ export function coreConnection(useHttps) { } return tags; - }; + }, - connection.putChangeset = function(changes, version, comment, imageryUsed, callback) { + putChangeset: function(changes, version, comment, imageryUsed, callback) { + var that = this; oauth.xhr({ method: 'PUT', path: '/api/0.6/changeset/create', options: { header: { 'Content-Type': 'text/xml' } }, - content: JXON.stringify(connection.changesetJXON(connection.changesetTags(version, comment, imageryUsed))) + content: JXON.stringify(that.changesetJXON(that.changesetTags(version, comment, imageryUsed))) }, 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(changeset_id, changes)) + content: JXON.stringify(that.osmChangeJXON(changeset_id, changes)) }, function(err) { if (err) return callback(err); // POST was successful, safe to call the callback. @@ -324,10 +336,10 @@ export function coreConnection(useHttps) { }, utilFunctor(true)); }); }); - }; + }, - connection.userDetails = function(callback) { + userDetails: function(callback) { if (userDetails) { callback(undefined, userDetails); return; @@ -354,11 +366,11 @@ export function coreConnection(useHttps) { } oauth.xhr({ method: 'GET', path: '/api/0.6/user/details' }, done); - }; + }, - connection.userChangesets = function(callback) { - connection.userDetails(function(err, user) { + userChangesets: function(callback) { + this.userDetails(function(err, user) { if (err) return callback(err); function done(changesets) { @@ -372,10 +384,10 @@ export function coreConnection(useHttps) { .on('load', done) .on('error', callback); }); - }; + }, - connection.status = function(callback) { + status: function(callback) { function done(capabilities) { var apiStatus = capabilities.getElementsByTagName('status'); callback(undefined, apiStatus[0].getAttribute('api')); @@ -383,25 +395,21 @@ export function coreConnection(useHttps) { d3.xml(url + '/api/capabilities').get() .on('load', done) .on('error', callback); - }; + }, - function abortRequest(i) { - i.abort(); - } - - - connection.tileZoom = function(_) { + tileZoom: function(_) { if (!arguments.length) return tileZoom; tileZoom = _; - return connection; - }; + return this; + }, - connection.loadTiles = function(projection, dimensions, callback) { + loadTiles: function(projection, dimensions, callback) { if (off) return; - var s = projection.scale() * 2 * Math.PI, + var that = this, + s = projection.scale() * 2 * Math.PI, z = Math.max(Math.log(s) / Math.log(2) - 8, 0), ts = 256 * Math.pow(2, z - tileZoom), origin = [ @@ -446,7 +454,7 @@ export function coreConnection(useHttps) { dispatch.call('loading'); } - inflight[id] = connection.loadFromURL(bboxUrl(tile), function(err, parsed) { + inflight[id] = that.loadFromURL(bboxUrl(tile), function(err, parsed) { loadedTiles[id] = true; delete inflight[id]; @@ -457,60 +465,48 @@ export function coreConnection(useHttps) { } }); }); - }; + }, - connection.switch = function(options) { + switch: function(options) { url = options.url; oauth.options(_.extend({ loading: authenticating, done: authenticated }, options)); dispatch.call('auth'); - connection.flush(); - return connection; - }; + this.reset(); + return this; + }, - connection.toggle = function(_) { + toggle: function(_) { off = !_; - return connection; - }; + return this; + }, - connection.flush = function() { - userDetails = undefined; - _.forEach(inflight, abortRequest); - loadedTiles = {}; - inflight = {}; - return connection; - }; - - - connection.loadedTiles = function(_) { + loadedTiles: function(_) { if (!arguments.length) return loadedTiles; loadedTiles = _; - return connection; - }; + return this; + }, - connection.logout = function() { + logout: function() { userDetails = undefined; oauth.logout(); dispatch.call('auth'); - return connection; - }; + return this; + }, - connection.authenticate = function(callback) { + authenticate: function(callback) { userDetails = undefined; function done(err, res) { dispatch.call('auth'); if (callback) callback(err, res); } return oauth.authenticate(done); - }; - - - return utilRebind(connection, dispatch, 'on'); -} + } +}; diff --git a/modules/ui/account.js b/modules/ui/account.js index 3f1b41f58..e846541e0 100644 --- a/modules/ui/account.js +++ b/modules/ui/account.js @@ -67,7 +67,7 @@ export function uiAccount(context) { .attr('id', 'userLink') .classed('hide', true); - connection.on('auth.account', function() { update(selection); }); + connection.event.on('auth.account', function() { update(selection); }); update(selection); }; } diff --git a/modules/ui/spinner.js b/modules/ui/spinner.js index 9daaf4b92..1cd8b0d52 100644 --- a/modules/ui/spinner.js +++ b/modules/ui/spinner.js @@ -8,13 +8,13 @@ export function uiSpinner(context) { .attr('src', context.imagePath('loader-black.gif')) .style('opacity', 0); - connection + connection.event .on('loading.spinner', function() { img.transition() .style('opacity', 1); }); - connection + connection.event .on('loaded.spinner', function() { img.transition() .style('opacity', 0); diff --git a/test/spec/core/connection.js b/test/spec/services/osm.js similarity index 100% rename from test/spec/core/connection.js rename to test/spec/services/osm.js