Index: lams_learning/web/includes/javascript/learnerPage.js =================================================================== diff -u -r89f41df2ea71b152595e2497fd3ca690e5464d16 -rd1d25ad3d8e023bd467dda99f615f7fd1b70ef91 --- lams_learning/web/includes/javascript/learnerPage.js (.../learnerPage.js) (revision 89f41df2ea71b152595e2497fd3ca690e5464d16) +++ lams_learning/web/includes/javascript/learnerPage.js (.../learnerPage.js) (revision d1d25ad3d8e023bd467dda99f615f7fd1b70ef91) @@ -82,10 +82,108 @@ .css('width', progressBarWidgetValue + '%') .attr('aria-valuenow', progressBarWidgetValue); $('#progress-bar-widget-value').text(progressBarWidgetValue + '%'); + + + initCommandWebsocket(result.lessonID); } }); } +function initCommandWebsocket(lessonId) { + let commandWebsocketInitTime = Date.now(), + commandWebsocket = null, + commandWebsocketPingTimeout = null, + commandWebsocketPingFunc = null, + commandWebsocketReconnectAttempts = 0, + commandWebsocketHookTrigger = null, + commandWebsocketHook = null; + + commandWebsocketPingFunc = function (skipPing) { + if (commandWebsocket.readyState == commandWebsocket.CLOSING + || commandWebsocket.readyState == commandWebsocket.CLOSED) { + return; + } + + // check and ping every 3 minutes + commandWebsocketPingTimeout = setTimeout(commandWebsocketPingFunc, 3 * 60 * 1000); + // initial set up does not send ping + if (!skipPing) { + commandWebsocket.send("ping"); + } + }; + + // it is not an obvious place to init the websocket, but we need lesson ID + commandWebsocket = new WebSocket(LAMS_URL .replace('http', 'ws') + + 'learning/commandWebsocket?lessonID=' + lessonId); + + commandWebsocket.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() - commandWebsocketInitTime > 1000 && + commandWebsocketReconnectAttempts < 20) { + commandWebsocketReconnectAttempts++; + // maybe iPad went into sleep mode? + // we need this websocket working, so init it again after delay + setTimeout(initCommandWebsocket, 3000); + } + }; + + // set up timer for the first time + commandWebsocketPingFunc(true); + + // when the server pushes new commands + commandWebsocket.onmessage = function (e) { + // read JSON object + var command = JSON.parse(e.data); + if (command.message) { + // some tools implement autosave feature + // if it is such a tool, trigger it + if (command.message === 'autosave') { + // the name of this function is same in all tools + if (typeof learnerAutosave == 'function') { + learnerAutosave(true); + } + } else { + alert(command.message); + } + } + + // if learner's currently displayed page has hookTrigger same as in the JSON + // then a function also defined on that page will run + if (command.hookTrigger && command.hookTrigger == commandWebsocketHookTrigger + && typeof commandWebsocketHook === 'function') { + commandWebsocketHook(command.hookParams); + } + + if (command.redirectURL) { + window.location.href = command.redirectURL; + } + + if (command.discussion) { + var discussionCommand = $('#discussion-sentiment-command'); + if (discussionCommand.length === 0) { + discussionCommand = $('
').attr('id', 'discussion-sentiment-command').appendTo('body'); + } + discussionCommand.load(LAMS_URL + "learning/discussionSentiment/" + command.discussion + ".do", { + lessonId: lessonId + }); + } + + // reset ping timer + clearTimeout(commandWebsocketPingTimeout); + commandWebsocketPingFunc(true); + }; + + // check if there is a running discussion; if so, a websocket command will come to display the widget + $.ajax({ + url: LAMS_URL + "learning/discussionSentiment/checkLearner.do", + data: { + lessonId: lessonId + } + }); +} + function toggleProgressBar(forceClose) { let pageContent = $('.component-page-wrapper .component-page-content'), progressBar = $('.component-page-wrapper .component-sidebar'), @@ -105,7 +203,7 @@ progressBar.removeAttr('inert'); $('.sidebar-toggle-button', progressBar).focus(); - $('body').on('keyup', function (event){ + $('body').on('keyup', function (event) { if (event.key === "Escape") { toggleProgressBar(true); }