Index: lams_tool_scratchie/web/pages/learning/learning.jsp =================================================================== diff -u -r281e19ba98aa6bf38fc1f25507a9098304d668fb -r705c0f72b765849974bfa0d9f8b04797619e8da7 --- lams_tool_scratchie/web/pages/learning/learning.jsp (.../learning.jsp) (revision 281e19ba98aa6bf38fc1f25507a9098304d668fb) +++ lams_tool_scratchie/web/pages/learning/learning.jsp (.../learning.jsp) (revision 705c0f72b765849974bfa0d9f8b04797619e8da7) @@ -1,6 +1,7 @@ <%@ include file="/common/taglibs.jsp"%> + <%-- param has higher level for request attribute --%> @@ -10,6 +11,9 @@ + + + @@ -23,7 +27,19 @@ - + @@ -83,6 +99,11 @@ } }); + // hide Finish button for non-leaders until leader finishes + if (${hideFinishButton}) { + $("#finishButton").hide(); + } + <%-- Connect to command websocket only if it is learner UI --%> // command websocket stuff for refreshing @@ -272,46 +293,11 @@ (((prevHash << 5) - prevHash) + currVal.charCodeAt(0))|0, 0); } - //boolean to indicate whether ok dialog is still ON so that autosave can't be run - var isWaitingForConfirmation = ${isTimeLimitEnabled && isTimeLimitNotLaunched}; - //time limit feature - - $(document).ready(function(){ - - //show timelimit-start-dialog in order to start countdown - if (${isTimeLimitNotLaunched}) { - - //show confirmation dialog - $.blockUI({ - message: $('#timelimit-start-dialog'), - css: { width: '325px', height: '120px'}, - overlayCSS: { opacity: '.98'} - }); - - //once OK button pressed start countdown - $('#timelimit-start-ok').click(function() { - - //store date when user has started activity with time limit - $.ajax({ - async: true, - url: '', - data: 'sessionMapID=${sessionMapID}', - type: 'post' - }); - - $.unblockUI(); - displayCountdown(); - isWaitingForConfirmation = false; - }); - - } else { - displayCountdown(); - } - - }); + + // time limit feature - function displayCountdown(){ + function displayCountdown(secondsLeft){ var countdown = '
' $.blockUI({ message: countdown, @@ -329,30 +315,123 @@ }); $('#countdown').countdown({ - until: '+${secondsLeft}S', + until: '+' + secondsLeft +'S', format: 'hMS', compact: true, + alwaysExpire : true, onTick: function(periods) { - //check for 30 seconds - if ((periods[4] == 0) && (periods[5] == 0) && (periods[6] <= 30)) { - $('#countdown').css('color', '#FF3333'); - } + // check for 30 seconds or less and display timer in red + var secondsLeft = $.countdown.periodsToSeconds(periods); + if (secondsLeft <= 30) { + $(this).addClass('countdown-timeout'); + } else { + $(this).removeClass('countdown-timeout'); + } }, onExpiry: function(periods) { - $.blockUI({ message: '

' }); + $.blockUI({ message: '

' }); - setTimeout( - function() { - finish(true); - }, - 4000 - ); + setTimeout(function() { + if (${isUserLeader}) { + finish(true); + } else { + location.reload(); + } + }, 4000); }, description: "
" }); } -
+ + //init the connection with server using server URL but with different protocol + var scratchieWebsocketInitTime = Date.now(), + scratchieWebsocket = new WebSocket(''.replace('http', 'ws') + + 'learningWebsocket?toolSessionID=' + ${toolSessionID} + '&toolContentID=' + ${scratchie.contentId}), + scratchieWebsocketPingTimeout = null, + scratchieWebsocketPingFunc = null; + + scratchieWebsocket.onclose = function(e) { + // react only on abnormal close + if (e.code === 1006 && + Date.now() - scratchieWebsocketInitTime > 1000) { + location.reload(); + } + }; + + scratchieWebsocketPingFunc = function(skipPing){ + if (scratchieWebsocket.readyState == scratchieWebsocket.CLOSING + || scratchieWebsocket.readyState == scratchieWebsocket.CLOSED){ + return; + } + + // check and ping every 3 minutes + scratchieWebsocketPingTimeout = setTimeout(scratchieWebsocketPingFunc, 3*60*1000); + // initial set up does not send ping + if (!skipPing) { + scratchieWebsocket.send("ping"); + } + }; + + // set up timer for the first time + scratchieWebsocketPingFunc(true); + + // run when the server pushes new reports and vote statistics + scratchieWebsocket.onmessage = function(e) { + // create JSON object + var input = JSON.parse(e.data); + if (input.pageRefresh) { + location.reload(); + return; + } + + if (input.clearTimer == true) { + // teacher stopped the timer, destroy it + $('#countdown').countdown('destroy').remove(); + } else if (input.secondsLeft){ + // teacher updated the timer + var secondsLeft = +input.secondsLeft, + counterInitialised = $('#countdown').length > 0; + + if (counterInitialised) { + // just set the new time + $('#countdown').countdown('option', 'until', secondsLeft + 'S'); + } else if (secondsLeft){ + if (${isScratchingFinished}) { + // teacher gave extra time, reload to enable scratching again + location.reload(); + return; + } + // initialise the timer + displayCountdown(secondsLeft); + } + } else if (${not isUserLeader}){ + // reflect the leader's choices + $.each(input, function(itemUid, options) { + $.each(options, function(optionUid, optionProperties){ + + if (optionProperties.isVSA) { + var answer = optionUid; + optionUid = hashCode(optionUid); + + //check if such image exists, create it otherwise + if ($('#image-' + itemUid + '-' + optionUid).length == 0) { + paintNewVsaAnswer(eval(itemUid), answer); + } + } + + scratchImage(itemUid, optionUid, optionProperties.isCorrect); + }); + }); + } + + // reset ping timer + clearTimeout(scratchieWebsocketPingTimeout); + scratchieWebsocketPingFunc(true); + }; +
+ + //autosave feature var autosaveInterval = "60000"; // 60 seconds interval