PageRenderTime 60ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/public/messaging.js

https://gitlab.com/aprimediet/django-backbone-realtime
JavaScript | 537 lines | 398 code | 69 blank | 70 comment | 17 complexity | a4f81fcceaed6bed62d3feb5e136d0a8 MD5 | raw file
  1. // Configuration
  2. var config = {
  3. api: 'http://192.168.0.39:8000/',
  4. ws_message_new: 'ws://192.168.0.39:8000/message-new/',
  5. ws_message_read: 'ws://192.168.0.39:8000/message-read/'
  6. };
  7. /*
  8. MODELS
  9. */
  10. var BaseModel = Backbone.Model.extend({
  11. initialize: function(options) {
  12. if(!_.isUndefined(options)) _.extend(this, options);
  13. },
  14. sync: function(method, model, options) {
  15. // Check for available access token in cookie
  16. // var token = $.cookie('access_token');
  17. if(!_.isUndefined(window.App.Session.get('auth_token')) &&
  18. !_.isNull(window.App.Session.get('auth_token')) &&
  19. window.App.Session.get('auth_token') !== 'undefined') {
  20. options.beforeSend = function (xhr) {
  21. xhr.setRequestHeader(
  22. 'Authorization',
  23. 'Token ' +
  24. window.App.Session.get('auth_token'));
  25. };
  26. }
  27. return Backbone.sync(method, model, options);
  28. }
  29. });
  30. var SessionModel = BaseModel.extend({
  31. urlRoot: config.api+'auth/',
  32. defaults: {
  33. active: false,
  34. username: null,
  35. auth_token: null
  36. },
  37. sync: function(method, model, options) {
  38. var self = this;
  39. // Check active access token
  40. if(!_.isUndefined(self.get('auth_token')) &&
  41. !_.isNull(self.get('auth_token')) &&
  42. self.get('auth_token') !== 'undefined') {
  43. // Set authorization headers
  44. options.beforeSend = function(xhr) {
  45. xhr.setRequestHeader(
  46. 'Authorization',
  47. 'Token '+
  48. self.get('token')
  49. );
  50. };
  51. }
  52. // Override method for logout
  53. if(model.url === self.urlRoot+'logout/') {
  54. method = 'create';
  55. }
  56. return Backbone.sync(method, model, options);
  57. },
  58. start: function(options) {
  59. // Get auth token from local storage
  60. var auth_token,
  61. currentSession = localStorage.getItem('session');
  62. if(!_.isUndefined(currentSession) && !_.isNull(currentSession)) {
  63. // Parse current Session to JSON object
  64. currentSession = JSON.parse(currentSession);
  65. auth_token = currentSession.auth_token;
  66. username = currentSession.username;
  67. // Set session to true, and set auth token
  68. this.set({
  69. active: true,
  70. username: username,
  71. auth_token: auth_token
  72. });
  73. // Run success callback
  74. options.success();
  75. }
  76. else {
  77. // Run error callback
  78. options.error();
  79. }
  80. },
  81. login: function(options) {
  82. var self = this;
  83. // Set login URL
  84. self.url = self.urlRoot + 'login/';
  85. // Send credentials to backend
  86. self.save({
  87. username: options.creds.username,
  88. password: options.creds.password
  89. }, {
  90. success: function(model) {
  91. // Set session as active
  92. self.set('active', true);
  93. // Remove password from model
  94. self.unset('password');
  95. // Save credentials in local storage
  96. var creds = model.attributes;
  97. localStorage.setItem('session', JSON.stringify(creds));
  98. // Success callback
  99. options.success();
  100. },
  101. error: function() {
  102. // Error callback
  103. options.error();
  104. }
  105. });
  106. },
  107. logout: function(options) {
  108. var self = this;
  109. // Set logout url
  110. self.url = self.urlRoot + 'logout/';
  111. // Send logout request
  112. self.destroy({
  113. success: function(model) {
  114. // Clear model
  115. self.clear();
  116. // Set active to false
  117. self.set('active', false);
  118. // Remove session from local storage
  119. localStorage.removeItem('session');
  120. // Success callback
  121. options.success();
  122. },
  123. error: function() {
  124. // Error callback
  125. error.callback();
  126. }
  127. });
  128. }
  129. });
  130. var MessageModel = BaseModel.extend({
  131. urlRoot: config.api+'messages/',
  132. url: function() {
  133. var url = this.urlRoot;
  134. if(this.id) url = url + this.id;
  135. return url;
  136. },
  137. defaults: {
  138. id: null,
  139. author: null,
  140. destination: null,
  141. content: null,
  142. created: null
  143. }
  144. });
  145. var NotificationModel = BaseModel.extend({
  146. defaults: {
  147. id: null,
  148. content_type: null,
  149. content_id: null,
  150. author: null,
  151. author_id: null,
  152. destination: null,
  153. destination_id: null,
  154. content: null,
  155. created: null,
  156. is_read: null
  157. }
  158. });
  159. // Immediately start session
  160. var AppSession = new SessionModel();
  161. /*
  162. COLLECTIONS
  163. */
  164. var MessageCollection = Backbone.Collection.extend({
  165. initialize: function() {
  166. console.log('Message Model Initialized');
  167. },
  168. url: config.api+'messages/'
  169. });
  170. var NotificationCollection = Backbone.Collection.extend({
  171. model: NotificationModel
  172. });
  173. // Immediately start notification
  174. var AppNotifications = new NotificationCollection();
  175. /*
  176. TEMPLATES AND VIEWS
  177. */
  178. var BaseView = Backbone.View.extend({
  179. initialize: function(options) {
  180. if(!_.isUndefined(options)) _.extend(this, options);
  181. },
  182. render: function(){
  183. this.$el.html(this.template());
  184. if (this.onRender) this.onRender();
  185. return this;
  186. }
  187. });
  188. var LoginView = Backbone.View.extend({
  189. el: '#container',
  190. template: _.template($('#loginTemplate').html(), {}),
  191. ui: {
  192. form: 'form#loginForm'
  193. },
  194. events: {
  195. 'submit form#loginForm': 'submit'
  196. },
  197. render: function(){
  198. this.$el.html(this.template());
  199. },
  200. submit: function(ev) {
  201. ev.preventDefault();
  202. // Get form values
  203. var target = ev.currentTarget,
  204. username = $(ev.currentTarget).find('input[name="username"]').val(),
  205. password = $(ev.currentTarget).find('input[name="password"]').val();
  206. // Check if username and password is null
  207. if(!_.isNull(username) && !_.isNull(password)) {
  208. window.App.Session.login({
  209. creds: {
  210. username: username,
  211. password: password
  212. },
  213. success: function() {
  214. // Initialize websocket
  215. window.App.wsInit();
  216. Backbone.history.navigate('#!/', {trigger: true});
  217. },
  218. error: function() {
  219. alert('login error');
  220. }
  221. });
  222. }
  223. }
  224. });
  225. var HeaderView = BaseView.extend({
  226. initialize: function(options) {
  227. var self = this;
  228. if(!_.isUndefined(options)) _.extend(this, options);
  229. this.listenTo(this.model, 'change', function() {
  230. self.render();
  231. });
  232. // Render when get a new notifications
  233. this.listenTo(this.notifications, 'update', function() {
  234. self.render();
  235. });
  236. },
  237. events: {
  238. 'click .notif-item': 'readNotification'
  239. },
  240. template: _.template($('#headerTemplate').html(), {}),
  241. render: function() {
  242. var data = {
  243. user: this.model.toJSON(),
  244. notifications: this.notifications.toJSON()
  245. };
  246. this.$el.html(this.template(data));
  247. return this;
  248. },
  249. readNotification: function(e) {
  250. var target = $(e.currentTarget),
  251. data_index = target.attr('data-index'),
  252. data_id = target.attr('data-id');
  253. var read = {
  254. id: data_id
  255. };
  256. console.log("I'm reading a notification");
  257. window.App.ws.message_read.send(JSON.stringify(read));
  258. window.App.Notifications.remove(
  259. window.App.Notifications.at(data_index)
  260. );
  261. }
  262. });
  263. var LayoutView = Backbone.View.extend({
  264. initialize: function() {
  265. this.childViews = {
  266. header: null,
  267. content: null
  268. };
  269. },
  270. el: '#container',
  271. region: {
  272. header: '#header',
  273. content: '#content'
  274. },
  275. template: _.template($('#layoutTemplate').html(), {}),
  276. render: function() {
  277. console.log('Rendering Header');
  278. this.$el.html(this.template());
  279. var header = new HeaderView({
  280. model: AppSession,
  281. notifications: AppNotifications
  282. });
  283. this.subRender('header', header);
  284. return this;
  285. },
  286. subRender: function(region, view) {
  287. var el = this.$(this.region[region]),
  288. elChildren = el.children();
  289. if(this.childViews[region] !== null) {
  290. this.childViews[region].remove();
  291. }
  292. this.childViews[region] = view;
  293. el.html(view.render().$el);
  294. return this;
  295. }
  296. });
  297. var InboxView = BaseView.extend({
  298. template: _.template($('#inboxTemplate').html(), {})
  299. });
  300. var SentView = BaseView.extend({
  301. template: _.template($('#sentTemplate').html(), {})
  302. });
  303. var NewMessageView = BaseView.extend({
  304. template: _.template($('#newMessageTemplate').html(), {}),
  305. events: {
  306. 'submit form#newMessageForm': 'submit'
  307. },
  308. submit: function(ev) {
  309. var self = this;
  310. ev.preventDefault();
  311. var target = ev.currentTarget,
  312. destination = $(target).find('input[name="destination"]').val(),
  313. content = $(target).find('textarea[name="content"]').val();
  314. this.model.save({
  315. destination: destination,
  316. content: content
  317. }, {
  318. success: function(model) {
  319. Backbone.history.navigate('#!/sent', {trigger: true});
  320. },
  321. error: function() {
  322. alert('Send Message Error');
  323. }
  324. });
  325. }
  326. });
  327. /*
  328. CONTROLLERS AND ROUTERS
  329. */
  330. var AppRouter = Backbone.Router.extend({
  331. routes: {
  332. '!/login': 'login',
  333. '!/logout': 'logout',
  334. '!/': 'inbox',
  335. '!/sent': 'sent',
  336. '!/new': 'newMessage'
  337. },
  338. layout: function() {
  339. // Initialize Layout
  340. if (!window.App.Layout) {
  341. window.App.Layout = new LayoutView();
  342. window.App.Layout.render();
  343. }
  344. },
  345. login: function() {
  346. var login = new LoginView();
  347. login.render();
  348. },
  349. logout: function() {
  350. window.App.Session.logout({
  351. success: function() {
  352. Backbone.history.navigate('#!/login', {trigger: true});
  353. },
  354. error: function() {
  355. alert('Error logout');
  356. }
  357. });
  358. },
  359. inbox: function() {
  360. // Render layout if not initialized
  361. this.layout();
  362. var inbox = new InboxView();
  363. window.App.Layout.subRender('content', inbox);
  364. },
  365. sent: function() {
  366. // Render layout if not initialized
  367. this.layout();
  368. var sent = new SentView();
  369. window.App.Layout.subRender('content', sent);
  370. },
  371. newMessage: function() {
  372. // Render layout if not initialized
  373. this.layout();
  374. var Message = new MessageModel();
  375. var newMessage = new NewMessageView({
  376. model: Message
  377. });
  378. window.App.Layout.subRender('content', newMessage);
  379. }
  380. });
  381. /*
  382. APPLICATION
  383. */
  384. function App() {
  385. var self = this;
  386. // App initialization
  387. this.initialize = function() {
  388. // Start backbone history
  389. Backbone.history.start();
  390. // Start session initialization
  391. this.Session.start({
  392. success: function() {
  393. // Initialize websocket
  394. self.wsInit();
  395. // Redirect to / if authenticated
  396. Backbone.history.navigate('!/', {trigger: true});
  397. },
  398. error: function() {
  399. // Redirect to login if not authenticated
  400. Backbone.history.navigate('!/login', {trigger: true});
  401. }
  402. });
  403. };
  404. // Initialize session
  405. this.Session = AppSession;
  406. // Initialize router
  407. this.Router = new AppRouter();
  408. // Initialize notifications
  409. this.Notifications = AppNotifications;
  410. // Initialize websocket
  411. this.ws = {};
  412. this.wsInit = function() {
  413. // Get username
  414. var username = self.Session.get('username');
  415. // Initialize websocket connection for new message
  416. this.ws.message_new = new WebSocket(
  417. config.ws_message_new+username
  418. );
  419. this.ws.message_new.onopen = function() {
  420. waitForConnection(self.ws.message_new, function() {
  421. console.log('Channel New Message for', username, 'is opened');
  422. }, 1000);
  423. };
  424. this.ws.message_new.onerror = function(error) {
  425. alert('Websocket error: ', error);
  426. };
  427. this.ws.message_new.onmessage = function(e){
  428. var data = JSON.parse(e.data);
  429. var newNotification = new NotificationModel(data);
  430. self.Notifications.add(newNotification);
  431. };
  432. // Initialize websocket connection for read message
  433. this.ws.message_read = new WebSocket(
  434. config.ws_message_read+username
  435. );
  436. this.ws.message_read.onopen = function() {
  437. waitForConnection(self.ws.message_read, function() {
  438. console.log('Channel Read Message for', username, 'is opened');
  439. }, 1000);
  440. };
  441. this.ws.message_read.onerror = function(error) {
  442. alert('Websocket error: ', error);
  443. };
  444. this.ws.message_read.onmessage = function(e){
  445. // var data = JSON.parse(e.data);
  446. console.log(e);
  447. };
  448. };
  449. // Run the initialization
  450. this.initialize();
  451. }
  452. /*
  453. UTILITIES
  454. */
  455. function waitForConnection(ws, cb, interval) {
  456. if(ws.readyState === 1) {
  457. cb();
  458. }
  459. else {
  460. setTimeout(function() {
  461. waitForConnection(cb, interval);
  462. }, interval);
  463. }
  464. }
  465. // Running the app
  466. var app = new App();
  467. // Register app to window
  468. window.App = app;