diff --git a/js/id/graph/graph.js b/js/id/graph/graph.js index f44245385..f5a615852 100644 --- a/js/id/graph/graph.js +++ b/js/id/graph/graph.js @@ -1,5 +1,5 @@ -iD.Graph = function(entities) { - if (!(this instanceof iD.Graph)) return new iD.Graph(entities); +iD.Graph = function(entities, mutable) { + if (!(this instanceof iD.Graph)) return new iD.Graph(entities, mutable); if (_.isArray(entities)) { this.entities = {}; @@ -15,9 +15,8 @@ iD.Graph = function(entities) { this._parentRels = {}; this._fetches = {}; - if (iD.debug) { - Object.freeze(this); - Object.freeze(this.entities); + if (!mutable) { + this.freeze(); } }; @@ -87,27 +86,46 @@ iD.Graph.prototype = { }, merge: function(graph) { - var entities = _.clone(this.entities); - _.defaults(entities, graph.entities); - return iD.Graph(entities); + return this.update(function () { + _.defaults(this.entities, graph.entities); + }); }, replace: function(entity) { - var entities = _.clone(this.entities); - entities[entity.id] = entity; - return iD.Graph(entities); + return this.update(function () { + this.entities[entity.id] = entity; + }); }, remove: function(entity) { - var entities = _.clone(this.entities); + return this.update(function () { + if (entity.created()) { + delete this.entities[entity.id]; + } else { + this.entities[entity.id] = undefined; + } + }); + }, - if (entity.created()) { - delete entities[entity.id]; - } else { - entities[entity.id] = undefined; + update: function() { + var graph = this.frozen ? iD.Graph(_.clone(this.entities), true) : this; + + for (var i = 0; i < arguments.length; i++) { + arguments[i].call(graph, graph); } - return iD.Graph(entities); + return this.frozen ? graph.freeze() : this; + }, + + freeze: function() { + this.frozen = true; + + if (iD.debug) { + Object.freeze(this); + Object.freeze(this.entities); + } + + return this; }, // get all objects that intersect an extent. diff --git a/test/spec/graph/graph.js b/test/spec/graph/graph.js index 5cd57e617..81e76ca6f 100644 --- a/test/spec/graph/graph.js +++ b/test/spec/graph/graph.js @@ -64,6 +64,50 @@ describe('iD.Graph', function() { }); }); + describe("#update", function () { + it("returns a new graph if self is frozen", function () { + var graph = iD.Graph(); + expect(graph.update()).not.to.equal(graph); + }); + + it("returns self if self is not frozen", function () { + var graph = iD.Graph({}, true); + expect(graph.update()).to.equal(graph); + }); + + it("doesn't modify self is self is frozen", function () { + var node = iD.Node(), + graph = iD.Graph([node]); + + graph.update(function (graph) { graph.remove(node); }); + + expect(graph.entity(node.id)).to.equal(node); + }); + + it("modifies self is self is not frozen", function () { + var node = iD.Node(), + graph = iD.Graph([node], true); + + graph.update(function (graph) { graph.remove(node); }); + + expect(graph.entity(node.id)).to.be.undefined; + }); + + it("executes all of the given functions", function () { + var a = iD.Node(), + b = iD.Node(), + graph = iD.Graph([a]); + + graph = graph.update( + function (graph) { graph.remove(a); }, + function (graph) { graph.replace(b); } + ); + + expect(graph.entity(a.id)).to.be.undefined; + expect(graph.entity(b.id)).to.equal(b); + }); + }); + describe("#parentWays", function() { it("returns an array of ways that contain the given node id", function () { var node = iD.Node({id: "n1"}),