152 lines
5.6 KiB
Python
152 lines
5.6 KiB
Python
"""Mode "capture de bloc au survol".
|
|
|
|
Ce script est injecté une seule fois par page (au tout début du chargement,
|
|
comme le pont QWebChannel) et reste actif en permanence. Il gère deux façons
|
|
indépendantes d'activer la capture, combinées via un petit état :
|
|
|
|
- "sticky" : activé/désactivé depuis Python (bouton de la barre d'outils
|
|
ou raccourci Ctrl+Shift+B) — reste actif jusqu'à nouvelle bascule.
|
|
- "ctrlHeld" : activé tant que la touche Ctrl est physiquement maintenue
|
|
enfoncée dans la page, désactivé au relâchement (ou si la fenêtre perd
|
|
le focus pendant que Ctrl est tenu, pour éviter un blocage).
|
|
|
|
Le mode est actif dès que l'un des deux est vrai. Quand actif :
|
|
1) le bloc de contenu (paragraphe, article, div, item de liste, etc.)
|
|
sous le curseur se surligne via un overlay flottant qui ne modifie
|
|
pas le DOM de la page ;
|
|
2) un clic sur ce bloc empêche la navigation normale et transmet son
|
|
texte à Python via le pont QWebChannel (window.pdfscrapperBridge,
|
|
voir bridge.py).
|
|
|
|
Le bloc retenu est le plus proche ancêtre "de type bloc" de l'élément
|
|
survolé (via Element.closest) : un <p> si on survole un paragraphe, un
|
|
<article>/<div> plus large si on survole une zone qui n'a pas de bloc plus
|
|
précis en dessous.
|
|
"""
|
|
|
|
# Script persistant, injecté une fois par page (voir main_window.py :
|
|
# QWebEngineScript à InjectionPoint.DocumentCreation). Attention : le
|
|
# <body> n'existe pas encore à ce stade, d'où l'accès différé via
|
|
# ensureOverlay()/setCursor().
|
|
BLOCK_PICKER_INIT_JS = r"""
|
|
(function () {
|
|
if (window.__pdfscrapperInitialized) { return; }
|
|
window.__pdfscrapperInitialized = true;
|
|
|
|
var BLOCK_SELECTOR = 'p, li, blockquote, h1, h2, h3, h4, h5, h6, pre, td, th, figcaption, article, section, div';
|
|
|
|
var state = { sticky: false, ctrlHeld: false, active: false };
|
|
var prevCursor = '';
|
|
var overlay = null;
|
|
|
|
function ensureOverlay() {
|
|
if (overlay || !document.body) { return overlay; }
|
|
overlay = document.createElement('div');
|
|
overlay.id = '__pdfscrapper_overlay';
|
|
overlay.style.position = 'fixed';
|
|
overlay.style.pointerEvents = 'none';
|
|
overlay.style.zIndex = '2147483647';
|
|
overlay.style.background = 'rgba(33, 150, 243, 0.22)';
|
|
overlay.style.border = '2px solid #2196f3';
|
|
overlay.style.borderRadius = '2px';
|
|
overlay.style.boxSizing = 'border-box';
|
|
overlay.style.display = 'none';
|
|
document.body.appendChild(overlay);
|
|
return overlay;
|
|
}
|
|
|
|
function hideOverlay() {
|
|
if (overlay) { overlay.style.display = 'none'; }
|
|
}
|
|
|
|
function positionOverlay(el) {
|
|
var ov = ensureOverlay();
|
|
if (!ov) { return; }
|
|
var rect = el.getBoundingClientRect();
|
|
ov.style.left = rect.left + 'px';
|
|
ov.style.top = rect.top + 'px';
|
|
ov.style.width = rect.width + 'px';
|
|
ov.style.height = rect.height + 'px';
|
|
ov.style.display = 'block';
|
|
}
|
|
|
|
function setCursor(value) {
|
|
if (document.body) { document.body.style.cursor = value; }
|
|
}
|
|
|
|
function updateActive() {
|
|
var shouldBeActive = state.sticky || state.ctrlHeld;
|
|
if (shouldBeActive === state.active) { return; }
|
|
state.active = shouldBeActive;
|
|
if (state.active) {
|
|
prevCursor = document.body ? document.body.style.cursor : '';
|
|
setCursor('crosshair');
|
|
} else {
|
|
setCursor(prevCursor || '');
|
|
hideOverlay();
|
|
window.__pdfscrapperCurrentBlock = null;
|
|
}
|
|
}
|
|
|
|
function findBlock(el) {
|
|
if (!el || typeof el.closest !== 'function') { return null; }
|
|
var block = el.closest(BLOCK_SELECTOR);
|
|
if (!block || block === document.body || block === document.documentElement) {
|
|
return null;
|
|
}
|
|
return block;
|
|
}
|
|
|
|
document.addEventListener('mousemove', function (e) {
|
|
if (!state.active) { return; }
|
|
var el = findBlock(e.target);
|
|
window.__pdfscrapperCurrentBlock = el;
|
|
if (!el) { hideOverlay(); return; }
|
|
positionOverlay(el);
|
|
}, true);
|
|
|
|
document.addEventListener('click', function (e) {
|
|
if (!state.active) { return; }
|
|
var el = window.__pdfscrapperCurrentBlock || findBlock(e.target);
|
|
if (!el) { return; }
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
var text = (el.innerText || el.textContent || '').trim();
|
|
if (!text || !window.pdfscrapperBridge) { return; }
|
|
window.pdfscrapperBridge.captureBlock(text);
|
|
}, true);
|
|
|
|
document.addEventListener('keydown', function (e) {
|
|
if (e.key === 'Control') {
|
|
state.ctrlHeld = true;
|
|
updateActive();
|
|
}
|
|
}, true);
|
|
|
|
document.addEventListener('keyup', function (e) {
|
|
if (e.key === 'Control') {
|
|
state.ctrlHeld = false;
|
|
updateActive();
|
|
}
|
|
}, true);
|
|
|
|
// Filet de sécurité : si la fenêtre perd le focus pendant que Ctrl est
|
|
// tenu (ex. Alt-Tab), on ne recevra jamais le keyup correspondant.
|
|
window.addEventListener('blur', function () {
|
|
state.ctrlHeld = false;
|
|
updateActive();
|
|
});
|
|
|
|
// Appelé depuis Python (bouton de la barre d'outils / Ctrl+Shift+B).
|
|
window.__pdfscrapperSetSticky = function (value) {
|
|
state.sticky = !!value;
|
|
updateActive();
|
|
};
|
|
})();
|
|
"""
|
|
|
|
# Appelé après chaque chargement de page pour reporter l'état "sticky"
|
|
# (activé via le bouton/raccourci) sur la nouvelle page — l'état JS
|
|
# précédent ne survit pas à une navigation.
|
|
SET_STICKY_JS_TEMPLATE = "window.__pdfscrapperSetSticky && window.__pdfscrapperSetSticky({value});"
|