From 9cfcb36b17bbab145554df4e0fe713697a0598ba Mon Sep 17 00:00:00 2001 From: Zachary Vance Date: Wed, 3 Jun 2015 17:25:21 -0700 Subject: [PATCH] Add undo/redo system in code --- dist/flowy.js | 58 +++++++++++++++++++++++++++++++++++++---- dist/flowy.unwrapped.js | 58 +++++++++++++++++++++++++++++++++++++---- src/actions.js | 24 +++++++++++++++++ src/flowy.js | 7 +++++ src/views/app.js | 25 ++++++++++++++---- src/views/todo.js | 1 + 6 files changed, 158 insertions(+), 15 deletions(-) create mode 100644 src/actions.js diff --git a/dist/flowy.js b/dist/flowy.js index 32af476..cd291a4 100644 --- a/dist/flowy.js +++ b/dist/flowy.js @@ -408,6 +408,7 @@ var TodoView = Backbone.View.extend({ "input > .text": "textChange", "blur > .text": "render", // Because the model shouldn't update the view during active editing, add a re-render at the end "keydown > .text": "keydown", + // "Shortcut(ID, TEXT, DEFAULT KEYBINDING) SELECTOR": "ACTION" 'Shortcut("combinePrevious", "Combine an item with the previous item", "backspace") > .text': "combinePrevious", 'Shortcut("combineNext", "Combine an item with the next item", "del") > .text': "combineNext", 'Shortcut("next", "Next", "down") > .text': 'focusNext', @@ -1050,12 +1051,38 @@ var testTodos = [ ]; +BaseAction = function() {}; +BaseAction.prototype= { + apply: function() {}, + rewind: function() {}, + reapply: function() { return this.apply.apply(this, arguments); }, +}; + +Actions = {}; + +Actions.AppendWords = function(array, word) { + this.array = array; + this.word = word; +}; +Actions.AppendWords.prototype = _.extend(new BaseAction(), { + apply: function() { + this.array.push(this.word); + }, + rewind: function() { + var a = this.array.pop(); + if (a !== this.word) { + throw "Rewind faiiiiiled :("; + } + }, +}); + /** * @depend ../views/todo.js * @depend ../models/flowyDoc.js * @depend ../models/todo.js * @depend ../library/viewShortcuts.js * @depend ../views/testTodos.js + * @depend ../actions.js */ var todos = new FlowyDocModel({ @@ -1091,6 +1118,8 @@ var AppView = Backbone.View.extend({ }); Shortcut.bindShortcutsDisplay(this.$("#shortcuts-wrapper")[0], {allowRebind: true, noDisplay: ['combineNext', 'combinePrevious', 'next', 'previous', 'left', 'right'] }); this.views = {}; // A list of views for each element in the collection + this.history = []; + this.history.redo = []; this.list.fetch(); this.render(); }, @@ -1110,6 +1139,23 @@ var AppView = Backbone.View.extend({ this.renderTodo(todo, { rerender: true }); // Don't remove existing view since it's still valid. } }, + act: function(action) { + action.apply(); + this.history.push(action); + this.history.redo = []; + }, + undo: function() { + var action = this.history.pop(); + if (!action) throw "Rewind called with empty history stack"; + action.rewind(); + this.history.redo.push(action); + }, + redo: function() { + var action = this.history.redo.pop(); + if (!action) throw "Replay called with empty redo stack"; + action.reapply(); + this.history.push(action); + }, toggleShortcuts: function() { this.$(".shortcuts").toggle(); return false; @@ -1164,17 +1210,19 @@ var AppView = Backbone.View.extend({ }, }); -// DEBUGGING -window.Backbone = Backbone; -window.Shortcut = Shortcut; -window.AppView = AppView; - /** * @depend views/app.js */ $(function() { var flowyView = new AppView(); + window.Flowy = flowyView; + + // DEBUGGING + window.Backbone = Backbone; + window.Shortcut = Shortcut; + window.Actions = Actions; }); + }); diff --git a/dist/flowy.unwrapped.js b/dist/flowy.unwrapped.js index 7e9d913..a5f4ddb 100644 --- a/dist/flowy.unwrapped.js +++ b/dist/flowy.unwrapped.js @@ -407,6 +407,7 @@ var TodoView = Backbone.View.extend({ "input > .text": "textChange", "blur > .text": "render", // Because the model shouldn't update the view during active editing, add a re-render at the end "keydown > .text": "keydown", + // "Shortcut(ID, TEXT, DEFAULT KEYBINDING) SELECTOR": "ACTION" 'Shortcut("combinePrevious", "Combine an item with the previous item", "backspace") > .text': "combinePrevious", 'Shortcut("combineNext", "Combine an item with the next item", "del") > .text': "combineNext", 'Shortcut("next", "Next", "down") > .text': 'focusNext', @@ -1049,12 +1050,38 @@ var testTodos = [ ]; +BaseAction = function() {}; +BaseAction.prototype= { + apply: function() {}, + rewind: function() {}, + reapply: function() { return this.apply.apply(this, arguments); }, +}; + +Actions = {}; + +Actions.AppendWords = function(array, word) { + this.array = array; + this.word = word; +}; +Actions.AppendWords.prototype = _.extend(new BaseAction(), { + apply: function() { + this.array.push(this.word); + }, + rewind: function() { + var a = this.array.pop(); + if (a !== this.word) { + throw "Rewind faiiiiiled :("; + } + }, +}); + /** * @depend ../views/todo.js * @depend ../models/flowyDoc.js * @depend ../models/todo.js * @depend ../library/viewShortcuts.js * @depend ../views/testTodos.js + * @depend ../actions.js */ var todos = new FlowyDocModel({ @@ -1090,6 +1117,8 @@ var AppView = Backbone.View.extend({ }); Shortcut.bindShortcutsDisplay(this.$("#shortcuts-wrapper")[0], {allowRebind: true, noDisplay: ['combineNext', 'combinePrevious', 'next', 'previous', 'left', 'right'] }); this.views = {}; // A list of views for each element in the collection + this.history = []; + this.history.redo = []; this.list.fetch(); this.render(); }, @@ -1109,6 +1138,23 @@ var AppView = Backbone.View.extend({ this.renderTodo(todo, { rerender: true }); // Don't remove existing view since it's still valid. } }, + act: function(action) { + action.apply(); + this.history.push(action); + this.history.redo = []; + }, + undo: function() { + var action = this.history.pop(); + if (!action) throw "Rewind called with empty history stack"; + action.rewind(); + this.history.redo.push(action); + }, + redo: function() { + var action = this.history.redo.pop(); + if (!action) throw "Replay called with empty redo stack"; + action.reapply(); + this.history.push(action); + }, toggleShortcuts: function() { this.$(".shortcuts").toggle(); return false; @@ -1163,15 +1209,17 @@ var AppView = Backbone.View.extend({ }, }); -// DEBUGGING -window.Backbone = Backbone; -window.Shortcut = Shortcut; -window.AppView = AppView; - /** * @depend views/app.js */ $(function() { var flowyView = new AppView(); + window.Flowy = flowyView; + + // DEBUGGING + window.Backbone = Backbone; + window.Shortcut = Shortcut; + window.Actions = Actions; }); + diff --git a/src/actions.js b/src/actions.js new file mode 100644 index 0000000..15543bf --- /dev/null +++ b/src/actions.js @@ -0,0 +1,24 @@ +BaseAction = function() {}; +BaseAction.prototype= { + apply: function() {}, + rewind: function() {}, + reapply: function() { return this.apply.apply(this, arguments); }, +}; + +Actions = {}; + +Actions.AppendWords = function(array, word) { + this.array = array; + this.word = word; +}; +Actions.AppendWords.prototype = _.extend(new BaseAction(), { + apply: function() { + this.array.push(this.word); + }, + rewind: function() { + var a = this.array.pop(); + if (a !== this.word) { + throw "Rewind faiiiiiled :("; + } + }, +}); diff --git a/src/flowy.js b/src/flowy.js index 8aacf11..ecba84a 100644 --- a/src/flowy.js +++ b/src/flowy.js @@ -4,4 +4,11 @@ $(function() { var flowyView = new AppView(); + window.Flowy = flowyView; + + // DEBUGGING + window.Backbone = Backbone; + window.Shortcut = Shortcut; + window.Actions = Actions; }); + diff --git a/src/views/app.js b/src/views/app.js index ebbd4a2..e6a2c07 100644 --- a/src/views/app.js +++ b/src/views/app.js @@ -4,6 +4,7 @@ * @depend ../models/todo.js * @depend ../library/viewShortcuts.js * @depend ../views/testTodos.js + * @depend ../actions.js */ var todos = new FlowyDocModel({ @@ -39,6 +40,8 @@ var AppView = Backbone.View.extend({ }); Shortcut.bindShortcutsDisplay(this.$("#shortcuts-wrapper")[0], {allowRebind: true, noDisplay: ['combineNext', 'combinePrevious', 'next', 'previous', 'left', 'right'] }); this.views = {}; // A list of views for each element in the collection + this.history = []; + this.history.redo = []; this.list.fetch(); this.render(); }, @@ -58,6 +61,23 @@ var AppView = Backbone.View.extend({ this.renderTodo(todo, { rerender: true }); // Don't remove existing view since it's still valid. } }, + act: function(action) { + action.apply(); + this.history.push(action); + this.history.redo = []; + }, + undo: function() { + var action = this.history.pop(); + if (!action) throw "Rewind called with empty history stack"; + action.rewind(); + this.history.redo.push(action); + }, + redo: function() { + var action = this.history.redo.pop(); + if (!action) throw "Replay called with empty redo stack"; + action.reapply(); + this.history.push(action); + }, toggleShortcuts: function() { this.$(".shortcuts").toggle(); return false; @@ -111,8 +131,3 @@ var AppView = Backbone.View.extend({ this.list.each(this.addOne, this); }, }); - -// DEBUGGING -window.Backbone = Backbone; -window.Shortcut = Shortcut; -window.AppView = AppView; diff --git a/src/views/todo.js b/src/views/todo.js index f0806d5..25619e1 100644 --- a/src/views/todo.js +++ b/src/views/todo.js @@ -11,6 +11,7 @@ var TodoView = Backbone.View.extend({ "input > .text": "textChange", "blur > .text": "render", // Because the model shouldn't update the view during active editing, add a re-render at the end "keydown > .text": "keydown", + // "Shortcut(ID, TEXT, DEFAULT KEYBINDING) SELECTOR": "ACTION" 'Shortcut("combinePrevious", "Combine an item with the previous item", "backspace") > .text': "combinePrevious", 'Shortcut("combineNext", "Combine an item with the next item", "del") > .text': "combineNext", 'Shortcut("next", "Next", "down") > .text': 'focusNext', -- 2.47.3