Index: lams_tool_assessment/web/pages/learning/learning.jsp =================================================================== diff -u -rb9300513239d652c59e3bfd190d0973295844f37 -r998ba383ec2a06647d309f910ebefe0a33fa30a4 --- lams_tool_assessment/web/pages/learning/learning.jsp (.../learning.jsp) (revision b9300513239d652c59e3bfd190d0973295844f37) +++ lams_tool_assessment/web/pages/learning/learning.jsp (.../learning.jsp) (revision 998ba383ec2a06647d309f910ebefe0a33fa30a4) @@ -125,39 +125,81 @@ //timelimit feature - $(document).ready(function(){ - //show timelimit-start-dialog in order to start countdown - if (${sessionMap.isTimeLimitNotLaunched}) { - - $.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(); + // websocket needs pinging and reconnection feature in case it fails + // it works pretty much the same as command websocket in Page.tag + var assessmentTimeLimitWebsocketInitTime = null, + assessmentTimeLimitWebsocket = null, + assessmentTimeLimitWebsocketPingTimeout = null, + assessmentTimeLimitWebsocketPingFunc = null, + assessmentTimeLimitWebsocketReconnectAttempts = 0, + counterInitialised = false; + + assessmentTimeLimitWebsocketPingFunc = function(skipPing){ + if (assessmentTimeLimitWebsocket.readyState == assessmentTimeLimitWebsocket.CLOSING + || assessmentTimeLimitWebsocket.readyState == assessmentTimeLimitWebsocket.CLOSED){ + return; } - }); + + // check and ping every 3 minutes + assessmentTimeLimitWebsocketPingTimeout = setTimeout(assessmentTimeLimitWebsocketPingFunc, 3*60*1000); + // initial set up does not send ping + if (!skipPing) { + assessmentTimeLimitWebsocket.send("ping"); + } + }; + + function initAssessmentTimeLimitWebsocket(){ + assessmentTimeLimitWebsocketInitTime = Date.now(); + assessmentTimeLimitWebsocket = new WebSocket(''.replace('http', 'ws') + + 'learningWebsocket?toolContentID=' + ${sessionMap.assessment.contentId}); + + assessmentTimeLimitWebsocket.onclose = function(e){ + // check reason and whether the close did not happen immediately after websocket creation + // (possible access denied, user logged out?) + if (e.code === 1006 && + Date.now() - assessmentTimeLimitWebsocketInitTime > 1000 && + assessmentTimeLimitWebsocketReconnectAttempts < 20) { + assessmentTimeLimitWebsocketReconnectAttempts++; + // maybe iPad went into sleep mode? + // we need this websocket working, so init it again after delay + setTimeout(initAssessmentTimeLimitWebsocket, 3000); + } + }; + + // set up timer for the first time + assessmentTimeLimitWebsocketPingFunc(true); + + // when the server pushes new inputs + assessmentTimeLimitWebsocket.onmessage = function(e){ + // read JSON object + var input = JSON.parse(e.data); + + if (input.clearTimer == true) { + // teacher has stopped the timer, destroy it + $('#countdown').countdown('destroy').remove(); + counterInitialised = false; + } else { + // teacher has updated the timer + var secondsLeft = +input.secondsLeft; + if (counterInitialised) { + // just set the new time + $('#countdown').countdown('option', 'until', secondsLeft + 'S'); + } else { + // initialise the timer + displayCountdown(secondsLeft); + } + } + + // reset ping timer + clearTimeout(assessmentTimeLimitWebsocketPingTimeout); + assessmentTimeLimitWebsocketPingFunc(true); + }; + } - function displayCountdown(){ - var countdown = '
' + function displayCountdown(secondsLeft){ + counterIntialised = true; + var countdown = '
'; + $.blockUI({ message: countdown, showOverlay: false, @@ -174,9 +216,10 @@ }); $('#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)) { @@ -192,18 +235,39 @@ }, description: "
" }); + } - <%-- double check if we have the correct number of seconds left in case user has clicked refresh --%> - $.ajax({ - url: '', - data: 'sessionMapID=${sessionMapID}', - dataType: 'json', - type: 'post', - success: function (json) { - $('#countdown').countdown('option', 'until', json.secondsLeft+'S'); - } - }); - } + + $(document).ready(function(){ + //show timelimit-start-dialog in order to start countdown + if (${sessionMap.isTimeLimitNotLaunched}) { + + $.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(); + initAssessmentTimeLimitWebsocket(); + isWaitingForConfirmation = false; + }); + + } else { + initAssessmentTimeLimitWebsocket(); + } + });
//autosave feature