Implemented notes UI.
- Switched from a global reset in www/css/ts-screen.scss to a selected top-level elements reset to allow default formatting for user notes. - Restructured the #entry-list and entry displays. - Restructured notes div, now has a sub-div for text and a textarea element for input. - Added Showdown.js, a JavaScript Markdown library for formatting comments. - Moved EntryView blur events to the View events map. - Added images for expansion of notes. - Added the ability to edit notes. - Split EntryListView.addOne into renderOne and addOne so that renderOne can be called with a new entry (fixes duration glitch)
This commit is contained in:
94
www/js/ts.js
94
www/js/ts.js
@ -91,18 +91,30 @@ $(document).ready(function(){
|
||||
|
||||
className: 'entry',
|
||||
|
||||
notesCache: false,
|
||||
|
||||
events: {
|
||||
"click img.notes-icon" : "toggleNotes",
|
||||
"click img.expand-entry" : "showNotes",
|
||||
"click img.collapse-entry" : "hideNotes",
|
||||
"dblclick div.mark" : "editMark",
|
||||
"dblclick div.timestamp" : "editTimestamp",
|
||||
"dblclick div.notes" : "editNotes",
|
||||
"keypress .mark-input" : "updateOnEnter",
|
||||
"keypress .timestamp-input" : "updateOnEnter"
|
||||
"keypress .timestamp-input" : "updateOnEnter",
|
||||
"keypress .notes-input" : "updateOnCtrlEnter",
|
||||
"blur .mark-input" : "close",
|
||||
"blur .timestamp-input" : "close",
|
||||
"blur .notes-input" : "close"
|
||||
},
|
||||
|
||||
initialize: function(options) {
|
||||
_.bindAll(this, 'render', 'close', 'editTImestamp',
|
||||
'editMark', 'updateOnEnter', 'getViewModel', 'toggleNotes');
|
||||
this.model.bind('change', this.render);
|
||||
_.bindAll(this, 'render', 'close', 'editTImestamp', 'editMark',
|
||||
'update', 'updateOnEnter', 'updateOnCtrlEnter', 'getViewModel',
|
||||
'renderNotes', 'showNotes', 'hideNotes');
|
||||
|
||||
this.markdownConverter = options.markdownConverter;
|
||||
|
||||
this.model.bind('change', this.update);
|
||||
this.model.view = this;
|
||||
|
||||
this.nextModel = options.nextModel;
|
||||
@ -113,9 +125,12 @@ $(document).ready(function(){
|
||||
* HTML content. Add new `blur` listeners to the input fields.
|
||||
*/
|
||||
render: function() {
|
||||
// render the HTML
|
||||
$(this.el).html(ich.entryTemplate(this.getViewModel()));
|
||||
this.$(".mark-input").bind('blur', this.close);
|
||||
this.$(".timestamp-input").bind('blur', this.close);
|
||||
|
||||
// invalidate the notes display cache
|
||||
this.notesCache = false;
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
@ -130,9 +145,19 @@ $(document).ready(function(){
|
||||
this.$('.timestamp').text(data.start);
|
||||
this.$('.timestamp-input').val(data.timestamp);
|
||||
this.$('.duration').text(data.duration);
|
||||
this.$('.notes-text').html(this.renderNotes(data.notes));
|
||||
this.$('.notes-input').val(data.notes);
|
||||
return this;
|
||||
},
|
||||
|
||||
renderNotes: function(source) {
|
||||
if (!this.notesCache) {
|
||||
this.notesCache = this.markdownConverter.makeHtml(source);
|
||||
}
|
||||
|
||||
return this.notesCache
|
||||
},
|
||||
|
||||
editMark: function() {
|
||||
$(this.el).addClass('edit-mark');
|
||||
this.$('.mark-input').focus();
|
||||
@ -145,6 +170,18 @@ $(document).ready(function(){
|
||||
return this;
|
||||
},
|
||||
|
||||
editNotes: function() {
|
||||
// invalidate notes HTML cache
|
||||
this.notesCache = false;
|
||||
|
||||
// show notes textarea, hide display
|
||||
$(this.el).addClass('edit-notes');
|
||||
|
||||
// focus input
|
||||
this.$('.notes-input').focus();
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Translate the model data into a form suitable to be displayed.
|
||||
* @return a map including display-able `start` and `duration` values.
|
||||
@ -156,20 +193,21 @@ $(document).ready(function(){
|
||||
var tsDate = new Date(data.timestamp);
|
||||
data.start = this.formatStart(tsDate);
|
||||
data.duration = this.formatDuration(this.model, this.nextModel);
|
||||
data.notes = data.notes ? data.notes : '*No notes for this entry.*';
|
||||
return data;
|
||||
},
|
||||
|
||||
/** Close editable fields. */
|
||||
close: function() {
|
||||
$(this.el).removeClass('edit-mark edit-timestamp');
|
||||
$(this.el).removeClass('edit-mark edit-timestamp edit-notes');
|
||||
},
|
||||
|
||||
/** Persist changes in input fields. */
|
||||
save: function() {
|
||||
this.model.save({
|
||||
mark: this.$('.mark-input').val(),
|
||||
timestamp: this.$('.timestamp-input').val()});
|
||||
this.update();
|
||||
timestamp: this.$('.timestamp-input').val(),
|
||||
notes: this.$('.notes-input').val()});
|
||||
},
|
||||
|
||||
/** Event handler for keypresses on entry input fields. */
|
||||
@ -177,6 +215,10 @@ $(document).ready(function(){
|
||||
if(e.keyCode == 13) { this.save(); this.close(); }
|
||||
},
|
||||
|
||||
updateOnCtrlEnter: function(e) {
|
||||
if (e.keyCode == 10) { this.save(); this.close(); }
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the display-able start time from the entry timestamp.
|
||||
* @param startDate a Date object, the entry timestamp.
|
||||
@ -229,8 +271,19 @@ $(document).ready(function(){
|
||||
min + "m ";
|
||||
},
|
||||
|
||||
toggleNotes: function() {
|
||||
this.$('.notes').slideToggle();
|
||||
showNotes: function() {
|
||||
if (!this.notesCache) {
|
||||
this.$('.notes-text').html(
|
||||
this.renderNotes(this.model.get('notes')))
|
||||
}
|
||||
|
||||
this.$('.notes').slideDown();
|
||||
$(this.el).addClass('show-notes');
|
||||
},
|
||||
|
||||
hideNotes: function() {
|
||||
this.$('.notes').slideUp();
|
||||
$(this.el).removeClass('show-notes');
|
||||
}
|
||||
});
|
||||
|
||||
@ -243,15 +296,24 @@ $(document).ready(function(){
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
_.bindAll(this, 'addOne', 'createNewEntry', 'render');
|
||||
_.bindAll(this, 'addOne', 'createNewEntry', 'render', 'renderOne');
|
||||
this.collection.bind('add', this.addOne);
|
||||
this.collection.bind('refresh', this.render);
|
||||
this.collection.view = this;
|
||||
this.entryContainer = this.$("#entries")
|
||||
this.markdownConverter = new Showdown.converter();
|
||||
},
|
||||
|
||||
addOne: function(entry, nextEntry) {
|
||||
if (!entry.view) { new TS.EntryView({model: entry}); }
|
||||
addOne: function(entry) {
|
||||
var lastEntry = this.collection.at(this.collection.length - 2);
|
||||
lastEntry.view.nextModel = entry;
|
||||
lastEntry.view.update();
|
||||
this.renderOne(entry, null);
|
||||
},
|
||||
|
||||
renderOne: function(entry, nextEntry) {
|
||||
if (!entry.view) { new TS.EntryView(
|
||||
{model: entry, markdownConverter: this.markdownConverter}); }
|
||||
entry.view.nextModel = nextEntry
|
||||
this.entryContainer.prepend(entry.view.render().el);
|
||||
},
|
||||
@ -280,7 +342,7 @@ $(document).ready(function(){
|
||||
var entry = this.collection.at(i);
|
||||
var nextEntry = (i + 1 < len ? this.collection.at(i + 1) : null);
|
||||
|
||||
this.addOne(entry, nextEntry);
|
||||
this.renderOne(entry, nextEntry);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
Reference in New Issue
Block a user