PageRenderTime 45ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/js/app.js

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