Verbeter anatomie en plaatsing van avataraccessoires (v0.4.46-beta) #1
3 changed files with 16 additions and 5 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
0.4.16-beta
|
||||
0.4.17-beta
|
||||
|
|
|
|||
|
|
@ -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.4.16-beta";
|
||||
const VERSION = "0.4.17-beta";
|
||||
(function(){
|
||||
const tag = document.getElementById("verTag");
|
||||
tag.textContent = "v"+VERSION;
|
||||
|
|
|
|||
|
|
@ -695,7 +695,12 @@ function mountClock(root,init){
|
|||
};
|
||||
const assignment=(hour,minute,compact=false)=>{const wrap=document.createElement('div');wrap.className='clock-assignment'+(compact?' compact':'');if(['analogue','both'].includes(S.style)){const clock=makeClock(hour,minute,false);if(compact)clock.classList.add('clock-reference');wrap.append(clock)}if(['digital','both'].includes(S.style)){const digital=document.createElement('output');digital.className='clock-task-digital';digital.textContent=time(hour,minute);wrap.append(digital)}if(['words','both'].includes(S.style)){const written=document.createElement('p');written.className='clock-task-words';written.textContent=words(hour,minute);wrap.append(written)}if(uses24()&&S.style==='analogue'){const period=document.createElement('span');period.className='clock-daypart';period.textContent=T(daypartKey(hour));wrap.append(period)}return wrap};
|
||||
const periodControl=()=>{const control=document.createElement('div');control.className='clock-period';control.setAttribute('aria-label',T('clock24Hour'));[0,12].forEach(period=>{const button=btn(period?'12–23':'00–11',()=>{if(S.solved)return;const dial=clockHour(S.h);S.h=(dial%12)+period;S.feedback='';draw()},'ghost');button.dataset.period=period;button.title=period?'12:00–23:59':'00:00–11:59';const active=(S.h>=12?12:0)===period;button.classList.toggle('active',active);button.setAttribute('aria-pressed',String(active));control.append(button)});return control};
|
||||
const blockAnswer=()=>{const answer=document.createElement('div');answer.className='clock-block-answer';const instruction=document.createElement('p');instruction.textContent=T('clockBuildHint');const slots=document.createElement('div');slots.className='clock-answer-slots';const makeSlot=index=>{const slot=document.createElement('div');slot.className='slot sm clock-digit-slot'+(S.answerDigits[index]!==null?' filled':'');slot.dataset.index=index;const ghost=document.createElement('div');ghost.className='ghost';ghost.textContent=T('clockDigitGhost');const dots=dotGrid(S.answerDigits[index]===null?0:S.answerDigits[index]);dots.classList.add('slot-dots');slot.append(ghost,dots);if(S.answerDigits[index]!==null){const placed=woodDigit(S.answerDigits[index]);placed.classList.add('in-slot','clock-drag-block');slot.append(placed);slot.onclick=()=>{if(!S.solved){S.answerDigits[index]=null;S.feedback='';draw()}}}return slot};slots.append(makeSlot(0),makeSlot(1));const colon=document.createElement('strong');colon.className='clock-slot-colon';colon.textContent=':';slots.append(colon,makeSlot(2),makeSlot(3));const tray=document.createElement('div');tray.className='clock-block-tray lg-tray';const attachDrag=block=>{block.onpointerdown=e=>{if(S.solved||(e.pointerType==='mouse'&&e.button!==0))return;e.preventDefault();try{block.releasePointerCapture(e.pointerId)}catch(_){}const clone=block.cloneNode(true);clone.id='dragClone';document.body.append(clone);block.classList.add('dragging');const moveTo=ev=>{const zoom=VZ();clone.style.left=ev.clientX/zoom+'px';clone.style.top=ev.clientY/zoom+'px'},hit=ev=>{let found=null;slots.querySelectorAll('.clock-digit-slot').forEach(slot=>{const r=slot.getBoundingClientRect(),pad=8;if(ev.clientX>=r.left-pad&&ev.clientX<=r.right+pad&&ev.clientY>=r.top-pad&&ev.clientY<=r.bottom+pad)found=slot});return found};moveTo(e);let hovered=null;const move=ev=>{moveTo(ev);const target=hit(ev);if(hovered&&hovered!==target)hovered.classList.remove('droptarget');hovered=target;if(hovered)hovered.classList.add('droptarget')},clean=()=>{document.removeEventListener('pointermove',move);document.removeEventListener('pointerup',up);document.removeEventListener('pointercancel',cancel);clone.remove();block.classList.remove('dragging');if(hovered)hovered.classList.remove('droptarget')},up=ev=>{const target=hit(ev);clean();if(target){S.answerDigits[+target.dataset.index]=+block.dataset.digit;S.feedback='';draw()}},cancel=()=>clean();document.addEventListener('pointermove',move);document.addEventListener('pointerup',up);document.addEventListener('pointercancel',cancel)}};'0123456789'.split('').forEach(d=>{const block=woodDigit(+d);block.classList.add('clock-drag-block');block.dataset.digit=d;block.setAttribute('role','button');block.setAttribute('aria-label',T('clockDigit').replace('{digit}',d));block.tabIndex=0;block.onkeydown=e=>{if((e.key==='Enter'||e.key===' ')&&!S.solved){e.preventDefault();const index=S.answerDigits.indexOf(null);if(index>=0){S.answerDigits[index]=+d;draw()}}};attachDrag(block);tray.append(block)});answer.append(instruction,slots,tray);return answer};
|
||||
const blockAnswer=()=>{const answer=document.createElement('div');answer.className='clock-block-answer';const instruction=document.createElement('p');instruction.textContent=T('clockBuildHint');const slots=document.createElement('div');slots.className='clock-answer-slots';
|
||||
/* zelfde patroon als letterblokken/rekenblokken: het vak toont altijd het
|
||||
puntenpatroon van het júíste cijfer op die plek (niet van wat er nu in
|
||||
staat) - dat is de tastbare hint, ongeacht of er al iets ligt. */
|
||||
const targetDigits=String(S.targetH).padStart(2,'0')+String(S.targetM).padStart(2,'0');
|
||||
const makeSlot=index=>{const slot=document.createElement('div');slot.className='slot sm clock-digit-slot'+(S.answerDigits[index]!==null?' filled':'');slot.dataset.index=index;const ghost=document.createElement('div');ghost.className='ghost';ghost.textContent=T('clockDigitGhost');const dots=dotGrid(targetDigits[index]);dots.classList.add('slot-dots');slot.append(ghost,dots);if(S.answerDigits[index]!==null){const placed=woodDigit(S.answerDigits[index]);placed.classList.add('in-slot','clock-drag-block');slot.append(placed);slot.onclick=()=>{if(!S.solved){S.answerDigits[index]=null;S.feedback='';draw()}}}return slot};slots.append(makeSlot(0),makeSlot(1));const colon=document.createElement('strong');colon.className='clock-slot-colon';colon.textContent=':';slots.append(colon,makeSlot(2),makeSlot(3));const tray=document.createElement('div');tray.className='clock-block-tray lg-tray';const attachDrag=block=>{block.onpointerdown=e=>{if(S.solved||(e.pointerType==='mouse'&&e.button!==0))return;e.preventDefault();try{block.releasePointerCapture(e.pointerId)}catch(_){}const clone=block.cloneNode(true);clone.id='dragClone';document.body.append(clone);block.classList.add('dragging');const moveTo=ev=>{const zoom=VZ();clone.style.left=ev.clientX/zoom+'px';clone.style.top=ev.clientY/zoom+'px'},hit=ev=>{let found=null;slots.querySelectorAll('.clock-digit-slot').forEach(slot=>{const r=slot.getBoundingClientRect(),pad=8;if(ev.clientX>=r.left-pad&&ev.clientX<=r.right+pad&&ev.clientY>=r.top-pad&&ev.clientY<=r.bottom+pad)found=slot});return found};moveTo(e);let hovered=null;const move=ev=>{moveTo(ev);const target=hit(ev);if(hovered&&hovered!==target)hovered.classList.remove('droptarget');hovered=target;if(hovered)hovered.classList.add('droptarget')},clean=()=>{document.removeEventListener('pointermove',move);document.removeEventListener('pointerup',up);document.removeEventListener('pointercancel',cancel);clone.remove();block.classList.remove('dragging');if(hovered)hovered.classList.remove('droptarget')},up=ev=>{const target=hit(ev);clean();if(target){S.answerDigits[+target.dataset.index]=+block.dataset.digit;S.feedback='';draw()}},cancel=()=>clean();document.addEventListener('pointermove',move);document.addEventListener('pointerup',up);document.addEventListener('pointercancel',cancel)}};'0123456789'.split('').forEach(d=>{const block=woodDigit(+d);block.classList.add('clock-drag-block');block.dataset.digit=d;block.setAttribute('role','button');block.setAttribute('aria-label',T('clockDigit').replace('{digit}',d));block.tabIndex=0;block.onkeydown=e=>{if((e.key==='Enter'||e.key===' ')&&!S.solved){e.preventDefault();const index=S.answerDigits.indexOf(null);if(index>=0){S.answerDigits[index]=+d;draw()}}};attachDrag(block);tray.append(block)});answer.append(instruction,slots,tray);return answer};
|
||||
const setFeedback=ok=>{if(S.solved)return;if(ok){S.solved=true;S.score++;S.streak++;S.feedback='good';sndGood()}else{S.streak=0;S.feedback='bad';sndBad()}draw()};
|
||||
const check=()=>{if(!S.task)return;if(S.game==='read'){if(S.amode==='blocks'){if(S.answerDigits.some(x=>x===null)){S.feedback='incomplete';sndBad();draw();return}S.h=Number(''+S.answerDigits[0]+S.answerDigits[1]);S.m=Number(''+S.answerDigits[2]+S.answerDigits[3])}else{if(S.input.length<4){S.feedback='incomplete';sndBad();draw();return}S.h=+S.input.slice(0,2);S.m=+S.input.slice(2,4)}}const expectedHour=S.game==='set'?(uses24()?S.targetH:clockHour(S.targetH)):S.targetH;setFeedback(S.h===expectedHour&&S.m===S.targetM)};
|
||||
const typeAnswer=()=>{const wrap=document.createElement('div');wrap.className='clock-type-answer';const hint=document.createElement('p');hint.textContent=T('clockTypeHint');const display=document.createElement('output');display.className='clock-type-display';const shown=(S.input+'____').slice(0,4);display.textContent=shown.slice(0,2)+':'+shown.slice(2);const pad=document.createElement('div');pad.className='clock-keypad';['7','8','9','⌫','4','5','6','0','1','2','3','✓'].forEach(key=>{const button=btn(key,()=>{if(S.solved)return;if(key==='⌫')S.input=S.input.slice(0,-1);else if(key==='✓'){check();return}else if(S.input.length<4)S.input+=key;S.feedback='';draw()});if(key==='✓')button.classList.add('ok');pad.append(button)});wrap.append(hint,display,pad);return wrap};
|
||||
|
|
@ -729,9 +734,12 @@ function mountPlacevalue(root,init){
|
|||
const buildAnswer=()=>{
|
||||
const answer=document.createElement('div');answer.className='place-block-answer';const instruction=document.createElement('p');instruction.textContent=T('placeBuildHint');
|
||||
const slots=document.createElement('div');slots.className='place-answer-slots';const cols=activeCols();
|
||||
/* zelfde patroon als letterblokken/rekenblokken: het vak toont altijd het
|
||||
puntenpatroon van het júíste cijfer op die plek. */
|
||||
const targetDigits=String(S.target).padStart(cols.length,'0');
|
||||
const makeSlot=index=>{const wrap=document.createElement('div');wrap.className='place-slot-col';const colLabel=document.createElement('b');colLabel.textContent=cols[index];
|
||||
const slot=document.createElement('div');slot.className='slot sm place-digit-slot'+(S.answerDigits[index]!==null?' filled':'');slot.dataset.index=index;
|
||||
const ghost=document.createElement('div');ghost.className='ghost';ghost.textContent=T('placeDigitGhost');const dots=dotGrid(S.answerDigits[index]===null?0:S.answerDigits[index]);dots.classList.add('slot-dots');slot.append(ghost,dots);
|
||||
const ghost=document.createElement('div');ghost.className='ghost';ghost.textContent=T('placeDigitGhost');const dots=dotGrid(targetDigits[index]);dots.classList.add('slot-dots');slot.append(ghost,dots);
|
||||
if(S.answerDigits[index]!==null){const placed=woodDigit(S.answerDigits[index]);placed.classList.add('in-slot','place-drag-block');slot.append(placed);slot.onclick=()=>{if(!S.solved){S.answerDigits[index]=null;S.feedback='';drawBuild()}}}
|
||||
wrap.append(colLabel,slot);return wrap};
|
||||
cols.forEach((_,i)=>slots.append(makeSlot(i)));
|
||||
|
|
@ -781,7 +789,10 @@ function mountMoney(root,init){
|
|||
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)};
|
||||
const digitAnswer=()=>{const answer=document.createElement('div');answer.className='money-digit-answer';const instruction=document.createElement('p');instruction.textContent=T('moneyBuildHint');const slots=document.createElement('div');slots.className='money-answer-slots';const fixed=new Set(fixedIndices());
|
||||
const makeSlot=index=>{const slot=document.createElement('div');slot.className='slot sm money-digit-slot'+(fixed.has(index)?' fixed':'')+(S.answerDigits[index]!==null?' filled':'');slot.dataset.index=index;const ghost=document.createElement('div');ghost.className='ghost';ghost.textContent='?';const dots=dotGrid(S.answerDigits[index]===null?0:S.answerDigits[index]);dots.classList.add('slot-dots');slot.append(ghost,dots);if(S.answerDigits[index]!==null){const placed=woodDigit(S.answerDigits[index]);placed.classList.add('in-slot','money-drag-digit');slot.append(placed);if(!fixed.has(index))slot.onclick=()=>{if(!S.solved){S.answerDigits[index]=null;S.feedback='';draw()}}}return slot};
|
||||
/* zelfde patroon als letterblokken/rekenblokken: het vak toont altijd het
|
||||
puntenpatroon van het júíste cijfer op die plek. */
|
||||
const targetDigits=String(expected()).padStart(4,'0');
|
||||
const makeSlot=index=>{const slot=document.createElement('div');slot.className='slot sm money-digit-slot'+(fixed.has(index)?' fixed':'')+(S.answerDigits[index]!==null?' filled':'');slot.dataset.index=index;const ghost=document.createElement('div');ghost.className='ghost';ghost.textContent='?';const dots=dotGrid(targetDigits[index]);dots.classList.add('slot-dots');slot.append(ghost,dots);if(S.answerDigits[index]!==null){const placed=woodDigit(S.answerDigits[index]);placed.classList.add('in-slot','money-drag-digit');slot.append(placed);if(!fixed.has(index))slot.onclick=()=>{if(!S.solved){S.answerDigits[index]=null;S.feedback='';draw()}}}return slot};
|
||||
const euro=document.createElement('strong');euro.textContent='€';slots.append(euro,makeSlot(0),makeSlot(1));const comma=document.createElement('strong');comma.textContent=LANG==='nl'?',':'.';slots.append(comma,makeSlot(2),makeSlot(3));
|
||||
const tray=document.createElement('div');tray.className='money-digit-tray lg-tray';const attachDrag=block=>{block.onpointerdown=e=>{if(S.solved||(e.pointerType==='mouse'&&e.button!==0))return;e.preventDefault();const clone=block.cloneNode(true);clone.id='dragClone';document.body.append(clone);block.classList.add('dragging');const moveTo=ev=>{const zoom=VZ();clone.style.left=ev.clientX/zoom+'px';clone.style.top=ev.clientY/zoom+'px'},hit=ev=>{let found=null;slots.querySelectorAll('.money-digit-slot:not(.fixed)').forEach(slot=>{const r=slot.getBoundingClientRect(),pad=8;if(ev.clientX>=r.left-pad&&ev.clientX<=r.right+pad&&ev.clientY>=r.top-pad&&ev.clientY<=r.bottom+pad)found=slot});return found};moveTo(e);let hovered=null;const move=ev=>{moveTo(ev);const target=hit(ev);if(hovered&&hovered!==target)hovered.classList.remove('droptarget');hovered=target;if(hovered)hovered.classList.add('droptarget')},clean=()=>{document.removeEventListener('pointermove',move);document.removeEventListener('pointerup',up);document.removeEventListener('pointercancel',cancel);clone.remove();block.classList.remove('dragging');if(hovered)hovered.classList.remove('droptarget')},up=ev=>{const target=hit(ev);clean();if(target){S.answerDigits[+target.dataset.index]=+block.dataset.digit;S.feedback='';draw()}},cancel=()=>clean();document.addEventListener('pointermove',move);document.addEventListener('pointerup',up);document.addEventListener('pointercancel',cancel)}};'0123456789'.split('').forEach(digit=>{const block=woodDigit(+digit);block.classList.add('money-drag-digit');block.setAttribute('role','button');block.setAttribute('aria-label',T('clockDigit').replace('{digit}',digit));block.tabIndex=0;block.onkeydown=e=>{if((e.key==='Enter'||e.key===' ')&&!S.solved){e.preventDefault();const index=S.answerDigits.findIndex((value,i)=>value===null&&!fixed.has(i));if(index>=0){S.answerDigits[index]=+digit;draw()}}};attachDrag(block);tray.append(block)});answer.append(instruction,slots,tray);return answer};
|
||||
const typeAnswer=()=>{const wrap=document.createElement('div');wrap.className='money-type-answer';const hint=document.createElement('p');hint.textContent=T('moneyTypeHint');const display=document.createElement('output');display.className='money-type-display';display.textContent='€ '+(S.input||'…');const pad=document.createElement('div');pad.className='money-keypad';['7','8','9','⌫','4','5','6',LANG==='nl'?',':'.','1','2','3','0'].forEach(key=>{const button=btn(key,()=>{if(S.solved)return;if(key==='⌫')S.input=S.input.slice(0,-1);else if(key===','||key==='.'){if(!/[,.]/.test(S.input))S.input=(S.input||'0')+key}else{const parts=S.input.split(/[,.]/);if((parts.length===1&&parts[0].length<2)||(parts.length===2&&parts[1].length<2))S.input+=key}S.feedback='';draw()});pad.append(button)});wrap.append(hint,display,pad);return wrap};
|
||||
|
|
|
|||
Loading…
Reference in a new issue