]> git.za3k.com Git - flowy.git/commitdiff
Indent works in model, but not in UI
authorZachary Vance <vanceza@gmail.com>
Tue, 26 May 2015 01:09:41 +0000 (18:09 -0700)
committerZachary Vance <vanceza@gmail.com>
Tue, 26 May 2015 01:09:41 +0000 (18:09 -0700)
dist/flowy.js
dist/flowy.unwrapped.js
src/library/shortcut.js
src/library/viewShortcuts.js
src/models/todo.js
src/views/app.js
src/views/todo.js

index 0be641af42f87fc15c01d2362bc96965ffa82f08..dac4809e8d753a7b68d60d7017af12a8c05db1ae 100644 (file)
@@ -301,7 +301,8 @@ var Shortcut = (function(document, _) {
         },
         _displayKeybinding: function(shortcut, options) {
             options = _.defaults({}, options, { displayMultiple: true, allowRebind: false });
-            var keybindings = shortcut.keybinding.split(",");
+            var keybindings = shortcut.keybinding;
+            if (typeof(keybindings) === 'string') keybindings = [keybindings];
             if (!options.displayMultiple && keybindings.length > 1) {
                 keybindings = [keybindings[0]];
             }
@@ -334,6 +335,7 @@ var Shortcut = (function(document, _) {
     var undelegateEvents = View.undelegateEvents;
     var ShortcutRegex = /^Shortcut\("([^")]*)", ?"([^")]*)", ?"([^")]*)"\) (.*)$/;
     function delegate(id, description, defaultKeybinding, objectType, callback){
+        if (typeof(defaultKeybinding) !== 'string' && defaultKeybinding.split) defaultKeybinding = defaultKeybinding.split(',');
         var shortcut = Shortcut.registerShortcut({
             id: id,
             description: description,
@@ -408,7 +410,8 @@ var TodoView = Backbone.View.extend({
     'Shortcut("zoomIn", "Not Done - Zoom in", "alt+right") > .text': 'zoomIn',
     'Shortcut("zoomOut", "Not Done - Zoom out", "alt+left") > .text': 'zoomOut',
     'Shortcut("expand", "Not Done - Expand / collapse", "ctrl+space") > .text': 'expand',
-    'Shortcut("indent", "Not Done - Indent", "tab,alt+shift+right") > .text': 'indent',
+    //'Shortcut("indent", "Not Done - Indent", "tab,alt+shift+right") > .text': 'indent',
+    'Shortcut("indent", "Not Done - Indent", "tab") > .text': 'indent',
     'Shortcut("outdent", "Not Done - Outdent", "shift+tab,alt+shift+left") > .text': 'outdent',
     'Shortcut("moveDown", "Not Done - Move", "alt+shift+down") > .text': 'moveDown',
     'Shortcut("moveUp", "Not Done - Move", "alt+shift+up") > .text': 'moveUp',
@@ -525,14 +528,13 @@ var TodoView = Backbone.View.extend({
     return false;
   },
   indent: function() {
-    // Last child of previous sibling, then nothing
-    console.log("Indent not implemented"); // TODO
-    this.moveTo({keyboard:true});
+    // TODO: maintain focus
+    this.model.indent(this.model.collection);
+    return false;
   },
   outdent: function() {
-    // After parent, then nothing
-    console.log("Outdent not implemented"); // TODO
-    this.moveTo({keyboard:true});
+    // TODO: maintain focus
+    this.model.outdent(this.model.collection);
   },
   expand: function() {
     console.log("Expand not implemented"); // TODO
@@ -546,22 +548,12 @@ var TodoView = Backbone.View.extend({
     console.log("Zoom not implemented"); // TODO
   },
   moveDown: function() {
-    // After next sibling, then as first child of next node after parent, then up one level, then nothing
-    console.log("Move not implemented"); // TODO
-    this.moveTo({keyboard:true});
+    // TODO: maintain focus
+    this.model.moveDown(this.model.collection);
   },
   moveUp: function() {
-    // Before previous sibling, then as last child of previous node of parent, then before parent, then nothing
-    console.log("Move not implemented"); // TODO
-    this.moveTo({keyboard:true});
-  },
-  moveTo: function(loc, options) {
-    loc = _.defaults({}, loc, { parent: this.model.getParent(this.model.collection), index: this.model.getParent(this.model.collection).findChild(this.model.id) });
-    options = _.defaults({}, options, {
-        keyboard: false, // Whether the action was done with keyboard vs mouse (affects UI focus)
-    });
-    console.log("Move not implemented");
-    return false;
+    // TODO: maintain focus
+    this.model.moveUp(this.model.collection);
   },
   textChange: function(e) {
     var collection = this.model.collection;
@@ -743,6 +735,59 @@ var TodoModel = Backbone.Model.extend({
         this.set("bullets", bullets);
         return this;
     },
+    moveTo: function(newLocation, collection) {
+        if (collection.rootId == this.id) {
+            console.log("Cannot move root");
+            return this;
+        }
+        var existingLocation = {
+            parent: this.getParent(collection),
+            index: this.getParent(collection).findChild(this.id),
+        };
+        newLocation = _.defaults({}, newLocation, existingLocation);
+        var newChildren;
+        if (newLocation.parent === existingLocation.parent && newLocation.index === existingLocation.index) {
+            // No-op
+        } else if (newLocation.parent === this.parent) {
+            // We moved to a new location under the same parent.
+            newChildren = _.clone(newLocation.parent.get("bullets"));
+            newChildren.splice(existingLocation.index, 1);
+            newLocation.index = (newLocation.index < existingLocation.index) ? newLocation.index : newLocation.index - 1; // Adjust indices for insertion
+            newChildren.splice(existingLocation.index, 0, this.id);
+
+            newLocation.parent.save("bullets", newChildren);
+            this.save();
+        } else {
+            // We moved to a new parent
+            newChildren = _.clone(existingLocation.parent.get("bullets"));
+            newChildren.splice(existingLocation.index, 1);
+            existingLocation.parent.save("bullets", newChildren);
+            
+            newLocation.parent.insertChild(this, newLocation.index);
+            newLocation.parent.save();
+            this.save();
+        }
+        collection.trigger("move", this, existingLocation);
+        return this;
+    },
+    moveUp: function(collection) {
+        // Before previous sibling, then as last child of previous node of parent, then before parent, then nothing
+    },
+    moveDown: function(collection) {
+        // After next sibling, then as first child of next node after parent, then up one level, then nothing
+    },
+    indent: function(collection) {
+        // Last child of previous sibling, then nothing
+        var previous = this.getPreviousSibling(collection);
+        if (!previous) return undefined;
+        return this.moveTo({
+            parent: previous,
+            index: previous.getChildrenCount(),
+        }, collection);
+    },
+    outdent: function(collection) {
+        // After parent, then nothing
+    },
     removeChild: function(childTodoModel, collection) {
         if (childTodoModel.get("bullets").length > 0) {
             console.log("Cannot delete node with children.");
@@ -896,6 +941,7 @@ var AppView = Backbone.View.extend({
         this.list = options.list || new FlowyDocModel();
         this.list.app = this;
         this.listenTo(this.list, 'add', this.addOne);
+        this.listenTo(this.list, 'move', this.moveOne);
         this.listenTo(this.list, 'reset', this.addAll);
         var self = this;
         this.$("#reset-button").on("click", function() {
@@ -919,6 +965,18 @@ var AppView = Backbone.View.extend({
     addOne: function(todo) {
         this.renderTodo(todo);
     },
+    moveOne: function(todo, oldLocation) {
+        var view = this.getView(todo);
+
+        var oldParent = oldLocation.parent;
+        var oldParentView;
+        if (oldParent) oldParentView = this.getView(oldParent);
+        if (oldParentView) {
+           // Remove from old parent 
+        }
+
+        this.renderTodo(todo, { rerender: true }); // Don't remove existing view since it's still valid.
+    },
     toggleShortcuts: function() {
         this.$(".shortcuts").toggle();
     },
@@ -928,8 +986,11 @@ var AppView = Backbone.View.extend({
     search: function() {
         console.log("Search not yet implemented"); // TODO
     },
-    renderTodo: function(todo) {
-        if (this.getView(todo)) {
+    renderTodo: function(todo, options) {
+        options = _.defaults({}, options, {
+            rerender: false,
+        });
+        if (this.getView(todo) && !options.rerender) {
             console.log("View rendered more than once for todo #" + todo.id);
             return;
         }
@@ -958,6 +1019,9 @@ var AppView = Backbone.View.extend({
     getView: function(model) {
         return this.views[model.id];
     },
+    removeView: function(model) {
+        delete this.views[model.id];
+    },
     addAll: function() {
         this.views = {};
         this.$("#todo-list").html(null);
index 3dc2344ec9de81f300e1a96f3923ef4a2c7d10bd..64385a6b3c8f7ddb31b65ad3bbd6d344338857fa 100644 (file)
@@ -300,7 +300,8 @@ var Shortcut = (function(document, _) {
         },
         _displayKeybinding: function(shortcut, options) {
             options = _.defaults({}, options, { displayMultiple: true, allowRebind: false });
-            var keybindings = shortcut.keybinding.split(",");
+            var keybindings = shortcut.keybinding;
+            if (typeof(keybindings) === 'string') keybindings = [keybindings];
             if (!options.displayMultiple && keybindings.length > 1) {
                 keybindings = [keybindings[0]];
             }
@@ -333,6 +334,7 @@ var Shortcut = (function(document, _) {
     var undelegateEvents = View.undelegateEvents;
     var ShortcutRegex = /^Shortcut\("([^")]*)", ?"([^")]*)", ?"([^")]*)"\) (.*)$/;
     function delegate(id, description, defaultKeybinding, objectType, callback){
+        if (typeof(defaultKeybinding) !== 'string' && defaultKeybinding.split) defaultKeybinding = defaultKeybinding.split(',');
         var shortcut = Shortcut.registerShortcut({
             id: id,
             description: description,
@@ -407,7 +409,8 @@ var TodoView = Backbone.View.extend({
     'Shortcut("zoomIn", "Not Done - Zoom in", "alt+right") > .text': 'zoomIn',
     'Shortcut("zoomOut", "Not Done - Zoom out", "alt+left") > .text': 'zoomOut',
     'Shortcut("expand", "Not Done - Expand / collapse", "ctrl+space") > .text': 'expand',
-    'Shortcut("indent", "Not Done - Indent", "tab,alt+shift+right") > .text': 'indent',
+    //'Shortcut("indent", "Not Done - Indent", "tab,alt+shift+right") > .text': 'indent',
+    'Shortcut("indent", "Not Done - Indent", "tab") > .text': 'indent',
     'Shortcut("outdent", "Not Done - Outdent", "shift+tab,alt+shift+left") > .text': 'outdent',
     'Shortcut("moveDown", "Not Done - Move", "alt+shift+down") > .text': 'moveDown',
     'Shortcut("moveUp", "Not Done - Move", "alt+shift+up") > .text': 'moveUp',
@@ -524,14 +527,13 @@ var TodoView = Backbone.View.extend({
     return false;
   },
   indent: function() {
-    // Last child of previous sibling, then nothing
-    console.log("Indent not implemented"); // TODO
-    this.moveTo({keyboard:true});
+    // TODO: maintain focus
+    this.model.indent(this.model.collection);
+    return false;
   },
   outdent: function() {
-    // After parent, then nothing
-    console.log("Outdent not implemented"); // TODO
-    this.moveTo({keyboard:true});
+    // TODO: maintain focus
+    this.model.outdent(this.model.collection);
   },
   expand: function() {
     console.log("Expand not implemented"); // TODO
@@ -545,22 +547,12 @@ var TodoView = Backbone.View.extend({
     console.log("Zoom not implemented"); // TODO
   },
   moveDown: function() {
-    // After next sibling, then as first child of next node after parent, then up one level, then nothing
-    console.log("Move not implemented"); // TODO
-    this.moveTo({keyboard:true});
+    // TODO: maintain focus
+    this.model.moveDown(this.model.collection);
   },
   moveUp: function() {
-    // Before previous sibling, then as last child of previous node of parent, then before parent, then nothing
-    console.log("Move not implemented"); // TODO
-    this.moveTo({keyboard:true});
-  },
-  moveTo: function(loc, options) {
-    loc = _.defaults({}, loc, { parent: this.model.getParent(this.model.collection), index: this.model.getParent(this.model.collection).findChild(this.model.id) });
-    options = _.defaults({}, options, {
-        keyboard: false, // Whether the action was done with keyboard vs mouse (affects UI focus)
-    });
-    console.log("Move not implemented");
-    return false;
+    // TODO: maintain focus
+    this.model.moveUp(this.model.collection);
   },
   textChange: function(e) {
     var collection = this.model.collection;
@@ -742,6 +734,59 @@ var TodoModel = Backbone.Model.extend({
         this.set("bullets", bullets);
         return this;
     },
+    moveTo: function(newLocation, collection) {
+        if (collection.rootId == this.id) {
+            console.log("Cannot move root");
+            return this;
+        }
+        var existingLocation = {
+            parent: this.getParent(collection),
+            index: this.getParent(collection).findChild(this.id),
+        };
+        newLocation = _.defaults({}, newLocation, existingLocation);
+        var newChildren;
+        if (newLocation.parent === existingLocation.parent && newLocation.index === existingLocation.index) {
+            // No-op
+        } else if (newLocation.parent === this.parent) {
+            // We moved to a new location under the same parent.
+            newChildren = _.clone(newLocation.parent.get("bullets"));
+            newChildren.splice(existingLocation.index, 1);
+            newLocation.index = (newLocation.index < existingLocation.index) ? newLocation.index : newLocation.index - 1; // Adjust indices for insertion
+            newChildren.splice(existingLocation.index, 0, this.id);
+
+            newLocation.parent.save("bullets", newChildren);
+            this.save();
+        } else {
+            // We moved to a new parent
+            newChildren = _.clone(existingLocation.parent.get("bullets"));
+            newChildren.splice(existingLocation.index, 1);
+            existingLocation.parent.save("bullets", newChildren);
+            
+            newLocation.parent.insertChild(this, newLocation.index);
+            newLocation.parent.save();
+            this.save();
+        }
+        collection.trigger("move", this, existingLocation);
+        return this;
+    },
+    moveUp: function(collection) {
+        // Before previous sibling, then as last child of previous node of parent, then before parent, then nothing
+    },
+    moveDown: function(collection) {
+        // After next sibling, then as first child of next node after parent, then up one level, then nothing
+    },
+    indent: function(collection) {
+        // Last child of previous sibling, then nothing
+        var previous = this.getPreviousSibling(collection);
+        if (!previous) return undefined;
+        return this.moveTo({
+            parent: previous,
+            index: previous.getChildrenCount(),
+        }, collection);
+    },
+    outdent: function(collection) {
+        // After parent, then nothing
+    },
     removeChild: function(childTodoModel, collection) {
         if (childTodoModel.get("bullets").length > 0) {
             console.log("Cannot delete node with children.");
@@ -895,6 +940,7 @@ var AppView = Backbone.View.extend({
         this.list = options.list || new FlowyDocModel();
         this.list.app = this;
         this.listenTo(this.list, 'add', this.addOne);
+        this.listenTo(this.list, 'move', this.moveOne);
         this.listenTo(this.list, 'reset', this.addAll);
         var self = this;
         this.$("#reset-button").on("click", function() {
@@ -918,6 +964,18 @@ var AppView = Backbone.View.extend({
     addOne: function(todo) {
         this.renderTodo(todo);
     },
+    moveOne: function(todo, oldLocation) {
+        var view = this.getView(todo);
+
+        var oldParent = oldLocation.parent;
+        var oldParentView;
+        if (oldParent) oldParentView = this.getView(oldParent);
+        if (oldParentView) {
+           // Remove from old parent 
+        }
+
+        this.renderTodo(todo, { rerender: true }); // Don't remove existing view since it's still valid.
+    },
     toggleShortcuts: function() {
         this.$(".shortcuts").toggle();
     },
@@ -927,8 +985,11 @@ var AppView = Backbone.View.extend({
     search: function() {
         console.log("Search not yet implemented"); // TODO
     },
-    renderTodo: function(todo) {
-        if (this.getView(todo)) {
+    renderTodo: function(todo, options) {
+        options = _.defaults({}, options, {
+            rerender: false,
+        });
+        if (this.getView(todo) && !options.rerender) {
             console.log("View rendered more than once for todo #" + todo.id);
             return;
         }
@@ -957,6 +1018,9 @@ var AppView = Backbone.View.extend({
     getView: function(model) {
         return this.views[model.id];
     },
+    removeView: function(model) {
+        delete this.views[model.id];
+    },
     addAll: function() {
         this.views = {};
         this.$("#todo-list").html(null);
index cf2e92e625c16901de89711feb59f78f99becd5a..7ab8fbacfa604406a1f40a386512528a396d4f48 100644 (file)
@@ -239,7 +239,8 @@ var Shortcut = (function(document, _) {
         },
         _displayKeybinding: function(shortcut, options) {
             options = _.defaults({}, options, { displayMultiple: true, allowRebind: false });
-            var keybindings = shortcut.keybinding.split(",");
+            var keybindings = shortcut.keybinding;
+            if (typeof(keybindings) === 'string') keybindings = [keybindings];
             if (!options.displayMultiple && keybindings.length > 1) {
                 keybindings = [keybindings[0]];
             }
index 659c59f35d9b1b936dedcb6fb33e411d354f507d..d0048642d1ab1d53bfe43fb772a432f523d7be69 100644 (file)
@@ -9,6 +9,7 @@
     var undelegateEvents = View.undelegateEvents;
     var ShortcutRegex = /^Shortcut\("([^")]*)", ?"([^")]*)", ?"([^")]*)"\) (.*)$/;
     function delegate(id, description, defaultKeybinding, objectType, callback){
+        if (typeof(defaultKeybinding) !== 'string' && defaultKeybinding.split) defaultKeybinding = defaultKeybinding.split(',');
         var shortcut = Shortcut.registerShortcut({
             id: id,
             description: description,
index d5d366a8581ad6018cb29ed1b3fb8f159860603e..58aaf63555f0d369f5421844791f90b99c4406be 100644 (file)
@@ -100,6 +100,59 @@ var TodoModel = Backbone.Model.extend({
         this.set("bullets", bullets);
         return this;
     },
+    moveTo: function(newLocation, collection) {
+        if (collection.rootId == this.id) {
+            console.log("Cannot move root");
+            return this;
+        }
+        var existingLocation = {
+            parent: this.getParent(collection),
+            index: this.getParent(collection).findChild(this.id),
+        };
+        newLocation = _.defaults({}, newLocation, existingLocation);
+        var newChildren;
+        if (newLocation.parent === existingLocation.parent && newLocation.index === existingLocation.index) {
+            // No-op
+        } else if (newLocation.parent === this.parent) {
+            // We moved to a new location under the same parent.
+            newChildren = _.clone(newLocation.parent.get("bullets"));
+            newChildren.splice(existingLocation.index, 1);
+            newLocation.index = (newLocation.index < existingLocation.index) ? newLocation.index : newLocation.index - 1; // Adjust indices for insertion
+            newChildren.splice(existingLocation.index, 0, this.id);
+
+            newLocation.parent.save("bullets", newChildren);
+            this.save();
+        } else {
+            // We moved to a new parent
+            newChildren = _.clone(existingLocation.parent.get("bullets"));
+            newChildren.splice(existingLocation.index, 1);
+            existingLocation.parent.save("bullets", newChildren);
+            
+            newLocation.parent.insertChild(this, newLocation.index);
+            newLocation.parent.save();
+            this.save();
+        }
+        collection.trigger("move", this, existingLocation);
+        return this;
+    },
+    moveUp: function(collection) {
+        // Before previous sibling, then as last child of previous node of parent, then before parent, then nothing
+    },
+    moveDown: function(collection) {
+        // After next sibling, then as first child of next node after parent, then up one level, then nothing
+    },
+    indent: function(collection) {
+        // Last child of previous sibling, then nothing
+        var previous = this.getPreviousSibling(collection);
+        if (!previous) return undefined;
+        return this.moveTo({
+            parent: previous,
+            index: previous.getChildrenCount(),
+        }, collection);
+    },
+    outdent: function(collection) {
+        // After parent, then nothing
+    },
     removeChild: function(childTodoModel, collection) {
         if (childTodoModel.get("bullets").length > 0) {
             console.log("Cannot delete node with children.");
index fa573e8fadbf1492159c2ba93a0fd7ca50c1e23a..f515ae929a086587ea28dd3b16789be4350c0777 100644 (file)
@@ -72,6 +72,7 @@ var AppView = Backbone.View.extend({
         this.list = options.list || new FlowyDocModel();
         this.list.app = this;
         this.listenTo(this.list, 'add', this.addOne);
+        this.listenTo(this.list, 'move', this.moveOne);
         this.listenTo(this.list, 'reset', this.addAll);
         var self = this;
         this.$("#reset-button").on("click", function() {
@@ -95,6 +96,18 @@ var AppView = Backbone.View.extend({
     addOne: function(todo) {
         this.renderTodo(todo);
     },
+    moveOne: function(todo, oldLocation) {
+        var view = this.getView(todo);
+
+        var oldParent = oldLocation.parent;
+        var oldParentView;
+        if (oldParent) oldParentView = this.getView(oldParent);
+        if (oldParentView) {
+           // Remove from old parent 
+        }
+
+        this.renderTodo(todo, { rerender: true }); // Don't remove existing view since it's still valid.
+    },
     toggleShortcuts: function() {
         this.$(".shortcuts").toggle();
     },
@@ -104,8 +117,11 @@ var AppView = Backbone.View.extend({
     search: function() {
         console.log("Search not yet implemented"); // TODO
     },
-    renderTodo: function(todo) {
-        if (this.getView(todo)) {
+    renderTodo: function(todo, options) {
+        options = _.defaults({}, options, {
+            rerender: false,
+        });
+        if (this.getView(todo) && !options.rerender) {
             console.log("View rendered more than once for todo #" + todo.id);
             return;
         }
@@ -134,6 +150,9 @@ var AppView = Backbone.View.extend({
     getView: function(model) {
         return this.views[model.id];
     },
+    removeView: function(model) {
+        delete this.views[model.id];
+    },
     addAll: function() {
         this.views = {};
         this.$("#todo-list").html(null);
index 67a850ab80fd26432c5486bc786c92540ab2de12..405464f75b16055a18909771eb3fd04b3a983d3f 100644 (file)
@@ -18,7 +18,8 @@ var TodoView = Backbone.View.extend({
     'Shortcut("zoomIn", "Not Done - Zoom in", "alt+right") > .text': 'zoomIn',
     'Shortcut("zoomOut", "Not Done - Zoom out", "alt+left") > .text': 'zoomOut',
     'Shortcut("expand", "Not Done - Expand / collapse", "ctrl+space") > .text': 'expand',
-    'Shortcut("indent", "Not Done - Indent", "tab,alt+shift+right") > .text': 'indent',
+    //'Shortcut("indent", "Not Done - Indent", "tab,alt+shift+right") > .text': 'indent',
+    'Shortcut("indent", "Not Done - Indent", "tab") > .text': 'indent',
     'Shortcut("outdent", "Not Done - Outdent", "shift+tab,alt+shift+left") > .text': 'outdent',
     'Shortcut("moveDown", "Not Done - Move", "alt+shift+down") > .text': 'moveDown',
     'Shortcut("moveUp", "Not Done - Move", "alt+shift+up") > .text': 'moveUp',
@@ -135,14 +136,13 @@ var TodoView = Backbone.View.extend({
     return false;
   },
   indent: function() {
-    // Last child of previous sibling, then nothing
-    console.log("Indent not implemented"); // TODO
-    this.moveTo({keyboard:true});
+    // TODO: maintain focus
+    this.model.indent(this.model.collection);
+    return false;
   },
   outdent: function() {
-    // After parent, then nothing
-    console.log("Outdent not implemented"); // TODO
-    this.moveTo({keyboard:true});
+    // TODO: maintain focus
+    this.model.outdent(this.model.collection);
   },
   expand: function() {
     console.log("Expand not implemented"); // TODO
@@ -156,22 +156,12 @@ var TodoView = Backbone.View.extend({
     console.log("Zoom not implemented"); // TODO
   },
   moveDown: function() {
-    // After next sibling, then as first child of next node after parent, then up one level, then nothing
-    console.log("Move not implemented"); // TODO
-    this.moveTo({keyboard:true});
+    // TODO: maintain focus
+    this.model.moveDown(this.model.collection);
   },
   moveUp: function() {
-    // Before previous sibling, then as last child of previous node of parent, then before parent, then nothing
-    console.log("Move not implemented"); // TODO
-    this.moveTo({keyboard:true});
-  },
-  moveTo: function(loc, options) {
-    loc = _.defaults({}, loc, { parent: this.model.getParent(this.model.collection), index: this.model.getParent(this.model.collection).findChild(this.model.id) });
-    options = _.defaults({}, options, {
-        keyboard: false, // Whether the action was done with keyboard vs mouse (affects UI focus)
-    });
-    console.log("Move not implemented");
-    return false;
+    // TODO: maintain focus
+    this.model.moveUp(this.model.collection);
   },
   textChange: function(e) {
     var collection = this.model.collection;