X-Git-Url: http://git.shiar.nl/sheet.git/blobdiff_plain/30bf0fcde3f7e44f1ec7bcfc8253dfce8f81e8e6..20332e82f5c6365a8d65c75192985d846a99a2dd:/word/quiz.js diff --git a/word/quiz.js b/word/quiz.js index ce60d3a..d32368b 100644 --- a/word/quiz.js +++ b/word/quiz.js @@ -1,41 +1,87 @@ -let quiz = { -dataurl: '/data/wordlist.nl.json', - -next: () => { - let word = quiz.words.shift(); - let question = document.createElement('img'); - question.src = `/data/word/en/${word[0]}.jpg`; - question.style.maxWidth = '50%'; - - let answers = [word[2], quiz.words[1][2], quiz.words[2][2], quiz.words[3][2]] - .sort(() => {return .5 - Math.random()}) // shuffle - let form = document.createElement('ul'); - answers.forEach(suggest => { - let option = document.createElement('li'); - option.onclick = () => { - if (suggest != word[2]) { - // incorrect - option.classList.add('wrong'); - return; - } - option.classList.add('good'); - window.setTimeout(quiz.next, 500); - }; - option.append(suggest); - form.append(option); - }); - quiz.form.append(question, form); -}, - -setup: () => { - fetch(quiz.dataurl).then(res => res.json()).then(json => { - quiz.form = document.getElementById('quiz'); - quiz.words = Object.values(json) - .sort(() => {return .5 - Math.random()}) // shuffle - .map(row => row.split(/:/)) - quiz.next(); - }); -}, +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; }; -quiz.setup(); +class WordQuiz { + dataselect(json) { + this.data = json; + this.cats = {}; // category lookup + for (let i in json) { + let cat = json[i][3]; + if (this.cats[cat]) { + this.cats[cat].push(i); + } + else { + this.cats[cat] = [i]; + } + } + return this.datafilter(json); + } + + datafilter(json) { + // find viable rows from json data + let rows = Object.values(json); + + if (this.preset.cat !== undefined) { + rows = []; + let children = this.cats[this.preset.cat]; + for (let loop = 0; children.length && loop < 20; loop++) { + rows.push(...children); + children = children.map(cat => this.cats[cat]).filter(is => is).flat(); + } + rows = rows.map(row => json[row]).filter(row => row[2]); + } + if (this.preset.level !== undefined) { + rows = rows.filter(row => row[1] <= this.preset.level); + } + + { + let cats = new Set(); + let subcats = rows.map(row => row[3]); // direct parents + for (let loop = 0; subcats.length && loop < 20; loop++) { + subcats.forEach(cat => cats.add(cat)); + subcats = subcats.map(row => json[row] && json[row][3]).filter(val => val); // recurse grandparents + } + rows = rows.filter(row => !cats.has(row[2])); // remove referenced categories + } + return rows.shuffle(); + } + + load(dataurl) { + this.preset = {}; + let input; + if (input = window.location.hash.match(/\d+/)) { + this.preset.cat = input[0]; + } + if (window.location.hash.match(/a/)) { + this.preset.level = 3; + } + + fetch(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(dataurl) { + this.load(dataurl); + this.history = []; + window.onbeforeunload = e => { + this.stop('abort'); + }; + } +}