From: Benjamin Auder Date: Mon, 12 Feb 2018 23:35:51 +0000 (+0100) Subject: Add blur + resize monitoring in exam mode X-Git-Url: https://git.auder.net/variants/current/doc/css/assets/mini-custom.min.css?a=commitdiff_plain;h=2bada710d28ffe9f45b150e5744b43af83e93d99;p=qomet.git Add blur + resize monitoring in exam mode --- diff --git a/bin/www b/bin/www index eacc6e5..36f54dd 100755 --- a/bin/www +++ b/bin/www @@ -72,7 +72,7 @@ function onError(error) if (error.syscall !== 'listen') throw error; - let bind = typeof port === 'string' + const bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port; diff --git a/public/javascripts/assessment.js b/public/javascripts/assessment.js index acce548..342e963 100644 --- a/public/javascripts/assessment.js +++ b/public/javascripts/assessment.js @@ -39,25 +39,53 @@ new Vue({ }, mounted: function() { $(".modal").modal(); - if (assessment.mode != "secure") - return; - window.addEventListener("keydown", e => { - // Ignore F12 (avoid accidental window resize due to devtools) - // NOTE: in Chromium at least, fullscreen mode exit with F11 cannot be prevented. - // Workaround: disable key at higher level. Possible xbindkey config: - // "false" - // m:0x10 + c:95 - // Mod2 + F11 - if (e.keyCode == 123) - e.preventDefault(); - }, false); + if (assessment.mode != "open") + { + window.addEventListener("keydown", e => { + // Ignore F12 (avoid accidental window resize due to devtools) + // NOTE: in Chromium at least, fullscreen mode exit with F11 cannot be prevented. + // Workaround: disable key at higher level. Possible xbindkey config: + // "false" + // m:0x10 + c:95 + // Mod2 + F11 + if (e.keyCode == 123) + e.preventDefault(); + }, false); + } window.addEventListener("blur", () => { - this.trySendCurrentAnswer(); - document.location.href= "/noblur"; + if (!socket) + return; + if (assessment.mode == "secure") + { + this.trySendCurrentAnswer(); + document.location.href= "/noblur"; + } + else if (assessment.mode == "exam") + socket.emit(message.studentBlur, {number:this.student.number}); }, false); + if (assessment.mode == "exam") + { + window.addEventListener("focus", () => { + if (!socket) + return; + socket.emit(message.studentFocus, {number:this.student.number}); + }, false); + } window.addEventListener("resize", e => { - this.trySendCurrentAnswer(); - document.location.href= "/fullscreen"; + if (!socket) + return; + if (assessment.mode == "secure") + { + this.trySendCurrentAnswer(); + document.location.href= "/fullscreen"; + } + else if (assessment.mode == "exam") + { + if (checkWindowSize()) + socket.emit(message.studentFullscreen, {number:this.student.number}); + else + socket.emit(message.studentResize, {number:this.student.number}); + } }, false); }, methods: { diff --git a/public/javascripts/monitor.js b/public/javascripts/monitor.js index 8963731..1b8d62f 100644 --- a/public/javascripts/monitor.js +++ b/public/javascripts/monitor.js @@ -86,6 +86,33 @@ new Vue({ socket = io.connect("/", { query: "aid=" + this.assessment._id + "&secret=" + s.secret }); + socket.on(message.studentBlur, m => { + const sIdx = this.students.findIndex( item => { return item.number == m.number; }); + Vue.set(this.students, sIdx, Object.assign({},this.students[sIdx],{blur: true})); + //this.students[sIdx].blur = true; + }); + socket.on(message.studentFocus, m => { + const sIdx = this.students.findIndex( item => { return item.number == m.number; }); + this.students[sIdx].blur = false; + }); + socket.on(message.studentResize, m => { + const sIdx = this.students.findIndex( item => { return item.number == m.number; }); + Vue.set(this.students, sIdx, Object.assign({},this.students[sIdx],{resize: true})); + //this.students[sIdx].resize = true; + }); + socket.on(message.studentFullscreen, m => { + const sIdx = this.students.findIndex( item => { return item.number == m.number; }); + this.students[sIdx].resize = false; + }); + socket.on(message.studentDisconnect, m => { + const sIdx = this.students.findIndex( item => { return item.number == m.number; }); + Vue.set(this.students, sIdx, Object.assign({},this.students[sIdx],{disco: true})); + //this.students[sIdx].disco = true; + }); + socket.on(message.studentConnect, m => { + const sIdx = this.students.findIndex( item => { return item.number == m.number; }); + this.students[sIdx].disco = false; + }); socket.on(message.newAnswer, m => { let paperIdx = this.assessment.papers.findIndex( item => { return item.number == m.number; @@ -107,6 +134,7 @@ new Vue({ }, endMonitoring: function() { // In the end, send answers to students + // TODO: disable this button until everyone finished (need ability to mark absents) socket.emit( message.allAnswers, { answers: JSON.stringify(this.assessment.questions.map( q => { return q.answer; })) } diff --git a/public/javascripts/utils/socketMessages.js b/public/javascripts/utils/socketMessages.js index 7c86fd8..1fa607a 100644 --- a/public/javascripts/utils/socketMessages.js +++ b/public/javascripts/utils/socketMessages.js @@ -8,6 +8,11 @@ let message = { // Next 2 to monitor students disconnections studentConnect: "student connect", studentDisconnect: "student disconnect", + // And blur + onResize events (sockets only) + studentBlur: "student blur", + studentFocus: "student focus", + studentResize: "student resize", + studentFullscreen: "student fullscreen", }; -try { module.exports = message; } catch (err) {} //for server +try { module.exports = message; } catch (err) { } //for server diff --git a/public/stylesheets/monitor.css b/public/stylesheets/monitor.css index b585800..303d68e 100644 --- a/public/stylesheets/monitor.css +++ b/public/stylesheets/monitor.css @@ -1,3 +1,14 @@ +.blur { + background-color: lightsalmon; +} +.resize { + font-style: italic; + color: darkred; +} +.disconnect { + background-color: grey; +} + /* TODO: factor this piece of code from assessment (and course, and here...) */ .question { margin: 20px 5px; @@ -35,7 +46,12 @@ table.in-question { width: auto; margin: 10px auto; } + table.in-question th, table.in-question td { - padding: 3px; + padding: 7px; border-bottom: 1px solid grey; } + +/*table { border: none; border-collapse: collapse; }*/ +table.in-question td { border-left: 1px solid grey; } +table.in-question td:first-child { border-left: none; } diff --git a/sockets.js b/sockets.js index 9c230be..57fd657 100644 --- a/sockets.js +++ b/sockets.js @@ -30,6 +30,18 @@ module.exports = function(io) socket.on(message.newAnswer, m => { //got answer from student client socket.broadcast.to(aid + "_teacher").emit(message.newAnswer, m); }); + socket.on(message.studentBlur, m => { + socket.broadcast.to(aid + "_teacher").emit(message.studentBlur, m); + }); + socket.on(message.studentFocus, m => { + socket.broadcast.to(aid + "_teacher").emit(message.studentFocus, m); + }); + socket.on(message.studentResize, m => { + socket.broadcast.to(aid + "_teacher").emit(message.studentResize, m); + }); + socket.on(message.studentFullscreen, m => { + socket.broadcast.to(aid + "_teacher").emit(message.studentFullscreen, m); + }); socket.on("disconnect", () => { //notify monitor + server AssessmentEntity.setDiscoTime(ObjectId(aid), number); socket.broadcast.to(aid + "_teacher").emit(message.studentDisconnect, {number: number}); diff --git a/views/monitor.pug b/views/monitor.pug index 891dda2..fa9daee 100644 --- a/views/monitor.pug +++ b/views/monitor.pug @@ -6,7 +6,7 @@ block append stylesheets block content .container#monitor .row - .col.s12.m10.offset-m1.l8.offset-l2.xl6.offset-xl3 + .col.s12.m10.offset-m1 h4= examName #stage0(v-show="stage==0") .card @@ -31,7 +31,7 @@ block content th(v-for="(q,i) in assessment.questions") Q.{{ (i+1) }} tbody tr.assessment(v-for="s in studentList(group)") - td {{ s.name }} + td(:class="{blur:!!s.blur,resize:!!s.resize,disconnect:!!s.disco}") {{ s.name }} td(v-for="(q,i) in assessment.questions" :style="{backgroundColor: getColor(s.number,i)}" @click="seeDetails(s.number,i)")   h4.title(@click="toggleDisplay('assessment')") Assessment div(v-show="display=='assessment'")