refactor: distribute lab-init.js into 34 engine files
lab-init.js: 4098 -> 543 lines (infrastructure + THEORY only) Each sim's _open*() + UI helpers moved to its engine file: graph.js, projectile.js, collision.js, magnetic.js, triangle.js, geometry.js, trigcircle.js, gas.js (molphys), coulomb.js, circuit.js, reactions.js (chemistry), newton.js (dynamics), chemsandbox.js, celldivision.js, photosynthesis.js, angrybirds.js, quadratic.js, normaldist.js, graphtransform.js, pendulum.js, equilibrium.js, thinlens.js, mirror.js, isoprocess.js, titration.js, refraction.js, probability.js, bohratom.js, electrolysis.js, waves.js, crystal.js, orbitals.js, stereo.js, hydrostatics.js All 34 engine files syntax-checked OK.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
/* Strip SVG markup for canvas fillText — replaces icon SVGs with Unicode */
|
||||
function _csClean(s) {
|
||||
@@ -1680,3 +1680,158 @@ class ChemSandboxSim {
|
||||
if (this.onUpdate) this.onUpdate(this.info());
|
||||
}
|
||||
}
|
||||
|
||||
/* ─── lab UI init ─────────────────────────────────── */
|
||||
function _openChemSandbox() {
|
||||
document.getElementById('sim-topbar-title').textContent = 'Химическая песочница';
|
||||
_simShow('sim-chemsandbox');
|
||||
_simShow('ctrl-chemsandbox');
|
||||
|
||||
requestAnimationFrame(() => requestAnimationFrame(() => {
|
||||
const c = document.getElementById('chemsandbox-canvas');
|
||||
if (!chemSandSim) {
|
||||
chemSandSim = new ChemSandboxSim(c);
|
||||
chemSandSim.onUpdate = _chemSandUpdateUI;
|
||||
chemSandSim.onQuizUpdate = _chemSandQuizUI;
|
||||
c.addEventListener('click', e => chemSandSim.handleClick(e));
|
||||
c.addEventListener('mousedown', e => chemSandSim.handleMouseDown(e));
|
||||
c.addEventListener('mousemove', e => chemSandSim.handleMouseMove(e));
|
||||
c.addEventListener('mouseup', e => chemSandSim.handleMouseUp(e));
|
||||
c.addEventListener('wheel', e => chemSandSim.handleWheel(e), { passive: false });
|
||||
c.addEventListener('contextmenu', e => chemSandSim.handleContextMenu(e));
|
||||
_addTouchSupport(c, chemSandSim);
|
||||
_chemSandBuildReagents('all');
|
||||
}
|
||||
chemSandSim.fit();
|
||||
chemSandSim.start();
|
||||
chemSandSim.draw();
|
||||
}));
|
||||
}
|
||||
|
||||
function chemSandCat(cat, el) {
|
||||
document.querySelectorAll('.chemsand-cat').forEach(b => b.classList.remove('active'));
|
||||
el.classList.add('active');
|
||||
if (chemSandSim) chemSandSim.setCategory(cat);
|
||||
_chemSandBuildReagents(cat);
|
||||
if (chemSandSim) chemSandSim.draw();
|
||||
}
|
||||
|
||||
function chemSandPreset(name) { if (chemSandSim) { chemSandSim.preset(name); _chemSandBuildReagents(chemSandSim.filterCat); } }
|
||||
function chemSandReset() { if (chemSandSim) { chemSandSim.reset(); _chemSandBuildReagents(chemSandSim.filterCat); } }
|
||||
function chemSandResetReaction() { if (chemSandSim) { chemSandSim.resetReaction(); _chemSandBuildReagents(chemSandSim.filterCat); } }
|
||||
|
||||
function chemSandConcChange() {
|
||||
const v = +document.getElementById('sl-csand-conc').value;
|
||||
document.getElementById('csand-conc-val').textContent = v + '%';
|
||||
}
|
||||
function chemSandTempChange() {
|
||||
const v = +document.getElementById('sl-csand-temp').value;
|
||||
document.getElementById('csand-temp-val').textContent = v + '°C';
|
||||
}
|
||||
|
||||
function chemSandAdd(formula) {
|
||||
if (!chemSandSim) return;
|
||||
// toggle: if already in mix — remove, else add
|
||||
if (chemSandSim.mixContents.includes(formula)) {
|
||||
chemSandSim.removeFromMix(formula);
|
||||
} else {
|
||||
chemSandSim.addToMix(formula);
|
||||
}
|
||||
_chemSandBuildReagents(chemSandSim.filterCat);
|
||||
}
|
||||
|
||||
function _chemSandBuildReagents(cat) {
|
||||
const box = document.getElementById('chemsand-reagents');
|
||||
if (!box) return;
|
||||
const subs = ChemSandboxSim.SUBSTANCES;
|
||||
const keys = Object.keys(subs).filter(k => cat === 'all' || subs[k].cat === cat);
|
||||
const inMix = chemSandSim ? chemSandSim.mixContents : [];
|
||||
box.innerHTML = keys.map(k => {
|
||||
const s = subs[k];
|
||||
const active = inMix.includes(k);
|
||||
const cls = active ? 'proj-preset-chip reac-mode-btn active' : 'proj-preset-chip reac-mode-btn';
|
||||
const sf = chemSandSim ? chemSandSim._shortFormula(k) : k;
|
||||
const removeHint = active ? ' (клик — убрать)' : '';
|
||||
return `<button class="${cls}" onclick="chemSandAdd('${k}')" title="${s.name}${removeHint}" style="font-size:.68rem;padding:4px 7px">
|
||||
<span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:${s.color};margin-right:3px;vertical-align:middle"></span>${sf}${active ? ' ×' : ''}</button>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function chemSandSetMode(mode, el) {
|
||||
document.querySelectorAll('.chemsand-mode').forEach(b => b.classList.remove('active'));
|
||||
if (el) el.classList.add('active');
|
||||
if (!chemSandSim) return;
|
||||
if (mode === 'quiz') {
|
||||
if (window._simQuizAllowed === false) {
|
||||
LS.toast('Режим заданий недоступен — администратор ограничил доступ', 'error');
|
||||
// revert button state
|
||||
document.querySelectorAll('.chemsand-mode').forEach(b => b.classList.remove('active'));
|
||||
document.getElementById('csand-mode-free')?.classList.add('active');
|
||||
return;
|
||||
}
|
||||
chemSandSim.startQuiz();
|
||||
// reset category filter to 'all' so all reagents are accessible
|
||||
document.querySelectorAll('.chemsand-cat').forEach(b => b.classList.remove('active'));
|
||||
const allBtn = document.querySelector('.chemsand-cat');
|
||||
if (allBtn) allBtn.classList.add('active');
|
||||
_chemSandBuildReagents('all');
|
||||
} else {
|
||||
chemSandSim.stopQuiz();
|
||||
document.getElementById('csand-quiz-question').style.display = 'none';
|
||||
document.getElementById('csand-quiz-result').style.display = 'none';
|
||||
document.getElementById('csand-quiz-next').style.display = 'none';
|
||||
document.getElementById('csand-quiz-score').textContent = '';
|
||||
}
|
||||
}
|
||||
|
||||
function chemSandQuizNext() {
|
||||
if (chemSandSim && chemSandSim._quizMode) {
|
||||
chemSandSim._nextQuizTask();
|
||||
_chemSandBuildReagents(chemSandSim.filterCat);
|
||||
}
|
||||
}
|
||||
|
||||
function _chemSandQuizUI(qi) {
|
||||
const qEl = document.getElementById('csand-quiz-question');
|
||||
const rEl = document.getElementById('csand-quiz-result');
|
||||
const nEl = document.getElementById('csand-quiz-next');
|
||||
const sEl = document.getElementById('csand-quiz-score');
|
||||
if (!qi.active) {
|
||||
qEl.style.display = 'none'; rEl.style.display = 'none'; nEl.style.display = 'none';
|
||||
sEl.textContent = '';
|
||||
return;
|
||||
}
|
||||
qEl.style.display = 'block';
|
||||
qEl.textContent = qi.question || '';
|
||||
sEl.textContent = qi.total > 0 ? `${qi.score}/${qi.total}` : '';
|
||||
if (qi.result) {
|
||||
rEl.style.display = 'block';
|
||||
rEl.style.color = qi.result === 'correct' ? '#7BF5A4' : '#EF476F';
|
||||
rEl.textContent = qi.result === 'correct' ? 'Верно!' : 'Неверно — ' + (qi.answer || '');
|
||||
nEl.style.display = qi.result === 'wrong' ? 'inline-block' : 'none';
|
||||
} else {
|
||||
rEl.style.display = 'none'; nEl.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
let _lastReportedEquation = null;
|
||||
function _chemSandUpdateUI(info) {
|
||||
document.getElementById('csbar-v1').textContent = info.mixed;
|
||||
document.getElementById('csbar-v3').textContent = info.type || '—';
|
||||
const eqEl = document.getElementById('csbar-v4');
|
||||
eqEl.innerHTML = info.equation || '—';
|
||||
eqEl.title = (info.equation || '').replace(/<[^>]*>/g, '');
|
||||
document.getElementById('csbar-v5').textContent = info.products || '—';
|
||||
const ionEl = document.getElementById('csbar-v6');
|
||||
ionEl.innerHTML = info.ionNet || '—';
|
||||
ionEl.title = (info.ionNet || '').replace(/<[^>]*>/g, '');
|
||||
// rebuild reagent buttons to reflect active state
|
||||
_chemSandBuildReagents(chemSandSim ? chemSandSim.filterCat : 'all');
|
||||
// Report lab activity for gamification (once per unique reaction)
|
||||
if (info.reaction && info.equation && info.equation !== _lastReportedEquation) {
|
||||
_lastReportedEquation = info.equation;
|
||||
if (window.LS?.reportLabActivity) LS.reportLabActivity(1).catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Cell Division ── */
|
||||
|
||||
Reference in New Issue
Block a user