From 2d861507b9cd5e51caa215639caea84039c7c1d3 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Sat, 24 Nov 2012 11:13:04 -0400 Subject: [PATCH 1/5] Move top-level setup into iD() function --- index.html | 118 ++--------------------------------------- js/iD/id.js | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 152 insertions(+), 115 deletions(-) diff --git a/index.html b/index.html index 86c2db050..6b36f3ac3 100644 --- a/index.html +++ b/index.html @@ -7,30 +7,6 @@ - - -
-
- -
- -
- -
-
- -
-
-

Work in progress: introduction, - code, - docs. - Imagery © 2012 Bing, GeoEye, Getmapping, Intermap, Microsoft.

-
@@ -63,95 +39,9 @@ - - + + +
+ diff --git a/js/iD/id.js b/js/iD/id.js index 9041b915c..849b50113 100644 --- a/js/iD/id.js +++ b/js/iD/id.js @@ -1,4 +1,151 @@ -if (typeof iD === 'undefined') var iD = {}; +var iD = function(container) { + container = d3.select(container); + + var m = container.append('div') + .attr('id', 'map'); + + var map = iD.Map(m.node()) + .setAPI('http://api06.dev.openstreetmap.org/api/0.6/'); + + var controller = iD.Controller(map); + + var oauth = iD.OAuth(map) + .setAPI('http://api06.dev.openstreetmap.org/api/0.6'); + + var bar = container.append('div') + .attr('id', 'bar'); + + bar.append('button') + .attr('id', 'place') + .html('+ Place') + .on('click', function() { + controller.go(iD.actions.AddPlace); + }); + + bar.append('button') + .attr('id', 'road') + .html('+ Road') + .on('click', function() { + controller.go(iD.actions.AddRoad); + }); + + bar.append('button') + .attr('id', 'area') + .html('+ Area') + .on('click', function() { + controller.go(iD.actions.AddArea); + }); + + bar.append('button') + .attr('id', 'undo') + .attr('class', 'mini') + .html('← ') + .on('click', map.undo); + + bar.append('button') + .attr('id', 'redo') + .attr('class', 'mini') + .html('→ ') + .on('click', map.redo); + + bar.append('input') + .attr('type', 'text') + .attr('placeholder', 'find a place') + .attr('id', 'geocode-location') + .on('keydown', function () { + if (d3.event.keyCode !== 13) return; + d3.event.preventDefault(); + var val = d3.select('#geocode-location').node().value; + var scr = document.body.appendChild(document.createElement('script')); + scr.src = 'http://api.tiles.mapbox.com/v3/mapbox/geocode/' + + encodeURIComponent(val) + '.jsonp?callback=grid'; + }); + + function grid(resp) { + map.setCentre(resp.results[0][0]); + } + + bar.append('div') + .attr('class', 'messages'); + + + bar.append('button') + .attr('id', 'save') + .html("Save") + .on('click', function() { + oauth.authenticate(function() { + map.commit(); + }); + }); + + var zoom = bar.append('div') + .attr('class', 'zoombuttons'); + + zoom.append('button') + .attr('class', 'zoom-in') + .text('+') + .on('click', map.zoomIn); + + zoom.append('button') + .attr('class', 'zoom-out') + .text('–') + .on('click', map.zoomOut); + + container.append('div') + .attr('class', 'inspector-wrap'); + + container.append('div') + .attr('id', 'about') + .html("

Work in progress: introduction," + + "code," + + "docs." + + "Imagery © 2012 Bing, GeoEye, Getmapping, Intermap, Microsoft.

"); + + window.onresize = function() { + map.setSize({ + width: m.node().offsetWidth, + height: m.node().offsetHeight + }); + }; + + d3.select(document).on('keydown', function() { + // console.log(d3.event); + // cmd-z + if (d3.event.which === 90 && d3.event.metaKey) { + map.undo(); + } + // cmd-shift-z + if (d3.event.which === 90 && d3.event.metaKey && d3.event.shiftKey) { + map.redo(); + } + // p + if (d3.event.which === 80) controller.go(iD.actions.AddPlace); + // r + if (d3.event.which === 82) controller.go(iD.actions.AddRoad); + // a + if (d3.event.which === 65) controller.go(iD.actions.AddArea); + }); + + var hash = iD.Hash().map(map); + if (!hash.hadHash) { + map.setZoom(19).setCenter({ + lat: 51.87502, + lon: -1.49475 + }); + } + + if (oauth.authenticated()) { + oauth.xhr({ method: 'GET', path: '/user/details' }, function(user_details) { + var u = user_details.getElementsByTagName('user')[0]; + map.connection.user({ + display_name: u.attributes.display_name.nodeValue, + id: u.attributes.id.nodeValue + }); + d3.select('.messages').text('logged in as ' + + map.connection.user().display_name); + }); + } +}; iD.supported = function() { if (navigator.appName !== 'Microsoft Internet Explorer') { From 22e9eb39cce83fce141910c184ec8c5ecdf7e371 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Sat, 24 Nov 2012 13:07:23 -0400 Subject: [PATCH 2/5] Inject connection dependency in map --- js/iD/id.js | 10 ++++++---- js/iD/renderer/Map.js | 10 +--------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/js/iD/id.js b/js/iD/id.js index 849b50113..21aa6319f 100644 --- a/js/iD/id.js +++ b/js/iD/id.js @@ -4,8 +4,10 @@ var iD = function(container) { var m = container.append('div') .attr('id', 'map'); - var map = iD.Map(m.node()) - .setAPI('http://api06.dev.openstreetmap.org/api/0.6/'); + var connection = iD.Connection() + .url('http://api06.dev.openstreetmap.org/api/0.6/'); + + var map = iD.Map(m.node(), connection); var controller = iD.Controller(map); @@ -137,12 +139,12 @@ var iD = function(container) { if (oauth.authenticated()) { oauth.xhr({ method: 'GET', path: '/user/details' }, function(user_details) { var u = user_details.getElementsByTagName('user')[0]; - map.connection.user({ + connection.user({ display_name: u.attributes.display_name.nodeValue, id: u.attributes.id.nodeValue }); d3.select('.messages').text('logged in as ' + - map.connection.user().display_name); + connection.user().display_name); }); } }; diff --git a/js/iD/renderer/Map.js b/js/iD/renderer/Map.js index 38494cba1..2615ce1ec 100644 --- a/js/iD/renderer/Map.js +++ b/js/iD/renderer/Map.js @@ -1,4 +1,4 @@ -iD.Map = function(elem) { +iD.Map = function(elem, connection) { if (!iD.supported()) { elem.innerHTML = 'This editor is supported in Firefox, Chrome, Safari, Opera, ' + @@ -12,7 +12,6 @@ iD.Map = function(elem) { dimensions = { width: null, height: null }, dispatch = d3.dispatch('move', 'update'), history = iD.History(), - connection = iD.Connection(), inspector = iD.Inspector(), parent = d3.select(elem), selection = null, @@ -535,11 +534,6 @@ iD.Map = function(elem) { return map; } - function setAPI(x) { - connection.url(x); - return map; - } - function commit() { var modified = _.filter(history.graph().entities, function(e) { return e.modified; @@ -583,10 +577,8 @@ iD.Map = function(elem) { map.zoomIn = zoomIn; map.zoomOut = zoomOut; - map.connection = connection; map.projection = projection; map.setSize = setSize; - map.setAPI = setAPI; map.history = history; map.surface = surface; From efb521aeb532892e298acc6a9d0762f198370d8e Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Sat, 24 Nov 2012 13:30:31 -0400 Subject: [PATCH 3/5] Move oauth details into connection --- js/iD/Connection.js | 50 ++++++++++++++++++++++++++++++++++++++++++- js/iD/OAuth.js | 4 +--- js/iD/id.js | 18 +++++----------- js/iD/renderer/Map.js | 24 ++------------------- 4 files changed, 57 insertions(+), 39 deletions(-) diff --git a/js/iD/Connection.js b/js/iD/Connection.js index 24760a66b..55a6aa8bf 100644 --- a/js/iD/Connection.js +++ b/js/iD/Connection.js @@ -2,7 +2,8 @@ iD.Connection = function() { var apiURL = 'http://www.openstreetmap.org/api/0.6/', connection = {}, refNodes = {}, - user = {}; + user = {}, + oauth = iD.OAuth().setAPI(apiURL); // Request data within the bbox from an external OSM server. function bboxFromAPI(box, callback) { @@ -88,9 +89,52 @@ iD.Connection = function() { return iD.Graph(entities); } + function authenticate(callback) { + return oauth.authenticate(callback); + } + + function authenticated() { + return oauth.authenticated(); + } + + function createChangeset(modified) { + oauth.xhr({ + method: 'PUT', + path: '/changeset/create', + options: { header: { 'Content-Type': 'text/xml' } }, + content: iD.format.XML.changeset() + }, + function (changeset_id) { + oauth.xhr({ + method: 'POST', + path: '/changeset/' + changeset_id + '/upload', + options: { header: { 'Content-Type': 'text/xml' } }, + content: iD.format.XML.osmChange(user.id, changeset_id, modified) + }, function () { + oauth.xhr({ + method: 'PUT', + path: '/changeset/' + changeset_id + '/close' + }, function () { + alert('saved! ' + apiURL.replace('/api/0.6/', '/browse') + '/changeset/' + changeset_id); + }); + }); + }); + } + + function userDetails(callback) { + oauth.xhr({ method: 'GET', path: '/user/details' }, function(user_details) { + var u = user_details.getElementsByTagName('user')[0]; + callback({ + display_name: u.attributes.display_name.nodeValue, + id: u.attributes.id.nodeValue + }); + }); + } + connection.url = function(x) { if (!arguments.length) return apiURL; apiURL = x; + oauth.setAPI(x); return connection; }; @@ -103,6 +147,10 @@ iD.Connection = function() { connection.bboxFromAPI = bboxFromAPI; connection.wayFromAPI = wayFromAPI; connection.loadFromURL = loadFromURL; + connection.userDetails = userDetails; + connection.authenticate = authenticate; + connection.authenticated = authenticated; + connection.createChangeset = createChangeset; connection.objectData = objectData; connection.apiURL = apiURL; diff --git a/js/iD/OAuth.js b/js/iD/OAuth.js index c6b1218db..85bbdf78a 100644 --- a/js/iD/OAuth.js +++ b/js/iD/OAuth.js @@ -1,4 +1,4 @@ -iD.OAuth = function(map) { +iD.OAuth = function() { var baseurl = 'http://api06.dev.openstreetmap.org', apibase = 'http://api06.dev.openstreetmap.org/api/0.6', oauth_secret = 'aMnOOCwExO2XYtRVWJ1bI9QOdqh1cay2UgpbhA6p', @@ -90,7 +90,5 @@ iD.OAuth = function(map) { return oauth; }; - map.oauth = oauth; - return oauth; }; diff --git a/js/iD/id.js b/js/iD/id.js index 21aa6319f..d2498bcbb 100644 --- a/js/iD/id.js +++ b/js/iD/id.js @@ -11,9 +11,6 @@ var iD = function(container) { var controller = iD.Controller(map); - var oauth = iD.OAuth(map) - .setAPI('http://api06.dev.openstreetmap.org/api/0.6'); - var bar = container.append('div') .attr('id', 'bar'); @@ -75,7 +72,7 @@ var iD = function(container) { .attr('id', 'save') .html("Save") .on('click', function() { - oauth.authenticate(function() { + connection.authenticate(function() { map.commit(); }); }); @@ -136,15 +133,10 @@ var iD = function(container) { }); } - if (oauth.authenticated()) { - oauth.xhr({ method: 'GET', path: '/user/details' }, function(user_details) { - var u = user_details.getElementsByTagName('user')[0]; - connection.user({ - display_name: u.attributes.display_name.nodeValue, - id: u.attributes.id.nodeValue - }); - d3.select('.messages').text('logged in as ' + - connection.user().display_name); + if (connection.authenticated()) { + connection.userDetails(function(user_details) { + connection.user(user_details); + d3.select('.messages').text('logged in as ' + user_details.display_name); }); } }; diff --git a/js/iD/renderer/Map.js b/js/iD/renderer/Map.js index 2615ce1ec..c6cf90b2b 100644 --- a/js/iD/renderer/Map.js +++ b/js/iD/renderer/Map.js @@ -535,29 +535,9 @@ iD.Map = function(elem, connection) { } function commit() { - var modified = _.filter(history.graph().entities, function(e) { + connection.createChangeset(_.filter(history.graph().entities, function(e) { return e.modified; - }); - var userid = connection.user().id; - map.oauth.xhr({ - method: 'PUT', - path: '/changeset/create', - options: { header: { 'Content-Type': 'text/xml' } }, - content: iD.format.XML.changeset() }, function(changeset_id) { - map.oauth.xhr({ - method: 'POST', - path: '/changeset/' + changeset_id + '/upload', - options: { header: { 'Content-Type': 'text/xml' } }, - content: iD.format.XML.osmChange(userid, changeset_id, modified) - }, function() { - map.oauth.xhr({ - method: 'PUT', - path: '/changeset/' + changeset_id + '/close' - }, function() { - alert('saved! ' + connection.url().replace('/api/0.6/', '/browse') + '/changeset/' + changeset_id); - }); - }); - }); + })); } map.handleDrag = handleDrag; From ca38da7cbdb44b4ee0f368ab8e5b201ca569146d Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Sun, 25 Nov 2012 12:12:16 -0400 Subject: [PATCH 4/5] Extract Graph#modifications --- js/iD/graph/Graph.js | 6 ++++++ js/iD/renderer/Map.js | 4 +--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/js/iD/graph/Graph.js b/js/iD/graph/Graph.js index daaf1b5f0..c2e44f259 100644 --- a/js/iD/graph/Graph.js +++ b/js/iD/graph/Graph.js @@ -84,5 +84,11 @@ iD.Graph.prototype = { } entity.nodes = nodes; return entity; + }, + + modifications: function() { + _.filter(this.entities, function(entity) { + return entity.modified; + }); } }; diff --git a/js/iD/renderer/Map.js b/js/iD/renderer/Map.js index c6cf90b2b..269dc1d19 100644 --- a/js/iD/renderer/Map.js +++ b/js/iD/renderer/Map.js @@ -535,9 +535,7 @@ iD.Map = function(elem, connection) { } function commit() { - connection.createChangeset(_.filter(history.graph().entities, function(e) { - return e.modified; - })); + connection.createChangeset(history.graph().modifications()); } map.handleDrag = handleDrag; From e022e5f85b8d60a8ddc75935e0bde0f641f2d654 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Sun, 25 Nov 2012 13:30:04 -0400 Subject: [PATCH 5/5] Fix/improve UI feedback on undo/redo buttons --- css/app.css | 9 +++++++-- js/iD/graph/History.js | 16 ++++++++++++++++ js/iD/id.js | 21 +++++++++++++++++++-- js/iD/renderer/Map.js | 15 +++++++-------- 4 files changed, 49 insertions(+), 12 deletions(-) diff --git a/css/app.css b/css/app.css index 2de090677..83bf1fa33 100644 --- a/css/app.css +++ b/css/app.css @@ -84,11 +84,16 @@ input[type=text]:focus { padding:10px; } -#bar button:hover { +#bar button[disabled] { + color:#eee; + cursor:auto; +} + +#bar button:hover:not([disabled]) { background:#eee; } -#bar button.active { +#bar button.active:not([disabled]) { background:#eee; color:#000; } diff --git a/js/iD/graph/History.js b/js/iD/graph/History.js index 6cd188e0d..d40be99e3 100644 --- a/js/iD/graph/History.js +++ b/js/iD/graph/History.js @@ -38,5 +38,21 @@ iD.History.prototype = { this.index++; if (this.stack[this.index].annotation) break; } + }, + + undoAnnotation: function() { + var index = this.index; + while (index >= 0) { + if (this.stack[index].annotation) return this.stack[index].annotation; + index--; + } + }, + + redoAnnotation: function() { + var index = this.index + 1; + while (index <= this.stack.length - 1) { + if (this.stack[index].annotation) return this.stack[index].annotation; + index++; + } } }; diff --git a/js/iD/id.js b/js/iD/id.js index d2498bcbb..cce77684e 100644 --- a/js/iD/id.js +++ b/js/iD/id.js @@ -38,13 +38,15 @@ var iD = function(container) { bar.append('button') .attr('id', 'undo') .attr('class', 'mini') - .html('← ') + .property('disabled', true) + .html('←') .on('click', map.undo); bar.append('button') .attr('id', 'redo') .attr('class', 'mini') - .html('→ ') + .property('disabled', true) + .html('→') .on('click', map.redo); bar.append('input') @@ -100,6 +102,21 @@ var iD = function(container) { "docs." + "Imagery © 2012 Bing, GeoEye, Getmapping, Intermap, Microsoft.

"); + map.on('update', function() { + var undo = map.history.undoAnnotation(), + redo = map.history.redoAnnotation(); + + bar.select('#undo') + .property('disabled', !undo) + .select('small') + .text(undo); + + bar.select('#redo') + .property('disabled', !redo) + .select('small') + .text(redo); + }); + window.onresize = function() { map.setSize({ width: m.node().offsetWidth, diff --git a/js/iD/renderer/Map.js b/js/iD/renderer/Map.js index 269dc1d19..e554ada4f 100644 --- a/js/iD/renderer/Map.js +++ b/js/iD/renderer/Map.js @@ -39,7 +39,7 @@ iD.Map = function(elem, connection) { only[entity.id] = true; redraw(only); }) - .on('dragend', map.update), + .on('dragend', update), nodeline = function(d) { return 'M' + d.nodes.map(ll2a).map(projection).map(roundCoords).join('L'); }, @@ -446,26 +446,25 @@ iD.Map = function(elem, connection) { // UI elements // ----------- - var undolabel = d3.select('button#undo small'); - dispatch.on('update', function() { - undolabel.text(history.graph().annotation); + function update() { + map.update(); redraw(); - }); + } function _do(operation) { history.operate(operation); - map.update(); + update(); } // Undo/redo function undo() { history.undo(); - map.update(); + update(); } function redo() { history.redo(); - map.update(); + update(); } // Getters & setters for map state