main.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. const emojiPatterns = [
  2. { pattern: ':)', replacement: '🙂' },
  3. { pattern: ':-)', replacement: '🙂' },
  4. { pattern: ':D', replacement: '😀' },
  5. { pattern: ':-D', replacement: '😀' },
  6. { pattern: ';)', replacement: '😉' },
  7. { pattern: ';-)', replacement: '😉' },
  8. { pattern: ':(', replacement: '😞' },
  9. { pattern: ':-(', replacement: '😞' },
  10. { pattern: ':\'(', replacement: '😭' },
  11. { pattern: ':\'-(', replacement: '😭' },
  12. { pattern: ':\\', replacement: '😕' },
  13. { pattern: ':-\\', replacement: '😕' },
  14. { pattern: ':/', replacement: '😕' },
  15. { pattern: ':-/', replacement: '😕' },
  16. { pattern: ':|', replacement: '😐' },
  17. { pattern: ':-|', replacement: '😐' },
  18. { pattern: ':*', replacement: '😘' },
  19. { pattern: ':-*', replacement: '😘' },
  20. { pattern: '<3', replacement: '❤️' },
  21. { pattern: '(y)', replacement: '👍' },
  22. ];
  23. const socket = io();
  24. $(() => {
  25. socket.on('serverHandshake', onServerHandshake);
  26. socket.on('serverLogin', onServerLogin);
  27. socket.on('serverKick', onServerKick);
  28. });
  29. function onServerHandshake() {
  30. resetChatListeners();
  31. resetLoginListeners();
  32. var template = $('#chat-login-template').html();
  33. $('#chat-host').empty();
  34. $('#chat-host').append(template);
  35. $('#chat-login-form').submit((e) => {
  36. e.preventDefault();
  37. socket.emit('login', $('#chat-login-form .username').val(), $('#chat-login-form .password').val());
  38. return false;
  39. });
  40. socket.on('usernameInvalid', () => {
  41. $('#chat-login-form .username').focus();
  42. $('#chat-host .loginError').text('Username invalid.');
  43. });
  44. socket.on('usernameTaken', () => {
  45. $('#chat-login-form .username').focus();
  46. $('#chat-host .loginError').text('Username already in use.');
  47. });
  48. socket.on('passwordRequired', () => {
  49. $('#chat-login-form .passwordWrapper').css('display', '');
  50. $('#chat-login-form .password').focus();
  51. $('#chat-host .loginError').text('Für diesen reservierten Benutzer ist ein Passwort erforderlich.');
  52. });
  53. socket.on('passwordWrong', () => {
  54. $('#chat-login-form .passwordWrapper').css('display', '');
  55. $('#chat-login-form .password').focus();
  56. $('#chat-host .loginError').text('Das eingegebene Passwort ist ungültig.');
  57. });
  58. }
  59. function onServerLogin(user, history) {
  60. resetChatListeners();
  61. resetLoginListeners();
  62. var template;
  63. if (user.admin) {
  64. template = $('#chat-admin-template').html()
  65. } else {
  66. template = $('#chat-template').html()
  67. }
  68. $('#chat-host').empty();
  69. $('#chat-host').append(template);
  70. $('#chat-host').append($('#chat-message-form-template').html());
  71. $('#chat-host .messageField').focus();
  72. for (const historyMsg of history) {
  73. appendUserMessage(historyMsg.msg, historyMsg.user, historyMsg.timestamp, user);
  74. }
  75. socket.on('usersUpdated', (userList) => {
  76. $('#chat-host .main .userlist').empty();
  77. userList.sort((userA, userB) => userA.name.localeCompare(userB.name));
  78. userList.sort((userA, userB) => {
  79. if (userA.admin && !userB.admin) {
  80. return Number.MIN_SAFE_INTEGER;
  81. } else if (!userA.admin && userB.admin) {
  82. return Number.MAX_SAFE_INTEGER;
  83. }
  84. return 0;
  85. });
  86. for (let listUser of userList) {
  87. let userString = listUser.name;
  88. if (listUser.admin) {
  89. userString = '👑 ' + userString;
  90. }
  91. const userListEl = $('<li>');
  92. const usernameEl = $('<span>');
  93. usernameEl.text(userString);
  94. usernameEl.css('color', listUser.color);
  95. userListEl.append(usernameEl);
  96. if (user.admin) {
  97. const kickBtn = $('<button class="kickBtn">🥾</button>');
  98. kickBtn.click(() => onKickUser(listUser));
  99. userListEl.append(kickBtn);
  100. }
  101. $('#chat-host .main .userlist').append(userListEl);
  102. }
  103. });
  104. if (user.admin) {
  105. socket.on('bannedUpdated', (bannedList) => {
  106. $('#chat-host .main .bannedlist').empty();
  107. for (let bannedItem of bannedList) {
  108. const bannedListEl = $('<li>');
  109. bannedListEl.text(bannedItem.ip + ' (' + bannedItem.user.name + ')');
  110. const removeBtn = $('<button class="removeBtn">✖</button>');
  111. removeBtn.click(() => onLiftBan(bannedItem.ip));
  112. bannedListEl.append(removeBtn);
  113. $('#chat-host .main .bannedlist').append(bannedListEl);
  114. }
  115. });
  116. }
  117. socket.on('message', (msg, msgUser, timestamp) => appendUserMessage(msg, msgUser, timestamp, user));
  118. socket.on('userJoined', (msg) => {
  119. appendMessage($('<li class="systemMsg user joined">').text(msg + ' hat den Chat betreten.'));
  120. });
  121. socket.on('userLeft', (msg) => {
  122. appendMessage($('<li class="systemMsg user left">').text(msg + ' hat den Chat verlassen.'));
  123. });
  124. $('#chat-host .messageForm').submit((e) => {
  125. e.preventDefault();
  126. socket.emit('message', $('#chat-host .messageForm .messageField').val());
  127. $('#chat-host .messageForm .messageField').val('');
  128. $('#chat-host .messageForm .messageField').focus();
  129. return false;
  130. });
  131. $("#chat-host .messageField").keypress((e) => {
  132. const field = $('#chat-host .messageForm .messageField');
  133. if (field.prop('selectionStart') == field.prop('selectionEnd')) {
  134. const val = field.val();
  135. const cursorPos = field.prop('selectionStart');
  136. const valBefore = val.substring(0, cursorPos);
  137. const valAfter = val.substring(cursorPos);
  138. for (const emojiPatternItem of emojiPatterns) {
  139. if (valBefore.endsWith(emojiPatternItem.pattern)) {
  140. field.val(
  141. valBefore.substring(0, valBefore.length - emojiPatternItem.pattern.length) + emojiPatternItem.replacement + valAfter
  142. );
  143. field.prop('selectionStart', cursorPos);
  144. field.prop('selectionEnd', cursorPos);
  145. }
  146. }
  147. }
  148. if(e.key == 'Enter' && !e.shiftKey) {
  149. e.preventDefault();
  150. $('#chat-host .messageForm').submit();
  151. return false;
  152. }
  153. });
  154. }
  155. function onServerKick() {
  156. resetChatListeners();
  157. resetLoginListeners();
  158. var template = $('#chat-kick-template').html();
  159. $('#chat-host').empty();
  160. $('#chat-host').append(template);
  161. }
  162. function resetChatListeners() {
  163. socket.removeAllListeners('usersUpdated');
  164. socket.removeAllListeners('message');
  165. socket.removeAllListeners('userJoined');
  166. socket.removeAllListeners('userLeft');
  167. }
  168. function resetLoginListeners() {
  169. socket.removeAllListeners('usernameInvalid');
  170. socket.removeAllListeners('usernameTaken');
  171. socket.removeAllListeners('passwordRequired');
  172. socket.removeAllListeners('passwordWrong');
  173. }
  174. function appendMessage(element) {
  175. const messagesListEl = $('#chat-host .main .messages')[0];
  176. const scrollMax = messagesListEl.scrollHeight - messagesListEl.clientHeight;
  177. const scrolled = messagesListEl.scrollTop != scrollMax;
  178. $('#chat-host .main .messages').append(element);
  179. // TODO: Scrolling
  180. //if (!scrolled) {
  181. messagesListEl.scrollTop = messagesListEl.scrollHeight - messagesListEl.clientHeight;
  182. //}
  183. }
  184. function appendUserMessage(msg, msgUser, timestamp, user) {
  185. const messageWrapper = $('<li class="userMsg">');
  186. if(user.name == msgUser.name) {
  187. messageWrapper.addClass('self');
  188. }
  189. messageWrapper.css('borderColor', msgUser.color);
  190. const avaEl = $('<img class="avatar" />');
  191. avaEl.prop('src', '/img/ava/' + msgUser.name);
  192. avaEl.css('backgroundColor', msgUser.color);
  193. messageWrapper.append(avaEl);
  194. const timeEl = $('<span class="time">');
  195. timeEl.text(new Date(timestamp).toLocaleTimeString());
  196. timeEl.prop('title', new Date(timestamp).toLocaleString());
  197. messageWrapper.append(timeEl);
  198. const userEl = $('<span class="user">');
  199. let userString = msgUser.name;
  200. if (msgUser.admin) {
  201. userString = '👑 ' + userString;
  202. }
  203. userEl.text(userString);
  204. userEl.css('color', msgUser.color);
  205. messageWrapper.append(userEl);
  206. const messageEl = $('<span class="message">');
  207. messageEl.html(msg)
  208. messageWrapper.append(messageEl);
  209. appendMessage(messageWrapper);
  210. }
  211. function onKickUser(user) {
  212. if (confirm('Möchten Sie den Benutzer "' + user.name + '" wirklich bannen?\nDies wird sich auf alle Benutzer mit der gleichen IP-Adresse auswirken.')) {
  213. socket.emit('requestKick', user);
  214. }
  215. }
  216. function onLiftBan(ip) {
  217. socket.emit('requestLiftBan', ip);
  218. }