Voeg thematische kleurplaten en creatieve niveaus toe (v0.4.65-beta) #3

Open
bes-r wants to merge 207 commits from bes-r/kleurplaten-v0-4-65 into main AGit
5 changed files with 22 additions and 12 deletions
Showing only changes of commit 2fa9879107 - Show all commits

View file

@ -1 +1 @@
0.3.50-beta
0.3.51-beta

View file

@ -1456,15 +1456,17 @@ body.dark .bd-stage{background:radial-gradient(circle at 50% 20%,var(--surface-2
.money-top{position:sticky;top:-16px;z-index:6;width:100%;display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin-bottom:8px;padding:8px 4px;background:var(--surface);border-bottom:1px solid var(--line)}.money-level-control{display:flex;align-items:center;gap:6px}.money-level-control>span{color:var(--muted);font-size:12px;font-weight:900}.money-levels button{min-width:31px;padding:6px 9px;white-space:nowrap}.money-mode{width:36px;padding:0;font-size:18px}.money-score{margin-left:auto;padding:6px 10px;border-radius:999px;background:var(--surface-2);color:var(--muted);font-size:12px;font-weight:900}
.money-task-card{width:min(100%,650px);margin:0 auto;display:flex;flex-direction:column;align-items:center;gap:12px;padding:16px;border:1px solid var(--line);border-radius:20px;background:var(--surface);box-shadow:var(--shadow-1)}.money-task-card h3{margin:0;text-align:center;font-size:clamp(19px,3vw,25px)}
.money-target{width:100%;min-height:98px;display:flex;align-items:center;justify-content:center;gap:10px;flex-wrap:wrap;padding:12px;border-radius:16px;background:var(--surface-2)}.money-target>span:first-child{flex-basis:100%;color:var(--muted);font-size:12px;font-weight:900;text-transform:uppercase;letter-spacing:.5px}.money-assignment{display:flex;align-items:center;justify-content:center;gap:8px;flex-wrap:wrap;text-align:center}.money-task-amount{font-size:clamp(45px,10vw,72px);line-height:1;font-weight:950;color:var(--green)}.money-task-words{flex-basis:100%;margin:0;font-size:clamp(21px,4vw,31px);line-height:1.15;font-weight:950;color:var(--purple)}.money-assignment.compact .money-task-amount{font-size:clamp(38px,8vw,58px)}.money-assignment.compact .money-task-words{font-size:clamp(19px,4vw,27px)}
.money-wallet{width:100%;min-height:112px;padding:10px;border:3px dashed var(--line);border-radius:18px;background:linear-gradient(145deg,var(--surface-2),var(--surface));transition:border-color .15s,background .15s,transform .15s}.money-wallet>div:first-child{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:8px}.money-wallet output{font-size:24px;font-weight:950;color:var(--green)}.money-wallet>p{margin:22px auto;color:var(--muted);font-weight:800;text-align:center}.money-wallet.droptarget{border-color:var(--green);background:#e4f6eb;transform:scale(1.01)}
.money-pile{display:flex;align-items:center;justify-content:center;gap:8px;flex-wrap:wrap;min-height:54px}.money-piece{display:grid;place-items:center;flex:none;border:2px solid rgba(35,45,60,.3);font:900 13px/1 system-ui;color:#26354a;box-shadow:0 3px 5px rgba(20,35,60,.2);user-select:none}.money-piece:is(button){cursor:grab;touch-action:none}.money-piece:is(button):active{cursor:grabbing}.money-piece.coin{width:48px;height:48px;border-radius:50%;background:radial-gradient(circle at 35% 30%,#fff3a9,#d5a72e 68%,#a87812)}.money-piece.coin[data-value="100"]{background:radial-gradient(circle,#e9edf0 0 46%,#d7ad37 48% 70%,#ffec92)}.money-piece.coin[data-value="200"]{background:radial-gradient(circle,#e6bd4b 0 45%,#dfe4e8 48% 71%,#fff)}.money-piece.coin[data-value="1"],.money-piece.coin[data-value="2"],.money-piece.coin[data-value="5"]{background:radial-gradient(circle at 35% 30%,#ffd1b1,#b9683d 72%,#824125)}.money-piece.note{width:76px;height:43px;border-radius:7px;background:linear-gradient(135deg,#c9c9c2,#f0eee2);font-size:15px}.money-piece.note[data-value="500"]{background:linear-gradient(135deg,#a6b7a3,#e7ddd0)}.money-piece.note[data-value="1000"]{background:linear-gradient(135deg,#d09a91,#f4d2b5)}.money-piece.note[data-value="2000"]{background:linear-gradient(135deg,#7eb2c4,#d2e9d8)}.money-piece.note[data-value="5000"]{background:linear-gradient(135deg,#d99f58,#f1d6a4)}.money-drag-clone{opacity:.95}
.money-wallet{width:100%;min-height:112px;padding:10px;border:3px dashed var(--line);border-radius:18px;background:linear-gradient(145deg,var(--surface-2),var(--surface));transition:border-color .15s,background .15s,transform .15s}.money-wallet>div:first-child{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:8px}.money-wallet output{font-size:24px;font-weight:950;color:var(--green)}.money-wallet>p{margin:22px auto;color:var(--muted);font-weight:800;text-align:center}.money-wallet.droptarget{border-color:var(--green);background:#e4f6eb;transform:scale(1.01)}.money-wallet.money-remove-target{border-color:var(--red);background:#fff0ed;box-shadow:0 0 0 4px rgba(190,55,45,.1)}
.money-pile{display:flex;align-items:center;justify-content:center;gap:7px;flex-wrap:wrap;min-height:58px}.money-piece{position:relative;display:block;flex:none;overflow:hidden;padding:0;border:1px solid rgba(30,40,55,.48);font-family:Arial,sans-serif;color:#26354a;box-shadow:inset 0 1px 1px rgba(255,255,255,.75),inset 0 -2px 3px rgba(35,30,15,.2),0 3px 5px rgba(20,35,60,.24);user-select:none}.money-piece:is(button){cursor:grab;touch-action:none}.money-piece:is(button):active{cursor:grabbing}.money-piece:is(button):focus-visible{outline:3px solid var(--accent);outline-offset:3px}
.money-piece.coin{width:44px;height:44px;border-radius:50%;background:radial-gradient(circle at 34% 28%,#fff1a6 0,#dfbd53 35%,#bd8420 72%,#8c5d0c 100%)}.money-piece.coin::before{content:'';position:absolute;inset:2px;border:1px dashed rgba(65,53,25,.48);border-radius:50%}.money-piece.coin::after{content:'';position:absolute;inset:5px;border:1px solid rgba(255,255,255,.34);border-radius:50%;box-shadow:inset 0 0 0 1px rgba(45,38,20,.13)}.money-stars{position:absolute;z-index:2;left:5px;right:5px;top:3px;overflow:hidden;font-size:5px;line-height:1;letter-spacing:.2px;white-space:nowrap;color:rgba(44,45,42,.72)}.money-map{position:absolute;z-index:2;right:7px;top:14px;font:900 8px/1 Arial;color:rgba(48,57,57,.55);transform:rotate(-9deg)}.money-value{position:absolute;z-index:3;left:7px;bottom:7px;font-size:18px;line-height:1;letter-spacing:-1px;text-shadow:0 1px rgba(255,255,255,.7)}.money-unit{position:absolute;z-index:3;left:8px;bottom:2px;font-size:5px;font-weight:900;letter-spacing:.3px}.money-piece.coin[data-value="1"]{width:36px;height:36px}.money-piece.coin[data-value="2"]{width:39px;height:39px}.money-piece.coin[data-value="5"]{width:42px;height:42px}.money-piece.coin[data-value="10"]{width:44px;height:44px}.money-piece.coin[data-value="20"]{width:47px;height:47px}.money-piece.coin[data-value="50"]{width:50px;height:50px}.money-piece.coin[data-value="100"]{width:52px;height:52px;background:radial-gradient(circle,#e8ebeb 0 46%,#ca9c2b 48% 69%,#f4d970 71% 78%,#a26d0c 100%)}.money-piece.coin[data-value="200"]{width:56px;height:56px;background:radial-gradient(circle,#e1b33d 0 44%,#eee 47% 69%,#aeb8bd 72% 80%,#727d82 100%)}.money-piece.coin[data-value="1"],.money-piece.coin[data-value="2"],.money-piece.coin[data-value="5"]{background:radial-gradient(circle at 35% 28%,#ffd4b2 0,#d68a5b 40%,#a6532f 74%,#71341e 100%)}.money-piece.coin[data-value="20"]{border-radius:43% 50% 46% 49%}
.money-piece.note{height:43px;border-radius:3px;background:#c9c9c2;font-size:15px}.money-piece.note::before{content:'';position:absolute;inset:3px;background:repeating-linear-gradient(25deg,rgba(255,255,255,.24) 0 2px,rgba(30,45,60,.08) 2px 3px,transparent 3px 6px);border:1px solid rgba(255,255,255,.35)}.money-piece.note::after{content:'';position:absolute;z-index:2;left:53%;top:0;width:2px;height:100%;background:rgba(50,55,65,.32);box-shadow:1px 0 rgba(255,255,255,.35)}.money-note-name{position:absolute;z-index:3;left:5px;top:3px;font-size:6px;font-weight:900;letter-spacing:.5px}.money-piece.note .money-value{left:5px;bottom:4px;font-size:22px}.money-arch{position:absolute;z-index:3;right:15px;top:10px;width:24px;height:21px;border:2px solid rgba(40,55,70,.48);border-bottom-width:3px;border-radius:12px 12px 2px 2px}.money-arch::after{content:'';position:absolute;left:7px;top:5px;width:7px;height:12px;border:1px solid rgba(40,55,70,.42);border-bottom:0;border-radius:5px 5px 0 0}.money-hologram{position:absolute;z-index:4;right:3px;top:3px;width:8px;height:calc(100% - 6px);display:grid;place-items:center;background:linear-gradient(160deg,rgba(255,255,255,.8),rgba(116,151,166,.5),rgba(240,205,116,.7));font-size:8px;color:#42556a}.money-serial{position:absolute;z-index:3;right:14px;bottom:2px;font-size:4px;letter-spacing:.5px;color:rgba(30,40,55,.65)}.money-piece.note[data-value="500"]{width:82px;height:42px;background:linear-gradient(135deg,#a9aca8,#e8e3d8 52%,#9ea6a1)}.money-piece.note[data-value="1000"]{width:86px;height:45px;background:linear-gradient(135deg,#bd6e6b,#f0b3a5 52%,#ae5d5d)}.money-piece.note[data-value="2000"]{width:89px;height:48px;background:linear-gradient(135deg,#5b96b6,#b9dce5 52%,#4b839f)}.money-piece.note[data-value="5000"]{width:93px;height:51px;background:linear-gradient(135deg,#d1843f,#f1c782 52%,#bd6e2c)}.money-drag-clone{opacity:.96}.money-drag-clone.from-wallet{opacity:.9;filter:drop-shadow(0 14px 18px rgba(160,45,45,.3))}
.money-tray-wrap{width:100%;display:flex;flex-direction:column;align-items:center;gap:8px}.money-tray-wrap>b{color:var(--muted);font-size:12px;text-transform:uppercase;letter-spacing:.4px}.money-cash-tray{width:100%;display:flex;align-items:center;justify-content:center;gap:8px;flex-wrap:wrap;padding:11px;border:1px solid var(--line);border-radius:16px;background:var(--surface-2)}
.money-count-task{width:100%;min-height:128px;display:grid;place-items:center;padding:14px;border-radius:17px;background:linear-gradient(145deg,#eef7e9,var(--surface-2));border:1px solid var(--line)}.money-count-task .money-piece{transform:rotate(var(--money-tilt,0deg))}
.money-shop-task{width:100%;display:grid;grid-template-columns:minmax(0,1fr) minmax(150px,.8fr);gap:12px}.money-product,.money-paid{min-height:150px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:7px;padding:12px;border:1px solid var(--line);border-radius:17px;background:var(--surface-2)}.money-product>span{font-size:48px}.money-product>b,.money-paid>b{color:var(--muted);font-size:12px;text-transform:uppercase}.money-product .money-task-amount{font-size:37px}.money-product .money-task-words{font-size:19px}.money-paid>output{font-size:31px;font-weight:950;color:var(--blue)}
.money-digit-answer,.money-type-answer{width:min(100%,300px);display:flex;flex-direction:column;align-items:center;gap:10px}.money-digit-answer>p,.money-type-answer>p{margin:0;color:var(--muted);font-weight:800;text-align:center}.money-answer-slots{display:flex;align-items:center;justify-content:center;gap:6px}.money-answer-slots>strong{font-size:29px}.money-digit-slot.slot.sm{width:43px;height:64px;padding-top:1px}.money-digit-slot.slot.sm .ghost{height:24px;font-size:20px}.money-digit-slot.slot.sm .slot-dots{margin-bottom:3px}.money-digit-slot.filled{padding:0}.money-digit-slot.fixed{border-style:solid;background:var(--surface-2);opacity:.72}.money-digit-block.blok{width:39px;height:58px;min-height:58px;padding-top:2px;cursor:default}.money-digit-block.blok .bl{height:28px;font-size:25px;color:var(--purple)}.money-digit-block.blok .blok-dots{margin-bottom:4px}.money-digit-tray.lg-tray{display:grid;grid-template-columns:repeat(5,39px);gap:7px;min-height:0;padding:11px 14px}.money-digit-tray .money-drag-digit{cursor:grab}.money-digit-tray .money-drag-digit:active{cursor:grabbing}.money-digit-slot.droptarget{border-color:var(--green);background:#e3f6ea;transform:scale(1.06)}
.money-type-display{min-width:190px;padding:9px 14px;border:3px dashed var(--line);border-radius:14px;background:var(--surface-2);font-size:34px;font-weight:950;letter-spacing:2px;color:var(--green)}.money-keypad{width:100%;display:grid;grid-template-columns:repeat(4,1fr);gap:7px}.money-keypad .tbtn{min-width:0;padding:0;font-size:20px}
.money-feedback{min-height:24px;margin:0;text-align:center;font-weight:900}.money-feedback.ok{color:var(--green)}.money-feedback.bad{color:var(--red)}.money-actions{display:flex;justify-content:center;gap:8px;flex-wrap:wrap}.money-actions .tbtn:not(.ghost){background:var(--green);color:#fff}
@media(max-width:520px){.money-task-card{padding:12px}.money-level-control>span{display:none}.money-top{gap:5px}.money-shop-task{grid-template-columns:1fr}.money-product,.money-paid{min-height:120px}.money-cash-tray{gap:6px;padding:9px}.money-piece.coin{width:44px;height:44px}.money-piece.note{width:69px;height:40px}}
@media(max-width:520px){.money-task-card{padding:12px}.money-level-control>span{display:none}.money-top{gap:5px}.money-shop-task{grid-template-columns:1fr}.money-product,.money-paid{min-height:120px}.money-cash-tray{gap:6px;padding:9px}}
.wordtypes{display:flex;gap:9px;flex-wrap:wrap;justify-content:center}.wordtypes button{border:0;border-radius:10px;padding:10px 13px;font:inherit;font-weight:900;background:var(--surface-2)}.wordtypes button[data-type=noun]{background:#dce9ff;color:#174c9b}.wordtypes button[data-type=verb]{background:#dcf7e7;color:#176b3a}.wordtypes button[data-type=adjective]{background:#fff0cf;color:#855b00}.wordtypes p{flex-basis:100%;text-align:center;color:var(--muted);font-size:12px}
.schedule{width:min(100%,450px);display:flex;flex-direction:column;gap:7px}.schedule>div{display:flex;align-items:center;gap:12px;padding:9px;background:var(--surface-2);border-radius:10px}.schedule time{font-weight:900;color:var(--blue)}.schedule span{flex:1;font-weight:800}.schedule .tbtn{height:30px;min-height:30px;padding:0 9px}.mood{display:flex;gap:8px;flex-wrap:wrap}.mood .tbtn{font-size:36px;height:auto;padding:12px}.mood span{display:block;font-size:14px}.poll{width:min(100%,440px);display:flex;flex-direction:column;gap:8px}.poll h3{text-align:center}.poll .tbtn{position:relative;overflow:hidden;text-align:left}.poll .tbtn i{position:absolute;inset:0 auto 0 0;background:var(--accent-soft);z-index:0}.poll .tbtn span{position:relative;float:right;font-weight:900}.poll .tbtn{isolation:isolate}
@media(max-width:600px){.gallery{grid-template-columns:repeat(2,minmax(0,1fr))}.gcard{min-height:120px;padding:13px 8px}.gcard .gd{display:none}.widget .wdup,.widget .wpreview{display:none}.edu-stage{padding:10px}.place-grid b{font-size:9px}.place-grid output{padding:12px 2px}}

View file

@ -2,7 +2,7 @@
"use strict";
/* version shown until /api/version resolves (or if the fetch fails, e.g. offline).
Kept in sync by hand with the VERSION file at the repo root on every release. */
const VERSION = "0.3.50-beta";
const VERSION = "0.3.51-beta";
(function(){
const tag = document.getElementById("verTag");
tag.textContent = "v"+VERSION;
@ -123,7 +123,7 @@ const I18N = {
moneyGame:"Spel", moneyGameMake:"Bedrag maken", moneyGameCount:"Geld tellen", moneyGameChange:"Wisselgeld",
moneyLevel:"Niveau", moneyLevel1Hint:"Niveau 1: hele euros tot € 9", moneyLevel2Hint:"Niveau 2: tot € 10 in stappen van 5 cent", moneyLevel3Hint:"Niveau 3: tot € 50 met alle centen, munten en biljetten",
moneyScore:"Score: {score} · reeks: {streak}", moneyMakeTitle:"Maak het gevraagde bedrag", moneyCountTitle:"Hoeveel geld zie je?", moneyChangeTitle:"Bereken het wisselgeld",
moneyYourTask:"Jouw opdracht", moneyPrice:"Prijs", moneyPaid:"Betaald", moneyWallet:"Jouw geld", moneyWalletHint:"Sleep geld hierheen of tik op een munt of biljet.", moneyTray:"Munten en biljetten",
moneyYourTask:"Jouw opdracht", moneyPrice:"Prijs", moneyPaid:"Betaald", moneyWallet:"Jouw geld", moneyWalletHint:"Sleep geld hierheen of tik op een munt of biljet. Sleep geplaatst geld naar buiten om het weg te halen.", moneyRemove:"Sleep naar buiten om weg te halen", moneyTray:"Munten en biljetten",
moneyBuildHint:"Sleep de cijfers naar de vakken en maak het bedrag.", moneyTypeHint:"Voer het bedrag in met een komma.", moneyIncomplete:"Maak eerst het volledige bedrag af.",
moneyCorrect:"Goed gedaan! Kies de volgende opdracht. 🌟", moneyTryAgain:"Bijna! Reken het bedrag nog eens na.", moneyNeed:"Nog {amount} nodig.", moneyTooMuch:"{amount} te veel.",
moneyClear:"Wissen", moneyNextTask:"Volgende opdracht",
@ -330,7 +330,7 @@ const I18N = {
moneyGame:"Game", moneyGameMake:"Make an amount", moneyGameCount:"Count money", moneyGameChange:"Change",
moneyLevel:"Level", moneyLevel1Hint:"Level 1: whole euros up to €9", moneyLevel2Hint:"Level 2: up to €10 in five-cent steps", moneyLevel3Hint:"Level 3: up to €50 with all cents, coins and notes",
moneyScore:"Score: {score} · streak: {streak}", moneyMakeTitle:"Make the requested amount", moneyCountTitle:"How much money do you see?", moneyChangeTitle:"Work out the change",
moneyYourTask:"Your task", moneyPrice:"Price", moneyPaid:"Paid", moneyWallet:"Your money", moneyWalletHint:"Drag money here or tap a coin or note.", moneyTray:"Coins and notes",
moneyYourTask:"Your task", moneyPrice:"Price", moneyPaid:"Paid", moneyWallet:"Your money", moneyWalletHint:"Drag money here or tap a coin or note. Drag placed money outside to remove it.", moneyRemove:"Drag outside to remove", moneyTray:"Coins and notes",
moneyBuildHint:"Drag the digits into the slots to make the amount.", moneyTypeHint:"Enter the amount with a decimal point.", moneyIncomplete:"Complete the full amount first.",
moneyCorrect:"Well done! Choose the next task. 🌟", moneyTryAgain:"Almost! Check the amount again.", moneyNeed:"You still need {amount}.", moneyTooMuch:"{amount} too much.",
moneyClear:"Clear", moneyNextTask:"Next task",

View file

@ -62,8 +62,8 @@ function mountMoney(root,init){
const paymentFor=price=>{const options=S.level===1?[500,1000]:S.level===2?[500,1000,2000]:[100,200,500,1000,2000,5000];return options.find(value=>value>price)||Math.ceil((price+1)/5000)*5000};
const breakdown=amount=>{const result=[];let rest=amount;denominations().forEach(value=>{while(rest>=value&&result.length<24){result.push(value);rest-=value}});return result.sort(()=>Math.random()-.5)};
const fixedIndices=()=>S.level===1?[0,2,3]:S.level===2?[0]:[],emptyDigits=()=>S.level===1?[0,null,0,0]:S.level===2?[0,null,null,null]:[null,null,null,null];
const piece=(value,interactive=false)=>{const el=document.createElement(interactive?'button':'span');if(interactive)el.type='button';el.className='money-piece '+(value>=500?'note':'coin');el.dataset.value=value;el.textContent=value>=100?'€'+value/100:value+'c';el.setAttribute('aria-label',format(value));return el};
const pile=(values,interactive=false)=>{const wrap=document.createElement('div');wrap.className='money-pile'+(interactive?' interactive':' readonly');values.forEach((value,index)=>{const el=piece(value,interactive);if(interactive)el.onclick=()=>{if(!S.solved){S.selected.splice(index,1);S.feedback='';draw()}};wrap.append(el)});return wrap};
const piece=(value,interactive=false)=>{const el=document.createElement(interactive?'button':'span');if(interactive)el.type='button';el.className='money-piece '+(value>=500?'note':'coin');el.dataset.value=value;const amount=document.createElement('strong');amount.className='money-value';amount.textContent=value>=100?String(value/100):String(value);if(value>=500){const name=document.createElement('span'),arch=document.createElement('i'),hologram=document.createElement('span'),serial=document.createElement('small');name.className='money-note-name';name.textContent='EURO';arch.className='money-arch';hologram.className='money-hologram';hologram.textContent='€';serial.className='money-serial';serial.textContent='EU 2019';el.append(name,arch,amount,hologram,serial)}else{const stars=document.createElement('span'),map=document.createElement('i'),unit=document.createElement('small');stars.className='money-stars';stars.textContent='✦ · ✦ · ✦';map.className='money-map';map.textContent='EU';unit.className='money-unit';unit.textContent=value>=100?'EURO':'CENT';el.append(stars,map,amount,unit)}el.setAttribute('aria-label',format(value));return el};
const pile=(values,interactive=false,wallet=null)=>{const wrap=document.createElement('div');wrap.className='money-pile'+(interactive?' interactive':' readonly');values.forEach((value,index)=>{const el=piece(value,interactive);if(interactive){el.title=T('moneyRemove');el.onpointerdown=e=>{if(S.solved||(e.pointerType==='mouse'&&e.button!==0))return;e.preventDefault();const startX=e.clientX,startY=e.clientY,clone=el.cloneNode(true);clone.id='dragClone';clone.classList.add('money-drag-clone','from-wallet');document.body.append(clone);const moveTo=ev=>{const zoom=VZ();clone.style.left=ev.clientX/zoom+'px';clone.style.top=ev.clientY/zoom+'px'},over=ev=>{const r=wallet.getBoundingClientRect();return ev.clientX>=r.left&&ev.clientX<=r.right&&ev.clientY>=r.top&&ev.clientY<=r.bottom},hasMoved=ev=>Math.hypot(ev.clientX-startX,ev.clientY-startY)>=6;moveTo(e);const move=ev=>{moveTo(ev);wallet.classList.toggle('money-remove-target',hasMoved(ev)&&!over(ev))},clean=()=>{document.removeEventListener('pointermove',move);document.removeEventListener('pointerup',up);document.removeEventListener('pointercancel',cancel);wallet.classList.remove('money-remove-target');clone.remove()},remove=()=>{S.selected.splice(index,1);S.feedback='';draw()},up=ev=>{const moved=hasMoved(ev),outside=!over(ev);clean();if(!moved||(moved&&outside))remove()},cancel=()=>clean();document.addEventListener('pointermove',move);document.addEventListener('pointerup',up);document.addEventListener('pointercancel',cancel)};el.onkeydown=e=>{if((e.key==='Enter'||e.key===' '||e.key==='Delete'||e.key==='Backspace')&&!S.solved){e.preventDefault();S.selected.splice(index,1);S.feedback='';draw()}}}wrap.append(el)});return wrap};
const amountDisplay=(cents,compact=false)=>{const wrap=document.createElement('div');wrap.className='money-assignment'+(compact?' compact':'');if(['amount','both'].includes(S.style)){const output=document.createElement('output');output.className='money-task-amount';output.textContent=format(cents);wrap.append(output)}if(['words','both'].includes(S.style)){const words=document.createElement('p');words.className='money-task-words';words.textContent=amountWords(cents);wrap.append(words)}return wrap};
const woodDigit=value=>{const block=document.createElement('span');block.className='blok sm money-digit-block';block.dataset.digit=value;const label=document.createElement('span');label.className='bl';label.textContent=String(value);const dots=dotGrid(Number(value));dots.classList.add('blok-dots');block.append(label,dots);return block};
const answerValue=()=>{if(S.amode==='blocks'){if(S.answerDigits.some(value=>value===null))return null;return Number(S.answerDigits.join(''))}if(!S.input)return null;const parts=S.input.replace('.',',').split(',');if(parts.length>2||parts[1]?.length>2)return null;return (+parts[0]||0)*100+Number((parts[1]||'').padEnd(2,'0')||0)};
@ -82,7 +82,7 @@ function mountMoney(root,init){
const chooseLevel=value=>{S.level=value;S.score=0;S.streak=0;newTask()},toggleMode=()=>{S.amode=S.amode==='blocks'?'type':'blocks';S.input='';S.answerDigits=emptyDigits();S.feedback='';draw()};
const draw=()=>{S.style=style.value;S.game=game.value;stage.className='money-app';stage.innerHTML='';const top=document.createElement('div');top.className='money-top';const levelControl=document.createElement('div');levelControl.className='money-level-control';const levelLabel=document.createElement('span');levelLabel.textContent=T('moneyLevel');const levels=document.createElement('div');levels.className='lg-levels money-levels';[1,2,3].forEach(value=>{const button=document.createElement('button');button.type='button';button.textContent=T('lvl'+value);button.title=T('moneyLevel'+value+'Hint');button.classList.toggle('active',S.level===value);button.onclick=()=>chooseLevel(value);levels.append(button)});levelControl.append(levelLabel,levels);top.append(levelControl);if(S.game!=='make'){const mode=btn(S.amode==='blocks'?'🧱':'⌨️',toggleMode);mode.classList.add('money-mode');mode.title=T('amode');top.append(mode)}const score=document.createElement('span');score.className='money-score';score.textContent=T('moneyScore').replace('{score}',S.score).replace('{streak}',S.streak);top.append(score);stage.append(top);
const card=document.createElement('section');card.className='money-task-card';const title=document.createElement('h3');title.textContent=T(S.game==='make'?'moneyMakeTitle':S.game==='count'?'moneyCountTitle':'moneyChangeTitle');card.append(title);
if(S.game==='make'){const target=document.createElement('div');target.className='money-target';const label=document.createElement('span');label.textContent=T('moneyYourTask');target.append(label,amountDisplay(S.targetCents,true));card.append(target);const wallet=document.createElement('div');wallet.className='money-wallet';const walletHead=document.createElement('div');const walletLabel=document.createElement('b');walletLabel.textContent=T('moneyWallet');const total=document.createElement('output');total.textContent=format(S.selected.reduce((sum,item)=>sum+item,0));walletHead.append(walletLabel,total);wallet.append(walletHead);if(S.selected.length)wallet.append(pile(S.selected,true));else{const hint=document.createElement('p');hint.textContent=T('moneyWalletHint');wallet.append(hint)}card.append(wallet,moneyTray(wallet))}
if(S.game==='make'){const target=document.createElement('div');target.className='money-target';const label=document.createElement('span');label.textContent=T('moneyYourTask');target.append(label,amountDisplay(S.targetCents,true));card.append(target);const wallet=document.createElement('div');wallet.className='money-wallet';const walletHead=document.createElement('div');const walletLabel=document.createElement('b');walletLabel.textContent=T('moneyWallet');const total=document.createElement('output');total.textContent=format(S.selected.reduce((sum,item)=>sum+item,0));walletHead.append(walletLabel,total);wallet.append(walletHead);if(S.selected.length)wallet.append(pile(S.selected,true,wallet));else{const hint=document.createElement('p');hint.textContent=T('moneyWalletHint');wallet.append(hint)}card.append(wallet,moneyTray(wallet))}
if(S.game==='count'){const target=document.createElement('div');target.className='money-count-task';target.append(pile(S.given));card.append(target,S.amode==='blocks'?digitAnswer():typeAnswer())}
if(S.game==='change'){const shop=document.createElement('div');shop.className='money-shop-task';const product=document.createElement('div');product.className='money-product';const emoji=document.createElement('span');emoji.textContent=S.product;const priceLabel=document.createElement('b');priceLabel.textContent=T('moneyPrice');product.append(emoji,priceLabel,amountDisplay(S.targetCents,true));const paid=document.createElement('div');paid.className='money-paid';const paidLabel=document.createElement('b');paidLabel.textContent=T('moneyPaid');const paidAmount=document.createElement('output');paidAmount.textContent=format(S.paidCents);paid.append(paidLabel,paidAmount,pile([S.paidCents]));shop.append(product,paid);card.append(shop,S.amode==='blocks'?digitAnswer():typeAnswer())}
const feedback=document.createElement('p');feedback.className='money-feedback '+(S.feedback==='good'?'ok':S.feedback?'bad':'');feedback.setAttribute('role','status');feedback.textContent=S.feedback?feedbackText():'';card.append(feedback);const actions=document.createElement('div');actions.className='money-actions';if(!S.solved)actions.append(btn(T('numberlineCheck'),check,''));if(!S.solved)actions.append(btn(T('moneyClear'),resetAnswer,'ghost'));actions.append(btn(T('moneyNextTask'),newTask,'ghost'));card.append(actions);stage.append(card)};

View file

@ -108,14 +108,22 @@ test('geldrekenen biedt drie niveaus, leerwijzen, spellen en directe bediening',
for(const value of ["['make','moneyGameMake']","['count','moneyGameCount']","['change','moneyGameChange']","['amount','moneyStyleAmount']","['words','moneyStyleWords']","['both','moneyStyleBoth']","S.amode==='blocks'?'🧱':'⌨️'"])assert.ok(moneySource.includes(value),value);
assert.ok(moneySource.includes("S.level===1?(1+rnd(9))*100:S.level===2?(10+rnd(190))*5:1+rnd(4999)"));
assert.ok(moneySource.includes("S.level===1?[200,100]:S.level===2?[500,200,100,50,20,10,5]"));
for(const feature of ['money-wallet','money-cash-tray','money-answer-slots','money-digit-tray','money-keypad','dragClone','droptarget','getBoundingClientRect','amountWords','paymentFor'])assert.ok(moneySource.includes(feature),feature);
for(const feature of ['money-wallet','money-cash-tray','money-answer-slots','money-digit-tray','money-keypad','dragClone','droptarget','getBoundingClientRect','amountWords','paymentFor','money-arch','money-hologram'])assert.ok(moneySource.includes(feature),feature);
assert.ok(moneySource.includes("learningShell(root,[styleSetting,gameSetting],stage)"),'alleen voorkeuren staan onder instellingen');
assert.ok(moneySource.includes("style.onchange=draw"));
assert.ok(moneySource.includes("game.onchange=()=>{S.game=game.value;syncSettings();newTask()}"));
assert.doesNotMatch(moneySource,/learningShell\(root,\[[^\]]*(?:numberlineCheck|numberlineNew|moneyClear|moneyNextTask)/);
for(const key of ['moneyLearningStyle','moneyStyleAmount','moneyStyleWords','moneyStyleBoth','moneyGame','moneyGameMake','moneyGameCount','moneyGameChange','moneyLevel','moneyLevel1Hint','moneyLevel2Hint','moneyLevel3Hint','moneyScore','moneyMakeTitle','moneyCountTitle','moneyChangeTitle','moneyWallet','moneyTray','moneyBuildHint','moneyTypeHint','moneyCorrect','moneyNextTask'])assert.equal((core.match(new RegExp(key+':','g'))||[]).length,2,key);
for(const key of ['moneyLearningStyle','moneyStyleAmount','moneyStyleWords','moneyStyleBoth','moneyGame','moneyGameMake','moneyGameCount','moneyGameChange','moneyLevel','moneyLevel1Hint','moneyLevel2Hint','moneyLevel3Hint','moneyScore','moneyMakeTitle','moneyCountTitle','moneyChangeTitle','moneyWallet','moneyTray','moneyBuildHint','moneyTypeHint','moneyCorrect','moneyNextTask','moneyRemove'])assert.equal((core.match(new RegExp(key+':','g'))||[]).length,2,key);
assert.match(css,/money-app\{[^}]*justify-content:flex-start/);
assert.match(css,/money-wallet\.droptarget/);
assert.match(css,/money-wallet\.money-remove-target/);
assert.ok(moneySource.includes("pile(S.selected,true,wallet)"));
assert.ok(moneySource.includes("const moved=hasMoved(ev),outside=!over(ev)"));
assert.ok(moneySource.includes("moved&&outside"));
assert.match(css,/coin\[data-value="1"\]\{width:36px;height:36px/);
assert.match(css,/coin\[data-value="200"\]\{width:56px;height:56px/);
assert.match(css,/note\[data-value="500"\]\{width:82px;height:42px/);
assert.match(css,/note\[data-value="5000"\]\{width:93px;height:51px/);
assert.match(css,/money-piece\.coin\[data-value="100"\]/);
assert.match(board,/id:"money"[^\n]*w:580, h:560, minW:380, minH:360/);
});