X-Git-Url: http://git.shiar.nl/sheet.git/blobdiff_plain/e889c7543227d2aeee90553fea9c59524c2cec95..9b8d7e610cef31f15f12c1058dfd45efffac5fb0:/word/quiz.js diff --git a/word/quiz.js b/word/quiz.js index ef5e0d8..f47fde1 100644 --- a/word/quiz.js +++ b/word/quiz.js @@ -1,35 +1,109 @@ -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 option = put(form, 'li', suggest, {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; +}; + +Set.prototype.filter = function (f) { + return new Set([...this].filter(f)); +}; + +class WordQuiz { + dataselect(json) { + this.data = this.datafilter(json); + let rows = Object.values(this.data); + rows = rows.filter(row => !row[3].length); // remove referenced categories + return rows.shuffle(); + } + + datafilter(json) { + // find viable rows from json data + let ids = new Set(Object.keys(json)); + const selection = {...json}; // clone + + for (let cat of selection[''][3]) { + if (selection[cat]) + selection[cat][1] = 0; // keep root categories + } + + if (this.preset.cat !== undefined) { + ids.clear(); + let children = this.preset.cat; + for (let loop = 0; children.length && loop < 20; loop++) { + for (let child of children) ids.add(child.toString()); + children = children.map(cat => json[cat][3]).filter(is => is).flat() + } + } + if (this.preset.images) { + ids = ids.filter(id => json[id][2]); + } + if (this.preset.level !== undefined) { + ids = ids.filter(id => json[id][1] <= this.preset.level); + } + + // keep only wanted ids + for (let id in selection) { + if (id && !ids.has(id)) { + delete selection[id]; + } + } + + // retain orphaned references in grandparent categories + for (let id in selection) { + selection[id][3] = function subresolve(subs) { + //console.log(subs); + return (subs || []).flatMap(sub => + sub in selection ? [sub] : json[sub] ? subresolve(json[sub][3]) : [] + ); + }(selection[id][3]); + } + return selection; } - constructor() { - this.dataurl = '/data/wordlist.nl.json'; - fetch(this.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(); + configure(params) { + 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; + } + } + } + + load(dataurl) { + this.configure(window.location.hash.split('#')); + fetch(dataurl).then(res => res.json()).then(json => { + this.words = this.dataselect(json) + this.setup(); }); } -}; -new Quiz(); + 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(dataurl) { + this.preset = {images: true}; + this.load(dataurl); + this.history = []; + window.onbeforeunload = e => { + this.stop('abort'); + }; + window.onhashchange = e => { + this.load(dataurl); + }; + } +}