feat(textbooks): объединить 3 части Физики 8 в один hub-учебник
Подход: hub-страница, а не слияние файлов. Проблема: 3 готовых файла-главы (thermal/electro/optics) занимали 3 карточки в каталоге. Физическое слияние в один файл = 800КБ+, конфликты CSS/JS namespaces, риск сломать KaTeX. Решение: - Создан frontend/textbooks/physics_8.html — hub-страница с 3 крупными карточками-разделами (амбер/синий/фиолетовый) - Карточки ссылаются напрямую на /textbooks/physics8_thermal.html и т.д. (express.static уже отдаёт эти файлы) - Из каталога /textbooks теперь видна ОДНА карточка «Физика 8», sort_order 4 - Hub-страница показывает прогресс по каждой главе через LocalStorage (best-effort парсинг) - Header «К каталогу», переключатель темы синхронизирован с главами Миграция 010: удалила 3 прежние записи (physics-8-thermal/electro/optics), добавила физическо-8 → physics_8.html, para_count=40. Эмодзи в hub не используются (только inline SVG). Эмодзи в файлах глав остались — это контент.
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
-- Объединить 3 отдельных учебника физики 8 (thermal/electro/optics) в один hub-учебник.
|
||||
-- Hub-страница physics_8.html содержит карточки 3 разделов и ссылки на исходные файлы.
|
||||
|
||||
-- 1. Удаляем 3 прежние записи (создали их часом раньше, прогресса пользователей ещё нет).
|
||||
DELETE FROM textbooks WHERE slug IN ('physics-8-thermal','physics-8-electro','physics-8-optics');
|
||||
|
||||
-- 2. Регистрируем единый учебник «Физика 8» с hub-страницей.
|
||||
INSERT OR IGNORE INTO textbooks (slug, subject, grade, title, author, description, html_path, para_count, color, sort_order) VALUES
|
||||
('physics-8', 'physics', 8, 'Физика — 8 класс', 'Исаченкова Л. А.',
|
||||
'Интерактивный учебник по физике 8 класса. 40 параграфов в трёх разделах: Тепловые явления (§1–§11), Электрические явления (§12–§31), Световые явления (§32–§40).',
|
||||
'physics_8.html', 40, 'blue', 4);
|
||||
@@ -0,0 +1,355 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>Физика 8 — учебник · Исаченкова Л. А.</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800;900&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root{
|
||||
--bg:#eff6ff; --card:#fff;
|
||||
--text:#1e293b; --muted:#64748b;
|
||||
--border:#dbeafe;
|
||||
--pri:#2563eb; --pri-d:#1d4ed8;
|
||||
--amber:#d97706; --amber-d:#b45309; --amber-bg:#fef3c7;
|
||||
--blue:#2563eb; --blue-d:#1d4ed8; --blue-bg:#dbeafe;
|
||||
--violet:#7c3aed; --violet-d:#6d28d9; --violet-bg:#ede9fe;
|
||||
--sh:0 4px 16px rgba(37,99,235,.10);
|
||||
--sh-h:0 12px 36px rgba(37,99,235,.18);
|
||||
}
|
||||
html.dark{
|
||||
--bg:#0b1220; --card:#152033;
|
||||
--text:#e2e8f0; --muted:#94a3b8;
|
||||
--border:#1e293b;
|
||||
--amber-bg:rgba(217,119,6,.16);
|
||||
--blue-bg:rgba(37,99,235,.18);
|
||||
--violet-bg:rgba(124,58,237,.18);
|
||||
}
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
html,body{min-height:100vh}
|
||||
body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);line-height:1.55;transition:background .25s,color .25s}
|
||||
|
||||
.hdr{position:relative;background:linear-gradient(120deg,#1e3a8a 0%,#2563eb 55%,#3b82f6 100%);color:#fff;padding:32px 24px 28px;overflow:hidden;border-bottom:2px solid rgba(255,255,255,.08)}
|
||||
.hdr::before{content:'ФИЗИКА';position:absolute;right:-14px;top:-18%;font-family:'Outfit',sans-serif;font-size:clamp(5rem,16vw,13rem);font-weight:900;letter-spacing:-.04em;color:transparent;-webkit-text-stroke:1.5px rgba(255,255,255,.10);line-height:1;pointer-events:none;user-select:none}
|
||||
.hdr-inner{position:relative;z-index:1;max-width:1100px;margin:0 auto;display:flex;align-items:center;gap:18px;flex-wrap:wrap}
|
||||
.hdr-back{display:inline-flex;align-items:center;gap:8px;padding:8px 14px;background:rgba(255,255,255,.14);border-radius:9px;color:#fff;text-decoration:none;font-size:.85rem;font-weight:600;transition:background .15s}
|
||||
.hdr-back:hover{background:rgba(255,255,255,.24)}
|
||||
.hdr h1{font-family:'Outfit',sans-serif;font-size:1.85rem;font-weight:900;letter-spacing:-.01em}
|
||||
.hdr-sub{font-size:.92rem;opacity:.85;margin-top:4px}
|
||||
.hdr-side{margin-left:auto;display:flex;gap:8px}
|
||||
.hdr-btn{padding:8px 12px;background:rgba(255,255,255,.14);border:none;color:#fff;border-radius:9px;cursor:pointer;font-weight:600;font-size:.82rem;display:inline-flex;align-items:center;gap:6px;transition:background .15s}
|
||||
.hdr-btn:hover{background:rgba(255,255,255,.24)}
|
||||
.ic{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
|
||||
|
||||
main{max-width:1100px;margin:0 auto;padding:32px 24px 60px}
|
||||
|
||||
/* INTRO */
|
||||
.intro{background:var(--card);border:1px solid var(--border);border-radius:18px;padding:24px 26px;margin-bottom:30px;box-shadow:var(--sh);position:relative;overflow:hidden}
|
||||
.intro::before{content:'⚡';position:absolute;right:-10px;top:-30px;font-size:11rem;opacity:.05;line-height:1;pointer-events:none}
|
||||
.intro h2{font-family:'Outfit',sans-serif;font-size:1.45rem;font-weight:800;color:var(--pri-d);margin-bottom:10px;letter-spacing:-.01em}
|
||||
.intro p{font-size:1.02rem;color:var(--text);opacity:.9;max-width:720px;margin-bottom:8px}
|
||||
.intro .meta{margin-top:14px;display:flex;gap:18px;flex-wrap:wrap;font-size:.86rem;color:var(--muted)}
|
||||
.intro .meta b{color:var(--pri-d)}
|
||||
|
||||
/* OVERALL PROGRESS */
|
||||
.prog-overall{background:linear-gradient(135deg,var(--blue-bg),var(--violet-bg));border:1px solid var(--border);border-radius:14px;padding:14px 18px;margin-bottom:28px;display:flex;gap:14px;align-items:center;flex-wrap:wrap}
|
||||
.po-icon{width:46px;height:46px;border-radius:12px;background:linear-gradient(135deg,#3b82f6,#7c3aed);color:#fff;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-family:'Outfit',sans-serif;font-size:1.4rem;font-weight:900}
|
||||
.po-text{flex:1;min-width:160px}
|
||||
.po-label{font-size:.78rem;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:4px}
|
||||
.po-bar{height:8px;background:rgba(37,99,235,.18);border-radius:5px;overflow:hidden;margin-top:6px}
|
||||
.po-fill{height:100%;background:linear-gradient(90deg,var(--blue),var(--violet));border-radius:5px;transition:width .5s}
|
||||
|
||||
/* CHAPTERS */
|
||||
.ch-grid{display:grid;grid-template-columns:1fr;gap:18px;margin-bottom:30px}
|
||||
@media(min-width:760px){.ch-grid{grid-template-columns:1fr 1fr 1fr}}
|
||||
|
||||
.ch-card{background:var(--card);border:1.5px solid var(--border);border-radius:18px;overflow:hidden;display:flex;flex-direction:column;transition:transform .2s,box-shadow .2s,border-color .2s;cursor:pointer;text-decoration:none;color:inherit}
|
||||
.ch-card:hover{transform:translateY(-4px);box-shadow:var(--sh-h)}
|
||||
.ch-cover{padding:22px 22px 18px;color:#fff;position:relative;overflow:hidden}
|
||||
.ch-cover-wm{position:absolute;right:-10px;top:-30px;font-size:8rem;font-weight:900;font-family:'Outfit',sans-serif;line-height:1;letter-spacing:-.04em;color:rgba(255,255,255,.13);pointer-events:none}
|
||||
.ch-num{display:inline-block;padding:4px 10px;background:rgba(255,255,255,.20);border-radius:99px;font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:8px;position:relative;z-index:1}
|
||||
.ch-title{font-family:'Outfit',sans-serif;font-size:1.25rem;font-weight:800;letter-spacing:-.01em;position:relative;z-index:1}
|
||||
.ch-range{font-size:.86rem;opacity:.85;margin-top:4px;position:relative;z-index:1;font-weight:500}
|
||||
|
||||
.ch-cover.thermal{background:linear-gradient(135deg,#92400e,#d97706 60%,#f59e0b)}
|
||||
.ch-cover.electro{background:linear-gradient(135deg,#1e40af,#2563eb 60%,#3b82f6)}
|
||||
.ch-cover.optics{background:linear-gradient(135deg,#5b21b6,#7c3aed 60%,#a855f7)}
|
||||
|
||||
.ch-body{padding:18px 22px 20px;display:flex;flex-direction:column;flex:1}
|
||||
.ch-desc{font-size:.92rem;color:var(--text);opacity:.85;flex:1;margin-bottom:14px;line-height:1.55}
|
||||
.ch-topics{display:flex;flex-wrap:wrap;gap:5px;margin-bottom:14px}
|
||||
.ch-topic{padding:3px 9px;background:var(--blue-bg);color:var(--blue-d);border-radius:6px;font-size:.72rem;font-weight:600}
|
||||
.ch-card .ch-topic.amber{background:var(--amber-bg);color:var(--amber-d)}
|
||||
.ch-card .ch-topic.violet{background:var(--violet-bg);color:var(--violet-d)}
|
||||
|
||||
.ch-prog{margin-bottom:14px}
|
||||
.ch-prog-label{display:flex;justify-content:space-between;font-size:.74rem;color:var(--muted);font-weight:600;margin-bottom:4px}
|
||||
.ch-prog-bar{height:6px;background:rgba(37,99,235,.12);border-radius:4px;overflow:hidden}
|
||||
.ch-prog-fill{height:100%;border-radius:4px;transition:width .5s}
|
||||
.ch-card.thermal .ch-prog-fill{background:linear-gradient(90deg,#d97706,#f59e0b)}
|
||||
.ch-card.electro .ch-prog-fill{background:linear-gradient(90deg,#2563eb,#3b82f6)}
|
||||
.ch-card.optics .ch-prog-fill{background:linear-gradient(90deg,#7c3aed,#a855f7)}
|
||||
|
||||
.ch-action{display:flex;align-items:center;justify-content:space-between;padding:10px 14px;border-radius:11px;font-weight:700;font-size:.92rem;color:#fff;transition:filter .15s}
|
||||
.ch-action:hover{filter:brightness(1.08)}
|
||||
.ch-card.thermal .ch-action{background:linear-gradient(135deg,#d97706,#f59e0b)}
|
||||
.ch-card.electro .ch-action{background:linear-gradient(135deg,#2563eb,#3b82f6)}
|
||||
.ch-card.optics .ch-action{background:linear-gradient(135deg,#7c3aed,#a855f7)}
|
||||
|
||||
/* INFO BLOCKS */
|
||||
.info-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px;margin-bottom:30px}
|
||||
.info-card{background:var(--card);border:1px solid var(--border);border-radius:13px;padding:16px 18px;display:flex;gap:12px;align-items:flex-start}
|
||||
.info-card-ic{width:38px;height:38px;border-radius:10px;background:var(--blue-bg);color:var(--blue-d);display:flex;align-items:center;justify-content:center;flex-shrink:0}
|
||||
.info-card-ic .ic{width:20px;height:20px;stroke-width:2.2}
|
||||
.info-card-title{font-weight:700;color:var(--pri-d);margin-bottom:3px}
|
||||
.info-card-text{font-size:.85rem;color:var(--muted);line-height:1.5}
|
||||
|
||||
.foot{text-align:center;padding:24px 16px;color:var(--muted);font-size:.78rem;border-top:1px solid var(--border)}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header class="hdr">
|
||||
<div class="hdr-inner">
|
||||
<div>
|
||||
<a href="/textbooks" class="hdr-back">
|
||||
<svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg>
|
||||
К каталогу
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<h1>Физика 8 класс</h1>
|
||||
<div class="hdr-sub">Интерактивный учебник · Исаченкова Л. А. · 40 параграфов · 3 раздела</div>
|
||||
</div>
|
||||
<div class="hdr-side">
|
||||
<button id="theme-btn" class="hdr-btn" title="Сменить тему">
|
||||
<svg class="ic" viewBox="0 0 24 24"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>
|
||||
<span id="theme-lab">Тёмная</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
|
||||
<section class="intro">
|
||||
<h2>Изучаем 3 раздела физики</h2>
|
||||
<p>Программа 8 класса разделена на три большие темы. Каждый раздел — это отдельная интерактивная книга со своими симуляциями, формулами, задачами и тестами. Вы можете изучать их в любом порядке, но программа предполагает последовательное прохождение от теплоты к оптике.</p>
|
||||
<div class="meta">
|
||||
<span><b>40</b> параграфов</span>
|
||||
<span><b>3</b> раздела</span>
|
||||
<span><b>Автор:</b> Исаченкова Л. А.</span>
|
||||
<span><b>Год:</b> 2018</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="prog-overall">
|
||||
<div class="po-icon">Σ</div>
|
||||
<div class="po-text">
|
||||
<div class="po-label">Общий прогресс по курсу</div>
|
||||
<div id="overall-text" style="font-size:1.05rem;font-weight:700">— параграфов</div>
|
||||
<div class="po-bar"><div id="overall-fill" class="po-fill" style="width:0%"></div></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="ch-grid">
|
||||
|
||||
<a href="/textbooks/physics8_thermal.html" class="ch-card thermal" id="ch-thermal">
|
||||
<div class="ch-cover thermal">
|
||||
<div class="ch-cover-wm">I</div>
|
||||
<div class="ch-num">Раздел I · §1–§11</div>
|
||||
<div class="ch-title">Тепловые явления</div>
|
||||
<div class="ch-range">11 параграфов</div>
|
||||
</div>
|
||||
<div class="ch-body">
|
||||
<div class="ch-desc">Внутренняя энергия, теплопередача, удельная теплоёмкость, фазовые переходы, плавление и парообразование, тепловые двигатели.</div>
|
||||
<div class="ch-topics">
|
||||
<span class="ch-topic amber">Температура</span>
|
||||
<span class="ch-topic amber">Теплоёмкость</span>
|
||||
<span class="ch-topic amber">Кипение</span>
|
||||
<span class="ch-topic amber">КПД</span>
|
||||
</div>
|
||||
<div class="ch-prog">
|
||||
<div class="ch-prog-label"><span>Прогресс</span><span id="prog-thermal">0%</span></div>
|
||||
<div class="ch-prog-bar"><div class="ch-prog-fill" id="fill-thermal" style="width:0%"></div></div>
|
||||
</div>
|
||||
<div class="ch-action">
|
||||
<span id="btn-thermal">Открыть раздел</span>
|
||||
<svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="/textbooks/physics8_electro.html" class="ch-card electro" id="ch-electro">
|
||||
<div class="ch-cover electro">
|
||||
<div class="ch-cover-wm">II</div>
|
||||
<div class="ch-num">Раздел II · §12–§31</div>
|
||||
<div class="ch-title">Электрические явления</div>
|
||||
<div class="ch-range">20 параграфов</div>
|
||||
</div>
|
||||
<div class="ch-body">
|
||||
<div class="ch-desc">Электризация, закон Кулона, электрический ток, закон Ома, работа и мощность тока, электромагнитные явления, генератор и трансформатор.</div>
|
||||
<div class="ch-topics">
|
||||
<span class="ch-topic">Электричество</span>
|
||||
<span class="ch-topic">Сопротивление</span>
|
||||
<span class="ch-topic">Закон Ома</span>
|
||||
<span class="ch-topic">Магнетизм</span>
|
||||
</div>
|
||||
<div class="ch-prog">
|
||||
<div class="ch-prog-label"><span>Прогресс</span><span id="prog-electro">0%</span></div>
|
||||
<div class="ch-prog-bar"><div class="ch-prog-fill" id="fill-electro" style="width:0%"></div></div>
|
||||
</div>
|
||||
<div class="ch-action">
|
||||
<span id="btn-electro">Открыть раздел</span>
|
||||
<svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a href="/textbooks/physics8_optics.html" class="ch-card optics" id="ch-optics">
|
||||
<div class="ch-cover optics">
|
||||
<div class="ch-cover-wm">III</div>
|
||||
<div class="ch-num">Раздел III · §32–§40</div>
|
||||
<div class="ch-title">Световые явления</div>
|
||||
<div class="ch-range">9 параграфов</div>
|
||||
</div>
|
||||
<div class="ch-body">
|
||||
<div class="ch-desc">Источники света, прямолинейное распространение, отражение и преломление, плоское зеркало, линзы, оптические приборы, цвет и спектр.</div>
|
||||
<div class="ch-topics">
|
||||
<span class="ch-topic violet">Свет</span>
|
||||
<span class="ch-topic violet">Линзы</span>
|
||||
<span class="ch-topic violet">Зеркала</span>
|
||||
<span class="ch-topic violet">Спектр</span>
|
||||
</div>
|
||||
<div class="ch-prog">
|
||||
<div class="ch-prog-label"><span>Прогресс</span><span id="prog-optics">0%</span></div>
|
||||
<div class="ch-prog-bar"><div class="ch-prog-fill" id="fill-optics" style="width:0%"></div></div>
|
||||
</div>
|
||||
<div class="ch-action">
|
||||
<span id="btn-optics">Открыть раздел</span>
|
||||
<svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<section class="info-grid">
|
||||
<div class="info-card">
|
||||
<div class="info-card-ic"><svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg></div>
|
||||
<div>
|
||||
<div class="info-card-title">Интерактив в каждом §</div>
|
||||
<div class="info-card-text">Анимации, виджеты-калькуляторы, тесты на проверку — материал «оживает» прямо в учебнике.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-card">
|
||||
<div class="info-card-ic"><svg class="ic" viewBox="0 0 24 24"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg></div>
|
||||
<div>
|
||||
<div class="info-card-title">Прогресс сохраняется</div>
|
||||
<div class="info-card-text">Закрываете вкладку — прогресс остаётся. Открываете снова — продолжаете с того же места.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-card">
|
||||
<div class="info-card-ic"><svg class="ic" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg></div>
|
||||
<div>
|
||||
<div class="info-card-title">Формулы — KaTeX</div>
|
||||
<div class="info-card-text">Математика рендерится настоящей типографикой, а не картинками. Работает офлайн после первой загрузки.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-card">
|
||||
<div class="info-card-ic"><svg class="ic" viewBox="0 0 24 24"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg></div>
|
||||
<div>
|
||||
<div class="info-card-title">Светлая и тёмная тема</div>
|
||||
<div class="info-card-text">Переключатель в шапке. Тема запоминается между визитами и переходит на все главы.</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
|
||||
<footer class="foot">
|
||||
Интерактивный учебник «Физика — 8 класс» · автор: Исаченкова Л. А. · «Адукацыя i выхаванне», Минск, 2018
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
/* THEME (sync with chapter files: they likely also use localStorage 'theme' or similar) */
|
||||
(function(){
|
||||
const t = localStorage.getItem('physics8_theme') || localStorage.getItem('theme') || 'light';
|
||||
if(t === 'dark') document.documentElement.classList.add('dark');
|
||||
const lab = document.getElementById('theme-lab');
|
||||
if(lab) lab.textContent = t === 'dark' ? 'Светлая' : 'Тёмная';
|
||||
document.getElementById('theme-btn').addEventListener('click', ()=>{
|
||||
document.documentElement.classList.toggle('dark');
|
||||
const dark = document.documentElement.classList.contains('dark');
|
||||
localStorage.setItem('physics8_theme', dark ? 'dark' : 'light');
|
||||
localStorage.setItem('theme', dark ? 'dark' : 'light');
|
||||
if(lab) lab.textContent = dark ? 'Светлая' : 'Тёмная';
|
||||
});
|
||||
})();
|
||||
|
||||
/* PROGRESS — читаем из LocalStorage главных файлов
|
||||
(каждая глава физики 8 хранит свой прогресс, попробуем найти ключи) */
|
||||
function readChapterProgress(prefix, totalParas){
|
||||
// Поищем ключи в localStorage: физика 8 главы хранят что-то типа "p1_done", "thermal_p3" и т.д.
|
||||
// Применим эвристику: ищем ключи, содержащие prefix
|
||||
let count = 0;
|
||||
for(let i = 0; i < localStorage.length; i++){
|
||||
const k = localStorage.key(i);
|
||||
if(!k) continue;
|
||||
const v = localStorage.getItem(k);
|
||||
if(k.toLowerCase().includes(prefix) && (v === 'true' || v === '1' || v === 'done')){
|
||||
count++;
|
||||
}
|
||||
}
|
||||
// Fallback: попробуем разные форматы — JSON-массив пройденных
|
||||
const candidates = [`physics8_${prefix}_done`, `${prefix}_progress`, `physics8_${prefix}_read`];
|
||||
for(const key of candidates){
|
||||
const v = localStorage.getItem(key);
|
||||
if(v){
|
||||
try{
|
||||
const arr = JSON.parse(v);
|
||||
if(Array.isArray(arr)) return Math.min(arr.length, totalParas);
|
||||
}catch{}
|
||||
}
|
||||
}
|
||||
return Math.min(count, totalParas);
|
||||
}
|
||||
|
||||
function updateProgress(){
|
||||
const tBy = readChapterProgress('thermal', 11);
|
||||
const eBy = readChapterProgress('electro', 20);
|
||||
const oBy = readChapterProgress('optics', 9);
|
||||
function set(id, fillId, n, total){
|
||||
const pct = total ? Math.round(n * 100 / total) : 0;
|
||||
const el = document.getElementById(id);
|
||||
const fl = document.getElementById(fillId);
|
||||
if(el) el.textContent = pct + '% · ' + n + '/' + total;
|
||||
if(fl) fl.style.width = pct + '%';
|
||||
}
|
||||
set('prog-thermal', 'fill-thermal', tBy, 11);
|
||||
set('prog-electro', 'fill-electro', eBy, 20);
|
||||
set('prog-optics', 'fill-optics', oBy, 9);
|
||||
const total = tBy + eBy + oBy;
|
||||
const totalPct = Math.round(total * 100 / 40);
|
||||
document.getElementById('overall-text').textContent = total + ' из 40 параграфов · ' + totalPct + '%';
|
||||
document.getElementById('overall-fill').style.width = totalPct + '%';
|
||||
// Buttons text
|
||||
['thermal','electro','optics'].forEach(k=>{
|
||||
const elBtn = document.getElementById('btn-'+k);
|
||||
if(elBtn){
|
||||
const n = k==='thermal'?tBy : k==='electro'?eBy : oBy;
|
||||
const total = k==='thermal'?11 : k==='electro'?20 : 9;
|
||||
if(n > 0 && n < total) elBtn.textContent = 'Продолжить';
|
||||
else if(n >= total) elBtn.textContent = 'Открыть снова';
|
||||
else elBtn.textContent = 'Открыть раздел';
|
||||
}
|
||||
});
|
||||
}
|
||||
updateProgress();
|
||||
window.addEventListener('focus', updateProgress);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user