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/index.html b/index.html index 61ea4924f..fa2f27297 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/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/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/graph/History.js b/js/iD/graph/History.js index eafc2acd4..87a6b7d3f 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 9041b915c..cce77684e 100644 --- a/js/iD/id.js +++ b/js/iD/id.js @@ -1,4 +1,162 @@ -if (typeof iD === 'undefined') var iD = {}; +var iD = function(container) { + container = d3.select(container); + + var m = container.append('div') + .attr('id', 'map'); + + 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); + + 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') + .property('disabled', true) + .html('←') + .on('click', map.undo); + + bar.append('button') + .attr('id', 'redo') + .attr('class', 'mini') + .property('disabled', true) + .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() { + connection.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.

"); + + 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, + 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 (connection.authenticated()) { + connection.userDetails(function(user_details) { + connection.user(user_details); + d3.select('.messages').text('logged in as ' + user_details.display_name); + }); + } +}; iD.supported = function() { if (navigator.appName !== 'Microsoft Internet Explorer') { diff --git a/js/iD/renderer/Map.js b/js/iD/renderer/Map.js index 6d02c5f1a..470bcf023 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, @@ -40,7 +39,7 @@ iD.Map = function(elem) { 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'); }, @@ -412,18 +411,16 @@ iD.Map = function(elem) { .scale(d3.event.scale); if (fast) { if (!translateStart) translateStart = d3.mouse(document.body).slice(); - fastPan(d3.mouse(document.body), translateStart); + var a = d3.mouse(document.body), + b = translateStart; + surface.style(transformProp, + 'translate3d(' + (a[0] - b[0]) + 'px,' + (a[1] - b[1]) + 'px, 0px)'); } else { redraw(); translateStart = null; } } - function fastPan(a, b) { - surface.style(transformProp, - 'translate3d(' + (a[0] - b[0]) + 'px,' + (a[1] - b[1]) + 'px, 0px)'); - } - surface.on('mouseup', function() { if (surface.style(transformProp)) { translateStart = null; @@ -447,26 +444,30 @@ iD.Map = function(elem) { // UI elements // ----------- - var undolabel = d3.select('button#undo small'); - dispatch.on('update', function() { - undolabel.text(history.graph().annotation); + function update() { + map.update(); redraw(); - }); + } function perform(action) { history.perform(action); map.update(); } + function _do(operation) { + history.operate(operation); + update(); + } + // Undo/redo function undo() { history.undo(); - map.update(); + update(); } function redo() { history.redo(); - map.update(); + update(); } // Getters & setters for map state @@ -535,35 +536,8 @@ 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; - }); - 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); - }); - }); - }); + connection.createChangeset(history.graph().modifications()); } map.handleDrag = handleDrag; @@ -583,10 +557,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;