$(function () { $(document).ready(function () { $('.chat-container').removeClass('no-js'); var socket = io.connect( 'http://' + document.domain + ':' + location.port + '/chat', { transports: ['websocket'] } ); // Store current username received from app context var current_username = $('#username').val().trim(); // Triggered on any status change by any user, such as some // user joined, or changed username, or left, etc. socket.on('status', function (data) { addMessageToPanel(data, current_username, 'status'); console.log(data, current_username); }); // Triggered when message is received from a user. Even when sent // by self, it get triggered after the server sends back the emit. socket.on('chat_message', function (data) { addMessageToPanel(data, current_username, 'chat'); console.log(data, current_username); }); // Triggered when disconnected either by server stop or timeout socket.on('disconnect', function (data) { addMessageToPanel({ 'msg': 'The chat server is disconnected.' }, current_username, 'status'); }) socket.on('connect_error', function (error) { console.log("error"); }) // Trigger new message on enter or click of send message button. $('#new-message').on('keypress', function (e) { var code = e.keyCode || e.which; if (code == 13) { emitMessage(socket); } }); // Keep buttons disabled unless changed or not empty $('#username').on('keyup', function (event) { if ($('#username').val() !== '' && $('#username').val() !== current_username) { if (event.keyCode == 13 || event.which == 13) { this.blur(); current_username = updateUsername(socket) || current_username; } } }); // Show warning of losing data $(window).on('beforeunload', function (e) { e.preventDefault(); e.returnValue = ''; return ''; }); }); }); var addMessageToPanel = function (data, current_username, messageType) { var scrollDiff = getScrollDiffBefore(); if (messageType === 'status') { addStatusMessage(data.msg); if (data.connected_users) { addUserList(data.connected_users, current_username); } } else if (messageType === 'chat') { addChatMessage(data.username, data.msg) } scrollBottomMaybe(scrollDiff); } var emitMessage = function (socket) { var text = $('#new-message').val(); $('#new-message').val(''); $('#chat').scrollTop($('#chat')[0].scrollHeight); socket.emit('text', { msg: text }); } var updateUsername = function (socket) { var username = $('#username').val(); if (!checkUsernameExists(username) && !checkUsernameTooLong(username) && !checkUsernameAscii(username)) { $.ajax({ method: 'POST', url: `http://${document.domain}:${location.port}/update-session-username`, contentType: 'application/json', dataType: 'json', data: JSON.stringify({ 'username': username }) }).done(function (response) { console.log(response); if (response.success && response.username == username) { socket.emit('update_username', { username: username }); } else { addStatusMessage("Failed to update username.") } }); return username; } return false; } /************************************/ /********* Util Functions ***********/ /************************************/ var createUserListHTML = function (connected_users, current_user) { var userListHTML = ''; connected_users.sort(); connected_users.forEach(function (username) { if (username !== current_user) { userListHTML += `
  • ${sanitizeHTML(username)}
  • `; } }); return userListHTML; } var checkUsernameAscii = function (username) { // ASCII characters have code points in the range U+0000-U+007F. $('#username-error').text(''); if (!/^[\u0000-\u007f]*$/.test(username)) { $('#username-error').text('Non-ASCII usernames are not supported.'); return true; } return false; } var checkUsernameExists = function (username) { $('#username-error').text(''); var userMatches = $('#user-list li').filter(function () { return $(this).text() === username; }); if (userMatches.length) { $('#username-error').text('User with that username exists!'); return true; } return false; } var checkUsernameTooLong = function (username) { $('#username-error').text(''); if (username.length > 128) { $('#username-error').text('Please choose a shorter username.'); return true; } return false; } var getScrollDiffBefore = function () { return $('#chat').scrollTop() - ($('#chat')[0].scrollHeight - $('#chat')[0].offsetHeight); } var scrollBottomMaybe = function (scrollDiff) { // Scrolls to bottom if the user is scrolled at bottom // if the user has scrolled up, it won't scroll at bottom. // Note: when a user themselves send a message, it will still // scroll to the bottom even if they had scrolled up before. if (scrollDiff > 0) { $('#chat').scrollTop($('#chat')[0].scrollHeight); } } var addStatusMessage = function (message) { $('#chat').append( `

    ${sanitizeHTML(message)}

    ` ); } var addChatMessage = function (username, message) { $('#chat').append(`

    ${sanitizeHTML(username)}${sanitizeHTML(message)}

    `); } var addUserList = function (connected_users, current_username) { $('#user-list').html( createUserListHTML( connected_users, current_username ) ); } var sanitizeHTML = function (str) { var temp = document.createElement('span'); temp.textContent = str; return temp.innerHTML; };