From fa43237a3eb6cc3f399fd3b2584ef6152a07dc1e Mon Sep 17 00:00:00 2001 From: Mischa POSLAWSKY Date: Sun, 19 Jun 2022 02:20:22 +0200 Subject: [PATCH] word/quiz: objects to access row array elements --- word/finder.js | 25 +++++---- word/memory.js | 2 +- word/multichoice.js | 13 +++-- word/quiz.js | 123 +++++++++++++++++++++++++++++++------------- word/wijzer.js | 7 ++- 5 files changed, 108 insertions(+), 62 deletions(-) diff --git a/word/finder.js b/word/finder.js index e720bfd..630db8e 100644 --- a/word/finder.js +++ b/word/finder.js @@ -1,6 +1,5 @@ class WordFinder extends WordQuiz { namehtml(name) { - //let wbr = new RegExp('\w{4} [^aoeuiyc\W] [rl]?+ \K (?= [^aoeuiy\W] [rl]? [aoeuiy] \w)', 'g'); let aliases = name.split('/'); let html = aliases.shift(); html = html.replace(/\((.+)\)/, '$1'); @@ -11,29 +10,29 @@ class WordFinder extends WordQuiz { } add(catitem, rows) { - rows.forEach(ref => { - const [title, level, imgid, subs] = this.data[ref]; + rows.forEach(word => { + if (!word) return; const worditem = put(catitem, 'li'); const figitem = put(worditem, 'figure'); - if (imgid) { - put(figitem, 'img[src=$]', `/data/word/32/${imgid}.jpg`); + if (word.imgid) { + put(figitem, 'img[src=$]', word.thumb()); } - if (title) { + if (word.title) { put(figitem, 'figcaption', { - innerHTML: this.namehtml(title), + innerHTML: this.namehtml(word.title), }); } - put(worditem, '.level' + level); - if (level <= 1 && subs.length >= 4) { + put(worditem, '.level' + word.level); + if (word.level <= 1 && word.subs.length >= 4) { put(worditem, '.large'); } - if (subs.length) { + if (word.subs.length) { // delve into subcategory put(worditem, '.parent'); - this.add(put(worditem, 'ul'), subs); + this.add(put(worditem, 'ul'), word.subs); } if (this.preset.debug) { - put(figitem, '[title=$]', `id ${ref} level ${level}`); + put(figitem, '[title=$]', `id ${ref} level ${word.level}`); } }); } @@ -51,7 +50,7 @@ class WordFinder extends WordQuiz { } this.form.innerHTML = ''; put(this.form, 'p', 'Under construction.'); - for (let cat of this.preset.cat || this.data[''][3]) { + for (let cat of this.data.root()) { this.add(put(this.form, 'ul.gallery'), [cat]); } } diff --git a/word/memory.js b/word/memory.js index a70ec06..b539b2d 100644 --- a/word/memory.js +++ b/word/memory.js @@ -69,7 +69,7 @@ class WordMemory extends WordQuiz { let cols = Math.round(Math.sqrt(count) * aspect**.5); count = cols * Math.ceil(count / cols); this.form.style['grid-template-columns'] = `repeat(${cols}, 1fr)`; - cards = this.words.splice(0, count>>1).map(row => row[2]); + cards = this.words.splice(0, count>>1).map(row => row.imgid); cards.push(...cards.map(val => -val)); } else { diff --git a/word/multichoice.js b/word/multichoice.js index 7402507..300a32f 100644 --- a/word/multichoice.js +++ b/word/multichoice.js @@ -3,17 +3,16 @@ class WordMultichoice extends WordQuiz { if (this.words.length < 4) return; let word = this.words.shift(); let form = put(this.form, - '+img[src=$]+ul', `/data/word/32/${word[2]}.jpg`, + '+img[src=$]+ul', word.thumb() ); - let answers = [word[0], this.words[0][0], this.words[1][0], this.words[2][0]] + let answers = [word, this.words[0], this.words[1], this.words[2]] .shuffle() - this.log('ask', word[2], answers); + this.log('ask', word.id, answers.map(w => w.id)); answers.forEach(suggest => { - let label = suggest.replace(/\/.*/, ''); - let option = put(form, 'li', label, {onclick: () => { - this.log('pick', suggest, null, word[0]); - if (suggest != word[0]) { + let option = put(form, 'li', suggest.label, {onclick: () => { + this.log('pick', suggest.id, null, word.id); + if (suggest.label != word.label) { // incorrect put(option, '.wrong'); return; diff --git a/word/quiz.js b/word/quiz.js index 3243b19..5283628 100644 --- a/word/quiz.js +++ b/word/quiz.js @@ -6,59 +6,108 @@ Array.prototype.shuffle = function () { return this; }; -Set.prototype.filter = function (f) { - return new Set([...this].filter(f)); -}; +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); + } + } + + *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); - let rows = Object.values(this.data); - rows = rows.filter(row => !row[3].length); // remove referenced categories - return rows.shuffle(); + return [...this.data.random()]; } 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 - } + const selection = new Words(json, this.preset.cat); - 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]); + selection.filter(id => json[id][2]); } if (this.preset.level !== undefined) { - ids = ids.filter(id => json[id][1] <= this.preset.level); + selection.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]; - } + if (this.preset.distinct) { + // remove referenced categories + selection.filter(id => !selection.get(id).subs.length); } - // 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; } diff --git a/word/wijzer.js b/word/wijzer.js index 2f73052..7ae3d3e 100644 --- a/word/wijzer.js +++ b/word/wijzer.js @@ -12,8 +12,8 @@ class WordWijzer extends WordQuiz { this.question.innerHTML = ''; put(this.question, - '[data-id=$] img[src=$]', word[2], - `/data/word/32/${word[2]}.jpg` + '[data-id=$] img[src=$]', word.id, + word.thumb() ); } @@ -36,9 +36,8 @@ class WordWijzer extends WordQuiz { let answers = put(this.form, 'ul'); this.words .forEach((answer, seq) => { - let label = answer[0].replace(/\/.*/, ''); // primary form put(answers, 'li[data-id=$][onclick=""]', - answer[2], label, { + answer.id, answer.label, { onclick: e => this.verify(e), index: seq, } -- 2.30.0