Files
Learn_System/frontend/js/textbook-deeplink.js
Maxim Dolgolyov 49f01fd23c fix(textbook): рабочий deep-link к § (/textbook/<slug>#sec-pN открывает нужный §)
Раньше статические страницы алгебры/геометрии игнорировали location.hash (init всегда
goTo('p10')), а textbook-tracker матчил только #pN через .para-pill — поэтому ссылки
exam-prep вида #sec-pN вели на главу, но не на §.

- server.js: /textbook/:slug всегда инжектит хелпер (и в обычном режиме, и в embed),
  _renderEmbed → _renderTextbook (кэш по filePath|mode, заголовки no-store сохранены).
- frontend/js/textbook-deeplink.js: по #sec-pN / #pN кликает .psel-card[data-id]
  (фолбэк .para-pill[data-para] → goTo → scrollIntoView). Универсально для статических
  и движковых страниц, идемпотентно, не конфликтует с textbook-tracker.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 16:32:57 +03:00

51 lines
2.0 KiB
JavaScript

/* textbook-deeplink.js
Injected by the server into every /textbook/<slug> page.
Makes deep links like /textbook/<slug>#sec-p13 (and #p13) actually open § 13.
The static algebra/geometry pages ignore location.hash in their own init()
(they always show §1), and textbook-tracker.js only matches the bare "#pN"
form via .para-pill — so exam-prep's "#sec-pN" links never navigated.
This helper is page-agnostic: it activates the target section by clicking the
matching paragraph card/pill, which works on both the static pages
(.psel-card[data-id]) and the math6-engine pages (.psel-card / .para-pill),
falling back to goTo()/scrollIntoView. Idempotent; safe alongside
textbook-tracker.js (a repeated activation of the same § is a no-op). */
(function () {
'use strict';
// "#sec-p13" | "#p13" -> "p13" (null if not a paragraph anchor)
function targetId() {
var h = (location.hash || '').replace(/^#/, '');
if (!h) return null;
var m = h.match(/^sec-(p\d+)$/) || h.match(/^(p\d+)$/);
return m ? m[1] : null;
}
function go() {
var id = targetId();
if (!id) return;
var card = document.querySelector('.psel-card[data-id="' + id + '"]');
if (card) { card.click(); return; }
var pill = document.querySelector('.para-pill[data-para="' + id + '"]');
if (pill) { pill.click(); return; }
if (typeof window.goTo === 'function') { try { window.goTo(id); return; } catch (e) {} }
var el = document.getElementById('sec-' + id) || document.getElementById(id);
if (el && el.scrollIntoView) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
// The page's own init builds the cards/sections on DOMContentLoaded; run after
// it (microtask + a slower pass for engine-rendered math5/6 pages).
function boot() {
setTimeout(go, 60);
setTimeout(go, 350);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', boot);
} else {
boot();
}
window.addEventListener('hashchange', go);
})();