mirror of
https://github.com/FoggedLens/iD.git
synced 2026-02-14 01:33:03 +00:00
Merge branch 'master' of github.com:systemed/iD
This commit is contained in:
1
Makefile
1
Makefile
@@ -17,6 +17,7 @@ all: \
|
||||
js/lib/d3.keybinding.js \
|
||||
js/lib/d3.one.js \
|
||||
js/lib/d3.size.js \
|
||||
js/lib/d3.trigger.js \
|
||||
js/lib/d3.typeahead.js \
|
||||
js/lib/jxon.js \
|
||||
js/lib/lodash.js \
|
||||
|
||||
83
css/app.css
83
css/app.css
@@ -291,6 +291,25 @@ button.delete:hover {
|
||||
|
||||
button.save {
|
||||
background-color: #6bc641;
|
||||
width: 120px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
button.save.has-count {
|
||||
padding-right:10px;
|
||||
}
|
||||
|
||||
button.save.has-count .count {
|
||||
display: block;
|
||||
position: absolute;
|
||||
font-size:small;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background: #fff;
|
||||
height: 37px;
|
||||
border-radius: 0 3px 3px 0;
|
||||
padding: 0 10px;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
button.save:hover {
|
||||
@@ -452,7 +471,7 @@ button.Browse .label {
|
||||
position:relative;
|
||||
}
|
||||
|
||||
.inspector-wrap a.permalink {
|
||||
.inspector-inner.head a {
|
||||
text-decoration:none;
|
||||
margin-right: 10px;
|
||||
display: inline-block
|
||||
@@ -489,7 +508,7 @@ button.Browse .label {
|
||||
|
||||
.tag-row input {
|
||||
width: 50%;
|
||||
border-right: 0;
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
.tag-row input.key {
|
||||
@@ -500,9 +519,8 @@ button.Browse .label {
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.tag-row input.value {
|
||||
position: relative;
|
||||
border-right: 1px solid #ccc;
|
||||
.tag-row input.key {
|
||||
border-left: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.input-wrap::after {
|
||||
@@ -531,8 +549,17 @@ button.Browse .label {
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.tag-row-empty button {
|
||||
display: none;
|
||||
.inspector-inner .add-tag-row {
|
||||
width: 100%;
|
||||
padding-right: 70px;
|
||||
}
|
||||
|
||||
.inspector-inner .add-tag {
|
||||
width: 50%;
|
||||
height: 30px;
|
||||
font-size: 100%;
|
||||
border: 1px solid #ccc;
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
/* Map Controls */
|
||||
@@ -572,6 +599,30 @@ button.Browse .label {
|
||||
top:210px;
|
||||
}
|
||||
|
||||
.layerswitcher-control .adjustments {
|
||||
padding:5px;
|
||||
opacity:0.2;
|
||||
}
|
||||
|
||||
.layerswitcher-control .adjustments:hover {
|
||||
opacity:1;
|
||||
}
|
||||
|
||||
.layerswitcher-control .adjustments .reset {
|
||||
height:20px;
|
||||
font-size:10px;
|
||||
font-weight:normal;
|
||||
padding:0 5px;
|
||||
}
|
||||
|
||||
.layerswitcher-control .nudge {
|
||||
height:20px;
|
||||
width:20px;
|
||||
font-size:10px;
|
||||
margin-right:2px;
|
||||
font-weight:normal;
|
||||
}
|
||||
|
||||
.opacity-options-wrapper {
|
||||
padding: 10px 10px 0 10px;
|
||||
}
|
||||
@@ -630,6 +681,10 @@ button.Browse .label {
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
.geolocate-control {
|
||||
top:260px;
|
||||
}
|
||||
|
||||
/* Map
|
||||
------------------------------------------------------- */
|
||||
|
||||
@@ -709,8 +764,8 @@ div.typeahead {
|
||||
box-shadow: 0 5px 10px 0 rgba(0,0,0,.2);
|
||||
margin-top: -1px;
|
||||
background: white;
|
||||
max-height: 120px;
|
||||
overflow: auto;
|
||||
max-height: 180px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
@@ -722,6 +777,7 @@ div.typeahead a {
|
||||
border-top:1px solid #ccc;
|
||||
background-color: #fff;
|
||||
padding:1px 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
div.typeahead a:hover,
|
||||
@@ -801,6 +857,14 @@ div.typeahead a:first-child {
|
||||
padding:5px 10px;
|
||||
}
|
||||
|
||||
.changeset-list li span.count {
|
||||
font-size:10px;
|
||||
color:#555;
|
||||
}
|
||||
|
||||
.changeset-list li span.count:before { content: '('; }
|
||||
.changeset-list li span.count:after { content: ')'; }
|
||||
|
||||
.changeset-list li:first-child { border-top: 0;}
|
||||
|
||||
.commit-modal .changeset-comment {
|
||||
@@ -823,6 +887,7 @@ div.typeahead a:first-child {
|
||||
left:70px;
|
||||
width:250px;
|
||||
height:50px;
|
||||
padding:10px;
|
||||
background:#fff;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
|
||||
59
css/map.css
59
css/map.css
@@ -10,30 +10,51 @@ g.point circle {
|
||||
fill:#fff;
|
||||
}
|
||||
|
||||
g.point.hover circle,
|
||||
g.point.selected circle {
|
||||
fill:#ffff00;
|
||||
stroke-width:4;
|
||||
stroke:#fff
|
||||
g.point.hover circle.stroke,
|
||||
g.point.selected circle.stroke {
|
||||
fill:#333;
|
||||
-webkit-transform:scale(1.2, 1.2);
|
||||
-moz-transform:scale(1.2, 1.2);
|
||||
transform:scale(1.2, 1.2);
|
||||
}
|
||||
|
||||
/* interactive elements */
|
||||
circle.vertex {
|
||||
g.vertex circle.fill {
|
||||
fill:white;
|
||||
stroke:#333;
|
||||
fill-opacity:1;
|
||||
stroke-width:2;
|
||||
stroke-opacity: 1;
|
||||
}
|
||||
|
||||
circle.vertex.shared {
|
||||
circle.stroke,
|
||||
circle.fill {
|
||||
-webkit-transition: -webkit-transform 50ms linear;
|
||||
transition: transform 50ms linear;
|
||||
-moz-transition: stroke 50ms linear;
|
||||
-webkit-transform:scale(1, 1);
|
||||
-moz-transform:scale(1, 1);
|
||||
transform:scale(1, 1);
|
||||
}
|
||||
|
||||
g.vertex circle.stroke {
|
||||
fill:#333;
|
||||
}
|
||||
|
||||
g.vertex.shared circle {
|
||||
fill:#aff;
|
||||
}
|
||||
|
||||
circle.vertex.hover {
|
||||
g.vertex.hover circle.fill {
|
||||
-webkit-transform:scale(2, 2);
|
||||
-moz-transform:scale(2, 2);
|
||||
transform:scale(2, 2);
|
||||
}
|
||||
|
||||
circle.vertex.selected {
|
||||
g.vertex.hover circle.stroke {
|
||||
-webkit-transform:scale(1.8, 1.8);
|
||||
-moz-transform:scale(1.8, 1.8);
|
||||
transform:scale(1.8, 1.8);
|
||||
}
|
||||
|
||||
g.vertex circle.selected {
|
||||
fill: #ffff00;
|
||||
}
|
||||
|
||||
@@ -60,12 +81,12 @@ path.casing {
|
||||
|
||||
path.casing.hover {
|
||||
stroke:#FF0F0F !important;
|
||||
opacity:0.8;
|
||||
stroke-opacity:0.8;
|
||||
}
|
||||
|
||||
path.casing.selected {
|
||||
stroke:#E96666 !important;
|
||||
opacity:1 !important;
|
||||
stroke-opacity:1 !important;
|
||||
stroke-width:10 !important;
|
||||
}
|
||||
|
||||
@@ -134,7 +155,7 @@ path.stroke.highway-residential {
|
||||
path.casing.highway-residential {
|
||||
stroke:#E8E8E8;
|
||||
stroke-width:10;
|
||||
opacity:0.4;
|
||||
stroke-opacity:0.4;
|
||||
}
|
||||
|
||||
path.stroke.highway-unclassified,
|
||||
@@ -179,12 +200,12 @@ path.stroke.highway-motorway, path.stroke.highway-motorway_link {
|
||||
path.casing.highway-motorway, path.casing.highway-motorway_link {
|
||||
stroke:#809BC0;
|
||||
stroke-width:9;
|
||||
opacity:0.4;
|
||||
stroke-opacity:0.4;
|
||||
}
|
||||
|
||||
path.stroke.highway-trunk, path.stroke.highway-trunk_link {
|
||||
stroke-width:7;
|
||||
opacity:0.4;
|
||||
stroke-opacity:0.4;
|
||||
stroke:#7FC97F;
|
||||
}
|
||||
path.casing.highway-trunk, path.casing.highway-trunk_link {
|
||||
@@ -198,7 +219,7 @@ path.stroke.highway-primary, path.stroke.highway-primary_link {
|
||||
}
|
||||
path.casing.highway-primary, path.casing.highway-primary_link {
|
||||
stroke:#FF6363;
|
||||
opacity:0.4;
|
||||
stroke-opacity:0.4;
|
||||
stroke-width:12;
|
||||
}
|
||||
|
||||
@@ -207,7 +228,7 @@ path.stroke.highway-secondary, path.stroke.highway-secondary_link {
|
||||
stroke-width:4;
|
||||
}
|
||||
path.casing.highway-secondary, path.casing.highway-secondary_link {
|
||||
opacity:0.4;
|
||||
stroke-opacity:0.4;
|
||||
stroke:#FDBF6F;
|
||||
stroke-width:11;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<script src='js/lib/d3.typeahead.js'></script>
|
||||
<script src='js/lib/d3.geo.tile.js'></script>
|
||||
<script src='js/lib/d3.size.js'></script>
|
||||
<script src='js/lib/d3.trigger.js'></script>
|
||||
<script src='js/lib/d3.keybinding.js'></script>
|
||||
<script src='js/lib/d3-compat.js'></script>
|
||||
<script src='js/lib/queue.js'></script>
|
||||
@@ -39,6 +40,7 @@
|
||||
<script src='js/id/ui/modal.js'></script>
|
||||
<script src='js/id/ui/confirm.js'></script>
|
||||
<script src='js/id/ui/commit.js'></script>
|
||||
<script src='js/id/ui/success.js'></script>
|
||||
<script src='js/id/ui/loading.js'></script>
|
||||
<script src='js/id/ui/userpanel.js'></script>
|
||||
<script src='js/id/ui/layerswitcher.js'></script>
|
||||
|
||||
@@ -14,17 +14,19 @@ iD.Connection = function() {
|
||||
}
|
||||
|
||||
function bboxFromAPI(box, tile, callback) {
|
||||
loadFromURL(bboxUrl(box), function(err, parsed) {
|
||||
function done(err, parsed) {
|
||||
loadedTiles[tile.toString()] = true;
|
||||
callback(err, parsed);
|
||||
});
|
||||
}
|
||||
loadFromURL(bboxUrl(box), done);
|
||||
}
|
||||
|
||||
function loadFromURL(url, callback) {
|
||||
function done(dom) {
|
||||
return callback(null, parse(dom));
|
||||
}
|
||||
inflight.push(d3.xml(url).get()
|
||||
.on('load', function(dom) {
|
||||
return callback(null, parse(dom));
|
||||
}));
|
||||
.on('load', done));
|
||||
}
|
||||
|
||||
function getNodes(obj) {
|
||||
@@ -67,9 +69,7 @@ iD.Connection = function() {
|
||||
tags: getTags(obj)
|
||||
};
|
||||
for (var i = 0, l = obj.attributes.length; i < l; i++) {
|
||||
var n = obj.attributes[i].nodeName;
|
||||
var v = obj.attributes[i].nodeValue;
|
||||
o[n] = v;
|
||||
o[obj.attributes[i].nodeName] = obj.attributes[i].nodeValue;
|
||||
}
|
||||
if (o.lon && o.lat) {
|
||||
o.loc = [parseFloat(o.lon), parseFloat(o.lat)];
|
||||
@@ -129,13 +129,14 @@ iD.Connection = function() {
|
||||
};
|
||||
|
||||
function userDetails(callback) {
|
||||
oauth.xhr({ method: 'GET', path: '/api/0.6/user/details' }, function(err, user_details) {
|
||||
function done(err, user_details) {
|
||||
var u = user_details.getElementsByTagName('user')[0];
|
||||
callback(connection.user({
|
||||
display_name: u.attributes.display_name.nodeValue,
|
||||
id: u.attributes.id.nodeValue
|
||||
}).user());
|
||||
});
|
||||
}
|
||||
oauth.xhr({ method: 'GET', path: '/api/0.6/user/details' }, done);
|
||||
}
|
||||
|
||||
function tileAlreadyLoaded(c) { return !loadedTiles[c.toString()]; }
|
||||
@@ -143,9 +144,10 @@ iD.Connection = function() {
|
||||
function abortRequest(i) { i.abort(); }
|
||||
|
||||
function loadTile(e) {
|
||||
bboxFromAPI(e.box, e.tile, function(err, g) {
|
||||
function done(err, g) {
|
||||
event.load(err, g);
|
||||
});
|
||||
}
|
||||
bboxFromAPI(e.box, e.tile, done);
|
||||
}
|
||||
|
||||
function loadTiles(projection) {
|
||||
@@ -211,10 +213,11 @@ iD.Connection = function() {
|
||||
};
|
||||
|
||||
connection.authenticate = function(callback) {
|
||||
return oauth.authenticate(function(err, res) {
|
||||
function done(err, res) {
|
||||
event.auth();
|
||||
if (callback) callback(err, res);
|
||||
});
|
||||
}
|
||||
return oauth.authenticate(done);
|
||||
};
|
||||
|
||||
connection.bboxFromAPI = bboxFromAPI;
|
||||
|
||||
@@ -84,6 +84,28 @@ iD.Entity.prototype = {
|
||||
key != 'odbl' &&
|
||||
key.indexOf('tiger:') !== 0;
|
||||
});
|
||||
},
|
||||
|
||||
friendlyName: function() {
|
||||
// Generate a string such as 'river' or 'Fred's House' for an entity.
|
||||
if (!this.tags || !Object.keys(this.tags).length) { return ''; }
|
||||
|
||||
var mainkeys = ['highway','amenity','railway','waterway','natural'],
|
||||
n = [];
|
||||
|
||||
if (this.tags.name) n.push(this.tags.name);
|
||||
if (this.tags.ref) n.push(this.tags.ref);
|
||||
|
||||
if (!n.length) {
|
||||
for (var k in this.tags) {
|
||||
if (mainkeys.indexOf(k) !== -1) {
|
||||
n.push(this.tags[k]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return n.length === 0 ? 'unknown' : n.join('; ');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -3,5 +3,9 @@ iD.Node = iD.Entity.extend({
|
||||
|
||||
extent: function() {
|
||||
return [this.loc, this.loc];
|
||||
},
|
||||
|
||||
geometry: function() {
|
||||
return this._poi ? 'point' : 'vertex';
|
||||
}
|
||||
});
|
||||
|
||||
@@ -36,5 +36,9 @@ iD.Way = iD.Entity.extend({
|
||||
this.tags.area !== 'no' &&
|
||||
!this.tags.highway &&
|
||||
!this.tags.barrier);
|
||||
},
|
||||
|
||||
geometry: function() {
|
||||
return this.isArea() ? 'area' : 'line';
|
||||
}
|
||||
});
|
||||
|
||||
47
js/id/id.js
47
js/id/id.js
@@ -107,10 +107,11 @@ window.iD = function(container) {
|
||||
.append('div')
|
||||
.attr('class', 'hello');
|
||||
|
||||
bar.append('button')
|
||||
var save_button = bar.append('button')
|
||||
.attr('class', 'save action wide')
|
||||
.html("<span class='icon icon-pre-text save'></span><span class='label'>Save</span><small id='as-username'></small>")
|
||||
.attr('title', 'Save changes to OpenStreetMap, making them visible to other users')
|
||||
.property('disabled', true)
|
||||
.call(bootstrap.tooltip()
|
||||
.placement('bottom'))
|
||||
.on('click', function() {
|
||||
@@ -121,6 +122,17 @@ window.iD = function(container) {
|
||||
l.remove();
|
||||
history.reset();
|
||||
map.flush().redraw();
|
||||
var modal = iD.modal();
|
||||
modal.select('.content')
|
||||
.classed('success-modal', true)
|
||||
.datum({
|
||||
id: changeset_id,
|
||||
comment: e.comment
|
||||
})
|
||||
.call(iD.success()
|
||||
.on('cancel', function() {
|
||||
modal.remove();
|
||||
}));
|
||||
});
|
||||
}
|
||||
var changes = history.changes();
|
||||
@@ -146,6 +158,24 @@ window.iD = function(container) {
|
||||
}
|
||||
});
|
||||
|
||||
save_button.append('span')
|
||||
.attr('class', 'count');
|
||||
|
||||
history.on('change.save-button', function() {
|
||||
var changes = history.changes(),
|
||||
num_changes = d3.sum(d3.values(changes).map(function(c) {
|
||||
return c.length;
|
||||
}));
|
||||
|
||||
save_button.property('disabled', num_changes === 0);
|
||||
|
||||
save_button
|
||||
.classed('has-count', num_changes > 0);
|
||||
|
||||
save_button.select('span.count')
|
||||
.text(num_changes);
|
||||
});
|
||||
|
||||
bar.append('div')
|
||||
.attr('class', 'messages');
|
||||
|
||||
@@ -160,6 +190,21 @@ window.iD = function(container) {
|
||||
return d[0] + ' icon';
|
||||
});
|
||||
|
||||
function geolocateSuccess(position) {
|
||||
map.center([position.coords.longitude, position.coords.latitude]);
|
||||
}
|
||||
function geolocateError() { }
|
||||
if (navigator.geolocation) {
|
||||
container.append('div')
|
||||
.attr('class', 'geolocate-control map-control')
|
||||
.append('button')
|
||||
.attr('class', 'narrow')
|
||||
.text('G')
|
||||
.on('click', function() {
|
||||
navigator.geolocation.getCurrentPosition(geolocateSuccess, geolocateError);
|
||||
});
|
||||
}
|
||||
|
||||
var gc = container.append('div').attr('class', 'geocode-control map-control')
|
||||
.call(iD.geocoder().map(map));
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ 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.');
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ iD.modes.AddPoint = function() {
|
||||
};
|
||||
|
||||
mode.exit = function() {
|
||||
map.hoverEnable(true);
|
||||
mode.map.hoverEnable(true);
|
||||
mode.map.hint(false);
|
||||
mode.map.surface.on('click.addpoint', null);
|
||||
mode.map.keybinding().on('⎋.addpoint', null);
|
||||
|
||||
@@ -66,14 +66,35 @@ iD.modes.Select = function (entity) {
|
||||
mode.controller.exit();
|
||||
});
|
||||
|
||||
surface.on('click.select', function () {
|
||||
function click() {
|
||||
var datum = d3.select(d3.event.target).datum();
|
||||
if (datum instanceof iD.Entity) {
|
||||
mode.controller.enter(iD.modes.Select(datum));
|
||||
} else {
|
||||
mode.controller.enter(iD.modes.Browse());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function dblclick() {
|
||||
var datum = d3.select(d3.event.target).datum();
|
||||
if (datum instanceof iD.Entity &&
|
||||
(datum.geometry() === 'area' || datum.geometry() === 'line')) {
|
||||
var choice = iD.util.geo.chooseIndex(datum,
|
||||
d3.mouse(mode.map.surface.node()), mode.map),
|
||||
node = iD.Node({ loc: choice.loc });
|
||||
|
||||
mode.history.perform(
|
||||
iD.actions.AddNode(node),
|
||||
iD.actions.AddWayNode(datum.id, node.id, choice.index),
|
||||
'added a point to a road');
|
||||
|
||||
d3.event.preventDefault();
|
||||
d3.event.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
surface.on('click.select', click)
|
||||
.on('dblclick.browse', dblclick);
|
||||
|
||||
mode.map.keybinding().on('⌫.select', function(e) {
|
||||
remove();
|
||||
|
||||
@@ -66,6 +66,10 @@ iD.OAuth = function() {
|
||||
|
||||
var l = iD.loading('contacting openstreetmap...');
|
||||
|
||||
// it would make more sense to have this code within the callback
|
||||
// to oauth.xhr below. however, it needs to be directly within a
|
||||
// browser event handler in order to open a popup without it being
|
||||
// blocked.
|
||||
var w = 600, h = 550,
|
||||
settings = [
|
||||
['width', w], ['height', h],
|
||||
|
||||
@@ -2,6 +2,7 @@ iD.Background = function() {
|
||||
var tile = d3.geo.tile(),
|
||||
projection,
|
||||
cache = {},
|
||||
offset = [0, 0],
|
||||
transformProp = iD.util.prefixCSSProperty('Transform'),
|
||||
source = d3.functor('');
|
||||
|
||||
@@ -27,6 +28,10 @@ iD.Background = function() {
|
||||
return tiles;
|
||||
}
|
||||
|
||||
function tileSize(d, z) {
|
||||
return Math.ceil(256 * Math.pow(2, z - d[2])) / 256;
|
||||
}
|
||||
|
||||
// derive the tiles onscreen, remove those offscreen and position tiles
|
||||
// correctly for the currentstate of `projection`
|
||||
function background() {
|
||||
@@ -82,21 +87,29 @@ iD.Background = function() {
|
||||
.on('error', error)
|
||||
.on('load', load);
|
||||
|
||||
function tileSize(d) {
|
||||
return Math.ceil(256 * Math.pow(2, z - d[2])) / 256;
|
||||
}
|
||||
|
||||
image.style(transformProp, function(d) {
|
||||
var _ts = 256 * Math.pow(2, z - d[2]);
|
||||
var scale = tileSize(d);
|
||||
var scale = tileSize(d, z);
|
||||
return 'translate(' +
|
||||
Math.round((d[0] * _ts) - tile_origin[0]) + 'px,' +
|
||||
Math.round((d[1] * _ts) - tile_origin[1]) + 'px) scale(' + scale + ',' + scale + ')';
|
||||
(Math.round((d[0] * _ts) - tile_origin[0]) + offset[0]) + 'px,' +
|
||||
(Math.round((d[1] * _ts) - tile_origin[1]) + offset[1]) + 'px) scale(' + scale + ',' + scale + ')';
|
||||
});
|
||||
|
||||
if (Object.keys(cache).length > 100) cache = {};
|
||||
}
|
||||
|
||||
background.offset = function(_) {
|
||||
if (!arguments.length) return offset;
|
||||
offset = _;
|
||||
return background;
|
||||
};
|
||||
|
||||
background.nudge = function(_) {
|
||||
offset[0] += _[0];
|
||||
offset[1] += _[1];
|
||||
return background;
|
||||
};
|
||||
|
||||
background.projection = function(_) {
|
||||
if (!arguments.length) return projection;
|
||||
projection = _;
|
||||
|
||||
@@ -45,3 +45,7 @@ iD.BackgroundSource.Tiger2012 = iD.BackgroundSource.template(
|
||||
iD.BackgroundSource.OSM = iD.BackgroundSource.template(
|
||||
'http://{t}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
['a', 'b', 'c'], [0, 18]);
|
||||
|
||||
iD.BackgroundSource.MapBox = iD.BackgroundSource.template(
|
||||
'http://{t}.tiles.mapbox.com/v3/openstreetmap.map-4wvf9l0l/{z}/{x}/{y}.jpg70',
|
||||
['a', 'b', 'c'], [0, 16]);
|
||||
|
||||
@@ -84,9 +84,9 @@ iD.Map = function() {
|
||||
filter = d3.functor(true);
|
||||
} else {
|
||||
var only = {};
|
||||
difference.forEach(function (id) {
|
||||
difference.forEach(function buildDifference(id) {
|
||||
only[id] = graph.fetch(id);
|
||||
graph.parentWays(id).forEach(function (parent) {
|
||||
graph.parentWays(id).forEach(function buildOnly(parent) {
|
||||
only[parent.id] = graph.fetch(parent.id);
|
||||
});
|
||||
});
|
||||
@@ -94,7 +94,6 @@ iD.Map = function() {
|
||||
filter = function(d) { return d.accuracy ? d.way in only : d.id in only; };
|
||||
}
|
||||
|
||||
|
||||
if (all.length > 10000) return editOff();
|
||||
else editOn();
|
||||
|
||||
@@ -112,9 +111,10 @@ iD.Map = function() {
|
||||
}
|
||||
}
|
||||
var parentStructure = graph.parentStructure(ways);
|
||||
var wayAccuracyHandles = ways.reduce(function(mem, w) {
|
||||
return mem.concat(accuracyHandles(w));
|
||||
}, []);
|
||||
var wayAccuracyHandles = [];
|
||||
for (i = 0; i < ways.length; i++) {
|
||||
accuracyHandles(ways[i], wayAccuracyHandles);
|
||||
}
|
||||
drawVertices(vertices, parentStructure, filter);
|
||||
drawAccuracyHandles(wayAccuracyHandles, filter);
|
||||
drawCasings(lines, filter);
|
||||
@@ -123,8 +123,8 @@ iD.Map = function() {
|
||||
drawPoints(points, filter);
|
||||
}
|
||||
|
||||
function accuracyHandles(way) {
|
||||
var handles = [];
|
||||
// updates handles by reference
|
||||
function accuracyHandles(way, handles) {
|
||||
for (var i = 0; i < way.nodes.length - 1; i++) {
|
||||
if (iD.util.geo.dist(way.nodes[i].loc, way.nodes[i + 1].loc) > 0.0001) {
|
||||
handles.push({
|
||||
@@ -135,32 +135,36 @@ iD.Map = function() {
|
||||
});
|
||||
}
|
||||
}
|
||||
return handles;
|
||||
}
|
||||
|
||||
function pointTransform(entity) {
|
||||
return 'translate(' + iD.util.geo.roundCoords(projection(entity.loc)) + ')';
|
||||
}
|
||||
|
||||
function drawVertices(vertices, parentStructure, filter) {
|
||||
function shared(d) { return parentStructure[d.id] > 1; }
|
||||
|
||||
var circles = g.hit.selectAll('circle.vertex')
|
||||
var circles = g.hit.selectAll('g.vertex')
|
||||
.filter(filter)
|
||||
.data(vertices, key);
|
||||
|
||||
circles.exit().remove();
|
||||
|
||||
circles.enter().insert('circle', ':first-child')
|
||||
var cg = circles.enter()
|
||||
.insert('g', ':first-child')
|
||||
.attr('class', 'node vertex');
|
||||
|
||||
circles.attr('transform', function(entity) {
|
||||
var p = projection(entity.loc);
|
||||
return 'translate(' + [~~p[0], ~~p[1]] +
|
||||
')';
|
||||
})
|
||||
cg.append('circle')
|
||||
.attr('class', 'stroke')
|
||||
.attr('r', 6);
|
||||
|
||||
cg.append('circle')
|
||||
.attr('class', 'fill')
|
||||
.attr('r', 4);
|
||||
|
||||
circles.attr('transform', pointTransform)
|
||||
.classed('shared', shared)
|
||||
.classed('hover', classHover);
|
||||
|
||||
circles.transition().duration(50).attr('r', function(d) {
|
||||
return d.id === hover ? 8: 4;
|
||||
});
|
||||
}
|
||||
|
||||
function drawAccuracyHandles(waynodes, filter) {
|
||||
@@ -170,10 +174,7 @@ iD.Map = function() {
|
||||
handles.exit().remove();
|
||||
handles.enter().append('circle')
|
||||
.attr({ r: 3, 'class': 'accuracy-handle' });
|
||||
handles.attr('transform', function(entity) {
|
||||
var p = projection(entity.loc);
|
||||
return 'translate(' + [~~p[0], ~~p[1]] + ')';
|
||||
});
|
||||
handles.attr('transform', pointTransform);
|
||||
}
|
||||
|
||||
function editOff() {
|
||||
@@ -209,20 +210,30 @@ iD.Map = function() {
|
||||
}
|
||||
|
||||
function drawPoints(points, filter) {
|
||||
|
||||
var groups = g.hit.selectAll('g.point')
|
||||
.filter(filter)
|
||||
.data(points, key);
|
||||
|
||||
groups.exit().remove();
|
||||
|
||||
var group = groups.enter().append('g')
|
||||
.attr('class', 'node point');
|
||||
|
||||
group.append('circle')
|
||||
.attr({ r: 10, cx: 8, cy: 8 });
|
||||
.attr('class', 'stroke')
|
||||
.attr({ r: 10 });
|
||||
|
||||
group.append('circle')
|
||||
.attr('class', 'fill')
|
||||
.attr({ r: 10 });
|
||||
|
||||
group.append('image')
|
||||
.attr({ width: 16, height: 16 });
|
||||
groups.attr('transform', function(d) {
|
||||
var pt = projection(d.loc);
|
||||
return 'translate(' + [~~pt[0], ~~pt[1]] + ') translate(-8, -8)';
|
||||
});
|
||||
.attr({ width: 16, height: 16 })
|
||||
.attr('transform', 'translate(-8, -8)');
|
||||
|
||||
groups.attr('transform', pointTransform);
|
||||
|
||||
groups.classed('hover', classHover);
|
||||
groups.select('image').attr('xlink:href', iD.Style.pointImage);
|
||||
}
|
||||
@@ -299,11 +310,11 @@ iD.Map = function() {
|
||||
if (fast) {
|
||||
if (!translateStart) translateStart = d3.event.translate.slice();
|
||||
var a = d3.event.translate,
|
||||
b = translateStart;
|
||||
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)');
|
||||
b = translateStart,
|
||||
translate = 'translate(' + ~~(a[0] - b[0]) + 'px,' +
|
||||
~~(a[1] - b[1]) + 'px)';
|
||||
tilegroup.style(transformProp, translate);
|
||||
surface.style(transformProp, translate);
|
||||
} else {
|
||||
redraw();
|
||||
translateStart = null;
|
||||
|
||||
@@ -5,7 +5,7 @@ iD.taginfo = function() {
|
||||
taginfo.keys = function(parameters, callback) {
|
||||
d3.json(endpoint + 'db/keys?' +
|
||||
iD.util.qsString(_.extend({
|
||||
rp: 20,
|
||||
rp: 6,
|
||||
sortname: 'count_all',
|
||||
sortorder: 'desc',
|
||||
page: 1
|
||||
|
||||
@@ -14,51 +14,77 @@ iD.commit = function() {
|
||||
.attr('class', 'changeset-comment')
|
||||
.attr('placeholder', 'Brief Description of your contributions');
|
||||
|
||||
var buttonwrap = commit.append('div')
|
||||
.attr('class', 'buttons');
|
||||
var buttonwrap = commit.append('div')
|
||||
.attr('class', 'buttons');
|
||||
|
||||
var savebutton = buttonwrap.append('button')
|
||||
.attr('class', 'action wide')
|
||||
.on('click.save', function() {
|
||||
event.save({
|
||||
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');
|
||||
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');
|
||||
var savebutton = buttonwrap.append('button')
|
||||
.attr('class', 'action wide')
|
||||
.on('click.save', function() {
|
||||
event.save({
|
||||
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');
|
||||
|
||||
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');
|
||||
|
||||
function changesLength(d) { return changes[d].length; }
|
||||
|
||||
var section = body.selectAll('div.commit-section')
|
||||
.data(['modified', 'deleted', 'created'].filter(function(d) {
|
||||
return changes[d].length;
|
||||
}))
|
||||
.data(['modified', 'deleted', 'created'].filter(changesLength))
|
||||
.enter()
|
||||
.append('div').attr('class', 'commit-section modal-section fillL2');
|
||||
|
||||
section.append('h3').text(String)
|
||||
.append('small')
|
||||
.attr('class', 'count')
|
||||
.text(function(d) { return changes[d].length; });
|
||||
.text(changesLength);
|
||||
|
||||
function zipSame(d) {
|
||||
var c = [], n = -1;
|
||||
for (var i = 0; i < d.length; i++) {
|
||||
var desc = {
|
||||
name: d[i].friendlyName(),
|
||||
type: d[i].type,
|
||||
count: 1,
|
||||
tagText: iD.util.tagText(d[i])
|
||||
};
|
||||
if (c[n] &&
|
||||
c[n].name == desc.name &&
|
||||
c[n].tagText == desc.tagText) {
|
||||
c[n].count++;
|
||||
} else {
|
||||
c[++n] = desc;
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
var li = section.append('ul')
|
||||
.attr('class','changeset-list')
|
||||
.selectAll('li')
|
||||
.data(function(d) { return changes[d]; })
|
||||
.data(function(d) { return zipSame(changes[d]); })
|
||||
.enter()
|
||||
.append('li');
|
||||
|
||||
li.append('strong').text(function(d) { return d.type + ' '; });
|
||||
li.append('strong').text(function(d) {
|
||||
return (d.count > 1) ? d.type + 's ' : d.type + ' ';
|
||||
});
|
||||
li.append('span')
|
||||
.text(function(d) {
|
||||
return iD.util.friendlyName(d);
|
||||
})
|
||||
.attr('title', iD.util.tagText);
|
||||
.text(function(d) { return d.name; })
|
||||
.attr('title', function(d) { return d.tagText; });
|
||||
|
||||
li.filter(function(d) { return d.count > 1; })
|
||||
.append('span')
|
||||
.attr('class', 'count')
|
||||
.text(function(d) { return d.count; });
|
||||
}
|
||||
|
||||
return d3.rebind(commit, event, 'on');
|
||||
|
||||
@@ -2,220 +2,237 @@ iD.Inspector = function() {
|
||||
var event = d3.dispatch('changeTags', 'changeWayDirection',
|
||||
'update', 'remove', 'close', 'splitWay'),
|
||||
taginfo = iD.taginfo(),
|
||||
inspectorwrap;
|
||||
tagList;
|
||||
|
||||
function inspector(selection) {
|
||||
var entity = selection.datum();
|
||||
|
||||
selection.html("").append('button')
|
||||
.attr('class', 'narrow close')
|
||||
.html("<span class='icon close'></span>")
|
||||
.on('click', function() {
|
||||
event.close(entity);
|
||||
});
|
||||
|
||||
selection.append('div')
|
||||
.attr('class', 'head inspector-inner')
|
||||
.call(drawHead);
|
||||
|
||||
var inspectorbody = selection.append('div')
|
||||
.attr('class', 'inspector-body');
|
||||
|
||||
var inspectorwrap = inspectorbody.append('div')
|
||||
.attr('class', 'inspector-inner tag-wrap fillL2');
|
||||
|
||||
inspectorwrap.append('h4')
|
||||
.text('Edit tags');
|
||||
|
||||
tagList = inspectorwrap.append('ul');
|
||||
|
||||
inspectorwrap.append('div').attr('class', 'add-tag-row').append('button')
|
||||
.attr('class', 'add-tag')
|
||||
.text('+ Add New Tag')
|
||||
.on('click', function() {
|
||||
addTag();
|
||||
tagList.selectAll('li:last-child input.key').node().focus();
|
||||
});
|
||||
|
||||
var formsel = drawTags(entity.tags);
|
||||
|
||||
inspectorbody.append('div')
|
||||
.attr('class', 'inspector-buttons')
|
||||
.call(drawButtons);
|
||||
|
||||
var inHeight = inspectorbody.node().offsetHeight;
|
||||
|
||||
inspectorbody.style('display', 'none')
|
||||
.style('margin-top', (-inHeight) + 'px');
|
||||
|
||||
var inspectortoggle = selection.append('button')
|
||||
.attr('class', 'inspector-toggle action')
|
||||
.on('click', function() {
|
||||
inspectortoggle.style('display', 'none');
|
||||
inspectorbody
|
||||
.style('display', 'block')
|
||||
.transition()
|
||||
.style('margin-top', '0px');
|
||||
});
|
||||
|
||||
formsel.selectAll('input').node().focus();
|
||||
|
||||
inspectortoggle.append('span')
|
||||
.text('Details')
|
||||
.attr('class','label');
|
||||
}
|
||||
|
||||
function drawHead(selection) {
|
||||
var entity = selection.datum();
|
||||
|
||||
function drawhead(selection) {
|
||||
function osmLink(d) {
|
||||
return 'http://www.openstreetmap.org/browse/' + d.type + '/' + d.osmId();
|
||||
}
|
||||
function emitChangeDirection(d) { event.changeWayDirection(d); }
|
||||
function emitSplitWay(d) { event.splitWay(d); }
|
||||
selection.html('');
|
||||
var h2 = selection.append('h2');
|
||||
h2.append('span').attr('class', function(d) {
|
||||
var icons = { way: 'line', node: 'point' };
|
||||
return 'icon big icon-pre-text big-' + icons[d.type];
|
||||
});
|
||||
h2.append('span').text(iD.util.friendlyName(selection.datum()));
|
||||
|
||||
h2.append('span')
|
||||
.attr('class', 'icon big icon-pre-text big-' + entity.geometry());
|
||||
|
||||
h2.append('span')
|
||||
.text(entity.friendlyName());
|
||||
|
||||
selection.append('a')
|
||||
.attr('class', 'permalink')
|
||||
.attr('href', osmLink)
|
||||
.attr('href', 'http://www.openstreetmap.org/browse/' + entity.type + '/' + entity.osmId())
|
||||
.text('View on OSM');
|
||||
if (selection.datum().type === 'way') {
|
||||
|
||||
if (entity.type === 'way') {
|
||||
selection.append('a')
|
||||
.attr('class', 'permalink')
|
||||
.attr('href', '#')
|
||||
.text('Reverse Direction')
|
||||
.on('click', emitChangeDirection);
|
||||
.on('click', function() { event.changeWayDirection(entity); });
|
||||
}
|
||||
if (selection.datum().type === 'node' && !selection.datum()._poi) {
|
||||
|
||||
if (entity.geometry() === 'vertex') {
|
||||
selection.append('a')
|
||||
.attr('class', 'permalink')
|
||||
.attr('href', '#')
|
||||
.text('Split Way')
|
||||
.on('click', emitSplitWay);
|
||||
.on('click', function() { event.splitWay(entity); });
|
||||
}
|
||||
}
|
||||
|
||||
function inspector(selection) {
|
||||
selection.each(function(entity) {
|
||||
function drawButtons(selection) {
|
||||
selection.append('button')
|
||||
.attr('class', 'apply wide action')
|
||||
.html("<span class='icon icon-pre-text apply'></span><span class='label'>Apply</span>")
|
||||
.on('click', apply);
|
||||
|
||||
function draw(tags) {
|
||||
selection.append('button')
|
||||
.attr('class', 'delete wide action')
|
||||
.html("<span class='icon icon-pre-text delete'></span><span class='label'>Delete</span>")
|
||||
.on('click', function(entity) { event.remove(entity); });
|
||||
}
|
||||
|
||||
function emptyTag(d) {
|
||||
return d.key === '';
|
||||
}
|
||||
function drawTags(tags) {
|
||||
tags = d3.entries(tags);
|
||||
|
||||
function pushMore() {
|
||||
if (d3.event.keyCode === 9) {
|
||||
draw(inspector.tags());
|
||||
}
|
||||
}
|
||||
if (!tags.length) {
|
||||
tags = [{key: '', value: ''}];
|
||||
}
|
||||
|
||||
function bindTypeahead() {
|
||||
var row = d3.select(this),
|
||||
key = row.selectAll('.key'),
|
||||
value = row.selectAll('.value');
|
||||
var li = tagList.selectAll('li')
|
||||
.data(tags, function(d) { return d.key; });
|
||||
|
||||
key.call(d3.typeahead()
|
||||
.data(function(_, callback) {
|
||||
taginfo.keys({query: key.property('value')}, function(err, data) {
|
||||
callback(data.data.map(function (d) {
|
||||
return {value: d.key};
|
||||
}));
|
||||
});
|
||||
}));
|
||||
li.exit().remove();
|
||||
|
||||
value.call(d3.typeahead()
|
||||
.data(function(_, callback) {
|
||||
taginfo.values({key: key.property('value'), query: value.property('value')}, function(err, data) {
|
||||
callback(data.data.map(function (d) {
|
||||
return {value: d.value, title: d.description};
|
||||
}));
|
||||
});
|
||||
}));
|
||||
}
|
||||
var row = li.enter().append('li')
|
||||
.attr('class', 'tag-row');
|
||||
|
||||
tags = d3.entries(tags);
|
||||
tags.push({ key: '', value: ''});
|
||||
var inputs = row.append('div')
|
||||
.attr('class', 'input-wrap');
|
||||
|
||||
var li = inspectorwrap.selectAll('li')
|
||||
.data(tags, function(d) { return d.key; });
|
||||
inputs.append('input')
|
||||
.property('type', 'text')
|
||||
.attr('class', 'key')
|
||||
.property('value', function(d) { return d.key; })
|
||||
.on('change', function(d) { d.key = this.value; });
|
||||
|
||||
li.exit().remove();
|
||||
inputs.append('input')
|
||||
.property('type', 'text')
|
||||
.attr('class', 'value')
|
||||
.property('value', function(d) { return d.value; })
|
||||
.on('change', function(d) { d.value = this.value; })
|
||||
.on('keydown.push-more', pushMore);
|
||||
|
||||
var row = li.enter().append('li').attr('class','tag-row');
|
||||
var inputs = row.append('div').attr('class','input-wrap');
|
||||
inputs.each(bindTypeahead);
|
||||
|
||||
li.classed('tag-row-empty', emptyTag);
|
||||
var removeBtn = row.append('button')
|
||||
.attr('tabindex', -1)
|
||||
.attr('class','remove minor')
|
||||
.on('click', removeTag);
|
||||
|
||||
inputs.append('input')
|
||||
.property('type', 'text')
|
||||
.attr('class', 'key')
|
||||
.property('value', function(d) { return d.key; });
|
||||
removeBtn.append('span')
|
||||
.attr('class', 'icon remove');
|
||||
|
||||
inputs.append('input')
|
||||
.property('type', 'text')
|
||||
.attr('class', 'value')
|
||||
.property('value', function(d) { return d.value; })
|
||||
.on('keydown.push-more', pushMore);
|
||||
|
||||
inputs.each(bindTypeahead);
|
||||
|
||||
var removeBtn = row.append('button')
|
||||
.attr('tabindex', -1)
|
||||
.attr('class','remove minor')
|
||||
.on('click', removeTag);
|
||||
|
||||
removeBtn.append('span').attr('class', 'icon remove');
|
||||
|
||||
var helpBtn = row.append('button')
|
||||
.attr('tabindex', -1)
|
||||
.attr('class', 'tag-help minor')
|
||||
.append('a')
|
||||
.attr('tabindex', -1)
|
||||
.attr('target', '_blank')
|
||||
.on('click', function(d) {
|
||||
taginfo.docs(d, function(err, docs) {
|
||||
var en = _.find(docs, function(d) {
|
||||
return d.lang == 'en';
|
||||
});
|
||||
if (en) {
|
||||
var types = [];
|
||||
if (en.on_area) types.push('area');
|
||||
if (en.on_node) types.push('point');
|
||||
if (en.on_way) types.push('line');
|
||||
en.types = types;
|
||||
var mod = iD.modal();
|
||||
mod.select('.content')
|
||||
.datum(en)
|
||||
.call(iD.tagReference);
|
||||
}
|
||||
});
|
||||
d3.event.preventDefault();
|
||||
})
|
||||
.attr('href', function(d) {
|
||||
return 'http://taginfo.openstreetmap.org/keys/' + d.key;
|
||||
var helpBtn = row.append('button')
|
||||
.attr('tabindex', -1)
|
||||
.attr('class', 'tag-help minor')
|
||||
.append('a')
|
||||
.attr('tabindex', -1)
|
||||
.attr('target', '_blank')
|
||||
.on('click', function(d) {
|
||||
taginfo.docs(d, function(err, docs) {
|
||||
var en = _.find(docs, function(d) {
|
||||
return d.lang == 'en';
|
||||
});
|
||||
|
||||
helpBtn.append('span').attr('class', 'icon inspect');
|
||||
|
||||
return li;
|
||||
}
|
||||
|
||||
function removeTag(d) {
|
||||
var tags = inspector.tags();
|
||||
delete tags[d.key];
|
||||
draw(tags);
|
||||
}
|
||||
|
||||
function apply(entity) {
|
||||
event.changeTags(entity, inspector.tags());
|
||||
event.close(entity);
|
||||
}
|
||||
|
||||
function drawbuttons(selection) {
|
||||
selection.append('button')
|
||||
.attr('class', 'apply wide action')
|
||||
.html("<span class='icon icon-pre-text apply'></span><span class='label'>Apply</span>")
|
||||
.on('click', apply);
|
||||
selection.append('button')
|
||||
.attr('class', 'delete wide action')
|
||||
.html("<span class='icon icon-pre-text delete'></span><span class='label'>Delete</span>")
|
||||
.on('click', function(entity) { event.remove(entity); });
|
||||
}
|
||||
|
||||
selection.html("").append('button')
|
||||
.attr('class', 'narrow close')
|
||||
.html("<span class='icon close'></span>")
|
||||
.on('click', function() {
|
||||
event.close(entity);
|
||||
if (en) {
|
||||
var types = [];
|
||||
if (en.on_area) types.push('area');
|
||||
if (en.on_node) types.push('point');
|
||||
if (en.on_way) types.push('line');
|
||||
en.types = types;
|
||||
var mod = iD.modal();
|
||||
mod.select('.content')
|
||||
.datum(en)
|
||||
.call(iD.tagReference);
|
||||
}
|
||||
});
|
||||
d3.event.preventDefault();
|
||||
})
|
||||
.attr('href', function(d) {
|
||||
return 'http://taginfo.openstreetmap.org/keys/' + d.key;
|
||||
});
|
||||
|
||||
selection.append('div')
|
||||
.attr('class', 'head inspector-inner')
|
||||
.call(drawhead);
|
||||
helpBtn.append('span')
|
||||
.attr('class', 'icon inspect');
|
||||
|
||||
var inspectorbody = selection.append('div')
|
||||
.attr('class', 'inspector-body');
|
||||
return li;
|
||||
}
|
||||
|
||||
inspectorwrap = inspectorbody
|
||||
.append('ul').attr('class', 'inspector-inner tag-wrap fillL2');
|
||||
function pushMore() {
|
||||
if (d3.event.keyCode === 9 && tagList.selectAll('li:last-child input.value').node() === this) {
|
||||
addTag();
|
||||
}
|
||||
}
|
||||
|
||||
inspectorwrap.append('h4').text('Edit tags');
|
||||
function bindTypeahead() {
|
||||
var row = d3.select(this),
|
||||
key = row.selectAll('.key'),
|
||||
value = row.selectAll('.value');
|
||||
|
||||
|
||||
var formsel = draw(entity.tags);
|
||||
|
||||
inspectorbody.append('div')
|
||||
.attr('class', 'inspector-buttons').call(drawbuttons);
|
||||
|
||||
var inHeight = inspectorbody.node().offsetHeight;
|
||||
|
||||
inspectorbody.style('display', 'none')
|
||||
.style('margin-top', (-inHeight) + 'px');
|
||||
|
||||
var inspectortoggle = selection.append('button')
|
||||
.attr('class', 'inspector-toggle action')
|
||||
.on('click', function() {
|
||||
inspectortoggle.style('display', 'none');
|
||||
inspectorbody
|
||||
.style('display', 'block')
|
||||
.transition()
|
||||
.style('margin-top', '0px');
|
||||
key.call(d3.typeahead()
|
||||
.data(function(_, callback) {
|
||||
taginfo.keys({query: key.property('value')}, function(err, data) {
|
||||
callback(data.data.map(function (d) {
|
||||
return {value: d.key};
|
||||
}));
|
||||
});
|
||||
}));
|
||||
|
||||
formsel.select('input').node().focus();
|
||||
value.call(d3.typeahead()
|
||||
.data(function(_, callback) {
|
||||
taginfo.values({key: key.property('value'), query: value.property('value')}, function(err, data) {
|
||||
callback(data.data.map(function (d) {
|
||||
return {value: d.value, title: d.description};
|
||||
}));
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
inspectortoggle.append('span')
|
||||
.text('Details')
|
||||
.attr('class','label');
|
||||
});
|
||||
function addTag() {
|
||||
var tags = inspector.tags();
|
||||
tags[''] = '';
|
||||
drawTags(tags);
|
||||
}
|
||||
|
||||
function removeTag(d) {
|
||||
var tags = inspector.tags();
|
||||
delete tags[d.key];
|
||||
drawTags(tags);
|
||||
}
|
||||
|
||||
function apply(entity) {
|
||||
event.changeTags(entity, inspector.tags());
|
||||
event.close(entity);
|
||||
}
|
||||
|
||||
inspector.tags = function () {
|
||||
var tags = {};
|
||||
inspectorwrap.selectAll('li').each(function() {
|
||||
tagList.selectAll('li').each(function() {
|
||||
var row = d3.select(this),
|
||||
key = row.selectAll('.key').property('value'),
|
||||
value = row.selectAll('.value').property('value');
|
||||
|
||||
@@ -12,6 +12,10 @@ iD.layerswitcher = function(map) {
|
||||
name: 'OSM',
|
||||
source: iD.BackgroundSource.OSM,
|
||||
description: 'The default OpenStreetMap layer.'
|
||||
}, {
|
||||
name: 'MapBox',
|
||||
source: iD.BackgroundSource.MapBox,
|
||||
description: 'Satellite and Aerial Imagery'
|
||||
}, {
|
||||
name: 'Custom',
|
||||
source: iD.BackgroundSource.Custom,
|
||||
@@ -121,6 +125,36 @@ iD.layerswitcher = function(map) {
|
||||
.insert('span')
|
||||
.attr('class','icon toggle');
|
||||
|
||||
var adjustments = content
|
||||
.append('div')
|
||||
.attr('class', 'adjustments');
|
||||
|
||||
var directions = [
|
||||
['←', [-1, 0]],
|
||||
['↑', [0, -1]],
|
||||
['→', [1, 0]],
|
||||
['↓', [0, 1]]];
|
||||
|
||||
function nudge(d) {
|
||||
map.background.nudge(d[1]);
|
||||
map.redraw();
|
||||
}
|
||||
|
||||
adjustments.selectAll('button')
|
||||
.data(directions).enter()
|
||||
.append('button')
|
||||
.attr('class', 'nudge')
|
||||
.text(function(d) { return d[0]; })
|
||||
.on('click', nudge);
|
||||
|
||||
adjustments.append('button')
|
||||
.text('reset')
|
||||
.attr('class', 'reset')
|
||||
.on('click', function() {
|
||||
map.background.offset([0, 0]);
|
||||
map.redraw();
|
||||
});
|
||||
|
||||
|
||||
selection.call(clickoutside);
|
||||
selectLayer(map.background.source());
|
||||
|
||||
@@ -3,7 +3,7 @@ iD.notice = function(selection) {
|
||||
notice = {};
|
||||
|
||||
notice.message = function(_) {
|
||||
selection.attr('class','inner');
|
||||
selection.attr('class', 'notice inner');
|
||||
if (!arguments.length) return _;
|
||||
if (!message && _) {
|
||||
selection
|
||||
|
||||
43
js/id/ui/success.js
Normal file
43
js/id/ui/success.js
Normal file
@@ -0,0 +1,43 @@
|
||||
iD.success = function() {
|
||||
var event = d3.dispatch('cancel', 'save');
|
||||
|
||||
function success(selection) {
|
||||
var changeset = selection.datum(),
|
||||
header = selection.append('div').attr('class', 'header modal-section'),
|
||||
body = selection.append('div').attr('class', 'body');
|
||||
|
||||
var section = body.append('div').attr('class','modal-section');
|
||||
|
||||
header.append('h2').text('You Just Edited OpenStreetMap!');
|
||||
header.append('p').text('You just improved the world\'s best free map');
|
||||
|
||||
var m = '';
|
||||
if (changeset.comment) {
|
||||
m = '"' + changeset.comment.substring(0, 20) + '" ';
|
||||
}
|
||||
|
||||
var message = 'Edited OpenStreetMap! ' + m +
|
||||
'http://osm.org/browse/changeset/' + changeset.id;
|
||||
|
||||
section.append('a')
|
||||
.attr('href', function(d) {
|
||||
return 'https://twitter.com/intent/tweet?source=webclient&text=' +
|
||||
encodeURIComponent(message);
|
||||
})
|
||||
.text('Tweet: ' + message);
|
||||
|
||||
var buttonwrap = section.append('div')
|
||||
.attr('class', 'buttons');
|
||||
|
||||
var okbutton = buttonwrap.append('button')
|
||||
.attr('class', 'action wide')
|
||||
.on('click.save', function() {
|
||||
event.cancel();
|
||||
});
|
||||
|
||||
okbutton.append('span').attr('class','icon apply icon-pre-text');
|
||||
okbutton.append('span').attr('class','label').text('OK');
|
||||
}
|
||||
|
||||
return d3.rebind(success, event, 'on');
|
||||
};
|
||||
@@ -6,28 +6,6 @@ iD.util.trueObj = function(arr) {
|
||||
return o;
|
||||
};
|
||||
|
||||
iD.util.friendlyName = function(entity) {
|
||||
// Generate a string such as 'river' or 'Fred's House' for an entity.
|
||||
if (!entity.tags || !Object.keys(entity.tags).length) { return ''; }
|
||||
|
||||
var mainkeys = ['highway','amenity','railway','waterway','natural'],
|
||||
n = [];
|
||||
|
||||
if (entity.tags.name) n.push(entity.tags.name);
|
||||
if (entity.tags.ref) n.push(entity.tags.ref);
|
||||
|
||||
if (!n.length) {
|
||||
for (var k in entity.tags) {
|
||||
if (mainkeys.indexOf(k) !== -1) {
|
||||
n.push(entity.tags[k]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return n.length === 0 ? 'unknown' : n.join('; ');
|
||||
};
|
||||
|
||||
iD.util.codeWindow = function(content) {
|
||||
top.win = window.open('','contentWindow',
|
||||
'width=350,height=350,menubar=0' +
|
||||
|
||||
7
js/lib/d3.trigger.js
Normal file
7
js/lib/d3.trigger.js
Normal file
@@ -0,0 +1,7 @@
|
||||
d3.selection.prototype.trigger = function (type) {
|
||||
this.each(function() {
|
||||
var evt = document.createEvent('HTMLEvents');
|
||||
evt.initEvent(type, true, true);
|
||||
this.dispatchEvent(evt);
|
||||
});
|
||||
};
|
||||
@@ -14,35 +14,47 @@ d3.typeahead = function() {
|
||||
top: rect.bottom + 'px'
|
||||
});
|
||||
selection
|
||||
.on('keyup.typeahead', update);
|
||||
.on('keyup.typeahead', key);
|
||||
hidden = false;
|
||||
}
|
||||
|
||||
function hide() {
|
||||
window.setTimeout(function() {
|
||||
container.remove();
|
||||
idx = 0;
|
||||
hidden = true;
|
||||
}, 500);
|
||||
container.remove();
|
||||
idx = 0;
|
||||
hidden = true;
|
||||
}
|
||||
|
||||
function slowHide() {
|
||||
window.setTimeout(hide, 150);
|
||||
}
|
||||
|
||||
selection
|
||||
.on('focus.typeahead', setup)
|
||||
.on('blur.typeahead', hide);
|
||||
.on('blur.typeahead', slowHide);
|
||||
|
||||
function update() {
|
||||
if (hidden) setup();
|
||||
|
||||
if (d3.event.keyCode === 40) idx++;
|
||||
if (d3.event.keyCode === 38) idx--;
|
||||
if (d3.event.keyCode === 13) {
|
||||
selection.property('value', container.select('a.selected').datum().value);
|
||||
hide();
|
||||
}
|
||||
function key() {
|
||||
if (d3.event.keyCode === 40) {
|
||||
idx++;
|
||||
return highlight();
|
||||
} else if (d3.event.keyCode === 38) {
|
||||
idx--;
|
||||
return highlight();
|
||||
} else if (d3.event.keyCode === 13) {
|
||||
select(container.select('a.selected').datum());
|
||||
hide();
|
||||
} else {
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
function highlight() {
|
||||
container
|
||||
.selectAll('a')
|
||||
.classed('selected', function(d, i) { return i == idx; });
|
||||
}
|
||||
|
||||
function update() {
|
||||
if (hidden) setup();
|
||||
|
||||
data(selection, function(data) {
|
||||
container.style('display', function() {
|
||||
@@ -57,11 +69,21 @@ d3.typeahead = function() {
|
||||
.append('a')
|
||||
.text(function(d) { return d.value; })
|
||||
.attr('title', function(d) { return d.title; })
|
||||
.on('click', function(d) { selection.property('value', d.value); });
|
||||
.on('click', select);
|
||||
|
||||
options.exit().remove();
|
||||
|
||||
options
|
||||
.classed('selected', function(d, i) { return i == idx; });
|
||||
});
|
||||
}
|
||||
|
||||
function select(d) {
|
||||
selection
|
||||
.property('value', d.value)
|
||||
.trigger('change');
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
typeahead.data = function(_) {
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<script src='../js/lib/d3.geo.tile.js'></script>
|
||||
<script src='../js/lib/d3.keybinding.js'></script>
|
||||
<script src='../js/lib/d3.size.js'></script>
|
||||
<script src='../js/lib/d3.trigger.js'></script>
|
||||
<script src='../js/lib/d3.typeahead.js'></script>
|
||||
<script src='../js/lib/d3.one.js'></script>
|
||||
<script src='../js/lib/ohauth.js'></script>
|
||||
|
||||
@@ -130,4 +130,18 @@ describe('iD.Entity', function () {
|
||||
expect(iD.Entity({tags: {'tiger:source': 'blah', 'tiger:foo': 'bar'}}).hasInterestingTags()).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#friendlyName", function () {
|
||||
it("returns the name", function () {
|
||||
expect(iD.Entity({ tags: { name: 'hi' }}).friendlyName()).to.equal('hi');
|
||||
});
|
||||
|
||||
it("returns a highway tag value", function () {
|
||||
expect(iD.Entity({ tags: { highway: 'Route 5' }}).friendlyName()).to.equal('Route 5');
|
||||
});
|
||||
|
||||
it("prefers the name to a highway tag value", function () {
|
||||
expect(iD.Entity({ tags: { name: 'hi', highway: 'Route 5' }}).friendlyName()).to.equal('hi');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -36,4 +36,14 @@ describe('iD.Node', function () {
|
||||
expect(iD.Node({loc: [0, 0]}).intersects([[100, 90], [180, -90]])).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#geometry", function () {
|
||||
it("returns 'vertex' if the node is not a point", function () {
|
||||
expect(iD.Node().geometry()).to.equal('vertex');
|
||||
});
|
||||
|
||||
it("returns 'point' if the node is a point", function () {
|
||||
expect(iD.Node({_poi: true}).geometry()).to.equal('point');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -110,4 +110,14 @@ describe('iD.Way', function() {
|
||||
expect(iD.Way({tags: { highway: 'residential' }, nodes: ['n1', 'n1']}).isArea()).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#geometry", function() {
|
||||
it("returns 'line' when the way is not an area", function () {
|
||||
expect(iD.Way().geometry()).to.equal('line');
|
||||
});
|
||||
|
||||
it("returns 'area' when the way is an area", function () {
|
||||
expect(iD.Way({tags: { area: 'yes' }}).geometry()).to.equal('area');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,6 +8,8 @@ describe("iD.modes.AddPoint", function () {
|
||||
controller = iD.Controller(map, history);
|
||||
|
||||
container.call(map);
|
||||
container.append('div')
|
||||
.attr('class', 'inspector-wrap');
|
||||
|
||||
mode = iD.modes.AddPoint();
|
||||
controller.enter(mode);
|
||||
|
||||
@@ -26,7 +26,7 @@ describe("iD.taginfo", function() {
|
||||
server.respond();
|
||||
|
||||
expect(query(server.requests[0].url)).to.eql(
|
||||
{query: "amen", page: "1", rp: "20", sortname: "count_all", sortorder: "desc"});
|
||||
{query: "amen", page: "1", rp: "6", sortname: "count_all", sortorder: "desc"});
|
||||
expect(callback).to.have.been.calledWith(null,
|
||||
{"data":[{"count_all":5190337,"key":"amenity"}]});
|
||||
});
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
describe("iD.Inspector", function () {
|
||||
var inspector, element,
|
||||
tags = {highway: 'residential'},
|
||||
entity = iD.Entity({type: 'node', id: "n12345", tags: tags});
|
||||
entity;
|
||||
|
||||
beforeEach(function () {
|
||||
function render() {
|
||||
inspector = iD.Inspector();
|
||||
element = d3.select('body')
|
||||
.append('div')
|
||||
.attr('id', 'inspector-wrap')
|
||||
.datum(entity)
|
||||
.call(inspector);
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
entity = iD.Entity({type: 'node', id: "n12345", tags: tags});
|
||||
render();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
@@ -22,9 +27,9 @@ describe("iD.Inspector", function () {
|
||||
});
|
||||
|
||||
it("returns updated tags when input values have changed", function () {
|
||||
element.selectAll(".tag-row-empty input.key").property('value', 'k');
|
||||
element.selectAll(".tag-row-empty input.value").property('value', 'v');
|
||||
expect(inspector.tags()).to.eql({highway: 'residential', k: 'v'});
|
||||
element.selectAll("input.key").property('value', 'k');
|
||||
element.selectAll("input.value").property('value', 'v');
|
||||
expect(inspector.tags()).to.eql({k: 'v'});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -33,17 +38,22 @@ describe("iD.Inspector", function () {
|
||||
expect(element.selectAll("input[value=residential]")).not.to.be.empty;
|
||||
});
|
||||
|
||||
it("creates one trailing pair of empty input elements", function () {
|
||||
it("creates a pair of empty input elements if the entity has no tags", function () {
|
||||
element.remove();
|
||||
entity = entity.update({tags: {}});
|
||||
render();
|
||||
expect(element.selectAll("input.value").property('value')).to.be.empty;
|
||||
expect(element.selectAll("input.key").property('value')).to.be.empty;
|
||||
});
|
||||
|
||||
it("adds tags when clicking the add button", function () {
|
||||
element.selectAll("button.add-tag").trigger('click');
|
||||
expect(element.selectAll("input")[0][2].value).to.be.empty;
|
||||
expect(element.selectAll("input")[0][3].value).to.be.empty;
|
||||
});
|
||||
|
||||
it("sets the 'tag-row-empty' class on the placeholder row", function () {
|
||||
expect(element.selectAll(".tag-row:last-child").classed('tag-row-empty')).to.be.true;
|
||||
});
|
||||
|
||||
it("removes tags when clicking the remove button", function () {
|
||||
happen.click(element.selectAll("button.remove").node());
|
||||
element.selectAll("button.remove").trigger('click');
|
||||
expect(inspector.tags()).to.eql({});
|
||||
});
|
||||
|
||||
@@ -51,7 +61,7 @@ describe("iD.Inspector", function () {
|
||||
var spy = sinon.spy();
|
||||
inspector.on('close', spy);
|
||||
|
||||
happen.click(element.select('.close').node());
|
||||
element.select('.close').trigger('click');
|
||||
|
||||
expect(spy).to.have.been.calledWith(entity);
|
||||
});
|
||||
@@ -60,7 +70,7 @@ describe("iD.Inspector", function () {
|
||||
var spy = sinon.spy();
|
||||
inspector.on('changeTags', spy);
|
||||
|
||||
happen.click(element.select('.apply').node());
|
||||
element.select('.apply').trigger('click');
|
||||
|
||||
expect(spy).to.have.been.calledWith(entity, tags);
|
||||
});
|
||||
@@ -69,7 +79,7 @@ describe("iD.Inspector", function () {
|
||||
var spy = sinon.spy();
|
||||
inspector.on('remove', spy);
|
||||
|
||||
happen.click(element.select('.delete').node());
|
||||
element.select('.delete').trigger('click');
|
||||
|
||||
expect(spy).to.have.been.calledWith(entity);
|
||||
});
|
||||
|
||||
@@ -24,12 +24,6 @@ describe('Util', function() {
|
||||
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');
|
||||
expect(iD.util.friendlyName({ tags: { name: 'hi', highway: 'Route 5' }})).to.equal('hi');
|
||||
});
|
||||
|
||||
describe('geo', function() {
|
||||
describe('#roundCoords', function() {
|
||||
expect(iD.util.geo.roundCoords([0.1, 1])).to.eql([0, 1]);
|
||||
|
||||
Reference in New Issue
Block a user