All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 8s
132 lines
5.7 KiB
JavaScript
132 lines
5.7 KiB
JavaScript
/* teach - woorddata: algemene kaart, thema's, gedeelde woordbronnen */
|
||
/* =========================================================
|
||
Letter game data
|
||
==========================================================*/
|
||
const DOTCODE = {
|
||
a:[1], b:[1,2], c:[1,4], d:[1,4,5], e:[1,5], f:[1,2,4], g:[1,2,4,5], h:[1,2,5],
|
||
i:[2,4], j:[2,4,5], k:[1,3], l:[1,2,3], m:[1,3,4], n:[1,3,4,5], o:[1,3,5],
|
||
p:[1,2,3,4], q:[1,2,3,4,5], r:[1,2,3,5], s:[2,3,4], t:[2,3,4,5], u:[1,3,6],
|
||
v:[1,2,3,6], w:[2,4,5,6], x:[1,3,4,6], y:[1,3,4,5,6], z:[1,3,5,6],
|
||
/* digits use the braille number patterns (a–j) */
|
||
"1":[1], "2":[1,2], "3":[1,4], "4":[1,4,5], "5":[1,5],
|
||
"6":[1,2,4], "7":[1,2,4,5], "8":[1,2,5], "9":[2,4], "0":[2,4,5]
|
||
};
|
||
const VOWELS = "aeiou";
|
||
function dotGrid(letter){
|
||
const code = DOTCODE[letter] || [];
|
||
const grid = document.createElement("div");
|
||
grid.className = "dots";
|
||
[1,4,2,5,3,6].forEach(p=>{
|
||
const d = document.createElement("i");
|
||
d.className = code.includes(p) ? "on" : "off";
|
||
grid.appendChild(d);
|
||
});
|
||
return grid;
|
||
}
|
||
|
||
/* built-in general words: ONE card per language (extendable via the editor) */
|
||
const WORDS = {
|
||
nl:[
|
||
["aap","🐵"],["vis","🐟"],["zon","☀️"],["kat","🐈"],["bal","⚽"],["ei","🥚"],
|
||
["boom","🌳"],["huis","🏠"],["maan","🌙"],["boot","⛵"],["kip","🐔"],["muis","🐭"],
|
||
["boek","📕"],["ster","⭐"],["roos","🌹"],["vuur","🔥"],["fiets","🚲"],["taart","🎂"],
|
||
["jas","🧥"],["bril","👓"],["klok","⏰"],["peer","🍐"],["kaas","🧀"],["eend","🦆"],
|
||
["hand","✋"],["voet","🦶"],["neus","👃"],["oog","👁️"],["oor","👂"],["mond","👄"]
|
||
],
|
||
en:[
|
||
["cat","🐈"],["dog","🐕"],["sun","☀️"],["egg","🥚"],["ball","⚽"],["fish","🐟"],
|
||
["tree","🌳"],["moon","🌙"],["boat","⛵"],["star","⭐"],["book","📕"],["cake","🎂"],
|
||
["house","🏠"],["mouse","🐭"],["apple","🍎"],["train","🚂"],["fire","🔥"],["rose","🌹"],
|
||
["bear","🐻"],["duck","🦆"],["frog","🐸"],["bee","🐝"],["cow","🐄"],["pig","🐷"],
|
||
["hand","✋"],["foot","🦶"],["nose","👃"],["eye","👁️"],["ear","👂"],["milk","🥛"]
|
||
]
|
||
};
|
||
/* teacher's own additions to the general card (persisted per user) */
|
||
let GENERAL_EXTRA = {nl:[], en:[]};
|
||
function generalList(){ return WORDS[LANG].concat(GENERAL_EXTRA[LANG]); }
|
||
const shuffle = a => { a=a.slice(); for(let i=a.length-1;i>0;i--){const j=Math.floor(Math.random()*(i+1)); [a[i],a[j]]=[a[j],a[i]];} return a; };
|
||
|
||
/* custom themes: folders of own words.
|
||
{nl:[{name:"Herfst", words:[[word,pic],...]}], en:[...]} */
|
||
let THEMES = {nl:[], en:[]};
|
||
function themesFromData(data){
|
||
if(!data) return {nl:[],en:[]};
|
||
if(data.themes) return data.themes;
|
||
/* migrate old flat word lists into one theme */
|
||
const t = {nl:[],en:[]};
|
||
if(data.words) ["nl","en"].forEach(l=>{
|
||
if(Array.isArray(data.words[l]) && data.words[l].length)
|
||
t[l] = [{name: l==="nl" ? "Mijn woorden" : "My words", words: data.words[l]}];
|
||
});
|
||
return t;
|
||
}
|
||
const isImg = p => typeof p==="string" && (p.startsWith("data:") || p.startsWith("http"));
|
||
function setPic(el, pic){
|
||
if(isImg(pic)){ el.innerHTML = ""; const im = document.createElement("img"); im.src = pic; im.alt=""; el.appendChild(im); }
|
||
else el.textContent = pic;
|
||
}
|
||
|
||
/* ---- shared word source (general card + themes + anchor grids) ---- */
|
||
/* anchor-word widgets register their words here so every language widget
|
||
can use them as a theme automatically */
|
||
let ANCHORS = []; /* [{el, name, words:[[w,pic],...]}] */
|
||
function anchorSources(){
|
||
ANCHORS = ANCHORS.filter(a=>a.el && document.contains(a.el));
|
||
return ANCHORS.filter(a=>a.words.length);
|
||
}
|
||
function normWordSource(card){
|
||
if(typeof card==="string"){
|
||
if(card.startsWith("t")){
|
||
const th = THEMES[LANG][+card.slice(1)];
|
||
if(th && th.words.length) return card;
|
||
}else if(card.startsWith("a")){
|
||
if(anchorSources()[+card.slice(1)]) return card;
|
||
}
|
||
}
|
||
return "g";
|
||
}
|
||
function wordSourceList(card){
|
||
card = normWordSource(card);
|
||
if(card.startsWith("t")) return THEMES[LANG][+card.slice(1)].words;
|
||
if(card.startsWith("a")) return anchorSources()[+card.slice(1)].words;
|
||
return generalList();
|
||
}
|
||
function fillWordSourceSel(sel, card){
|
||
sel.innerHTML = "";
|
||
const og = document.createElement("option");
|
||
og.value = "g"; og.textContent = T("cardGeneral");
|
||
sel.appendChild(og);
|
||
THEMES[LANG].forEach((th,i)=>{
|
||
if(!th.words.length) return;
|
||
const o = document.createElement("option");
|
||
o.value = "t"+i; o.textContent = "★ "+th.name;
|
||
sel.appendChild(o);
|
||
});
|
||
anchorSources().forEach((a,i)=>{
|
||
const o = document.createElement("option");
|
||
o.value = "a"+i; o.textContent = "⚓ "+a.name;
|
||
sel.appendChild(o);
|
||
});
|
||
sel.value = normWordSource(card);
|
||
if(sel.selectedIndex < 0) sel.selectedIndex = 0;
|
||
}
|
||
|
||
/* emoji palette (grouped) */
|
||
const splitEmoji = s => {
|
||
const out = [];
|
||
for(const ch of s){
|
||
if(out.length && (ch==="️" || ch==="")) out[out.length-1]+=ch;
|
||
else if(out.length && out[out.length-1].endsWith("")) out[out.length-1]+=ch;
|
||
else out.push(ch);
|
||
}
|
||
return out;
|
||
};
|
||
const EMOJI_CATS = [
|
||
splitEmoji("🐵🐶🐱🐭🐹🐰🦊🐻🐼🐨🐯🦁🐮🐷🐸🐔🐧🐦🦆🦉🐴🦄🐝🐛🦋🐌🐞🐠🐟🐬🐳🐢🐍🦎🐘🦒"),
|
||
splitEmoji("🍎🍐🍊🍋🍌🍉🍇🍓🍒🍑🥝🍅🥕🌽🥔🧀🥚🍞🥐🥞🍕🍟🍦🍪🎂🍫🍬🥛🧃🍯"),
|
||
splitEmoji("🌳🌲🌴🌵🌷🌹🌻🌸💐🍄⭐🌙☀️⛅🌈❄️🔥💧🌊🪨"),
|
||
splitEmoji("🚗🚕🚌🚑🚒🚜🚲🛴🏍️🚂✈️🚁⛵🚀🛶🚢"),
|
||
splitEmoji("⚽🏀🎈🎁🧸📕✏️🖍️✂️🎨🥁🎸👓⌚⏰💡🔑🧤🧢👟👗🧥🛏️🪑🚪🏠"),
|
||
splitEmoji("✋👂👃👁️👄🦶😀😢😴🤧👶👦👧👵👴")
|
||
];
|
||
|