mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-16 22:03:37 +02:00
Merge branch 'master' into design-overhaul
This commit is contained in:
@@ -44,10 +44,6 @@ path.casing {
|
||||
stroke-width: 3;
|
||||
}
|
||||
|
||||
.elastic-true {
|
||||
pointer-events:none;
|
||||
}
|
||||
|
||||
path.casing.hover {
|
||||
stroke:#FF0F0F !important;
|
||||
opacity:0.8;
|
||||
|
||||
+5
-1
@@ -20,8 +20,10 @@
|
||||
<script src='js/lib/d3.typeahead.js'></script>
|
||||
<script src='js/lib/d3.geo.tile.js'></script>
|
||||
<script src='js/lib/d3.size.js'></script>
|
||||
<script src='js/lib/d3.latedrag.js'></script>
|
||||
<script src='js/lib/d3.keybinding.js'></script>
|
||||
<script src='js/lib/d3-compat.js'></script>
|
||||
<script src='js/lib/queue.js'></script>
|
||||
<script src='js/lib/bootstrap-tooltip.js'></script>
|
||||
|
||||
<script src='js/id/id.js'></script>
|
||||
@@ -44,6 +46,7 @@
|
||||
|
||||
<script src='js/id/actions.js'></script>
|
||||
<script src='js/id/actions/add_node.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>
|
||||
<script src="js/id/actions/delete_node.js"></script>
|
||||
@@ -53,9 +56,10 @@
|
||||
<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/start_way.js'></script>
|
||||
<script src='js/id/actions/split_way.js'></script>
|
||||
|
||||
<script src='js/id/modes.js'></script>
|
||||
<script src='js/id/modes/drag_features.js'></script>
|
||||
<script src='js/id/modes/add_area.js'></script>
|
||||
<script src='js/id/modes/add_place.js'></script>
|
||||
<script src='js/id/modes/add_road.js'></script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/AddCommand.java
|
||||
iD.actions.AddNode = function(node) {
|
||||
return function(graph) {
|
||||
return graph.replace(node, 'added a place');
|
||||
return graph.replace(node);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
iD.actions.AddWay = function(way) {
|
||||
return function(graph) {
|
||||
return graph.replace(way);
|
||||
};
|
||||
};
|
||||
@@ -1,8 +1,10 @@
|
||||
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
|
||||
iD.actions.AddWayNode = function(way, node, index) {
|
||||
iD.actions.AddWayNode = function(wayId, nodeId, index) {
|
||||
return function(graph) {
|
||||
var nodes = way.nodes.slice();
|
||||
nodes.splice(index || nodes.length, 0, node.id);
|
||||
return graph.replace(way.update({nodes: nodes})).replace(node, 'added to a road');
|
||||
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}));
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
iD.actions.ChangeEntityTags = function(entity, tags) {
|
||||
iD.actions.ChangeEntityTags = function(entityId, tags) {
|
||||
return function(graph) {
|
||||
return graph.replace(entity.update({
|
||||
tags: tags
|
||||
}), 'changed tags');
|
||||
var entity = graph.entity(entityId);
|
||||
return graph.replace(entity.update({tags: tags}));
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteNodeAction.as
|
||||
iD.actions.DeleteNode = function(node) {
|
||||
iD.actions.DeleteNode = function(nodeId) {
|
||||
return function(graph) {
|
||||
graph.parentWays(node.id)
|
||||
var node = graph.entity(nodeId);
|
||||
|
||||
graph.parentWays(nodeId)
|
||||
.forEach(function(parent) {
|
||||
graph = iD.actions.RemoveWayNode(parent, node)(graph);
|
||||
graph = iD.actions.RemoveWayNode(parent.id, nodeId)(graph);
|
||||
});
|
||||
|
||||
graph.parentRelations(node.id)
|
||||
graph.parentRelations(nodeId)
|
||||
.forEach(function(parent) {
|
||||
graph = iD.actions.RemoveRelationMember(parent, node)(graph);
|
||||
graph = iD.actions.RemoveRelationMember(parent.id, nodeId)(graph);
|
||||
});
|
||||
|
||||
return graph.remove(node, 'removed a node');
|
||||
return graph.remove(node);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteWayAction.as
|
||||
iD.actions.DeleteWay = function(way) {
|
||||
iD.actions.DeleteWay = function(wayId) {
|
||||
return function(graph) {
|
||||
graph.parentRelations(way.id)
|
||||
var way = graph.entity(wayId);
|
||||
|
||||
graph.parentRelations(wayId)
|
||||
.forEach(function(parent) {
|
||||
graph = iD.actions.RemoveRelationMember(parent, way)(graph);
|
||||
graph = iD.actions.RemoveRelationMember(parent.id, wayId)(graph);
|
||||
});
|
||||
|
||||
way.nodes.forEach(function (id) {
|
||||
var node = graph.entity(id);
|
||||
way.nodes.forEach(function (nodeId) {
|
||||
var node = graph.entity(nodeId);
|
||||
|
||||
graph = iD.actions.RemoveWayNode(way, node)(graph);
|
||||
// Circular ways include nodes more than once, so they
|
||||
// can be deleted on earlier iterations of this loop.
|
||||
if (!node) return;
|
||||
|
||||
if (!graph.parentWays(id).length && !graph.parentRelations(id).length) {
|
||||
graph = iD.actions.RemoveWayNode(wayId, nodeId)(graph);
|
||||
|
||||
if (!graph.parentWays(nodeId).length && !graph.parentRelations(nodeId).length) {
|
||||
if (!node.hasInterestingTags()) {
|
||||
graph = graph.remove(node);
|
||||
} else {
|
||||
@@ -20,6 +26,6 @@ iD.actions.DeleteWay = function(way) {
|
||||
}
|
||||
});
|
||||
|
||||
return graph.remove(way, 'removed a way');
|
||||
return graph.remove(way);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
|
||||
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
|
||||
iD.actions.Move = function(entity, loc) {
|
||||
iD.actions.Move = function(entityId, loc) {
|
||||
return function(graph) {
|
||||
return graph.replace(entity.update({loc: loc}), 'moved an element');
|
||||
var entity = graph.entity(entityId);
|
||||
return graph.replace(entity.update({loc: loc}));
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
iD.actions.RemoveRelationMember = function(relation, member) {
|
||||
iD.actions.RemoveRelationMember = function(relationId, memberId) {
|
||||
return function(graph) {
|
||||
var members = _.without(relation.members, member.id);
|
||||
return graph.replace(relation.update({members: members}), 'removed from a relation');
|
||||
var relation = graph.entity(relationId),
|
||||
members = _.reject(relation.members, function(r) {
|
||||
return r.id === memberId;
|
||||
});
|
||||
return graph.replace(relation.update({members: members}));
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
iD.actions.RemoveWayNode = function(way, node) {
|
||||
iD.actions.RemoveWayNode = function(wayId, nodeId) {
|
||||
return function(graph) {
|
||||
var nodes = _.without(way.nodes, node.id);
|
||||
return graph.replace(way.update({nodes: nodes}), 'removed from a road');
|
||||
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}));
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
|
||||
iD.actions.ReverseWay = function(way) {
|
||||
iD.actions.ReverseWay = function(wayId) {
|
||||
return function(graph) {
|
||||
return graph.replace(way.update({
|
||||
nodes: way.nodes.slice()
|
||||
}), 'changed way direction');
|
||||
var way = graph.entity(wayId),
|
||||
nodes = way.nodes.slice().reverse();
|
||||
return graph.replace(way.update({nodes: nodes}));
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
// https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
|
||||
iD.actions.SplitWay = function(nodeId) {
|
||||
return function(graph) {
|
||||
|
||||
var parents = graph.parentWays(nodeId);
|
||||
|
||||
// splitting ways at intersections TODO
|
||||
if (parents.length !== 1) return graph;
|
||||
|
||||
var way = parents[0];
|
||||
|
||||
var idx = _.indexOf(way.nodes, nodeId);
|
||||
|
||||
// Create a 'b' way that contains all of the tags in the second
|
||||
// half of this way
|
||||
var newWay = iD.Way({ tags: _.clone(way.tags), nodes: way.nodes.slice(idx) });
|
||||
graph = graph.replace(newWay);
|
||||
|
||||
// Reduce the original way to only contain the first set of nodes
|
||||
graph = graph.replace(way.update({ nodes: way.nodes.slice(0, idx + 1) }), 'changed way direction');
|
||||
|
||||
var parentRelations = graph.parentRelations(way.id);
|
||||
|
||||
function isVia(x) { return x.role = 'via'; }
|
||||
function isSelf(x) { return x.id = way.id; }
|
||||
|
||||
parentRelations.forEach(function(relation) {
|
||||
if (relation.tags.type === 'restriction') {
|
||||
var via = _.find(relation.members, isVia);
|
||||
var ownrole = _.find(relation.members, isSelf).role;
|
||||
if (via && !_.contains(newWay.nodes, via.id)) {
|
||||
// the new way doesn't contain the node that's important
|
||||
// to the turn restriction, so we don't need to worry
|
||||
// about adding it to the turn restriction.
|
||||
} else {
|
||||
graph = graph.replace(iD.actions.AddRelationMember(relation.id, {
|
||||
role: ownrole,
|
||||
id: newWay.id,
|
||||
type: 'way'
|
||||
}));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return graph;
|
||||
};
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
iD.actions.StartWay = function(way) {
|
||||
return function(graph) {
|
||||
return graph.replace(way, 'started a road');
|
||||
};
|
||||
};
|
||||
+13
-8
@@ -67,8 +67,7 @@ iD.Connection = function() {
|
||||
delete o.lon;
|
||||
delete o.lat;
|
||||
}
|
||||
o._id = o.id;
|
||||
o.id = o.type[0] + o.id;
|
||||
o.id = iD.Entity.id.fromOSM(o.type, o.id);
|
||||
return iD.Entity(o);
|
||||
}
|
||||
|
||||
@@ -150,10 +149,6 @@ iD.Connection = function() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function apiRequestExtent(extent) {
|
||||
bboxFromAPI(extent, event.load);
|
||||
}
|
||||
|
||||
function loadTiles(projection) {
|
||||
var scaleExtent = [16, 16],
|
||||
s = projection.scale(),
|
||||
@@ -177,10 +172,20 @@ iD.Connection = function() {
|
||||
projection.invert([x + ts, y + ts])];
|
||||
}
|
||||
|
||||
return tiles
|
||||
var q = queue(2);
|
||||
|
||||
var bboxes = tiles
|
||||
.filter(tileAlreadyLoaded)
|
||||
.map(apiExtentBox)
|
||||
.map(apiRequestExtent);
|
||||
.forEach(function(e) {
|
||||
q.defer(bboxFromAPI, e);
|
||||
});
|
||||
|
||||
q.awaitAll(function(err, res) {
|
||||
var g = iD.Graph();
|
||||
res.forEach(function(r) { g = g.merge(r); });
|
||||
event.load(err, g);
|
||||
});
|
||||
}
|
||||
|
||||
connection.url = function(_) {
|
||||
|
||||
+3
-3
@@ -52,17 +52,17 @@ iD.format.XML = {
|
||||
'@version': 0.3,
|
||||
'@generator': 'iD',
|
||||
// TODO: copy elements first
|
||||
create: nest(changes.create.map(function(c) {
|
||||
create: nest(changes.created.map(function(c) {
|
||||
var x = iD.Entity(c);
|
||||
x.changeset = changeset_id;
|
||||
return x;
|
||||
}).map(iD.format.XML.rep)),
|
||||
modify: changes.modify.map(function(c) {
|
||||
modify: changes.modified.map(function(c) {
|
||||
var x = iD.Entity(c);
|
||||
x.changeset = changeset_id;
|
||||
return x;
|
||||
}).map(iD.format.XML.rep),
|
||||
'delete': changes['delete'].map(function(c) {
|
||||
'delete': changes.deleted.map(function(c) {
|
||||
var x = iD.Entity(c);
|
||||
x.changeset = changeset_id;
|
||||
return x;
|
||||
|
||||
+22
-4
@@ -14,8 +14,8 @@ iD.Entity = function(a, b, c) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.id) {
|
||||
this.id = iD.util.id(this.type);
|
||||
if (!this.id && this.type) {
|
||||
this.id = iD.Entity.id(this.type);
|
||||
this._updated = true;
|
||||
}
|
||||
|
||||
@@ -28,17 +28,35 @@ iD.Entity = function(a, b, c) {
|
||||
}
|
||||
};
|
||||
|
||||
iD.Entity.id = function (type) {
|
||||
return iD.Entity.id.fromOSM(type, iD.Entity.id.next[type]--);
|
||||
};
|
||||
|
||||
iD.Entity.id.next = {node: -1, way: -1, relation: -1};
|
||||
|
||||
iD.Entity.id.fromOSM = function (type, id) {
|
||||
return type[0] + id;
|
||||
};
|
||||
|
||||
iD.Entity.id.toOSM = function (id) {
|
||||
return +id.slice(1);
|
||||
};
|
||||
|
||||
iD.Entity.prototype = {
|
||||
osmId: function() {
|
||||
return iD.Entity.id.toOSM(this.id);
|
||||
},
|
||||
|
||||
update: function(attrs) {
|
||||
return iD.Entity(this, attrs, {_updated: true});
|
||||
},
|
||||
|
||||
created: function() {
|
||||
return this._updated && +this.id.slice(1) < 0;
|
||||
return this._updated && this.osmId() < 0;
|
||||
},
|
||||
|
||||
modified: function() {
|
||||
return this._updated && +this.id.slice(1) > 0;
|
||||
return this._updated && this.osmId() > 0;
|
||||
},
|
||||
|
||||
intersects: function(extent, resolver) {
|
||||
|
||||
+57
-26
@@ -1,5 +1,5 @@
|
||||
iD.Graph = function(entities, annotation) {
|
||||
if (!(this instanceof iD.Graph)) return new iD.Graph(entities, annotation);
|
||||
iD.Graph = function(entities) {
|
||||
if (!(this instanceof iD.Graph)) return new iD.Graph(entities);
|
||||
|
||||
if (_.isArray(entities)) {
|
||||
this.entities = {};
|
||||
@@ -10,8 +10,6 @@ iD.Graph = function(entities, annotation) {
|
||||
this.entities = entities || {};
|
||||
}
|
||||
|
||||
this.annotation = annotation;
|
||||
|
||||
if (iD.debug) {
|
||||
Object.freeze(this);
|
||||
Object.freeze(this.entities);
|
||||
@@ -26,33 +24,40 @@ iD.Graph.prototype = {
|
||||
parentWays: function(id) {
|
||||
// This is slow and a bad hack.
|
||||
return _.filter(this.entities, function(e) {
|
||||
return e.type === 'way' && e.nodes.indexOf(id) !== -1;
|
||||
return e && e.type === 'way' && e.nodes.indexOf(id) !== -1;
|
||||
});
|
||||
},
|
||||
|
||||
parentRelations: function(id) {
|
||||
// This is slow and a bad hack.
|
||||
return _.filter(this.entities, function(e) {
|
||||
return e.type === 'relation' && e.members.indexOf(id) !== -1;
|
||||
return e && e.type === 'relation' &&
|
||||
_.pluck(e.members, 'id').indexOf(id) !== -1;
|
||||
});
|
||||
},
|
||||
|
||||
merge: function(graph) {
|
||||
var entities = _.clone(this.entities);
|
||||
_.defaults(entities, graph.entities);
|
||||
return iD.Graph(entities, this.annotation);
|
||||
return iD.Graph(entities);
|
||||
},
|
||||
|
||||
replace: function(entity, annotation) {
|
||||
replace: function(entity) {
|
||||
var entities = _.clone(this.entities);
|
||||
entities[entity.id] = entity;
|
||||
return iD.Graph(entities, annotation);
|
||||
return iD.Graph(entities);
|
||||
},
|
||||
|
||||
remove: function(entity, annotation) {
|
||||
remove: function(entity) {
|
||||
var entities = _.clone(this.entities);
|
||||
delete entities[entity.id];
|
||||
return iD.Graph(entities, annotation);
|
||||
|
||||
if (entity.created()) {
|
||||
delete entities[entity.id];
|
||||
} else {
|
||||
entities[entity.id] = undefined;
|
||||
}
|
||||
|
||||
return iD.Graph(entities);
|
||||
},
|
||||
|
||||
// get all objects that intersect an extent.
|
||||
@@ -60,7 +65,7 @@ iD.Graph.prototype = {
|
||||
var items = [];
|
||||
for (var i in this.entities) {
|
||||
var entity = this.entities[i];
|
||||
if (entity.intersects(extent, this)) {
|
||||
if (entity && entity.intersects(extent, this)) {
|
||||
items.push(this.fetch(entity.id));
|
||||
}
|
||||
}
|
||||
@@ -70,26 +75,52 @@ iD.Graph.prototype = {
|
||||
// Resolve the id references in a way, replacing them with actual objects.
|
||||
fetch: function(id) {
|
||||
var entity = this.entities[id], nodes = [];
|
||||
if (!entity.nodes || !entity.nodes.length) return iD.Entity(entity); // TODO: shouldn't be necessary
|
||||
if (!entity || !entity.nodes || !entity.nodes.length) return entity;
|
||||
for (var i = 0, l = entity.nodes.length; i < l; i++) {
|
||||
nodes[i] = this.fetch(entity.nodes[i]);
|
||||
}
|
||||
return iD.Entity(entity, {nodes: nodes});
|
||||
},
|
||||
|
||||
modifications: function() {
|
||||
return _.filter(this.entities, function(entity) {
|
||||
return entity.modified();
|
||||
}).map(function(e) {
|
||||
return this.fetch(e.id);
|
||||
}.bind(this));
|
||||
difference: function (graph) {
|
||||
var result = [];
|
||||
|
||||
_.each(this.entities, function(entity, id) {
|
||||
if (entity !== graph.entities[id]) {
|
||||
result.push(id);
|
||||
}
|
||||
});
|
||||
|
||||
_.each(graph.entities, function(entity, id) {
|
||||
if (entity && !this.entities.hasOwnProperty(id)) {
|
||||
result.push(id);
|
||||
}
|
||||
}, this);
|
||||
|
||||
return result.sort();
|
||||
},
|
||||
|
||||
creations: function() {
|
||||
return _.filter(this.entities, function(entity) {
|
||||
return entity.created();
|
||||
}).map(function(e) {
|
||||
return this.fetch(e.id);
|
||||
}.bind(this));
|
||||
modified: function() {
|
||||
var result = [];
|
||||
_.each(this.entities, function(entity, id) {
|
||||
if (entity && entity.modified()) result.push(id);
|
||||
});
|
||||
return result;
|
||||
},
|
||||
|
||||
created: function() {
|
||||
var result = [];
|
||||
_.each(this.entities, function(entity, id) {
|
||||
if (entity && entity.created()) result.push(id);
|
||||
});
|
||||
return result;
|
||||
},
|
||||
|
||||
deleted: function() {
|
||||
var result = [];
|
||||
_.each(this.entities, function(entity, id) {
|
||||
if (!entity) result.push(id);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
+53
-35
@@ -2,50 +2,77 @@ iD.History = function() {
|
||||
var stack, index,
|
||||
dispatch = d3.dispatch('change');
|
||||
|
||||
function maybeChange() {
|
||||
if (stack[index].annotation) {
|
||||
dispatch.change();
|
||||
function perform(actions) {
|
||||
actions = Array.prototype.slice.call(actions);
|
||||
|
||||
var annotation;
|
||||
|
||||
if (_.isString(_.last(actions))) {
|
||||
annotation = actions.pop();
|
||||
}
|
||||
|
||||
var graph = stack[index].graph;
|
||||
for (var i = 0; i < actions.length; i++) {
|
||||
graph = actions[i](graph);
|
||||
}
|
||||
|
||||
return {graph: graph, annotation: annotation};
|
||||
}
|
||||
|
||||
function change(previous) {
|
||||
dispatch.change(history.graph().difference(previous));
|
||||
}
|
||||
|
||||
var history = {
|
||||
graph: function () {
|
||||
return stack[index];
|
||||
return stack[index].graph;
|
||||
},
|
||||
|
||||
merge: function (graph) {
|
||||
for (var i = 0; i < stack.length; i++) {
|
||||
stack[i] = stack[i].merge(graph);
|
||||
stack[i].graph = stack[i].graph.merge(graph);
|
||||
}
|
||||
},
|
||||
|
||||
perform: function (action) {
|
||||
perform: function () {
|
||||
var previous = stack[index].graph;
|
||||
|
||||
stack = stack.slice(0, index + 1);
|
||||
stack.push(action(this.graph()));
|
||||
stack.push(perform(arguments));
|
||||
index++;
|
||||
maybeChange();
|
||||
|
||||
change(previous);
|
||||
},
|
||||
|
||||
replace: function (action) {
|
||||
replace: function () {
|
||||
var previous = stack[index].graph;
|
||||
|
||||
// assert(index == stack.length - 1)
|
||||
stack[index] = action(this.graph());
|
||||
maybeChange();
|
||||
stack[index] = perform(arguments);
|
||||
|
||||
change(previous);
|
||||
},
|
||||
|
||||
undo: function () {
|
||||
var previous = stack[index].graph;
|
||||
|
||||
while (index > 0) {
|
||||
index--;
|
||||
if (stack[index].annotation) break;
|
||||
}
|
||||
dispatch.change();
|
||||
|
||||
change(previous);
|
||||
},
|
||||
|
||||
redo: function () {
|
||||
var previous = stack[index].graph;
|
||||
|
||||
while (index < stack.length - 1) {
|
||||
index++;
|
||||
if (stack[index].annotation) break;
|
||||
}
|
||||
dispatch.change();
|
||||
|
||||
change(previous);
|
||||
},
|
||||
|
||||
undoAnnotation: function () {
|
||||
@@ -64,34 +91,25 @@ iD.History = function() {
|
||||
}
|
||||
},
|
||||
|
||||
// generate reports of changes for changesets to use
|
||||
modify: function () {
|
||||
return stack[index].modifications();
|
||||
},
|
||||
|
||||
create: function () {
|
||||
return stack[index].creations();
|
||||
},
|
||||
|
||||
'delete': function () {
|
||||
return _.difference(
|
||||
_.pluck(stack[0].entities, 'id'),
|
||||
_.pluck(stack[index].entities, 'id')
|
||||
).map(function (id) {
|
||||
return stack[0].fetch(id);
|
||||
});
|
||||
},
|
||||
|
||||
changes: function () {
|
||||
var initial = stack[0].graph,
|
||||
current = stack[index].graph;
|
||||
|
||||
return {
|
||||
modify: this.modify(),
|
||||
create: this.create(),
|
||||
'delete': this['delete']()
|
||||
modified: current.modified().map(function (id) {
|
||||
return current.fetch(id);
|
||||
}),
|
||||
created: current.created().map(function (id) {
|
||||
return current.fetch(id);
|
||||
}),
|
||||
deleted: current.deleted().map(function (id) {
|
||||
return initial.fetch(id);
|
||||
})
|
||||
};
|
||||
},
|
||||
|
||||
reset: function () {
|
||||
stack = [iD.Graph()];
|
||||
stack = [{graph: iD.Graph()}];
|
||||
index = 0;
|
||||
dispatch.change();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
window.iD = function(container) {
|
||||
var connection = iD.Connection()
|
||||
.url('http://api06.dev.openstreetmap.org'),
|
||||
// .url('http://www.openstreetmap.org'),
|
||||
history = iD.History(),
|
||||
map = iD.Map()
|
||||
.connection(connection)
|
||||
|
||||
+26
-14
@@ -7,29 +7,41 @@ iD.modes.AddArea = function() {
|
||||
};
|
||||
|
||||
mode.enter = function() {
|
||||
mode.map.dblclickEnable(false);
|
||||
mode.map.hint('Click on the map to start drawing an area, like a park, lake, or building.');
|
||||
var map = mode.map,
|
||||
history = mode.history,
|
||||
controller = mode.controller;
|
||||
|
||||
mode.map.surface.on('click.addarea', function() {
|
||||
map.dblclickEnable(false)
|
||||
.hint('Click on the map to start drawing an area, like a park, lake, or building.');
|
||||
|
||||
map.surface.on('click.addarea', function() {
|
||||
var datum = d3.select(d3.event.target).datum() || {},
|
||||
node,
|
||||
way = iD.Way({tags: { building: 'yes', area: 'yes', elastic: 'true' }});
|
||||
way = iD.Way({tags: { building: 'yes', area: 'yes' }});
|
||||
|
||||
// connect a way to an existing way
|
||||
if (datum.type === 'node') {
|
||||
node = datum;
|
||||
// start from an existing node
|
||||
history.perform(
|
||||
iD.actions.AddWay(way),
|
||||
iD.actions.AddWayNode(way.id, datum.id),
|
||||
iD.actions.AddWayNode(way.id, datum.id),
|
||||
'started an area');
|
||||
|
||||
} else {
|
||||
node = iD.Node({loc: mode.map.mouseCoordinates()});
|
||||
// start from a new node
|
||||
var node = iD.Node({loc: map.mouseCoordinates()});
|
||||
history.perform(
|
||||
iD.actions.AddWay(way),
|
||||
iD.actions.AddNode(node),
|
||||
iD.actions.AddWayNode(way.id, node.id),
|
||||
iD.actions.AddWayNode(way.id, node.id),
|
||||
'started an area');
|
||||
}
|
||||
|
||||
mode.history.perform(iD.actions.StartWay(way));
|
||||
mode.history.perform(iD.actions.AddWayNode(way, node));
|
||||
|
||||
mode.controller.enter(iD.modes.DrawArea(way.id));
|
||||
controller.enter(iD.modes.DrawArea(way.id));
|
||||
});
|
||||
|
||||
mode.map.keybinding().on('⎋.addarea', function() {
|
||||
mode.controller.exit();
|
||||
map.keybinding().on('⎋.addarea', function() {
|
||||
controller.exit();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -6,16 +6,24 @@ iD.modes.AddPlace = function() {
|
||||
};
|
||||
|
||||
mode.enter = function() {
|
||||
mode.map.hint('Click on the map to add a place.');
|
||||
var map = mode.map,
|
||||
history = mode.history,
|
||||
controller = mode.controller;
|
||||
|
||||
mode.map.surface.on('click.addplace', function() {
|
||||
var node = iD.Node({loc: mode.map.mouseCoordinates(), _poi: true});
|
||||
mode.history.perform(iD.actions.AddNode(node));
|
||||
mode.controller.enter(iD.modes.Select(node));
|
||||
map.hint('Click on the map to add a place.');
|
||||
|
||||
map.surface.on('click.addplace', function() {
|
||||
var node = iD.Node({loc: map.mouseCoordinates(), _poi: true});
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddNode(node),
|
||||
'added a place');
|
||||
|
||||
controller.enter(iD.modes.Select(node));
|
||||
});
|
||||
|
||||
mode.map.keybinding().on('⎋.addplace', function() {
|
||||
mode.controller.exit();
|
||||
map.keybinding().on('⎋.addplace', function() {
|
||||
controller.exit();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
+39
-32
@@ -7,55 +7,62 @@ iD.modes.AddRoad = function() {
|
||||
};
|
||||
|
||||
mode.enter = function() {
|
||||
mode.map.dblclickEnable(false);
|
||||
var map = mode.map,
|
||||
node,
|
||||
history = mode.history,
|
||||
controller = mode.controller;
|
||||
|
||||
mode.map.hint('Click on the map to start drawing an road, path, or route.');
|
||||
map.dblclickEnable(false)
|
||||
.hint('Click on the map to start drawing an road, path, or route.');
|
||||
|
||||
mode.map.surface.on('click.addroad', function() {
|
||||
map.surface.on('click.addroad', function() {
|
||||
var datum = d3.select(d3.event.target).datum() || {},
|
||||
node,
|
||||
direction = 'forward',
|
||||
start = true,
|
||||
way = iD.Way({ tags: { highway: 'residential', elastic: 'true' } });
|
||||
way = iD.Way({ tags: { highway: 'residential' } }),
|
||||
direction = 'forward';
|
||||
|
||||
if (datum.type === 'node') {
|
||||
// continue an existing way
|
||||
node = datum;
|
||||
|
||||
var id = datum.id;
|
||||
var parents = mode.history.graph().parentWays(id);
|
||||
if (parents.length) {
|
||||
if (parents[0].nodes[0] === id) {
|
||||
way = parents[0];
|
||||
direction = 'backward';
|
||||
start = false;
|
||||
} else if (_.last(parents[0].nodes) === id) {
|
||||
way = parents[0];
|
||||
start = false;
|
||||
}
|
||||
var parents = history.graph().parentWays(id);
|
||||
if (parents.length && parents[0].nodes[0] === id) {
|
||||
way = parents[0];
|
||||
direction = 'backward';
|
||||
} else if (parents.length && _.last(parents[0].nodes) === id) {
|
||||
way = parents[0];
|
||||
} else {
|
||||
history.perform(
|
||||
iD.actions.AddWay(way),
|
||||
iD.actions.AddWayNode(way.id, datum.id),
|
||||
'started a road');
|
||||
}
|
||||
|
||||
} else if (datum.type === 'way') {
|
||||
// begin a new way starting from an existing way
|
||||
node = iD.Node({loc: mode.map.mouseCoordinates()});
|
||||
node = iD.Node({loc: map.mouseCoordinates()}),
|
||||
index = iD.util.geo.chooseIndex(datum, d3.mouse(map.surface.node()), map);
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddWay(way),
|
||||
iD.actions.AddWayNode(datum.id, node, index),
|
||||
iD.actions.AddWayNode(way.id, node.id),
|
||||
'started a road');
|
||||
|
||||
var index = iD.util.geo.chooseIndex(datum, d3.mouse(mode.map.surface.node()), mode.map);
|
||||
var connectedWay = mode.history.graph().entity(datum.id);
|
||||
mode.history.perform(iD.actions.AddWayNode(connectedWay, node, index));
|
||||
} else {
|
||||
// begin a new way
|
||||
node = iD.Node({loc: mode.map.mouseCoordinates()});
|
||||
node = iD.Node({loc: map.mouseCoordinates()});
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddWay(way),
|
||||
iD.actions.AddNode(node),
|
||||
iD.actions.AddWayNode(way.id, node.id),
|
||||
'started a road');
|
||||
}
|
||||
|
||||
if (start) {
|
||||
mode.history.perform(iD.actions.StartWay(way));
|
||||
mode.history.perform(iD.actions.AddWayNode(way, node));
|
||||
}
|
||||
|
||||
mode.controller.enter(iD.modes.DrawRoad(way.id, direction));
|
||||
controller.enter(iD.modes.DrawRoad(way.id, direction));
|
||||
});
|
||||
|
||||
mode.map.keybinding().on('⎋.addroad', function() {
|
||||
mode.controller.exit();
|
||||
map.keybinding().on('⎋.addroad', function() {
|
||||
controller.exit();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ iD.modes.Browse = function() {
|
||||
};
|
||||
|
||||
mode.enter = function() {
|
||||
iD.modes._dragFeatures(mode);
|
||||
mode.map.surface.on('click.browse', function () {
|
||||
var datum = d3.select(d3.event.target).datum();
|
||||
if (datum instanceof iD.Entity) {
|
||||
@@ -16,6 +17,7 @@ iD.modes.Browse = function() {
|
||||
};
|
||||
|
||||
mode.exit = function() {
|
||||
mode.map.surface.on('mousedown.latedrag', null);
|
||||
mode.map.surface.on('click.browse', null);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
iD.modes._dragFeatures = function(mode) {
|
||||
var dragging;
|
||||
|
||||
var dragbehavior = d3.behavior.drag()
|
||||
.origin(function(entity) {
|
||||
var p = mode.map.projection(entity.loc);
|
||||
return { x: p[0], y: p[1] };
|
||||
})
|
||||
.on('drag', function(entity) {
|
||||
d3.event.sourceEvent.stopPropagation();
|
||||
|
||||
var loc = mode.map.projection.invert([d3.event.x, d3.event.y]);
|
||||
|
||||
if (!dragging) {
|
||||
if (entity.accuracy) {
|
||||
dragging = iD.Node({loc: loc});
|
||||
mode.history.perform(
|
||||
iD.actions.AddNode(dragging),
|
||||
iD.actions.AddWayNode(entity.way, dragging.id, entity.index));
|
||||
} else {
|
||||
dragging = entity;
|
||||
mode.history.perform(
|
||||
iD.actions.Move(dragging.id, loc));
|
||||
}
|
||||
}
|
||||
|
||||
mode.history.replace(iD.actions.Move(dragging.id, loc));
|
||||
})
|
||||
.on('dragend', function (entity) {
|
||||
if (!dragging) return;
|
||||
dragging = undefined;
|
||||
|
||||
mode.history.replace(
|
||||
iD.actions.Noop(),
|
||||
entity.accuracy ? 'added a node to a way' : 'moved a node');
|
||||
});
|
||||
|
||||
mode.map.surface
|
||||
.call(dragbehavior)
|
||||
.call(d3.latedrag()
|
||||
.filter(function(d) {
|
||||
return (d.type === 'node' || d.accuracy);
|
||||
}));
|
||||
};
|
||||
+51
-44
@@ -1,73 +1,80 @@
|
||||
iD.modes.DrawArea = function(way_id) {
|
||||
iD.modes.DrawArea = function(wayId) {
|
||||
var mode = {
|
||||
button: 'area'
|
||||
};
|
||||
|
||||
mode.enter = function() {
|
||||
var map = mode.map,
|
||||
history = mode.history,
|
||||
controller = mode.controller,
|
||||
way = history.graph().entity(wayId),
|
||||
headId = _.last(way.nodes),
|
||||
tailId = _.first(way.nodes),
|
||||
node = iD.Node({loc: map.mouseCoordinates()});
|
||||
|
||||
mode.map.hint('Click on the map to add points to your area. Finish the ' +
|
||||
map.dblclickEnable(false)
|
||||
.fastEnable(false)
|
||||
.hint('Click on the map to add points to your area. Finish the ' +
|
||||
'area by clicking on your first point');
|
||||
mode.map.dblclickEnable(false);
|
||||
|
||||
var way = mode.history.graph().entity(way_id),
|
||||
firstnode_id = _.first(way.nodes),
|
||||
node = iD.Node({loc: mode.map.mouseCoordinates()});
|
||||
history.perform(
|
||||
iD.actions.AddNode(node),
|
||||
iD.actions.AddWayNode(way.id, node.id, -1));
|
||||
|
||||
function finish(next) {
|
||||
way = mode.history.graph().entity(way.id);
|
||||
way.tags = _.omit(way.tags, 'elastic');
|
||||
mode.history.perform(iD.actions.ChangeEntityTags(way, way.tags));
|
||||
return mode.controller.enter(next);
|
||||
}
|
||||
|
||||
mode.history.perform(iD.actions.AddWayNode(way, node));
|
||||
|
||||
mode.map.surface.on('mousemove.drawarea', function() {
|
||||
mode.history.replace(iD.actions.AddWayNode(way, node.update({loc: mode.map.mouseCoordinates()})));
|
||||
map.surface.on('mousemove.drawarea', function() {
|
||||
history.replace(iD.actions.Move(node.id, map.mouseCoordinates()));
|
||||
});
|
||||
|
||||
mode.map.surface.on('click.drawarea', function() {
|
||||
d3.event.stopPropagation();
|
||||
map.surface.on('click.drawarea', function() {
|
||||
var datum = d3.select(d3.event.target).datum() || {};
|
||||
|
||||
var datum = d3.select(d3.event.target).datum();
|
||||
if (datum.id === tailId) {
|
||||
history.replace(
|
||||
iD.actions.DeleteNode(node.id),
|
||||
iD.actions.AddWayNode(way.id, tailId, -1),
|
||||
'added to an area');
|
||||
|
||||
if (datum.type === 'node') {
|
||||
if (datum.id == firstnode_id) {
|
||||
mode.history.replace(iD.actions.DeleteNode(node));
|
||||
mode.history.replace(iD.actions.AddWayNode(way,
|
||||
mode.history.graph().entity(way.nodes[0])));
|
||||
controller.enter(iD.modes.Select(way));
|
||||
|
||||
} else if (datum.type === 'node' && datum.id !== node.id) {
|
||||
// connect the way to an existing node
|
||||
history.replace(
|
||||
iD.actions.DeleteNode(node.id),
|
||||
iD.actions.AddWayNode(way.id, datum.id, -1),
|
||||
'added to an area');
|
||||
|
||||
controller.enter(iD.modes.DrawArea(wayId));
|
||||
|
||||
return finish(iD.modes.Select(way));
|
||||
} else {
|
||||
// connect a way to an existing way
|
||||
mode.history.replace(iD.actions.AddWayNode(way, datum));
|
||||
}
|
||||
} else {
|
||||
node = node.update({loc: mode.map.mouseCoordinates()});
|
||||
mode.history.replace(iD.actions.AddWayNode(way, node));
|
||||
history.replace(
|
||||
iD.actions.Noop(),
|
||||
'added to an area');
|
||||
|
||||
controller.enter(iD.modes.DrawArea(wayId));
|
||||
}
|
||||
|
||||
mode.controller.enter(iD.modes.DrawArea(way_id));
|
||||
});
|
||||
|
||||
mode.map.keybinding().on('⎋.drawarea', function() {
|
||||
finish(iD.modes.Browse());
|
||||
map.keybinding().on('⎋.drawarea', function() {
|
||||
history.replace(
|
||||
iD.actions.DeleteNode(node.id));
|
||||
|
||||
controller.enter(iD.modes.Browse());
|
||||
});
|
||||
|
||||
mode.map.keybinding().on('⌫.drawarea', function() {
|
||||
map.keybinding().on('⌫.drawarea', function() {
|
||||
d3.event.preventDefault();
|
||||
var lastNode = _.last(way.nodes);
|
||||
mode.history.replace(iD.actions.RemoveWayNode(way,
|
||||
mode.history.graph().entity(lastNode)));
|
||||
mode.history.replace(iD.actions.DeleteNode(
|
||||
mode.history.graph().entity(lastNode)));
|
||||
mode.history.replace(iD.actions.DeleteNode(node));
|
||||
mode.controller.enter(iD.modes.DrawArea(way_id));
|
||||
|
||||
history.replace(
|
||||
iD.actions.DeleteNode(node.id),
|
||||
iD.actions.DeleteNode(headId));
|
||||
|
||||
controller.enter(iD.modes.DrawArea(wayId));
|
||||
});
|
||||
};
|
||||
|
||||
mode.exit = function() {
|
||||
mode.map.hint(false);
|
||||
mode.map.fastEnable(true);
|
||||
mode.map.surface
|
||||
.on('mousemove.drawarea', null)
|
||||
.on('click.drawarea', null);
|
||||
|
||||
+67
-69
@@ -1,94 +1,93 @@
|
||||
iD.modes.DrawRoad = function(way_id, direction) {
|
||||
iD.modes.DrawRoad = function(wayId, direction) {
|
||||
var mode = {
|
||||
button: 'road'
|
||||
};
|
||||
|
||||
mode.enter = function() {
|
||||
mode.map.dblclickEnable(false)
|
||||
.dragEnable(false)
|
||||
var map = mode.map,
|
||||
history = mode.history,
|
||||
controller = mode.controller,
|
||||
way = history.graph().entity(wayId),
|
||||
node = iD.Node({loc: map.mouseCoordinates()}),
|
||||
index = (direction === 'forward') ? undefined : 0,
|
||||
headId = (direction === 'forward') ? _.last(way.nodes) : _.first(way.nodes),
|
||||
tailId = (direction === 'forward') ? _.first(way.nodes) : _.last(way.nodes);
|
||||
|
||||
map.dblclickEnable(false)
|
||||
.fastEnable(false)
|
||||
.hint('Click to add more points to the road. ' +
|
||||
'Click on other roads to connect to them, and double-click to ' +
|
||||
'end the road.');
|
||||
'Click on other roads to connect to them, and double-click to ' +
|
||||
'end the road.');
|
||||
|
||||
var index = (direction === 'forward') ? undefined : -1,
|
||||
node = iD.Node({loc: mode.map.mouseCoordinates(), tags: { elastic: true } }),
|
||||
way = mode.history.graph().entity(way_id),
|
||||
firstNode = way.nodes[0],
|
||||
lastNode = _.last(way.nodes);
|
||||
history.perform(
|
||||
iD.actions.AddNode(node),
|
||||
iD.actions.AddWayNode(wayId, node.id, index));
|
||||
|
||||
function finish(next) {
|
||||
way.tags = _.omit(way.tags, 'elastic');
|
||||
mode.history.perform(iD.actions.ChangeEntityTags(way, way.tags));
|
||||
return mode.controller.enter(next);
|
||||
}
|
||||
|
||||
mode.history.perform(iD.actions.AddWayNode(way, node, index));
|
||||
|
||||
mode.map.surface.on('mousemove.drawroad', function() {
|
||||
mode.history.replace(iD.actions.AddWayNode(way,
|
||||
node.update({ loc: mode.map.mouseCoordinates() }), index));
|
||||
map.surface.on('mousemove.drawroad', function() {
|
||||
history.replace(iD.actions.Move(node.id, map.mouseCoordinates()));
|
||||
});
|
||||
|
||||
mode.map.surface.on('click.drawroad', function() {
|
||||
// d3.event.stopPropagation();
|
||||
|
||||
map.surface.on('click.drawroad', function() {
|
||||
var datum = d3.select(d3.event.target).datum() || {};
|
||||
|
||||
if (datum.id === tailId) {
|
||||
// connect the way in a loop
|
||||
history.replace(
|
||||
iD.actions.DeleteNode(node.id),
|
||||
iD.actions.AddWayNode(wayId, tailId, index),
|
||||
'added to a road');
|
||||
|
||||
controller.enter(iD.modes.Select(way));
|
||||
|
||||
} else if (datum.id === headId) {
|
||||
// finish the way
|
||||
history.replace(iD.actions.DeleteNode(node.id));
|
||||
|
||||
controller.enter(iD.modes.Select(way));
|
||||
|
||||
} 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),
|
||||
'added to a road');
|
||||
|
||||
controller.enter(iD.modes.DrawRoad(wayId, direction));
|
||||
|
||||
if (datum.type === 'node') {
|
||||
if (datum.id == firstNode || datum.id == lastNode) {
|
||||
// If mode is drawing a loop and mode is not the drawing
|
||||
// end of the stick, finish the circle
|
||||
if (direction === 'forward' && datum.id == firstNode) {
|
||||
mode.history.replace(iD.actions.AddWayNode(way,
|
||||
mode.history.graph().entity(firstNode), index));
|
||||
} else if (direction === 'backward' && datum.id == lastNode) {
|
||||
mode.history.replace(iD.actions.AddWayNode(way,
|
||||
mode.history.graph().entity(lastNode), index));
|
||||
}
|
||||
mode.history.replace(iD.actions.DeleteNode(node));
|
||||
return finish(iD.modes.Select(way));
|
||||
} else if (datum.id == node.id) {
|
||||
datum = datum.update({ tags: {} });
|
||||
mode.history.replace(iD.actions.ChangeEntityTags(datum, {}));
|
||||
mode.history.replace(iD.actions.DeleteNode(node));
|
||||
mode.history.replace(iD.actions.AddWayNode(way, datum, index));
|
||||
} else {
|
||||
// connect a way to an existing way
|
||||
mode.history.replace(iD.actions.DeleteNode(node));
|
||||
mode.history.replace(iD.actions.AddWayNode(way, datum, index));
|
||||
}
|
||||
} else if (datum.type === 'way') {
|
||||
node = node.update({loc: mode.map.mouseCoordinates() });
|
||||
mode.history.replace(iD.actions.AddWayNode(way, node, index));
|
||||
// connect the way to an existing way
|
||||
var connectedIndex = iD.modes.chooseIndex(datum, d3.mouse(map.surface.node()), map);
|
||||
|
||||
history.replace(
|
||||
iD.actions.AddWayNode(datum.id, node.id, connectedIndex),
|
||||
'added to a road');
|
||||
|
||||
controller.enter(iD.modes.DrawRoad(wayId, direction));
|
||||
|
||||
var connectedWay = mode.history.graph().entity(datum.id);
|
||||
var connectedIndex = iD.modes.chooseIndex(datum,
|
||||
d3.mouse(mode.map.surface.node()),
|
||||
mode.map);
|
||||
mode.history.perform(iD.actions.AddWayNode(connectedWay,
|
||||
node,
|
||||
connectedIndex));
|
||||
} else {
|
||||
mode.history.replace(iD.actions.AddWayNode(way, node, index));
|
||||
history.replace(
|
||||
iD.actions.Noop(),
|
||||
'added to a road');
|
||||
|
||||
controller.enter(iD.modes.DrawRoad(wayId, direction));
|
||||
}
|
||||
|
||||
mode.controller.enter(iD.modes.DrawRoad(way_id, direction));
|
||||
});
|
||||
|
||||
mode.map.keybinding().on('⎋.drawroad', function() {
|
||||
finish(iD.modes.Browse());
|
||||
map.keybinding().on('⎋.drawroad', function() {
|
||||
history.replace(
|
||||
iD.actions.DeleteNode(node.id));
|
||||
|
||||
controller.enter(iD.modes.Browse());
|
||||
});
|
||||
|
||||
mode.map.keybinding().on('⌫.drawroad', function() {
|
||||
map.keybinding().on('⌫.drawroad', function() {
|
||||
d3.event.preventDefault();
|
||||
mode.history.replace(iD.actions.RemoveWayNode(way,
|
||||
mode.history.graph().entity(lastNode)));
|
||||
mode.history.replace(iD.actions.DeleteNode(
|
||||
mode.history.graph().entity(lastNode)));
|
||||
mode.history.replace(iD.actions.DeleteNode(node));
|
||||
mode.controller.enter(iD.modes.DrawRoad(way_id, direction));
|
||||
|
||||
history.replace(
|
||||
iD.actions.DeleteNode(node.id),
|
||||
iD.actions.DeleteNode(headId));
|
||||
|
||||
controller.enter(iD.modes.DrawRoad(wayId, direction));
|
||||
});
|
||||
};
|
||||
|
||||
@@ -103,7 +102,6 @@ iD.modes.DrawRoad = function(way_id, direction) {
|
||||
.on('⌫.drawroad', null);
|
||||
window.setTimeout(function() {
|
||||
mode.map.dblclickEnable(true);
|
||||
mode.map.dragEnable(true);
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
|
||||
+34
-15
@@ -1,8 +1,9 @@
|
||||
iD.modes.Select = function (entity) {
|
||||
var mode = {
|
||||
button: ''
|
||||
},
|
||||
inspector = iD.Inspector(),
|
||||
button: 'browse'
|
||||
};
|
||||
|
||||
var inspector = iD.Inspector(),
|
||||
dragging, target;
|
||||
|
||||
var dragWay = d3.behavior.drag()
|
||||
@@ -11,25 +12,23 @@ iD.modes.Select = function (entity) {
|
||||
return { x: p[0], y: p[1] };
|
||||
})
|
||||
.on('drag', function(entity) {
|
||||
if (!mode.map.dragEnable()) return;
|
||||
|
||||
d3.event.sourceEvent.stopPropagation();
|
||||
|
||||
if (!dragging) {
|
||||
dragging = iD.util.trueObj([entity.id].concat(
|
||||
_.pluck(mode.history.graph().parentWays(entity.id), 'id')));
|
||||
dragging = true;
|
||||
mode.history.perform(iD.actions.Noop());
|
||||
}
|
||||
|
||||
entity.nodes.forEach(function(node) {
|
||||
var start = mode.map.projection(node.loc);
|
||||
var end = mode.map.projection.invert([start[0] + d3.event.dx, start[1] + d3.event.dy]);
|
||||
node.loc = end;
|
||||
mode.history.replace(iD.actions.Move(node, end));
|
||||
var end = mode.map.projection.invert([
|
||||
start[0] + d3.event.dx,
|
||||
start[1] + d3.event.dy]);
|
||||
mode.history.replace(iD.actions.Move(node.id, end));
|
||||
});
|
||||
})
|
||||
.on('dragend', function () {
|
||||
if (!mode.map.dragEnable() || !dragging) return;
|
||||
if (!dragging) return;
|
||||
dragging = undefined;
|
||||
mode.map.redraw();
|
||||
});
|
||||
@@ -37,16 +36,22 @@ iD.modes.Select = function (entity) {
|
||||
function remove() {
|
||||
switch (entity.type) {
|
||||
case 'way':
|
||||
mode.history.perform(iD.actions.DeleteWay(entity));
|
||||
mode.history.perform(
|
||||
iD.actions.DeleteWay(entity.id),
|
||||
'deleted a way');
|
||||
break;
|
||||
case 'node':
|
||||
mode.history.perform(iD.actions.DeleteNode(entity));
|
||||
mode.history.perform(
|
||||
iD.actions.DeleteNode(entity.id),
|
||||
'deleted a node');
|
||||
}
|
||||
|
||||
mode.controller.exit();
|
||||
}
|
||||
|
||||
mode.enter = function () {
|
||||
iD.modes._dragFeatures(mode);
|
||||
|
||||
target = mode.map.surface.selectAll("*")
|
||||
.filter(function (d) { return d === entity; });
|
||||
|
||||
@@ -57,11 +62,23 @@ iD.modes.Select = function (entity) {
|
||||
.call(inspector);
|
||||
|
||||
inspector.on('changeTags', function(d, tags) {
|
||||
mode.history.perform(iD.actions.ChangeEntityTags(mode.history.graph().entity(d.id), tags));
|
||||
mode.history.perform(
|
||||
iD.actions.ChangeEntityTags(d.id, tags),
|
||||
'changed tags');
|
||||
|
||||
}).on('changeWayDirection', function(d) {
|
||||
mode.history.perform(iD.actions.ReverseWay(d));
|
||||
mode.history.perform(
|
||||
iD.actions.ReverseWay(d.id),
|
||||
'reversed a way');
|
||||
|
||||
}).on('splitWay', function(d) {
|
||||
mode.history.perform(
|
||||
iD.actions.SplitWay(d.id),
|
||||
'split a way on a node');
|
||||
|
||||
}).on('remove', function() {
|
||||
remove();
|
||||
|
||||
}).on('close', function() {
|
||||
mode.controller.exit();
|
||||
});
|
||||
@@ -88,6 +105,8 @@ iD.modes.Select = function (entity) {
|
||||
};
|
||||
|
||||
mode.exit = function () {
|
||||
mode.map.surface.on('mousedown.latedrag', null);
|
||||
|
||||
d3.select('.inspector-wrap')
|
||||
.style('display', 'none');
|
||||
|
||||
|
||||
@@ -67,12 +67,20 @@ iD.Background = function() {
|
||||
|
||||
image.exit().remove();
|
||||
|
||||
function load(d) {
|
||||
cache[d] = true;
|
||||
d3.select(this).on('load', null);
|
||||
}
|
||||
|
||||
function error() {
|
||||
d3.select(this).remove();
|
||||
}
|
||||
|
||||
image.enter().append('img')
|
||||
.attr('class', 'tile')
|
||||
.attr('src', function(d) { return d[3]; })
|
||||
.on('load', function(d) {
|
||||
cache[d] = true;
|
||||
});
|
||||
.on('error', error)
|
||||
.on('load', load);
|
||||
|
||||
function tileSize(d) {
|
||||
return Math.ceil(256 * Math.pow(2, z - d[2])) / 256;
|
||||
|
||||
+40
-80
@@ -12,38 +12,8 @@ iD.Map = function() {
|
||||
.scaleExtent([1024, 256 * Math.pow(2, 24)])
|
||||
.on('zoom', zoomPan),
|
||||
dblclickEnabled = true,
|
||||
dragEnabled = true,
|
||||
dragging = false,
|
||||
fastEnabled = true,
|
||||
dragging,
|
||||
dragbehavior = d3.behavior.drag()
|
||||
.origin(function(entity) {
|
||||
if (!dragEnabled) return { x: 0, y: 0 };
|
||||
var p = projection(entity.loc);
|
||||
return { x: p[0], y: p[1] };
|
||||
})
|
||||
.on('drag', function(entity) {
|
||||
d3.event.sourceEvent.stopPropagation();
|
||||
|
||||
if (!dragging) {
|
||||
if (entity.accuracy) {
|
||||
var way = history.graph().entity(entity.way),
|
||||
index = entity.index;
|
||||
entity = iD.Node(entity);
|
||||
history.perform(iD.actions.AddWayNode(way, entity, index));
|
||||
}
|
||||
|
||||
dragging = iD.util.trueObj([entity.id].concat(
|
||||
_.pluck(history.graph().parentWays(entity.id), 'id')));
|
||||
history.perform(iD.actions.Noop());
|
||||
}
|
||||
|
||||
var to = projection.invert([d3.event.x, d3.event.y]);
|
||||
history.replace(iD.actions.Move(entity, to));
|
||||
})
|
||||
.on('dragend', function () {
|
||||
if (!dragEnabled || !dragging) return;
|
||||
dragging = undefined;
|
||||
}),
|
||||
background = iD.Background()
|
||||
.projection(projection),
|
||||
class_stroke = iD.Style.styleClasses('stroke'),
|
||||
@@ -51,18 +21,7 @@ iD.Map = function() {
|
||||
class_area = iD.Style.styleClasses('area'),
|
||||
class_casing = iD.Style.styleClasses('casing'),
|
||||
transformProp = iD.util.prefixProperty('Transform'),
|
||||
support3d = (function() {
|
||||
// test for translate3d support. Based on https://gist.github.com/3794226 by lorenzopolidori and webinista
|
||||
var el = document.createElement('div'),
|
||||
has3d = false;
|
||||
document.body.insertBefore(el,null);
|
||||
if (el.style[transformProp] !== undefined) {
|
||||
el.style[transformProp] = 'translate3d(1px,1px,1px)';
|
||||
has3d = window.getComputedStyle(el).getPropertyValue(transformProp);
|
||||
}
|
||||
document.body.removeChild(el);
|
||||
return (has3d && has3d.length>0 && has3d!=="none");
|
||||
})(),
|
||||
support3d = iD.util.support3d(),
|
||||
supersurface, surface, defs, tilegroup, r, g, alength;
|
||||
|
||||
function map() {
|
||||
@@ -90,7 +49,7 @@ iD.Map = function() {
|
||||
.attr('clip-path', 'url(#clip)');
|
||||
|
||||
g = ['fill', 'casing', 'stroke', 'text', 'hit', 'temp'].reduce(function(mem, i) {
|
||||
return (mem[i] = r.append('g').attr('class', 'layer-g')) && mem;
|
||||
return (mem[i] = r.append('g').attr('class', 'layer-g layer-' + i)) && mem;
|
||||
}, {});
|
||||
|
||||
var arrow = surface.append('text').text('►----');
|
||||
@@ -110,21 +69,30 @@ iD.Map = function() {
|
||||
return 'M' + _.pluck(d.nodes, 'loc').map(projection).map(iD.util.geo.roundCoords).join('L');
|
||||
}
|
||||
|
||||
function drawVector(only) {
|
||||
function drawVector(difference) {
|
||||
if (surface.style(transformProp) != 'none') return;
|
||||
var all = [], ways = [], areas = [], points = [], waynodes = [],
|
||||
var filter, all, ways = [], areas = [], points = [], waynodes = [],
|
||||
extent = map.extent(),
|
||||
graph = history.graph();
|
||||
|
||||
if (!only) {
|
||||
if (!difference) {
|
||||
all = graph.intersects(extent);
|
||||
filter = d3.functor(true);
|
||||
} else {
|
||||
for (var id in only) all.push(graph.fetch(id));
|
||||
var only = {};
|
||||
difference.forEach(function (id) {
|
||||
var entity = graph.fetch(id);
|
||||
if (entity) {
|
||||
only[id] = entity;
|
||||
graph.parentWays(id).forEach(function (entity) {
|
||||
only[entity.id] = graph.fetch(entity.id);
|
||||
});
|
||||
}
|
||||
});
|
||||
all = _.values(only);
|
||||
filter = function(d) { return d.accuracy ? only[d.way] : only[d.id]; };
|
||||
}
|
||||
|
||||
var filter = only ?
|
||||
function(d) { return only[d.id]; } : function() { return true; };
|
||||
|
||||
if (all.length > 200000) return hideVector();
|
||||
|
||||
for (var i = 0; i < all.length; i++) {
|
||||
@@ -158,8 +126,7 @@ iD.Map = function() {
|
||||
loc: iD.util.geo.interp(way.nodes[i].loc, way.nodes[i + 1].loc, 0.5),
|
||||
way: way.id,
|
||||
index: i + 1,
|
||||
accuracy: true,
|
||||
tags: { name: 'Improve way accuracy' }
|
||||
accuracy: true
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -171,30 +138,32 @@ iD.Map = function() {
|
||||
.filter(filter)
|
||||
.data(waynodes, key);
|
||||
function olderOnTop(a, b) {
|
||||
return (+a.id.slice(1)) - (+b.id.slice(1));
|
||||
return a.osmId() - b.osmId();
|
||||
}
|
||||
handles.exit().remove();
|
||||
handles.enter().append('image')
|
||||
.attr({ width: 6, height: 6, 'class': 'handle', 'xlink:href': 'css/handle.png' })
|
||||
.each(function(d) {
|
||||
if (d.tags && d.tags.elastic) return;
|
||||
d3.select(this).call(dragbehavior);
|
||||
.attr({
|
||||
width: 6,
|
||||
height: 6,
|
||||
'class': 'handle',
|
||||
'xlink:href': 'css/handle.png'
|
||||
});
|
||||
handles.attr('transform', function(entity) {
|
||||
var p = projection(entity.loc);
|
||||
return 'translate(' + [~~p[0], ~~p[1]] + ') translate(-3, -3) rotate(45, 3, 3)';
|
||||
return 'translate(' + [~~p[0], ~~p[1]] +
|
||||
') translate(-3, -3) rotate(45, 3, 3)';
|
||||
})
|
||||
.classed('active', classActive)
|
||||
.sort(olderOnTop);
|
||||
}
|
||||
|
||||
function drawAccuracyHandles(waynodes) {
|
||||
function drawAccuracyHandles(waynodes, filter) {
|
||||
var handles = g.hit.selectAll('circle.accuracy-handle')
|
||||
.data(waynodes, key);
|
||||
.filter(filter)
|
||||
.data(waynodes, function (d) { return [d.way, d.index].join(","); });
|
||||
handles.exit().remove();
|
||||
handles.enter().append('circle')
|
||||
.attr({ r: 2, 'class': 'accuracy-handle' })
|
||||
.call(dragbehavior);
|
||||
.attr({ r: 2, 'class': 'accuracy-handle' });
|
||||
handles.attr('transform', function(entity) {
|
||||
var p = projection(entity.loc);
|
||||
return 'translate(' + [~~p[0], ~~p[1]] + ')';
|
||||
@@ -236,8 +205,7 @@ iD.Map = function() {
|
||||
.data(points, key);
|
||||
markers.exit().remove();
|
||||
var marker = markers.enter().append('g')
|
||||
.attr('class', 'marker')
|
||||
.call(dragbehavior);
|
||||
.attr('class', 'marker');
|
||||
marker.append('circle')
|
||||
.attr({ r: 10, cx: 8, cy: 8 });
|
||||
marker.append('image')
|
||||
@@ -282,14 +250,14 @@ iD.Map = function() {
|
||||
|
||||
function connectionLoad(err, result) {
|
||||
history.merge(result);
|
||||
drawVector(iD.util.trueObj(Object.keys(result.entities)));
|
||||
drawVector(Object.keys(result.entities));
|
||||
}
|
||||
|
||||
function hoverIn() {
|
||||
var datum = d3.select(d3.event.target).datum();
|
||||
if (datum instanceof iD.Entity) {
|
||||
hover = datum.id;
|
||||
drawVector(iD.util.trueObj([hover]));
|
||||
drawVector([hover]);
|
||||
d3.select('.messages').text(datum.tags.name || '#' + datum.id);
|
||||
}
|
||||
}
|
||||
@@ -298,7 +266,7 @@ iD.Map = function() {
|
||||
if (hover) {
|
||||
var oldHover = hover;
|
||||
hover = null;
|
||||
drawVector(iD.util.trueObj([oldHover]));
|
||||
drawVector([oldHover]);
|
||||
d3.select('.messages').text('');
|
||||
}
|
||||
}
|
||||
@@ -340,14 +308,12 @@ iD.Map = function() {
|
||||
redraw();
|
||||
}
|
||||
|
||||
function redraw() {
|
||||
if (!dragging) {
|
||||
dispatch.move(map);
|
||||
tilegroup.call(background);
|
||||
}
|
||||
function redraw(difference) {
|
||||
dispatch.move(map);
|
||||
tilegroup.call(background);
|
||||
if (map.zoom() > 16) {
|
||||
connection.loadTiles(projection);
|
||||
drawVector(dragging);
|
||||
drawVector(difference);
|
||||
} else {
|
||||
hideVector();
|
||||
}
|
||||
@@ -376,12 +342,6 @@ iD.Map = function() {
|
||||
return map;
|
||||
};
|
||||
|
||||
map.dragEnable = function(_) {
|
||||
if (!arguments.length) return dragEnabled;
|
||||
dragEnabled = _;
|
||||
return map;
|
||||
};
|
||||
|
||||
map.fastEnable = function(_) {
|
||||
if (!arguments.length) return fastEnabled;
|
||||
fastEnabled = _;
|
||||
|
||||
@@ -50,7 +50,7 @@ iD.Style.markerimage = function(d) {
|
||||
|
||||
iD.Style.TAG_CLASSES = iD.util.trueObj([
|
||||
'highway', 'railway', 'motorway', 'amenity', 'natural',
|
||||
'landuse', 'building', 'oneway', 'bridge', 'elastic'
|
||||
'landuse', 'building', 'oneway', 'bridge'
|
||||
]);
|
||||
|
||||
iD.Style.styleClasses = function(pre) {
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ iD.commit = function() {
|
||||
header.append('p').text('the changes you upload will be visible on all maps using OpenStreetMap data');
|
||||
|
||||
var section = body.selectAll('div.commit-section')
|
||||
.data(['modify', 'delete', 'create'].filter(function(d) {
|
||||
.data(['modified', 'deleted', 'created'].filter(function(d) {
|
||||
return changes[d].length;
|
||||
}))
|
||||
.enter()
|
||||
|
||||
+12
-5
@@ -1,5 +1,5 @@
|
||||
iD.Inspector = function() {
|
||||
var event = d3.dispatch('changeTags', 'changeWayDirection', 'update', 'remove', 'close'),
|
||||
var event = d3.dispatch('changeTags', 'changeWayDirection', 'update', 'remove', 'close', 'splitWay'),
|
||||
taginfo = iD.taginfo();
|
||||
|
||||
function drawhead(selection) {
|
||||
@@ -10,7 +10,7 @@ iD.Inspector = function() {
|
||||
.attr('class', 'permalink')
|
||||
.attr('href', function(d) {
|
||||
return 'http://www.openstreetmap.org/browse/' +
|
||||
d.type + '/' + d.id.slice(1);
|
||||
d.type + '/' + d.osmId();
|
||||
})
|
||||
.text('View on OSM');
|
||||
selection.append('a')
|
||||
@@ -32,9 +32,16 @@ iD.Inspector = function() {
|
||||
.attr('href', '#')
|
||||
.text('Reverse Direction')
|
||||
.on('click', function(d) {
|
||||
event.changeWayDirection(iD.Entity(d, {
|
||||
nodes: _.pluck(d.nodes.reverse(), 'id')
|
||||
}));
|
||||
event.changeWayDirection(iD.Entity(d));
|
||||
});
|
||||
}
|
||||
if (selection.datum().type === 'node' && !selection.datum()._poi) {
|
||||
selection.append('a')
|
||||
.attr('class', 'permalink')
|
||||
.attr('href', '#')
|
||||
.text('Split Way')
|
||||
.on('click', function(d) {
|
||||
event.splitWay(iD.Entity(d));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ iD.userpanel = function(connection) {
|
||||
function update() {
|
||||
selection.html('');
|
||||
if (connection.authenticated()) {
|
||||
selection.style('display', 'block');
|
||||
connection.userDetails(function(user_details) {
|
||||
selection.append('span')
|
||||
.text('signed in as ')
|
||||
@@ -24,12 +25,7 @@ iD.userpanel = function(connection) {
|
||||
});
|
||||
});
|
||||
} else {
|
||||
selection
|
||||
.append('a')
|
||||
.attr('class', 'login')
|
||||
.attr('href', '#')
|
||||
.text('login')
|
||||
.on('click', event.login);
|
||||
selection.html('').style('display', 'none');
|
||||
}
|
||||
}
|
||||
connection.on('auth', update);
|
||||
|
||||
+14
-8
@@ -1,13 +1,5 @@
|
||||
iD.util = {};
|
||||
|
||||
iD.util._counters = {};
|
||||
|
||||
iD.util.id = function(counter) {
|
||||
counter = counter || 'default';
|
||||
if (!iD.util._counters[counter]) iD.util._counters[counter] = 0;
|
||||
return counter[0] + (--iD.util._counters[counter]);
|
||||
};
|
||||
|
||||
iD.util.trueObj = function(arr) {
|
||||
var o = {};
|
||||
for (var i = 0, l = arr.length; i < l; i++) o[arr[i]] = true;
|
||||
@@ -74,6 +66,20 @@ iD.util.prefixProperty = function(property) {
|
||||
})(prefixes);
|
||||
};
|
||||
|
||||
iD.util.support3d = function() {
|
||||
// test for translate3d support. Based on https://gist.github.com/3794226 by lorenzopolidori and webinista
|
||||
var transformProp = iD.util.prefixProperty('Transform');
|
||||
var el = document.createElement('div'),
|
||||
has3d = false;
|
||||
document.body.insertBefore(el, null);
|
||||
if (el.style[transformProp] !== undefined) {
|
||||
el.style[transformProp] = 'translate3d(1px,1px,1px)';
|
||||
has3d = window.getComputedStyle(el).getPropertyValue(transformProp);
|
||||
}
|
||||
document.body.removeChild(el);
|
||||
return (has3d && has3d.length>0 && has3d!=="none");
|
||||
};
|
||||
|
||||
iD.util.geo = {};
|
||||
|
||||
iD.util.geo.roundCoords = function(c) {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
d3.latedrag = function() {
|
||||
var filter = d3.functor(true);
|
||||
|
||||
function latedrag(selection) {
|
||||
var mousedown = selection.on('mousedown.drag');
|
||||
selection.on('mousedown.drag', null);
|
||||
selection.on('mousedown.latedrag', function() {
|
||||
var datum = d3.select(d3.event.target).datum();
|
||||
if (datum && filter(datum)) {
|
||||
mousedown.apply(selection.node(), [datum]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
latedrag.filter = function(_) {
|
||||
if (!arguments.length) return filter;
|
||||
filter = _;
|
||||
return latedrag;
|
||||
};
|
||||
|
||||
return latedrag;
|
||||
};
|
||||
@@ -0,0 +1,84 @@
|
||||
(function() {
|
||||
if (typeof module === "undefined") self.queue = queue;
|
||||
else module.exports = queue;
|
||||
|
||||
queue.version = "1.0.0";
|
||||
|
||||
function queue(parallelism) {
|
||||
var queue = {},
|
||||
active = 0, // number of in-flight deferrals
|
||||
remaining = 0, // number of deferrals remaining
|
||||
head, tail, // singly-linked list of deferrals
|
||||
error = null,
|
||||
results = [],
|
||||
await = noop,
|
||||
awaitAll;
|
||||
|
||||
if (arguments.length < 1) parallelism = Infinity;
|
||||
|
||||
queue.defer = function() {
|
||||
if (!error) {
|
||||
var node = arguments;
|
||||
node.index = results.push(undefined) - 1;
|
||||
if (tail) tail.next = node, tail = tail.next;
|
||||
else head = tail = node;
|
||||
++remaining;
|
||||
pop();
|
||||
}
|
||||
return queue;
|
||||
};
|
||||
|
||||
queue.await = function(f) {
|
||||
await = f;
|
||||
awaitAll = false;
|
||||
if (!remaining) notify();
|
||||
return queue;
|
||||
};
|
||||
|
||||
queue.awaitAll = function(f) {
|
||||
await = f;
|
||||
awaitAll = true;
|
||||
if (!remaining) notify();
|
||||
return queue;
|
||||
};
|
||||
|
||||
function pop() {
|
||||
if (head && active < parallelism) {
|
||||
var node = head,
|
||||
f = node[0],
|
||||
a = Array.prototype.slice.call(node, 1),
|
||||
i = node.index;
|
||||
if (head === tail) head = tail = null;
|
||||
else head = head.next;
|
||||
++active;
|
||||
a.push(function(e, r) {
|
||||
--active;
|
||||
if (error != null) return;
|
||||
if (e != null) {
|
||||
// clearing remaining cancels subsequent callbacks
|
||||
// clearing head stops queued tasks from being executed
|
||||
// setting error ignores subsequent calls to defer
|
||||
error = e;
|
||||
remaining = results = head = tail = null;
|
||||
notify();
|
||||
} else {
|
||||
results[i] = r;
|
||||
if (--remaining) pop();
|
||||
else notify();
|
||||
}
|
||||
});
|
||||
f.apply(null, a);
|
||||
}
|
||||
}
|
||||
|
||||
function notify() {
|
||||
if (error != null) await(error);
|
||||
else if (awaitAll) await(null, results);
|
||||
else await.apply(null, [null].concat(results));
|
||||
}
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
function noop() {}
|
||||
})();
|
||||
+9
-1
@@ -45,6 +45,7 @@
|
||||
|
||||
<script src='../js/id/actions.js'></script>
|
||||
<script src='../js/id/actions/add_node.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>
|
||||
<script src="../js/id/actions/delete_node.js"></script>
|
||||
@@ -54,7 +55,6 @@
|
||||
<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/start_way.js'></script>
|
||||
|
||||
<script src='../js/id/modes.js'></script>
|
||||
<script src='../js/id/modes/add_area.js'></script>
|
||||
@@ -83,10 +83,18 @@
|
||||
</script>
|
||||
|
||||
<!-- include spec files here... -->
|
||||
<script src="spec/actions/add_node.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.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/format/geojson.js"></script>
|
||||
<script src="spec/format/xml.js"></script>
|
||||
<script src="spec/graph/graph.js"></script>
|
||||
|
||||
@@ -25,10 +25,18 @@
|
||||
</script>
|
||||
|
||||
<!-- include spec files here... -->
|
||||
<script src="spec/actions/add_node.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.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/format/geojson.js"></script>
|
||||
<script src="spec/format/xml.js"></script>
|
||||
<script src="spec/graph/graph.js"></script>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
describe("iD.actions.AddNode", function () {
|
||||
it("adds a node to the graph", function () {
|
||||
var node = iD.Node(),
|
||||
graph = iD.actions.AddNode(node)(iD.Graph());
|
||||
expect(graph.entity(node.id)).to.equal(node);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
describe("iD.actions.AddWay", function () {
|
||||
it("adds a way to the graph", function () {
|
||||
var way = iD.Way(),
|
||||
graph = iD.actions.AddWay(way)(iD.Graph());
|
||||
expect(graph.entity(way.id)).to.equal(way);
|
||||
});
|
||||
});
|
||||
@@ -2,14 +2,28 @@ 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, node)(iD.Graph());
|
||||
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 the specified index", function () {
|
||||
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, node, 1)(iD.Graph());
|
||||
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"]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
describe("iD.actions.ChangeEntityTags", function () {
|
||||
it("changes an entity's tags", function () {
|
||||
var entity = iD.Entity(),
|
||||
tags = {foo: 'bar'},
|
||||
graph = iD.actions.ChangeEntityTags(entity.id, tags)(iD.Graph([entity]));
|
||||
expect(graph.entity(entity.id).tags).to.eql(tags);
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
describe("iD.actions.DeleteNode", function () {
|
||||
it("removes the node from the graph", function () {
|
||||
var node = iD.Node(),
|
||||
action = iD.actions.DeleteNode(node),
|
||||
action = iD.actions.DeleteNode(node.id),
|
||||
graph = action(iD.Graph([node]));
|
||||
expect(graph.entity(node.id)).to.be.undefined;
|
||||
});
|
||||
@@ -9,15 +9,15 @@ describe("iD.actions.DeleteNode", function () {
|
||||
it("removes the node from parent ways", function () {
|
||||
var node = iD.Node(),
|
||||
way = iD.Way({nodes: [node.id]}),
|
||||
action = iD.actions.DeleteNode(node),
|
||||
action = iD.actions.DeleteNode(node.id),
|
||||
graph = action(iD.Graph([node, way]));
|
||||
expect(graph.entity(way.id).nodes).not.to.contain(node.id);
|
||||
});
|
||||
|
||||
it("removes the node from parent relations", function () {
|
||||
var node = iD.Node(),
|
||||
relation = iD.Relation({members: [node.id]}),
|
||||
action = iD.actions.DeleteNode(node),
|
||||
relation = iD.Relation({members: [{ id: node.id }]}),
|
||||
action = iD.actions.DeleteNode(node.id),
|
||||
graph = action(iD.Graph([node, relation]));
|
||||
expect(graph.entity(relation.id).members).not.to.contain(node.id);
|
||||
});
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
describe("iD.actions.DeleteWay", function () {
|
||||
it("removes the way from the graph", function () {
|
||||
var way = iD.Way(),
|
||||
action = iD.actions.DeleteWay(way),
|
||||
action = iD.actions.DeleteWay(way.id),
|
||||
graph = action(iD.Graph([way]));
|
||||
expect(graph.entity(way.id)).to.be.undefined;
|
||||
});
|
||||
|
||||
it("removes a way from parent relations", function () {
|
||||
var way = iD.Way(),
|
||||
relation = iD.Relation({members: [way.id]}),
|
||||
action = iD.actions.DeleteWay(way),
|
||||
relation = iD.Relation({members: [{ id: way.id }]}),
|
||||
action = iD.actions.DeleteWay(way.id),
|
||||
graph = action(iD.Graph([way, relation]));
|
||||
expect(graph.entity(relation.id).members).not.to.contain(way.id);
|
||||
expect(_.pluck(graph.entity(relation.id).members, 'id')).not.to.contain(way.id);
|
||||
});
|
||||
|
||||
it("deletes member nodes not referenced by another parent", function () {
|
||||
var node = iD.Node(),
|
||||
way = iD.Way({nodes: [node.id]}),
|
||||
action = iD.actions.DeleteWay(way),
|
||||
action = iD.actions.DeleteWay(way.id),
|
||||
graph = action(iD.Graph([node, way]));
|
||||
expect(graph.entity(node.id)).to.be.undefined;
|
||||
});
|
||||
@@ -26,7 +26,7 @@ describe("iD.actions.DeleteWay", function () {
|
||||
var node = iD.Node(),
|
||||
way1 = iD.Way({nodes: [node.id]}),
|
||||
way2 = iD.Way({nodes: [node.id]}),
|
||||
action = iD.actions.DeleteWay(way1),
|
||||
action = iD.actions.DeleteWay(way1.id),
|
||||
graph = action(iD.Graph([node, way1, way2]));
|
||||
expect(graph.entity(node.id)).not.to.be.undefined;
|
||||
});
|
||||
@@ -34,7 +34,7 @@ describe("iD.actions.DeleteWay", function () {
|
||||
it("does not delete member nodes with interesting tags", function () {
|
||||
var node = iD.Node({tags: {highway: 'traffic_signals'}}),
|
||||
way = iD.Way({nodes: [node.id]}),
|
||||
action = iD.actions.DeleteWay(way),
|
||||
action = iD.actions.DeleteWay(way.id),
|
||||
graph = action(iD.Graph([node, way]));
|
||||
expect(graph.entity(node.id)).not.to.be.undefined;
|
||||
});
|
||||
@@ -42,7 +42,7 @@ describe("iD.actions.DeleteWay", function () {
|
||||
it("registers member nodes with interesting tags as POIs", function () {
|
||||
var node = iD.Node({tags: {highway: 'traffic_signals'}}),
|
||||
way = iD.Way({nodes: [node.id]}),
|
||||
action = iD.actions.DeleteWay(way),
|
||||
action = iD.actions.DeleteWay(way.id),
|
||||
graph = action(iD.Graph([node, way]));
|
||||
expect(graph.entity(node.id)._poi).to.be.ok;
|
||||
});
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
describe("iD.actions.Move", function () {
|
||||
it("changes an entity's location", function () {
|
||||
var entity = iD.Entity(),
|
||||
loc = [2, 3],
|
||||
graph = iD.actions.Move(entity.id, loc)(iD.Graph([entity]));
|
||||
expect(graph.entity(entity.id).loc).to.eql(loc);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
describe("iD.actions.Noop", function () {
|
||||
it("does nothing", function () {
|
||||
var graph = iD.Graph(),
|
||||
action = iD.actions.Noop(graph);
|
||||
expect(action(graph)).to.equal(graph);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
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([]);
|
||||
});
|
||||
});
|
||||
@@ -2,7 +2,7 @@ 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, node)(iD.Graph({n1: node, w1: way}));
|
||||
graph = iD.actions.RemoveWayNode(way.id, node.id)(iD.Graph({n1: node, w1: way}));
|
||||
expect(graph.entity(way.id).nodes).to.eql([]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
describe("iD.actions.ReverseWay", function () {
|
||||
it("reverses the order of nodes in the way", function () {
|
||||
var node1 = iD.Node(),
|
||||
node2 = iD.Node(),
|
||||
way = iD.Way({nodes: [node1.id, node2.id]}),
|
||||
graph = iD.actions.ReverseWay(way.id)(iD.Graph([node1, node2, way]));
|
||||
expect(graph.entity(way.id).nodes).to.eql([node2.id, node1.id]);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
describe('Entity', function () {
|
||||
describe('iD.Entity', function () {
|
||||
if (iD.debug) {
|
||||
it("is frozen", function () {
|
||||
expect(Object.isFrozen(iD.Entity())).to.be.true;
|
||||
@@ -13,6 +13,24 @@ describe('Entity', function () {
|
||||
});
|
||||
}
|
||||
|
||||
describe(".id", function () {
|
||||
it("generates unique IDs", function () {
|
||||
expect(iD.Entity.id('node')).not.to.equal(iD.Entity.id('node'));
|
||||
});
|
||||
|
||||
describe(".fromOSM", function () {
|
||||
it("returns a ID string unique across entity types", function () {
|
||||
expect(iD.Entity.id.fromOSM('node', 1)).to.equal("n1");
|
||||
});
|
||||
});
|
||||
|
||||
describe(".toOSM", function () {
|
||||
it("reverses fromOSM", function () {
|
||||
expect(iD.Entity.id.toOSM(iD.Entity.id.fromOSM('node', 1))).to.equal(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#update", function () {
|
||||
it("returns a new Entity", function () {
|
||||
var a = iD.Entity(),
|
||||
@@ -105,7 +123,7 @@ describe('Entity', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Node', function () {
|
||||
describe('iD.Node', function () {
|
||||
it("returns a node", function () {
|
||||
expect(iD.Node().type).to.equal("node");
|
||||
});
|
||||
@@ -138,7 +156,7 @@ describe('Node', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Way', function () {
|
||||
describe('iD.Way', function () {
|
||||
if (iD.debug) {
|
||||
it("freezes nodes", function () {
|
||||
expect(Object.isFrozen(iD.Way().nodes)).to.be.true;
|
||||
@@ -191,7 +209,7 @@ describe('Way', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Relation', function () {
|
||||
describe('iD.Relation', function () {
|
||||
if (iD.debug) {
|
||||
it("freezes nodes", function () {
|
||||
expect(Object.isFrozen(iD.Relation().members)).to.be.true;
|
||||
|
||||
+87
-47
@@ -11,11 +11,6 @@ describe('iD.Graph', function() {
|
||||
expect(graph.entity(entity.id)).to.equal(entity);
|
||||
});
|
||||
|
||||
it('can be constructed with an annotation', function() {
|
||||
var graph = iD.Graph({}, 'first graph');
|
||||
expect(graph.annotation).to.equal('first graph');
|
||||
});
|
||||
|
||||
if (iD.debug) {
|
||||
it("is frozen", function () {
|
||||
expect(Object.isFrozen(iD.Graph())).to.be.true;
|
||||
@@ -26,35 +21,46 @@ describe('iD.Graph', function() {
|
||||
});
|
||||
}
|
||||
|
||||
describe('operations', function() {
|
||||
it('#remove', function() {
|
||||
var entities = { 'n-1': {
|
||||
type: 'node',
|
||||
loc: [-80, 30],
|
||||
id: 'n-1'
|
||||
}
|
||||
};
|
||||
var graph = iD.Graph(entities, 'first graph');
|
||||
var g2 = graph.remove(entities['n-1'], 'Removed node');
|
||||
expect(graph.entity('n-1')).to.equal(entities['n-1']);
|
||||
expect(g2.entity('n-1')).to.equal(undefined);
|
||||
describe("#remove", function () {
|
||||
it("returns a new graph", function () {
|
||||
var node = iD.Node(),
|
||||
graph = iD.Graph([node]);
|
||||
expect(graph.remove(node)).not.to.equal(graph);
|
||||
});
|
||||
it('#replace', function() {
|
||||
var entities = { 'n-1': {
|
||||
type: 'node',
|
||||
loc: [-80, 30],
|
||||
id: 'n-1'
|
||||
}
|
||||
};
|
||||
var replacement = {
|
||||
type: 'node',
|
||||
loc: [-80, 40],
|
||||
id: 'n-1'
|
||||
};
|
||||
var graph = iD.Graph(entities, 'first graph');
|
||||
var g2 = graph.replace(replacement, 'Removed node');
|
||||
expect(graph.entity('n-1').loc[1]).to.equal(30);
|
||||
expect(g2.entity('n-1').loc[1]).to.equal(40);
|
||||
|
||||
it("doesn't modify the receiver", function () {
|
||||
var node = iD.Node(),
|
||||
graph = iD.Graph([node]);
|
||||
graph.remove(node);
|
||||
expect(graph.entity(node.id)).to.equal(node);
|
||||
});
|
||||
|
||||
it("removes the entity from the result", function () {
|
||||
var node = iD.Node(),
|
||||
graph = iD.Graph([node]);
|
||||
expect(graph.remove(node).entity(node.id)).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
describe("#replace", function () {
|
||||
it("returns a new graph", function () {
|
||||
var node = iD.Node(),
|
||||
graph = iD.Graph([node]);
|
||||
expect(graph.replace(node)).not.to.equal(graph);
|
||||
});
|
||||
|
||||
it("doesn't modify the receiver", function () {
|
||||
var node = iD.Node(),
|
||||
graph = iD.Graph([node]);
|
||||
graph.replace(node);
|
||||
expect(graph.entity(node.id)).to.equal(node);
|
||||
});
|
||||
|
||||
it("replaces the entity in the result", function () {
|
||||
var node1 = iD.Node(),
|
||||
node2 = node1.update({}),
|
||||
graph = iD.Graph([node1]);
|
||||
expect(graph.replace(node2).entity(node2.id)).to.equal(node2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -71,7 +77,7 @@ describe('iD.Graph', function() {
|
||||
describe("#parentRelations", function() {
|
||||
it("returns an array of relations that contain the given entity id", function () {
|
||||
var node = iD.Node({id: "n1"}),
|
||||
relation = iD.Relation({id: "r1", members: ["n1"]}),
|
||||
relation = iD.Relation({id: "r1", members: [{ id: "n1", role: 'from' }]}),
|
||||
graph = iD.Graph({n1: node, r1: relation});
|
||||
expect(graph.parentRelations("n1")).to.eql([relation]);
|
||||
expect(graph.parentRelations("n2")).to.eql([]);
|
||||
@@ -83,25 +89,59 @@ describe('iD.Graph', function() {
|
||||
var node = iD.Node({id: "n1"}),
|
||||
way = iD.Way({id: "w1", nodes: ["n1"]}),
|
||||
graph = iD.Graph({n1: node, w1: way});
|
||||
expect(graph.fetch("w1").nodes[0].id).to.equal("n1");
|
||||
expect(graph.fetch("w1").nodes).to.eql([node]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#modifications", function () {
|
||||
it("filters entities by modified", function () {
|
||||
var a = {id: 'a', modified: function () { return true; }},
|
||||
b = {id: 'b', modified: function () { return false; }},
|
||||
graph = iD.Graph({ 'a': a, 'b': b });
|
||||
expect(graph.modifications()).to.eql([graph.fetch('a')]);
|
||||
describe("#difference", function () {
|
||||
it("returns an Array of ids of changed entities", function () {
|
||||
var initial = iD.Node({id: "n1"}),
|
||||
updated = initial.update({}),
|
||||
created = iD.Node(),
|
||||
deleted = iD.Node({id: 'n2'}),
|
||||
graph1 = iD.Graph([initial, deleted]),
|
||||
graph2 = graph1.replace(updated).replace(created).remove(deleted);
|
||||
expect(graph2.difference(graph1)).to.eql([created.id, updated.id, deleted.id]);
|
||||
});
|
||||
|
||||
it("includes created entities that were subsequently deleted", function () {
|
||||
var node = iD.Node(),
|
||||
graph1 = iD.Graph([node]),
|
||||
graph2 = graph1.remove(node);
|
||||
expect(graph2.difference(graph1)).to.eql([node.id]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#creations", function () {
|
||||
it("filters entities by created", function () {
|
||||
var a = {id: 'a', created: function () { return true; }},
|
||||
b = {id: 'b', created: function () { return false; }},
|
||||
graph = iD.Graph({ 'a': a, 'b': b });
|
||||
expect(graph.creations()).to.eql([graph.fetch('a')]);
|
||||
describe("#modified", function () {
|
||||
it("returns an Array of ids of modified entities", function () {
|
||||
var node1 = iD.Node({id: 'n1', _updated: true}),
|
||||
node2 = iD.Node({id: 'n2'}),
|
||||
graph = iD.Graph([node1, node2]);
|
||||
expect(graph.modified()).to.eql([node1.id]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#created", function () {
|
||||
it("returns an Array of ids of created entities", function () {
|
||||
var node1 = iD.Node({id: 'n-1', _updated: true}),
|
||||
node2 = iD.Node({id: 'n2'}),
|
||||
graph = iD.Graph([node1, node2]);
|
||||
expect(graph.created()).to.eql([node1.id]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#deleted", function () {
|
||||
it("returns an Array of ids of deleted entities", function () {
|
||||
var node1 = iD.Node({id: "n1"}),
|
||||
node2 = iD.Node(),
|
||||
graph = iD.Graph([node1, node2]).remove(node1);
|
||||
expect(graph.deleted()).to.eql([node1.id]);
|
||||
});
|
||||
|
||||
it("doesn't include created entities that were subsequently deleted", function () {
|
||||
var node = iD.Node(),
|
||||
graph = iD.Graph([node]).remove(node);
|
||||
expect(graph.deleted()).to.eql([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+75
-18
@@ -1,7 +1,6 @@
|
||||
describe("History", function () {
|
||||
describe("iD.History", function () {
|
||||
var history, spy,
|
||||
graph = iD.Graph([], "action"),
|
||||
action = function() { return graph; };
|
||||
action = function() { return iD.Graph(); };
|
||||
|
||||
beforeEach(function () {
|
||||
history = iD.History();
|
||||
@@ -16,46 +15,79 @@ describe("History", function () {
|
||||
|
||||
describe("#perform", function () {
|
||||
it("updates the graph", function () {
|
||||
history.perform(action);
|
||||
var graph = iD.Graph();
|
||||
history.perform(d3.functor(graph));
|
||||
expect(history.graph()).to.equal(graph);
|
||||
});
|
||||
|
||||
it("pushes the undo stack", function () {
|
||||
history.perform(action);
|
||||
expect(history.undoAnnotation()).to.equal("action");
|
||||
it("pushes an undo annotation", function () {
|
||||
history.perform(action, "annotation");
|
||||
expect(history.undoAnnotation()).to.equal("annotation");
|
||||
});
|
||||
|
||||
it("emits a change event", function () {
|
||||
history.on('change', spy);
|
||||
history.perform(action);
|
||||
expect(spy).to.have.been.called;
|
||||
expect(spy).to.have.been.calledWith([]);
|
||||
});
|
||||
|
||||
it("does not emit a change event when performing a noop", function () {
|
||||
it("performs multiple actions", function () {
|
||||
var action1 = sinon.stub().returns(iD.Graph()),
|
||||
action2 = sinon.stub().returns(iD.Graph());
|
||||
history.perform(action1, action2, "annotation");
|
||||
expect(action1).to.have.been.called;
|
||||
expect(action2).to.have.been.called;
|
||||
expect(history.undoAnnotation()).to.equal("annotation");
|
||||
});
|
||||
});
|
||||
|
||||
describe("#replace", function () {
|
||||
it("updates the graph", function () {
|
||||
var graph = iD.Graph();
|
||||
history.replace(d3.functor(graph));
|
||||
expect(history.graph()).to.equal(graph);
|
||||
});
|
||||
|
||||
it("replaces the undo annotation", function () {
|
||||
history.perform(action, "annotation1");
|
||||
history.replace(action, "annotation2");
|
||||
expect(history.undoAnnotation()).to.equal("annotation2");
|
||||
});
|
||||
|
||||
it("emits a change event", function () {
|
||||
history.on('change', spy);
|
||||
history.perform(iD.actions.Noop);
|
||||
expect(spy).not.to.have.been.called;
|
||||
history.replace(action);
|
||||
expect(spy).to.have.been.calledWith([]);
|
||||
});
|
||||
|
||||
it("performs multiple actions", function () {
|
||||
var action1 = sinon.stub().returns(iD.Graph()),
|
||||
action2 = sinon.stub().returns(iD.Graph());
|
||||
history.replace(action1, action2, "annotation");
|
||||
expect(action1).to.have.been.called;
|
||||
expect(action2).to.have.been.called;
|
||||
expect(history.undoAnnotation()).to.equal("annotation");
|
||||
});
|
||||
});
|
||||
|
||||
describe("#undo", function () {
|
||||
it("pops the undo stack", function () {
|
||||
history.perform(action);
|
||||
history.perform(action, "annotation");
|
||||
history.undo();
|
||||
expect(history.undoAnnotation()).to.be.undefined;
|
||||
});
|
||||
|
||||
it("pushes the redo stack", function () {
|
||||
history.perform(action);
|
||||
history.perform(action, "annotation");
|
||||
history.undo();
|
||||
expect(history.redoAnnotation()).to.equal("action");
|
||||
expect(history.redoAnnotation()).to.equal("annotation");
|
||||
});
|
||||
|
||||
it("emits a change event", function () {
|
||||
history.perform(action);
|
||||
history.on('change', spy);
|
||||
history.undo();
|
||||
expect(spy).to.have.been.called;
|
||||
expect(spy).to.have.been.calledWith([]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -65,14 +97,39 @@ describe("History", function () {
|
||||
history.undo();
|
||||
history.on('change', spy);
|
||||
history.redo();
|
||||
expect(spy).to.have.been.called;
|
||||
expect(spy).to.have.been.calledWith([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#changes", function () {
|
||||
it("includes created entities", function () {
|
||||
var node = iD.Node();
|
||||
history.perform(function (graph) { return graph.replace(node); });
|
||||
expect(history.changes().created).to.eql([node]);
|
||||
});
|
||||
|
||||
it("includes modified entities", function () {
|
||||
var node1 = iD.Node({id: "n1"}),
|
||||
node2 = node1.update({}),
|
||||
graph = iD.Graph([node1]);
|
||||
history.merge(graph);
|
||||
history.perform(function (graph) { return graph.replace(node2); });
|
||||
expect(history.changes().modified).to.eql([node2]);
|
||||
});
|
||||
|
||||
it("includes deleted entities", function () {
|
||||
var node = iD.Node({id: "n1"}),
|
||||
graph = iD.Graph([node]);
|
||||
history.merge(graph);
|
||||
history.perform(function (graph) { return graph.remove(node); });
|
||||
expect(history.changes().deleted).to.eql([node]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#reset", function () {
|
||||
it("clears the version stack", function () {
|
||||
history.perform(action);
|
||||
history.perform(action);
|
||||
history.perform(action, "annotation");
|
||||
history.perform(action, "annotation");
|
||||
history.undo();
|
||||
history.reset();
|
||||
expect(history.undoAnnotation()).to.be.undefined;
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
describe('Util', function() {
|
||||
var util;
|
||||
|
||||
it('#id', function() {
|
||||
var a = iD.util.id(),
|
||||
b = iD.util.id(),
|
||||
c = iD.util.id(),
|
||||
d = iD.util.id();
|
||||
expect(a === b).to.equal(false);
|
||||
expect(b === c).to.equal(false);
|
||||
expect(c === d).to.equal(false);
|
||||
});
|
||||
|
||||
it('#trueObj', function() {
|
||||
expect(iD.util.trueObj(['a', 'b', 'c'])).to.eql({ a: true, b: true, c: true });
|
||||
expect(iD.util.trueObj([])).to.eql({});
|
||||
|
||||
Reference in New Issue
Block a user