From a21da4f15fef4b149d2ea88d5927093ef6ec4e98 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Wed, 6 Feb 2013 12:48:42 -0500 Subject: [PATCH] Saving graph to and reinstating from localStorage --- js/id/graph/graph.js | 45 +++++++++++++++++++++++++++++++++++++++++- js/id/graph/history.js | 45 +++++++++++++++++++++++++++++++++++++++++- js/id/id.js | 15 +++++++------- js/id/ui.js | 3 ++- js/id/ui/splash.js | 4 ++-- 5 files changed, 100 insertions(+), 12 deletions(-) diff --git a/js/id/graph/graph.js b/js/id/graph/graph.js index 2692aeba6..d9118045a 100644 --- a/js/id/graph/graph.js +++ b/js/id/graph/graph.js @@ -227,10 +227,53 @@ iD.Graph.prototype = { var items = []; for (var i in this.entities) { var entity = this.entities[i]; - if (entity && entity.intersects(extent, this)) { + if (entity && this.hasAllChildren(entity) && entity.intersects(extent, this)) { items.push(entity); } } return items; + }, + + hasAllChildren: function(entity) { + // we're only checking changed entities, since we assume fetched data + // must have all children present + if (this.entities.hasOwnProperty(entity.id)) { + if (entity.type === 'way') { + for (i = 0; i < entity.nodes.length; i++) { + if (!this.entities[entity.nodes[i]]) return false; + } + } else if (entity.type === 'relation') { + for (i = 0; i < entity.members.length; i++) { + if (!this.entities[entity.members[i].id]) return false; + } + } + } + return true; + }, + + // Obliterates any existing entities + load: function(entities) { + + var base = this.base(), + i, entity, prefix; + this.entities = Object.create(base.entities); + + for (i in entities) { + entity = entities[i]; + prefix = i[0]; + + if (prefix == 'n') { + this.entities[i] = new iD.Node(entity); + + } else if (prefix == 'w') { + this.entities[i] = new iD.Way(entity); + + } else if (prefix == 'r') { + this.entities[i] = new iD.Relation(entity); + } + this._updateCalculated(base.entities[i], this.entities[i]); + } + return this; } + }; diff --git a/js/id/graph/history.js b/js/id/graph/history.js index a0930203c..dba04ec7e 100644 --- a/js/id/graph/history.js +++ b/js/id/graph/history.js @@ -1,4 +1,4 @@ -iD.History = function() { +iD.History = function(context) { var stack, index, imagery_used = 'Bing', dispatch = d3.dispatch('change', 'undone', 'redone'); @@ -26,6 +26,10 @@ iD.History = function() { return difference; } + function getKey(n) { + return 'iD_' + window.location.origin + '_' + n; + } + var history = { graph: function() { return stack[index].graph; @@ -148,8 +152,47 @@ iD.History = function() { reset: function() { stack = [{graph: iD.Graph()}]; index = 0; + this.load(); dispatch.change(); + }, + + save: function() { + var json = JSON.stringify(stack.map(function(i) { + return _.extend(i, { + graph: i.graph.entities + }); + })); + + context.storage(getKey('history'), json); + context.storage(getKey('nextIDs'), JSON.stringify(iD.Entity.id.next)); + context.storage(getKey('index'), index); + context.storage(getKey('lock'), ''); + }, + + lock: function() { + if (context.storage(getKey('lock'))) return false; + context.storage(getKey('lock'), true); + return true; + }, + + load: function() { + if (!this.lock()) return; + + var json = context.storage(getKey('history')), + nextIDs = context.storage(getKey('nextIDs')), + index_ = context.storage(getKey('index')); + + if (!json) return; + if (nextIDs) iD.Entity.id.next = JSON.parse(nextIDs); + if (index_ !== null) index = parseInt(index_, 10); + + stack = JSON.parse(json).map(function(d) { + d.graph = iD.Graph().load(d.graph); + return d; + }); + } + }; history.reset(); diff --git a/js/id/id.js b/js/id/id.js index 56d3e481a..e20b3b111 100644 --- a/js/id/id.js +++ b/js/id/id.js @@ -1,18 +1,19 @@ window.iD = function () { var context = {}, - history = iD.History(), - storage = localStorage || {}, - dispatch = d3.dispatch('enter', 'exit'), - mode, - container, - ui = iD.ui(context), - map = iD.Map(context); + storage = localStorage || {}; context.storage = function(k, v) { if (arguments.length === 1) return storage[k]; else storage[k] = v; }; + var history = iD.History(context), + dispatch = d3.dispatch('enter', 'exit'), + mode, + container, + ui = iD.ui(context), + map = iD.Map(context); + // the connection requires .storage() to be available on calling. var connection = iD.Connection(context); diff --git a/js/id/ui.js b/js/id/ui.js index ace66703d..dbbff15c8 100644 --- a/js/id/ui.js +++ b/js/id/ui.js @@ -194,6 +194,7 @@ iD.ui = function(context) { history.on('change.editor', function() { window.onbeforeunload = history.hasChanges() ? function() { + history.save(); return 'You have unsaved changes.'; } : null; @@ -252,7 +253,7 @@ iD.ui = function(context) { context.enter(iD.modes.Browse(context)); if (!context.storage('sawSplash')) { - iD.ui.splash(); + iD.ui.splash(context.container()); context.storage('sawSplash', true); } }; diff --git a/js/id/ui/splash.js b/js/id/ui/splash.js index a4d2a915e..bfcbf3e05 100644 --- a/js/id/ui/splash.js +++ b/js/id/ui/splash.js @@ -1,5 +1,5 @@ -iD.ui.splash = function() { - var modal = iD.ui.modal(); +iD.ui.splash = function(selection) { + var modal = iD.ui.modal(selection); modal.select('.modal') .attr('class', 'modal-splash modal');