From 41dfce505a17e747531e62f1ad7fd9fb7a960f6c Mon Sep 17 00:00:00 2001 From: Zachary Vance Date: Wed, 3 Jun 2015 18:12:41 -0700 Subject: [PATCH] Undo/redo typing and moving --- dist/flowy.js | 93 ++++++++++++++++++++++++------------ dist/flowy.unwrapped.js | 93 ++++++++++++++++++++++++------------ src/actions.js | 30 ++++++++---- src/library/viewShortcuts.js | 1 + src/models/todo.js | 20 ++++++-- src/views/app.js | 42 +++++++++------- 6 files changed, 189 insertions(+), 90 deletions(-) diff --git a/dist/flowy.js b/dist/flowy.js index cd291a4..aa43858 100644 --- a/dist/flowy.js +++ b/dist/flowy.js @@ -362,6 +362,7 @@ var Shortcut = (function(document, _) { var el = this.el; if (objectType === "global") { Shortcut.globalObject = el; + Shortcut.globalObject.options = { view: this }; this._shortcutObject = Shortcut.globalObject; } else { this._shortcutObject = Shortcut.addObject(el, objectType, { view: this }); @@ -719,8 +720,11 @@ var TodoModel = Backbone.Model.extend({ toggleCollapsed: function() { this.save({collapsed: !this.get("collapsed")}); }, + _setText: function(text) { + return this.save({text: text}); + }, setText: function(text) { - this.save({text: text}); + return Actions.App.act(new Actions.ChangeText(this, text)); }, hasChildren: function() { return this.get("bullets").length > 0; @@ -809,16 +813,23 @@ var TodoModel = Backbone.Model.extend({ this.set("bullets", bullets); return this; }, + getLocation: function() { + return { + parent: this.getParent(), + index: this.getParent().findChild(this.id), + }; + }, moveTo: function(newLocation) { if (this.isRoot()) { console.log("Cannot move root"); return this; } - var existingLocation = { - parent: this.getParent(), - index: this.getParent().findChild(this.id), - }; + var existingLocation = this.getLocation(); newLocation = _.defaults({}, newLocation, existingLocation); + return Actions.App.act(new Actions.MoveBullet(this, newLocation)); + }, + _moveTo: function(newLocation) { + var existingLocation = this.getLocation(); var newChildren; if (newLocation.parent === existingLocation.parent && newLocation.index === existingLocation.index) { // No-op @@ -1060,19 +1071,31 @@ BaseAction.prototype= { Actions = {}; -Actions.AppendWords = function(array, word) { - this.array = array; - this.word = word; +Actions.MoveBullet = function(bullet, newLocation) { + this.bullet = bullet; + this.oldLocation = bullet.getLocation(); + this.newLocation = newLocation; }; -Actions.AppendWords.prototype = _.extend(new BaseAction(), { +Actions.MoveBullet.prototype = _.extend(new BaseAction(), { apply: function() { - this.array.push(this.word); + return this.bullet._moveTo(this.newLocation); }, rewind: function() { - var a = this.array.pop(); - if (a !== this.word) { - throw "Rewind faiiiiiled :("; - } + return this.bullet._moveTo(this.oldLocation); + }, +}); + +Actions.ChangeText = function(bullet, newText) { + this.bullet = bullet; + this.oldText = bullet.get("text"); + this.newText = newText; +}; +Actions.ChangeText.prototype = _.extend(new BaseAction(), { + apply: function() { + return this.bullet._setText(this.newText); + }, + rewind: function() { + return this.bullet._setText(this.oldText); }, }); @@ -1094,9 +1117,11 @@ var AppView = Backbone.View.extend({ el: $("#todo-app"), shortcutObject: "global", events: { - 'Shortcut("toggleShortcuts", "Keyboard Shortcuts", "ctrl+shift+/") > .text': 'toggleShortcuts', - 'Shortcut("toggleShowCompleted", "Show/hide completed", "ctrl+o") > .text': 'toggleShowCompleted', - 'Shortcut("search", "Not Done - Search", "esc") > .text': 'search', + 'Shortcut("toggleShortcuts", "Keyboard Shortcuts", "ctrl+shift+/") body': 'toggleShortcuts', + 'Shortcut("toggleShowCompleted", "Show/hide completed", "ctrl+o") body': 'toggleShowCompleted', + 'Shortcut("search", "Not Done - Search", "esc") body': 'search', + 'Shortcut("undo", "Undo", "ctrl+z") body': 'undo', + 'Shortcut("redo", "Redo", "ctrl+shift+z,ctrl+y") body': 'redo', }, initialize: function(options) { options = _.defaults({}, options, appDefaults); @@ -1116,10 +1141,10 @@ var AppView = Backbone.View.extend({ e.save(); }); }); - Shortcut.bindShortcutsDisplay(this.$("#shortcuts-wrapper")[0], {allowRebind: true, noDisplay: ['combineNext', 'combinePrevious', 'next', 'previous', 'left', 'right'] }); + Shortcut.bindShortcutsDisplay(this.$("#shortcuts-wrapper")[0], {allowRebind: true, noDisplay: ['combineNext', 'combinePrevious', 'next', 'previous', 'left', 'right', 'undo', 'redo'] }); this.views = {}; // A list of views for each element in the collection - this.history = []; - this.history.redo = []; + Actions.App = this; + this.undoRedoHistory = {undo: [], redo:[]}; // Linear zipper this.list.fetch(); this.render(); }, @@ -1140,21 +1165,29 @@ var AppView = Backbone.View.extend({ } }, act: function(action) { - action.apply(); - this.history.push(action); - this.history.redo = []; + var retVal = action.apply(); + this.undoRedoHistory.undo.push(action); + this.undoRedoHistory.redo = []; + return retVal; }, undo: function() { - var action = this.history.pop(); - if (!action) throw "Rewind called with empty history stack"; - action.rewind(); - this.history.redo.push(action); + var action = this.undoRedoHistory.undo.pop(); + if (!action) { + console.log("Rewind called with empty history stack"); + return; + } + var retVal = action.rewind(); + this.undoRedoHistory.redo.push(action); + return retVal; }, redo: function() { - var action = this.history.redo.pop(); - if (!action) throw "Replay called with empty redo stack"; + var action = this.undoRedoHistory.redo.pop(); + if (!action) { + console.log("Replay called with empty redo stack"); + return; + } action.reapply(); - this.history.push(action); + this.undoRedoHistory.undo.push(action); }, toggleShortcuts: function() { this.$(".shortcuts").toggle(); diff --git a/dist/flowy.unwrapped.js b/dist/flowy.unwrapped.js index a5f4ddb..4765349 100644 --- a/dist/flowy.unwrapped.js +++ b/dist/flowy.unwrapped.js @@ -361,6 +361,7 @@ var Shortcut = (function(document, _) { var el = this.el; if (objectType === "global") { Shortcut.globalObject = el; + Shortcut.globalObject.options = { view: this }; this._shortcutObject = Shortcut.globalObject; } else { this._shortcutObject = Shortcut.addObject(el, objectType, { view: this }); @@ -718,8 +719,11 @@ var TodoModel = Backbone.Model.extend({ toggleCollapsed: function() { this.save({collapsed: !this.get("collapsed")}); }, + _setText: function(text) { + return this.save({text: text}); + }, setText: function(text) { - this.save({text: text}); + return Actions.App.act(new Actions.ChangeText(this, text)); }, hasChildren: function() { return this.get("bullets").length > 0; @@ -808,16 +812,23 @@ var TodoModel = Backbone.Model.extend({ this.set("bullets", bullets); return this; }, + getLocation: function() { + return { + parent: this.getParent(), + index: this.getParent().findChild(this.id), + }; + }, moveTo: function(newLocation) { if (this.isRoot()) { console.log("Cannot move root"); return this; } - var existingLocation = { - parent: this.getParent(), - index: this.getParent().findChild(this.id), - }; + var existingLocation = this.getLocation(); newLocation = _.defaults({}, newLocation, existingLocation); + return Actions.App.act(new Actions.MoveBullet(this, newLocation)); + }, + _moveTo: function(newLocation) { + var existingLocation = this.getLocation(); var newChildren; if (newLocation.parent === existingLocation.parent && newLocation.index === existingLocation.index) { // No-op @@ -1059,19 +1070,31 @@ BaseAction.prototype= { Actions = {}; -Actions.AppendWords = function(array, word) { - this.array = array; - this.word = word; +Actions.MoveBullet = function(bullet, newLocation) { + this.bullet = bullet; + this.oldLocation = bullet.getLocation(); + this.newLocation = newLocation; }; -Actions.AppendWords.prototype = _.extend(new BaseAction(), { +Actions.MoveBullet.prototype = _.extend(new BaseAction(), { apply: function() { - this.array.push(this.word); + return this.bullet._moveTo(this.newLocation); }, rewind: function() { - var a = this.array.pop(); - if (a !== this.word) { - throw "Rewind faiiiiiled :("; - } + return this.bullet._moveTo(this.oldLocation); + }, +}); + +Actions.ChangeText = function(bullet, newText) { + this.bullet = bullet; + this.oldText = bullet.get("text"); + this.newText = newText; +}; +Actions.ChangeText.prototype = _.extend(new BaseAction(), { + apply: function() { + return this.bullet._setText(this.newText); + }, + rewind: function() { + return this.bullet._setText(this.oldText); }, }); @@ -1093,9 +1116,11 @@ var AppView = Backbone.View.extend({ el: $("#todo-app"), shortcutObject: "global", events: { - 'Shortcut("toggleShortcuts", "Keyboard Shortcuts", "ctrl+shift+/") > .text': 'toggleShortcuts', - 'Shortcut("toggleShowCompleted", "Show/hide completed", "ctrl+o") > .text': 'toggleShowCompleted', - 'Shortcut("search", "Not Done - Search", "esc") > .text': 'search', + 'Shortcut("toggleShortcuts", "Keyboard Shortcuts", "ctrl+shift+/") body': 'toggleShortcuts', + 'Shortcut("toggleShowCompleted", "Show/hide completed", "ctrl+o") body': 'toggleShowCompleted', + 'Shortcut("search", "Not Done - Search", "esc") body': 'search', + 'Shortcut("undo", "Undo", "ctrl+z") body': 'undo', + 'Shortcut("redo", "Redo", "ctrl+shift+z,ctrl+y") body': 'redo', }, initialize: function(options) { options = _.defaults({}, options, appDefaults); @@ -1115,10 +1140,10 @@ var AppView = Backbone.View.extend({ e.save(); }); }); - Shortcut.bindShortcutsDisplay(this.$("#shortcuts-wrapper")[0], {allowRebind: true, noDisplay: ['combineNext', 'combinePrevious', 'next', 'previous', 'left', 'right'] }); + Shortcut.bindShortcutsDisplay(this.$("#shortcuts-wrapper")[0], {allowRebind: true, noDisplay: ['combineNext', 'combinePrevious', 'next', 'previous', 'left', 'right', 'undo', 'redo'] }); this.views = {}; // A list of views for each element in the collection - this.history = []; - this.history.redo = []; + Actions.App = this; + this.undoRedoHistory = {undo: [], redo:[]}; // Linear zipper this.list.fetch(); this.render(); }, @@ -1139,21 +1164,29 @@ var AppView = Backbone.View.extend({ } }, act: function(action) { - action.apply(); - this.history.push(action); - this.history.redo = []; + var retVal = action.apply(); + this.undoRedoHistory.undo.push(action); + this.undoRedoHistory.redo = []; + return retVal; }, undo: function() { - var action = this.history.pop(); - if (!action) throw "Rewind called with empty history stack"; - action.rewind(); - this.history.redo.push(action); + var action = this.undoRedoHistory.undo.pop(); + if (!action) { + console.log("Rewind called with empty history stack"); + return; + } + var retVal = action.rewind(); + this.undoRedoHistory.redo.push(action); + return retVal; }, redo: function() { - var action = this.history.redo.pop(); - if (!action) throw "Replay called with empty redo stack"; + var action = this.undoRedoHistory.redo.pop(); + if (!action) { + console.log("Replay called with empty redo stack"); + return; + } action.reapply(); - this.history.push(action); + this.undoRedoHistory.undo.push(action); }, toggleShortcuts: function() { this.$(".shortcuts").toggle(); diff --git a/src/actions.js b/src/actions.js index 15543bf..973f484 100644 --- a/src/actions.js +++ b/src/actions.js @@ -7,18 +7,30 @@ BaseAction.prototype= { Actions = {}; -Actions.AppendWords = function(array, word) { - this.array = array; - this.word = word; +Actions.MoveBullet = function(bullet, newLocation) { + this.bullet = bullet; + this.oldLocation = bullet.getLocation(); + this.newLocation = newLocation; }; -Actions.AppendWords.prototype = _.extend(new BaseAction(), { +Actions.MoveBullet.prototype = _.extend(new BaseAction(), { apply: function() { - this.array.push(this.word); + return this.bullet._moveTo(this.newLocation); }, rewind: function() { - var a = this.array.pop(); - if (a !== this.word) { - throw "Rewind faiiiiiled :("; - } + return this.bullet._moveTo(this.oldLocation); + }, +}); + +Actions.ChangeText = function(bullet, newText) { + this.bullet = bullet; + this.oldText = bullet.get("text"); + this.newText = newText; +}; +Actions.ChangeText.prototype = _.extend(new BaseAction(), { + apply: function() { + return this.bullet._setText(this.newText); + }, + rewind: function() { + return this.bullet._setText(this.oldText); }, }); diff --git a/src/library/viewShortcuts.js b/src/library/viewShortcuts.js index 1ac7180..e4868a4 100644 --- a/src/library/viewShortcuts.js +++ b/src/library/viewShortcuts.js @@ -32,6 +32,7 @@ var el = this.el; if (objectType === "global") { Shortcut.globalObject = el; + Shortcut.globalObject.options = { view: this }; this._shortcutObject = Shortcut.globalObject; } else { this._shortcutObject = Shortcut.addObject(el, objectType, { view: this }); diff --git a/src/models/todo.js b/src/models/todo.js index 11f9206..79e9c74 100644 --- a/src/models/todo.js +++ b/src/models/todo.js @@ -14,8 +14,11 @@ var TodoModel = Backbone.Model.extend({ toggleCollapsed: function() { this.save({collapsed: !this.get("collapsed")}); }, + _setText: function(text) { + return this.save({text: text}); + }, setText: function(text) { - this.save({text: text}); + return Actions.App.act(new Actions.ChangeText(this, text)); }, hasChildren: function() { return this.get("bullets").length > 0; @@ -104,16 +107,23 @@ var TodoModel = Backbone.Model.extend({ this.set("bullets", bullets); return this; }, + getLocation: function() { + return { + parent: this.getParent(), + index: this.getParent().findChild(this.id), + }; + }, moveTo: function(newLocation) { if (this.isRoot()) { console.log("Cannot move root"); return this; } - var existingLocation = { - parent: this.getParent(), - index: this.getParent().findChild(this.id), - }; + var existingLocation = this.getLocation(); newLocation = _.defaults({}, newLocation, existingLocation); + return Actions.App.act(new Actions.MoveBullet(this, newLocation)); + }, + _moveTo: function(newLocation) { + var existingLocation = this.getLocation(); var newChildren; if (newLocation.parent === existingLocation.parent && newLocation.index === existingLocation.index) { // No-op diff --git a/src/views/app.js b/src/views/app.js index e6a2c07..27d054a 100644 --- a/src/views/app.js +++ b/src/views/app.js @@ -16,9 +16,11 @@ var AppView = Backbone.View.extend({ el: $("#todo-app"), shortcutObject: "global", events: { - 'Shortcut("toggleShortcuts", "Keyboard Shortcuts", "ctrl+shift+/") > .text': 'toggleShortcuts', - 'Shortcut("toggleShowCompleted", "Show/hide completed", "ctrl+o") > .text': 'toggleShowCompleted', - 'Shortcut("search", "Not Done - Search", "esc") > .text': 'search', + 'Shortcut("toggleShortcuts", "Keyboard Shortcuts", "ctrl+shift+/") body': 'toggleShortcuts', + 'Shortcut("toggleShowCompleted", "Show/hide completed", "ctrl+o") body': 'toggleShowCompleted', + 'Shortcut("search", "Not Done - Search", "esc") body': 'search', + 'Shortcut("undo", "Undo", "ctrl+z") body': 'undo', + 'Shortcut("redo", "Redo", "ctrl+shift+z,ctrl+y") body': 'redo', }, initialize: function(options) { options = _.defaults({}, options, appDefaults); @@ -38,10 +40,10 @@ var AppView = Backbone.View.extend({ e.save(); }); }); - Shortcut.bindShortcutsDisplay(this.$("#shortcuts-wrapper")[0], {allowRebind: true, noDisplay: ['combineNext', 'combinePrevious', 'next', 'previous', 'left', 'right'] }); + Shortcut.bindShortcutsDisplay(this.$("#shortcuts-wrapper")[0], {allowRebind: true, noDisplay: ['combineNext', 'combinePrevious', 'next', 'previous', 'left', 'right', 'undo', 'redo'] }); this.views = {}; // A list of views for each element in the collection - this.history = []; - this.history.redo = []; + Actions.App = this; + this.undoRedoHistory = {undo: [], redo:[]}; // Linear zipper this.list.fetch(); this.render(); }, @@ -62,21 +64,29 @@ var AppView = Backbone.View.extend({ } }, act: function(action) { - action.apply(); - this.history.push(action); - this.history.redo = []; + var retVal = action.apply(); + this.undoRedoHistory.undo.push(action); + this.undoRedoHistory.redo = []; + return retVal; }, undo: function() { - var action = this.history.pop(); - if (!action) throw "Rewind called with empty history stack"; - action.rewind(); - this.history.redo.push(action); + var action = this.undoRedoHistory.undo.pop(); + if (!action) { + console.log("Rewind called with empty history stack"); + return; + } + var retVal = action.rewind(); + this.undoRedoHistory.redo.push(action); + return retVal; }, redo: function() { - var action = this.history.redo.pop(); - if (!action) throw "Replay called with empty redo stack"; + var action = this.undoRedoHistory.redo.pop(); + if (!action) { + console.log("Replay called with empty redo stack"); + return; + } action.reapply(); - this.history.push(action); + this.undoRedoHistory.undo.push(action); }, toggleShortcuts: function() { this.$(".shortcuts").toggle(); -- 2.47.3