🇬🇧United Kingdom rblake87
/**
* @file
* Adds JS functionality to the Private Message Windows module.
*/
/*global jQuery, Drupal, drupalSettings, window*/
(function ($, Drupal, drupalSettings, window) {
Drupal.PrivateMessageInbox = Drupal.PrivateMessageInbox || {};
// Drupal.PrivateMessageInbox.updateInbox = Drupal.PrivateMessageInbox.updateInbox || {};
"use strict";
var pmWindows, updateInterval, inboxRequesting, blockSettings;
var requesting = { initedNeed: 1, thread: {}, threadRefresh: {}, msgSend: {}, newThread: 0, prevMsgsLoading: {}, userSearch: 0, threadDelete: {} };
var winWidth = 260;
var langPrefix = '';
/**
* Used to manually trigger Drupal's JavaScript commands.
* @param {Object} data The data.
*/
function triggerCommands(data) {
var ajaxObject = Drupal.ajax({
url: '',
base: false,
element: false,
progress: false
});
// Trigger any ajax commands in the response.
ajaxObject.success(data, 'success');
}
/**
* Initializes the private message inbox window.
*/
function init(context) {
if(!requesting.initedNeed) return;
var block= $('.block-private-message-windows-block');
var elements = once('overlay-block-ajax', '.block-private-message-windows-block');
if(!block.length) { // block is still absent in the site DOM
requesting.initedNeed++;
setTimeout(function(){ init(context); }, 300);
return;
}
requesting.initedNeed = 0;
elements.forEach(function (element) {
var displayWidth = $(document).width();
if(displayWidth <= 600) {
winWidth = Math.round(displayWidth*0.49);
}else if(displayWidth <= 768) {
winWidth = Math.round(displayWidth*0.32);
}
if(!drupalSettings.privateMessageWindowsBlock) return;
blockSettings = drupalSettings.privateMessageWindowsBlock;
langPrefix = blockSettings.currentLanguagePrefix;
pmWindows = $(element).first().find('.block-content:first');
var mainTitle = $(block).find('.main-title');
mainTitle.find('.x').on('click touchstart', function () { pmWindows.hide(); return false; });
mainTitle.find('.add-new').on('click touchstart', function () { $(block).find('.new-user-search').toggle(); return false; });
mainTitle.after('<div class="new-user-search"><input placeholder="Type user name" /><div class="user-list"></div></div>');
pmWindows.append('<style>' +
'@-webkit-keyframes pmsgblink {' +
'50% { color: ' + blockSettings.titleTextColor +
'; } 51% { color: ' + blockSettings.titleNewMsgTextColor +
'; } 100% { color: ' + blockSettings.titleNewMsgTextColor + '; }' +
'} @keyframes pmsgblink {' +
'50% { color: ' + blockSettings.titleTextColor +
'; } 51% { color: ' + blockSettings.titleNewMsgTextColor +
'; } 100% { color: ' + blockSettings.titleNewMsgTextColor + '; }' +
'}' +
'.block-private-message-windows-block .label { color: ' +
blockSettings.titleTextColor +
'; background-color: '+
blockSettings.titleTextBackground +
'; } .block-private-message-windows-block .block-content.new .label.main-title {' +
' color: ' + blockSettings.titleNewMsgTextColor + ';' +
' -webkit-animation: pmsgblink 2s linear infinite;' +
' animation: blink 2s pmsgblink infinite;' +
'} </style>');
if(newThreadCount()>0) {
pmWindows.addClass('new');
}else if(blockSettings.defaultHidden > 0) pmWindows.hide();
updateInterval = blockSettings.ajaxRefreshRate * 1000;
if (updateInterval) {
window.setTimeout(loadUpdateInbox, updateInterval);
}
pmWindows.find('.inbox-thread-list .thread').on('click touchstart', inboxThreadLinkListenerHandler);
pmWindows.find('.label').on('click touchstart', showHideWin);
$('a[href*="/private-message/create?recipient="]').on('click touchstart', newRecipientMessage);
$('a[href^="'+langPrefix+'/private-messages/"]').on('click touchstart', existsRecipientThread);
$('a[href="'+langPrefix+'/private-messages"]').on('click touchstart', showMainWindow);
pmWindows.find('.new-user-search input').on('input', getUserList4NewThread);
});
}
/**
* Show main window by click
*/
function showMainWindow(e) {
e.preventDefault();
pmWindows.addClass('active');
pmWindows.show();
}
/**
* Show and hide inbox or thread window
*/
function showHideWin() {
$(this).parent().toggleClass('active');
}
/**
* Request to delete the thread
*/
function runDeleteThread() {
var threadDiv = $(this).closest('.thread');
var messagesDiv = threadDiv.find('.messages');
if(messagesDiv.find('.request').length) return;
messagesDiv.after('<div class="request"><div class="question">' + blockSettings['deleteThreadQuestion'] + '</div><div><div class="butt ok">OK</div><div class="butt cancel">Cancel</div></div><div class="note">' + blockSettings['deleteThreadNote'] + '</div></div>');
threadDiv.find('.request .butt.ok').on('click touchstart', function () {
execDeleteThread(threadDiv.attr('data-thread-id'));
return false;
});
threadDiv.find('.request .butt.cancel').on('click touchstart', function () {
$(this).closest('.request').empty().remove();
return false;
});
}
/**
* Execute thread delete command
*
* @param threadId
* @returns {boolean}
*/
function execDeleteThread(threadId) {
if(requesting.threadDelete[threadId]) return;
requesting.threadDelete[threadId] = 1;
$(this).addClass('exec');
$.ajax({
url: blockSettings.deleteThreadUrl,
method: "POST",
data: { thread_id: threadId },
success: function (data) {
triggerCommands(data);
requesting.threadDelete[threadId] = 0;
}
});
return false;
}
/**
* Run update the inbox.
*/
function loadUpdateInbox() {
if (!inboxRequesting) {
inboxRequesting = true;
var ids = {};
pmWindows.find('.inbox-thread-list .thread').each(function () {
ids[$(this).attr('data-thread-id')] = $(this).attr('data-last-update');
});
$.ajax({
url: blockSettings.loadNewThreadUrl,
method: "POST",
data: { ids: ids },
success: function (data) {
inboxRequesting = false;
triggerCommands(data);
if (updateInterval) {
window.setTimeout(loadUpdateInbox, updateInterval);
}
}
});
}
}
/**
* Refresh inbox after ajax call
* @param threadIds - ids of threads
* @param newThreads - updated threads
*/
function updateInbox(threadIds, newThreads) {
var revertedIndex = [];
for(var index in threadIds) {
revertedIndex.unshift(index);
}
var inboxWrapper = pmWindows.find('.inbox-thread-list');
if(threadIds.length) inboxWrapper.find('.no-thread').remove();
for(var i in revertedIndex) {
var index = revertedIndex[i];
var threadId = threadIds[index];
var message = inboxWrapper.find('.thread-'+threadId);
if(message.length) {
message.remove().empty();
loadThreadRefresh(threadId);
}
inboxWrapper.prepend(newThreads[threadId]);
var newThread = inboxWrapper.find('.thread-'+threadId);
if(!newThread.hasClass('own') && newThread.hasClass('new')) {
pmWindows.addClass('new').show();
newThreadCount(); //update count number
}
inboxWrapper.find('.thread-'+threadId).on('click touchstart', inboxThreadLinkListenerHandler);
};
}
/**
* Click Handler executed when private message threads are clicked.
*
* Loads the thread into the private message window.
*/
var inboxThreadLinkListenerHandler = function (e) {
e.preventDefault();
var threadId = $(this).attr("data-thread-id");
var inboxThread = pmWindows.find('.inbox-thread-list .thread-'+threadId);
var thWrapper = pmWindows.find('.threads-wrapper');
var thread = thWrapper.find('.thread-'+threadId);
if (thread.length) {
thread.addClass('active');
if(inboxThread.hasClass('new')) {
inboxThreadDeNew(inboxThread);
thread.find('textarea').focus();
}else {
if(thread.is(':hidden')) {
hideLastWin(thWrapper);
}
thread.toggle();
}
} else {
inboxThreadDeNew(inboxThread);
loadThread(threadId);
}
};
var newThreadCount = function () {
var newThreadCnt = pmWindows.find('.inbox-thread-list .thread.new').length;
pmWindows.find('.unreaded-cnt').empty().html(newThreadCnt);
return newThreadCnt;
};
var inboxThreadDeNew = function(inboxThread) {
inboxThread.removeClass('new');
if(!newThreadCount()) {
pmWindows.removeClass('new');
}
};
/**
* Loading upper(older) messages to the thread message list
*/
function loadPrevMessages() {
var loadPrevDiv = $(this);
loadPrevDiv.addClass('loading');
var threadDiv = loadPrevDiv.parent();
var threadId = threadDiv.attr('data-thread-id');
var firstMsgId = threadDiv.attr('data-first-msg-id');
requesting.prevMsgsLoading[threadId] = true;
$.ajax({
url: blockSettings.loadPrevMsgsUrl,
data: { thread_id:threadId, message_id:firstMsgId },
success: function (data) {
loadPrevDiv.removeClass('loading');
requesting.prevMsgsLoading[threadId] = false;
triggerCommands(data);
}
});
}
/**
* Downloading thread content
* @param threadId
*/
function loadThread(threadId) {
var threadDiv = pmWindows.find('.thread-wrapper .thread-'+threadId);
if(threadDiv.length) return;
if(requesting.thread[threadId]) return;
requesting.thread[threadId] = true;
var inboxThreadDiv = pmWindows.find('.inbox-thread-list .thread-'+threadId);
inboxThreadDiv.addClass('opening');
$.ajax({
url: blockSettings.loadThreadMsgsUrl,
data: { threadid:threadId },
success: function (data) {
inboxThreadDiv.removeClass('opening');
requesting.thread[threadId] = false;
triggerCommands(data);
}
});
}
function hideLastWin(thWrapper) {
var thWrapperW = thWrapper.width();
var winLimit = Math.floor(thWrapperW/winWidth);
var existsThreads = thWrapper.find('.thread:visible');
if(existsThreads.length >= winLimit) {
existsThreads.each(function(i,item) {
if((i-(-1)) >= winLimit) $(item).hide();
});
}
}
/**
* Filling thread after ajax request
* @param threadId - thread id
* @param firstMsgId - begin message id
* @param lastMsgId - end message id
* @param userId - user id
* @param userName - name of user
* @param userPicture - image url of user icon
* @param messages - html of thread messages
* @param loadPrevTitle - "load previous" text in the right language
*/
function loadThreadResponse(threadId, firstMsgId, lastMsgId, userId, userName, userPicture, messages, loadPrevTitle) {
var thWrapper = pmWindows.find('.threads-wrapper');
hideLastWin(thWrapper);
var userPictureTag = userPicture ? '<img src="' + userPicture + '" />' : '';
var deleteThreadMsg = blockSettings['deleteThreadMsg'];
thWrapper.prepend('<div class="thread thread-' + threadId + ' thread-user-' + userId + ' active" data-thread-id="' +
threadId + '" data-user-id="' + userId + '" data-last-msg-id="'+lastMsgId+'" data-first-msg-id="'+firstMsgId+'">' +
'<div class="label"><div class="x">×</div>' + userPictureTag + userName + '</div>' +
'<div class="load-prev">' + loadPrevTitle + '</div>' +
'<div class="messages"></div><div class="operations"><div class="delete-thread">' + deleteThreadMsg + '</div><textarea></textarea><input type="button" value="Send" /></div></div>');
var threadDiv = thWrapper.find('.thread-'+threadId);
threadDiv.find('.load-prev').on('click touchstart', loadPrevMessages);
var threadMsgDiv = threadDiv.find('.messages');
threadMsgDiv.prepend(messages);
threadDiv.find('input[type="button"]').on('click touchstart', sendMessage);
threadDiv.find('.label').on('click touchstart', showHideWin);
threadDiv.find('.label .x').on('click touchstart', function () { $(this).closest('.thread').hide(); return false; });
threadDiv.find('.delete-thread').on('click touchstart', runDeleteThread);
messagesScrollDown(threadDiv);
}
/**
* Get user list for new thread creation
*/
function getUserList4NewThread() {
if(requesting.userSearch) requesting.userSearch.abort();
var searchLine = $(this).val().trim();
if((!searchLine) || (searchLine.length < 3)) return;
requesting.userSearch = $.ajax({
url: blockSettings.getUsersUrl,
data: { user_name:searchLine },
method: 'POST',
success: function (data) {
requesting.userSearch = false;
triggerCommands(data);
}
});
}
/**
* Show user list for start new thread
*
* @param userList
*/
function showSearchedUserList(userList) {
var userListTag = pmWindows.find('.new-user-search .user-list');
userListTag.empty();
for(var i in userList) {
var user = userList[i];
userListTag.append('<div class="user-for-new-thread" data-uid="' + user.uid + '">' + user.username + '</div>');
}
userListTag.find('.user-for-new-thread').on('click', function () {
var uid = parseInt($(this).attr('data-uid'));
userListTag.empty();
pmWindows.find('.new-user-search input').val('');
if(uid) openRecipientMessage(uid);
return false;
})
}
/**
* Send message to user
*/
function sendMessage() {
var $this = $(this);
var operations = $this.closest('.operations');
operations.addClass('sending');
var textArea = operations.find('textarea');
var text = textArea.val();
var thread = $this.closest('.thread');
var threadId = thread.attr('data-thread-id');
var msgId = thread.attr('data-last-msg-id');
if(requesting.msgSend[threadId]) return;
requesting.msgSend[threadId] = true;
$.ajax({
url: blockSettings.sendMsgUrl,
data: { thread_id:threadId, text:text, last_message_id:msgId },
method: 'POST',
success: function (data) {
textArea.val('');
requesting.msgSend[threadId] = false;
operations.removeClass('sending');
triggerCommands(data);
}
});
}
/**
* Respone to the ajax request of previous thread messages
* @param oldMessages
* @param threadId
* @param firstMessageId
* @param hasNext
*/
function insertOldThreadMsgs(oldMessages, threadId, firstMessageId, hasNext) {
var thread = pmWindows.find('.threads-wrapper .thread-'+threadId);
var threadMessages = thread.find('.messages');
//const elements = once('private-message-'+firstMessageId, 'private-message-'+firstMessageId);
var messages = $('<div>' + oldMessages + '</div').find('.private-message');
if(!messages.length) { thread.find('.load-prev').hide(); return; }
var firstMsgId = thread.attr('data-first-msg-id');
var messages4Insert = {};
var newFirstMsgId = firstMsgId;
//to avoid duplicate of messages, we need existing check
messages.each(function(i, item) {
var msg = $(item);
var msgId = msg.attr('data-message-id');
if(!threadMessages.find('#private-message-' + msgId).length && (firstMsgId > msgId)) {
messages4Insert[msgId] = item;
if(newFirstMsgId> msgId) newFirstMsgId = msgId;
}
});
var ids = Object.keys(messages4Insert);
ids.sort(function(a, b){ return b - a; });//inserting backward
for(var i in ids) {
threadMessages.prepend(messages4Insert[ids[i]]);
}
thread.attr('data-first-msg-id', newFirstMsgId);
}
/**
* Response to the ajax request of message sending or thread update
*/
function updateThreadMsgs(newMessages, threadId, lastMsgId) {
var thread = pmWindows.find('.threads-wrapper .thread-'+threadId);
var threadMessages = thread.find('.messages');
const elements = once('private-message-'+lastMsgId, '.private-message');
var messages = $('<div>' + newMessages + '</div').find('.private-message');
var oldMsgId = thread.attr('data-last-msg-id');
//to avoid duplicate of messages, we add each message with existing check
messages.each(function(i, item) {
var msg = $(item);
var msgId = msg.attr('data-message-id');
if(!threadMessages.find('#private-message-' + msgId).length && (oldMsgId < msgId)) {
threadMessages.append(item);
thread.attr('data-last-msg-id', msgId);
oldMsgId = msgId;
}
});
messagesScrollDown(thread);
loadUpdateInbox();
}
/**
* Refresh of thread window for new messages
* @param threadId
*/
function loadThreadRefresh(threadId) {
var threadDiv = pmWindows.find('.threads-wrapper .thread-'+threadId);
if(!threadDiv.length) return;
if(requesting.threadRefresh[threadId]) return;
requesting.threadRefresh[threadId] = true;
var msgId = threadDiv.attr('data-last-msg-id');
$.ajax({
url: blockSettings.loadNewMsgsUrl,
data: { thread_id:threadId, message_id: msgId },
success: function (data) {
requesting.threadRefresh[threadId] = false;
triggerCommands(data);
}
});
}
/**
* Moving message list to the last message
* @param threadDiv
*/
function messagesScrollDown(threadDiv) {
var messages = threadDiv.find('.messages');
var height = 0;
messages.find('.private-message').each(function(i,item){
height -= (-1)*$(item).outerHeight();
});
messages.scrollTop(height - (-1)*300);
}
/**
* Click on the recipient link for new thread creation
*
* @param e
*/
function newRecipientMessage(e) {
e.stopPropagation();
e.preventDefault();
let aTag = $(this);
let href = aTag.attr('href');
let check = new RegExp('recipient=(\\d+)');
let checkRes = check.exec(href);
if(!checkRes[1] || !(checkRes[1]>0)) {
window.location = href;
return;
}
openRecipientMessage(checkRes[1])
}
/**
* Click on the recipient link to open exists thread
*
* @param e
*/
function existsRecipientThread(e) {
e.stopPropagation();
e.preventDefault();
let aTag = $(this);
let href = aTag.attr('href');
let check = new RegExp('/private-messages/(\\d+)');
let checkRes = check.exec(href);
if(!checkRes[1] || !(checkRes[1]>0)) {
window.location = href;
return;
}
openThreadMessages(checkRes[1])
}
/**
* Opening a window with exists thread
*
* @param threadId
*/
function openThreadMessages(threadId) {
if(requesting.existsThread) return;
requesting.existsThread = true;
pmWindows.show();
let userThreadLink = pmWindows.find('.inbox-thread-list .thread-' + threadId);
if (userThreadLink.length) {
let userThread = pmWindows.find('.threads-wrapper .thread-' + threadId);
if(userThread.length) {
userThread.addClass('active').show();
} else {
userThreadLink.trigger('click');
}
requesting.existsThread = false;
return;
}
$.ajax({
url: blockSettings.loadThreadMsgsUrl,
data: { threadid: threadId },
method: 'POST',
success: function (data) {
requesting.existsThread = false;
triggerCommands(data);
}
});
}
/**
* Opening a window for new thread (with new contact)
*
* @param recipientId
*/
function openRecipientMessage(recipientId) {
if(requesting.newThread) return;
requesting.newThread = true;
pmWindows.show();
var userThreadLink = pmWindows.find('.inbox-thread-list .thread-user-' + recipientId);
if(userThreadLink.length) {
var userThread = pmWindows.find('.threads-wrapper .thread-user-' + recipientId);
if(userThread.length) {
userThread.addClass('active').show();
} else {
userThreadLink.trigger('click');
}
requesting.newThread = false;
return;
}
$.ajax({
url: blockSettings.newThreadUrl,
data: { recip_id:recipientId },
method: 'POST',
success: function (data) {
requesting.newThread = false;
triggerCommands(data);
}
});
}
/**
* Cleaning interface after thread deleting
* @param threadId
*/
function deleteThread(threadId) {
pmWindows.find('.inbox-thread-list .thread.thread-' + threadId).empty().remove();
pmWindows.find('.threads-wrapper .thread-' + threadId).empty().remove();
}
Drupal.behaviors.privateMessageWindowsThreadList = {
attach:function (context) {
init(context);
Drupal.AjaxCommands.prototype.
PrivateMessageWindowsInboxUpdate = function (ajax, response) {
ajax = ajax; // For jSlint compatibility.
updateInbox(response.threadIds, response.newThreads);
};
Drupal.AjaxCommands.prototype.PrivateMessageWindowsFillThread = function (ajax, response) {
ajax = ajax; // For jSlint compatibility.
loadThreadResponse(response.threadId, response.firstMsgId, response.lastMsgId, response.userId, response.userName, response.userPicture, response.messages, response.loadPrevTitle);
};
Drupal.AjaxCommands.prototype.PrivateMessageThreadMsgsUpdate = function (ajax, response) {
ajax = ajax; // For jSlint compatibility.
updateThreadMsgs(response.messages, response.threadId, response.lastMsgId);
};
Drupal.AjaxCommands.prototype.PrivateMessageInsThreadOldMessages = function (ajax, response) {
ajax = ajax; // For jSlint compatibility.
insertOldThreadMsgs(response.messages, response.threadId, response.firstMessageId, response.hasNext);
};
Drupal.AjaxCommands.prototype.PrivateMessageShowUserList = function (ajax, response) {
ajax = ajax; // For jSlint compatibility.
showSearchedUserList(response.users);
};
Drupal.AjaxCommands.prototype.PrivateMessageDeleteThread = function (ajax, response) {
ajax = ajax; // For jSlint compatibility.
deleteThread(response.thread_id);
};
},
detach:function (context) {
}
};
}(jQuery, Drupal, drupalSettings, window));
No time to create a patach, the above fixes the the Javascript updates needed for D10