mirror of
https://github.com/FoggedLens/iD.git
synced 2026-04-21 19:26:41 +02:00
Merge remote-tracking branch 'systemed/master' into labels
Conflicts: js/id/util.js
This commit is contained in:
+23
-16
@@ -106,7 +106,6 @@ input[type=text] {
|
||||
background-color: black;
|
||||
border:1px solid rgba(255, 255, 255, .25);
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
textarea:focus,
|
||||
@@ -181,17 +180,13 @@ ul li { list-style: none;}
|
||||
|
||||
ul.toggle-list li a {
|
||||
font-weight: bold;
|
||||
color: #c1c1c1;
|
||||
color: #333;
|
||||
padding: 10px;
|
||||
border-top: 1px solid white;
|
||||
display:block;
|
||||
border-top: 1px solid rgba(0, 0, 0, .5);
|
||||
}
|
||||
|
||||
ul.toggle-list li a.selected {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
ul.toggle-list .icon {
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
@@ -945,6 +940,17 @@ div.typeahead a:first-child {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.modal-flash .content {
|
||||
box-shadow: none;
|
||||
border-radius: 4px;
|
||||
background: #111;
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.modal-flash .close-modal {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.changeset-list li {
|
||||
border-top:1px solid #ccc;
|
||||
padding:5px 10px;
|
||||
@@ -975,18 +981,19 @@ div.typeahead a:first-child {
|
||||
------------------------------------------------------- */
|
||||
|
||||
.notice {
|
||||
position:absolute;
|
||||
top:11px;
|
||||
left:11px;
|
||||
width:278px;
|
||||
float:left;
|
||||
width:33.333%;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.notice .notice-text {
|
||||
height: 40px;
|
||||
text-align: center;
|
||||
height:38px;
|
||||
padding:10px 20px;
|
||||
background:#fff;
|
||||
color:#000;
|
||||
font-weight: normal;
|
||||
line-height: 21px;
|
||||
border-radius:5px;
|
||||
border-radius: 5px;
|
||||
line-height: 40px;
|
||||
background: #fff;
|
||||
color: #000;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
|
||||
+1
-4
@@ -73,7 +73,6 @@
|
||||
|
||||
<script src='js/id/actions.js'></script>
|
||||
<script src='js/id/actions/add_node.js'></script>
|
||||
<script src='js/id/actions/add_relation_member.js'></script>
|
||||
<script src='js/id/actions/add_way.js'></script>
|
||||
<script src='js/id/actions/add_way_node.js'></script>
|
||||
<script src='js/id/actions/change_entity_tags.js'></script>
|
||||
@@ -82,11 +81,9 @@
|
||||
<script src='js/id/actions/move_node.js'></script>
|
||||
<script src='js/id/actions/move_way.js'></script>
|
||||
<script src='js/id/actions/noop.js'></script>
|
||||
<script src='js/id/actions/remove_relation_member.js'></script>
|
||||
<script src='js/id/actions/remove_way_node.js'></script>
|
||||
<script src='js/id/actions/reverse_way.js'></script>
|
||||
<script src='js/id/actions/split_way.js'></script>
|
||||
<script src='js/id/actions/update_relation_member.js'></script>
|
||||
<script src='js/id/actions/unjoin_node.js'></script>
|
||||
|
||||
<script src='js/id/behavior.js'></script>
|
||||
<script src='js/id/behavior/drag.js'></script>
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
iD.actions.AddRelationMember = function(relationId, member, index) {
|
||||
return function(graph) {
|
||||
var relation = graph.entity(relationId),
|
||||
members = relation.members.slice();
|
||||
|
||||
members.splice((index === undefined) ? members.length : index, 0, member);
|
||||
return graph.replace(relation.update({members: members}));
|
||||
};
|
||||
};
|
||||
@@ -1,10 +1,6 @@
|
||||
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
|
||||
iD.actions.AddWayNode = function(wayId, nodeId, index) {
|
||||
return function(graph) {
|
||||
var way = graph.entity(wayId),
|
||||
node = graph.entity(nodeId),
|
||||
nodes = way.nodes.slice();
|
||||
nodes.splice((index === undefined) ? nodes.length : index, 0, nodeId);
|
||||
return graph.replace(way.update({nodes: nodes}));
|
||||
return graph.replace(graph.entity(wayId).addNode(nodeId, index));
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,18 +1,29 @@
|
||||
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteNodeAction.as
|
||||
iD.actions.DeleteNode = function(nodeId) {
|
||||
return function(graph) {
|
||||
var action = function(graph) {
|
||||
var node = graph.entity(nodeId);
|
||||
|
||||
graph.parentWays(node)
|
||||
.forEach(function(parent) {
|
||||
graph = iD.actions.RemoveWayNode(parent.id, nodeId)(graph);
|
||||
parent = parent.removeNode(nodeId);
|
||||
graph = graph.replace(parent);
|
||||
|
||||
if (parent.isDegenerate()) {
|
||||
graph = iD.actions.DeleteWay(parent.id)(graph);
|
||||
}
|
||||
});
|
||||
|
||||
graph.parentRelations(node)
|
||||
.forEach(function(parent) {
|
||||
graph = iD.actions.RemoveRelationMember(parent.id, nodeId)(graph);
|
||||
graph = graph.replace(parent.removeMember(nodeId));
|
||||
});
|
||||
|
||||
return graph.remove(node);
|
||||
};
|
||||
|
||||
action.enabled = function(graph) {
|
||||
return true;
|
||||
};
|
||||
|
||||
return action;
|
||||
};
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteWayAction.as
|
||||
iD.actions.DeleteWay = function(wayId) {
|
||||
return function(graph) {
|
||||
var action = function(graph) {
|
||||
var way = graph.entity(wayId);
|
||||
|
||||
graph.parentRelations(way)
|
||||
.forEach(function(parent) {
|
||||
graph = iD.actions.RemoveRelationMember(parent.id, wayId)(graph);
|
||||
graph = graph.replace(parent.removeMember(wayId));
|
||||
});
|
||||
|
||||
way.nodes.forEach(function (nodeId) {
|
||||
@@ -15,7 +15,7 @@ iD.actions.DeleteWay = function(wayId) {
|
||||
// can be deleted on earlier iterations of this loop.
|
||||
if (!node) return;
|
||||
|
||||
graph = iD.actions.RemoveWayNode(wayId, nodeId)(graph);
|
||||
graph = graph.replace(way.removeNode(nodeId));
|
||||
|
||||
if (!graph.parentWays(node).length &&
|
||||
!graph.parentRelations(node).length) {
|
||||
@@ -29,4 +29,10 @@ iD.actions.DeleteWay = function(wayId) {
|
||||
|
||||
return graph.remove(way);
|
||||
};
|
||||
|
||||
action.enabled = function(graph) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return action;
|
||||
};
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
|
||||
iD.actions.MoveNode = function(nodeId, loc) {
|
||||
return function(graph) {
|
||||
var node = graph.entity(nodeId);
|
||||
return graph.replace(node.update({loc: loc}));
|
||||
return graph.replace(graph.entity(nodeId).move(loc));
|
||||
};
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ iD.actions.MoveWay = function(wayId, delta, projection) {
|
||||
var node = graph.entity(id),
|
||||
start = projection(node.loc),
|
||||
end = projection.invert([start[0] + delta[0], start[1] + delta[1]]);
|
||||
graph = iD.actions.MoveNode(id, end)(graph);
|
||||
graph = graph.replace(node.move(end));
|
||||
});
|
||||
|
||||
return graph;
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
iD.actions.RemoveRelationMember = function(relationId, memberId) {
|
||||
return function(graph) {
|
||||
var relation = graph.entity(relationId),
|
||||
members = _.reject(relation.members, function(r) {
|
||||
return r.id === memberId;
|
||||
});
|
||||
return graph.replace(relation.update({members: members}));
|
||||
};
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
iD.actions.RemoveWayNode = function(wayId, nodeId) {
|
||||
return function(graph) {
|
||||
var way = graph.entity(wayId), nodes;
|
||||
// If this is the connecting node in a closed area
|
||||
if (way.nodes.length > 1 &&
|
||||
_.indexOf(way.nodes, nodeId) === 0 &&
|
||||
_.lastIndexOf(way.nodes, nodeId) === way.nodes.length - 1) {
|
||||
// Remove the node
|
||||
nodes = _.without(way.nodes, nodeId);
|
||||
// And reclose the way on the new first node.
|
||||
nodes.push(nodes[0]);
|
||||
} else {
|
||||
nodes = _.without(way.nodes, nodeId);
|
||||
}
|
||||
return graph.replace(way.update({nodes: nodes}));
|
||||
};
|
||||
};
|
||||
@@ -53,7 +53,7 @@ iD.actions.ReverseWay = function(wayId) {
|
||||
}
|
||||
}
|
||||
|
||||
return function(graph) {
|
||||
var action = function(graph) {
|
||||
var way = graph.entity(wayId),
|
||||
nodes = way.nodes.slice().reverse(),
|
||||
tags = {}, key, role;
|
||||
@@ -65,11 +65,18 @@ iD.actions.ReverseWay = function(wayId) {
|
||||
graph.parentRelations(way).forEach(function (relation) {
|
||||
relation.members.forEach(function (member, index) {
|
||||
if (member.id === way.id && (role = {forward: 'backward', backward: 'forward'}[member.role])) {
|
||||
graph = iD.actions.UpdateRelationMember(relation.id, {role: role}, index)(graph);
|
||||
relation = relation.updateMember({role: role}, index);
|
||||
graph = graph.replace(relation);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return graph.replace(way.update({nodes: nodes, tags: tags}));
|
||||
};
|
||||
|
||||
action.enabled = function(graph) {
|
||||
return true;
|
||||
};
|
||||
|
||||
return action;
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ iD.actions.SplitWay = function(nodeId, newWayId) {
|
||||
}
|
||||
|
||||
var action = function(graph) {
|
||||
if (!action.permitted(graph))
|
||||
if (!action.enabled(graph))
|
||||
return graph;
|
||||
|
||||
var way = candidateWays(graph)[0],
|
||||
@@ -37,11 +37,8 @@ iD.actions.SplitWay = function(nodeId, newWayId) {
|
||||
if (relation.isRestriction()) {
|
||||
var via = relation.memberByRole('via');
|
||||
if (via && newWay.contains(via.id)) {
|
||||
graph = iD.actions.UpdateRelationMember(
|
||||
relation.id,
|
||||
{id: newWay.id},
|
||||
relation.memberById(way.id).index
|
||||
)(graph);
|
||||
relation = relation.updateMember({id: newWay.id}, relation.memberById(way.id).index);
|
||||
graph = graph.replace(relation);
|
||||
}
|
||||
} else {
|
||||
var role = relation.memberById(way.id).role,
|
||||
@@ -50,23 +47,21 @@ iD.actions.SplitWay = function(nodeId, newWayId) {
|
||||
j;
|
||||
|
||||
for (j = 0; j < relation.members.length; j++) {
|
||||
if (relation.members[j].type === 'way' && graph.entity(relation.members[j].id).contains(last)) {
|
||||
var entity = graph.entity(relation.members[j].id);
|
||||
if (entity && entity.type === 'way' && entity.contains(last)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
graph = iD.actions.AddRelationMember(
|
||||
relation.id,
|
||||
{id: newWay.id, type: 'way', role: role},
|
||||
i <= j ? i + 1 : i
|
||||
)(graph);
|
||||
relation = relation.addMember({id: newWay.id, type: 'way', role: role}, i <= j ? i + 1 : i);
|
||||
graph = graph.replace(relation);
|
||||
}
|
||||
});
|
||||
|
||||
return graph;
|
||||
};
|
||||
|
||||
action.permitted = function(graph) {
|
||||
action.enabled = function(graph) {
|
||||
return candidateWays(graph).length === 1;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// Unjoin the ways at the given node.
|
||||
//
|
||||
// For testing convenience, accepts an ID to assign to the (first) new node.
|
||||
// Normally, this will be undefined and the way will automatically
|
||||
// be assigned a new ID.
|
||||
//
|
||||
// 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
|
||||
//
|
||||
iD.actions.UnjoinNode = function(nodeId, newNodeId) {
|
||||
var action = function(graph) {
|
||||
if (!action.enabled(graph))
|
||||
return graph;
|
||||
|
||||
var node = graph.entity(nodeId);
|
||||
|
||||
graph.parentWays(node).forEach(function(parent, i) {
|
||||
if (i === 0)
|
||||
return;
|
||||
|
||||
var index = parent.nodes.indexOf(nodeId),
|
||||
newNode = iD.Node({id: newNodeId, loc: node.loc, tags: node.tags});
|
||||
|
||||
graph = graph.replace(newNode);
|
||||
graph = graph.replace(parent.updateNode(newNode.id, index));
|
||||
});
|
||||
|
||||
return graph;
|
||||
};
|
||||
|
||||
action.enabled = function(graph) {
|
||||
return graph.parentWays(graph.entity(nodeId)).length >= 2;
|
||||
};
|
||||
|
||||
return action;
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
iD.actions.UpdateRelationMember = function(relationId, properties, index) {
|
||||
return function(graph) {
|
||||
var relation = graph.entity(relationId),
|
||||
members = relation.members.slice();
|
||||
|
||||
members.splice(index, 1, _.extend({}, members[index], properties));
|
||||
return graph.replace(relation.update({members: members}));
|
||||
};
|
||||
};
|
||||
+2
-9
@@ -77,15 +77,8 @@ iD.Connection = function() {
|
||||
delete o.lat;
|
||||
}
|
||||
o.id = iD.Entity.id.fromOSM(o.type, o.id);
|
||||
switch (o.type) {
|
||||
case 'node':
|
||||
o._poi = !refNodes[o.id];
|
||||
return iD.Node(o);
|
||||
case 'way':
|
||||
return iD.Way(o);
|
||||
case 'relation':
|
||||
return iD.Relation(o);
|
||||
}
|
||||
if (o.type === 'node') o._poi = !refNodes[o.id];
|
||||
return new iD.Entity[o.type](o);
|
||||
}
|
||||
|
||||
function parse(dom) {
|
||||
|
||||
@@ -1 +1,75 @@
|
||||
iD.geo = {};
|
||||
|
||||
iD.geo.roundCoords = function(c) {
|
||||
return [Math.floor(c[0]), Math.floor(c[1])];
|
||||
};
|
||||
|
||||
iD.geo.interp = function(p1, p2, t) {
|
||||
return [p1[0] + (p2[0] - p1[0]) * t,
|
||||
p1[1] + (p2[1] - p1[1]) * t];
|
||||
};
|
||||
|
||||
iD.geo.dist = function(a, b) {
|
||||
return Math.sqrt(Math.pow(a[0] - b[0], 2) +
|
||||
Math.pow(a[1] - b[1], 2));
|
||||
};
|
||||
|
||||
iD.geo.chooseIndex = function(way, point, map) {
|
||||
var dist = iD.geo.dist,
|
||||
projNodes = way.nodes.map(function(n) {
|
||||
return map.projection(n.loc);
|
||||
});
|
||||
|
||||
for (var i = 0, changes = []; i < projNodes.length - 1; i++) {
|
||||
changes[i] =
|
||||
(dist(projNodes[i], point) + dist(point, projNodes[i + 1])) /
|
||||
dist(projNodes[i], projNodes[i + 1]);
|
||||
}
|
||||
|
||||
var idx = _.indexOf(changes, _.min(changes)),
|
||||
ratio = dist(projNodes[idx], point) / dist(projNodes[idx], projNodes[idx + 1]),
|
||||
loc = iD.geo.interp(way.nodes[idx].loc, way.nodes[idx + 1].loc, ratio);
|
||||
|
||||
return {
|
||||
index: idx + 1,
|
||||
loc: loc
|
||||
};
|
||||
};
|
||||
|
||||
// Return whether point is contained in polygon.
|
||||
//
|
||||
// `point` should be a 2-item array of coordinates.
|
||||
// `polygon` should be an array of 2-item arrays of coordinates.
|
||||
//
|
||||
// From https://github.com/substack/point-in-polygon.
|
||||
// ray-casting algorithm based on
|
||||
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
|
||||
//
|
||||
iD.geo.pointInPolygon = function(point, polygon) {
|
||||
var x = point[0],
|
||||
y = point[1],
|
||||
inside = false;
|
||||
|
||||
for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
||||
var xi = polygon[i][0], yi = polygon[i][1];
|
||||
var xj = polygon[j][0], yj = polygon[j][1];
|
||||
|
||||
var intersect = ((yi > y) != (yj > y)) &&
|
||||
(x < (xj - xi) * (y - yi) / (yj - yi) + xi);
|
||||
if (intersect) inside = !inside;
|
||||
}
|
||||
|
||||
return inside;
|
||||
};
|
||||
|
||||
iD.geo.polygonContainsPolygon = function(outer, inner) {
|
||||
return _.every(inner, function (point) {
|
||||
return iD.geo.pointInPolygon(point, outer);
|
||||
});
|
||||
};
|
||||
|
||||
iD.geo.polygonIntersectsPolygon = function(outer, inner) {
|
||||
return _.some(inner, function (point) {
|
||||
return iD.geo.pointInPolygon(point, outer);
|
||||
});
|
||||
};
|
||||
|
||||
+1
-13
@@ -50,6 +50,7 @@ iD.Entity.prototype = {
|
||||
Object.freeze(this);
|
||||
Object.freeze(this.tags);
|
||||
|
||||
if (this.loc) Object.freeze(this.loc);
|
||||
if (this.nodes) Object.freeze(this.nodes);
|
||||
if (this.members) Object.freeze(this.members);
|
||||
}
|
||||
@@ -109,16 +110,3 @@ iD.Entity.prototype = {
|
||||
return n.length === 0 ? 'unknown' : n.join('; ');
|
||||
}
|
||||
};
|
||||
|
||||
iD.Entity.extend = function(properties) {
|
||||
var Subclass = function() {
|
||||
if (this instanceof Subclass) return;
|
||||
return (new Subclass()).initialize(arguments);
|
||||
};
|
||||
|
||||
Subclass.prototype = new iD.Entity();
|
||||
_.extend(Subclass.prototype, properties);
|
||||
iD.Entity[properties.type] = Subclass;
|
||||
|
||||
return Subclass;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
iD.History = function() {
|
||||
var stack, index,
|
||||
imagery_used = 'Bing',
|
||||
dispatch = d3.dispatch('change');
|
||||
dispatch = d3.dispatch('change', 'undone', 'redone');
|
||||
|
||||
function perform(actions) {
|
||||
actions = Array.prototype.slice.call(actions);
|
||||
@@ -62,6 +62,7 @@ iD.History = function() {
|
||||
if (stack[index].annotation) break;
|
||||
}
|
||||
|
||||
dispatch.undone();
|
||||
change(previous);
|
||||
},
|
||||
|
||||
@@ -73,6 +74,7 @@ iD.History = function() {
|
||||
if (stack[index].annotation) break;
|
||||
}
|
||||
|
||||
dispatch.redone();
|
||||
change(previous);
|
||||
},
|
||||
|
||||
|
||||
+15
-1
@@ -1,4 +1,14 @@
|
||||
iD.Node = iD.Entity.extend({
|
||||
iD.Node = iD.Entity.node = function iD_Node() {
|
||||
if (!(this instanceof iD_Node)) {
|
||||
return (new iD_Node()).initialize(arguments);
|
||||
} else if (arguments.length) {
|
||||
this.initialize(arguments);
|
||||
}
|
||||
};
|
||||
|
||||
iD.Node.prototype = Object.create(iD.Entity.prototype);
|
||||
|
||||
_.extend(iD.Node.prototype, {
|
||||
type: "node",
|
||||
|
||||
extent: function() {
|
||||
@@ -7,5 +17,9 @@ iD.Node = iD.Entity.extend({
|
||||
|
||||
geometry: function() {
|
||||
return this._poi ? 'point' : 'vertex';
|
||||
},
|
||||
|
||||
move: function(loc) {
|
||||
return this.update({loc: loc});
|
||||
}
|
||||
});
|
||||
|
||||
+31
-4
@@ -1,4 +1,14 @@
|
||||
iD.Relation = iD.Entity.extend({
|
||||
iD.Relation = iD.Entity.relation = function iD_Relation() {
|
||||
if (!(this instanceof iD_Relation)) {
|
||||
return (new iD_Relation()).initialize(arguments);
|
||||
} else if (arguments.length) {
|
||||
this.initialize(arguments);
|
||||
}
|
||||
};
|
||||
|
||||
iD.Relation.prototype = Object.create(iD.Entity.prototype);
|
||||
|
||||
_.extend(iD.Relation.prototype, {
|
||||
type: "relation",
|
||||
members: [],
|
||||
|
||||
@@ -38,6 +48,23 @@ iD.Relation = iD.Entity.extend({
|
||||
}
|
||||
},
|
||||
|
||||
addMember: function(member, index) {
|
||||
var members = this.members.slice();
|
||||
members.splice(index === undefined ? members.length : index, 0, member);
|
||||
return this.update({members: members});
|
||||
},
|
||||
|
||||
updateMember: function(member, index) {
|
||||
var members = this.members.slice();
|
||||
members.splice(index, 1, _.extend({}, members[index], member));
|
||||
return this.update({members: members});
|
||||
},
|
||||
|
||||
removeMember: function(id) {
|
||||
var members = _.reject(this.members, function(m) { return m.id === id; });
|
||||
return this.update({members: members});
|
||||
},
|
||||
|
||||
isRestriction: function() {
|
||||
return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
|
||||
},
|
||||
@@ -110,13 +137,13 @@ iD.Relation = iD.Entity.extend({
|
||||
|
||||
for (o = 0; o < outers.length; o++) {
|
||||
outer = _.pluck(outers[o], 'loc');
|
||||
if (iD.util.geo.polygonContainsPolygon(outer, inner))
|
||||
if (iD.geo.polygonContainsPolygon(outer, inner))
|
||||
return o;
|
||||
}
|
||||
|
||||
for (o = 0; o < outers.length; o++) {
|
||||
outer = _.pluck(outers[o], 'loc');
|
||||
if (iD.util.geo.polygonIntersectsPolygon(outer, inner))
|
||||
if (iD.geo.polygonIntersectsPolygon(outer, inner))
|
||||
return o;
|
||||
}
|
||||
}
|
||||
@@ -130,7 +157,7 @@ iD.Relation = iD.Entity.extend({
|
||||
if (o !== undefined)
|
||||
result[o].push(inners[i]);
|
||||
else
|
||||
result.push(inners[i]); // Invalid geometry
|
||||
result.push([inners[i]]); // Invalid geometry
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
+38
-1
@@ -1,4 +1,14 @@
|
||||
iD.Way = iD.Entity.extend({
|
||||
iD.Way = iD.Entity.way = function iD_Way() {
|
||||
if (!(this instanceof iD_Way)) {
|
||||
return (new iD_Way()).initialize(arguments);
|
||||
} else if (arguments.length) {
|
||||
this.initialize(arguments);
|
||||
}
|
||||
};
|
||||
|
||||
iD.Way.prototype = Object.create(iD.Entity.prototype);
|
||||
|
||||
_.extend(iD.Way.prototype, {
|
||||
type: "way",
|
||||
nodes: [],
|
||||
|
||||
@@ -48,7 +58,34 @@ iD.Way = iD.Entity.extend({
|
||||
!this.tags.barrier);
|
||||
},
|
||||
|
||||
isDegenerate: function() {
|
||||
return _.uniq(this.nodes).length < (this.isArea() ? 3 : 2);
|
||||
},
|
||||
|
||||
geometry: function() {
|
||||
return this.isArea() ? 'area' : 'line';
|
||||
},
|
||||
|
||||
addNode: function(id, index) {
|
||||
var nodes = this.nodes.slice();
|
||||
nodes.splice(index === undefined ? nodes.length : index, 0, id);
|
||||
return this.update({nodes: nodes});
|
||||
},
|
||||
|
||||
updateNode: function(id, index) {
|
||||
var nodes = this.nodes.slice();
|
||||
nodes.splice(index, 1, id);
|
||||
return this.update({nodes: nodes});
|
||||
},
|
||||
|
||||
removeNode: function(id) {
|
||||
var nodes = _.without(this.nodes, id);
|
||||
|
||||
// Preserve circularity
|
||||
if (this.nodes.length > 1 && this.first() === id && this.last() === id) {
|
||||
nodes.push(nodes[0]);
|
||||
}
|
||||
|
||||
return this.update({nodes: nodes});
|
||||
}
|
||||
});
|
||||
|
||||
+13
-23
@@ -50,9 +50,7 @@ window.iD = function(container) {
|
||||
}
|
||||
}
|
||||
|
||||
var notice = iD.ui.notice(limiter
|
||||
.append('div')
|
||||
.attr('class', 'notice'));
|
||||
var notice = iD.ui.notice(limiter).message(null);
|
||||
|
||||
map.on('move.disable-buttons', disableTooHigh)
|
||||
.on('move.contributors', _.debounce(function() {
|
||||
@@ -184,26 +182,18 @@ window.iD = function(container) {
|
||||
map.size(m.size());
|
||||
});
|
||||
|
||||
map.keybinding()
|
||||
.on('a', function(evt, mods) {
|
||||
if (mods) return;
|
||||
controller.enter(iD.modes.AddArea());
|
||||
})
|
||||
.on('⌫.prevent_navigation', function(evt, mods) {
|
||||
evt.preventDefault();
|
||||
})
|
||||
.on('p', function(evt, mods) {
|
||||
if (mods) return;
|
||||
controller.enter(iD.modes.AddPoint());
|
||||
})
|
||||
.on('l', function(evt, mods) {
|
||||
if (mods) return;
|
||||
controller.enter(iD.modes.AddLine());
|
||||
})
|
||||
.on('z', function(evt, mods) {
|
||||
if (mods === '⇧⌘' || mods === '⌃⇧') history.redo();
|
||||
if (mods === '⌘' || mods === '⌃') history.undo();
|
||||
});
|
||||
var keybinding = d3.keybinding('main')
|
||||
.on('P', function() { controller.enter(iD.modes.AddPoint()); })
|
||||
.on('L', function() { controller.enter(iD.modes.AddLine()); })
|
||||
.on('A', function() { controller.enter(iD.modes.AddArea()); })
|
||||
.on('⌘+Z', function() { history.undo(); })
|
||||
.on('⌃+Z', function() { history.undo(); })
|
||||
.on('⌘+⇧+Z', function() { history.redo(); })
|
||||
.on('⌃+⇧+Z', function() { history.redo(); })
|
||||
.on('⌫', function() { d3.event.preventDefault(); });
|
||||
|
||||
d3.select(document)
|
||||
.call(keybinding);
|
||||
|
||||
var hash = iD.Hash().controller(controller).map(map);
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ iD.modes.AddArea = function() {
|
||||
description: 'Add parks, buildings, lakes, or other areas to the map.'
|
||||
};
|
||||
|
||||
var keybinding = d3.keybinding('add-area');
|
||||
|
||||
mode.enter = function() {
|
||||
var map = mode.map,
|
||||
history = mode.history,
|
||||
@@ -38,9 +40,12 @@ iD.modes.AddArea = function() {
|
||||
controller.enter(iD.modes.DrawArea(way.id));
|
||||
});
|
||||
|
||||
map.keybinding().on('⎋.addarea', function() {
|
||||
keybinding.on('⎋', function() {
|
||||
controller.exit();
|
||||
});
|
||||
|
||||
d3.select(document)
|
||||
.call(keybinding);
|
||||
};
|
||||
|
||||
mode.exit = function() {
|
||||
@@ -49,7 +54,7 @@ iD.modes.AddArea = function() {
|
||||
}, 1000);
|
||||
mode.map.tail(false);
|
||||
mode.map.surface.on('click.addarea', null);
|
||||
mode.map.keybinding().on('⎋.addarea', null);
|
||||
keybinding.off();
|
||||
};
|
||||
|
||||
return mode;
|
||||
|
||||
@@ -6,6 +6,8 @@ iD.modes.AddLine = function() {
|
||||
description: 'Lines can be highways, streets, pedestrian paths, or even canals.'
|
||||
};
|
||||
|
||||
var keybinding = d3.keybinding('add-line');
|
||||
|
||||
mode.enter = function() {
|
||||
var map = mode.map,
|
||||
node,
|
||||
@@ -38,7 +40,7 @@ iD.modes.AddLine = function() {
|
||||
|
||||
} else if (datum.type === 'way') {
|
||||
// begin a new way starting from an existing way
|
||||
var choice = iD.util.geo.chooseIndex(datum, d3.mouse(map.surface.node()), map);
|
||||
var choice = iD.geo.chooseIndex(datum, d3.mouse(map.surface.node()), map);
|
||||
node = iD.Node({ loc: choice.loc });
|
||||
|
||||
history.perform(
|
||||
@@ -60,16 +62,19 @@ iD.modes.AddLine = function() {
|
||||
controller.enter(iD.modes.DrawLine(way.id, direction));
|
||||
});
|
||||
|
||||
map.keybinding().on('⎋.addline', function() {
|
||||
keybinding.on('⎋', function() {
|
||||
controller.exit();
|
||||
});
|
||||
|
||||
d3.select(document)
|
||||
.call(keybinding);
|
||||
};
|
||||
|
||||
mode.exit = function() {
|
||||
mode.map.dblclickEnable(true);
|
||||
mode.map.tail(false);
|
||||
mode.map.surface.on('click.addline', null);
|
||||
mode.map.keybinding().on('⎋.addline', null);
|
||||
keybinding.off();
|
||||
};
|
||||
|
||||
return mode;
|
||||
|
||||
@@ -5,6 +5,8 @@ iD.modes.AddPoint = function() {
|
||||
description: 'Restaurants, monuments, and postal boxes are points.'
|
||||
};
|
||||
|
||||
var keybinding = d3.keybinding('add-point');
|
||||
|
||||
mode.enter = function() {
|
||||
var map = mode.map,
|
||||
history = mode.history,
|
||||
@@ -22,15 +24,18 @@ iD.modes.AddPoint = function() {
|
||||
controller.enter(iD.modes.Select(node, true));
|
||||
});
|
||||
|
||||
map.keybinding().on('⎋.addpoint', function() {
|
||||
keybinding.on('⎋', function() {
|
||||
controller.exit();
|
||||
});
|
||||
|
||||
d3.select(document)
|
||||
.call(keybinding);
|
||||
};
|
||||
|
||||
mode.exit = function() {
|
||||
mode.map.tail(false);
|
||||
mode.map.surface.on('click.addpoint', null);
|
||||
mode.map.keybinding().on('⎋.addpoint', null);
|
||||
keybinding.off();
|
||||
};
|
||||
|
||||
return mode;
|
||||
|
||||
+46
-26
@@ -4,15 +4,16 @@ iD.modes.DrawArea = function(wayId) {
|
||||
id: 'draw-area'
|
||||
};
|
||||
|
||||
var keybinding = d3.keybinding('draw-area');
|
||||
|
||||
mode.enter = function() {
|
||||
var map = mode.map,
|
||||
surface = map.surface,
|
||||
history = mode.history,
|
||||
controller = mode.controller,
|
||||
way = history.graph().entity(wayId),
|
||||
headId = (way.nodes.length == 1) ?
|
||||
way.nodes[0] :
|
||||
way.nodes[way.nodes.length - 2],
|
||||
index = way.nodes.length - 1,
|
||||
headId = way.nodes[index - 1],
|
||||
tailId = way.first(),
|
||||
node = iD.Node({loc: map.mouseCoordinates()});
|
||||
|
||||
@@ -22,12 +23,20 @@ iD.modes.DrawArea = function(wayId) {
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddNode(node),
|
||||
iD.actions.AddWayNode(way.id, node.id, -1));
|
||||
iD.actions.AddWayNode(way.id, node.id, index));
|
||||
|
||||
surface.selectAll('.way, .node')
|
||||
.filter(function (d) { return d.id === wayId || d.id === node.id; })
|
||||
.classed('active', true);
|
||||
|
||||
function ReplaceTemporaryNode(replacementId) {
|
||||
return function(graph) {
|
||||
graph = graph.replace(graph.entity(wayId).updateNode(replacementId, index));
|
||||
graph = graph.remove(node);
|
||||
return graph;
|
||||
}
|
||||
}
|
||||
|
||||
function mousemove() {
|
||||
history.replace(iD.actions.MoveNode(node.id, map.mouseCoordinates()));
|
||||
}
|
||||
@@ -53,8 +62,7 @@ iD.modes.DrawArea = function(wayId) {
|
||||
} 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),
|
||||
ReplaceTemporaryNode(datum.id),
|
||||
way.nodes.length > 2 ? 'added to an area' : '');
|
||||
|
||||
controller.enter(iD.modes.DrawArea(wayId));
|
||||
@@ -75,13 +83,11 @@ iD.modes.DrawArea = function(wayId) {
|
||||
iD.actions.DeleteNode(node.id),
|
||||
iD.actions.DeleteNode(headId));
|
||||
|
||||
if (history.graph().fetch(wayId).nodes.length === 2) {
|
||||
history.replace(
|
||||
iD.actions.DeleteNode(way.nodes[0]),
|
||||
iD.actions.DeleteWay(wayId));
|
||||
controller.enter(iD.modes.Browse());
|
||||
} else {
|
||||
if (history.graph().entity(wayId)) {
|
||||
controller.enter(iD.modes.DrawArea(wayId));
|
||||
} else {
|
||||
// The way was deleted because it had too few nodes.
|
||||
controller.enter(iD.modes.Browse());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,8 +99,15 @@ iD.modes.DrawArea = function(wayId) {
|
||||
|
||||
function ret() {
|
||||
d3.event.preventDefault();
|
||||
|
||||
history.replace(iD.actions.DeleteNode(node.id));
|
||||
controller.enter(iD.modes.Select(way, true));
|
||||
|
||||
if (history.graph().entity(wayId)) {
|
||||
controller.enter(iD.modes.Select(way, true));
|
||||
} else {
|
||||
// The way was deleted because it had too few nodes.
|
||||
controller.enter(iD.modes.Browse());
|
||||
}
|
||||
}
|
||||
|
||||
surface
|
||||
@@ -102,31 +115,38 @@ iD.modes.DrawArea = function(wayId) {
|
||||
.on('mouseover.drawarea', mouseover)
|
||||
.on('click.drawarea', click);
|
||||
|
||||
map.keybinding()
|
||||
.on('⌫.drawarea', backspace)
|
||||
.on('⌦.drawarea', del)
|
||||
.on('⎋.drawarea', ret)
|
||||
.on('↩.drawarea', ret);
|
||||
keybinding
|
||||
.on('⌫', backspace)
|
||||
.on('⌦', del)
|
||||
.on('⎋', ret)
|
||||
.on('↩', ret);
|
||||
|
||||
d3.select(document)
|
||||
.call(keybinding);
|
||||
|
||||
history.on('undone.drawarea', function () {
|
||||
controller.enter(iD.modes.Browse());
|
||||
});
|
||||
};
|
||||
|
||||
mode.exit = function() {
|
||||
var surface = mode.map.surface;
|
||||
var map = mode.map,
|
||||
surface = map.surface,
|
||||
history = mode.history;
|
||||
|
||||
surface.selectAll('.way, .node')
|
||||
.classed('active', false);
|
||||
|
||||
mode.map.tail(false);
|
||||
mode.map.fastEnable(true);
|
||||
map.tail(false);
|
||||
map.fastEnable(true);
|
||||
|
||||
surface
|
||||
.on('mousemove.drawarea', null)
|
||||
.on('click.drawarea', null);
|
||||
|
||||
mode.map.keybinding()
|
||||
.on('⎋.drawarea', null)
|
||||
.on('⌫.drawarea', null)
|
||||
.on('⌦.drawarea', null)
|
||||
.on('↩.drawarea', null);
|
||||
keybinding.off();
|
||||
|
||||
history.on('undone.drawarea', null);
|
||||
|
||||
window.setTimeout(function() {
|
||||
mode.map.dblclickEnable(true);
|
||||
|
||||
+45
-36
@@ -4,6 +4,8 @@ iD.modes.DrawLine = function(wayId, direction) {
|
||||
id: 'draw-line'
|
||||
};
|
||||
|
||||
var keybinding = d3.keybinding('draw-line');
|
||||
|
||||
mode.enter = function() {
|
||||
var map = mode.map,
|
||||
surface = map.surface,
|
||||
@@ -11,7 +13,7 @@ iD.modes.DrawLine = function(wayId, direction) {
|
||||
controller = mode.controller,
|
||||
way = history.graph().entity(wayId),
|
||||
node = iD.Node({loc: map.mouseCoordinates()}),
|
||||
index = (direction === 'forward') ? undefined : 0,
|
||||
index = (direction === 'forward') ? way.nodes.length : 0,
|
||||
headId = (direction === 'forward') ? way.last() : way.first(),
|
||||
tailId = (direction === 'forward') ? way.first() : way.last();
|
||||
|
||||
@@ -33,6 +35,14 @@ iD.modes.DrawLine = function(wayId, direction) {
|
||||
.filter(function (d) { return d.id === wayId || d.id === node.id; })
|
||||
.classed('active', true);
|
||||
|
||||
function ReplaceTemporaryNode(replacementId) {
|
||||
return function(graph) {
|
||||
graph = graph.replace(graph.entity(wayId).updateNode(replacementId, index));
|
||||
graph = graph.remove(node);
|
||||
return graph;
|
||||
}
|
||||
}
|
||||
|
||||
function mousemove() {
|
||||
history.replace(iD.actions.MoveNode(node.id, map.mouseCoordinates()));
|
||||
}
|
||||
@@ -44,8 +54,7 @@ iD.modes.DrawLine = function(wayId, direction) {
|
||||
// connect the way in a loop
|
||||
if (way.nodes.length > 2) {
|
||||
history.replace(
|
||||
iD.actions.DeleteNode(node.id),
|
||||
iD.actions.AddWayNode(wayId, tailId, index),
|
||||
ReplaceTemporaryNode(tailId),
|
||||
'added to a line');
|
||||
|
||||
controller.enter(iD.modes.Select(way, true));
|
||||
@@ -64,8 +73,7 @@ iD.modes.DrawLine = function(wayId, direction) {
|
||||
} 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(wayId, datum.id, index),
|
||||
ReplaceTemporaryNode(datum.id),
|
||||
'added to a line');
|
||||
|
||||
controller.enter(iD.modes.DrawLine(wayId, direction));
|
||||
@@ -78,7 +86,7 @@ iD.modes.DrawLine = function(wayId, direction) {
|
||||
datum.id = datum.way;
|
||||
choice = datum;
|
||||
} else {
|
||||
choice = iD.util.geo.chooseIndex(datum, d3.mouse(surface.node()), map);
|
||||
choice = iD.geo.chooseIndex(datum, d3.mouse(surface.node()), map);
|
||||
}
|
||||
|
||||
history.replace(
|
||||
@@ -104,11 +112,11 @@ iD.modes.DrawLine = function(wayId, direction) {
|
||||
iD.actions.DeleteNode(node.id),
|
||||
iD.actions.DeleteNode(headId));
|
||||
|
||||
if (history.graph().fetch(wayId).nodes.length === 0) {
|
||||
history.replace(iD.actions.DeleteWay(wayId));
|
||||
controller.enter(iD.modes.Browse());
|
||||
} else {
|
||||
if (history.graph().entity(wayId)) {
|
||||
controller.enter(iD.modes.DrawLine(wayId, direction));
|
||||
} else {
|
||||
// The way was deleted because it had too few nodes.
|
||||
controller.enter(iD.modes.Browse());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,53 +128,54 @@ iD.modes.DrawLine = function(wayId, direction) {
|
||||
|
||||
function ret() {
|
||||
d3.event.preventDefault();
|
||||
history.replace(iD.actions.DeleteNode(node.id));
|
||||
controller.enter(iD.modes.Select(way, true));
|
||||
}
|
||||
|
||||
function undo() {
|
||||
history.undo();
|
||||
controller.enter(iD.modes.Browse());
|
||||
history.replace(iD.actions.DeleteNode(node.id));
|
||||
|
||||
if (history.graph().entity(wayId)) {
|
||||
controller.enter(iD.modes.Select(way, true));
|
||||
} else {
|
||||
// The way was deleted because it had too few nodes.
|
||||
controller.enter(iD.modes.Browse());
|
||||
}
|
||||
}
|
||||
|
||||
surface
|
||||
.on('mousemove.drawline', mousemove)
|
||||
.on('click.drawline', click);
|
||||
|
||||
map.keybinding()
|
||||
.on('⌫.drawline', backspace)
|
||||
.on('⌦.drawline', del)
|
||||
.on('⎋.drawline', ret)
|
||||
.on('↩.drawline', ret)
|
||||
.on('z.drawline', function(evt, mods) {
|
||||
if (mods === '⌘' || mods === '⌃') undo();
|
||||
});
|
||||
keybinding
|
||||
.on('⌫', backspace)
|
||||
.on('⌦', del)
|
||||
.on('⎋', ret)
|
||||
.on('↩', ret);
|
||||
|
||||
d3.select('#undo').on('click.drawline', undo);
|
||||
d3.select(document)
|
||||
.call(keybinding);
|
||||
|
||||
history.on('undone.drawline', function () {
|
||||
controller.enter(iD.modes.Browse());
|
||||
});
|
||||
};
|
||||
|
||||
mode.exit = function() {
|
||||
var surface = mode.map.surface;
|
||||
var map = mode.map,
|
||||
surface = map.surface,
|
||||
history = mode.history;
|
||||
|
||||
surface.selectAll('.way, .node')
|
||||
.classed('active', false);
|
||||
|
||||
mode.map.tail(false);
|
||||
mode.map.fastEnable(true);
|
||||
mode.map.minzoom(0);
|
||||
map.tail(false);
|
||||
map.fastEnable(true);
|
||||
map.minzoom(0);
|
||||
|
||||
surface
|
||||
.on('mousemove.drawline', null)
|
||||
.on('click.drawline', null);
|
||||
|
||||
mode.map.keybinding()
|
||||
.on('⌫.drawline', null)
|
||||
.on('⌦.drawline', null)
|
||||
.on('⎋.drawline', null)
|
||||
.on('↩.drawline', null)
|
||||
.on('z.drawline', null);
|
||||
keybinding.off();
|
||||
|
||||
d3.select('#undo').on('click.drawline', null);
|
||||
history.on('undone.drawline', null);
|
||||
|
||||
window.setTimeout(function() {
|
||||
mode.map.dblclickEnable(true);
|
||||
|
||||
+24
-19
@@ -6,6 +6,7 @@ iD.modes.Select = function(entity, initial) {
|
||||
};
|
||||
|
||||
var inspector = iD.ui.inspector().initial(!!initial),
|
||||
keybinding = d3.keybinding('select'),
|
||||
behaviors;
|
||||
|
||||
function remove() {
|
||||
@@ -14,13 +15,9 @@ iD.modes.Select = function(entity, initial) {
|
||||
iD.actions.DeleteWay(entity.id),
|
||||
'deleted a way');
|
||||
} else if (entity.type === 'node') {
|
||||
var parents = mode.history.graph().parentWays(entity),
|
||||
operations = [iD.actions.DeleteNode(entity.id)];
|
||||
parents.forEach(function(parent) {
|
||||
if (_.uniq(parent.nodes).length === 1) operations.push(iD.actions.DeleteWay(parent.id));
|
||||
});
|
||||
mode.history.perform.apply(mode.history,
|
||||
operations.concat(['deleted a node']));
|
||||
mode.history.perform(
|
||||
iD.actions.DeleteNode(entity.id),
|
||||
'deleted a node');
|
||||
}
|
||||
|
||||
mode.controller.exit();
|
||||
@@ -48,9 +45,9 @@ iD.modes.Select = function(entity, initial) {
|
||||
});
|
||||
|
||||
var q = iD.util.stringQs(location.hash.substring(1));
|
||||
location.hash = '#' + iD.util.qsString(_.assign(q, {
|
||||
location.replace('#' + iD.util.qsString(_.assign(q, {
|
||||
id: entity.id
|
||||
}), true);
|
||||
}), true));
|
||||
|
||||
d3.select('.inspector-wrap')
|
||||
.style('display', 'block')
|
||||
@@ -74,7 +71,7 @@ iD.modes.Select = function(entity, initial) {
|
||||
|
||||
inspector
|
||||
.on('changeTags', changeTags)
|
||||
.on('changeWayDirection', function(d) {
|
||||
.on('reverseWay', function(d) {
|
||||
mode.history.perform(
|
||||
iD.actions.ReverseWay(d.id),
|
||||
'reversed a way');
|
||||
@@ -84,6 +81,11 @@ iD.modes.Select = function(entity, initial) {
|
||||
iD.actions.SplitWay(d.id),
|
||||
'split a way');
|
||||
|
||||
}).on('unjoin', function(d) {
|
||||
mode.history.perform(
|
||||
iD.actions.UnjoinNode(d.id),
|
||||
'unjoined ways');
|
||||
|
||||
}).on('remove', function() {
|
||||
remove();
|
||||
|
||||
@@ -115,7 +117,7 @@ iD.modes.Select = function(entity, initial) {
|
||||
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,
|
||||
var choice = iD.geo.chooseIndex(datum,
|
||||
d3.mouse(mode.map.surface.node()), mode.map),
|
||||
node = iD.Node({ loc: choice.loc });
|
||||
|
||||
@@ -130,12 +132,12 @@ iD.modes.Select = function(entity, initial) {
|
||||
}
|
||||
|
||||
surface.on('click.select', click)
|
||||
.on('dblclick.browse', dblclick);
|
||||
.on('dblclick.select', dblclick);
|
||||
|
||||
mode.map.keybinding().on('⌫.select', function(e) {
|
||||
remove();
|
||||
e.preventDefault();
|
||||
});
|
||||
keybinding.on('⌫', remove);
|
||||
|
||||
d3.select(document)
|
||||
.call(keybinding);
|
||||
|
||||
surface.selectAll("*")
|
||||
.filter(function (d) {
|
||||
@@ -164,10 +166,13 @@ iD.modes.Select = function(entity, initial) {
|
||||
});
|
||||
|
||||
var q = iD.util.stringQs(location.hash.substring(1));
|
||||
location.hash = '#' + iD.util.qsString(_.omit(q, 'id'), true);
|
||||
location.replace('#' + iD.util.qsString(_.omit(q, 'id'), true));
|
||||
|
||||
keybinding.off();
|
||||
|
||||
surface.on('click.select', null)
|
||||
.on('dblclick.select', null);
|
||||
|
||||
surface.on("click.select", null);
|
||||
mode.map.keybinding().on('⌫.select', null);
|
||||
mode.history.on('change.entity-undone', null);
|
||||
|
||||
surface.selectAll(".selected")
|
||||
|
||||
@@ -38,6 +38,12 @@ iD.Background = function() {
|
||||
return Math.ceil(256 * Math.pow(2, z - d[2])) / 256;
|
||||
}
|
||||
|
||||
function lookUp(d) {
|
||||
for (var up = -1; up > -d[2]; up--) {
|
||||
if (cache[atZoom(d, up)] !== false) return atZoom(d, up);
|
||||
}
|
||||
}
|
||||
|
||||
// derive the tiles onscreen, remove those offscreen and position tiles
|
||||
// correctly for the currentstate of `projection`
|
||||
function background() {
|
||||
@@ -70,16 +76,13 @@ iD.Background = function() {
|
||||
|
||||
// if this tile has not finished, req the one above
|
||||
} else if (cache[d] === undefined &&
|
||||
lookUp(d)) {
|
||||
|
||||
// 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));
|
||||
var upTile = lookUp(d);
|
||||
if (!ups[upTile]) {
|
||||
ups[upTile] = true;
|
||||
tiles.push(upTile);
|
||||
}
|
||||
|
||||
// if this tile has not yet completed, try keeping the
|
||||
// tiles below it
|
||||
@@ -98,8 +101,6 @@ iD.Background = function() {
|
||||
.selectAll('img')
|
||||
.data(tiles, function(d) { return d; });
|
||||
|
||||
image.exit().remove();
|
||||
|
||||
function load(d) {
|
||||
cache[d.slice(0, 3)] = true;
|
||||
d3.select(this).on('load', null);
|
||||
@@ -116,6 +117,8 @@ iD.Background = function() {
|
||||
.on('error', error)
|
||||
.on('load', load);
|
||||
|
||||
image.exit().remove();
|
||||
|
||||
image.style(transformProp, function(d) {
|
||||
var _ts = 256 * Math.pow(2, z - d[2]);
|
||||
var scale = tileSize(d, z);
|
||||
|
||||
+3
-10
@@ -2,7 +2,6 @@ iD.Map = function() {
|
||||
var connection, history,
|
||||
dimensions = [],
|
||||
dispatch = d3.dispatch('move', 'drawn'),
|
||||
keybinding = d3.keybinding(),
|
||||
projection = d3.geo.mercator().scale(1024),
|
||||
roundedProjection = iD.svg.RoundProjection(projection),
|
||||
zoom = d3.behavior.zoom()
|
||||
@@ -12,6 +11,7 @@ iD.Map = function() {
|
||||
.on('zoom', zoomPan),
|
||||
dblclickEnabled = true,
|
||||
fastEnabled = true,
|
||||
transformStart,
|
||||
minzoom = 0,
|
||||
background = iD.Background()
|
||||
.projection(projection),
|
||||
@@ -49,8 +49,6 @@ iD.Map = function() {
|
||||
|
||||
supersurface
|
||||
.call(tail);
|
||||
|
||||
d3.select(document).call(keybinding);
|
||||
}
|
||||
|
||||
function pxCenter() { return [dimensions[0] / 2, dimensions[1] / 2]; }
|
||||
@@ -274,9 +272,10 @@ iD.Map = function() {
|
||||
map.centerEase = function(loc) {
|
||||
var from = map.center().slice(), t = 0;
|
||||
d3.timer(function() {
|
||||
map.center(iD.util.geo.interp(from, loc, (t += 1) / 10));
|
||||
map.center(iD.geo.interp(from, loc, (t += 1) / 10));
|
||||
return t == 10;
|
||||
}, 20);
|
||||
return map;
|
||||
};
|
||||
|
||||
map.extent = function(_) {
|
||||
@@ -346,12 +345,6 @@ iD.Map = function() {
|
||||
return map;
|
||||
};
|
||||
|
||||
map.keybinding = function (_) {
|
||||
if (!arguments.length) return keybinding;
|
||||
keybinding = _;
|
||||
return map;
|
||||
};
|
||||
|
||||
map.background = background;
|
||||
map.projection = projection;
|
||||
map.redraw = redraw;
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
iD.svg = {
|
||||
RoundProjection: function (projection) {
|
||||
return function (d) {
|
||||
return iD.util.geo.roundCoords(projection(d));
|
||||
return iD.geo.roundCoords(projection(d));
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ iD.svg.Midpoints = function(projection) {
|
||||
for (var j = 0; j < entity.nodes.length - 1; j++) {
|
||||
var a = projection(entity.nodes[j].loc);
|
||||
var b = projection(entity.nodes[j + 1].loc);
|
||||
if (iD.util.geo.dist(a, b) > 40) {
|
||||
if (iD.geo.dist(a, b) > 40) {
|
||||
midpoints.push({
|
||||
loc: iD.util.geo.interp(entity.nodes[j].loc, entity.nodes[j + 1].loc, 0.5),
|
||||
loc: iD.geo.interp(entity.nodes[j].loc, entity.nodes[j + 1].loc, 0.5),
|
||||
way: entity.id,
|
||||
index: j + 1,
|
||||
midpoint: true
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
iD.ui.flash = function() {
|
||||
var modal = iD.ui.modal();
|
||||
|
||||
modal.select('.modal').classed('modal-alert', true);
|
||||
modal.select('.modal').classed('modal-flash', true);
|
||||
|
||||
modal.select('.content')
|
||||
.classed('modal-section', true)
|
||||
@@ -13,7 +13,7 @@ iD.ui.flash = function() {
|
||||
setTimeout(function() {
|
||||
modal.remove();
|
||||
return true;
|
||||
}, 1000);
|
||||
}, 1500);
|
||||
|
||||
return modal;
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@ iD.ui.geocoder = function() {
|
||||
if (!resp.results.length) {
|
||||
return iD.ui.flash()
|
||||
.select('.content')
|
||||
.append('h3')
|
||||
.text('No location found for "' + resp.query[0] + '"');
|
||||
}
|
||||
var bounds = resp.results[0][0].bounds;
|
||||
|
||||
+14
-3
@@ -1,6 +1,6 @@
|
||||
iD.ui.inspector = function() {
|
||||
var event = d3.dispatch('changeTags', 'changeWayDirection',
|
||||
'update', 'remove', 'close', 'splitWay'),
|
||||
var event = d3.dispatch('changeTags', 'reverseWay',
|
||||
'update', 'remove', 'close', 'splitWay', 'unjoin'),
|
||||
taginfo = iD.taginfo(),
|
||||
initial = false,
|
||||
tagList;
|
||||
@@ -58,8 +58,10 @@ iD.ui.inspector = function() {
|
||||
|
||||
function drawButtons(selection) {
|
||||
var entity = selection.datum();
|
||||
|
||||
var inspectorButtonWrap = selection.append('div')
|
||||
.attr('class','button-wrap joined fl');
|
||||
|
||||
var inspectorButton1 = inspectorButtonWrap.append('button')
|
||||
.attr('class', 'apply col6 action')
|
||||
.on('click', apply);
|
||||
@@ -80,17 +82,24 @@ iD.ui.inspector = function() {
|
||||
.attr('href', 'http://www.openstreetmap.org/browse/' + entity.type + '/' + entity.osmId())
|
||||
.attr('target', '_blank')
|
||||
.text('View on OSM');
|
||||
|
||||
if (entity.type === 'way') {
|
||||
minorButtons.append('a')
|
||||
.attr('href', '#')
|
||||
.text('Reverse Direction')
|
||||
.on('click', function() { event.changeWayDirection(entity); });
|
||||
.on('click', function() { event.reverseWay(entity); });
|
||||
}
|
||||
|
||||
if (entity.geometry() === 'vertex') {
|
||||
minorButtons.append('a')
|
||||
.attr('href', '#')
|
||||
.text('Split Way')
|
||||
.on('click', function() { event.splitWay(entity); });
|
||||
|
||||
minorButtons.append('a')
|
||||
.attr('href', '#')
|
||||
.text('Unjoin')
|
||||
.on('click', function() { event.unjoin(entity); });
|
||||
}
|
||||
|
||||
}
|
||||
@@ -169,6 +178,7 @@ iD.ui.inspector = function() {
|
||||
} else {
|
||||
iD.ui.flash()
|
||||
.select('.content')
|
||||
.append('h3')
|
||||
.text('This is no documentation available for this tag combination');
|
||||
}
|
||||
});
|
||||
@@ -186,6 +196,7 @@ iD.ui.inspector = function() {
|
||||
} else {
|
||||
iD.ui.flash()
|
||||
.select('.content')
|
||||
.append('h3')
|
||||
.text('This is no documentation available for this key');
|
||||
}
|
||||
});
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
iD.ui.loading = function(message) {
|
||||
var modal = iD.ui.modal();
|
||||
iD.ui.loading = function(message, blocking) {
|
||||
var modal = iD.ui.modal(blocking);
|
||||
|
||||
var loadertext = modal.select('.content')
|
||||
.classed('loading-modal', true)
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
iD.ui.modal = function() {
|
||||
iD.ui.modal = function(blocking) {
|
||||
var animate = d3.select('div.modal').empty();
|
||||
|
||||
d3.select('div.modal').transition()
|
||||
@@ -9,7 +9,7 @@ iD.ui.modal = function() {
|
||||
.attr('class', 'shaded')
|
||||
.style('opacity', 0)
|
||||
.on('click.remove-modal', function() {
|
||||
if (d3.event.target == this) d3.select(this).remove();
|
||||
if (d3.event.target == this && !blocking) d3.select(this).remove();
|
||||
});
|
||||
|
||||
var modal = shaded.append('div')
|
||||
@@ -18,7 +18,7 @@ iD.ui.modal = function() {
|
||||
modal.append('button')
|
||||
.attr('class', 'icon remove close-modal')
|
||||
.on('click', function() {
|
||||
shaded.remove();
|
||||
if (!blocking) shaded.remove();
|
||||
});
|
||||
|
||||
modal.append('div')
|
||||
|
||||
+29
-10
@@ -2,21 +2,40 @@ iD.ui.notice = function(selection) {
|
||||
var message = '',
|
||||
notice = {};
|
||||
|
||||
var div = selection.append('div')
|
||||
.attr('class', 'notice')
|
||||
.style('display', 'none');
|
||||
|
||||
var txt = div.append('div')
|
||||
.attr('class', 'notice-text');
|
||||
|
||||
function replace(a, b) {
|
||||
a.style('opacity', 1)
|
||||
.transition()
|
||||
.each('end', function() {
|
||||
a.style('display', 'none');
|
||||
b.style('display', 'inline-block')
|
||||
.style('opacity', 0)
|
||||
.transition()
|
||||
.style('opacity', 1);
|
||||
})
|
||||
.style('opacity', 0);
|
||||
}
|
||||
|
||||
notice.message = function(_) {
|
||||
selection.attr('class', 'notice inner');
|
||||
div.attr('class', 'notice inner');
|
||||
if (!arguments.length) return _;
|
||||
if (!message && _) {
|
||||
selection
|
||||
.text(_)
|
||||
.transition()
|
||||
.style('display', '');
|
||||
txt.text(_);
|
||||
replace(selection.select('.button-wrap'), div);
|
||||
} else if (_ && message !== _) {
|
||||
selection.text(_);
|
||||
txt.text(_);
|
||||
selection.select('.button-wrap').style('display', 'none');
|
||||
} else if (!_) {
|
||||
selection
|
||||
.text('')
|
||||
.transition()
|
||||
.style('display', 'none');
|
||||
txt.text('');
|
||||
if (message) {
|
||||
replace(div, selection.select('.button-wrap'));
|
||||
}
|
||||
}
|
||||
message = _;
|
||||
return notice;
|
||||
|
||||
+51
-50
@@ -14,60 +14,61 @@ iD.ui.save = function() {
|
||||
.placement('bottom'))
|
||||
.on('click', function() {
|
||||
|
||||
function commit(e) {
|
||||
d3.select('.shaded').remove();
|
||||
var l = iD.ui.loading('Uploading changes to OpenStreetMap.');
|
||||
connection.putChangeset(history.changes(), e.comment, history.imagery_used(), function(err, changeset_id) {
|
||||
l.remove();
|
||||
history.reset();
|
||||
map.flush().redraw();
|
||||
if (err) {
|
||||
var desc = iD.ui.confirm()
|
||||
.select('.description');
|
||||
desc.append('h2')
|
||||
.text('An error occurred while trying to save');
|
||||
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()
|
||||
.on('cancel', function() {
|
||||
modal.remove();
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (history.hasChanges()) {
|
||||
connection.authenticate(function(err) {
|
||||
function commit(e) {
|
||||
d3.select('.shaded').remove();
|
||||
var l = iD.ui.loading('Uploading changes to OpenStreetMap.', true);
|
||||
connection.putChangeset(history.changes(), e.comment, history.imagery_used(), function(err, changeset_id) {
|
||||
l.remove();
|
||||
history.reset();
|
||||
map.flush().redraw();
|
||||
if (err) {
|
||||
var desc = iD.ui.confirm()
|
||||
.select('.description');
|
||||
desc.append('h2')
|
||||
.text('An error occurred while trying to save');
|
||||
desc.append('p').text(err.responseText);
|
||||
} else {
|
||||
var modal = iD.ui.modal();
|
||||
var changes = history.changes();
|
||||
changes.connection = connection;
|
||||
modal.select('.content')
|
||||
.classed('commit-modal', true)
|
||||
.datum(changes)
|
||||
.call(iD.ui.commit()
|
||||
.classed('success-modal', true)
|
||||
.datum({
|
||||
id: changeset_id,
|
||||
comment: e.comment
|
||||
})
|
||||
.call(iD.ui.success()
|
||||
.on('cancel', function() {
|
||||
modal.remove();
|
||||
})
|
||||
.on('fix', function(d) {
|
||||
map.extent(d.entity.extent(map.history().graph()));
|
||||
if (map.zoom() > 19) map.zoom(19);
|
||||
controller.enter(iD.modes.Select(d.entity));
|
||||
modal.remove();
|
||||
})
|
||||
.on('save', commit));
|
||||
});
|
||||
} else {
|
||||
iD.ui.confirm().select('.description')
|
||||
.append('h3').text('You don\'t have any changes to save.');
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (history.hasChanges()) {
|
||||
connection.authenticate(function(err) {
|
||||
var modal = iD.ui.modal();
|
||||
var changes = history.changes();
|
||||
changes.connection = connection;
|
||||
modal.select('.content')
|
||||
.classed('commit-modal', true)
|
||||
.datum(changes)
|
||||
.call(iD.ui.commit()
|
||||
.on('cancel', function() {
|
||||
modal.remove();
|
||||
})
|
||||
.on('fix', function(d) {
|
||||
map.extent(d.entity.extent(map.history().graph()));
|
||||
if (map.zoom() > 19) map.zoom(19);
|
||||
controller.enter(iD.modes.Select(d.entity));
|
||||
modal.remove();
|
||||
})
|
||||
.on('save', commit));
|
||||
});
|
||||
} else {
|
||||
iD.ui.confirm().select('.description')
|
||||
.append('h3').text('You don\'t have any changes to save.');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
selection.append('span')
|
||||
.attr('class', 'count');
|
||||
|
||||
+192
-115
@@ -1,120 +1,197 @@
|
||||
d3.keybinding = function() {
|
||||
// via https://github.com/keithamus/jwerty/
|
||||
// and https://github.com/madrobby/keymaster
|
||||
var _keys = {
|
||||
// MOD aka toggleable keys
|
||||
mods: {
|
||||
// Shift key, ⇧
|
||||
'⇧': 16,
|
||||
// CTRL key, on Mac: ⌃
|
||||
'⌃': 17,
|
||||
// ALT key, on Mac: ⌥ (Alt)
|
||||
'⌥': 18,
|
||||
// META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
|
||||
'⌘': 91
|
||||
},
|
||||
// Normal keys
|
||||
keys: {
|
||||
// Backspace key, on Mac: ⌫ (Backspace)
|
||||
'⌫': 8, backspace: 8,
|
||||
// Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
|
||||
'⇥': 9, '⇆': 9, tab: 9,
|
||||
// Return key, ↩
|
||||
'↩': 13, 'return': 13, enter: 13, '⌅': 13,
|
||||
// Pause/Break key
|
||||
'pause': 19, 'pause-break': 19,
|
||||
// Caps Lock key, ⇪
|
||||
'⇪': 20, caps: 20, 'caps-lock': 20,
|
||||
// Escape key, on Mac: ⎋, on Windows: Esc
|
||||
'⎋': 27, escape: 27, esc: 27,
|
||||
// Space key
|
||||
space: 32,
|
||||
// Page-Up key, or pgup, on Mac: ↖
|
||||
'↖': 33, pgup: 33, 'page-up': 33,
|
||||
// Page-Down key, or pgdown, on Mac: ↘
|
||||
'↘': 34, pgdown: 34, 'page-down': 34,
|
||||
// END key, on Mac: ⇟
|
||||
'⇟': 35, end: 35,
|
||||
// HOME key, on Mac: ⇞
|
||||
'⇞': 36, home: 36,
|
||||
// Insert key, or ins
|
||||
ins: 45, insert: 45,
|
||||
// Delete key, on Mac: ⌦ (Delete)
|
||||
'⌦': 46, del: 46, 'delete': 46,
|
||||
// Left Arrow Key, or ←
|
||||
'←': 37, left: 37, 'arrow-left': 37,
|
||||
// Up Arrow Key, or ↑
|
||||
'↑': 38, up: 38, 'arrow-up': 38,
|
||||
// Right Arrow Key, or →
|
||||
'→': 39, right: 39, 'arrow-right': 39,
|
||||
// Up Arrow Key, or ↓
|
||||
'↓': 40, down: 40, 'arrow-down': 40,
|
||||
// odities, printing characters that come out wrong:
|
||||
// Num-Multiply, or *
|
||||
'*': 106, star: 106, asterisk: 106, multiply: 106,
|
||||
// Num-Plus or +
|
||||
'+': 107, 'plus': 107,
|
||||
// Num-Subtract, or -
|
||||
'-': 109, subtract: 109,
|
||||
// Semicolon
|
||||
';': 186, semicolon:186,
|
||||
// = or equals
|
||||
'=': 187, 'equals': 187,
|
||||
// Comma, or ,
|
||||
',': 188, comma: 188,
|
||||
//'-': 189, //???
|
||||
// Period, or ., or full-stop
|
||||
'.': 190, period: 190, 'full-stop': 190,
|
||||
// Slash, or /, or forward-slash
|
||||
'/': 191, slash: 191, 'forward-slash': 191,
|
||||
// Tick, or `, or back-quote
|
||||
'`': 192, tick: 192, 'back-quote': 192,
|
||||
// Open bracket, or [
|
||||
'[': 219, 'open-bracket': 219,
|
||||
// Back slash, or \
|
||||
'\\': 220, 'back-slash': 220,
|
||||
// Close backet, or ]
|
||||
']': 221, 'close-bracket': 221,
|
||||
// Apostraphe, or Quote, or '
|
||||
'\'': 222, quote: 222, apostraphe: 222
|
||||
/*
|
||||
* This code is licensed under the MIT license.
|
||||
*
|
||||
* Copyright © 2013, iD authors.
|
||||
*
|
||||
* Portions copyright © 2011, Keith Cirkel
|
||||
* See https://github.com/keithamus/jwerty
|
||||
*
|
||||
*/
|
||||
d3.keybinding = function(namespace) {
|
||||
var bindings = [];
|
||||
|
||||
function matches(binding, event) {
|
||||
for (var p in binding.event) {
|
||||
if (event[p] != binding.event[p])
|
||||
return false;
|
||||
}
|
||||
};
|
||||
// To minimise code bloat, add all of the NUMPAD 0-9 keys in a loop
|
||||
var i = 95, n = 0;
|
||||
while (++i < 106) _keys.keys['num-' + n] = i; ++n;
|
||||
// To minimise code bloat, add all of the top row 0-9 keys in a loop
|
||||
i = 47, n = 0;
|
||||
while (++i < 58) _keys.keys[n] = i; ++n;
|
||||
// To minimise code bloat, add all of the F1-F25 keys in a loop
|
||||
i = 111, n = 1;
|
||||
while (++i < 136) _keys.keys['f' + n] = i; ++n;
|
||||
// To minimise code bloat, add all of the letters of the alphabet in a loop
|
||||
i = 64;
|
||||
while(++i < 91) _keys.keys[String.fromCharCode(i).toLowerCase()] = i;
|
||||
|
||||
var pairs = d3.entries(_keys.keys),
|
||||
event = d3.dispatch.apply(d3, d3.keys(_keys.keys));
|
||||
|
||||
function keys(selection) {
|
||||
selection.on('keydown', function () {
|
||||
var tagName = d3.select(d3.event.target).node().tagName;
|
||||
if (tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA') {
|
||||
return;
|
||||
}
|
||||
|
||||
var modifiers = '';
|
||||
if (d3.event.shiftKey) modifiers += '⇧';
|
||||
if (d3.event.ctrlKey) modifiers += '⌃';
|
||||
if (d3.event.altKey) modifiers += '⌥';
|
||||
if (d3.event.metaKey) modifiers += '⌘';
|
||||
|
||||
pairs.filter(function(d) {
|
||||
return d.value === d3.event.keyCode;
|
||||
}).forEach(function(d) {
|
||||
event[d.key](d3.event, modifiers);
|
||||
});
|
||||
});
|
||||
return (!binding.capture) === (event.eventPhase !== Event.CAPTURING_PHASE);
|
||||
}
|
||||
|
||||
return d3.rebind(keys, event, 'on');
|
||||
function capture() {
|
||||
for (var i = 0; i < bindings.length; i++) {
|
||||
var binding = bindings[i];
|
||||
if (matches(binding, d3.event)) {
|
||||
binding.callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function bubble() {
|
||||
var tagName = d3.select(d3.event.target).node().tagName;
|
||||
if (tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA') {
|
||||
return;
|
||||
}
|
||||
capture();
|
||||
}
|
||||
|
||||
function keybinding(selection) {
|
||||
selection = selection || d3.select(document);
|
||||
selection.on('keydown.capture' + namespace, capture, true);
|
||||
selection.on('keydown.bubble' + namespace, bubble, false);
|
||||
return keybinding;
|
||||
}
|
||||
|
||||
keybinding.off = function(selection) {
|
||||
selection = selection || d3.select(document);
|
||||
selection.on('keydown.capture' + namespace, null);
|
||||
selection.on('keydown.bubble' + namespace, null);
|
||||
return keybinding;
|
||||
};
|
||||
|
||||
keybinding.on = function(code, callback, capture) {
|
||||
var binding = {
|
||||
event: {
|
||||
keyCode: 0,
|
||||
shiftKey: false,
|
||||
ctrlKey: false,
|
||||
altKey: false,
|
||||
metaKey: false
|
||||
},
|
||||
capture: capture,
|
||||
callback: callback
|
||||
};
|
||||
|
||||
code = code.toLowerCase().match(/(?:(?:[^+])+|\+\+|^\+$)/g);
|
||||
|
||||
for (var i = 0; i < code.length; i++) {
|
||||
// Normalise matching errors
|
||||
if (code[i] === '++') code[i] = '+';
|
||||
|
||||
if (code[i] in d3.keybinding.modifierCodes) {
|
||||
binding.event[d3.keybinding.modifierProperties[d3.keybinding.modifierCodes[code[i]]]] = true;
|
||||
} else if (code[i] in d3.keybinding.keyCodes) {
|
||||
binding.event.keyCode = d3.keybinding.keyCodes[code[i]];
|
||||
}
|
||||
}
|
||||
|
||||
bindings.push(binding);
|
||||
|
||||
return keybinding;
|
||||
};
|
||||
|
||||
return keybinding;
|
||||
};
|
||||
|
||||
(function () {
|
||||
d3.keybinding.modifierCodes = {
|
||||
// Shift key, ⇧
|
||||
'⇧': 16, shift: 16,
|
||||
// CTRL key, on Mac: ⌃
|
||||
'⌃': 17, ctrl: 17,
|
||||
// ALT key, on Mac: ⌥ (Alt)
|
||||
'⌥': 18, alt: 18, option: 18,
|
||||
// META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
|
||||
'⌘': 91, meta: 91, cmd: 91, 'super': 91, win: 91
|
||||
};
|
||||
|
||||
d3.keybinding.modifierProperties = {
|
||||
16: 'shiftKey',
|
||||
17: 'ctrlKey',
|
||||
18: 'altKey',
|
||||
91: 'metaKey'
|
||||
};
|
||||
|
||||
d3.keybinding.keyCodes = {
|
||||
// Backspace key, on Mac: ⌫ (Backspace)
|
||||
'⌫': 8, backspace: 8,
|
||||
// Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
|
||||
'⇥': 9, '⇆': 9, tab: 9,
|
||||
// Return key, ↩
|
||||
'↩': 13, 'return': 13, enter: 13, '⌅': 13,
|
||||
// Pause/Break key
|
||||
'pause': 19, 'pause-break': 19,
|
||||
// Caps Lock key, ⇪
|
||||
'⇪': 20, caps: 20, 'caps-lock': 20,
|
||||
// Escape key, on Mac: ⎋, on Windows: Esc
|
||||
'⎋': 27, escape: 27, esc: 27,
|
||||
// Space key
|
||||
space: 32,
|
||||
// Page-Up key, or pgup, on Mac: ↖
|
||||
'↖': 33, pgup: 33, 'page-up': 33,
|
||||
// Page-Down key, or pgdown, on Mac: ↘
|
||||
'↘': 34, pgdown: 34, 'page-down': 34,
|
||||
// END key, on Mac: ⇟
|
||||
'⇟': 35, end: 35,
|
||||
// HOME key, on Mac: ⇞
|
||||
'⇞': 36, home: 36,
|
||||
// Insert key, or ins
|
||||
ins: 45, insert: 45,
|
||||
// Delete key, on Mac: ⌦ (Delete)
|
||||
'⌦': 46, del: 46, 'delete': 46,
|
||||
// Left Arrow Key, or ←
|
||||
'←': 37, left: 37, 'arrow-left': 37,
|
||||
// Up Arrow Key, or ↑
|
||||
'↑': 38, up: 38, 'arrow-up': 38,
|
||||
// Right Arrow Key, or →
|
||||
'→': 39, right: 39, 'arrow-right': 39,
|
||||
// Up Arrow Key, or ↓
|
||||
'↓': 40, down: 40, 'arrow-down': 40,
|
||||
// odities, printing characters that come out wrong:
|
||||
// Num-Multiply, or *
|
||||
'*': 106, star: 106, asterisk: 106, multiply: 106,
|
||||
// Num-Plus or +
|
||||
'+': 107, 'plus': 107,
|
||||
// Num-Subtract, or -
|
||||
'-': 109, subtract: 109,
|
||||
// Semicolon
|
||||
';': 186, semicolon:186,
|
||||
// = or equals
|
||||
'=': 187, 'equals': 187,
|
||||
// Comma, or ,
|
||||
',': 188, comma: 188,
|
||||
//'-': 189, //???
|
||||
// Period, or ., or full-stop
|
||||
'.': 190, period: 190, 'full-stop': 190,
|
||||
// Slash, or /, or forward-slash
|
||||
'/': 191, slash: 191, 'forward-slash': 191,
|
||||
// Tick, or `, or back-quote
|
||||
'`': 192, tick: 192, 'back-quote': 192,
|
||||
// Open bracket, or [
|
||||
'[': 219, 'open-bracket': 219,
|
||||
// Back slash, or \
|
||||
'\\': 220, 'back-slash': 220,
|
||||
// Close backet, or ]
|
||||
']': 221, 'close-bracket': 221,
|
||||
// Apostrophe, or Quote, or '
|
||||
'\'': 222, quote: 222, apostrophe: 222
|
||||
};
|
||||
|
||||
// NUMPAD 0-9
|
||||
var i = 95, n = 0;
|
||||
while (++i < 106) {
|
||||
d3.keybinding.keyCodes['num-' + n] = i;
|
||||
++n;
|
||||
}
|
||||
|
||||
// 0-9
|
||||
i = 47; n = 0;
|
||||
while (++i < 58) {
|
||||
d3.keybinding.keyCodes[n] = i;
|
||||
++n;
|
||||
}
|
||||
|
||||
// F1-F25
|
||||
i = 111; n = 1;
|
||||
while (++i < 136) {
|
||||
d3.keybinding.keyCodes['f' + n] = i;
|
||||
++n;
|
||||
}
|
||||
|
||||
// a-z
|
||||
i = 64;
|
||||
while (++i < 91) {
|
||||
d3.keybinding.keyCodes[String.fromCharCode(i).toLowerCase()] = i;
|
||||
}
|
||||
})();
|
||||
|
||||
+4
-9
@@ -66,7 +66,6 @@
|
||||
|
||||
<script src='../js/id/actions.js'></script>
|
||||
<script src='../js/id/actions/add_node.js'></script>
|
||||
<script src="../js/id/actions/add_relation_member.js"></script>
|
||||
<script src='../js/id/actions/add_way.js'></script>
|
||||
<script src='../js/id/actions/add_way_node.js'></script>
|
||||
<script src='../js/id/actions/change_entity_tags.js'></script>
|
||||
@@ -75,11 +74,9 @@
|
||||
<script src='../js/id/actions/move_node.js'></script>
|
||||
<script src='../js/id/actions/move_way.js'></script>
|
||||
<script src='../js/id/actions/noop.js'></script>
|
||||
<script src='../js/id/actions/remove_relation_member.js'></script>
|
||||
<script src='../js/id/actions/remove_way_node.js'></script>
|
||||
<script src='../js/id/actions/reverse_way.js'></script>
|
||||
<script src='../js/id/actions/split_way.js'></script>
|
||||
<script src='../js/id/actions/update_relation_member.js'></script>
|
||||
<script src='../js/id/actions/unjoin_node.js'></script>
|
||||
|
||||
<script src='../js/id/behavior.js'></script>
|
||||
<script src='../js/id/behavior/drag.js'></script>
|
||||
@@ -124,21 +121,19 @@
|
||||
<script src="spec/spec_helpers.js"></script>
|
||||
|
||||
<!-- include spec files here... -->
|
||||
<script src="spec/lib/d3.keybinding.js"></script>
|
||||
|
||||
<script src="spec/actions/add_node.js"></script>
|
||||
<script src="spec/actions/add_relation_member.js"></script>
|
||||
<script src="spec/actions/add_way.js"></script>
|
||||
<script src="spec/actions/add_way_node.js"></script>
|
||||
<script src="spec/actions/change_entity_tags.js"></script>
|
||||
<script src="spec/actions/delete_node.js"></script>
|
||||
<script src="spec/actions/delete_way.js"></script>
|
||||
<script src="spec/actions/move_node.js"></script>
|
||||
<script src="spec/actions/move_way.js"></script>
|
||||
<script src="spec/actions/noop.js"></script>
|
||||
<script src="spec/actions/remove_way_node.js"></script>
|
||||
<script src="spec/actions/remove_relation_member.js"></script>
|
||||
<script src="spec/actions/reverse_way.js"></script>
|
||||
<script src="spec/actions/split_way.js"></script>
|
||||
<script src="spec/actions/update_relation_member.js"></script>
|
||||
<script src='spec/actions/unjoin_node.js'></script>
|
||||
|
||||
<script src="spec/behavior/hover.js"></script>
|
||||
|
||||
|
||||
@@ -30,8 +30,9 @@
|
||||
<script src="spec/spec_helpers.js"></script>
|
||||
|
||||
<!-- include spec files here... -->
|
||||
<script src="spec/lib/d3.keybinding.js"></script>
|
||||
|
||||
<script src="spec/actions/add_node.js"></script>
|
||||
<script src="spec/actions/add_relation_member.js"></script>
|
||||
<script src="spec/actions/add_way.js"></script>
|
||||
<script src="spec/actions/add_way_node.js"></script>
|
||||
<script src="spec/actions/change_entity_tags.js"></script>
|
||||
@@ -40,11 +41,9 @@
|
||||
<script src="spec/actions/move_node.js"></script>
|
||||
<script src="spec/actions/move_way.js"></script>
|
||||
<script src="spec/actions/noop.js"></script>
|
||||
<script src="spec/actions/remove_way_node.js"></script>
|
||||
<script src="spec/actions/remove_relation_member.js"></script>
|
||||
<script src="spec/actions/reverse_way.js"></script>
|
||||
<script src="spec/actions/split_way.js"></script>
|
||||
<script src="spec/actions/update_relation_member.js"></script>
|
||||
<script src='spec/actions/unjoin_node.js'></script>
|
||||
|
||||
<script src="spec/behavior/hover.js"></script>
|
||||
|
||||
@@ -76,6 +75,11 @@
|
||||
<script src="spec/svg/tag_classes.js"></script>
|
||||
|
||||
<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>
|
||||
|
||||
+31
-13
@@ -19,10 +19,10 @@
|
||||
evt = new Event(o.type);
|
||||
evt.keyCode = o.keyCode || 0;
|
||||
evt.charCode = o.charCode || 0;
|
||||
evt.shift = o.shift || false;
|
||||
evt.meta = o.meta || false;
|
||||
evt.ctrl = o.ctrl || false;
|
||||
evt.alt = o.alt || false;
|
||||
evt.shiftKey = o.shiftKey || false;
|
||||
evt.metaKey = o.metaKey || false;
|
||||
evt.ctrlKey = o.ctrlKey || false;
|
||||
evt.altKey = o.altKey || false;
|
||||
} else {
|
||||
evt = document.createEvent('KeyboardEvent');
|
||||
// https://developer.mozilla.org/en/DOM/event.initKeyEvent
|
||||
@@ -33,13 +33,30 @@
|
||||
true, // in boolean canBubbleArg,
|
||||
true, // in boolean cancelableArg,
|
||||
null, // in nsIDOMAbstractView viewArg, Specifies UIEvent.view. This value may be null.
|
||||
o.ctrl || false, // in boolean ctrlKeyArg,
|
||||
o.alt || false, // in boolean altKeyArg,
|
||||
o.shift || false, // in boolean shiftKeyArg,
|
||||
o.meta || false, // in boolean metaKeyArg,
|
||||
o.ctrlKey || false, // in boolean ctrlKeyArg,
|
||||
o.altKey || false, // in boolean altKeyArg,
|
||||
o.shiftKey || false, // in boolean shiftKeyArg,
|
||||
o.metaKey || false, // in boolean metaKeyArg,
|
||||
o.keyCode || 0, // in unsigned long keyCodeArg,
|
||||
o.charCode || 0 // in unsigned long charCodeArg);
|
||||
);
|
||||
|
||||
// Workaround for https://bugs.webkit.org/show_bug.cgi?id=16735
|
||||
if (evt.ctrlKey != (o.ctrlKey || 0) ||
|
||||
evt.altKey != (o.altKey || 0) ||
|
||||
evt.shiftKey != (o.shiftKey || 0) ||
|
||||
evt.metaKey != (o.metaKey || 0) ||
|
||||
evt.keyCode != (o.keyCode || 0) ||
|
||||
evt.charCode != (o.charCode || 0)) {
|
||||
evt = document.createEvent('Event');
|
||||
evt.initEvent(o.type, true, true);
|
||||
evt.ctrlKey = o.ctrlKey || false;
|
||||
evt.altKey = o.altKey || false;
|
||||
evt.shiftKey = o.shiftKey || false;
|
||||
evt.metaKey = o.metaKey || false;
|
||||
evt.keyCode = o.keyCode || 0;
|
||||
evt.charCode = o.charCode || 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
evt = document.createEvent('MouseEvents');
|
||||
@@ -53,10 +70,10 @@
|
||||
o.screenY || 0, // screenY
|
||||
o.clientX || 0, // clientX
|
||||
o.clientY || 0, // clientY
|
||||
o.ctrl || 0, // ctrl
|
||||
o.alt || false, // alt
|
||||
o.shift || false, // shift
|
||||
o.meta || false, // meta
|
||||
o.ctrlKey || 0, // ctrl
|
||||
o.altKey || false, // alt
|
||||
o.shiftKey || false, // shift
|
||||
o.metaKey || false, // meta
|
||||
o.button || false, // mouse button
|
||||
null // relatedTarget
|
||||
);
|
||||
@@ -65,7 +82,8 @@
|
||||
x.dispatchEvent(evt);
|
||||
};
|
||||
|
||||
var shortcuts = ['click', 'mousedown', 'mouseup', 'mousemove', 'keydown', 'keyup', 'keypress'],
|
||||
var shortcuts = ['click', 'mousedown', 'mouseup', 'mousemove',
|
||||
'mouseover', 'mouseout', 'keydown', 'keyup', 'keypress'],
|
||||
s, i = 0;
|
||||
|
||||
while (s = shortcuts[i++]) {
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
describe("iD.actions.AddRelationMember", function () {
|
||||
it("adds a member at the end of the relation", function () {
|
||||
var relation = iD.Relation(),
|
||||
graph = iD.Graph([relation]);
|
||||
|
||||
graph = iD.actions.AddRelationMember(relation.id, {id: '1'})(graph);
|
||||
|
||||
expect(graph.entity(relation.id).members).to.eql([{id: '1'}]);
|
||||
});
|
||||
|
||||
it("adds a member at index 0", function () {
|
||||
var relation = iD.Relation({members: [{id: '1'}]}),
|
||||
graph = iD.Graph([relation]);
|
||||
|
||||
graph = iD.actions.AddRelationMember(relation.id, {id: '2'}, 0)(graph);
|
||||
|
||||
expect(graph.entity(relation.id).members).to.eql([{id: '2'}, {id: '1'}]);
|
||||
});
|
||||
|
||||
it("adds a member at a positive index", function () {
|
||||
var relation = iD.Relation({members: [{id: '1'}, {id: '3'}]}),
|
||||
graph = iD.Graph([relation]);
|
||||
|
||||
graph = iD.actions.AddRelationMember(relation.id, {id: '2'}, 1)(graph);
|
||||
|
||||
expect(graph.entity(relation.id).members).to.eql([{id: '1'}, {id: '2'}, {id: '3'}]);
|
||||
});
|
||||
|
||||
it("adds a member at a negative index", function () {
|
||||
var relation = iD.Relation({members: [{id: '1'}, {id: '3'}]}),
|
||||
graph = iD.Graph([relation]);
|
||||
|
||||
graph = iD.actions.AddRelationMember(relation.id, {id: '2'}, -1)(graph);
|
||||
|
||||
expect(graph.entity(relation.id).members).to.eql([{id: '1'}, {id: '2'}, {id: '3'}]);
|
||||
});
|
||||
});
|
||||
@@ -1,29 +0,0 @@
|
||||
describe("iD.actions.AddWayNode", function () {
|
||||
it("adds a node to the end of a way", function () {
|
||||
var way = iD.Way(),
|
||||
node = iD.Node({id: "n1"}),
|
||||
graph = iD.actions.AddWayNode(way.id, node.id)(iD.Graph([way, node]));
|
||||
expect(graph.entity(way.id).nodes).to.eql(["n1"]);
|
||||
});
|
||||
|
||||
it("adds a node to a way at index 0", function () {
|
||||
var way = iD.Way({nodes: ["n1", "n3"]}),
|
||||
node = iD.Node({id: "n2"}),
|
||||
graph = iD.actions.AddWayNode(way.id, node.id, 0)(iD.Graph([way, node]));
|
||||
expect(graph.entity(way.id).nodes).to.eql(["n2", "n1", "n3"]);
|
||||
});
|
||||
|
||||
it("adds a node to a way at a positive index", function () {
|
||||
var way = iD.Way({nodes: ["n1", "n3"]}),
|
||||
node = iD.Node({id: "n2"}),
|
||||
graph = iD.actions.AddWayNode(way.id, node.id, 1)(iD.Graph([way, node]));
|
||||
expect(graph.entity(way.id).nodes).to.eql(["n1", "n2", "n3"]);
|
||||
});
|
||||
|
||||
it("adds a node to a way at a negative index", function () {
|
||||
var way = iD.Way({nodes: ["n1", "n3"]}),
|
||||
node = iD.Node({id: "n2"}),
|
||||
graph = iD.actions.AddWayNode(way.id, node.id, -1)(iD.Graph([way, node]));
|
||||
expect(graph.entity(way.id).nodes).to.eql(["n1", "n2", "n3"]);
|
||||
});
|
||||
});
|
||||
@@ -24,4 +24,22 @@ describe("iD.actions.DeleteNode", function () {
|
||||
graph = action(iD.Graph([node1, node2, relation]));
|
||||
expect(graph.entity(relation.id).members).to.eql([{ id: node2.id }]);
|
||||
});
|
||||
|
||||
it("deletes parent ways that would otherwise have less than two nodes", function () {
|
||||
var node1 = iD.Node(),
|
||||
node2 = iD.Node(),
|
||||
way = iD.Way({nodes: [node1.id, node2.id]}),
|
||||
action = iD.actions.DeleteNode(node1.id),
|
||||
graph = action(iD.Graph([node1, node2, way]));
|
||||
expect(graph.entity(way.id)).to.be.undefined;
|
||||
});
|
||||
|
||||
it("deletes degenerate circular ways", function () {
|
||||
var node1 = iD.Node(),
|
||||
node2 = iD.Node(),
|
||||
way = iD.Way({nodes: [node1.id, node2.id, node1.id]}),
|
||||
action = iD.actions.DeleteNode(node2.id),
|
||||
graph = action(iD.Graph([node1, node2, way]));
|
||||
expect(graph.entity(way.id)).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
describe("iD.actions.RemoveRelationMember", function () {
|
||||
it("removes a member from a relation", function () {
|
||||
var node = iD.Node(),
|
||||
relation = iD.Way({members: [{ id: node.id }]}),
|
||||
graph = iD.actions.RemoveRelationMember(relation.id, node.id)(iD.Graph([node, relation]));
|
||||
expect(graph.entity(relation.id).members).to.eql([]);
|
||||
});
|
||||
});
|
||||
@@ -1,8 +0,0 @@
|
||||
describe("iD.actions.RemoveWayNode", function () {
|
||||
it("removes a node from a way", function () {
|
||||
var node = iD.Node({id: "n1"}),
|
||||
way = iD.Way({id: "w1", nodes: ["n1"]}),
|
||||
graph = iD.actions.RemoveWayNode(way.id, node.id)(iD.Graph({n1: node, w1: way}));
|
||||
expect(graph.entity(way.id).nodes).to.eql([]);
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
describe("iD.actions.SplitWay", function () {
|
||||
describe("#permitted", function () {
|
||||
describe("#enabled", function () {
|
||||
it("returns true for a non-end node of a single way", function () {
|
||||
var graph = iD.Graph({
|
||||
'a': iD.Node({id: 'a'}),
|
||||
@@ -8,7 +8,7 @@ describe("iD.actions.SplitWay", function () {
|
||||
'-': iD.Way({id: '-', nodes: ['a', 'b', 'c']})
|
||||
});
|
||||
|
||||
expect(iD.actions.SplitWay('b').permitted(graph)).to.be.true;
|
||||
expect(iD.actions.SplitWay('b').enabled(graph)).to.be.true;
|
||||
});
|
||||
|
||||
it("returns false for the first node of a single way", function () {
|
||||
@@ -18,7 +18,7 @@ describe("iD.actions.SplitWay", function () {
|
||||
'-': iD.Way({id: '-', nodes: ['a', 'b']})
|
||||
});
|
||||
|
||||
expect(iD.actions.SplitWay('a').permitted(graph)).to.be.false;
|
||||
expect(iD.actions.SplitWay('a').enabled(graph)).to.be.false;
|
||||
});
|
||||
|
||||
it("returns false for the last node of a single way", function () {
|
||||
@@ -28,7 +28,7 @@ describe("iD.actions.SplitWay", function () {
|
||||
'-': iD.Way({id: '-', nodes: ['a', 'b']})
|
||||
});
|
||||
|
||||
expect(iD.actions.SplitWay('b').permitted(graph)).to.be.false;
|
||||
expect(iD.actions.SplitWay('b').enabled(graph)).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -175,6 +175,20 @@ describe("iD.actions.SplitWay", function () {
|
||||
expect(_.pluck(graph.entity('r').members, 'id')).to.eql(['~', '=', '-']);
|
||||
});
|
||||
|
||||
it("handles incomplete relations", function () {
|
||||
var graph = iD.Graph({
|
||||
'a': iD.Node({id: 'a'}),
|
||||
'b': iD.Node({id: 'b'}),
|
||||
'c': iD.Node({id: 'c'}),
|
||||
'-': iD.Way({id: '-', nodes: ['a', 'b', 'c']}),
|
||||
'r': iD.Relation({id: 'r', members: [{id: '~', type: 'way'}, {id: '-', type: 'way'}]})
|
||||
});
|
||||
|
||||
graph = iD.actions.SplitWay('b', '=')(graph);
|
||||
|
||||
expect(_.pluck(graph.entity('r').members, 'id')).to.eql(['~', '-', '=']);
|
||||
});
|
||||
|
||||
['restriction', 'restriction:bus'].forEach(function (type) {
|
||||
it("updates a restriction's 'from' role", function () {
|
||||
// Situation:
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
describe("iD.actions.UnjoinNode", function () {
|
||||
describe("#enabled", function () {
|
||||
it("returns false for a node shared by less than two ways", function () {
|
||||
var graph = iD.Graph({'a': iD.Node()});
|
||||
|
||||
expect(iD.actions.UnjoinNode('a').enabled(graph)).to.equal(false);
|
||||
});
|
||||
|
||||
it("returns true for a node shared by two or more ways", function () {
|
||||
// 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'}),
|
||||
'-': iD.Way({id: '-', nodes: ['a', 'b', 'c']}),
|
||||
'|': iD.Way({id: '|', nodes: ['d', 'b']})
|
||||
});
|
||||
|
||||
expect(iD.actions.UnjoinNode('b').enabled(graph)).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
it("replaces the node with a new node in all but the first way", function () {
|
||||
// Situation:
|
||||
// a ---- b ---- c
|
||||
// |
|
||||
// d
|
||||
// Split at b.
|
||||
//
|
||||
// Expected result:
|
||||
// a ---- b ---- c
|
||||
//
|
||||
// e
|
||||
// |
|
||||
// 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'}),
|
||||
'-': iD.Way({id: '-', nodes: ['a', 'b', 'c']}),
|
||||
'|': iD.Way({id: '|', nodes: ['d', 'b']})
|
||||
});
|
||||
|
||||
graph = iD.actions.UnjoinNode('b', 'e')(graph);
|
||||
|
||||
expect(graph.entity('-').nodes).to.eql(['a', 'b', 'c']);
|
||||
expect(graph.entity('|').nodes).to.eql(['d', 'e']);
|
||||
});
|
||||
|
||||
it("copies location and tags to the new nodes", function () {
|
||||
var tags = {highway: 'traffic_signals'},
|
||||
loc = [1, 2],
|
||||
graph = iD.Graph({
|
||||
'a': iD.Node({id: 'a'}),
|
||||
'b': iD.Node({id: 'b', loc: loc, tags: tags}),
|
||||
'c': iD.Node({id: 'c'}),
|
||||
'd': iD.Node({id: 'd'}),
|
||||
'-': iD.Way({id: '-', nodes: ['a', 'b', 'c']}),
|
||||
'|': iD.Way({id: '|', nodes: ['d', 'b']})
|
||||
});
|
||||
|
||||
graph = iD.actions.UnjoinNode('b', 'e')(graph);
|
||||
|
||||
// Immutable loc => should be shared by identity.
|
||||
expect(graph.entity('b').loc).to.equal(loc);
|
||||
expect(graph.entity('e').loc).to.equal(loc);
|
||||
|
||||
// Immutable tags => should be shared by identity.
|
||||
expect(graph.entity('b').tags).to.equal(tags);
|
||||
expect(graph.entity('e').tags).to.equal(tags);
|
||||
});
|
||||
});
|
||||
@@ -1,8 +0,0 @@
|
||||
describe("iD.actions.UpdateRelationMember", function () {
|
||||
it("updates the properties of the relation member at the specified index", function () {
|
||||
var node = iD.Node(),
|
||||
relation = iD.Relation({members: [{id: node.id, role: 'forward'}]}),
|
||||
graph = iD.actions.UpdateRelationMember(relation.id, {role: 'backward'}, 0)(iD.Graph([node, relation]));
|
||||
expect(graph.entity(relation.id).members).to.eql([{id: node.id, role: 'backward'}]);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
describe('Connection', function() {
|
||||
describe('iD.Connection', function() {
|
||||
var c;
|
||||
|
||||
beforeEach(function() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
describe('GeoJSON', function() {
|
||||
describe('iD.format.GeoJSON', function() {
|
||||
|
||||
describe('#mapping', function() {
|
||||
it('should be able to map a node to geojson', function() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
describe('XML', function() {
|
||||
describe('iD.format.XML', function() {
|
||||
var node = iD.Node({ id: 'n-1', type: 'node', loc: [-77, 38] }),
|
||||
way = iD.Way({ id: 'w-1', type: 'way', nodes: [] });
|
||||
|
||||
|
||||
@@ -83,6 +83,13 @@ describe("iD.History", function () {
|
||||
expect(history.redoAnnotation()).to.equal("annotation");
|
||||
});
|
||||
|
||||
it("emits an undone event", function () {
|
||||
history.perform(action);
|
||||
history.on('undone', spy);
|
||||
history.undo();
|
||||
expect(spy).to.have.been.called;
|
||||
});
|
||||
|
||||
it("emits a change event", function () {
|
||||
history.perform(action);
|
||||
history.on('change', spy);
|
||||
@@ -92,6 +99,14 @@ describe("iD.History", function () {
|
||||
});
|
||||
|
||||
describe("#redo", function () {
|
||||
it("emits an redone event", function () {
|
||||
history.perform(action);
|
||||
history.undo();
|
||||
history.on('change', spy);
|
||||
history.redo();
|
||||
expect(spy).to.have.been.called;
|
||||
});
|
||||
|
||||
it("emits a change event", function () {
|
||||
history.perform(action);
|
||||
history.undo();
|
||||
|
||||
@@ -104,6 +104,42 @@ describe('iD.Relation', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addMember", function () {
|
||||
it("adds a member at the end of the relation", function () {
|
||||
var r = iD.Relation();
|
||||
expect(r.addMember({id: '1'}).members).to.eql([{id: '1'}]);
|
||||
});
|
||||
|
||||
it("adds a member at index 0", function () {
|
||||
var r = iD.Relation({members: [{id: '1'}]});
|
||||
expect(r.addMember({id: '2'}, 0).members).to.eql([{id: '2'}, {id: '1'}]);
|
||||
});
|
||||
|
||||
it("adds a member at a positive index", function () {
|
||||
var r = iD.Relation({members: [{id: '1'}, {id: '3'}]});
|
||||
expect(r.addMember({id: '2'}, 1).members).to.eql([{id: '1'}, {id: '2'}, {id: '3'}]);
|
||||
});
|
||||
|
||||
it("adds a member at a negative index", function () {
|
||||
var r = iD.Relation({members: [{id: '1'}, {id: '3'}]});
|
||||
expect(r.addMember({id: '2'}, -1).members).to.eql([{id: '1'}, {id: '2'}, {id: '3'}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#updateMember", function () {
|
||||
it("updates the properties of the relation member at the specified index", function () {
|
||||
var r = iD.Relation({members: [{role: 'forward'}]});
|
||||
expect(r.updateMember({role: 'backward'}, 0).members).to.eql([{role: 'backward'}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#removeMember", function () {
|
||||
it("removes a member", function () {
|
||||
var r = iD.Relation({members: [{id: 'a'}]});
|
||||
expect(r.removeMember('a').members).to.eql([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#multipolygon", function () {
|
||||
specify("single polygon consisting of a single way", function () {
|
||||
var a = iD.Node(),
|
||||
@@ -298,6 +334,17 @@ describe('iD.Relation', function () {
|
||||
expect(r.multipolygon(graph)).to.eql([[[a, b, c, a], [d, e, f, d]], [[g, h, i, g]]]);
|
||||
});
|
||||
|
||||
specify("invalid geometry: unmatched inner", function () {
|
||||
var a = iD.Node(),
|
||||
b = iD.Node(),
|
||||
c = iD.Node(),
|
||||
w = iD.Way({nodes: [a.id, b.id, c.id, a.id]}),
|
||||
r = iD.Relation({members: [{id: w.id, role: 'inner', type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, w, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a, b, c, a]]]);
|
||||
});
|
||||
|
||||
specify("incomplete relation", function () {
|
||||
var a = iD.Node(),
|
||||
b = iD.Node(),
|
||||
|
||||
@@ -117,6 +117,31 @@ describe('iD.Way', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("#isDegenerate", function() {
|
||||
it("returns true for a linear way with zero or one nodes", function () {
|
||||
expect(iD.Way({nodes: []}).isDegenerate()).to.equal(true);
|
||||
expect(iD.Way({nodes: ['a']}).isDegenerate()).to.equal(true);
|
||||
});
|
||||
|
||||
it("returns true for a circular way with only one unique node", function () {
|
||||
expect(iD.Way({nodes: ['a', 'a']}).isDegenerate()).to.equal(true);
|
||||
});
|
||||
|
||||
it("returns false for a linear way with two or more nodes", function () {
|
||||
expect(iD.Way({nodes: ['a', 'b']}).isDegenerate()).to.equal(false);
|
||||
});
|
||||
|
||||
it("returns true for an area with zero, one, or two unique nodes", function () {
|
||||
expect(iD.Way({tags: {area: 'yes'}, nodes: []}).isDegenerate()).to.equal(true);
|
||||
expect(iD.Way({tags: {area: 'yes'}, nodes: ['a', 'a']}).isDegenerate()).to.equal(true);
|
||||
expect(iD.Way({tags: {area: 'yes'}, nodes: ['a', 'b', 'a']}).isDegenerate()).to.equal(true);
|
||||
});
|
||||
|
||||
it("returns false for an area with three or more unique nodes", function () {
|
||||
expect(iD.Way({tags: {area: 'yes'}, nodes: ['a', 'b', 'c', 'a']}).isDegenerate()).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#geometry", function() {
|
||||
it("returns 'line' when the way is not an area", function () {
|
||||
expect(iD.Way().geometry()).to.equal('line');
|
||||
@@ -126,4 +151,52 @@ describe('iD.Way', function() {
|
||||
expect(iD.Way({tags: { area: 'yes' }}).geometry()).to.equal('area');
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addNode", function () {
|
||||
it("adds a node to the end of a way", function () {
|
||||
var w = iD.Way();
|
||||
expect(w.addNode('a').nodes).to.eql(['a']);
|
||||
});
|
||||
|
||||
it("adds a node to a way at index 0", function () {
|
||||
var w = iD.Way({nodes: ['a', 'b']});
|
||||
expect(w.addNode('c', 0).nodes).to.eql(['c', 'a', 'b']);
|
||||
});
|
||||
|
||||
it("adds a node to a way at a positive index", function () {
|
||||
var w = iD.Way({nodes: ['a', 'b']});
|
||||
expect(w.addNode('c', 1).nodes).to.eql(['a', 'c', 'b']);
|
||||
});
|
||||
|
||||
it("adds a node to a way at a negative index", function () {
|
||||
var w = iD.Way({nodes: ['a', 'b']});
|
||||
expect(w.addNode('c', -1).nodes).to.eql(['a', 'c', 'b']);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#updateNode", function () {
|
||||
it("updates the node id at the specified index", function () {
|
||||
var w = iD.Way({nodes: ['a', 'b', 'c']});
|
||||
expect(w.updateNode('d', 1).nodes).to.eql(['a', 'd', 'c']);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#removeNode", function () {
|
||||
it("removes the node", function () {
|
||||
var a = iD.Node({id: 'a'}),
|
||||
w = iD.Way({nodes: ['a']});
|
||||
|
||||
expect(w.removeNode('a').nodes).to.eql([]);
|
||||
});
|
||||
|
||||
it("preserves circularity", function () {
|
||||
var a = iD.Node({id: 'a'}),
|
||||
b = iD.Node({id: 'b'}),
|
||||
c = iD.Node({id: 'c'}),
|
||||
d = iD.Node({id: 'd'}),
|
||||
w = iD.Way({nodes: ['a', 'b', 'c', 'd', 'a']});
|
||||
|
||||
expect(w.removeNode('a').nodes).to.eql(['b', 'c', 'd', 'b']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
describe("iD", function () {
|
||||
var container, editor;
|
||||
|
||||
beforeEach(function() {
|
||||
container = d3.select('body').append('div');
|
||||
editor = iD()
|
||||
editor.map().background.source(null);
|
||||
editor.call(container);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
container.remove();
|
||||
});
|
||||
|
||||
it("allows an editor to add a place", function () {
|
||||
// click 'Add Place'
|
||||
// click on map
|
||||
// select tags
|
||||
// save
|
||||
// check uploaded XML
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
describe("d3.keybinding", function() {
|
||||
var keybinding, spy, input;
|
||||
|
||||
beforeEach(function () {
|
||||
keybinding = d3.keybinding('keybinding-test');
|
||||
spy = sinon.spy();
|
||||
input = d3.select('body')
|
||||
.append('input');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
keybinding.off(d3.select(document));
|
||||
input.remove();
|
||||
});
|
||||
|
||||
describe("#on", function () {
|
||||
it("returns self", function () {
|
||||
expect(keybinding.on('a', spy)).to.equal(keybinding);
|
||||
});
|
||||
|
||||
it("adds a binding for the specified bare key", function () {
|
||||
d3.select(document).call(keybinding.on('A', spy));
|
||||
|
||||
happen.keydown(document, {keyCode: 65, metaKey: true});
|
||||
expect(spy).not.to.have.been.called;
|
||||
|
||||
happen.keydown(document, {keyCode: 65});
|
||||
expect(spy).to.have.been.called;
|
||||
});
|
||||
|
||||
it("adds a binding for the specified key combination", function () {
|
||||
d3.select(document).call(keybinding.on('⌘+A', spy));
|
||||
|
||||
happen.keydown(document, {keyCode: 65});
|
||||
expect(spy).not.to.have.been.called;
|
||||
|
||||
happen.keydown(document, {keyCode: 65, metaKey: true});
|
||||
expect(spy).to.have.been.called;
|
||||
});
|
||||
|
||||
it("does not dispatch when focus is in input elements by default", function () {
|
||||
d3.select(document).call(keybinding.on('A', spy));
|
||||
|
||||
happen.keydown(input.node(), {keyCode: 65});
|
||||
expect(spy).not.to.have.been.called;
|
||||
});
|
||||
|
||||
it("dispatches when focus is in input elements when the capture flag was passed", function () {
|
||||
d3.select(document).call(keybinding.on('A', spy, true));
|
||||
|
||||
happen.keydown(input.node(), {keyCode: 65});
|
||||
expect(spy).to.have.been.called;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -33,7 +33,7 @@ describe("iD.modes.AddPoint", function () {
|
||||
});
|
||||
|
||||
describe("pressing ⎋", function () {
|
||||
xit("exits to browse mode", function () {
|
||||
it("exits to browse mode", function () {
|
||||
happen.keydown(document, {keyCode: 27});
|
||||
expect(controller.mode.id).to.equal('browse');
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
describe('Background', function() {
|
||||
describe('iD.Background', function() {
|
||||
var c, d;
|
||||
|
||||
beforeEach(function() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
describe("hash", function () {
|
||||
describe("iD.Hash", function () {
|
||||
var hash, map, controller;
|
||||
|
||||
beforeEach(function () {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
describe('Map', function() {
|
||||
describe('iD.Map', function() {
|
||||
var container, map;
|
||||
|
||||
beforeEach(function() {
|
||||
@@ -42,6 +42,12 @@ describe('Map', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#minzoom', function() {
|
||||
it('is zero by default', function() {
|
||||
expect(map.minzoom()).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#center', function() {
|
||||
it('gets and sets center', function() {
|
||||
expect(map.center([0, 0])).to.equal(map);
|
||||
@@ -52,6 +58,27 @@ describe('Map', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#centerEase', function() {
|
||||
it('sets center', function(done) {
|
||||
expect(map.center([10, 10])).to.equal(map);
|
||||
expect(map.centerEase([20, 20])).to.equal(map);
|
||||
window.setTimeout(function() {
|
||||
expect(map.center()[0]).to.be.closeTo(20, 0.5);
|
||||
expect(map.center()[1]).to.be.closeTo(20, 0.5);
|
||||
done();
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#centerZoom', function() {
|
||||
it('gets and sets center and zoom', function() {
|
||||
expect(map.centerZoom([20, 25], 4)).to.equal(map);
|
||||
expect(map.center()[0]).to.be.closeTo(20, 0.5);
|
||||
expect(map.center()[1]).to.be.closeTo(25, 0.5);
|
||||
expect(map.zoom()).to.be.equal(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#extent', function() {
|
||||
it('gets and sets extent', function() {
|
||||
map.size([100, 100])
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
describe('iD.Style', function() {
|
||||
describe('#waystack', function() {
|
||||
it('stacks bridges over non-bridges', function() {
|
||||
var a = { tags: { bridge: 'yes' } },
|
||||
b = { tags: {} };
|
||||
expect(iD.Style.waystack(a, b)).to.equal(1);
|
||||
expect(iD.Style.waystack(b, a)).to.equal(-1);
|
||||
});
|
||||
|
||||
it('stacks layers', function() {
|
||||
var a = { tags: { layer: 1 } },
|
||||
b = { tags: { layer: 0 } };
|
||||
expect(iD.Style.waystack(a, b)).to.equal(1);
|
||||
expect(iD.Style.waystack(b, a)).to.equal(-1);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -11,7 +11,7 @@ describe("iD.ui.flash", function () {
|
||||
|
||||
it('leaves after 1000 ms', function () {
|
||||
var flash = iD.ui.flash();
|
||||
clock.tick(1010);
|
||||
clock.tick(1610);
|
||||
expect(flash.node().parentNode).to.be.null;
|
||||
});
|
||||
});
|
||||
|
||||
+16
-16
@@ -1,4 +1,4 @@
|
||||
describe('Util', function() {
|
||||
describe('iD.Util', function() {
|
||||
var util;
|
||||
|
||||
it('#trueObj', function() {
|
||||
@@ -26,21 +26,21 @@ describe('Util', function() {
|
||||
|
||||
describe('geo', function() {
|
||||
describe('#roundCoords', function() {
|
||||
expect(iD.util.geo.roundCoords([0.1, 1])).to.eql([0, 1]);
|
||||
expect(iD.util.geo.roundCoords([0, 1])).to.eql([0, 1]);
|
||||
expect(iD.util.geo.roundCoords([0, 1.1])).to.eql([0, 1]);
|
||||
expect(iD.geo.roundCoords([0.1, 1])).to.eql([0, 1]);
|
||||
expect(iD.geo.roundCoords([0, 1])).to.eql([0, 1]);
|
||||
expect(iD.geo.roundCoords([0, 1.1])).to.eql([0, 1]);
|
||||
});
|
||||
|
||||
describe('#interp', function() {
|
||||
it('interpolates halfway', function() {
|
||||
var a = [0, 0],
|
||||
b = [10, 10];
|
||||
expect(iD.util.geo.interp(a, b, 0.5)).to.eql([5, 5]);
|
||||
expect(iD.geo.interp(a, b, 0.5)).to.eql([5, 5]);
|
||||
});
|
||||
it('interpolates to one side', function() {
|
||||
var a = [0, 0],
|
||||
b = [10, 10];
|
||||
expect(iD.util.geo.interp(a, b, 0)).to.eql([0, 0]);
|
||||
expect(iD.geo.interp(a, b, 0)).to.eql([0, 0]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -48,17 +48,17 @@ describe('Util', function() {
|
||||
it('distance between two same points is zero', function() {
|
||||
var a = [0, 0],
|
||||
b = [0, 0];
|
||||
expect(iD.util.geo.dist(a, b)).to.eql(0);
|
||||
expect(iD.geo.dist(a, b)).to.eql(0);
|
||||
});
|
||||
it('a straight 10 unit line is 10', function() {
|
||||
var a = [0, 0],
|
||||
b = [10, 0];
|
||||
expect(iD.util.geo.dist(a, b)).to.eql(10);
|
||||
expect(iD.geo.dist(a, b)).to.eql(10);
|
||||
});
|
||||
it('a pythagorean triangle is right', function() {
|
||||
var a = [0, 0],
|
||||
b = [4, 3];
|
||||
expect(iD.util.geo.dist(a, b)).to.eql(5);
|
||||
expect(iD.geo.dist(a, b)).to.eql(5);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -66,7 +66,7 @@ describe('Util', 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;
|
||||
expect(iD.geo.pointInPolygon(point, poly)).to.be.true;
|
||||
});
|
||||
it('says a point outside of a polygon is outside', function() {
|
||||
var poly = [
|
||||
@@ -76,7 +76,7 @@ describe('Util', function() {
|
||||
[1, 0],
|
||||
[0, 0]];
|
||||
var point = [0.5, 1.5];
|
||||
expect(iD.util.geo.pointInPolygon(point, poly)).to.be.false;
|
||||
expect(iD.geo.pointInPolygon(point, poly)).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -84,12 +84,12 @@ describe('Util', 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;
|
||||
expect(iD.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;
|
||||
expect(iD.geo.polygonContainsPolygon(outer, inner)).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -97,19 +97,19 @@ describe('Util', function() {
|
||||
it('says a polygon in a polygon intersects it', 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.polygonIntersectsPolygon(outer, inner)).to.be.true;
|
||||
expect(iD.geo.polygonIntersectsPolygon(outer, inner)).to.be.true;
|
||||
});
|
||||
|
||||
it('says a polygon that partially intersects does', 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.polygonIntersectsPolygon(outer, inner)).to.be.true;
|
||||
expect(iD.geo.polygonIntersectsPolygon(outer, inner)).to.be.true;
|
||||
});
|
||||
|
||||
it('says totally disjoint polygons do not intersect', 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.polygonIntersectsPolygon(outer, inner)).to.be.false;
|
||||
expect(iD.geo.polygonIntersectsPolygon(outer, inner)).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user