diff --git a/api.js b/api.js index ded9972c6e177a6ae1c5fa95cae655f7564b148b..c1a046da58aa1fc2975832be585e502e011b4537 100644 --- a/api.js +++ b/api.js @@ -1,195 +1,186 @@ -console.log("api.js chargé correctement"); - -// Fonction pour récupérer les lexiques (personnels) de l'utilisateur -async function getUserLexicons(authToken) { - const lexiconsApiUrl = 'https://babalex.lezinter.net/api/user/lexicons'; - try { - const response = await fetch(lexiconsApiUrl, { - method: 'GET', - headers: { - Authorization: `Bearer ${authToken}`, - 'Content-Type': 'application/json', - }, +console.log("✅ api.js chargé correctement"); + +// ───────────────────────────────────────────────────────────────────────────── +// â–Œ Sélection de texte sur la page +// ───────────────────────────────────────────────────────────────────────────── +document.addEventListener("mouseup", () => { + const selectedText = window.getSelection().toString().trim(); + if (selectedText) { + browser.runtime.sendMessage({ + action: "mot_selectionne", + selectedText }); - - if (!response.ok) { - throw new Error(`Erreur lors de la récupération des lexiques : ${response.statusText}`); - } - - return await response.json(); - } catch (error) { - console.error('Erreur lors de la récupération des lexiques :', error); - throw error; } -} +}); + +// ───────────────────────────────────────────────────────────────────────────── +// â–Œ Fonction utilitaire pour appeler l’API +// ───────────────────────────────────────────────────────────────────────────── +/** + * Effectue une requête API + * @param {string} url - L'URL de l'API à appeler. + * @param {string|null} authToken - Le token d'authentification. + * @param {string} [method='GET'] - La méthode HTTP. + * @returns {Promise<any>} - La réponse en JSON. + * @throws {Error} - En cas d'échec. + */ +async function callApi(url, authToken = null, method = 'GET') { + const headers = { 'Content-Type': 'application/json' }; + if (authToken) headers.Authorization = `Bearer ${authToken}`; -// Fonction pour récupérer les lexiques de l'utilisateur (ici utilisateur test) -async function getLexicons(authToken, userId = 52, language = 'fr') { - const lexiconsApiUrl = `https://babalex.lezinter.net/api/lexicon/search?user_id=${userId}&language=${language}`; try { - const response = await fetch(lexiconsApiUrl, { - method: 'GET', - headers: { - Authorization: `Bearer ${authToken}`, - 'Content-Type': 'application/json', - }, - }); - + const response = await fetch(url, { method, headers }); if (!response.ok) { - throw new Error(`Erreur lors de la récupération des lexiques : ${response.statusText}`); + throw new Error(`⌠Erreur API (${response.status}): ${response.statusText}`); } return await response.json(); } catch (error) { - console.error('Erreur lors de la récupération des lexiques :', error); + console.error(`🚨 Erreur lors de l'appel API [${url}]:`, error); throw error; } } +// ───────────────────────────────────────────────────────────────────────────── +// â–Œ Récupération des lexiques de l'utilisateur +// ───────────────────────────────────────────────────────────────────────────── + +/** + * Récupère les lexiques pour l’utilisateur 4, + * en langue par défaut "fr". + * + * @param {string} authToken - Le token d'authentification. + * @param {string} [language='fr'] - La langue (optionnel). + * @returns {Promise<any[]>} - Liste des lexiques trouvés. + */ +async function getLexicons(authToken, language = 'fr') { + const userId = 4; + const baseUrl = "https://babalex.lezinter.net/api/lexicon/search"; + const url = `${baseUrl}?user_id=${userId}&language=${encodeURIComponent(language)}`; + + return callApi(url, authToken); +} -// Fonction pour récupérer les entrées d'un lexique donné (simple liste des mots) -async function getLexiconEntries(authToken, idLexicon) { - const entriesApiUrl = `https://babalex.lezinter.net/api/lexicon/extract/0?lexiconId=${idLexicon}`; +/** + * Récupère tous les lexiques pour l’utilisateur 4 + */ +async function getAllCategoriesLexicons(authToken) { + const categories = ["User", "Group", "Zero", "New words"]; - try { - const response = await fetch(entriesApiUrl, { - method: 'GET', - headers: { - Authorization: `Bearer ${authToken}`, - 'Content-Type': 'application/json', - }, - }); + const userId = 4; + const groupId = 1; - if (!response.ok) { - throw new Error(`Erreur lors de la récupération des entrées du lexique : ${response.statusText}`); - } + const promises = categories.map(async (category) => { + const baseUrl = "https://babalex.lezinter.net/api/lexicon/search"; + const url = `${baseUrl}?user_id=${userId}&group_id=${groupId}&category=${encodeURIComponent(category)}&language=fr`; - const data = await response.json(); - if (Array.isArray(data)) { - console.log(`Entrées récupérées pour le lexique ID ${idLexicon} :`, data); - return data; // Retourne un tableau de mots ex. ["raquette", "chat", "science"] - } else { - console.warn('Le format des données retournées est inattendu.', data); + try { + return await callApi(url, authToken); + } catch (error) { + console.warn(`âš ï¸ Aucune donnée trouvée (ou accès refusé) pour la catégorie "${category}" :`, error.message); return []; } - } catch (error) { - console.error('Erreur lors de la récupération des entrées du lexique :', error); - throw error; - } -} - - -// Fonction pour récupérer les graphies et leurs ID -async function getLexiconEntriesID(authToken, idLexicon) { - const entriesApiUrl = `https://babalex.lezinter.net/api/lexicon/entries/${idLexicon}`; + }); try { - const response = await fetch(entriesApiUrl, { - method: 'GET', - headers: { - Authorization: `Bearer ${authToken}`, - 'Content-Type': 'application/json', - }, - }); - - if (!response.ok) { - throw new Error(`Erreur lors de la récupération des graphies : ${response.statusText}`); - } + const resultsByCategory = await Promise.all(promises); + const combined = resultsByCategory.flat(); - return await response.json(); + console.log("✅ Lexiques récupérés (toutes catégories confondues) :", combined); + return combined; } catch (error) { - console.error('Erreur lors de la récupération des graphies :', error); - throw error; + console.error("⌠Erreur lors de la récupération multi-catégories :", error); + return []; } } +// ───────────────────────────────────────────────────────────────────────────── +// â–Œ Récupération des entrées d'un lexique +// ───────────────────────────────────────────────────────────────────────────── -// Fonction pour obtenir les informations d'un mot par la graphie (dans quels lexiques utilisateur ou de groupe la graphie est présente) -async function getGraphyInfo(authToken, word) { - const definitionApiUrl = `https://babalex.lezinter.net/api/entry/search?graphy=${encodeURIComponent(word)}&language=fr&target_lex=3&target_lex=4`; - - try { - const response = await fetch(definitionApiUrl, { - method: 'GET', - headers: { - Authorization: `Bearer ${authToken}`, - 'Content-Type': 'application/json', - }, - }); - - if (!response.ok) { - throw new Error(`Erreur lors de la récupération de la définition : ${response.statusText}`); - } - - return await response.json(); - } catch (error) { - console.error('Erreur lors de la récupération de la définition :', error); - throw error; - } +/** + * Récupère les entrées d'un lexique donné. + */ +async function getLexiconEntries(authToken, lexiconId) { + const url = `https://babalex.lezinter.net/api/lexicon/entries/${lexiconId}`; + return callApi(url, authToken); } - -// Fonction pour obtenir les informations sur un mot par son ID -async function getDefinition(authToken, entryId) { - const entryInfoApiUrl = `https://babalex.lezinter.net/api/entry/get/${entryId}`; +/** + * Récupère toutes les graphies présentes dans tous les lexiques de l'utilisateur. + */ +async function getAllLexiconWords(authToken) { + const searchUrl = "https://babalex.lezinter.net/api/lexicon/search" + + "?user_id=4" + + "&language=fr"; try { - const response = await fetch(entryInfoApiUrl, { - method: 'GET', - headers: { - Authorization: `Bearer ${authToken}`, - 'Content-Type': 'application/json', - }, - }); + // 1) Récupération de la liste des lexiques + const lexicons = await callApi(searchUrl, authToken); + + if (!Array.isArray(lexicons) || lexicons.length === 0) { + console.warn("âš ï¸ Aucun lexique retourné par l’API pour ces paramètres."); + return {}; + } - if (!response.ok) { - throw new Error(`Erreur lors de la récupération des informations sur l'entrée : ${response.statusText}`); + // 2) Pour chaque lexique, on récupère ses entrées via /api/lexicon/entries/{id} + const allGraphiesByLexicon = {}; + + for (const lexicon of lexicons) { + const entries = await getLexiconEntries(authToken, lexicon.id); + const allGraphies = entries.map(entry => entry.graphy); + + // Création d'un libellé unique pour le lexique + const lexiconName = + lexicon.category === "User" + ? `Lexique personnel (${lexicon.user?.pseudo || "Inconnu"}) [${lexicon.id}]` + : `Lexique de groupe (${lexicon.group?.name || "Inconnu"}) [${lexicon.id}]`; + + allGraphiesByLexicon[lexiconName] = allGraphies; } - const data = await response.json(); - console.log(`Informations pour l'entrée ID ${entryId} :`, data); - return data; + console.log("✅ Toutes les graphies récupérées :", allGraphiesByLexicon); + return allGraphiesByLexicon; } catch (error) { - console.error('Erreur lors de la récupération des informations sur l’entrée :', error); - throw error; + console.error("⌠Erreur lors de la récupération des graphies des lexiques :", error); + return {}; } } +// ───────────────────────────────────────────────────────────────────────────── +// â–Œ Récupération de définition du Wiktionnaire +// ───────────────────────────────────────────────────────────────────────────── -// Fonction pour obtenir une définition depuis le Wiktionnaire -async function getWiktionaryDefinition(authToken, word) { - // Construire l'URL de l'API avec le mot sélectionné - const wiktionaryApiUrl = `https://babalex.lezinter.net/api/wiktionary/search?graphy=${encodeURIComponent(word)}&language=fr`; - +/** + * Récupère une définition du Wiktionnaire. + */ +async function getWiktionaryDefinition(word) { try { - const response = await fetch(wiktionaryApiUrl, { - method: 'GET', - headers: { - Authorization: `Bearer ${authToken}`, - 'Content-Type': 'application/json', - }, - }); - + const url = `https://fr.wiktionary.org/w/api.php?action=query&format=json&origin=*&prop=extracts&exintro=true&titles=${encodeURIComponent(word)}`; + const response = await fetch(url); if (!response.ok) { - throw new Error(`Erreur lors de la récupération de la définition depuis le Wiktionnaire : ${response.statusText}`); + throw new Error(`Erreur API Wiktionnaire: ${response.statusText}`); } - const data = await response.json(); - console.log(`Résultats du Wiktionnaire pour le mot "${word}" :`, data); - return data; + const pages = data.query?.pages; + const page = pages ? Object.values(pages)[0] : null; + const definition = page?.extract?.trim() || "Aucune définition trouvée."; + + console.log(`📖 Définition trouvée pour '${word}':`, definition); + return [definition]; } catch (error) { - console.error('Erreur lors de la récupération de la définition depuis le Wiktionnaire :', error); - throw error; + console.error("Erreur lors de la récupération du Wiktionnaire :", error); + return ["Erreur : " + error.message]; } } +// ───────────────────────────────────────────────────────────────────────────── +// â–Œ Exposition des fonctions pour un usage global +// ───────────────────────────────────────────────────────────────────────────── - - -// METTRE LES FONCTIONS GLOBALES -window.getUserLexicons = getUserLexicons; +window.callApi = callApi; +window.getLexicons = getLexicons; +window.getAllCategoriesLexicons = getAllCategoriesLexicons; window.getLexiconEntries = getLexiconEntries; -window.getLexiconEntriesID = getLexiconEntriesID; -window.getGraphyInfo = getGraphyInfo; -window.getDefinition = getDefinition; -window.getWiktionaryDefinition = getWiktionaryDefinition; +window.getAllLexiconWords = getAllLexiconWords; +window.getWiktionaryDefinition = getWiktionaryDefinition; \ No newline at end of file diff --git a/background/background.js b/background/background.js index 65b528a37c0a4c3b2acb30caab0a4e6a57aaec45..e1f36c53f1e805ff9d2735b4f99e0bb8af0e5785 100644 --- a/background/background.js +++ b/background/background.js @@ -1,14 +1,17 @@ -// === Variables globales / de configuration === -let isExtensionActive = true; // Activation globale de l'extension +// ───────────────────────────────────────────────────────────────────────────── +// Variables globales +// ───────────────────────────────────────────────────────────────────────────── +let isExtensionActive = true; let areStatsActive = false; - let originalTabId = null; let loginTabId = null; const AUTH_LOGIN_URL = "https://prisms.lezinter.net/fr/login"; const AUTH_BALEX_URL = "https://prisms.lezinter.net/fr/headquarters/balex"; -// === Logs de démarrage === +// ───────────────────────────────────────────────────────────────────────────── +// Logs de démarrage +// ───────────────────────────────────────────────────────────────────────────── console.log("🚀 ff2BaLex (background) chargé."); browser.runtime.onInstalled.addListener((details) => { @@ -19,7 +22,9 @@ browser.runtime.onStartup.addListener(() => { console.log("🔄 Extension démarrée (onStartup)."); }); -// === Suivi des changements dans le stockage (extensionActive, statsActive) === +// ───────────────────────────────────────────────────────────────────────────── +// Suivi des changements dans le stockage +// ───────────────────────────────────────────────────────────────────────────── browser.storage.onChanged.addListener((changes) => { if (changes.extensionActive) { isExtensionActive = changes.extensionActive.newValue; @@ -29,58 +34,75 @@ browser.storage.onChanged.addListener((changes) => { areStatsActive = changes.statsActive.newValue; console.log("📊 Statistiques activées :", areStatsActive); } - // Recréer le menu contextuel du navigateur quand l'état change (ex: extension désactivée) - createBrowserContextMenu(); + refreshAllUI(); }); -// === Vérifie si l'utilisateur est connecté (token présent) === +// ───────────────────────────────────────────────────────────────────────────── +// Fonctions utilitaires +// ───────────────────────────────────────────────────────────────────────────── async function isUserConnected() { const { accessToken } = await browser.storage.local.get("accessToken"); return !!accessToken; } -// === Ouvre la page de login BaLex === -async function openLoginPage() { +async function refreshAllUI() { + console.log("🔄 Rafraîchissement global de l'UI..."); + browser.runtime.sendMessage({ action: "refreshUI" }); +} + +// ───────────────────────────────────────────────────────────────────────────── +// Fonctions d'authentification & de redirection +// ───────────────────────────────────────────────────────────────────────────── +browser.runtime.onConnect.addListener((port) => { + if (port.name === "auth") { + port.onMessage.addListener(async (message) => { + if (message.action === "toggleAuth") { + console.log("🔄 toggleAuth reçu via port dans le background."); + const isConnected = await isUserConnected(); + if (isConnected) { + await disconnectFromLexicalDB(); + } else { + // Ouvre directement la page de connexion + actuallyOpenLoginPage(); + } + } + }); + } +}); + +// Ouvre directement la page de connexion +async function actuallyOpenLoginPage() { console.log("🔗 Ouverture de la page de connexion."); - // 1) Retrouver l'onglet actuellement actif (pour y revenir plus tard) + // Mémoriser l'onglet actif const [currentTab] = await browser.tabs.query({ active: true, currentWindow: true }); if (currentTab) { originalTabId = currentTab.id; console.log("✅ Onglet courant mémorisé, ID =", originalTabId); } - - // 2) Créer l’onglet de login - const loginTab = await browser.tabs.create({ url: AUTH_LOGIN_URL }); + // Ouvre un nouvel onglet pour la page de connexion et l'active + const loginTab = await browser.tabs.create({ url: AUTH_LOGIN_URL, active: true }); loginTabId = loginTab.id; console.log("✅ Onglet de login créé, ID =", loginTabId); - - // Notifie (éventuellement) qu'on est “en cours†de connexion + // Notifie que l'authentification est en cours browser.runtime.sendMessage({ action: "authStatusChanged", isLoggedIn: false }); } -// === Déconnecte l'utilisateur (supprime le token) === +// Déconnecte l'utilisateur (sans notification) async function disconnectFromLexicalDB() { console.log("🔓 Déconnexion en cours..."); await browser.storage.local.remove("accessToken"); console.log("🔓 Token supprimé avec succès."); - - showNotification( - "Déconnexion réussie", - "Vous êtes maintenant déconnecté(e).", - "icons/logout.png" - ); - - // On effectue un rafraîchissement global - await refreshAllUI(); + setTimeout(async () => { + await refreshAllUI(); + }, 500); } -// === Sauvegarde du token et finalisation de la connexion === +// Sauvegarde le token et ferme l'onglet de login si nécessaire async function saveToken(token) { console.log("✅ Sauvegarde du token :", token); await browser.storage.local.set({ accessToken: token }); - // On ferme la page de login si encore ouverte if (loginTabId) { try { await browser.tabs.remove(loginTabId); @@ -88,10 +110,8 @@ async function saveToken(token) { } catch (err) { console.warn("Impossible de fermer l'onglet de login :", err); } - loginTabId = null; // Reset l'identifiant + loginTabId = null; } - - // On refait apparaître l’onglet initial, s'il existe encore if (originalTabId) { try { await browser.tabs.update(originalTabId, { active: true }); @@ -99,56 +119,54 @@ async function saveToken(token) { } catch (err) { console.warn("Impossible de basculer sur l'onglet initial :", err); } - originalTabId = null; // Reset l'identifiant + originalTabId = null; } - - // Et on rafraîchit toute l’UI await refreshAllUI(); } -// === Fonction de REFRESH centralisée === -async function refreshAllUI() { - console.log("🔄 Rafraîchissement global de l'UI..."); - - // 1) Recrée le menu contextuel “navigateur†(clic-droit) - createBrowserContextMenu(); - - // 2) Envoie un message unique “refreshUI†à toutes les parties - // (sidebar, popup, content script) pour qu'elles se mettent à jour. - browser.runtime.sendMessage({ action: "refreshUI" }); -} - -// === Gestion des messages reçus === +// ───────────────────────────────────────────────────────────────────────────── +// Gestion des messages reçus +// ───────────────────────────────────────────────────────────────────────────── browser.runtime.onMessage.addListener(async (message, sender, sendResponse) => { console.log("📩 Message reçu dans background.js :", message); switch (message.action) { - // --- Authentification --- case "toggleAuth": { - console.log("Action toggleAuth reçue"); const isConnected = await isUserConnected(); if (isConnected) { await disconnectFromLexicalDB(); } else { - await openLoginPage(); + actuallyOpenLoginPage(); + } + break; + } + case "getDefinitionWiki": { + if (message.selectedText && message.selectedText.trim() !== "") { + console.log("🌠Requête Wiktionnaire pour :", message.selectedText); + const definition = await window.fetchWiktionaryDefinition(message.selectedText.trim()); + browser.runtime.sendMessage({ + action: "fetchWiktionaryDefinitionResponse", + selectedText: message.selectedText, + definitions: [{ + source: "Wiktionnaire", + text: definition, + }], + }); + } else { + console.warn("âš ï¸ Texte sélectionné vide. Annulation de la requête."); } break; } - case "checkAuthStatus": { const connected = await isUserConnected(); sendResponse(connected); break; } - case "authStatusChanged": { console.log("🔄 Mise à jour de l'état d'authentification :", message.isLoggedIn); - // Éventuellement on peut relancer un refreshAllUI() si souhaité break; } - case "saveToken": { - // Lorsqu'on reçoit le token depuis la page BaLex if (message.token) { await saveToken(message.token); } else { @@ -156,58 +174,57 @@ browser.runtime.onMessage.addListener(async (message, sender, sendResponse) => { } break; } - - // (Optionnel) Si d’autres messages… - // ex: "toggleLexiconHighlight", etc. - default: break; } return true; }); -// === Navigation : injection / récupération token sur la page BaLex === +// ───────────────────────────────────────────────────────────────────────────── +// Web Navigation : Injection de scripts et récupération du token +// ───────────────────────────────────────────────────────────────────────────── browser.webNavigation.onCompleted.addListener(async (details) => { if (!isExtensionActive) { console.log("🚫 Extension désactivée, aucune injection script."); return; } - const url = new URL(details.url); - // Sur la page de login : injecter un petit popup d’instruction + // Injection d'un popup d'instruction sur la page de login if (url.hostname === "prisms.lezinter.net" && url.pathname === "/fr/login") { console.log("📘 Injection du popup d'instruction sur la page de login Prisms."); showInstructionPopup(details); } - - // Sur la page /balex : tenter de récupérer le token + // Récupération du token sur la page /balex if (url.hostname === "prisms.lezinter.net" && url.pathname === "/fr/headquarters/balex") { console.log("🟢 Page /balex détectée. Tentative de récupération du token."); try { - await new Promise(resolve => setTimeout(resolve, 3000)); // Attendre 3s + await new Promise(resolve => setTimeout(resolve, 3000)); await browser.tabs.executeScript(details.tabId, { code: ` - console.log("🔠Recherche du token..."); - const tokenElement = document.getElementById("accesToken"); - if (tokenElement) { - const token = tokenElement.innerText.trim(); - console.log("🔠Token détecté :", token); - browser.runtime.sendMessage({ action: "saveToken", token }); - } else { - console.error("⌠Token introuvable."); - } + (function() { + console.log("🔠Recherche du token..."); + const tokenElement = document.getElementById("accessToken") || document.getElementById("accesToken"); + if (tokenElement) { + const token = tokenElement.innerText.trim(); + console.log("🔠Token détecté :", token); + browser.runtime.sendMessage({ action: "saveToken", token }); + } else { + console.error("⌠Token introuvable."); + } + return null; + })(); ` }); } catch (error) { console.error("⌠Erreur lors de la récupération du token :", error); } } -}, { - url: [{ hostContains: "prisms.lezinter.net" }] -}); +}, { url: [{ hostContains: "prisms.lezinter.net" }] }); -// === Redirection automatique vers /balex (facultatif) === +// ───────────────────────────────────────────────────────────────────────────── +// Web Request : Redirection automatique vers /balex +// ───────────────────────────────────────────────────────────────────────────── browser.webRequest.onBeforeRequest.addListener( function(details) { if (details.url === "https://prisms.lezinter.net/fr/headquarters/") { @@ -219,7 +236,9 @@ browser.webRequest.onBeforeRequest.addListener( ["blocking"] ); -// === Affichage d'un popup d’instruction sur /fr/login === +// ───────────────────────────────────────────────────────────────────────────── +// Affichage d'un popup d'instruction sur /fr/login +// ───────────────────────────────────────────────────────────────────────────── function showInstructionPopup(details) { browser.tabs.executeScript(details.tabId, { code: ` @@ -240,7 +259,7 @@ function showInstructionPopup(details) { popup.style.fontSize = "14px"; popup.style.width = "300px"; popup.style.textAlign = "center"; - + popup.innerHTML = \` <h5 style="color: #fff; font-weight: bold; margin-top: 0;">🔑 Connexion à l'extension</h5> <p style="margin: 15px 0;"> @@ -259,108 +278,12 @@ function showInstructionPopup(details) { border-radius: 5px; ">Fermer</button> \`; - + document.body.appendChild(popup); - + const closeBtn = document.getElementById("close-popup-btn"); closeBtn.onclick = () => popup.remove(); } ` }); } - -// === Création du menu contextuel (navigateur) standard === -async function createBrowserContextMenu() { - // Supprimer les items existants - browser.contextMenus.removeAll(); - - const isConnected = await isUserConnected(); - - // Si connecté : options supplémentaires - if (isConnected) { - browser.contextMenus.create({ - id: "searchLexicon", - title: "Vérifier si le mot est dans le lexique personnel", - contexts: ["selection"], - }); - browser.contextMenus.create({ - id: "checkLexicon", - title: "Afficher dans quels lexiques le mot est présent", - contexts: ["selection"], - }); - browser.contextMenus.create({ - id: "getDefinition", - title: "Obtenir une définition à partir de mes lexiques", - contexts: ["selection"], - }); - } - - // Option accessible à tout le monde - browser.contextMenus.create({ - id: "getDefinitionWiki", - title: "Rechercher une définition sur le Wiktionnaire", - contexts: ["selection"], - }); - - // Bouton de connexion/déconnexion - browser.contextMenus.create({ - id: "login", - title: isConnected ? "Se déconnecter de BaLex" : "Se connecter à BaLex", - contexts: ["all"], - }); -} - -// Initialise le menu contextuel du navigateur une première fois -createBrowserContextMenu(); - -// Gère les clics sur les items du menu contextuel du navigateur -browser.contextMenus.onClicked.addListener(async (info, tab) => { - console.log("📦 Menu contextuel - item cliqué :", info.menuItemId); - switch (info.menuItemId) { - case "searchLexicon": - browser.tabs.sendMessage(tab.id, { - action: "searchLexicon", - selectedText: info.selectionText, - }); - break; - - case "checkLexicon": - browser.tabs.sendMessage(tab.id, { - action: "checkLexicon", - selectedText: info.selectionText, - }); - break; - - case "getDefinition": - browser.tabs.sendMessage(tab.id, { - action: "getDefinition", - selectedText: info.selectionText, - }); - break; - - case "getDefinitionWiki": - browser.tabs.sendMessage(tab.id, { - action: "getDefinitionWiki", - selectedText: info.selectionText, - }); - break; - - case "login": { - const connected = await isUserConnected(); - if (connected) { - await disconnectFromLexicalDB(); - } else { - await openLoginPage(); - } - break; - } - - default: - console.warn(`Aucune action définie pour l'élément : ${info.menuItemId}`); - } -}); - -// === Exemple de notification === -function showNotification(title, message, iconPath) { - console.log(`🔔 NOTIFICATION: [${title}] ${message}`); -} diff --git a/background/browser_context_menu.js b/background/browser_context_menu.js deleted file mode 100644 index d562cab33def88e20fc233d3e0c2ccf890d5c221..0000000000000000000000000000000000000000 --- a/background/browser_context_menu.js +++ /dev/null @@ -1,231 +0,0 @@ -console.log("browser_context_menu.js chargé correctement"); // Vérifie que le script est correctement chargé - -// Variable globale qui contient le token pour les requêtes API -let authToken = null; - -// Fonction pour charger le token depuis le stockage local -async function loadAuthToken() { - try { - const result = await browser.storage.local.get("accessToken"); - authToken = result.accessToken; - console.log("🔑 Token chargé au démarrage :", authToken); - } catch (error) { - console.error("⌠Erreur lors de la récupération du token :", error); - } -} - -// Charger le token dès le démarrage du script -loadAuthToken(); - -// Création des options du menu contextuel du navigateur -browser.contextMenus.create({ - id: "searchLexicon", - title: "Ce mot est-il dans mon lexique ?", - contexts: ["selection"], // Affiché uniquement lorsqu'un texte est sélectionné - icons: { - "16": "icons/recherche_lexique.png" - }, -}); - -browser.contextMenus.create({ - id: "checkLexicon", - title: "Afficher dans quel(s) lexique(s) ce mot est présent", - contexts: ["selection"], - icons: { - "16": "icons/quel_lexique.png" - }, - -}); - -browser.contextMenus.create({ - id: "addtoLexicon", - title: "Ajouter ce mot à mon lexique", - contexts: ["selection"], - icons: { - "16": "icons/ajout_lexique.png" - }, -}); - - -browser.contextMenus.create({ - id: "separatorExtension", - type: "separator", - contexts: ["all"], -}); - -browser.contextMenus.create({ - id: "getDefinition", - title: "Obtenir une définition à partir de mes lexiques", - contexts: ["selection"], - icons: { - "16": "icons/definition.png" - }, -}); - -browser.contextMenus.create({ - id: "getDefinitionWiki", - title: "Rechercher une définition dans le Wiktionnaire", - contexts: ["selection"], - icons: { - "16": "icons/definition_wiktionnaire.png" - }, -}); - -// Ajouter un séparateur après les options de l'extension pour les distinguer des autres éléments du menu -browser.contextMenus.create({ - id: "separatorAfterExtension", - type: "separator", - contexts: ["all"], -}); - -// Gestion des clics sur les options du menu contextuel -browser.contextMenus.onClicked.addListener(async (info, tab) => { - if (info.selectionText) { - console.log(`📩 Texte sélectionné dans le menu du navigateur : ${info.selectionText}`); - - // Vérifier quelle option a été cliquée - switch (info.menuItemId) { - case "searchLexicon": - console.log("🔠Recherche dans le lexique :", info.selectionText); - if (authToken) { - await searchLexicon(info.selectionText); - } else { - alert("âš ï¸ Vous devez être connecté pour utiliser cette fonction."); - } - break; - - case "checkLexicon": - console.log("📋 Vérification des lexiques :", info.selectionText); - if (authToken) { - await checkLexicon(info.selectionText); - } else { - alert("âš ï¸ Vous devez être connecté pour utiliser cette fonction."); - } - break; - - case "getDefinition": - console.log("📖 Recherche des définitions :", info.selectionText); - if (authToken) { - await getDefinition(info.selectionText); - } else { - alert("âš ï¸ Vous devez être connecté pour utiliser cette fonction."); - } - break; - - case "getDefinitionWiki": - console.log("🌠Recherche sur le Wiktionnaire :", info.selectionText); - await getDefinitionWiki(info.selectionText); - break; - - default: - console.error(`⌠Action inconnue : ${info.menuItemId}`); - } - } -}); - -// Fonction pour rechercher un mot dans le lexique personnel -async function searchLexicon(selectedText) { - try { - const lexicons = await getUserLexicons(authToken); - console.log("📚 Lexiques récupérés :", lexicons); - - const frenchLexicon = lexicons.find((lexicon) => lexicon.language === "fr"); - if (!frenchLexicon) { - alert("âš ï¸ Aucun lexique français trouvé."); - return; - } - - const entries = await getLexiconEntriesID(authToken, frenchLexicon.id); - const isWordPresent = entries.some( - (entry) => entry.graphy.toLowerCase() === selectedText.toLowerCase() - ); - - if (isWordPresent) { - alert(`✅ Le mot "${selectedText}" est présent dans votre lexique personnel.`); - } else { - alert(`⌠Le mot "${selectedText}" n'est pas présent dans votre lexique personnel.`); - } - } catch (error) { - console.error("⌠Erreur lors de la recherche dans le lexique :", error); - } -} - -// Fonction pour vérifier dans quels lexiques un mot est présent -async function checkLexicon(selectedText) { - try { - const lexicons = await getLexicons(authToken); - const results = lexicons.map((lexicon) => ({ - name: lexicon.name, - isPresent: lexicon.entries.some( - (entry) => entry.graphy.toLowerCase() === selectedText.toLowerCase() - ), - })); - - console.log("📋 Résultats des lexiques :", results); - let message = `Résultats pour "${selectedText}":\n\n`; - results.forEach(({ name, isPresent }) => { - message += `- ${name}: ${isPresent ? "✅ Présent" : "⌠Non présent"}\n`; - }); - alert(message); - } catch (error) { - console.error("⌠Erreur lors de la vérification des lexiques :", error); - } -} - -// Fonction pour obtenir les définitions d'un mot -async function getDefinition(selectedText) { - try { - const lexicons = await getLexicons(authToken); - let definitions = []; - - for (const lexicon of lexicons) { - const entries = await getLexiconEntriesID(authToken, lexicon.id); - const entry = entries.find( - (entry) => entry.graphy.toLowerCase() === selectedText.toLowerCase() - ); - - if (entry) { - const definitionData = await getDefinition(authToken, entry.id); - definitions.push(...extractDefinitions(definitionData)); - } - } - - console.log("📖 Définitions trouvées :", definitions); - if (definitions.length > 0) { - alert(`📖 Définitions pour "${selectedText}":\n\n` + definitions.join("\n")); - } else { - alert(`⌠Aucune définition trouvée pour "${selectedText}".`); - } - } catch (error) { - console.error("⌠Erreur lors de la récupération des définitions :", error); - } -} - -// Fonction pour rechercher une définition sur le Wiktionnaire -async function getDefinitionWiki(selectedText) { - try { - const result = await fetch(`https://fr.wiktionary.org/wiki/${encodeURIComponent(selectedText)}`); - if (result.ok) { - alert(`🌠Définition trouvée pour "${selectedText}". Consultez le Wiktionnaire.`); - } else { - alert(`⌠Aucune définition trouvée sur le Wiktionnaire pour "${selectedText}".`); - } - } catch (error) { - console.error("⌠Erreur lors de la recherche sur le Wiktionnaire :", error); - } -} - -// Fonction pour extraire les définitions d'une réponse API -function extractDefinitions(response) { - if (!response || !response.attributes || !response.attributes.Items) { - console.warn("âš ï¸ Structure de réponse inattendue."); - return []; - } - - const items = response.attributes.Items; - return items - .flatMap((item) => - item.Sense?.Definitions?.map((def) => def.Def || "Définition non disponible") || [] - ) - .filter(Boolean); -} diff --git "a/barre_lat\303\251rale/sidebar.html" "b/barre_lat\303\251rale/sidebar.html" index 55e12d5593c893291d7b81230180cb818133ab82..5ea40ba7d782810b15615b1871ce659499938cf8 100644 --- "a/barre_lat\303\251rale/sidebar.html" +++ "b/barre_lat\303\251rale/sidebar.html" @@ -4,7 +4,10 @@ <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>BaLex - Barre Latérale</title> - <script src="sidebar.js"></script> + <script src="../api.js" defer></script> + <script src="../definitions.js" defer></script> + <script src="sidebar.js" defer></script> + <style> /* Style global */ body { @@ -16,20 +19,50 @@ color: #323046; } - /* Conteneurs */ - #menu, #etat { + /* Conteneurs principaux */ + #menu, #etat, #definitionContainer { padding: 10px; - margin-bottom: 0px; + margin-bottom: 10px; border-radius: 10px; background-color: #a08e9f; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); } - /* Titre */ - h3 { + /* En-têtes de blocs */ + .block-header { + position: relative; + text-align: center; + margin-top: 2px; + } + + .block-header h3 { + display: inline-block; + margin-top: 5px; + margin-bottom: 0; + } + + /* Utilisation de la classe pour les boutons de bascule */ + .toggle-btn { + position: absolute; + right: 8px; + background: none; + margin: 0; + border: none; color: #fff; - text-align: center; - font-weight: bold; + font-size: 15px; + padding: 3px 5px; + cursor: pointer; + width: auto; + display: inline-block; + } + + /* Contenu des blocs */ + .block-content { + padding-top: 2px; + } + + .hidden { + display: none; } /* Boutons */ @@ -97,23 +130,101 @@ /* Espace pour les pictogrammes */ .lexique-icon { - width: 25px; - height: 25px; - border-radius: 50%; - background-color: #ccc; - margin-right: 10px; - flex-shrink: 0; - } + width: 25px; + height: 25px; + border-radius: 50%; + background-color: #ccc; + margin-right: 10px; + flex-shrink: 0; + } + + .lexicon-section { + margin-bottom: 10px; + } + + .lexicon-header { + font-weight: bold; + cursor: pointer; + padding: 5px; + background-color: #8d5c70; + border-radius: 5px; + text-align: center; + } + + .lexicon-header:hover { + background-color: #dddedd; + color: #8d5c70; + } + + .lexicon-content { + margin-top: 5px; + } + + #mesLexiquesList { + display: inline; + padding: 0; + align-items: center; + } + + #mesLexiquesContainer h4 { + margin-bottom: 5px; + } /* Définition */ - #definition { - margin-top: 10px; + #definitionContainer { background-color: #444; padding: 10px; - border-radius: 5px; + border-radius: 10px; color: white; } + + #definitionsList { + list-style: none; + padding: 0; + } + #definitionsList li { + margin-bottom: 10px; + } + + .definition-source { + font-weight: bold; + color: #ffa500; + } + + /* Popup (modal) caché par défaut */ + .modal-overlay { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background: rgba(0,0,0,0.5); + display: none; + align-items: center; + justify-content: center; + z-index: 9999; + } + + .modal-content { + background: white; + color: #8d5c70; + padding: 1rem; + max-width: 600px; + max-height: 80vh; + overflow-y: auto; + border-radius: 8px; + } + + .close-button { + float: right; + cursor: pointer; + font-weight: bold; + color: #666; + } + .close-button:hover { + color: #000; + } /* Activer/désactiver le surlignage */ #highlighting-options p { @@ -121,41 +232,89 @@ font-size: small; color: #333; } + + #noDefinitionsContainer { + display: block !important; + color: red !important; + font-weight: bold; + } </style> + </head> <body> - <!-- Menu des lexiques --> + <!-- Bloc 1 : Menu des lexiques --> <div id="menu"> - <h3>Lexiques</h3> - <p id="highlight-note" style="display: none;">Cochez un/des lexique(s) pour activer le surlignage des mots sur la page.</p> - <div id="lexiques">Chargement...</div> - - <div id="auth-section"> - <button id="auth-button">Se connecter</button> + <div class="block-header"> + <h3>Lexiques</h3> + <button class="toggle-btn"></button> + </div> + <div id="menuContent" class="block-content"> + <p id="highlight-note" style="display: none;">Cochez un/des lexique(s) pour activer le surlignage des mots sur la page.</p> + <div id="lexiques">Chargement...</div> + <div id="auth-section"> + <button id="auth-button">Se connecter</button> + </div> </div> </div> - <!-- État de la sélection --> - <hr> + <!-- Bloc 2 : État de la sélection --> <div id="etat"> - <h3>Mot sélectionné</h3> - <p id="motSelectionne">Aucun mot sélectionné</p> - <div id="add-to-lexiques" style="display: none;"> - <button id="add-word-button">Ajouter le mot sélectionné</button> + <div class="block-header"> + <h3>Mot sélectionné</h3> + <button class="toggle-btn"></button> </div> - - <button id="chercherDef">Chercher la/les définition(s)</button> - <div id="possible-definitions" style="display: none;"> - <label style="font-size: small;"> - <input type="checkbox" id="toggle-definitions"> - Afficher toutes les définitions - </label> + <div id="etatContent" class="block-content"> + <p id="motSelectionne">Aucun mot sélectionné</p> + <p id="lexiconResult"></p> + <div id="add-to-lexiques" style="display: none;"> + <button id="add-word-button">Ajouter le mot sélectionné</button> + </div> + <button id="chercherDef">Rechercher une définition</button> + <div id="possible-definitions" style="display: none;"></div> </div> </div> + + <!-- Bloc 3 : Définitions --> + <div id="definitionContainer"> + <div class="block-header"> + <h3>Définitions</h3> + <button class="toggle-btn"></button> + </div> + <div id="definitionContent" class="block-content"> + <!-- Définitions des lexiques de l'utilisateur --> + <div id="mesLexiquesContainer"> + <h4>📚 Mes lexiques</h4> + <label style="font-size: small;"> + <input type="checkbox" id="toggle-definitions"> + Afficher toutes les définitions + </label> + <ul id="mesLexiquesList"></ul> + </div> + + <div id="noLexiconDefinitionsContainer" style="display: none; color: #8d5c70;"> + <p>Aucune définition trouvée dans les lexiques.</p> + </div> + + <div id="noWiktionaryDefinitionsContainer" style="display: none; color: #8d5c70;"> + <p>Aucune définition trouvée dans le Wiktionnaire.</p> + </div> + + <!-- Définitions issues du Wiktionnaire --> + <div id="wiktionnaireContainer"> + <h4>🌠Wiktionnaire</h4> + <ul id="wiktionnaireList"></ul> + </div> - <!-- Définition affichée --> - <div id="definition"></div> + <!-- Fenêtre modale cachée --> + <div id="modalOverlay" class="modal-overlay"> + <div class="modal-content"> + <span id="closeModalBtn" class="close-button">X</span> + <div id="modalFullText"></div> + </div> + </div> + </div> + </div> </body> </html> diff --git "a/barre_lat\303\251rale/sidebar.js" "b/barre_lat\303\251rale/sidebar.js" index 947a8547d2fd6836bd0e49d1a3163fbc2754f227..b920188bcb008c93a066822d8efad2d8effaae76 100644 --- "a/barre_lat\303\251rale/sidebar.js" +++ "b/barre_lat\303\251rale/sidebar.js" @@ -1,41 +1,42 @@ -// ✅ Chargement du script de la barre latérale -console.log("✅ sidebar.js chargé avec succès !"); +console.log("✅ sidebar.js chargé !"); +console.log( + "🌠Vérification API browser :", + typeof browser !== "undefined" ? "✅ Disponible" : "⌠Non disponible" +); -// Variable globale pour le token d'authentification + +// ───────────────────────────────────────────────────────────────────────────── +// â–Œ Variables globales +// ───────────────────────────────────────────────────────────────────────────── let authToken = null; -// Gestionnaire global des erreurs -window.addEventListener('error', (e) => { - console.error("â—ï¸ Erreur globale détectée :", e.message, "dans", e.filename, ":", e.lineno); -}); +window.lexiconMap = lexiconMap; +window.authToken = authToken; -// Fonction pour valider le format du token (JWT par exemple) -function validateToken(token) { - const isValid = /^\S+\.\S+\.\S+$/.test(token); - if (!isValid) { - console.warn("âš ï¸ Le format du token semble incorrect :", token); - } - return isValid; -} - -// Lecture du token depuis le stockage local +// ───────────────────────────────────────────────────────────────────────────── +// â–Œ Fonctions liées au token +// ───────────────────────────────────────────────────────────────────────────── async function getAuthTokenFromStorage() { try { const { accessToken } = await browser.storage.local.get("accessToken"); - if (accessToken && validateToken(accessToken)) { + if (accessToken) { console.log(`🔑 Token récupéré depuis le stockage local : [${accessToken}]`); authToken = accessToken; + window.authToken = accessToken; return authToken; } - console.warn("âš ï¸ Aucun token valide trouvé dans le stockage local."); + console.warn("âš ï¸ Aucun token trouvé dans le stockage local."); } catch (error) { console.error("⌠Erreur lors de la récupération du token :", error); } authToken = null; + window.authToken = null; return null; } -// Met à jour le bouton de connexion/déconnexion +// ───────────────────────────────────────────────────────────────────────────── +// â–Œ Mise à jour de l'interface utilisateur (UI) +// ───────────────────────────────────────────────────────────────────────────── function updateAuthButton(isLoggedIn) { const authButton = document.getElementById("auth-button"); if (authButton) { @@ -46,17 +47,17 @@ function updateAuthButton(isLoggedIn) { } } -// Affiche/masque certains éléments quand l'utilisateur est connecté/déconnecté function toggleElementsVisibility(isLoggedIn) { const elementsToShowOrHide = [ - { id: 'add-to-lexiques', shouldShow: isLoggedIn }, - { id: 'possible-definitions', shouldShow: isLoggedIn }, + { id: "add-to-lexiques", shouldShow: isLoggedIn }, + { id: "possible-definitions", shouldShow: isLoggedIn }, + { id: "mesLexiquesContainer", shouldShow: isLoggedIn }, ]; elementsToShowOrHide.forEach(({ id, shouldShow }) => { const element = document.getElementById(id); if (element) { - element.style.display = shouldShow ? 'block' : 'none'; + element.style.display = shouldShow ? "block" : "none"; console.log(`🔄 Élément #${id} ${shouldShow ? "affiché" : "masqué"}.`); } else { console.warn(`âš ï¸ Ã‰lément #${id} introuvable.`); @@ -64,15 +65,16 @@ function toggleElementsVisibility(isLoggedIn) { }); } -// Affiche/masque un message de surlignage function toggleHighlightMessage(isLoggedIn) { - const highlightNote = document.getElementById('highlight-note'); + const highlightNote = document.getElementById("highlight-note"); if (highlightNote) { - highlightNote.style.display = isLoggedIn ? 'block' : 'none'; + highlightNote.style.display = isLoggedIn ? "block" : "none"; } } -// Rafraîchit l'état de la barre latérale +/** + * Met à jour l'état global de la sidebar (bouton d'authentification, etc.) + */ async function refreshSidebarState() { console.log("🔄 Début de l'actualisation de la barre latérale..."); const token = await getAuthTokenFromStorage(); @@ -83,69 +85,148 @@ async function refreshSidebarState() { toggleHighlightMessage(isLoggedIn); if (isLoggedIn) { - // Charger les lexiques await fetchLexicons(); } else { - // Masquer ou vider le container des lexiques const lexiquesContainer = document.getElementById("lexiques"); if (lexiquesContainer) { lexiquesContainer.textContent = "Veuillez vous connecter pour voir vos lexiques."; + lexiquesContainer.style.textAlign = "center"; + lexiquesContainer.style.fontStyle = "italic"; + } + const lexiconResultElement = document.getElementById("lexiconResult"); + if (lexiconResultElement) { + lexiconResultElement.innerHTML = ""; } } console.log("✅ Barre latérale actualisée. Utilisateur connecté :", isLoggedIn); } -// Affiche les lexiques (checkbox + icône) -function displayLexicons(lexicons) { - const lexiquesContainer = document.getElementById('lexiques'); +/** + * 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) { + const block = document.getElementById(blockId); + if (block) { + if (block.classList.contains("hidden")) { + block.classList.remove("hidden"); + if (btn) btn.textContent = "–"; + } else { + block.classList.add("hidden"); + if (btn) btn.textContent = "+"; + } + } +} + +/** + * 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) { + const block = document.getElementById(blockId); + if (block && block.classList.contains("hidden")) { + block.classList.remove("hidden"); + + if (btn) { + btn.textContent = "–"; + } else { + const header = block.previousElementSibling; + if (header) { + const toggleBtn = header.querySelector(".toggle-btn"); + if (toggleBtn) { + toggleBtn.textContent = "–"; + } + } + } + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// â–Œ Gestion des lexiques (Affichage) +// ───────────────────────────────────────────────────────────────────────────── + +async function fetchLexicons() { + try { + console.log("🔄 Début de la récupération des lexiques..."); + console.log("🔑 Token utilisé :", authToken); + + if (!authToken) { + throw new Error("âš ï¸ Aucun token disponible. Veuillez vous connecter."); + } + + const lexicons = await getLexicons(authToken, "fr"); + console.log("📚 Réponse brute de l'API :", lexicons); + + if (!Array.isArray(lexicons) || lexicons.length === 0) { + throw new Error("âš ï¸ Aucun lexique trouvé."); + } + + lexiconMap.clear(); + lexicons.forEach((lex) => { + const lexiconName = + lex.category === "User" + ? `Lexique personnel : ${lex.user?.pseudo || "Inconnu"} (${lex.id})` + : `Lexique de groupe : ${lex.group?.name || "Inconnu"} (${lex.id})`; + lexiconMap.set(lex.id, lexiconName); + }); + + displayLexiconsWithCheckbox(lexicons.map((lex) => ({ + lexiconName: + lex.category === "User" + ? `Lexique personnel : ${lex.user?.pseudo || "Inconnu"} (${lex.id})` + : `Lexique de groupe : ${lex.group?.name || "Inconnu"} (${lex.id})`, + lexiconId: lex.id, + active: lex.active || false, + }))); + } catch (error) { + console.error("⌠Erreur lors du chargement des lexiques :", error.message); + const lexiquesContainer = document.getElementById("lexiques"); + if (lexiquesContainer) { + lexiquesContainer.textContent = error.message || "Erreur lors du chargement des lexiques."; + } + } +} + +function displayLexiconsWithCheckbox(lexicons) { + const lexiquesContainer = document.getElementById("lexiques"); if (!lexiquesContainer) { console.warn("âš ï¸ Ã‰lément #lexiques introuvable."); return; } - - lexiquesContainer.innerHTML = ''; - + lexiquesContainer.innerHTML = ""; if (lexicons.length === 0) { console.log("âš ï¸ Aucun lexique à afficher."); - lexiquesContainer.textContent = 'Aucun lexique disponible.'; + lexiquesContainer.textContent = "Aucun lexique disponible."; return; } - - console.log("ðŸ–¼ï¸ Affichage des lexiques..."); lexicons.forEach(({ lexiconName, lexiconId, active }) => { - console.log(`âž¡ï¸ Affichage du lexique : ${lexiconName} (ID: ${lexiconId})`); - - const lexiqueDiv = document.createElement('div'); - lexiqueDiv.className = 'lexique-item'; - - const iconDiv = document.createElement('div'); - iconDiv.className = 'lexique-icon'; - iconDiv.style.backgroundColor = '#ccc'; - - const labelSpan = document.createElement('span'); - labelSpan.className = 'lexique-label'; + const lexiqueDiv = document.createElement("div"); + lexiqueDiv.className = "lexique-item"; + const iconDiv = document.createElement("div"); + iconDiv.className = "lexique-icon"; + iconDiv.style.backgroundColor = "#ccc"; + const labelSpan = document.createElement("span"); + labelSpan.className = "lexique-label"; labelSpan.textContent = lexiconName; - - const checkbox = document.createElement('input'); - checkbox.type = 'checkbox'; - checkbox.className = 'lexique-checkbox'; + const checkbox = document.createElement("input"); + checkbox.type = "checkbox"; + checkbox.className = "lexique-checkbox"; checkbox.checked = active; - - checkbox.addEventListener('change', async () => { + checkbox.addEventListener("change", async () => { console.log( `🔄 Changement de surlignage pour ${lexiconName} (ID: ${lexiconId}): ${ - checkbox.checked ? 'activé' : 'désactivé' + checkbox.checked ? "activé" : "désactivé" }` ); - // Envoyer un message au background await browser.runtime.sendMessage({ action: "toggleLexiconHighlight", lexiconId, - isActive: checkbox.checked + isActive: checkbox.checked, }); }); - lexiqueDiv.appendChild(iconDiv); lexiqueDiv.appendChild(labelSpan); lexiqueDiv.appendChild(checkbox); @@ -153,78 +234,107 @@ function displayLexicons(lexicons) { }); } -// Récupération des lexiques -async function fetchLexicons() { - try { - console.log("🔄 Début de la récupération des lexiques..."); - console.log("🔑 Token utilisé :", authToken); - - if (!authToken) { - throw new Error("âš ï¸ Aucun token disponible. Veuillez vous connecter."); - } - - // Appel à l'API - const lexicons = await getLexicons(authToken, 52, 'fr'); // À adapter - console.log("📚 Réponse brute de l'API :", lexicons); - - if (!Array.isArray(lexicons) || lexicons.length === 0) { - throw new Error("âš ï¸ Aucun lexique trouvé."); - } - - // Transformation des données - const results = lexicons.map((lexicon) => { - console.log("📋 Transformation du lexique :", lexicon); - return { - lexiconName: lexicon.category === 'User' - ? `Lexique personnel (${lexicon.user?.pseudo || 'Inconnu'})` - : `Lexique de groupe (${lexicon.group?.name || 'Inconnu'})`, - lexiconId: lexicon.id, - active: lexicon.active || false, - }; - }); - - console.log("Appel de displayLexicons avec :", results); +function initModal() { + console.log("initModal appelé"); + const modalOverlay = document.getElementById("modalOverlay"); + const modalFullText = document.getElementById("modalFullText"); + const closeModalBtn = document.getElementById("closeModalBtn"); - // Affichage des lexiques - displayLexicons(results); + console.log("closeModalBtn =", closeModalBtn); - } catch (error) { - console.error("⌠Erreur lors du chargement des lexiques :", error.message); - console.error("Détails de l'erreur :", error); - const lexiquesContainer = document.getElementById("lexiques"); - if (lexiquesContainer) { - lexiquesContainer.textContent = error.message || "Erreur lors du chargement des lexiques."; - } + if (!modalOverlay || !modalFullText || !closeModalBtn) { + console.error("Les éléments modaux ne sont pas trouvés !"); + return; } + + closeModalBtn.addEventListener("click", () => { + console.log("clic sur closeModalBtn !"); + closeDefinitionPopup(); + }); + modalOverlay.addEventListener("click", (event) => { + if (event.target === modalOverlay) closeDefinitionPopup(); + }); } -// Gestion du clic sur le bouton de connexion/déconnexion +document.addEventListener("DOMContentLoaded", initModal); + +// ───────────────────────────────────────────────────────────────────────────── +// â–Œ Bouton de connexion/déconnexion +// ───────────────────────────────────────────────────────────────────────────── async function handleAuthButtonClick() { await browser.runtime.sendMessage({ action: "toggleAuth" }); await refreshSidebarState(); } -// Écoute des messages envoyés par d'autres parties de l'extension -browser.runtime.onMessage.addListener((message) => { +// ───────────────────────────────────────────────────────────────────────────── +// â–Œ Réception des messages +// ───────────────────────────────────────────────────────────────────────────── +browser.runtime.onMessage.addListener(async (message) => { console.log("📩 Message reçu dans sidebar.js :", message); - - if (message.action === "refreshUI") { - console.log("🔄 Rafraîchissement de la barre latérale demandé."); - refreshSidebarState(); - } else if (message.action === "mot_selectionne" && message.selectedText) { - console.log("ðŸ–‹ï¸ Mise à jour du mot sélectionné :", message.selectedText); - - const selectedWordElement = document.getElementById("motSelectionne"); - if (selectedWordElement) { - selectedWordElement.textContent = message.selectedText; - } else { - console.warn("âš ï¸ Ã‰lément #motSelectionne introuvable."); - } + switch (message.action) { + case "refreshUI": + console.log("🔄 Demande de rafraîchissement de la barre latérale."); + await refreshSidebarState(); + break; + + case "mot_selectionne": + if (message.selectedText) { + console.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 "getDefinition": + if (message.selectedText) { + console.log("📖 Recherche des définitions pour :", message.selectedText); + openBlock("definitionContent"); + await window.showDefinitions(message.selectedText); + } + break; + + case "showDefinitions": + if (Array.isArray(message.definitions)) { + window.displayDefinitions(message.definitions); + } + break; + + case "fetchWiktionaryDefinitionResponse": + if (message.selectedText) { + console.log(`📖 Réception de la définition du Wiktionnaire pour '${message.selectedText}'`); + window.displayDefinitions(message.definitions); + } + break; + + case "showLexiconResult": + console.log("📚 Résultat des lexiques reçus :", message.lexicons); + window.displayLexiconResults(message.lexicons); + break; + + case "toggleAuth": + break; + + case "authStatusChanged": + break; + + default: + console.warn("âš ï¸ Action inconnue reçue :", message.action); } }); -// Initialisation de la barre latérale après chargement du DOM -document.addEventListener('DOMContentLoaded', async () => { +// ───────────────────────────────────────────────────────────────────────────── +// â–Œ DOMContentLoaded +// ───────────────────────────────────────────────────────────────────────────── +document.addEventListener("DOMContentLoaded", async () => { console.log("📦 DOM entièrement chargé. Initialisation de la sidebar."); await refreshSidebarState(); @@ -232,4 +342,60 @@ document.addEventListener('DOMContentLoaded', async () => { if (authButton) { authButton.addEventListener("click", handleAuthButtonClick); } + + const chercherDefButton = document.getElementById("chercherDef"); + if (chercherDefButton) { + chercherDefButton.addEventListener("click", async () => { + openBlock("definitionContent"); + const selectedWord = document.getElementById("motSelectionne")?.textContent?.trim(); + if (selectedWord && selectedWord !== "Aucun mot sélectionné") { + console.log(`📩 Recherche des définitions pour '${selectedWord}'`); + await window.showDefinitions(selectedWord); + } else { + console.warn("âš ï¸ Aucun mot sélectionné pour la recherche."); + } + }); + console.log("✅ Bouton #chercherDef détecté dans le DOM."); + chercherDefButton.style.pointerEvents = "auto"; + chercherDefButton.style.display = "block"; + chercherDefButton.style.visibility = "visible"; + chercherDefButton.disabled = false; + } else { + console.error("⌠ERREUR : Bouton #chercherDef introuvable."); + } + + // Écouteur pour la case à cocher "toggle-definitions" + const toggleCheckbox = document.getElementById("toggle-definitions"); + if (toggleCheckbox) { + toggleCheckbox.addEventListener("change", (event) => { + const showAll = event.target.checked; + const lexiconContents = document.querySelectorAll("#mesLexiquesList .lexicon-content"); + lexiconContents.forEach(content => { + if (showAll) { + content.classList.remove("hidden"); + } else { + content.classList.add("hidden"); + } + }); + }); + } +}); + +document.addEventListener("DOMContentLoaded", () => { + const toggleButtons = document.querySelectorAll(".toggle-btn"); + toggleButtons.forEach((btn) => { + if (!btn.textContent.trim()) { + btn.textContent = "-"; + btn.style.fontSize = "15px"; + } + btn.addEventListener("click", (event) => { + event.stopPropagation(); + const header = btn.parentElement; + const content = header.nextElementSibling; + if (content) { + content.classList.toggle("hidden"); + btn.textContent = content.classList.contains("hidden") ? "+" : "–"; + } + }); + }); }); diff --git a/definitions.js b/definitions.js new file mode 100644 index 0000000000000000000000000000000000000000..0e22bffd67d3271183fe398dee3c36c2c4a79462 --- /dev/null +++ b/definitions.js @@ -0,0 +1,462 @@ +// ───────────────────────────────────────────────────────────────────────────── +// â–Œ Fonctions pour récupérer/afficher les définitions +// ───────────────────────────────────────────────────────────────────────────── + +const lexiconMap = new Map(); + +/** +* Récupère les définitions d'un mot dans les lexiques de l'utilisateur (ex. user_id=4), +* en effectuant un appel global à l'API pour le mot recherché, puis en filtrant +* les entrées pour ne conserver que celles dont le lexicon.id est présent dans la liste +* des lexiques de l'utilisateur. +* +* Retourne un tableau d'objets { source, text } où: +* - source = nom du lexique (ou "Lexique #ID") +* - text = texte de la définition +*/ +async function fetchLexiconDefinitions(word) { + try { + console.log(`🔠Recherche des définitions de '${word}' dans les lexiques de l'utilisateur...`); + + if (!authToken) { + console.warn("Aucun token disponible, impossible de requêter l'API protégée."); + return []; + } + + // 1) Récupérer la liste complète des lexiques de l'utilisateur + const userId = 4; + const lexUrl = `https://babalex.lezinter.net/api/lexicon/search?user_id=${userId}&language=fr`; + const lexResponse = await fetch(lexUrl, { + headers: { Authorization: `Bearer ${authToken}` } + }); + if (!lexResponse.ok) { + throw new Error(`⌠Erreur API lors de la récupération des lexiques: ${lexResponse.statusText}`); + } + const userLexicons = await lexResponse.json(); + console.log("ðŸ—‚ï¸ Lexiques de l'utilisateur :", userLexicons); + + if (!Array.isArray(userLexicons) || userLexicons.length === 0) { + console.warn("âš ï¸ Aucun lexique trouvé pour cet utilisateur."); + return []; + } + + // Mise à jour de lexiconMap avec des libellés uniques (ajout de l'ID) + lexiconMap.clear(); + userLexicons.forEach((lex) => { + const lexiconName = + lex.category === "User" + ? `Lexique personnel : ${lex.user?.pseudo || "Inconnu"}` + : `Lexique de groupe : ${lex.group?.name || "Inconnu"}`; + lexiconMap.set(lex.id, lexiconName); + }); + console.log("📌 LexiconMap :", lexiconMap); + + // 2) Pour chaque lexique, rechercher le mot en ajoutant target_lex + const definitionsPromises = userLexicons.map(async (lex) => { + const searchUrl = `https://babalex.lezinter.net/api/entry/search?graphy=${encodeURIComponent(word)}&language=fr&target_lex=${lex.id}`; + console.log(`🔎 Appel API pour le lexique ${lex.id} avec l'URL : ${searchUrl}`); + + const searchResponse = await fetch(searchUrl, { + headers: { Authorization: `Bearer ${authToken}` } + }); + if (!searchResponse.ok) { + console.warn(`âš ï¸ Erreur pour le lexique ${lex.id} : ${searchResponse.statusText}`); + return { lexiconId: lex.id, entries: [] }; + } + const entries = await searchResponse.json(); + + // Filtrage côté client : ne garder que les entrées dont entry.lexicon.id correspond exactement à lex.id + const filteredEntries = entries.filter(entry => { + if (!entry.lexicon) return false; + return Number(entry.lexicon.id) === Number(lex.id); + }); + + console.log(`Pour le lexique ${lex.id} (${lexiconMap.get(lex.id)}), entrées filtrées :`, filteredEntries); + + return { lexiconId: lex.id, entries: filteredEntries }; + }); + + const results = await Promise.all(definitionsPromises); + + // 3) Parcourir les résultats et extraire les définitions + let allDefinitions = []; + results.forEach(result => { + const lexiconId = result.lexiconId; + const sourceName = lexiconMap.get(lexiconId) || `Lexique #${lexiconId}`; + + result.entries.forEach(entry => { + if (!entry.lexicon || Number(entry.lexicon.id) !== Number(lexiconId)) return; + + let items = entry.attributes?.Items; + if (!Array.isArray(items)) { + if (typeof items === 'object' && items !== null) { + items = Object.values(items); + } else { + return; + } + } + + items.forEach(item => { + const definitionsArray = item.Sense?.Definitions; + if (!Array.isArray(definitionsArray)) return; + + definitionsArray.forEach(defObj => { + if (defObj.Def) { + allDefinitions.push({ + source: sourceName, + text: defObj.Def, + lexiconId: lexiconId + }); + } + }); + }); + }); + }); + + console.log("Résultat final filtré :", allDefinitions); + return allDefinitions; + } catch (error) { + console.error("⌠Erreur générale lors de la récupération des définitions :", error); + return []; + } +} + +/** +* Récupère la définition d'un mot depuis le Wiktionnaire (fr). +* Retourne un tableau d'objets : [{ source: 'Wiktionnaire', text: '...' }] +*/ +async function fetchWiktionaryDefinition(word) { + try { + console.log(`🔠Requête Wiktionnaire pour "${word}"...`); + if (!word || word.trim() === "") { + throw new Error("âš ï¸ Mot vide, impossible d'envoyer la requête."); + } + const wiktionaryURL = `https://fr.wiktionary.org/w/api.php?action=query&format=json&origin=*&prop=extracts&explaintext=true&redirects=1&titles=${encodeURIComponent(word)}`; + const response = await fetch(wiktionaryURL); + if (!response.ok) { + throw new Error(`⌠Erreur API Wiktionnaire: ${response.statusText}`); + } + const data = await response.json(); + console.log("📖 Réponse API (Wiktionnaire) :", data); + + const pages = data.query?.pages; + const page = pages ? Object.values(pages)[0] : null; + + const definitionText = page && page.extract + ? page.extract.trim() + : "âš ï¸ Aucune définition trouvée sur le Wiktionnaire."; + + console.log("🌠Définition Wiktionnaire extraite :", definitionText); + + return [ + { + source: "Wiktionnaire", + text: definitionText + } + ]; + } catch (error) { + console.error("⌠Erreur Wiktionnaire :", error); + return [ + { + source: "Wiktionnaire", + text: "âš ï¸ Erreur lors de la récupération sur le Wiktionnaire." + } + ]; + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// â–Œ Affichage des définitions dans la barre latérale +// ───────────────────────────────────────────────────────────────────────────── + +const MAX_LENGTH = 200; + +/** +* Affiche les définitions dans la barre latérale. +*/ +function displayDefinitions(definitions) { + console.log("📖 Affichage des définitions reçues :", definitions); + if (!Array.isArray(definitions)) return; + + const mesLexiquesList = document.getElementById("mesLexiquesList"); + const wiktionnaireList = document.getElementById("wiktionnaireList"); + const noLexiconDefinitionsContainer = document.getElementById("noLexiconDefinitionsContainer"); + const noWiktionaryDefinitionsContainer = document.getElementById("noWiktionaryDefinitionsContainer"); + + mesLexiquesList.innerHTML = ""; + wiktionnaireList.innerHTML = ""; + + if (noLexiconDefinitionsContainer) noLexiconDefinitionsContainer.style.display = "none"; + if (noWiktionaryDefinitionsContainer) noWiktionaryDefinitionsContainer.style.display = "none"; + let hasLexiconDefinitions = false; + let hasWiktionaryDefinitions = false; + + const lexiconGroups = {}; + + definitions.forEach(({ source, text }) => { + if (!source || !text) return; + + const li = document.createElement("li"); + let displayedText = text; + + if (text.length > MAX_LENGTH) { + displayedText = text.slice(0, MAX_LENGTH) + "... "; + const readMoreLink = document.createElement("a"); + readMoreLink.href = "#"; + readMoreLink.textContent = "[Lire la suite]"; + readMoreLink.style.marginLeft = "5px"; + readMoreLink.style.color = "#8d5c70"; + readMoreLink.style.textDecoration = "underline"; + readMoreLink.style.cursor = "pointer"; + readMoreLink.addEventListener("click", (event) => { + event.preventDefault(); + openDefinitionPopup(text); + }); + li.appendChild(document.createTextNode(displayedText)); + li.appendChild(readMoreLink); + } else { + li.textContent = displayedText; + } + + // Vérifier la source : Wiktionnaire/Lexiques + if (source === "Wiktionnaire") { + wiktionnaireList.appendChild(li); + hasWiktionaryDefinitions = true; + } else { + if (!lexiconGroups[source]) { + lexiconGroups[source] = []; + } + lexiconGroups[source].push(li); + hasLexiconDefinitions = true; + } + }); + + Object.entries(lexiconGroups).forEach(([lexiconName, definitionItems]) => { + const lexiconContainer = document.createElement("div"); + lexiconContainer.className = "lexicon-section"; + + const lexiconHeader = document.createElement("div"); + lexiconHeader.className = "lexicon-header"; + lexiconHeader.textContent = lexiconName; + lexiconHeader.addEventListener("click", () => { + lexiconContent.classList.toggle("hidden"); + }); + + const lexiconContent = document.createElement("ul"); + lexiconContent.className = "lexicon-content hidden"; + + definitionItems.forEach(li => lexiconContent.appendChild(li)); + + lexiconContainer.appendChild(lexiconHeader); + lexiconContainer.appendChild(lexiconContent); + mesLexiquesList.appendChild(lexiconContainer); + }); + + if (!hasLexiconDefinitions && noLexiconDefinitionsContainer) { + if (!authToken) { + noLexiconDefinitionsContainer.textContent = "Veuillez vous connecter pour accéder aux définitions de vos lexiques."; + noLexiconDefinitionsContainer.style.textAlign = "center"; + noLexiconDefinitionsContainer.style.fontStyle = "italic"; + } else { + noLexiconDefinitionsContainer.textContent = "Aucune définition trouvée dans les lexiques."; + } + noLexiconDefinitionsContainer.style.display = "block"; + } + if (!hasWiktionaryDefinitions && noWiktionaryDefinitionsContainer) { + noWiktionaryDefinitionsContainer.style.display = "block"; + } + + const mesLexiquesContainer = document.getElementById("mesLexiquesContainer"); + if (mesLexiquesContainer) { + mesLexiquesContainer.style.display = hasLexiconDefinitions ? "block" : "none"; + } + const wiktionnaireContainer = document.getElementById("wiktionnaireContainer"); + if (wiktionnaireContainer) { + wiktionnaireContainer.style.display = hasWiktionaryDefinitions ? "block" : "none"; + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// â–Œ Gestion du popup pour afficher la définition complète du Wiktionnaire +// ───────────────────────────────────────────────────────────────────────────── + +function openDefinitionPopup(fullText) { + const modalOverlay = document.getElementById("modalOverlay"); + const modalFullText = document.getElementById("modalFullText"); + if (!modalOverlay || !modalFullText) { + console.error("Modal elements not found!"); + return; + } + modalFullText.innerHTML = "<p>" + fullText.replace(/\n/g, "<br>") + "</p>"; + modalOverlay.style.display = "flex"; +} + +/** +* Ferme le popup et nettoie le contenu +*/ +function closeDefinitionPopup() { + const modalOverlay = document.getElementById("modalOverlay"); + const modalFullText = document.getElementById("modalFullText"); + if (!modalOverlay || !modalFullText) return; + modalOverlay.style.display = "none"; + modalFullText.innerHTML = ""; +} + +// ───────────────────────────────────────────────────────────────────────────── +// â–Œ Affichage des définitions Babalex + Wiktionnaire +// ───────────────────────────────────────────────────────────────────────────── + +/** +* Récupère en parallèle : +* - les définitions des lexiques de l'utilisateur (fetchLexiconDefinitions) +* - la définition Wiktionnaire (fetchWiktionaryDefinition) +* Puis fusionne les résultats. +*/ +async function combineDefinitions(word) { + console.log(`[combineDefinitions] Récupération des définitions pour "${word}"...`); + + const results = await Promise.allSettled([ + fetchLexiconDefinitions(word), + fetchWiktionaryDefinition(word) + ]); + + const lexiconDefinitions = + results[0].status === "fulfilled" ? results[0].value : []; + const wiktionaryDefinitions = + results[1].status === "fulfilled" ? results[1].value : []; + + const allDefinitions = [...lexiconDefinitions, ...wiktionaryDefinitions]; + + console.log("📚 [combineDefinitions] Résultat fusionné :", allDefinitions); + + return allDefinitions; +} + +/** +* Récupère et affiche toutes les définitions (lexiques + Wiktionnaire). +*/ +async function showDefinitions(word) { + console.log(`[showDefinitions] Recherche + affichage pour "${word}"...`); + + const noDefinitionsContainer = document.getElementById("noDefinitionsContainer"); + if (noDefinitionsContainer) { + noDefinitionsContainer.textContent = "Chargement des définitions..."; + noDefinitionsContainer.style.display = "block"; + } + + try { + const allDefinitions = await combineDefinitions(word); + + console.log("[showDefinitions] Définitions récupérées :", allDefinitions); + + if (!allDefinitions || allDefinitions.length === 0) { + if (noDefinitionsContainer) { + noDefinitionsContainer.textContent = "âš ï¸ Aucune définition trouvée."; + } + return; + } + + displayDefinitions(allDefinitions); + + if (noDefinitionsContainer) { + noDefinitionsContainer.style.display = "none"; + } + return allDefinitions; + + } catch (error) { + console.error("⌠[showDefinitions] Erreur : ", error); + + if (noDefinitionsContainer) { + noDefinitionsContainer.textContent = + "⌠Une erreur est survenue lors de la récupération des définitions."; + noDefinitionsContainer.style.display = "block"; + } + return []; + } +} + +/** +* Appel direct pour récupérer les définitions d’un mot uniquement via l’API +* (sans Wiktionnaire), puis gérer l’affichage d’erreur ou non. +*/ +async function fetchDefinition(word) { + console.log(`🔠Recherche de la définition pour '${word}'...`); + + const noDefinitionsContainer = document.getElementById("noDefinitionsContainer"); + if (!noDefinitionsContainer) { + console.error("⌠Élément #noDefinitionsContainer introuvable."); + return; + } + + try { + const definition = await fetchLexiconDefinitions(word); + console.log("🔠Résultat API :", definition); + + if (!definition || definition.length === 0) { + console.warn(`âš ï¸ Aucune définition trouvée pour '${word}'`); + noDefinitionsContainer.style.display = "block"; + return; + } + + noDefinitionsContainer.style.display = "none"; + } catch (error) { + console.error("⌠Erreur lors de la récupération de la définition :", error); + noDefinitionsContainer.style.display = "block"; + } +} + +/** +* Affiche les lexiques où le mot sélectionné est présent. +*/ +function displayLexiconResults(lexicons) { + const resultDiv = document.getElementById("lexiconResult"); + if (!resultDiv) return; + + resultDiv.innerHTML = ""; + + if (!lexicons || lexicons.length === 0) { + resultDiv.textContent = "⌠Ce mot n'est présent dans aucun lexique."; + return; + } + const title = document.createElement("p"); + title.innerHTML = "Ce mot est présent dans le(s) lexique(s) suivant(s) :"; + title.style.fontSize = "12px"; + resultDiv.appendChild(title); + + const ul = document.createElement("ul"); + ul.style.paddingLeft = "20px"; + + lexicons.forEach((lexicon) => { + if (!lexicon) { + console.warn("âš ï¸ Lexique incorrect :", lexicon); + return; + } + if (!lexicon.id) { + console.warn("ID non défini :", lexicon.id); + return; + } + + const lexiconName = lexicon.name || `Lexique #${lexicon.id}`; + const li = document.createElement("li"); + li.innerHTML = `<strong>${lexiconName}</strong>`; + ul.appendChild(li); + + console.log(`✅ Lexique ajouté : ${lexiconName} (ID: ${lexicon.id})`); + }); + + resultDiv.appendChild(ul); +} + +// ───────────────────────────────────────────────────────────────────────────── +// â–Œ Utilisation des fonctions dans d'autres scripts +// ───────────────────────────────────────────────────────────────────────────── + +window.fetchLexiconDefinitions = fetchLexiconDefinitions; +window.fetchWiktionaryDefinition = fetchWiktionaryDefinition; +window.displayDefinitions = displayDefinitions; +window.openDefinitionPopup = openDefinitionPopup; +window.closeDefinitionPopup = closeDefinitionPopup; +window.combineDefinitions = combineDefinitions; +window.showDefinitions = showDefinitions; +window.fetchDefinition = fetchDefinition; +window.displayLexiconResults = displayLexiconResults; diff --git a/manifest.json b/manifest.json index 012ee577cf5923c01d4554510d95fd1fd7dc3e0c..c2b94cae3c2c3e9d69e078dd0b3301d284765db5 100644 --- a/manifest.json +++ b/manifest.json @@ -14,11 +14,16 @@ "notifications", "*://babalex.lezinter.net/*", "*://prisms.lezinter.net/*", - "*://fr.wiktionary.org/*" + "*://fr.wiktionary.org/*", + "<all_urls>" ], "background": { - "scripts": ["background/background.js", "background/browser_context_menu.js"], + "scripts": [ + "background/background.js", + "definitions.js", + "menu_contextuel/browser_context_menu.js", + "api.js"], "persistent": true }, @@ -39,7 +44,7 @@ "default_title": "BaLex", "default_panel": "barre_latérale/sidebar.html", "default_icon": { - "16": "icons/icon-16.png", + "16": "icons/logo.png", "48": "icons/icon-48.png" } }, diff --git a/menu_contextuel/browser_context_menu.js b/menu_contextuel/browser_context_menu.js new file mode 100644 index 0000000000000000000000000000000000000000..a2ba1fab3be52725c98a005b6603114c0e824100 --- /dev/null +++ b/menu_contextuel/browser_context_menu.js @@ -0,0 +1,230 @@ +console.log("browser_context_menu.js chargé correctement"); + +let authToken = null; + +/** + * Charge le token depuis le stockage local et le stocke dans la variable globale authToken. + */ +async function loadAuthToken() { + try { + const result = await browser.storage.local.get("accessToken"); + authToken = result.accessToken; + console.log("🔑 Token chargé au démarrage :", authToken); + } catch (error) { + console.error("⌠Erreur lors de la récupération du token :", error); + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// Création du menu contextuel en fonction de l'authentification +// ───────────────────────────────────────────────────────────────────────────── +async function createContextMenu() { + await browser.contextMenus.removeAll(); + + if (authToken) { + // Item 1 : Recherche dans les lexiques de l’utilisateur + browser.contextMenus.create({ + id: "searchInLexicons", + title: "Rechercher dans mes lexiques", + contexts: ["selection"], + icons: { "16": "icons/quel_lexique.png" }, + }); + + // Item 2 : Ajouter un mot au lexique personnel + browser.contextMenus.create({ + id: "addtoLexicon", + title: "Ajouter ce mot à mon lexique", + contexts: ["selection"], + icons: { "16": "icons/ajout_lexique.png" }, + }); + } + + browser.contextMenus.create({ + id: "separatorExtension", + type: "separator", + contexts: ["all"], + }); + + // Item 3 : Recherche globale de définition (Lexiques + Wiktionnaire) + browser.contextMenus.create({ + id: "getDefinition", + title: "Obtenir une définition", + contexts: ["selection"], + icons: { "16": "icons/definition.png" }, + }); + + browser.contextMenus.create({ + id: "separatorAfterExtension", + type: "separator", + contexts: ["all"], + }); + + browser.contextMenus.create({ + id: "login", + title: authToken ? "Se déconnecter de BaLex" : "Se connecter à BaLex", + contexts: ["all"], + }); +} + +loadAuthToken().then(createContextMenu); + +browser.runtime.onMessage.addListener((message) => { + if (message.action === "refreshUI") { + console.log("🔄 refreshUI reçu dans browser_context_menu.js"); + loadAuthToken().then(createContextMenu); + } +}); + +browser.storage.onChanged.addListener((changes, area) => { + if (area === "local" && changes.accessToken) { + console.log("🔄 Token modifié, actualisation du menu contextuel."); + loadAuthToken().then(createContextMenu); + } +}); + +// ───────────────────────────────────────────────────────────────────────────── +// Gestion des clics sur le menu contextuel +// ───────────────────────────────────────────────────────────────────────────── +browser.contextMenus.onClicked.addListener(async (info, tab) => { + console.log("Item de menu cliqué :", info.menuItemId); + + if (info.menuItemId === "login") { + console.log("🔄 Action login/déconnexion demandée."); + if (typeof actuallyOpenLoginPage === "function") { + actuallyOpenLoginPage(); + } else { + console.error("La fonction actuallyOpenLoginPage n'est pas accessible."); + } + return; + } + + if (!info.selectionText) { + console.warn("Aucun texte sélectionné pour cette action :", info.menuItemId); + return; + } + console.log(`📩 Texte sélectionné : ${info.selectionText}`); + + switch (info.menuItemId) { + case "searchInLexicons": + if (!authToken) { + alert("âš ï¸ Vous devez être connecté pour utiliser cette fonction."); + return; + } + await searchInLexicons(info.selectionText); + break; + + case "addtoLexicon": + if (!authToken) { + alert("âš ï¸ Vous devez être connecté pour utiliser cette fonction."); + return; + } + await addWordToLexicon(info.selectionText); + break; + + case "getDefinition": + await getDefinition(info.selectionText); + break; + + default: + console.error(`⌠Action inconnue : ${info.menuItemId}`); + } +}); + +// ───────────────────────────────────────────────────────────────────────────── +// Fonctions liées aux définitions (definitions.js) +// ───────────────────────────────────────────────────────────────────────────── + +/** + * 1) Recherche de la définition combinée (lexiques + Wiktionnaire) + * et envoi des résultats pour affichage. + */ +async function getDefinition(selectedText) { + try { + let lexiconDefs = []; + if (authToken) { + lexiconDefs = await fetchLexiconDefinitions(selectedText); + } + + const wikiDefs = await fetchWiktionaryDefinition(selectedText); + + const allDefinitions = [...lexiconDefs, ...wikiDefs]; + console.log("📠Définitions combinées :", allDefinitions); + + browser.runtime.sendMessage({ + action: "showDefinitions", + selectedText, + definitions: allDefinitions, + }); + } catch (error) { + console.error("⌠Erreur lors de la recherche combinée des définitions :", error); + } +} + +/** + * 2) Recherche dans les lexiques pour savoir dans quels lexiques se trouve le mot. + */ +async function searchInLexicons(selectedText) { + try { + console.log("🔎 Recherche dans mes lexiques :", selectedText); + + const allDefinitions = await fetchLexiconDefinitions(selectedText); + if (!allDefinitions || allDefinitions.length === 0) { + console.log("⌠Aucun lexique trouvé pour ce mot."); + browser.runtime.sendMessage({ + action: "showLexiconResult", + lexicons: [], + selectedText, + }); + return; + } + + const lexMap = new Map(); + for (const def of allDefinitions) { + if (def.lexiconId) { + lexMap.set(def.lexiconId, def.source); + } + } + + const foundInLexicons = []; + for (const [id, name] of lexMap.entries()) { + foundInLexicons.push({ id, name }); + } + + console.log("📩 Envoi du message 'showLexiconResult' avec :", foundInLexicons); + + browser.runtime.sendMessage({ + action: "showLexiconResult", + lexicons: foundInLexicons, + selectedText, + }); + } catch (error) { + console.error("⌠Erreur lors de la recherche dans les lexiques :", error); + browser.runtime.sendMessage({ + action: "showLexiconResult", + lexicons: [], + selectedText, + }); + } +} + +/** + * 3) Ajouter un mot dans le lexique personnel. + */ +async function addWordToLexicon(selectedText) { + try { + console.log("🔠Ajout du mot à mon lexique :", selectedText); + alert(`(Exemple) Le mot "${selectedText}" a été ajouté à votre lexique personnel !`); + } catch (error) { + console.error("⌠Erreur lors de l'ajout au lexique :", error); + alert("⌠Impossible d'ajouter ce mot au lexique."); + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// Écouteur interne pour d’autres messages éventuels +// ───────────────────────────────────────────────────────────────────────────── +browser.runtime.onMessage.addListener(async (message) => { + if (message.action === "getDefinitionAll") { + await getDefinition(message.selectedText); + } +}); diff --git a/menu_contextuel/custom_context_menu.js b/menu_contextuel/custom_context_menu.js index 4c1da228fb16ccce9f3cf1d7080741b8f21f8e50..510295e6ef12d9c09c6bbf758c14e1afb57a3d47 100644 --- a/menu_contextuel/custom_context_menu.js +++ b/menu_contextuel/custom_context_menu.js @@ -2,7 +2,17 @@ console.log("custom_context_menu.js chargé correctement"); // === Variables globales === let authToken = null; -const WHITE_BOX_ID = "whiteBox"; // Pour éviter les "magic strings" +const WHITE_BOX_ID = "whiteBox"; + +// Fonction utilitaire pour envoyer une notification via le background +function sendNotification(title, message, iconPath) { + browser.runtime.sendMessage({ + action: "showNotification", + title, + message, + iconPath + }); +} // Récupère le token depuis le stockage local et le stocke dans authToken async function loadAuthToken() { @@ -16,170 +26,168 @@ async function loadAuthToken() { } } -// Crée le menu contextuel personnalisé s'il n'existe pas déjà +/** + * Crée le menu contextuel personnalisé (whiteBox) s'il n'existe pas déjà . + */ function injectWhiteBox() { - if (!document.getElementById(WHITE_BOX_ID)) { - const whiteBox = document.createElement("div"); + let whiteBox = document.getElementById(WHITE_BOX_ID); + if (!whiteBox) { + whiteBox = document.createElement("div"); whiteBox.id = WHITE_BOX_ID; - // Exemple pour générer toutes les URLs + // Définition de styles essentiels pour le positionnement et la visibilité + whiteBox.style.position = "absolute"; + whiteBox.style.zIndex = "9999"; + whiteBox.style.backgroundColor = "#fff"; + whiteBox.style.border = "1px solid #ccc"; + whiteBox.style.padding = "5px"; + whiteBox.style.borderRadius = "4px"; + whiteBox.style.boxShadow = "0px 2px 10px rgba(0,0,0,0.2)"; + + // Génération des URLs des icônes const addLexiconPath = browser.runtime.getURL("icons/ajout_lexique.png"); const getDefinitionPath = browser.runtime.getURL("icons/definition.png"); - const getDefinitionWikiPath = browser.runtime.getURL("icons/definition_wiktionnaire.png"); const loginPath = browser.runtime.getURL("icons/connexion.png"); // Construction du HTML whiteBox.innerHTML = ` - <p id="selectedWord">Mot sélectionné : Aucun</p> - <hr style="border: 0; height: 1px; background-color: #323046; margin: 8px 0;"> - <div style="display: flex; flex-wrap: wrap; justify-content: center;"> - - <!-- Bouton 1 - Ajout lexique --> - <div class="icon-container" title="Ajoutez ce mot à votre lexique"> + <p id="selectedWord" style="margin: 0; padding: 0;">Mot sélectionné : Aucun</p> + <hr style="border: 0; height: 1px; background-color: #323046; margin: 8px 0;"> + <div style="display: flex; flex-wrap: wrap; justify-content: center;"> + <!-- Bouton 1 - Ajouter au lexique --> + <div class="icon-container" title="Ajouter ce mot à un lexique"> <img src="${addLexiconPath}" alt="Ajouter au lexique" class="icon" id="addLexiconButton"> </div> - - <!-- Bouton 2 - Définition interne --> - <div class="icon-container" title="Obtenez la définition de ce mot"> + <!-- Bouton 2 - Définition (Babalex + Wiki) --> + <div class="icon-container" title="Obtenir la définition"> <img src="${getDefinitionPath}" alt="Obtenir la définition" class="icon" id="getDefinitionButton"> </div> - - <!-- Bouton 3 - Wiktionnaire --> - <div class="icon-container" title="Rechercher dans le Wiktionnaire"> - <img src="${getDefinitionWikiPath}" alt="Wiktionnaire" class="icon" id="getDefinitionWikiButton"> - </div> - - <!-- Bouton 4 - Connexion --> + <!-- Bouton 3 - Connexion --> <div class="icon-container" title="Connectez-vous à BaLex"> <img src="${loginPath}" alt="Se connecter" class="icon" id="loginButton" style="display: none;"> </div> </div> `; - document.body.appendChild(whiteBox); - - // Configure les actions de chaque bouton setupWhiteBoxActions(); - } else { - console.log(`#${WHITE_BOX_ID} déjà présent dans le DOM.`); } + return whiteBox; +} + +/** + * Renvoie le whiteBox s'il existe, ou le crée via injectWhiteBox(). + */ +function getOrCreateWhiteBox() { + return document.getElementById(WHITE_BOX_ID) || injectWhiteBox(); } -// Configure les actions/boutons du menu +/** + * Configure les actions/boutons du menu contextuel. + */ function setupWhiteBoxActions() { const addLexiconBtn = document.getElementById("addLexiconButton"); const getDefinitionBtn = document.getElementById("getDefinitionButton"); - const getDefinitionWikiBtn = document.getElementById("getDefinitionWikiButton"); const loginBtn = document.getElementById("loginButton"); - // Ajout au lexique + // Bouton : Ajouter le mot au lexique personnel addLexiconBtn.onclick = async () => { const selectedText = getSelectedWord(); console.log("🔠Ajout au lexique :", selectedText); + if (!selectedText) return; + if (authToken) { await searchLexicon(selectedText); } else { - alert("âš ï¸ Veuillez vous connecter pour utiliser cette fonction."); + sendNotification( + "Connexion requise", + "âš ï¸ Veuillez vous connecter pour utiliser cette fonction.", + "icons/connexion.png" + ); } }; - // Obtenir la définition - getDefinitionBtn.onclick = async () => { - const selectedText = getSelectedWord(); - console.log("📖 Recherche des définitions :", selectedText); - if (authToken) { - await getDefinition(selectedText); - } else { - alert("âš ï¸ Veuillez vous connecter pour utiliser cette fonction."); + // Bouton : Obtenir une définition (Babalex + Wiktionnaire) + getDefinitionBtn.onclick = () => { + const selectedText = getSelectedWord().trim(); + if (selectedText) { + browser.runtime.sendMessage({ + action: "getDefinition", + selectedText + }); } }; - // Définition Wiktionnaire - getDefinitionWikiBtn.onclick = () => { - const selectedText = getSelectedWord(); - console.log("🌠Recherche sur le Wiktionnaire :", selectedText); - alert(`Recherche sur le Wiktionnaire pour "${selectedText}".`); - }; - - // Login + // Bouton : Connexion loginBtn.onclick = () => { - alert("Vous allez être redirigé(e) vers la page de connexion."); + sendNotification( + "Redirection", + "Vous allez être redirigé(e) vers la page de connexion.", + "icons/connexion.png" + ); browser.runtime.sendMessage({ action: "toggleAuth" }); }; } -// Met à jour la visibilité des boutons en fonction de l'état de connexion +/** + * Met à jour la visibilité des boutons en fonction de l'état d'authentification. + */ function updateMenuVisibility() { - // S'assurer que la whiteBox est injectée - injectWhiteBox(); - + // S'assurer que le whiteBox existe + getOrCreateWhiteBox(); const addLexiconBtn = document.getElementById("addLexiconButton"); const getDefinitionBtn = document.getElementById("getDefinitionButton"); - const getDefinitionWikiBtn = document.getElementById("getDefinitionWikiButton"); const loginBtn = document.getElementById("loginButton"); - if (!addLexiconBtn || !getDefinitionBtn || !getDefinitionWikiBtn || !loginBtn) { + if (!addLexiconBtn || !getDefinitionBtn || !loginBtn) { console.warn("âš ï¸ Un des boutons n'a pas été trouvé dans le DOM."); return; } if (authToken) { - // Utilisateur connecté + // Utilisateur connecté => afficher "Ajouter" et "Définition", masquer "Connexion" addLexiconBtn.style.display = "inline-block"; getDefinitionBtn.style.display = "inline-block"; - getDefinitionWikiBtn.style.display = "inline-block"; loginBtn.style.display = "none"; } else { - // Utilisateur déconnecté + // Utilisateur déconnecté => masquer "Ajouter", afficher "Définition" et "Connexion" addLexiconBtn.style.display = "none"; - getDefinitionBtn.style.display = "none"; - getDefinitionWikiBtn.style.display = "inline-block"; + getDefinitionBtn.style.display = "inline-block"; loginBtn.style.display = "inline-block"; } } -// Récupère le mot affiché dans #selectedWord. +/** + * Récupère le mot affiché dans #selectedWord. + */ function getSelectedWord() { const selectedWordElement = document.getElementById("selectedWord"); - if (!selectedWordElement) { - return ""; - } - return selectedWordElement.textContent.split(": ")[1] || ""; + return selectedWordElement ? selectedWordElement.textContent.trim() : ""; } -// Affiche le menu contextuel (whiteBox) à la position du clic en mettant à jour le mot sélectionné. +/** + * Affiche le menu contextuel à la position du clic. + */ function showWhiteBox(event, selectedText) { - const whiteBox = document.getElementById(WHITE_BOX_ID); - if (!whiteBox) { - console.error(`#${WHITE_BOX_ID} introuvable dans le DOM.`); - return; - } + const whiteBox = getOrCreateWhiteBox(); + const selectedWordElement = document.getElementById("selectedWord"); + selectedWordElement.textContent = `${selectedText}`; - if (selectedText) { - // Met à jour l'affichage du mot sélectionné - const selectedWordElement = document.getElementById("selectedWord"); - selectedWordElement.textContent = `${selectedText}`; - - // Récupère la position de la sélection - const selection = window.getSelection(); - const range = selection.getRangeAt(0); - const rect = range.getBoundingClientRect(); // Obtenez la position et taille du mot sélectionné - - // Calcule la position du menu en bas à droite du mot sélectionné - const top = rect.bottom + window.scrollY; // Bas du mot + défilement vertical - const left = rect.right + window.scrollX; // Droite du mot + défilement horizontal - - // Positionne la whiteBox - whiteBox.style.left = `${left}px`; - whiteBox.style.top = `${top}px`; - whiteBox.style.display = "block";nsole.log("Affichage du menu contextuel avec le mot :", selectedText); - - // Mettre à jour la visibilité des boutons - updateMenuVisibility(); - } + // Calculer la position du menu en fonction de la sélection + const selection = window.getSelection(); + if (!selection.rangeCount) return; // sécurité au cas où + const range = selection.getRangeAt(0); + const rect = range.getBoundingClientRect(); + const top = rect.bottom + window.scrollY; + const left = rect.right + window.scrollX; + + whiteBox.style.left = `${left}px`; + whiteBox.style.top = `${top}px`; + whiteBox.style.display = "block"; + + console.log("Affichage du menu contextuel avec le mot :", selectedText); + updateMenuVisibility(); } -// Masque la whiteBox. function hideWhiteBox() { const whiteBox = document.getElementById(WHITE_BOX_ID); if (whiteBox) { @@ -187,44 +195,53 @@ function hideWhiteBox() { } } -// === Écoute les événements de sélection de texte (mouseup) === +// === Écoute des événements de sélection de texte (mouseup) === document.addEventListener("mouseup", (event) => { const selectedText = window.getSelection().toString().trim(); if (selectedText) { console.log("Événement de sélection détecté. Texte sélectionné :", selectedText); - injectWhiteBox(); + getOrCreateWhiteBox(); showWhiteBox(event, selectedText); - // Envoie le mot sélectionné à la barre latérale + // Envoi du mot sélectionné au background browser.runtime.sendMessage({ action: "mot_selectionne", selectedText, }); } else { - // Masquer le menu si rien n'est sélectionné hideWhiteBox(); } }); -// Réécouter les messages envoyés par le background script +// Réécoute des messages envoyés browser.runtime.onMessage.addListener((message) => { if (message.action === "refreshUI") { - console.log("🔄 Mise à jour du menu contextuel personnalisé (double-clic/sélection)."); - // Recharger le token puis mettre à jour l'affichage + console.log("🔄 Mise à jour du menu contextuel personnalisé."); loadAuthToken().then(() => { updateMenuVisibility(); }); } }); -// === Charger le token au démarrage, puis mettre à jour le menu === +// Initialisation au démarrage loadAuthToken().then(() => { - injectWhiteBox(); + getOrCreateWhiteBox(); updateMenuVisibility(); }); +browser.storage.onChanged.addListener((changes) => { + if (changes.accessToken) { + console.log("🔄 Token changé dans le stockage, mise à jour du menu personnalisé."); + loadAuthToken().then(() => { + updateMenuVisibility(); + }); + } +}); + +// ───────────────────────────────────────────────────────────────────────────── +// â–Œ Fonctions d'API Babalex pour l'ajout ou la vérification d'un mot +// ───────────────────────────────────────────────────────────────────────────── -// Recherche un mot dans le lexique personnel async function searchLexicon(selectedText) { console.log("🔄 Recherche dans le lexique personnel pour :", selectedText); try { @@ -252,73 +269,3 @@ async function searchLexicon(selectedText) { alert(`Erreur lors de la recherche : ${error.message}`); } } - -// Vérifie dans quels lexiques le mot est présent -async function checkLexicon(selectedText) { - try { - const lexicons = await getLexicons(authToken); - const results = lexicons.map((lexicon) => ({ - name: lexicon.name, - isPresent: lexicon.entries.some( - (entry) => entry.graphy.toLowerCase() === selectedText.toLowerCase() - ), - })); - - console.log("📋 Résultats des lexiques :", results); - let message = `Résultats pour "${selectedText}":\n\n`; - results.forEach(({ name, isPresent }) => { - message += `- ${name}: ${isPresent ? "✅ Présent" : "⌠Non présent"}\n`; - }); - alert(message); - } catch (error) { - console.error("⌠Erreur lors de la vérification des lexiques :", error); - alert(`Erreur lors de la vérification : ${error.message}`); - } -} - -// Obtenir les définitions d'un mot à partir de plusieurs lexiques. -async function getDefinition(selectedText) { - try { - const lexicons = await getLexicons(authToken); - let definitions = []; - - for (const lexicon of lexicons) { - const entries = await getLexiconEntriesID(authToken, lexicon.id); - const entry = entries.find( - (e) => e.graphy.toLowerCase() === selectedText.toLowerCase() - ); - - if (entry) { - const definitionData = await getDefinitionAPI(authToken, entry.id); - // Remarque: j'ai renommé en getDefinitionAPI pour éviter - // la confusion avec la fonction "getDefinition" ci-dessus. - definitions.push(...extractDefinitions(definitionData)); - } - } - - console.log("📖 Définitions trouvées :", definitions); - if (definitions.length > 0) { - alert(`📖 Définitions pour "${selectedText}":\n\n` + definitions.join("\n")); - } else { - alert(`⌠Aucune définition trouvée pour "${selectedText}".`); - } - } catch (error) { - console.error("⌠Erreur lors de la récupération des définitions :", error); - alert(`Erreur lors de la récupération des définitions : ${error.message}`); - } -} - -// Extrait un tableau de définitions à partir d'une réponse JSON de l'API. -function extractDefinitions(response) { - if (!response || !response.attributes || !response.attributes.Items) { - console.warn("âš ï¸ Structure de réponse inattendue."); - return []; - } - - const items = response.attributes.Items; - return items.flatMap((item) => { - const defs = item.Sense?.Definitions?.map((def) => def.Def || "Définition non disponible"); - return defs || []; - }); -} -