Merge branch 'master' into gh-pages

Conflicts:
	javascripts/vendor/custom.modernizr.js
	javascripts/vendor/zepto.js
	mockup.html
	stylesheets/normalize.css
This commit is contained in:
2016-02-01 17:05:29 +08:00
17 changed files with 343 additions and 2569 deletions
+44 -51
View File
@@ -3,7 +3,6 @@ define(
'backbone',
'collections/list',
'views/task',
'data/demo',
'text!../../templates/task.html',
'marionette'
],
@@ -13,7 +12,6 @@ define(
Backbone,
List,
TaskView,
demoData,
listTemplate,
Marionette
) {
@@ -27,59 +25,53 @@ define(
template: _.template(listTemplate),
events: {
'click #add': 'addTask'
'click #add': 'addTask',
},
initialize: function () {
var self = this;
// this wholeCollection holds all items
this.wholeCollection = Tasks = new List();
//this.collection = new List();
// custom events
this.listenTo(this, 'childview:rerender', this.render);
// this.listenTo(this.collection, 'add remove', this.render);
/** Load demo data **/
function loadDemoData() {
for (var i = 0; i < demoData.length; i++) {
var task = Tasks.add(demoData[i]);
task.save();
}
}
function success(children, data, promise) {
// load demo data if the server returns nothing
var directChildren = children.filter(this.filterDirectChildren);
if (directChildren.length === 0)
loadDemoData();
this.collection = new List(Tasks.filter(this.filterDirectChildren));
this.render();
}
Tasks.fetch({
success: success,
error: function () {
// switch to localforage database if server isn't present and fetch again
// from there
window.hackflowyOffline = true;
$('#header').append('<div class="alert-box secondary round">Running in offline mode, data may be lost </div>');
Tasks.fetch({
success: success,
context: this
});
},
context: this
});
},
/** Get root id for the displayed list from the url hash or 0 **/
// getRootId: function(){
// var hash = window.location.hash.slice(1);
// return hash ? hash: 0;
//
// },
// /** Called when window location hash changes **/
// changeRootId: function(){
// this.collection.remove(this.collection.models);
// this.collection.add(pageView.collection.filter(this.filterDirectChildren,this));
// this.updateBreadCrumbs();
// },
// updateBreadCrumbs: function(){
// var rootId = this.getRootId();
// if (rootId){
// var current = Tasks.get(rootId);
// var breadCrumbs = _.template('<a href="#<%= id %>"><%= content %></a>')(current.attributes);
// var depth=0;
// while (current.get('parentId') && depth<100){
// depth++;
// current = Tasks.get(current.get('parentId'));
// breadCrumbs=_.template('<a href="#<%= id %>"><%= content %></a> > ')(current.attributes)+breadCrumbs;
// }
// if (depth>=100) console.error('Max depth exceeded while making breadCrumbs');
// breadCrumbs='<a href="#">Home</a> > '+breadCrumbs;
// $('#task-breadcrumbs').append(breadCrumbs);
//
// }
// },
// Only show direct children
filterDirectChildren: function (child, index, collection) {
return child.get('parentId') === 0;
},
// filterDirectChildren: function (child, index, collection) {
// var rootId = this.getRootId();
// if (rootId)
// return child.get('id') == rootId;
// else
// return child.get('parentId') == rootId;
// },
/** This is the root view in the tree **/
getParentView: function () {
@@ -88,8 +80,9 @@ define(
/** Update parentId when added to collection **/
onAddChild: function(childView){
if (childView.model.get('parentId')!==0)
childView.model.save({parentId: 0});
var rootId = pageView.getRootId();
if (childView.model.get('parentId')!=rootId && childView.model==rootId)
childView.model.save({parentId: rootId});
},
});
+104 -15
View File
@@ -1,41 +1,130 @@
define(
['jquery',
'backbone',
'marionette',
'views/list',
'models/task'
'data/demo',
'models/task',
'collections/list'
],
function(
$,
Backbone,
Marionette,
ListView,
Task
demoData,
Task,
List
) {
var PageView = Backbone.View.extend({
var PageView = Marionette.View.extend({
el: $("#main"),
el: $("#hackflowy"),
events: {
'keypress #newTask': 'createNewTask',
'blur #newTask': 'createNewTask'
},
initialize: function() {
listView = this.listView = new ListView();
this.input = $('#newTask');
// this wholeCollection holds all items
this.collection = new List();
function success(children, data, promise) {
// load demo data if the server returns nothing
var directChildren = children.filter(this.filterDirectChildren,this);
if (directChildren.length === 0){
for (var i = 0; i < demoData.length; i++) {
var task = this.collection.add(demoData[i]);
task.save();
}
}
this.listView.collection.add(children.filter(this.filterDirectChildren,this));
this.listView.render();
this.updateBreadCrumbs();
}
this.collection.fetch({
success: success,
error: function () {
// switch to localforage database if server isn't present and fetch again
// from there
window.hackflowyOffline = true;
this.$('#header-alerts').append('<div class="alert-box secondary round">Running in offline mode, data may be lost </div>');
this.collection.fetch({
success: success,
context: this
});
},
context: this
});
var rootItems = new List(this.collection.filter(this.filterDirectChildren,this));
this.listView = new ListView({collection: rootItems});
// change root item when the url hash changes
// HACK should ideally use http://stackoverflow.com/a/19114496/221742
$(window).on("hashchange", this.changeRootId.bind(this));
// stop browser going back a page when jamming backspace
window.addEventListener('keydown',function(e){if(e.keyIdentifier=='U+0008'||e.keyIdentifier=='Backspace'){if(e.target==document.body){e.preventDefault();}}},true);
$(window).on('keydown',function(e){if(e.keyIdentifier=='U+0008'||e.keyIdentifier=='Backspace'){if(e.target==document.body){e.preventDefault();}}});
},
createNewTask: function(e) {
if (e.keyCode != 13) return;
if (!this.input.val().trim()) return;
this.listView.collection.add(new Task({content: this.input.val().trim() }));
this.input.val('');
}
/** remove non backbone listener on delete **/
onDestroy: function() {
$(window).off("haschange",this.changeRootId);
$(window).off('keydown',function(e){if(e.keyIdentifier=='U+0008'||e.keyIdentifier=='Backspace'){if(e.target==document.body){e.preventDefault();}}});
},
/** Get root id for the displayed list from the url hash or 0 **/
getRootId: function(){
var hash = window.location.hash.slice(1);
if (hash && pageView.collection.get(hash))
return hash;
else if (hash)
window.location.hash='';
return 0;
},
/** Called when window location hash changes **/
changeRootId: function(){
// change the listview children
this.listView.collection.remove(this.collection.models);
this.listView.collection.add(this.collection.filter(this.filterDirectChildren,this));
this.updateBreadCrumbs();
},
// Only show direct children
filterDirectChildren: function (child, index, collection) {
var rootId = this.getRootId();
if (rootId)
return child.get('id') == rootId;
else
return child.get('parentId') == rootId;
},
updateBreadCrumbs: function(){
var rootId = this.getRootId();
this.$('#task-breadcrumbs').empty();
if (rootId){
var current = this.collection.get(rootId);
var breadCrumbs = '';//_.template('<a href="#<%= id %>"><%= content %></a>')(current.attributes);
var depth=0;
while (current.get('parentId') && depth<100){
depth++;
current = this.collection.get(current.get('parentId'));
breadCrumbs=_.template('<a href="#<%= id %>"><%= content %></a> > ')(current.attributes)+breadCrumbs;
}
if (depth>=100) console.error('Max depth exceeded while making breadCrumbs');
breadCrumbs='<a href="#">Home</a> > '+breadCrumbs;
this.$('#task-breadcrumbs').append(breadCrumbs);
}
},
});
+23 -20
View File
@@ -20,9 +20,10 @@ define(
// The recursive tree view. Ref:http://jsfiddle.net/wassname/zf61mLvh/2/
var TaskView = Backbone.Marionette.CompositeView.extend({
template: _.template(taskTemplate), //'#task-view-template',
template: _.template(taskTemplate),
tagName: 'ul',
className: "shift",
className: "task-view",
viewComparator: List.prototype.comporator,
childView: TaskView,
childViewContainer: '.children',
childViewOptions: {
@@ -50,38 +51,42 @@ define(
'click .complete:first': 'markComplete',
'click .uncomplete:first': 'unmarkComlete',
'click .note:first': 'addNote',
'click .mouse-tip:first': 'foldChildren',
'click .fold-button:first': 'foldChildren',
'click .fold:first': 'foldChildren',
'click .destroy:first': 'destroy',
// custom events
'focus': 'this.model.focusOnView',
},
collectionEvents: {},
collectionEvents: {
},
childEvents: {},
initialize: function (options) {
var task = this;
// options
if (!('reorderOnSort' in options)) {
options.reorderOnSort = true;
}
// backlink
this.model.view = this;
var children = Tasks.filter(
var children = pageView.collection.filter(
function (child, index, collection) {
return child.get('parentId') === this.model.id;
}, this);
this.collection = new List(children);
// there is probobly a better way to do this
if (this.isEmpty()){
this.$el.addClass('empty');
}
// events
this.listenTo(this.model, 'change', this.render);
this.listenTo(this.model, 'destroy', this.remove);
// refresh ui hashes after children are rendered
this.listenTo(this, 'render:collection', this.bindUIElements);
// custom event
this.listenTo(this, 'childview:rerender', this.render);
this.listenTo(this, 'focus', this.model.focusOnView);
// updates from server
@@ -113,9 +118,9 @@ define(
/** Get the parent view or root view **/
getParentView: function () {
var parent = Tasks.get(this.model.get('parentId'));
var parent = pageView.collection.get(this.model.get('parentId'));
if (parent) return parent.view;
else return listView;
else return pageView.listView;
},
/** Update parentId when added to collection **/
@@ -126,7 +131,7 @@ define(
/** Focus on the next visible element down despite list level **/
focusOnPrev: function () {
var all = listView.$el.find('ul:visible');
var all = pageView.listView.$el.find('ul:visible');
var prev = $(all[all.index(this.$el) - 1]);
if (prev.length){
prev.find('input:first').focus();
@@ -135,7 +140,7 @@ define(
},
focusOnNext: function () {
var all = listView.$el.find('ul:visible');
var all = pageView.listView.$el.find('ul:visible');
var next = $(all[all.index(this.$el) + 1]);
if (next)
next.find('input:first').focus();
@@ -169,13 +174,11 @@ define(
// move down list by swapping priority with next sibling
e.preventDefault();
this.getParentView().collection.moveDown(this.model);
this.trigger('rerender');
this.model.focusOnView();
} else if (e.ctrlKey && e.keyCode == constants.UP_ARROW) {
// move up the list
e.preventDefault();
this.getParentView().collection.moveUp(this.model);
this.trigger('rerender');
this.model.focusOnView();
} else if (e.keyCode == constants.DOWN_ARROW) {
this.focusOnNext();
@@ -188,7 +191,7 @@ define(
if (parentId===0){
} else if (parentId){
parentModel = Tasks.get(parentId);
parentModel = pageView.collection.get(parentId);
this.getParentView().collection.remove(this.model);
var index = parentModel.view.getParentView().collection.indexOf(parentModel);
if (index<0) index=this.getParentView().collection.length-1;
@@ -203,7 +206,7 @@ define(
var prevSibling = this.$el.prev('ul');
if (prevSibling.length) {
var prevSibId = prevSibling.find('input:first').data('id');
var prevSibView =Tasks.get(prevSibId).view;
var prevSibView =pageView.collection.get(prevSibId).view;
this.getParentView().collection.remove(this.model);
prevSibView.collection.add(this.model);
this.model.focusOnView();
@@ -269,7 +272,7 @@ define(
var index= this.getParentView().collection.indexOf(this.model);
var task = Tasks.add({parentId: this.model.get('parentId')});
var task = pageView.collection.add({parentId: this.model.get('parentId')});
task.save();
if (index>=0)
this.getParentView().collection.add(task, {at:index+1});