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"]}(${parts[1]}) =`; let dst_exp = `(${parts[4]}) (${parts[6]} digiten)`; 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);