You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Chat.js 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. /* global APP, $ */
  2. import {processReplacements, linkify} from './Replacement';
  3. import CommandsProcessor from './Commands';
  4. import ToolbarToggler from '../../toolbars/ToolbarToggler';
  5. import VideoLayout from "../../videolayout/VideoLayout";
  6. import UIUtil from '../../util/UIUtil';
  7. import UIEvents from '../../../../service/UI/UIEvents';
  8. import { smileys } from './smileys';
  9. let unreadMessages = 0;
  10. const sidePanelsContainerId = 'sideToolbarContainer';
  11. const htmlStr = `
  12. <div id="chat_container" class="sideToolbarContainer__inner">
  13. <div id="nickname">
  14. <span data-i18n="chat.nickname.title"></span>
  15. <form>
  16. <input type='text' id="nickinput" autofocus
  17. data-i18n="[placeholder]chat.nickname.popover">
  18. </form>
  19. </div>
  20. <div id="chatconversation"></div>
  21. <audio id="chatNotification" src="sounds/incomingMessage.wav"
  22. preload="auto"></audio>
  23. <textarea id="usermsg" autofocus
  24. data-i18n="[placeholder]chat.messagebox"></textarea>
  25. <div id="smileysarea">
  26. <div id="smileys" id="toggle_smileys">
  27. <img src="images/smile.svg"/>
  28. </div>
  29. </div>
  30. </div>`;
  31. function initHTML() {
  32. $(`#${sidePanelsContainerId}`)
  33. .append(htmlStr);
  34. }
  35. /**
  36. * The container id, which is and the element id.
  37. */
  38. var CHAT_CONTAINER_ID = "chat_container";
  39. /**
  40. * Updates visual notification, indicating that a message has arrived.
  41. */
  42. function updateVisualNotification() {
  43. var unreadMsgElement = document.getElementById('unreadMessages');
  44. if (unreadMessages) {
  45. unreadMsgElement.innerHTML = unreadMessages.toString();
  46. ToolbarToggler.dockToolbar(true);
  47. var chatButtonElement
  48. = document.getElementById('toolbar_button_chat');
  49. var leftIndent = (UIUtil.getTextWidth(chatButtonElement) -
  50. UIUtil.getTextWidth(unreadMsgElement)) / 2;
  51. var topIndent = (UIUtil.getTextHeight(chatButtonElement) -
  52. UIUtil.getTextHeight(unreadMsgElement)) / 2 - 5;
  53. unreadMsgElement.setAttribute(
  54. 'style',
  55. 'top:' + topIndent +
  56. '; left:' + leftIndent + ';');
  57. }
  58. else {
  59. unreadMsgElement.innerHTML = '';
  60. }
  61. $(unreadMsgElement).parent()[unreadMessages > 0 ? 'show' : 'hide']();
  62. }
  63. /**
  64. * Returns the current time in the format it is shown to the user
  65. * @returns {string}
  66. */
  67. function getCurrentTime(stamp) {
  68. var now = (stamp? new Date(stamp): new Date());
  69. var hour = now.getHours();
  70. var minute = now.getMinutes();
  71. var second = now.getSeconds();
  72. if(hour.toString().length === 1) {
  73. hour = '0'+hour;
  74. }
  75. if(minute.toString().length === 1) {
  76. minute = '0'+minute;
  77. }
  78. if(second.toString().length === 1) {
  79. second = '0'+second;
  80. }
  81. return hour+':'+minute+':'+second;
  82. }
  83. function toggleSmileys() {
  84. var smileys = $('#smileysContainer');
  85. if(!smileys.is(':visible')) {
  86. smileys.show("slide", { direction: "down", duration: 300});
  87. } else {
  88. smileys.hide("slide", { direction: "down", duration: 300});
  89. }
  90. $('#usermsg').focus();
  91. }
  92. function addClickFunction(smiley, number) {
  93. smiley.onclick = function addSmileyToMessage() {
  94. var usermsg = $('#usermsg');
  95. var message = usermsg.val();
  96. message += smileys['smiley' + number];
  97. usermsg.val(message);
  98. usermsg.get(0).setSelectionRange(message.length, message.length);
  99. toggleSmileys();
  100. usermsg.focus();
  101. };
  102. }
  103. /**
  104. * Adds the smileys container to the chat
  105. */
  106. function addSmileys() {
  107. var smileysContainer = document.createElement('div');
  108. smileysContainer.id = 'smileysContainer';
  109. for(var i = 1; i <= 21; i++) {
  110. var smileyContainer = document.createElement('div');
  111. smileyContainer.id = 'smiley' + i;
  112. smileyContainer.className = 'smileyContainer';
  113. var smiley = document.createElement('img');
  114. smiley.src = 'images/smileys/smiley' + i + '.svg';
  115. smiley.className = 'smiley';
  116. addClickFunction(smiley, i);
  117. smileyContainer.appendChild(smiley);
  118. smileysContainer.appendChild(smileyContainer);
  119. }
  120. $("#chat_container").append(smileysContainer);
  121. }
  122. /**
  123. * Resizes the chat conversation.
  124. */
  125. function resizeChatConversation() {
  126. var msgareaHeight = $('#usermsg').outerHeight();
  127. var chatspace = $('#' + CHAT_CONTAINER_ID);
  128. var width = chatspace.width();
  129. var chat = $('#chatconversation');
  130. var smileys = $('#smileysarea');
  131. smileys.height(msgareaHeight);
  132. $("#smileys").css('bottom', (msgareaHeight - 26) / 2);
  133. $('#smileysContainer').css('bottom', msgareaHeight);
  134. chat.width(width - 10);
  135. chat.height(window.innerHeight - 15 - msgareaHeight);
  136. }
  137. /**
  138. * Chat related user interface.
  139. */
  140. var Chat = {
  141. /**
  142. * Initializes chat related interface.
  143. */
  144. init (eventEmitter) {
  145. initHTML();
  146. if (APP.settings.getDisplayName()) {
  147. Chat.setChatConversationMode(true);
  148. }
  149. $("#toggle_smileys").click(function() {
  150. Chat.toggleSmileys();
  151. });
  152. $('#nickinput').keydown(function (event) {
  153. if (event.keyCode === 13) {
  154. event.preventDefault();
  155. let val = this.value;
  156. this.value = '';
  157. eventEmitter.emit(UIEvents.NICKNAME_CHANGED, val);
  158. }
  159. });
  160. var usermsg = $('#usermsg');
  161. usermsg.keydown(function (event) {
  162. if (event.keyCode === 13) {
  163. event.preventDefault();
  164. var value = this.value;
  165. usermsg.val('').trigger('autosize.resize');
  166. this.focus();
  167. var command = new CommandsProcessor(value, eventEmitter);
  168. if (command.isCommand()) {
  169. command.processCommand();
  170. } else {
  171. var message = UIUtil.escapeHtml(value);
  172. eventEmitter.emit(UIEvents.MESSAGE_CREATED, message);
  173. }
  174. }
  175. });
  176. var onTextAreaResize = function () {
  177. resizeChatConversation();
  178. Chat.scrollChatToBottom();
  179. };
  180. usermsg.autosize({callback: onTextAreaResize});
  181. eventEmitter.on(UIEvents.SIDE_TOOLBAR_CONTAINER_TOGGLED,
  182. function(containerId, isVisible) {
  183. if (containerId !== CHAT_CONTAINER_ID || !isVisible)
  184. return;
  185. unreadMessages = 0;
  186. updateVisualNotification();
  187. // Undock the toolbar when the chat is shown and if we're in a
  188. // video mode.
  189. if (VideoLayout.isLargeVideoVisible()) {
  190. ToolbarToggler.dockToolbar(false);
  191. }
  192. // if we are in conversation mode focus on the text input
  193. // if we are not, focus on the display name input
  194. if (APP.settings.getDisplayName())
  195. $('#usermsg').focus();
  196. else
  197. $('#nickinput').focus();
  198. });
  199. addSmileys();
  200. updateVisualNotification();
  201. },
  202. /**
  203. * Appends the given message to the chat conversation.
  204. */
  205. updateChatConversation (id, displayName, message, stamp) {
  206. var divClassName = '';
  207. if (APP.conference.isLocalId(id)) {
  208. divClassName = "localuser";
  209. } else {
  210. divClassName = "remoteuser";
  211. if (!Chat.isVisible()) {
  212. unreadMessages++;
  213. UIUtil.playSoundNotification('chatNotification');
  214. updateVisualNotification();
  215. }
  216. }
  217. // replace links and smileys
  218. // Strophe already escapes special symbols on sending,
  219. // so we escape here only tags to avoid double &amp;
  220. var escMessage = message.replace(/</g, '&lt;').
  221. replace(/>/g, '&gt;').replace(/\n/g, '<br/>');
  222. var escDisplayName = UIUtil.escapeHtml(displayName);
  223. message = processReplacements(escMessage);
  224. var messageContainer =
  225. '<div class="chatmessage">'+
  226. '<img src="images/chatArrow.svg" class="chatArrow">' +
  227. '<div class="username ' + divClassName +'">' + escDisplayName +
  228. '</div>' + '<div class="timestamp">' + getCurrentTime(stamp) +
  229. '</div>' + '<div class="usermessage">' + message + '</div>' +
  230. '</div>';
  231. $('#chatconversation').append(messageContainer);
  232. $('#chatconversation').animate(
  233. { scrollTop: $('#chatconversation')[0].scrollHeight}, 1000);
  234. },
  235. /**
  236. * Appends error message to the conversation
  237. * @param errorMessage the received error message.
  238. * @param originalText the original message.
  239. */
  240. chatAddError (errorMessage, originalText) {
  241. errorMessage = UIUtil.escapeHtml(errorMessage);
  242. originalText = UIUtil.escapeHtml(originalText);
  243. $('#chatconversation').append(
  244. '<div class="errorMessage"><b>Error: </b>' + 'Your message' +
  245. (originalText? (' \"'+ originalText + '\"') : "") +
  246. ' was not sent.' +
  247. (errorMessage? (' Reason: ' + errorMessage) : '') + '</div>');
  248. $('#chatconversation').animate(
  249. { scrollTop: $('#chatconversation')[0].scrollHeight}, 1000);
  250. },
  251. /**
  252. * Sets the subject to the UI
  253. * @param subject the subject
  254. */
  255. setSubject (subject) {
  256. let toggleFunction;
  257. if (subject) {
  258. subject = subject.trim();
  259. }
  260. toggleFunction = subject ? UIUtil.showElement : UIUtil.hideElement;
  261. toggleFunction = toggleFunction.bind(UIUtil);
  262. let subjectId = 'subject';
  263. let html = linkify(UIUtil.escapeHtml(subject));
  264. $(`#${subjectId}`).html(html);
  265. toggleFunction(subjectId);
  266. },
  267. /**
  268. * Sets the chat conversation mode.
  269. * Conversation mode is the normal chat mode, non conversation mode is
  270. * where we ask user to input its display name.
  271. * @param {boolean} isConversationMode if chat should be in
  272. * conversation mode or not.
  273. */
  274. setChatConversationMode (isConversationMode) {
  275. $('#' + CHAT_CONTAINER_ID)
  276. .toggleClass('is-conversation-mode', isConversationMode);
  277. // this is needed when we transition from no conversation mode to
  278. // conversation mode. When user enters his nickname and hits enter,
  279. // to focus on the write area.
  280. if (isConversationMode) {
  281. $('#usermsg').focus();
  282. }
  283. },
  284. /**
  285. * Resizes the chat area.
  286. */
  287. resizeChat (width, height) {
  288. $('#' + CHAT_CONTAINER_ID).width(width).height(height);
  289. resizeChatConversation();
  290. },
  291. /**
  292. * Indicates if the chat is currently visible.
  293. */
  294. isVisible () {
  295. return UIUtil.isVisible(
  296. document.getElementById(CHAT_CONTAINER_ID));
  297. },
  298. /**
  299. * Shows and hides the window with the smileys
  300. */
  301. toggleSmileys,
  302. /**
  303. * Scrolls chat to the bottom.
  304. */
  305. scrollChatToBottom () {
  306. setTimeout(function () {
  307. $('#chatconversation').scrollTop(
  308. $('#chatconversation')[0].scrollHeight);
  309. }, 5);
  310. }
  311. };
  312. export default Chat;