(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); }); } }); })();