Trying to tighten up the design. More functionality implemented.
This commit is contained in:
295
www/js/ts.js
295
www/js/ts.js
@ -20,7 +20,7 @@ $(document).ready(function(){
|
||||
/* Timeline model.
|
||||
* Attributes:
|
||||
* - id
|
||||
* - desc
|
||||
* - description
|
||||
* - created
|
||||
*/
|
||||
TS.TimelineModel = Backbone.Model.extend({
|
||||
@ -56,7 +56,7 @@ $(document).ready(function(){
|
||||
},
|
||||
|
||||
url: function() {
|
||||
return "/entries/" + this.timeline.get('user_id') + "/" + this.timeline.get('id');
|
||||
return "/ts_api/entries/" + this.timeline.get('user_id') + "/" + this.timeline.get('id');
|
||||
}
|
||||
});
|
||||
|
||||
@ -89,6 +89,8 @@ $(document).ready(function(){
|
||||
|
||||
model: TS.EntryModel,
|
||||
|
||||
className: 'entry',
|
||||
|
||||
events: {
|
||||
"dblclick div.mark" : "editMark",
|
||||
"dblclick div.timestamp" : "editTimestamp",
|
||||
@ -104,7 +106,7 @@ $(document).ready(function(){
|
||||
},
|
||||
|
||||
render: function() {
|
||||
$(this.el).html(ich.entry(this.model.toJSON()));
|
||||
$(this.el).html(ich.entryTemplate(this.model.toJSON()));
|
||||
return this;
|
||||
},
|
||||
|
||||
@ -125,6 +127,7 @@ $(document).ready(function(){
|
||||
mark: this.$('.mark-input').val(),
|
||||
timestamp: this.$('.timestamp-input').val()});
|
||||
$(this.el).removeClass('edit-mark edit-timestamp');
|
||||
this.render();
|
||||
},
|
||||
|
||||
updateOnEnter: function(e) {
|
||||
@ -137,7 +140,7 @@ $(document).ready(function(){
|
||||
el: $("#entry-list"),
|
||||
|
||||
events: {
|
||||
"#new-entry" : "createNewEntry"
|
||||
"keypress #new-entry-input" : "createNewEntryOnEnter"
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
@ -153,10 +156,18 @@ $(document).ready(function(){
|
||||
this.entryContainer.prepend(entry.view.render().el);
|
||||
},
|
||||
|
||||
createNewEntry: function() {
|
||||
var entryMark = this.$("#new-entry-input").val();
|
||||
var newEntry = TS.EntryModel({mark: entryMark});
|
||||
this.collection.create({mark: entryMark}).fetch();
|
||||
createNewEntryOnEnter: function(e) {
|
||||
|
||||
if (e.keyCode == 13) {
|
||||
|
||||
// grab the mark data
|
||||
var entryMark = this.$("#new-entry-input").val();
|
||||
|
||||
// create the mark. Immediately fetch to get server-side timestamp
|
||||
this.collection.create({mark: entryMark,
|
||||
notes: '',
|
||||
timestamp: getUTCTimestamp()}).fetch();
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
@ -165,11 +176,10 @@ $(document).ready(function(){
|
||||
}
|
||||
});
|
||||
|
||||
TS.TimelineView = Backbone.View.extend({
|
||||
|
||||
TS.TimelineListView = Backbone.View.extend({
|
||||
el: $("#timeline"),
|
||||
|
||||
model: TS.TimelineModel,
|
||||
collection: TS.TimelineList,
|
||||
|
||||
events: {
|
||||
"dblclick .timeline-id" : "editId",
|
||||
@ -178,18 +188,31 @@ $(document).ready(function(){
|
||||
"keypress .timeline-desc-input" : "updateOnEnter"
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
_.bindAll(this, 'render', 'close', 'editId', 'editDesc',
|
||||
'updateOnEnter');
|
||||
this.model.bind('change', this.render);
|
||||
this.model.view = this;
|
||||
initialize: function(options) {
|
||||
_.bindAll(this, 'render', 'renderOne', 'editId',
|
||||
'editDesc', 'updateOnEnter');
|
||||
|
||||
if (options.initialTimelineId == undefined) {
|
||||
throw "Can not create a TimelineListView without an initial timeline."
|
||||
} else {
|
||||
this.selected = this.collection.get(options.initialTimelineId);
|
||||
}
|
||||
|
||||
this.collection.bind('add', this.renderOne);
|
||||
this.collection.bind('refresh', this.render);
|
||||
},
|
||||
|
||||
renderOne: function(timeline) {
|
||||
this.$('.drop-menu-items').append(
|
||||
ich.timelineLinkTemplate(timeline.toJSON()));
|
||||
},
|
||||
|
||||
render: function() {
|
||||
this.$('.timeline-id').html('( ' +
|
||||
this.model.get('id') + ' )');
|
||||
this.$('.timeline-desc').text(this.model.get('desc'));
|
||||
return this;
|
||||
// render the basic template
|
||||
$(this.el).html(ich.timelineTemplate(this.selected.toJSON()));
|
||||
|
||||
// render the selection list
|
||||
_.each(this.collection.without([this.selected]), this.renderOne);
|
||||
},
|
||||
|
||||
editId: function() {
|
||||
@ -205,39 +228,17 @@ $(document).ready(function(){
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.model.save({
|
||||
id: this.$('timeline-id-input').val(),
|
||||
desc: this.$('.timeline-desc-input').val()});
|
||||
$(this.el).removeClass('.edit-id .edit-desc');
|
||||
this.selected.save({
|
||||
id: this.$('.timeline-id-input').val(),
|
||||
description: this.$('.timeline-desc-input').val()});
|
||||
$(this.el).removeClass('edit-id edit-desc');
|
||||
this.render();
|
||||
},
|
||||
|
||||
updateOnEnter: function(e) {
|
||||
if (e.keyCode == 13) this.close();
|
||||
}
|
||||
});
|
||||
|
||||
TS.TimelineListView = Backbone.View.extend({
|
||||
el: $("#timeline .drop-menu-items"),
|
||||
|
||||
collection: TS.TimelineList,
|
||||
|
||||
initialize: function() {
|
||||
_.bindAll(this, 'render', 'renderOne');
|
||||
this.collection.bind('add', this.renderOne);
|
||||
this.collection.bind('refresh', this.render);
|
||||
this.collection.view = this;
|
||||
},
|
||||
|
||||
renderOne: function(timeline) {
|
||||
if (!timeline.view) { new TS.TimelineView(timeline); }
|
||||
$(this.el).append(ich.timelineLink(timeline.toJSON()));
|
||||
},
|
||||
|
||||
render: function() {
|
||||
$(this.el).remove(".timeline-link");
|
||||
this.collection.each(this.renderOne);
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
TS.UserView = Backbone.View.extend({
|
||||
@ -258,25 +259,24 @@ $(document).ready(function(){
|
||||
},
|
||||
|
||||
render: function() {
|
||||
this.$('.fullname').text(this.model.get('name'));
|
||||
this.$('.username').text(" - " + this.model.get('id'));
|
||||
$(this.el).html(ich.userTemplate(this.model.toJSON()));
|
||||
return this;
|
||||
},
|
||||
|
||||
editFullname: function() {
|
||||
$(this.el).addClass('.edit-fullname');
|
||||
$(this.el).addClass('edit-fullname');
|
||||
this.$('.fullname-input').focus();
|
||||
return this;
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.model.set({name: this.$('.fullname-input').val()});
|
||||
this.model.set({name: this.$('fullname-input').val()});
|
||||
this.model.save();
|
||||
$(this.el).removeClass('.edit-fullname');
|
||||
$(this.el).removeClass('edit-fullname');
|
||||
},
|
||||
|
||||
updateOnEnter: function(e) {
|
||||
if (keyCode == 13) this.close();
|
||||
if (e.keyCode == 13) this.close();
|
||||
}
|
||||
});
|
||||
|
||||
@ -285,71 +285,152 @@ $(document).ready(function(){
|
||||
el: $("body"),
|
||||
|
||||
events: {
|
||||
'click #timeline .drop-menu-items a': 'selectTimeline',
|
||||
'keypress #new-entry-input' : 'newTimestamp',
|
||||
'click #timeline .drop-menu-items a': 'selectTimeline'
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
_.bindAll(this, 'render');
|
||||
|
||||
_.bindAll(this, 'initializeViews', 'loadInitialData');
|
||||
|
||||
appThis = this;
|
||||
|
||||
// create the login dialog
|
||||
this.loginDialog = new TS.LoginView
|
||||
|
||||
if (window.bootstrap) { this.initializeData(window.bootstrap()) }
|
||||
else {
|
||||
// this is async (waiting for user input)
|
||||
this.loginDialog.authenticate(function() {
|
||||
appThis.initializeData(appThis.loadInitialData())});
|
||||
}
|
||||
},
|
||||
|
||||
renderTimelineList: function() {
|
||||
var tlUL = this.$('#timeline ul.drop-menu-items');
|
||||
//var curTimeline =
|
||||
//var remTimelines = TS.user.timelines.filter(function(timeline)
|
||||
initializeData: function(data) {
|
||||
|
||||
// create user data
|
||||
this.user = {};
|
||||
this.user.model = new TS.UserModel(data.user);
|
||||
this.user.view = new TS.UserView({model: this.user.model});
|
||||
|
||||
// create timeline models from the bootstrapped data
|
||||
var tlModels = _.map(data.timelines, function(timeline) {
|
||||
return new TS.TimelineModel(timeline);
|
||||
});
|
||||
|
||||
// create the timeline list collection
|
||||
this.timelines = {};
|
||||
this.timelines.collection = new TS.TimelineList(
|
||||
tlModels, {user: this.user.model});
|
||||
this.timelines.view = new TS.TimelineListView(
|
||||
{collection: this.timelines.collection,
|
||||
initialTimelineId: data.initialTimelineId});
|
||||
|
||||
// create entry models from the bootstrapped data
|
||||
var entryModels = _.map(data.entries, function(entry) {
|
||||
return new TS.EntryModel(entry);
|
||||
});
|
||||
|
||||
// create the entry collection
|
||||
this.entries = {};
|
||||
this.entries.collection = new TS.EntryList(entryModels,
|
||||
{timeline: this.timelines.view.selected});
|
||||
this.entries.view = new TS.EntryListView(
|
||||
{collection: this.entries.collection});
|
||||
|
||||
// render views
|
||||
this.user.view.render();
|
||||
this.timelines.view.render();
|
||||
this.entries.view.render();
|
||||
|
||||
|
||||
},
|
||||
|
||||
loadInitialData: function() {
|
||||
// assume we are authenticated
|
||||
|
||||
var username = $("#login-name").val(); // hackish
|
||||
var data = jQuery.parseJSON($.ajax({
|
||||
url: '/ts_api/app/user_summary/' + username,
|
||||
async: false}).responseText);
|
||||
|
||||
data.initialTimelineId = data.timelines[0].id;
|
||||
data.entries = jQuery.parseJSON($.ajax({
|
||||
url: '/ts_api/entries/' + username + '/' +
|
||||
data.initialTimelineId,
|
||||
async: false}).responseText);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
TS.LoginView = Backbone.View.extend({
|
||||
el: $("#login-dialog"),
|
||||
|
||||
initialize: function() {
|
||||
_.bindAll(this, 'doLogin', 'authenticate');
|
||||
|
||||
var viewThis = this;
|
||||
$(this.el).dialog({
|
||||
autoOpen: false,
|
||||
height: 400,
|
||||
width: 400,
|
||||
modal: true,
|
||||
buttons: { Login: viewThis.doLogin}
|
||||
});
|
||||
},
|
||||
|
||||
action: function() {},
|
||||
|
||||
authenticate: function(nextAction) {
|
||||
this.action = nextAction;
|
||||
$(this.el).dialog("open");
|
||||
},
|
||||
|
||||
doLogin: function(){
|
||||
var viewThis = this;
|
||||
var name = $("#login-name");
|
||||
var pwd = $("#login-password");
|
||||
|
||||
// call the API via AJAX
|
||||
$.ajax({
|
||||
url: "/ts_api/login",
|
||||
processData: false,
|
||||
data: JSON.stringify({username: name.val(), password: pwd.val()}),
|
||||
type: "POST",
|
||||
async: false,
|
||||
|
||||
error: function(jqXHR, textStatus, error) {
|
||||
// assuming bad credentials (possible server error or bad request,
|
||||
// we should check that, FIXME
|
||||
var tips = $(".validate-tips");
|
||||
tips.text("Incorrect username/password combination.");
|
||||
tips.addClass("ui-state-error");
|
||||
tips.slideDown();
|
||||
},
|
||||
|
||||
success: function(data, textStatus, jqXHR) {
|
||||
$(viewThis.el).dialog("close");
|
||||
viewThis.action()
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// wire the login dialog using jQuery UI
|
||||
$("#login-dialog").dialog({
|
||||
autoOpen: false,
|
||||
height: 400,
|
||||
width: 400,
|
||||
modal: true,
|
||||
buttons: { Login: function(){login()} }
|
||||
});
|
||||
|
||||
$('#login-dialog').dialog('open');
|
||||
TS.app = new TS.AppView;
|
||||
|
||||
})
|
||||
|
||||
function login() {
|
||||
// lookup the login dialog elements
|
||||
var name = $("#login-name");
|
||||
var pwd = $("#login-password");
|
||||
function getUTCTimestamp() {
|
||||
var d = new Date();
|
||||
|
||||
// call the API via AJAX
|
||||
$.ajax({
|
||||
url: "/ts_api/login",
|
||||
processData: false,
|
||||
data: JSON.stringify({username: name.val(), password: pwd.val()}),
|
||||
type: "POST",
|
||||
function pad(n){return n<10 ? '0'+n : n}
|
||||
|
||||
error: function(jqXHR, textStatus, error) {
|
||||
// assuming bad credentials (possible server error or bad request,
|
||||
// we should check that, FIXME
|
||||
var tips = $(".validate-tips");
|
||||
tips.text("Incorrect username/password combination.");
|
||||
tips.addClass("ui-state-error");
|
||||
tips.slideDown();
|
||||
},
|
||||
|
||||
success: function(data, textStatus, jqXHR) {
|
||||
|
||||
// initialize the app data
|
||||
// TODO: possiblty replace by script generated server-side
|
||||
|
||||
// create the user model
|
||||
TS.user = new TS.UserModel({
|
||||
id: name.val(),
|
||||
name: '' });
|
||||
|
||||
// fetch the initial user data from the server
|
||||
TS.user.fetch();
|
||||
|
||||
// create the user view
|
||||
new TS.UserView({model: TS.user});
|
||||
TS.user.view.render();
|
||||
|
||||
}});
|
||||
return d.getUTCFullYear()+'-'
|
||||
+ pad(d.getUTCMonth()+1)+'-'
|
||||
+ pad(d.getUTCDate())+'T'
|
||||
+ pad(d.getUTCHours())+':'
|
||||
+ pad(d.getUTCMinutes())+':'
|
||||
+ pad(d.getUTCSeconds())+'Z';
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user