Index: lams_learning/web/includes/javascript/presence.js
===================================================================
diff -u -rd1915c892bc5ca61b4c5065d7fc1a188fdc0b1f2 -r9c603f82a3332d5e2e5b290bcc550f263c09a8f8
--- lams_learning/web/includes/javascript/presence.js (.../presence.js) (revision d1915c892bc5ca61b4c5065d7fc1a188fdc0b1f2)
+++ lams_learning/web/includes/javascript/presence.js (.../presence.js) (revision 9c603f82a3332d5e2e5b290bcc550f263c09a8f8)
@@ -1,111 +1,174 @@
-var windowHeight;
-var pollInProgress = false;
+$(document).ready(function (){
+ presenceChat = $("#presenceChat");
+ rosterDiv = $("#presenceUserListings");
-var roster = {
- // association nick -> localId, the latter being just some ID made in this script
- users : {},
- maxUserLocalId : 0,
- // map "tab" -> "last message in tab", so we don't fetch all messages every time, only new ones
- lastMessageUids : [],
-
- // when user clicked another user in roster
- handleUserClicked : function(localId) {
- var nick = null;
- $.each(roster.users, function(key, value){
- if (value == localId) {
- nick = key;
- return false;
+ // if presence IM is enabled
+ if (presenceEnabled) {
+ // make visible
+ presenceChat.removeClass("startHidden");
+
+ // create chat tabs
+ presenceChatTabs = $("#presenceChatTabs").tabs({
+ 'activate' : function(event, ui) {
+ // remove visual indicators (underline) of new message
+ var nick = getUserFromTabIndex(presenceChatTabs.tabs('option','active')),
+ tag = nickToTag(nick),
+ tabLabel = tagToTabLabel(tag);
+ $("#" + tabLabel).removeClass('presenceTabNewMessage');
}
});
-
- if(nick != nickname){
- var tag = nickToTag(nick);
- var tabLabel = tagToTabLabel(tag);
- var tab = $('#' + tabLabel);
- // if the clicked user's tab is open
- if(tab.length){
- // make the sender's tab label unbold
- tab.html(nick);
- } else {
- // if the clicked user's tab is not already open, create it and select it
- addTab(nick, tag);
+ }
+});
+
+
+var windowHeight = null,
+ roster = {
+ // association nick -> localId, the latter being just some ID made in this script
+ users : {},
+ maxUserLocalId : 0,
+ lastMessageUids : {},
+
+ // when user clicked another user in roster
+ handleUserClicked : function(localId) {
+ var nick = null;
+ // find the nick based on the ID
+ $.each(roster.users, function(key, value){
+ if (value == localId) {
+ nick = key;
+ return false;
+ }
+ });
+
+ if (nick != nickname){
+ var tag = nickToTag(nick),
+ tabLabel = tagToTabLabel(tag),
+ tab = $('#' + tabLabel);
+ // if the clicked learner's tab is open
+ if (tab.length == 0){
+ // if the clicked user's tab is not already open, create it and select it
+ addTab(nick, tag);
+ }
+ // select the added tab
+ presenceChatTabs.tabs( "option", "active", $('li', presenceChatTabs).length - 1);
}
+ },
+
+ updateDisplay : function(users) {
+ // repopulate user objects array
+ var rosterUsers = {};
+ $.each(users, function(index, nick){
+ var localId = roster.users[nick];
+ rosterUsers[nick] = localId ? localId : ++roster.maxUserLocalId;
+ });
+ this.users = rosterUsers;
- // select the added tab
- presenceChatTabs.tabs('select' , tag);
+ // add HTML entries for each user
+ rosterDiv.empty();
+ jQuery.each(this.users, function(nick, localId){
+ var tag = nickToTag(nick),
+ listingName = tagToListing(tag),
+ listing = $("#" + listingName);
+ // if no listing in roster exists
+ if (listing.length == 0){
+ // create listing div
+ var listingDiv = $('
'
+ + createPresenceListing(nick, tag)
+ + '
');
+
+ // add the listing div
+ rosterDiv.append(listingDiv);
+ } else {
+ // remove and append at the right place (from sort)
+ rosterDiv.append(listing.remove());
+ }
+ });
+
+ // update presenceTabLabel
+ $("#presenceUserCount").html(labelUsers + " (" + users.length + ")");
}
},
+
+ // init the connection with server using server URL but with different protocol
+ websocket = new WebSocket(APP_URL.replace('http', 'ws') + 'presenceChatWebsocket?lessonID=' + lessonId +
+ '&imEnabled=' + presenceImEnabled + '&nickname=' + encodeURIComponent(nickname));
- updateDisplay : function(users) {
- // sort alphabetically
- users.sort();
- // repopulate user objects array
- var rosterUsers = {};
- $.each(users, function(index, nick){
- var localId = roster.users[nick];
- rosterUsers[nick] = localId ? localId : ++roster.maxUserLocalId;
- });
- this.users = rosterUsers;
-
- jQuery.each(this.users, function(nick, localId){
- var tag = nickToTag(nick);
- var listingName = tagToListing(tag);
- var listing = $("#" + listingName);
- // if no listing in roster exists
- if (listing.length == 0){
- // create listing div
- var listingDiv = $('
'
- + createPresenceListing(nick, tag)
- + '
');
+// when the server pushes new messages and roster to the learner's browser
+websocket.onmessage = function(e){
+ // create JSON object
+ var input = JSON.parse(e.data);
+ if (input.roster) {
+ roster.updateDisplay(input.roster);
+ }
+
+ if (input.messages) {
+ var activeNick = getUserFromTabIndex(presenceChatTabs.tabs('option','active')),
+ selectedTabTag = nickToTag(activeNick);
+
+ jQuery.each(input.messages, function(){
+ // which tab are we talking about?
+ var from = this.to ? (this.from == nickname ? this.to : this.from) : groupChatInfo.nick,
+ lastMessageUid = roster.lastMessageUids[from] || 0;
+
+ // are the messages new?
+ if (this.uid > lastMessageUid) {
+ var tag = nickToTag(from);
+ if (tag != selectedTabTag) {
+ var tab = $("#" + tagToTabLabel(tag));
+ if (tab.length == 0) {
+ // no tab opened yet, create it
+ tab = addTab(from, tag);
+ }
- // add the listing div
- rosterDiv.append(listingDiv);
- } else {
- // remove and append at the right place (from sort)
- rosterDiv.append(listing.remove());
- }
- });
-
- // update presenceTabLabel
- var presenceTabLabelDiv = $("#presence_tabLabel");
- presenceTabLabelDiv.html(labelUsers + " (" + users.length + ")");
- }
-}
+ // notify of new message
+ tab.addClass('presenceTabNewMessage');
+ if (tag != groupChatInfo.tag) {
+ $("#" + tagToListing(tag)).addClass('presenceListingNewMessage');
+ }
+ }
+
+ roster.lastMessageUids[from] = this.uid;
+ var messageArea = $("#" + (nickToMessageArea(from)));
+ messageArea.append(generateMessageHTML(this.from, this.message, this.dateSent));
+ messageArea.scrollTop(messageArea.prop('scrollHeight'));
+ }
+ });
+ }
+
+ // remove conversation tabs with learners who are gone
+ $('li a', presenceChatTabs).each(function() {
+ var nick = $(this).text();
+ if (nick != groupChatInfo.nick && !roster.users[nick]) {
+ var tag = $(this).attr('href');
+ $(tag).remove();
+ $(this).parent().remove();
+ presenceChatTabs.tabs('refresh');
+ }
+ });
+};
/* ******* HTML write Functions ******* */
function createPrivateTabLabel(nick, tag){
- return '
' +
- '
' +
- '
' +
- '
' + nick + '
' +
- '
' +
- '
' +
- '
';
+ return '' + nick + 'Remove Tab';
}
function createPrivateTabContent(nick, tag){
- return ' ' +
- '
' +
- '' +
- '' +
- '
';
+ return '' +
+ '
' +
+ '' +
+ '' +
+ '
';
}
function createPresenceListing(nick, tag){
- return '
' +
- '
' +
- '
' +
- '
' + nick + '
' +
- '
' +
- '
';
+ return '
' + nick + '
';
}
/* ******* Helper Functions ******* */
function generateMessageHTML(nick, message, date) {
- var fromElem = $('
(' + date.substring(11, 19) + ') ' + nick + '
');
- var msgElem = $('
' + message + '
');
+ var fromElem = $('
(' + date.substring(11, 19) + ') ' + nick + '
'),
+ msgElem = $('
' + message + '
');
return completeElem = $('').append(fromElem).append(msgElem);
}
@@ -128,22 +191,29 @@
'top' : windowHeight + "px"
});
}
-
- $("#presenceChatWarning").css({
- 'top' : windowHeight - 10 + "px"
- });
}
function getUserFromTabIndex(tabIndex) {
- return $(".ui-tabs-label")[tabIndex].innerHTML;
+ return tabIndex == 0 ? groupChatInfo.nick : $('li a', presenceChatTabs)[tabIndex].innerHTML;
}
+// Adds a new tab and fetches conversation history
function addTab(nick, tag) {
- // add a tab with the the nick specified
- presenceChatTabs.tabs('add', '#' + tag,
- createPrivateTabLabel(nick, tag));
- // add the content
- $("#" + tag).html(createPrivateTabContent(nick, tag));
+ var tab = $('').attr('id', tag + '_tabLabel').html(createPrivateTabLabel(nick, tag)).appendTo($('ul', presenceChatTabs));
+ $('').attr('id', tag).html(createPrivateTabContent(nick, tag)).appendTo(presenceChatTabs);
+ presenceChatTabs.tabs('refresh');
+
+ // fetch all messages from the start
+ roster.lastMessageUids[nick] = null;
+ var data = {
+ 'type' : 'fetchConversation',
+ 'lessonID' : lessonId,
+ 'to' : nick
+ };
+
+ websocket.send(JSON.stringify(data));
+
+ return tab;
}
function nickToTag(nick) {
@@ -163,10 +233,10 @@
}
function getTime() {
- var currentTime = new Date();
- var hours = currentTime.getHours();
- var minutes = currentTime.getMinutes();
- var seconds = currentTime.getSeconds();
+ var currentTime = new Date(),
+ hours = currentTime.getHours(),
+ minutes = currentTime.getMinutes(),
+ seconds = currentTime.getSeconds();
if (hours < 10) {
hours = "0" + hours;
@@ -186,123 +256,35 @@
/* ******* Main chat functions ******* */
function sendMessage(receiver) {
- var tag = nickToTag(receiver);
- var messageInput = $('#' + tag + '_messageInput');
- var message = messageInput.val();
+ var tag = nickToTag(receiver),
+ messageInput = $('#' + tag + '_messageInput'),
+ message = messageInput.val();
if (!message || message == '') {
return false; // do not send empty messages.
}
messageInput.val('');
messageInput.focus();
- $.ajax({
- url : actionUrl,
- data : {'method' : 'sendMessage',
- 'lessonID' : lessonId,
- 'from' : nickname,
- 'to' : tag == groupChatInfo.tag ? null : receiver,
- 'message' : message
- },
- cache : false,
- complete : updateChat
- });
+
+ var data = {
+ 'type' : 'message',
+ 'lessonID' : lessonId,
+ 'to' : tag == groupChatInfo.tag ? '' : receiver,
+ 'message' : message
+ };
+
+ websocket.send(JSON.stringify(data));
}
-function updateChat(){
- // skip another attempt if previous did not return yet (slow server?)
- if (!pollInProgress) {
- pollInProgress = true;
- var from = null;
- var selected = null;
- var lastMessageUid = null;
- var getMessages = presenceShown && presenceImEnabled;
- if (getMessages) {
- selected = presenceChatTabs.tabs('option','active');
- from = getUserFromTabIndex(selected);
- if (groupChatInfo.nick == from) {
- from = null;
- }
- lastMessageUid = roster.lastMessageUids[from ? from : 'group'];
- }
-
- $.ajax({
- url : actionUrl,
- data : {'method' : 'getChatContent',
- 'lessonID' : lessonId,
- 'getMessages' : getMessages,
- 'lastMessageUid' : lastMessageUid,
- 'to' : nickname,
- 'from' : from
- },
- cache : false,
- dataType : 'json',
- complete : function(){
- pollInProgress = false;
- },
- success : function (result) {
- roster.updateDisplay(result.roster);
-
- // real new messages for the opentab
- if (result.messages) {
- var messageArea = $("#" + (nickToMessageArea(from ? from : groupChatInfo.nick)));
- var lastMessageUid = null;
- jQuery.each(result.messages, function(){
- messageArea.append(generateMessageHTML(this.from, this.message, this.dateSent));
- lastMessageUid = this.uid;
- });
- // store last message uid and get new messages starting from this one
- roster.lastMessageUids[from ? from : 'group'] = lastMessageUid;
- messageArea.scrollTop(messageArea.prop('scrollHeight'));
- }
-
- // check if other users wrote something new
- if (result.newConversations) {
- var selectedTabTag = nickToTag(getUserFromTabIndex(selected));
- jQuery.each(result.newConversations, function(index, nick){
- var tag = nick == 'group' ? groupChatInfo.tag : nickToTag(nick);
- if (tag != selectedTabTag) {
- var tab = $("#" + tagToTabLabel(tag));
- if (tab.length == 0) {
- addTab(this, tag);
- tab = $("#" + tagToTabLabel(tag));
- }
-
- // notify of new message
- tab.addClass('presenceTabNewMessage');
- if (tag != groupChatInfo.tag) {
- $("#" + tagToListing(tag)).addClass('presenceListingNewMessage');
- }
- }
- });
- }
- }
- });
- }
-}
-
-
/* ******* Click handlers ******* */
-
-function handleCloseTab(label){
- var tabLabelsLocal = $(".ui-tabs-label");
- for (var i = 0; i < tabLabelsLocal.length; i++){
- if(tabLabelsLocal[i].innerHTML == label){
- presenceChatTabs.tabs('remove' , i);
- roster.lastMessageUids[label] = null;
- }
- }
+function handleCloseTab(tag){
+ $('#' + tag + '_tabLabel').remove();
+ $('#' + tag).remove();
+ presenceChatTabs.tabs('refresh');
}
-function handleLeftScrollClick(){
- presenceChatTabs.tabs('scrollLeft');
-}
-
-function handleRightScrollClick(){
- presenceChatTabs.tabs('scrollRight');
-}
-
function handlePresenceClick() {
if (presenceShown) {
presenceChat.animate({