/
var
/
www
/
html
/
cdhu
/
message
/
amd
/
src
/
Upload File
HOME
// This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * This module handles the message area of the messaging area. * * @module core_message/message_area_messages * @package core_message * @copyright 2016 Mark Nelson <markn@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/custom_interaction_events', 'core/auto_rows', 'core_message/message_area_actions', 'core/modal_factory', 'core/modal_events', 'core/str', 'core_message/message_area_events', 'core/backoff_timer'], function($, Ajax, Templates, Notification, CustomEvents, AutoRows, Actions, ModalFactory, ModalEvents, Str, Events, BackOffTimer) { /** @type {int} The message area default height. */ var MESSAGES_AREA_DEFAULT_HEIGHT = 500; /** @type {int} The response default height. */ var MESSAGES_RESPONSE_DEFAULT_HEIGHT = 50; /** @type {Object} The list of selectors for the message area. */ var SELECTORS = { BLOCKTIME: "[data-region='blocktime']", CANCELDELETEMESSAGES: "[data-action='cancel-delete-messages']", CONTACT: "[data-region='contact']", CONVERSATIONS: "[data-region='contacts'][data-region-content='conversations']", DELETEALLMESSAGES: "[data-action='delete-all-messages']", DELETEMESSAGES: "[data-action='delete-messages']", LOADINGICON: '.loading-icon', MESSAGE: "[data-region='message']", MESSAGERESPONSE: "[data-region='response']", MESSAGES: "[data-region='messages']", MESSAGESAREA: "[data-region='messages-area']", MESSAGINGAREA: "[data-region='messaging-area']", SENDMESSAGE: "[data-action='send-message']", SENDMESSAGETEXT: "[data-region='send-message-txt']", SHOWCONTACTS: "[data-action='show-contacts']", STARTDELETEMESSAGES: "[data-action='start-delete-messages']", }; /** @type {int} The number of milliseconds in a second. */ var MILLISECONDSINSEC = 1000; /** * Messages class. * * @param {Messagearea} messageArea The messaging area object. */ function Messages(messageArea) { this.messageArea = messageArea; this._init(); } /** @type {Boolean} checks if we are sending a message */ Messages.prototype._isSendingMessage = false; /** @type {Boolean} checks if we are currently loading messages */ Messages.prototype._isLoadingMessages = false; /** @type {int} the number of messagess displayed */ Messages.prototype._numMessagesDisplayed = 0; /** @type {array} the messages displayed or about to be displayed on the page */ Messages.prototype._messageQueue = []; /** @type {int} the number of messages to retrieve */ Messages.prototype._numMessagesToRetrieve = 20; /** @type {Modal} the confirmation modal */ Messages.prototype._confirmationModal = null; /** @type {int} the timestamp for the most recent visible message */ Messages.prototype._latestMessageTimestamp = 0; /** @type {BackOffTimer} the backoff timer */ Messages.prototype._backoffTimer = null; /** @type {Messagearea} The messaging area object. */ Messages.prototype.messageArea = null; /** * Initialise the event listeners. * * @private */ Messages.prototype._init = function() { CustomEvents.define(this.messageArea.node, [ CustomEvents.events.activate, CustomEvents.events.up, CustomEvents.events.down, CustomEvents.events.enter, ]); // We have a responsive media query based on height that reduces this size on screens shorter than 670. if ($(window).height() <= 670) { MESSAGES_AREA_DEFAULT_HEIGHT = 400; } AutoRows.init(this.messageArea.node); this.messageArea.onCustomEvent(Events.CONVERSATIONSELECTED, this._viewMessages.bind(this)); this.messageArea.onCustomEvent(Events.SENDMESSAGE, this._viewMessages.bind(this)); this.messageArea.onCustomEvent(Events.CHOOSEMESSAGESTODELETE, this._chooseMessagesToDelete.bind(this)); this.messageArea.onCustomEvent(Events.CANCELDELETEMESSAGES, this._hideDeleteAction.bind(this)); this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.SENDMESSAGE, this._sendMessage.bind(this)); this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.STARTDELETEMESSAGES, this._startDeleting.bind(this)); this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.DELETEMESSAGES, this._deleteMessages.bind(this)); this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.DELETEALLMESSAGES, this._deleteAllMessages.bind(this)); this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.CANCELDELETEMESSAGES, this._triggerCancelMessagesToDelete.bind(this)); this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.MESSAGE, this._toggleMessage.bind(this)); this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.SHOWCONTACTS, this._hideMessagingArea.bind(this)); this.messageArea.onDelegateEvent(CustomEvents.events.up, SELECTORS.MESSAGE, this._selectPreviousMessage.bind(this)); this.messageArea.onDelegateEvent(CustomEvents.events.down, SELECTORS.MESSAGE, this._selectNextMessage.bind(this)); this.messageArea.onDelegateEvent('focus', SELECTORS.SENDMESSAGETEXT, this._setMessaging.bind(this)); this.messageArea.onDelegateEvent('blur', SELECTORS.SENDMESSAGETEXT, this._clearMessaging.bind(this)); $(document).on(AutoRows.events.ROW_CHANGE, this._adjustMessagesAreaHeight.bind(this)); // Check if any messages have been displayed on page load. var messages = this.messageArea.find(SELECTORS.MESSAGES); if (messages.length) { this._addScrollEventListener(messages.find(SELECTORS.MESSAGE).length); this._latestMessageTimestamp = messages.find(SELECTORS.MESSAGE + ':last').data('timecreated'); } // Create a timer to poll the server for new messages. this._backoffTimer = new BackOffTimer(this._loadNewMessages.bind(this), BackOffTimer.getIncrementalCallback(this.messageArea.pollmin * MILLISECONDSINSEC, MILLISECONDSINSEC, this.messageArea.pollmax * MILLISECONDSINSEC, this.messageArea.polltimeout * MILLISECONDSINSEC)); // Start the timer. this._backoffTimer.start(); }; /** * View the message panel. * * @param {Event} event * @param {int} userid * @return {Promise} The promise resolved when the messages have been loaded. * @private */ Messages.prototype._viewMessages = function(event, userid) { // We are viewing another user, or re-loading the panel, so set number of messages displayed to 0. this._numMessagesDisplayed = 0; // Stop the existing timer so we can set up the new user's messages. this._backoffTimer.stop(); // Reset the latest timestamp when we change the messages view. this._latestMessageTimestamp = 0; // Mark all the messages as read. var markMessagesAsRead = Ajax.call([{ methodname: 'core_message_mark_all_messages_as_read', args: { useridto: this.messageArea.getCurrentUserId(), useridfrom: userid } }]); // Keep track of the number of messages received. var numberreceived = 0; // Show loading template. return Templates.render('core/loading', {}).then(function(html, js) { Templates.replaceNodeContents(this.messageArea.find(SELECTORS.MESSAGESAREA), html, js); return markMessagesAsRead[0]; }.bind(this)).then(function() { var conversationnode = this.messageArea.find(SELECTORS.CONVERSATIONS + " " + SELECTORS.CONTACT + "[data-userid='" + userid + "']"); if (conversationnode.hasClass('unread')) { // Remove the class. conversationnode.removeClass('unread'); // Trigger an event letting the notification popover (and whoever else) know. $(document).trigger('messagearea:conversationselected', userid); } return this._getMessages(userid); }.bind(this)).then(function(data) { numberreceived = data.messages.length; // We have the data - lets render the template with it. return Templates.render('core_message/message_area_messages_area', data); }).then(function(html, js) { Templates.replaceNodeContents(this.messageArea.find(SELECTORS.MESSAGESAREA), html, js); this._addScrollEventListener(numberreceived); // Restart the poll timer. this._backoffTimer.restart(); this.messageArea.find(SELECTORS.SENDMESSAGETEXT).focus(); }.bind(this)).fail(Notification.exception); }; /** * Loads messages while scrolling. * * @return {Promise|boolean} The promise resolved when the messages have been loaded. * @private */ Messages.prototype._loadMessages = function() { if (this._isLoadingMessages) { return false; } this._isLoadingMessages = true; // Keep track of the number of messages received. var numberreceived = 0; // Show loading template. return Templates.render('core/loading', {}).then(function(html, js) { Templates.prependNodeContents(this.messageArea.find(SELECTORS.MESSAGES), "<div style='text-align:center'>" + html + "</div>", js); return this._getMessages(this._getUserId()); }.bind(this)).then(function(data) { numberreceived = data.messages.length; // We have the data - lets render the template with it. return Templates.render('core_message/message_area_messages', data); }).then(function(html, js) { // Remove the loading icon. this.messageArea.find(SELECTORS.MESSAGES + " " + SELECTORS.LOADINGICON).remove(); // Check if we got something to do. if (numberreceived > 0) { var newHtml = $('<div>' + html + '</div>'); if (this._hasMatchingBlockTime(this.messageArea.node, newHtml, true)) { this.messageArea.node.find(SELECTORS.BLOCKTIME + ':first').remove(); } // Get height before we add the messages. var oldheight = this.messageArea.find(SELECTORS.MESSAGES)[0].scrollHeight; // Show the new content. Templates.prependNodeContents(this.messageArea.find(SELECTORS.MESSAGES), html, js); // Get height after we add the messages. var newheight = this.messageArea.find(SELECTORS.MESSAGES)[0].scrollHeight; // Make sure scroll bar is at the location before we loaded more messages. this.messageArea.find(SELECTORS.MESSAGES).scrollTop(newheight - oldheight); // Increment the number of messages displayed. this._numMessagesDisplayed += numberreceived; } // Mark that we are no longer busy loading data. this._isLoadingMessages = false; }.bind(this)).fail(Notification.exception); }; /** * Loads and renders messages newer than the most recently seen messages. * * @return {Promise|boolean} The promise resolved when the messages have been loaded. * @private */ Messages.prototype._loadNewMessages = function() { if (this._isLoadingMessages) { return false; } // If we have no user id yet then bail early. if (!this._getUserId()) { return false; } this._isLoadingMessages = true; // Only scroll the message window if the user hasn't scrolled up. var shouldScrollBottom = false; var messages = this.messageArea.find(SELECTORS.MESSAGES); if (messages.length !== 0) { var scrollTop = messages.scrollTop(); var innerHeight = messages.innerHeight(); var scrollHeight = messages[0].scrollHeight; if (scrollTop + innerHeight >= scrollHeight) { shouldScrollBottom = true; } } // Keep track of the number of messages received. return this._getMessages(this._getUserId(), true).then(function(data) { return this._addMessagesToDom(data.messages, shouldScrollBottom); }.bind(this)).always(function() { // Mark that we are no longer busy loading data. this._isLoadingMessages = false; }.bind(this)).fail(Notification.exception); }; /** * Handles returning a list of messages to display. * * @param {int} userid * @param {bool} fromTimestamp Load messages from the latest known timestamp * @return {Promise} The promise resolved when the contact area has been rendered * @private */ Messages.prototype._getMessages = function(userid, fromTimestamp) { var args = { currentuserid: this.messageArea.getCurrentUserId(), otheruserid: userid, limitfrom: this._numMessagesDisplayed, limitnum: this._numMessagesToRetrieve, newest: true }; // If we're trying to load new messages since the message UI was // rendered. Used for ajax polling while user is on the message UI. if (fromTimestamp) { args.timefrom = this._latestMessageTimestamp; // Remove limit and offset. We want all new messages. args.limitfrom = 0; args.limitnum = 0; } // Call the web service to get our data. var promises = Ajax.call([{ methodname: 'core_message_data_for_messagearea_messages', args: args, }]); // Do stuff when we get data back. return promises[0].then(function(data) { var messages = data.messages; // Did we get any new messages? if (messages && messages.length) { var latestMessage = messages[messages.length - 1]; // Update our record of the latest known message for future requests. if (latestMessage.timecreated > this._latestMessageTimestamp) { // Next request should be for the second after the most recent message we've seen. this._latestMessageTimestamp = latestMessage.timecreated + 1; } } return data; }.bind(this)).fail(function(ex) { // Stop the timer if we received an error so that we don't keep spamming the server. this._backoffTimer.stop(); Notification.exception(ex); }.bind(this)); }; /** * Handles sending a message. * * @return {Promise|boolean} The promise resolved once the message has been sent. * @private */ Messages.prototype._sendMessage = function() { var element = this.messageArea.find(SELECTORS.SENDMESSAGETEXT); var text = element.val().trim(); // Do not do anything if it is empty. if (text === '') { return false; } // If we are sending a message, don't do anything, be patient! if (this._isSendingMessage) { return false; } // Ok, mark that we are sending a message. this._isSendingMessage = true; // Call the web service to save our message. var promises = Ajax.call([{ methodname: 'core_message_send_instant_messages', args: { messages: [ { touserid: this._getUserId(), text: text } ] } }]); element.prop('disabled', true); // Update the DOM when we get some data back. return promises[0].then(function(response) { if (response.length < 0) { // Even errors should return valid data. throw new Error('Invalid response'); } if (response[0].errormessage) { throw new Error(response[0].errormessage); } // Fire an event to say the message was sent. this.messageArea.trigger(Events.MESSAGESENT, [this._getUserId(), text]); // Update the messaging area. return this._addLastMessageToDom(); }.bind(this)).then(function() { // Ok, we are no longer sending a message. this._isSendingMessage = false; }.bind(this)).always(function() { element.prop('disabled', false); element.focus(); }).fail(Notification.exception); }; /** * Handles selecting messages to delete. * * @private */ Messages.prototype._chooseMessagesToDelete = function() { this.messageArea.find(SELECTORS.MESSAGESAREA).addClass('editing'); this.messageArea.find(SELECTORS.MESSAGE) .attr('role', 'checkbox') .attr('aria-checked', 'false'); }; /** * Handles deleting messages. * * @private */ Messages.prototype._deleteMessages = function() { var userid = this.messageArea.getCurrentUserId(); var checkboxes = this.messageArea.find(SELECTORS.MESSAGE + "[aria-checked='true']"); var requests = []; var messagestoremove = []; // Go through all the checked checkboxes and prepare them for deletion. checkboxes.each(function(id, element) { var node = $(element); var messageid = node.data('messageid'); var isread = node.data('messageread') ? 1 : 0; messagestoremove.push(node); requests.push({ methodname: 'core_message_delete_message', args: { messageid: messageid, userid: userid, read: isread } }); }); if (requests.length > 0) { $.when(Ajax.call(requests)).then(function() { // Store the last message on the page, and the last message being deleted. var updatemessage = null; var messages = this.messageArea.find(SELECTORS.MESSAGE); var lastmessage = messages.last(); var lastremovedmessage = messagestoremove[messagestoremove.length - 1]; // Remove the messages from the DOM. $.each(messagestoremove, function(key, message) { // Remove the message. message.remove(); }); // If the last message was deleted then we need to provide the new last message. if (lastmessage.data('id') === lastremovedmessage.data('id')) { updatemessage = this.messageArea.find(SELECTORS.MESSAGE).last(); } // Now we have removed all the messages from the DOM lets remove any block times we may need to as well. $.each(messagestoremove, function(key, message) { // First - let's make sure there are no more messages in that time block. var blocktime = message.data('blocktime'); if (this.messageArea.find(SELECTORS.MESSAGE + "[data-blocktime='" + blocktime + "']").length === 0) { this.messageArea.find(SELECTORS.BLOCKTIME + "[data-blocktime='" + blocktime + "']").remove(); } }.bind(this)); // If there are no messages at all, then remove conversation panel. if (this.messageArea.find(SELECTORS.MESSAGE).length === 0) { this.messageArea.find(SELECTORS.CONVERSATIONS + " " + SELECTORS.CONTACT + "[data-userid='" + this._getUserId() + "']").remove(); } // Trigger event letting other modules know messages were deleted. this.messageArea.trigger(Events.MESSAGESDELETED, [this._getUserId(), updatemessage]); }.bind(this)).catch(Notification.exception); } else { // Trigger event letting other modules know messages were deleted. this.messageArea.trigger(Events.MESSAGESDELETED, this._getUserId()); } // Hide the items responsible for deleting messages. this._hideDeleteAction(); }; /** * Handles adding a scrolling event listener. * * @param {int} numberreceived The number of messages received * @private */ Messages.prototype._addScrollEventListener = function(numberreceived) { // Scroll to the bottom. this._scrollBottom(); // Set the number of messages displayed. this._numMessagesDisplayed = numberreceived; // Now enable the ability to infinitely scroll through messages. CustomEvents.define(this.messageArea.find(SELECTORS.MESSAGES), [ CustomEvents.events.scrollTop ]); // Assign the event for scrolling. this.messageArea.onCustomEvent(CustomEvents.events.scrollTop, this._loadMessages.bind(this)); }; /** * Handles deleting a conversation. * * @private */ Messages.prototype._deleteAllMessages = function() { if (this._confirmationModal) { // Just show the existing modal. this._confirmationModal.show(); return; } var stringsPromise = Str.get_strings([ {key: 'confirm'}, {key: 'deleteallconfirm', component: 'message'}, {key: 'delete'} ]); var deleteModalPromise = ModalFactory.create( { type: ModalFactory.types.SAVE_CANCEL }, this.messageArea.find(SELECTORS.DELETEALLMESSAGES) ); $.when(stringsPromise, deleteModalPromise).then(function(s, modal) { modal.setTitle(s[0]); modal.setBody(s[1]); modal.setSaveButtonText(s[2]); this._confirmationModal = modal; // Only delete the conversation if the user agreed in the confirmation modal. modal.getRoot().on(ModalEvents.save, function() { var otherUserId = this._getUserId(); var request = { methodname: 'core_message_delete_conversation', args: { userid: this.messageArea.getCurrentUserId(), otheruserid: otherUserId } }; // Delete the conversation. Ajax.call([request])[0].then(function() { // Clear the message area. this.messageArea.find(SELECTORS.MESSAGESAREA).empty(); // Let the app know a conversation was deleted. this.messageArea.trigger(Events.CONVERSATIONDELETED, otherUserId); this._hideDeleteAction(); }.bind(this)).catch(Notification.exception); }.bind(this)); // Display the confirmation. modal.show(); }.bind(this)).catch(Notification.exception); }; /** * Handles hiding the delete checkboxes and replacing the response area. * * @private */ Messages.prototype._hideDeleteAction = function() { this.messageArea.find(SELECTORS.MESSAGE) .removeAttr('role') .removeAttr('aria-checked'); this.messageArea.find(SELECTORS.MESSAGESAREA).removeClass('editing'); }; /** * Triggers the CANCELDELETEMESSAGES event. * * @private */ Messages.prototype._triggerCancelMessagesToDelete = function() { // Trigger event letting other modules know message deletion was canceled. this.messageArea.trigger(Events.CANCELDELETEMESSAGES); }; /** * Handles adding messages to the DOM. * * @param {array} messages An array of messages to be added to the DOM. * @param {boolean} shouldScrollBottom True will scroll to the bottom of the message window and show the new messages. * @return {Promise} The promise resolved when the messages have been added to the DOM. * @private */ Messages.prototype._addMessagesToDom = function(messages, shouldScrollBottom) { var numberreceived = 0; var messagesArea = this.messageArea.find(SELECTORS.MESSAGES); messages = messages.filter(function(message) { var id = "" + message.id + message.isread; // If the message is already queued to be rendered, remove from the list of messages. if (this._messageQueue[id]) { return false; } // Filter out any messages already rendered. var result = messagesArea.find(SELECTORS.MESSAGE + '[data-id="' + id + '"]'); // Any message we are rendering should go in the messageQueue. if (!result.length) { this._messageQueue[id] = true; } return !result.length; }.bind(this)); numberreceived = messages.length; // We have the data - lets render the template with it. return Templates.render('core_message/message_area_messages', {messages: messages}).then(function(html, js) { // Check if we got something to do. if (numberreceived > 0) { var newHtml = $('<div>' + html + '</div>'); if (this._hasMatchingBlockTime(this.messageArea.node, newHtml, false)) { newHtml.find(SELECTORS.BLOCKTIME + ':first').remove(); } // Show the new content. Templates.appendNodeContents(this.messageArea.find(SELECTORS.MESSAGES), newHtml, js); // Scroll the new message into view. if (shouldScrollBottom) { this._scrollBottom(); } // Increment the number of messages displayed. this._numMessagesDisplayed += numberreceived; // Reset the poll timer because the user may be active. this._backoffTimer.restart(); } }.bind(this)); }; /** * Handles adding the last message to the DOM. * * @return {Promise} The promise resolved when the message has been added to the DOM. * @private */ Messages.prototype._addLastMessageToDom = function() { // Call the web service to return how the message should look. var promises = Ajax.call([{ methodname: 'core_message_data_for_messagearea_get_most_recent_message', args: { currentuserid: this.messageArea.getCurrentUserId(), otheruserid: this._getUserId() } }]); // Add the message. return promises[0].then(function(data) { return this._addMessagesToDom([data], true); }.bind(this)).always(function() { // Empty the response text area.text this.messageArea.find(SELECTORS.SENDMESSAGETEXT).val('').trigger('input'); }.bind(this)).fail(Notification.exception); }; /** * Returns the ID of the other user in the conversation. * * @return {int} The user id * @private */ Messages.prototype._getUserId = function() { return this.messageArea.find(SELECTORS.MESSAGES).data('userid'); }; /** * Scrolls to the bottom of the messages. * * @private */ Messages.prototype._scrollBottom = function() { // Scroll to the bottom. var messages = this.messageArea.find(SELECTORS.MESSAGES); if (messages.length !== 0) { messages.scrollTop(messages[0].scrollHeight); } }; /** * Select the previous message in the list. * * @param {event} e The jquery event * @param {object} data Extra event data * @private */ Messages.prototype._selectPreviousMessage = function(e, data) { var currentMessage = $(e.target).closest(SELECTORS.MESSAGE); do { currentMessage = currentMessage.prev(); } while (currentMessage.length && !currentMessage.is(SELECTORS.MESSAGE)); currentMessage.focus(); data.originalEvent.preventDefault(); data.originalEvent.stopPropagation(); }; /** * Select the next message in the list. * * @param {event} e The jquery event * @param {object} data Extra event data * @private */ Messages.prototype._selectNextMessage = function(e, data) { var currentMessage = $(e.target).closest(SELECTORS.MESSAGE); do { currentMessage = currentMessage.next(); } while (currentMessage.length && !currentMessage.is(SELECTORS.MESSAGE)); currentMessage.focus(); data.originalEvent.preventDefault(); data.originalEvent.stopPropagation(); }; /** * Flag the response area as messaging. * * @param {event} e The jquery event * @private */ Messages.prototype._setMessaging = function(e) { $(e.target).closest(SELECTORS.MESSAGERESPONSE).addClass('messaging'); }; /** * Clear the response area as messaging flag. * * @param {event} e The jquery event * @private */ Messages.prototype._clearMessaging = function(e) { $(e.target).closest(SELECTORS.MESSAGERESPONSE).removeClass('messaging'); }; /** * Turn on delete message mode. * * @param {event} e The jquery event * @private */ Messages.prototype._startDeleting = function(e) { var actions = new Actions(this.messageArea); actions.chooseMessagesToDelete(); e.preventDefault(); }; /** * Check if the message area is in editing mode. * * @return {bool} * @private */ Messages.prototype._isEditing = function() { return this.messageArea.find(SELECTORS.MESSAGESAREA).hasClass('editing'); }; /** * Check or uncheck the message if the message area is in editing mode. * * @param {event} e The jquery event * @private */ Messages.prototype._toggleMessage = function(e) { if (!this._isEditing()) { return; } var message = $(e.target).closest(SELECTORS.MESSAGE); if (message.attr('aria-checked') === 'true') { message.attr('aria-checked', 'false'); } else { message.attr('aria-checked', 'true'); } }; /** * Adjust the height of the messages area to match the changed height of * the response area. * * @private */ Messages.prototype._adjustMessagesAreaHeight = function() { var messagesArea = this.messageArea.find(SELECTORS.MESSAGES); var messagesResponse = this.messageArea.find(SELECTORS.MESSAGERESPONSE); var currentMessageResponseHeight = messagesResponse.outerHeight(); var diffResponseHeight = currentMessageResponseHeight - MESSAGES_RESPONSE_DEFAULT_HEIGHT; var newMessagesAreaHeight = MESSAGES_AREA_DEFAULT_HEIGHT - diffResponseHeight; messagesArea.outerHeight(newMessagesAreaHeight); }; /** * Handle the event that triggers sending a message from the messages area. * * @param {event} e The jquery event * @param {object} data Additional event data * @private */ Messages.prototype._sendMessageHandler = function(e, data) { data.originalEvent.preventDefault(); this._sendMessage(); }; /** * Hide the messaging area. This only applies on smaller screen resolutions. * * @private */ Messages.prototype._hideMessagingArea = function() { this.messageArea.find(SELECTORS.MESSAGINGAREA) .removeClass('show-messages') .addClass('hide-messages'); }; /** * Checks if a day separator needs to be removed. * * Example - scrolling up and loading previous messages that belong to the * same day as the last message that was previously shown, meaning we can * remove the original separator. * * @param {jQuery} domHtml The HTML in the DOM. * @param {jQuery} newHtml The HTML to compare to the DOM * @param {boolean} loadingPreviousMessages Are we loading previous messages? * @return {boolean} * @private */ Messages.prototype._hasMatchingBlockTime = function(domHtml, newHtml, loadingPreviousMessages) { var blockTime, blockTimePos, newBlockTime, newBlockTimePos; if (loadingPreviousMessages) { blockTimePos = ':first'; newBlockTimePos = ':last'; } else { blockTimePos = ':last'; newBlockTimePos = ':first'; } blockTime = domHtml.find(SELECTORS.BLOCKTIME + blockTimePos); newBlockTime = newHtml.find(SELECTORS.BLOCKTIME + newBlockTimePos); if (blockTime.length && newBlockTime.length) { return blockTime.data('blocktime') == newBlockTime.data('blocktime'); } return false; }; return Messages; } );