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
12 this.cats = {}; // category lookup
16 this.cats[cat].push(i);
22 return this.datafilter(json);
26 // find viable rows from json data
27 let rows = Object.values(json);
29 if (this.preset.cat !== undefined) {
31 let children = this.cats[this.preset.cat];
32 for (let loop = 0; children.length && loop < 20; loop++) {
33 rows.push(...children);
34 children = children.map(cat => this.cats[cat]).filter(is => is).flat();
36 rows = rows.map(row => json[row]).filter(row => row[2]);
38 if (this.preset.level !== undefined) {
39 rows = rows.filter(row => row[1] <= this.preset.level);
44 let subcats = rows.map(row => row[3]); // direct parents
45 for (let loop = 0; subcats.length && loop < 20; loop++) {
46 subcats.forEach(cat => cats.add(cat));
47 subcats = subcats.map(row => json[row] && json[row][3]).filter(val => val); // recurse grandparents
49 rows = rows.filter(row => !cats.has(row[2])); // remove referenced categories
51 return rows.shuffle();
57 if (input = window.location.hash.match(/\d+/)) {
58 this.preset.cat = input[0];
60 if (window.location.hash.match(/a/)) {
61 this.preset.level = 3;
64 fetch(dataurl).then(res => res.json()).then(json => {
65 this.words = this.dataselect(json)
71 this.history.push([new Date().toISOString(), ...args]);
76 window.onbeforeunload = null;
77 fetch('/word/report', {method: 'POST', body: JSON.stringify(this.history)});
80 constructor(dataurl) {
83 window.onbeforeunload = e => {