MDL-61359 message: Updated sender contact text to text node content
[moodle.git] / message / amd / src / message_area_contacts.js
CommitLineData
e237d2bd
MN
1// This file is part of Moodle - http://moodle.org/
2//
3// Moodle is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, either version 3 of the License, or
6// (at your option) any later version.
7//
8// Moodle is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
15
16/**
17 * This module handles the contacts area of the messaging area.
18 *
6b2657d9 19 * @module core_message/message_area_contacts
e237d2bd
MN
20 * @package core_message
21 * @copyright 2016 Mark Nelson <markn@moodle.com>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
c476de18
MN
24define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/custom_interaction_events', 'core/str',
25 'core_message/message_area_events'],
26 function($, Ajax, Templates, Notification, CustomEvents, Str, Events) {
27
28 /** @type {Object} The list of selectors for the message area. */
29 var SELECTORS = {
30 CONTACT: "[data-region='contact']",
31 CONTACTICONBLOCKED: "[data-region='contact-icon-blocked']",
32 CONTACTS: "[data-region='contacts'][data-region-content='contacts']",
33 CONTACTSAREA: "[data-region='contacts-area']",
34 CONVERSATIONS: "[data-region='contacts'][data-region-content='conversations']",
d14207fd 35 COURSE: "[data-region='course']",
c476de18
MN
36 LASTMESSAGETEXT: "[data-region='last-message-text']",
37 LASTMESSAGEUSER: "[data-region='last-message-user']",
38 LOADINGICON: '.loading-icon',
39 MESSAGETEXT: "[data-region='message-text']",
40 MESSAGINGAREA: "[data-region='messaging-area']",
41 NOCONTACTS: "[data-region=no-contacts]",
42 SEARCHBOX: "[data-region='search-box']",
43 SEARCHRESULTSAREA: "[data-region='search-results-area']",
44 SEARCHTEXTAREA: "[data-region='search-text-area']",
45 SELECTEDVIEWCONVERSATION: "[data-action='view-contact-msg'].selected",
46 SELECTEDVIEWPROFILE: "[data-action='view-contact-profile'].selected",
47 SHOWMESSAGES: "[data-action='show-messages']",
48 VIEWCONVERSATION: "[data-action='view-contact-msg']",
49 VIEWPROFILE: "[data-action='view-contact-profile']"
50 };
e237d2bd
MN
51
52 /**
53 * Contacts class.
54 *
55 * @param {Messagearea} messageArea The messaging area object.
56 */
57 function Contacts(messageArea) {
58 this.messageArea = messageArea;
59 this._init();
60 }
61
9661810e
MN
62 /** @type {Boolean} checks if we are currently loading conversations */
63 Contacts.prototype._isLoadingConversations = false;
64
65 /** @type {Boolean} checks if we are currently loading contacts */
66 Contacts.prototype._isLoadingContacts = false;
67
68 /** @type {int} the number of contacts displayed */
69 Contacts.prototype._numContactsDisplayed = 0;
70
71 /** @type {int} the number of contacts to retrieve */
72 Contacts.prototype._numContactsToRetrieve = 20;
73
74 /** @type {int} the number of conversations displayed */
75 Contacts.prototype._numConversationsDisplayed = 0;
76
77 /** @type {int} the number of conversations to retrieve */
78 Contacts.prototype._numConversationsToRetrieve = 20;
79
80 /** @type {int} the number of chars of the message to show */
81 Contacts.prototype._messageLength = 60;
82
e237d2bd
MN
83 /** @type {Messagearea} The messaging area object. */
84 Contacts.prototype.messageArea = null;
85
86 /**
87 * Initialise the event listeners.
88 *
89 * @private
90 */
91 Contacts.prototype._init = function() {
c476de18
MN
92 CustomEvents.define(this.messageArea.node, [
93 CustomEvents.events.activate,
94 CustomEvents.events.down,
95 CustomEvents.events.up,
fbdcd499
RW
96 ]);
97
c476de18
MN
98 this.messageArea.onCustomEvent(Events.MESSAGESEARCHCANCELED, this._viewConversations.bind(this));
99 this.messageArea.onCustomEvent(Events.USERSSEARCHCANCELED, this._viewContacts.bind(this));
100 this.messageArea.onCustomEvent(Events.CONTACTSSELECTED, this._viewContacts.bind(this));
101 this.messageArea.onCustomEvent(Events.CONVERSATIONDELETED, this._deleteConversation.bind(this));
102 this.messageArea.onCustomEvent(Events.CONVERSATIONSSELECTED, this._viewConversations.bind(this));
103 this.messageArea.onCustomEvent(Events.CONTACTSSELECTED, this._viewContacts.bind(this));
104 this.messageArea.onCustomEvent(Events.MESSAGESDELETED, this._updateLastMessage.bind(this));
105 this.messageArea.onCustomEvent(Events.MESSAGESENT, this._handleMessageSent.bind(this));
106 this.messageArea.onCustomEvent(Events.CONTACTREMOVED, function(e, userid) {
107 this._removeContact(SELECTORS.CONTACTS, userid);
9661810e 108 }.bind(this));
c476de18 109 this.messageArea.onCustomEvent(Events.CONTACTADDED, function(e, userid) {
9661810e
MN
110 this._addContact(userid);
111 }.bind(this));
c476de18 112 this.messageArea.onCustomEvent(Events.CONTACTBLOCKED, function(e, userid) {
dd0c1403
MN
113 this._blockContact(userid);
114 }.bind(this));
c476de18 115 this.messageArea.onCustomEvent(Events.CONTACTUNBLOCKED, function(e, userid) {
dd0c1403
MN
116 this._unblockContact(userid);
117 }.bind(this));
c476de18 118 this.messageArea.onCustomEvent(Events.CHOOSEMESSAGESTODELETE,
99c7f0a7 119 this._startDeleting.bind(this));
c476de18 120 this.messageArea.onCustomEvent(Events.CANCELDELETEMESSAGES,
99c7f0a7 121 this._stopDeleting.bind(this));
c476de18 122 this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.VIEWCONVERSATION,
99c7f0a7 123 this._viewConversation.bind(this));
c476de18 124 this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.VIEWPROFILE,
fbdcd499 125 this._viewContact.bind(this));
c476de18 126 this.messageArea.onDelegateEvent(CustomEvents.events.activate, SELECTORS.SHOWMESSAGES,
99c7f0a7 127 this._showMessagingArea.bind(this));
fbdcd499 128
c476de18 129 this.messageArea.onDelegateEvent(CustomEvents.events.up, SELECTORS.CONTACT,
2be15a66 130 this._selectPreviousContact.bind(this));
c476de18 131 this.messageArea.onDelegateEvent(CustomEvents.events.down, SELECTORS.CONTACT,
2be15a66 132 this._selectNextContact.bind(this));
c476de18 133 this.messageArea.onDelegateEvent(CustomEvents.events.up, SELECTORS.VIEWCONVERSATION,
2be15a66 134 this._selectPreviousConversation.bind(this));
c476de18 135 this.messageArea.onDelegateEvent(CustomEvents.events.down, SELECTORS.VIEWCONVERSATION,
2be15a66 136 this._selectNextConversation.bind(this));
9661810e 137
d14207fd
AG
138 this.messageArea.onDelegateEvent(CustomEvents.events.up, SELECTORS.COURSE, this._selectPreviousCourse.bind());
139 this.messageArea.onDelegateEvent(CustomEvents.events.down, SELECTORS.COURSE, this._selectNextCourse.bind());
140
c476de18
MN
141 this.messageArea.onDelegateEvent('focus', SELECTORS.SEARCHBOX, this._setSearching.bind(this));
142 this.messageArea.onDelegateEvent('blur', SELECTORS.SEARCHBOX, this._clearSearching.bind(this));
4d0fa501 143
9661810e 144 // Now enable the ability to infinitely scroll through conversations and contacts.
c476de18
MN
145 CustomEvents.define(this.messageArea.find(SELECTORS.CONVERSATIONS), [
146 CustomEvents.events.scrollBottom
9661810e 147 ]);
c476de18
MN
148 CustomEvents.define(this.messageArea.find(SELECTORS.CONTACTS), [
149 CustomEvents.events.scrollBottom
9661810e 150 ]);
c476de18 151 this.messageArea.onDelegateEvent(CustomEvents.events.scrollBottom, SELECTORS.CONVERSATIONS,
9661810e 152 this._loadConversations.bind(this));
c476de18 153 this.messageArea.onDelegateEvent(CustomEvents.events.scrollBottom, SELECTORS.CONTACTS,
9661810e
MN
154 this._loadContacts.bind(this));
155
81517613
AG
156 if (!this.messageArea.showContactsFirst()) {
157 // Set the initial number of conversations to retrieve. Otherwise it will display no conversations.
158 this._numConversationsDisplayed = 20;
159 }
e237d2bd
MN
160 };
161
99c7f0a7
RW
162 /**
163 * Turn on deleting.
164 *
165 * @private
166 */
167 Contacts.prototype._startDeleting = function() {
c476de18 168 this.messageArea.find(SELECTORS.CONTACTSAREA).addClass('editing');
99c7f0a7
RW
169 };
170
171 /**
172 * Turn off deleting.
173 *
174 * @private
175 */
176 Contacts.prototype._stopDeleting = function() {
c476de18 177 this.messageArea.find(SELECTORS.CONTACTSAREA).removeClass('editing');
99c7f0a7
RW
178 };
179
e237d2bd
MN
180 /**
181 * Handles viewing the list of conversations.
182 *
183 * @private
184 */
185 Contacts.prototype._viewConversations = function() {
4d1b76ee
MN
186 // If conversations is empty then try load some.
187 if (this._numConversationsDisplayed === 0) {
188 this._loadConversations();
189 }
190
c476de18
MN
191 this.messageArea.find(SELECTORS.CONTACTS).hide();
192 this.messageArea.find(SELECTORS.CONVERSATIONS).show();
9661810e
MN
193 };
194
195 /**
196 * Handles viewing the list of contacts.
197 *
198 * @private
199 */
200 Contacts.prototype._viewContacts = function() {
4d1b76ee 201 // If contacts is empty then try load some.
9661810e
MN
202 if (this._numContactsDisplayed === 0) {
203 this._loadContacts();
dec0cd99
MN
204 }
205
c476de18
MN
206 this.messageArea.find(SELECTORS.CONVERSATIONS).hide();
207 this.messageArea.find(SELECTORS.CONTACTS).show();
e237d2bd
MN
208 };
209
210 /**
9661810e 211 * Handles when a message is sent.
e237d2bd
MN
212 *
213 * @param {Event} event The message sent event
214 * @param {int} userid The id of the user who the message was sent to
9661810e
MN
215 * @param {String} text The message text
216 * @private
217 */
218 Contacts.prototype._handleMessageSent = function(event, userid, text) {
219 // Switch to viewing the conversations.
220 this._viewConversations();
9661810e 221 // Get the user node.
c476de18 222 var user = this._getUserNode(SELECTORS.CONVERSATIONS, userid);
bf4c393c 223 // If the user has not been loaded yet, let's copy the element from contact or search panel to the conversation panel.
9661810e
MN
224 if (user.length === 0) {
225 // Let's clone the data on the contact page.
c476de18 226 var usercontact = this._getUserNode(SELECTORS.CONTACTS, userid);
bf4c393c
MN
227 if (usercontact.length === 0) {
228 // No luck, maybe we sent the message to a user we searched for - check search page.
c476de18 229 usercontact = this._getUserNode(SELECTORS.SEARCHRESULTSAREA, userid);
bf4c393c
MN
230 }
231 if (usercontact.length == 0) {
232 // Can't do much.
233 return;
234 }
9661810e
MN
235 user = usercontact.clone();
236 // Change the data action attribute.
237 user.attr('data-action', 'view-contact-msg');
4d1b76ee 238 // Remove the 'no conversations' message.
c476de18
MN
239 this.messageArea.find(SELECTORS.CONVERSATIONS + " " +
240 SELECTORS.NOCONTACTS).remove();
9661810e
MN
241 // Increment the number of conversations displayed.
242 this._numConversationsDisplayed++;
243 }
244 // Move the contact to the top of the list.
c476de18 245 user.prependTo(this.messageArea.find(SELECTORS.CONVERSATIONS));
9661810e 246 // Scroll to the top.
c476de18 247 this.messageArea.find(SELECTORS.CONVERSATIONS).scrollTop(0);
89a70ba1
MN
248 // Get the new text to show.
249 this._updateContactText(user, text, true);
9661810e 250 // Ensure user is selected.
cd03b8d7 251 this._setSelectedUser("[data-userid='" + userid + "']");
9661810e
MN
252 };
253
254 /**
255 * Handles loading conversations.
256 *
7b55aaa1 257 * @return {Promise|boolean} The promise resolved when the contact area has been rendered,
e237d2bd
MN
258 * @private
259 */
9661810e 260 Contacts.prototype._loadConversations = function() {
9661810e 261 if (this._isLoadingConversations) {
7b55aaa1 262 return false;
9661810e
MN
263 }
264
265 // Tell the user we are loading items.
266 this._isLoadingConversations = true;
267
268 // Keep track of the number of contacts
269 var numberreceived = 0;
270 // Add loading icon to the end of the list.
c476de18 271 return Templates.render('core/loading', {}).then(function(html, js) {
4d1b76ee 272 if (this._numConversationsDisplayed) {
c476de18 273 Templates.appendNodeContents(this.messageArea.find(SELECTORS.CONVERSATIONS),
4d1b76ee
MN
274 "<div style='text-align:center'>" + html + "</div>", js);
275 } else { // No conversations, just replace contents.
c476de18 276 Templates.replaceNodeContents(this.messageArea.find(SELECTORS.CONVERSATIONS),
4d1b76ee
MN
277 "<div style='text-align:center'>" + html + "</div>", js);
278 }
9661810e
MN
279 return this._getItems('core_message_data_for_messagearea_conversations',
280 this._numConversationsDisplayed, this._numConversationsToRetrieve);
281 }.bind(this)).then(function(data) {
282 numberreceived = data.contacts.length;
de55cb1b 283 data.isconversation = true;
c476de18 284 return Templates.render('core_message/message_area_contacts', data);
9661810e
MN
285 }).then(function(html, js) {
286 // Remove the loading icon.
c476de18
MN
287 this.messageArea.find(SELECTORS.CONVERSATIONS + " " +
288 SELECTORS.LOADINGICON).remove();
9661810e
MN
289 // Only append data if we got data back.
290 if (numberreceived > 0) {
291 // Show the new content.
c476de18 292 Templates.appendNodeContents(this.messageArea.find(SELECTORS.CONVERSATIONS), html, js);
82073cb1
MN
293 // Increment the number of conversations displayed. We increment by the number of conversations we
294 // asked to retrieve not by the number that was actually retrieved, see MDL-55870.
295 this._numConversationsDisplayed += this._numConversationsToRetrieve;
4d1b76ee
MN
296 } else if (!this._numConversationsDisplayed) {
297 // If we didn't receive any contacts and there are currently none, then we want to show a message.
c476de18 298 Templates.replaceNodeContents(this.messageArea.find(SELECTORS.CONVERSATIONS), html, js);
9661810e
MN
299 }
300 // Mark that we are no longer busy loading data.
301 this._isLoadingConversations = false;
c476de18 302 }.bind(this)).fail(Notification.exception);
e237d2bd
MN
303 };
304
305 /**
9661810e 306 * Handles loading contacts.
e237d2bd 307 *
7b55aaa1 308 * @return {Promise|boolean} The promise resolved when the contact area has been rendered
e237d2bd
MN
309 * @private
310 */
9661810e 311 Contacts.prototype._loadContacts = function() {
9661810e 312 if (this._isLoadingContacts) {
7b55aaa1 313 return false;
dec0cd99
MN
314 }
315
9661810e
MN
316 // Tell the user we are loading items.
317 this._isLoadingContacts = true;
318
319 // Keep track of the number of contacts
320 var numberreceived = 0;
321 // Add loading icon to the end of the list.
c476de18 322 return Templates.render('core/loading', {}).then(function(html, js) {
4d1b76ee 323 if (this._numContactsDisplayed) {
c476de18 324 Templates.appendNodeContents(this.messageArea.find(SELECTORS.CONTACTS),
4d1b76ee
MN
325 "<div style='text-align:center'>" + html + "</div>", js);
326 } else { // No contacts, just replace contents.
c476de18 327 Templates.replaceNodeContents(this.messageArea.find(SELECTORS.CONTACTS),
4d1b76ee
MN
328 "<div style='text-align:center'>" + html + "</div>", js);
329 }
9661810e
MN
330 return this._getItems('core_message_data_for_messagearea_contacts',
331 this._numContactsDisplayed, this._numContactsToRetrieve);
332 }.bind(this)).then(function(data) {
333 numberreceived = data.contacts.length;
de55cb1b 334 data.isconversation = false;
c476de18 335 return Templates.render('core_message/message_area_contacts', data);
9661810e
MN
336 }).then(function(html, js) {
337 // Remove the loading icon.
c476de18
MN
338 this.messageArea.find(SELECTORS.CONTACTS + " " +
339 SELECTORS.LOADINGICON).remove();
9661810e
MN
340 // Only append data if we got data back.
341 if (numberreceived > 0) {
342 // Show the new content.
c476de18 343 Templates.appendNodeContents(this.messageArea.find(SELECTORS.CONTACTS), html, js);
9661810e
MN
344 // Increment the number of contacts displayed.
345 this._numContactsDisplayed += numberreceived;
4d1b76ee
MN
346 } else if (!this._numContactsDisplayed) {
347 // If we didn't receive any contacts and there are currently none, then we want to show a message.
c476de18 348 Templates.replaceNodeContents(this.messageArea.find(SELECTORS.CONTACTS), html, js);
9661810e
MN
349 }
350 // Mark that we are no longer busy loading data.
351 this._isLoadingContacts = false;
c476de18 352 }.bind(this)).fail(Notification.exception);
e237d2bd
MN
353 };
354
355 /**
356 * Handles viewing a particular conversation.
357 *
358 * @param {Event} event
359 * @private
360 */
361 Contacts.prototype._viewConversation = function(event) {
c96717fc
MN
362 // Cancel any deletion of messages we may have.
363 this.messageArea.trigger(Events.CANCELDELETEMESSAGES);
cd03b8d7 364
99c7f0a7
RW
365 var userid = $(event.currentTarget).data('userid');
366 var messageid = $(event.currentTarget).data('messageid');
367 var selector = "[data-userid='" + userid + "']";
368 // If we have a specific message id then we did a search and the contact may appear in multiple
369 // places - we don't want to highlight them all.
370 if (messageid) {
371 selector = "[data-messageid='" + messageid + "']";
dec0cd99 372 }
99c7f0a7
RW
373
374 this._setSelectedUser(selector);
c476de18 375 this.messageArea.trigger(Events.CONVERSATIONSELECTED, userid);
99c7f0a7 376 // Don't highlight the contact because the message region has changed.
c476de18 377 this.messageArea.find(SELECTORS.SELECTEDVIEWPROFILE).removeClass('selected');
99c7f0a7 378 this._showMessagingArea();
e237d2bd
MN
379 };
380
381 /**
382 * Handles viewing a particular contact.
383 *
384 * @param {Event} event
385 * @private
386 */
387 Contacts.prototype._viewContact = function(event) {
c96717fc
MN
388 // Cancel any deletion of messages we may have.
389 this.messageArea.trigger(Events.CANCELDELETEMESSAGES);
390
391 var userid = $(event.currentTarget).data('userid');
392 this._setSelectedUser("[data-userid='" + userid + "']");
393 this.messageArea.trigger(Events.CONTACTSELECTED, userid);
394 // Don't highlight the conversation because the message region has changed.
395 this.messageArea.find(SELECTORS.SELECTEDVIEWCONVERSATION).removeClass('selected');
396 this._showMessagingArea();
e237d2bd
MN
397 };
398
399 /**
9661810e 400 * Handles returning a list of items to display.
e237d2bd
MN
401 *
402 * @param {String} webservice The web service to call
9661810e
MN
403 * @param {int} limitfrom
404 * @param {int} limitnum
7b55aaa1 405 * @return {Promise} The promise resolved when the contact area has been rendered
e237d2bd
MN
406 * @private
407 */
9661810e 408 Contacts.prototype._getItems = function(webservice, limitfrom, limitnum) {
e237d2bd 409 // Call the web service to return the data we want to view.
c476de18 410 var promises = Ajax.call([{
e237d2bd
MN
411 methodname: webservice,
412 args: {
9661810e
MN
413 userid: this.messageArea.getCurrentUserId(),
414 limitfrom: limitfrom,
415 limitnum: limitnum
e237d2bd
MN
416 }
417 }]);
418
9661810e 419 return promises[0];
e237d2bd
MN
420 };
421
dec0cd99 422 /**
99c7f0a7 423 * Handles deleting a conversation.
dec0cd99 424 *
7b55aaa1
MN
425 * @param {Event} event
426 * @param {int} userid The user id belonging to the messages we are deleting.
dec0cd99
MN
427 * @private
428 */
99c7f0a7
RW
429 Contacts.prototype._deleteConversation = function(event, userid) {
430 // Remove the conversation.
c476de18 431 this._removeContact(SELECTORS.CONVERSATIONS, userid);
99c7f0a7
RW
432 this._numConversationsDisplayed--;
433 this._hideMessagingArea();
434 // Now we have done all the deletion we can set the flag back to false.
435 this._stopDeleting();
dec0cd99
MN
436 };
437
438 /**
99c7f0a7 439 * Handles updating the last message in the contact.
dec0cd99 440 *
7b55aaa1
MN
441 * @param {Event} event
442 * @param {int} userid The user id belonging to the messages we are deleting
443 * @param {jQuery|null} updatemessage The message we need to update the contact panel with
dec0cd99
MN
444 * @private
445 */
99c7f0a7 446 Contacts.prototype._updateLastMessage = function(event, userid, updatemessage) {
9661810e 447 // Check if the last message needs updating.
89a70ba1 448 if (updatemessage) {
c476de18
MN
449 var user = this._getUserNode(SELECTORS.CONVERSATIONS, userid);
450 var updatemessagetext = updatemessage.find(SELECTORS.MESSAGETEXT).text().trim();
89a70ba1
MN
451 var sentbyuser = false;
452 if (updatemessage.data('useridto') == userid) {
453 // Must have been sent by the currently logged in user.
454 sentbyuser = true;
455 }
456
457 this._updateContactText(user, updatemessagetext, sentbyuser);
9661810e
MN
458 }
459
460 // Now we have done all the deletion we can set the flag back to false.
99c7f0a7 461 this._stopDeleting();
9661810e 462 };
dec0cd99 463
9661810e
MN
464 /**
465 * Handles adding a contact to the list.
466 *
9661810e
MN
467 * @private
468 */
4d1b76ee 469 Contacts.prototype._addContact = function() {
c476de18 470 this.messageArea.find(SELECTORS.CONTACTS).empty();
4d1b76ee
MN
471 this._numContactsDisplayed = 0;
472 this._loadContacts();
dec0cd99
MN
473 };
474
bf4c393c 475 /**
dd0c1403 476 * Handles removing a contact from the list.
bf4c393c
MN
477 *
478 * @param {String} selector
479 * @param {int} userid
480 * @private
481 */
dd0c1403
MN
482 Contacts.prototype._removeContact = function(selector, userid) {
483 this._getUserNode(selector, userid).remove();
484 this._numContactsDisplayed--;
bf4c393c
MN
485 };
486
e237d2bd 487 /**
dd0c1403
MN
488 * Handles marking a contact as blocked on the list.
489 *
490 * @param {int} userid
491 * @private
492 */
493 Contacts.prototype._blockContact = function(userid) {
c476de18
MN
494 var user = this._getUserNode(SELECTORS.CONTACTS, userid);
495 user.find(SELECTORS.CONTACTICONBLOCKED).removeClass('hidden');
dd0c1403 496
c476de18
MN
497 user = this._getUserNode(SELECTORS.CONVERSATIONS, userid);
498 user.find(SELECTORS.CONTACTICONBLOCKED).removeClass('hidden');
dd0c1403 499
c476de18
MN
500 user = this._getUserNode(SELECTORS.SEARCHRESULTSAREA, userid);
501 user.find(SELECTORS.CONTACTICONBLOCKED).removeClass('hidden');
dd0c1403
MN
502 };
503
504 /**
505 * Handles marking a contact as unblocked on the list.
506 *
507 * @param {int} userid
508 * @private
509 */
510 Contacts.prototype._unblockContact = function(userid) {
c476de18
MN
511 var user = this._getUserNode(SELECTORS.CONTACTS, userid);
512 user.find(SELECTORS.CONTACTICONBLOCKED).addClass('hidden');
dd0c1403 513
c476de18
MN
514 user = this._getUserNode(SELECTORS.CONVERSATIONS, userid);
515 user.find(SELECTORS.CONTACTICONBLOCKED).addClass('hidden');
dd0c1403 516
c476de18
MN
517 user = this._getUserNode(SELECTORS.SEARCHRESULTSAREA, userid);
518 user.find(SELECTORS.CONTACTICONBLOCKED).addClass('hidden');
dd0c1403
MN
519 };
520
521 /**
522 * Handles retrieving a user node from a list.
e237d2bd 523 *
9661810e 524 * @param {String} selector
e237d2bd 525 * @param {int} userid
7b55aaa1 526 * @return {jQuery} The user node
e237d2bd
MN
527 * @private
528 */
dd0c1403 529 Contacts.prototype._getUserNode = function(selector, userid) {
c476de18 530 return this.messageArea.find(selector + " " + SELECTORS.CONTACT +
dd0c1403 531 "[data-userid='" + userid + "']");
e237d2bd
MN
532 };
533
534 /**
535 * Handles selecting a contact in the list.
536 *
cd03b8d7 537 * @param {String} selector
e237d2bd
MN
538 * @private
539 */
cd03b8d7 540 Contacts.prototype._setSelectedUser = function(selector) {
e237d2bd 541 // Remove the 'selected' class from any other contact.
c476de18 542 this.messageArea.find(SELECTORS.CONTACT).removeClass('selected');
d14207fd 543 this.messageArea.find(SELECTORS.CONTACT).attr('aria-pressed', false);
e237d2bd 544 // Set the tab for the user to selected.
c476de18 545 this.messageArea.find(SELECTORS.CONTACT + selector).addClass('selected');
d14207fd 546 this.messageArea.find(SELECTORS.CONTACT + selector).attr('aria-pressed', true);
e237d2bd
MN
547 };
548
dec0cd99 549 /**
9661810e 550 * Converts a text message into the text that should be stored in the contact list
dec0cd99 551 *
9661810e 552 * @param {String} text
7b55aaa1 553 * @return {String} The altered text
dec0cd99 554 */
9661810e
MN
555 Contacts.prototype._getContactText = function(text) {
556 if (text.length > this._messageLength) {
557 text = text.substr(0, this._messageLength - 3);
558 text += '...';
dec0cd99
MN
559 }
560
6924e32e
MH
561 // Text node prevents script injection through HTML entities.
562 return document.createTextNode(text);
dec0cd99
MN
563 };
564
89a70ba1
MN
565 /**
566 * Handles updating the contact text.
567 *
568 * @param {jQuery} user The user to update
569 * @param {String} text The text to update the contact with
570 * @param {Boolean} sentbyuser Was it sent by the currently logged in user?
571 * @private
572 */
573 Contacts.prototype._updateContactText = function(user, text, sentbyuser) {
574 // Get the text we will display on the contact panel.
575 text = this._getContactText(text);
576 if (sentbyuser) {
7b55aaa1 577 Str.get_string('you', 'message').done(function(string) {
89a70ba1 578 // Ensure we display that the message is from this user.
c476de18
MN
579 user.find(SELECTORS.LASTMESSAGEUSER).empty().append(string);
580 }).always(function() {
581 user.find(SELECTORS.LASTMESSAGETEXT).empty().append(text);
582 });
89a70ba1 583 } else {
c476de18
MN
584 user.find(SELECTORS.LASTMESSAGEUSER).empty();
585 user.find(SELECTORS.LASTMESSAGETEXT).empty().append(text);
89a70ba1
MN
586 }
587 };
588
fbdcd499
RW
589 /**
590 * Shifts focus to the next contact in the list.
591 *
592 * @param {event} e The jquery event
593 * @param {object} data Additional event data
594 */
595 Contacts.prototype._selectNextContact = function(e, data) {
c476de18 596 var contact = $(e.target).closest(SELECTORS.CONTACT);
fbdcd499
RW
597 var next = contact.next();
598 next.focus();
599
600 data.originalEvent.preventDefault();
601 data.originalEvent.stopPropagation();
602 };
603
604 /**
605 * Shifts focus to the previous contact in the list.
606 *
607 * @param {event} e The jquery event
608 * @param {object} data Additional event data
609 */
610 Contacts.prototype._selectPreviousContact = function(e, data) {
c476de18 611 var contact = $(e.target).closest(SELECTORS.CONTACT);
fbdcd499
RW
612 var previous = contact.prev();
613 previous.focus();
614
615 data.originalEvent.preventDefault();
616 data.originalEvent.stopPropagation();
617 };
618
d14207fd
AG
619 /**
620 * Shifts focus to the next course in the list.
621 *
622 * @param {event} e The jquery event
623 * @param {object} data Additional event data
624 */
625 Contacts.prototype._selectNextCourse = function(e, data) {
626 var course = $(e.target).closest(SELECTORS.COURSE);
627 course.next().focus();
628
629 data.originalEvent.preventDefault();
630 data.originalEvent.stopPropagation();
631 };
632
633 /**
634 * Shifts focus to the previous course in the list.
635 *
636 * @param {event} e The jquery event
637 * @param {object} data Additional event data
638 */
639 Contacts.prototype._selectPreviousCourse = function(e, data) {
640 var course = $(e.target).closest(SELECTORS.COURSE);
641 course.prev().focus();
642
643 data.originalEvent.preventDefault();
644 data.originalEvent.stopPropagation();
645 };
646
fbdcd499
RW
647 /**
648 * Shifts focus to the next conversation in the list.
649 *
650 * @param {event} e The jquery event
651 * @param {object} data Additional event data
652 */
653 Contacts.prototype._selectNextConversation = function(e, data) {
c476de18 654 var conversation = $(e.target).closest(SELECTORS.VIEWCONVERSATION);
fbdcd499
RW
655 var next = conversation.next();
656 next.focus();
657
658 data.originalEvent.preventDefault();
659 data.originalEvent.stopPropagation();
660 };
661
662 /**
663 * Shifts focus to the previous conversation in the list.
664 *
665 * @param {event} e The jquery event
666 * @param {object} data Additional event data
667 */
668 Contacts.prototype._selectPreviousConversation = function(e, data) {
c476de18 669 var conversation = $(e.target).closest(SELECTORS.VIEWCONVERSATION);
fbdcd499
RW
670 var previous = conversation.prev();
671 previous.focus();
672
673 data.originalEvent.preventDefault();
674 data.originalEvent.stopPropagation();
675 };
676
4d0fa501
RW
677 /**
678 * Flags the search area as seaching.
679 */
680 Contacts.prototype._setSearching = function() {
c476de18 681 $(SELECTORS.SEARCHTEXTAREA).addClass('searching');
4d0fa501
RW
682 };
683
684 /**
685 * Flags the search area as seaching.
686 */
687 Contacts.prototype._clearSearching = function() {
c476de18 688 $(SELECTORS.SEARCHTEXTAREA).removeClass('searching');
4d0fa501
RW
689 };
690
691 /**
99c7f0a7 692 * Make the messaging area visible.
4d0fa501 693 */
99c7f0a7 694 Contacts.prototype._showMessagingArea = function() {
c476de18 695 this.messageArea.find(SELECTORS.MESSAGINGAREA)
99c7f0a7
RW
696 .removeClass('hide-messages')
697 .addClass('show-messages');
4d0fa501
RW
698 };
699
700 /**
99c7f0a7 701 * Hide the messaging area.
4d0fa501 702 */
99c7f0a7 703 Contacts.prototype._hideMessagingArea = function() {
c476de18 704 this.messageArea.find(SELECTORS.MESSAGINGAREA)
99c7f0a7
RW
705 .removeClass('show-messages')
706 .addClass('hide-messages');
4d0fa501
RW
707 };
708
e237d2bd
MN
709 return Contacts;
710 }
fbdcd499 711);