From d79231ccc72249156eb77ee407f275f0fdf36444 Mon Sep 17 00:00:00 2001 From: Zachary Vance Date: Tue, 26 May 2015 17:39:58 -0700 Subject: [PATCH] View doesn't know about collection, model is always associated with being part of a particular collection --- dist/flowy.js | 156 ++++++++++++++++++++-------------------- dist/flowy.unwrapped.js | 156 ++++++++++++++++++++-------------------- src/models/todo.js | 111 ++++++++++++++-------------- src/views/app.js | 10 +-- src/views/todo.js | 35 +++++---- 5 files changed, 237 insertions(+), 231 deletions(-) diff --git a/dist/flowy.js b/dist/flowy.js index 230d67b..c41bc86 100644 --- a/dist/flowy.js +++ b/dist/flowy.js @@ -460,21 +460,21 @@ var TodoView = Backbone.View.extend({ } else { this.stopEditingText(); this.model.toggleComplete(); - var next = this.model.nextNode(this.model.collection, { childrenAllowed: false}) || this.model.getParent(this.model.collection, { rootAllowed: false}); + var next = this.model.nextNode({ childrenAllowed: false}) || this.model.getParent({ rootAllowed: false}); if (!next) return false; next.getView().startEditingText(); } return false; }, focusNext: function() { - var nextNode = this.model.nextNode(this.model.collection, { childrenAllowed: true }); + var nextNode = this.model.nextNode({ childrenAllowed: true }); if (!nextNode) return false; this.stopEditingText(); nextNode.getView().startEditingText(); return false; }, focusPrevious: function() { - var previousNode = this.model.previousNode(this.model.collection, { childrenAllowed: true }); + var previousNode = this.model.previousNode({ childrenAllowed: true }); if (!previousNode) return false; this.stopEditingText(); previousNode.getView().startEditingText(); @@ -484,17 +484,17 @@ var TodoView = Backbone.View.extend({ if (this.model.hasChildren()) { return; } - var previousNode = this.model.previousNode(this.model.collection); + var previousNode = this.model.previousNode(); if (!previousNode) { return; } if (this.model.get("text") === "") { - this.model.remove(this.model.collection); + this.model.remove(); previousNode.getView().startEditingText({"atEnd":true}); return false; } else if (this.isFocusAtBeginning()) { var text = this.model.get("text"); - this.model.remove(this.model.collection); + this.model.remove(); previousNode.setText(previousNode.get("text") + '' + text); previousNode.getView().startEditingText({"atMarker": ".focus"}); return false; @@ -504,17 +504,17 @@ var TodoView = Backbone.View.extend({ if (this.model.hasChildren()) { return; } - var nextNode = this.model.nextNode(this.model.collection); + var nextNode = this.model.nextNode(); if (!nextNode) { return; } if (this.model.get("text") === "") { - this.model.remove(this.model.collection); + this.model.remove(); nextNode.getView().startEditingText(); return false; } else if (this.isFocusAtEnd()) { var text = nextNode.get("text"); - nextNode.remove(this.model.collection); + nextNode.remove(); this.stopEditingText(); this.model.setText(this.model.get("text") + '' + text); this.startEditingText({"atMarker": ".focus"}); @@ -524,17 +524,17 @@ var TodoView = Backbone.View.extend({ "delete": function() { // Delete node and its entire subtree this.focusPrevious(); - this.model.removeAll(this.model.collection); + this.model.removeAll(); return false; }, indent: function() { // TODO: maintain focus - this.model.indent(this.model.collection); + this.model.indent(); return false; }, outdent: function() { // TODO: maintain focus - this.model.outdent(this.model.collection); + this.model.outdent(); }, expand: function() { console.log("Expand not implemented"); // TODO @@ -549,14 +549,13 @@ var TodoView = Backbone.View.extend({ }, moveDown: function() { // TODO: maintain focus - this.model.moveDown(this.model.collection); + this.model.moveDown(); }, moveUp: function() { // TODO: maintain focus - this.model.moveUp(this.model.collection); + this.model.moveUp(); }, textChange: function(e) { - var collection = this.model.collection; var lines = $(e.target).html().split(//); if (lines.length === 0) { console.log("unexpected number of lines in textChange"); @@ -581,16 +580,16 @@ var TodoView = Backbone.View.extend({ this.outdent(); } else if ((lines.length === 2 && lines[1] === "") || (lines.length === 3 && lines[1] === "" && lines[2] === "")) { // Line break at end this.model.setText(this.decodeText(lines[0])); - var emptyAfter = this.model.addTodoAfter({text: this.decodeText(lines[1])}, collection); // Child or not depending on whether this has children + var emptyAfter = this.model.addTodoAfter({text: this.decodeText(lines[1])}); // Child or not depending on whether this has children this.stopEditingText(); emptyAfter.getView().startEditingText(); } else if (lines.length === 2 && lines[0] === "") { // Line break at beginning - var emptyBefore = this.model.addTodoBefore({text: this.decodeText(lines[0])}, collection); + var emptyBefore = this.model.addTodoBefore({text: this.decodeText(lines[0])}); this.model.setText(this.decodeText(lines[1])); this.stopEditingText(); emptyBefore.getView().startEditingText(); } else if (lines.length === 2) { // Line break in middle - var newNode = this.model.addTodoAfter({text: this.decodeText(lines[1])}, collection); + var newNode = this.model.addTodoAfter({text: this.decodeText(lines[1])}); this.model.setText(this.decodeText(lines[0])); this.stopEditingText(); // For re-render newNode.getView().startEditingText(); // Keep focus on current node (second half) @@ -658,59 +657,59 @@ var TodoModel = Backbone.Model.extend({ getChildrenCount: function() { return this.get("bullets").length; }, - getChildren: function(collection) { + getChildren: function() { return _.map(this.get("bullets"), function(id) { - return collection.get(id); + return this.collection.get(id); }, this); }, - getChild: function(collection, index) { - return collection.get(this.get("bullets")[index]); + getChild: function(index) { + return this.collection.get(this.get("bullets")[index]); }, - getParent: function(collection, options) { + getParent: function(options) { options = _.defaults({}, options, { rootAllowed: true }); - var parent = collection.get(this.get("parent")); - if (parent && parent.id == collection.rootId && !options.rootAllowed) return undefined; + var parent = this.collection.get(this.get("parent")); + if (parent && parent.isRoot() && !options.rootAllowed) return undefined; return parent; }, - getPreviousSibling: function(collection) { - var parent = this.getParent(collection); + getPreviousSibling: function() { + var parent = this.getParent(); if (!parent) return undefined; var index = parent.findChild(this.id); if (index < 0 || index === 0) return undefined; - return parent.getChild(collection, index - 1); + return parent.getChild(index - 1); }, - getNextSibling: function(collection) { - var parent = this.getParent(collection); + getNextSibling: function() { + var parent = this.getParent(); if (!parent) return undefined; var index = parent.findChild(this.id); var numChildren = parent.getChildrenCount(); if (index < 0 || index === numChildren - 1) return undefined; - return parent.getChild(collection, index + 1); + return parent.getChild(index + 1); }, - nextNode: function(collection, options) { + nextNode: function(options) { options = _.defaults({}, options, {childrenAllowed: true}); if (options.childrenAllowed) { if (this.hasChildren()) { - return this.getChild(collection, 0); + return this.getChild(0); } } - for (var node = this; node && node.id !== collection.rootId; node = node.getParent(collection)) { - var next = node.getNextSibling(collection); + for (var node = this; node && !node.isRoot(); node = node.getParent()) { + var next = node.getNextSibling(); if (next) return next; } return undefined; }, - previousNode: function(collection, options) { + previousNode: function(options) { options = _.defaults({}, options, {childrenAllowed: true}); - var previous = this.getPreviousSibling(collection); + var previous = this.getPreviousSibling(); if (previous && options.childrenAllowed) { while (previous.hasChildren()) { - previous = previous.getChild(collection, previous.getChildrenCount() - 1); + previous = previous.getChild(previous.getChildrenCount() - 1); } } - if (!previous) previous = this.getParent(collection); + if (!previous) previous = this.getParent(); if (!previous) return undefined; - if (previous.id === collection.rootId) { + if (previous.isRoot()) { return undefined; } else { return previous; @@ -719,11 +718,11 @@ var TodoModel = Backbone.Model.extend({ getView: function() { //TODO: remove return this.collection.app.getView(this); }, - isTopLevel: function(collection) { - return this.get("parent") === collection.rootId; + isTopLevel: function() { + return this.get("parent") === this.collection.rootId; }, - isParentLoaded: function(collection, collectionView) { - return this.isTopLevel(collection) ? true : (this.getParent(collection) && collectionView.getView(this.getParent(collection))); + isParentLoaded: function(collectionView) { + return this.isTopLevel() ? true : (this.getParent() && collectionView.getView(this.getParent())); }, findChild: function(childId) { return this.get("bullets").indexOf(childId); @@ -735,14 +734,14 @@ var TodoModel = Backbone.Model.extend({ this.set("bullets", bullets); return this; }, - moveTo: function(newLocation, collection) { - if (collection.rootId == this.id) { + moveTo: function(newLocation) { + if (this.isRoot()) { console.log("Cannot move root"); return this; } var existingLocation = { - parent: this.getParent(collection), - index: this.getParent(collection).findChild(this.id), + parent: this.getParent(), + index: this.getParent().findChild(this.id), }; newLocation = _.defaults({}, newLocation, existingLocation); var newChildren; @@ -767,28 +766,31 @@ var TodoModel = Backbone.Model.extend({ newLocation.parent.save(); this.save(); } - collection.trigger("move", this, existingLocation); + this.collection.trigger("move", this, existingLocation); return this; }, - moveUp: function(collection) { + moveUp: function() { // Before previous sibling, then as last child of previous node of parent, then before parent, then nothing }, - moveDown: function(collection) { + moveDown: function() { // After next sibling, then as first child of next node after parent, then up one level, then nothing }, - indent: function(collection) { + indent: function() { // Last child of previous sibling, then nothing - var previous = this.getPreviousSibling(collection); + var previous = this.getPreviousSibling(); if (!previous) return undefined; return this.moveTo({ parent: previous, index: previous.getChildrenCount(), - }, collection); + }); }, - outdent: function(collection) { + outdent: function() { // After parent, then nothing }, - removeChild: function(childTodoModel, collection) { + isRoot: function() { + return this.id === this.collection.rootId; + }, + removeChild: function(childTodoModel) { if (childTodoModel.get("bullets").length > 0) { console.log("Cannot delete node with children."); return; @@ -806,48 +808,48 @@ var TodoModel = Backbone.Model.extend({ }}); return this; }, - remove: function(collection) { - this.getParent(collection).removeChild(this, collection); + remove: function() { + this.getParent().removeChild(this); }, - removeAll: function(collection) { - if (collection.rootId == this.id) { + removeAll: function() { + if (this.isRoot()) { console.log("Cannot remove root node of collection"); return; } if (this.hasChildren()) { - var children = _.clone(this.getChildren(collection)); + var children = _.clone(this.getChildren()); _.each(children, function(child) { - child.removeAll(collection); + child.removeAll(); }); } - this.remove(collection); + this.remove(); }, - addTodoBefore: function(todo, collection) { + addTodoBefore: function(todo) { // Todo always goes as the previous sibling - var parent = this.getParent(collection); - var todoModel = collection.create(_.extend(todo, {"parent": parent.id})); + var parent = this.getParent(); + var todoModel = this.collection.create(_.extend(todo, {"parent": parent.id})); parent.insertChild(todoModel, parent.findChild(this.id)); parent.save(); - collection.trigger("add", todoModel); + this.collection.trigger("add", todoModel); return todoModel; }, - addTodoAfter: function(todo, collection) { + addTodoAfter: function(todo) { // If there are children, the todo goes as the first child. // Otherwise, the todo goes as the next sibling var todoModel; if (this.get("bullets").length > 0) { - todoModel = collection.create(_.extend(todo, {"parent": this.id})); + todoModel = this.collection.create(_.extend(todo, {"parent": this.id})); this.insertChild(todoModel, 0); this.save(); } else { - parent = this.getParent(collection); - todoModel = collection.create(_.extend(todo, {"parent": parent.id})); + parent = this.getParent(); + todoModel = this.collection.create(_.extend(todo, {"parent": parent.id})); parent.insertChild(todoModel, parent.findChild(this.id)+1); parent.save(); } - collection.trigger("add", todoModel); + this.collection.trigger("add", todoModel); return todoModel; } }); @@ -994,19 +996,19 @@ var AppView = Backbone.View.extend({ console.log("View rendered more than once for todo #" + todo.id); return; } - var view = new TodoView({model: todo, collection: this.list}); - if (todo.isTopLevel(this.list)) { + var view = new TodoView({model: todo}); + if (todo.isTopLevel()) { this.setView(todo, view); this.$("#todo-list").append(view.render().el); - } else if (todo.isParentLoaded(this.list, this) && todo.getParent(this.list).findChild(todo.id) >= 0 /* because move/insert is nonatomic */) { + } else if (todo.isParentLoaded(this) && todo.getParent().findChild(todo.id) >= 0 /* because move/insert is nonatomic */) { this.setView(todo, view); - var parent = todo.getParent(this.list); + var parent = todo.getParent(); var parentView = this.getView(parent); parentView.addChild(view.render().el, parent.findChild(todo.id)); } // Find unrendered descendents and render them, too. - _.each(todo.getChildren(this.list), function(child) { + _.each(todo.getChildren(), function(child) { if (child && !this.getView(child)) { this.renderTodo(child); } diff --git a/dist/flowy.unwrapped.js b/dist/flowy.unwrapped.js index fce3cbc..512f6d9 100644 --- a/dist/flowy.unwrapped.js +++ b/dist/flowy.unwrapped.js @@ -459,21 +459,21 @@ var TodoView = Backbone.View.extend({ } else { this.stopEditingText(); this.model.toggleComplete(); - var next = this.model.nextNode(this.model.collection, { childrenAllowed: false}) || this.model.getParent(this.model.collection, { rootAllowed: false}); + var next = this.model.nextNode({ childrenAllowed: false}) || this.model.getParent({ rootAllowed: false}); if (!next) return false; next.getView().startEditingText(); } return false; }, focusNext: function() { - var nextNode = this.model.nextNode(this.model.collection, { childrenAllowed: true }); + var nextNode = this.model.nextNode({ childrenAllowed: true }); if (!nextNode) return false; this.stopEditingText(); nextNode.getView().startEditingText(); return false; }, focusPrevious: function() { - var previousNode = this.model.previousNode(this.model.collection, { childrenAllowed: true }); + var previousNode = this.model.previousNode({ childrenAllowed: true }); if (!previousNode) return false; this.stopEditingText(); previousNode.getView().startEditingText(); @@ -483,17 +483,17 @@ var TodoView = Backbone.View.extend({ if (this.model.hasChildren()) { return; } - var previousNode = this.model.previousNode(this.model.collection); + var previousNode = this.model.previousNode(); if (!previousNode) { return; } if (this.model.get("text") === "") { - this.model.remove(this.model.collection); + this.model.remove(); previousNode.getView().startEditingText({"atEnd":true}); return false; } else if (this.isFocusAtBeginning()) { var text = this.model.get("text"); - this.model.remove(this.model.collection); + this.model.remove(); previousNode.setText(previousNode.get("text") + '' + text); previousNode.getView().startEditingText({"atMarker": ".focus"}); return false; @@ -503,17 +503,17 @@ var TodoView = Backbone.View.extend({ if (this.model.hasChildren()) { return; } - var nextNode = this.model.nextNode(this.model.collection); + var nextNode = this.model.nextNode(); if (!nextNode) { return; } if (this.model.get("text") === "") { - this.model.remove(this.model.collection); + this.model.remove(); nextNode.getView().startEditingText(); return false; } else if (this.isFocusAtEnd()) { var text = nextNode.get("text"); - nextNode.remove(this.model.collection); + nextNode.remove(); this.stopEditingText(); this.model.setText(this.model.get("text") + '' + text); this.startEditingText({"atMarker": ".focus"}); @@ -523,17 +523,17 @@ var TodoView = Backbone.View.extend({ "delete": function() { // Delete node and its entire subtree this.focusPrevious(); - this.model.removeAll(this.model.collection); + this.model.removeAll(); return false; }, indent: function() { // TODO: maintain focus - this.model.indent(this.model.collection); + this.model.indent(); return false; }, outdent: function() { // TODO: maintain focus - this.model.outdent(this.model.collection); + this.model.outdent(); }, expand: function() { console.log("Expand not implemented"); // TODO @@ -548,14 +548,13 @@ var TodoView = Backbone.View.extend({ }, moveDown: function() { // TODO: maintain focus - this.model.moveDown(this.model.collection); + this.model.moveDown(); }, moveUp: function() { // TODO: maintain focus - this.model.moveUp(this.model.collection); + this.model.moveUp(); }, textChange: function(e) { - var collection = this.model.collection; var lines = $(e.target).html().split(//); if (lines.length === 0) { console.log("unexpected number of lines in textChange"); @@ -580,16 +579,16 @@ var TodoView = Backbone.View.extend({ this.outdent(); } else if ((lines.length === 2 && lines[1] === "") || (lines.length === 3 && lines[1] === "" && lines[2] === "")) { // Line break at end this.model.setText(this.decodeText(lines[0])); - var emptyAfter = this.model.addTodoAfter({text: this.decodeText(lines[1])}, collection); // Child or not depending on whether this has children + var emptyAfter = this.model.addTodoAfter({text: this.decodeText(lines[1])}); // Child or not depending on whether this has children this.stopEditingText(); emptyAfter.getView().startEditingText(); } else if (lines.length === 2 && lines[0] === "") { // Line break at beginning - var emptyBefore = this.model.addTodoBefore({text: this.decodeText(lines[0])}, collection); + var emptyBefore = this.model.addTodoBefore({text: this.decodeText(lines[0])}); this.model.setText(this.decodeText(lines[1])); this.stopEditingText(); emptyBefore.getView().startEditingText(); } else if (lines.length === 2) { // Line break in middle - var newNode = this.model.addTodoAfter({text: this.decodeText(lines[1])}, collection); + var newNode = this.model.addTodoAfter({text: this.decodeText(lines[1])}); this.model.setText(this.decodeText(lines[0])); this.stopEditingText(); // For re-render newNode.getView().startEditingText(); // Keep focus on current node (second half) @@ -657,59 +656,59 @@ var TodoModel = Backbone.Model.extend({ getChildrenCount: function() { return this.get("bullets").length; }, - getChildren: function(collection) { + getChildren: function() { return _.map(this.get("bullets"), function(id) { - return collection.get(id); + return this.collection.get(id); }, this); }, - getChild: function(collection, index) { - return collection.get(this.get("bullets")[index]); + getChild: function(index) { + return this.collection.get(this.get("bullets")[index]); }, - getParent: function(collection, options) { + getParent: function(options) { options = _.defaults({}, options, { rootAllowed: true }); - var parent = collection.get(this.get("parent")); - if (parent && parent.id == collection.rootId && !options.rootAllowed) return undefined; + var parent = this.collection.get(this.get("parent")); + if (parent && parent.isRoot() && !options.rootAllowed) return undefined; return parent; }, - getPreviousSibling: function(collection) { - var parent = this.getParent(collection); + getPreviousSibling: function() { + var parent = this.getParent(); if (!parent) return undefined; var index = parent.findChild(this.id); if (index < 0 || index === 0) return undefined; - return parent.getChild(collection, index - 1); + return parent.getChild(index - 1); }, - getNextSibling: function(collection) { - var parent = this.getParent(collection); + getNextSibling: function() { + var parent = this.getParent(); if (!parent) return undefined; var index = parent.findChild(this.id); var numChildren = parent.getChildrenCount(); if (index < 0 || index === numChildren - 1) return undefined; - return parent.getChild(collection, index + 1); + return parent.getChild(index + 1); }, - nextNode: function(collection, options) { + nextNode: function(options) { options = _.defaults({}, options, {childrenAllowed: true}); if (options.childrenAllowed) { if (this.hasChildren()) { - return this.getChild(collection, 0); + return this.getChild(0); } } - for (var node = this; node && node.id !== collection.rootId; node = node.getParent(collection)) { - var next = node.getNextSibling(collection); + for (var node = this; node && !node.isRoot(); node = node.getParent()) { + var next = node.getNextSibling(); if (next) return next; } return undefined; }, - previousNode: function(collection, options) { + previousNode: function(options) { options = _.defaults({}, options, {childrenAllowed: true}); - var previous = this.getPreviousSibling(collection); + var previous = this.getPreviousSibling(); if (previous && options.childrenAllowed) { while (previous.hasChildren()) { - previous = previous.getChild(collection, previous.getChildrenCount() - 1); + previous = previous.getChild(previous.getChildrenCount() - 1); } } - if (!previous) previous = this.getParent(collection); + if (!previous) previous = this.getParent(); if (!previous) return undefined; - if (previous.id === collection.rootId) { + if (previous.isRoot()) { return undefined; } else { return previous; @@ -718,11 +717,11 @@ var TodoModel = Backbone.Model.extend({ getView: function() { //TODO: remove return this.collection.app.getView(this); }, - isTopLevel: function(collection) { - return this.get("parent") === collection.rootId; + isTopLevel: function() { + return this.get("parent") === this.collection.rootId; }, - isParentLoaded: function(collection, collectionView) { - return this.isTopLevel(collection) ? true : (this.getParent(collection) && collectionView.getView(this.getParent(collection))); + isParentLoaded: function(collectionView) { + return this.isTopLevel() ? true : (this.getParent() && collectionView.getView(this.getParent())); }, findChild: function(childId) { return this.get("bullets").indexOf(childId); @@ -734,14 +733,14 @@ var TodoModel = Backbone.Model.extend({ this.set("bullets", bullets); return this; }, - moveTo: function(newLocation, collection) { - if (collection.rootId == this.id) { + moveTo: function(newLocation) { + if (this.isRoot()) { console.log("Cannot move root"); return this; } var existingLocation = { - parent: this.getParent(collection), - index: this.getParent(collection).findChild(this.id), + parent: this.getParent(), + index: this.getParent().findChild(this.id), }; newLocation = _.defaults({}, newLocation, existingLocation); var newChildren; @@ -766,28 +765,31 @@ var TodoModel = Backbone.Model.extend({ newLocation.parent.save(); this.save(); } - collection.trigger("move", this, existingLocation); + this.collection.trigger("move", this, existingLocation); return this; }, - moveUp: function(collection) { + moveUp: function() { // Before previous sibling, then as last child of previous node of parent, then before parent, then nothing }, - moveDown: function(collection) { + moveDown: function() { // After next sibling, then as first child of next node after parent, then up one level, then nothing }, - indent: function(collection) { + indent: function() { // Last child of previous sibling, then nothing - var previous = this.getPreviousSibling(collection); + var previous = this.getPreviousSibling(); if (!previous) return undefined; return this.moveTo({ parent: previous, index: previous.getChildrenCount(), - }, collection); + }); }, - outdent: function(collection) { + outdent: function() { // After parent, then nothing }, - removeChild: function(childTodoModel, collection) { + isRoot: function() { + return this.id === this.collection.rootId; + }, + removeChild: function(childTodoModel) { if (childTodoModel.get("bullets").length > 0) { console.log("Cannot delete node with children."); return; @@ -805,48 +807,48 @@ var TodoModel = Backbone.Model.extend({ }}); return this; }, - remove: function(collection) { - this.getParent(collection).removeChild(this, collection); + remove: function() { + this.getParent().removeChild(this); }, - removeAll: function(collection) { - if (collection.rootId == this.id) { + removeAll: function() { + if (this.isRoot()) { console.log("Cannot remove root node of collection"); return; } if (this.hasChildren()) { - var children = _.clone(this.getChildren(collection)); + var children = _.clone(this.getChildren()); _.each(children, function(child) { - child.removeAll(collection); + child.removeAll(); }); } - this.remove(collection); + this.remove(); }, - addTodoBefore: function(todo, collection) { + addTodoBefore: function(todo) { // Todo always goes as the previous sibling - var parent = this.getParent(collection); - var todoModel = collection.create(_.extend(todo, {"parent": parent.id})); + var parent = this.getParent(); + var todoModel = this.collection.create(_.extend(todo, {"parent": parent.id})); parent.insertChild(todoModel, parent.findChild(this.id)); parent.save(); - collection.trigger("add", todoModel); + this.collection.trigger("add", todoModel); return todoModel; }, - addTodoAfter: function(todo, collection) { + addTodoAfter: function(todo) { // If there are children, the todo goes as the first child. // Otherwise, the todo goes as the next sibling var todoModel; if (this.get("bullets").length > 0) { - todoModel = collection.create(_.extend(todo, {"parent": this.id})); + todoModel = this.collection.create(_.extend(todo, {"parent": this.id})); this.insertChild(todoModel, 0); this.save(); } else { - parent = this.getParent(collection); - todoModel = collection.create(_.extend(todo, {"parent": parent.id})); + parent = this.getParent(); + todoModel = this.collection.create(_.extend(todo, {"parent": parent.id})); parent.insertChild(todoModel, parent.findChild(this.id)+1); parent.save(); } - collection.trigger("add", todoModel); + this.collection.trigger("add", todoModel); return todoModel; } }); @@ -993,19 +995,19 @@ var AppView = Backbone.View.extend({ console.log("View rendered more than once for todo #" + todo.id); return; } - var view = new TodoView({model: todo, collection: this.list}); - if (todo.isTopLevel(this.list)) { + var view = new TodoView({model: todo}); + if (todo.isTopLevel()) { this.setView(todo, view); this.$("#todo-list").append(view.render().el); - } else if (todo.isParentLoaded(this.list, this) && todo.getParent(this.list).findChild(todo.id) >= 0 /* because move/insert is nonatomic */) { + } else if (todo.isParentLoaded(this) && todo.getParent().findChild(todo.id) >= 0 /* because move/insert is nonatomic */) { this.setView(todo, view); - var parent = todo.getParent(this.list); + var parent = todo.getParent(); var parentView = this.getView(parent); parentView.addChild(view.render().el, parent.findChild(todo.id)); } // Find unrendered descendents and render them, too. - _.each(todo.getChildren(this.list), function(child) { + _.each(todo.getChildren(), function(child) { if (child && !this.getView(child)) { this.renderTodo(child); } diff --git a/src/models/todo.js b/src/models/todo.js index 58aaf63..dc28a52 100644 --- a/src/models/todo.js +++ b/src/models/todo.js @@ -23,59 +23,59 @@ var TodoModel = Backbone.Model.extend({ getChildrenCount: function() { return this.get("bullets").length; }, - getChildren: function(collection) { + getChildren: function() { return _.map(this.get("bullets"), function(id) { - return collection.get(id); + return this.collection.get(id); }, this); }, - getChild: function(collection, index) { - return collection.get(this.get("bullets")[index]); + getChild: function(index) { + return this.collection.get(this.get("bullets")[index]); }, - getParent: function(collection, options) { + getParent: function(options) { options = _.defaults({}, options, { rootAllowed: true }); - var parent = collection.get(this.get("parent")); - if (parent && parent.id == collection.rootId && !options.rootAllowed) return undefined; + var parent = this.collection.get(this.get("parent")); + if (parent && parent.isRoot() && !options.rootAllowed) return undefined; return parent; }, - getPreviousSibling: function(collection) { - var parent = this.getParent(collection); + getPreviousSibling: function() { + var parent = this.getParent(); if (!parent) return undefined; var index = parent.findChild(this.id); if (index < 0 || index === 0) return undefined; - return parent.getChild(collection, index - 1); + return parent.getChild(index - 1); }, - getNextSibling: function(collection) { - var parent = this.getParent(collection); + getNextSibling: function() { + var parent = this.getParent(); if (!parent) return undefined; var index = parent.findChild(this.id); var numChildren = parent.getChildrenCount(); if (index < 0 || index === numChildren - 1) return undefined; - return parent.getChild(collection, index + 1); + return parent.getChild(index + 1); }, - nextNode: function(collection, options) { + nextNode: function(options) { options = _.defaults({}, options, {childrenAllowed: true}); if (options.childrenAllowed) { if (this.hasChildren()) { - return this.getChild(collection, 0); + return this.getChild(0); } } - for (var node = this; node && node.id !== collection.rootId; node = node.getParent(collection)) { - var next = node.getNextSibling(collection); + for (var node = this; node && !node.isRoot(); node = node.getParent()) { + var next = node.getNextSibling(); if (next) return next; } return undefined; }, - previousNode: function(collection, options) { + previousNode: function(options) { options = _.defaults({}, options, {childrenAllowed: true}); - var previous = this.getPreviousSibling(collection); + var previous = this.getPreviousSibling(); if (previous && options.childrenAllowed) { while (previous.hasChildren()) { - previous = previous.getChild(collection, previous.getChildrenCount() - 1); + previous = previous.getChild(previous.getChildrenCount() - 1); } } - if (!previous) previous = this.getParent(collection); + if (!previous) previous = this.getParent(); if (!previous) return undefined; - if (previous.id === collection.rootId) { + if (previous.isRoot()) { return undefined; } else { return previous; @@ -84,11 +84,11 @@ var TodoModel = Backbone.Model.extend({ getView: function() { //TODO: remove return this.collection.app.getView(this); }, - isTopLevel: function(collection) { - return this.get("parent") === collection.rootId; + isTopLevel: function() { + return this.get("parent") === this.collection.rootId; }, - isParentLoaded: function(collection, collectionView) { - return this.isTopLevel(collection) ? true : (this.getParent(collection) && collectionView.getView(this.getParent(collection))); + isParentLoaded: function(collectionView) { + return this.isTopLevel() ? true : (this.getParent() && collectionView.getView(this.getParent())); }, findChild: function(childId) { return this.get("bullets").indexOf(childId); @@ -100,14 +100,14 @@ var TodoModel = Backbone.Model.extend({ this.set("bullets", bullets); return this; }, - moveTo: function(newLocation, collection) { - if (collection.rootId == this.id) { + moveTo: function(newLocation) { + if (this.isRoot()) { console.log("Cannot move root"); return this; } var existingLocation = { - parent: this.getParent(collection), - index: this.getParent(collection).findChild(this.id), + parent: this.getParent(), + index: this.getParent().findChild(this.id), }; newLocation = _.defaults({}, newLocation, existingLocation); var newChildren; @@ -132,28 +132,31 @@ var TodoModel = Backbone.Model.extend({ newLocation.parent.save(); this.save(); } - collection.trigger("move", this, existingLocation); + this.collection.trigger("move", this, existingLocation); return this; }, - moveUp: function(collection) { + moveUp: function() { // Before previous sibling, then as last child of previous node of parent, then before parent, then nothing }, - moveDown: function(collection) { + moveDown: function() { // After next sibling, then as first child of next node after parent, then up one level, then nothing }, - indent: function(collection) { + indent: function() { // Last child of previous sibling, then nothing - var previous = this.getPreviousSibling(collection); + var previous = this.getPreviousSibling(); if (!previous) return undefined; return this.moveTo({ parent: previous, index: previous.getChildrenCount(), - }, collection); + }); }, - outdent: function(collection) { + outdent: function() { // After parent, then nothing }, - removeChild: function(childTodoModel, collection) { + isRoot: function() { + return this.id === this.collection.rootId; + }, + removeChild: function(childTodoModel) { if (childTodoModel.get("bullets").length > 0) { console.log("Cannot delete node with children."); return; @@ -171,48 +174,48 @@ var TodoModel = Backbone.Model.extend({ }}); return this; }, - remove: function(collection) { - this.getParent(collection).removeChild(this, collection); + remove: function() { + this.getParent().removeChild(this); }, - removeAll: function(collection) { - if (collection.rootId == this.id) { + removeAll: function() { + if (this.isRoot()) { console.log("Cannot remove root node of collection"); return; } if (this.hasChildren()) { - var children = _.clone(this.getChildren(collection)); + var children = _.clone(this.getChildren()); _.each(children, function(child) { - child.removeAll(collection); + child.removeAll(); }); } - this.remove(collection); + this.remove(); }, - addTodoBefore: function(todo, collection) { + addTodoBefore: function(todo) { // Todo always goes as the previous sibling - var parent = this.getParent(collection); - var todoModel = collection.create(_.extend(todo, {"parent": parent.id})); + var parent = this.getParent(); + var todoModel = this.collection.create(_.extend(todo, {"parent": parent.id})); parent.insertChild(todoModel, parent.findChild(this.id)); parent.save(); - collection.trigger("add", todoModel); + this.collection.trigger("add", todoModel); return todoModel; }, - addTodoAfter: function(todo, collection) { + addTodoAfter: function(todo) { // If there are children, the todo goes as the first child. // Otherwise, the todo goes as the next sibling var todoModel; if (this.get("bullets").length > 0) { - todoModel = collection.create(_.extend(todo, {"parent": this.id})); + todoModel = this.collection.create(_.extend(todo, {"parent": this.id})); this.insertChild(todoModel, 0); this.save(); } else { - parent = this.getParent(collection); - todoModel = collection.create(_.extend(todo, {"parent": parent.id})); + parent = this.getParent(); + todoModel = this.collection.create(_.extend(todo, {"parent": parent.id})); parent.insertChild(todoModel, parent.findChild(this.id)+1); parent.save(); } - collection.trigger("add", todoModel); + this.collection.trigger("add", todoModel); return todoModel; } }); diff --git a/src/views/app.js b/src/views/app.js index f515ae9..2d8f367 100644 --- a/src/views/app.js +++ b/src/views/app.js @@ -125,19 +125,19 @@ var AppView = Backbone.View.extend({ console.log("View rendered more than once for todo #" + todo.id); return; } - var view = new TodoView({model: todo, collection: this.list}); - if (todo.isTopLevel(this.list)) { + var view = new TodoView({model: todo}); + if (todo.isTopLevel()) { this.setView(todo, view); this.$("#todo-list").append(view.render().el); - } else if (todo.isParentLoaded(this.list, this) && todo.getParent(this.list).findChild(todo.id) >= 0 /* because move/insert is nonatomic */) { + } else if (todo.isParentLoaded(this) && todo.getParent().findChild(todo.id) >= 0 /* because move/insert is nonatomic */) { this.setView(todo, view); - var parent = todo.getParent(this.list); + var parent = todo.getParent(); var parentView = this.getView(parent); parentView.addChild(view.render().el, parent.findChild(todo.id)); } // Find unrendered descendents and render them, too. - _.each(todo.getChildren(this.list), function(child) { + _.each(todo.getChildren(), function(child) { if (child && !this.getView(child)) { this.renderTodo(child); } diff --git a/src/views/todo.js b/src/views/todo.js index a7a48f0..5c0b41d 100644 --- a/src/views/todo.js +++ b/src/views/todo.js @@ -68,21 +68,21 @@ var TodoView = Backbone.View.extend({ } else { this.stopEditingText(); this.model.toggleComplete(); - var next = this.model.nextNode(this.model.collection, { childrenAllowed: false}) || this.model.getParent(this.model.collection, { rootAllowed: false}); + var next = this.model.nextNode({ childrenAllowed: false}) || this.model.getParent({ rootAllowed: false}); if (!next) return false; next.getView().startEditingText(); } return false; }, focusNext: function() { - var nextNode = this.model.nextNode(this.model.collection, { childrenAllowed: true }); + var nextNode = this.model.nextNode({ childrenAllowed: true }); if (!nextNode) return false; this.stopEditingText(); nextNode.getView().startEditingText(); return false; }, focusPrevious: function() { - var previousNode = this.model.previousNode(this.model.collection, { childrenAllowed: true }); + var previousNode = this.model.previousNode({ childrenAllowed: true }); if (!previousNode) return false; this.stopEditingText(); previousNode.getView().startEditingText(); @@ -92,17 +92,17 @@ var TodoView = Backbone.View.extend({ if (this.model.hasChildren()) { return; } - var previousNode = this.model.previousNode(this.model.collection); + var previousNode = this.model.previousNode(); if (!previousNode) { return; } if (this.model.get("text") === "") { - this.model.remove(this.model.collection); + this.model.remove(); previousNode.getView().startEditingText({"atEnd":true}); return false; } else if (this.isFocusAtBeginning()) { var text = this.model.get("text"); - this.model.remove(this.model.collection); + this.model.remove(); previousNode.setText(previousNode.get("text") + '' + text); previousNode.getView().startEditingText({"atMarker": ".focus"}); return false; @@ -112,17 +112,17 @@ var TodoView = Backbone.View.extend({ if (this.model.hasChildren()) { return; } - var nextNode = this.model.nextNode(this.model.collection); + var nextNode = this.model.nextNode(); if (!nextNode) { return; } if (this.model.get("text") === "") { - this.model.remove(this.model.collection); + this.model.remove(); nextNode.getView().startEditingText(); return false; } else if (this.isFocusAtEnd()) { var text = nextNode.get("text"); - nextNode.remove(this.model.collection); + nextNode.remove(); this.stopEditingText(); this.model.setText(this.model.get("text") + '' + text); this.startEditingText({"atMarker": ".focus"}); @@ -132,17 +132,17 @@ var TodoView = Backbone.View.extend({ "delete": function() { // Delete node and its entire subtree this.focusPrevious(); - this.model.removeAll(this.model.collection); + this.model.removeAll(); return false; }, indent: function() { // TODO: maintain focus - this.model.indent(this.model.collection); + this.model.indent(); return false; }, outdent: function() { // TODO: maintain focus - this.model.outdent(this.model.collection); + this.model.outdent(); }, expand: function() { console.log("Expand not implemented"); // TODO @@ -157,14 +157,13 @@ var TodoView = Backbone.View.extend({ }, moveDown: function() { // TODO: maintain focus - this.model.moveDown(this.model.collection); + this.model.moveDown(); }, moveUp: function() { // TODO: maintain focus - this.model.moveUp(this.model.collection); + this.model.moveUp(); }, textChange: function(e) { - var collection = this.model.collection; var lines = $(e.target).html().split(//); if (lines.length === 0) { console.log("unexpected number of lines in textChange"); @@ -189,16 +188,16 @@ var TodoView = Backbone.View.extend({ this.outdent(); } else if ((lines.length === 2 && lines[1] === "") || (lines.length === 3 && lines[1] === "" && lines[2] === "")) { // Line break at end this.model.setText(this.decodeText(lines[0])); - var emptyAfter = this.model.addTodoAfter({text: this.decodeText(lines[1])}, collection); // Child or not depending on whether this has children + var emptyAfter = this.model.addTodoAfter({text: this.decodeText(lines[1])}); // Child or not depending on whether this has children this.stopEditingText(); emptyAfter.getView().startEditingText(); } else if (lines.length === 2 && lines[0] === "") { // Line break at beginning - var emptyBefore = this.model.addTodoBefore({text: this.decodeText(lines[0])}, collection); + var emptyBefore = this.model.addTodoBefore({text: this.decodeText(lines[0])}); this.model.setText(this.decodeText(lines[1])); this.stopEditingText(); emptyBefore.getView().startEditingText(); } else if (lines.length === 2) { // Line break in middle - var newNode = this.model.addTodoAfter({text: this.decodeText(lines[1])}, collection); + var newNode = this.model.addTodoAfter({text: this.decodeText(lines[1])}); this.model.setText(this.decodeText(lines[0])); this.stopEditingText(); // For re-render newNode.getView().startEditingText(); // Keep focus on current node (second half) -- 2.47.3