diff --git a/modules/core/history.js b/modules/core/history.js index 09f2279b7..b881ec9c3 100644 --- a/modules/core/history.js +++ b/modules/core/history.js @@ -15,6 +15,7 @@ export function coreHistory(context) { dispatch = d3.dispatch('change', 'undone', 'redone'), lock = utilSessionMutex('lock'), duration = 150, + checkpoints = {}, stack, index, tree; @@ -278,10 +279,27 @@ export function coreHistory(context) { }, - reset: function() { - stack = [{graph: coreGraph()}]; - index = 0; - tree = coreTree(stack[0].graph); + // save the current history state + checkpoint: function(key) { + checkpoints[key] = { + stack: _.cloneDeep(stack), + index: index + }; + return history; + }, + + + // restore history state to a given checkpoint or reset completely + reset: function(key) { + if (key !== undefined) { + stack = _.cloneDeep(checkpoints[key].stack); + index = checkpoints[key].index; + } else { + stack = [{graph: coreGraph()}]; + index = 0; + tree = coreTree(stack[0].graph); + checkpoints = {}; + } dispatch.call('change'); return history; }, diff --git a/test/spec/core/history.js b/test/spec/core/history.js index 10693c726..13734de6e 100644 --- a/test/spec/core/history.js +++ b/test/spec/core/history.js @@ -283,6 +283,36 @@ describe('iD.History', function () { }); }); + describe('#checkpoint', function () { + it('saves and resets to checkpoints', function () { + history.perform(action, 'annotation1'); + history.perform(action, 'annotation2'); + history.perform(action, 'annotation3'); + history.checkpoint('check1'); + history.perform(action, 'annotation4'); + history.perform(action, 'annotation5'); + history.checkpoint('check2'); + history.perform(action, 'annotation6'); + history.perform(action, 'annotation7'); + history.perform(action, 'annotation8'); + + history.reset('check1'); + expect(history.undoAnnotation()).to.equal('annotation3'); + + history.reset('check2'); + expect(history.undoAnnotation()).to.equal('annotation5'); + + history.reset('check1'); + expect(history.undoAnnotation()).to.equal('annotation3'); + }); + + it('emits a change event', function () { + history.on('change', spy); + history.reset(); + expect(spy).to.have.been.called; + }); + }); + describe('#toJSON', function() { it('doesn\'t generate unsaveable changes', function() { var node_1 = iD.Node({id: 'n-1'});