PageRenderTime 43ms CodeModel.GetById 25ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 0ms

/chat_example/_attachments/js/app.js

https://github.com/carljmosca/backbone-couchdb
JavaScript | 333 lines | 288 code | 20 blank | 25 comment | 5 complexity | 78c7e016986050d6d7a64231d760c71a MD5 | raw file
  1// This is a simple app that demonstrates how to use the Backbone.js couch-connector.
  2// It is sort of a real time chat with private messages support.
  3
  4$(function(){
  5  // Fill this with your database information.
  6  // `ddoc_name` is the name of your couchapp project.
  7  Backbone.couch_connector.config.db_name = "backbone-couchapp";
  8  Backbone.couch_connector.config.ddoc_name = "backbone_example";
  9  
 10  // If set to true, the connector will listen to the changes feed
 11  // and will provide your models with real time remote updates.
 12  // But in this case we enable the changes feed for each Collection on our own.
 13  Backbone.couch_connector.config.global_changes = false;
 14  
 15  // Enables Mustache.js-like templating.
 16  _.templateSettings = {
 17    interpolate : /\{\{(.+?)\}\}/g
 18  };
 19  
 20  var UserModel = Backbone.Model.extend({
 21    defaults : {
 22      name : "Anonymus"
 23    }
 24  });
 25  
 26  window.CurrentUser = new UserModel();
 27  
 28  // The model for a message is kinda simple.
 29  // We only need a name, a text and a date.
 30  var MessageModel = Backbone.Model.extend({
 31    initialize : function(){
 32      if(!this.get("date")){
 33        this.set({"date": new Date().getTime()});
 34      }
 35    }
 36  });
 37  
 38  // Now let's define a new Collection of Messages
 39  var MessagesList = Backbone.Collection.extend({
 40    db : {
 41      view : "messages",
 42      changes : true,
 43      // If you don't know what filters are in CouchDB, then read it up here:
 44      // <a href="http://guide.couchdb.org/draft/notifications.html#filters">http://guide.couchdb.org/draft/notifications.html#filters</a>
 45      // Look up how the filter works in `chat_example/filters/private_messages.js`.
 46      // IMPORTANT: see `filters/messages.js` to see how to retrieve remove events
 47      filter : Backbone.couch_connector.config.ddoc_name + "/messages"
 48    },
 49    // The couchdb-connector is capable of mapping the url scheme
 50    // proposed by the authors of Backbone to documents in your database,
 51    // so that you don't have to change existing apps when you switch the sync-strategy
 52    url : "/messages",
 53    model : MessageModel,
 54    // The messages should be ordered by date
 55    comparator : function(comment){
 56      return comment.get("date");
 57    }
 58  });
 59  
 60  var Messages = new MessagesList();
 61
 62  var PrivateMessage = MessageModel.extend({
 63  });
 64  
 65  // Private messages get an own collection because they need a filter
 66  var PrivateMessageList = Backbone.Collection.extend({
 67    db : {
 68      view : "none__",
 69      changes : false,
 70      // The filter avoids that private messages appear in the public stream.
 71      filter : Backbone.couch_connector.config.ddoc_name + "/private_messages"
 72    },
 73    
 74    url : "/private_messages",
 75    
 76    model : PrivateMessage
 77  });
 78  
 79  var PrivateMessages = new PrivateMessageList();
 80
 81  // Displays the current user's name and the message input field.
 82  var InputView = Backbone.View.extend({
 83    el : $('#input'),
 84    
 85    regex : /@(\w+)/,
 86    
 87    events : {
 88      "click #send" : "onSubmit",
 89      "keypress #message" : "keypress"
 90    },
 91    
 92    initialize : function(){
 93      _.bindAll(this, "onSubmit", "nameChanged", "keypress");
 94      CurrentUser.bind("change:name", this.nameChanged);
 95    },
 96    
 97    onSubmit : function(){
 98      var message = $("#message").val();
 99      // Sanitize messages a bit before we send them to the server.
100      message = message.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/"/g, '&quot;');
101      if(message.length > 0){
102        var executed = this.regex.exec(message);
103        // Do a regex check to see if it is a private message
104        if(executed != null)
105          PrivateMessages.create({
106            "from" : CurrentUser.get("name"),
107            "to" : executed[1],
108            "message" : message.replace(executed[0], "")
109          });
110        else
111          Messages.create({
112            "from" : CurrentUser.get("name"),
113            "message" : message
114          });
115      }
116      $("#message").val("");
117    },
118    
119    nameChanged : function(){
120      $('#name').text(CurrentUser.get('name'));
121    },
122    
123    keypress : function(ev){
124      // Send message on ENTER
125      if(ev.keyCode == 13)
126        this.onSubmit();
127    },
128    
129    fillAndFocus : function(text){
130      $('#message').val(text).focus();
131    }
132  });
133  
134  // Represents a message entry
135  var EntryView = Backbone.View.extend({
136    tagName : "li",
137    
138    template : _.template($("#entry-template").html()),
139    
140    events : {
141      "click .delete" : "delete_me"
142    },
143    
144    // If there's a change in our model, rerender it
145    initialize : function(){
146      _.bindAll(this, 'render', 'delete_me', 'delete_row');
147      this.model.bind('change', this.render);
148      this.model.bind('remove', this.delete_row);
149    },
150    
151    render : function(){ 
152      var content = this.model.toJSON();
153      $(this.el).html(this.template(content));
154      return this;
155    },
156    
157    delete_me : function(){
158      if(CurrentUser.get('name') == this.model.get('from')){
159        this.model.destroy();
160        this.delete_row(); 
161      }else{
162        alert("You can only delete your own messages!");
163      }
164    },
165    
166    delete_row : function(){
167      $(this.el).remove();
168    }
169  });
170  
171  // A private message
172  var PrivateEntryView = EntryView.extend({
173    className : "private",
174    template : _.template($("#private-entry-template").html())
175  });
176  
177  // The view for the chat messages
178  var MessagesList = Backbone.View.extend({
179    el: $("#messages"),
180  
181    initialize : function(){
182      _.bindAll(this, 'reseted', 'addRow', 'addPrivateRow');
183    
184      Messages.bind("reset", this.reseted);
185      Messages.bind("add", this.addRow);
186      PrivateMessages.bind("add", this.addPrivateRow);
187    },
188  
189    // Adds an entry row 
190    addRow : function(comment){
191      var view = new EntryView({model: comment});
192      var rendered = view.render().el;
193      this.el.append(rendered);
194    },
195  
196    addPrivateRow : function(private_message){
197      var view = new PrivateEntryView({model: private_message});
198      var rendered = view.render().el;
199      this.el.append(rendered);
200    },
201  
202    // Renders all comments into the table
203    reseted : function(){
204      // reset the table
205      this.el.html("");
206      if(Messages.length > 0){
207        // add each element
208        Messages.each(this.addRow);
209      }
210    }
211  });
212
213  
214  var UserSession = Backbone.Model.extend({
215  });
216
217  var UserListCollection = Backbone.Collection.extend({
218    db : {
219      changes : true
220    },
221    url : "/user_list",
222    model : UserSession
223  });
224
225  var UserList = new UserListCollection();
226
227  var UserListEntry = Backbone.View.extend({
228    tagName : "li",
229    className : "user",
230  
231    initialize : function(){
232      _.bindAll(this, 'remove_me');
233      
234      // When the session gets destroyed, the row will be destroyed too
235      this.model.bind("remove", this.remove_me)
236    },
237  
238    render : function(){
239      this.el = $(this.el);
240      this.el.html("");
241      this.el.unbind();
242      this.el.text(this.model.get("name"));
243      // Insert "@username" into the input field
244      var temp = "@" + this.model.get("name") + " ";
245      this.el.click(function(){
246        Input.fillAndFocus(temp);
247      });
248      return this.el;
249    },
250  
251    remove_me : function(){
252      that = this;
253      this.el.fadeOut(function(){
254        that.el.remove();
255      })
256    }
257  });
258  
259  // The list where all usernames are displayed
260  var UserListView = Backbone.View.extend({
261    el : $('#userlist'),
262  
263    initialize : function(){
264      _.bindAll(this, 'reseted', 'addRow');
265      
266      // The view listens to the realtime updates of the couchdb changes feed
267      UserList.bind("add", this.addRow);
268      UserList.bind("reset", this.reseted);
269    },
270  
271    addRow : function(model){
272      this.el.append(new UserListEntry({model:model}).render());
273    },
274  
275    reseted : function(){
276      UserList.each(this.addRow);
277    }
278  });
279
280  // The App router initializes the app by calling `UserList.fetch()`
281  var App = Backbone.Router.extend({
282    initialize : function(){
283      UserList.fetch();
284    }
285  });
286
287  // The current session will be stored in here
288  var CurrentSession = null;
289
290  var Input = new InputView();
291  
292  // Booststrap app after delay to avoid continuous activity spinner 
293  _.delay(function(){
294    
295    // Destroy the current session on unload
296    $(window).unload(function(){
297      $.ajaxSetup({
298        async : false
299      });
300      if(CurrentSession != null)
301        CurrentSession.destroy();
302    });
303    
304    // Includes the couchlogin
305    // check it out here: <a href="https://github.com/couchapp/couchdb-login-jquery">https://github.com/couchapp/couchdb-login-jquery</a>
306    $('#login').couchLogin({
307      loggedIn : function(user){
308        CurrentUser.set(user);
309        PrivateMessages.listen_to_changes();
310        // Only add a User if it's not already in the list
311        if(!UserList.detect(function(user){return user.get("name") == CurrentUser.get("name");})){
312          CurrentSession = UserList.create({
313            "name" : CurrentUser.get("name"),
314            "logged_in_at" : new Date().getTime()
315          });
316        }
317      },
318      loggedOut : function(){
319        PrivateMessages.stop_changes();
320        CurrentUser.set(new UserModel().toJSON());
321        CurrentUser.trigger("change:name");
322        if(CurrentSession != null)
323          CurrentSession.destroy();
324      }
325    });
326    
327    // Bootstrapping
328    new MessagesList();
329    new UserListView();
330    new App();
331
332  }, 100);
333});