|
|
|
@ -1,4 +1,4 @@
|
|
|
|
|
// Click Listeners
|
|
|
|
|
// Bind Listeners
|
|
|
|
|
|
|
|
|
|
$("input[name='font-select']").change(function(){
|
|
|
|
|
let $choice = $(this).val();
|
|
|
|
@ -16,6 +16,8 @@ $("input[name='bg-select']").change(function(){
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$("#btn-toggle-navigator").click(function(event){
|
|
|
|
|
check_answered();
|
|
|
|
|
update_navigator();
|
|
|
|
|
if ($quiz_navigator.is(":hidden")) {
|
|
|
|
|
if ($quiz_settings.is(":visible")) {
|
|
|
|
|
toggle_settings = true;
|
|
|
|
@ -23,6 +25,8 @@ $("#btn-toggle-navigator").click(function(event){
|
|
|
|
|
}
|
|
|
|
|
$quiz_render.hide();
|
|
|
|
|
$quiz_navigator.show();
|
|
|
|
|
$(".navigator-text").show();
|
|
|
|
|
$(".review-text").hide();
|
|
|
|
|
toggle_navigator = false;
|
|
|
|
|
} else {
|
|
|
|
|
$quiz_navigator.hide();
|
|
|
|
@ -57,12 +61,35 @@ $("#btn-toggle-settings").click(function(event){
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$(".btn-quiz-return").click(function(event){
|
|
|
|
|
$quiz_navigator.hide();
|
|
|
|
|
$quiz_settings.hide();
|
|
|
|
|
$quiz_render.show();
|
|
|
|
|
toggle_settings = false;
|
|
|
|
|
toggle_navigator = false;
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$(".btn-dummy").click(function(event){
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$("#navigator-container").on("click", ".q-navigator-button", function(event){
|
|
|
|
|
check_answered();
|
|
|
|
|
update_navigator();
|
|
|
|
|
current_question = parseInt($(this).attr("name"));
|
|
|
|
|
$quiz_render.show();
|
|
|
|
|
$quiz_navigator.hide();
|
|
|
|
|
toggle_navigator = false;
|
|
|
|
|
toggle_settings = false;
|
|
|
|
|
render_question();
|
|
|
|
|
check_flag();
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$(".q-question-nav").click(function(event){
|
|
|
|
|
check_answered();
|
|
|
|
|
update_navigator();
|
|
|
|
|
if ($(this).attr("id") == "q-nav-next") {
|
|
|
|
|
if (current_question < questions.length) {
|
|
|
|
|
current_question ++;
|
|
|
|
@ -71,6 +98,12 @@ $(".q-question-nav").click(function(event){
|
|
|
|
|
if (current_question > 0) {
|
|
|
|
|
current_question --;
|
|
|
|
|
}
|
|
|
|
|
} else if ($(this).hasClass("q-navigator-button")) {
|
|
|
|
|
current_question = $(this).attr("name");
|
|
|
|
|
$quiz_render.show();
|
|
|
|
|
$quiz_navigator.hide();
|
|
|
|
|
toggle_navigator = false;
|
|
|
|
|
toggle_settings = false;
|
|
|
|
|
}
|
|
|
|
|
render_question();
|
|
|
|
|
check_flag();
|
|
|
|
@ -81,10 +114,14 @@ $("#q-nav-flag").click(function(event){
|
|
|
|
|
if (question_status[current_question] != 1) {
|
|
|
|
|
question_status[current_question] = 1;
|
|
|
|
|
$(this).removeClass().addClass("btn btn-warning");
|
|
|
|
|
$(this).attr("title", "Question Flagged for revision. Click to un-flag.");
|
|
|
|
|
} else {
|
|
|
|
|
question_status[current_question] = 0;
|
|
|
|
|
$(this).removeClass().addClass("btn btn-secondary");
|
|
|
|
|
$(this).attr("title", "Question Un-Flagged. Click to flag for revision.");
|
|
|
|
|
}
|
|
|
|
|
window.localStorage.setItem('question_status', JSON.stringify(question_status));
|
|
|
|
|
update_navigator();
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
@ -102,12 +139,22 @@ $("#btn-start-quiz").click(function(event){
|
|
|
|
|
data: JSON.stringify({'_id': _id}),
|
|
|
|
|
contentType: "application/json",
|
|
|
|
|
success: function(response) {
|
|
|
|
|
time_limit_data = response.time_limit;
|
|
|
|
|
time_limit = response.time_limit;
|
|
|
|
|
start_time = response.start_time;
|
|
|
|
|
questions = response.questions;
|
|
|
|
|
total_questions = questions.length;
|
|
|
|
|
window.localStorage.setItem('questions', JSON.stringify(questions));
|
|
|
|
|
window.localStorage.setItem('start_time', JSON.stringify(start_time));
|
|
|
|
|
window.localStorage.setItem('time_limit', JSON.stringify(time_limit));
|
|
|
|
|
render_question();
|
|
|
|
|
build_navigator();
|
|
|
|
|
check_flag();
|
|
|
|
|
if (time_limit != 'null') {
|
|
|
|
|
time_remaining = get_time_remaining();
|
|
|
|
|
clock = setInterval(timer, 1000);
|
|
|
|
|
} else {
|
|
|
|
|
$("#q-timer-widget").hide();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
error: function(response) {
|
|
|
|
|
console.log(response);
|
|
|
|
@ -117,6 +164,79 @@ $("#btn-start-quiz").click(function(event){
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$("#quiz-question-options").on("change", ".quiz-option", function(event){
|
|
|
|
|
$name = parseInt($(this).attr("name"));
|
|
|
|
|
$value = $(this).attr("value");
|
|
|
|
|
answers[$name] = $value;
|
|
|
|
|
window.localStorage.setItem('answers', JSON.stringify(answers));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$("#q-review-answers").click(function(event){
|
|
|
|
|
check_answered();
|
|
|
|
|
update_navigator();
|
|
|
|
|
if ($quiz_navigator.is(":hidden")) {
|
|
|
|
|
if ($quiz_settings.is(":visible")) {
|
|
|
|
|
toggle_settings = true;
|
|
|
|
|
$quiz_settings.hide();
|
|
|
|
|
}
|
|
|
|
|
$quiz_render.hide();
|
|
|
|
|
$quiz_navigator.show();
|
|
|
|
|
$(".navigator-text").hide();
|
|
|
|
|
$(".review-text").show();
|
|
|
|
|
toggle_navigator = false;
|
|
|
|
|
} else {
|
|
|
|
|
$quiz_navigator.hide();
|
|
|
|
|
if (toggle_settings) {
|
|
|
|
|
$quiz_settings.show();
|
|
|
|
|
toggle_settings = false;
|
|
|
|
|
} else {
|
|
|
|
|
$quiz_render.show();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$(".quiz-button-submit").click(function(event){
|
|
|
|
|
let submission = {
|
|
|
|
|
'_id': _id,
|
|
|
|
|
'answers': answers
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$.ajax({
|
|
|
|
|
url: `/api/submit/`,
|
|
|
|
|
type: 'POST',
|
|
|
|
|
data: JSON.stringify(submission),
|
|
|
|
|
contentType: "application/json",
|
|
|
|
|
success: function(response) {
|
|
|
|
|
window.localStorage.clear();
|
|
|
|
|
window.location.href = `/result/`;
|
|
|
|
|
},
|
|
|
|
|
error: function(response) {
|
|
|
|
|
if (typeof response.responseJSON.error === 'string' || response.responseJSON.error instanceof String) {
|
|
|
|
|
alert.innerHTML = alert.innerHTML + `
|
|
|
|
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
|
|
|
|
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
|
|
|
|
|
${response.responseJSON.error}
|
|
|
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
|
|
|
</div>
|
|
|
|
|
`;
|
|
|
|
|
} else if (response.responseJSON.error instanceof Array) {
|
|
|
|
|
for (var i = 0; i < response.responseJSON.error.length; i ++) {
|
|
|
|
|
alert.innerHTML = alert.innerHTML + `
|
|
|
|
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
|
|
|
|
<i class="bi bi-exclamation-triangle-fill" title="Danger"></i>
|
|
|
|
|
${response.responseJSON.error[i]}
|
|
|
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
|
|
|
</div>
|
|
|
|
|
`;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Functions
|
|
|
|
|
|
|
|
|
|
function set_font(value = 'osdefault') {
|
|
|
|
@ -229,41 +349,204 @@ function render_question() {
|
|
|
|
|
$question_text.html(question.text);
|
|
|
|
|
$question_title.html(`Question ${current_question + 1} of ${ questions.length }.`);
|
|
|
|
|
|
|
|
|
|
var q_no = question['q_no'];
|
|
|
|
|
var options = question.options;
|
|
|
|
|
var options_output = '';
|
|
|
|
|
for (let i = 0; i < options.length; i ++) {
|
|
|
|
|
var add_checked = ''
|
|
|
|
|
if (q_no in answers) {
|
|
|
|
|
if (answers[q_no] == options[i]) {
|
|
|
|
|
add_checked = 'checked';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
options_output += `<div class="form-check">
|
|
|
|
|
<input type="radio" class="form-check-input quiz-option" id="q${current_question}-${i}" name="${question.q_no}" value="${options[i]}">
|
|
|
|
|
<input type="radio" class="form-check-input quiz-option" id="q${current_question}-${i}" name="${q_no}" value="${options[i]}" ${add_checked}>
|
|
|
|
|
<label for="q${current_question}-${i}" class="form-check-label">${options[i]}</label>
|
|
|
|
|
</div>`;
|
|
|
|
|
}
|
|
|
|
|
$question_options.html(options_output);
|
|
|
|
|
$question_title.focus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function check_answered() {
|
|
|
|
|
var question = questions[current_question];
|
|
|
|
|
var name = question.q_no;
|
|
|
|
|
if (!$(`input[name='${name}']:checked`).val() && question_status[current_question] == 0) {
|
|
|
|
|
question_status[current_question] = -1;
|
|
|
|
|
if (question_status[current_question] == 0) {
|
|
|
|
|
if (!$(`input[name='${name}']:checked`).val()) {
|
|
|
|
|
question_status[current_question] = -1;
|
|
|
|
|
} else {
|
|
|
|
|
question_status[current_question] = 2;
|
|
|
|
|
}
|
|
|
|
|
window.localStorage.setItem('question_status', JSON.stringify(question_status));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function check_flag() {
|
|
|
|
|
if (!(current_question in question_status)) {
|
|
|
|
|
question_status[current_question] = 0;
|
|
|
|
|
window.localStorage.setItem('question_status', JSON.stringify(question_status));
|
|
|
|
|
}
|
|
|
|
|
switch (question_status[current_question]) {
|
|
|
|
|
case -1:
|
|
|
|
|
$nav_flag.removeClass().addClass('btn btn-danger');
|
|
|
|
|
$nav_flag.removeClass().addClass('btn btn-danger progress-bar-striped');
|
|
|
|
|
$nav_flag.attr("title", "Question Incomplete. Click to flag for revision.");
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
$nav_flag.removeClass().addClass('btn btn-warning');
|
|
|
|
|
$nav_flag.attr("title", "Question Flagged for revision. Click to un-flag.");
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
$nav_flag.removeClass().addClass('btn btn-success');
|
|
|
|
|
$nav_flag.attr("title", "Question Answered. Click to flag for revision.");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
$nav_flag.removeClass().addClass('btn btn-secondary');
|
|
|
|
|
$nav_flag.removeClass().addClass('btn btn-secondary');
|
|
|
|
|
$nav_flag.attr("title", "Question Un-Flagged. Click to flag for revision.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function build_navigator() {
|
|
|
|
|
$nav_container.html('')
|
|
|
|
|
var output = ''
|
|
|
|
|
for (let i = 0; i < questions.length; i ++) {
|
|
|
|
|
let add_class, add_href, add_status = '';
|
|
|
|
|
switch (question_status[i]) {
|
|
|
|
|
case -1:
|
|
|
|
|
add_class = 'btn-danger progress-bar-striped';
|
|
|
|
|
add_href = 'href="#"';
|
|
|
|
|
add_status = 'Incomplete';
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
add_class = 'btn-warning';
|
|
|
|
|
add_href = 'href="#"';
|
|
|
|
|
add_status = 'Flagged';
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
add_class = 'btn-success';
|
|
|
|
|
add_href = 'href="#"';
|
|
|
|
|
add_status = 'Answered';
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
add_class = 'btn-secondary disabled';
|
|
|
|
|
add_href = '';
|
|
|
|
|
add_status = 'Unseen';
|
|
|
|
|
}
|
|
|
|
|
output += `<a ${add_href} class="q-navigator-button btn ${add_class}" name=${i} title="Question ${i+1}: ${add_status}">Q${i + 1}</a>`;
|
|
|
|
|
}
|
|
|
|
|
$nav_container.html(output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function update_navigator() {
|
|
|
|
|
let button = $(`.q-navigator-button[name=${current_question}]`)
|
|
|
|
|
if (current_question in question_status) {
|
|
|
|
|
switch (question_status[current_question]) {
|
|
|
|
|
case -1:
|
|
|
|
|
button.removeClass().addClass("q-navigator-button btn btn-danger progress-bar-striped");
|
|
|
|
|
button.attr("title", `Question ${current_question + 1}: Incomplete`);
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
button.removeClass().addClass("q-navigator-button btn btn-warning");
|
|
|
|
|
button.attr("title", `Question ${current_question + 1}: Flagged`);
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
button.removeClass().addClass("q-navigator-button btn btn-success");
|
|
|
|
|
button.attr("title", `Question ${current_question + 1}: Answered`);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
button.removeClass().addClass("q-navigator-button btn btn-secondary disabled");
|
|
|
|
|
button.attr("title", `Question ${current_question + 1}: Unseen`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function start() {
|
|
|
|
|
$("#btn-start-quiz").hide();
|
|
|
|
|
$(".btn-quiz-return").show();
|
|
|
|
|
$(".quiz-console").show();
|
|
|
|
|
$("#quiz-settings").hide();
|
|
|
|
|
$("#quiz-navigator").hide();
|
|
|
|
|
$(".quiz-start-text").hide();
|
|
|
|
|
|
|
|
|
|
questions = JSON.parse(window.localStorage.getItem('questions'));
|
|
|
|
|
total_questions = questions.length;
|
|
|
|
|
start_time = window.localStorage.getItem('start_time');
|
|
|
|
|
time_limit = window.localStorage.getItem('time_limit');
|
|
|
|
|
|
|
|
|
|
let get_answers = window.localStorage.getItem('answers');
|
|
|
|
|
if (get_answers != null) {
|
|
|
|
|
answers = JSON.parse(get_answers);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let get_status = window.localStorage.getItem('question_status');
|
|
|
|
|
if (get_status != null) {
|
|
|
|
|
question_status = JSON.parse(get_status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
render_question();
|
|
|
|
|
build_navigator();
|
|
|
|
|
check_flag();
|
|
|
|
|
if (time_limit != 'null') {
|
|
|
|
|
time_remaining = get_time_remaining();
|
|
|
|
|
clock = setInterval(timer, 1000);
|
|
|
|
|
} else {
|
|
|
|
|
$("#q-timer-widget").hide();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function check_started() {
|
|
|
|
|
let questions = window.localStorage.getItem('questions');
|
|
|
|
|
let time_limit = window.localStorage.getItem('time_limit');
|
|
|
|
|
let start_time = window.localStorage.getItem('start_time')
|
|
|
|
|
if (questions != null && start_time != null && time_limit != null) {
|
|
|
|
|
start();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function get_time_remaining() {
|
|
|
|
|
var end_time = new Date(time_limit).getTime();
|
|
|
|
|
var _start_time = new Date().getTime();
|
|
|
|
|
return end_time - _start_time;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function timer() {
|
|
|
|
|
var hours = Math.floor((time_remaining % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
|
|
|
|
var minutes = Math.floor((time_remaining % (1000 * 60 * 60)) / (1000 * 60));
|
|
|
|
|
var seconds = Math.floor((time_remaining % (1000 * 60)) / 1000);
|
|
|
|
|
|
|
|
|
|
if (time_remaining > 0) {
|
|
|
|
|
var timer_display = '';
|
|
|
|
|
if (hours > 0) {
|
|
|
|
|
timer_display = `${hours.toString()}: `;
|
|
|
|
|
}
|
|
|
|
|
if (minutes > 0 || hours > 0) {
|
|
|
|
|
if (minutes < 10) {
|
|
|
|
|
timer_display += `0${minutes.toString()}:`;
|
|
|
|
|
} else {
|
|
|
|
|
timer_display += `${minutes.toString()}:`;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (seconds < 10) {
|
|
|
|
|
timer_display += `0${seconds.toString()}`;
|
|
|
|
|
} else {
|
|
|
|
|
timer_display += seconds.toString();
|
|
|
|
|
}
|
|
|
|
|
$timer.html(timer_display);
|
|
|
|
|
time_remaining -= 1000
|
|
|
|
|
} else {
|
|
|
|
|
$timer.html('Expired');
|
|
|
|
|
clearInterval(clock);
|
|
|
|
|
stop()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function stop() {
|
|
|
|
|
$quiz_render.hide();
|
|
|
|
|
$quiz_navigator.hide();
|
|
|
|
|
$quiz_timeout.show();
|
|
|
|
|
$("#btn-toggle-navigator").addClass('disabled');
|
|
|
|
|
$("#btn-toggle-settings").addClass('disabled')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Variable Definitions
|
|
|
|
|
|
|
|
|
|
const _id = window.localStorage.getItem('_id');
|
|
|
|
@ -272,16 +555,21 @@ var current_question = 0;
|
|
|
|
|
var total_questions = 0;
|
|
|
|
|
var question_status = {};
|
|
|
|
|
var answers = {};
|
|
|
|
|
var questions = []
|
|
|
|
|
var time_limit_data = ''
|
|
|
|
|
var questions = [];
|
|
|
|
|
var time_limit, start_time, time_remaining;
|
|
|
|
|
|
|
|
|
|
var display_settings = get_settings_from_storage();
|
|
|
|
|
|
|
|
|
|
const $quiz_settings = $("#quiz-settings");
|
|
|
|
|
const $quiz_navigator = $("#quiz-navigator");
|
|
|
|
|
const $quiz_render = $("#quiz-render");
|
|
|
|
|
const $quiz_timeout = $("#quiz-timeout");
|
|
|
|
|
const $nav_flag = $("#q-nav-flag");
|
|
|
|
|
const $nav_next = $("#q-nav-next");
|
|
|
|
|
const $nav_prev = $("#q-nav-prev");
|
|
|
|
|
const $nav_container = $("#navigator-container");
|
|
|
|
|
const $timer = $("#q-timer-display");
|
|
|
|
|
var clock
|
|
|
|
|
|
|
|
|
|
var toggle_settings = false;
|
|
|
|
|
var toggle_navigator = false;
|
|
|
|
@ -294,9 +582,7 @@ const $question_options = $("#quiz-question-options");
|
|
|
|
|
// Execution on Load
|
|
|
|
|
|
|
|
|
|
apply_settings(display_settings);
|
|
|
|
|
check_started();
|
|
|
|
|
|
|
|
|
|
// TODO Build navigator
|
|
|
|
|
// TODO Navigator Link button behaviour
|
|
|
|
|
// TODO Resume Exam button
|
|
|
|
|
// TODO Load state from storage
|
|
|
|
|
// TODO Answer Registry
|
|
|
|
|
// TODO Timeout Function
|
|
|
|
|
// TODO Send data to server
|
|
|
|
|