diff --git a/manifest.json b/manifest.json index 399a0d408af048ee4abf6b8d04e5c0e37faa062c..2591e942483dc87a856c4dfc98f2a935162d2486 100644 --- a/manifest.json +++ b/manifest.json @@ -61,7 +61,9 @@ "src/utils/definitions.js", "src/sidebar/sidebar.js", "src/context_menu/custom_context_menu.js", - "src/utils/stats.js"], + "src/utils/stats.js", + "src/utils/highlighting.js" + ], "css": ["src/context_menu/custom_context_menu.css"], "run_at": "document_idle" }, diff --git a/src/background/background.js b/src/background/background.js index cd3d0126e50b61e76c825b31e464ed2f9e77e2e9..ea71766a3395bc7f1307b6309866f7da87b6b2d2 100644 --- a/src/background/background.js +++ b/src/background/background.js @@ -167,7 +167,7 @@ async function saveToken(token) { } // ───────────────────────────────────────────────────────────────────────────── -// Gestion des messages reçus (depuis popup, etc.) +// Gestion des messages reçus // ───────────────────────────────────────────────────────────────────────────── browser.runtime.onMessage.addListener(async (message, sender, sendResponse) => { log("📩 Message reçu dans background.js :", message); @@ -217,6 +217,34 @@ browser.runtime.onMessage.addListener(async (message, sender, sendResponse) => { } break; } + case "toggleLexiconHighlight": { + const tabs = await browser.tabs.query({active: true, currentWindow: true}); + if (tabs[0]) { + try { + // S'assurer que le script est injecté + await browser.scripting.executeScript({ + target: { tabId: tabs[0].id }, + files: ["src/utils/highlighting.js"] + }); + + // Envoyer le message d'activation + await browser.tabs.sendMessage(tabs[0].id, { + command: message.isActive ? "activate-highlighting" : "deactivate-highlighting", + lexiconId: message.lexiconId + }); + log(`✅ Message de surlignage transmis à l'onglet ${tabs[0].id}`); + } catch (error) { + console.error("⌠Erreur lors de la gestion du surlignage:", error); + } + } + break; + } + + case "register-highlighting-script": { + log("📠Script de surlignage enregistré pour l'onglet", sender.tab.id); + break; + } + default: break; } @@ -543,3 +571,26 @@ function sendStoplistToWorker() { browser.runtime.onStartup.addListener(loadStoplist); browser.runtime.onInstalled.addListener(loadStoplist); +async function handleHighlighting(command, lexiconId, tabId) { + console.log(`🎯 Gestion du surlignage: ${command} pour le lexique ${lexiconId}`); + + try { + // S'assurer que le script est injecté + await browser.scripting.executeScript({ + target: { tabId: tabId }, + files: ["utils/highlighting.js"] + }); + + // Envoyer le message d'activation + const response = await browser.tabs.sendMessage(tabId, { + command: command, + lexiconId: lexiconId + }); + + console.log("✅ Réponse du content script:", response); + return response; + } catch (error) { + console.error("⌠Erreur lors de la gestion du surlignage:", error); + return false; + } +} \ No newline at end of file diff --git a/src/sidebar/sidebar.html b/src/sidebar/sidebar.html index 06f98b50bed31a02366fe242e4edfe96dd17037e..79f9c09a9d628179d8fb1bff4edf678d15f993c0 100644 --- a/src/sidebar/sidebar.html +++ b/src/sidebar/sidebar.html @@ -293,6 +293,16 @@ transform: translateX(-50%) translateY(-5px); } + .lexicon-highlight { + background-color: rgba(255, 255, 0, 0.3); + border-bottom: 1px dashed #666; + transition: background-color 0.3s; + } + + .lexicon-highlight:hover { + background-color: rgba(255, 255, 0, 0.5); + } + .lexicon-section { margin-bottom: 10px; } diff --git a/src/sidebar/sidebar.js b/src/sidebar/sidebar.js index 0b40b7881febb0f0e60c8e0ed73f1435d2eb7cd7..2c4123be6e852f6ed2cf7ee380f376e11feaa332 100644 --- a/src/sidebar/sidebar.js +++ b/src/sidebar/sidebar.js @@ -116,8 +116,8 @@ async function refreshSidebarState() { } /** - * Bascule l’affichage d’un bloc. - * @param {string} blockId - L’ID du conteneur à basculer. + * Bascule l'affichage d'un bloc. + * @param {string} blockId - L'ID du conteneur à basculer. * @param {HTMLElement} btn - Le bouton qui déclenche la bascule, pour mettre à jour son texte. */ function toggleBlock(blockId, btn) { @@ -134,8 +134,8 @@ function toggleBlock(blockId, btn) { } /** - * Ouvre un bloc s’il est fermé et met à jour le bouton de bascule. - * @param {string} blockId - L’ID du conteneur à ouvrir. + * Ouvre un bloc s'il est fermé et met à jour le bouton de bascule. + * @param {string} blockId - L'ID du conteneur à ouvrir. * @param {HTMLElement} [btn] - (Optionnel) Le bouton de bascule à mettre à jour. */ function openBlock(blockId, btn) { @@ -301,12 +301,9 @@ async function displayLexiconsWithCheckbox(lexicons) { let currentState = highlightButton.dataset.active === "true"; let newState = !currentState; try { - await browser.runtime.sendMessage({ - action: "toggleLexiconHighlight", - lexiconId, - isActive: newState, - }); + await toggleLexiconHighlight(lexiconId, newState); highlightButton.dataset.active = newState ? "true" : "false"; + highlightButton.classList.toggle("active", newState); } catch (error) { console.error("Erreur lors du toggle de surlignage pour le lexique", lexiconId, ":", error); } @@ -413,7 +410,7 @@ async function handleAddWordClick() { return; } - // 3) Vérifier si le mot existe déjà dans l’un des lexiques sélectionnés + // 3) Vérifier si le mot existe déjà dans l'un des lexiques sélectionnés let definitions = []; try { definitions = await fetchLexiconDefinitions(selectedWord); @@ -445,7 +442,7 @@ async function handleAddWordClick() { return; } - // 6) Envoi d’une seule requête pour tous les lexiques restants + // 6) Envoi d'une seule requête pour tous les lexiques restants try { log(`📡 Envoi de l'ajout du mot "${selectedWord}" dans les lexiques :`, lexiconsToAdd); const result = await window.AddWord(authToken, selectedWord, lexiconsToAdd, false); @@ -464,9 +461,9 @@ async function handleAddWordClick() { } } catch (error) { - console.error("Erreur lors de l’ajout du mot :", error); + console.error("Erreur lors de l'ajout du mot :", error); if (lexiconResultElement) { - lexiconResultElement.textContent = "Erreur lors de l’ajout : " + error.message; + lexiconResultElement.textContent = "Erreur lors de l'ajout : " + error.message; } } } @@ -476,95 +473,126 @@ async function handleAddWordClick() { // ───────────────────────────────────────────────────────────────────────────── browser.runtime.onMessage.addListener(async (message) => { log("📩 Message reçu dans sidebar.js :", message); - switch (message.action) { - case "refreshUI": - log("🔄 Demande de rafraîchissement de la barre latérale."); - await refreshSidebarState(); - break; - - case "mot_selectionne": - if (message.selectedText) { - log("ðŸ–‹ï¸ Mot sélectionné :", message.selectedText); - const selectedWordElement = document.getElementById("motSelectionne"); - if (selectedWordElement) { - selectedWordElement.textContent = message.selectedText; - } else { - console.warn("âš ï¸ Ã‰lément #motSelectionne introuvable."); + + if (message.action === "refreshUI") { + log("🔄 Demande de rafraîchissement de la barre latérale."); + await refreshSidebarState(); + return; + } + + if (message.command) { + switch (message.command) { + case "activate-highlighting": + const highlightButton = document.querySelector(`[data-lexicon-id="${message.lexiconId}"] .lexique-highlight-toggle`); + if (highlightButton) { + highlightButton.dataset.active = "true"; + highlightButton.classList.add("active"); } - const lexiconResultElement = document.getElementById("lexiconResult"); - if (lexiconResultElement) { - lexiconResultElement.innerHTML = ""; + break; + + case "deactivate-highlighting": + const highlightButtonOff = document.querySelector(`[data-lexicon-id="${message.lexiconId}"] .lexique-highlight-toggle`); + if (highlightButtonOff) { + highlightButtonOff.dataset.active = "false"; + highlightButtonOff.classList.remove("active"); } - openBlock("etatContent"); - } - break; - - case "getDefinition": - if (message.selectedText) { - log("📖 Recherche des définitions pour :", message.selectedText); - openBlock("definitionContent"); - await window.showDefinitions(message.selectedText); - } - break; + break; + + case "mot_selectionne": + if (message.selectedText) { + log("ðŸ–‹ï¸ Mot sélectionné :", message.selectedText); + const selectedWordElement = document.getElementById("motSelectionne"); + if (selectedWordElement) { + selectedWordElement.textContent = message.selectedText; + } else { + console.warn("âš ï¸ Ã‰lément #motSelectionne introuvable."); + } + const lexiconResultElement = document.getElementById("lexiconResult"); + if (lexiconResultElement) { + lexiconResultElement.innerHTML = ""; + } + openBlock("etatContent"); + } + break; - case "showDefinitions": - if (Array.isArray(message.definitions)) { - window.displayDefinitions(message.definitions); - } - break; + case "getDefinition": + if (message.selectedText) { + log("📖 Recherche des définitions pour :", message.selectedText); + openBlock("definitionContent"); + await window.showDefinitions(message.selectedText); + } + break; - case "fetchWiktionaryDefinitionResponse": - if (message.selectedText) { - log(`📖 Réception de la définition du Wiktionnaire pour '${message.selectedText}'`); - window.displayDefinitions(message.definitions); - } - break; + case "showDefinitions": + if (Array.isArray(message.definitions)) { + window.displayDefinitions(message.definitions); + } + break; - case "showLexiconResult": - log("📚 Résultat des lexiques reçus :", message.lexicons); - window.displayLexiconResults(message.lexicons); - break; - - case "addWordResult": - const lexiconResultElement = document.getElementById("lexiconResult"); - if (lexiconResultElement) { - lexiconResultElement.innerHTML = message.lexicons; - } - break; + case "fetchWiktionaryDefinitionResponse": + if (message.selectedText) { + log(`📖 Réception de la définition du Wiktionnaire pour '${message.selectedText}'`); + window.displayDefinitions(message.definitions); + } + break; - case "addToLexicon": - handleAddWordClick(); - break; - - case "openLexiconBlock": - openBlock("menuContent"); - break; + case "showLexiconResult": + log("📚 Résultat des lexiques reçus :", message.lexicons); + window.displayLexiconResults(message.lexicons); + break; - case "toggleAuth": - break; - - case "authStatusChanged": - break; - - case "updateUI": - await refreshSidebarState(); - break; - - case "pyodide-simplemma-ready": - return; - - case "saveToken": - authToken = message.token; - break; + case "addWordResult": + const lexiconResultElement = document.getElementById("lexiconResult"); + if (lexiconResultElement) { + lexiconResultElement.innerHTML = message.lexicons; + } + break; - case "closeSidebarBlocks": - closeBlock("menuContent"); - closeBlock("etatContent"); - closeBlock("definitionContent") - break; + case "addToLexicon": + handleAddWordClick(); + break; + + case "openLexiconBlock": + openBlock("menuContent"); + break; + + case "toggleAuth": + break; + + case "authStatusChanged": + break; + + case "updateUI": + await refreshSidebarState(); + break; + + case "pyodide-simplemma-ready": + return; + + case "saveToken": + authToken = message.token; + break; + + case "closeSidebarBlocks": + closeBlock("menuContent"); + closeBlock("etatContent"); + closeBlock("definitionContent") + break; + + case "register-stats-script": + break; + + case "register-highlighting-script": + log("✅ Script de surlignage enregistré"); + browser.runtime.sendMessage({ + command: "updateAuthToken", + token: authToken + }); + break; - default: - console.warn("âš ï¸ Action inconnue reçue :", message.action); + default: + console.warn("âš ï¸ Action inconnue reçue :", message.command); + } } }); @@ -677,3 +705,18 @@ document.addEventListener("DOMContentLoaded", async () => { } }); }); + +async function toggleLexiconHighlight(lexiconId, isActive) { + try { + await browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => { + browser.tabs.sendMessage(tabs[0].id, { + command: isActive ? "activate-highlighting" : "deactivate-highlighting", + lexiconId: lexiconId + }); + }); + + log(`✅ Surlignage ${isActive ? 'activé' : 'désactivé'} pour le lexique ${lexiconId}`); + } catch (error) { + console.error(`⌠Erreur lors du toggle du surlignage pour le lexique ${lexiconId}:`, error); + } +} diff --git a/src/utils/api.js b/src/utils/api.js index 73d7fd0a1a8533bd75e4c0ad1e744974e9860b85..d725f8da4dabaab7ab6ed3063130a63ff80c4fb6 100644 --- a/src/utils/api.js +++ b/src/utils/api.js @@ -15,7 +15,7 @@ document.addEventListener("mouseup", () => { }); // ───────────────────────────────────────────────────────────────────────────── -// â–Œ Fonction utilitaire pour appeler l’API +// â–Œ Fonction utilitaire pour appeler l'API // ───────────────────────────────────────────────────────────────────────────── /** * Effectue une requête API (GET, POST, etc.) avec ou sans body JSON @@ -54,7 +54,7 @@ async function callApi(url, authToken = null, method = 'GET', data = null) { // ───────────────────────────────────────────────────────────────────────────── /** - * Récupère les lexiques de l’utilisateur, + * Récupère les lexiques de l'utilisateur, * en langue par défaut "fr". * * @param {string} authToken - Le token d'authentification. @@ -67,7 +67,7 @@ async function getLexicons(authToken) { } /** - * Récupère tous les lexiques (catégories) de l’utilisateur + * Récupère tous les lexiques (catégories) de l'utilisateur */ async function getAllCategoriesLexicons(authToken) { const categories = ["User", "Group", "Zero", "New words"]; @@ -118,7 +118,7 @@ async function getAllLexiconWords(authToken) { const lexicons = await getLexicons(authToken); if (!Array.isArray(lexicons) || lexicons.length === 0) { - console.warn("âš ï¸ Aucun lexique retourné par l’API pour ces paramètres."); + console.warn("âš ï¸ Aucun lexique retourné par l'API pour ces paramètres."); return {}; } @@ -127,6 +127,11 @@ async function getAllLexiconWords(authToken) { for (const lexicon of lexicons) { const entries = await getLexiconEntries(authToken, lexicon.id); + // Vérification que entries est bien un tableau + if (!Array.isArray(entries)) { + console.warn(`âš ï¸ Format invalide pour les entrées du lexique ${lexicon.id}:`, entries); + continue; + } const allGraphies = entries.map(entry => entry.graphy); // Création d'un libellé unique pour le lexique @@ -187,13 +192,13 @@ async function getWiktionaryDefinition(word) { */ async function AddWord(authToken, selectedWord, lexiconIds, force = false) { if (!authToken) { - throw new Error("Aucun token d’authentification fourni."); + throw new Error("Aucun token d'authentification fourni."); } if (!selectedWord) { - throw new Error("Aucun mot n’a été spécifié pour l’ajout."); + throw new Error("Aucun mot n'a été spécifié pour l'ajout."); } if (!Array.isArray(lexiconIds) || lexiconIds.length === 0) { - throw new Error("Aucun lexique sélectionné pour l’ajout."); + throw new Error("Aucun lexique sélectionné pour l'ajout."); } const url = "https://babalex.lezinter.net/api/entry/create"; diff --git a/src/utils/highlighting.js b/src/utils/highlighting.js new file mode 100644 index 0000000000000000000000000000000000000000..97d622599a546e96a814b834a340b0a214baada0 --- /dev/null +++ b/src/utils/highlighting.js @@ -0,0 +1,465 @@ +// Logs immédiats pour vérifier l'injection +console.log("🔵 DÉBUT DU FICHIER highlighting.js"); +try { + log("✅ highlighting.js chargé"); +} catch (e) { + console.error("⌠Erreur avec la fonction log:", e); +} + +// Vérification de l'existence de l'objet browser +console.log("🔠Vérification de l'environnement:", { + hasBrowser: typeof browser !== 'undefined', + hasChrome: typeof chrome !== 'undefined', + windowLocation: window.location.href +}); + +// Ajout d'un log global pour capturer les erreurs non gérées +window.onerror = function(msg, url, line, col, error) { + console.error("🔴 Erreur globale:", { + message: msg, + url: url, + line: line, + col: col, + error: error + }); + return false; +}; + +// Au début du fichier, récupérons le token du storage local +async function initAuthToken() { + try { + const { accessToken } = await browser.storage.local.get("accessToken"); + if (accessToken) { + window.authToken = accessToken; + log("🔑 Token récupéré depuis le stockage local"); + } else { + log("âš ï¸ Aucun token trouvé dans le stockage local"); + } + } catch (error) { + console.error("⌠Erreur lors de la récupération du token:", error); + } +} + +// Appel immédiat de l'initialisation +initAuthToken(); + +// Gardons aussi l'écoute des mises à jour du token +browser.runtime.onMessage.addListener((message) => { + if (message.command === "updateAuthToken" && message.token) { + window.authToken = message.token; + log("🔑 Token mis à jour via message"); + } +}); + +// Déplacer la déclaration de l'observer au début du fichier, avant son utilisation +let observer = null; + +// Au début du fichier, après la déclaration de observer +document.addEventListener('visibilitychange', async () => { + if (document.visibilityState === 'visible' && highlightingActive) { + log("📄 Page redevenue visible, réinitialisation complète du surlignage"); + // Réinitialiser complètement le surlignage + removeAllHighlights(); + await updateLexiconCache(); + highlightVisibleContent(); + attachMutationObserver(); + } +}); + +// Ajoutons aussi un listener pour le chargement complet de la page +window.addEventListener('load', async () => { + if (highlightingActive) { + log("📄 Page chargée, réinitialisation complète du surlignage"); + // Réinitialiser complètement le surlignage + removeAllHighlights(); + await updateLexiconCache(); + highlightVisibleContent(); + attachMutationObserver(); + } +}); + +(function () { + try { + console.log("🟢 IIFE de highlighting.js démarrée"); + + // Vérification immédiate des objets critiques + console.log("🔠État initial:", { + hasAuthToken: !!window.authToken, + hasGetAllLexiconWords: !!window.getAllLexiconWords, + browserRuntime: !!browser?.runtime, + documentBody: !!document.body + }); + + // Amélioration du log initial pour vérifier si le script s'exécute + console.log("🟢 Début d'exécution du script highlighting.js"); + + // Cache des mots par lexique + let lexiconWordsCache = new Map(); + let highlightingActive = false; + let activeLexiconIds = new Set(); + + // ───────────────────────────────────────────────────────────────────────────── + // Gestion des messages du background + // ───────────────────────────────────────────────────────────────────────────── + log("📡 Enregistrement du listener de messages"); + browser.runtime.onMessage.addListener((message, sender, sendResponse) => { + console.log("📨 Message reçu:", message, "Context:", { + highlightingActive, + activeLexiconIds: Array.from(activeLexiconIds), + hasAuthToken: !!window.authToken, + hasGetAllLexiconWords: !!window.getAllLexiconWords + }); + + if (message.command === "activate-highlighting") { + log(`🎯 Activation du surlignage pour le lexique ${message.lexiconId}`); + // Exécuter startHighlighting de manière synchrone et envoyer la réponse + startHighlighting(message.lexiconId) + .then(result => sendResponse(result)) + .catch(error => { + console.error("Erreur lors de l'activation:", error); + sendResponse(false); + }); + return true; // Indique que nous allons envoyer une réponse asynchrone + } + + if (message.command === "deactivate-highlighting") { + log(`🚫 Désactivation du surlignage pour le lexique ${message.lexiconId}`); + stopHighlighting(message.lexiconId); + sendResponse(true); + return true; + } + + sendResponse(false); + return false; + }); + + log("📡 Enregistrement du script auprès du background"); + browser.runtime.sendMessage({ command: "register-highlighting-script" }); + + // Déplacer la vérification hasRun ici + if (window.hasRun) { + console.log("âš ï¸ highlighting.js déjà chargé, mais listener maintenu"); + return; + } + window.hasRun = true; + log("🚀 Initialisation de highlighting.js"); + + // ───────────────────────────────────────────────────────────────────────────── + // Fonctions principales + // ───────────────────────────────────────────────────────────────────────────── + async function startHighlighting(lexiconId) { + console.log("🎬 startHighlighting appelé avec:", { + lexiconId, + windowContext: { + authToken: !!window.authToken, + getAllLexiconWords: !!window.getAllLexiconWords, + location: window.location.href + } + }); + log(`🎬 Démarrage du surlignage (lexiconId: ${lexiconId})`); + + try { + // Attendre jusqu'à 5 secondes maximum pour le token + let attempts = 0; + while (!window.authToken && attempts < 50) { + await new Promise(resolve => setTimeout(resolve, 100)); + attempts++; + } + + if (!window.authToken) { + throw new Error("⌠Pas de token d'authentification disponible après attente"); + } + + if (lexiconId) { + activeLexiconIds.add(lexiconId); + log("📊 Lexiques actifs:", Array.from(activeLexiconIds)); + } + + highlightingActive = true; + + log("🔄 Début de la mise à jour du cache"); + await updateLexiconCache(); + log("✅ Cache mis à jour, début du surlignage"); + highlightVisibleContent(); + attachMutationObserver(); + return true; + } catch (error) { + console.error("⌠Erreur détaillée lors du démarrage du surlignage:", { + error: error.message, + stack: error.stack, + authToken: !!window.authToken, + getAllLexiconWords: typeof window.getAllLexiconWords, + activeLexiconIds: Array.from(activeLexiconIds) + }); + return false; + } + } + + function stopHighlighting(lexiconId) { + log(`🛑 Arrêt du surlignage (lexiconId: ${lexiconId})`); + if (lexiconId) { + activeLexiconIds.delete(lexiconId); + log("📊 Lexiques actifs restants:", Array.from(activeLexiconIds)); + + if (activeLexiconIds.size === 0) { + log("🔄 Plus aucun lexique actif, désactivation complète"); + highlightingActive = false; + removeAllHighlights(); + detachMutationObserver(); + } else { + log("🔄 Rafraîchissement du surlignage pour les lexiques restants"); + removeAllHighlights(); + highlightVisibleContent(); + } + } else { + log("🧹 Désactivation globale du surlignage"); + highlightingActive = false; + activeLexiconIds.clear(); + removeAllHighlights(); + detachMutationObserver(); + } + } + + async function updateLexiconCache() { + console.log("📥 updateLexiconCache - Début avec context:", { + authToken: !!window.authToken, + getAllLexiconWords: !!window.getAllLexiconWords, + activeLexiconIds: Array.from(activeLexiconIds) + }); + try { + if (!window.authToken) { + throw new Error("Pas de token d'authentification"); + } + + // Vérification explicite de getAllLexiconWords + if (typeof window.getAllLexiconWords !== 'function') { + log("âš ï¸ getAllLexiconWords n'est pas une fonction"); + log("Type de getAllLexiconWords:", typeof window.getAllLexiconWords); + log("Contenu de window.getAllLexiconWords:", window.getAllLexiconWords); + throw new Error("getAllLexiconWords n'est pas disponible"); + } + + log("📥 Appel de getAllLexiconWords..."); + const allWords = await window.getAllLexiconWords(window.authToken); + log("📠Réponse de getAllLexiconWords:", allWords); + + if (!allWords || typeof allWords !== 'object') { + throw new Error(`Format de données invalide: ${JSON.stringify(allWords)}`); + } + + lexiconWordsCache.clear(); + + // Vérification de la structure des données + if (Object.keys(allWords).length === 0) { + log("âš ï¸ Aucun lexique reçu de getAllLexiconWords"); + return false; + } + + for (const [lexiconName, words] of Object.entries(allWords)) { + if (!Array.isArray(words)) { + console.warn(`âš ï¸ Format invalide pour le lexique ${lexiconName}:`, words); + continue; + } + + const lexiconId = lexiconName.match(/\[(\d+)\]$/)?.[1]; + if (!lexiconId) { + console.warn(`âš ï¸ Impossible d'extraire l'ID du lexique depuis: ${lexiconName}`); + continue; + } + + log(`📎 Traitement du lexique ${lexiconName} (ID: ${lexiconId})`); + + if (activeLexiconIds.has(Number(lexiconId))) { + lexiconWordsCache.set(lexiconId, new Set(words)); + log(`📖 Lexique ${lexiconId} chargé avec ${words.length} mots`); + } + } + + log("✅ Cache des lexiques mis à jour:", + Object.fromEntries([...lexiconWordsCache.entries()].map(([id, words]) => [id, [...words]]))); + return true; + } catch (error) { + console.error("⌠Erreur dans updateLexiconCache:", { + message: error.message, + stack: error.stack, + authTokenExists: !!window.authToken, + getAllLexiconWordsType: typeof window.getAllLexiconWords, + response: allWords + }); + throw error; + } + } + + // ───────────────────────────────────────────────────────────────────────────── + // Fonctions de surlignage + // ───────────────────────────────────────────────────────────────────────────── + function highlightVisibleContent() { + if (!highlightingActive) { + log("â¸ï¸ Surlignage inactif, sortie"); + return; + } + log("🔠Début du surlignage du contenu visible"); + + const textNodes = []; + const walker = document.createTreeWalker( + document.body, + NodeFilter.SHOW_TEXT, + { + acceptNode: (node) => { + if (node.parentElement?.closest('script, style, .lexicon-highlight')) { + return NodeFilter.FILTER_REJECT; + } + return NodeFilter.FILTER_ACCEPT; + } + } + ); + + let node; + while (node = walker.nextNode()) { + textNodes.push(node); + } + + log(`🔤 ${textNodes.length} nÅ“uds de texte trouvés à traiter`); + textNodes.forEach(processTextNode); + } + + function processTextNode(textNode) { + if (activeLexiconIds.size === 0) { + log("âš ï¸ Aucun lexique actif, sortie du processTextNode"); + return; + } + + const text = textNode.textContent; + log(`🔠Traitement du texte: "${text.substring(0, 50)}..."`); + + let lastIndex = 0; + let fragments = []; + + const allWords = new Set(); + for (const [lexiconId, words] of lexiconWordsCache.entries()) { + // Convertir directement en nombre car lexiconId est déjà l'ID numérique + const numericId = parseInt(lexiconId); + log(`🔄 Vérification du lexique ${lexiconId} (ID: ${numericId})`); + if (activeLexiconIds.has(numericId)) { + log(`✅ Lexique ${lexiconId} actif, ajout de ${words.size} mots`); + words.forEach(word => allWords.add(word)); + } + } + + log(`🔤 Nombre total de mots à rechercher: ${allWords.size}`); + if (allWords.size === 0) { + log("âš ï¸ Aucun mot à rechercher dans les lexiques actifs"); + return; + } + + const wordsPattern = Array.from(allWords) + .sort((a, b) => b.length - a.length) + .map(escapeRegExp) + .join("|"); + + if (!wordsPattern) { + log("âš ï¸ Aucun mot à rechercher, sortie"); + return; + } + + const regex = new RegExp(`\\b(${wordsPattern})\\b`, "gi"); + let match; + let matchCount = 0; + + while ((match = regex.exec(text)) !== null) { + matchCount++; + if (match.index > lastIndex) { + fragments.push(document.createTextNode(text.slice(lastIndex, match.index))); + } + + const span = document.createElement("span"); + span.textContent = match[0]; + span.className = "lexicon-highlight"; + span.style.backgroundColor = "rgba(255, 255, 0, 0.3)"; + span.style.borderBottom = "1px dashed #666"; + fragments.push(span); + + lastIndex = regex.lastIndex; + } + + if (matchCount > 0) { + log(`✨ ${matchCount} correspondances trouvées dans le nÅ“ud`); + } + + if (lastIndex < text.length) { + fragments.push(document.createTextNode(text.slice(lastIndex))); + } + + if (fragments.length > 0) { + const parent = textNode.parentNode; + fragments.forEach(fragment => parent.insertBefore(fragment, textNode)); + parent.removeChild(textNode); + } + } + + function removeAllHighlights() { + log("🧹 Suppression de tous les surlignages"); + const highlights = document.querySelectorAll('.lexicon-highlight'); + log(`📊 ${highlights.length} surlignages à supprimer`); + + highlights.forEach(highlight => { + const text = highlight.textContent; + highlight.replaceWith(text); + }); + } + + // ───────────────────────────────────────────────────────────────────────────── + // Gestion des mutations DOM + // ───────────────────────────────────────────────────────────────────────────── + function attachMutationObserver() { + log("👀 Attachement de l'observateur de mutations"); + observer = new MutationObserver((mutations) => { + log(`🔄 ${mutations.length} mutations DOM détectées`); + mutations.forEach(mutation => { + if (mutation.type === 'childList') { + mutation.addedNodes.forEach(node => { + if (node.nodeType === Node.ELEMENT_NODE) { + highlightVisibleContent(); + } + }); + } + }); + }); + + observer.observe(document.body, { + childList: true, + subtree: true + }); + } + + function detachMutationObserver() { + if (observer) { + log("👋 Détachement de l'observateur de mutations"); + observer.disconnect(); + observer = null; + } + } + + // ───────────────────────────────────────────────────────────────────────────── + // Utilitaires + // ───────────────────────────────────────────────────────────────────────────── + function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } + + function getLexiconIdFromName(lexiconName) { + const match = lexiconName.match(/\[(\d+)\]$/); + const id = match ? parseInt(match[1]) : null; + log(`ðŸ·ï¸ Extraction de l'ID depuis '${lexiconName}': ${id}`); + return id; + } + + // Ajout d'une vérification au démarrage + log("🔠Vérification des dépendances au chargement:", { + authToken: !!window.authToken, + getAllLexiconWords: typeof window.getAllLexiconWords + }); + } catch (error) { + console.error("🔴 Erreur critique dans l'IIFE:", error); + } +})();