'use strict'; /* ════════════════════════════════════════════════════════════════════════ registerSpecSim — адаптер: JSON-спека -> манифест LabRegistry (Фаза 0). Строит манифест и регистрирует его в window.LabRegistry, чтобы спек-симуляция открывалась тем же путём, что и ~40 рукописных симуляций (через openSim -> реестр). Никаких правок чужих манифестов — только register(). Контракт LabRegistry-манифеста (см. _registry.js): { id, cat, title, desc, preview, theory?, open(ctx), stop(), destroy() }. Особенности интеграции с /lab: - openSim() прячет все ИЗВЕСТНЫЕ ему тела (ALL_SIM_BODIES) и зовёт _pauseAllSims() -> LabRegistry.stopActive() (наш stop спрячет наш хост). Спек-хосты openSim не знает, поэтому при switch именно stop() прошлой активной спек-симуляции прячет её. - Каждая спек-симуляция получает собственный хост-div внутри #lab-sim, создаётся лениво при первом open и переиспользуется. - Заголовок топбара ставим как делают рукописные _openX (sim-topbar-title). ════════════════════════════════════════════════════════════════════════ */ (function (global) { var HOST_PREFIX = 'sim-spec-host-'; // Найти контейнер для тел симуляций (#lab-sim) — туда вставляем хост. function simContainer() { return document.getElementById('lab-sim') || document.body; } // Лениво создать/получить хост-элемент для данного id. function ensureHost(id) { var hid = HOST_PREFIX + id; var el = document.getElementById(hid); if (el) return el; el = document.createElement('div'); el.id = hid; el.className = 'sim-spec-host'; // занимает то же место, что .sim-body-wrap у рукописных симуляций el.style.cssText = 'flex:1;min-height:0;display:none'; var cont = simContainer(); // вставить перед панелью теории, если она есть, иначе в конец var theory = document.getElementById('theory-panel'); if (theory && theory.parentNode === cont) cont.insertBefore(el, theory); else cont.appendChild(el); return el; } // Спрятать все спек-хосты (на случай переключения с одной спеки на другую, // когда openSim не знает наших хостов). function hideAllSpecHosts() { var nodes = document.querySelectorAll('.sim-spec-host'); for (var i = 0; i < nodes.length; i++) nodes[i].style.display = 'none'; } function setTitle(spec) { var t = document.getElementById('sim-topbar-title'); if (t) t.textContent = (spec.meta && spec.meta.title) || spec.title || 'Симуляция'; } /* preview из спеки: если задана строка/функция — использовать; иначе сгенерировать простой SVG-плейсхолдер с названием. */ function buildPreview(spec) { if (spec.preview) return spec.preview; var title = (spec.meta && spec.meta.title) || spec.title || 'Симуляция'; var bg = (spec.viewport && spec.viewport.bg) || '#0D0D1A'; return function () { return '' + '' + '' + '' + '' + '' + '' + _esc(title) + ''; }; } function _esc(s) { return String(s).replace(/&/g, '&').replace(//g, '>') .replace(/"/g, '"'); } /* ── Главная функция ── */ function registerSpecSim(spec) { if (!global.LabRegistry) { if (global.console) console.warn('[registerSpecSim] LabRegistry недоступен'); return null; } if (!spec || !spec.id) { if (global.console) console.warn('[registerSpecSim] спека без id'); return null; } var id = spec.id; var _inst = null; // активный SimEngine-инстанс этой симуляции var manifest = { id: id, cat: spec.cat || 'phys', title: (spec.meta && spec.meta.title) || spec.title || id, desc: (spec.meta && spec.meta.desc) || spec.desc || '', preview: buildPreview(spec), theory: spec.theory || null, subject: spec.subject, grade: spec.grade, topics: spec.topics, _spec: spec, // храним исходную спеку (билдеру/доске пригодится) open: function (ctx) { hideAllSpecHosts(); var host = ensureHost(id); host.style.display = 'flex'; setTitle(spec); // пере-смонтировать заново на каждый open (чистое состояние) if (_inst) { try { _inst.destroy(); } catch (e) {} _inst = null; } host.innerHTML = ''; if (global.SimEngine) { _inst = global.SimEngine.mount(host, spec); manifest._instance = _inst; } else if (global.console) { console.warn('[registerSpecSim] SimEngine недоступен для', id); } }, stop: function () { if (_inst) { try { _inst.pause(); } catch (e) {} } var host = document.getElementById(HOST_PREFIX + id); if (host) host.style.display = 'none'; }, destroy: function () { if (_inst) { try { _inst.destroy(); } catch (e) {} _inst = null; } manifest._instance = null; var host = document.getElementById(HOST_PREFIX + id); if (host) host.style.display = 'none'; }, // доступ к живому инстансу (доска онлайн-урока, билдер — Фазы 4/7) instance: function () { return _inst; } }; return global.LabRegistry.register(manifest); } global.registerSpecSim = registerSpecSim; global.SimAdapter = { register: registerSpecSim, ensureHost: ensureHost, hideAllSpecHosts: hideAllSpecHosts }; })(typeof window !== 'undefined' ? window : globalThis);