mirror of
https://github.com/FoggedLens/iD.git
synced 2026-06-04 14:08:13 +02:00
Merge branch 'master' of github.com:systemed/iD
This commit is contained in:
+17
-14
@@ -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';
|
||||
}
|
||||
});
|
||||
|
||||
+46
-1
@@ -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);
|
||||
|
||||
+23
-2
@@ -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]);
|
||||
|
||||
+46
-35
@@ -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;
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+54
-28
@@ -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');
|
||||
|
||||
+190
-173
@@ -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());
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -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' +
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
};
|
||||
+39
-17
@@ -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(_) {
|
||||
|
||||
Reference in New Issue
Block a user