bb12a832c73b170e496dc09e25e6fb88aa0c8849
[sheet.git] / word / quiz.js
1 Array.prototype.shuffle = function () {
2         for (let i = this.length - 1; i > 0; i--) {
3                 const j = Math.floor(Math.random() * (i + 1)); // random index 0..i
4                 [this[i], this[j]] = [this[j], this[i]]; // swap elements
5         }
6         return this;
7 };
8
9 class WordQuiz {
10         dataselect(json) {
11                 // find viable rows from json data
12                 let rows = Object.values(json);
13                 if (this.preset.cat !== undefined) {
14                         let cats = {}; // category lookup
15                         for (let i in json) {
16                                 let cat = json[i][3];
17                                 if (!cats[cat]) cats[cat] = [];
18                                 cats[cat].push(i)
19                         }
20
21                         rows = [];
22                         let children = cats[this.preset.cat];
23                         for (let loop = 0; children.length && loop < 20; loop++) {
24                                 rows.push(...children);
25                                 children = children.map(cat => cats[cat]).filter(is => is).flat();
26                         }
27                         rows = rows.map(row => json[row]).filter(row => row[2]);
28                 }
29                 if (this.preset.level !== undefined) {
30                         rows = rows.filter(row => row[1] <= this.preset.level);
31                 }
32
33                 {
34                         let cats = new Set();
35                         let subcats = rows.map(row => row[3]); // direct parents
36                         for (let loop = 0; subcats.length && loop < 20; loop++) {
37                                 subcats.forEach(cat => cats.add(cat));
38                                 subcats = subcats.map(row => json[row] && json[row][3]).filter(val => val); // recurse grandparents
39                         }
40                         rows = rows.filter(row => !cats.has(row[2])); // remove referenced categories
41                 }
42                 return rows.shuffle();
43         }
44
45         load(dataurl) {
46                 this.preset = {};
47                 let input;
48                 if (input = window.location.hash.match(/\d+/)) {
49                         this.preset.cat = input[0];
50                 }
51                 if (window.location.hash.match(/a/)) {
52                         this.preset.level = 3;
53                 }
54
55                 fetch(dataurl).then(res => res.json()).then(json => {
56                         this.words = this.dataselect(json)
57                         this.setup();
58                 });
59         }
60
61         log(...args) {
62                 this.history.push([new Date().toISOString(), ...args]);
63         }
64
65         stop(...args) {
66                 this.log(...args);
67                 window.onbeforeunload = null;
68                 fetch('/word/report', {method: 'POST', body: JSON.stringify(this.history)});
69         }
70
71         constructor(dataurl) {
72                 this.load(dataurl);
73                 this.history = [];
74                 window.onbeforeunload = e => {
75                         this.stop('abort');
76                 };
77         }
78 }