Merge branch 'master' into dynamic-layers

This commit is contained in:
Tom MacWright
2013-02-05 12:03:27 -05:00
31 changed files with 657 additions and 1210 deletions
+3 -1
View File
@@ -50,7 +50,9 @@ all: \
js/id/validate.js \
js/id/end.js \
locale/locale.js \
locale/en.js
locale/en.js \
data/data.js \
data/deprecated.js
iD.js: Makefile
@rm -f $@
+76 -55
View File
@@ -17,11 +17,11 @@ g.point .shadow {
-moz-transition: fill 100ms linear;
}
.behavior-hover g.point.hover:not(.selected) .shadow {
fill: #E96666;
fill-opacity: 0.3;
fill: #f6634f;
fill-opacity: 0.5;
}
g.point.selected .shadow {
fill: #E96666;
fill: #f6634f;
fill-opacity: 0.7;
}
@@ -30,8 +30,10 @@ g.point.selected .shadow {
g.vertex .fill {
fill:white;
}
g.vertex .stroke {
stroke:#333;
stroke:black;
stroke-opacity: .5;
stroke-width:2;
fill:white;
}
@@ -101,18 +103,18 @@ g.vertex.shared .fill {
g.vertex .shadow {
fill: none;
pointer-events: all;
stroke-width: 10;
stroke-width: 20;
-webkit-transition: -webkit-transform 100ms linear;
transition: transform 100ms linear;
-moz-transition: fill 100ms linear;
}
.behavior-hover g.vertex.hover:not(.selected) .shadow {
fill: #E96666;
fill: #f6634f;
fill-opacity: 0.3;
}
}
g.vertex.selected .shadow {
fill: #E96666;
fill-opacity: 0.7;
fill: #f6634f;
fill-opacity: 0.5;
}
/* midpoints */
@@ -121,16 +123,20 @@ g.vertex.selected .shadow {
.mode-draw-line g.midpoint,
.mode-add-area g.midpoint,
.mode-add-line g.midpoint,
.mode-add-point g.midpoint {
.mode-add-point g.midpoint,
.behavior-drag-node g.midpoint {
display: none;
}
g.midpoint .fill {
fill:#aaa;
fill:#ddd;
stroke:black;
stroke-opacity: .5;
opacity: .5;
}
.behavior-hover g.midpoint .fill.hover:not(.selected) {
fill:#fff;
stroke:#000;
fill:white;
opacity: .75;
}
g.midpoint .shadow {
@@ -142,7 +148,7 @@ g.midpoint .shadow {
-moz-transition: fill 100ms linear;
}
.behavior-hover g.midpoint .shadow.hover:not(.selected) {
fill:#E96666;
fill:#f6634f;
fill-opacity: 0.3;
}
@@ -154,8 +160,8 @@ path.line {
}
path.stroke {
stroke: #222;
stroke-width: 2;
stroke: black;
stroke-width: 4;
}
path.shadow {
@@ -165,12 +171,12 @@ path.shadow {
}
.behavior-hover path.shadow.hover:not(.selected) {
stroke: #E96666;
stroke: #f6634f;
stroke-opacity: 0.3;
}
path.shadow.selected {
stroke: #E96666;
stroke: #f6634f;
stroke-opacity: 0.7;
}
@@ -199,31 +205,31 @@ path.area.stroke.selected {
path.area.stroke.tag-natural,
path.multipolygon.tag-natural {
stroke: #ADD6A5;
stroke: #b6e199;
stroke-width:1;
}
path.area.fill.tag-natural,
path.multipolygon.tag-natural {
fill: #ADD6A5;
fill: #b6e199;
}
path.area.stroke.tag-natural-water,
path.multipolygon.tag-natural-water {
stroke: #6382FF;
stroke: #77d3de;
}
path.area.fill.tag-natural-water,
path.multipolygon.tag-natural-water {
fill: #ADBEFF;
fill: #77d3de;
}
path.area.stroke.tag-building,
path.multipolygon.tag-building {
stroke: #9E176A;
stroke: #e06e5f;
stroke-width: 1;
}
path.area.fill.tag-building,
path.multipolygon.tag-building {
fill: #ff6ec7;
fill: #e06e5f;
}
path.area.stroke.tag-landuse,
@@ -236,7 +242,7 @@ path.multipolygon.tag-natural-wood,
path.multipolygon.tag-natural-tree,
path.multipolygon.tag-natural-grassland,
path.multipolygon.tag-leisure-park {
stroke: #006B34;
stroke: #8cd05f;
stroke-width: 1;
}
path.area.fill.tag-landuse,
@@ -249,18 +255,18 @@ path.multipolygon.tag-natural-wood,
path.multipolygon.tag-natural-tree,
path.multipolygon.tag-natural-grassland,
path.multipolygon.tag-leisure-park {
fill: #189E59;
fill: #8cd05f;
fill-opacity: 0.2;
}
path.area.stroke.tag-amenity-parking,
path.multipolygon.tag-amenity-parking {
stroke: #beb267;
stroke: #aaa;
stroke-width: 1;
}
path.area.fill.tag-amenity-parking,
path.multipolygon.tag-amenity-parking {
fill: #edecc0;
fill: #aaa;
}
path.multipolygon.tag-boundary {
@@ -294,56 +300,57 @@ svg[data-zoom="16"] path.stroke.tag-highway {
path.stroke.tag-highway-motorway,
path.stroke.tag-highway-motorway_link,
path.stroke.tag-construction-motorway {
stroke:#809bc0;
stroke:#58a9ed;
}
path.casing.tag-highway-motorway,
path.casing.tag-highway-motorway_link,
path.casing.tag-construction-motorway {
stroke:#506077;
stroke:#2c5476;
}
path.stroke.tag-highway-trunk,
path.stroke.tag-highway-trunk_link,
path.stroke.tag-construction-trunk {
stroke:#97d397;
stroke:#8cd05f;
}
path.casing.tag-highway-trunk,
path.casing.tag-highway-trunk_link,
path.casing.tag-construction-trunk {
stroke:#477147;
stroke:#46682f;
}
path.stroke.tag-highway-primary,
path.stroke.tag-highway-primary_link,
path.stroke.tag-construction-primary {
stroke:#ec989a;
stroke:#e06d5f;
}
path.casing.tag-highway-primary,
path.casing.tag-highway-primary_link,
path.casing.tag-construction-primary {
stroke:#8d4346;
stroke:#70372f;
}
path.stroke.tag-highway-secondary,
path.stroke.tag-highway-secondary_link,
path.stroke.tag-construction-secondary {
stroke:#fecc8b;
stroke:#eab056;
}
path.casing.tag-highway-secondary,
path.casing.tag-highway-secondary_link,
path.casing.tag-construction-secondary {
stroke:#a37b48;
stroke:#75582b;
}
path.stroke.tag-highway-tertiary,
path.stroke.tag-highway-tertiary_link,
path.stroke.tag-construction-tertiary {
stroke:#ffffb3;
stroke:#ffff7e;
}
path.casing.tag-highway-tertiary,
path.casing.tag-highway-tertiary_link,
path.casing.tag-construction-tertiary {
stroke:#bbb;
stroke:#7f7f3f;
}
path.stroke.tag-highway-unclassified,
@@ -380,7 +387,7 @@ path.stroke.tag-highway-pedestrian {
shapeRendering: auto;
}
path.casing.tag-highway-pedestrian {
stroke:#84C382;
stroke:#8cd05f;
stroke-width:6 !important;
}
@@ -453,17 +460,17 @@ svg[data-zoom="16"] path.casing.tag-highway-bridleway {
}
path.stroke.tag-highway-footway {
stroke: #996600;
stroke: #ae8681;
}
path.stroke.tag-highway-cycleway {
stroke: #69f;
stroke: #58a9ed;
}
path.stroke.tag-highway-bridleway {
stroke: green;
stroke: #e06d5f;
}
path.stroke.tag-highway-steps {
stroke: #ff6257;
stroke: #81d25c;
stroke-width: 4;
stroke-linecap: butt;
stroke-dasharray: 3, 3;
@@ -475,7 +482,7 @@ path.casing.tag-highway-steps {
path.casing.tag-bridge-yes {
stroke-width: 14;
stroke: #000;
stroke: #333;
}
path.stroke.tag-highway-construction,
@@ -519,12 +526,16 @@ path.casing.tag-railway-subway {
/* waterways */
path.area.fill.tag-waterway {
fill: #77d3de;
}
path.stroke.tag-waterway {
stroke: #10539a;
stroke: #77d3de;
stroke-width: 2;
}
path.casing.tag-waterway {
stroke: #6AA2FF;
stroke: #77d3de;
stroke-width: 4;
}
@@ -543,11 +554,11 @@ svg[data-zoom="16"] path.casing.tag-waterway-river {
}
path.stroke.tag-waterway-ditch {
stroke: #10539a;
stroke: #6591ff;
stroke-width: 1;
}
path.casing.tag-waterway-ditch {
stroke: #999692;
stroke: #6591ff;
stroke-width: 3;
}
@@ -576,17 +587,22 @@ path.casing.tag-boundary {
path.casing.tag-boundary-protected_area,
path.casing.tag-boundary-national_park {
stroke: #4D9849;
stroke: #b0e298;
}
text {
font-size:10px;
pointer-events: none;
color: #222;
opacity: 1;
}
.oneway .textpath {
pointer-events: none;
font-size: 7px;
baseline-shift: 2px;
opacity: .7;
}
text.tag-oneway {
@@ -614,7 +630,7 @@ text.pathlabel,
text.pointlabel {
font-size: 12px;
font-weight: bold;
fill: black;
fill: #333;
text-anchor: middle;
pointer-events: none;
}
@@ -640,7 +656,8 @@ text.pointlabel {
text.point {
font-size: 9px;
font-size: 10px;
baseline-shift: 2px;
}
/* Cursors */
@@ -700,14 +717,16 @@ text.point {
.mode-draw-line .behavior-hover .way,
.mode-draw-area .behavior-hover .way,
.mode-add-line .behavior-hover .way,
.mode-add-area .behavior-hover .way {
.mode-add-area .behavior-hover .way,
.behavior-drag-node.behavior-hover .way {
cursor:url(../img/cursor-draw-connect-line.png) 9 9, auto;
}
.mode-draw-line .behavior-hover .vertex,
.mode-draw-area .behavior-hover .vertex,
.mode-add-line .behavior-hover .vertex,
.mode-add-area .behavior-hover .vertex {
.mode-add-area .behavior-hover .vertex,
.behavior-drag-node.behavior-hover .vertex {
cursor:url(../img/cursor-draw-connect-vertex.png) 9 9, auto;
}
@@ -718,12 +737,14 @@ text.point {
/* Modes */
.mode-draw-line .vertex.active,
.mode-draw-area .vertex.active {
.mode-draw-area .vertex.active,
.behavior-drag-node .vertex.active {
display: none;
}
.mode-draw-line .way.active,
.mode-draw-area .way.active {
.mode-draw-area .way.active,
.behavior-drag-node .active {
pointer-events: none;
}
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 B

After

Width:  |  Height:  |  Size: 158 B

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 338 B

+227 -1057
View File
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 156 KiB

After

Width:  |  Height:  |  Size: 157 KiB

+5 -4
View File
@@ -9,8 +9,8 @@
<!-- mobile devices -->
<meta name='viewport' content='initial-scale=1.0 maximum-scale=1.0'>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name='apple-mobile-web-app-capable' content='yes' />
<meta name='apple-mobile-web-app-status-bar-style' content='black-translucent' />
<script src='js/lib/lodash.js'></script>
<script src='js/lib/d3.v3.js'></script>
@@ -81,6 +81,7 @@
<script src='js/id/actions/add_entity.js'></script>
<script src='js/id/actions/add_vertex.js'></script>
<script src='js/id/actions/change_tags.js'></script>
<script src='js/id/actions/connect.js'></script>
<script src='js/id/actions/delete_multiple.js'></script>
<script src='js/id/actions/delete_node.js'></script>
<script src="js/id/actions/delete_relation.js"></script>
@@ -142,7 +143,7 @@
</head>
<body>
<div id="iD"></div><script>
<div id='iD'></div><script>
locale.current = 'en';
d3.json('keys.json', function(err, keys) {
var id = iD();
@@ -155,7 +156,7 @@
.call(id.ui())
});
</script>
<script type="text/javascript">
<script type='text/javascript'>
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-38039653-2']);
_gaq.push(['_trackPageview']);
+54
View File
@@ -0,0 +1,54 @@
// Connect the ways at the given nodes.
//
// The last node will survive. All other nodes will be replaced with
// the surviving node in parent ways, and then removed.
//
// Tags and relation memberships of of non-surviving nodes are merged
// to the survivor.
//
// This is the inverse of `iD.actions.Disconnect`.
//
// Reference:
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeNodesAction.as
// https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/MergeNodesAction.java
//
iD.actions.Connect = function(nodeIds) {
var action = function(graph) {
var survivor = graph.entity(_.last(nodeIds));
for (var i = 0; i < nodeIds.length - 1; i++) {
var node = graph.entity(nodeIds[i]), index;
graph.parentWays(node).forEach(function (parent) {
while (true) {
index = parent.nodes.indexOf(node.id);
if (index < 0)
break;
parent = parent.updateNode(survivor.id, index);
}
graph = graph.replace(parent);
});
graph.parentRelations(node).forEach(function (parent) {
var memberA = parent.memberById(survivor.id),
memberB = parent.memberById(node.id);
if (!memberA) {
graph = graph.replace(parent.addMember({id: survivor.id, role: memberB.role, type: 'node'}));
}
});
survivor = survivor.mergeTags(node.tags);
graph = iD.actions.DeleteNode(node.id)(graph);
}
graph = graph.replace(survivor);
return graph;
};
action.enabled = function(graph) {
return nodeIds.length > 1;
};
return action;
};
+2
View File
@@ -4,6 +4,8 @@
// Normally, this will be undefined and the way will automatically
// be assigned a new ID.
//
// This is the inverse of `iD.actions.Connect`.
//
// Reference:
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/UnjoinNodeAction.as
// https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/UnGlueAction.java
+1 -1
View File
@@ -42,7 +42,7 @@ iD.actions.Join = function(idA, idB) {
var memberA = parent.memberById(idA),
memberB = parent.memberById(idB);
if (!memberA) {
graph = graph.replace(parent.addMember({id: idA, role: memberB.role}));
graph = graph.replace(parent.addMember({id: idA, role: memberB.role, type: 'way'}));
}
});
+80 -23
View File
@@ -17,39 +17,96 @@ iD.behavior.DragNode = function(context) {
}, 50);
}
function stopNudge(nudge) {
function stopNudge() {
if (nudgeInterval) window.clearInterval(nudgeInterval);
nudgeInterval = null;
}
function annotation(entity) {
function moveAnnotation(entity) {
return t('operations.move.annotation.' + entity.geometry(context.graph()));
}
return iD.behavior.drag()
.delegate(".node")
.origin(function(entity) {
return context.projection(entity.loc);
})
.on('start', function() {
context.perform(
iD.actions.Noop());
})
.on('move', function(entity) {
d3.event.sourceEvent.stopPropagation();
function connectAnnotation(datum) {
return t('operations.connect.annotation.' + datum.geometry(context.graph()));
}
var nudge = edge(d3.event.point, context.map().size());
if (nudge) startNudge(nudge);
else stopNudge();
function origin(entity) {
return context.projection(entity.loc);
}
function start(entity) {
var activeIDs = _.pluck(context.graph().parentWays(entity), 'id');
activeIDs.push(entity.id);
context.surface()
.classed('behavior-drag-node', true)
.selectAll('.node, .way')
.filter(function (d) { return activeIDs.indexOf(d.id) >= 0; })
.classed('active', true);
context.perform(
iD.actions.Noop());
}
function datum() {
if (d3.event.sourceEvent.altKey) {
return {};
} else {
return d3.event.sourceEvent.target.__data__ || {};
}
}
function move(entity) {
d3.event.sourceEvent.stopPropagation();
var nudge = edge(d3.event.point, context.map().size());
if (nudge) startNudge(nudge);
else stopNudge();
var loc = context.map().mouseCoordinates();
var d = datum();
if (d.type === 'node') {
loc = d.loc;
} else if (d.type === 'way') {
loc = iD.geo.chooseIndex(d, d3.mouse(context.surface().node()), context).loc;
}
context.replace(iD.actions.MoveNode(entity.id, loc));
}
function end(entity) {
context.surface()
.classed('behavior-drag-node', false)
.selectAll('.active')
.classed('active', false);
stopNudge();
var d = datum();
if (d.type === 'way') {
var choice = iD.geo.chooseIndex(d, d3.mouse(context.surface().node()), context);
context.replace(
iD.actions.MoveNode(entity.id, context.projection.invert(d3.event.point)),
annotation(entity));
})
.on('end', function(entity) {
stopNudge();
iD.actions.MoveNode(entity.id, choice.loc),
iD.actions.AddVertex(d.id, entity.id, choice.index),
connectAnnotation(d));
} else if (d.type === 'node' && d.id !== entity.id) {
context.replace(
iD.actions.Connect([entity.id, d.id]),
connectAnnotation(d));
} else {
context.replace(
iD.actions.Noop(),
annotation(entity));
});
moveAnnotation(entity));
}
}
return iD.behavior.drag()
.delegate("g.node")
.origin(origin)
.on('start', start)
.on('move', move)
.on('end', end);
};
+1 -1
View File
@@ -10,7 +10,7 @@ iD.modes.AddArea = function(context) {
var behavior = iD.behavior.AddWay(context)
.on('start', start)
.on('startFromWay', startFromWay)
.on('startFromNode', startFromNode)
.on('startFromNode', startFromNode),
defaultTags = {area: 'yes'};
function start(loc) {
+1 -1
View File
@@ -10,7 +10,7 @@ iD.modes.AddLine = function(context) {
var behavior = iD.behavior.AddWay(context)
.on('start', start)
.on('startFromWay', startFromWay)
.on('startFromNode', startFromNode)
.on('startFromNode', startFromNode),
defaultTags = {highway: 'residential'};
function start(loc) {
+3 -2
View File
@@ -38,9 +38,10 @@ iD.modes.Select = function(context, selection, initial) {
context.install(behavior);
});
var operations = d3.values(iD.operations)
var operations = _.without(d3.values(iD.operations), iD.operations.Delete)
.map(function(o) { return o(selection, context); })
.filter(function(o) { return o.available(); });
operations.unshift(iD.operations.Delete(selection, context));
operations.forEach(function(operation) {
keybinding.on(operation.key, function() {
@@ -56,7 +57,7 @@ iD.modes.Select = function(context, selection, initial) {
}), true));
if (entity) {
inspector.graph(context.graph());
inspector.context(context);
context.container()
.select('.inspector-wrap')
+2 -2
View File
@@ -60,7 +60,7 @@ iD.OAuth = function(context) {
o.oauth_signature = ohauth.signature(oauth_secret, '',
ohauth.baseString('POST', url, o));
var l = iD.ui.loading('contacting openstreetmap...');
var l = iD.ui.loading(context.container(), '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
@@ -109,7 +109,7 @@ iD.OAuth = function(context) {
var request_token_secret = token('oauth_request_token_secret');
o.oauth_signature = ohauth.signature(oauth_secret, request_token_secret,
ohauth.baseString('POST', url, o));
var l = iD.ui.loading('contacting openstreetmap...');
var l = iD.ui.loading(context.container(), 'contacting openstreetmap...');
function accessTokenDone(err, xhr) {
if (err) callback(err);
+1 -1
View File
@@ -1,6 +1,6 @@
iD.svg.Lines = function(projection) {
var arrowtext = '►\u3000\u3000',
var arrowtext = '►\u3000\u3000\u3000',
alength;
var highway_stack = {
+1 -1
View File
@@ -42,7 +42,7 @@ iD.svg.Midpoints = function(projection) {
.attr('class', 'midpoint');
group.append('circle')
.attr('r', 7)
.attr('r', 8)
.attr('class', 'shadow');
group.append('circle')
+4 -4
View File
@@ -23,15 +23,15 @@ iD.svg.Vertices = function(projection) {
group.append('circle')
.attr('r', 10)
.attr('class', 'shadow');
.attr('class', 'node vertex shadow');
group.append('circle')
.attr('r', 6)
.attr('class', 'stroke');
.attr('r', 4)
.attr('class', 'node vertex stroke');
group.append('circle')
.attr('r', 3)
.attr('class', 'fill');
.attr('class', 'node vertex fill');
groups.attr('transform', iD.svg.PointTransform(projection))
.call(iD.svg.TagClasses())
+1 -1
View File
@@ -128,7 +128,7 @@ iD.ui = function(context) {
}
container.append('div').attr('class', 'geocode-control map-control')
.call(iD.ui.geocoder().map(map));
.call(iD.ui.geocoder().map(map).context(context));
container.append('div').attr('class', 'map-control layerswitcher-control')
.call(iD.ui.layerswitcher(context));
+2 -2
View File
@@ -1,5 +1,5 @@
iD.ui.confirm = function() {
var modal = iD.ui.modal();
iD.ui.confirm = function(selection) {
var modal = iD.ui.modal(selection);
modal.select('.modal').classed('modal-alert', true);
modal.select('.content')
.attr('class','modal-section fillD')
+2 -2
View File
@@ -1,5 +1,5 @@
iD.ui.flash = function() {
var modal = iD.ui.modal();
iD.ui.flash = function(selection) {
var modal = iD.ui.modal(selection);
modal.select('.modal').classed('modal-flash', true);
+8 -2
View File
@@ -1,6 +1,6 @@
iD.ui.geocoder = function() {
var map;
var map, context;
function geocoder(selection) {
function keydown() {
@@ -28,7 +28,7 @@ iD.ui.geocoder = function() {
.on('click.geocoder-inside', function() {
return d3.event.stopPropagation();
});
d3.select('body').on('click.geocoder-outside', hide);
context.container().on('click.geocoder-outside', hide);
}
function show() { setVisible(true); }
@@ -64,5 +64,11 @@ iD.ui.geocoder = function() {
return geocoder;
};
geocoder.context = function(_) {
if (!arguments.length) return context;
context = _;
return geocoder;
};
return geocoder;
};
+10 -10
View File
@@ -2,7 +2,7 @@ iD.ui.inspector = function() {
var event = d3.dispatch('changeTags', 'close'),
taginfo = iD.taginfo(),
initial = false,
graph,
context,
tagList;
function inspector(selection) {
@@ -50,7 +50,7 @@ iD.ui.inspector = function() {
var h2 = selection.append('h2');
h2.append('span')
.attr('class', 'icon big icon-pre-text big-' + entity.geometry(graph));
.attr('class', 'icon big icon-pre-text big-' + entity.geometry(context.graph()));
h2.append('span')
.text(entity.friendlyName());
@@ -124,7 +124,7 @@ iD.ui.inspector = function() {
.attr('class', 'tag-help minor')
.on('click', function(d) {
var params = _.extend({}, d, {
geometry: entity.geometry(graph)
geometry: entity.geometry(context.graph())
});
if (d.key && d.value) {
taginfo.docs(params, function(err, docs) {
@@ -140,12 +140,12 @@ iD.ui.inspector = function() {
if (en.on_node) types.push('point');
if (en.on_way) types.push('line');
en.types = types;
iD.ui.modal()
iD.ui.modal(context.container())
.select('.content')
.datum(en)
.call(iD.ui.tagReference);
} else {
iD.ui.flash()
iD.ui.flash(context.container())
.select('.content')
.append('h3')
.text(t('inspector.no_documentation_combination'));
@@ -154,14 +154,14 @@ iD.ui.inspector = function() {
} else if (d.key) {
taginfo.values(params, function(err, values) {
if (!err && values.data.length) {
iD.ui.modal()
iD.ui.modal(context.container())
.select('.content')
.datum({
data: values.data,
title: 'Key:' + params.key,
geometry: params.geometry
})
.call(iD.keyReference);
.call(iD.keyReference(context));
} else {
iD.ui.flash()
.select('.content')
@@ -194,7 +194,7 @@ iD.ui.inspector = function() {
function bindTypeahead() {
var entity = tagList.datum(),
geometry = entity.geometry(graph),
geometry = entity.geometry(context.graph()),
row = d3.select(this),
key = row.selectAll('.key'),
value = row.selectAll('.value');
@@ -275,8 +275,8 @@ iD.ui.inspector = function() {
return inspector;
};
inspector.graph = function(_) {
graph = _;
inspector.context = function(_) {
context = _;
return inspector;
};
+5 -4
View File
@@ -1,11 +1,12 @@
iD.ui.loading = function(message, blocking) {
var modal = iD.ui.modal(blocking);
iD.ui.loading = function(selection, message, blocking) {
var modal = iD.ui.modal(selection, blocking);
var loadertext = modal.select('.content')
.classed('loading-modal', true)
.append('div').attr('class','modal-section fillL');
loadertext.append('img').attr('class','loader').attr('src', 'img/loader.gif');
loadertext.append('h3').text(message || '');
loadertext.append('img').attr('class','loader').attr('src', 'img/loader.gif');
loadertext.append('h3').text(message || '');
return modal;
};
+5 -4
View File
@@ -1,6 +1,7 @@
iD.ui.modal = function(blocking) {
iD.ui.modal = function(selection, blocking) {
var animate = d3.select('div.modal').empty();
var previous = selection.select('div.modal');
var animate = previous.empty();
var keybinding = d3.keybinding('modal')
.on('⌫', close)
@@ -8,10 +9,10 @@ iD.ui.modal = function(blocking) {
d3.select(document).call(keybinding);
d3.select('div.modal').transition()
previous.transition()
.style('opacity', 0).remove();
var shaded = d3.select(document.body)
var shaded = selection
.append('div')
.attr('class', 'shaded')
.style('opacity', 0)
+33 -28
View File
@@ -6,17 +6,32 @@ iD.ui.save = function(context) {
tooltip = bootstrap.tooltip()
.placement('bottom');
selection.html("<span class='label'>" + t('save') + "</span><small id='as-username'></small>")
.attr('title', t('save_help'))
.attr('tabindex', -1)
.property('disabled', true)
.call(tooltip)
.on('click', function() {
function success(e, changeset_id) {
var modal = iD.ui.modal(context.container());
modal.select('.content')
.classed('success-modal', true)
.datum({
id: changeset_id,
comment: e.comment
})
.call(iD.ui.success(connection)
.on('cancel', function() {
modal.remove();
}));
}
function clickFix(d) {
map.extent(d.entity.extent(context.graph()));
if (map.zoom() > 19) map.zoom(19);
context.enter(iD.modes.Select(context, [d.entity.id]));
modal.remove();
}
function click() {
function commit(e) {
d3.select('.shaded').remove();
var l = iD.ui.loading(t('uploading_changes'), true);
var l = iD.ui.loading(context.container(), t('uploading_changes'), true);
connection.putChangeset(history.changes(),
e.comment,
@@ -31,25 +46,14 @@ iD.ui.save = function(context) {
.text(t('save_error'));
desc.append('p').text(err.responseText);
} else {
var modal = iD.ui.modal();
modal.select('.content')
.classed('success-modal', true)
.datum({
id: changeset_id,
comment: e.comment
})
.call(iD.ui.success(connection)
.on('cancel', function() {
modal.remove();
}));
success(e, changeset_id);
}
});
}
if (history.hasChanges()) {
connection.authenticate(function(err) {
var modal = iD.ui.modal();
var modal = iD.ui.modal(context.container());
var changes = history.changes();
changes.connection = connection;
modal.select('.content')
@@ -59,20 +63,21 @@ iD.ui.save = function(context) {
.on('cancel', function() {
modal.remove();
})
.on('fix', function(d) {
map.extent(d.entity.extent(context.graph()));
if (map.zoom() > 19) map.zoom(19);
context.enter(iD.modes.Select(context, [d.entity.id]));
modal.remove();
})
.on('fix', clickFix)
.on('save', commit));
});
} else {
iD.ui.confirm().select('.description')
.append('h3').text(t('no_changes'));
}
}
});
selection.html("<span class='label'>" + t('save') + "</span><small id='as-username'></small>")
.attr('title', t('save_help'))
.attr('tabindex', -1)
.property('disabled', true)
.call(tooltip)
.on('click', click);
selection.append('span')
.attr('class', 'count');
+9
View File
@@ -83,9 +83,18 @@ locale.en = {
vertex: "Deleted a node from a way.",
line: "Deleted a line.",
area: "Deleted an area.",
relation: "Deleted a relation.",
multiple: "Deleted {n} objects."
}
},
connect: {
annotation: {
point: "Connected a way to a point.",
vertex: "Connected a way to another.",
line: "Connected a way to a line.",
area: "Connected a way to an area."
}
},
disconnect: {
title: "Disconnect",
description: "Disconnect these ways from each other.",
+2
View File
@@ -72,6 +72,7 @@
<script src='../js/id/actions/add_entity.js'></script>
<script src='../js/id/actions/add_vertex.js'></script>
<script src='../js/id/actions/change_tags.js'></script>
<script src='../js/id/actions/connect.js'></script>
<script src='../js/id/actions/circularize.js'></script>
<script src='../js/id/actions/orthogonalize.js'></script>
<script src="../js/id/actions/delete_multiple.js"></script>
@@ -151,6 +152,7 @@
<script src="spec/actions/add_midpoint.js"></script>
<script src="spec/actions/add_entity.js"></script>
<script src="spec/actions/change_tags.js"></script>
<script src='spec/actions/connect.js'></script>
<script src="spec/actions/delete_multiple.js"></script>
<script src="spec/actions/delete_node.js"></script>
<script src="spec/actions/delete_relation.js"></script>
+1
View File
@@ -35,6 +35,7 @@
<script src="spec/actions/add_midpoint.js"></script>
<script src="spec/actions/add_entity.js"></script>
<script src="spec/actions/change_tags.js"></script>
<script src='spec/actions/connect.js'></script>
<script src="spec/actions/delete_multiple.js"></script>
<script src="spec/actions/delete_node.js"></script>
<script src="spec/actions/delete_relation.js"></script>
+110
View File
@@ -0,0 +1,110 @@
describe("iD.actions.Connect", function() {
describe("#enabled", function () {
it("returns true for two or more nodes", function () {
expect(iD.actions.Connect(['a', 'b']).enabled()).to.be.true;
});
it("returns false for less than two nodes", function () {
expect(iD.actions.Connect(['a']).enabled()).to.be.false;
});
});
it("removes all but the final node", function() {
var graph = iD.Graph({
'a': iD.Node({id: 'a'}),
'b': iD.Node({id: 'b'}),
'c': iD.Node({id: 'c'})
});
graph = iD.actions.Connect(['a', 'b', 'c'])(graph);
expect(graph.entity('a')).to.be.undefined;
expect(graph.entity('b')).to.be.undefined;
expect(graph.entity('c')).not.to.be.undefined;
});
it("replaces non-surviving nodes in parent ways", function() {
// a --- b --- c
//
// e
// |
// d
//
// Connect [e, b].
//
// Expected result:
//
// a --- b --- c
// |
// d
//
var graph = iD.Graph({
'a': iD.Node({id: 'a'}),
'b': iD.Node({id: 'b'}),
'c': iD.Node({id: 'c'}),
'd': iD.Node({id: 'd'}),
'e': iD.Node({id: 'e'}),
'-': iD.Way({id: '-', nodes: ['a', 'b', 'c']}),
'|': iD.Way({id: '|', nodes: ['d', 'e']})
});
graph = iD.actions.Connect(['e', 'b'])(graph);
expect(graph.entity('-').nodes).to.eql(['a', 'b', 'c']);
expect(graph.entity('|').nodes).to.eql(['d', 'b']);
});
it("handles circular ways", function() {
// c -- a d === e
// | /
// | /
// | /
// b
//
// Connect [a, d].
//
var graph = iD.Graph({
'a': iD.Node({id: 'a'}),
'b': iD.Node({id: 'b'}),
'c': iD.Node({id: 'c'}),
'd': iD.Node({id: 'd'}),
'e': iD.Node({id: 'e'}),
'-': iD.Way({id: '-', nodes: ['a', 'b', 'c', 'a']}),
'=': iD.Way({id: '=', nodes: ['d', 'e']})
});
graph = iD.actions.Connect(['a', 'd'])(graph);
expect(graph.entity('-').nodes).to.eql(['d', 'b', 'c', 'd']);
});
it("merges tags to the surviving node", function() {
var graph = iD.Graph({
'a': iD.Node({id: 'a', tags: {a: 'a'}}),
'b': iD.Node({id: 'b', tags: {b: 'b'}}),
'c': iD.Node({id: 'c', tags: {c: 'c'}})
});
graph = iD.actions.Connect(['a', 'b', 'c'])(graph);
expect(graph.entity('c').tags).to.eql({a: 'a', b: 'b', c: 'c'});
});
it("merges memberships to the surviving node", function() {
var graph = iD.Graph({
'a': iD.Node({id: 'a'}),
'b': iD.Node({id: 'b'}),
'c': iD.Node({id: 'c'}),
'd': iD.Node({id: 'c'}),
'-': iD.Way({id: '-', nodes: ['a', 'b']}),
'=': iD.Way({id: '=', nodes: ['c', 'd']}),
'r1': iD.Relation({id: 'r1', members: [{id: 'b', role: 'r1', type: 'node'}]}),
'r2': iD.Relation({id: 'r2', members: [{id: 'b', role: 'r1', type: 'node'}, {id: 'c', role: 'r2', type: 'node'}]})
});
graph = iD.actions.Connect(['b', 'c'])(graph);
expect(graph.entity('r1').members).to.eql([{id: 'c', role: 'r1', type: 'node'}]);
expect(graph.entity('r2').members).to.eql([{id: 'c', role: 'r2', type: 'node'}]);
});
});
+4 -4
View File
@@ -160,13 +160,13 @@ describe("iD.actions.Join", function () {
'c': iD.Node({id: 'c'}),
'-': iD.Way({id: '-', nodes: ['a', 'b']}),
'=': iD.Way({id: '=', nodes: ['b', 'c']}),
'r1': iD.Relation({id: 'r1', members: [{id: '=', role: 'r1'}]}),
'r2': iD.Relation({id: 'r2', members: [{id: '=', role: 'r1'}, {id: '-', role: 'r2'}]})
'r1': iD.Relation({id: 'r1', members: [{id: '=', role: 'r1', type: 'way'}]}),
'r2': iD.Relation({id: 'r2', members: [{id: '=', role: 'r1', type: 'way'}, {id: '-', role: 'r2', type: 'way'}]})
});
graph = iD.actions.Join('-', '=')(graph);
expect(graph.entity('r1').members).to.eql([{id: '-', role: 'r1'}]);
expect(graph.entity('r2').members).to.eql([{id: '-', role: 'r2'}]);
expect(graph.entity('r1').members).to.eql([{id: '-', role: 'r1', type: 'way'}]);
expect(graph.entity('r2').members).to.eql([{id: '-', role: 'r2', type: 'way'}]);
});
});
+4
View File
@@ -14,6 +14,8 @@ describe("iD.svg.Midpoints", function () {
line = iD.Way({nodes: [a.id, b.id]}),
graph = iD.Graph([a, b, line]);
// If no vertices are drawn, no midpoints are drawn. This dependence needs to be removed
surface.call(iD.svg.Vertices(projection), graph, [a], filter);
surface.call(iD.svg.Midpoints(projection), graph, [line], filter);
expect(surface.select('.midpoint').datum().loc).to.eql([25, 0]);
@@ -42,6 +44,8 @@ describe("iD.svg.Midpoints", function () {
graph = iD.Graph([a, b, c, d, l1, l2, l3, l4]),
ab = function (d) { return d.id === [a.id, b.id].sort().join("-"); };
// If no vertices are drawn, no midpoints are drawn. This dependence needs to be removed
surface.call(iD.svg.Vertices(projection), graph, [a], filter);
surface.call(iD.svg.Midpoints(projection), graph, [l1, l2, l3, l4], filter);
expect(surface.selectAll('.midpoint').filter(ab).datum().ways).to.eql([