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