Files
lms/lms/templates/quiz/quiz.js
2024-02-21 17:02:18 +05:30

297 lines
8.0 KiB
JavaScript

frappe.ready(() => {
const self = this;
this.quiz_submitted = false;
this.answer = [];
this.is_correct = [];
this.show_answers = $("#quiz-title").data("show-answers");
this.current_index = 0;
localStorage.removeItem($("#quiz-title").data("name"));
$(".btn-start-quiz").click((e) => {
$("#start-banner").addClass("hide");
$("#quiz-form").removeClass("hide");
mark_active_question();
});
$(".option").click((e) => {
if (!$("#check").hasClass("hide")) enable_check(e);
});
$(".possibility").keyup((e) => {
enable_check(e);
});
$("#summary").click((e) => {
e.preventDefault();
if (!this.show_answers) check_answer();
setTimeout(() => {
quiz_summary(e);
}, 500);
});
$("#check").click((e) => {
e.preventDefault();
check_answer(e);
});
$("#next").click((e) => {
e.preventDefault();
if (!this.show_answers) check_answer();
mark_active_question(e);
});
$("#try-again").click((e) => {
try_quiz_again(e);
});
});
const mark_active_question = (e = undefined) => {
let total_questions = $(".question").length;
let current_index = this.current_index;
let next_index = parseInt(current_index) + 1;
if (this.show_answers) {
$("#next").addClass("hide");
} else if (!this.show_answers && next_index == total_questions) {
$("#next").addClass("hide");
$("#summary").removeClass("hide");
}
$(".question").addClass("hide").removeClass("active-question");
$(`.question[data-qt-index='${next_index}']`)
.removeClass("hide")
.addClass("active-question");
$(".current-question").text(`${next_index}`);
$("#check").removeClass("hide").attr("disabled", true);
$("#next").attr("disabled", true);
$(".explanation").addClass("hide");
$(".timer").addClass("hide");
calculate_and_display_time(100);
$(".timer").removeClass("hide");
initialize_timer();
};
const calculate_and_display_time = (percent_time) => {
$(".timer .progress-bar").attr("aria-valuenow", percent_time);
$(".timer .progress-bar").attr("aria-valuemax", percent_time);
$(".timer .progress-bar").css("width", `${percent_time}%`);
let progress_color = percent_time < 20 ? "red" : "var(--primary-color)";
$(".timer .progress-bar").css("background-color", progress_color);
};
const initialize_timer = () => {
this.time_left = $(".timer").data("time");
calculate_and_display_time(100, this.time_left);
$(".timer").removeClass("hide");
const total_time = $(".timer").data("time");
this.start_time = new Date().getTime();
const self = this;
let old_diff;
this.timer = setInterval(function () {
var diff = (new Date().getTime() - self.start_time) / 1000;
var variation = old_diff ? diff - old_diff : diff;
old_diff = diff;
self.time_left -= variation;
let percent_time = (self.time_left / total_time) * 100;
calculate_and_display_time(percent_time);
if (self.time_left <= 0) {
clearInterval(self.timer);
$(".timer").addClass("hide");
check_answer();
}
}, 100);
};
const enable_check = (e) => {
if ($(".option:checked").length || $(".possibility").val().trim()) {
$("#check").removeAttr("disabled");
$("#next").removeAttr("disabled");
$(".custom-checkbox").removeClass("active-option");
$(".option:checked")
.closest(".custom-checkbox")
.addClass("active-option");
}
};
const quiz_summary = (e = undefined) => {
e && e.preventDefault();
let quiz_name = $("#quiz-title").data("name");
let self = this;
frappe.call({
method: "lms.lms.doctype.lms_quiz.lms_quiz.quiz_summary",
args: {
quiz: quiz_name,
results: localStorage.getItem(quiz_name),
},
callback: (data) => {
$(".question").addClass("hide");
$("#summary").addClass("hide");
$(".quiz-footer span").addClass("hide");
$("#quiz-form").prepend(
`<div class="summary bold-heading text-center">
${__("You got")} ${Math.ceil(data.message.percentage)}% ${__("correct answers")}
</div>
<div class="summary bold-heading text-center mt-2">
${__("Your score is")} ${data.message.score}
${__("out of")} ${data.message.score_out_of}
</div>`
);
$("#try-again").attr("data-submission", data.message.submission);
$("#try-again").removeClass("hide");
self.quiz_submitted = true;
if (
this.hasOwnProperty("marked_as_complete") &&
data.message.pass
) {
mark_progress();
}
},
});
};
const try_quiz_again = (e) => {
e.preventDefault();
if (window.location.href.includes("new-submission")) {
const target = $(e.currentTarget);
window.location.href = `/quiz-submission/
${target.data("quiz")}/
${target.data("submission")}`;
} else {
window.location.reload();
}
};
const check_answer = (e = undefined) => {
e && e.preventDefault();
let answer = $(".active-question textarea");
let total_questions = $(".question").length;
let current_index = this.current_index;
if (answer.length && !answer.val().trim()) {
frappe.throw(__("Please enter your answer"));
}
clearInterval(self.timer);
$(".timer").addClass("hide");
$(".explanation").removeClass("hide");
$("#check").addClass("hide");
if (current_index == total_questions - 1) {
$("#summary").removeClass("hide");
} else if (this.show_answers) {
$("#next").removeClass("hide");
}
parse_options();
this.current_index += 1;
};
const parse_options = () => {
let user_answers = [];
let element;
let type = $(".active-question").data("type");
if (type == "Choices") {
$(".active-question input").each((i, element) => {
if ($(element).prop("checked")) {
user_answers.push(decodeURIComponent($(element).val()));
}
});
element = $(".active-question input");
} else {
user_answers.push($(".active-question textarea").val());
element = $(".active-question textarea");
}
is_answer_correct(type, user_answers, element);
};
const is_answer_correct = (type, user_answers, element) => {
frappe.call({
async: false,
method: "lms.lms.doctype.lms_quiz.lms_quiz.check_answer",
args: {
question: $(".active-question").data("name"),
type: type,
answers: user_answers,
},
callback: (data) => {
type == "Choices"
? parse_choices(element, data.message)
: parse_possible_answers(element, data.message);
add_to_local_storage();
},
});
};
const parse_choices = (element, is_correct) => {
element.each((i, elem) => {
if ($(elem).prop("checked")) {
self.answer.push(decodeURIComponent($(elem).val()));
self.is_correct.push(is_correct[i]);
if (this.show_answers)
is_correct[i]
? add_icon(elem, "check")
: add_icon(elem, "wrong");
} else {
if (this.show_answers && is_correct[i] == 2)
add_icon(elem, "minus-circle-green");
else add_icon(elem, "minus-circle");
}
});
};
const parse_possible_answers = (element, correct) => {
self.answer.push(decodeURIComponent($(element).val()));
self.is_correct.push(correct);
if (this.show_answers)
correct
? show_indicator("success", element)
: show_indicator("failure", element);
};
const show_indicator = (class_name, element) => {
let label = class_name == "success" ? "Correct" : "Incorrect";
let icon =
class_name == "success" ? "#icon-solid-success" : "#icon-solid-error";
$(`<div class="answer-indicator ${class_name}">
<svg class="icon icon-md">
<use href=${icon}>
</svg>
<span style="font-weight: 500">${__(label)}</span>
</div>`).insertAfter(element);
};
const add_icon = (element, icon) => {
$(element).closest(".custom-checkbox").removeClass("active-option");
$(element).closest(".option").addClass("hide");
let label = $(element).siblings(".option-text").text();
$(element).siblings(".option-text").html(`
<div>
<img class="d-inline mr-3" src="/assets/lms/icons/${icon}.svg">
${label}
</div>
`);
};
const add_to_local_storage = () => {
let quiz_name = $("#quiz-title").data("name");
let quiz_stored = JSON.parse(localStorage.getItem(quiz_name));
let quiz_obj = {
question_index: this.current_index,
answer: self.answer.join(),
is_correct: self.is_correct,
};
quiz_stored ? quiz_stored.push(quiz_obj) : (quiz_stored = [quiz_obj]);
localStorage.setItem(quiz_name, JSON.stringify(quiz_stored));
self.answer = [];
self.is_correct = [];
};