/* 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:[]}; /* ankerbibliotheek: los van borden bewaarde ankerdiagrammen, herbruikbaar op elk bord. [{id, name, folder?, cells, pics}] - cells/pics in dezelfde vorm als de widget-staat van anchor.js; folder is een pad ("A/B") zoals bij de thema's en borden in de mappenverkenner. */ let ANCHOR_LIB = []; 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; } /* ---- ARASAAC pictogram lookup (shared by the word editor + anchor grid) ---- */ async function arasaacSearch(term, lang){ const resp = await fetch(`https://api.arasaac.org/api/pictograms/${lang}/search/${encodeURIComponent(term)}`); if(!resp.ok) throw new Error("http "+resp.status); return resp.json(); } const arasaacUrl = id => `https://static.arasaac.org/pictograms/${id}/${id}_300.png`; /* ---- 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); /* thema's zonder map bovenaan, daarna per map gegroepeerd (optgroup) - zelfde indeling als de theme-dropdown in de woorden-editor */ const groups = new Map(); THEMES[LANG].forEach((th,i)=>{ if(!th.words.length) return; const f = th.folder || ""; if(!groups.has(f)) groups.set(f, []); groups.get(f).push([th, i]); }); const addOpt = (parent, th, i)=>{ const o = document.createElement("option"); o.value = "t"+i; o.textContent = "โ˜… "+th.name; parent.appendChild(o); }; (groups.get("") || []).forEach(([th,i])=>addOpt(sel, th, i)); [...groups.keys()].filter(f=>f).sort().forEach(f=>{ const grp = document.createElement("optgroup"); grp.label = "๐Ÿ“ " + f; groups.get(f).forEach(([th,i])=>addOpt(grp, th, i)); sel.appendChild(grp); }); 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("โœ‹๐Ÿ‘‚๐Ÿ‘ƒ๐Ÿ‘๏ธ๐Ÿ‘„๐Ÿฆถ๐Ÿ˜€๐Ÿ˜ข๐Ÿ˜ด๐Ÿคง๐Ÿ‘ถ๐Ÿ‘ฆ๐Ÿ‘ง๐Ÿ‘ต๐Ÿ‘ด") ];