136 lines
No EOL
3.5 KiB
JavaScript
136 lines
No EOL
3.5 KiB
JavaScript
import show_error from './show-error.mjs';
|
|
console.clear();
|
|
|
|
(async () => {
|
|
const compilation_error = await show_error;
|
|
if(compilation_error) {
|
|
return;
|
|
}
|
|
});
|
|
|
|
class NumbasEmbedder {
|
|
constructor() {
|
|
this.exams = {};
|
|
|
|
this.numbas_promise = new Promise(resolve => {
|
|
Numbas.queueScript('go', ['start-exam', 'display'], async () => {
|
|
Numbas.display.init();
|
|
resolve(Numbas);
|
|
})
|
|
})
|
|
}
|
|
|
|
get_exam(id) {
|
|
if(!this.exams[id]) {
|
|
const exam_ref = this.exams[id] = {}
|
|
exam_ref.promise = new Promise((resolve,reject) => {
|
|
exam_ref.resolve = resolve;
|
|
exam_ref.reject = reject;
|
|
});
|
|
}
|
|
|
|
return this.exams[id];
|
|
}
|
|
|
|
async add_exam(id, source_url) {
|
|
console.log('add exam',id, source_url);
|
|
await this.numbas_promise;
|
|
console.log('numbas loaded', id);
|
|
const res = await fetch(source_url);
|
|
const exam_file = await res.text();
|
|
const exam_json = JSON.parse(exam_file.slice(exam_file.indexOf('\n')))
|
|
const exam = window.exam = Numbas.createExamFromJSON(exam_json);
|
|
|
|
exam.display_id = id;
|
|
exam.init();
|
|
|
|
exam.signals.on(['ready', 'display question list initialised'], () => {
|
|
console.info('exam ready', id);
|
|
Numbas.signals.trigger('exam ready');
|
|
exam.begin();
|
|
|
|
const exam_promise = this.get_exam(id);
|
|
console.log("LOADED", id);
|
|
exam_promise.resolve(exam);
|
|
});
|
|
}
|
|
}
|
|
|
|
const numbas_embedder = window.numbas_embedder = new NumbasEmbedder();
|
|
|
|
class ElmHTMLElement extends HTMLElement {
|
|
constructor() {
|
|
super();
|
|
this.attachShadow({mode:'open'});
|
|
}
|
|
static get observedAttributes() { return ['html'] };
|
|
|
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
if(name == 'html') {
|
|
this.html = newValue;
|
|
}
|
|
}
|
|
|
|
set html(value) {
|
|
if(typeof value == 'string') {
|
|
this.shadowRoot.innerHTML = value;
|
|
} else {
|
|
this.shadowRoot.innerHTML = '';
|
|
this.shadowRoot.append(value);
|
|
}
|
|
}
|
|
}
|
|
customElements.define('elm-html', ElmHTMLElement);
|
|
|
|
class NumbasPartElement extends HTMLElement {
|
|
constructor() {
|
|
super();
|
|
|
|
this.attachShadow({mode:'open'});
|
|
|
|
const style = document.createElement('link');
|
|
style.rel = 'stylesheet';
|
|
style.href = 'numbas-part.css';
|
|
this.shadowRoot.append(style);
|
|
|
|
this.init_app();
|
|
}
|
|
|
|
async init_app() {
|
|
const exam_id = this.getAttribute('exam');
|
|
const mode = this.getAttribute('mode');
|
|
const questionNumber = parseInt(this.getAttribute('question'));
|
|
const partPath = this.getAttribute('part');
|
|
|
|
const exam = await numbas_embedder.get_exam(exam_id).promise;
|
|
const container = document.createElement('div');
|
|
this.shadowRoot.append(container);
|
|
const app = Elm.App.init({
|
|
node: container,
|
|
flags: {exam: exam, view_mode: {mode, questionNumber, partPath}}
|
|
});
|
|
|
|
|
|
const message_handlers = {
|
|
'question': ({questionNumber, msg}) => {
|
|
const question = exam.questionList[questionNumber];
|
|
question.display.handle_message(msg);
|
|
}
|
|
}
|
|
|
|
app.ports.sendMessage.subscribe(data => {
|
|
const fn = message_handlers[data.msgtype];
|
|
if(fn) {
|
|
fn(data);
|
|
}
|
|
})
|
|
|
|
exam.events.on('update app', ({type, arg}) => {
|
|
//type != 'showTiming' && console.log('update app', {type,arg});
|
|
app.ports.receiveMessage.send({type, arg});
|
|
});
|
|
|
|
}
|
|
}
|
|
|
|
customElements.define('numbas-part', NumbasPartElement); |