diff --git a/js/id/core/difference.js b/js/id/core/difference.js index a5a29cf3a..9c1a80ca5 100644 --- a/js/id/core/difference.js +++ b/js/id/core/difference.js @@ -9,9 +9,13 @@ iD.Difference = function(base, head) { var changes = {}, length = 0; + function changed(h, b) { + return !_.isEqual(_.omit(h, 'v'), _.omit(b, 'v')); + } + _.each(head.entities, function(h, id) { var b = base.entities[id]; - if (!_.isEqual(h, b)) { + if (changed(h, b)) { changes[id] = {base: b, head: h}; length++; } @@ -19,7 +23,7 @@ iD.Difference = function(base, head) { _.each(base.entities, function(b, id) { var h = head.entities[id]; - if (!changes[id] && !_.isEqual(h, b)) { + if (!changes[id] && changed(h, b)) { changes[id] = {base: b, head: h}; length++; } diff --git a/js/id/core/entity.js b/js/id/core/entity.js index ab6f76bc6..0ec14f70b 100644 --- a/js/id/core/entity.js +++ b/js/id/core/entity.js @@ -31,7 +31,7 @@ iD.Entity.id.type = function(id) { // A function suitable for use as the second argument to d3.selection#data(). iD.Entity.key = function(entity) { - return entity.id; + return entity.id + ',' + entity.v; }; iD.Entity.prototype = { @@ -72,7 +72,7 @@ iD.Entity.prototype = { }, update: function(attrs) { - return iD.Entity(this, attrs); + return iD.Entity(this, attrs, {v: 1 + (this.v || 0)}); }, mergeTags: function(tags) { diff --git a/js/id/svg/areas.js b/js/id/svg/areas.js index 7b40b8441..8f94a3f69 100644 --- a/js/id/svg/areas.js +++ b/js/id/svg/areas.js @@ -58,25 +58,27 @@ iD.svg.Areas = function(projection) { }); function drawPaths(areas, klass, closeWay) { - var tagClasses = iD.svg.TagClasses(); - - if (klass === 'stroke') { - tagClasses.tags(iD.svg.MultipolygonMemberTags(graph)); - } - var paths = surface.select('.layer-' + klass) .selectAll('path.area') .filter(filter) .data(areas, iD.Entity.key); - paths.enter() + var enter = paths.enter() .append('path') .attr('class', function(d) { return d.type + ' area ' + klass + ' ' + d.id; }); + // Optimization: call simple TagClasses only on enter selection. This + // works because iD.Entity.key is defined to include the entity v attribute. + if (klass !== 'stroke') { + enter.call(iD.svg.TagClasses()); + } else { + paths.call(iD.svg.TagClasses() + .tags(iD.svg.MultipolygonMemberTags(graph))); + } + paths .order() .attr('d', function(entity) { return path(entity.asGeoJSON(graph, closeWay)); }) - .call(tagClasses) .call(iD.svg.MemberClasses(graph)); if (klass === 'fill') paths.call(setPattern); diff --git a/js/id/svg/lines.js b/js/id/svg/lines.js index 23a02cedd..789bcb3d7 100644 --- a/js/id/svg/lines.js +++ b/js/id/svg/lines.js @@ -50,25 +50,27 @@ iD.svg.Lines = function(projection) { lines.sort(waystack); function drawPaths(klass) { - var tagClasses = iD.svg.TagClasses(); - - if (klass === 'stroke') { - tagClasses.tags(iD.svg.MultipolygonMemberTags(graph)); - } - var paths = surface.select('.layer-' + klass) .selectAll('path.line') .filter(filter) .data(lines, iD.Entity.key); - paths.enter() + var enter = paths.enter() .append('path') .attr('class', function(d) { return 'way line ' + klass + ' ' + d.id; }); + // Optimization: call simple TagClasses only on enter selection. This + // works because iD.Entity.key is defined to include the entity v attribute. + if (klass !== 'stroke') { + enter.call(iD.svg.TagClasses()); + } else { + paths.call(iD.svg.TagClasses() + .tags(iD.svg.MultipolygonMemberTags(graph))); + } + paths .order() .attr('d', path) - .call(tagClasses) .call(iD.svg.MemberClasses(graph)); paths.exit() diff --git a/js/id/svg/tag_classes.js b/js/id/svg/tag_classes.js index 96b907828..434acc62b 100644 --- a/js/id/svg/tag_classes.js +++ b/js/id/svg/tag_classes.js @@ -19,7 +19,7 @@ iD.svg.TagClasses = function() { var t = tags(entity); for (var k in t) { if (!keys.has(k)) continue; - classes += ' tag-' + k + ' ' + 'tag-' + k + '-' + t[k]; + classes += ' tag-' + k + ' tag-' + k + '-' + t[k]; } classes = classes.trim(); diff --git a/test/spec/core/entity.js b/test/spec/core/entity.js index 8ab310f5d..2190d7125 100644 --- a/test/spec/core/entity.js +++ b/test/spec/core/entity.js @@ -61,6 +61,14 @@ describe('iD.Entity', function () { it("doesn't copy prototype properties", function () { expect(iD.Entity().update({})).not.to.have.ownProperty('update'); }); + + it("sets v to 1 if previously undefined", function() { + expect(iD.Entity().update({}).v).to.equal(1); + }); + + it("increments v", function() { + expect(iD.Entity({v: 1}).update({}).v).to.equal(2); + }); }); describe("#mergeTags", function () {