Merge branch 'master' of github.com:systemed/iD

This commit is contained in:
Tom MacWright
2012-12-07 14:03:08 -05:00
6 changed files with 218 additions and 106 deletions
+3 -3
View File
@@ -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;
+49 -17
View File
@@ -24,14 +24,14 @@ 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' && e.members.indexOf(id) !== -1;
});
},
@@ -49,7 +49,13 @@ iD.Graph.prototype = {
remove: function(entity) {
var entities = _.clone(this.entities);
delete entities[entity.id];
if (entity.created()) {
delete entities[entity.id];
} else {
entities[entity.id] = undefined;
}
return iD.Graph(entities);
},
@@ -58,7 +64,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));
}
}
@@ -68,26 +74,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 entity;
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;
}
};
+30 -29
View File
@@ -19,10 +19,8 @@ iD.History = function() {
return {graph: graph, annotation: annotation};
}
function maybeChange() {
if (stack[index].annotation) {
dispatch.change();
}
function change(previous) {
dispatch.change(history.graph().difference(previous));
}
var history = {
@@ -37,32 +35,44 @@ iD.History = function() {
},
perform: function () {
var previous = stack[index].graph;
stack = stack.slice(0, index + 1);
stack.push(perform(arguments));
index++;
dispatch.change();
change(previous);
},
replace: function () {
var previous = stack[index].graph;
// assert(index == stack.length - 1)
stack[index] = perform(arguments);
dispatch.change();
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 () {
@@ -81,29 +91,20 @@ iD.History = function() {
}
},
// generate reports of changes for changesets to use
modify: function () {
return stack[index].graph.modifications();
},
create: function () {
return stack[index].graph.creations();
},
'delete': function () {
return _.difference(
_.pluck(stack[0].graph.entities, 'id'),
_.pluck(stack[index].graph.entities, 'id')
).map(function (id) {
return stack[0].graph.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);
})
};
},
+22 -13
View File
@@ -69,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.id] : 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++) {
@@ -151,7 +160,7 @@ iD.Map = function() {
function drawAccuracyHandles(waynodes, filter) {
var handles = g.hit.selectAll('circle.accuracy-handle')
.filter(filter)
.data(waynodes, key);
.data(waynodes, function (d) { return [d.way.id, d.index].join(","); });
handles.exit().remove();
handles.enter().append('circle')
.attr({ r: 2, 'class': 'accuracy-handle' });
@@ -241,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);
}
}
@@ -257,7 +266,7 @@ iD.Map = function() {
if (hover) {
var oldHover = hover;
hover = null;
drawVector(iD.util.trueObj([oldHover]));
drawVector([oldHover]);
d3.select('.messages').text('');
}
}
@@ -299,12 +308,12 @@ iD.Map = function() {
redraw();
}
function redraw() {
function redraw(difference) {
dispatch.move(map);
tilegroup.call(background);
if (map.zoom() > 16) {
connection.loadTiles(projection);
drawVector();
drawVector(difference);
} else {
hideVector();
}
+85 -40
View File
@@ -21,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);
});
});
@@ -82,21 +93,55 @@ describe('iD.Graph', function() {
});
});
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([]);
});
});
});
+29 -4
View File
@@ -28,7 +28,7 @@ describe("iD.History", function () {
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("performs multiple actions", function () {
@@ -57,7 +57,7 @@ describe("iD.History", function () {
it("emits a change event", function () {
history.on('change', spy);
history.replace(action);
expect(spy).to.have.been.called;
expect(spy).to.have.been.calledWith([]);
});
it("performs multiple actions", function () {
@@ -87,7 +87,7 @@ describe("iD.History", function () {
history.perform(action);
history.on('change', spy);
history.undo();
expect(spy).to.have.been.called;
expect(spy).to.have.been.calledWith([]);
});
});
@@ -97,7 +97,32 @@ describe("iD.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]);
});
});