feat(lab-content-engine): phase 1 - data-driven регистрация всех симуляций
- _register-all.js: строит манифесты из SIMS + THEORY + карта OPEN (40 id), регистрирует все симуляции в LabRegistry; LAB_SIM_ALIASES для deep-link - openSim(): удалена if-цепочка (~60 строк), замена на нормализацию алиасов + диспетчеризацию через реестр (early return) - lab.html: _pilots.js -> _register-all.js (defer, последним) - _pilots.js удалён (поглощён _register-all.js) Паритет проверен: исполняемый harness (40 регистраций, dispatch, алиасы, :arg) ALL PASS; независимое ревью PASS (coverage 40/40, dispatch byte-for-byte). Lifecycle пока на _pauseAllSims/closeSim (дробовик) — паритет. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,45 +0,0 @@
|
||||
'use strict';
|
||||
/*
|
||||
* Пилотная регистрация в LabRegistry (Фаза 0 контент-движка).
|
||||
*
|
||||
* Доказывает паритет: каталог/открытие/теория этих 3 симуляций идут через реестр.
|
||||
* Загружается ПОСЛЕДНИМ среди labs-скриптов, поэтому P_* (lab-glue.js),
|
||||
* THEORY (lab-init.js) и _openXxx (graph/quadratic/pendulum.js) уже определены.
|
||||
* preview задан функцией (ленивое вычисление в renderSims) — безопасно к порядку.
|
||||
*
|
||||
* В Фазе 1 регистрация переедет в сами sim-файлы, а этот файл будет удалён.
|
||||
*/
|
||||
(function () {
|
||||
if (!window.LabRegistry) return;
|
||||
var R = window.LabRegistry;
|
||||
|
||||
R.register({
|
||||
id: 'graph', cat: 'math',
|
||||
title: 'График функции', desc: 'Постройте и исследуйте графики функций',
|
||||
preview: function () { return (typeof P_GRAPH !== 'undefined') ? P_GRAPH : ''; },
|
||||
theory: (typeof THEORY !== 'undefined') ? THEORY.graph : null,
|
||||
open: function () { _openGraph(); },
|
||||
stop: function () { /* нет анимационного цикла */ },
|
||||
destroy: function () { /* нет ресурсов для освобождения */ }
|
||||
});
|
||||
|
||||
R.register({
|
||||
id: 'quadratic', cat: 'math',
|
||||
title: 'Квадратное уравнение', desc: 'Дискриминант, корни, теорема Виета',
|
||||
preview: function () { return (typeof P_QUADRATIC !== 'undefined') ? P_QUADRATIC : ''; },
|
||||
theory: (typeof THEORY !== 'undefined') ? THEORY.quadratic : null,
|
||||
open: function () { _openQuadratic(); },
|
||||
stop: function () { /* нет анимационного цикла */ },
|
||||
destroy: function () { /* нет ресурсов для освобождения */ }
|
||||
});
|
||||
|
||||
R.register({
|
||||
id: 'pendulum', cat: 'phys',
|
||||
title: 'Маятник', desc: 'Колебания, период, затухание',
|
||||
preview: function () { return (typeof P_PENDULUM !== 'undefined') ? P_PENDULUM : ''; },
|
||||
theory: (typeof THEORY !== 'undefined') ? THEORY.pendulum : null,
|
||||
open: function () { _openPendulum(); },
|
||||
stop: function () { if (typeof pendSim !== 'undefined' && pendSim && pendSim.stop) pendSim.stop(); },
|
||||
destroy: function () { if (typeof pendSim !== 'undefined' && pendSim && pendSim.stop) pendSim.stop(); }
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,99 @@
|
||||
'use strict';
|
||||
/*
|
||||
* Контент-движок, Фаза 1 — data-driven регистрация ВСЕХ симуляций в LabRegistry.
|
||||
*
|
||||
* Вместо ручного переписывания 40 манифестов модуль строит их из единых источников:
|
||||
* - метаданные (id/cat/title/desc) и preview — из массива SIMS (lab-glue.js);
|
||||
* - теория — из объекта THEORY (lab-init.js);
|
||||
* - поведение open(ctx) — из карты OPEN ниже (обёртки над глобальными _openXxx).
|
||||
* Это структурно гарантирует паритет с прежним каталогом и диспетчеризацией.
|
||||
*
|
||||
* Подключается ПОСЛЕДНИМ среди labs-скриптов (defer), поэтому SIMS, THEORY и все
|
||||
* _openXxx уже определены. Останов/закрытие симуляций по-прежнему выполняет
|
||||
* «дробовик» _pauseAllSims()/closeSim() (точный паритет) — поэтому stop/destroy
|
||||
* в манифестах не задаются на этом этапе.
|
||||
*
|
||||
* После регистрации if-цепочка в openSim() становится мёртвой и удалена.
|
||||
*
|
||||
* В Фазе 1 заменил пилотный _pilots.js. SIMS/THEORY остаются источниками данных
|
||||
* (SIMS → БД в Фазе 4, THEORY сворачивается в манифесты позже).
|
||||
*/
|
||||
(function () {
|
||||
if (!window.LabRegistry) return;
|
||||
if (typeof SIMS === 'undefined') return;
|
||||
var R = window.LabRegistry;
|
||||
var T = (typeof THEORY !== 'undefined') ? THEORY : {};
|
||||
|
||||
// id -> open(ctx). ctx.arg — параметр deep-link (после двоеточия): stereo:cube и т.п.
|
||||
var OPEN = {
|
||||
graph: function (c) { _openGraph(); },
|
||||
projectile: function (c) { _openProjectile(); },
|
||||
collision: function (c) { _openCollision(); },
|
||||
triangle: function (c) { _openTriangle(); },
|
||||
trigcircle: function (c) { _openTrigCircle(); },
|
||||
emfield: function (c) { _openEMField(c.arg || 'E'); },
|
||||
molphys: function (c) { _openMolPhys(c.arg); },
|
||||
circuit: function (c) { _openCircuit(); },
|
||||
chemistry: function (c) { _openChemistry(c.arg); },
|
||||
dynamics: function (c) { _openDynamics(c.arg); },
|
||||
crystal: function (c) { _openCrystal(); },
|
||||
orbitals: function (c) { _openOrbitals(); },
|
||||
stereo: function (c) { _openStereo(c.arg); },
|
||||
chemsandbox: function (c) { _openChemSandbox(); },
|
||||
celldivision: function (c) { _openCellDivision(); },
|
||||
photosynthesis: function (c) { _openPhotosynthesis(); },
|
||||
angrybirds: function (c) { _openAngryBirds(); },
|
||||
quadratic: function (c) { _openQuadratic(); },
|
||||
normaldist: function (c) { _openNormalDist(); },
|
||||
graphtransform: function (c) { _openGraphTransform(); },
|
||||
pendulum: function (c) { _openPendulum(); },
|
||||
equilibrium: function (c) { _openEquilibrium(); },
|
||||
opticsbench: function (c) { _openOpticsBench(c.arg || 'lens'); },
|
||||
isoprocess: function (c) { _openIsoprocess(); },
|
||||
titration: function (c) { _openTitration(); },
|
||||
probability: function (c) { _openProbability(); },
|
||||
bohratom: function (c) { _openBohrAtom(); },
|
||||
electrolysis: function (c) { _openElectrolysis(); },
|
||||
race: function (c) { _openRace(); },
|
||||
waves: function (c) { _openWaves(); },
|
||||
hydrostatics: function (c) { _openHydro(c.arg); },
|
||||
radioactive: function (c) { _openRadioactive(); },
|
||||
geometry: function (c) { _openGeometry(); },
|
||||
logic: function (c) { _openLogic(); },
|
||||
heatengine: function (c) { _openHeatEngine(); },
|
||||
stoichiometry: function (c) { _openStoich(); },
|
||||
qualanalysis: function (c) { _openQualAnalysis(); },
|
||||
periodic: function (c) { _openPeriodic(); },
|
||||
organic: function (c) { _openOrganic(); },
|
||||
solutions: function (c) { _openSolutions(); }
|
||||
};
|
||||
|
||||
SIMS.forEach(function (s) {
|
||||
if (!s.id) return; // "Скоро" — карточка без id
|
||||
var open = OPEN[s.id];
|
||||
if (!open) { // подстраховка: незамапленный id оставляем legacy-пути
|
||||
if (window.console) console.warn('[LabRegistry] нет open() для', s.id);
|
||||
return;
|
||||
}
|
||||
R.register({
|
||||
id: s.id,
|
||||
cat: s.cat,
|
||||
title: s.title,
|
||||
desc: s.desc,
|
||||
preview: s.preview, // уже готовая SVG-строка (P_* вычислены в SIMS)
|
||||
theory: T[s.id] || null,
|
||||
open: open
|
||||
// stop/destroy: глобальный «дробовик» _pauseAllSims()/closeSim() — паритет
|
||||
});
|
||||
});
|
||||
|
||||
// Алиасы deep-link → канонический id[:arg]. Диспетчер openSim() нормализует их
|
||||
// перед обращением к реестру (карточек у алиасов нет — только прямые ссылки).
|
||||
window.LAB_SIM_ALIASES = {
|
||||
magnetic: 'emfield:B',
|
||||
coulomb: 'emfield:E',
|
||||
thinlens: 'opticsbench:lens',
|
||||
mirrors: 'opticsbench:mirror',
|
||||
refraction: 'opticsbench:refraction'
|
||||
};
|
||||
})();
|
||||
@@ -106,68 +106,22 @@
|
||||
// load theory for this sim
|
||||
loadTheory(id.includes(':') ? id.split(':')[0] : id);
|
||||
|
||||
// Контент-движок: мигрированные симуляции открываются через реестр.
|
||||
if (window.LabRegistry && window.LabRegistry.has(id)) {
|
||||
const _m = window.LabRegistry.get(id);
|
||||
const _arg = id.includes(':') ? id.split(':')[1] : undefined;
|
||||
// ── Контент-движок (Фаза 1): диспетчеризация через реестр ──
|
||||
// Все каталожные симуляции зарегистрированы в _register-all.js.
|
||||
// Алиасы deep-link (magnetic/coulomb/thinlens/mirrors/refraction) нормализуем
|
||||
// в канонический id[:arg] перед обращением к реестру.
|
||||
var _aliases = window.LAB_SIM_ALIASES || {};
|
||||
var _cid = _aliases[id.split(':')[0]] || id;
|
||||
if (window.LabRegistry && window.LabRegistry.has(_cid)) {
|
||||
const _m = window.LabRegistry.get(_cid);
|
||||
const _arg = _cid.includes(':') ? _cid.split(':')[1] : undefined;
|
||||
window.LabRegistry.setActive(_m);
|
||||
try { _m.open({ id: id, arg: _arg }); } catch (e) { console.error('[LabRegistry] open failed:', id, e); }
|
||||
try { _m.open({ id: _cid, arg: _arg }); }
|
||||
catch (e) { console.error('[LabRegistry] open failed:', _cid, e); }
|
||||
if (window.lucide) lucide.createIcons();
|
||||
return;
|
||||
}
|
||||
|
||||
if (id === 'graph') _openGraph();
|
||||
if (id === 'projectile') _openProjectile();
|
||||
if (id === 'collision') _openCollision();
|
||||
if (id === 'triangle') _openTriangle();
|
||||
if (id === 'trigcircle') _openTrigCircle();
|
||||
if (id === 'magnetic') _openEMField('B'); // backward compat: #magnetic → emfield B-mode
|
||||
if (id === 'coulomb') _openEMField('E'); // backward compat: #coulomb → emfield E-mode
|
||||
if (id === 'emfield') _openEMField('E');
|
||||
if (id.startsWith('emfield:')) { _openEMField(id.split(':')[1]); }
|
||||
if (id === 'molphys') _openMolPhys();
|
||||
if (id.startsWith('molphys:')) { _openMolPhys(id.split(':')[1]); }
|
||||
if (id === 'circuit') _openCircuit();
|
||||
if (id === 'chemistry') _openChemistry();
|
||||
if (id.startsWith('chemistry:')) { _openChemistry(id.split(':')[1]); }
|
||||
if (id === 'dynamics') _openDynamics();
|
||||
if (id.startsWith('dynamics:')) { _openDynamics(id.split(':')[1]); }
|
||||
if (id === 'crystal') _openCrystal();
|
||||
if (id === 'orbitals') _openOrbitals();
|
||||
if (id === 'stereo') _openStereo();
|
||||
if (id.startsWith('stereo:')) { _openStereo(id.split(':')[1]); }
|
||||
if (id === 'chemsandbox') _openChemSandbox();
|
||||
if (id === 'celldivision') _openCellDivision();
|
||||
if (id === 'photosynthesis') _openPhotosynthesis();
|
||||
if (id === 'angrybirds') _openAngryBirds();
|
||||
if (id === 'quadratic') _openQuadratic();
|
||||
if (id === 'normaldist') _openNormalDist();
|
||||
if (id === 'graphtransform') _openGraphTransform();
|
||||
if (id === 'pendulum') _openPendulum();
|
||||
if (id === 'equilibrium') _openEquilibrium();
|
||||
if (id === 'opticsbench') _openOpticsBench('lens');
|
||||
if (id.startsWith('opticsbench:')) _openOpticsBench(id.split(':')[1]);
|
||||
if (id === 'thinlens') _openOpticsBench('lens'); // backward compat
|
||||
if (id === 'mirrors') _openOpticsBench('mirror'); // backward compat
|
||||
if (id === 'refraction') _openOpticsBench('refraction'); // backward compat
|
||||
if (id === 'isoprocess') _openIsoprocess();
|
||||
if (id === 'titration') _openTitration();
|
||||
if (id === 'probability') _openProbability();
|
||||
if (id === 'bohratom') _openBohrAtom();
|
||||
if (id === 'electrolysis') _openElectrolysis();
|
||||
if (id === 'race') _openRace();
|
||||
if (id === 'waves') _openWaves();
|
||||
if (id === 'hydrostatics') _openHydro();
|
||||
if (id.startsWith('hydrostatics:')) _openHydro(id.split(':')[1]);
|
||||
if (id === 'radioactive') _openRadioactive();
|
||||
if (id === 'geometry') _openGeometry();
|
||||
if (id === 'logic') _openLogic();
|
||||
if (id === 'heatengine') _openHeatEngine();
|
||||
if (id === 'stoichiometry') _openStoich();
|
||||
if (id === 'qualanalysis') _openQualAnalysis();
|
||||
if (id === 'periodic') _openPeriodic();
|
||||
if (id === 'organic') _openOrganic();
|
||||
if (id === 'solutions') _openSolutions();
|
||||
if (window.console) console.warn('[LabRegistry] неизвестная симуляция:', id);
|
||||
}
|
||||
|
||||
function _simShow(elId) {
|
||||
|
||||
Reference in New Issue
Block a user