Skip to content
Snippets Groups Projects
Commit 7fc0e0f6 authored by Philip Kaluđerčić's avatar Philip Kaluđerčić :u7121:
Browse files

Initial revision

parents
No related branches found
No related tags found
No related merge requests found
quiz.js 0 → 100644
// This horrible file is distributed under the CC0 license. Consult
// ./LICENSE for more details.
// $Id$
"use strict";
const answers = document.getElementById("answers");
const text = document.getElementById("text");
const media = document.getElementById("media");
const metadata = document.getElementById("metadata-data");
const info = document.getElementById("info");
const info_toggle = document.getElementById("info-toggle");
const action = document.getElementById("action");
const stats = document.getElementById("stats");
const class_list = document.documentElement.classList;
// toggle information panel
info_toggle.onclick = _ => class_list.toggle("open");
// state variables
var questions; // quiz data
var current; // current question
var done = {}; // question object -> correct answer
var opts = []; // DOM objects of each answer
var progress = JSON.parse(window.localStorage.getItem("progress")) || {};
function shuf(arr) {
// shuffle option-order using Fisher–Yates shuffle
for (let i = 0; i < arr.length - 1; i++) {
const j = Math.floor(Math.random() * arr.length);
const tmp = arr[j];
arr[j] = arr[i];
arr[i] = tmp;
}
}
function evaluate(q) {
const data = progress[q.id] || { "right": 0, "wrong": 0 };
return 2 * data["right"] - data["wrong"] + 4 * Math.random() - 2;
}
function get_data(q) {
return progress[q.id] || { "right": 0, "wrong": 0, "count": 0 };;
}
function update_stats(q) {
const data = get_data(q);
// update stats
stats.innerHTML = "ID: <code>" + q.id + "</code>"
+ ", <abbr title=\"Wie oft gesehen\">n</abbr> = " + data.count
+ ", <abbr title=\"Wie oft Richtig\">r</abbr> = " + data.right
+ ", <abbr title=\"Wie oft Falsch\">w</abbr> = " + data.wrong
+ "; <abbr title=\"Anzahl an Fragen insgesammt\">#</abbr> = " + questions.length;
}
function remember(q, ok) {
const data = get_data(q);
if (ok) {
data["right"]++;
} else {
data["wrong"]++;
}
data["count"]++;
progress[q.id] = data;
window.sessionStorage.setItem("progress", progress);
}
function pick() {
const idx = Math.random() * questions.length;
let choice = questions[Math.floor(idx)];
let worst = evaluate(choice);
// FIXME: Replace this with a proper priority queue
for (let i = 0; i < questions.length; i++) {
const q = questions[i];
let val = evaluate(q);
if (val <= worst) {
choice = q;
worst = val;
}
}
return choice;
}
// function to load random questions
function next() {
const q = pick();
// load question
text.innerHTML = q.question;
// load metadata
metadata.innerHTML = q.source;
// load image, if specified
if (q.media) {
media.style.display = (media.src = q.media) ? "block" : "none";
} else {
media.style.display = "none";
}
// remove all previous questions
while (answers.firstChild)
answers.removeChild(answers.firstChild);
opts = [];
shuf(q.options);
// insert questions
for (let i = 0; i < q.options.length; i++) {
const o = q.options[i];
const li = document.createElement("li");
const input = document.createElement("input");
const answ = document.createElement("p");
const comm = document.createElement("p");
// prepare button
input.type = q.multiple ? "checkbox" : "radio";
input.name = "answer";
input.onclick = _ => class_list.add("tried");
action.disabled = false;
opts[i] = input;
// prepare answer
answ.classList.add("option");
answ.innerHTML = o.option;
// prepare comment
let fallback = "Falsch";
comm.classList.add("comment");
switch (o.correct) {
case true:
comm.classList.add("correct");
fallback = "Wahr";
break;
case null:
comm.classList.add("maybe");
fallback = "Unsicher";
break;
}
comm.innerHTML = o.comment || fallback;
li.appendChild(input);
li.appendChild(answ);
li.appendChild(comm);
li.onclick = _ => input.click();
answers.appendChild(li);
}
// set current question
current = q;
// unset answer mode
class_list.remove("answer");
class_list.remove("right");
// display question metadata
update_stats(q);
}
function submit() {
let ok = true;
// check all answers
for (let i = 0; i < current.options.length; i++) {
const checked = opts[i].checked;
const correct = current.options[i].correct;
if (correct != null && checked != correct) { // wrong answer
ok = false;
}
opts[i].disabled = true;
}
// respond to result
if (ok) {
class_list.add("right");
}
remember(current, ok);
// set answer mode
class_list.add("answer");
class_list.remove("tried");
action.disabled = false;
}
// listen to keys
document.onkeyup = evt => {
switch (evt.code) {
// shortcuts for first 10 options
case "Digit1": if (0 in opts) opts[0].click(); break;
case "Digit2": if (1 in opts) opts[1].click(); break;
case "Digit3": if (2 in opts) opts[2].click(); break;
case "Digit4": if (3 in opts) opts[3].click(); break;
case "Digit5": if (4 in opts) opts[4].click(); break;
case "Digit6": if (5 in opts) opts[5].click(); break;
case "Digit7": if (6 in opts) opts[6].click(); break;
case "Digit8": if (7 in opts) opts[7].click(); break;
case "Digit9": if (8 in opts) opts[8].click(); break;
case "Digit0": if (9 in opts) opts[9].click(); break;
case "Space":
case "Enter": // shortcut for action button
action.click();
break;
case "Escape": // close info section
document.documentElement.classList.remove("open");
break;
}
};
// setup action button
action.onclick = _ => {
if (class_list.contains("answer"))
next();
else if (class_list.contains("tried"))
submit();
}
// query quiz data (unpleasant hack)
(async function() {
// query json specification of quiz
questions = await (await fetch("./quiz.json")).json();
shuf(questions);
// when done, load first question
next();
})();
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment