diff --git a/frontend/js/textbook-tracker.js b/frontend/js/textbook-tracker.js index 5e4520f..5148c5d 100644 --- a/frontend/js/textbook-tracker.js +++ b/frontend/js/textbook-tracker.js @@ -225,24 +225,64 @@ не добавит дубликат. Эта «избыточность» лечит самовосстановлением ситуации, когда localState.read был засорён старым syncPending-багом (есть ключ локально, нет на сервере, mark_read иначе никогда не уйдёт). */ + function recordParaVisit(key) { + if (!key) return; + localState.last = key; + if (!localState.read.includes(key)) { + localState.read.push(key); + } + refreshPillUI(key); + refreshCheckUI(key); + persist(); + syncToServer({ mark_read: key }); + } + function wirePillTracking() { + // Hook 1: всплытие click до body. Работает в обычном HTML. document.body.addEventListener('click', e => { - const tag = e.target && e.target.tagName; - const cls = e.target && e.target.className; - console.log('[tracker] click event target:', tag, '| class:', cls, '| has .para-pill[data-para] up the tree:', !!e.target.closest('.para-pill[data-para]')); - const pill = e.target.closest('.para-pill[data-para]'); + const pill = e.target && e.target.closest && e.target.closest('.para-pill[data-para]'); if (!pill) return; - const key = pill.dataset.para; - console.log('[tracker] клик по пилюле', key); - localState.last = key; - if (!localState.read.includes(key)) { - localState.read.push(key); - } - refreshPillUI(key); - refreshCheckUI(key); - persist(); - syncToServer({ mark_read: key }); + console.log('[tracker] клик по пилюле (bubble)', pill.dataset.para); + recordParaVisit(pill.dataset.para); }); + // Hook 2: capture-фаза (ловит до того, как кто-то остановит propagation). + document.addEventListener('click', e => { + const pill = e.target && e.target.closest && e.target.closest('.para-pill[data-para]'); + if (!pill) return; + // Защита от двойного срабатывания — отметим pill сразу. + if (pill.__tbVisited) return; + pill.__tbVisited = true; + setTimeout(() => { pill.__tbVisited = false; }, 100); + console.log('[tracker] клик по пилюле (capture)', pill.dataset.para); + recordParaVisit(pill.dataset.para); + }, true); + // Hook 3: monkey-patch setParaTab — кликом по пилюле химия/физика 9 вызывают + // inline onclick="setParaTab('pN')". Перехват напрямую = работает даже если + // event-bubbling сломан расширением браузера или CSS overlay. + function patchSetParaTab() { + if (typeof window.setParaTab !== 'function' || window.setParaTab.__tbPatched) return; + const orig = window.setParaTab; + const wrapped = function (para) { + try { + if (para && /^p\d+$/i.test(String(para))) { + console.log('[tracker] setParaTab перехвачен', para); + recordParaVisit(String(para)); + } + } catch (e) { console.warn('[tracker] patch error:', e); } + return orig.apply(this, arguments); + }; + wrapped.__tbPatched = true; + window.setParaTab = wrapped; + console.log('[tracker] setParaTab успешно обёрнут'); + } + patchSetParaTab(); + // Если страница определяет setParaTab позже — поймаем через короткие опросы. + let tries = 0; + const ivl = setInterval(() => { + patchSetParaTab(); + if (typeof window.setParaTab === 'function' && window.setParaTab.__tbPatched) clearInterval(ivl); + if (++tries > 20) clearInterval(ivl); + }, 100); } /* ── 8. Inject styling for read-pills (subtle green dot) ─────── */