This is an automated email from the ASF dual-hosted git repository.

solomax pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openmeetings.git


The following commit(s) were added to refs/heads/master by this push:
     new 6751e89  [OPENMEETINGS-2000] moving JS code to npm
6751e89 is described below

commit 6751e89777048a1e5d884ae102bf31410fc28888
Author: Maxim Solodovnik <[email protected]>
AuthorDate: Thu Dec 24 15:31:32 2020 +0700

    [OPENMEETINGS-2000] moving JS code to npm
---
 openmeetings-web/pom.xml                           |  35 +-
 openmeetings-web/src/main/front/chat/package.json  |  18 +
 openmeetings-web/src/main/front/chat/src/chat.js   | 544 +++++++++++++++++++++
 .../src/main/front/chat/src/cssemoticons.js        | 107 ++++
 openmeetings-web/src/main/front/chat/src/index.js  |   4 +
 openmeetings-web/src/main/front/room/src/index.js  |  10 -
 openmeetings-web/src/main/front/room/src/room.js   |  51 +-
 .../src/main/front/room/src/sip-dialer.js          |  48 ++
 .../apache/openmeetings/web/user/chat/raw-chat.js  | 525 --------------------
 .../openmeetings/web/user/chat/raw-cssemoticons.js | 106 ----
 10 files changed, 743 insertions(+), 705 deletions(-)

diff --git a/openmeetings-web/pom.xml b/openmeetings-web/pom.xml
index e027999..e35439d 100644
--- a/openmeetings-web/pom.xml
+++ b/openmeetings-web/pom.xml
@@ -103,6 +103,24 @@
                                                </configuration>
                                        </execution>
                                        <execution>
+                                               <id>chat-install</id>
+                                               <goals><goal>npm</goal></goals>
+                                               <configuration>
+                                                       
<workingDirectory>src/main/front/chat</workingDirectory>
+                                               </configuration>
+                                       </execution>
+                                       <execution>
+                                               <id>chat</id>
+                                               <goals><goal>npm</goal></goals>
+                                               <configuration>
+                                                       <arguments>run 
build</arguments>
+                                                       
<workingDirectory>src/main/front/chat</workingDirectory>
+                                                       <environmentVariables>
+                                                               
<outDir>${project.build.directory}/generated-sources/js/org/apache/openmeetings/web/user/chat/</outDir>
+                                                       </environmentVariables>
+                                               </configuration>
+                                       </execution>
+                                       <execution>
                                                <id>settings-install</id>
                                                <goals><goal>npm</goal></goals>
                                                <configuration>
@@ -251,23 +269,6 @@
                                                        
<jsTargetDir>../generated-sources/js/org/apache/openmeetings/web/room</jsTargetDir>
                                                </configuration>
                                        </execution>
-                                       <execution>
-                                               <id>chat-js</id>
-                                               <goals>
-                                                       <goal>minify</goal>
-                                               </goals>
-                                               <configuration>
-                                                       <charset>UTF-8</charset>
-                                                       
<jsSourceDir>../java/org/apache/openmeetings/web/user/chat</jsSourceDir>
-                                                       <jsSourceFiles>
-                                                               
<jsSourceFile>raw-cssemoticons.js</jsSourceFile>
-                                                               
<jsSourceFile>raw-chat.js</jsSourceFile>
-                                                       </jsSourceFiles>
-                                                       
<jsFinalFile>chat.js</jsFinalFile>
-                                                       
<jsEngine>CLOSURE</jsEngine>
-                                                       
<jsTargetDir>../generated-sources/js/org/apache/openmeetings/web/user/chat</jsTargetDir>
-                                               </configuration>
-                                       </execution>
                                </executions>
                        </plugin>
                        <plugin>
diff --git a/openmeetings-web/src/main/front/chat/package.json 
b/openmeetings-web/src/main/front/chat/package.json
new file mode 100644
index 0000000..52f0c0d
--- /dev/null
+++ b/openmeetings-web/src/main/front/chat/package.json
@@ -0,0 +1,18 @@
+{
+  "name": "chat",
+  "version": "1.0.0",
+  "description": "Methods for OM chat",
+  "main": "src/index.js",
+  "scripts": {
+    "build-dev": "browserify src/index.js --transform-key=staging -o 
${outDir}${npm_package_name}.js",
+    "build-prod": "browserify src/index.js --transform-key=production -p 
tinyify -o ${outDir}${npm_package_name}.min.js",
+    "build": "npm run build-dev && npm run build-prod"
+  },
+  "author": "",
+  "license": "Apache-2.0",
+  "rat-license": "Licensed under the Apache License, Version 2.0 (the 
\"License\") http://www.apache.org/licenses/LICENSE-2.0";,
+  "devDependencies": {
+    "browserify": "^17.0.0",
+    "tinyify": "^3.0.0"
+  }
+}
diff --git a/openmeetings-web/src/main/front/chat/src/chat.js 
b/openmeetings-web/src/main/front/chat/src/chat.js
new file mode 100644
index 0000000..097f900
--- /dev/null
+++ b/openmeetings-web/src/main/front/chat/src/chat.js
@@ -0,0 +1,544 @@
+/* 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
+               };
+       }
+       muted = s.chat.muted === true;
+       sendOn = s.chat.sendOn === SEND_ENTER ? SEND_ENTER : SEND_CTRL;
+       return s;
+}
+function _updateAudioBtn(btn) {
+       btn.removeClass('sound' + (muted ? '' : '-mute')).addClass('sound' + 
(muted ? '-mute' : ''))
+                       .attr('title', btn.data(muted ? 'sound-enabled' : 
'sound-muted'));
+}
+function _updateSendBtn(btn) {
+       const 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(a);
+               _updateSendBtn(sbtn)
+       }
+       a.off().click(function() {
+               const s = _load();
+               muted = s.chat.muted = !s.chat.muted;
+               _updateAudioBtn(a);
+               Settings.save(s);
+       });
+       sbtn.off().click(function() {
+               const s = _load();
+               sendOn = s.chat.sendOn = s.chat.sendOn !== SEND_CTRL ? 
SEND_CTRL : SEND_ENTER;
+               _updateSendBtn(sbtn);
+               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() {
+               if (event.keyCode === 13) {
+                       $(this).parent().find('button').trigger('click');
+               }
+               return event.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) {
+                               if (window === window.parent) {
+                                       function _newMessage() {
+                                               
OmUtil.notify(newMsgNotification, 'new_chat_msg');
+                                       }
+                                       if (Notification.permission === 
'granted') {
+                                               _newMessage();
+                                       } else if (Notification.permission !== 
'denied') {
+                                               
Notification.requestPermission().then(permission => {
+                                                       if (permission === 
'granted') {
+                                                               _newMessage();
+                                                       }
+                                               });
+                                       }
+                               } else {
+                                       // 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 = Room.getClient(uid).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
+               }
+       });
+});
+
+module.exports = {
+       reinit: _reinit
+       , removeTab: _removeTab
+       , addTab: _addTab
+       , addMessage: _addMessage
+       , open: _open
+       , setOpened: function() {
+               _open(function() {
+                       _setOpened();
+               });
+       }
+       , close: _close
+       , toggle: _toggle
+       , setRoomMode: _setRoomMode
+       , clean: _clean
+       , validate: function() {
+               return !!editor && editor.text().trim().length > 0;
+       }
+};
diff --git a/openmeetings-web/src/main/front/chat/src/cssemoticons.js 
b/openmeetings-web/src/main/front/chat/src/cssemoticons.js
new file mode 100644
index 0000000..b6abd61
--- /dev/null
+++ b/openmeetings-web/src/main/front/chat/src/cssemoticons.js
@@ -0,0 +1,107 @@
+/*
+ * Based on
+ *
+ * jQuery CSSEmoticons plugin 0.2.9
+ *
+ * Copyright (c) 2010 Steve Schwartz (JangoSteve)
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ *   http://www.opensource.org/licenses/mit-license.php
+ *   http://www.gnu.org/licenses/gpl.html
+ *
+ * Date: Sun Oct 22 1:00:00 2010 -0500
+ */
+const escapeCharacters = [")", "(", "*", "[", "]", "{", "}", "|", "^", "<", 
">", "\\", "?", "+", "=", "."]
+       , specialRegex = new RegExp('(\\' + escapeCharacters.join('|\\') + ')', 
'g')
+       // One of these characters must be present before the matched emoticon, 
or the matched emoticon must be the first character in the container HTML
+       //  This is to ensure that the characters in the middle of HTML 
properties or URLs are not matched as emoticons
+       //  Below matches ^ (first character in container HTML), \s (whitespace 
like space or tab), or \0 (NULL character)
+       // (<\\S+.*>) matches <\\S+.*> (matches an HTML tag like <span> or 
<div>), but haven't quite gotten it working yet, need to push this fix now
+       , preMatch = '(^|[\\s\\0])'
+       , emoticons = []
+       , matchers = [];
+
+function createMatcher(m) {
+       const str = m.text.replace(specialRegex, '\\$1');
+       m.regexp = new RegExp(preMatch + '(' + str + ')', 'g');
+       return m;
+}
+function addMatchers(arr) {
+       for (let i = 0; i < arr.length; ++i) {
+               const o = arr[i]
+               let m = typeof(o) === 'object' ? JSON.parse(JSON.stringify(o)) 
: {text: o, cssClass: ' '};
+               emoticons.push(m.text);
+
+               matchers.push(createMatcher(m));
+               if (m.text.indexOf('=') > -1) {
+                       m = JSON.parse(JSON.stringify(m));
+                       m.text = m.text.replace(/=/g, '&#61;').replace(/[+]/g, 
'&#43;');
+                       matchers.push(createMatcher(m));
+               }
+               if (m.text.indexOf('\'') > -1) {
+                       m = JSON.parse(JSON.stringify(m));
+                       m.text = m.text.replace(/'/g, '&#39;');
+                       matchers.push(createMatcher(m));
+               }
+       }
+}
+addMatchers([
+       ":-)", ":o)", ":c)", ":^)", ":-D", ":-(", ":-9", ";-)", ":-P", ":-p", 
":-Þ", ":-b", ":-O", ":-/", ":-X", ":-#", ":'(", "B-)", "8-)", ";*(", ":-*", 
":-\\",
+       "?-)" // <== This is my own invention, it's a smiling pirate (with an 
eye-patch)!
+]);
+addMatchers([ // separate these out so that we can add a letter-spacing 
between the characters for better proportions
+       ":)", ":]", "=]", "=)", "8)", ":}", ":D", ":(", ":[", ":{", "=(", ";)", 
";]", ";D", ":P", ":p", "=P", "=p", ":b", ":Þ", ":O", ":/", "=/", ":S", ":#", 
":X", "B)", ":|", ":\\", "=\\", ":*", ":&gt;", ":&lt;"
+]);
+addMatchers([ // emoticons to be treated with a special class, hash specifies 
the additional class to add, along with standard css-emoticon class
+       {text: "&gt;:)", cssClass: "red-emoticon small-emoticon 
spaced-emoticon"},
+       {text: "&gt;;)", cssClass: "red-emoticon small-emoticon 
spaced-emoticon"},
+       {text: "&gt;:(", cssClass: "red-emoticon small-emoticon 
spaced-emoticon"},
+       {text: "&gt;: )", cssClass: "red-emoticon small-emoticon"},
+       {text: "&gt;; )", cssClass: "red-emoticon small-emoticon"},
+       {text: "&gt;: (", cssClass: "red-emoticon small-emoticon"},
+       {text: ";(", cssClass: "red-emoticon spaced-emoticon"},
+       {text: "&lt;3", cssClass: "pink-emoticon counter-rotated"},
+       {text: "O_O", cssClass: "no-rotate"},
+       {text: "o_o", cssClass: "no-rotate"},
+       {text: "0_o", cssClass: "no-rotate"},
+       {text: "O_o", cssClass: "no-rotate"},
+       {text: "T_T", cssClass: "no-rotate"},
+       {text: "^_^", cssClass: "no-rotate"},
+       {text: "O:)", cssClass: "small-emoticon spaced-emoticon"},
+       {text: "O: )", cssClass: "small-emoticon"},
+       {text: "8D", cssClass: "small-emoticon spaced-emoticon"},
+       {text: "XD", cssClass: "small-emoticon spaced-emoticon"},
+       {text: "xD", cssClass: "small-emoticon spaced-emoticon"},
+       {text: "=D", cssClass: "small-emoticon spaced-emoticon"},
+       {text: "8O", cssClass: "small-emoticon spaced-emoticon"},
+       {text: "[+=..]", cssClass: "no-rotate nintendo-controller"}
+]);
+
+module.exports = {
+       emoticons: emoticons
+       , matchers: matchers
+       , defaults: {animate: true, delay: 500, exclude: 
'pre,code,.no-emoticons'}
+       , emoticonize: function(str, options) {
+               const opts = $.extend({}, this.defaults, options);
+
+               let cssClass = 'css-emoticon';
+               if (opts.animate) {
+                       cssClass += ' un-transformed-emoticon 
animated-emoticon';
+               }
+               for (let i = 0; i < this.matchers.length; ++i) {
+                       const m = this.matchers[i];
+                       const css = cssClass + " " + m.cssClass;
+                       str = str.replace(m.regexp, "$1<span class='" + css + 
"'>$2</span>");
+               }
+               return str;
+       }
+       , animate: function(options) {
+               const opts = $.extend({}, this.defaults, options);
+               // animate emoticons
+               if (opts.animate) {
+                       setTimeout(function () {
+                               
$('.un-transformed-emoticon').removeClass('un-transformed-emoticon');
+                       }, opts.delay);
+               }
+       }
+};
diff --git a/openmeetings-web/src/main/front/chat/src/index.js 
b/openmeetings-web/src/main/front/chat/src/index.js
new file mode 100644
index 0000000..6ccf043
--- /dev/null
+++ b/openmeetings-web/src/main/front/chat/src/index.js
@@ -0,0 +1,4 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
+Object.assign(window, {
+       Chat: require('./chat')
+});
diff --git a/openmeetings-web/src/main/front/room/src/index.js 
b/openmeetings-web/src/main/front/room/src/index.js
index f6449d2..fc210b9 100644
--- a/openmeetings-web/src/main/front/room/src/index.js
+++ b/openmeetings-web/src/main/front/room/src/index.js
@@ -18,14 +18,4 @@ Object.assign(window, {
        , Sharer: require('./sharer')
        , Room: require('./room')
        , Activities: require('./activities')
-
-       /***** functions required by SIP   ******/
-       , typingActivity: function(uid, active) {
-               const u = Room.getClient(uid).find('.typing-activity');
-               if (active) {
-                       u.addClass("typing");
-               } else {
-                       u.removeClass("typing");
-               }
-       }
 });
diff --git a/openmeetings-web/src/main/front/room/src/room.js 
b/openmeetings-web/src/main/front/room/src/room.js
index 3e440f4..030f456 100644
--- a/openmeetings-web/src/main/front/room/src/room.js
+++ b/openmeetings-web/src/main/front/room/src/room.js
@@ -3,6 +3,7 @@ const NoSleep = require('nosleep.js');
 const VideoManager = require('./video-manager');
 const Sharer = require('./sharer');
 const Activities = require('./activities');
+const SipDialer = require('./sip-dialer');
 
 const sbSide = Settings.isRtl ? 'right' : 'left';
 let options, menuHeight, sb, dock, noSleep;
@@ -158,11 +159,11 @@ function _load() {
        Wicket.Event.subscribe("/websocket/error", _close);
        $(window).on('keydown.openmeetings', _preventKeydown);
        $(window).on('keyup.openmeetings', _keyHandler);
-       $(window).on('keydown.om-sip', _sipKeyDown);
-       $(window).on('keyup.om-sip', _sipKeyUp);
+       $(window).on('keydown.om-sip', SipDialer.keyDown);
+       $(window).on('keyup.om-sip', SipDialer.keyUp);
        $(document).click(_mouseHandler);
        _addNoSleep();
-       _initSip();
+       SipDialer.init();
 }
 function _addNoSleep() {
        _removeNoSleep();
@@ -479,50 +480,6 @@ function _updateClient(c) {
        }
        VideoManager.update(c)
 }
-function __addSipText(v) {
-       const txt = $('.sip-number');
-       txt.val(txt.val() + v);
-}
-function __eraseSipText() {
-       const txt = $('.sip-number')
-               , t = txt.val();
-       if (!!t) {
-               txt.val(t.substring(0, t.length - 1));
-       }
-}
-function _initSip() {
-       $('.sip .button-row button').off().click(function() {
-               __addSipText($(this).data('value'));
-       });
-       $('#sip-dialer-btn-erase').off().click(__eraseSipText);
-}
-function _sipGetKey(evt) {
-       let k = -1;
-       if (evt.keyCode > 47 && evt.keyCode < 58) {
-               k = evt.keyCode - 48;
-       }
-       if (evt.keyCode > 95 && evt.keyCode < 106) {
-               k = evt.keyCode - 96;
-       }
-       return k;
-}
-function _sipKeyDown(evt) {
-       const k = _sipGetKey(evt);
-       if (k > 0) {
-               $('#sip-dialer-btn-' + k).addClass('bg-warning');
-       }
-}
-function _sipKeyUp(evt) {
-       if (evt.key === 'Backspace') {
-               __eraseSipText();
-       } else {
-               const k = _sipGetKey(evt);
-               if (k > 0) {
-                       $('#sip-dialer-btn-' + k).removeClass('bg-warning');
-                       __addSipText(k);
-               }
-       }
-}
 
 module.exports = {
        init: _init
diff --git a/openmeetings-web/src/main/front/room/src/sip-dialer.js 
b/openmeetings-web/src/main/front/room/src/sip-dialer.js
new file mode 100644
index 0000000..713e7c5
--- /dev/null
+++ b/openmeetings-web/src/main/front/room/src/sip-dialer.js
@@ -0,0 +1,48 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
+function __addSipText(v) {
+       const txt = $('.sip-number');
+       txt.val(txt.val() + v);
+}
+function __eraseSipText() {
+       const txt = $('.sip-number')
+               , t = txt.val();
+       if (!!t) {
+               txt.val(t.substring(0, t.length - 1));
+       }
+}
+function _sipGetKey(evt) {
+       let k = -1;
+       if (evt.keyCode > 47 && evt.keyCode < 58) {
+               k = evt.keyCode - 48;
+       }
+       if (evt.keyCode > 95 && evt.keyCode < 106) {
+               k = evt.keyCode - 96;
+       }
+       return k;
+}
+
+module.exports = {
+       init: function() {
+               $('.sip .button-row button').off().click(function() {
+                       __addSipText($(this).data('value'));
+               });
+               $('#sip-dialer-btn-erase').off().click(__eraseSipText);
+       }
+       , keyDown: function(evt) {
+               const k = _sipGetKey(evt);
+               if (k > 0) {
+                       $('#sip-dialer-btn-' + k).addClass('bg-warning');
+               }
+       }
+       , keyUp: function(evt) {
+               if (evt.key === 'Backspace') {
+                       __eraseSipText();
+               } else {
+                       const k = _sipGetKey(evt);
+                       if (k > 0) {
+                               $('#sip-dialer-btn-' + 
k).removeClass('bg-warning');
+                               __addSipText(k);
+                       }
+               }
+       }
+};
\ No newline at end of file
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/raw-chat.js
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/raw-chat.js
deleted file mode 100644
index 36e0258..0000000
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/raw-chat.js
+++ /dev/null
@@ -1,525 +0,0 @@
-/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
-var Chat = function() {
-       const msgIdPrefix = 'chat-msg-id-'
-               , closedSize = 20
-               , closedSizePx = closedSize + "px"
-               , emoticon = new CSSEmoticon()
-               , 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
-                       };
-               }
-               muted = s.chat.muted === true;
-               sendOn = s.chat.sendOn === SEND_ENTER ? SEND_ENTER : SEND_CTRL;
-               return s;
-       }
-       function _updateAudioBtn(btn) {
-               btn.removeClass('sound' + (muted ? '' : 
'-mute')).addClass('sound' + (muted ? '-mute' : ''))
-                               .attr('title', btn.data(muted ? 'sound-enabled' 
: 'sound-muted'));
-       }
-       function _updateSendBtn(btn) {
-               const 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 = emoticon.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(emoticon.emoticonize(emots[i]))
-                                               .data('emt', 
emots[i]).click(_emtClick)
-                               ));
-               }
-               const emtBtn = $('#emoticons');
-               emtBtn.html('');
-               emtBtn.append(' ' + emoticon.emoticonize(':)'));
-               const a = $('#chat .audio');
-               const sbtn = $('#chat .send-btn');
-               { //scope
-                       _load();
-                       _updateAudioBtn(a);
-                       _updateSendBtn(sbtn)
-               }
-               a.off().click(function() {
-                       const s = _load();
-                       muted = s.chat.muted = !s.chat.muted;
-                       _updateAudioBtn(a);
-                       Settings.save(s);
-               });
-               sbtn.off().click(function() {
-                       const s = _load();
-                       sendOn = s.chat.sendOn = s.chat.sendOn !== SEND_CTRL ? 
SEND_CTRL : SEND_ENTER;
-                       _updateSendBtn(sbtn);
-                       Settings.save(s);
-               });
-               $('#chat 
#hyperlink').parent().find('button').off().click(function() {
-                       _insertLink();
-               });
-               emoticon.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() {
-                       if (event.keyCode === 13) {
-                               
$(this).parent().find('button').trigger('click');
-                       }
-                       return event.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(emoticon.emoticonize(!!cm.message ? cm.message : ""));
-                               if (btm) {
-                                       _scrollDown(area);
-                               }
-                       }
-                       if (notify) {
-                               ctrlBlk.addClass('bg-warning');
-                               if (p.is(':visible') && !muted) {
-                                       if (window === window.parent) {
-                                               function _newMessage() {
-                                                       
OmUtil.notify(newMsgNotification, 'new_chat_msg');
-                                               }
-                                               if (Notification.permission === 
'granted') {
-                                                       _newMessage();
-                                               } else if 
(Notification.permission !== 'denied') {
-                                                       
Notification.requestPermission().then(permission => {
-                                                               if (permission 
=== 'granted') {
-                                                                       
_newMessage();
-                                                               }
-                                                       });
-                                               }
-                                       } else {
-                                               // impossible to use 
Notification API from iFrame
-                                               audio.play()
-                                                       .then(function() {
-                                                               // Automatic 
playback started!
-                                                       }).catch(function() {
-                                                               // Automatic 
playback failed.
-                                                       });
-                                       }
-                               }
-                       }
-                       emoticon.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);
-                               }
-                       }
-               }
-       }
-       return {
-               reinit: _reinit
-               , removeTab: _removeTab
-               , addTab: _addTab
-               , addMessage: _addMessage
-               , open: _open
-               , setOpened: function() {
-                       _open(function() {
-                               _setOpened();
-                       });
-               }
-               , close: _close
-               , toggle: _toggle
-               , setRoomMode: _setRoomMode
-               , clean: _clean
-               , validate: function() {
-                       return !!editor && editor.text().trim().length > 0;
-               }
-       };
-}();
-$(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 {
-                                                       Chat.addMessage(m);
-                                               }
-                                               break;
-                                       case "typing":
-                                               if (typeof(typingActivity) === 
"function") {
-                                                       typingActivity(m.uid, 
m.active);
-                                               }
-                                               break;
-                               }
-                       }
-               } catch (err) {
-                       //no-op
-               }
-       });
-});
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/raw-cssemoticons.js
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/raw-cssemoticons.js
deleted file mode 100644
index 948b8e7..0000000
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/raw-cssemoticons.js
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * jQuery CSSEmoticons plugin 0.2.9
- *
- * Copyright (c) 2010 Steve Schwartz (JangoSteve)
- *
- * Dual licensed under the MIT and GPL licenses:
- *   http://www.opensource.org/licenses/mit-license.php
- *   http://www.gnu.org/licenses/gpl.html
- *
- * Date: Sun Oct 22 1:00:00 2010 -0500
- */
-var CSSEmoticon = function() {
-       const escapeCharacters = [")", "(", "*", "[", "]", "{", "}", "|", "^", 
"<", ">", "\\", "?", "+", "=", "."];
-       const self = {
-               emoticons: []
-               , matchers: []
-               , defaults: {animate: true, delay: 500, exclude: 
'pre,code,.no-emoticons'}
-               , emoticonize: function(str, options) {
-                       const opts = $.extend({}, this.defaults, options);
-
-                       let cssClass = 'css-emoticon';
-                       if (opts.animate) {
-                               cssClass += ' un-transformed-emoticon 
animated-emoticon';
-                       }
-                       for (let i = 0; i < this.matchers.length; ++i) {
-                               const m = this.matchers[i];
-                               const css = cssClass + " " + m.cssClass;
-                               str = str.replace(m.regexp, "$1<span class='" + 
css + "'>$2</span>");
-                       }
-                       return str;
-               }
-               , animate: function(options) {
-                       const opts = $.extend({}, this.defaults, options);
-                       // animate emoticons
-                       if (opts.animate) {
-                               setTimeout(function () {
-                                       
$('.un-transformed-emoticon').removeClass('un-transformed-emoticon');
-                               }, opts.delay);
-                       }
-               }
-       }
-
-       const specialRegex = new RegExp('(\\' + escapeCharacters.join('|\\') + 
')', 'g');
-       // One of these characters must be present before the matched emoticon, 
or the matched emoticon must be the first character in the container HTML
-       //  This is to ensure that the characters in the middle of HTML 
properties or URLs are not matched as emoticons
-       //  Below matches ^ (first character in container HTML), \s (whitespace 
like space or tab), or \0 (NULL character)
-       // (<\\S+.*>) matches <\\S+.*> (matches an HTML tag like <span> or 
<div>), but haven't quite gotten it working yet, need to push this fix now
-       const preMatch = '(^|[\\s\\0])';
-
-       function createMatcher(m) {
-               const str = m.text.replace(specialRegex, '\\$1');
-               m.regexp = new RegExp(preMatch + '(' + str + ')', 'g');
-               return m;
-       }
-       function addMatchers(arr) {
-               for (let i = 0; i < arr.length; ++i) {
-                       const o = arr[i]
-                       let m = typeof(o) === 'object' ? 
JSON.parse(JSON.stringify(o)) : {text: o, cssClass: ' '};
-                       self.emoticons.push(m.text);
-
-                       self.matchers.push(createMatcher(m));
-                       if (m.text.indexOf('=') > -1) {
-                               m = JSON.parse(JSON.stringify(m));
-                               m.text = m.text.replace(/=/g, 
'&#61;').replace(/[+]/g, '&#43;');
-                               self.matchers.push(createMatcher(m));
-                       }
-                       if (m.text.indexOf('\'') > -1) {
-                               m = JSON.parse(JSON.stringify(m));
-                               m.text = m.text.replace(/'/g, '&#39;');
-                               self.matchers.push(createMatcher(m));
-                       }
-               }
-       }
-       addMatchers([
-               ":-)", ":o)", ":c)", ":^)", ":-D", ":-(", ":-9", ";-)", ":-P", 
":-p", ":-Þ", ":-b", ":-O", ":-/", ":-X", ":-#", ":'(", "B-)", "8-)", ";*(", 
":-*", ":-\\",
-               "?-)" // <== This is my own invention, it's a smiling pirate 
(with an eye-patch)!
-       ]);
-       addMatchers([ // separate these out so that we can add a letter-spacing 
between the characters for better proportions
-               ":)", ":]", "=]", "=)", "8)", ":}", ":D", ":(", ":[", ":{", 
"=(", ";)", ";]", ";D", ":P", ":p", "=P", "=p", ":b", ":Þ", ":O", ":/", "=/", 
":S", ":#", ":X", "B)", ":|", ":\\", "=\\", ":*", ":&gt;", ":&lt;"
-       ]);
-       addMatchers([ // emoticons to be treated with a special class, hash 
specifies the additional class to add, along with standard css-emoticon class
-               {text: "&gt;:)", cssClass: "red-emoticon small-emoticon 
spaced-emoticon"},
-               {text: "&gt;;)", cssClass: "red-emoticon small-emoticon 
spaced-emoticon"},
-               {text: "&gt;:(", cssClass: "red-emoticon small-emoticon 
spaced-emoticon"},
-               {text: "&gt;: )", cssClass: "red-emoticon small-emoticon"},
-               {text: "&gt;; )", cssClass: "red-emoticon small-emoticon"},
-               {text: "&gt;: (", cssClass: "red-emoticon small-emoticon"},
-               {text: ";(", cssClass: "red-emoticon spaced-emoticon"},
-               {text: "&lt;3", cssClass: "pink-emoticon counter-rotated"},
-               {text: "O_O", cssClass: "no-rotate"},
-               {text: "o_o", cssClass: "no-rotate"},
-               {text: "0_o", cssClass: "no-rotate"},
-               {text: "O_o", cssClass: "no-rotate"},
-               {text: "T_T", cssClass: "no-rotate"},
-               {text: "^_^", cssClass: "no-rotate"},
-               {text: "O:)", cssClass: "small-emoticon spaced-emoticon"},
-               {text: "O: )", cssClass: "small-emoticon"},
-               {text: "8D", cssClass: "small-emoticon spaced-emoticon"},
-               {text: "XD", cssClass: "small-emoticon spaced-emoticon"},
-               {text: "xD", cssClass: "small-emoticon spaced-emoticon"},
-               {text: "=D", cssClass: "small-emoticon spaced-emoticon"},
-               {text: "8O", cssClass: "small-emoticon spaced-emoticon"},
-               {text: "[+=..]", cssClass: "no-rotate nintendo-controller"}
-       ]);
-       return self;
-};

Reply via email to