main.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. $(function() {
  2. var FADE_TIME = 150; // ms
  3. var TYPING_TIMER_LENGTH = 400; // ms
  4. var COLORS = [
  5. '#e21400', '#91580f', '#f8a700', '#f78b00',
  6. '#58dc00', '#287b00', '#a8f07a', '#4ae8c4',
  7. '#3b88eb', '#3824aa', '#a700ff', '#d300e7'
  8. ];
  9. // Initialize variables
  10. var $window = $(window);
  11. var $usernameInput = $('.usernameInput'); // Input for username
  12. var $messages = $('.messages'); // Messages area
  13. var $inputMessage = $('.inputMessage'); // Input message input box
  14. var $loginPage = $('.login.page'); // The login page
  15. var $chatPage = $('.chat.page'); // The chatroom page
  16. // Prompt for setting a username
  17. var username;
  18. var connected = false;
  19. var typing = false;
  20. var lastTypingTime;
  21. var $currentInput = $usernameInput.focus();
  22. var socket = io();
  23. function addParticipantsMessage (data) {
  24. var message = '';
  25. if (data.numUsers === 1) {
  26. message += "there's 1 participant";
  27. } else {
  28. message += "there are " + data.numUsers + " participants";
  29. }
  30. log(message);
  31. }
  32. // Sets the client's username
  33. function setUsername () {
  34. username = cleanInput($usernameInput.val().trim());
  35. // If the username is valid
  36. if (username) {
  37. $loginPage.fadeOut();
  38. $chatPage.show();
  39. $loginPage.off('click');
  40. $currentInput = $inputMessage.focus();
  41. // Tell the server your username
  42. socket.emit('add user', username);
  43. }
  44. }
  45. // Sends a chat message
  46. function sendMessage () {
  47. var message = $inputMessage.val();
  48. // Prevent markup from being injected into the message
  49. message = cleanInput(message);
  50. // if there is a non-empty message and a socket connection
  51. if (message && connected) {
  52. $inputMessage.val('');
  53. addChatMessage({
  54. username: username,
  55. message: message
  56. });
  57. // tell server to execute 'new message' and send along one parameter
  58. socket.emit('new message', message);
  59. }
  60. }
  61. // Log a message
  62. function log (message, options) {
  63. var $el = $('<li>').addClass('log').text(message);
  64. addMessageElement($el, options);
  65. }
  66. // Adds the visual chat message to the message list
  67. function addChatMessage (data, options) {
  68. // Don't fade the message in if there is an 'X was typing'
  69. var $typingMessages = getTypingMessages(data);
  70. options = options || {};
  71. if ($typingMessages.length !== 0) {
  72. options.fade = false;
  73. $typingMessages.remove();
  74. }
  75. var $usernameDiv = $('<span class="username"/>')
  76. .text(data.username)
  77. .css('color', getUsernameColor(data.username));
  78. var $messageBodyDiv = $('<span class="messageBody">')
  79. .text(data.message);
  80. var typingClass = data.typing ? 'typing' : '';
  81. var $messageDiv = $('<li class="message"/>')
  82. .data('username', data.username)
  83. .addClass(typingClass)
  84. .append($usernameDiv, $messageBodyDiv);
  85. addMessageElement($messageDiv, options);
  86. }
  87. // Adds the visual chat typing message
  88. function addChatTyping (data) {
  89. data.typing = true;
  90. data.message = 'is typing';
  91. addChatMessage(data);
  92. }
  93. // Removes the visual chat typing message
  94. function removeChatTyping (data) {
  95. getTypingMessages(data).fadeOut(function () {
  96. $(this).remove();
  97. });
  98. }
  99. // Adds a message element to the messages and scrolls to the bottom
  100. // el - The element to add as a message
  101. // options.fade - If the element should fade-in (default = true)
  102. // options.prepend - If the element should prepend
  103. // all other messages (default = false)
  104. function addMessageElement (el, options) {
  105. var $el = $(el);
  106. // Setup default options
  107. if (!options) {
  108. options = {};
  109. }
  110. if (typeof options.fade === 'undefined') {
  111. options.fade = true;
  112. }
  113. if (typeof options.prepend === 'undefined') {
  114. options.prepend = false;
  115. }
  116. // Apply options
  117. if (options.fade) {
  118. $el.hide().fadeIn(FADE_TIME);
  119. }
  120. if (options.prepend) {
  121. $messages.prepend($el);
  122. } else {
  123. $messages.append($el);
  124. }
  125. $messages[0].scrollTop = $messages[0].scrollHeight;
  126. }
  127. // Prevents input from having injected markup
  128. function cleanInput (input) {
  129. return $('<div/>').text(input).text();
  130. }
  131. // Updates the typing event
  132. function updateTyping () {
  133. if (connected) {
  134. if (!typing) {
  135. typing = true;
  136. socket.emit('typing');
  137. }
  138. lastTypingTime = (new Date()).getTime();
  139. setTimeout(function () {
  140. var typingTimer = (new Date()).getTime();
  141. var timeDiff = typingTimer - lastTypingTime;
  142. if (timeDiff >= TYPING_TIMER_LENGTH && typing) {
  143. socket.emit('stop typing');
  144. typing = false;
  145. }
  146. }, TYPING_TIMER_LENGTH);
  147. }
  148. }
  149. // Gets the 'X is typing' messages of a user
  150. function getTypingMessages (data) {
  151. return $('.typing.message').filter(function (i) {
  152. return $(this).data('username') === data.username;
  153. });
  154. }
  155. // Gets the color of a username through our hash function
  156. function getUsernameColor (username) {
  157. // Compute hash code
  158. var hash = 7;
  159. for (var i = 0; i < username.length; i++) {
  160. hash = username.charCodeAt(i) + (hash << 5) - hash;
  161. }
  162. // Calculate color
  163. var index = Math.abs(hash % COLORS.length);
  164. return COLORS[index];
  165. }
  166. // Keyboard events
  167. $window.keydown(function (event) {
  168. // Auto-focus the current input when a key is typed
  169. if (!(event.ctrlKey || event.metaKey || event.altKey)) {
  170. $currentInput.focus();
  171. }
  172. // When the client hits ENTER on their keyboard
  173. if (event.which === 13) {
  174. if (username) {
  175. sendMessage();
  176. socket.emit('stop typing');
  177. typing = false;
  178. } else {
  179. setUsername();
  180. }
  181. }
  182. });
  183. $inputMessage.on('input', function() {
  184. updateTyping();
  185. });
  186. // Click events
  187. // Focus input when clicking anywhere on login page
  188. $loginPage.click(function () {
  189. $currentInput.focus();
  190. });
  191. // Focus input when clicking on the message input's border
  192. $inputMessage.click(function () {
  193. $inputMessage.focus();
  194. });
  195. // Socket events
  196. // Whenever the server emits 'login', log the login message
  197. socket.on('login', function (data) {
  198. connected = true;
  199. // Display the welcome message
  200. var message = "Welcome – ";
  201. log(message, {
  202. prepend: true
  203. });
  204. addParticipantsMessage(data);
  205. });
  206. // Whenever the server emits 'new message', update the chat body
  207. socket.on('new message', function (data) {
  208. addChatMessage(data);
  209. });
  210. // Whenever the server emits 'user joined', log it in the chat body
  211. socket.on('user joined', function (data) {
  212. log(data.username + ' joined');
  213. addParticipantsMessage(data);
  214. });
  215. // Whenever the server emits 'user left', log it in the chat body
  216. socket.on('user left', function (data) {
  217. log(data.username + ' left');
  218. addParticipantsMessage(data);
  219. removeChatTyping(data);
  220. });
  221. // Whenever the server emits 'typing', show the typing message
  222. socket.on('typing', function (data) {
  223. addChatTyping(data);
  224. });
  225. // Whenever the server emits 'stop typing', kill the typing message
  226. socket.on('stop typing', function (data) {
  227. removeChatTyping(data);
  228. });
  229. });