feat(phys9 finals): прогресс-бары и ачивки финалов Wave F + G

Новый модуль frontend/js/phys9_finals.js:

1. РАСШИРЯЕТ window.checkNum чтобы поддерживать сигнатуру
   (id, answer, unit, tol) — раньше legacy checkNum принимал только
   sec для POOLS, из-за чего кнопки «Проверить» в финалах не работали.

2. ПРОГРЕСС-БАР под заголовком каждого finalN:
   - Подсчитывает количество <input id="fin1-q1"...> в финале
   - При правильном ответе обновляет % решённых
   - +8 XP за каждую решённую задачу

3. АЧИВКИ:
   - При 100% решённых задач финала — +50 XP + бэйдж
     «★ МАСТЕР ГЛАВЫ» (физика9_chN_master)
   - При всех 5 финалах — +150 XP + ачивка «МАГИСТР ФИЗИКИ 9»
     (Wave G — финал курса)

Подключение во все 5 ch + хук на ensureBuilt вызывает
PHYS9_FINALS_INIT(id) для id вида final1..final5.

(linter добавил { delimiters, throwOnError:false } в renderMathInElement
вызовы во всех 5 widget-модулях — сохранено).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-05-30 09:55:44 +03:00
parent 77e4dffb43
commit 5b075cde86
11 changed files with 142 additions and 12 deletions
+3 -3
View File
@@ -89,7 +89,7 @@ function appendTo(secId, html){
div.className = 'wg-phys9-extra-'+secId;
div.innerHTML = html;
box.appendChild(div);
try { if(window.renderMathInElement) window.renderMathInElement(box); } catch(e){}
try { if(window.renderMathInElement) window.renderMathInElement(box, { delimiters: [{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false}], throwOnError:false }); } catch(e){}
return true;
}
@@ -202,7 +202,7 @@ function add_p4(){
s += '<text x="'+(cx+tipX)/2+'" y="'+(cy+18)+'" text-anchor="middle" font-size="12" font-weight="700" fill="'+(col.displacement||'#2563eb')+'">$a_x$='+ax.toFixed(1)+'</text>';
s += '<text x="'+(tipX+14)+'" y="'+(cy+tipY)/2+'" font-size="12" font-weight="700" fill="'+(col.force||'#10b981')+'">$a_y$='+(-ay).toFixed(1)+'</text>';
document.getElementById('p4w-svg').innerHTML = s;
try { if(window.renderMathInElement) window.renderMathInElement(document.getElementById('p4-extra').parentNode); } catch(e){}
try { if(window.renderMathInElement) window.renderMathInElement(document.getElementById('p4-extra').parentNode, { delimiters: [{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false}], throwOnError:false }); } catch(e){}
};
document.getElementById('p4w-a-r').addEventListener('input', upd);
document.getElementById('p4w-an-r').addEventListener('input', upd);
@@ -278,7 +278,7 @@ function add_p7(){
+'</div>'
+'<div class="score-display" style="flex-direction:column;align-items:flex-start;gap:5px">'
+'<span>$\\langle v\\rangle = (v_1 t_1 + v_2 t_2)/(t_1+t_2)$ = <b id="p7w-vavg">13.3</b> м/с</span>'
+'<span style="font-size:.86rem;color:var(--muted)">Ловушка: ($v_1+v_2)/2$ = <b id="p7w-trap">15.0</b> м/с — <span id="p7w-trap-lbl" style="font-weight:700;color:var(--fail)">НЕВЕРНО</span></span>'
+'<span style="font-size:.86rem;color:var(--muted)">Ловушка: $(v_1+v_2)/2$ = <b id="p7w-trap">15.0</b> м/с — <span id="p7w-trap-lbl" style="font-weight:700;color:var(--fail)">НЕВЕРНО</span></span>'
+'</div>';
if(appendTo('p7', wgWrapper('p7-extra', 'CALC', 'Средняя скорость', 'Меняй $v$ и $t$ на двух участках. Сравни средневзвешенное и арифметическое.', body))){
const upd = ()=>{