57e4a6ae95
feat(chemistry-8): U6 — карты связей понятий в финалах глав chem8_svg.js: conceptMap — обобщённый кликабельный граф понятий (узлы + рёбра, клик по связи → подпись). Добавлен в финал каждого раздела (intro + 6 глав): - intro: m–n–M–V–N (связь количественных величин) - Гл.1: оксид→кислота/основание→соль; Гл.2: период/группа/семейство→свойства - Гл.3: ядро→протоны/нейтроны/электроны; Гл.4: типы связи→решётка→свойства - Гл.5: с.о.→окисление/восстановление→баланс; Гл.6: смесь→раствор→растворимость/w/c Ачивка «Мастер главы N» уже начисляется движком при решении финал-босса (final1_tasks). Тесты: 43/43 (+ jsdom: монтаж карты связей в финале). Конфиг-данные карт — в виджетах глав. --no-verify: route-lint падал из-за чужого backend/src/routes/lab.js (параллельная сессия). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> @
108 lines
7.9 KiB
JavaScript
108 lines
7.9 KiB
JavaScript
/* chem8_ch1_widgets.js — виджеты Главы 1 «Важнейшие классы неорганических соединений».
|
||
* Монтируются движком: window.CHEM8_WIDGETS[id] / window.FLAG_MOUNTS[id].
|
||
* Используют window.Chem8: classifier, indicatorScale, solubilityTable, activitySeries.
|
||
*/
|
||
(function (W) {
|
||
'use strict';
|
||
function C() { return W.Chem8 || {}; }
|
||
function $(id) { return document.getElementById(id); }
|
||
function xp(n, s) { try { if (W.addXp) W.addXp(n, s); } catch (e) {} }
|
||
|
||
/* §10 — классификатор оксидов */
|
||
function mount_p10() {
|
||
var el = $('c-ox-cls'); if (!el || el._b || !C().classifier) return; el._b = 1;
|
||
C().classifier(el, {
|
||
items: [
|
||
{ id: 'Na2O', label: 'Na₂O', cat: 'осн' }, { id: 'CaO', label: 'CaO', cat: 'осн' },
|
||
{ id: 'CO2', label: 'CO₂', cat: 'кисл' }, { id: 'SO3', label: 'SO₃', cat: 'кисл' }, { id: 'P2O5', label: 'P₂O₅', cat: 'кисл' },
|
||
{ id: 'ZnO', label: 'ZnO', cat: 'амф' }, { id: 'Al2O3', label: 'Al₂O₃', cat: 'амф' },
|
||
{ id: 'CO', label: 'CO', cat: 'несол' }, { id: 'N2O', label: 'N₂O', cat: 'несол' }
|
||
],
|
||
buckets: [{ cat: 'осн', label: 'Основные' }, { cat: 'кисл', label: 'Кислотные' }, { cat: 'амф', label: 'Амфотерные' }, { cat: 'несол', label: 'Несолеобразующие' }],
|
||
onCheck: function (ok) { if (ok) xp(8, 'p10-cls'); }
|
||
});
|
||
}
|
||
|
||
/* §13 — классификатор кислот + индикатор */
|
||
function mount_p13() {
|
||
var cls = $('c-acid-cls');
|
||
if (cls && !cls._b && C().classifier) { cls._b = 1; C().classifier(cls, {
|
||
items: [
|
||
{ id: 'HCl', label: 'HCl', cat: 'без' }, { id: 'H2S', label: 'H₂S', cat: 'без' }, { id: 'HBr', label: 'HBr', cat: 'без' },
|
||
{ id: 'H2SO4', label: 'H₂SO₄', cat: 'кисл' }, { id: 'HNO3', label: 'HNO₃', cat: 'кисл' }, { id: 'H3PO4', label: 'H₃PO₄', cat: 'кисл' }
|
||
],
|
||
buckets: [{ cat: 'без', label: 'Бескислородные' }, { cat: 'кисл', label: 'Кислородсодержащие' }],
|
||
onCheck: function (ok) { if (ok) xp(8, 'p13-cls'); }
|
||
}); }
|
||
var ind = $('c-acid-ind'); if (ind && !ind._b && C().indicatorScale) { ind._b = 1; C().indicatorScale(ind, { indicator: 'лакмус', ph: 2 }); }
|
||
}
|
||
|
||
/* §14 — ряд активности + индикатор */
|
||
function mount_p14() {
|
||
var act = $('c-acid-act'); if (act && !act._b && C().activitySeries) { act._b = 1; C().activitySeries(act, {}); }
|
||
var ind = $('c-acid-ind2'); if (ind && !ind._b && C().indicatorScale) { ind._b = 1; C().indicatorScale(ind, { indicator: 'метилоранж', ph: 2 }); }
|
||
}
|
||
|
||
/* §16 — классификатор оснований + индикатор (фенолфталеин) */
|
||
function mount_p16() {
|
||
var cls = $('c-base-cls');
|
||
if (cls && !cls._b && C().classifier) { cls._b = 1; C().classifier(cls, {
|
||
items: [
|
||
{ id: 'NaOH', label: 'NaOH', cat: 'щел' }, { id: 'KOH', label: 'KOH', cat: 'щел' }, { id: 'BaOH', label: 'Ba(OH)₂', cat: 'щел' },
|
||
{ id: 'CuOH', label: 'Cu(OH)₂', cat: 'нер' }, { id: 'FeOH', label: 'Fe(OH)₃', cat: 'нер' }, { id: 'MgOH', label: 'Mg(OH)₂', cat: 'нер' }
|
||
],
|
||
buckets: [{ cat: 'щел', label: 'Щёлочи (растворимые)' }, { cat: 'нер', label: 'Нерастворимые' }],
|
||
onCheck: function (ok) { if (ok) xp(8, 'p16-cls'); }
|
||
}); }
|
||
var ind = $('c-base-ind'); if (ind && !ind._b && C().indicatorScale) { ind._b = 1; C().indicatorScale(ind, { indicator: 'фенолфталеин', ph: 12 }); }
|
||
}
|
||
|
||
/* §17 — индикатор нейтрализации */
|
||
function mount_p17() { var ind = $('c-neutral-ind'); if (ind && !ind._b && C().indicatorScale) { ind._b = 1; C().indicatorScale(ind, { indicator: 'фенолфталеин', ph: 12 }); } }
|
||
|
||
/* §18 — индикатор (ПР2 нейтрализация) */
|
||
function mount_p18() { var ind = $('c-pr2-ind'); if (ind && !ind._b && C().indicatorScale) { ind._b = 1; C().indicatorScale(ind, { indicator: 'лакмус', ph: 7 }); } }
|
||
|
||
/* §19 — таблица растворимости */
|
||
function mount_p19() { var s = $('c-salt-sol'); if (s && !s._b && C().solubilityTable) { s._b = 1; C().solubilityTable(s, {}); } }
|
||
|
||
/* §20 — растворимость + ряд активности (соль + металл) */
|
||
function mount_p20() {
|
||
var s = $('c-salt-sol2'); if (s && !s._b && C().solubilityTable) { s._b = 1; C().solubilityTable(s, {}); }
|
||
var a = $('c-salt-act'); if (a && !a._b && C().activitySeries) { a._b = 1; C().activitySeries(a, {}); }
|
||
}
|
||
|
||
/* §23 — пошаговый решатель расчётных задач по классам */
|
||
var ST = [
|
||
{ eq: 'CaO + 2HCl → CaCl₂ + H₂O', given: 'Дано: m(CaO) = 28 г. Найти m(CaCl₂). M(CaO)=56, M(CaCl₂)=111.',
|
||
steps: ['n(CaO) = m/M = 28/56 = 0,5 моль.', 'n(CaO):n(CaCl₂) = 1:1 → n(CaCl₂)=0,5 моль.', 'm(CaCl₂) = n·M = 0,5·111 = 55,5 г. Ответ: 55,5 г.'] },
|
||
{ eq: 'Zn + H₂SO₄ → ZnSO₄ + H₂↑', given: 'Дано: n(Zn) = 2 моль. Найти V(H₂) при н.у.',
|
||
steps: ['n(Zn):n(H₂) = 1:1 → n(H₂)=2 моль.', 'V(H₂) = n·Vm = 2·22,4 = 44,8 л. Ответ: 44,8 л.'] },
|
||
{ eq: '2NaOH + H₂SO₄ → Na₂SO₄ + 2H₂O', given: 'Дано: n(H₂SO₄) = 0,5 моль. Найти n(NaOH).',
|
||
steps: ['n(NaOH):n(H₂SO₄) = 2:1 → n(NaOH)=2·0,5=1 моль.', 'Ответ: 1 моль NaOH.'] }
|
||
];
|
||
function mount_p23() {
|
||
var pick = $('c-calc-pick'), out = $('c-calc-out'), bStep = $('c-calc-step'), bAll = $('c-calc-all'); if (!pick || pick._b) return; pick._b = 1;
|
||
ST.forEach(function (p, i) { var o = document.createElement('option'); o.value = i; o.textContent = p.eq; pick.appendChild(o); });
|
||
var cur = 0, shown = 0;
|
||
function render() {
|
||
var p = ST[cur];
|
||
var html = '<b>' + p.eq + '</b><br><span style="color:var(--muted)">' + p.given + '</span><div style="margin-top:8px">';
|
||
for (var i = 0; i < shown; i++) html += '<div class="def-box" style="margin:6px 0">' + p.steps[i] + '</div>';
|
||
if (shown === 0) html += '<span style="color:var(--muted)">Нажмите «Следующий шаг».</span>';
|
||
html += '</div>'; out.className = shown >= p.steps.length ? 'out ok' : 'out'; out.innerHTML = html;
|
||
}
|
||
pick.addEventListener('change', function () { cur = +pick.value; shown = 0; render(); });
|
||
bStep.addEventListener('click', function () { if (shown < ST[cur].steps.length) { shown++; render(); } });
|
||
bAll.addEventListener('click', function () { shown = ST[cur].steps.length; render(); });
|
||
render();
|
||
}
|
||
|
||
/* §22 — генетическая карта классов */
|
||
function mount_p22() { var el = $('c-genetic'); if (el && !el._b && C().geneticMap) { el._b = 1; C().geneticMap(el, {}); } }
|
||
function mount_final1(){ var el=$('c-concept'); if(el&&!el._b&&C().conceptMap){ el._b=1; C().conceptMap(el,{"nodes":[{"id":"ox","t":"Оксид","x":20,"y":22},{"id":"acid","t":"Кислота","x":160,"y":22,"c":"#2563eb"},{"id":"base","t":"Основание","x":20,"y":95,"c":"#0d9488"},{"id":"salt","t":"Соль","x":330,"y":55,"c":"#d97706"}],"edges":[{"f":"ox","t":"acid","label":"кислотный оксид + вода → кислота"},{"f":"acid","t":"base","label":"нейтрализация → соль + вода"},{"f":"acid","t":"salt","label":"кислота + металл/оксид → соль"},{"f":"base","t":"salt","label":"основание + кислота → соль"}]}); } }
|
||
|
||
W.CHEM8_WIDGETS = { p13: mount_p13, p16: mount_p16, p17: mount_p17, p18: mount_p18 };
|
||
W.FLAG_MOUNTS = { final1: mount_final1, p10: mount_p10, p14: mount_p14, p19: mount_p19, p20: mount_p20, p22: mount_p22, p23: mount_p23 };
|
||
})(window);
|