X-Git-Url: http://git.shiar.nl/sheet.git/blobdiff_plain/0f3a55ca3cfa02e2446a20dc050d04490b23bb8d..5d0bed0b1a591ba0f12526961a5d857d36df0267:/word/quiz.js diff --git a/word/quiz.js b/word/quiz.js index 0518fa1..dce5f7a 100644 --- a/word/quiz.js +++ b/word/quiz.js @@ -1,33 +1,169 @@ -class Quiz { - next() { - let word = this.words.shift(); - let form = put(this.form, - '+img[src=$]+ul', `/data/word/en/${word[0]}.jpg`, - ); - - let answers = [word[2], this.words[1][2], this.words[2][2], this.words[3][2]] - .sort(() => {return .5 - Math.random()}) // shuffle - answers.forEach(suggest => { - let label = suggest.replace(/\/.*/, ''); - let option = put(form, 'li', label, {onclick: () => { - if (suggest != word[2]) { - // incorrect - put(option, '.wrong'); - return; - } - put(option, '.good'); - window.setTimeout(() => this.next(), 500); - }}); - }); +Array.prototype.shuffle = function () { + for (let i = this.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); // random index 0..i + [this[i], this[j]] = [this[j], this[i]]; // swap elements + } + return this; +}; + +function hashparams() { + // location.hash is not encoded in firefox + const encodedhash = (window.location.href.split('#'))[1] || ''; + return decodeURIComponent(encodedhash).split('#'); +} + +class Words { + constructor(data, root = undefined) { + this.data = data; + this.selection = root || this.data[''][3]; + this.visible = new Set(root || Object.keys(data).flatMap(id => id && parseInt(id))); + if (root) { + let children = root; + for (let loop = 0; children.length && loop < 20; loop++) { + for (let child of children) this.visible.add(child); + children = children.map(cat => data[cat][3]).filter(is => is).flat(); + } + } + } + + filter(f) { + // keep only matching entries, and root selection regardless + this.visible = new Set([...this.visible].filter(f).concat(this.selection)); + } + + *root() { + for (let i of this.selection) { + if (!this.has(i)) { + continue; + } + yield this.get(i); + } } - constructor(dataurl) { - fetch(dataurl).then(res => res.json()).then(json => { - this.form = document.getElementById('quiz'); - this.words = Object.values(json) - .sort(() => {return .5 - Math.random()}) // shuffle - .map(row => row.split(/:/)) - this.next(); + *random() { + let order = [...this.visible.keys()].shuffle(); + for (let i of order) { + if (!this.has(i)) { + continue; + } + yield this.get(i); + } + } + + has(id) { + return this.visible.has(id); + } + + subs(id) { + let refs = this.data[id][3]; + if (!refs) { + return []; + } + for (let ref of refs) { + // retain orphaned references in grandparent categories + if (!this.has(ref)) { + refs = refs.concat(this.subs(ref)); + } + } + return refs; + } + + get(id) { + if (!this.has(id)) { + return; + } + const p = this; + const row = this.data[id]; + return row && { + id: id, + title: row[0], + get label() { + return row[0].replace(/\/.*/, ''); // primary form + }, + level: row[1], + imgid: row[2], + thumb(size = 32) { + return `/data/word/${size}/${row[2]}.jpg`; + }, + get subs() { + return p.subs(id).map(e => p.get(e)); + }, + }; + } +} + +class WordQuiz { + dataselect(json) { + this.data = this.datafilter(json); + return [...this.data.random()]; + } + + datafilter(json) { + // find viable rows from json data + const selection = new Words(json, this.preset.cat); + + if (this.preset.images) { + selection.filter(id => json[id][2]); + } + if (this.preset.level !== undefined) { + selection.filter(id => json[id][1] <= this.preset.level); + } + + if (this.preset.distinct) { + // remove referenced categories + selection.filter(id => !selection.get(id).subs.length); + } + + return selection; + } + + configure(params = hashparams()) { + const opts = new Map(params.map(arg => arg.split(/[:=](.*)/))); + for (let [query, val] of opts) { + if (query.match(/^\d+$/)) { + this.preset.cat = [parseInt(query)]; + } + else if (query === 'level') { + this.preset.level = parseInt(val); + } + else { + this.preset[query] = val; + } + } + this.preset.dataurl = `/data/wordlist.${this.preset.lang}.json` + } + + setup() { + this.form = document.getElementById('quiz'); + } + + load() { + this.configure(); + fetch(this.preset.dataurl).then(res => res.json()).then(json => { + this.words = this.dataselect(json) + this.setup(); }); } -}; + + log(...args) { + this.history.push([new Date().toISOString(), ...args]); + } + + stop(...args) { + this.log(...args); + window.onbeforeunload = null; + fetch('/word/report', {method: 'POST', body: JSON.stringify(this.history)}); + } + + constructor() { + this.preset = {images: true, lang: 'en'}; + this.load(); + this.history = []; + window.onbeforeunload = e => { + this.stop('abort'); + }; + window.onhashchange = e => { + this.load(); + }; + } +}