466 lines
12 KiB
JavaScript
466 lines
12 KiB
JavaScript
class Task extends HTMLElement {
|
|
static sequence_number = 0;
|
|
constructor(type) {
|
|
super();
|
|
|
|
this.task_type = type;
|
|
this.sequence_number = Task.sequence_number++;
|
|
this.concluded = false;
|
|
this.view_only = false;
|
|
this.upload_answer_cb = null;
|
|
this.player_answer = null;
|
|
|
|
this.shadow = this.attachShadow({mode: "open"});
|
|
this.createStyle();
|
|
this.createElements();
|
|
}
|
|
|
|
createStyle() {
|
|
this.css = document.createElement("style");
|
|
this.css.innerHTML = `
|
|
span.question {
|
|
font-size: 1.3em;
|
|
font-weight: bold;
|
|
color: #176767;
|
|
}
|
|
section.task {
|
|
display: block;
|
|
position: relative;
|
|
margin: 0 auto;
|
|
width: 40em;
|
|
padding: 1em;
|
|
background-color: #d3e5e5;
|
|
margin-bottom: 0.5em;
|
|
border-radius: 0.3em;
|
|
}
|
|
section.seq-num {
|
|
display: block;
|
|
position: absolute;
|
|
right: 0;
|
|
padding: 0.5em;
|
|
bottom: 0;
|
|
background-color: #176767;
|
|
color: whitesmoke;
|
|
border-bottom-right-radius: 0.3em;
|
|
border-top-left-radius: 0.3em;
|
|
width: 2em;
|
|
}
|
|
section.answer-container {
|
|
/* (empty) */
|
|
}
|
|
code {
|
|
font-family: 'Monaco', monospace;
|
|
}
|
|
|
|
@media only screen and (max-width: 800px) {
|
|
section.task {
|
|
width: calc(100vw - 3em);
|
|
}
|
|
section.answer-container {
|
|
margin-bottom: 1.5em;
|
|
}
|
|
}
|
|
`;
|
|
this.shadow.append(this.css);
|
|
}
|
|
|
|
createElements() {
|
|
let task_box = document.createElement("section");
|
|
task_box.classList.add("task");
|
|
let question_span = document.createElement("span");
|
|
question_span.classList.add("question");
|
|
let answer_container = document.createElement("section");
|
|
answer_container.classList.add("answer-container");
|
|
let seq_num_section = document.createElement("section");
|
|
seq_num_section.classList.add("seq-num");
|
|
seq_num_section.innerText = `${this.sequence_number+1}.`;
|
|
|
|
task_box.append(question_span);
|
|
task_box.append(answer_container);
|
|
task_box.append(seq_num_section);
|
|
|
|
this.shadow.append(task_box);
|
|
|
|
this.task_box = task_box;
|
|
this.question_span = question_span;
|
|
this.answer_container = answer_container;
|
|
this.seq_num_section = seq_num_section;
|
|
}
|
|
|
|
connectedCallback() {}
|
|
|
|
disconnectedCallback() {}
|
|
|
|
get type() {
|
|
return this.task_type;
|
|
}
|
|
|
|
set type(type) {
|
|
this.task_type = type;
|
|
}
|
|
|
|
get sequenceNumber() {
|
|
return this.sequence_number;
|
|
}
|
|
|
|
setQuestion(question) {
|
|
this.question_span.innerHTML = preprocess_inserts(question);
|
|
}
|
|
|
|
get isConcluded() {
|
|
return this.concluded;
|
|
}
|
|
|
|
set isConcluded(concluded) {
|
|
this.concluded = concluded;
|
|
}
|
|
|
|
get isViewOnly() {
|
|
return this.view_only;
|
|
}
|
|
|
|
set isViewOnly(viewOnly) {
|
|
this.view_only = viewOnly;
|
|
}
|
|
|
|
get isCorrect() {
|
|
return false;
|
|
}
|
|
|
|
set playerAnswer(player_answer) {
|
|
this.player_answer = player_answer;
|
|
}
|
|
|
|
get playerAnswer() {
|
|
return this.player_answer;
|
|
}
|
|
|
|
set uploadAnswerCb(cb) {
|
|
this.upload_answer_cb = cb;
|
|
}
|
|
|
|
uploadAnswer() {
|
|
if (this.upload_answer_cb !== null) {
|
|
this.upload_answer_cb(this.sequence_number, this.playerAnswer);
|
|
}
|
|
}
|
|
|
|
fromArray(a) {
|
|
this.setQuestion(a["question"]);
|
|
this.playerAnswer = a["player_answer"];
|
|
}
|
|
}
|
|
|
|
class PicturedTask extends Task {
|
|
constructor(type) {
|
|
super(type);
|
|
}
|
|
|
|
createStyle() {
|
|
super.createStyle();
|
|
|
|
this.css.innerHTML += `
|
|
img.question-image {
|
|
display: none;
|
|
position: relative;
|
|
margin: 1em auto;
|
|
border-radius: 0.3em;
|
|
max-width: 100%;
|
|
}
|
|
`;
|
|
}
|
|
|
|
createElements() {
|
|
super.createElements();
|
|
|
|
this.img = document.createElement("img");
|
|
this.img.classList.add("question-image");
|
|
this.img.src = "";
|
|
|
|
this.task_box.insertBefore(this.img, this.question_span);
|
|
}
|
|
|
|
set imgUrl(url) {
|
|
url = url.trim();
|
|
this.img.src = url.trim();
|
|
this.img.style.display = (url !== "") ? "block" : "none";
|
|
}
|
|
|
|
get imgUrl() {
|
|
return this.img.src;
|
|
}
|
|
}
|
|
|
|
class SingleChoiceTask extends PicturedTask {
|
|
constructor() {
|
|
super("singlechoice");
|
|
|
|
this.answers = []
|
|
this.correct_answer = -1;
|
|
this.player_answer = -1;
|
|
}
|
|
|
|
createStyle() {
|
|
super.createStyle();
|
|
|
|
this.css.innerHTML += `
|
|
section.answer {
|
|
margin: 0.3em 0.8em;
|
|
display: block;
|
|
}
|
|
section.answer label {
|
|
margin-left: 0.5em;
|
|
padding: 0.3em 0.5em;
|
|
border-radius: 0.3em;
|
|
|
|
display: inline-block;
|
|
max-width: 85%;
|
|
vertical-align: middle;
|
|
}
|
|
section.answer label.correct-answer {
|
|
border: 2px solid #176767 !important;
|
|
background-color: #176767;
|
|
color: whitesmoke;
|
|
/*padding: 0.1em;*/
|
|
}
|
|
section.answer input[type="radio"]:checked+label:not(.correct-answer) {
|
|
background-color: #176767;
|
|
color: whitesmoke;
|
|
}
|
|
section.bad-answer {
|
|
background-color: #e5d8d3;
|
|
}
|
|
section.bad-answer section.answer input[type="radio"]:checked+label:not(.correct-answer) {
|
|
background-color: #aa8a7d;
|
|
}
|
|
.MathJax {
|
|
display: block;
|
|
margin: 0.5em auto;
|
|
font-size: 120%;
|
|
}
|
|
@media only screen and (max-width: 800px) {
|
|
section.answer label {
|
|
max-width: calc(100% - 4em);
|
|
}
|
|
}
|
|
`;
|
|
}
|
|
|
|
createElements() {
|
|
super.createElements();
|
|
}
|
|
|
|
// --------
|
|
|
|
setAnswers(answers) {
|
|
this.answers = answers;
|
|
|
|
this.answers.forEach((answer, i) => {
|
|
let answer_section = document.createElement("section");
|
|
answer_section.classList.add("answer");
|
|
let answer_radio = document.createElement("input");
|
|
answer_radio.type = "radio";
|
|
answer_radio.id = `${this.sequenceNumber}_${i}`;
|
|
answer_radio.name = `task_${this.sequenceNumber}`;
|
|
answer_radio.disabled = this.isConcluded || this.isViewOnly;
|
|
let answer_N_snapshot = i;
|
|
answer_radio.addEventListener("input", () => {
|
|
this.playerAnswer = answer_N_snapshot;
|
|
this.uploadAnswer();
|
|
});
|
|
|
|
let answer_text = document.createElement("label");
|
|
answer_text.innerHTML = preprocess_inserts(answer);
|
|
answer_text.htmlFor = answer_radio.id;
|
|
if (this.isConcluded && (this.correctAnswer === i)) {
|
|
answer_text.classList.add("correct-answer")
|
|
|
|
if (this.playerAnswer !== this.correctAnswer) {
|
|
this.task_box.classList.add("bad-answer");
|
|
}
|
|
}
|
|
|
|
if (this.playerAnswer === i) {
|
|
answer_radio.checked = true;
|
|
}
|
|
|
|
answer_section.append(answer_radio, answer_text);
|
|
this.answer_container.append(answer_section);
|
|
});
|
|
|
|
MathJax.typeset([ this.task_box ]);
|
|
}
|
|
|
|
set correctAnswer(correct_answer) {
|
|
this.correct_answer = correct_answer;
|
|
}
|
|
|
|
get correctAnswer() {
|
|
return this.correct_answer;
|
|
}
|
|
|
|
get isCorrect() {
|
|
return this.player_answer === this.correct_answer;
|
|
}
|
|
|
|
fromArray(a) {
|
|
super.fromArray(a);
|
|
|
|
this.correctAnswer = a["correct_answer"];
|
|
this.setAnswers(a["answers"]);
|
|
}
|
|
|
|
}
|
|
|
|
class OpenEndedTask extends PicturedTask {
|
|
constructor() {
|
|
super("openended");
|
|
}
|
|
|
|
createStyle() {
|
|
super.createStyle();
|
|
|
|
this.css.innerHTML += `
|
|
input[type="text"] {
|
|
font-family: 'Monaco', monospaced;
|
|
border-width: 0 0 2.2pt 0;
|
|
background-color: transparent;
|
|
width: calc(100% - 4em);
|
|
margin: 1em 0;
|
|
border-bottom-color: #176767;
|
|
font-size: 110%;
|
|
}
|
|
input[type="text"]:hover {
|
|
border-bottom-color: #408d8d;
|
|
}
|
|
`
|
|
}
|
|
|
|
createElements() {
|
|
super.createElements();
|
|
|
|
let answer_tf = document.createElement("input");
|
|
answer_tf.type = "text";
|
|
answer_tf.placeholder = "(válasz)";
|
|
|
|
answer_tf.onblur = () => {
|
|
this.uploadAnswer();
|
|
}
|
|
answer_tf.oninput = () => {
|
|
this.player_answer = answer_tf.value;
|
|
};
|
|
|
|
this.answer_container.append(answer_tf);
|
|
|
|
this.answer_tf = answer_tf;
|
|
}
|
|
|
|
fromArray(a) {
|
|
super.fromArray(a);
|
|
}
|
|
|
|
set playerAnswer(player_answer) {
|
|
super.playerAnswer = player_answer;
|
|
this.answer_tf.value = player_answer;
|
|
}
|
|
|
|
get playerAnswer() {
|
|
return this.player_answer;
|
|
}
|
|
|
|
updateAnswerFieldState() {
|
|
this.answer_tf.disabled = this.isViewOnly || this.isConcluded;
|
|
}
|
|
|
|
set isConcluded(concluded) {
|
|
super.isConcluded = concluded;
|
|
this.updateAnswerFieldState();
|
|
}
|
|
|
|
get isConcluded() {
|
|
return super.isConcluded;
|
|
}
|
|
|
|
set isViewOnly(is_view_only) {
|
|
super.isViewOnly = is_view_only;
|
|
}
|
|
|
|
get isViewOnly() {
|
|
return super.isViewOnly;
|
|
}
|
|
}
|
|
|
|
class NumberConversionTask extends OpenEndedTask {
|
|
constructor() {
|
|
super();
|
|
this.type = "numberconversion";
|
|
}
|
|
|
|
createStyle() {
|
|
super.createStyle();
|
|
|
|
this.css.innerHTML += `
|
|
input[type="text"] {
|
|
min-width: 5em;
|
|
width: unset;
|
|
}
|
|
section#src, section#dst {
|
|
position: relative;
|
|
display: inline-block;
|
|
font-family: 'Monaco', monospace;
|
|
color: #176767;
|
|
}
|
|
section#src {
|
|
margin-right: 1ch;
|
|
}
|
|
sub {
|
|
position: relative;
|
|
top: 0.8em;
|
|
}
|
|
`;
|
|
}
|
|
|
|
createElements() {
|
|
super.createElements();
|
|
|
|
let src_sec = document.createElement("section");
|
|
src_sec.id = "src";
|
|
let dst_sec = document.createElement("section");
|
|
dst_sec.id = "dst";
|
|
|
|
this.answer_container.insertBefore(src_sec, this.answer_tf);
|
|
this.answer_container.append(dst_sec);
|
|
|
|
this.src_sec = src_sec;
|
|
this.dst_sec = dst_sec;
|
|
|
|
this.answer_tf.addEventListener("input", () => {
|
|
this.updateAnswerFieldLength();
|
|
})
|
|
}
|
|
|
|
fromArray(a) {
|
|
super.fromArray(a);
|
|
|
|
const regex = /([0-9]+)([suc]):([0-9]+)->([0-9]+)([suc]):([0-9]+)/g;
|
|
let parts = [ ...a["instruction"].matchAll(regex) ][0];
|
|
|
|
let src_exp = `${a["source"]}<sub>(${parts[1]})</sub> =`;
|
|
let dst_exp = `<sub>(${parts[4]})</sub> <i>(${parts[6]} digiten)</i>`;
|
|
|
|
this.src_sec.innerHTML = src_exp;
|
|
this.dst_sec.innerHTML = dst_exp;
|
|
|
|
this.updateAnswerFieldLength();
|
|
}
|
|
|
|
updateAnswerFieldLength() {
|
|
this.answer_tf.style.width = this.answer_tf.value.length + "ch";
|
|
}
|
|
}
|
|
|
|
customElements.define('singlechoice-task', SingleChoiceTask);
|
|
customElements.define('openended-task', OpenEndedTask);
|
|
customElements.define('numberconversion-task', NumberConversionTask);
|
|
|