(function () {
var previewZoom = 1;
var previewUrl = '';
window.chatSidebarFilter = 'all';
function initializeEmployeeFilter() {
var control = document.querySelector('.chatfilter-control');
var select = document.getElementById('c1');
if (!control || !select) {
return;
}
var toggle = control.querySelector('.chatfilter__toggle');
var menu = control.querySelector('.chatfilter__menu');
var currentLabel = control.querySelector('.chatfilter__current');
function updateSelectedOption() {
var selectedValue = select.value;
var selectedOption = Array.prototype.find.call(select.options, function (option) {
return option.value === selectedValue && option.textContent.trim() !== 'All Clients';
});
var isAllClients = selectedValue === '';
var selectedText = isAllClients ? '' : (selectedOption ? selectedOption.textContent.trim() : 'All Clients');
if (currentLabel) {
currentLabel.textContent = selectedText;
}
toggle.classList.toggle('has-selection', !isAllClients);
toggle.setAttribute('aria-label', isAllClients ? 'All Clients' : ('Select user: ' + selectedText));
toggle.setAttribute('title', isAllClients ? 'All Clients' : (selectedText || 'All Clients'));
menu.querySelectorAll('.chatfilter__option').forEach(function (button) {
var selected = button.dataset.value === select.value;
button.classList.toggle('is-selected', selected);
button.setAttribute('aria-checked', selected ? 'true' : 'false');
});
}
var seenValues = Object.create(null);
var employeeOptions = Array.prototype.filter.call(select.options, function (option, index, options) {
if (option.textContent.trim() !== 'Select User') {
return true;
}
return !Array.prototype.some.call(options, function (candidate) {
return candidate !== option && candidate.value === option.value && candidate.textContent.trim() !== 'Select User';
});
});
if (!menu.querySelector('.chatfilter__option')) employeeOptions.forEach(function (option) {
if (seenValues[option.value]) {
return;
}
seenValues[option.value] = true;
var button = document.createElement('button');
button.type = 'button';
button.className = 'chatfilter__option';
button.setAttribute('role', 'menuitemradio');
button.setAttribute('aria-checked', option.selected ? 'true' : 'false');
button.dataset.value = option.value;
var label = document.createElement('span');
label.className = 'chatfilter__option-label';
label.textContent = option.textContent.trim();
var check = document.createElement('span');
check.className = 'chatfilter__option-check';
check.textContent = '\u2713';
button.appendChild(label);
button.appendChild(check);
menu.appendChild(button);
});
updateSelectedOption();
menu.addEventListener('click', function (event) {
var optionButton = event.target.closest('.chatfilter__option');
if (!optionButton) {
return;
}
select.value = optionButton.dataset.value;
updateSelectedOption();
menu.classList.remove('is-open');
toggle.setAttribute('aria-expanded', 'false');
$(select).trigger('change');
toggle.focus();
});
document.addEventListener('click', function (event) {
if (!control.contains(event.target)) {
menu.classList.remove('is-open');
toggle.setAttribute('aria-expanded', 'false');
}
});
document.addEventListener('keydown', function (event) {
if (event.key === 'Escape' && menu.classList.contains('is-open')) {
menu.classList.remove('is-open');
toggle.setAttribute('aria-expanded', 'false');
toggle.focus();
}
});
}
function initializeChatSidebarFilters() {
var filterBar = document.getElementById('chatSidebarFilters');
var chatTable = $('#chat-table');
if (!filterBar) {
return;
}
function updateGroupCount() {
var count = document.querySelectorAll('#groupChatsBox .group-chat-item').length;
var badge = document.getElementById('chatGroupsCount');
if (badge) {
badge.textContent = count;
}
}
function applyFilter(filter, reloadTable) {
window.chatSidebarFilter = filter;
filterBar.querySelectorAll('.chat-sidebar-filter').forEach(function (button) {
var isActive = button.dataset.chatFilter === filter;
button.classList.toggle('is-active', isActive);
button.setAttribute('aria-pressed', isActive ? 'true' : 'false');
});
var showGroups = filter === 'groups';
var tableWrapper = document.getElementById('chat-table_wrapper');
var chatTableFooter = document.querySelector('#chat-table_wrapper .chat-table-footer');
var groupChatsBox = document.getElementById('groupChatsBox');
var chatSearchSlot = document.getElementById('user-sidebar-search-slot');
if (tableWrapper) tableWrapper.style.display = showGroups ? 'none' : '';
if (chatTableFooter) chatTableFooter.style.display = showGroups ? 'none' : '';
if (groupChatsBox) groupChatsBox.style.display = showGroups ? 'block' : 'none';
if (chatSearchSlot) chatSearchSlot.style.display = showGroups ? 'none' : '';
if (reloadTable && !showGroups && $.fn.DataTable && $.fn.DataTable.isDataTable('#chat-table')) {
chatTable.DataTable().ajax.reload(null, true);
}
updateGroupCount();
}
if (chatTable.length) {
chatTable.on('preXhr.dt', function (event, settings, data) {
if (Array.isArray(data)) {
data.push({ name: 'chatFilter', value: window.chatSidebarFilter });
} else {
data.chatFilter = window.chatSidebarFilter;
}
});
chatTable.on('xhr.dt', function (event, settings, json) {
var badge = document.getElementById('chatUnreadCount');
if (badge && json && typeof json.unreadCount !== 'undefined') {
badge.textContent = json.unreadCount;
}
});
if ($.fn.DataTable && $.fn.DataTable.isDataTable('#chat-table')) {
var currentResponse = chatTable.DataTable().ajax.json();
var unreadBadge = document.getElementById('chatUnreadCount');
if (unreadBadge && currentResponse && typeof currentResponse.unreadCount !== 'undefined') {
unreadBadge.textContent = currentResponse.unreadCount;
}
}
}
applyFilter('all', false);
}
function getContactInitials(name) {
var parts = (name || '').trim().split(/\s+/).filter(Boolean);
if (!parts.length) {
return '?';
}
return parts.slice(0, 2).map(function (part) {
return Array.from(part)[0].toLocaleUpperCase();
}).join('');
}
function getContactHue(name) {
var hash = 0;
for (var index = 0; index < name.length; index++) {
hash = ((hash << 5) - hash + name.charCodeAt(index)) >>> 0;
}
return hash % 360;
}
function applyContactInitials(avatar, name) {
var contactName = (name || '').trim();
if (avatar.classList.contains('avatar-initials') && avatar.dataset.initialsName === contactName) {
return;
}
avatar.textContent = getContactInitials(contactName);
avatar.dataset.initialsName = contactName;
avatar.style.setProperty('--avatar-hue', getContactHue(contactName));
avatar.classList.add('avatar-initials');
avatar.setAttribute('aria-label', contactName || 'Contact');
}
function initializeChatHeaderMeta() {
document.querySelectorAll('.chat-profile[data-client-id]').forEach(function (profile) {
var clientId = profile.dataset.clientId;
var contactCard = document.querySelector('.sidebar-user-card[data-clientid="' + clientId + '"]');
var statusBadge = profile.querySelector('.chat-header__window-badge');
if (!contactCard) {
if (statusBadge) {
statusBadge.textContent = 'Inactive';
statusBadge.classList.add('is-visible', 'chat-header__window-badge--offline');
}
return;
}
var roleSource = contactCard.querySelector('.sidebar-user-admin-meta span');
var statusSource = contactCard.querySelector('.sidebar-chat-status');
var roleBadge = profile.querySelector('.chat-header__role');
if (roleSource && roleBadge) {
roleBadge.textContent = roleSource.textContent.trim();
roleBadge.classList.toggle('is-visible', Boolean(roleBadge.textContent));
}
if (statusBadge) {
statusBadge.textContent = statusSource ? statusSource.textContent.trim() : 'Inactive';
statusBadge.classList.toggle('is-visible', Boolean(statusBadge.textContent));
statusBadge.classList.toggle('chat-header__window-badge--online', Boolean(statusSource && statusSource.classList.contains('sidebar-chat-status--online')));
statusBadge.classList.toggle('chat-header__window-badge--offline', !statusSource || statusSource.classList.contains('sidebar-chat-status--offline'));
}
});
}
function initializeContactInitials(root) {
var scope = root || document;
var headerAvatars = [];
var chatCards = [];
if (scope.nodeType === 1 && scope.matches('.chat-profile-img[data-contact-name]')) {
headerAvatars.push(scope);
}
if (scope.nodeType === 1 && scope.matches('.sidebar-user-card')) {
chatCards.push(scope);
}
scope.querySelectorAll('.chat-profile-img[data-contact-name]').forEach(function (avatar) {
headerAvatars.push(avatar);
});
scope.querySelectorAll('.sidebar-user-card').forEach(function (card) {
chatCards.push(card);
});
headerAvatars.forEach(function (avatar) {
applyContactInitials(avatar, avatar.dataset.contactName);
});
chatCards.forEach(function (card) {
var avatar = card.querySelector('.sidebar-user-avatar-wrap .avatar');
var name = card.querySelector('.sidebar-user-name');
if (avatar && name) {
applyContactInitials(avatar, name.textContent);
}
});
if (headerAvatars.length || chatCards.length) {
initializeChatHeaderMeta();
}
}
function initializeCaptionedImageMessages(root) {
var scope = root || document;
var imageMessages = [];
if (scope.nodeType === 1 && scope.matches('.chat-message__media--image')) {
imageMessages.push(scope);
}
scope.querySelectorAll('.chat-message__media--image').forEach(function (imageMessage) {
imageMessages.push(imageMessage);
});
imageMessages.forEach(function (imageMessage) {
imageMessage.classList.toggle('chat-message__media--captioned', Boolean(imageMessage.querySelector('.chat-message__caption')));
});
}
function initializeDocumentPreviews(root) {
var scope = root || document;
var documentMessages = [];
if (scope.nodeType === 1 && scope.matches('.chat-message__file--document')) {
documentMessages.push(scope);
}
scope.querySelectorAll('.chat-message__file--document').forEach(function (documentMessage) {
documentMessages.push(documentMessage);
});
documentMessages.forEach(function (documentMessage) {
if (documentMessage.dataset.previewInitialized === 'true') {
return;
}
var documentLink = documentMessage.querySelector('.chat-message__doc-link');
if (!documentLink || !/\.pdf(?:$|[?#])/i.test(documentLink.href)) {
return;
}
documentMessage.dataset.previewInitialized = 'true';
documentMessage.classList.add('chat-message__file--pdf');
var fileName = documentLink.textContent.trim().split(/[\\/]/).pop();
var previewLink = document.createElement('a');
previewLink.className = 'chat-message__doc-preview';
previewLink.href = documentLink.href;
previewLink.target = '_blank';
previewLink.rel = 'noopener';
previewLink.setAttribute('aria-label', 'Preview ' + fileName);
var previewFrame = document.createElement('iframe');
previewFrame.src = documentLink.href + '#page=1&toolbar=0&navpanes=0&scrollbar=0&view=FitH';
previewFrame.title = 'First page preview of ' + fileName;
previewFrame.loading = 'lazy';
previewFrame.setAttribute('aria-hidden', 'true');
previewFrame.tabIndex = -1;
previewLink.appendChild(previewFrame);
documentMessage.insertBefore(previewLink, documentLink);
var optionsButton = document.createElement('button');
optionsButton.type = 'button';
optionsButton.className = 'chat-message__image-menu js-chat-image-menu';
optionsButton.setAttribute('aria-label', 'Document options');
optionsButton.setAttribute('aria-expanded', 'false');
optionsButton.innerHTML = '';
var optionsMenu = document.createElement('div');
optionsMenu.className = 'chat-message__image-dropdown';
optionsMenu.setAttribute('role', 'menu');
optionsMenu.setAttribute('aria-label', 'Document actions');
optionsMenu.innerHTML = '';
documentMessage.appendChild(optionsButton);
documentMessage.appendChild(optionsMenu);
var documentIcon = documentLink.querySelector('.chat-message__doc-icon');
var fileDetails = document.createElement('span');
fileDetails.className = 'chat-message__doc-details';
var fileNameLabel = document.createElement('span');
fileNameLabel.className = 'chat-message__doc-name';
fileNameLabel.textContent = fileName;
var fileMetadata = document.createElement('span');
fileMetadata.className = 'chat-message__doc-meta';
fileMetadata.textContent = 'PDF document';
documentLink.textContent = '';
documentLink.rel = 'noopener';
if (documentIcon) {
documentLink.appendChild(documentIcon);
}
fileDetails.appendChild(fileNameLabel);
fileDetails.appendChild(fileMetadata);
documentLink.appendChild(fileDetails);
fetch(documentLink.href, { method: 'HEAD', credentials: 'same-origin' }).then(function (response) {
var contentLength = Number(response.headers.get('content-length'));
if (!response.ok || !contentLength) {
return;
}
var fileSize = contentLength < 1048576
? Math.round(contentLength / 1024) + ' KB'
: (contentLength / 1048576).toFixed(1) + ' MB';
fileMetadata.textContent = 'PDF ยท ' + fileSize;
}).catch(function () {});
});
}
function bindZoomButtons() {
var zoomButtons = document.querySelectorAll('.chat-image-overlay__action--zoom');
zoomButtons.forEach(function (button) {
button.removeEventListener('click', button._chatZoomClickHandler);
button._chatZoomClickHandler = function () {
if (button.dataset.action === 'zoom-in') {
setPreviewZoom(previewZoom + 0.1);
}
if (button.dataset.action === 'zoom-out') {
setPreviewZoom(previewZoom - 0.1);
}
};
button.addEventListener('click', button._chatZoomClickHandler);
});
}
function setPreviewZoom(nextZoom) {
previewZoom = Math.min(1.5, Math.max(1, nextZoom));
var img = document.getElementById('chatImagePreview');
var footer = document.querySelector('.chat-image-overlay__footer');
var badge = document.getElementById('chatImagePreviewZoomBadge');
var zoomButtons = document.querySelectorAll('.chat-image-overlay__action--zoom');
var resetButton = document.getElementById('chatImagePreviewReset');
var showBadge = Math.abs(previewZoom - 1) > 0.001;
if (img) {
img.style.setProperty('--chat-preview-zoom', previewZoom.toFixed(2));
}
if (badge) {
badge.classList.toggle('is-visible', showBadge);
if (showBadge) {
badge.textContent = 'Zoom in ' + Math.round(previewZoom * 100) + '%';
} else {
badge.textContent = '';
}
}
if (resetButton) {
resetButton.style.display = showBadge ? 'inline-flex' : 'none';
resetButton.classList.toggle('is-visible', showBadge);
}
zoomButtons.forEach(function (button) {
var isActive = false;
if (button.dataset.action === 'zoom-in' && previewZoom < 1.5) {
isActive = true;
}
if (button.dataset.action === 'zoom-out' && previewZoom > 1) {
isActive = true;
}
button.classList.toggle('is-active', isActive);
});
if (footer) {
if (previewZoom === 1) {
footer.style.opacity = '1';
footer.style.visibility = 'visible';
footer.style.pointerEvents = 'auto';
} else {
footer.style.opacity = '0';
footer.style.visibility = 'hidden';
footer.style.pointerEvents = 'none';
}
}
}
function handlePreviewAction(action) {
var img = document.getElementById('chatImagePreview');
if (!img || !img.src) {
return;
}
if (action === 'zoom-in') {
setPreviewZoom(previewZoom + 0.1);
return;
}
if (action === 'zoom-out') {
setPreviewZoom(previewZoom - 0.1);
return;
}
if (action === 'reset') {
setPreviewZoom(1);
return;
}
if (action === 'download') {
var link = document.createElement('a');
link.href = img.src;
link.download = img.src.split('/').pop() || 'chat-image';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
return;
}
if (action === 'copy') {
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(img.src).catch(function () {});
}
return;
}
if (action === 'forward') {
var overlay = document.getElementById('chatImagePreviewOverlay');
if (overlay) {
forwardChatMessage(overlay);
}
return;
}
if (action === 'delete') {
return;
}
}
document.addEventListener('click', function (event) {
var trigger = event.target.closest('[data-action]');
if (!trigger) {
return;
}
if (trigger.dataset.action === 'forward') {
event.preventDefault();
event.stopPropagation();
forwardChatMessage(trigger.closest('.chat-message'));
return;
}
if (trigger.closest('.chat-image-overlay__action')) {
event.preventDefault();
event.stopPropagation();
handlePreviewAction(trigger.dataset.action);
}
});
function forwardChatMessage(messageElement) {
if (!messageElement) {
return;
}
var messageId = messageElement.getAttribute('data-db-id');
var sourceClientId = messageElement.getAttribute('data-source-client-id');
if (!messageId) {
return;
}
$('#forwardMessageId').val(messageId);
$('#forwardSourceClientId').val(sourceClientId);
$('#forwardReceiverId').val('');
$('#forwardMessageError').text('');
var forwardModal = document.getElementById('forwardMessageModal');
if (forwardModal && window.bootstrap && bootstrap.Modal && typeof bootstrap.Modal.getOrCreateInstance === 'function') {
bootstrap.Modal.getOrCreateInstance(forwardModal).show();
} else if ($.fn.modal) {
$('#forwardMessageModal').modal('show');
}
}
// Expose the handler before any message action is clicked.
window.chatForwardMessage = forwardChatMessage;
$(document).on('click', '#forwardMessageSubmit', function () {
var button = $(this);
var receiverId = $('#forwardReceiverId').val();
$('#forwardMessageError').text('');
if (!receiverId) {
$('#forwardMessageError').text('Please select a contact.');
return;
}
button.prop('disabled', true).text('Sending...');
$.post('dashboard/forwardMessage', {
message_id: $('#forwardMessageId').val(),
source_client_id: $('#forwardSourceClientId').val(),
receiver_id: receiverId
}, function (data) {
var result = typeof data === 'string' ? JSON.parse(data) : data;
if (result.status === 'success') {
var forwardModal = document.getElementById('forwardMessageModal');
if (forwardModal && window.bootstrap && bootstrap.Modal && typeof bootstrap.Modal.getOrCreateInstance === 'function') {
bootstrap.Modal.getOrCreateInstance(forwardModal).hide();
} else if ($.fn.modal) {
$('#forwardMessageModal').modal('hide');
}
if (typeof toastSuccess === 'function') toastSuccess('Success!!!', 'Message forwarded!!!', 'top-right');
} else {
$('#forwardMessageError').text(result.message || 'Message could not be forwarded.');
}
}).fail(function () {
$('#forwardMessageError').text('Message could not be forwarded.');
}).always(function () {
button.prop('disabled', false).text('Forward');
});
});
function initializeForwardButtons() {
document.querySelectorAll('.chat-message[data-db-id]').forEach(function (message) {
if (message.querySelector('[data-action="forward"]')) return;
var content = message.querySelector('.chat-message__content');
if (!content) return;
var button = document.createElement('button');
button.type = 'button';
button.className = 'btn btn-sm btn-link text-primary p-0 ms-2 message-forward-btn';
button.textContent = 'Forward';
button.addEventListener('click', function () { forwardChatMessage(message); });
content.appendChild(button);
});
}
document.addEventListener('keydown', function (event) {
var overlay = document.getElementById('chatImagePreviewOverlay');
if (!overlay || !overlay.classList.contains('is-open')) {
return;
}
if (event.key === '+' || event.key === '=') {
setPreviewZoom(previewZoom + 0.1);
}
if (event.key === '-' || event.key === '_') {
setPreviewZoom(previewZoom - 0.1);
}
if (event.key === 'Escape') {
var closeBtn = document.getElementById('chatImagePreviewClose');
if (closeBtn) {
closeBtn.click();
}
}
});
function initializeLoginPreviewAnimation() {
var preview = document.querySelector('[data-login-preview]');
if (!preview) {
return;
}
var messages = preview.querySelector('[data-login-messages]');
var contact = preview.querySelector('[data-login-contact]');
var avatar = preview.querySelector('[data-login-avatar]');
var previousIndex = -1;
var conversations = [
{
name: 'Maya Patel',
incoming: 'What should I review before choosing a stock?',
outgoing: 'Compare earnings, valuation, debt, and your risk tolerance.',
feedback: 'Thanks, your team makes market research easier!'
},
{
name: 'Sofia Chen',
incoming: 'How can I make sense of market volatility?',
outgoing: 'Keep goals in focus and spread exposure across sectors.',
feedback: 'Appreciate the clear guidance and excellent service!'
},
{
name: 'Arjun Rao',
incoming: 'What is a useful way to compare companies?',
outgoing: 'Review revenue, margins, cash flow, and industry peers.',
feedback: 'That was helpful. Your support is excellent!'
},
{
name: 'Lena Brooks',
incoming: 'How do I build a thoughtful stock watchlist?',
outgoing: 'Track businesses you understand and review their updates.',
feedback: 'Thank you for the thoughtful service!'
}
];
function appendMessage(direction, text) {
var message = document.createElement('div');
message.className = 'login-preview__message login-preview__message--' + direction;
message.textContent = text;
messages.appendChild(message);
}
function appendTyping() {
var typing = document.createElement('div');
typing.className = 'login-preview__typing';
typing.setAttribute('aria-label', 'Typing');
for (var dotIndex = 0; dotIndex < 3; dotIndex++) {
typing.appendChild(document.createElement('span'));
}
messages.appendChild(typing);
return typing;
}
function showNextConversation() {
if (!preview.isConnected) {
return;
}
var conversationIndex = Math.floor(Math.random() * conversations.length);
if (conversationIndex === previousIndex) {
conversationIndex = (conversationIndex + 1) % conversations.length;
}
previousIndex = conversationIndex;
var conversation = conversations[conversationIndex];
contact.textContent = conversation.name;
avatar.textContent = conversation.name.split(' ').map(function (part) {
return part.charAt(0);
}).join('');
messages.replaceChildren();
appendMessage('incoming', conversation.incoming);
window.setTimeout(function () {
var typing = appendTyping();
window.setTimeout(function () {
typing.remove();
appendMessage('outgoing', conversation.outgoing);
window.setTimeout(function () {
appendMessage('incoming', conversation.feedback);
window.setTimeout(showNextConversation, 2600);
}, 1300);
}, 1100);
}, 900);
}
showNextConversation();
}
document.addEventListener('DOMContentLoaded', function () {
var closeBtn = document.getElementById('chatImagePreviewClose');
// Initialize the client dropdown first so unrelated chat helpers
// cannot prevent its click handler from being attached.
// Start the login-page live chat preview before other chat helpers.
initializeLoginPreviewAnimation();
initializeEmployeeFilter();
bindZoomButtons();
setPreviewZoom(1);
initializeContactInitials();
initializeCaptionedImageMessages();
initializeDocumentPreviews();
initializeChatSidebarFilters();
if (window.MutationObserver) {
var contactInitialsObserver = new MutationObserver(function (records) {
records.forEach(function (record) {
record.addedNodes.forEach(function (node) {
if (node.nodeType === 1) {
initializeContactInitials(node);
initializeCaptionedImageMessages(node);
initializeDocumentPreviews(node);
}
});
});
});
contactInitialsObserver.observe(document.body, { childList: true, subtree: true });
}
if (closeBtn) {
closeBtn.addEventListener('click', function () {
setPreviewZoom(1);
});
}
});
})();