diff --git a/public/javascripts/collections/list.js b/public/javascripts/collections/list.js
index a2467e5..b702b45 100644
--- a/public/javascripts/collections/list.js
+++ b/public/javascripts/collections/list.js
@@ -29,6 +29,11 @@ localforageBackbone
return Backbone.sync.apply(this, arguments);
},
+ /** sort by priority then created date **/
+ comporator: function(child){
+ return [child.get('priority'),child.get('createdAt')];
+ },
+
url: '/tasks'
});
diff --git a/public/javascripts/util/constants.js b/public/javascripts/util/constants.js
index 52cc4bb..b6c8006 100644
--- a/public/javascripts/util/constants.js
+++ b/public/javascripts/util/constants.js
@@ -9,6 +9,7 @@ function() {
UP_ARROW: 38,
DOWN_ARROW: 40,
TAB: 9,
+ CNTRL: 17,
};
diff --git a/public/javascripts/views/list.js b/public/javascripts/views/list.js
index c295db6..81cb339 100644
--- a/public/javascripts/views/list.js
+++ b/public/javascripts/views/list.js
@@ -18,8 +18,7 @@ define(
Marionette
) {
- // The tree's root: a simple collection view that renders
- // a recursive tree structure for each item in the collection
+ // renders recursive tree structure for each item in collection
var ListView = Backbone.Marionette.CollectionView.extend({
el: $("#main .children"),
@@ -33,9 +32,7 @@ define(
initialize: function () {
var self = this;
- Tasks = new List();
-
- // this.listenTo(this.collection, 'add', this.renderTask);
+ this.collection = Tasks = new List();
/** Load demo data **/
function loadDemoData() {
@@ -50,7 +47,6 @@ define(
if (children.length === 0)
loadDemoData();
else {
- this.collection = {models:Tasks.filter({'parentId':0})};
this.render();
}
}
@@ -60,7 +56,8 @@ define(
error: function () {
// switch to localforage database if server isn't present and fetch again
- window.hackflowyOffline=true;
+ // from there
+ window.hackflowyOffline = true;
$('#header').append('
Running in offline mode, data may be lost
');
Tasks.fetch({
success: success,
@@ -74,63 +71,8 @@ define(
// Only show direct children
filter: function (child, index, collection) {
- return child.get('parentId') === 0;
- },
-
- // render: function () {
- // var models = Tasks.filter({'parentId':0});
- //
- // _.each(models, function(model, index) {
- // var taskView = new this.childView({
- // model: model,
- // collection: model.collection
- // });
- // var a = taskView.render();
- // this.$el.append(a.el);
- // }, this);
- //
- // this.triggerMethod('render', this);
- // return this;
- // },
-
- renderTask: function (task) {
- var taskView = new TaskView({
- model: task
- });
- var a = taskView.render();
- this.$el.append(a.el);
- //
- // if (a.model.get('parentId') === 0) {
- // // insert it at the end of the top-level items
- // this.$el.append(a.el);
- // } else {
- //
- // // TODO use dom nesting instead of data attributes
- // var parent = $('*[data-id="' + a.model.get('parentId') + '"]').parents('li:first');
- // var siblings = parent.find('.children:first li');
- // var editingSibling = siblings.filter('.editing:first');
- //
- // if (editingSibling.length > 0){
- // // insert it after open sibling
- // a.$el.insertAfter(editingSibling);
- // }
- // else if (parent.length > 0) {
- // // insert under parent
- // a.$el.appendTo(parent.find('.children:first'));
- // } else {
- // // we have an orphan :(
- // // insert at root for lack of a better option
- // this.$el.append(a.el);
- //
- // // TODO deal with loading order
- // console.error("Parent not rendered yet: ", {
- // selector: parent.selector,
- // task: task
- // });
- //
- // }
- // }
- }
+ return child.get('parentId') === 0;
+ },
});
diff --git a/public/javascripts/views/task.js b/public/javascripts/views/task.js
index 9b6c2c6..9d1f66b 100644
--- a/public/javascripts/views/task.js
+++ b/public/javascripts/views/task.js
@@ -1,5 +1,3 @@
-
-
define(
['jquery',
'backbone',
@@ -55,7 +53,7 @@ define(
this.listenTo(this.model, 'destroy', this.remove);
// updates from server
- if (!window.hackflowyOffline){
+ if (!window.hackflowyOffline) {
this.socket = io.connect();
this.socket.on('task', function (data) {
if (task.model.id == data.id) {
@@ -64,16 +62,23 @@ define(
'isCompleted': data.isCompleted
});
} else {
- console.error("task.model.id != data.id",task.model.id , data.id);
+ console.error("task.model.id != data.id", task.model.id, data.id);
}
});
}
},
- // Only show direct children
+ // override marionette filter to filter displayed children
filter: function (child, index, collection) {
- return child.get('parentId') === this.model.get('id');
+ return child.get('parentId') === this.model.get('id');
+ },
+
+ /** Get the parent view or root view **/
+ getParentView: function(){
+ var parentId = this.model.get('parentId');
+ if (parentId>0) return Tasks.get(parentId).view;
+ else return listView;
},
edit: function () {
@@ -84,32 +89,58 @@ define(
handleKey: function (e) {
if (e.keyCode === constants.ENTER_KEY)
this.addNote(e.currentTarget);
- if (e.keyCode == constants.DOWN_ARROW)
- this.$el.next('li').find('input').focus();
- else if (e.keyCode == constants.UP_ARROW)
- this.$el.prev('li').find('input').focus();
-
- // shift and tab
- if (e.shiftKey && e.keyCode == constants.TAB) {
+ else if (e.ctrlKey && e.keyCode == constants.DOWN_ARROW){
e.preventDefault();
- var newParentId = this.$el.parents('ul:first').find('input:first').data('parent-id');
- if (newParentId === null) newParentId = 0;
- this.model.set('parentId', newParentId);
- this.model.save();
- Tasks.get(newParentId).view.render()
+ this.model.save({priority: this.model.get('priority')-1});
+ this.getParentView().collection.sortBy();
+ this.getParentView().resortView();
this.model.view.ui.input.focus();
+ } else if (e.ctrlKey && e.keyCode == constants.UP_ARROW){
+ e.preventDefault();
+ this.model.save({priority: this.model.get('priority')+1});
+ this.getParentView().collection.sortBy();
+ this.getParentView().resortView();
+ this.model.view.ui.input.focus();
+ } else if (e.keyCode == constants.DOWN_ARROW){
+ var all = listView.$el.find('ul:visible');
+ var next = $(all[all.index(this.$el)+1]);
+ if (next)
+ next.find('input:first').focus();
+ } else if (e.keyCode == constants.UP_ARROW){
+ var all = listView.$el.find('ul:visible');
+ var prev = $(all[all.index(this.$el)-1]);
+ if (prev)
+ prev.find('input:first').focus();
}
+ // indent one less, by changing parent
+ else if (e.shiftKey && e.keyCode == constants.TAB) {
+ e.preventDefault();
+ var parent = this.$el.parents('ul:first');
+ var grandparentId = parent.find('input:first').data('parent-id') || 0;
+ if (this.model.get('parentId') !== grandparentId) {
+ this.model.save({parentId: grandparentId});
+ this.getParentView().render();
+ this.model.view.ui.input.focus();
+ } else {
+ console.warn("Can't untab any further");
+ }
+ }
+ // indent one more, by changing parent
else if (e.keyCode == constants.TAB) {
e.preventDefault();
- var newParentId = this.$el.prev('ul').find('input:first').data('id');
- this.model.set('parentId', newParentId);
- this.model.save();
- this.model.view.remove();
- Tasks.get(newParentId).view.render();
- this.model.view.ui.input.focus();
+ var prevSibling = this.$el.prev('ul');
+ if (prevSibling.length > 0) {
+ var siblingId = prevSibling.find('input:first').data('id');
+ this.model.save({parentId: siblingId});
+ this.model.view.remove();
+ this.getParentView().render();
+ this.model.view.ui.input.focus();
+ } else {
+ console.warn("Can't tab any further");
+ }
}
- if (!window.hackflowyOffline){
+ if (!window.hackflowyOffline) {
this.socket.emit('task', {
id: this.model.id,
parentId: this.model.parentId,
@@ -126,9 +157,12 @@ define(
var value = this.$('.edit:first').val().trim();
if (value === '')
// remove empty items
+ // TODO let us tab and untab empty ones
this.model.destroy();
else
- this.model.save({content: value});
+ this.model.save({
+ content: value
+ });
this.$el.removeClass('editing');
},
@@ -162,6 +196,7 @@ define(
/** Add a new blank note **/
addNote: function () {
+ // TODO add it after the current one
var currentId = this.ui.input.data('id') || 0;
parentId = this.ui.input.data('parent-id');
@@ -173,7 +208,7 @@ define(
},
/** Fold children of the clicked element */
- foldChildren: function(){
+ foldChildren: function () {
this.$el.find('ul').toggle();
this.$el.find('li').toggleClass('folded');
},