resolved conflict

This commit is contained in:
Saman Bemel-Benrud
2013-01-17 19:03:19 -05:00
33 changed files with 504 additions and 186 deletions

View File

@@ -1064,7 +1064,17 @@ div.typeahead a:first-child {
}
.Browse .tooltip .tooltip-arrow {
left: 30px;
}
left: 30px;
}
.tail {
pointer-events:none;
position: absolute;
background: rgba(255, 255, 255, 0.7);
max-width: 250px;
margin-top: -15px;
padding: 5px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}

View File

@@ -65,9 +65,12 @@ g.vertex circle.stroke {
fill:#333;
}
g.vertex.shared circle {
g.vertex.shared circle.fill {
fill:#aff;
}
g.vertex.shared circle.stroke {
fill:#044;
}
g.vertex.hover circle.fill {
-webkit-transform:scale(1.5, 1.5);
@@ -81,9 +84,12 @@ g.vertex.hover circle.stroke {
transform:scale(1.4, 1.4);
}
g.vertex circle.selected {
g.vertex circle.selected.fill {
fill: #ffff00;
}
g.vertex circle.selected.stroke {
fill: #38380A;
}
circle.midpoint {
fill:#aaa;
@@ -252,9 +258,6 @@ path.casing.tag-highway-secondary_link {
stroke:#444;
}
path.stroke.tag-bridge-yes {
stroke:#eee;
}
path.casing.tag-bridge-yes {
stroke-width: 14;
stroke: #000;

View File

@@ -22,6 +22,7 @@
<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.tail.js'></script>
<script src='js/lib/d3-compat.js'></script>
<script src='js/lib/queue.js'></script>
<script src='js/lib/bootstrap-tooltip.js'></script>
@@ -31,7 +32,6 @@
<script src='js/id/oauth.js'></script>
<script src='js/id/services/taginfo.js'></script>
<script src='js/id/renderer/style.js'></script>
<script src='js/id/renderer/background.js'></script>
<script src='js/id/renderer/background_source.js'></script>
<script src='js/id/renderer/map.js'></script>

View File

@@ -16,6 +16,7 @@ iD.Connection = function() {
function bboxFromAPI(box, tile, callback) {
function done(err, parsed) {
loadedTiles[tile.toString()] = true;
delete inflight[tile.toString()];
callback(err, parsed);
}
inflight[tile.toString()] = loadFromURL(bboxUrl(box), done);
@@ -26,8 +27,6 @@ iD.Connection = function() {
return callback(null, parse(dom));
}
return d3.xml(url).get().on('load', done);
inflight.push(d3.xml(url).get()
.on('load', done));
}
function getNodes(obj) {
@@ -201,7 +200,7 @@ iD.Connection = function() {
}
function loadTiles(projection) {
var scaleExtent = [16, 16],
var scaleExtent = [15, 15],
s = projection.scale(),
tiles = d3.geo.tile()
.scaleExtent(scaleExtent)

View File

@@ -13,6 +13,7 @@ iD.Graph = function(entities) {
this.transients = {};
this._parentWays = {};
this._parentRels = {};
this._fetches = {};
if (iD.debug) {
Object.freeze(this);
@@ -119,20 +120,29 @@ iD.Graph.prototype = {
// Resolve the id references in a way, replacing them with actual objects.
fetch: function(id) {
if (this._fetches[id]) return this._fetches[id];
var entity = this.entities[id], nodes = [];
if (!entity || !entity.nodes || !entity.nodes.length) return entity;
for (var i = 0, l = entity.nodes.length; i < l; i++) {
nodes[i] = this.fetch(entity.nodes[i]);
}
return iD.Entity(entity, {nodes: nodes});
return (this._fetches[id] = iD.Entity(entity, {nodes: nodes}));
},
difference: function (graph) {
var result = [], entity, id;
var result = [], entity, oldentity, id;
for (id in this.entities) {
entity = this.entities[id];
if (entity !== graph.entities[id]) {
oldentity = graph.entities[id];
if (entity !== oldentity) {
if (entity && entity.type === 'way') {
result = oldentity ?
result
.concat(_.difference(entity.nodes, oldentity.nodes))
.concat(_.difference(oldentity.nodes, entity.nodes))
: result.concat(entity.nodes);
}
result.push(id);
}
}
@@ -141,6 +151,7 @@ iD.Graph.prototype = {
entity = graph.entities[id];
if (entity && !this.entities.hasOwnProperty(id)) {
result.push(id);
if (entity.type === 'way') result = result.concat(entity.nodes);
}
}

View File

@@ -6,7 +6,8 @@ iD.Way = iD.Entity.extend({
return resolver.transient(this, 'extent', function() {
var extent = [[-Infinity, Infinity], [Infinity, -Infinity]];
for (var i = 0, l = this.nodes.length; i < l; i++) {
var node = resolver.entity(this.nodes[i]);
var node = this.nodes[i];
if (node.loc === undefined) node = resolver.entity(node);
if (node.loc[0] > extent[0][0]) extent[0][0] = node.loc[0];
if (node.loc[0] < extent[1][0]) extent[1][0] = node.loc[0];
if (node.loc[1] < extent[0][1]) extent[0][1] = node.loc[1];

View File

@@ -183,9 +183,9 @@ window.iD = function(container) {
.call(redo ? refreshTooltip : undo_tooltip.hide);
});
window.onresize = function() {
d3.select(window).on('resize.map-size', function() {
map.size(m.size());
};
});
map.keybinding()
.on('a', function(evt, mods) {

View File

@@ -12,7 +12,7 @@ iD.modes.AddArea = function() {
controller = mode.controller;
map.dblclickEnable(false)
.hint('Click on the map to start drawing an area, like a park, lake, or building.');
.tail('Click on the map to start drawing an area, like a park, lake, or building.');
map.surface.on('click.addarea', function() {
var datum = d3.select(d3.event.target).datum() || {},
@@ -23,8 +23,7 @@ iD.modes.AddArea = function() {
history.perform(
iD.actions.AddWay(way),
iD.actions.AddWayNode(way.id, datum.id),
iD.actions.AddWayNode(way.id, datum.id),
'started an area');
iD.actions.AddWayNode(way.id, datum.id));
} else {
// start from a new node
@@ -33,8 +32,7 @@ iD.modes.AddArea = function() {
iD.actions.AddWay(way),
iD.actions.AddNode(node),
iD.actions.AddWayNode(way.id, node.id),
iD.actions.AddWayNode(way.id, node.id),
'started an area');
iD.actions.AddWayNode(way.id, node.id));
}
controller.enter(iD.modes.DrawArea(way.id));
@@ -49,7 +47,7 @@ iD.modes.AddArea = function() {
window.setTimeout(function() {
mode.map.dblclickEnable(true);
}, 1000);
mode.map.hint(false);
mode.map.tail(false);
mode.map.surface.on('click.addarea', null);
mode.map.keybinding().on('⎋.addarea', null);
};

View File

@@ -13,7 +13,7 @@ iD.modes.AddLine = function() {
controller = mode.controller;
map.dblclickEnable(false)
.hint('Click on the map to start drawing an road, path, or route.');
.tail('Click on the map to start drawing an road, path, or route.');
map.surface.on('click.addline', function() {
var datum = d3.select(d3.event.target).datum() || {},
@@ -33,8 +33,7 @@ iD.modes.AddLine = function() {
} else {
history.perform(
iD.actions.AddWay(way),
iD.actions.AddWayNode(way.id, datum.id),
'started a line');
iD.actions.AddWayNode(way.id, datum.id));
}
} else if (datum.type === 'way') {
@@ -46,8 +45,7 @@ iD.modes.AddLine = function() {
iD.actions.AddWay(way),
iD.actions.AddNode(node),
iD.actions.AddWayNode(datum.id, node.id, choice.index),
iD.actions.AddWayNode(way.id, node.id),
'started a line');
iD.actions.AddWayNode(way.id, node.id));
} else {
// begin a new way
@@ -56,8 +54,7 @@ iD.modes.AddLine = function() {
history.perform(
iD.actions.AddWay(way),
iD.actions.AddNode(node),
iD.actions.AddWayNode(way.id, node.id),
'started a line');
iD.actions.AddWayNode(way.id, node.id));
}
controller.enter(iD.modes.DrawLine(way.id, direction));
@@ -70,7 +67,7 @@ iD.modes.AddLine = function() {
mode.exit = function() {
mode.map.dblclickEnable(true);
mode.map.hint(false);
mode.map.tail(false);
mode.map.surface.on('click.addline', null);
mode.map.keybinding().on('⎋.addline', null);
};

View File

@@ -10,7 +10,7 @@ iD.modes.AddPoint = function() {
history = mode.history,
controller = mode.controller;
map.hint('Click on the map to add a point.');
map.tail('Click on the map to add a point.');
map.surface.on('click.addpoint', function() {
var node = iD.Node({loc: map.mouseCoordinates(), _poi: true});
@@ -28,7 +28,7 @@ iD.modes.AddPoint = function() {
};
mode.exit = function() {
mode.map.hint(false);
mode.map.tail(false);
mode.map.surface.on('click.addpoint', null);
mode.map.keybinding().on('⎋.addpoint', null);
};

View File

@@ -18,8 +18,7 @@ iD.modes.DrawArea = function(wayId) {
map.dblclickEnable(false)
.fastEnable(false);
map.hint('Click on the map to add points to your area. Finish the ' +
'area by clicking on your first point');
map.tail('Click to add points to your area. Click the first point to finish the area.');
history.perform(
iD.actions.AddNode(node),
@@ -42,22 +41,28 @@ iD.modes.DrawArea = function(wayId) {
var datum = d3.select(d3.event.target).datum() || {};
if (datum.id === tailId || datum.id === headId) {
history.replace(iD.actions.DeleteNode(node.id));
controller.enter(iD.modes.Select(way));
if (way.nodes.length > 3) {
history.undo();
controller.enter(iD.modes.Select(way));
} else {
// Areas with less than 3 nodes gets deleted
history.replace(iD.actions.DeleteWay(way.id));
controller.enter(iD.modes.Browse());
}
} else if (datum.type === 'node' && datum.id !== node.id) {
// connect the way to an existing node
history.replace(
iD.actions.DeleteNode(node.id),
iD.actions.AddWayNode(way.id, datum.id, -1),
'added to an area');
way.nodes.length > 2 ? 'added to an area' : '');
controller.enter(iD.modes.DrawArea(wayId));
} else {
history.replace(
iD.actions.Noop(),
'added to an area');
way.nodes.length > 2 ? 'added to an area' : '');
controller.enter(iD.modes.DrawArea(wayId));
}
@@ -110,7 +115,7 @@ iD.modes.DrawArea = function(wayId) {
surface.selectAll('.way, .node')
.classed('active', false);
mode.map.hint(false);
mode.map.tail(false);
mode.map.fastEnable(true);
surface

View File

@@ -19,7 +19,7 @@ iD.modes.DrawLine = function(wayId, direction) {
map.dblclickEnable(false)
.fastEnable(false)
.hint('Click to add more points to the line. ' +
.tail('Click to add more points to the line. ' +
'Click on other lines to connect to them, and double-click to ' +
'end the line.');
@@ -42,16 +42,22 @@ iD.modes.DrawLine = function(wayId, direction) {
if (datum.id === tailId) {
// connect the way in a loop
history.replace(
iD.actions.DeleteNode(node.id),
iD.actions.AddWayNode(wayId, tailId, index),
'added to a line');
if (way.nodes.length > 2) {
history.replace(
iD.actions.DeleteNode(node.id),
iD.actions.AddWayNode(wayId, tailId, index),
'added to a line');
controller.enter(iD.modes.Select(way));
controller.enter(iD.modes.Select(way));
} else {
history.replace(iD.actions.DeleteWay(way.id));
controller.enter(iD.modes.Browse());
}
} else if (datum.id === headId) {
// finish the way
history.replace(iD.actions.DeleteNode(node.id));
history.undo();
controller.enter(iD.modes.Select(way));
@@ -146,7 +152,7 @@ iD.modes.DrawLine = function(wayId, direction) {
surface.selectAll('.way, .node')
.classed('active', false);
mode.map.hint(false);
mode.map.tail(false);
mode.map.fastEnable(true);
mode.map.minzoom(0);

View File

@@ -87,11 +87,12 @@ iD.modes.Select = function (entity) {
// Exit mode if selected entity gets undone
mode.history.on('change.entity-undone', function() {
var old = entity;
entity = mode.history.graph().entity(entity.id);
if (!entity) {
mode.controller.enter(iD.modes.Browse());
} else {
d3.select('.inspector-wrap').datum(entity).call(inspector);
} else if(!_.isEqual(entity.tags, old.tags)) {
inspector.tags(entity.tags);
}
});
@@ -138,7 +139,7 @@ iD.modes.Select = function (entity) {
mode.exit = function () {
var surface = mode.map.surface;
changeTags(entity, inspector.tags());
entity && changeTags(entity, inspector.tags());
d3.select('.inspector-wrap')
.style('display', 'none')
.html('');

View File

@@ -55,14 +55,36 @@ iD.Background = function() {
ups = {};
tiles.forEach(function(d) {
d.push(source(d));
// this tile has not been loaded yet
if (!cache[d] &&
cache[atZoom(d, -1)] &&
// if this tile has already failed, do
// not request it
if (cache[d] !== false) d.push(source(d));
// if this tile has failed, try to request its tile above
if (cache[d] === false &&
cache[atZoom(d, -1)] !== false &&
!ups[atZoom(d, -1)]) {
ups[atZoom(d, -1)] = true;
tiles.push(atZoom(d, -1));
} else if (!cache[d]) {
// if this tile has not finished, req the one above
} else if (cache[d] === undefined &&
// but the tile above is in the cache
cache[atZoom(d, -1)] &&
// and another tile has not already requested the
// tile above
!ups[atZoom(d, -1)]) {
ups[atZoom(d, -1)] = true;
tiles.push(atZoom(d, -1));
// if this tile has not yet completed, try keeping the
// tiles below it
} else if (cache[d] === undefined ||
cache[d] === false) {
upZoom(d, 1).forEach(function(u) {
if (cache[u] && !ups[u]) {
ups[u] = true;
@@ -79,11 +101,12 @@ iD.Background = function() {
image.exit().remove();
function load(d) {
cache[d] = true;
cache[d.slice(0, 3)] = true;
d3.select(this).on('load', null);
}
function error() {
function error(d) {
cache[d.slice(0, 3)] = false;
d3.select(this).remove();
}

View File

@@ -21,6 +21,7 @@ iD.Map = function() {
lines = iD.svg.Lines(),
areas = iD.svg.Areas(),
midpoints = iD.svg.Midpoints(),
tail = d3.tail(),
surface, tilegroup;
function map(selection) {
@@ -29,14 +30,14 @@ iD.Map = function() {
var supersurface = selection.append('div')
.style('position', 'absolute')
.on('mousedown.drag', function() {
translateStart = projection.translate();
})
.call(zoom);
surface = supersurface.append('svg')
.on('mouseup.reset-transform', resetTransform)
.on('touchend.reset-transform', resetTransform)
.on('mousedown.drag', function() {
translateStart = projection.translate();
})
.on('mousedown.zoom', function() {
if (d3.event.button == 2) {
d3.event.stopPropagation();
@@ -44,9 +45,13 @@ iD.Map = function() {
})
.call(iD.svg.Surface());
map.size(selection.size());
map.surface = surface;
supersurface
.call(tail);
d3.select(document).call(keybinding);
}
@@ -62,16 +67,22 @@ iD.Map = function() {
all = graph.intersects(extent);
filter = d3.functor(true);
} else {
var only = {};
var only = {},
filterOnly = {};
for (var j = 0; j < difference.length; j++) {
var id = difference[j];
only[id] = graph.fetch(id);
if (only[id] && only[id].type === 'node') {
var parents = graph.parentWays(only[id]);
for (var k = 0; k < parents.length; k++) {
// Don't re-fetch parents
if (only[parents[k].id] === undefined) {
only[parents[k].id] = graph.fetch(parents[k].id);
var id = difference[j],
entity = graph.fetch(id);
// Even if the entity is false (deleted), it needs to be
// removed from the surface
only[id] = entity;
if (entity && entity.intersects(extent, graph)) {
if (only[id].type === 'node') {
var parents = graph.parentWays(only[id]);
for (var k = 0; k < parents.length; k++) {
// Don't re-fetch parents
if (only[parents[k].id] === undefined) {
only[parents[k].id] = graph.fetch(parents[k].id);
}
}
}
}
@@ -80,7 +91,7 @@ iD.Map = function() {
filter = function(d) { return d.midpoint ? d.way in only : d.id in only; };
}
if (all.length > 10000) {
if (all.length > 100000) {
editOff();
return;
}
@@ -113,14 +124,14 @@ iD.Map = function() {
if (Math.log(d3.event.scale / Math.LN2 - 8) < minzoom + 1) {
iD.flash()
.select('.content')
.text('Cannot zoom out further in current mode.')
.text('Cannot zoom out further in current mode.');
return map.zoom(16);
}
var fast = (d3.event.scale === projection.scale() && fastEnabled);
projection
.translate(d3.event.translate)
.scale(d3.event.scale);
if (fast) {
if (fast && translateStart) {
var a = d3.event.translate,
b = translateStart,
translate = 'translate(' + ~~(a[0] - b[0]) + 'px,' +
@@ -278,6 +289,11 @@ iD.Map = function() {
return map;
};
map.tail = function (_) {
tail.text(_);
return map;
};
map.hint = function (_) {
if (_ === false) {
d3.select('div.inspector-wrap')

View File

@@ -1,38 +0,0 @@
iD.Style = {};
// all styling that is done outside of CSS in iD.
//
// Since SVG does not support z-index, we sort roads manually with d3's `sort`
// and the `waystack` fn.
//
// This also chooses kosher CSS classes for ways, and images for points
iD.Style.highway_stack = {
motorway: 0,
motorway_link: 1,
trunk: 2,
trunk_link: 3,
primary: 4,
primary_link: 5,
secondary: 6,
tertiary: 7,
unclassified: 8,
residential: 9,
service: 10,
footway: 11
};
iD.Style.waystack = function(a, b) {
if (!a || !b) return 0;
if (a.tags.layer !== undefined && b.tags.layer !== undefined) {
return a.tags.layer - b.tags.layer;
}
if (a.tags.bridge) return 1;
if (b.tags.bridge) return -1;
var as = 0, bs = 0;
if (a.tags.highway && b.tags.highway) {
as -= iD.Style.highway_stack[a.tags.highway];
bs -= iD.Style.highway_stack[b.tags.highway];
}
return as - bs;
};

View File

@@ -33,6 +33,25 @@ iD.taginfo = function() {
return _.omit(parameters, 'geometry');
}
function popularKeys(parameters) {
var pop_field = 'count_all_fraction';
if (parameters.filter) pop_field = 'count_' + parameters.filter + '_fraction';
return function(d) { return parseFloat(d[pop_field]) > 0.01; };
}
function popularValues(parameters) {
return function(d) { return parseFloat(d['fraction']) > 0.01; };
}
function valKey(d) { return { value: d.key }; }
function valKeyDescription(d) {
return {
value: d.value,
title: d.description
};
}
taginfo.keys = function(parameters, callback) {
parameters = clean(setSort(setFilter(parameters)));
d3.json(endpoint + 'keys/all?' +
@@ -41,7 +60,10 @@ iD.taginfo = function() {
sortname: 'count_all',
sortorder: 'desc',
page: 1
}, parameters)), callback);
}, parameters)), function(err, d) {
if (err) return callback(err);
callback(null, d.data.filter(popularKeys(parameters)).map(valKey));
});
};
taginfo.values = function(parameters, callback) {
@@ -52,7 +74,10 @@ iD.taginfo = function() {
sortname: 'count_all',
sortorder: 'desc',
page: 1
}, parameters)), callback);
}, parameters)), function(err, d) {
if (err) return callback(err);
callback(null, d.data.filter(popularValues()).map(valKeyDescription));
});
};
taginfo.docs = function(parameters, callback) {

View File

@@ -2,13 +2,13 @@ iD.svg = {
RoundProjection: function (projection) {
return function (d) {
return iD.util.geo.roundCoords(projection(d));
}
};
},
PointTransform: function (projection) {
projection = iD.svg.RoundProjection(projection);
return function (entity) {
return 'translate(' + projection(entity.loc) + ')';
}
};
}
};

View File

@@ -1,5 +1,32 @@
iD.svg.Areas = function() {
return function(surface, graph, entities, filter, projection) {
var area_stack = {
building: 0,
manmade: 1,
natural: 1,
boundary: 2
};
function findKey(a) {
var vals = Object.keys(a.tags).filter(function(k) {
return area_stack[k] !== undefined;
});
if (vals.length > 0) return area_stack[vals[0]];
else return -1;
}
function areastack(a, b) {
if (!a || !b || !a.tags || !b.tags) return 0;
if (a.tags.layer !== undefined && b.tags.layer !== undefined) {
return a.tags.layer - b.tags.layer;
}
var as = 0, bs = 0;
as -= findKey(a);
bs -= findKey(b);
return as - bs;
}
return function drawAreas(surface, graph, entities, filter, projection) {
var areas = [];
for (var i = 0; i < entities.length; i++) {
@@ -9,6 +36,8 @@ iD.svg.Areas = function() {
}
}
areas.sort(areastack);
var lineStrings = {};
function lineString(entity) {

View File

@@ -1,16 +1,68 @@
iD.svg.Lines = function() {
var arrowtext = '►\u3000\u3000',
alength;
var arrowtext = '►\u3000\u3000',
alength;
var highway_stack = {
motorway: 0,
motorway_link: 1,
trunk: 2,
trunk_link: 3,
primary: 4,
primary_link: 5,
secondary: 6,
tertiary: 7,
unclassified: 8,
residential: 9,
service: 10,
footway: 11
};
function waystack(a, b) {
if (!a || !b || !a.tags || !b.tags) return 0;
if (a.tags.layer !== undefined && b.tags.layer !== undefined) {
return a.tags.layer - b.tags.layer;
}
if (a.tags.bridge) return 1;
if (b.tags.bridge) return -1;
var as = 0, bs = 0;
if (a.tags.highway && b.tags.highway) {
as -= highway_stack[a.tags.highway];
bs -= highway_stack[b.tags.highway];
}
return as - bs;
}
function drawPaths(group, lines, filter, classes, lineString) {
var paths = group.selectAll('path')
.filter(filter)
.data(lines, iD.Entity.key);
paths.enter()
.append('path')
.attr('class', classes);
paths
.order()
.attr('d', lineString)
.call(iD.svg.TagClasses());
paths.exit()
.remove();
return paths;
}
return function drawLines(surface, graph, entities, filter, projection) {
return function(surface, graph, entities, filter, projection) {
if (!alength) {
var arrow = surface.append('text').text(arrowtext);
alength = arrow.node().getComputedTextLength();
arrow.remove();
}
var lines = [];
var lines = [],
lineStrings = {};
for (var i = 0; i < entities.length; i++) {
var entity = entities[i];
@@ -19,7 +71,7 @@ iD.svg.Lines = function() {
}
}
var lineStrings = {};
lines.sort(waystack);
function lineString(entity) {
if (lineStrings[entity.id] !== undefined) {
@@ -31,32 +83,12 @@ iD.svg.Lines = function() {
'M' + nodes.map(iD.svg.RoundProjection(projection)).join('L'));
}
function drawPaths(group, lines, filter, classes) {
var paths = group.selectAll('path')
.filter(filter)
.data(lines, iD.Entity.key);
paths.enter()
.append('path')
.attr('class', classes);
paths
.order()
.attr('d', lineString)
.call(iD.svg.TagClasses());
paths.exit()
.remove();
return paths;
}
var casing = surface.select('.layer-casing'),
stroke = surface.select('.layer-stroke'),
defs = surface.select('defs'),
text = surface.select('.layer-text'),
casings = drawPaths(casing, lines, filter, 'way line casing'),
strokes = drawPaths(stroke, lines, filter, 'way line stroke');
casings = drawPaths(casing, lines, filter, 'way line casing', lineString),
strokes = drawPaths(stroke, lines, filter, 'way line stroke', lineString);
// Determine the lengths of oneway paths
var lengths = {},
@@ -97,5 +129,5 @@ iD.svg.Lines = function() {
// adding longer text than necessary, since overflow is hidden
return (new Array(Math.floor(lengths[d.id] * 1.1))).join(arrowtext);
});
}
};
};

View File

@@ -1,5 +1,5 @@
iD.svg.Midpoints = function() {
return function(surface, graph, entities, filter, projection) {
return function drawMidpoints(surface, graph, entities, filter, projection) {
var midpoints = [];
for (var i = 0; i < entities.length; i++) {

View File

@@ -10,7 +10,7 @@ iD.svg.Points = function() {
return 'icons/unknown.png';
}
return function(surface, graph, entities, filter, projection) {
return function drawPoints(surface, graph, entities, filter, projection) {
var points = [];
for (var i = 0; i < entities.length; i++) {
@@ -20,6 +20,10 @@ iD.svg.Points = function() {
}
}
if (points.length > 100) {
return surface.select('.layer-hit').selectAll('g.point').remove();
}
var groups = surface.select('.layer-hit').selectAll('g.point')
.filter(filter)
.data(points, iD.Entity.key);

View File

@@ -1,5 +1,5 @@
iD.svg.Surface = function() {
return function(selection) {
return function drawSurface(selection) {
selection.append('defs')
.append('clipPath')
.attr('id', 'clip')

View File

@@ -1,5 +1,5 @@
iD.svg.Vertices = function() {
return function(surface, graph, entities, filter, projection) {
return function drawVertices(surface, graph, entities, filter, projection) {
var vertices = [];
for (var i = 0; i < entities.length; i++) {
@@ -9,6 +9,10 @@ iD.svg.Vertices = function() {
}
}
if (vertices.length > 2000) {
return surface.select('.layer-hit').selectAll('g.vertex').remove();
}
var groups = surface.select('.layer-hit').selectAll('g.vertex')
.filter(filter)
.data(vertices, iD.Entity.key);

View File

@@ -83,6 +83,7 @@ iD.ui.inspector = function() {
}
function drawButtons(selection) {
<<<<<<< HEAD
var inspectorButton1 = selection.append('div')
.attr('class', 'button-wrap')
.append('button')
@@ -111,7 +112,8 @@ iD.ui.inspector = function() {
tags = [{key: '', value: ''}];
}
var li = tagList.selectAll('li')
var li = tagList.html('')
.selectAll('li')
.data(tags, function(d) { return d.key; });
li.exit().remove();
@@ -166,7 +168,6 @@ iD.ui.inspector = function() {
if (en.on_node) types.push('point');
if (en.on_way) types.push('line');
en.types = types;
console.log(en);
iD.ui.modal()
.select('.content')
.datum(en)
@@ -225,15 +226,26 @@ iD.ui.inspector = function() {
key = row.selectAll('.key'),
value = row.selectAll('.value');
function sort(value, data) {
var sameletter = [],
other = [];
for (var i = 0; i < data.length; i++) {
if (data[i].value.substring(0, value.length) === value) {
sameletter.push(data[i]);
} else {
other.push(data[i]);
}
}
return sameletter.concat(other);
}
key.call(d3.typeahead()
.data(_.debounce(function(_, callback) {
taginfo.keys({
geometry: geometry,
query: key.property('value')
}, function(err, data) {
callback(data.data.map(function (d) {
return {value: d.key};
}));
if (!err) callback(sort(key.property('value'), data));
});
}, 500)));
@@ -244,9 +256,7 @@ iD.ui.inspector = function() {
geometry: geometry,
query: value.property('value')
}, function(err, data) {
callback(data.data.map(function (d) {
return {value: d.value, title: d.description};
}));
if (!err) callback(sort(value.property('value'), data));
});
}, 500)));
}
@@ -272,15 +282,19 @@ iD.ui.inspector = function() {
event.close(entity);
}
inspector.tags = function () {
var tags = {};
tagList.selectAll('li').each(function() {
var row = d3.select(this),
key = row.selectAll('.key').property('value'),
value = row.selectAll('.value').property('value');
if (key !== '') tags[key] = value;
});
return tags;
inspector.tags = function (tags) {
if (!arguments.length) {
var tags = {};
tagList.selectAll('li').each(function() {
var row = d3.select(this),
key = row.selectAll('.key').property('value'),
value = row.selectAll('.value').property('value');
if (key !== '') tags[key] = value;
});
return tags;
} else {
drawTags(tags);
}
};
return d3.rebind(inspector, event, 'on');

70
js/lib/d3.tail.js Normal file
View File

@@ -0,0 +1,70 @@
d3.tail = function() {
var text = false,
container,
xmargin = 20,
tooltip_size = [0, 0],
selection_size = [0, 0],
transformProp = iD.util.prefixCSSProperty('Transform');
var tail = function(selection) {
d3.select(window).on('resize.tail-size', function() {
selection_size = selection.size();
});
function setup() {
container = d3.select(document.body)
.append('div').attr('class', 'tail');
selection
.on('mousemove.tail', mousemove)
.on('mouseover.tail', mouseover)
.on('mouseout.tail', mouseout);
container
.on('mousemove.tail', mousemove);
selection_size = selection.size();
}
function mousemove() {
if (text === false) return;
var xoffset = ((d3.event.x + tooltip_size[0] + xmargin) > selection_size[0]) ?
-tooltip_size[0] - xmargin : xoffset = xmargin;
container.style(transformProp, 'translate(' +
(~~d3.event.x + xoffset) + 'px,' +
~~d3.event.y + 'px)');
}
function mouseout() {
if (d3.event.relatedTarget !== container.node() &&
text !== false) container.style('display', 'none');
}
function mouseover() {
if (d3.event.relatedTarget !== container.node() &&
text !== false) container.style('display', 'block');
}
if (!container) setup();
};
tail.text = function(_) {
if (_ === false) {
text = _;
container.style('display', 'none');
return tail;
} else if (container.style('display') == 'none') {
container.style('display', 'block');
}
text = _;
container.text(text);
tooltip_size = container.size();
return tail;
};
return tail;
};

View File

@@ -2,7 +2,7 @@ d3.typeahead = function() {
var data;
var typeahead = function(selection) {
var container, hidden, idx = 0;
var container, hidden, idx = -1;
function setup() {
var rect = selection.node().getBoundingClientRect();
@@ -20,7 +20,7 @@ d3.typeahead = function() {
function hide() {
container.remove();
idx = 0;
idx = -1;
hidden = true;
}
@@ -33,14 +33,17 @@ d3.typeahead = function() {
.on('blur.typeahead', slowHide);
function key() {
var len = container.selectAll('a').data().length;
if (d3.event.keyCode === 40) {
idx++;
idx = Math.min(idx + 1, len - 1);
return highlight();
} else if (d3.event.keyCode === 38) {
idx--;
idx = Math.max(idx - 1, 0);
return highlight();
} else if (d3.event.keyCode === 13) {
select(container.select('a.selected').datum());
if (container.select('a.selected').node()) {
select(container.select('a.selected').datum());
}
hide();
} else {
update();

View File

@@ -37,7 +37,6 @@
<script src='../js/id/renderer/background_source.js'></script>
<script src='../js/id/renderer/map.js'></script>
<script src='../js/id/renderer/hash.js'></script>
<script src='../js/id/renderer/style.js'></script>
<script src="../js/id/svg.js"></script>
<script src="../js/id/svg/areas.js"></script>
@@ -55,6 +54,9 @@
<script src='../js/id/ui/userpanel.js'></script>
<script src='../js/id/ui/geocoder.js'></script>
<script src='../js/id/ui/notice.js'></script>
<script src='../js/id/ui/modal.js'></script>
<script src='../js/id/ui/flash.js'></script>
<script src='../js/id/ui/confirm.js'></script>
<script src='../js/id/actions.js'></script>
<script src='../js/id/actions/add_node.js'></script>
@@ -152,6 +154,10 @@
<script src="spec/ui/inspector.js"></script>
<script src="spec/ui/geocoder.js"></script>
<script src="spec/ui/modal.js"></script>
<script src="spec/ui/flash.js"></script>
<script src="spec/ui/confirm.js"></script>
<script src="spec/connection.js"></script>
<script src="spec/oauth.js"></script>
<script src="spec/taginfo.js"></script>

View File

@@ -1,8 +1,9 @@
describe("iD.taginfo", function() {
var server;
var server, taginfo;
beforeEach(function() {
server = sinon.fakeServer.create();
taginfo = iD.taginfo();
});
afterEach(function() {
@@ -15,46 +16,81 @@ describe("iD.taginfo", function() {
describe("#keys", function() {
it("calls the given callback with the results of the keys query", function() {
var taginfo = iD.taginfo(),
callback = sinon.spy();
var callback = sinon.spy();
taginfo.keys({query: "amen"}, callback);
server.respondWith("GET", new RegExp("http://taginfo.openstreetmap.org/api/4/keys/all"),
[200, { "Content-Type": "application/json" },
'{"data":[{"count_all":5190337,"key":"amenity"}]}']);
'{"data":[{"count_all":5190337,"key":"amenity","count_all_fraction":1.0}]}']);
server.respond();
expect(query(server.requests[0].url)).to.eql(
{query: "amen", page: "1", rp: "6", sortname: "count_all", sortorder: "desc"});
expect(callback).to.have.been.calledWith(null,
{"data":[{"count_all":5190337,"key":"amenity"}]});
expect(callback).to.have.been.calledWith(null, [{"value":"amenity"}]);
});
it("filters only popular nodes", function() {
var callback = sinon.spy();
taginfo.keys({query: "amen"}, callback);
server.respondWith("GET", new RegExp("http://taginfo.openstreetmap.org/api/4/keys/all"),
[200, { "Content-Type": "application/json" },
'{"data":[{"count_all":5190337,"key":"amenity","count_all_fraction":1.0, "count_nodes_fraction":1.0},\
{"count_all":1,"key":"amenityother","count_all_fraction":0.0, "count_nodes_fraction":0.0}]}']);
server.respond();
expect(callback).to.have.been.calledWith(null, [{"value":"amenity"}]);
});
it("filters only popular nodes with an entity type filter", function() {
var callback = sinon.spy();
taginfo.keys({query: "amen", filter: "nodes"}, callback);
server.respondWith("GET", new RegExp("http://taginfo.openstreetmap.org/api/4/keys/all"),
[200, { "Content-Type": "application/json" },
'{"data":[{"count_all":5190337,"key":"amenity","count_all_fraction":1.0, "count_nodes_fraction":1.0},\
{"count_all":1,"key":"amenityother","count_all_fraction":0.0, "count_nodes_fraction":1.0}]}']);
server.respond();
expect(callback).to.have.been.calledWith(null, [{"value":"amenity"},{"value":"amenityother"}]);
});
});
describe("#values", function() {
it("calls the given callback with the results of the values query", function() {
var taginfo = iD.taginfo(),
callback = sinon.spy();
var callback = sinon.spy();
taginfo.values({key: "amenity", query: "par"}, callback);
server.respondWith("GET", new RegExp("http://taginfo.openstreetmap.org/api/4/key/values"),
[200, { "Content-Type": "application/json" },
'{"data":[{"value":"parking","description":"A place for parking cars"}]}']);
'{"data":[{"value":"parking","description":"A place for parking cars", "fraction":0.1}]}']);
server.respond();
expect(query(server.requests[0].url)).to.eql(
{key: "amenity", query: "par", page: "1", rp: "20", sortname: 'count_all', sortorder: 'desc'});
expect(callback).to.have.been.calledWith(null,
{"data":[{"value":"parking","description":"A place for parking cars"}]});
expect(callback).to.have.been.calledWith(null, [{"value":"parking","title":"A place for parking cars"}]);
});
it("filters popular values", function() {
var callback = sinon.spy();
taginfo.values({key: "amenity", query: "par"}, callback);
server.respondWith("GET", new RegExp("http://taginfo.openstreetmap.org/api/4/key/values"),
[200, { "Content-Type": "application/json" },
'{"data":[{"value":"parking","description":"A place for parking cars", "fraction":1.0},\
{"value":"party","description":"A place for partying", "fraction":0.0}]}']);
server.respond();
expect(callback).to.have.been.calledWith(null, [{"value":"parking","title":"A place for parking cars"}]);
});
});
describe("#docs", function() {
it("calls the given callback with the results of the docs query", function() {
var taginfo = iD.taginfo(),
callback = sinon.spy();
var callback = sinon.spy();
taginfo.docs({key: "amenity", value: "parking"}, callback);

11
test/spec/ui/confirm.js Normal file
View File

@@ -0,0 +1,11 @@
describe("iD.ui.confirm", function () {
it('can be instantiated', function () {
var confirm = iD.ui.confirm();
expect(confirm).to.be.ok;
});
it('can be dismissed', function () {
var confirm = iD.ui.confirm();
happen.click(confirm.select('button').node());
expect(confirm.node().parentNode).to.be.null;
});
});

13
test/spec/ui/flash.js Normal file
View File

@@ -0,0 +1,13 @@
describe("iD.ui.flash", function () {
it('can be instantiated', function () {
var flash = iD.ui.flash();
expect(flash).to.be.ok;
});
it('leaves after 1000 ms', function (done) {
var flash = iD.ui.flash();
window.setTimeout(function() {
expect(flash.node().parentNode).to.be.null;
done();
}, 1200);
});
});

8
test/spec/ui/modal.js Normal file
View File

@@ -0,0 +1,8 @@
describe("iD.ui.modal", function () {
it('can be instantiated', function () {
var modal = iD.ui.modal()
.select('.content')
.text('foo');
expect(modal).to.be.ok;
});
});

View File

@@ -61,5 +61,36 @@ describe('Util', function() {
expect(iD.util.geo.dist(a, b)).to.eql(5);
});
});
describe('#pointInPolygon', function() {
it('says a point in a polygon is on a polygon', function() {
var poly = [[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]];
var point = [0.5, 0.5];
expect(iD.util.geo.pointInPolygon(point, poly)).to.be.true;
});
it('says a point outside of a polygon is outside', function() {
var poly = [
[0, 0],
[0, 1],
[1, 1],
[1, 0],
[0, 0]];
var point = [0.5, 1.5];
expect(iD.util.geo.pointInPolygon(point, poly)).to.be.false;
});
});
describe('#polygonContainsPolygon', function() {
it('says a polygon in a polygon is in', function() {
var outer = [[0, 0], [0, 3], [3, 3], [3, 0], [0, 0]];
var inner = [[1, 1], [1, 2], [2, 2], [2, 1], [1, 1]];
expect(iD.util.geo.polygonContainsPolygon(outer, inner)).to.be.true;
});
it('says a polygon outside of a polygon is out', function() {
var outer = [[0, 0], [0, 3], [3, 3], [3, 0], [0, 0]];
var inner = [[1, 1], [1, 9], [2, 2], [2, 1], [1, 1]];
expect(iD.util.geo.polygonContainsPolygon(outer, inner)).to.be.false;
});
});
});
});