| /* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */ |
| const CSSEmoticon = require('./cssemoticons'); |
| |
| const msgIdPrefix = 'chat-msg-id-' |
| , closedSize = 20 |
| , closedSizePx = closedSize + "px" |
| , doneTypingInterval = 5000 //time in ms, 5 second for example |
| , SEND_ENTER = 'enter', SEND_CTRL = 'ctrl' |
| , audio = new Audio('./public/chat_message.mp3') |
| ; |
| let p, ctrlBlk, tabs, openedHeight = "345px", openedWidth = "300px", allPrefix = "All" |
| , roomPrefix = "Room ", typingTimer, roomMode = false |
| , editor = $('#chatMessage .wysiwyg-editor'), muted = false, sendOn, DEF_SEND |
| , userId, inited = false, newMsgNotification |
| ; |
| |
| function __setCssVar(key, _val) { |
| const val = ('' + _val).endsWith('px') ? _val : _val + 'px'; |
| if (roomMode) { |
| if (typeof(Room) == 'object' && typeof(Room.setCssVar) === 'function') { |
| Room.setCssVar(key, val); |
| } |
| } else { |
| OmUtil.setCssVar(key, val); |
| } |
| } |
| function __setCssWidth(val) { |
| __setCssVar('--chat-width', val) |
| } |
| function __setCssHeight(val) { |
| __setCssVar('--chat-height', val) |
| } |
| function _load() { |
| const s = Settings.load(); |
| if (typeof(s.chat) === 'undefined') { |
| s.chat = { |
| muted: false |
| , sendOn: DEF_SEND |
| }; |
| Settings.save(s) |
| } |
| muted = s.chat.muted === true; |
| sendOn = s.chat.sendOn === SEND_ENTER ? SEND_ENTER : SEND_CTRL; |
| return s; |
| } |
| function _updateAudioBtn() { |
| const btn = $('#chat .audio'); |
| btn.removeClass('sound' + (muted ? '' : '-mute')).addClass('sound' + (muted ? '-mute' : '')) |
| .attr('title', btn.data(muted ? 'sound-enabled' : 'sound-muted')); |
| } |
| function _updateSendBtn() { |
| const btn = $('#chat .send-btn') |
| , ctrl = sendOn === SEND_CTRL; |
| if (ctrl) { |
| btn.addClass('send-ctrl'); |
| editor.off('keydown', _sendOnEnter).keydown('Ctrl+return', _sendOnEnter); |
| } else { |
| btn.removeClass('send-ctrl'); |
| editor.off('keydown', _sendOnEnter).keydown('return', _sendOnEnter); |
| } |
| btn.attr('title', btn.data(ctrl ? 'send-ctrl' : 'send-enter')); |
| } |
| function _sendOnEnter() { |
| $('#chat .send').trigger('click'); |
| } |
| function doneTyping () { |
| typingTimer = null; |
| chatActivity('typing_stop', $('.room-block .room-container').data('room-id')); |
| } |
| function _emtClick() { |
| _editorAppend($(this).data('emt')); |
| } |
| function initToolbar() { |
| const emots = CSSEmoticon.emoticons; |
| const rowSize = 20, emotMenuList = $('#emotMenuList'); |
| emotMenuList.html(''); |
| let row; |
| for (let i = 0; i < emots.length; ++i) { |
| if (i % rowSize === 0) { |
| row = $('<tr></tr>'); |
| emotMenuList.append(row); |
| } |
| row.append($('<td>').append( |
| $('<div>').addClass('emt').html(CSSEmoticon.emoticonize(emots[i])) |
| .data('emt', emots[i]).click(_emtClick) |
| )); |
| } |
| const emtBtn = $('#emoticons'); |
| emtBtn.html(''); |
| emtBtn.append(' ' + CSSEmoticon.emoticonize(':)')); |
| const a = $('#chat .audio'); |
| const sbtn = $('#chat .send-btn'); |
| { //scope |
| _load(); |
| _updateAudioBtn(); |
| _updateSendBtn() |
| } |
| a.off().click(function() { |
| const s = _load(); |
| muted = s.chat.muted = !s.chat.muted; |
| _updateAudioBtn(); |
| Settings.save(s); |
| }); |
| sbtn.off().click(function() { |
| const s = _load(); |
| sendOn = s.chat.sendOn = s.chat.sendOn !== SEND_CTRL ? SEND_CTRL : SEND_ENTER; |
| _updateSendBtn(); |
| Settings.save(s); |
| }); |
| $('#chat #hyperlink').parent().find('button').off().click(function() { |
| _insertLink(); |
| }); |
| CSSEmoticon.animate(); |
| } |
| function isClosed() { |
| return p.hasClass('closed'); |
| } |
| function activateTab(id) { |
| if (isClosed()) { |
| tabs.find('.nav.nav-tabs .nav-link').each(function() { |
| const self = $(this) |
| , tabId = self.attr('aria-controls') |
| , tab = $('#' + tabId); |
| |
| if (tabId === id) { |
| self.addClass('active'); |
| tab.addClass('active'); |
| self.attr('aria-selected', true); |
| } else { |
| self.removeClass('active'); |
| tab.removeClass('active'); |
| self.attr('aria-selected', false); |
| } |
| }); |
| } else { |
| $('#chatTabs li a[aria-controls="' + id + '"]').tab('show'); |
| } |
| $('#activeChatTab').val(id).trigger('change'); |
| } |
| function _reinit(opts) { |
| userId = opts.userId; |
| allPrefix = opts.all; |
| roomPrefix = opts.room; |
| DEF_SEND = opts.sendOnEnter === true ? SEND_ENTER : SEND_CTRL; |
| sendOn = DEF_SEND; |
| p = $('#chatPanel'); |
| clearTimeout(p.data('timeout')); |
| ctrlBlk = $('#chatPopup .control.block'); |
| newMsgNotification = ctrlBlk.data('new-msg'); |
| editor = $('#chatMessage .wysiwyg-editor'); |
| initToolbar(); |
| tabs = $("#chatTabs"); |
| tabs.off().on('shown.bs.tab', function (e) { |
| const ct = $(e.target).attr('aria-controls'); |
| _scrollDown($('#' + ct)); |
| $('#activeChatTab').val(ct).trigger('change'); |
| }); |
| tabs.delegate(".btn.close-chat", "click", function() { |
| const panelId = $(this).closest("a").attr("aria-controls"); |
| _removeTab(panelId); |
| $('#chatTabs li:last-child a').tab('show'); |
| }); |
| if (roomMode) { |
| _removeResize(); |
| } else { |
| ctrlBlk.attr('title', ''); |
| p.removeClass('room opened').addClass('closed') |
| .off('mouseenter mouseleave') |
| .resizable({ |
| handles: 'n, ' + (Settings.isRtl ? 'e' : 'w') |
| , disabled: isClosed() |
| , minHeight: 195 |
| , minWidth: 260 |
| , stop: function(_, ui) { |
| p.css({'top': '', 'left': ''}); |
| openedHeight = ui.size.height + 'px'; |
| __setCssHeight(openedHeight); |
| __setCssWidth(ui.size.width); |
| } |
| }); |
| __setCssHeight(closedSize); |
| } |
| ctrlBlk.off().click(Chat.toggle); |
| $('#chatMessage').off().on('input propertychange paste', function () { |
| const room = $('.room-block .room-container'); |
| if (room.length) { |
| if (!!typingTimer) { |
| clearTimeout(typingTimer); |
| } else { |
| chatActivity('typing_start', room.data('room-id')); |
| } |
| typingTimer = setTimeout(doneTyping, doneTypingInterval); |
| } |
| }); |
| $('#chat .chat-toolbar .link-field').off().on('keypress', function(evt) { |
| if (evt.keyCode === 13) { |
| $(this).parent().find('button').trigger('click'); |
| } |
| return evt.keyCode !== 13; |
| }); |
| inited = true; |
| } |
| function _removeTab(id) { |
| $('#chatTabs li a[aria-controls="' + id + '"]').parent().remove(); |
| $('#' + id).remove(); |
| } |
| function _addTab(id, label) { |
| if (!inited) { |
| _reinit({}); |
| } |
| if ($('#chat').length < 1 || $('#' + id).length) { |
| return; |
| } |
| if (!label) { |
| label = id === "chatTab-all" ? allPrefix : roomPrefix + id.substr(9); |
| } |
| const link = $('<a class="nav-link" data-toggle="tab" role="tab">') |
| .attr('aria-controls', id) |
| .attr('href', '#' + id).text(label) |
| , li = $('<li class="nav-item">').append(link); |
| if (id.indexOf("chatTab-u") === 0) { |
| link.append(OmUtil.tmpl('#chat-close-block')); |
| } |
| tabs.find('.nav.nav-tabs').append(li); |
| const msgArea = OmUtil.tmpl('#chat-msg-area-template', id); |
| tabs.find('.tab-content').append(msgArea); |
| msgArea.append($('<div class="clear icons actions align-left">').addClass('short') |
| .append(OmUtil.tmpl('#chat-actions-short-template'))); |
| msgArea.append($('<div class="clear icons actions align-left">').addClass('short-mod') |
| .append(OmUtil.tmpl('#chat-actions-short-template')) |
| .append(OmUtil.tmpl('#chat-actions-accept-template'))); |
| msgArea.append($('<div class="clear icons actions align-left">').addClass('full') |
| .append(OmUtil.tmpl('#chat-actions-short-template')) |
| .append(OmUtil.tmpl('#chat-actions-others-template').children().clone())); |
| msgArea.append($('<div class="clear icons actions align-left">').addClass('full-mod') |
| .append(OmUtil.tmpl('#chat-actions-short-template')) |
| .append(OmUtil.tmpl('#chat-actions-others-template').children().clone()) |
| .append(OmUtil.tmpl('#chat-actions-accept-template'))); |
| const actions = __hideActions(); |
| actions.find('.user').off().click(function() { |
| const e = $(this).parent(); |
| showUserInfo(e.data("userId")); |
| }); |
| actions.find('.add').off().click(function() { |
| const e = $(this).parent(); |
| addContact(e.data("userId")); |
| }); |
| actions.find('.new-email').off().click(function() { |
| const e = $(this).parent(); |
| privateMessage(e.data("userId")); |
| }); |
| actions.find('.invite').off().click(function() { |
| const e = $(this).parent(); |
| inviteUser(e.data("userId")); |
| }); |
| actions.find('.accept').off().click(function() { |
| const e = $(this).parent() |
| , msgId = e.data('msgId'); |
| chatActivity('accept', e.data('roomId'), msgId); |
| __hideActions(); |
| $('#chat-msg-id-' + msgId).remove(); |
| }); |
| activateTab(id); |
| } |
| function __hideActions() { |
| return $('#chat .tab-content .messageArea .icons').hide(); |
| } |
| function __getActions(row) { |
| return row.closest('.messageArea').find('.actions.' + row.data('actions')); |
| } |
| function _addMessage(m) { |
| if ($('#chat').length > 0 && m && m.type === "chat") { |
| let msg, cm, notify = false; |
| while (!!(cm = m.msg.pop())) { |
| let area = $('#' + cm.scope); |
| if (cm.from.id !== userId && (isClosed() || !area.is(':visible'))) { |
| notify = true; |
| } |
| const actions = ('full' === cm.actions ? 'full' : 'short') + (cm.needModeration ? '-mod' : ''); |
| msg = OmUtil.tmpl('#chat-msg-template', msgIdPrefix + cm.id) |
| const row = msg.find('.user-row') |
| .data('userId', cm.from.id) |
| .data('actions', actions) |
| .mouseenter(function() { |
| __hideActions(); |
| __getActions($(this)) |
| .data('userId', $(this).data('userId')) |
| .data('roomId', $(this).data('roomId')) |
| .data('msgId', $(this).data('msgId')) |
| .css('top', ($(this).closest('.msg-row')[0].offsetTop + 20) + 'px') |
| .show(); |
| }); |
| if (cm.needModeration) { |
| row.parent().addClass('need-moderation'); |
| row.data('roomId', cm.scope.substring(9)) |
| .data('msgId', cm.id); |
| } |
| area.mouseleave(function() { |
| __hideActions(); |
| }); |
| msg.find('.from').data('user-id', cm.from.id).html(cm.from.displayName || cm.from.name); |
| msg.find('.time').html(cm.time).attr('title', cm.sent); |
| if (!area.length) { |
| _addTab(cm.scope, cm.scopeName); |
| area = $('#' + cm.scope); |
| } |
| if (m.mode === "accept") { |
| $('#chat-msg-id-' + cm.id).remove(); |
| } |
| const btm = area[0].scrollHeight - (area.scrollTop() + area.innerHeight()) < 3; //approximately equal |
| if (area.data('lastDate') !== cm.date) { |
| area.append(OmUtil.tmpl('#chat-date-template').html(cm.date).mouseenter(function() { |
| __hideActions(); |
| })); |
| area.data('lastDate', cm.date); |
| } |
| area.append(msg); |
| msg.find('.user-row')[0].style.backgroundImage = 'url(' + (!!cm.from.img ? cm.from.img : './profile/' + cm.from.id + '?anticache=' + Date.now()) + ')'; |
| |
| msg.find('.msg').html(CSSEmoticon.emoticonize(!!cm.message ? cm.message : "")); |
| if (btm) { |
| _scrollDown(area); |
| } |
| } |
| if (notify) { |
| ctrlBlk.addClass('bg-warning'); |
| if (p.is(':visible') && !muted) { |
| OmUtil.notify(newMsgNotification, 'new_chat_msg', () => { |
| // impossible to use Notification API from iFrame |
| audio.play() |
| .then(function() { |
| // Automatic playback started! |
| }).catch(function() { |
| // Automatic playback failed. |
| }); |
| }); |
| } |
| } |
| CSSEmoticon.animate(); |
| } |
| } |
| function _setOpened() { |
| __setCssWidth(openedWidth); |
| p.resizable({ |
| handles: (Settings.isRtl ? 'e' : 'w') |
| , minWidth: 165 |
| , stop: function(_, ui) { |
| p.css({'left': '', 'width': '', 'height': ''}); |
| openedWidth = ui.size.width + 'px'; |
| __setCssWidth(openedWidth); |
| } |
| }); |
| } |
| function _removeResize() { |
| if (p.resizable('instance') !== undefined) { |
| p.resizable('destroy'); |
| } |
| } |
| function _open(handler) { |
| if (isClosed()) { |
| ctrlBlk.removeClass('bg-warning'); |
| let opts; |
| if (roomMode) { |
| opts = {width: openedWidth}; |
| } else { |
| opts = {height: openedHeight}; |
| p.resizable("option", "disabled", false); |
| } |
| p.removeClass('closed').animate(opts, 1000, function() { |
| __hideActions(); |
| p.removeClass('closed'); |
| p.css({'height': '', 'width': ''}); |
| if (typeof(handler) === 'function') { |
| handler(); |
| } |
| ctrlBlk.attr('title', ctrlBlk.data('ttl-undock')); |
| if (roomMode) { |
| _setOpened(); |
| } else { |
| __setCssHeight(openedHeight); |
| } |
| _setAreaHeight(); |
| }); |
| } |
| } |
| function _close(handler) { |
| if (!isClosed()) { |
| let opts; |
| if (roomMode) { |
| opts = {width: closedSizePx}; |
| } else { |
| opts = {height: closedSizePx}; |
| p.resizable("option", "disabled", true); |
| } |
| p.animate(opts, 1000, function() { |
| p.addClass('closed').css({'height': '', 'width': ''}); |
| if (roomMode) { |
| __setCssWidth(closedSizePx); |
| _removeResize(); |
| } else { |
| __setCssHeight(closedSizePx); |
| } |
| if (typeof(handler) === 'function') { |
| handler(); |
| } |
| ctrlBlk.attr('title', ctrlBlk.data('ttl-dock')); |
| }); |
| } |
| } |
| function _toggle() { |
| if (isClosed()) { |
| _open(); |
| } else { |
| _close(); |
| } |
| } |
| function _editorAppend(_emoticon) { |
| editor.html(editor.html() + ' ' + _emoticon + ' ').trigger('change'); |
| } |
| function _clean() { |
| editor.html('').trigger('change'); |
| } |
| function _setRoomMode(_mode) { |
| roomMode = _mode; |
| if (inited && !roomMode) { |
| // remove all private chats on room exit |
| $('li[aria-controls^="chatTab-u"]').remove(); |
| $('div[id^="chatTab-u"]').remove(); |
| } |
| _reinit({userId: userId, all: allPrefix, room: roomPrefix, sendOnEnter: sendOn === SEND_ENTER}); |
| } |
| function _scrollDown(area) { |
| area.animate({ |
| scrollTop: area[0].scrollHeight |
| }, 300); |
| } |
| function _setAreaHeight() { |
| $('#chat .messageArea').each(function() { |
| _scrollDown($(this)); |
| }); |
| } |
| function _insertLink() { |
| const text = $('#chat #hyperlink').parent().find('input').val(); |
| if ('' === text) { |
| return; |
| } |
| let url = text.trim(); |
| if ('' === url) { |
| return; |
| } |
| if (!/^(https?:)?\/\//i.test(url)) { |
| url = 'http://' + url; |
| } |
| const a = $('<div>').append($('<a></a>').attr('target', '_blank').attr('href', url).text(url)).html(); |
| if (window.getSelection) { |
| const sel = window.getSelection(); |
| if (sel.rangeCount) { |
| const range = sel.getRangeAt(0); |
| if ($(range.startContainer).parents('.wysiwyg-editor').length > 0) { |
| range.deleteContents(); |
| range.insertNode(a); |
| } else { |
| _editorAppend(a); |
| } |
| } |
| } |
| } |
| function _typingActivity(uid, active) { |
| if (typeof(Room) !== 'object') { |
| return; |
| } |
| const c = Room.getClient(uid); |
| if (!c) { |
| return; |
| } |
| const u = c.find('.typing-activity'); |
| if (!u) { |
| return; |
| } |
| if (active) { |
| u.addClass("typing"); |
| } else { |
| u.removeClass("typing"); |
| } |
| } |
| |
| $(function() { |
| Wicket.Event.subscribe("/websocket/message", function(_, msg) { |
| try { |
| if (msg instanceof Blob) { |
| return; //ping |
| } |
| const m = JSON.parse(msg); |
| if (m) { |
| switch(m.type) { |
| case "chat": |
| if ('clean' === m.action) { |
| $('#' + m.scope).html(''); |
| } else { |
| _addMessage(m); |
| } |
| break; |
| case "typing": |
| if (typeof(typingActivity) === "function") { |
| _typingActivity(m.uid, m.active); |
| } |
| break; |
| } |
| } |
| } catch (err) { |
| //no-op |
| } |
| }); |
| function _cancelAskNotification() { |
| $(document).off('click', _askNotification) |
| } |
| function _askNotification() { |
| OmUtil.requestNotifyPermission(_cancelAskNotification, _cancelAskNotification); |
| } |
| $(document).on('click', _askNotification); |
| }); |
| |
| module.exports = { |
| SEND_ENTER: SEND_ENTER |
| , SEND_CTRL: SEND_CTRL |
| |
| , reinit: _reinit |
| , removeTab: _removeTab |
| , addTab: _addTab |
| , addMessage: _addMessage |
| , open: _open |
| , setOpened: function() { |
| _open(function() { |
| _setOpened(); |
| }); |
| } |
| , close: _close |
| , toggle: _toggle |
| , setRoomMode: _setRoomMode |
| , clean: _clean |
| , reload: () => { |
| _load(); |
| _updateAudioBtn(); |
| _updateSendBtn(); |
| } |
| , validate: function() { |
| return !!editor && editor.text().trim().length > 0; |
| } |
| }; |