43fe90d601
Сервер инжектит в /textbook/<slug> плавающую кнопку «В мои материалы» (js/textbook-clip.js + material-save.js рядом с deep-link). Сохраняет текущий § как ссылку /textbook/<slug>#sec-<id> (заголовок = название §, источник = глава). Скрыта в classroom-embed и для неавторизованных. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
50 lines
2.8 KiB
JavaScript
50 lines
2.8 KiB
JavaScript
'use strict';
|
||
/* textbook-clip.js — floating «В мои материалы» button on textbook pages.
|
||
* Injected by the server into /textbook/<slug>. Saves the currently open
|
||
* paragraph as a link (/textbook/<slug>#sec-<id>) into the student's
|
||
* personal materials. Reuses MaterialSave (material-save.js) + LS (api.js).
|
||
* Hidden inside the classroom embed (iframe) to avoid clutter. */
|
||
(function () {
|
||
if (window.parent !== window) return; // skip in classroom embed
|
||
if (!window.LS || !LS.getToken || !LS.getToken()) return; // only for logged-in users
|
||
|
||
function chapterTitle() {
|
||
return (document.title || 'Учебник').replace(/\s*[—|].*$/, '').replace(/\s*·\s*LearnSpace.*$/i, '').trim() || 'Учебник';
|
||
}
|
||
function sectionTitle() {
|
||
const h = document.querySelector('.sec.active .sec-h');
|
||
if (h && h.textContent.trim()) return h.textContent.trim();
|
||
const n = document.querySelector('.psel-card.active .psel-name');
|
||
if (n && n.textContent.trim()) return n.textContent.trim();
|
||
return (document.title || 'Тема').split('·').pop().trim() || 'Тема';
|
||
}
|
||
function activeId() {
|
||
const c = document.querySelector('.psel-card.active');
|
||
return c && c.dataset ? c.dataset.id : null;
|
||
}
|
||
|
||
function save(btn) {
|
||
if (!window.MaterialSave) { if (LS.toast) LS.toast('Модуль не загружен', 'error'); return; }
|
||
const slug = location.pathname.replace(/^\/textbook\//, '').replace(/\/+$/, '');
|
||
if (!slug) return;
|
||
const id = activeId();
|
||
const hash = id ? '#sec-' + id : (location.hash || '');
|
||
MaterialSave.link({ title: sectionTitle(), url: '/textbook/' + slug + hash, sourceTitle: chapterTitle() }, btn);
|
||
}
|
||
|
||
function build() {
|
||
if (document.getElementById('__ls_clip') || !document.body) return;
|
||
const btn = document.createElement('button');
|
||
btn.id = '__ls_clip';
|
||
btn.type = 'button';
|
||
btn.title = 'Сохранить эту тему в «Мои материалы»';
|
||
btn.innerHTML = '<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg><span>В мои материалы</span>';
|
||
btn.style.cssText = 'position:fixed;right:18px;bottom:18px;z-index:9000;display:inline-flex;align-items:center;gap:7px;padding:10px 14px;border-radius:99px;border:none;background:#8b5cf6;color:#fff;font:600 13px/1 Inter,system-ui,sans-serif;cursor:pointer;box-shadow:0 6px 20px rgba(139,92,246,.4)';
|
||
btn.addEventListener('click', function () { save(btn); });
|
||
document.body.appendChild(btn);
|
||
}
|
||
|
||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function () { setTimeout(build, 300); });
|
||
else setTimeout(build, 300);
|
||
})();
|