From b3765a4467f9236d4bd051a59d3992ca447379ae Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Tue, 22 Jan 2013 09:35:34 -0500 Subject: [PATCH 1/6] Microbranching jshint stuff. --- js/id/modes/select.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/js/id/modes/select.js b/js/id/modes/select.js index 8d59fbe43..1de8ab0f7 100644 --- a/js/id/modes/select.js +++ b/js/id/modes/select.js @@ -138,7 +138,9 @@ iD.modes.Select = function (entity) { mode.exit = function () { var surface = mode.map.surface; - entity && changeTags(entity, inspector.tags()); + if (entity) { + changeTags(entity, inspector.tags()); + } d3.select('.inspector-wrap') .style('display', 'none') .html(''); From 7cf234365e85dbdf1f690ebc683e5abf0cf1bd5b Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Tue, 22 Jan 2013 09:41:29 -0500 Subject: [PATCH 2/6] Non-overwriting hash, add hash in select mode. Needs code for dealing with hash. --- js/id/modes/select.js | 8 ++++++++ js/id/renderer/hash.js | 13 ++++++++----- js/id/util.js | 5 +++-- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/js/id/modes/select.js b/js/id/modes/select.js index 1de8ab0f7..505aff07a 100644 --- a/js/id/modes/select.js +++ b/js/id/modes/select.js @@ -47,6 +47,11 @@ iD.modes.Select = function (entity) { behavior(surface); }); + var q = iD.util.stringQs(location.hash.substring(1)); + location.hash = '#' + iD.util.qsString(_.assign(q, { + id: entity.id + }), true); + d3.select('.inspector-wrap') .style('display', 'block') .style('opacity', 1) @@ -149,6 +154,9 @@ iD.modes.Select = function (entity) { behavior.off(surface); }); + var q = iD.util.stringQs(location.hash.substring(1)); + location.hash = '#' + iD.util.qsString(_.omit(q, 'id'), true); + surface.on("click.select", null); mode.map.keybinding().on('⌫.select', null); mode.history.on('change.entity-undone', null); diff --git a/js/id/renderer/hash.js b/js/id/renderer/hash.js index bb454811b..652e64f20 100644 --- a/js/id/renderer/hash.js +++ b/js/id/renderer/hash.js @@ -1,6 +1,6 @@ iD.Hash = function() { var hash = { hadHash: false }, - s0, // cached location.hash + s0 = null, // cached location.hash lat = 90 - 1e-8, // allowable latitude range map; @@ -20,9 +20,12 @@ iD.Hash = function() { var center = map.center(), zoom = map.zoom(), precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)); - return '#?map=' + zoom.toFixed(2) + - '/' + center[1].toFixed(precision) + - '/' + center[0].toFixed(precision); + var q = iD.util.stringQs(location.hash.substring(1)); + return '#' + iD.util.qsString(_.assign(q, { + map: zoom.toFixed(2) + + '/' + center[1].toFixed(precision) + + '/' + center[0].toFixed(precision) + }), true); }; var move = _.throttle(function() { @@ -32,7 +35,7 @@ iD.Hash = function() { function hashchange() { if (location.hash === s0) return; // ignore spurious hashchange events - if (parser(map, (s0 = location.hash).substring(2))) { + if (parser(map, (s0 = location.hash).substring(1))) { move(); // replace bogus hash } } diff --git a/js/id/util.js b/js/id/util.js index 79b65c524..b3ae537ec 100644 --- a/js/id/util.js +++ b/js/id/util.js @@ -30,9 +30,10 @@ iD.util.stringQs = function(str) { }, {}); }; -iD.util.qsString = function(obj) { +iD.util.qsString = function(obj, noencode) { return Object.keys(obj).sort().map(function(key) { - return encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]); + return encodeURIComponent(key) + '=' + ( + noencode ? obj[key] : encodeURIComponent(obj[key])); }).join('&'); }; From 16ba8af4998da766dc5eb28aea7bded4b50e355e Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Tue, 22 Jan 2013 09:48:30 -0500 Subject: [PATCH 3/6] Fix tests for non-question hash --- test/spec/renderer/hash.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/spec/renderer/hash.js b/test/spec/renderer/hash.js index 44d3c64a2..9de281453 100644 --- a/test/spec/renderer/hash.js +++ b/test/spec/renderer/hash.js @@ -24,13 +24,13 @@ describe("hash", function () { }); it("sets hadHash if location.hash is present", function () { - location.hash = "?map=20.00/38.87952/-77.02405"; + location.hash = "map=20.00/38.87952/-77.02405"; hash.map(map); expect(hash.hadHash).to.be.true; }); it("centerZooms map to requested level", function () { - location.hash = "?map=20.00/38.87952/-77.02405"; + location.hash = "map=20.00/38.87952/-77.02405"; sinon.spy(map, 'centerZoom'); hash.map(map); expect(map.centerZoom).to.have.been.calledWith([-77.02405,38.87952], 20.0); @@ -67,7 +67,7 @@ describe("hash", function () { }); sinon.spy(map, 'centerZoom'); - location.hash = "#?map=20.00/38.87952/-77.02405"; + location.hash = "#map=20.00/38.87952/-77.02405"; }); }); @@ -75,7 +75,7 @@ describe("hash", function () { it("stores the current zoom and coordinates in location.hash", function () { sinon.stub(map, 'on').yields(); hash.map(map); - expect(location.hash).to.equal("#?map=0.00/0/0"); + expect(location.hash).to.equal("#map=0.00/0/0"); }); }); }); From 3d90801e63b0bbf756f1287971917b0fc2ed2ec3 Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Tue, 22 Jan 2013 10:22:00 -0500 Subject: [PATCH 4/6] Add willselect to hash, works with non-line features. --- js/id/id.js | 2 +- js/id/modes/select.js | 24 +++++++++++++----------- js/id/renderer/hash.js | 31 +++++++++++++++++++++++++++++++ js/id/renderer/map.js | 42 +++++++++++++++++++++--------------------- 4 files changed, 66 insertions(+), 33 deletions(-) diff --git a/js/id/id.js b/js/id/id.js index 4d3948107..3d9004bfb 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -202,7 +202,7 @@ window.iD = function(container) { if (mods === '⌘' || mods === '⌃') history.undo(); }); - var hash = iD.Hash().map(map); + var hash = iD.Hash().controller(controller).map(map); if (!hash.hadHash) { map.centerZoom([-77.02271, 38.90085], 20); diff --git a/js/id/modes/select.js b/js/id/modes/select.js index 505aff07a..3b9667de3 100644 --- a/js/id/modes/select.js +++ b/js/id/modes/select.js @@ -1,4 +1,4 @@ -iD.modes.Select = function (entity) { +iD.modes.Select = function(entity) { var mode = { id: 'select', button: 'browse', @@ -34,7 +34,7 @@ iD.modes.Select = function (entity) { } } - mode.enter = function () { + mode.enter = function() { var surface = mode.map.surface; behaviors = [ @@ -58,16 +58,18 @@ iD.modes.Select = function (entity) { .datum(entity) .call(inspector); - // Pan the map if the clicked feature intersects with the position - // of the inspector - var inspector_size = d3.select('.inspector-wrap').size(), - map_size = mode.map.size(), - offset = 50, - shift_left = d3.event.x - map_size[0] + inspector_size[0] + offset, - center = (map_size[0] / 2) + shift_left + offset; + if (d3.event) { + // Pan the map if the clicked feature intersects with the position + // of the inspector + var inspector_size = d3.select('.inspector-wrap').size(), + map_size = mode.map.size(), + offset = 50, + shift_left = d3.event.x - map_size[0] + inspector_size[0] + offset, + center = (map_size[0] / 2) + shift_left + offset; - if (shift_left > 0 && inspector_size[1] > d3.event.y) { - mode.map.centerEase(mode.map.projection.invert([center, map_size[1]/2])); + if (shift_left > 0 && inspector_size[1] > d3.event.y) { + mode.map.centerEase(mode.map.projection.invert([center, map_size[1]/2])); + } } inspector diff --git a/js/id/renderer/hash.js b/js/id/renderer/hash.js index 652e64f20..81da12d90 100644 --- a/js/id/renderer/hash.js +++ b/js/id/renderer/hash.js @@ -2,6 +2,7 @@ iD.Hash = function() { var hash = { hadHash: false }, s0 = null, // cached location.hash lat = 90 - 1e-8, // allowable latitude range + controller, map; var parser = function(map, s) { @@ -40,6 +41,32 @@ iD.Hash = function() { } } + // the hash can declare that the map should select a feature, but it can + // do so before any features are loaded. thus wait for the feature to + // be loaded and then select + function willselect(id) { + map.on('drawn.after-draw-select', function() { + var entity = map.history().graph().entity(id); + if (entity === undefined) return; + else selectoff(); + controller.enter(iD.modes.Select(entity)); + map.on('drawn.after-draw-select', null); + }); + controller.on('enter', function() { + if (controller.mode.id !== 'browse') selectoff(); + }); + } + + function selectoff() { + map.on('drawn.after-draw-select', null); + } + + hash.controller = function(_) { + if (!arguments.length) return controller; + controller = _; + return hash; + }; + hash.map = function(x) { if (!arguments.length) return map; if (map) { @@ -51,6 +78,10 @@ iD.Hash = function() { map.on("move", move); window.addEventListener("hashchange", hashchange, false); if (location.hash) { + var q = iD.util.stringQs(location.hash.substring(1)); + if (q.id) { + willselect(q.id); + } hashchange(); hash.hadHash = true; } diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index 3f55f7271..c2de92855 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -1,7 +1,7 @@ iD.Map = function() { var connection, history, dimensions = [], - dispatch = d3.dispatch('move'), + dispatch = d3.dispatch('move', 'drawn'), translateStart, keybinding = d3.keybinding(), projection = d3.geo.mercator().scale(1024), @@ -65,22 +65,22 @@ iD.Map = function() { extent = map.extent(), graph = history.graph(); + function addParents(parents) { + for (var i = 0; i < parents.length; i++) { + var parent = parents[i]; + if (only[parent.id] === undefined) { + only[parent.id] = graph.fetch(parent.id); + addParents(graph.parentRelations(parent)); + } + } + } + if (!difference) { all = graph.intersects(extent); filter = d3.functor(true); } else { var only = {}; - function addParents(parents) { - for (var i = 0; i < parents.length; i++) { - var parent = parents[i]; - if (only[parent.id] === undefined) { - only[parent.id] = graph.fetch(parent.id); - addParents(graph.parentRelations(parent)); - } - } - } - for (var j = 0; j < difference.length; j++) { var id = difference[j], entity = graph.fetch(id); @@ -101,16 +101,16 @@ iD.Map = function() { if (all.length > 100000) { editOff(); - return; + } else { + surface + .call(points, graph, all, filter) + .call(vertices, graph, all, filter) + .call(lines, graph, all, filter) + .call(areas, graph, all, filter) + .call(multipolygons, graph, all, filter) + .call(midpoints, graph, all, filter); } - - surface - .call(points, graph, all, filter) - .call(vertices, graph, all, filter) - .call(lines, graph, all, filter) - .call(areas, graph, all, filter) - .call(multipolygons, graph, all, filter) - .call(midpoints, graph, all, filter); + dispatch.drawn(map); } function editOff() { @@ -302,7 +302,7 @@ iD.Map = function() { map.connection = function(_) { if (!arguments.length) return connection; connection = _; - connection.on('load', connectionLoad); + connection.on('load.tile', connectionLoad); return map; }; From 38c02509eadd6e9ff8fa62c48e0e9450d375068d Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Tue, 22 Jan 2013 10:39:10 -0500 Subject: [PATCH 5/6] Do not tolerate modifiers for action shortcuts. Tolerates id-equality for selecting features. --- js/id/id.js | 3 +++ js/id/modes/select.js | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/js/id/id.js b/js/id/id.js index 3d9004bfb..31ff26836 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -186,15 +186,18 @@ window.iD = function(container) { map.keybinding() .on('a', function(evt, mods) { + if (mods) return; controller.enter(iD.modes.AddArea()); }) .on('⌫.prevent_navigation', function(evt, mods) { evt.preventDefault(); }) .on('p', function(evt, mods) { + if (mods) return; controller.enter(iD.modes.AddPoint()); }) .on('l', function(evt, mods) { + if (mods) return; controller.enter(iD.modes.AddLine()); }) .on('z', function(evt, mods) { diff --git a/js/id/modes/select.js b/js/id/modes/select.js index 3b9667de3..9397b1e98 100644 --- a/js/id/modes/select.js +++ b/js/id/modes/select.js @@ -138,7 +138,9 @@ iD.modes.Select = function(entity) { }); surface.selectAll("*") - .filter(function (d) { return d === entity; }) + .filter(function (d) { + return d && entity && d.id === entity.id; + }) .classed('selected', true); }; From d214c651f274b977238007c76ee0ad8299644a6d Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Tue, 22 Jan 2013 10:42:16 -0500 Subject: [PATCH 6/6] Fix tests for hash updates --- test/spec/renderer/hash.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/spec/renderer/hash.js b/test/spec/renderer/hash.js index 9de281453..1304602f1 100644 --- a/test/spec/renderer/hash.js +++ b/test/spec/renderer/hash.js @@ -1,5 +1,5 @@ describe("hash", function () { - var hash, map; + var hash, map, controller; beforeEach(function () { hash = iD.Hash(); @@ -7,8 +7,12 @@ describe("hash", function () { on: function () { return map; }, off: function () { return map; }, zoom: function () { return arguments.length ? map : 0; }, - center: function () { return arguments.length ? map : [0, 0] }, - centerZoom: function () { return arguments.length ? map : [0, 0] } + center: function () { return arguments.length ? map : [0, 0]; }, + centerZoom: function () { return arguments.length ? map : [0, 0]; } + }; + controller = { + on: function () { return controller; }, + off: function () { return controller; } }; }); @@ -19,7 +23,7 @@ describe("hash", function () { describe("#map()", function () { it("gets and sets map", function () { - expect(hash.map(map)).to.equal(hash); + expect(hash.controller(controller).map(map)).to.equal(hash); expect(hash.map()).to.equal(map); });