// TimeStamper namespace var TS = {}; /* Setup after the document is ready for manipulation. */ $(document).ready(function(){ // ======== DEFINE MODELS ========// /* Entry model. * Attributes * - id * - mark * - notes * - start */ TS.EntryModel = Backbone.Model.extend({ }); /* Timeline model. * Attributes: * - id * - desc * - created */ TS.TimelineModel = Backbone.Model.extend({ }); /* User model. * Attributes: * - username * - fullname * - email * - join_date */ TS.UserModel = Backbone.Model.extend({ url: function() { return '/ts_api/users/' + this.get('id'); }, initialize: function(attrs, options) { _.bind(this, 'url'); } }); TS.EntryList = Backbone.Collection.extend({ model: TS.EntryModel, comparator: function(entry) { return entry.get('timestamp'); }, initialize: function(model, options) { if (options.timeline == undefined) { throw "Cannot create an EntryList without a TimelineModel reference." } else { this.timeline = options.timeline; } _.bindAll(this, "url"); }, url: function() { return "/entries/" + this.timeline.get('user_id') + "/" + this.timeline.get('id'); } }); TS.TimelineList = Backbone.Collection.extend({ model: TS.TimelineModel, initialize: function(models, options) { if (options.user == undefined) { throw "Cannot create a TimelineList without a UserModel reference."; } else { this.user = options.user; } _.bindAll(this, 'url'); }, comparator: function(timeline) { return timeline.get('id'); }, url: function() { return "/ts_api/timelines/" + this.user.get('id'); } }); // ======== DEFINE VIEWS ========// /* Entry view */ TS.EntryView = Backbone.View.extend({ model: TS.EntryModel, events: { "dblclick div.mark" : "editMark", "dblclick div.timestamp" : "editTimestamp", "keypress .mark-input" : "updateOnEnter", "keypress .timestamp-input" : "updateOnEnter" }, initialize: function() { _.bindAll(this, 'render', 'close', 'editTImestamp', 'editMark', 'updateOnEnter'); this.model.bind('change', this.render); this.model.view = this; }, render: function() { $(this.el).html(ich.entry(this.model.toJSON())); return this; }, editMark: function() { $(this.el).addClass('edit-mark'); this.$('.mark-input').focus(); return this; }, editTimestamp: function() { $(this.el).addClass('edit-timestamp'); this.$('timestamp-input').focus(); return this; }, close: function() { this.model.save({ mark: this.$('.mark-input').val(), timestamp: this.$('.timestamp-input').val()}); $(this.el).removeClass('edit-mark edit-timestamp'); }, updateOnEnter: function(e) { if(e.keyCode == 13) this.close(); } }); TS.EntryListView = Backbone.View.extend({ el: $("#entry-list"), events: { "#new-entry" : "createNewEntry" }, initialize: function() { _.bindAll(this, 'addOne', 'createNewEntry', 'render'); this.collection.bind('add', this.addOne); this.collection.bind('refresh', this.render); this.collection.view = this; this.entryContainer = this.$("#entries") }, addOne: function(entry) { if (!entry.view) { new TS.EntryView({model: entry}); } 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(); }, render: function() { this.entryContainer.empty(); this.collection.each(this.addOne); } }); TS.TimelineView = Backbone.View.extend({ el: $("#timeline"), model: TS.TimelineModel, events: { "dblclick .timeline-id" : "editId", "dblclick .timeline-desc" : "editDesc", "keypress .timeline-id-input" : "updateOnEnter", "keypress .timeline-desc-input" : "updateOnEnter" }, initialize: function() { _.bindAll(this, 'render', 'close', 'editId', 'editDesc', 'updateOnEnter'); this.model.bind('change', this.render); this.model.view = this; }, render: function() { this.$('.timeline-id').html('( ' + this.model.get('id') + ' )'); this.$('.timeline-desc').text(this.model.get('desc')); return this; }, editId: function() { $(this.el).addClass('edit-id'); this.$('.timeline-id-input').focus(); return this; }, editDesc: function() { $(this.el).addClass('edit-desc'); this.$('.timeline-desc-input').focus(); return this; }, close: function() { this.model.save({ id: this.$('timeline-id-input').val(), desc: this.$('.timeline-desc-input').val()}); $(this.el).removeClass('.edit-id .edit-desc'); }, 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({ el: $("#user"), model: TS.UserModel, events: { 'dblclick .fullname': 'editFullname', 'keypress .fullname-input': 'updateOnEnter' }, initialize: function() { _.bindAll(this, 'render', 'close', 'editFullname', 'updateOnEnter'); this.model.bind('change', this.render); this.model.view = this; }, render: function() { this.$('.fullname').text(this.model.get('name')); this.$('.username').text(" - " + this.model.get('id')); return this; }, editFullname: function() { $(this.el).addClass('.edit-fullname'); this.$('.fullname-input').focus(); return this; }, close: function() { this.model.set({name: this.$('.fullname-input').val()}); this.model.save(); $(this.el).removeClass('.edit-fullname'); }, updateOnEnter: function(e) { if (keyCode == 13) this.close(); } }); TS.AppView = Backbone.View.extend({ el: $("body"), events: { 'click #timeline .drop-menu-items a': 'selectTimeline', 'keypress #new-entry-input' : 'newTimestamp', }, initialize: function() { _.bindAll(this, 'render'); }, renderTimelineList: function() { var tlUL = this.$('#timeline ul.drop-menu-items'); //var curTimeline = //var remTimelines = TS.user.timelines.filter(function(timeline) } }); // 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'); }) function login() { // lookup the login dialog elements 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", 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(); }}); }