From aca0098ea098d53a7191aa98297cb77187fba633 Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Wed, 2 Jan 2013 13:01:18 -0500 Subject: [PATCH 1/6] Use 2d transforms and fix jshint problems --- js/id/behavior/drag.js | 2 +- js/id/id.js | 4 ++-- js/id/renderer/map.js | 38 +++++++++++++++----------------------- js/id/ui/commit.js | 8 ++++---- js/id/ui/notice.js | 2 +- js/id/ui/tag_reference.js | 4 ++-- js/id/ui/userpanel.js | 2 +- js/id/util.js | 14 -------------- 8 files changed, 26 insertions(+), 48 deletions(-) diff --git a/js/id/behavior/drag.js b/js/id/behavior/drag.js index fefcd2c99..fcf31fb14 100644 --- a/js/id/behavior/drag.js +++ b/js/id/behavior/drag.js @@ -123,7 +123,7 @@ iD.behavior.drag = function () { return mousedown.call(target, target.__data__); } } - } + }; } selection.on("mousedown.drag" + selector, delegate) diff --git a/js/id/id.js b/js/id/id.js index 12219b38a..c041e21f9 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -170,7 +170,7 @@ window.iD = function(container) { .attr('class', 'inspector-wrap fillL') .style('display', 'none'); - var about = container.append('div') + var about = container.append('div'); about.append('ul') .attr('id','about') @@ -226,7 +226,7 @@ window.iD = function(container) { if (!hash.hadHash) { map.zoom(20) - .center([-77.02405, 38.87952]); + .center([-77.02271,38.90085]); } d3.select('.user-container').call(iD.userpanel(connection) diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index afc2a5f46..60cfad861 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -20,7 +20,6 @@ iD.Map = function() { class_casing = iD.Style.styleClasses('way line casing'), class_area = iD.Style.styleClasses('way area'), transformProp = iD.util.prefixCSSProperty('Transform'), - support3d = iD.util.support3d(), supersurface, surface, defs, tilegroup, r, g, alength; function map() { @@ -141,16 +140,16 @@ iD.Map = function() { function drawVertices(vertices, parentStructure, filter) { function shared(d) { return parentStructure[d.id] > 1; } - var vertices = g.hit.selectAll('circle.vertex') + var circles = g.hit.selectAll('circle.vertex') .filter(filter) .data(vertices, key); - vertices.exit().remove(); + circles.exit().remove(); - vertices.enter().insert('circle', ':first-child') + circles.enter().insert('circle', ':first-child') .attr('class', 'node vertex'); - vertices.attr('transform', function(entity) { + circles.attr('transform', function(entity) { var p = projection(entity.loc); return 'translate(' + [~~p[0], ~~p[1]] + ')'; @@ -158,7 +157,7 @@ iD.Map = function() { .classed('shared', shared) .classed('hover', classHover); - vertices.transition().duration(50).attr('r', function(d) { + circles.transition().duration(50).attr('r', function(d) { return d.id === hover ? 8: 4; }); } @@ -209,22 +208,22 @@ iD.Map = function() { } function drawPoints(points, filter) { - var points = g.hit.selectAll('g.point') + var groups = g.hit.selectAll('g.point') .filter(filter) .data(points, key); - points.exit().remove(); - var group = points.enter().append('g') + groups.exit().remove(); + var group = groups.enter().append('g') .attr('class', 'node point'); group.append('circle') .attr({ r: 10, cx: 8, cy: 8 }); group.append('image') .attr({ width: 16, height: 16 }); - points.attr('transform', function(d) { + groups.attr('transform', function(d) { var pt = projection(d.loc); return 'translate(' + [~~pt[0], ~~pt[1]] + ') translate(-8, -8)'; }); - points.classed('hover', classHover); - points.select('image').attr('xlink:href', iD.Style.pointImage); + groups.classed('hover', classHover); + groups.select('image').attr('xlink:href', iD.Style.pointImage); } function drawStrokes(ways, filter) { @@ -299,17 +298,10 @@ iD.Map = function() { if (!translateStart) translateStart = d3.event.translate.slice(); var a = d3.event.translate, b = translateStart; - if (support3d) { - tilegroup.style(transformProp, - 'translate3d(' + ~~(a[0] - b[0]) + 'px,' + ~~(a[1] - b[1]) + 'px, 0px)'); - surface.style(transformProp, - 'translate3d(' + ~~(a[0] - b[0]) + 'px,' + ~~(a[1] - b[1]) + 'px, 0px)'); - } else { - tilegroup.style(transformProp, - 'translate(' + ~~(a[0] - b[0]) + 'px,' + ~~(a[1] - b[1]) + 'px)'); - surface.style(transformProp, - 'translate(' + ~~(a[0] - b[0]) + 'px,' + ~~(a[1] - b[1]) + 'px)'); - } + tilegroup.style(transformProp, + 'translate(' + ~~(a[0] - b[0]) + 'px,' + ~~(a[1] - b[1]) + 'px)'); + surface.style(transformProp, + 'translate(' + ~~(a[0] - b[0]) + 'px,' + ~~(a[1] - b[1]) + 'px)'); } else { redraw(); translateStart = null; diff --git a/js/id/ui/commit.js b/js/id/ui/commit.js index fa3ba810b..314433e7e 100644 --- a/js/id/ui/commit.js +++ b/js/id/ui/commit.js @@ -24,15 +24,15 @@ iD.commit = function() { comment: d3.select('textarea.changeset-comment').node().value }); }); - savebutton.append('span').attr('class','icon save icon-pre-text') - savebutton.append('span').attr('class','label').text('Save') + savebutton.append('span').attr('class','icon save icon-pre-text'); + savebutton.append('span').attr('class','label').text('Save'); var cancelbutton = buttonwrap.append('button') .attr('class', 'cancel wide') .on('click.cancel', function() { event.cancel(); }); - cancelbutton.append('span').attr('class','icon close icon-pre-text') - cancelbutton.append('span').attr('class','label').text('Cancel') + cancelbutton.append('span').attr('class','icon close icon-pre-text'); + cancelbutton.append('span').attr('class','label').text('Cancel'); var section = body.selectAll('div.commit-section') .data(['modified', 'deleted', 'created'].filter(function(d) { diff --git a/js/id/ui/notice.js b/js/id/ui/notice.js index d1d3eec52..05f3c85b6 100644 --- a/js/id/ui/notice.js +++ b/js/id/ui/notice.js @@ -3,7 +3,7 @@ iD.notice = function(selection) { notice = {}; notice.message = function(_) { - selection.attr('class','inner') + selection.attr('class','inner'); if (!arguments.length) return _; if (!message && _) { selection diff --git a/js/id/ui/tag_reference.js b/js/id/ui/tag_reference.js index 945f03b7b..4835f1eaa 100644 --- a/js/id/ui/tag_reference.js +++ b/js/id/ui/tag_reference.js @@ -23,7 +23,7 @@ iD.tagReference = function(selection) { .attr('class','modal-section'); referenceBody .append('h5') - .text('Description') + .text('Description'); referenceBody .append('p') .text(g('description')); @@ -36,4 +36,4 @@ iD.tagReference = function(selection) { return d.title + ' on wiki.osm.org'; }); }); -}; \ No newline at end of file +}; diff --git a/js/id/ui/userpanel.js b/js/id/ui/userpanel.js index 6543620cd..e04ba0d0c 100644 --- a/js/id/ui/userpanel.js +++ b/js/id/ui/userpanel.js @@ -7,7 +7,7 @@ iD.userpanel = function(connection) { if (connection.authenticated()) { selection.style('display', 'block'); connection.userDetails(function(user_details) { - selection.append('span').attr('class','icon avatar icon-pre-text') + selection.append('span').attr('class','icon avatar icon-pre-text'); selection.append('span') .append('a') .attr('href', connection.url() + '/user/' + diff --git a/js/id/util.js b/js/id/util.js index fb377bf9c..7e2df0d34 100644 --- a/js/id/util.js +++ b/js/id/util.js @@ -90,20 +90,6 @@ iD.util.prefixCSSProperty = function(property) { return false; }; -iD.util.support3d = function() { - // test for translate3d support. Based on https://gist.github.com/3794226 by lorenzopolidori and webinista - var transformProp = iD.util.prefixCSSProperty('Transform'); - var el = document.createElement('div'), - has3d = false; - document.body.insertBefore(el, null); - if (el.style[transformProp] !== undefined) { - el.style[transformProp] = 'translate3d(1px,1px,1px)'; - has3d = window.getComputedStyle(el).getPropertyValue(transformProp); - } - document.body.removeChild(el); - return (has3d && has3d.length>0 && has3d!=="none"); -}; - iD.util.geo = {}; iD.util.geo.roundCoords = function(c) { From 06c3caa1c0a188af3c3aec1e8072a92b1cc54206 Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Wed, 2 Jan 2013 13:26:36 -0500 Subject: [PATCH 2/6] Expand util tests --- js/id/util.js | 4 +++- test/spec/util.js | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/js/id/util.js b/js/id/util.js index 7e2df0d34..f9c515ae5 100644 --- a/js/id/util.js +++ b/js/id/util.js @@ -45,7 +45,9 @@ iD.util.tagText = function(entity) { iD.util.stringQs = function(str) { return str.split('&').reduce(function(obj, pair){ var parts = pair.split('='); - obj[parts[0]] = (null === parts[1]) ? '' : decodeURIComponent(parts[1]); + if (parts.length === 2) { + obj[parts[0]] = (null === parts[1]) ? '' : decodeURIComponent(parts[1]); + } return obj; }, {}); }; diff --git a/test/spec/util.js b/test/spec/util.js index 02d8ca2b1..3b9b547a0 100644 --- a/test/spec/util.js +++ b/test/spec/util.js @@ -6,6 +6,24 @@ describe('Util', function() { expect(iD.util.trueObj([])).to.eql({}); }); + it('#tagText', function() { + expect(iD.util.tagText({})).to.eql(''); + expect(iD.util.tagText({tags:{foo:'bar'}})).to.eql('foo: bar'); + expect(iD.util.tagText({tags:{foo:'bar',two:'three'}})).to.eql('foo: bar\ntwo: three'); + }); + + it('#stringQs', function() { + expect(iD.util.stringQs('foo=bar')).to.eql({foo: 'bar'}); + expect(iD.util.stringQs('foo=bar&one=2')).to.eql({foo: 'bar', one: '2' }); + expect(iD.util.stringQs('')).to.eql({}); + }); + + it('#qsString', function() { + expect(iD.util.qsString({ foo: 'bar' })).to.eql('foo=bar'); + expect(iD.util.qsString({ foo: 'bar', one: 2 })).to.eql('foo=bar&one=2'); + expect(iD.util.qsString({})).to.eql(''); + }); + it('#friendlyName', function() { expect(iD.util.friendlyName({ tags: { name: 'hi' }})).to.equal('hi'); expect(iD.util.friendlyName({ tags: { highway: 'Route 5' }})).to.equal('Route 5'); @@ -13,6 +31,12 @@ describe('Util', function() { }); describe('geo', function() { + describe('#roundCoords', function() { + expect(iD.util.geo.roundCoords([0.1, 1])).to.eql([0, 1]); + expect(iD.util.geo.roundCoords([0, 1])).to.eql([0, 1]); + expect(iD.util.geo.roundCoords([0, 1.1])).to.eql([0, 1]); + }); + describe('#interp', function() { it('interpolates halfway', function() { var a = [0, 0], From 3544bb9d3caee4896e9fd5053ba0d6186c836aab Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Wed, 2 Jan 2013 13:30:14 -0500 Subject: [PATCH 3/6] Add osmId to entity tests --- test/spec/graph/entity.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/spec/graph/entity.js b/test/spec/graph/entity.js index e2790d815..6dd5cafa0 100644 --- a/test/spec/graph/entity.js +++ b/test/spec/graph/entity.js @@ -69,6 +69,14 @@ describe('iD.Entity', function () { }); }); + describe("#osmId", function () { + it("returns a numeric osm id", function () { + expect(iD.Entity({id: 'w1234'}).osmId()).to.eql(1234); + expect(iD.Entity({id: 'n1234'}).osmId()).to.eql(1234); + expect(iD.Entity({id: 'r1234'}).osmId()).to.eql(1234); + }); + }); + describe("#created", function () { it("returns falsy by default", function () { expect(iD.Entity({id: 'w1234'}).created()).not.to.be.ok; From 349367ada427f3275a5c49095b7424ffcb7c8845 Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Wed, 2 Jan 2013 14:17:43 -0500 Subject: [PATCH 4/6] Simplify connection, fix errors for high-zoom lat lon box calculation. Fixes #288 --- js/id/connection.js | 51 +++++++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/js/id/connection.js b/js/id/connection.js index c65bd5dea..9ccbb5255 100644 --- a/js/id/connection.js +++ b/js/id/connection.js @@ -13,9 +13,9 @@ iD.Connection = function() { return url + '/api/0.6/map?bbox=' + [b[0][0],b[1][1],b[1][0],b[0][1]]; } - function bboxFromAPI(box, callback) { + function bboxFromAPI(box, tile, callback) { loadFromURL(bboxUrl(box), function(err, parsed) { - loadedTiles[box] = true; + loadedTiles[tile.toString()] = true; callback(err, parsed); }); } @@ -138,24 +138,14 @@ iD.Connection = function() { }); } - connection.userUrl = function(username) { - return url + "/user/" + username; - }; + function tileAlreadyLoaded(c) { return !loadedTiles[c.toString()]; } - function tileAtZoom(t, distance) { - var power = Math.pow(2, distance); - return [ - Math.floor(t[0] * power), - Math.floor(t[1] * power), - t[2] + distance]; - } + function abortRequest(i) { i.abort(); } - function tileAlreadyLoaded(c) { - if (loadedTiles[c]) return false; - for (var i = 0; i < 4; i++) { - if (loadedTiles[tileAtZoom(c, -i)]) return false; - } - return true; + function loadTile(e) { + bboxFromAPI(e.box, e.tile, function(err, g) { + event.load(err, g); + }); } function loadTiles(projection) { @@ -175,26 +165,27 @@ iD.Connection = function() { function apiExtentBox(c) { var x = (c[0] * ts) - tile_origin[0]; var y = (c[1] * ts) - tile_origin[1]; - return [ - projection.invert([x, y]), - projection.invert([x + ts, y + ts])]; + return { + box: [ + projection.invert([x, y]), + projection.invert([x + ts, y + ts])], + tile: c + }; } - inflight.map(function(i) { - i.abort(); - }); + inflight.map(abortRequest); inflight = []; tiles - .map(apiExtentBox) .filter(tileAlreadyLoaded) - .forEach(function(e) { - bboxFromAPI(e, function(err, g) { - event.load(err, g); - }); - }); + .map(apiExtentBox) + .forEach(loadTile); } + connection.userUrl = function(username) { + return url + "/user/" + username; + }; + connection.url = function(_) { if (!arguments.length) return url; url = _; From 912e61220621ba3551239f9227cf7bd29287f6db Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Wed, 2 Jan 2013 14:22:03 -0500 Subject: [PATCH 5/6] Use proper geocoding endpoint. Fixes #315 --- js/id/ui/geocoder.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/id/ui/geocoder.js b/js/id/ui/geocoder.js index 35b68dd9d..1f11f3e11 100644 --- a/js/id/ui/geocoder.js +++ b/js/id/ui/geocoder.js @@ -6,8 +6,9 @@ iD.geocoder = function() { function keydown() { if (d3.event.keyCode !== 13) return; d3.event.preventDefault(); - d3.json('http://api.tiles.mapbox.com/v3/mapbox/geocode/' + + d3.json('http://api.tiles.mapbox.com/v3/openstreetmap.map-hn253zqn/geocode/' + encodeURIComponent(this.value) + '.json', function(err, resp) { + if (err) return hide(); hide(); map.center([resp.results[0][0].lon, resp.results[0][0].lat]); }); From f5873a3bc1eeb7e9fc77edc591b87061daeb1f3f Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Wed, 2 Jan 2013 14:29:04 -0500 Subject: [PATCH 6/6] Add .hoverEnable to map and disable it for drawing-related modes. Fixes #234 --- js/id/modes/add_area.js | 4 +++- js/id/modes/add_line.js | 4 +++- js/id/modes/add_point.js | 3 +++ js/id/modes/draw_area.js | 4 +++- js/id/modes/draw_line.js | 4 +++- js/id/renderer/map.js | 10 +++++++++- 6 files changed, 24 insertions(+), 5 deletions(-) diff --git a/js/id/modes/add_area.js b/js/id/modes/add_area.js index 33facb041..8051da962 100644 --- a/js/id/modes/add_area.js +++ b/js/id/modes/add_area.js @@ -12,6 +12,7 @@ iD.modes.AddArea = function() { controller = mode.controller; map.dblclickEnable(false) + .hoverEnable(false) .hint('Click on the map to start drawing an area, like a park, lake, or building.'); map.surface.on('click.addarea', function() { @@ -47,7 +48,8 @@ iD.modes.AddArea = function() { mode.exit = function() { window.setTimeout(function() { - mode.map.dblclickEnable(true); + mode.map.dblclickEnable(true) + .hoverEnable(true); }, 1000); mode.map.hint(false); mode.map.surface.on('click.addarea', null); diff --git a/js/id/modes/add_line.js b/js/id/modes/add_line.js index baa4364bc..0c3ef27aa 100644 --- a/js/id/modes/add_line.js +++ b/js/id/modes/add_line.js @@ -13,6 +13,8 @@ iD.modes.AddLine = function() { controller = mode.controller; map.dblclickEnable(false) + .hoverEnable(false) + .hoverEnable(false) .hint('Click on the map to start drawing an road, path, or route.'); map.surface.on('click.addline', function() { @@ -68,7 +70,7 @@ iD.modes.AddLine = function() { }; mode.exit = function() { - mode.map.dblclickEnable(true); + mode.map.dblclickEnable(true).hoverEnable(true); mode.map.hint(false); mode.map.surface.on('click.addline', null); mode.map.keybinding().on('⎋.addline', null); diff --git a/js/id/modes/add_point.js b/js/id/modes/add_point.js index 37767f3de..677830b32 100644 --- a/js/id/modes/add_point.js +++ b/js/id/modes/add_point.js @@ -10,6 +10,8 @@ iD.modes.AddPoint = function() { history = mode.history, controller = mode.controller; + map.hoverEnable(false); + map.hint('Click on the map to add a point.'); map.surface.on('click.addpoint', function() { @@ -28,6 +30,7 @@ iD.modes.AddPoint = function() { }; mode.exit = function() { + map.hoverEnable(true); mode.map.hint(false); mode.map.surface.on('click.addpoint', null); mode.map.keybinding().on('⎋.addpoint', null); diff --git a/js/id/modes/draw_area.js b/js/id/modes/draw_area.js index 00ef6590f..e54163dda 100644 --- a/js/id/modes/draw_area.js +++ b/js/id/modes/draw_area.js @@ -16,6 +16,7 @@ iD.modes.DrawArea = function(wayId) { node = iD.Node({loc: map.mouseCoordinates()}); map.dblclickEnable(false) + .hoverEnable(false) .fastEnable(false); map.hint('Click on the map to add points to your area. Finish the ' + 'area by clicking on your first point'); @@ -125,7 +126,8 @@ iD.modes.DrawArea = function(wayId) { mode.exit = function() { mode.map.hint(false); - mode.map.fastEnable(true); + mode.map.fastEnable(true) + .hoverEnable(true); mode.map.surface .on('mousemove.drawarea', null) diff --git a/js/id/modes/draw_line.js b/js/id/modes/draw_line.js index b7e614012..56c5cfe6b 100644 --- a/js/id/modes/draw_line.js +++ b/js/id/modes/draw_line.js @@ -16,6 +16,7 @@ iD.modes.DrawLine = function(wayId, direction) { map.dblclickEnable(false) .fastEnable(false) + .hoverEnable(false) .hint('Click to add more points to the line. ' + 'Click on other lines to connect to them, and double-click to ' + 'end the line.'); @@ -121,7 +122,8 @@ iD.modes.DrawLine = function(wayId, direction) { mode.exit = function() { mode.map.hint(false); - mode.map.fastEnable(true); + mode.map.fastEnable(true) + .hoverEnable(true); mode.map.surface .on('mousemove.drawline', null) diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index 60cfad861..66727cd06 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -12,6 +12,7 @@ iD.Map = function() { .scaleExtent([1024, 256 * Math.pow(2, 24)]) .on('zoom', zoomPan), dblclickEnabled = true, + hoverEnabled = true, fastEnabled = true, notice, background = iD.Background() @@ -265,6 +266,7 @@ iD.Map = function() { } function hoverIn() { + if (!hoverEnabled) return; var datum = d3.select(d3.event.target).datum(); if (datum instanceof iD.Entity) { hover = datum.id; @@ -274,7 +276,7 @@ iD.Map = function() { } function hoverOut() { - if (hover) { + if (hoverEnabled && hover) { var oldHover = hover; hover = null; redraw([oldHover]); @@ -350,6 +352,12 @@ iD.Map = function() { return map; }; + map.hoverEnable = function(_) { + if (!arguments.length) return hoverEnabled; + hoverEnabled = _; + return map; + }; + map.fastEnable = function(_) { if (!arguments.length) return fastEnabled; fastEnabled = _;