Index: lams_build/lib/lams/lams.jar =================================================================== RCS file: /usr/local/cvsroot/lams_build/lib/lams/lams.jar,v diff -u -r1.420.2.54 -r1.420.2.55 Binary files differ Fisheye: Tag 1.5.2.1 refers to a dead (removed) revision in file `lams_central/src/java/org/lamsfoundation/lams/web/PresenceServlet.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.2.2.3 refers to a dead (removed) revision in file `lams_central/src/java/org/lamsfoundation/lams/webservice/PresenceChatAction.java'. Fisheye: No comparison available. Pass `N' to diff? Index: lams_common/src/java/org/lamsfoundation/lams/commonContext.xml =================================================================== RCS file: /usr/local/cvsroot/lams_common/src/java/org/lamsfoundation/lams/commonContext.xml,v diff -u -r1.100.2.20 -r1.100.2.21 --- lams_common/src/java/org/lamsfoundation/lams/commonContext.xml 11 Sep 2015 16:08:08 -0000 1.100.2.20 +++ lams_common/src/java/org/lamsfoundation/lams/commonContext.xml 29 Jan 2016 08:41:38 -0000 1.100.2.21 @@ -226,11 +226,6 @@ - - - - - @@ -508,11 +503,6 @@ - - - - - Fisheye: Tag 1.1.2.1 refers to a dead (removed) revision in file `lams_common/src/java/org/lamsfoundation/lams/presence/dao/IPresenceChatDAO.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.1.2.4 refers to a dead (removed) revision in file `lams_common/src/java/org/lamsfoundation/lams/presence/dao/hibernate/PresenceChatDAO.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.3.2.1 refers to a dead (removed) revision in file `lams_common/src/java/org/lamsfoundation/lams/presence/model/PresenceChatMessage.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.1.2.1 refers to a dead (removed) revision in file `lams_common/src/java/org/lamsfoundation/lams/presence/model/PresenceChatUser.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.1.2.1 refers to a dead (removed) revision in file `lams_common/src/java/org/lamsfoundation/lams/presence/service/IPresenceChatService.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.1.2.1 refers to a dead (removed) revision in file `lams_common/src/java/org/lamsfoundation/lams/presence/service/PresenceChatService.java'. Fisheye: No comparison available. Pass `N' to diff? Index: lams_learning/.classpath =================================================================== RCS file: /usr/local/cvsroot/lams_learning/.classpath,v diff -u -r1.12.2.8 -r1.12.2.9 --- lams_learning/.classpath 25 Nov 2014 10:47:04 -0000 1.12.2.8 +++ lams_learning/.classpath 29 Jan 2016 08:41:39 -0000 1.12.2.9 @@ -18,6 +18,7 @@ + Index: lams_learning/build.xml =================================================================== RCS file: /usr/local/cvsroot/lams_learning/build.xml,v diff -u -r1.34 -r1.34.2.1 --- lams_learning/build.xml 12 Dec 2012 13:50:42 -0000 1.34 +++ lams_learning/build.xml 29 Jan 2016 08:41:39 -0000 1.34.2.1 @@ -3,5 +3,23 @@ + + + + + ${ant.project.name}: Copying additional Java classes to WAR + + \ No newline at end of file Index: lams_learning/src/java/org/lamsfoundation/lams/learning/learningApplicationContext.xml =================================================================== RCS file: /usr/local/cvsroot/lams_learning/src/java/org/lamsfoundation/lams/learning/learningApplicationContext.xml,v diff -u -r1.32.2.6 -r1.32.2.7 --- lams_learning/src/java/org/lamsfoundation/lams/learning/learningApplicationContext.xml 10 Aug 2015 15:24:24 -0000 1.32.2.6 +++ lams_learning/src/java/org/lamsfoundation/lams/learning/learningApplicationContext.xml 29 Jan 2016 08:41:39 -0000 1.32.2.7 @@ -97,8 +97,33 @@ + + + + + + + + + + + + + + + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + PROPAGATION_REQUIRED + + + + + + + + Fisheye: Tag 1.1 refers to a dead (removed) revision in file `lams_learning/src/java/org/lamsfoundation/lams/learning/presence/PresenceWebsocketServer.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.1 refers to a dead (removed) revision in file `lams_learning/src/java/org/lamsfoundation/lams/learning/presence/dao/IPresenceChatDAO.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.1 refers to a dead (removed) revision in file `lams_learning/src/java/org/lamsfoundation/lams/learning/presence/dao/hibernate/PresenceChatDAO.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.1 refers to a dead (removed) revision in file `lams_learning/src/java/org/lamsfoundation/lams/learning/presence/model/PresenceChatMessage.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.1 refers to a dead (removed) revision in file `lams_learning/src/java/org/lamsfoundation/lams/learning/presence/model/PresenceChatUser.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.1 refers to a dead (removed) revision in file `lams_learning/src/java/org/lamsfoundation/lams/learning/presence/service/IPresenceChatService.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.1 refers to a dead (removed) revision in file `lams_learning/src/java/org/lamsfoundation/lams/learning/presence/service/PresenceChatService.java'. Fisheye: No comparison available. Pass `N' to diff? Index: lams_learning/web/presenceChat.jsp =================================================================== RCS file: /usr/local/cvsroot/lams_learning/web/presenceChat.jsp,v diff -u -r1.5 -r1.5.2.1 --- lams_learning/web/presenceChat.jsp 5 Apr 2014 15:27:27 -0000 1.5 +++ lams_learning/web/presenceChat.jsp 29 Jan 2016 08:41:39 -0000 1.5.2.1 @@ -1,138 +1,52 @@ <%-- css --%> - + <%-- javascript --%> - - + <%-- initial html / presence.js adds on html into here --%>
<%-- only pop the message box if im is enabled --%> - -
-
-
- -
-
- -
-
-
-
-
- - " class="sendButton" - onclick="javascript:sendMessage(groupChatInfo.nick)" /> + +
+
    +
  • + +
  • +
+
+
+
+ + " class="sendButton" + onclick="javascript:sendMessage(groupChatInfo.nick)" />
-
-
-
- -
+
-
<%-- always pop the roster --%> -
- -
-
+
+
+
-
- -<%-- floating div shown when IE6 is being used --%> -
-
\ No newline at end of file Index: lams_learning/web/WEB-INF/tags/Head.tag =================================================================== RCS file: /usr/local/cvsroot/lams_learning/web/WEB-INF/tags/Head.tag,v diff -u -r1.2.2.1 -r1.2.2.2 --- lams_learning/web/WEB-INF/tags/Head.tag 22 Jan 2016 10:50:06 -0000 1.2.2.1 +++ lams_learning/web/WEB-INF/tags/Head.tag 29 Jan 2016 08:42:28 -0000 1.2.2.2 @@ -32,7 +32,6 @@ <%@ tag body-content="scriptless"%> - Index: lams_learning/web/css/presence.css =================================================================== RCS file: /usr/local/cvsroot/lams_learning/web/css/presence.css,v diff -u -r1.2 -r1.2.2.1 --- lams_learning/web/css/presence.css 31 Jan 2014 18:32:56 -0000 1.2 +++ lams_learning/web/css/presence.css 29 Jan 2016 08:41:39 -0000 1.2.2.1 @@ -1,15 +1,5 @@ -/* -************************************************* -LAMS 2.2 -Date modified: 29/04/2009 - -************************************************* -Presence stylesheet -********************************************** */ - -#presenceUserListings { - margin: 5px; - padding: 2px; +.startHidden { + display: none; } #presenceChat { @@ -20,15 +10,6 @@ z-index: 10; } -#presenceChatWarning { - position: absolute; - width: 400px; - right: 18px; - z-index: 10; - padding: 10px 10px 10px 40px; - margin: 0px; -} - #presenceChatTabs { position: absolute; bottom: 0px; @@ -37,70 +18,54 @@ height: 260px; } +#presenceChatTabs li a { + border-bottom: none; +} + +#presenceChatTabs li .ui-icon-close { + margin-top: 5px; + cursor: pointer; +} + #presenceChatRoster { position: absolute; right: 0pt; bottom: 0pt; width: 25%; height: 100%; + background-color: #FFFFFF; + border: #AAAAAA thin solid; } +#presenceUserCount { + height: 23px; + border-bottom: #AAAAAA thin solid; + text-align: center; + padding-top: 7px; + cursor: pointer; +} + #presenceUserListings{ + padding: 5px; height: 255px; overflow-x: hidden; overflow-y: scroll; } -#minMaxIcon{ - position:absolute; - top: 0px; - right:0px; -} - -#tabsHolder { - float: left; - width: 90%; - overflow:hidden; -} - -#leftSliderDiv { - float: left; - width: 5%; - text-align: left; -} - -#rightSliderDiv { - float: left; - width: 5%; - text-align: right; -} - -.smallImage { - width: 16px; - height: 16px; - border: none; -} - -.startHidden { - display: none; -} - .presenceListing { - width:"100%"; + cursor: pointer; } .presenceListing:hover { - width:"100%"; background-color: #CBDBFF; - cursor: pointer; } .presenceListingNewMessage { background-color: #88FF99; } -.presenceTabNewMessage { - text-decoration: underline; +.presenceTabNewMessage a { + text-decoration: underline !important; } .presenceMessage { @@ -111,26 +76,15 @@ font-weight: bold; } -.presenceMessageEmote { - font-style: italic; - padding-bottom: 4px; -} - -.chatPanel { - width: 95%; - height: 220px; -} - .messageArea { width: 100%; - height: 80%; + height: 190px; overflow-x: hidden; overflow-y: scroll; } .sendArea { - width: 100%; - height: 20%; + width: 95%; } .messageInput { @@ -140,20 +94,4 @@ .sendButton { float: left; -} - -table.tabLabelTable { - width: auto; -} - -.ui-tabs .ui-tabs-nav li.ui-tabs-active a{ - cursor: pointer; -} - -#presenceChatRoster li { - width: 100%; -} - -#presenceChatRoster li a{ - width: 83%; } \ No newline at end of file Index: lams_learning/web/includes/javascript/presence.js =================================================================== RCS file: /usr/local/cvsroot/lams_learning/web/includes/javascript/presence.js,v diff -u -r1.4 -r1.4.2.1 --- lams_learning/web/includes/javascript/presence.js 31 Jan 2014 18:32:56 -0000 1.4 +++ lams_learning/web/includes/javascript/presence.js 29 Jan 2016 08:41:39 -0000 1.4.2.1 @@ -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({