mirror of https://github.com/movim/movim
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.
639 lines
21 KiB
639 lines
21 KiB
var Chat = {
|
|
left : null,
|
|
right: null,
|
|
date: null,
|
|
currentDate: null,
|
|
lastScroll: null,
|
|
lastHeight: null,
|
|
edit: false,
|
|
|
|
// Chat state
|
|
state: null,
|
|
since: null,
|
|
sended: false,
|
|
|
|
// Autocomplete vars.
|
|
// What user want to autocomplete?
|
|
toAutocomplete: null,
|
|
// What was previously in autocomplete?
|
|
previouslyAutocompleted: null,
|
|
previouslyAutocompletedSeqID: null,
|
|
|
|
autocomplete: function(event, jid) {
|
|
event.preventDefault();
|
|
Rooms_ajaxMucUsersAutocomplete(jid);
|
|
},
|
|
onAutocomplete: function(usersList) {
|
|
var textarea = document.querySelector('#chat_textarea');
|
|
var text = textarea.value.toLowerCase();
|
|
|
|
// If user have deleted text from textarea - reinitialize
|
|
// autocompletion.
|
|
if (text == '' && Chat.previouslyAutocompleted !== null) {
|
|
Chat.previouslyAutocompleted = null;
|
|
Chat.previouslyAutocompletedSeqID = null;
|
|
}
|
|
|
|
// Assume that this is what we want to autocomplete if
|
|
// Chat.toAutocomplete is null.
|
|
if (Chat.toAutocomplete === null
|
|
|| (
|
|
Chat.toAutocomplete != text
|
|
&& text.indexOf(',') === -1)
|
|
) {
|
|
Chat.toAutocomplete = text;
|
|
}
|
|
|
|
// If it is a first autocomplete attempt and there was no
|
|
// substring to search found in input field - just add
|
|
// first element from users list to input field.
|
|
if (Chat.previouslyAutocompleted === null
|
|
&& Chat.toAutocomplete == '') {
|
|
var autocompleted = usersList[0]['resource'];
|
|
textarea.value = autocompleted + ', ';
|
|
Chat.previouslyAutocompleted = autocompleted;
|
|
} else {
|
|
// Otherwise we should autocomplete next to
|
|
// previouslyAutocompleted.
|
|
var autocompletedOk = false;
|
|
for (var i = 0; i < usersList.length; i++) {
|
|
var autocompleted = '';
|
|
// If we want to just-scroll thru all people in MUC.
|
|
if (usersList[i]['resource'] == Chat.previouslyAutocompleted
|
|
&& i !== usersList.length - 1
|
|
&& Chat.toAutocomplete == '') {
|
|
autocompleted = usersList[i+1]['resource'];
|
|
textarea.value = autocompleted + ', ';
|
|
Chat.previouslyAutocompleted = autocompleted;
|
|
Chat.previouslyAutocompletedSeqID = i;
|
|
autocompletedOk = true;
|
|
break;
|
|
} else {
|
|
// If we have substring to autocomplete.
|
|
var user_substr = usersList[i]['resource'].substring(0,
|
|
Chat.toAutocomplete.length)
|
|
if (i > Chat.previouslyAutocompletedSeqID
|
|
&& user_substr.toLowerCase().indexOf(Chat.toAutocomplete) !== -1
|
|
&& usersList[i]['resource'] != Chat.previouslyAutocompleted) {
|
|
autocompleted = usersList[i]['resource'];
|
|
textarea.value = autocompleted + ', ';
|
|
Chat.previouslyAutocompleted = autocompleted;
|
|
Chat.previouslyAutocompletedSeqID = i;
|
|
autocompletedOk = true;
|
|
break;
|
|
}
|
|
}
|
|
if (autocompletedOk) {
|
|
break;
|
|
}
|
|
}
|
|
// If autocompletion failed - emptify input field.
|
|
if (!autocompletedOk) {
|
|
textarea.value = '';
|
|
Chat.previouslyAutocompleted = null;
|
|
Chat.previouslyAutocompletedSeqID = null;
|
|
}
|
|
}
|
|
},
|
|
sendMessage: function()
|
|
{
|
|
var textarea = Chat.getTextarea();
|
|
|
|
var text = textarea.value;
|
|
var muc = Boolean(textarea.dataset.muc);
|
|
var jid = textarea.dataset.jid;
|
|
|
|
textarea.focus();
|
|
|
|
if(!Chat.sended) {
|
|
Chat.sended = true;
|
|
|
|
if(Chat.edit) {
|
|
Chat.edit = false;
|
|
Chat_ajaxCorrect(jid, text);
|
|
} else {
|
|
Chat_ajaxSendMessage(jid, text, muc);
|
|
}
|
|
}
|
|
|
|
// Emptify autocomplete data on message sending.
|
|
if (Chat.previouslyAutocompleted !== null) {
|
|
Chat.previouslyAutocompleted = null;
|
|
Chat.previouslyAutocompletedSeqID = null;
|
|
Chat.toAutocomplete = null;
|
|
}
|
|
},
|
|
sendedMessage: function()
|
|
{
|
|
Chat.sended = false;
|
|
Chat.clearReplace();
|
|
var textarea = Chat.getTextarea();
|
|
localStorage.removeItem(textarea.dataset.jid + '_message');
|
|
},
|
|
clearReplace: function()
|
|
{
|
|
Chat.edit = false;
|
|
var textarea = document.querySelector('#chat_textarea');
|
|
textarea.value = '';
|
|
MovimUtils.textareaAutoheight(textarea);
|
|
},
|
|
focus: function()
|
|
{
|
|
Chat.sended = false;
|
|
|
|
var textarea = Chat.getTextarea();
|
|
|
|
setTimeout(function() {
|
|
var textarea = Chat.getTextarea();
|
|
textarea.value = localStorage.getItem(textarea.dataset.jid + '_message');
|
|
|
|
MovimUtils.textareaAutoheight(textarea);
|
|
}, 0); // Fix Me
|
|
|
|
textarea.onkeydown = function(event) {
|
|
if (this.dataset.muc && event.keyCode == 9) {
|
|
Chat.autocomplete(event, this.dataset.jid);
|
|
return;
|
|
}
|
|
|
|
if(event.keyCode == 38 && this.value == '') {
|
|
Chat_ajaxLast(this.dataset.jid);
|
|
} else if(event.keyCode == 40
|
|
&& (this.value == '' || Chat.edit == true)) {
|
|
Chat.clearReplace();
|
|
}
|
|
};
|
|
|
|
textarea.onkeypress = function(event) {
|
|
if(event.keyCode == 13) {
|
|
if(event.shiftKey) {
|
|
return;
|
|
}
|
|
Chat.state = 0;
|
|
Chat.sendMessage();
|
|
|
|
return false;
|
|
} else if(!Boolean(this.dataset.muc)) {
|
|
if(Chat.state == 0 || Chat.state == 2) {
|
|
Chat.state = 1;
|
|
Chat_ajaxSendComposing(this.dataset.jid);
|
|
Chat.since = new Date().getTime();
|
|
}
|
|
}
|
|
};
|
|
|
|
textarea.onkeyup = function(event) {
|
|
localStorage.setItem(this.dataset.jid + '_message', this.value);
|
|
|
|
setTimeout(function()
|
|
{
|
|
var textarea = document.querySelector('#chat_textarea');
|
|
|
|
if(textarea
|
|
&& !Boolean(textarea.dataset.muc)
|
|
&& Chat.state == 1
|
|
&& Chat.since + 5000 < new Date().getTime()) {
|
|
Chat.state = 2;
|
|
Chat_ajaxSendPaused(textarea.dataset.jid);
|
|
}
|
|
},5000);
|
|
|
|
Chat.toggleAction(this.value.length);
|
|
};
|
|
|
|
textarea.oninput = function() {
|
|
MovimUtils.textareaAutoheight(this);
|
|
};
|
|
|
|
if(document.documentElement.clientWidth > 1024) {
|
|
document.querySelector('#chat_textarea').focus();
|
|
}
|
|
},
|
|
setTextarea: function(value)
|
|
{
|
|
Chat.edit = true;
|
|
var textarea = document.querySelector('#chat_textarea');
|
|
textarea.value = value;
|
|
MovimUtils.textareaAutoheight(textarea);
|
|
|
|
},
|
|
notify : function(title, body, image)
|
|
{
|
|
if(document_focus == false) {
|
|
movim_title_inc();
|
|
movim_desktop_notification(title, body, image);
|
|
}
|
|
},
|
|
empty : function()
|
|
{
|
|
Chat_ajaxGet();
|
|
},
|
|
setBubbles : function(left, right, date) {
|
|
var div = document.createElement('div');
|
|
|
|
Chat.currentDate = null;
|
|
|
|
div.innerHTML = left;
|
|
Chat.left = div.firstChild.cloneNode(true);
|
|
div.innerHTML = right;
|
|
Chat.right = div.firstChild.cloneNode(true);
|
|
div.innerHTML = date;
|
|
Chat.date = div.firstChild.cloneNode(true);
|
|
|
|
Chat.setScrollBehaviour();
|
|
},
|
|
setScrollBehaviour : function() {
|
|
var discussion = document.querySelector('#chat_widget div.contained');
|
|
discussion.onscroll = function() {
|
|
if(this.scrollTop < 1
|
|
&& discussion.querySelectorAll('ul li p').length >= Chat.pagination) {
|
|
Chat_ajaxGetHistory(Chat.getTextarea().dataset.jid, Chat.currentDate);
|
|
}
|
|
|
|
Chat.lastHeight = this.clientHeight;
|
|
};
|
|
},
|
|
checkDiscussion : function(page) {
|
|
for (var firstKey in page) break;
|
|
if(page[firstKey] == null) return false;
|
|
|
|
for (var firstMessageKey in page[firstKey]) break;
|
|
var firstMessage = page[firstKey][firstMessageKey];
|
|
if(firstMessage == null) return false;
|
|
|
|
var contactJid = firstMessage.session == firstMessage.jidfrom
|
|
? firstMessage.jidto
|
|
: firstMessage.jidfrom;
|
|
|
|
if(document.getElementById(MovimUtils.cleanupId(contactJid + '-discussion'))
|
|
== null) return false;
|
|
|
|
return true;
|
|
},
|
|
appendMessagesWrapper : function(page, prepend) {
|
|
if(page && Chat.checkDiscussion(page)) {
|
|
var scrolled = MovimTpl.isPanelScrolled();
|
|
|
|
var discussion = document.querySelector('#chat_widget div.contained');
|
|
|
|
if(discussion == null) return;
|
|
|
|
Chat.lastScroll = discussion.scrollHeight;
|
|
|
|
for(date in page) {
|
|
if(prepend === undefined) {
|
|
Chat.appendDate(date, prepend);
|
|
}
|
|
|
|
for(speakertime in page[date]) {
|
|
if(!Chat.currentDate) {
|
|
Chat.currentDate = page[date][speakertime].published;
|
|
}
|
|
|
|
Chat.appendMessage(speakertime, page[date][speakertime], prepend);
|
|
}
|
|
|
|
if(prepend && date) {
|
|
Chat.appendDate(date, prepend);
|
|
}
|
|
}
|
|
|
|
// Only scroll down if scroll was at the bottom before the new msg
|
|
// => don't scroll if the user was reading previous messages
|
|
if(scrolled && prepend !== true) {
|
|
setTimeout(function() {
|
|
MovimTpl.scrollPanel();
|
|
}, 20);
|
|
}
|
|
|
|
if(prepend) {
|
|
// And we scroll where we were
|
|
var scrollDiff = discussion.scrollHeight - Chat.lastScroll;
|
|
discussion.scrollTop += scrollDiff;
|
|
Chat.lastScroll = discussion.scrollHeight;
|
|
}
|
|
|
|
var chat = document.querySelector('#chat_widget');
|
|
var lastMessage = chat.querySelector('ul li:not(.oppose):last-child div.bubble > div:last-child');
|
|
var textarea = Chat.getTextarea();
|
|
|
|
if(textarea && lastMessage) {
|
|
Chat_ajaxDisplayed(
|
|
textarea.dataset.jid,
|
|
lastMessage.id
|
|
);
|
|
}
|
|
}
|
|
},
|
|
appendMessage : function(idjidtime, data, prepend) {
|
|
if(data.body == null) return;
|
|
|
|
var bubble = null,
|
|
mergeMsg = false,
|
|
msgStack,
|
|
refBubble;
|
|
|
|
var isMuc = (document.querySelector('#chat_widget div.contained').dataset.muc == 1);
|
|
var jidtime = idjidtime.substring(idjidtime.indexOf('<') + 1);
|
|
|
|
if(prepend) {
|
|
refBubble = document.querySelector("#chat_widget .contained li:first-child");
|
|
msgStack = document.querySelector("[data-bubble='" + jidtime + "']");
|
|
} else {
|
|
refBubble = document.querySelector("#chat_widget .contained li:last-child");
|
|
var stack = document.querySelectorAll("[data-bubble='" + jidtime + "']");
|
|
msgStack = stack[stack.length-1];
|
|
}
|
|
|
|
if(msgStack != null
|
|
&& msgStack.parentNode == refBubble
|
|
&& data.file === null
|
|
&& data.sticker === null
|
|
&& !MovimUtils.hasClass(refBubble.querySelector('div.bubble'), "sticker")
|
|
&& !MovimUtils.hasClass(refBubble.querySelector('div.bubble'), "file")
|
|
){
|
|
bubble = msgStack.parentNode;
|
|
mergeMsg = true;
|
|
} else {
|
|
if (data.session == data.jidfrom) {
|
|
bubble = Chat.right.cloneNode(true);
|
|
id = data.jidto + '_conversation';
|
|
} else {
|
|
bubble = Chat.left.cloneNode(true);
|
|
id = data.jidfrom + '_conversation';
|
|
}
|
|
|
|
id = MovimUtils.cleanupId(id);
|
|
|
|
bubble.querySelector('div.bubble').dataset.bubble = jidtime;
|
|
bubble.querySelector('div.bubble').dataset.publishedprepared = data.publishedPrepared;
|
|
}
|
|
|
|
var msg = bubble.querySelector('div.bubble > div');
|
|
var span = msg.getElementsByTagName('span')[0];
|
|
var p = msg.getElementsByTagName('p')[0];
|
|
|
|
// If there is already a msg in this bubble, create another div (next msg or replacement)
|
|
if (bubble.querySelector('div.bubble p')
|
|
&& bubble.querySelector('div.bubble p').innerHTML != '') {
|
|
msg = document.createElement("div");
|
|
p = document.createElement("p");
|
|
span = document.createElement("span");
|
|
span.className = 'info';
|
|
}
|
|
|
|
if (data.rtl) {
|
|
bubble.querySelector('div.bubble').setAttribute('dir', 'rtl');
|
|
}
|
|
|
|
if (data.body.match(/^\/me\s/)) {
|
|
p.classList.add('quote');
|
|
data.body = data.body.substr(4);
|
|
}
|
|
|
|
if (data.body.match(/^\/code/)) {
|
|
p.classList.add('code');
|
|
data.body = data.body.substr(6).trim();
|
|
}
|
|
|
|
if (data.id != null) {
|
|
msg.setAttribute("id", data.id);
|
|
if (data.newid != null) {
|
|
msg.setAttribute("id", data.newid);
|
|
}
|
|
}
|
|
|
|
if (data.sticker != null) {
|
|
MovimUtils.addClass(bubble.querySelector('div.bubble'), 'sticker');
|
|
p.appendChild(Chat.getStickerHtml(data.sticker));
|
|
} else {
|
|
p.innerHTML = data.body;
|
|
}
|
|
|
|
if (data.audio != null) {
|
|
MovimUtils.addClass(bubble.querySelector('div.bubble'), 'file');
|
|
p.appendChild(Chat.getAudioHtml(data.file));
|
|
} else if (data.file != null) {
|
|
MovimUtils.addClass(bubble.querySelector('div.bubble'), 'file');
|
|
p.appendChild(Chat.getFileHtml(data.file, data.sticker));
|
|
}
|
|
|
|
if (data.edited) {
|
|
span.appendChild(Chat.getEditedIcoHtml());
|
|
}
|
|
|
|
if (data.session == data.jidfrom) {
|
|
if (data.displayed) {
|
|
span.appendChild(Chat.getDisplayedIcoHtml(data.displayed));
|
|
} else if (data.delivered) {
|
|
span.appendChild(Chat.getDeliveredIcoHtml(data.delivered));
|
|
}
|
|
}
|
|
|
|
msg.appendChild(p);
|
|
msg.appendChild(span);
|
|
|
|
var elem = document.getElementById(data.id);
|
|
if (elem) {
|
|
elem.parentElement.replaceChild(msg, elem);
|
|
mergeMsg = true;
|
|
} else {
|
|
if(prepend) {
|
|
bubble.querySelector('div.bubble').insertBefore(msg, bubble.querySelector('div.bubble').firstChild);
|
|
} else {
|
|
bubble.querySelector('div.bubble').appendChild(msg);
|
|
}
|
|
}
|
|
|
|
/* MUC specific */
|
|
if(isMuc) {
|
|
bubble.querySelector('div.bubble').dataset.publishedprepared = data.resource + ' – ' + data.publishedPrepared;
|
|
|
|
icon = bubble.querySelector('span.primary.icon');
|
|
|
|
if(icon.querySelector('img') == undefined) {
|
|
if(data.icon_url) {
|
|
var img = document.createElement("img");
|
|
img.setAttribute("src", data.icon_url);
|
|
|
|
icon.appendChild(img);
|
|
} else {
|
|
icon.classList.add('color');
|
|
icon.classList.add(data.color);
|
|
icon.innerHTML = data.icon;
|
|
}
|
|
|
|
icon.dataset.resource = data.resource;
|
|
}
|
|
|
|
/*icon.onclick = function(n) {
|
|
var textarea = document.querySelector('#chat_textarea');
|
|
textarea.value = this.dataset.resource + ', ' + textarea.value;
|
|
textarea.focus();
|
|
};*/
|
|
}
|
|
|
|
if(prepend){
|
|
Chat.currentDate = data.published;
|
|
|
|
// We prepend
|
|
if (!mergeMsg) {
|
|
MovimTpl.prepend("#" + id, bubble.outerHTML);
|
|
}
|
|
} else {
|
|
if (!mergeMsg) {
|
|
MovimTpl.append("#" + id, bubble.outerHTML);
|
|
}
|
|
}
|
|
},
|
|
appendDate: function(date, prepend) {
|
|
var list = document.querySelector('#chat_widget > div ul');
|
|
dateNode = Chat.date.cloneNode(true);
|
|
dateNode.dataset.value = date;
|
|
dateNode.querySelector('p').innerHTML = date;
|
|
|
|
var dates = list.querySelectorAll('li.date');
|
|
|
|
if(prepend) {
|
|
// If the date was already displayed we remove it
|
|
if(dates.length > 0
|
|
&& dates[0].dataset.value == date) {
|
|
dates[0].parentNode.removeChild(dates[0]);
|
|
}
|
|
|
|
list.insertBefore(dateNode, list.firstChild);
|
|
} else {
|
|
if(dates.length > 0
|
|
&& dates[dates.length-1].dataset.value == date) {
|
|
return;
|
|
}
|
|
|
|
list.appendChild(dateNode);
|
|
}
|
|
},
|
|
getStickerHtml: function(sticker) {
|
|
var img = document.createElement("img");
|
|
if(sticker.url) {
|
|
img.setAttribute("src", sticker.url);
|
|
if(sticker.width) img.setAttribute("width", sticker.width);
|
|
if(sticker.height)
|
|
img.setAttribute("height", sticker.height);
|
|
else {
|
|
img.setAttribute("height", "170");
|
|
}
|
|
}
|
|
|
|
if(sticker.picture) {
|
|
var a = document.createElement("a");
|
|
a.setAttribute("href", sticker.url);
|
|
a.setAttribute("target", "_blank");
|
|
a.appendChild(img);
|
|
return a;
|
|
} else {
|
|
return img;
|
|
}
|
|
},
|
|
getAudioHtml: function(file) {
|
|
var audio = document.createElement("audio");
|
|
audio.setAttribute("controls", true);
|
|
|
|
var source = document.createElement("source");
|
|
source.setAttribute("src", file.uri);
|
|
source.setAttribute("type", file.type);
|
|
|
|
audio.appendChild(source);
|
|
|
|
return audio;
|
|
},
|
|
getFileHtml: function(file, sticker) {
|
|
var div = document.createElement("div");
|
|
div.setAttribute("class", "file");
|
|
|
|
var a = document.createElement("a");
|
|
if(sticker == null) {
|
|
a.innerHTML = file.name;
|
|
}
|
|
a.setAttribute("href", file.uri);
|
|
a.setAttribute("target", "_blank");
|
|
|
|
div.appendChild(a);
|
|
|
|
var span = document.createElement("span");
|
|
span.innerHTML = file.size;
|
|
span.setAttribute("class", "size");
|
|
|
|
a.appendChild(span);
|
|
|
|
return div;
|
|
},
|
|
getEditedIcoHtml: function() {
|
|
var i = document.createElement("i");
|
|
i.setAttribute("class", "zmdi zmdi-edit");
|
|
return i;
|
|
},
|
|
getDeliveredIcoHtml: function(delivered) {
|
|
var i = document.createElement("i");
|
|
i.setAttribute("class", "zmdi zmdi-check");
|
|
i.setAttribute("title", delivered);
|
|
return i;
|
|
},
|
|
getDisplayedIcoHtml: function(displayed) {
|
|
var i = document.createElement("i");
|
|
i.setAttribute("class", "zmdi zmdi-check-all");
|
|
i.setAttribute("title", displayed);
|
|
return i;
|
|
},
|
|
toggleAction: function(l) {
|
|
var send_button = document.querySelector(".chat_box span[data-jid]");
|
|
var attachment_button = document.querySelector(".chat_box span.control:not([data-jid])");
|
|
if(send_button && attachment_button) {
|
|
if(l > 0){
|
|
MovimUtils.showElement(send_button);
|
|
MovimUtils.hideElement(attachment_button);
|
|
} else {
|
|
MovimUtils.showElement(attachment_button);
|
|
MovimUtils.hideElement(send_button);
|
|
}
|
|
}
|
|
},
|
|
getTextarea: function() {
|
|
var textarea = document.querySelector('#chat_textarea');
|
|
if(textarea) return textarea;
|
|
}
|
|
};
|
|
|
|
MovimWebsocket.attach(function() {
|
|
var jid = MovimUtils.urlParts().params[0];
|
|
var room = MovimUtils.urlParts().params[1];
|
|
if(jid) {
|
|
MovimTpl.showPanel();
|
|
|
|
if(room) {
|
|
Chat_ajaxGetRoom(jid);
|
|
} else {
|
|
Chat_ajaxGet(jid);
|
|
Notification.current('chat|' + jid);
|
|
}
|
|
}
|
|
});
|
|
|
|
if(typeof Upload != 'undefined') {
|
|
Upload.attach(function(file) {
|
|
Chat_ajaxSendMessage(Chat.getTextarea().dataset.jid, false, Boolean(Chat.getTextarea().dataset.muc), false, false, file);
|
|
});
|
|
}
|
|
|
|
document.addEventListener('focus', function() {
|
|
var textarea = Chat.getTextarea();
|
|
if(textarea) textarea.focus();
|
|
});
|
|
|
|
window.addEventListener('resize', function() {
|
|
var discussion = document.querySelector('#chat_widget div.contained');
|
|
if(discussion) {
|
|
discussion.scrollTop += Chat.lastHeight - discussion.clientHeight;
|
|
Chat.lastHeight = discussion.clientHeight;
|
|
}
|
|
});
|
|
|
|
var state = 0;
|