chat.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. const SEND_MESSAGE_ACTION = "send-message" // 채팅 메시지
  2. const USER_JOINED_ACTION = "user-join" // 회원 접속
  3. const USER_LEFT_ACTION = "user-left" // 회원 접속 종료
  4. const JOIN_ROOM_ACTION = "join-room" // 방 입장
  5. const LEAVE_ROOM_ACTION = "leave-room" // 방 퇴장
  6. const JOIN_ROOM_PRIVATE_ACTION = "join-room-private" // 방 비밀 입장
  7. const ROOM_JOINED_ACTION = "room-joined" // 방 입장함
  8. const DUPLICATION_ACTION = "duplication" // 회원 중복
  9. const CHAT_HISTORY_ACTION = "set-history"
  10. const SERVER_URL = "ws://localhost:7070";
  11. const ChatClient = class {
  12. constructor(url) {
  13. this.url = url;
  14. }
  15. isConnected = null;
  16. socket = null;
  17. user = null;
  18. rooms = [];
  19. users = [];
  20. // 작업영역
  21. notice = null;
  22. log = null;
  23. message = null;
  24. btnPush = null;
  25. // 글자크기
  26. fontSize = 14;
  27. init() {
  28. if (!window.WebSocket) {
  29. this.setWarning("해당 브라우저는 채팅을 지원하지 않습니다. 최신 버전의 브라우저를 설치 후 실행 해주세요.")
  30. } else {
  31. this.isConnected = false;
  32. this.notice = document.getElementById("chatNotice");
  33. this.log = document.getElementById("chatLog");
  34. this.message = document.getElementById("message");
  35. this.btnPush = document.getElementById("btnPushMessage");
  36. this.connect();
  37. let $this = this;
  38. this.socket.onopen = function() {
  39. console.log("[Connected]");
  40. $this.isConnected = true;
  41. $this.btnPush.removeAttribute("disabled");
  42. $this.message.focus();
  43. $this.setNotice("서버와 연결 되었습니다.");
  44. $this.setEvent();
  45. $this.emit(CHAT_HISTORY_ACTION);
  46. };
  47. this.socket.onmessage = function (e) {
  48. let data = e.data;
  49. data = data.split(/\r?\n/);
  50. for (let i = 0; i < data.length; i++) {
  51. let msg = JSON.parse(data[i]);
  52. switch (msg.action) {
  53. case DUPLICATION_ACTION:
  54. $this.setAlert("중복접속으로 이전접속을 종료합니다.");
  55. break;
  56. case SEND_MESSAGE_ACTION:
  57. $this.setMessage(msg)
  58. break;
  59. case USER_JOINED_ACTION:
  60. $this.handleUserJoined(msg);
  61. break;
  62. case USER_LEFT_ACTION:
  63. $this.handleUserLeft(msg);
  64. break;
  65. case ROOM_JOINED_ACTION:
  66. break;
  67. case CHAT_HISTORY_ACTION:
  68. $this.handleSetHistory(msg);
  69. break;
  70. default:
  71. break;
  72. }
  73. }
  74. };
  75. this.socket.onerror = function() {
  76. $this.setWarning("연결 요청에 실패하였습니다.");
  77. };
  78. this.socket.onclose = function() {
  79. $this.setFailed("서버 연결이 종료되었습니다.");
  80. };
  81. }
  82. }
  83. // 서버 소켓 연결
  84. connect() {
  85. try {
  86. let query = "?";
  87. // 로그인 회원 정보
  88. if (IS_USER) {
  89. let userInfo = document.getElementById("userInfo").value.trim();
  90. if (typeof userInfo !== "undefined") {
  91. this.user = JSON.parse(atob(userInfo, true));
  92. query += "bearer=" + this.user.token;
  93. }
  94. }else{
  95. query += "name=" + generateRandomString(6);
  96. }
  97. // 웹소켓 연결
  98. this.socket = new WebSocket(this.url + query);
  99. } catch (err) {
  100. this.setFailed(err);
  101. }
  102. }
  103. // 클라이언트 기능 처리 담당
  104. setEvent() {
  105. // 메시지 전송
  106. document.getElementById("btnPushMessage").addEventListener("click", function (e) {
  107. this.emit(SEND_MESSAGE_ACTION, {
  108. message: this.message.value
  109. });
  110. }.bind(this));
  111. this.message.addEventListener("keyup", function (e) {
  112. if (e.key === "Enter" || e.keyCode === 13) {
  113. this.emit(SEND_MESSAGE_ACTION, {
  114. message: e.target.value
  115. });
  116. e.preventDefault();
  117. }
  118. }.bind(this));
  119. // 새창으로
  120. document.getElementById("btnPopupChat").addEventListener("click", function() {
  121. window.open(window.location.href, "master", "popup=1,toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=no,copyhistory=no");
  122. });
  123. // 청소하기
  124. document.getElementById("btnTxtClear").addEventListener("click", function() {
  125. this.clear();
  126. }.bind(this));
  127. // 글자크게
  128. document.getElementById("btnTxtPlus").addEventListener("click", function(e) {
  129. e.stopPropagation();
  130. this.textResize(1);
  131. }.bind(this));
  132. // 글자작게
  133. document.getElementById("btnTxtMinus").addEventListener("click", function(e) {
  134. e.stopPropagation();
  135. this.textResize(-1);
  136. }.bind(this));
  137. // 새로고침
  138. document.getElementById("btnTxtRefresh").addEventListener("click", function() {
  139. this.refresh();
  140. }.bind(this));
  141. }
  142. // 서버에 송신 담당
  143. emit(name, data) {
  144. if (this.socket.readyState === 1) {
  145. this.socket.send(JSON.stringify(Object.assign({action: name}, data)));
  146. }else{
  147. this.setFailed("통신이 불가합니다. 새로고침 후 다시 시도하세요.");
  148. }
  149. }
  150. // 송/수신 메시지 표시
  151. setDisplay(s) {
  152. $(this.log).append(s.trim());
  153. this.log.scrollTop = this.log.scrollHeight;
  154. this.message.value = "";
  155. }
  156. // 일반 메시지
  157. setMessage(s) {
  158. this.setDisplay(`<em style="font-size: ${this.fontSize}px;">${s.sender.User.Name}: ${s.message}</em>`);
  159. }
  160. // 공지사항
  161. setNotice(s) {
  162. this.setDisplay('<p class="notice">' + s + '</p>');
  163. }
  164. // 경고
  165. setAlert(s) {
  166. this.setDisplay('<p class="alert">' + s + '</p>');
  167. }
  168. // 알림
  169. setWarning(s) {
  170. this.setDisplay('<p class="warning">' + s + '</p>');
  171. }
  172. // 실패
  173. setFailed(s) {
  174. this.setDisplay('<p class="failed">' + s + '</p>');
  175. }
  176. // 글자 크게/작게
  177. textResize(num) {
  178. let n = Number(num);
  179. let fontSize = (Number(this.fontSize) + n);
  180. if (!(fontSize > 10 && fontSize < 23)) {
  181. return;
  182. }
  183. document.getElementById("chatLog").querySelectorAll("em").forEach(function (row) {
  184. row.style.fontSize = (fontSize + "px");
  185. });
  186. this.fontSize = fontSize;
  187. }
  188. // 청소하기
  189. clear() {
  190. this.message.focus();
  191. this.log.innerText = "";
  192. this.setAlert("채팅방이 청소되었습니다.");
  193. }
  194. // 새로고침
  195. refresh() {
  196. window.location.reload();
  197. }
  198. // 이미 존재하는 회원인지 확인
  199. userExists(user) {
  200. for (let i = 0; i < this.users.length; i++) {
  201. if (this.users[i].id == user.id) {
  202. return true;
  203. }
  204. }
  205. return false;
  206. }
  207. // 회원 입장
  208. handleUserJoined(msg) {
  209. document.getElementById("chatUserRows").innerText = msg.message;
  210. if (!this.userExists(msg.sender)) {
  211. this.users.push(msg.sender);
  212. }
  213. }
  214. // 회원 퇴장
  215. handleUserLeft(msg) {
  216. document.getElementById("chatUserRows").innerText = msg.message;
  217. for (let i = 0; i < this.users.length; i++) {
  218. if (this.users[i].id == msg.sender.id) {
  219. this.users.splice(i, 1);
  220. return;
  221. }
  222. }
  223. }
  224. handleSetHistory(msg) {
  225. let histories = JSON.parse(msg.message.replace("\x00", ""));
  226. if (histories.length > 0) {
  227. for (let row of histories) {
  228. this.setMessage(row);
  229. }
  230. }
  231. }
  232. }
  233. let client;
  234. window.addEventListener("DOMContentLoaded", function() {
  235. client = new ChatClient(SERVER_URL);
  236. client.init();
  237. });