return true;
// 3 is arbitrary, but a small tolerance is required (e.g. in Firefox)
return window.innerWidth >= screen.width-3 && window.innerHeight >= screen.height-3;
-};
+}
-let V = new Vue({
+new Vue({
el: "#assessment",
data: {
assessment: assessment,
// 2: locked: password set, exam started
// 3: completed
// 4: show answers
+ remainingTime: assessment.time, //integer or array
stage: assessment.mode != "open" ? 0 : 1,
- remainingTime: 0, //global, in seconds
warnMsg: "",
},
computed: {
countdown: function() {
- let seconds = this.remainingTime % 60;
- let minutes = Math.floor(this.remainingTime / 60);
+ const remainingTime = assessment.display == "one" && _.isArray(assessment.time)
+ ? this.remainingTime[this.answers.index]
+ : this.remainingTime;
+ let seconds = remainingTime % 60;
+ let minutes = Math.floor(remainingTime / 60);
return this.padWithZero(minutes) + ":" + this.padWithZero(seconds);
},
- showAnswers: function() {
- return this.stage == 4;
- },
},
mounted: function() {
$(".modal").modal();
- if (assessment.mode != "secure")
+ if (["exam","open"].includes(assessment.mode))
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);
window.addEventListener("blur", () => {
- this.trySendCurrentAnswer();
- document.location.href= "/noblur";
+ if (this.stage != 2)
+ return;
+ if (assessment.mode == "secure")
+ {
+ this.sendAnswer();
+ document.location.href= "/noblur";
+ }
+ else //"watch" mode
+ socket.emit(message.studentBlur, {number:this.student.number});
}, false);
+ if (assessment.mode == "watch")
+ {
+ window.addEventListener("focus", () => {
+ if (this.stage != 2)
+ return;
+ socket.emit(message.studentFocus, {number:this.student.number});
+ }, false);
+ }
window.addEventListener("resize", e => {
- this.trySendCurrentAnswer();
- document.location.href= "/fullscreen";
+ if (this.stage != 2)
+ return;
+ if (assessment.mode == "secure")
+ {
+ this.sendAnswer();
+ document.location.href = "/fullscreen";
+ }
+ else //"watch" mode
+ {
+ if (checkWindowSize())
+ socket.emit(message.studentFullscreen, {number:this.student.number});
+ else
+ socket.emit(message.studentResize, {number:this.student.number});
+ }
}, false);
},
methods: {
- // In case of AJAX errors
+ // In case of AJAX errors (not blur-ing)
showWarning: function(message) {
this.warnMsg = message;
$("#warning").modal("open");
return "0" + x;
return x;
},
- trySendCurrentAnswer: function() {
- if (this.stage == 2)
- this.sendAnswer();
- },
// stage 0 --> 1
- getStudent: function(cb) {
- $.ajax("/get/student", {
+ getStudent: function() {
+ $.ajax("/courses/student", {
method: "GET",
data: {
number: this.student.number,
this.stage = 1;
this.student = s.student;
Vue.nextTick( () => { Materialize.updateTextFields(); });
- if (!!cb)
- cb();
},
});
},
},
// stage 1 --> 2 (get all questions, set password)
startAssessment: function() {
- let initializeStage2 = (questions,paper) => {
+ let initializeStage2 = paper => {
$("#leftButton, #rightButton").hide();
- if (assessment.time > 0)
- {
- const deltaTime = !!paper ? Date.now() - paper.startTime : 0;
- this.remainingTime = assessment.time * 60 - Math.round(deltaTime / 1000);
- this.runTimer();
- }
// Initialize structured answer(s) based on questions type and nesting (TODO: more general)
+
+ // if display == "all" getQuestionS
+ // otherwise get first question
+
+
if (!!questions)
assessment.questions = questions;
this.answers.inputs = [ ];
let remainingIndices = _.difference( _.range(assessment.questions.length).map(String), indices );
this.answers.indices = indices.concat( _.shuffle(remainingIndices) );
}
+
+
+
+
+
+
+
+ if (assessment.time > 0)
+ {
+
+// TODO: distinguish total exam time AND question time
+
+ const deltaTime = !!paper ? Date.now() - paper.startTime : 0;
+ this.remainingTime = assessment.time * 60 - Math.round(deltaTime / 1000);
+ this.runTimer();
+ }
+
+
this.answers.index = !!paper ? paper.inputs.length : 0;
this.answers.displayAll = assessment.display == "all";
this.answers.showSolution = false;
};
if (assessment.mode == "open")
return initializeStage2();
- $.ajax("/start/assessment", {
- method: "GET",
+ $.ajax("/assessments/start", {
+ method: "PUT",
data: {
number: this.student.number,
aid: assessment._id
},
});
},
+
+
// stage 2
- runTimer: function() {
+ runGlobalTimer: function() {
if (assessment.time <= 0)
return;
let self = this;
}
}, 1000);
},
+ runQuestionTimer: function(idx) {
+ if (assessment.questions[idx].time <= 0)
+ return;
+ let self = this; //TODO: question remaining time
+ setInterval( function() {
+ self.remainingTime--;
+ if (self.remainingTime <= 0)
+ {
+ if (self.stage == 2)
+ self.endAssessment();
+ clearInterval(this);
+ }
+ }, 1000);
+ },
+
+//TODO: get question after sending answer
+
// stage 2
sendOneAnswer: function() {
const realIndex = this.answers.indices[this.answers.index];
number: this.student.number,
password: this.student.password,
};
- $.ajax("/send/answer", {
- method: "GET",
+ $.ajax("/assessments/answer", {
+ method: "PUT",
data: answerData,
dataType: "json",
success: ret => {
this.answers.displayAll = true;
return;
}
- $.ajax("/end/assessment", {
- method: "GET",
+ $.ajax("/assessments/end", {
+ method: "PUT",
data: {
aid: assessment._id,
number: this.student.number,
success: ret => {
if (!!ret.errmsg)
return this.showWarning(ret.errmsg);
- assessment.conclusion = ret.conclusion;
this.stage = 3;
delete this.student["password"]; //unable to send new answers now
},