<script type="text/javascript"> function isMobile() { return window.matchMedia("only screen and (max-width: 799px)").matches; } function load_next_page(last_id, refresh_html=false) { if (refresh_html) { window.lock = true; $('#chat-log').html(''); $('#loader').show(); } var param = { 'last_id': last_id, 'only_messages': true, } $.get("{{ url('chat', '') }}" + window.room_id, param) .fail(function() { console.log("Fail to load page, last_id = " + last_id); window.lock = false; }) .done(function(data) { if (refresh_html) { $('#chat-box').scrollTop($('#chat-box')[0].scrollHeight); } var time = refresh_html ? 0 : 200; setTimeout(function() { $(".has_next").remove(); let $chat_box = $('#chat-box'); let lastMsgPos = scrollTopOfBottom($chat_box) $('#loader').hide(); if (refresh_html) { $('#chat-log').append(data); } else { $('#chat-log').prepend(data); } postProcessMessages(); if (!refresh_html) { $chat_box.scrollTop(scrollTopOfBottom($chat_box) - lastMsgPos); } else { $('#chat-box').scrollTop($('#chat-box')[0].scrollHeight); } window.lock = false; window.has_next = parseInt($(".has_next").attr("value")); }, time); }) } function postProcessMessages() { register_time($('.time-with-rel')); renderKatex(); populateCopyButton(); merge_authors(); } function scrollTopOfBottom(container) { return container[0].scrollHeight - container.innerHeight() } function scrollContainer(container, loader) { container.scroll(function() { if (container.scrollTop() == 0) { if (!window.lock && window.has_next) { loader.show(); var message_ids = $('.message').map(function() { return parseInt($(this).attr('message-id')); }).get(); load_next_page(Math.min(...message_ids)); } } })} function refresh_status(refresh_chat_info=false) { $.get("{{url('online_status_ajax')}}") .fail(function() { console.log("Fail to get online status"); }) .done(function(data) { if (data.status == 403) { console.log("Fail to retrieve data"); } else { $('#chat-online-list').html(data).find('.toggle').each(function () { register_toggle($(this)); }); register_click_space(); register_setting(false); color_selected_room(); } }) var data = { 'user': window.other_user_id, }; if (refresh_chat_info) { $('#chat-info').html(''); } $.get("{{url('user_online_status_ajax')}}", data) .fail(function() { console.log("Fail to get user online status"); }) .done(function(data) { $('#chat-info').html(data); register_time($('.time-with-rel')); register_setting(true); }) } function add_message(data) { var $data = $(data); $('#chat-log').append($data); $('#chat-box').scrollTop($('#chat-box')[0].scrollHeight); postProcessMessages(); } function add_new_message(message, room, is_self_author) { function callback(update) { if (!document['hidden']) { if (update) update_last_seen(); refresh_status(); } else if (!is_self_author) { window.unread_message++; document.title = "(" + window.unread_message + ") " + "{{ _('New message(s)') }}"; } } if (room == window.room_id) { $.get({ url: "{{ url('chat_message_ajax') }}", data: { message: message, }, success: function (data) { add_message(data); callback(true); }, error: function (data) { console.log('Could not add new message'); } }); } else { callback(false); } } function check_new_message(message, tmp_id, room) { if (room == room_id) { $.get({ url: "{{ url('chat_message_ajax') }}", data: { message: message, }, success: function (data) { var $body_block = $(data).find('.body-block'); if ($('#message-'+tmp_id).length) { $('#message-'+tmp_id).replaceWith(data); } else if ($('#body-block-'+tmp_id).length) { $('#body-block-'+tmp_id).replaceWith($body_block); } else { add_new_message(message, room, true); } remove_unread_current_user(); postProcessMessages(); }, error: function (data) { console.log('Fail to check message'); var $body = $('#body-block-'+tmp_id + ' p'); $body.css('text-decoration', 'line-through'); $body.css('text-decoration-color', 'red'); } }); } } function merge_authors() { var time_limit = 5; // minutes var last = { username: null, time: null, $content: null }; $('.body-message').each(function() { var username = $(this).find(".username a").text().trim(); var $body = $(this).find(".content-message .body-block"); var time = moment($(this).find(".time-with-rel").attr('data-iso')); var $content = $(this).children('.content-message'); if (username == window.user.name) { $(this).find('.message-text').each(function() { $(this).removeClass('message-text-other').addClass('message-text-myself'); }); } if (username == last.username && time.diff(last.time, 'minutes') <= time_limit) { last.$content.append($body); $(this).parent().remove(); } else { last.username = username; last.time = time; last.$content = $content; } }); } function add_message_from_template(body, tmp_id) { if (window.room_id) { $("#last_msg-" + window.room_id).html(body); } var html = message_template(); html = html.replaceAll('$body', body).replaceAll('$id', tmp_id); var $html = $(html); $html.find('.time-with-rel').attr('data-iso', (new Date()).toISOString()); add_message($html[0].outerHTML); } function submit_chat() { {% if last_msg and not request.profile.mute %} if ($("#chat-input").val().trim()) { $('#chat-input-container').height('auto'); var body = $('#chat-input').val().trim(); var message = { body: body, room: window.room_id, tmp_id: Date.now(), }; $('#chat-input').val(''); $('#chat-input').css('height', '70%'); add_message_from_template(body, message.tmp_id); $.post("{{ url('post_chat_message') }}", message) .fail(function(res) { console.log('Fail to send message'); var $body = $('#message-text-'+ message.tmp_id); $body.css('text-decoration', 'line-through'); $body.css('background', 'red'); }) .done(function(res, status) { $('#empty_msg').hide(); $('#chat-input').focus(); }) } {% endif %} } function resize_emoji(element) { var html = element.html(); html = html.replace(/(\p{Extended_Pictographic})/ug, `<span class="big-emoji">$1</span>`); element.html(html); } function insert_char_after_cursor(elem, char) { var val = elem.value; if (typeof elem.selectionStart == "number" && typeof elem.selectionEnd == "number") { var start = elem.selectionStart; var prefix = elem.value.slice(0, start); var prefix_added = prefix + char; var chars = [...val]; chars.splice([...prefix].length, 0, char); elem.value = chars.join(''); elem.selectionStart = elem.selectionEnd = prefix_added.length; } else if (document.selection && document.selection.createRange) { var range = document.selection.createRange(); elem.focus(); range.text = char; range.collapse(false); range.select(); } } function color_selected_room() { $(".status-row").removeClass("selected-status-row"); $("#click_space_" + window.other_user_id).addClass("selected-status-row"); } function show_right_panel() { if (isMobile()) { $('.chat-left-panel').hide(); $('#chat-area').css('display', 'flex'); $('#chat-box').scrollTop($('#chat-box')[0].scrollHeight); } } function hide_right_panel() { if (isMobile()) { $('.chat-left-panel').show(); $('#chat-area').hide(); } } function load_room(encrypted_user) { if (window.lock_click_space) return; function callback() { history.replaceState(null, '', "{{url('chat', '')}}" + window.room_id); load_next_page(null, true); update_last_seen(); refresh_status(true); show_right_panel(); $('#chat-input').focus(); $('#chat-input').val('').trigger('input'); } window.lock_click_space = true; if (encrypted_user) { $.get("{{url('get_or_create_room')}}" + `?other=${encrypted_user}`) .done(function(data) { window.room_id = data.room; window.other_user_id = data.other_user_id; color_selected_room(); callback(); $('#chat-input').attr('maxlength', 5000); }) .fail(function() { console.log('Fail to get_or_create_room'); }) } else { window.room_id = ''; window.other_user_id = ''; color_selected_room(); callback(); $('#chat-input').attr('maxlength', 200); } window.lock_click_space = false; } function register_click_space() { $('.click_space').on('click', function(e) { if ($(this).attr('id') == 'click_space_' + window.other_user_id) { show_right_panel(); return; } var other_user = $(this).attr('value'); load_room(other_user); }); $('#lobby_row').on('click', function(e) { if (window.room_id) { load_room(null); } else { show_right_panel(); } }); } function update_last_seen() { var data = { room: window.room_id }; $.post("{{ url('update_last_seen') }}", data) .fail(function(data) { console.log('Fail to update last seen'); }) .done(function(data) { }) } function remove_unread_current_user() { if (window.other_user_id) { $("#unread-count-" + window.other_user_id).hide(); } else { $('#unread-count-lobby').hide(); } } function register_setting(is_on_user_status_bar) { let $setting_button = is_on_user_status_bar ? $('.user-setting-button') : $('.setting-button'); $setting_button.on('click', function(e) { e.stopPropagation(); $('.setting-content').not($(this).siblings('.setting-content')).hide(); $(this).siblings('.setting-content').toggle(); }); $('.setting-content a').on('click', function(e) { e.stopPropagation(); var href = $(this).attr('href'); href += '?next=' + window.location.pathname; $(this).attr('href', href); }) $(document).on('click', function() { $('.setting-content').hide(); }); } $(function() { $('#loader').hide(); update_last_seen(); merge_authors(); window.has_next = parseInt($(".has_next").attr("value")); scrollContainer($('#chat-box'), $('#loader')) {% if request.user.is_staff %} $(document).on("click", ".chat_remove", function() { var elt = $(this); $.ajax({ url: "{{ url('delete_chat_message') }}", type: 'post', data: { message: elt.attr('value'), }, dataType: 'json', success: function(data){ var $block = elt.parent(); if ($block.parent().find('.body-block').length > 1) { $block.remove(); } else { elt.closest('li').remove(); } }, fail: function(data) { console.log('Fail to delete'); }, }); }); $(document).on("click", ".chat_mute", function() { if (confirm("{{_('Mute this user and delete all messages?')}}")) { var elt = $(this); $.ajax({ url: "{{ url('mute_chat_message') }}", type: 'post', data: { message: elt.attr('value'), }, dataType: 'json', success: function(data){ window.location.reload(); }, fail: function(data) { console.log('Fail to delete'); }, }); } }); {% endif %} $("#chat-log").show(); $("#chat-log").change(function() { $('#chat-log').scrollTop($('#chat-log')[0].scrollHeight); }); $('#chat-input').focus(); $('#chat-input').keydown(function(e) { if (e.keyCode === 13) { if (e.ctrlKey || e.shiftKey) { insert_char_after_cursor(this, "\n"); } else { e.preventDefault(); submit_chat(); } return false } return true }); setInterval(refresh_status, 2 * 60 * 1000); $('#chat-box').scrollTop($('#chat-box')[0].scrollHeight); const button = document.querySelector('#emoji-button'); const tooltip = document.querySelector('.tooltip'); const popper = Popper.createPopper(button, tooltip, { placement: isMobile() ? 'auto-end' : 'left', }); function toggleEmoji() { tooltip.classList.toggle('shown'); popper.update(); } $('#emoji-button').on('click', function(e) { e.preventDefault(); e.stopPropagation(); toggleEmoji(); }); // Đóng bảng emoji khi click bất kỳ chỗ nào trên màn hình document.addEventListener("click", function(e) { if (!tooltip.contains(e.target)) { tooltip.classList.remove('shown'); // Ẩn bảng emoji } }); $('emoji-picker').on('emoji-click', function(e) { var $chat = $('#chat-input').get(0); insert_char_after_cursor($chat, e.detail.unicode); $chat.focus(); }) register_click_space(); document.addEventListener('keydown', function(e) { if (e.keyCode === 27 && $('.tooltip').hasClass('shown')) { toggleEmoji(); } }) $('#search-handle').replaceWith($('<select>').attr({ id: 'search-handle', name: 'other', onchange: 'form.submit()' })); var in_user_redirect = false; $('#search-handle').select2({ placeholder: '<i class="fa fa-search"></i> {{ _('Search by handle...') }}', ajax: { url: '{{ url('chat_user_search_select2_ajax') }}', delay: 250, cache: true, }, minimumInputLength: 1, escapeMarkup: function (markup) { return markup; }, templateResult: function (data, container) { return $('<span>') .append($('<img>', { 'class': 'user-search-image', src: data.gravatar_url, width: 24, height: 24 })) .append($('<span>', {'class': data.display_rank + ' user-search-name'}).text(data.text)) .append($('<a>', {href: '/user/' + data.text, 'class': 'user-redirect'}) .append($('<i>', {'class': 'fa fa-mail-forward'})) .mouseover(function () { in_user_redirect = true; }).mouseout(function () { in_user_redirect = false; })); } }).on('select2:selecting', function () { return !in_user_redirect; }); $("#chat-input").on("keyup", function() { $("#chat-input").scrollTop($("#chat-input")[0].scrollHeight); }); // https://stackoverflow.com/questions/42121565/detecting-class-change-without-setinterval if (typeof(MutationObserver) !== undefined) { var observer = new MutationObserver(function (event) { if (!document['hidden'] && window.unread_message > 0) { update_last_seen(); refresh_status(); window.unread_message = 0; document.title = "{{_('Chat Box')}}"; } }) observer.observe(document.body, { attributes: true, attributeFilter: ['class'], childList: false, characterData: false }) } register_setting(true); register_setting(false); color_selected_room(); $('#chat-input').on('input', function() { if (this.scrollHeight > this.clientHeight) { this.style.height = (this.scrollHeight) + 'px'; $(this).css('border-radius', '30px'); } else { $(this).css('height', '70%'); } }); $('#submit-button').on('click', submit_chat); register_copy_clipboard($("#chat-input"), () => { $('#chat-input').trigger('input'); }); }); </script>