diff --git a/frontend/js/labs/_loader.js b/frontend/js/labs/_loader.js new file mode 100644 index 0000000..f8435a1 --- /dev/null +++ b/frontend/js/labs/_loader.js @@ -0,0 +1,76 @@ +'use strict'; +/* + * LabLoader — ленивый загрузчик кода симуляций (контент-движок, Фаза 3). + * + * Тяжёлый код симуляций (~2.5 МБ) и three.js (~600 КБ) больше НЕ грузятся на старте. + * При открытии симуляции LabLoader.ensure(id) подгружает её файлы (по манифесту + * window.SIM_DEPS из _sim_deps.js) и, при необходимости, three.js — затем резолвит. + * + * Гарантия корректности (самовосстановление): если после загрузки указанных файлов + * глобальная open-функция (SIM_DEPS[id].open, напр. "_openPendulum") всё ещё не + * определена, грузятся ВСЕ ленивые файлы (window.LAB_LAZY_FILES). Поэтому ошибка в + * манифесте не может «сломать» симуляцию — в худшем случае грузится больше файлов + * (поведение как до Фазы 3). Манифест лишь оптимизирует объём загрузки. + * + * Все загрузки кешируются (по URL) и дедуплицируются. + */ +(function () { + var BASE = '/js/labs/'; + var THREE_URL = 'https://cdn.jsdelivr.net/npm/three@0.149.0/build/three.min.js'; + var _cache = {}; // url -> Promise + var _allLoaded = false; + + function loadScript(url) { + if (_cache[url]) return _cache[url]; + _cache[url] = new Promise(function (resolve, reject) { + var s = document.createElement('script'); + s.src = url; + s.async = false; // сохранить порядок при добавлении нескольких сразу + s.onload = function () { resolve(url); }; + s.onerror = function () { delete _cache[url]; reject(new Error('LabLoader: не удалось загрузить ' + url)); }; + document.head.appendChild(s); + }); + return _cache[url]; + } + + function ensureThree() { + if (typeof window.THREE !== 'undefined') return Promise.resolve(); + return loadScript(THREE_URL); + } + + function loadFiles(files) { + return Promise.all((files || []).map(function (f) { return loadScript(BASE + f); })); + } + + function loadAllLazy() { + if (_allLoaded) return Promise.resolve(); + var list = window.LAB_LAZY_FILES || []; + return loadFiles(list).then(function () { _allLoaded = true; }); + } + + // ensure(id): загрузить всё необходимое для симуляции id, вернуть Promise. + function ensure(id) { + var dep = (window.SIM_DEPS && window.SIM_DEPS[id]) || null; + if (!dep) { + // нет манифеста для id — безопасно грузим всё + return loadAllLazy(); + } + var p = dep.three ? ensureThree() : Promise.resolve(); + return p + .then(function () { return loadFiles(dep.files); }) + .then(function () { + var openName = dep.open; + if (openName && typeof window[openName] !== 'function') { + if (window.console) console.warn('[LabLoader] самовосстановление для "' + id + '": ' + openName + ' не найдена после загрузки ' + JSON.stringify(dep.files) + ' — гружу все ленивые файлы'); + return loadAllLazy(); + } + }); + } + + window.LabLoader = { + ensure: ensure, + ensureThree: ensureThree, + loadScript: loadScript, + loadAllLazy: loadAllLazy + }; +})(); diff --git a/frontend/js/labs/_sim_deps.js b/frontend/js/labs/_sim_deps.js new file mode 100644 index 0000000..114a8b9 --- /dev/null +++ b/frontend/js/labs/_sim_deps.js @@ -0,0 +1,300 @@ +'use strict'; +/* Контент-движок, Фаза 3 — манифест зависимостей симуляций (СГЕНЕРИРОВАН). + id -> { open: имя глобальной _openX, files: [ленивые файлы], three: нужен ли three.js }. + Файлы загружаются лениво по клику (см. _loader.js). three.js — только для 3D-симуляций. + Самовосстановление в _loader: если после загрузки open-функция не определена, + грузятся ВСЕ ленивые файлы -> корректность не зависит от точности манифеста. + Регенерация: node tools/gen-sim-deps.js (см. CONTEXT). НЕ редактировать вручную. */ +window.SIM_DEPS = { + "graph": { + "open": "_openGraph", + "files": [], + "three": false + }, + "projectile": { + "open": "_openProjectile", + "files": [ + "projectile.js" + ], + "three": false + }, + "collision": { + "open": "_openCollision", + "files": [ + "collision.js" + ], + "three": false + }, + "triangle": { + "open": "_openTriangle", + "files": [ + "triangle.js" + ], + "three": false + }, + "trigcircle": { + "open": "_openTrigCircle", + "files": [ + "trigcircle.js" + ], + "three": false + }, + "emfield": { + "open": "_openEMField", + "files": [ + "emfield.js", + "logic.js" + ], + "three": false + }, + "molphys": { + "open": "_openMolPhys", + "files": [ + "brownian.js", + "diffusion.js", + "gas.js", + "states.js" + ], + "three": false + }, + "circuit": { + "open": "_openCircuit", + "files": [ + "circuit.js" + ], + "three": false + }, + "chemistry": { + "open": "_openChemistry", + "files": [ + "circuit.js", + "flask.js", + "ionexchange.js", + "reactions.js", + "redox.js" + ], + "three": false + }, + "dynamics": { + "open": "_openDynamics", + "files": [ + "forcesandbox.js", + "newton.js" + ], + "three": false + }, + "crystal": { + "open": "_openCrystal", + "files": [ + "crystal.js" + ], + "three": true + }, + "orbitals": { + "open": "_openOrbitals", + "files": [ + "orbitals.js" + ], + "three": true + }, + "stereo": { + "open": "_openStereo", + "files": [ + "stereo.js" + ], + "three": true + }, + "chemsandbox": { + "open": "_openChemSandbox", + "files": [ + "chemsandbox.js", + "collision.js" + ], + "three": false + }, + "celldivision": { + "open": "_openCellDivision", + "files": [ + "celldivision.js" + ], + "three": false + }, + "photosynthesis": { + "open": "_openPhotosynthesis", + "files": [ + "photosynthesis.js" + ], + "three": false + }, + "angrybirds": { + "open": "_openAngryBirds", + "files": [ + "angrybirds.js" + ], + "three": false + }, + "quadratic": { + "open": "_openQuadratic", + "files": [ + "quadratic.js" + ], + "three": false + }, + "normaldist": { + "open": "_openNormalDist", + "files": [ + "normaldist.js" + ], + "three": false + }, + "graphtransform": { + "open": "_openGraphTransform", + "files": [ + "graphtransform.js" + ], + "three": false + }, + "pendulum": { + "open": "_openPendulum", + "files": [ + "pendulum.js" + ], + "three": false + }, + "equilibrium": { + "open": "_openEquilibrium", + "files": [ + "equilibrium.js" + ], + "three": false + }, + "opticsbench": { + "open": "_openOpticsBench", + "files": [ + "opticsbench.js" + ], + "three": false + }, + "isoprocess": { + "open": "_openIsoprocess", + "files": [ + "isoprocess.js" + ], + "three": false + }, + "titration": { + "open": "_openTitration", + "files": [ + "titration.js" + ], + "three": false + }, + "probability": { + "open": "_openProbability", + "files": [ + "probability.js" + ], + "three": false + }, + "bohratom": { + "open": "_openBohrAtom", + "files": [ + "bohratom.js" + ], + "three": false + }, + "electrolysis": { + "open": "_openElectrolysis", + "files": [ + "electrolysis.js" + ], + "three": false + }, + "race": { + "open": "_openRace", + "files": [ + "race.js" + ], + "three": false + }, + "waves": { + "open": "_openWaves", + "files": [ + "waves.js" + ], + "three": false + }, + "hydrostatics": { + "open": "_openHydro", + "files": [ + "hydrostatics.js" + ], + "three": false + }, + "radioactive": { + "open": "_openRadioactive", + "files": [ + "radioactive.js" + ], + "three": false + }, + "geometry": { + "open": "_openGeometry", + "files": [ + "geometry.js", + "triangle.js" + ], + "three": false + }, + "logic": { + "open": "_openLogic", + "files": [ + "logic.js" + ], + "three": false + }, + "heatengine": { + "open": "_openHeatEngine", + "files": [ + "heatengine.js" + ], + "three": false + }, + "stoichiometry": { + "open": "_openStoich", + "files": [ + "stoichiometry.js" + ], + "three": false + }, + "qualanalysis": { + "open": "_openQualAnalysis", + "files": [ + "qualanalysis.js" + ], + "three": false + }, + "periodic": { + "open": "_openPeriodic", + "files": [ + "_periodic_data.js", + "periodic.js" + ], + "three": true + }, + "organic": { + "open": "_openOrganic", + "files": [ + "organic.js" + ], + "three": false + }, + "solutions": { + "open": "_openSolutions", + "files": [ + "solutions.js" + ], + "three": false + } +}; +window.LAB_LAZY_FILES = ["angrybirds.js","bohratom.js","brownian.js","celldivision.js","chemsandbox.js","circuit.js","collision.js","crystal.js","diffusion.js","electrolysis.js","emfield.js","equilibrium.js","flask.js","forcesandbox.js","gas.js","geometry.js","graphtransform.js","heatengine.js","hydrostatics.js","ionexchange.js","isoprocess.js","logic.js","newton.js","normaldist.js","opticsbench.js","orbitals.js","organic.js","pendulum.js","periodic.js","photosynthesis.js","probability.js","projectile.js","quadratic.js","qualanalysis.js","race.js","radioactive.js","reactions.js","redox.js","solutions.js","states.js","stereo.js","stoichiometry.js","titration.js","triangle.js","trigcircle.js","waves.js","_periodic_data.js"]; diff --git a/frontend/lab.html b/frontend/lab.html index eae2f81..abc74da 100644 --- a/frontend/lab.html +++ b/frontend/lab.html @@ -400,71 +400,33 @@ - + + + - - - - - - - - - - - - + - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +